mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
fix(codex): reassemble chunk-split sequences before the strip; mouse parity on replay
Review fixes: - Hold back a trailing partial CSI (digit-only intro, ≤7 chars) in _handleTerminalOutput and prepend it to the next chunk. PTY chunk boundaries are arbitrary, so '\x1b[?1049h' can arrive as '\x1b[?104' + '9h' — the per-chunk strip misses it, xterm obeys the reassembled toggle, and (with the matching ?1049l stripped) stays stuck in the scrollback-less alt buffer until the next replay. Complete sequences are never held; the carry resets with the other buffers in _resetBuffers. - Replay path now also strips mouse-tracking enables (?1000-?1007), matching the live strip: buffers persisted BEFORE the live strip existed can still carry them, and a replayed ?1006h re-hijacks the scroll wheel. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -258,6 +258,9 @@ export class Session extends EventEmitter {
|
||||
private _messages: ClaudeMessage[] = [];
|
||||
private _lineBuffer: string = '';
|
||||
private _lineBufferFlushTimer: NodeJS.Timeout | null = null;
|
||||
// Codex only: trailing partial CSI held back so sequences split across PTY
|
||||
// chunks can't slip past the alt-screen/scrollback strip (see _handleTerminalOutput)
|
||||
private _codexSeqCarry: string = '';
|
||||
private resolvePromise: ((value: { result: string; cost: number }) => void) | null = null;
|
||||
private rejectPromise: ((reason: Error) => void) | null = null;
|
||||
private _promptResolved: boolean = false; // Guard against race conditions in runPrompt
|
||||
@@ -1069,6 +1072,21 @@ export class Session extends EventEmitter {
|
||||
// scrollback intact. Codex's cursor-positioned redraws overwrite only the
|
||||
// cells they actually target, so the non-erased rows keep their content.
|
||||
if (this.mode === 'codex') {
|
||||
// Reassemble sequences split across PTY chunk boundaries first: a chunk
|
||||
// ending mid-sequence ('\x1b[?104' now, '9h' next) would slip past the
|
||||
// strip below and leave xterm stuck in the scrollback-less alt buffer
|
||||
// until the next buffer replay. Hold back an incomplete digit-only CSI
|
||||
// tail (≤7 chars — the longest strippable intro is '\x1b[?1049') and
|
||||
// prepend it to the next chunk; complete sequences are never held.
|
||||
data = this._codexSeqCarry + data;
|
||||
this._codexSeqCarry = '';
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const splitTail = data.match(/\x1b(?:\[\??[0-9]{0,4})?$/);
|
||||
if (splitTail) {
|
||||
this._codexSeqCarry = splitTail[0];
|
||||
data = data.slice(0, -splitTail[0].length);
|
||||
if (!data) return;
|
||||
}
|
||||
data = data
|
||||
// eslint-disable-next-line no-control-regex
|
||||
.replace(/\x1b\[\?(?:47|1047|1049)[hl]/g, '')
|
||||
@@ -1691,6 +1709,7 @@ export class Session extends EventEmitter {
|
||||
this._errorBuffer = '';
|
||||
this._messages = [];
|
||||
this._lineBuffer = '';
|
||||
this._codexSeqCarry = '';
|
||||
this._lastActivityAt = Date.now();
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,12 @@ const LEADING_WHITESPACE_PATTERN = /^[\s\r\n]+/;
|
||||
const ALT_SCREEN_TOGGLE_PATTERN = /\x1b\[\?(?:47|1047|1049)[hl]/g;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const ERASE_SCROLLBACK_PATTERN = /\x1b\[3J/g;
|
||||
// Mouse-tracking enables (X10/button/any-event/UTF-8/SGR/alt-scroll) — once on,
|
||||
// xterm.js forwards wheel events to the app instead of scrolling the viewport.
|
||||
// Live streams are stripped at the source, but buffers persisted BEFORE that
|
||||
// strip existed can still carry them; strip on replay for parity.
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const MOUSE_TRACKING_PATTERN = /\x1b\[\?(?:1000|1001|1002|1003|1005|1006|1007)[hl]/g;
|
||||
|
||||
/**
|
||||
* Strip redundant Ink spinner/status-bar redraw frames from the terminal buffer.
|
||||
@@ -941,7 +947,10 @@ export function registerSessionRoutes(
|
||||
// xterm.js obeys them by switching to its scrollback-less alt buffer and
|
||||
// wiping saved lines, so conversation history disappears on tab switch.
|
||||
if (session.mode === 'codex') {
|
||||
strippedBuffer = strippedBuffer.replace(ALT_SCREEN_TOGGLE_PATTERN, '').replace(ERASE_SCROLLBACK_PATTERN, '');
|
||||
strippedBuffer = strippedBuffer
|
||||
.replace(ALT_SCREEN_TOGGLE_PATTERN, '')
|
||||
.replace(ERASE_SCROLLBACK_PATTERN, '')
|
||||
.replace(MOUSE_TRACKING_PATTERN, '');
|
||||
}
|
||||
|
||||
if (tailBytes > 0 && strippedBuffer.length > tailBytes) {
|
||||
|
||||
Reference in New Issue
Block a user