Skip to content

Commit 3f30ebc

Browse files
docs: add ghostty-web spike brief
1 parent 0ef4bb4 commit 3f30ebc

1 file changed

Lines changed: 160 additions & 0 deletions

File tree

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# Ghostty-Web Spike
2+
3+
Date: 2026-03-24
4+
Branch: `experiment/ghostty-web-spike`
5+
6+
## Problem Statement
7+
8+
TermCanvas currently embeds terminals with xterm.js. We have spent a long time
9+
fighting scroll behavior during streaming AI CLI output, especially for Codex
10+
and Claude sessions with sustained output.
11+
12+
The most painful user-visible failure mode right now is:
13+
14+
1. User scrolls upward to read previous output.
15+
2. User reaches the top of the visible scrollback.
16+
3. The viewport is forcibly snapped back to the live bottom.
17+
18+
This is not acceptable behavior for a terminal reader. It breaks trust in the
19+
viewport and makes long-running agent sessions hard to inspect.
20+
21+
There is also a second class of historical pain around scroll-pinning under
22+
heavy output: once scrollback fills and trimming begins, xterm.js internal
23+
scroll state can drift in ways that are difficult to reason about and have led
24+
to multiple local patches. Those patches have themselves become a source of
25+
regression risk.
26+
27+
## Current Understanding
28+
29+
The immediate snap-to-bottom bug appears to be caused by our own xterm.js
30+
patching layer, not by a mandatory xterm.js default behavior. Specifically,
31+
`src/terminal/TerminalTile.tsx` contains custom scroll recovery logic that tries
32+
to detect a "stuck at top during trimming" state and calls
33+
`scrollToBottom()`. In normal user scroll-to-top cases, that heuristic is too
34+
broad and misfires.
35+
36+
Even if that specific bug is fixed, the larger concern remains:
37+
38+
- We are relying on xterm.js internals and private state.
39+
- We already maintain custom behavior around scroll, selection, serialization,
40+
WebGL, and terminal restore.
41+
- Scroll semantics under streaming output have been fragile enough to consume
42+
significant debugging time.
43+
44+
Because of that, we want to evaluate whether a different terminal frontend
45+
foundation would reduce long-term complexity.
46+
47+
## Spike Goal
48+
49+
Evaluate whether `ghostty-web` is a credible replacement for xterm.js in
50+
TermCanvas.
51+
52+
This spike is not approved as a full migration. It is a bounded experiment to
53+
answer whether switching foundations is realistic and whether it improves the
54+
specific user experience problems that have been expensive to maintain on
55+
xterm.js.
56+
57+
## Primary Goals
58+
59+
1. Stand up a minimal `ghostty-web` terminal tile inside TermCanvas.
60+
2. Confirm that we can render PTY output in the existing React/Electron
61+
architecture.
62+
3. Test whether scrolling behavior during sustained AI CLI streaming is more
63+
predictable than our current xterm.js setup.
64+
4. Measure the migration surface area for the features we already depend on.
65+
5. Decide whether full migration is worth pursuing, or whether fixing xterm.js
66+
locally is still the better path.
67+
68+
## Non-Goals
69+
70+
This spike should not attempt to fully ship a new terminal stack.
71+
72+
Specifically out of scope:
73+
74+
- complete production migration
75+
- parity for every existing terminal feature
76+
- unrelated UI cleanup
77+
- changing PTY backend architecture
78+
- solving every historical xterm.js issue during the spike
79+
80+
## What We Need To Learn
81+
82+
### 1. Integration Feasibility
83+
84+
- Can `ghostty-web` run cleanly in our Electron renderer?
85+
- What initialization/runtime requirements does it impose?
86+
- Does it fit our current terminal lifecycle model?
87+
88+
### 2. Feature Parity Gaps
89+
90+
We currently depend on:
91+
92+
- terminal fitting on resize
93+
- scrollback restore / serialization
94+
- theme switching
95+
- selection + copy behavior
96+
- direct key handling
97+
- inline image support
98+
- GPU-accelerated rendering path
99+
- focus management
100+
- terminal buffer inspection for viewport state
101+
102+
The spike needs to identify which of these are:
103+
104+
- supported directly
105+
- available via different APIs
106+
- available only through custom adaptation
107+
- missing or impractical
108+
109+
### 3. Scroll Behavior
110+
111+
The spike must explicitly test:
112+
113+
- user scroll-up during streaming output
114+
- reaching top of scrollback
115+
- staying away from bottom while output continues
116+
- behavior once scrollback becomes large
117+
- whether any forced snap-to-bottom or unexpected drift still appears
118+
119+
### 4. Migration Cost
120+
121+
We need an honest estimate of:
122+
123+
- how much of `TerminalTile.tsx` would need replacement
124+
- whether our xterm-specific addons have equivalents
125+
- whether custom compatibility glue would offset any benefits
126+
127+
## Success Criteria For The Spike
128+
129+
The spike is successful if, by the end, we have:
130+
131+
1. A working experimental terminal path using `ghostty-web`.
132+
2. A written comparison of xterm.js vs `ghostty-web` for TermCanvas needs.
133+
3. A clear answer on whether migration should proceed.
134+
135+
## Decision Criteria
136+
137+
We should consider migration only if most of the following are true:
138+
139+
- scroll behavior is materially better
140+
- the integration is stable enough for our Electron app
141+
- missing features are limited and manageable
142+
- custom compatibility glue is smaller than the complexity we are trying to
143+
escape
144+
145+
We should stop and stay on xterm.js if:
146+
147+
- `ghostty-web` still requires heavy patching for our use cases
148+
- feature gaps are large
149+
- performance or correctness regresses
150+
- the migration cost is high relative to simply fixing the current bug
151+
152+
## Immediate Next Step
153+
154+
Build a minimal experimental terminal component on this branch that can:
155+
156+
- initialize `ghostty-web`
157+
- attach to a DOM container
158+
- stream PTY output
159+
- accept keyboard input
160+
- expose enough behavior to manually test scroll semantics

0 commit comments

Comments
 (0)