mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
Merge pull request #117 from aakhter/pr/cod-86-codex-frontend
fix(codex): smaller first-frame write budget + scroll-up grace for codex
This commit is contained in:
@@ -15,6 +15,10 @@
|
||||
(function (global) {
|
||||
const TERMINAL_QUERY_RESPONSE_PATTERN = /^\x1b\[[\?>=]?[\d;]*[cnR]$/;
|
||||
const TERMINAL_OSC_RESPONSE_PATTERN = /^\x1b\][\d;]*[^\x07\x1b]*(?:\x07|\x1b\\)$/;
|
||||
// Grace window after a manual scroll-up gesture during which sticky-scroll is
|
||||
// suppressed, so high-frequency Codex status redraws don't snap the viewport
|
||||
// back to the bottom while the user is inspecting earlier output.
|
||||
const USER_SCROLL_STICKY_SUPPRESS_MS = 1500;
|
||||
|
||||
function isTerminalQueryResponse(data) {
|
||||
return TERMINAL_QUERY_RESPONSE_PATTERN.test(data) || TERMINAL_OSC_RESPONSE_PATTERN.test(data);
|
||||
@@ -27,6 +31,7 @@
|
||||
global.CodemanTerminalInput = {
|
||||
isTerminalQueryResponse,
|
||||
shouldSuppressTerminalQueryResponse,
|
||||
USER_SCROLL_STICKY_SUPPRESS_MS,
|
||||
};
|
||||
})(window);
|
||||
|
||||
@@ -302,6 +307,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
const lines = Math.round(ev.deltaY / 25) || (ev.deltaY > 0 ? 1 : -1);
|
||||
this._noteTerminalUserScroll(lines);
|
||||
this.terminal.scrollLines(lines);
|
||||
},
|
||||
{ passive: false }
|
||||
@@ -376,6 +382,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
const ch = cellHeight();
|
||||
const lines = Math.trunc(pixelAccum / ch);
|
||||
if (lines !== 0) {
|
||||
this._noteTerminalUserScroll(lines);
|
||||
this.terminal.scrollLines(lines);
|
||||
pixelAccum -= lines * ch;
|
||||
}
|
||||
@@ -428,6 +435,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
this._chunkedWriteGen = 0;
|
||||
this._bufferLoadSeq = 0;
|
||||
this._bufferLoadOwner = null;
|
||||
this._lastUserScrollUpAt = null;
|
||||
|
||||
// Handle resize with throttling for performance
|
||||
this._resizeTimeout = null;
|
||||
@@ -1364,6 +1372,22 @@ Object.assign(CodemanApp.prototype, {
|
||||
return buffer.viewportY >= buffer.baseY - 2;
|
||||
},
|
||||
|
||||
// Record manual scroll gestures so sticky-scroll can give an upward scroll a
|
||||
// short grace window (see _hasRecentUserScrollUp). A downward scroll that
|
||||
// lands back at the bottom clears the suppression immediately.
|
||||
_noteTerminalUserScroll(lines) {
|
||||
if (lines < 0) {
|
||||
this._lastUserScrollUpAt = performance.now();
|
||||
} else if (this.isTerminalAtBottom()) {
|
||||
this._lastUserScrollUpAt = null;
|
||||
}
|
||||
},
|
||||
|
||||
_hasRecentUserScrollUp() {
|
||||
if (typeof this._lastUserScrollUpAt !== 'number') return false;
|
||||
return performance.now() - this._lastUserScrollUpAt < window.CodemanTerminalInput.USER_SCROLL_STICKY_SUPPRESS_MS;
|
||||
},
|
||||
|
||||
batchTerminalWrite(data) {
|
||||
// If a buffer load (chunkedTerminalWrite) is in progress, queue live events
|
||||
// to prevent interleaving historical buffer data with live SSE data.
|
||||
@@ -1615,8 +1639,16 @@ Object.assign(CodemanApp.prototype, {
|
||||
|
||||
// Per-frame byte budget to prevent main thread blocking.
|
||||
// Large writes (141KB+) can freeze Chrome for 2+ minutes.
|
||||
const MAX_FRAME_BYTES = 65536; // 64KB budget per frame
|
||||
// Codex's TUI emits dense synchronized redraws during thinking/high-effort
|
||||
// phases, so it gets a smaller first frame to keep per-frame xterm/WebGL
|
||||
// stalls short; other modes keep the larger 64KB budget.
|
||||
const activeSession = this.activeSessionId && this.sessions ? this.sessions.get(this.activeSessionId) : null;
|
||||
const MAX_FRAME_BYTES = activeSession?.mode === 'codex' ? 32768 : 65536;
|
||||
let deferred = false;
|
||||
// If the user recently scrolled up, remember the viewport so we can restore
|
||||
// it after the write — Codex status redraws would otherwise jump it.
|
||||
const preserveViewportY =
|
||||
this._hasRecentUserScrollUp() && this.terminal.buffer?.active ? this.terminal.buffer.active.viewportY : null;
|
||||
|
||||
if (_joinedLen <= MAX_FRAME_BYTES) {
|
||||
this.terminal.write(joined);
|
||||
@@ -1633,6 +1665,13 @@ Object.assign(CodemanApp.prototype, {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (
|
||||
preserveViewportY !== null &&
|
||||
this.terminal.buffer?.active?.viewportY !== preserveViewportY &&
|
||||
typeof this.terminal.scrollToLine === 'function'
|
||||
) {
|
||||
this.terminal.scrollToLine(preserveViewportY);
|
||||
}
|
||||
const bytesThisFrame = deferred ? MAX_FRAME_BYTES : _joinedLen;
|
||||
const _dt = performance.now() - _t0;
|
||||
if (_dt > 100 || deferred)
|
||||
@@ -1640,8 +1679,11 @@ Object.assign(CodemanApp.prototype, {
|
||||
`[CRASH-DIAG] flushPendingWrites: ${_dt.toFixed(0)}ms, ${(bytesThisFrame / 1024).toFixed(0)}KB written${deferred ? ', rest deferred' : ''} (total ${(_joinedLen / 1024).toFixed(0)}KB)`
|
||||
);
|
||||
|
||||
// Sticky scroll: if user was at bottom, keep them there after new output
|
||||
if (this._wasAtBottomBeforeWrite) {
|
||||
// Sticky scroll: if user was at bottom, keep them there after new output.
|
||||
// Give manual scroll-up gestures a short grace window so high-frequency
|
||||
// Codex status ticks do not snap the viewport back while the user is
|
||||
// trying to inspect earlier output.
|
||||
if (this._wasAtBottomBeforeWrite && !this._hasRecentUserScrollUp()) {
|
||||
this.terminal.scrollToBottom();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user