Repository navigation
Conversation
Streaming updates were rebuilding every history row. Cache each entry's rendered rows and keep those elements memoized so only the live message commits again. Co-authored-by: tt-a1i <tt-a1i@users.noreply.github.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.
Fixes #872
Problem
After #870 removed the history signature from streaming updates, a streaming token still re-rendered every history row in the Web transcript. Issue #872 records the headless Chrome measurement on 1000 messages of 1000 characters: React commit
actualDurationstayed around 170ms median and input-to-frame around 220ms. That work repeats for unchanged history, so scroll, selection, and find all pay for rows that did not change. Related: #837, #870, #841.Value
A live token should update the message that is actually growing. Unchanged history keeps its DOM, text selection, and findable copy. Tool output that arrives later still refreshes the call that owns it.
Approach
Each transcript entry caches the rows it produced, keyed by the inputs those rows close over (message identity, highlight, tool results and live tool state, turn, and the latest user prompt). A streaming update reuses the cached elements for unchanged entries and wraps them in a memoized frame, so those rows do not render again. The live entry misses the cache and renders. The row list, process groups, and turn structure stay the same DOM shape, so scroll following, selection, and in-page find keep their anchors.
List virtualization is not added. Memoized rows are enough to stop history renders, and a virtual window would risk the selection and find behavior the transcript already preserves.
Validation
bun run checkbun run test ui(100 files, 1534 tests)tests/web/transcript-live-row-render.spec.tschecks visible history and live text, that the history DOM node stays the same node, that a changed history row renders, that an unchanged row before or after it does not, and that a new tool result appears on its call without rendering an unrelated history row. The render counter is the row component itself, so removing the memo fails the test. No timers, retries, or widened timeouts.Headless Chrome 1280×800, Vite, the real
Transcript, history messages of 1000xcharacters, stableonResend. Each run records 20 streaming commits plus 12 input-to-frame samples (click to the second animation frame) andlongtaskentries.This machine did not reproduce the 170ms / 220ms figures in #872. Markdown rows were already able to bail out at the leaf, so the unthrottled baseline here is a few milliseconds. The render count is the direct evidence that history rows stop rendering.
c2aa6d3)c2aa6d3)Input-to-frame sits on a ~33ms floor because the sample waits for two frames. At 1000 rows the steady commits fell from about 9ms to about 4.5ms and the long tasks on the first updates disappeared. At 250 rows the commit median did not fall; reconciling the memoized list costs more than re-running those already-cheap rows on this host. The 1000-row case is the one #872 measured.
Impact