Skip to content

Commit 705fff4

Browse files
authored
Merge branch 'main' into main
2 parents 3f400f4 + e297e6c commit 705fff4

2 files changed

Lines changed: 30 additions & 10 deletions

File tree

README.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![NPM Version](https://img.shields.io/npm/v/ghostty-web)](https://npmjs.com/package/ghostty-web) [![NPM Downloads](https://img.shields.io/npm/dw/ghostty-web)](https://npmjs.com/package/ghostty-web) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/ghostty-web)](https://npmjs.com/package/ghostty-web) [![license](https://img.shields.io/github/license/coder/ghostty-web)](./LICENSE)
44

5-
[Ghostty](https://github.com/ghostty-org/ghostty) for the web with [xterm.js](https://github.com/xtermjs/xterm.js) API compatibility — giving you a proper VT100 implementation in the browser, not a JavaScript approximation of one.
5+
[Ghostty](https://github.com/ghostty-org/ghostty) for the web with [xterm.js](https://github.com/xtermjs/xterm.js) API compatibility — giving you a proper VT100 implementation in the browser.
66

77
- Migrate from xterm by changing your import: `@xterm/xterm``ghostty-web`
88
- WASM-compiled parser from Ghostty—the same code that runs the native app
@@ -12,23 +12,26 @@ Originally created for [Mux](https://github.com/coder/mux) (a desktop app for is
1212

1313
## Try It
1414

15-
```bash
16-
npx @ghostty-web/demo@next
17-
```
15+
- [Live Demo](https://ghostty.ondis.co) on an ephemeral VM (thank you to Greg from [disco.cloud](https://disco.cloud) for hosting).
16+
17+
- On your computer:
18+
19+
```bash
20+
npx @ghostty-web/demo@next
21+
```
1822

19-
This starts a local HTTP server with a real shell on `http://localhost:8080`. Works best on Linux and macOS.
23+
This starts a local HTTP server with a real shell on `http://localhost:8080`. Works best on Linux and macOS.
2024

2125
![ghostty](https://github.com/user-attachments/assets/aceee7eb-d57b-4d89-ac3d-ee1885d0187a)
2226

2327
## Comparison with xterm.js
2428

2529
xterm.js is everywhere—VS Code, Hyper, countless web terminals. But it has fundamental issues:
2630

27-
| Issue | xterm.js | ghostty-web |
28-
| ---------------------------------------- | ------------------------------------------------------------------- | -------------------------- |
29-
| **RTL languages** | [Broken since 2017](https://github.com/xtermjs/xterm.js/issues/701) | ✓ Works |
30-
| **Complex scripts** (Devanagari, Arabic) | Rendering issues | ✓ Proper grapheme handling |
31-
| **XTPUSHSGR/XTPOPSGR** | [Not supported](https://github.com/xtermjs/xterm.js/issues/2570) | ✓ Full support |
31+
| Issue | xterm.js | ghostty-web |
32+
| ---------------------------------------- | ---------------------------------------------------------------- | -------------------------- |
33+
| **Complex scripts** (Devanagari, Arabic) | Rendering issues | ✓ Proper grapheme handling |
34+
| **XTPUSHSGR/XTPOPSGR** | [Not supported](https://github.com/xtermjs/xterm.js/issues/2570) | ✓ Full support |
3235

3336
xterm.js reimplements terminal emulation in JavaScript. Every escape sequence, every edge case, every Unicode quirk—all hand-coded. Ghostty's emulator is the same battle-tested code that runs the native Ghostty app.
3437

lib/terminal.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,17 @@ export class Terminal implements ITerminalCore {
325325
parent.setAttribute('tabindex', '0');
326326
}
327327

328+
// Mark as contenteditable so browser extensions (Vimium, etc.) recognize
329+
// this as an input element and don't intercept keyboard events.
330+
parent.setAttribute('contenteditable', 'true');
331+
// Prevent actual content editing - we handle input ourselves
332+
parent.addEventListener('beforeinput', (e) => e.preventDefault());
333+
334+
// Add accessibility attributes for screen readers and extensions
335+
parent.setAttribute('role', 'textbox');
336+
parent.setAttribute('aria-label', 'Terminal input');
337+
parent.setAttribute('aria-multiline', 'true');
338+
328339
// Create WASM terminal with current dimensions and theme config
329340
const wasmConfig = this.buildWasmConfig();
330341
this.wasmTerm = this.ghostty!.createTerminal(this.cols, this.rows, wasmConfig);
@@ -1113,6 +1124,12 @@ export class Terminal implements ITerminalCore {
11131124
this.element.removeEventListener('mousemove', this.handleMouseMove);
11141125
this.element.removeEventListener('mouseleave', this.handleMouseLeave);
11151126
this.element.removeEventListener('click', this.handleClick);
1127+
1128+
// Remove contenteditable and accessibility attributes added in open()
1129+
this.element.removeAttribute('contenteditable');
1130+
this.element.removeAttribute('role');
1131+
this.element.removeAttribute('aria-label');
1132+
this.element.removeAttribute('aria-multiline');
11161133
}
11171134

11181135
// Remove document-level listeners (only if opened)

0 commit comments

Comments
 (0)