fix: prevent Chrome freeze and shell feedback delay from flicker filter#31
Closed
SGudbrandsson wants to merge 1 commit intoArk0N:masterfrom
Closed
fix: prevent Chrome freeze and shell feedback delay from flicker filter#31SGudbrandsson wants to merge 1 commit intoArk0N:masterfrom
SGudbrandsson wants to merge 1 commit intoArk0N:masterfrom
Conversation
Two related bugs in batchTerminalWrite's cursor-up flicker filter: **Bug 1 — Chrome freeze during active Claude sessions** The cursor-up flicker filter exists to batch Ink's status bar redraws atomically. When a cursor-up sequence arrives, a 50ms flush timer is set. The bug: every subsequent SSE terminal event — including non-cursor-up data — also reset the 50ms timer. During an active Claude run, the server emits terminal data faster than 50ms continuously, so the timer never fired. flickerFilterBuffer accumulated the full session output (potentially MBs). When Claude went idle, the timer fired and flushed everything to terminal.write() in one synchronous call, freezing Chrome. Fix: only reset the 50ms timer on cursor-up events (start of a new Ink redraw cycle). Non-cursor-up data while the filter is active is buffered without extending the deadline. Added a 256KB safety-valve to force-flush if a burst fills the buffer before the timer fires. **Bug 2 — Shell mode shows no character feedback until Enter** Shell sessions (bash/zsh with readline) also emit cursor-up on every keystroke for prompt redraws (syntax highlighting, right-side prompts). The filter treated these as Ink status bar redraws and reset the 50ms timer on each character typed, so nothing appeared until the user stopped typing for 50ms — making the terminal feel completely unresponsive. Fix: shell mode sessions bypass the cursor-up filter entirely (there is no Ink status bar to protect). The local echo overlay is also disabled for shell sessions since the shell handles its own PTY echo and the overlay was looking for the ❯ Claude prompt character which doesn't exist in shell prompts.
Ark0N
added a commit
that referenced
this pull request
Mar 4, 2026
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>
Owner
|
Thanks for this excellent contribution, @SGudbrandsson! Both bugs were real and well-analyzed:
Applied your changes directly to master in 1b76e6e (with co-author credit). Closing this PR — thanks again for the thorough investigation! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two related bugs in
batchTerminalWrite's cursor-up flicker filter.Bug 1 — Chrome tab freeze during active Claude sessions
The cursor-up flicker filter batches Ink's status bar redraws atomically: when a
\x1b[Acursor-up sequence arrives, a 50ms flush timer is set, and all incoming data is buffered until the timer fires.The bug: every incoming SSE terminal event reset the 50ms timer — not just cursor-up events. During an active Claude run the server emits terminal data continuously (tool calls, spinner updates, output), so the timer kept resetting and never fired.
flickerFilterBufferaccumulated the full session output, potentially growing to several MBs over a long run. When Claude finally went idle, the 50ms timer fired and flushed everything toterminal.write()in a single synchronous call — freezing Chrome until the xterm.js parser finished.Bug 2 — Shell mode shows no character feedback until Enter/pause
Shell sessions (bash/zsh with readline) emit cursor-up on every keystroke for prompt redraws (syntax highlighting, right-side prompts, etc.). The filter treated these the same as Ink status bar redraws and reset the 50ms timer on each character typed. Nothing appeared until the user stopped typing for 50ms, making the terminal feel completely unresponsive.
Fix
Bug 1: Only reset the 50ms flush timer when a cursor-up event arrives (the start of a new Ink redraw cycle). Non-cursor-up data that arrives while the filter is active is buffered without extending the deadline. Added a 256KB safety-valve to force-flush immediately if a burst fills the buffer before the timer fires.
Bug 2: Shell mode sessions (
mode === 'shell') bypass the cursor-up filter entirely — there is no Ink status bar to protect in a plain shell. The local echo overlay is also disabled for shell sessions (it was looking for the❯Claude prompt character which doesn't exist in shell prompts; the shell handles its own PTY echo).Changes
src/web/public/app.js—batchTerminalWriteand_updateLocalEchoState:hasCursorUpRedrawis nowfalsefor shell mode sessionsif (hasCursorUpRedraw)guard; a fallback timer is set if none is running_updateLocalEchoStateadds ashellbranch that clears and disables the overlay