fix(host-service): restore terminal modes on reattach via headless xterm#4231
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds a per-session headless xterm-based ModeTracker that ingests PTY output, computes a mode-setting preamble (kitty keyboard, bracketed paste, focus, mouse SGR, cursor state), and prepends that preamble before replaying buffered PTY bytes on reattach. ChangesTerminal Mode Replay on Reattach
Sequence Diagram(s)sequenceDiagram
participant Client
participant Session
participant ModeTracker
participant FIFO
Client->>Session: attach/reattach
Session->>ModeTracker: buildPreamble()
ModeTracker-->>Session: preamble bytes or null
Session->>FIFO: read buffered PTY bytes
Session-->>Client: send (preamble + FIFO bytes)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes terminal mode loss (kitty keyboard protocol, bracketed paste, focus reporting, mouse tracking) when a renderer reattaches mid-session by feeding every PTY output chunk through a headless
Confidence Score: 3/5The kitty keyboard restore is safe to merge; the SGR mouse encoding gap and unguarded _writeBuffer access are the main concerns before shipping. The kitty keyboard fix is sound and well-tested. The integration in terminal.ts is clean — tracker is fed before broadcast, disposed in every teardown path, and resized on client resize. Two gaps stand out: SGR mouse encoding (?1006h) is explicitly called out in the plan and PR description but is not emitted in the preamble (xterm's public modes API doesn't expose it, and no internal workaround was added as was done for kitty flags); the "focus reporting and mouse SGR" test name implies SGR encoding coverage it doesn't provide, and for any session running a mouse-enabled TUI in a wide terminal, mouse coordinates will be garbled after renderer reattach. Additionally, _core._writeBuffer is accessed without the same null guard applied to optionsService. packages/host-service/src/terminal/terminal-mode-tracker.ts — SGR mouse encoding gap and unguarded _writeBuffer access; packages/host-service/src/terminal/terminal-mode-tracker.test.ts — misleading "mouse SGR" test name.
|
| Filename | Overview |
|---|---|
| packages/host-service/src/terminal/terminal-mode-tracker.ts | New module that feeds PTY output through a headless xterm to track terminal mode state; relies on internal xterm APIs (_writeBuffer, _core.coreService.kittyKeyboard) that are unguarded for _writeBuffer, and omits SGR mouse encoding (?1006h) from the preamble despite the plan listing it. |
| packages/host-service/src/terminal/terminal-mode-tracker.test.ts | 10 test cases covering kitty push/pop/set, bracketed paste, focus, multi-mode ordering, cursor hide, resize idempotence, and split escapes; "focus reporting and mouse SGR" test name is misleading as it doesn't exercise actual SGR encoding (?1006h). |
| packages/host-service/src/terminal/terminal.ts | Integrates ModeTracker into TerminalSession: feeds PTY bytes to the tracker before broadcast/buffer, prepends preamble to the replay buffer on reattach, resizes tracker on client resize, and disposes cleanly in all teardown paths. |
| packages/host-service/package.json | Adds @xterm/headless 6.1.0-beta.197 as a runtime dependency. |
| plans/20260507-terminal-mode-replay.md | Design document for the mode-replay feature; implementation matches the plan except that mouse SGR (?1006h) listed in the plan is not included in the final preamble. |
Sequence Diagram
sequenceDiagram
participant PTY as pty-daemon
participant Host as host-service (terminal.ts)
participant Tracker as ModeTracker (headless xterm)
participant Buffer as FIFO replay buffer
participant WS as renderer WebSocket
PTY->>Host: onOutput(chunk)
Host->>Tracker: feed(bytes) [writeSync — synchronous parse]
alt socket connected
Host->>WS: broadcastBytes(bytes)
else no socket
Host->>Buffer: bufferOutput(bytes)
end
note over Host,WS: Renderer refresh / new attach
WS->>Host: WebSocket onOpen
Host->>Tracker: buildPreamble()
Tracker-->>Host: Uint8Array (mode-restore escapes) or null
Host->>WS: sendBytes(preamble + FIFO contents)
note over WS: xterm in renderer now has correct kitty flags,<br/>bracketed paste, focus, mouse tracking mode
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/host-service/src/terminal/terminal-mode-tracker.ts:150-154
**Unguarded `_writeBuffer` access**
`internals._core.optionsService` is guarded with `?.` before the injection, but `internals._core._writeBuffer` is stored unconditionally. If the headless xterm version ever lacks this internal property, `writeBuffer` will be `undefined` and every call to `feed()` will throw a `TypeError`, crashing the session's `onOutput` handler silently. A null-guard similar to what is done for `optionsService` would make the failure mode explicit.
### Issue 2 of 2
packages/host-service/src/terminal/terminal-mode-tracker.test.ts:67-74
**Test name "mouse SGR" doesn't cover SGR mouse encoding**
The test feeds `\x1b[?1002h` (button-tracking mode) and `\x1b[?1004h` (focus reporting) — neither is the SGR mouse-encoding sequence (`\x1b[?1006h`). The name "mouse SGR" implies SGR encoding (`?1006h`) is exercised here, but it isn't. This is notable because the implementation plan explicitly lists `\x1b[?1006h` as a mode to be re-emitted on reattach. Without `?1006h` in the preamble, terminal applications that rely on SGR mouse encoding (all modern TUIs with mouse support) will fall back to legacy X10 encoding after reattach, clipping mouse coordinates to ≤223 columns/rows in wide terminals. The test name should be updated to reflect what is actually covered, and a follow-up test or implementation for SGR encoding (`?1006h`) should be considered.
Reviews (1): Last reviewed commit: "fix(host-service): restore terminal mode..." | Re-trigger Greptile
| const writeBuffer = internals._core._writeBuffer; | ||
|
|
||
| return { | ||
| feed(bytes) { | ||
| writeBuffer.writeSync(bytes); |
There was a problem hiding this comment.
internals._core.optionsService is guarded with ?. before the injection, but internals._core._writeBuffer is stored unconditionally. If the headless xterm version ever lacks this internal property, writeBuffer will be undefined and every call to feed() will throw a TypeError, crashing the session's onOutput handler silently. A null-guard similar to what is done for optionsService would make the failure mode explicit.
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/terminal/terminal-mode-tracker.ts
Line: 150-154
Comment:
**Unguarded `_writeBuffer` access**
`internals._core.optionsService` is guarded with `?.` before the injection, but `internals._core._writeBuffer` is stored unconditionally. If the headless xterm version ever lacks this internal property, `writeBuffer` will be `undefined` and every call to `feed()` will throw a `TypeError`, crashing the session's `onOutput` handler silently. A null-guard similar to what is done for `optionsService` would make the failure mode explicit.
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/host-service/src/terminal/terminal-mode-tracker.ts`:
- Around line 137-151: The code dereferences private xterm internals without a
safety check; before using internals._core._writeBuffer, add an explicit guard
that verifies internals, internals._core, and
internals._core._writeBuffer?.writeSync exist (e.g. check
internals?._core?._writeBuffer?.writeSync) and if not present throw or log a
clear error like "Terminal writeBuffer.writeSync not available — incompatible
xterm internals" so session creation fails fast instead of crashing later;
update the block around internals, _core, optionsService/rawOptions.vtExtensions
and the subsequent writeBuffer usage to perform this guarded check.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 86faf266-3682-47f5-ac9b-2606d72a3e7f
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
packages/host-service/package.jsonpackages/host-service/src/terminal/terminal-mode-tracker.test.tspackages/host-service/src/terminal/terminal-mode-tracker.tspackages/host-service/src/terminal/terminal.tsplans/20260507-terminal-mode-replay.md
🚀 Preview Deployment🔗 Preview Links
Preview updates automatically with new commits |
696f8cf to
e76025b
Compare
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/host-service/src/terminal/terminal-mode-tracker.ts (1)
47-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winGuard private xterm internals before dereference.
Line 53/Line 60 and Line 96 assume private
@xterm/headlessinternals always exist; if they drift, session attach can crash with aTypeErrorinstead of failing fast with a clear error.Proposed hardening diff
type HeadlessInternals = { - _core: { + _core?: { _writeBuffer: { writeSync(data: string | Uint8Array): void }; coreService: { kittyKeyboard?: { flags: number } }; optionsService: { rawOptions: { vtExtensions?: { kittyKeyboard?: boolean } }; }; }; }; @@ const internals = term as unknown as HeadlessInternals; + const core = internals._core; + if (!core?._writeBuffer?.writeSync || !core.optionsService?.rawOptions) { + term.dispose(); + throw new Error( + "[terminal] Incompatible `@xterm/headless` internals: missing _core._writeBuffer.writeSync/optionsService.rawOptions", + ); + } @@ - internals._core.optionsService.rawOptions.vtExtensions = { + core.optionsService.rawOptions.vtExtensions = { kittyKeyboard: true, }; @@ - const writeBuffer = internals._core._writeBuffer; + const writeBuffer = core._writeBuffer; @@ - const kittyFlags = internals._core.coreService.kittyKeyboard?.flags ?? 0; + const kittyFlags = core.coreService.kittyKeyboard?.flags ?? 0;Run this to verify the pinned package still exposes the internal members being used:
#!/bin/bash set -euo pipefail VER="6.1.0-beta.197" META_JSON="$(curl -fsSL "https://unpkg.com/@xterm/headless@${VER}/?meta")" TERMINAL_PATH="$(jq -r '.files[].path' <<<"$META_JSON" | rg '/Terminal\.(js|mjs)$' | head -n1)" echo "Inspecting: ${TERMINAL_PATH}" curl -fsSL "https://unpkg.com/@xterm/headless@${VER}${TERMINAL_PATH}" \ | rg -n "_core|_writeBuffer|writeSync|optionsService|rawOptions|kittyKeyboard"Also applies to: 96-97
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/host-service/src/terminal/terminal-mode-tracker.ts` around lines 47 - 60, The code dereferences private xterm/headless internals (term as HeadlessInternals -> internals._core.optionsService.rawOptions and internals._core._writeBuffer) without guards; add defensive checks that internals and the nested properties exist before using them (validate internals, internals._core, internals._core.optionsService, internals._core.optionsService.rawOptions, and internals._core._writeBuffer), and if any is missing throw or return a clear, descriptive error (e.g., "unsupported xterm/headless version: missing internal <symbol>") so session attach fails fast instead of crashing with a TypeError; update the spots where you set vtExtensions and assign writeBuffer to use these guards (refer to internals, internals._core.optionsService.rawOptions.vtExtensions, and internals._core._writeBuffer).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@packages/host-service/src/terminal/terminal-mode-tracker.ts`:
- Around line 47-60: The code dereferences private xterm/headless internals
(term as HeadlessInternals -> internals._core.optionsService.rawOptions and
internals._core._writeBuffer) without guards; add defensive checks that
internals and the nested properties exist before using them (validate internals,
internals._core, internals._core.optionsService,
internals._core.optionsService.rawOptions, and internals._core._writeBuffer),
and if any is missing throw or return a clear, descriptive error (e.g.,
"unsupported xterm/headless version: missing internal <symbol>") so session
attach fails fast instead of crashing with a TypeError; update the spots where
you set vtExtensions and assign writeBuffer to use these guards (refer to
internals, internals._core.optionsService.rawOptions.vtExtensions, and
internals._core._writeBuffer).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8378e7db-b921-4ac1-88c3-dd64650e3437
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
packages/host-service/package.jsonpackages/host-service/src/terminal/terminal-mode-tracker.test.tspackages/host-service/src/terminal/terminal-mode-tracker.tspackages/host-service/src/terminal/terminal.tsplans/20260507-terminal-mode-replay.md
✅ Files skipped from review due to trivial changes (1)
- packages/host-service/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
- plans/20260507-terminal-mode-replay.md
- packages/host-service/src/terminal/terminal.ts
Renderer refreshes mid-session were dropping kitty keyboard, bracketed paste, focus reporting, mouse, and other DEC private modes — codex CLI's Shift+Enter started submitting instead of inserting a newline because the mode-setting escape (`\x1b[>7u`) was emitted once at startup, broadcast straight to the live socket, and never made it into the FIFO replay. Mirror VSCode's `XtermSerializer` approach: feed every PTY chunk through an `@xterm/headless` instance that tracks current mode state. On `replayBuffer`, prepend a preamble built from the tracker so a freshly created xterm rejoins in the modes the running program already believes are active. The preamble is now sent on every attach (even when the FIFO is empty), so the renderer is never wedged at default modes. Two headless-specific quirks worth flagging: - `Terminal.write` is async-buffered; we use the internal `_core._writeBuffer.writeSync(...)` so `term.modes` is up-to-date when we build the preamble. Same engine, same path xterm's own SerializeAddon takes. - `vtExtensions.kittyKeyboard` is in the public typings but headless's option sanitizer drops it, so kitty handlers early-return. Inject it on `rawOptions` post-construction. Plan: plans/20260507-terminal-mode-replay.md
e76025b to
cd1f109
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/host-service/src/terminal/terminal.ts (2)
825-869: ⚖️ Poor tradeoffAdoption path note: tracker is rebuilt from whatever the daemon replays.
onOutputcorrectly feeds every chunk throughsession.modeTrackerbefore broadcast, so live mode escapes are captured. Worth flagging for awareness (not a blocker for this PR): on host-service restart withadoptOnly/replayOnAdoption, the tracker is freshly constructed and only learns modes that are still inside the daemon's ring buffer. If startup mode escapes were already evicted on the daemon side, the preamble after a subsequent reattach will be incomplete. This is strictly better than the pre-PR behaviour (no preamble at all), but if you want full coverage across host-service restarts you'd eventually need the daemon to track modes too.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/host-service/src/terminal/terminal.ts` around lines 825 - 869, Adoption replay can miss startup mode escapes because session.modeTracker is reconstructed only from the daemon's ring-buffer replay (daemon.subscribe with replayOnAdoption); to fix, persist or transmit mode state on adoption so the newly constructed session.modeTracker is initialized with prior modes: either (a) extend daemon.subscribe/daemon replay to include a compact mode-history payload alongside output chunks so the handler in onOutput can seed session.modeTracker before feeding chunks, or (b) store the last-known mode state in session metadata and restore it when creating session.modeTracker (before calling session.modeTracker.feed in onOutput); reference session.modeTracker, daemon.subscribe (replayOnAdoption), resolveShellReady, broadcastBytes, and bufferOutput to find the replay/feeding site to implement the seeding.
450-474: 💤 Low valueReplay path is correct; preamble + FIFO concatenation is sound.
Allocating a single combined
Uint8Arrayand prepending the preamble before the FIFO is the right shape for the renderer attach hot path. The early-return when both are empty avoids spurious zero-byte sends.Minor (skippable):
bufferTotalis recomputed by walkingsession.buffer, butsession.bufferBytesis already maintained as the exact same total inbufferOutput. Swapping the loop forsession.bufferByteswould be a one-line tidy-up.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/host-service/src/terminal/terminal.ts` around lines 450 - 474, The replayBuffer function recomputes bufferTotal by iterating session.buffer even though session.bufferBytes already tracks the total; replace the manual loop and use session.bufferBytes when sizing the combined Uint8Array and computing preambleLen+bufferTotal (keep all other logic: preamble from session.modeTracker.buildPreamble(), concatenation into combined, clearing session.buffer and session.bufferBytes, and calling sendBytes(socket, combined)) so the change is a one-line tidy-up relying on session.bufferBytes instead of the loop.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/host-service/src/terminal/terminal.ts`:
- Around line 825-869: Adoption replay can miss startup mode escapes because
session.modeTracker is reconstructed only from the daemon's ring-buffer replay
(daemon.subscribe with replayOnAdoption); to fix, persist or transmit mode state
on adoption so the newly constructed session.modeTracker is initialized with
prior modes: either (a) extend daemon.subscribe/daemon replay to include a
compact mode-history payload alongside output chunks so the handler in onOutput
can seed session.modeTracker before feeding chunks, or (b) store the last-known
mode state in session metadata and restore it when creating session.modeTracker
(before calling session.modeTracker.feed in onOutput); reference
session.modeTracker, daemon.subscribe (replayOnAdoption), resolveShellReady,
broadcastBytes, and bufferOutput to find the replay/feeding site to implement
the seeding.
- Around line 450-474: The replayBuffer function recomputes bufferTotal by
iterating session.buffer even though session.bufferBytes already tracks the
total; replace the manual loop and use session.bufferBytes when sizing the
combined Uint8Array and computing preambleLen+bufferTotal (keep all other logic:
preamble from session.modeTracker.buildPreamble(), concatenation into combined,
clearing session.buffer and session.bufferBytes, and calling sendBytes(socket,
combined)) so the change is a one-line tidy-up relying on session.bufferBytes
instead of the loop.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 88d53b50-0e5d-4f56-a9df-4b32d936c8a2
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
packages/host-service/package.jsonpackages/host-service/src/terminal/terminal-mode-tracker.test.tspackages/host-service/src/terminal/terminal-mode-tracker.tspackages/host-service/src/terminal/terminal.tsplans/20260507-terminal-mode-replay.md
✅ Files skipped from review due to trivial changes (2)
- packages/host-service/package.json
- plans/20260507-terminal-mode-replay.md
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/host-service/src/terminal/terminal-mode-tracker.test.ts
…erm (superset-sh#4231) Renderer refreshes mid-session were dropping kitty keyboard, bracketed paste, focus reporting, mouse, and other DEC private modes — codex CLI's Shift+Enter started submitting instead of inserting a newline because the mode-setting escape (`\x1b[>7u`) was emitted once at startup, broadcast straight to the live socket, and never made it into the FIFO replay. Mirror VSCode's `XtermSerializer` approach: feed every PTY chunk through an `@xterm/headless` instance that tracks current mode state. On `replayBuffer`, prepend a preamble built from the tracker so a freshly created xterm rejoins in the modes the running program already believes are active. The preamble is now sent on every attach (even when the FIFO is empty), so the renderer is never wedged at default modes. Two headless-specific quirks worth flagging: - `Terminal.write` is async-buffered; we use the internal `_core._writeBuffer.writeSync(...)` so `term.modes` is up-to-date when we build the preamble. Same engine, same path xterm's own SerializeAddon takes. - `vtExtensions.kittyKeyboard` is in the public typings but headless's option sanitizer drops it, so kitty handlers early-return. Inject it on `rawOptions` post-construction. Plan: plans/20260507-terminal-mode-replay.md
…low-up superset-sh#4074 exhaustive worktree search in v2 branch picker: - adopt.ts / search-branches.ts / branch-search.ts: whole-file upstream overwrite. Fork's <repoPath>/.worktrees/<branch>-scoped variant (FORK NOTE in branch-search.ts) is dropped in favour of upstream's exhaustive 'all git worktrees including foreign ones' strategy. This matches superset-sh#4226's adopt-foreign-worktree UX direction. superset-sh#4160 listBranches single git spawn perf (skipped, HEAD-preferred): - packages/host-service/src/trpc/router/git/git.ts: keep fork's rich listBranches (sortOrder/pinDefault/buildBranch per-branch upstream/ahead-behind/last-commit). Upstream's 4xN -> 1 spawn simplification drops those fields the fork's BaseBranchSelector consumes; perf win not worth the rich UI regression. Revisit if BaseBranchSelector is unified with upstream's BaseBranchSelector in a future cycle. superset-sh#4218 respawn host-service on Electron auto-update: - packages/host-service/package.json: merge fork's ./settings export and upstream's ./package.json export (both needed). - packages/host-service/src/trpc/router/host/host.ts: whole-file upstream overwrite. HOST_SERVICE_VERSION switches from fork's hard-coded '0.7.0' to dynamic hostServicePackageJson.version (currently 0.8.1). Bumps via apps/desktop/electron-builder.ts respawn flow are now automatic. superset-sh#4231 restore terminal modes on reattach via headless xterm: - packages/host-service/src/terminal/terminal.ts: createModeTracker call sites referenced un-declared 'cols, rows' inside session-create closure; fix to hard-code (120, 32) which matches the daemon.open() call above. Real cols/rows arrive via subsequent resize() calls; modeTracker.resize handles the update path. Files clean cherry-picked: superset-sh#4046 host-service bump 0.7.0, plus adopt-foreign-worktree wiring.
Summary
\x1b[>7u) are emitted once at startup, broadcast straight to the live socket, and never enter the 64 KiB FIFO replay — a freshly attaching xterm has no way to learn them.@xterm/headlessinstance that tracks current mode state, and prepend a\x1b[…]preamble (built from the tracker) on every reattach. Mirrors VSCode'sXtermSerializerpattern (src/vs/platform/terminal/node/ptyService.ts); other xterm.js consumers I checked (Tabby, Wave, cmux) all have the same latent gap.plans/20260507-terminal-mode-replay.md.Two headless-specific gotchas, documented inline:
Terminal.writeis async-buffered. We synchronously drain via the internal_core._writeBuffer.writeSync(...)(xterm's own SerializeAddon does the same) soterm.modesreflects the feed immediately whenreplayBufferbuilds the preamble.vtExtensions.kittyKeyboardis in the public typings, but headless's option sanitizer drops it (itsDEFAULT_OPTIONStable omits the key). Without injecting it ontorawOptionspost-construction, kitty handlers early-return and\x1b[>7uis silently ignored.Test plan
terminal-mode-tracker.test.ts— 10 cases (kitty push survives 200 KB of unrelated output, kitty pop, kitty set-to-zero, bracketed paste, focus + mouse SGR, multi-mode preamble ordering, hide-cursor, resize idempotence, escape sequences split across feeds, default state).bun test src/terminal/— 47/47 pass.bun run typecheck— clean across all 29 workspace tasks.bun run lint— clean.Summary by cubic
Restore terminal modes on renderer reattach by tracking PTY output with
@xterm/headlessand sending a mode preamble. Fixes lost kitty keyboard (Shift+Enter), bracketed paste, focus, mouse tracking, and other DEC modes after refresh.Bug Fixes
vtExtensions.kittyKeyboard.Dependencies
@xterm/headless@6.1.0-beta.197.Written for commit 759881c. Summary will update on new commits.
Summary by CodeRabbit
New Features
Tests
Documentation
Chores