mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 06:59:42 +02:00
COD-47 replay full tmux scrollback on terminal reload
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.
This commit is contained in:
@@ -985,6 +985,15 @@ export function registerSessionRoutes(
|
||||
const query = req.query as { tail?: string };
|
||||
const session = findSessionOrFail(ctx, id);
|
||||
|
||||
// A request WITHOUT a `tail` param is a FULL RELOAD (the browser reloaded the
|
||||
// page and needs the whole scroll history back). A request WITH `tail` is a
|
||||
// tab switch — only the recent tail matters and speed wins. On a full reload
|
||||
// we capture the ENTIRE tmux scrollback (`-S -`, COD-47) so the user gets
|
||||
// back history that scrolled off Codeman's byte buffer; on a tab switch we
|
||||
// capture only the visible frame, which stays fast.
|
||||
const tailBytes = query.tail ? parseInt(query.tail, 10) : 0;
|
||||
const isFullReload = tailBytes <= 0;
|
||||
|
||||
// Prepend the live tmux pane buffer so tab-switch replay shows the current
|
||||
// on-screen frame, not just the accumulated byte history. This matters for
|
||||
// TUI modes (codex/opencode) that repaint only their latest frame: the
|
||||
@@ -995,15 +1004,20 @@ export function registerSessionRoutes(
|
||||
const muxName = session.muxName;
|
||||
const liveMuxBuffer =
|
||||
muxName && typeof ctx.mux.captureActivePaneBuffer === 'function'
|
||||
? ctx.mux.captureActivePaneBuffer(muxName)
|
||||
? ctx.mux.captureActivePaneBuffer(muxName, isFullReload ? { fullHistory: true } : undefined)
|
||||
: null;
|
||||
const source: 'history' | 'mux-visible' | 'mux-full-history' =
|
||||
liveMuxBuffer !== null && liveMuxBuffer.length > 0
|
||||
? isFullReload
|
||||
? 'mux-full-history'
|
||||
: 'mux-visible'
|
||||
: 'history';
|
||||
const rawBuffer =
|
||||
liveMuxBuffer !== null && liveMuxBuffer.length > 0
|
||||
? session.terminalBufferLength > 0
|
||||
? `${session.terminalBuffer}\x1b[H\x1b[2J${liveMuxBuffer}`
|
||||
: liveMuxBuffer
|
||||
: session.terminalBuffer;
|
||||
const tailBytes = query.tail ? parseInt(query.tail, 10) : 0;
|
||||
const fullSize = rawBuffer.length;
|
||||
let truncated = false;
|
||||
let cleanBuffer: string;
|
||||
@@ -1055,11 +1069,26 @@ export function registerSessionRoutes(
|
||||
// Remove Ctrl+L and leading whitespace (cheap on tailed subset)
|
||||
cleanBuffer = cleanBuffer.replace(CTRL_L_PATTERN, '').replace(LEADING_WHITESPACE_PATTERN, '');
|
||||
|
||||
// Cap the payload at the configured terminal buffer limit. Full-history
|
||||
// tmux capture (`-S -`) can be tens of MB of scrollback; shipping all of it
|
||||
// would freeze the browser xterm. Keep the most RECENT bytes (slice from the
|
||||
// end) and align to a line boundary so we never start mid-ANSI-escape.
|
||||
const { terminalBufferMaxBytes } = await ctx.getTerminalHistoryConfig();
|
||||
if (terminalBufferMaxBytes > 0 && cleanBuffer.length > terminalBufferMaxBytes) {
|
||||
cleanBuffer = cleanBuffer.slice(-terminalBufferMaxBytes);
|
||||
truncated = true;
|
||||
const firstNewline = cleanBuffer.indexOf('\n');
|
||||
if (firstNewline > 0 && firstNewline < 4096) {
|
||||
cleanBuffer = cleanBuffer.slice(firstNewline + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
terminalBuffer: cleanBuffer,
|
||||
status: session.status,
|
||||
fullSize,
|
||||
truncated,
|
||||
source,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user