feat(terminal): replay full tmux scrollback on terminal reload + CRLF normalization#148
Open
aakhter wants to merge 2 commits into
Open
feat(terminal): replay full tmux scrollback on terminal reload + CRLF normalization#148aakhter wants to merge 2 commits into
aakhter wants to merge 2 commits into
Conversation
A full page reload (GET /api/sessions/:id/terminal with no ?tail=) now captures
the ENTIRE tmux scrollback via capture-pane -p -e -S -, so users get back history
that scrolled off Codeman's byte buffer. Tab switches (?tail=N) keep the fast
visible-frame capture.
- tmux-manager capturePaneBuffer/captureActivePaneBuffer take { fullHistory }:
full-history returns raw linear scrollback (skips the single-screen
formatPaneSnapshot repaint, which would clip multi-screen history).
- /terminal selects full-history on full reload, visible on tail; caps the
payload at the configured terminalBufferMaxBytes (keeps most-recent bytes,
line-aligned) and returns source/fullSize/truncated metadata.
Verified: tsc 0, tmux-capture-full-history 5/5, session-routes 68/68.
Caveat: lines tmux already evicted past its history-limit can't be recovered.
A shell terminal could render output diagonally (each line shifted one column right) after a full page reload or a cursor-query-failure replay. Root cause: capturePaneBuffer's full-history path (capture-pane -p -e -S -) and its cursor-query-failure fallback returned raw scrollback, which tmux joins with a BARE \n. The browser xterm uses convertEol:false (correct for the live PTY stream, which carries real \r\n), so each bare \n dropped a row without returning the cursor to column 0 -> staircase. The visible / tab-switch path (formatPaneSnapshot) was immune because it repaints each row with an absolute cursor CSI. Fix: new pure helper normalizeScrollbackEol() (\r?\n -> \r\n, idempotent on CRLF, leaves lone \r overwrites untouched, adds/removes no rows) applied at both raw-return seams. The absolute-positioned snapshot path is unchanged. Tests: test/tmux-scrollback-eol.test.ts pins the invariant (no LF without a preceding CR) + CRLF idempotency + lone-CR preservation. 136/136 across tmux-scrollback-eol + tmux-capture-full-history + tmux-manager + routes/session-routes; build, tsc, prettier, frontend-syntax clean.
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.
Summary
A full page reload (
GET /api/sessions/:id/terminalwithout?tail=) previously only replayed Codeman's in-memory byte buffer — scrollback that had been pushed off the buffer was lost. This PR adds two complementary fixes:COD-47: Full tmux scrollback replay
GET /api/sessions/:id/terminal(no?tail=) now callscapture-pane -p -e -S -, capturing the entire tmux scrollback, not just the visible frame.?tail=N) keep using the fast visible-frame path — no regression.MuxInterface.capturePaneBuffergains an optionalopts.fullHistoryflag to request the deep capture.COD-138: CRLF normalization of scrollback replay
Without this, full-history capture causes a "staircase" rendering bug:
capture-pane -S -returns scrollback with bare\nline endings; xterm.js running withconvertEol: false(correct for the live PTY stream, which carries real\r\n) treats each bare\nas "move down without carriage return," shifting each replayed line one column to the right.Fix: new pure helper
normalizeScrollbackEol()converts\r?\n→\r\n(idempotent on CRLF; preserves lone\roverwrites; adds/removes no rows) applied at the raw-return seam. The absolute-cursor snapshot path (visible-frame / tab-switch) is unchanged.Test plan
test/tmux-capture-full-history.test.ts(new, 5 tests) — full-history flag + VPA-strip gatetest/tmux-scrollback-eol.test.ts(new, 6 tests) — CRLF normalization invariants (no bare LF, CRLF idempotency, lone-CR preservation)test/routes/session-routes.test.ts(13 new terminal-route tests added, 63 total pass)tsc --noEmit: 0 errorsnpm run build: clean