Skip to content

fix(server): restore the input modes ahead of the scrollback replay - #53

Merged
jesse23 merged 1 commit into
mainfrom
fix/replay-input-modes
Oct 20, 2026
Merged

jesse23 merged 1 commit into
mainfrom
fix/replay-input-modes

Conversation

@jesse23

@jesse23 jesse23 commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Problem

Pasting multi-line text into pi running inside herdr inside webtty submitted the first line and queued every other line as a separate message.

A joining client (reloaded tab, second tab, or a 4003 reconnect) rebuilds its terminal from the scrollback ring: the last 256 Ki characters of output. herdr writes ESC[?2004h (bracketed paste) once when it attaches and then redraws its whole screen (~35 KB each), so the sequence leaves the ring quickly. The new tab has bracketed paste off, so libghostty-vt pastes raw text with each newline sent as \r (Enter).

Fix

  • src/server/sessions/input-modes.ts (InputModes) follows DECSET/DECRST through the PTY output for the DEC modes that change what the client sends: 1, 9, 1000–1007, 1015, 1016, 2004, 2031, 2048. RIS clears them all and DECSTR clears mode 1. A sequence split across chunks is carried over to the next chunk. A chunk with no ESC returns at once.
  • Session.ensurePty sends a joining client one DECSET for the modes that are on, followed by the scrollback, in the same message. The DECSET goes first because the ring can end in the middle of a sequence.
  • ADR 070 records the decision; the webtty and architecture SPECs are updated.

Testing

  • Unit tests: input-modes.test.ts plus a session test that checks the restore when the ring has already been trimmed. All 3,104 tests pass; lint and coverage checks pass.
  • End to end, run on separate ports (2398 and 2399) with a separate HOME and a headless Chrome. The test shell sets mode 2004, writes 400 KB, then records its raw input. After a reload, a three-line paste arrived as:
    • before (1.x, port 2398): line one^Mline two^Mline three
    • after (this branch, port 2399): ^[[200~line one\nline two\nline three^[[201~

Not covered

The kitty keyboard protocol's flags (CSI > … u, a stack kept per screen) are not followed; they behave the same as before this change.

A full-screen app (herdr) sets bracketed paste once, and its redraws push
ESC[?2004h out of the 256 Ki replay ring. A reloaded tab then pasted raw
text, each newline an Enter, so pi submitted the first line and queued the
rest. The session now follows the DEC input modes through its output and
sends a DECSET for those that are on before the replay (ADR 070).
@jesse23

jesse23 commented Oct 20, 2026

Copy link
Copy Markdown
Owner Author

Checked end to end on the real stack: dev webtty (bun run webtty, port 2399, isolated HOME) → herdr (separate session, isolated state) → pi 1.3.0, driven from headless Chrome.

  1. Started pi in a herdr pane.
  2. Resized the page 32 times so herdr redrew its whole screen. A raw client then got a replay whose history no longer contains ESC[?2004h. The new restore prefix was ESC[?1000;1002;1003;1004;1006;1015;2004;2031h (herdr's bracketed paste, mouse, focus and color-scheme modes).
  3. Reloaded the tab and pasted line one\nline two\nline three. All three lines landed in pi's editor as one paste and nothing was submitted.

@jesse23
jesse23 merged commit c0b700f into main Oct 20, 2026
3 checks passed
@jesse23
jesse23 deleted the fix/replay-input-modes branch October 20, 2026 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant