Skip to content

Commit a67cfe5

Browse files
authored
add basic build dependencies
- Add package.json with scripts and dependencies - Add TypeScript configuration (tsconfig.json) - Add Vite config with Coder Desktop support - Add build-wasm.sh script for automated WASM builds - Add xterm.js-compatible interfaces (lib/interfaces.ts) - Add event emitter implementation (lib/event-emitter.ts) - Commit ghostty-vt.wasm (122 KB vanilla Ghostty build) - Update .gitignore (remove zig entries, add .vite/ and *.log) - Fix lib/ghostty.ts imports (remove .ts extension) All commands verified working: - bun install - bun run typecheck (0 errors) - bun run build:wasm - bun run dev - bun test
1 parent 1f83813 commit a67cfe5

10 files changed

Lines changed: 377 additions & 10 deletions

File tree

.gitignore

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
zig/.zig-cache
2-
zig/zig-out
3-
*.wasm
41
node_modules/
52
dist/
63
.DS_Store
4+
*.log
5+
.vite/

bun.lock

Lines changed: 148 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ghostty-vt.wasm

121 KB
Binary file not shown.

lib/event-emitter.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { IEvent, IDisposable } from './interfaces';
2+
3+
export class EventEmitter<T> {
4+
private listeners: Array<(arg: T) => void> = [];
5+
6+
fire(arg: T): void {
7+
for (const listener of this.listeners) {
8+
listener(arg);
9+
}
10+
}
11+
12+
event: IEvent<T> = (listener) => {
13+
this.listeners.push(listener);
14+
return {
15+
dispose: () => {
16+
const index = this.listeners.indexOf(listener);
17+
if (index >= 0) {
18+
this.listeners.splice(index, 1);
19+
}
20+
}
21+
};
22+
};
23+
24+
dispose(): void {
25+
this.listeners = [];
26+
}
27+
}

lib/ghostty.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
* exports from libghostty-vt.wasm
66
*/
77

8-
import type {
9-
GhosttyWasmExports,
10-
SgrAttribute,
8+
import {
9+
type GhosttyWasmExports,
10+
type SgrAttribute,
1111
SgrAttributeTag,
12-
RGBColor,
13-
KeyEvent,
12+
type RGBColor,
13+
type KeyEvent,
1414
KeyEncoderOption,
15-
KittyKeyFlags,
16-
} from './types.ts';
15+
type KittyKeyFlags,
16+
} from './types';
1717

1818
/**
1919
* Main Ghostty WASM wrapper class

lib/interfaces.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* xterm.js-compatible interfaces
3+
*/
4+
5+
export interface ITerminalOptions {
6+
cols?: number; // Default: 80
7+
rows?: number; // Default: 24
8+
cursorBlink?: boolean; // Default: false
9+
cursorStyle?: 'block' | 'underline' | 'bar';
10+
theme?: ITheme;
11+
scrollback?: number; // Default: 1000
12+
fontSize?: number; // Default: 15
13+
fontFamily?: string; // Default: 'monospace'
14+
allowTransparency?: boolean;
15+
}
16+
17+
export interface ITheme {
18+
foreground?: string;
19+
background?: string;
20+
cursor?: string;
21+
cursorAccent?: string;
22+
selectionBackground?: string;
23+
selectionForeground?: string;
24+
25+
// ANSI colors (0-15)
26+
black?: string;
27+
red?: string;
28+
green?: string;
29+
yellow?: string;
30+
blue?: string;
31+
magenta?: string;
32+
cyan?: string;
33+
white?: string;
34+
brightBlack?: string;
35+
brightRed?: string;
36+
brightGreen?: string;
37+
brightYellow?: string;
38+
brightBlue?: string;
39+
brightMagenta?: string;
40+
brightCyan?: string;
41+
brightWhite?: string;
42+
}
43+
44+
export interface IDisposable {
45+
dispose(): void;
46+
}
47+
48+
export interface IEvent<T> {
49+
(listener: (arg: T) => void): IDisposable;
50+
}
51+
52+
export interface ITerminalAddon {
53+
activate(terminal: ITerminalCore): void;
54+
dispose(): void;
55+
}
56+
57+
export interface ITerminalCore {
58+
cols: number;
59+
rows: number;
60+
element?: HTMLElement;
61+
textarea?: HTMLTextAreaElement;
62+
}

