|
1 | 1 | # ghostty-web |
2 | 2 |
|
3 | | - |
| 3 | +[](https://npmjs.com/package/ghostty-web) [](https://npmjs.com/package/ghostty-web) [](./LICENSE) |
4 | 4 |
|
5 | | -`ghostty-web` is a web terminal developed for [mux](https://github.com/coder/mux) that leverages |
6 | | -[Ghostty's](https://github.com/ghostty-org/ghostty) |
7 | | -terminal emulation core via WebAssembly. Because it leans on Ghostty to handle the complexity of terminal |
8 | | -emulation, `ghostty-web` can deliver fast, robust terminal emulation in the browser. The intent is |
9 | | -for this project to become a drop-in replacement for xterm.js. Under heavy development, no compatibility guarantees yet. |
| 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. |
10 | 6 |
|
11 | | -## Live Demo |
| 7 | +- Migrate from xterm by changing your import: `@xterm/xterm` → `ghostty-web` |
| 8 | +- WASM-compiled parser from Ghostty—the same code that runs the native app |
| 9 | +- Zero runtime dependencies, ~400KB WASM bundle |
12 | 10 |
|
13 | | -Try ghostty-web yourself with: |
| 11 | +## Try It |
14 | 12 |
|
15 | 13 | ```bash |
16 | 14 | npx @ghostty-web/demo@next |
17 | 15 | ``` |
18 | 16 |
|
19 | | -This starts a local demo server with a real shell session. The demo server works best when run from Linux, but you can also try |
20 | | -it on macOS. Windows is not supported (yet). |
| 17 | +This starts a local HTTP server with a real shell on `http://localhost:8080`. Works best on Linux and macOS. |
21 | 18 |
|
22 | | -<details> |
23 | | -<summary>Development setup (building from source)</summary> |
| 19 | + |
24 | 20 |
|
25 | | -> Requires Zig and Bun, see [Development](#development) |
| 21 | +## Comparison with xterm.js |
26 | 22 |
|
27 | | -```bash |
28 | | -git clone https://github.com/coder/ghostty-web |
29 | | -cd ghostty-web |
30 | | -bun install |
31 | | -bun run build # Builds the WASM module and library |
32 | | -bun run demo:dev # http://localhost:8000/demo/ |
33 | | -``` |
| 23 | +xterm.js is everywhere—VS Code, Hyper, countless web terminals. But it has fundamental issues: |
34 | 24 |
|
35 | | -</details> |
| 25 | +| Issue | xterm.js | ghostty-web | |
| 26 | +| ---------------------------------------- | ------------------------------------------------------------------- | -------------------------- | |
| 27 | +| **RTL languages** | [Broken since 2017](https://github.com/xtermjs/xterm.js/issues/701) | ✓ Works | |
| 28 | +| **Complex scripts** (Devanagari, Arabic) | Rendering issues | ✓ Proper grapheme handling | |
| 29 | +| **XTPUSHSGR/XTPOPSGR** | [Not supported](https://github.com/xtermjs/xterm.js/issues/2570) | ✓ Full support | |
36 | 30 |
|
37 | | -## Getting Started |
| 31 | +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. |
38 | 32 |
|
39 | | -Install the module via npm |
| 33 | +## Installation |
40 | 34 |
|
41 | 35 | ```bash |
42 | 36 | npm install ghostty-web |
43 | 37 | ``` |
44 | 38 |
|
45 | | -After install, using `ghostty-web` is as simple as |
46 | | - |
47 | | -```html |
48 | | -<!doctype html> |
49 | | -<html> |
50 | | - <body> |
51 | | - <div id="terminal"></div> |
52 | | - <script type="module"> |
53 | | - import { init, Terminal } from 'ghostty-web'; |
54 | | -
|
55 | | - await init(); |
56 | | - const term = new Terminal(); |
57 | | - term.open(document.getElementById('terminal')); |
58 | | - term.write('Hello from \x1B[1;3;31mghostty-web\x1B[0m $ '); |
59 | | - </script> |
60 | | - </body> |
61 | | -</html> |
62 | | -``` |
63 | | - |
64 | | -## Features |
65 | | - |
66 | | -`ghostty-web` compiles Ghostty's core terminal emulation engine (parser, state |
67 | | -machine, and screen buffer) to WebAssembly, providing: |
68 | | - |
69 | | -**Core Terminal:** |
70 | | - |
71 | | -- Full VT100/ANSI escape sequence support |
72 | | -- True color (24-bit RGB) + 256 color + 16 ANSI colors |
73 | | -- Text styles: bold, italic, underline, strikethrough, dim, reverse |
74 | | -- Alternate screen buffer (for vim, htop, less, etc.) |
75 | | -- Scrollback buffer with mouse wheel support |
76 | | - |
77 | | -**Input & Interaction:** |
78 | | - |
79 | | -- Text selection and clipboard integration |
80 | | -- Mouse tracking modes |
81 | | -- Custom key/wheel event handlers |
82 | | - |
83 | | -**API & Integration:** |
84 | | - |
85 | | -- xterm.js-compatible API (drop-in replacement for many use cases) |
86 | | -- FitAddon for responsive terminal sizing |
87 | | -- Event system (onData, onResize, onBell, onScroll, etc.) |
88 | | - |
89 | | -**Performance:** |
| 39 | +## Usage |
90 | 40 |
|
91 | | -- Canvas-based rendering at 60 FPS |
92 | | -- Zero runtime dependencies (just ghostty-web + bundled WASM) |
93 | | -- Parser/state machine from Ghostty |
| 41 | +ghostty-web aims to be API-compatible with the xterm.js API. |
94 | 42 |
|
95 | | -## Usage Examples |
| 43 | +```javascript |
| 44 | +import { init, Terminal } from 'ghostty-web'; |
96 | 45 |
|
97 | | -### Basic Terminal |
98 | | - |
99 | | -```typescript |
100 | | -import { init, Terminal, FitAddon } from 'ghostty-web'; |
101 | | - |
102 | | -// Initialize WASM (call once at app startup) |
103 | 46 | await init(); |
104 | 47 |
|
105 | 48 | const term = new Terminal({ |
106 | | - cursorBlink: true, |
107 | 49 | fontSize: 14, |
108 | 50 | theme: { |
109 | | - background: '#1e1e1e', |
110 | | - foreground: '#d4d4d4', |
| 51 | + background: '#1a1b26', |
| 52 | + foreground: '#a9b1d6', |
111 | 53 | }, |
112 | 54 | }); |
113 | 55 |
|
114 | | -const fitAddon = new FitAddon(); |
115 | | -term.loadAddon(fitAddon); |
116 | | - |
117 | 56 | term.open(document.getElementById('terminal')); |
118 | | -fitAddon.fit(); |
119 | | - |
120 | | -// Handle user input |
121 | | -term.onData((data) => { |
122 | | - // Send to backend/PTY |
123 | | - console.log('User typed:', data); |
124 | | -}); |
| 57 | +term.onData((data) => websocket.send(data)); |
| 58 | +websocket.onmessage = (e) => term.write(e.data); |
125 | 59 | ``` |
126 | 60 |
|
127 | | -## Development |
128 | | - |
129 | | -### Prerequisites |
| 61 | +For a comprehensive client <-> server example, refer to the [demo](./demo/index.html#L141). |
130 | 62 |
|
131 | | -- [bun](https://bun.com/docs/installation) |
132 | | -- [zig](https://ziglang.org/download/) |
| 63 | +## Development |
133 | 64 |
|
134 | | -### Building WASM |
| 65 | +ghostty-web builds from Ghostty's source with a [patch](./patches/ghostty-wasm-api.patch) to expose additional |
| 66 | +functionality. |
135 | 67 |
|
136 | | -`ghostty-web` builds a custom WASM binary from Ghostty's source with a patch to expose additional |
137 | | -functionality |
| 68 | +> Requires Zig and Bun. |
138 | 69 |
|
139 | 70 | ```bash |
140 | 71 | bun run build |
141 | 72 | ``` |
| 73 | + |
| 74 | +Mitchell Hashimoto (author of Ghostty) has [been working](https://mitchellh.com/writing/libghostty-is-coming) on `libghostty` which makes this all possible. The patches are very minimal thanks to the work the Ghostty team has done, and we expect them to get smaller. |
| 75 | + |
| 76 | +This library will eventually consume a native Ghostty WASM distribution once available, and will continue to provide an xterm.js compatible API. |
| 77 | + |
| 78 | +At Coder we're big fans of Ghostty, so kudos to that team for all the amazing work. |
| 79 | + |
| 80 | +## License |
| 81 | + |
| 82 | +[MIT](./LICENSE) |
0 commit comments