mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
fix: prevent Chrome freeze and shell feedback delay from flicker filter
Bug 1: Every incoming SSE terminal event reset the 50ms flush timer, not just cursor-up events. During active Claude runs the timer never fired, accumulating MBs in flickerFilterBuffer that froze Chrome on flush. Fix: only reset timer on cursor-up events; add 256KB safety valve. Bug 2: Shell sessions emit cursor-up on every keystroke for readline prompt redraws, triggering the flicker filter and delaying feedback. Fix: skip cursor-up filter for shell mode; disable local echo overlay. Based on PR #31 by @SGudbrandsson. Co-Authored-By: Sigurður Guðbrandsson <SGudbrandsson@users.noreply.github.com>
This commit is contained in:
co-authored by
Sigurður Guðbrandsson
parent
f8b81b8478
commit
1b76e6e2e2
+40
-6
@@ -1097,18 +1097,47 @@ class CodemanApp {
|
||||
// Ink's status bar updates use cursor-up + erase-line + rewrite, which can split
|
||||
// across render frames causing old/new status text to overlap (garbled output).
|
||||
// Buffering for 50ms ensures the full redraw arrives atomically.
|
||||
const hasCursorUpRedraw = /\x1b\[\d{1,2}A/.test(data);
|
||||
//
|
||||
// Shell mode is excluded: shell readline also uses cursor-up for prompt redraws
|
||||
// (e.g. zsh syntax highlighting on every keystroke), and there's no Ink status bar
|
||||
// to protect. Applying the filter in shell mode delays character feedback until the
|
||||
// user stops typing for 50ms, making the terminal feel unresponsive.
|
||||
const isShellMode = session?.mode === 'shell';
|
||||
const hasCursorUpRedraw = !isShellMode && /\x1b\[\d{1,2}A/.test(data);
|
||||
if (hasCursorUpRedraw || (this.flickerFilterActive && !flickerFilterEnabled)) {
|
||||
this.flickerFilterActive = true;
|
||||
this.flickerFilterBuffer += data;
|
||||
|
||||
if (this.flickerFilterTimeout) {
|
||||
clearTimeout(this.flickerFilterTimeout);
|
||||
// Only reset the 50ms timer on cursor-up events (start of a new Ink redraw cycle).
|
||||
// Non-cursor-up events while the filter is active are trailing data from the same
|
||||
// redraw — don't extend the deadline further. Without this guard, a busy Claude
|
||||
// session emitting terminal data faster than SYNC_WAIT_TIMEOUT_MS never flushes,
|
||||
// accumulating MBs in flickerFilterBuffer that freeze Chrome all at once.
|
||||
if (hasCursorUpRedraw) {
|
||||
if (this.flickerFilterTimeout) {
|
||||
clearTimeout(this.flickerFilterTimeout);
|
||||
}
|
||||
this.flickerFilterTimeout = setTimeout(() => {
|
||||
this.flickerFilterTimeout = null;
|
||||
this.flushFlickerBuffer();
|
||||
}, SYNC_WAIT_TIMEOUT_MS); // 50ms buffer window
|
||||
} else if (!this.flickerFilterTimeout) {
|
||||
// Safety: if no timer is running for some reason, ensure we eventually flush.
|
||||
this.flickerFilterTimeout = setTimeout(() => {
|
||||
this.flickerFilterTimeout = null;
|
||||
this.flushFlickerBuffer();
|
||||
}, SYNC_WAIT_TIMEOUT_MS);
|
||||
}
|
||||
this.flickerFilterTimeout = setTimeout(() => {
|
||||
this.flickerFilterTimeout = null;
|
||||
|
||||
// Safety valve: if buffer grew very large (e.g. from a burst before the timer fired),
|
||||
// flush immediately to avoid writing a huge block all at once.
|
||||
if (this.flickerFilterBuffer.length > 256 * 1024) {
|
||||
if (this.flickerFilterTimeout) {
|
||||
clearTimeout(this.flickerFilterTimeout);
|
||||
this.flickerFilterTimeout = null;
|
||||
}
|
||||
this.flushFlickerBuffer();
|
||||
}, SYNC_WAIT_TIMEOUT_MS); // 50ms buffer window
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1238,6 +1267,11 @@ class CodemanApp {
|
||||
} catch { return null; }
|
||||
}
|
||||
});
|
||||
} else if (session.mode === 'shell') {
|
||||
// Shell mode: the shell provides its own PTY echo so the overlay isn't needed.
|
||||
// Disable it by clearing any pending text.
|
||||
this._localEchoOverlay.clear();
|
||||
this._localEchoEnabled = false;
|
||||
} else {
|
||||
// Claude Code: scan for ❯ prompt character
|
||||
this._localEchoOverlay.setPrompt({ type: 'character', char: '\u276f', offset: 2 });
|
||||
|
||||
Reference in New Issue
Block a user