mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 14:39:42 +02:00
fix(terminal): let a Shell pane's scroll-up reach tmux history
A burst of output leaves a Shell pane with about one screen of browser scrollback, because tmux repaints the burst instead of scrolling it, while tmux itself keeps every line. Shell declined the scroll-to-top re-pull other modes use, and the Load full history button renders only once a replay was truncated, so a Shell tab under 1 MiB could not scroll back at all. The scroll gesture now pulls ?full=1&tail=TERMINAL_TAIL_SIZE, the same bound a tab switch loads; the route's existing tail cut marks longer histories 'tail', so the banner still offers the unbounded pull. A window no longer than the browser's buffer is not rewritten. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5ae574374f
commit
9676e90133
+29
-5
@@ -6320,10 +6320,15 @@ class CodemanApp {
|
||||
if (!sessionId || this._fullHistoryRepullInFlight || this._isLoadingBuffer) return;
|
||||
if (this.detachedSessions?.has(sessionId)) return;
|
||||
const session = this.sessions.get(sessionId);
|
||||
// A shell's full capture can be many megabytes. Replaying it from an
|
||||
// ordinary scroll gesture blocks xterm's main thread, so keep that cost
|
||||
// behind the explicit "Load full history" button.
|
||||
if (!force && session?.mode === 'shell') return;
|
||||
// A shell's full capture can be many megabytes, and replaying all of it from
|
||||
// an ordinary scroll gesture blocks xterm's main thread. So a shell scroll
|
||||
// pulls a BOUNDED window of tmux's full history (the same 1 MiB a tab switch
|
||||
// loads, but of the scrollback rather than the visible frame) and the
|
||||
// unbounded pull stays behind the "Load full history" button. Declining
|
||||
// outright left a shell pane about one screen of browser scrollback after any
|
||||
// burst, and the button only renders once a replay was truncated, so a young
|
||||
// shell tab had no way back to output tmux was still holding.
|
||||
const boundedShellPull = !force && session?.mode === 'shell';
|
||||
const now = Date.now();
|
||||
// Momentum scrolling fires this dozens of times per flick, and a burst of new
|
||||
// output is the normal reason to want a re-pull, so cooldown rather than latch.
|
||||
@@ -6336,7 +6341,12 @@ class CodemanApp {
|
||||
this._fullHistoryRepullInFlight = true;
|
||||
try {
|
||||
const requestStartedAt = performance.now();
|
||||
const capture = await this._fetchTerminalCapture(`/api/sessions/${sessionId}/terminal?full=1`, { full: true });
|
||||
const capture = await this._fetchTerminalCapture(
|
||||
boundedShellPull
|
||||
? `/api/sessions/${sessionId}/terminal?full=1&tail=${TERMINAL_TAIL_SIZE}`
|
||||
: `/api/sessions/${sessionId}/terminal?full=1`,
|
||||
{ full: true }
|
||||
);
|
||||
const headersReceivedAt = capture.headersAt;
|
||||
const payload = capture.json?.data ?? {};
|
||||
const bodyParsedAt = performance.now();
|
||||
@@ -6368,6 +6378,20 @@ class CodemanApp {
|
||||
this._setHistoryTruncation(sessionId, { ...payload, exhausted: true });
|
||||
return;
|
||||
}
|
||||
// A bounded window no longer than the browser's buffer buys nothing, and
|
||||
// resetting to rewrite it would jump the viewport on every scroll that
|
||||
// outlasts the cooldown at the top. An untruncated window IS all of tmux's
|
||||
// history, so nothing is missing; a window cut at the tail size would trade
|
||||
// more old rows than it recovers, and its 'tail' truncation keeps the
|
||||
// banner offering the unbounded pull. Not latched as useless: the next
|
||||
// burst of output can put more history in tmux than the browser has.
|
||||
if (
|
||||
boundedShellPull &&
|
||||
this._estimateReplayRows(buffer, this.terminal.cols) <= this.terminal.buffer.active.length
|
||||
) {
|
||||
this._setHistoryTruncation(sessionId, payload);
|
||||
return;
|
||||
}
|
||||
this._setHistoryTruncation(sessionId, payload);
|
||||
this._fullHistoryRepullUseless?.delete(sessionId);
|
||||
const rowsBefore = this.terminal.buffer.active.length;
|
||||
|
||||
@@ -3304,9 +3304,9 @@ Object.assign(CodemanApp.prototype, {
|
||||
/**
|
||||
* Post-scroll companion to _noteTerminalUserScroll: hitting the TOP of the
|
||||
* buffer while scrolling up gives the app a chance to pull the rest of tmux's
|
||||
* scrollback (issue #205, see _maybeRefetchFullHistory). Shell sessions decline
|
||||
* automatic pulls because their captures can be large; their banner button is
|
||||
* the explicit path. Must be called AFTER scrollLines(), since the check is on
|
||||
* scrollback (issue #205, see _maybeRefetchFullHistory). Shell sessions pull a
|
||||
* bounded window because their captures can be large; their banner button is
|
||||
* the unbounded path. Must be called AFTER scrollLines(), since the check is on
|
||||
* the resulting position, and it is deliberately not folded into
|
||||
* _noteTerminalUserScroll for exactly that reason.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user