package.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@cmux/ghostty-terminal",
3+
"version": "0.1.0",
4+
"description": "Terminal emulator using Ghostty's VT100 parser via WASM",
5+
"type": "module",
6+
"main": "./dist/index.js",
7+
"types": "./dist/index.d.ts",
8+
"exports": {
9+
".": {
10+
"types": "./dist/index.d.ts",
11+
"default": "./dist/index.js"
12+
},
13+
"./addons/fit": {
14+
"types": "./dist/addons/fit.d.ts",
15+
"default": "./dist/addons/fit.js"
16+
}
17+
},
18+
"files": [
19+
"dist/**/*",
20+
"ghostty-vt.wasm",
21+
"README.md",
22+
"LICENSE"
23+
],
24+
"scripts": {
25+
"build:wasm": "bash scripts/build-wasm.sh",
26+
"dev": "vite",
27+
"build": "tsc && vite build",
28+
"typecheck": "tsc --noEmit",
29+
"test": "bun test",
30+
"test:watch": "bun test --watch"
31+
},
32+
"keywords": ["terminal", "xterm", "ghostty", "wasm", "vt100"],
33+
"author": "Coder",
34+
"license": "AGPL-3.0-only",
35+
"repository": {
36+
"type": "git",
37+
"url": "git+https://github.com/coder/cmux.git"
38+
},
39+
"private": true,
40+
"devDependencies": {
41+
"@types/bun": "^1.2.23",
42+
"typescript": "^5.1.3",
43+
"vite": "^7.1.11"
44+
},
45+
"browserslist": [
46+
"Chrome >= 90"
47+
]
48+
}

scripts/build-wasm.sh

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/bin/bash
2+
set -e
3+
4+
echo "🔨 Building ghostty-vt.wasm..."
5+
6+
# Check for Zig
7+
if ! command -v zig &> /dev/null; then
8+
echo "❌ Error: Zig not found"
9+
echo ""
10+
echo "Install Zig 0.15.2+:"
11+
echo " macOS: brew install zig"
12+
echo " Linux: https://ziglang.org/download/"
13+
echo ""
14+
exit 1
15+
fi
16+
17+
ZIG_VERSION=$(zig version)
18+
echo "✓ Found Zig $ZIG_VERSION"
19+
20+
# Clone/update Ghostty
21+
GHOSTTY_DIR="/tmp/ghostty-for-wasm"
22+
if [ ! -d "$GHOSTTY_DIR" ]; then
23+
echo "📦 Cloning Ghostty..."
24+
git clone --depth=1 https://github.com/ghostty-org/ghostty.git "$GHOSTTY_DIR"
25+
else
26+
echo "📦 Updating Ghostty..."
27+
cd "$GHOSTTY_DIR"
28+
git pull --quiet
29+
fi
30+
31+
# Build WASM
32+
cd "$GHOSTTY_DIR"
33+
echo "⚙️ Building WASM (takes ~20 seconds)..."
34+
zig build lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall
35+
36+
# Copy to project root
37+
# Get absolute path to script directory (resolve relative paths)
38+
SCRIPT_PATH="$(readlink -f "$0")"
39+
SCRIPT_DIR="$(dirname "$SCRIPT_PATH")"
40+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
41+
cp "$GHOSTTY_DIR/zig-out/bin/ghostty-vt.wasm" "$PROJECT_ROOT/"
42+
43+
SIZE=$(du -h "$PROJECT_ROOT/ghostty-vt.wasm" | cut -f1)
44+
echo "✅ Built ghostty-vt.wasm ($SIZE)"

tsconfig.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "ESNext",
5+
"lib": ["ES2020", "DOM"],
6+
"moduleResolution": "bundler",
7+
"resolveJsonModule": true,
8+
"allowSyntheticDefaultImports": true,
9+
"esModuleInterop": true,
10+
"declaration": true,
11+
"declarationMap": true,
12+
"sourceMap": true,
13+
"outDir": "./dist",
14+
"rootDir": "./lib",
15+
"strict": true,
16+
"skipLibCheck": true,
17+
"forceConsistentCasingInFileNames": true,
18+
"types": ["bun-types"]
19+
},
20+
"include": ["lib/**/*"],
21+
"exclude": ["node_modules", "dist", "**/*.test.ts"]
22+
}

vite.config.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { defineConfig } from 'vite';
2+
3+
export default defineConfig({
4+
server: {
5+
port: 8000,
6+
// Disable auto-open since we're running in Coder Desktop
7+
// open: '/examples/sgr-demo.html',
8+
allowedHosts: ['.coder'],
9+
},
10+
build: {
11+
lib: {
12+
entry: 'lib/index.ts',
13+
name: 'GhosttyTerminal',
14+
fileName: 'ghostty-terminal',
15+
},
16+
},
17+
});

0 commit comments

Comments
 (0)