ghostty-web 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALL.md +181 -0
- package/README.md +293 -0
- package/dist/__vite-browser-external-2447137e.js +4 -0
- package/dist/ghostty-vt.wasm +0 -0
- package/dist/ghostty-web.js +1452 -0
- package/dist/ghostty-web.umd.cjs +3 -0
- package/dist/index.d.ts +953 -0
- package/ghostty-vt.wasm +0 -0
- package/package.json +70 -0
package/INSTALL.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Installation & Usage
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @coder/ghostty-web
|
|
7
|
+
# or
|
|
8
|
+
bun add @coder/ghostty-web
|
|
9
|
+
# or
|
|
10
|
+
yarn add @coder/ghostty-web
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Installing from Git
|
|
14
|
+
|
|
15
|
+
You can install directly from GitHub:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install github:coder/ghostty-web
|
|
19
|
+
# or
|
|
20
|
+
bun add github:coder/ghostty-web
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Note:** Git installs require manually building the package first. See [Local Development](#local-development) for build instructions.
|
|
24
|
+
|
|
25
|
+
## Basic Usage
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Terminal } from '@coder/ghostty-web';
|
|
29
|
+
|
|
30
|
+
const term = new Terminal({
|
|
31
|
+
cols: 80,
|
|
32
|
+
rows: 24,
|
|
33
|
+
cursorBlink: true,
|
|
34
|
+
theme: {
|
|
35
|
+
background: '#1e1e1e',
|
|
36
|
+
foreground: '#d4d4d4',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Mount to DOM
|
|
41
|
+
await term.open(document.getElementById('terminal'));
|
|
42
|
+
|
|
43
|
+
// Write output
|
|
44
|
+
term.write('Hello, World!\r\n');
|
|
45
|
+
term.write('\x1b[1;32mGreen text\x1b[0m\r\n');
|
|
46
|
+
|
|
47
|
+
// Handle user input
|
|
48
|
+
term.onData((data) => {
|
|
49
|
+
console.log('User typed:', data);
|
|
50
|
+
// Send to backend, echo, etc.
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## With FitAddon (Responsive Sizing)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { Terminal, FitAddon } from '@coder/ghostty-web';
|
|
58
|
+
|
|
59
|
+
const term = new Terminal();
|
|
60
|
+
const fitAddon = new FitAddon();
|
|
61
|
+
term.loadAddon(fitAddon);
|
|
62
|
+
|
|
63
|
+
await term.open(document.getElementById('terminal'));
|
|
64
|
+
fitAddon.fit(); // Resize to container
|
|
65
|
+
|
|
66
|
+
// Resize on window resize
|
|
67
|
+
window.addEventListener('resize', () => fitAddon.fit());
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## WebSocket Integration
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { Terminal } from '@coder/ghostty-web';
|
|
74
|
+
|
|
75
|
+
const term = new Terminal();
|
|
76
|
+
await term.open(document.getElementById('terminal'));
|
|
77
|
+
|
|
78
|
+
const ws = new WebSocket('ws://localhost:3001/ws');
|
|
79
|
+
|
|
80
|
+
// Send user input to backend
|
|
81
|
+
term.onData((data) => {
|
|
82
|
+
ws.send(JSON.stringify({ type: 'input', data }));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Display backend output
|
|
86
|
+
ws.onmessage = (event) => {
|
|
87
|
+
const msg = JSON.parse(event.data);
|
|
88
|
+
term.write(msg.data);
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## WASM File Handling
|
|
93
|
+
|
|
94
|
+
The library requires the `ghostty-vt.wasm` file at runtime. When installing from npm, the WASM is pre-built and included in the package.
|
|
95
|
+
|
|
96
|
+
### Local Development
|
|
97
|
+
|
|
98
|
+
After cloning:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
./scripts/build-wasm.sh
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The script will automatically initialize the submodule if needed. The WASM file is generated locally and gitignored.
|
|
105
|
+
|
|
106
|
+
### Vite (Recommended)
|
|
107
|
+
|
|
108
|
+
Vite handles WASM automatically. No extra config needed:
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// vite.config.js
|
|
112
|
+
export default {
|
|
113
|
+
// WASM works out of the box
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Webpack
|
|
118
|
+
|
|
119
|
+
Configure WASM as an asset:
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
// webpack.config.js
|
|
123
|
+
module.exports = {
|
|
124
|
+
module: {
|
|
125
|
+
rules: [
|
|
126
|
+
{
|
|
127
|
+
test: /\.wasm$/,
|
|
128
|
+
type: 'asset/resource',
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Manual Import (Advanced)
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import wasmUrl from '@coder/ghostty-web/ghostty-vt.wasm?url';
|
|
139
|
+
import { Ghostty } from '@coder/ghostty-web';
|
|
140
|
+
|
|
141
|
+
const ghostty = await Ghostty.load(wasmUrl);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## TypeScript Support
|
|
145
|
+
|
|
146
|
+
Full TypeScript definitions are included:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { Terminal, ITerminalOptions, ITheme } from '@coder/ghostty-web';
|
|
150
|
+
|
|
151
|
+
const options: ITerminalOptions = {
|
|
152
|
+
cols: 80,
|
|
153
|
+
rows: 24,
|
|
154
|
+
cursorBlink: true,
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const theme: ITheme = {
|
|
158
|
+
background: '#1e1e1e',
|
|
159
|
+
foreground: '#d4d4d4',
|
|
160
|
+
cursor: '#ffffff',
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## API Documentation
|
|
165
|
+
|
|
166
|
+
See [API.md](https://github.com/coder/ghostty-web/blob/main/packaging/docs/API.md) for complete API reference.
|
|
167
|
+
|
|
168
|
+
## Migration from xterm.js
|
|
169
|
+
|
|
170
|
+
This library follows xterm.js conventions:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Before (xterm.js)
|
|
174
|
+
import { Terminal } from 'xterm';
|
|
175
|
+
import { FitAddon } from 'xterm-addon-fit';
|
|
176
|
+
|
|
177
|
+
// After (@coder/ghostty-web)
|
|
178
|
+
import { Terminal, FitAddon } from '@coder/ghostty-web';
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Most xterm.js code works with minimal changes. See [API.md](https://github.com/coder/ghostty-web/blob/main/packaging/docs/API.md) for differences.
|
package/README.md
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Ghostty Web
|
|
2
|
+
|
|
3
|
+
A web-based terminal emulator that integrates [Ghostty's](https://github.com/ghostty-org/ghostty) VT100 parser via WebAssembly.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @coder/ghostty-web
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install from GitHub:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install github:coder/ghostty-web
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { Terminal } from '@coder/ghostty-web';
|
|
21
|
+
|
|
22
|
+
const term = new Terminal({ cols: 80, rows: 24 });
|
|
23
|
+
await term.open(document.getElementById('terminal'));
|
|
24
|
+
term.write('Hello, World!\r\n');
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
See [INSTALL.md](./INSTALL.md) for complete usage guide.
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- ✅ Full xterm.js-compatible API
|
|
32
|
+
- ✅ Production-tested VT100 parser (via Ghostty)
|
|
33
|
+
- ✅ ANSI colors (16, 256, RGB true color)
|
|
34
|
+
- ✅ Canvas rendering at 60 FPS
|
|
35
|
+
- ✅ Scrollback buffer
|
|
36
|
+
- ✅ Text selection & clipboard
|
|
37
|
+
- ✅ FitAddon for responsive sizing
|
|
38
|
+
- ✅ TypeScript declarations included
|
|
39
|
+
|
|
40
|
+
## Development & Demos
|
|
41
|
+
|
|
42
|
+
### Shell Terminal Demo
|
|
43
|
+
|
|
44
|
+
**Requires server**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Terminal 1: Start PTY shell server
|
|
48
|
+
cd demo/server
|
|
49
|
+
bun install
|
|
50
|
+
bun run start
|
|
51
|
+
|
|
52
|
+
# Terminal 2: Start web server (from project root)
|
|
53
|
+
bun run dev
|
|
54
|
+
|
|
55
|
+
# Open: http://localhost:8000/demo/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This provides a **real persistent shell session**! You can:
|
|
59
|
+
|
|
60
|
+
- Use `cd` and it persists between commands
|
|
61
|
+
- Run interactive programs like `vim`, `nano`, `top`, `htop`
|
|
62
|
+
- Use tab completion and command history (↑/↓)
|
|
63
|
+
- Use pipes, redirects, and background jobs
|
|
64
|
+
- Access all your shell aliases and environment
|
|
65
|
+
|
|
66
|
+
**Alternative: Command-by-Command Mode**
|
|
67
|
+
|
|
68
|
+
For the original file browser (executes each command separately):
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cd demo/server
|
|
72
|
+
bun run file-browser
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Remote Access:** If you're accessing via a forwarded hostname (e.g., `mux.coder`), make sure to forward both ports:
|
|
76
|
+
|
|
77
|
+
- Port 8000 (web server - Vite)
|
|
78
|
+
- Port 3001 (WebSocket server)
|
|
79
|
+
|
|
80
|
+
The terminal will automatically connect to the WebSocket using the same hostname you're accessing the page from.
|
|
81
|
+
|
|
82
|
+
**Colors Demo** (no server needed)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
bun run dev
|
|
86
|
+
# Open: http://localhost:8000/demo/colors-demo.html
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
See all ANSI colors (16, 256, RGB) and text styles in action.
|
|
90
|
+
|
|
91
|
+
## Usage
|
|
92
|
+
|
|
93
|
+
### Basic Terminal
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { Terminal } from './lib/index.ts';
|
|
97
|
+
import { FitAddon } from './lib/addons/fit.ts';
|
|
98
|
+
|
|
99
|
+
// Create terminal
|
|
100
|
+
const term = new Terminal({
|
|
101
|
+
cols: 80,
|
|
102
|
+
rows: 24,
|
|
103
|
+
cursorBlink: true,
|
|
104
|
+
theme: {
|
|
105
|
+
background: '#1e1e1e',
|
|
106
|
+
foreground: '#d4d4d4',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Add FitAddon for responsive sizing
|
|
111
|
+
const fitAddon = new FitAddon();
|
|
112
|
+
term.loadAddon(fitAddon);
|
|
113
|
+
|
|
114
|
+
// Open in container
|
|
115
|
+
await term.open(document.getElementById('terminal'));
|
|
116
|
+
fitAddon.fit();
|
|
117
|
+
|
|
118
|
+
// Write output (supports ANSI colors)
|
|
119
|
+
term.write('Hello, World!\r\n');
|
|
120
|
+
term.write('\x1b[1;32mGreen bold text\x1b[0m\r\n');
|
|
121
|
+
|
|
122
|
+
// Handle user input
|
|
123
|
+
term.onData((data) => {
|
|
124
|
+
console.log('User typed:', data);
|
|
125
|
+
// Send to backend, echo, etc.
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### WebSocket Integration
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const ws = new WebSocket('ws://localhost:3001/ws');
|
|
133
|
+
|
|
134
|
+
// Send user input to backend
|
|
135
|
+
term.onData((data) => {
|
|
136
|
+
ws.send(JSON.stringify({ type: 'input', data }));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Display backend output
|
|
140
|
+
ws.onmessage = (event) => {
|
|
141
|
+
const msg = JSON.parse(event.data);
|
|
142
|
+
term.write(msg.data);
|
|
143
|
+
};
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
See [AGENTS.md](AGENTS.md) for development guide and code patterns.
|
|
147
|
+
|
|
148
|
+
## Why This Approach?
|
|
149
|
+
|
|
150
|
+
**DON'T** re-implement VT100 parsing from scratch (years of work, thousands of edge cases).
|
|
151
|
+
|
|
152
|
+
**DO** use Ghostty's proven parser:
|
|
153
|
+
|
|
154
|
+
- ✅ Battle-tested by thousands of users
|
|
155
|
+
- ✅ Handles all VT100/ANSI quirks correctly
|
|
156
|
+
- ✅ Modern features (RGB colors, Kitty keyboard protocol)
|
|
157
|
+
- ✅ Get bug fixes and updates for free
|
|
158
|
+
|
|
159
|
+
**You build**: Screen buffer, rendering, UI (the "easy" parts in TypeScript)
|
|
160
|
+
**Ghostty handles**: VT100 parsing (the hard part via WASM)
|
|
161
|
+
|
|
162
|
+
## Architecture
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
┌─────────────────────────────────────────┐
|
|
166
|
+
│ Terminal (lib/terminal.ts) │
|
|
167
|
+
│ - Public xterm.js-compatible API │
|
|
168
|
+
│ - Event handling (onData, onResize) │
|
|
169
|
+
└───────────┬─────────────────────────────┘
|
|
170
|
+
│
|
|
171
|
+
├─► ScreenBuffer (lib/buffer.ts)
|
|
172
|
+
│ - 2D grid, cursor, scrollback
|
|
173
|
+
│
|
|
174
|
+
├─► VTParser (lib/vt-parser.ts)
|
|
175
|
+
│ - ANSI escape sequence parsing
|
|
176
|
+
│ └─► Ghostty WASM (SGR parser)
|
|
177
|
+
│
|
|
178
|
+
├─► CanvasRenderer (lib/renderer.ts)
|
|
179
|
+
│ - Canvas-based rendering
|
|
180
|
+
│ - 60 FPS, supports all colors
|
|
181
|
+
│
|
|
182
|
+
└─► InputHandler (lib/input-handler.ts)
|
|
183
|
+
- Keyboard events → escape codes
|
|
184
|
+
└─► Ghostty WASM (Key encoder)
|
|
185
|
+
|
|
186
|
+
WebSocket Server (server/file-browser-server.ts)
|
|
187
|
+
└─► Executes shell commands (ls, cd, cat, etc.)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Project Structure
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
├── lib/
|
|
194
|
+
│ ├── terminal.ts - Main Terminal class (xterm.js-compatible)
|
|
195
|
+
│ ├── buffer.ts - Screen buffer with scrollback
|
|
196
|
+
│ ├── vt-parser.ts - VT100/ANSI escape sequence parser
|
|
197
|
+
│ ├── renderer.ts - Canvas-based renderer
|
|
198
|
+
│ ├── input-handler.ts - Keyboard input handling
|
|
199
|
+
│ ├── ghostty.ts - Ghostty WASM wrapper
|
|
200
|
+
│ ├── types.ts - TypeScript type definitions
|
|
201
|
+
│ ├── interfaces.ts - xterm.js-compatible interfaces
|
|
202
|
+
│ └── addons/
|
|
203
|
+
│ └── fit.ts - FitAddon for responsive sizing
|
|
204
|
+
│
|
|
205
|
+
├── demo/
|
|
206
|
+
│ ├── index.html - File browser terminal
|
|
207
|
+
│ ├── colors-demo.html - ANSI colors showcase
|
|
208
|
+
│ └── server/
|
|
209
|
+
│ ├── file-browser-server.ts - WebSocket server
|
|
210
|
+
│ ├── package.json
|
|
211
|
+
│ └── start.sh - Startup script (auto-kills port conflicts)
|
|
212
|
+
│
|
|
213
|
+
├── docs/
|
|
214
|
+
│ └── API.md - Complete API documentation
|
|
215
|
+
│
|
|
216
|
+
└── ghostty-vt.wasm - Ghostty VT100 parser (122 KB)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Building WASM
|
|
220
|
+
|
|
221
|
+
The WASM binary is built from source, not committed to the repo.
|
|
222
|
+
|
|
223
|
+
**Requirements:**
|
|
224
|
+
|
|
225
|
+
- Zig 0.15.2+
|
|
226
|
+
- Git submodules initialized
|
|
227
|
+
|
|
228
|
+
**Build:**
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Initialize submodule (first time only)
|
|
232
|
+
git submodule update --init --recursive
|
|
233
|
+
|
|
234
|
+
# Build WASM
|
|
235
|
+
./scripts/build-wasm.sh
|
|
236
|
+
# or
|
|
237
|
+
bun run build:wasm
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**What it does:**
|
|
241
|
+
|
|
242
|
+
1. Initializes `ghostty/` submodule (ghostty-org/ghostty)
|
|
243
|
+
2. Applies patches from `patches/ghostty-wasm-api.patch`
|
|
244
|
+
3. Builds WASM with Zig (takes ~20 seconds)
|
|
245
|
+
4. Outputs `ghostty-vt.wasm` (404 KB)
|
|
246
|
+
5. Reverts patch to keep submodule clean
|
|
247
|
+
|
|
248
|
+
**Updating Ghostty:**
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
cd ghostty
|
|
252
|
+
git fetch origin
|
|
253
|
+
git checkout <commit-or-tag>
|
|
254
|
+
cd ..
|
|
255
|
+
./scripts/build-wasm.sh
|
|
256
|
+
# Test, then commit the updated submodule pointer
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**CI:** The WASM is built as part of the `test` and `build` jobs.
|
|
260
|
+
|
|
261
|
+
## Testing
|
|
262
|
+
|
|
263
|
+
Run the test suite:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
bun test # Run all tests
|
|
267
|
+
bun test --watch # Watch mode
|
|
268
|
+
bun run typecheck # Type checking
|
|
269
|
+
bun run build # Build distribution
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Test Coverage:**
|
|
273
|
+
|
|
274
|
+
- ✅ ScreenBuffer (63 tests, 163 assertions)
|
|
275
|
+
- ✅ VTParser (45 tests)
|
|
276
|
+
- ✅ CanvasRenderer (11 tests)
|
|
277
|
+
- ✅ InputHandler (35 tests)
|
|
278
|
+
- ✅ Terminal integration (25 tests)
|
|
279
|
+
- ✅ FitAddon (12 tests)
|
|
280
|
+
|
|
281
|
+
## Documentation
|
|
282
|
+
|
|
283
|
+
- **[AGENTS.md](AGENTS.md)** - Development guide for AI agents and developers
|
|
284
|
+
|
|
285
|
+
## Links
|
|
286
|
+
|
|
287
|
+
- [Ghostty Terminal](https://github.com/ghostty-org/ghostty)
|
|
288
|
+
- [libghostty-vt API](https://github.com/ghostty-org/ghostty/tree/main/include/ghostty/vt)
|
|
289
|
+
- [VT100 Reference](https://vt100.net/docs/vt100-ug/)
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
See cmux LICENSE (AGPL-3.0)
|
|
Binary file
|