Fix iPhone history lag: cap web client history buffers and coalesce renders#255
Open
serrebidev wants to merge 1 commit into
Open
Fix iPhone history lag: cap web client history buffers and coalesce renders#255serrebidev wants to merge 1 commit into
serrebidev wants to merge 1 commit into
Conversation
…enders On iPhone, tapping a choice grew to 10-20s of lag after a session ran a while (desktop unaffected). Root cause is the web client's history system: - store.addHistory() pushed to buffers with no cap, and every non-"all" line is also mirrored into the default-visible "all" buffer, so buffers grew with the entire session. - history.js render() runs on every notify() and rebuilds the whole <textarea> via join() plus a forced reflow. One tap emits a burst of speak/chat lines, each firing notify(), so cost per tap is O(lines added * total history) and grows all session. - Mobile is worst hit: it also shows the growing #history-log <p> list that desktop hides, on slower hardware. Fix: - store.js: cap each history buffer at 500 lines (FIFO trim from front). - ui/history.js: coalesce renders into one requestAnimationFrame, skip the DOM work when the visible buffer is unchanged, and rebuild the capped log cleanly (the old incremental append broke once the buffer front-trimmed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
On iPhone, tapping a menu choice grows to 10–20s of lag after a session has run for a while. Desktop is unaffected.
Root cause is the web client's history system:
store.addHistory()pushes to history buffers with no cap, and every non-allline is also mirrored into theallbuffer (the default visible one) — so buffers grow with the entire session.history.jsrender()runs on everynotify()and rebuilds the whole<textarea>vialines.join("\n")plus a forced reflow (scrollTop = scrollHeight). A single tap emits a burst ofspeak/chatlines, each firing its ownnotify(), so cost per tap ≈ (lines added) × (total history) — quadratic and growing all session.renderMobileVisibility()shows the growing#history-log<p>list that desktop hides, so phones pay for both the textarea rebuild and an unbounded visible DOM, on slower hardware.Fix
store.js— cap each history buffer at 500 lines (FIFO trim from the front). Bounds memory and makes every renderO(cap)instead ofO(session). History navigation indexes from the end, so front-trimming never disturbs scrollback position.ui/history.js— coalesce renders into onerequestAnimationFrame(a K-line burst → 1 render, not K), skip the DOM work entirely when the visible buffer is unchanged (keeps menu navigation cheap), and rebuild the capped log from scratch (the old count-based incremental append silently broke once the buffer trimmed from the front).Testing
allmirror capped, direct-allpath not double-pushed): pass.tests/markdown_viewer_security.test.mjs): 2/2 pass.Notes
version.jsin this PR to avoid merge conflicts in the integration branch; asset freshness is already handled by the server'sCache-Control: no-cache, must-revalidate+ ETag.🤖 Generated with Claude Code