A revived session stops typing the mouse at its own prompt, and the mobile controls stop shouting - #67
Merged
Merged
Conversation
… prompt A session that came back from a daemon restart typed at itself. Every pointer move over the terminal put a run of "35;61;22M35;61;21M…" at the shell prompt, and on a phone every touch did the same. Those are SGR mouse reports. A snapshot's ring is bytes, not state, so reviving one re-runs every mode change the dead shell ever wrote — including the sequence that turned mouse tracking on inside a program that was killed with the daemon and so never wrote the sequence that turns it back off. The client replays that, ends the replay armed, and reports the pointer to a brand new shell that has no idea what an SGR report is. Fixed at both ends, because either alone leaves a hole. Revive now writes a reset between the replayed scrollback and its marker, which covers the modes a dead program can strand: mouse tracking in every protocol and encoding, focus reporting, bracketed paste, application cursor keys, and the display state — scrolling region, charset, autowrap, cursor visibility — that would otherwise make the new shell unreadable rather than merely noisy. And the client clears mouse and focus reporting the moment a replayed backlog has drained, which covers the other way in: a program killed mid-session, with no restart involved. Only those two there, deliberately. Application cursor keys and bracketed paste are stale in the same way but change what a keystroke means rather than inventing keystrokes, so clearing them against a live program would break arrows and pastes in a client that had nothing wrong with it. The alternate screen is deliberately not left. A dead full-screen program will have been in it, but swapping in a main buffer whose contents were evicted from the ring long ago would trade a tidy terminal for an empty one where somebody's scrollback used to be. Also undoes two things #66 did to the controls. The floating chips went to 48px squares and the key bar to eight tall chips, which no longer fit and turned the bar into a scrolling strip across the bottom of the screen — and every chip on it fires on pointerdown, so dragging that strip sent whatever key the thumb started on. The paste chip fired the clipboard. Paste goes back to the platform. xterm keeps its input in a real textarea but ships it zero-sized and parked off-page, so a long press has nothing to land on and no phone will offer a paste menu over a div. On a coarse pointer that element now gets the terminal's own box, so the press finds editable text and the paste travels xterm's own path, which is what keeps newline handling and bracketed-paste mode right. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The long-press box gave xterm's input element the terminal's own dimensions, and a browser rings a focused control to say where typing will land — so the ring became a blue line around the whole terminal for as long as the session had focus. Dropped, and nothing is owed for it. The element is a proxy: what the ring would announce is already announced by the block cursor blinking in the cells. It had been focused and outlined all along, parked off-page at zero size, where nobody could see either. Safari's tap highlight is separate and ignores `outline`, so it goes too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What was happening
Every pointer move over a terminal put a run of
35;61;22M35;61;21M…at the shell prompt; on a phone every touch did the same.Those are SGR mouse reports with the
ESC[<eaten by the prompt.Cb=35is32 | 3— motion, no button — which needs any-event tracking (?1003h). So the browser terminal was reporting the pointer to a plain shell.Why
A snapshot's ring is bytes, not state.
Registry.Revivepreloads those bytes into a fresh shell, so replaying one re-runs every mode change the dead shell ever wrote — including the sequence that turned mouse tracking on inside a program that was killed with the daemon and therefore never wrote the sequence that turns it off. The client replays that, ends the replay armed, and reports the pointer to a shell that has no idea what an SGR report is.This is the third member of a family flue already knew about:
answerQueriesand the replay mute gate exist because a replay also re-runs device queries.The fix, at both ends
Daemon.
Revivewrites a reset between the replayed scrollback and its marker: mouse tracking in every protocol and encoding, focus reporting, bracketed paste, application cursor keys, and the display state (scrolling region, charset, autowrap, cursor visibility) that would otherwise make the new shell unreadable rather than merely noisy.Client.
Emulator.stopReporting()clears mouse and focus reporting the moment a replayed backlog has drained — which covers the other way in, a program killed mid-session with no restart involved. Only those two, deliberately: application cursor keys and bracketed paste are stale in the same way, but they change what a keystroke means rather than inventing keystrokes, so clearing them against a live program would break arrows and pastes in a client that had nothing wrong with it.The alternate screen is deliberately not left. A dead full-screen program will have been in it, but swapping in a main buffer whose contents were evicted from the ring long ago trades a tidy terminal for an empty one where somebody's scrollback used to be.
Known cost: a browser attaching to a session where a mouse-driven TUI is live loses mouse reporting in that client until the program re-enables it.
And the controls
#66 took the floating chips to 48px squares and the key bar to eight tall chips. Those no longer fit, so the bar became a scrolling strip across the bottom of the screen — and every chip fires on
pointerdown, so dragging that strip sent whatever key the thumb started on. The paste chip fired the clipboard. All reverted to the pre-#66 sizes.Paste goes back to the platform. xterm keeps its input in a real textarea but ships it zero-sized and parked off-page, so a long press has nothing to land on and no phone offers a paste menu over a div. On a coarse pointer that element now gets the terminal's own box, so the press finds editable text and the paste travels xterm's own path — which is what keeps newline handling and bracketed-paste mode right.
font-size: 16pxon it is a threshold and not a size: below that iOS zooms the page on focus.Tests
make test— 1329 web, 163 relay, every Go package greenmake lintNew coverage:
TestReviveSettlesTheModesTheDeadShellLeftBehindpins the reset and its ordering against the replay; emulator tests pinstopReportingclearing a replayed arm and leaving a live one alone; terminal tests pin that it lands after the backlog drains, never mid-backlog, and never on a fresh spawn;styles.build.test.tspins the long-press rule surviving a real build with its!importantintact.Needs a device check before merge
The long-press paste is the one thing I could not verify. It is a platform behaviour, so it needs a real phone — please check on yours that a long press over the terminal offers Paste, that typing and touch-scrolling still work, and that tapping does not zoom the page. If it misbehaves, the CSS block in
styles.cssis self-contained and reverting it costs nothing else.🤖 Generated with Claude Code