Skip to content

coder/ghostty-web

Repository files navigation

Ghostty Web

A web-based terminal emulator that integrates Ghostty's VT100 parser via WebAssembly.

Installation

npm install @coder/ghostty-web

Or install from GitHub:

npm install github:coder/ghostty-web

Quick Start

import { Terminal } from '@coder/ghostty-web';

const term = new Terminal({ cols: 80, rows: 24 });
await term.open(document.getElementById('terminal'));
term.write('Hello, World!\r\n');

See INSTALL.md for complete usage guide.

Features

  • ✅ Full xterm.js-compatible API
  • ✅ Production-tested VT100 parser (via Ghostty)
  • ✅ ANSI colors (16, 256, RGB true color)
  • ✅ Canvas rendering at 60 FPS
  • ✅ Scrollback buffer
  • ✅ Text selection & clipboard
  • ✅ FitAddon for responsive sizing
  • ✅ TypeScript declarations included

Development & Demos

Shell Terminal Demo

Requires server

# Terminal 1: Start PTY shell server
cd demo/server
bun install
bun run start

# Terminal 2: Start web server (from project root)
bun run dev

# Open: http://localhost:8000/demo/

This provides a real persistent shell session! You can:

  • Use cd and it persists between commands
  • Run interactive programs like vim, nano, top, htop
  • Use tab completion and command history (↑/↓)
  • Use pipes, redirects, and background jobs
  • Access all your shell aliases and environment

Alternative: Command-by-Command Mode

For the original file browser (executes each command separately):

cd demo/server
bun run file-browser

Remote Access: If you're accessing via a forwarded hostname (e.g., mux.coder), make sure to forward both ports:

  • Port 8000 (web server - Vite)
  • Port 3001 (WebSocket server)

The terminal will automatically connect to the WebSocket using the same hostname you're accessing the page from.

Colors Demo (no server needed)

bun run dev
# Open: http://localhost:8000/demo/colors-demo.html

See all ANSI colors (16, 256, RGB) and text styles in action.

Usage

Basic Terminal

import { Terminal } from './lib/index.ts';
import { FitAddon } from './lib/addons/fit.ts';

// Create terminal
const term = new Terminal({
  cols: 80,
  rows: 24,
  cursorBlink: true,
  theme: {
    background: '#1e1e1e',
    foreground: '#d4d4d4',
  },
});

// Add FitAddon for responsive sizing
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);

// Open in container
await term.open(document.getElementById('terminal'));
fitAddon.fit();

// Write output (supports ANSI colors)
term.write('Hello, World!\r\n');
term.write('\x1b[1;32mGreen bold text\x1b[0m\r\n');

// Handle user input
term.onData((data) => {
  console.log('User typed:', data);
  // Send to backend, echo, etc.
});

WebSocket Integration

const ws = new WebSocket('ws://localhost:3001/ws');

// Send user input to backend
term.onData((data) => {
  ws.send(JSON.stringify({ type: 'input', data }));
});

// Display backend output
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  term.write(msg.data);
};

See AGENTS.md for development guide and code patterns.

Why This Approach?

DON'T re-implement VT100 parsing from scratch (years of work, thousands of edge cases).

DO use Ghostty's proven parser:

  • ✅ Battle-tested by thousands of users
  • ✅ Handles all VT100/ANSI quirks correctly
  • ✅ Modern features (RGB colors, Kitty keyboard protocol)
  • ✅ Get bug fixes and updates for free

You build: Screen buffer, rendering, UI (the "easy" parts in TypeScript)
Ghostty handles: VT100 parsing (the hard part via WASM)

Architecture

┌─────────────────────────────────────────┐
│  Terminal (lib/terminal.ts)             │
│  - Public xterm.js-compatible API       │
│  - Event handling (onData, onResize)    │
└───────────┬─────────────────────────────┘
            │
            ├─► ScreenBuffer (lib/buffer.ts)
            │   - 2D grid, cursor, scrollback
            │
            ├─► VTParser (lib/vt-parser.ts)
            │   - ANSI escape sequence parsing
            │   └─► Ghostty WASM (SGR parser)
            │
            ├─► CanvasRenderer (lib/renderer.ts)
            │   - Canvas-based rendering
            │   - 60 FPS, supports all colors
            │
            └─► InputHandler (lib/input-handler.ts)
                - Keyboard events → escape codes
                └─► Ghostty WASM (Key encoder)

WebSocket Server (server/file-browser-server.ts)
└─► Executes shell commands (ls, cd, cat, etc.)

Project Structure

├── lib/
│   ├── terminal.ts       - Main Terminal class (xterm.js-compatible)
│   ├── buffer.ts         - Screen buffer with scrollback
│   ├── vt-parser.ts      - VT100/ANSI escape sequence parser
│   ├── renderer.ts       - Canvas-based renderer
│   ├── input-handler.ts  - Keyboard input handling
│   ├── ghostty.ts        - Ghostty WASM wrapper
│   ├── types.ts          - TypeScript type definitions
│   ├── interfaces.ts     - xterm.js-compatible interfaces
│   └── addons/
│       └── fit.ts        - FitAddon for responsive sizing
│
├── demo/
│   ├── index.html        - File browser terminal
│   ├── colors-demo.html  - ANSI colors showcase
│   └── server/
│       ├── file-browser-server.ts - WebSocket server
│       ├── package.json
│       └── start.sh      - Startup script (auto-kills port conflicts)
│
├── docs/
│   └── API.md            - Complete API documentation
│
└── ghostty-vt.wasm       - Ghostty VT100 parser (122 KB)

Building WASM

The WASM binary is built from source, not committed to the repo.

Requirements:

  • Zig 0.15.2+
  • Git submodules initialized

Build:

# Initialize submodule (first time only)
git submodule update --init --recursive

# Build WASM
./scripts/build-wasm.sh
# or
bun run build:wasm

What it does:

  1. Initializes ghostty/ submodule (ghostty-org/ghostty)
  2. Applies patches from patches/ghostty-wasm-api.patch
  3. Builds WASM with Zig (takes ~20 seconds)
  4. Outputs ghostty-vt.wasm (404 KB)
  5. Reverts patch to keep submodule clean

Updating Ghostty:

cd ghostty
git fetch origin
git checkout <commit-or-tag>
cd ..
./scripts/build-wasm.sh
# Test, then commit the updated submodule pointer

CI: The WASM is built as part of the test and build jobs.

Testing

Run the test suite:

bun test                # Run all tests
bun test --watch        # Watch mode
bun run typecheck       # Type checking
bun run build           # Build distribution

Test Coverage:

  • ✅ ScreenBuffer (63 tests, 163 assertions)
  • ✅ VTParser (45 tests)
  • ✅ CanvasRenderer (11 tests)
  • ✅ InputHandler (35 tests)
  • ✅ Terminal integration (25 tests)
  • ✅ FitAddon (12 tests)

Documentation

  • AGENTS.md - Development guide for AI agents and developers

Links

License

See cmux LICENSE (AGPL-3.0)

About

No description, website, or topics provided.

Resources

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages