Repository navigation
fix: keep the cursor hidden across a card switch - #312
Merged
Merged
Conversation
Hiding a session serializes its terminal and destroys it; showing it writes the snapshot into a fresh one. xterm's SerializeAddon replays only the modes it can read off term.modes, and cursor visibility is not one of them: it lives on the private coreService, so ?25 is never recorded. A fresh terminal shows its cursor, so every full-screen TUI that hid the real one to draw its own came back with lich's blinking block parked wherever the snapshot left the cursor. Restore it the way the mouse encoding already is — read at hide, written back between the snapshot and the replayed tail, so output the app produced while hidden still wins. Both now ride one carried-modes string. Cursor shape (DECSCUSR) is still not carried and is named as such in docs/ceilings.md: it only bites a TUI that shows a cursor at all.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A bubbletea TUI running in a card came back from a card switch with a blinking block cursor parked in the middle of its output — one it had hidden at startup and never asked back.
Root cause
Hiding a session serializes its terminal and destroys it; showing it writes the snapshot into a fresh one.
SerializeAddon@0.14.0replays only the modes it can read offterm.modes— checked against the shipped bundle, that is?1h ?6h ?7l ?9h ?45h ?66h ?1000h ?1002h ?1003h ?1004h ?1049h ?2004h.?25is not among them: xterm keeps cursor visibility on the privatecoreService.isCursorHidden, which the public API does not expose.A fresh terminal shows its cursor. So every full-screen TUI that hid the real one to draw its own — bubbletea and anything like it — got lich's blinking block back, at whatever position the snapshot restored.
Same family as the mouse-encoding gap already documented in
term-modes.ts; this is its sibling.The fix
term-view.ts—cursorHidden(term), a third private read besidecellDimensionsandmouseEncoding, in the file invariant feat: multi-session terminals per project #1 already exempts.term-modes.ts—cursorVisibilitySequence(hidden),\x1b[?25lor"", and the file's doc comment now covers both modes the snapshot cannot carry.TerminalView.tsx—mouseEncodingRefbecomescarriedModesRefand holds both sequences. Written between the snapshot and the replayed tail, so anything the app changed while hidden still wins, and kept across the overflow path where the snapshot is dropped: these describe the app, not the buffer.Left out, deliberately
Cursor shape (DECSCUSR) is still not carried — a TUI that chose a bar or underline cursor gets lich's block back. It only bites a TUI that shows a cursor at all, which is not this bug. Named in
docs/ceilings.mdbeside the bullet it belongs to.Test plan
pnpm test— 989 passedgo test ./...— 1617 passedtsc --noEmit,vite build,biome checkclean;gofmt -l .andgo vet ./...cleanterm-modes.test.tsfor both branches ofcursorVisibilitySequencetask dev: run a bubbletea TUI in a card, switch away and back — no stray cursor. (The frontend gate is node-only and cannot render.)