wraithterm 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +313 -2
- package/css/xterm.css +285 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +707 -0
- package/dist/keycodes.d.ts +1 -0
- package/dist/keycodes.js +179 -0
- package/dist/protocol.d.ts +117 -0
- package/dist/protocol.js +1 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.js +19 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +11 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +302 -0
- package/dist/xterm-api.d.ts +1956 -0
- package/dist/xterm-atlas.d.ts +39 -0
- package/dist/xterm-atlas.js +176 -0
- package/dist/xterm-internals.d.ts +120 -0
- package/dist/xterm-internals.js +19 -0
- package/dist/xterm-renderer.d.ts +77 -0
- package/dist/xterm-renderer.js +540 -0
- package/dist/xterm.d.ts +17 -0
- package/dist/xterm.js +35 -0
- package/docs/ARCHITECTURE.md +179 -0
- package/docs/COMPATIBILITY.md +66 -0
- package/docs/PERFORMANCE.md +122 -0
- package/docs/TOP_TIER.md +124 -0
- package/docs/XTERM_API.md +163 -0
- package/docs/compatibility/xterm-chrome.json +34 -0
- package/licenses/Ghostty-MIT.txt +21 -0
- package/licenses/JetBrainsMono-OFL.txt +93 -0
- package/licenses/NOTICE.md +93 -0
- package/licenses/Zig-MIT.txt +21 -0
- package/licenses/rust/arrayvec-0.7.8/LICENSE-APACHE +201 -0
- package/licenses/rust/arrayvec-0.7.8/LICENSE-MIT +25 -0
- package/licenses/rust/autocfg-1.5.1/LICENSE-APACHE +201 -0
- package/licenses/rust/autocfg-1.5.1/LICENSE-MIT +25 -0
- package/licenses/rust/bit-set-0.10.0/LICENSE-APACHE +201 -0
- package/licenses/rust/bit-set-0.10.0/LICENSE-MIT +25 -0
- package/licenses/rust/bit-vec-0.9.1/LICENSE-APACHE +201 -0
- package/licenses/rust/bit-vec-0.9.1/LICENSE-MIT +25 -0
- package/licenses/rust/bitflags-2.13.2/LICENSE-APACHE +201 -0
- package/licenses/rust/bitflags-2.13.2/LICENSE-MIT +25 -0
- package/licenses/rust/bumpalo-3.20.3/LICENSE-APACHE +201 -0
- package/licenses/rust/bumpalo-3.20.3/LICENSE-MIT +25 -0
- package/licenses/rust/bytemuck-1.25.2/LICENSE-APACHE +61 -0
- package/licenses/rust/bytemuck-1.25.2/LICENSE-MIT +9 -0
- package/licenses/rust/bytemuck-1.25.2/LICENSE-ZLIB +11 -0
- package/licenses/rust/bytemuck_derive-1.12.1/LICENSE-APACHE +61 -0
- package/licenses/rust/bytemuck_derive-1.12.1/LICENSE-MIT +9 -0
- package/licenses/rust/bytemuck_derive-1.12.1/LICENSE-ZLIB +11 -0
- package/licenses/rust/cfg-if-1.0.5/LICENSE-APACHE +201 -0
- package/licenses/rust/cfg-if-1.0.5/LICENSE-MIT +25 -0
- package/licenses/rust/cfg_aliases-0.2.2/LICENSE +9 -0
- package/licenses/rust/codespan-reporting-0.13.1/LICENSE +201 -0
- package/licenses/rust/crunchy-0.2.4/LICENSE +21 -0
- package/licenses/rust/document-features-0.2.12/LICENSE-APACHE +73 -0
- package/licenses/rust/document-features-0.2.12/LICENSE-MIT +19 -0
- package/licenses/rust/equivalent-1.0.2/LICENSE-APACHE +201 -0
- package/licenses/rust/equivalent-1.0.2/LICENSE-MIT +25 -0
- package/licenses/rust/foldhash-0.2.0/LICENSE +19 -0
- package/licenses/rust/font-types-0.12.6/LICENSE-APACHE +67 -0
- package/licenses/rust/font-types-0.12.6/LICENSE-MIT +25 -0
- package/licenses/rust/futures-core-0.3.34/LICENSE-APACHE +202 -0
- package/licenses/rust/futures-core-0.3.34/LICENSE-MIT +26 -0
- package/licenses/rust/futures-task-0.3.34/LICENSE-APACHE +202 -0
- package/licenses/rust/futures-task-0.3.34/LICENSE-MIT +26 -0
- package/licenses/rust/futures-util-0.3.34/LICENSE-APACHE +202 -0
- package/licenses/rust/futures-util-0.3.34/LICENSE-MIT +26 -0
- package/licenses/rust/half-2.7.1/LICENSE-APACHE +176 -0
- package/licenses/rust/half-2.7.1/LICENSE-MIT +19 -0
- package/licenses/rust/harfrust-0.13.3/LICENSE +22 -0
- package/licenses/rust/hashbrown-0.17.1/LICENSE-APACHE +201 -0
- package/licenses/rust/hashbrown-0.17.1/LICENSE-MIT +25 -0
- package/licenses/rust/indexmap-2.14.2/LICENSE-APACHE +201 -0
- package/licenses/rust/indexmap-2.14.2/LICENSE-MIT +25 -0
- package/licenses/rust/js-sys-0.3.106/LICENSE-APACHE +201 -0
- package/licenses/rust/js-sys-0.3.106/LICENSE-MIT +25 -0
- package/licenses/rust/libc-0.2.190/LICENSE-APACHE +176 -0
- package/licenses/rust/libc-0.2.190/LICENSE-MIT +25 -0
- package/licenses/rust/libloading-0.8.9/LICENSE +12 -0
- package/licenses/rust/libm-0.2.16/LICENSE.txt +258 -0
- package/licenses/rust/litrs-1.0.0/LICENSE-APACHE +176 -0
- package/licenses/rust/litrs-1.0.0/LICENSE-MIT +25 -0
- package/licenses/rust/lock_api-0.4.14/LICENSE-APACHE +201 -0
- package/licenses/rust/lock_api-0.4.14/LICENSE-MIT +25 -0
- package/licenses/rust/log-0.4.34/LICENSE-APACHE +201 -0
- package/licenses/rust/log-0.4.34/LICENSE-MIT +25 -0
- package/licenses/rust/naga-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/naga-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/naga-types-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/naga-types-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/num-traits-0.2.19/LICENSE-APACHE +201 -0
- package/licenses/rust/num-traits-0.2.19/LICENSE-MIT +25 -0
- package/licenses/rust/once_cell-1.21.4/LICENSE-APACHE +201 -0
- package/licenses/rust/once_cell-1.21.4/LICENSE-MIT +23 -0
- package/licenses/rust/parking_lot-0.12.5/LICENSE-APACHE +201 -0
- package/licenses/rust/parking_lot-0.12.5/LICENSE-MIT +25 -0
- package/licenses/rust/parking_lot_core-0.9.12/LICENSE-APACHE +201 -0
- package/licenses/rust/parking_lot_core-0.9.12/LICENSE-MIT +25 -0
- package/licenses/rust/pin-project-lite-0.2.17/LICENSE-APACHE +177 -0
- package/licenses/rust/pin-project-lite-0.2.17/LICENSE-MIT +23 -0
- package/licenses/rust/portable-atomic-1.15.0/LICENSE-APACHE +177 -0
- package/licenses/rust/portable-atomic-1.15.0/LICENSE-MIT +23 -0
- package/licenses/rust/portable-atomic-util-0.2.8/LICENSE-APACHE +177 -0
- package/licenses/rust/portable-atomic-util-0.2.8/LICENSE-MIT +23 -0
- package/licenses/rust/proc-macro2-1.0.107/LICENSE-APACHE +176 -0
- package/licenses/rust/proc-macro2-1.0.107/LICENSE-MIT +23 -0
- package/licenses/rust/quote-1.0.47/LICENSE-APACHE +176 -0
- package/licenses/rust/quote-1.0.47/LICENSE-MIT +23 -0
- package/licenses/rust/raw-window-handle-0.6.2/LICENSE-APACHE.md +177 -0
- package/licenses/rust/raw-window-handle-0.6.2/LICENSE-MIT.md +21 -0
- package/licenses/rust/raw-window-handle-0.6.2/LICENSE-ZLIB.md +11 -0
- package/licenses/rust/read-fonts-0.41.0/LICENSE-APACHE +67 -0
- package/licenses/rust/read-fonts-0.41.0/LICENSE-MIT +25 -0
- package/licenses/rust/read-fonts-0.43.3/LICENSE-APACHE +67 -0
- package/licenses/rust/read-fonts-0.43.3/LICENSE-MIT +25 -0
- package/licenses/rust/redox_syscall-0.5.18/LICENSE +22 -0
- package/licenses/rust/renderdoc-sys-1.1.0/LICENSE-APACHE +201 -0
- package/licenses/rust/renderdoc-sys-1.1.0/LICENSE-MIT +25 -0
- package/licenses/rust/rustc-hash-1.1.0/LICENSE-APACHE +201 -0
- package/licenses/rust/rustc-hash-1.1.0/LICENSE-MIT +23 -0
- package/licenses/rust/rustversion-1.0.23/LICENSE-APACHE +176 -0
- package/licenses/rust/rustversion-1.0.23/LICENSE-MIT +23 -0
- package/licenses/rust/scopeguard-1.2.0/LICENSE-APACHE +201 -0
- package/licenses/rust/scopeguard-1.2.0/LICENSE-MIT +25 -0
- package/licenses/rust/serde-1.0.229/LICENSE-APACHE +176 -0
- package/licenses/rust/serde-1.0.229/LICENSE-MIT +23 -0
- package/licenses/rust/serde_core-1.0.229/LICENSE-APACHE +176 -0
- package/licenses/rust/serde_core-1.0.229/LICENSE-MIT +23 -0
- package/licenses/rust/serde_derive-1.0.229/LICENSE-APACHE +176 -0
- package/licenses/rust/serde_derive-1.0.229/LICENSE-MIT +23 -0
- package/licenses/rust/skrifa-0.44.0/LICENSE-APACHE +67 -0
- package/licenses/rust/skrifa-0.44.0/LICENSE-MIT +25 -0
- package/licenses/rust/slab-0.4.12/LICENSE +25 -0
- package/licenses/rust/smallvec-1.16.2/LICENSE-APACHE +201 -0
- package/licenses/rust/smallvec-1.16.2/LICENSE-MIT +25 -0
- package/licenses/rust/static_assertions-1.1.0/LICENSE-APACHE +202 -0
- package/licenses/rust/static_assertions-1.1.0/LICENSE-MIT +21 -0
- package/licenses/rust/swash-0.2.10/LICENSE-APACHE +201 -0
- package/licenses/rust/swash-0.2.10/LICENSE-MIT +25 -0
- package/licenses/rust/syn-2.0.119/LICENSE-APACHE +176 -0
- package/licenses/rust/syn-2.0.119/LICENSE-MIT +23 -0
- package/licenses/rust/syn-3.0.6/LICENSE-APACHE +176 -0
- package/licenses/rust/syn-3.0.6/LICENSE-MIT +23 -0
- package/licenses/rust/termcolor-1.4.1/COPYING +3 -0
- package/licenses/rust/termcolor-1.4.1/LICENSE-MIT +21 -0
- package/licenses/rust/termcolor-1.4.1/UNLICENSE +24 -0
- package/licenses/rust/thiserror-2.0.21/LICENSE-APACHE +176 -0
- package/licenses/rust/thiserror-2.0.21/LICENSE-MIT +23 -0
- package/licenses/rust/thiserror-impl-2.0.21/LICENSE-APACHE +176 -0
- package/licenses/rust/thiserror-impl-2.0.21/LICENSE-MIT +23 -0
- package/licenses/rust/tokio-1.53.2/LICENSE +21 -0
- package/licenses/rust/unicode-ident-1.0.26/LICENSE-APACHE +176 -0
- package/licenses/rust/unicode-ident-1.0.26/LICENSE-MIT +23 -0
- package/licenses/rust/unicode-ident-1.0.26/LICENSE-UNICODE +39 -0
- package/licenses/rust/unicode-width-0.2.2/LICENSE-APACHE +201 -0
- package/licenses/rust/unicode-width-0.2.2/LICENSE-MIT +25 -0
- package/licenses/rust/wasm-bindgen-0.2.129/LICENSE-APACHE +201 -0
- package/licenses/rust/wasm-bindgen-0.2.129/LICENSE-MIT +25 -0
- package/licenses/rust/wasm-bindgen-futures-0.4.79/LICENSE-APACHE +201 -0
- package/licenses/rust/wasm-bindgen-futures-0.4.79/LICENSE-MIT +25 -0
- package/licenses/rust/wasm-bindgen-macro-0.2.129/LICENSE-APACHE +201 -0
- package/licenses/rust/wasm-bindgen-macro-0.2.129/LICENSE-MIT +25 -0
- package/licenses/rust/wasm-bindgen-macro-support-0.2.129/LICENSE-APACHE +201 -0
- package/licenses/rust/wasm-bindgen-macro-support-0.2.129/LICENSE-MIT +25 -0
- package/licenses/rust/wasm-bindgen-shared-0.2.129/LICENSE-APACHE +201 -0
- package/licenses/rust/wasm-bindgen-shared-0.2.129/LICENSE-MIT +25 -0
- package/licenses/rust/web-sys-0.3.106/LICENSE-APACHE +201 -0
- package/licenses/rust/web-sys-0.3.106/LICENSE-MIT +25 -0
- package/licenses/rust/wgpu-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/wgpu-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/wgpu-core-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/wgpu-core-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/wgpu-core-deps-windows-linux-android-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/wgpu-core-deps-windows-linux-android-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/wgpu-hal-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/wgpu-hal-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/wgpu-naga-bridge-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/wgpu-naga-bridge-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/wgpu-types-30.0.1/LICENSE.APACHE +176 -0
- package/licenses/rust/wgpu-types-30.0.1/LICENSE.MIT +21 -0
- package/licenses/rust/winapi-util-0.1.11/COPYING +3 -0
- package/licenses/rust/winapi-util-0.1.11/LICENSE-MIT +21 -0
- package/licenses/rust/winapi-util-0.1.11/UNLICENSE +24 -0
- package/licenses/rust/windows-link-0.2.1/license-apache-2.0 +201 -0
- package/licenses/rust/windows-link-0.2.1/license-mit +21 -0
- package/licenses/rust/windows-sys-0.61.2/license-apache-2.0 +201 -0
- package/licenses/rust/windows-sys-0.61.2/license-mit +21 -0
- package/licenses/rust/yazi-0.2.1/LICENSE-APACHE +201 -0
- package/licenses/rust/yazi-0.2.1/LICENSE-MIT +19 -0
- package/licenses/rust/zeno-0.3.3/LICENSE-APACHE +201 -0
- package/licenses/rust/zeno-0.3.3/LICENSE-MIT +25 -0
- package/licenses/rust/zerocopy-0.8.62/LICENSE-APACHE +202 -0
- package/licenses/rust/zerocopy-0.8.62/LICENSE-BSD +24 -0
- package/licenses/rust/zerocopy-0.8.62/LICENSE-MIT +26 -0
- package/licenses/rust/zerocopy-derive-0.8.62/LICENSE-APACHE +202 -0
- package/licenses/rust/zerocopy-derive-0.8.62/LICENSE-BSD +24 -0
- package/licenses/rust/zerocopy-derive-0.8.62/LICENSE-MIT +26 -0
- package/licenses/xterm-MIT.txt +21 -0
- package/package.json +69 -4
- package/wasm/Ghostty-MIT.txt +21 -0
- package/wasm/JetBrainsMono-Regular.ttf +0 -0
- package/wasm/OFL.txt +93 -0
- package/wasm/optimization.json +6 -0
- package/wasm/wraith_engine.d.ts +176 -0
- package/wasm/wraith_engine.js +2057 -0
- package/wasm/wraith_engine_bg.wasm +0 -0
- package/wasm/wraith_engine_bg.wasm.d.ts +71 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Wraith separates terminal semantics, rendering, and browser integration. The
|
|
4
|
+
`Wraith.mount()` terminal model never crosses JavaScript as a JSON screen or an array of cell
|
|
5
|
+
objects. Each `Wraith.mount()` instance owns a worker, a Wasm module, and a GPU device.
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
flowchart LR
|
|
9
|
+
App[Application / PTY transport] --> SDK[TypeScript SDK]
|
|
10
|
+
SDK -->|owned byte buffers / commands| Worker[Dedicated worker]
|
|
11
|
+
Worker -->|one Wasm instance| Ghostty[libghostty-vt]
|
|
12
|
+
Ghostty -->|C ABI / changed rows| Core[Retained Rust snapshot]
|
|
13
|
+
Core --> Text[HarfRust / swash / glyph atlas]
|
|
14
|
+
Text --> GPU[Rust wgpu / WGSL]
|
|
15
|
+
GPU --> Canvas[OffscreenCanvas / WebGPU]
|
|
16
|
+
Ghostty -->|responses / input encoding| Worker
|
|
17
|
+
Worker --> SDK
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Ownership
|
|
21
|
+
|
|
22
|
+
`Core` owns a Ghostty bridge handle and a stable Rust `State` allocation. The State's address
|
|
23
|
+
remains stable when Core moves. Its internal raw owner prevents an owning Box
|
|
24
|
+
field from being retagged while foreign code retains a userdata pointer. Public
|
|
25
|
+
snapshot access is read-only. Ghostty callbacks may borrow it only synchronously,
|
|
26
|
+
inside a write or render-state update. No callback reenters the terminal. The bridge
|
|
27
|
+
is freed before the State, and uses Rust allocation callbacks for every Ghostty
|
|
28
|
+
allocation. There are no competing Zig and Rust heaps.
|
|
29
|
+
|
|
30
|
+
`native/bridge.c` is the sole translation layer for upstream C structs, sized
|
|
31
|
+
struct initialization, and enum constants. Rust exposes its own small `Frame`
|
|
32
|
+
layout. A pinned Ghostty revision and C compilation isolate upstream API churn.
|
|
33
|
+
|
|
34
|
+
`Renderer` owns the device, surface, pipeline, atlas texture, cell storage buffer,
|
|
35
|
+
uniform buffer, and CPU staging cells. `Atlas` owns font bytes, shaping metadata,
|
|
36
|
+
the swash scaler context, and a bounded cache keyed by grapheme and style.
|
|
37
|
+
|
|
38
|
+
The SDK copies caller-owned input once before transferring it to the worker.
|
|
39
|
+
String input is encoded once. Wasm bindings copy input into Wasm memory. Inside
|
|
40
|
+
Wasm, changed cells are copied into retained Rust storage; they do not make an
|
|
41
|
+
expensive trip through JavaScript. WebGPU performs its required upload copies.
|
|
42
|
+
This is deliberately not described as an entirely zero-copy pipeline.
|
|
43
|
+
|
|
44
|
+
## A frame
|
|
45
|
+
|
|
46
|
+
1. libghostty-vt parses a streaming byte sequence and updates terminal state.
|
|
47
|
+
2. The worker coalesces presentation through requestAnimationFrame. Idle state
|
|
48
|
+
produces no redraw, except cursor or text blinking.
|
|
49
|
+
3. Ghostty updates its incremental render snapshot. The adapter visits dirty rows
|
|
50
|
+
and copies their grapheme, colors, and flags into reusable Rust cell storage.
|
|
51
|
+
4. Atlas hits reuse an existing tile. A miss chooses a fallback font, shapes the
|
|
52
|
+
grapheme with HarfRust, rasterizes with swash, and schedules one texture upload.
|
|
53
|
+
5. Each changed cell becomes a 20-byte GPU record: colors, flags and one glyph
|
|
54
|
+
word (atlas slot, ink width, color bit). Rows that only moved are copied
|
|
55
|
+
by libghostty row id and raw cells instead of re-read. Adjacent changed rows merge into
|
|
56
|
+
one `write_buffer` call. The GPU cell buffer stays resident between frames.
|
|
57
|
+
6. A single render pass issues two instanced draw calls: backgrounds first, then
|
|
58
|
+
glyphs and decorations. This prevents a wide glyph from being covered by the
|
|
59
|
+
next cell's background. Glyph sampling uses explicit LOD 0, so per-cell branches
|
|
60
|
+
do not violate WGSL derivative-uniformity rules.
|
|
61
|
+
7. The command buffer is submitted and the surface texture is presented. Dirty
|
|
62
|
+
flags clear only after successful submission.
|
|
63
|
+
|
|
64
|
+
Moving a cursor may dirty its previous and new rows, following Ghostty's damage
|
|
65
|
+
model. Scrolling currently repacks the changed viewport rows. GPU row remapping
|
|
66
|
+
is a priority for reducing that work in dense and real scrolling workloads.
|
|
67
|
+
|
|
68
|
+
## Input and output
|
|
69
|
+
|
|
70
|
+
Keyboard and mouse events are encoded by Ghostty, using its current modes. This
|
|
71
|
+
keeps legacy application cursor keys, modifiers, and Kitty keyboard behavior in
|
|
72
|
+
the terminal core. Browser code owns focus, composition events, pointer selection,
|
|
73
|
+
clipboard gestures, and safe activation of HTTP(S)/mailto OSC 8 links.
|
|
74
|
+
|
|
75
|
+
Terminal-generated replies share the same byte event path as user input.
|
|
76
|
+
Title and bell effects have separate typed events. Selection anchors belong to
|
|
77
|
+
Ghostty, so copying can use its formatter rather than reconstructing logical
|
|
78
|
+
lines from pixels.
|
|
79
|
+
|
|
80
|
+
The worker serializes commands. In particular, an asynchronous GPU fence holds a
|
|
81
|
+
Rust borrow until completion. Previously queued animation callbacks are canceled
|
|
82
|
+
or guarded while that borrow is active. This prevents a render callback from
|
|
83
|
+
recursively mutating a borrowed Wasm object.
|
|
84
|
+
|
|
85
|
+
## Bounds and failures
|
|
86
|
+
|
|
87
|
+
The grid is limited to 512 per dimension and 65,536 cells, and pixel dimensions
|
|
88
|
+
must fit the GPU's texture limits. The atlas is 2048×2048 RGBA. If a new glyph set
|
|
89
|
+
would exceed capacity, it resets and rebuilds all visible rows in the same frame.
|
|
90
|
+
If the visible set itself exceeds capacity, rendering reports an explicit error
|
|
91
|
+
rather than reusing live tiles. Font bytes remain independent of atlas entries.
|
|
92
|
+
|
|
93
|
+
Synchronized output preserves the last completed snapshot. A 150 ms worker timer
|
|
94
|
+
releases an abandoned hold when it can safely access the engine. GPU-fenced
|
|
95
|
+
benchmark batches and explicit flushes do not block the application UI thread.
|
|
96
|
+
|
|
97
|
+
Pipeline creation checks a WebGPU validation scope before mount resolves. Runtime
|
|
98
|
+
GPU errors retain the first diagnostic and reach `onError`; outstanding work is
|
|
99
|
+
rejected and the worker is terminated. Device-loss recovery currently means
|
|
100
|
+
remounting a terminal. Teardown disposes listeners, ResizeObserver, timers, DOM,
|
|
101
|
+
the Wasm owner, and GPU resources; a stalled worker is terminated after a bounded
|
|
102
|
+
wait.
|
|
103
|
+
|
|
104
|
+
## Where to extend
|
|
105
|
+
|
|
106
|
+
| Area | File / module |
|
|
107
|
+
| -------------------------------------------- | ---------------------------------------- |
|
|
108
|
+
| Upstream ABI and effects | `native/bridge.c` |
|
|
109
|
+
| Headless state ownership and snapshots | `crates/wraith-engine/src/core.rs` |
|
|
110
|
+
| Shaping, fallback, rasterization, cache | `crates/wraith-engine/src/text.rs` |
|
|
111
|
+
| Uploads, device, surface, render pass | `crates/wraith-engine/src/gpu.rs` |
|
|
112
|
+
| Cell geometry and visual styles | `crates/wraith-engine/src/terminal.wgsl` |
|
|
113
|
+
| Wasm public engine bindings | `crates/wraith-engine/src/web.rs` |
|
|
114
|
+
| Scheduling, protocol, benchmarks | `packages/wraith/src/worker.ts` |
|
|
115
|
+
| Browser API, input, transport, accessibility | `packages/wraith/src/index.ts` |
|
|
116
|
+
|
|
117
|
+
Performance work should be driven by a recorded workload and percentile evidence.
|
|
118
|
+
Broad compatibility additions should add a failing protocol fixture and, where
|
|
119
|
+
visual behavior is involved, a browser pixel assertion.
|
|
120
|
+
|
|
121
|
+
## xterm-compatible profile
|
|
122
|
+
|
|
123
|
+
`Terminal` inherits the official, pinned xterm 6.0 core. Its renderer adapter is
|
|
124
|
+
isolated in `xterm-internals.ts`; no private parser or input code is patched.
|
|
125
|
+
`xterm-api.d.ts` and the CSS are generated from that pinned package, with the MIT
|
|
126
|
+
notice preserved. An internal npm dependency alias prevents recursion when users
|
|
127
|
+
install Wraith as `@xterm/xterm`.
|
|
128
|
+
|
|
129
|
+
The synchronous constructor and `open()` keep the original DOM/input tree and
|
|
130
|
+
services. The adapter transfers ownership of the original DOM renderer through
|
|
131
|
+
xterm's `MutableDisposable.clearAndLeak()` and installs a composite renderer.
|
|
132
|
+
The adapter then owns its fallback, canvas, subscriptions, timer and compositor.
|
|
133
|
+
Replacement by another renderer or terminal disposal releases all of them. An
|
|
134
|
+
instance disposed during GPU initialization immediately frees a late result.
|
|
135
|
+
|
|
136
|
+
Warm scalar glyphs use numeric cache keys referencing the same text/style tiles.
|
|
137
|
+
Combining clusters retain the string path. Ordinary adjacent attribute runs
|
|
138
|
+
reuse one decoded style; extended attributes bypass that optimization. These
|
|
139
|
+
paths read the guarded three-word BufferLine storage through the private adapter.
|
|
140
|
+
Ordinary scalar cells bypass public cell loading; combined text, extended
|
|
141
|
+
attributes, invalid scalars and unfamiliar row layouts retain getter behavior.
|
|
142
|
+
|
|
143
|
+
The compositor owns a reusable Wasm arena containing cell words, four dimensions
|
|
144
|
+
and eight frame words. JavaScript packs directly into views of that allocation,
|
|
145
|
+
then submits a cell count and destination offset. This removes the three binding
|
|
146
|
+
allocations and JS-to-Wasm array copies from the compatible redraw path. GPU
|
|
147
|
+
upload copies remain. Bounds and device storage limits are checked before use.
|
|
148
|
+
|
|
149
|
+
Views refresh when configuration changes or Wasm memory grows. A cold glyph
|
|
150
|
+
upload can detach them during packing; the adapter refreshes the views and
|
|
151
|
+
repeats the affected row before submission. Arena slots use `UnsafeCell` to make
|
|
152
|
+
their JS write ownership explicit. Writes and synchronous Rust slice consumption
|
|
153
|
+
must not overlap, and rendering remains paused during benchmark fences.
|
|
154
|
+
|
|
155
|
+
Buffers, float views and uniforms are reused. Every packed cell overwrites its
|
|
156
|
+
complete record, including explicit zero UVs for blank and continuation cells.
|
|
157
|
+
A failed surface acquisition retains pending presentation and damage until an
|
|
158
|
+
actual submission. The native worker retries at its next animation opportunity;
|
|
159
|
+
the compatible renderer requests another redraw.
|
|
160
|
+
|
|
161
|
+
xterm's render service coalesces changed row ranges. The adapter reads those rows
|
|
162
|
+
from the authoritative buffer, reuses compositor-owned staging storage, and uses a
|
|
163
|
+
borrowed-text glyph cache with four bold/italic variants. Browser Canvas2D handles
|
|
164
|
+
CSS font rasterization on misses. Atlas exhaustion resets and repacks the full
|
|
165
|
+
viewport before submission, avoiding stale references. If a visible glyph set
|
|
166
|
+
still exceeds capacity, the DOM renderer takes over.
|
|
167
|
+
|
|
168
|
+
The Rust `Compositor` has no terminal parser or font allocation. It validates
|
|
169
|
+
geometry and upload ranges and shares the resident cell buffer, texture, shaders,
|
|
170
|
+
render pass and submission code with the native Ghostty renderer. xterm's
|
|
171
|
+
callbacks, events and buffer reads keep their original timing; write callbacks
|
|
172
|
+
indicate parsed state, rather than GPU completion.
|
|
173
|
+
|
|
174
|
+
Transparency, minimum contrast, character joiners, buffer decorations and link
|
|
175
|
+
hover currently delegate rendering to the retained DOM renderer. This is a
|
|
176
|
+
behavior-preserving fallback; the GPU profile resumes when those conditions end.
|
|
177
|
+
Selection, modes and input remain xterm-owned throughout. Browser rendering and
|
|
178
|
+
parser costs differ from the libghostty worker profile, so its benchmark numbers
|
|
179
|
+
cannot be assigned to the compatibility profile.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Compatibility
|
|
2
|
+
|
|
3
|
+
The complete xterm.js 6.0 public API is available through `Terminal`. It uses the
|
|
4
|
+
pinned upstream semantic core with Wraith's WebGPU compositor and automatic DOM
|
|
5
|
+
fallback. Original Fit, Search, Serialize, Attach and Unicode11 addons, unchanged
|
|
6
|
+
npm imports, and strict consumer types are verified. See [XTERM_API.md](XTERM_API.md)
|
|
7
|
+
for the complete surface and exact rendering limitations.
|
|
8
|
+
|
|
9
|
+
The protocol matrix below describes the separate **libghostty `Wraith.mount()`
|
|
10
|
+
profile**. Its remaining work does not imply missing xterm methods in `Terminal`.
|
|
11
|
+
|
|
12
|
+
Wraith uses upstream libghostty-vt at the commit in `upstream.json`, rather than
|
|
13
|
+
an independently invented escape-sequence parser. That gives it a substantial
|
|
14
|
+
terminal model, but complete browser-terminal conformance also depends on the
|
|
15
|
+
renderer, input path, effects, fonts, and browser integration.
|
|
16
|
+
|
|
17
|
+
## Implemented and verified
|
|
18
|
+
|
|
19
|
+
| Area | Verification |
|
|
20
|
+
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
21
|
+
| Streaming UTF-8, split CSI/OSC sequences | Every split boundary of a Unicode/SGR fixture; one-byte Wasm writes. |
|
|
22
|
+
| Cursor positioning, save/restore, delayed wrap | Native protocol fixtures. |
|
|
23
|
+
| Insert/delete/erase, BCE, scrolling margins, origin mode | Native fixtures with expected visible contents. |
|
|
24
|
+
| Alternate screen and main-screen restoration | Native and packaged Wasm fixtures. |
|
|
25
|
+
| Bounded scrollback, viewport scrolling, resize reflow | Native contents checks. |
|
|
26
|
+
| ANSI indexed and 24-bit color, colon syntax | Exact cell color assertions. |
|
|
27
|
+
| Bold, synthetic italic, faint, inverse, hidden, blink | Core styles and implemented renderer flags; visual sample in Chrome. |
|
|
28
|
+
| Single/double/curly/dotted/dashed underlines, underline color, strike, overline | Core style flags and WGSL implementations; underline/strike pixels in demo. |
|
|
29
|
+
| DEC line drawing | Expected Unicode box drawing and Chrome sample. |
|
|
30
|
+
| Wide and combining characters | Cell widths, tail cells, combined clusters, clipboard text fixtures. |
|
|
31
|
+
| OSC title and bell | Native and browser effect paths. |
|
|
32
|
+
| Device attributes, version, cursor, size, color-scheme reports | Registered effects and native fixtures. |
|
|
33
|
+
| Synchronized output | Snapshot hold, release, and deadline recovery. |
|
|
34
|
+
| OSC 8 hyperlinks | Core URI lookup; browser modifier-click handler restricts URL schemes. |
|
|
35
|
+
| Selection | Real pointer selection in Chrome; Ghostty formatter unwraps soft-wraps. |
|
|
36
|
+
| Legacy / application arrows and Kitty keyboard disambiguation | Packaged Wasm byte-for-byte encoding tests. |
|
|
37
|
+
| SGR mouse encoding, focus reports, bracketed paste | Native encoder path; browser paste test and sanitized frame contents. |
|
|
38
|
+
| IME commits and preedit placement | Browser composition test; cursor coordinates anchor the input element. |
|
|
39
|
+
| Accessible viewport mirror | Optional role=log mirror, exercised in browser lifecycle test. |
|
|
40
|
+
| Input ownership and backpressure | Original buffer survives transfer; oversize write rejects. |
|
|
41
|
+
| Multiple instances and disposal | Browser independent instance test and repeated Wasm allocation plateau test. |
|
|
42
|
+
| WebGPU rendering | Chrome pipeline validation and actual screenshot pixel assertions. |
|
|
43
|
+
|
|
44
|
+
The installed default font does not cover every Unicode character. Fonts affect
|
|
45
|
+
visual coverage independently of the terminal model's Unicode support.
|
|
46
|
+
|
|
47
|
+
## Remaining release work
|
|
48
|
+
|
|
49
|
+
| Area | Current status |
|
|
50
|
+
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
51
|
+
| Kitty graphics / Sixel | Kitty graphics compiled out; Sixel renderer absent. |
|
|
52
|
+
| OSC 52 / Kitty clipboard protocol | No terminal-initiated browser clipboard read/write bridge. User paste/copy works. |
|
|
53
|
+
| Cross-cell ligatures and contextual script runs | Shaping is per Ghostty grapheme cell. Full row-run shaping remains. |
|
|
54
|
+
| Bidi behavior | No separate browser bidi layout pipeline. |
|
|
55
|
+
| Native font discovery | Explicit bundled / supplied font data; no browser system-font rasterizer. |
|
|
56
|
+
| Dynamic DPR or font-size changes | DPR captured at mount. Remount with new settings. |
|
|
57
|
+
| Full screen-reader certification | Optional text mirror is implemented; assistive-technology auditing remains. |
|
|
58
|
+
| Search, word/line selection gestures, configurable key bindings | Not exposed in the SDK yet. |
|
|
59
|
+
| WebGL/Canvas fallback | WebGPU-only; unsupported environments receive an actionable initialization error. |
|
|
60
|
+
| Firefox / Safari certification | Capability checks exist; the current recorded browser suite is Chrome on macOS. |
|
|
61
|
+
| xterm.js addon compatibility | Available through the separate `Terminal` profile; see XTERM_API.md. |
|
|
62
|
+
| Complete terminal-spec certification | Requires expanded upstream fixtures, vttest, and real PTY/TUI integration coverage. |
|
|
63
|
+
|
|
64
|
+
The first release should keep this matrix current rather than advertise an
|
|
65
|
+
unqualified “full support” badge. Image protocols, script-run shaping, and
|
|
66
|
+
cross-browser/TUI certification are the next compatibility milestones.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Performance
|
|
2
|
+
|
|
3
|
+
**Current results:** the [efficiency pass](benchmarks/efficiency/REPORT.md)
|
|
4
|
+
measures both profiles against `a136240` with interleaved runs. Dense
|
|
5
|
+
240×80 updates are 23–60% faster, and log streaming is about 95% faster.
|
|
6
|
+
Redraw CPU drops to 0.3 ms (compat) and 0.5 ms (native). Every scene is
|
|
7
|
+
pixel-identical to the earlier build. Sections below are earlier
|
|
8
|
+
measurements, retained with their original builds.
|
|
9
|
+
|
|
10
|
+
These measurements cover the **libghostty `Wraith.mount()` worker profile**.
|
|
11
|
+
The xterm-compatible `Terminal` profile uses a different semantic core and
|
|
12
|
+
browser rasterizer; these throughput figures do not measure that profile.
|
|
13
|
+
|
|
14
|
+
The recorded Chrome suite verifies actual terminal pixels before measuring throughput.
|
|
15
|
+
It ran with default Chrome WebGPU settings on macOS, devicePixelRatio 1, a 13 CSS
|
|
16
|
+
pixel font, and line height 1.45. Chrome identified the backend as BrowserWebGpu;
|
|
17
|
+
it did not expose a hardware model, so no specific GPU model is claimed.
|
|
18
|
+
|
|
19
|
+
## Head-to-head comparison
|
|
20
|
+
|
|
21
|
+
The newer [dense-scene comparison](benchmarks/comparison/REPORT.md) measures the
|
|
22
|
+
compatible Terminal against xterm 6.0 DOM and WebGL, alongside the libghostty
|
|
23
|
+
engine. It records 60 runs at 240×80 with 4,800 style changes per frame, plus
|
|
24
|
+
post-warmup pixel/text validation. On the dense DPR 1 scene, compatible Wraith
|
|
25
|
+
completes about 290 GPU-fenced updates/s versus 182 for xterm WebGL. This workload
|
|
26
|
+
has substantially more input and styling than the earlier benchmark below.
|
|
27
|
+
|
|
28
|
+
The earlier results below retain their original binary SHA and date; the
|
|
29
|
+
comparison uses an instrumented build with a compositor fence export. Neither
|
|
30
|
+
set is a claim about physical display refresh.
|
|
31
|
+
|
|
32
|
+
[CPU and GPU utilization](benchmarks/comparison/UTILIZATION.md) is sampled
|
|
33
|
+
separately during repeated continuous runs. CPU covers the benchmark's dedicated
|
|
34
|
+
Chrome instance. GPU counters cover the entire device and include paired idle
|
|
35
|
+
baselines; they cannot be attributed to one library on this shared machine.
|
|
36
|
+
|
|
37
|
+
The [newer optimization pass](benchmarks/optimization/REPORT.md) compares a saved
|
|
38
|
+
pre-change build with the final optimized build in alternating order. Its source
|
|
39
|
+
and binary hashes, submitted-frame checks and differential results are recorded
|
|
40
|
+
separately. Earlier speed and usage figures below remain historical measurements.
|
|
41
|
+
|
|
42
|
+
The subsequent [bulk packing and persistent staging pass](benchmarks/packing/REPORT.md)
|
|
43
|
+
records 48 alternating runs and independent staging experiments. The compatible
|
|
44
|
+
pipeline improves by about 7–11% across the tested conditions; staging's separate
|
|
45
|
+
throughput effect remains noisy. It also verifies actual submitted cells after
|
|
46
|
+
forced Wasm memory growth. All new records carry their source/binary identities.
|
|
47
|
+
|
|
48
|
+
## Recorded results
|
|
49
|
+
|
|
50
|
+
| Grid | Workload | Frames | GPU-fenced frames/s | CPU p50 ms | CPU p95 ms | CPU p99 ms |
|
|
51
|
+
| ------ | ----------------- | -----: | ------------------: | ---------: | ---------: | ---------: |
|
|
52
|
+
| 80×24 | sparse | 512 | 11,532 | 0.00 | 0.10 | 0.20 |
|
|
53
|
+
| 80×24 | dense | 512 | 8,166 | 0.10 | 0.20 | 0.20 |
|
|
54
|
+
| 80×24 | scroll | 512 | 8,298 | 0.10 | 0.20 | 0.20 |
|
|
55
|
+
| 160×50 | sparse | 512 | 7,829 | 0.00 | 0.10 | 0.20 |
|
|
56
|
+
| 160×50 | dense | 512 | 2,238 | 0.40 | 0.50 | 0.60 |
|
|
57
|
+
| 160×50 | scroll | 512 | 2,353 | 0.40 | 0.50 | 0.60 |
|
|
58
|
+
| 160×50 | dense (sustained) | 8,192 | 2,280 | 0.40 | 0.50 | 0.60 |
|
|
59
|
+
|
|
60
|
+
The sustained dense run completed **8,192 frames in 3.59 seconds**, averaging
|
|
61
|
+
**2,280 GPU-completed frames/s**. Its CPU p95 was **0.50 ms**.
|
|
62
|
+
|
|
63
|
+
[Latest native browser verification](benchmarks/chrome.json) includes the exact Wasm SHA-256,
|
|
64
|
+
Ghostty revision, user agent, all timings, and the pixel assertion summary.
|
|
65
|
+
[An earlier unoptimized run](benchmarks/baseline-chrome.json) is retained for
|
|
66
|
+
context. Runs are observations on this machine, not a controlled A/B comparison.
|
|
67
|
+
|
|
68
|
+
## What the benchmark measures
|
|
69
|
+
|
|
70
|
+
Inputs are generated before the timed region. Sixty-four warmup frames populate
|
|
71
|
+
the glyph atlas and exercise the parser. Each measured iteration writes a fresh
|
|
72
|
+
payload, captures the terminal snapshot, updates dirty rows, encodes a render
|
|
73
|
+
pass, and submits it. Every 32 submissions, a four-byte GPU buffer copy is mapped
|
|
74
|
+
and awaited; a final fence waits for the last batch. The elapsed time therefore
|
|
75
|
+
includes GPU completion waits rather than counting an unchecked submission loop.
|
|
76
|
+
|
|
77
|
+
The dense payload changes every cell, its character cycle, and row colors. Sparse
|
|
78
|
+
updates one row; scroll appends a line and shifts the viewport. The same 64-frame
|
|
79
|
+
payload cycle makes the input deterministic. Large-grid rendering covers a full
|
|
80
|
+
1280×950 pixel canvas even when the page's host clips the visible viewport.
|
|
81
|
+
|
|
82
|
+
CPU percentiles include parsing, snapshot traversal, atlas lookup, uploads,
|
|
83
|
+
encoding, and submission. They exclude the main-thread SDK and worker message
|
|
84
|
+
transport, and do not isolate GPU execution time. Cold rasterization and pipeline
|
|
85
|
+
initialization are excluded by warmup. Browser timer resolution is about 0.1 ms
|
|
86
|
+
in this test; zero samples mean below timer resolution.
|
|
87
|
+
|
|
88
|
+
**These are completed render frames in fenced batches, not distinct monitor
|
|
89
|
+
presentations.** Browsers present through refresh-paced requestAnimationFrame.
|
|
90
|
+
A 120 Hz screen has an 8.33 ms presentation budget; 1000 Hz would have a 1 ms
|
|
91
|
+
budget. The measured warm pipeline has headroom for the target, but 120 Hz physical
|
|
92
|
+
presentation still requires an appropriate browser, display, workload, and input
|
|
93
|
+
feed. This suite does not certify a particular monitor refresh rate.
|
|
94
|
+
|
|
95
|
+
## Distribution size
|
|
96
|
+
|
|
97
|
+
The optimized Wasm module is **2.27 MiB raw / 797 KiB gzip**.
|
|
98
|
+
wasm-opt version 133 with -O3 --strip-dwarf reduced it from
|
|
99
|
+
2,931,003 to 2,380,758 bytes. Font data is a separate
|
|
100
|
+
270 KB asset for the libghostty profile. The xterm profile rasterizes CSS fonts
|
|
101
|
+
without a font download. Its pinned xterm core is the one npm runtime dependency.
|
|
102
|
+
|
|
103
|
+
## Repeat
|
|
104
|
+
|
|
105
|
+
Run the demo server, then:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
npm run test:browser
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The suite records six 512-frame runs, an 8192-frame sustained dense run, and PNG
|
|
112
|
+
proof under docs/benchmarks. Set WRAITH_REPORT_DIR to retain another run separately.
|
|
113
|
+
Use the playground to select a workload and export its raw JSON result. Test
|
|
114
|
+
physical refresh behavior separately on the intended 120/144/240 Hz displays.
|
|
115
|
+
|
|
116
|
+
## Next performance work
|
|
117
|
+
|
|
118
|
+
Measure cold Unicode glyph storms, atlas rebuilding, multiple simultaneous
|
|
119
|
+
terminals, large PTY output bursts, and background-to-foreground transitions.
|
|
120
|
+
Optional GPU timestamp queries can separate GPU pass time from CPU time and
|
|
121
|
+
queue/fence overhead. A row-identity mapping could reduce scroll uploads. These
|
|
122
|
+
should each be backed by a recorded before/after workload.
|
package/docs/TOP_TIER.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Wraith: engineering plan for a top-tier terminal
|
|
2
|
+
|
|
3
|
+
Wraith should win on responsive input, reliable 120 Hz presentation, correct terminal behavior, low resource use, and effortless integration. A 1000-update/s stress result remains a useful stretch target. It is one workload-specific throughput measurement, not the complete product standard.
|
|
4
|
+
|
|
5
|
+
This plan separates implemented changes from proposed work. Its priorities come from the current source, the dense-scene CPU profile, compatibility checks, and before/after measurements. Performance targets below are proposed acceptance criteria, not claims that Wraith already meets them on every device.
|
|
6
|
+
|
|
7
|
+
## Architecture decisions
|
|
8
|
+
|
|
9
|
+
Keep two explicit profiles. The compatible `Terminal` preserves xterm's semantic core, callbacks, parser hooks, live buffers and addons. The native `Wraith.mount()` profile keeps libghostty, shaping and GPU rendering together in a worker. Do not parse output twice, replay terminal state through ANSI, or silently assign the native engine's speed to the compatible API. Replacing xterm's semantic core with Ghostty requires a separate conformance project; moving synchronous API state into a worker is not a free optimization.
|
|
10
|
+
|
|
11
|
+
The renderer should consume stable row identities, revisions, glyph IDs and packed attributes. Presentation scheduling should consume damage and deadlines. Neither layer should own terminal semantics. Preserve ordered input and resize effects; coalesce redundant presentation work rather than dropping terminal bytes or callbacks.
|
|
12
|
+
|
|
13
|
+
## Work implemented in this pass
|
|
14
|
+
|
|
15
|
+
- Native atlas preflight borrows glyph strings, checks hits before creating missing-key entries, and excludes spaces that require no tile. A full warm viewport no longer clones thousands of glyph strings merely to check capacity.
|
|
16
|
+
- Compatibility packing reuses cell and uniform buffers and a float view. It overwrites blank/continuation glyph coordinates explicitly instead of clearing the complete staging array before writing every populated record.
|
|
17
|
+
- Adjacent cells reuse decoded ordinary attributes. This cache holds one style, resets per pack, and bypasses extended attributes. RGB churn cannot grow it, and selected colors remain resolved per cell.
|
|
18
|
+
- Unicode scalar glyphs have a numeric cache referencing the existing tile variants. Warm scalar cells avoid reconstructing Unicode strings. Combining clusters use the original string path, and both caches reset together.
|
|
19
|
+
- A temporarily unavailable surface retains pending presentation. Native damage is acknowledged only after submission; the worker retries without waiting for another write. The compatible renderer also requests a retry. An added browser regression simulates a skipped presentation.
|
|
20
|
+
- Benchmark pipelines check actual WebGPU submission counters rather than assuming every attempted update submitted a frame. Packed-output differential fixtures compare every word at DPR 1 and 2.
|
|
21
|
+
- The subsequent row-packing pass reads guarded three-word xterm rows directly for ordinary scalar cells and retains getter fallbacks. The compositor owns persistent Wasm cell and uniform staging, removing binding allocations and copies from normal compatible redraws. Cold glyph uploads that grow memory refresh views and repack the affected row. Bounds, reserved uniform reset, and actual submitted cells after forced growth are tested.
|
|
22
|
+
|
|
23
|
+
- The efficiency pass packs 20-byte cells with one glyph word and sizes glyph quads to ink. It replaces string hashing with a scalar glyph table and cuts per-cell libghostty queries. Both profiles reuse unchanged and moved rows and upload only changed rows, and write bursts reach the worker as one transfer. See [the efficiency report](benchmarks/efficiency/REPORT.md).
|
|
24
|
+
|
|
25
|
+
Results and limitations belong in [the efficiency report](benchmarks/efficiency/REPORT.md) and, for earlier passes, [the optimization report](benchmarks/optimization/REPORT.md). Archived earlier benchmarks remain attached to their original binaries.
|
|
26
|
+
|
|
27
|
+
## Prioritized implementation backlog
|
|
28
|
+
|
|
29
|
+
| Priority | Investment | Why it matters | Proof required |
|
|
30
|
+
| -------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| P0 | GPU support for search decorations, link hover and minimum contrast | Ordinary features currently trigger much slower DOM rendering | Original-addon and pixel tests; GPU mode remains active during decorated search and hover |
|
|
32
|
+
| P0 | GPU row remapping for scrolls | Moved rows are now reused on the CPU but still uploaded at their new position | Append-only PTY traces; one-row scroll uploads proportional to the new row, with correct selection/history |
|
|
33
|
+
| P0 | Input scheduling and latency instrumentation | A fast average can coexist with delayed keystrokes or large pauses | Local input-to-visible-echo distribution, main-thread long tasks and frame-miss counts |
|
|
34
|
+
| P0 | Separate model lifetime from GPU lifetime | Device loss currently makes the native profile terminate its worker | Simulated loss/recovery preserves text, history, modes, selections and ordered pending writes |
|
|
35
|
+
| P1 | Bounded multi-page glyph atlas with generations | A single 2048-square atlas can reset or overflow on large Unicode/Retina scenes | Atlas exhaustion, font changes, mixed scripts and stale-reference tests |
|
|
36
|
+
| P1 | Shared device/pipeline/atlas resources where ownership allows | Each terminal currently owns another GPU device and RGBA atlas | 1/4/8/16-terminal resource and responsiveness measurements |
|
|
37
|
+
| P1 | Smaller compositor-only Wasm artifact | The compatible renderer downloads code for native parsing/shaping it does not use | Cold-load and first-correct-frame comparisons; unchanged APIs and consumers |
|
|
38
|
+
| P1 | Complete rendering and input conformance corpus | Public API shape alone does not establish visual or protocol parity | Differential state, pixels, keyboard/IME and real application traces |
|
|
39
|
+
| P2 | Persistent render target with damage rendering | Sparse changes still redraw the entire canvas | GPU pass timings; scroll, cursor, transparency and resize correctness |
|
|
40
|
+
| P2 | SIMD, upload staging rings, compact glyph draw lists | These may improve specific bottlenecks after larger waste is removed | Per-platform measurements showing a meaningful benefit without regressions |
|
|
41
|
+
|
|
42
|
+
## CPU, copies and the terminal core
|
|
43
|
+
|
|
44
|
+
1. Profile parsing, snapshot extraction, attribute decoding, glyph lookup, packing, Wasm copies, GPU submission and browser paint independently. Use a profiling build for Ghostty/Wasm function attribution. RGB churn is a separate parser workload: the native parser is not universally faster than xterm's JavaScript parser in the recorded tests.
|
|
45
|
+
2. Build a guarded numeric glyph path and eventually a glyph-ID ABI. The current scalar cache is a first step. An immutable glyph descriptor can hold UV coordinates, bearings, dimensions and color mode once instead of repeating coordinates in every cell. Keep explicit underline colors and wide/continuation semantics when evaluating a 20-byte or smaller cell layout.
|
|
46
|
+
3. Persistent Wasm staging is implemented. Continue measuring its independent effect, since copy removal alone does not establish a throughput gain in a parser-heavy workload. Refresh views after growth, validate ranges, and keep ownership explicit. WebGPU still performs its required data snapshot/upload.
|
|
47
|
+
4. Reduce retained native text allocation using scalar/cluster IDs or inline small text, while preserving the public cell/text/selection behavior. Use row arenas or reusable storage before introducing unsafe borrowed pointers into Ghostty memory.
|
|
48
|
+
5. Expose a stream sink that waits for write credit instead of making callers invent retry loops. Write bursts are already batched into one worker transfer.
|
|
49
|
+
6. Investigate why toggling reverse video (CSI ?5h) after content is drawn leaves existing native cells in their old colors; a136240 already behaves this way.
|
|
50
|
+
7. Budget large input processing so it cannot starve rendering or keyboard responses. Preserve UTF-8/escape state across slices and respect synchronized output. Avoid changing xterm's callback or custom-parser semantics for the compatible profile.
|
|
51
|
+
8. Replace per-frame sample shifts with rings, make expensive diagnostics opt-in, and remove avoidable geometry/font-string work and row wrappers after profiling confirms their cost.
|
|
52
|
+
|
|
53
|
+
Shared memory can be an optional high-throughput lane. It must retain the transferable-buffer path for ordinary embedding; browser shared memory requires a secure, cross-origin-isolated document. [SharedArrayBuffer requirements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)
|
|
54
|
+
|
|
55
|
+
## GPU rendering and glyph residency
|
|
56
|
+
|
|
57
|
+
1. Keep unchanged rows resident. Map screen rows to retained physical GPU rows, update the mapping and only pack new/revised rows. Track parser mutations, viewport changes, selection and atlas generations explicitly. Use conservative full invalidation when a revision is unknown.
|
|
58
|
+
2. Merge uploads by dirty ranges and benchmark a threshold for partial-cell spans. Comparing packed cells can avoid unnecessary uploads, but it is additional CPU work; use it where the expected unchanged data justifies the comparison.
|
|
59
|
+
3. Experiment with a persistent backing texture for sparse damage. Swapchain contents cannot be assumed to preserve the previous image. Update damaged regions in the retained texture, then present through a copy or blit. Include the extra pass and texture bandwidth in the result.
|
|
60
|
+
4. Crop glyphs to ink bounds with descriptor bearings to reduce transparent overdraw. Compact the nonempty-glyph draw list for sparse logs. Dense workloads may not benefit from that compaction.
|
|
61
|
+
5. Evaluate an alpha-only atlas for monochrome glyphs with a separate RGBA atlas for emoji/images. The current RGBA texture consumes 16 MiB per terminal before cells, fonts and history; 16 independent atlases alone consume 256 MiB.
|
|
62
|
+
6. Add bounded pages, live-tile pinning, eviction generations and explicit budgets. An eviction must invalidate affected rows before a slot is reused. Never trade stale glyph references for a higher rate.
|
|
63
|
+
7. Prewarm common glyph/style sets without delaying the usable API. Budget cold shaping/raster work and measure time to a correct image separately from warm-frame speed. Handle CSS webfonts finishing loading, font fallback changes and DPR changes as atlas epochs.
|
|
64
|
+
8. Reuse a device and pipelines on the same rendering owner. For the native worker profile, a shared rendering worker is a possible design; GPU devices cannot simply be assumed transferable between dedicated workers. Measure fairness and fault isolation before adopting pooling.
|
|
65
|
+
9. Keep the frame queue bounded so throughput does not turn into latency. Do not introduce a synchronous GPU fence on every normal frame. A queue-completion fence establishes completed submitted work, not physical screen presentation. [GPUQueue completion semantics](https://gpuweb.github.io/types/interfaces/GPUQueue.html)
|
|
66
|
+
|
|
67
|
+
GPU timestamps are the right optional probe for render-pass duration. Their availability is feature-dependent; asynchronously resolve/read a bounded query ring and keep unsupported devices functional. This measures GPU command duration, not per-process device utilization. [WebGPU timestamp-query sample](https://webgpu.github.io/webgpu-samples/?sample=timestampQuery)
|
|
68
|
+
|
|
69
|
+
## Feature completeness and everyday quality
|
|
70
|
+
|
|
71
|
+
| Area | Work to cover |
|
|
72
|
+
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| Decorations | Search matches, active-match colors, overview rulers, foreground/background layers, link underlines and custom decorations without a whole-terminal renderer switch |
|
|
74
|
+
| Visual options | Contrast adjustment, transparency, correct selection tint, cursor modes, faint/inverse/hidden/underline/strike/overline behavior, fractional DPR and letter spacing |
|
|
75
|
+
| Text | Wide cells, combining marks, supplementary scalars, emoji/ZWJ/variation sequences, fallback fonts, ligatures/character joiners, font bearings and line-height seams |
|
|
76
|
+
| VT behavior | Chunked UTF-8 and controls, margins/origin, wrap/reflow, erase/insert/delete, alternate screens, synchronized output, queries/replies and scrollback anchors |
|
|
77
|
+
| Input | Keyboard modes, modifiers, AltGraph/dead keys, IME/composition, mobile keyboards, paste, focus reporting, mouse modes and browser shortcut handling |
|
|
78
|
+
| Interaction | Smooth trackpad scrolling, reliable selection across history/reflow, word/line selection, link hit testing, copy formatting, sticky-bottom behavior and resize transitions |
|
|
79
|
+
| Accessibility | Screen readers, keyboard-only navigation, focus, reduced-motion behavior, announcements that do not flood the user, and correct textarea/candidate-window positioning |
|
|
80
|
+
| Images | Explicitly scoped Kitty/iTerm/Sixel support, decoded resource budgets and asynchronous decode/upload; current Ghostty build disables Kitty graphics |
|
|
81
|
+
| Large history | Bounded memory, fast search/indexing, stable line identifiers and efficient serialization; preserve synchronous original-addon contracts, and offer separate async native features where appropriate |
|
|
82
|
+
|
|
83
|
+
Use the pinned core and protocol references as behavioral authorities. Ghostty's VT support does not automatically mean its desktop frontend features or every graphics protocol are exposed through this bridge. [Ghostty architecture](https://ghostty.org/docs/about), [VT protocol reference](https://ghostty.org/docs/vt)
|
|
84
|
+
|
|
85
|
+
## Reliability, safety and maintainability
|
|
86
|
+
|
|
87
|
+
- Preserve the terminal model through recoverable GPU failure, sleep/resume and renderer replacement. Rebuild texture/buffer state from authoritative cells, use DOM where that profile permits it, and bound retries.
|
|
88
|
+
- Bound history, atlas pages, queues, image decoding and control-string work. Test extreme combining clusters, malformed byte streams and invalid compositor ranges. Logical text correctness and rendering resource limits need separate policies.
|
|
89
|
+
- Keep remote clipboard writes, external navigation and downloaded resources within explicit host/browser permissions. OSC replies, title changes and link providers must not become an implicit HTML execution path.
|
|
90
|
+
- Stress dispose-during-init, worker termination, queue rejection, observer cleanup, repeated mount/unmount and rapid resizing. Verify resources return close to baseline after teardown.
|
|
91
|
+
- Keep the pinned private xterm boundary small and documented. The optional attribute/content words are optimizations; getter-based paths remain available. Run conformance and installed-package checks before any upstream upgrade.
|
|
92
|
+
- Generate/check ABI layout constants, maintain WGSL/Rust layout assertions, preserve dependency notices, and add native fuzz/sanitizer checks for the C boundary. The existing stable-state owner and synchronous callback discipline must survive performance refactors.
|
|
93
|
+
- Instrument cache occupancy, evictions, upload bytes, queue age, cold glyphs, renderer state, long tasks and high-percentile latency. Expose diagnostics for developers without making telemetry or profiling a mandatory hot-path cost.
|
|
94
|
+
|
|
95
|
+
## Integration and release quality
|
|
96
|
+
|
|
97
|
+
Keep the common setup to a constructor, `open`, `write`, and the stylesheet. Continue testing installation as `wraithterm` and as an alias for `@xterm/xterm`, with original addons. Consumers should receive prebuilt assets and never need Rust or Zig toolchains.
|
|
98
|
+
|
|
99
|
+
Add production fixtures for Vite, Webpack/Rspack, Rollup/esbuild and Next.js/SSR; React Strict Mode, hidden tabs, split panes, CSP, CDN/asset-base loading and versioned caching. Separate the compatible compositor download from the native engine if it materially improves startup. Test ESM and the documented Node require behavior rather than implying universal CommonJS support.
|
|
100
|
+
|
|
101
|
+
Provide small framework wrappers, a resize helper, binary WebSocket/stream examples, an optional backend PTY example, precise API references and a capability matrix. Framework wrappers should dispose correctly and not require state updates on every frame. Runtime errors should explain an actionable capability/asset problem, while fallback normally keeps the terminal usable.
|
|
102
|
+
|
|
103
|
+
Ship an experimental npm release first. A stable release requires the correctness, presentation and resource gates below, not only a successful publish dry run.
|
|
104
|
+
|
|
105
|
+
## Acceptance gates and measurement discipline
|
|
106
|
+
|
|
107
|
+
| Gate | Proposed target / verification |
|
|
108
|
+
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| 120 Hz | Real 120 Hz presentation tests on named reference hardware; 99% of presentation opportunities met in sustained representative traces; report missed frames and stalls |
|
|
110
|
+
| CPU headroom | Dense 240×80 reference scene: parser + pack + submission p95 under 4 ms and p99 under 6 ms; cold/rare glyph bursts reported separately |
|
|
111
|
+
| Input latency | Controlled local echo p95 under 16 ms, including scheduling and presentation; distinguish remote transport latency |
|
|
112
|
+
| Idle | No unnecessary redraw; target under 0.5% of one CPU core for a stationary terminal, measured with the same diagnostic settings |
|
|
113
|
+
| Resource scaling | 1/4/8/16 terminals, bounded atlas/history/queues, fair scheduling and teardown returning resources near baseline |
|
|
114
|
+
| Compatibility | Public API, original addons, protocol fixtures, visual options, keyboard/IME and accessibility on all declared support tiers |
|
|
115
|
+
| Recovery | No lost terminal state after transient surface failure or recoverable device loss; no hanging writes/disposal |
|
|
116
|
+
| 1000-update/s stretch | Publish grid, styling, input volume, font/DPR, actual submitted/completed frames and latency; require a repeatable named workload |
|
|
117
|
+
|
|
118
|
+
For this dense scene, 1000 updates/s means about 19.2 million cells, 4.8 million SGR changes and roughly 205 MiB of input per second. The current 20-byte layout would upload about 384 MB of cells per second before other copies. Parsing alone is around a millisecond in prior measurements, so GPU micro-optimization cannot by itself deliver that target.
|
|
119
|
+
|
|
120
|
+
Build replay corpora from append-only logs, compiler output, tmux/Neovim/btop, color churn, atlas exhaustion, cold Unicode, resizing and multiple terminals. Pin inputs and versions. Alternate before/after order, warm consistently, retain outliers, verify text/geometry/pixels, and report distributions and source/binary hashes. Run CPU/GPU profiling separately from the primary speed benchmark. Use isolated hardware for resource attribution and repeated sustained runs for confidence.
|
|
121
|
+
|
|
122
|
+
requestAnimationFrame is normally synchronized to the display's refresh cadence; the current headless browser is not a 120 Hz physical-display test. [requestAnimationFrame behavior](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame)
|
|
123
|
+
|
|
124
|
+
Implementation order: finish allocation reductions and transient-presentation recovery; retain rows through scrolling; support common decorations/contrast on GPU; add latency/recovery/cross-browser gates; then evaluate the compact ABI, smaller compositor artifact and resource pooling. Keep changes only when correctness and the relevant workload both improve.
|