Skip to content

Render streaming assistant rows outside virtualizer - #11153

Merged
imanolmzd-svg merged 5 commits into
mainfrom
imanol/improve-virtualized-scroll
Jun 12, 2026
Merged

Render streaming assistant rows outside virtualizer#11153
imanolmzd-svg merged 5 commits into
mainfrom
imanol/improve-virtualized-scroll

Conversation

@imanolmzd-svg

Copy link
Copy Markdown
Contributor

Why

We have recently changed the virtualization strategy to go from "per turn" to "per row" in #11094 and this caused the row being streamed to be virtualized too - Causing display issues as shown in the "Before" video below

Implementation

Replace the keepMounted index-based approach with a direct rendering suffix for active assistant turns. Rows belonging to the streaming turn are partitioned out of the virtualizer and rendered as plain DOM elements, eliminating measurement instability caused by rapidly growing content while preserving virtualized history for completed turns.

Screenshots / Video

Before

before-row-virt.mov

After

after-row-virt.mov

Testing

  • Manual testing as shown above
  • Unit tests
  • Typecheck

…revent layout jumps

Replace the `keepMounted` index-based approach with a direct rendering
suffix for active assistant turns. Rows belonging to the streaming turn
are partitioned out of the virtualizer and rendered as plain DOM elements,
eliminating measurement instability caused by rapidly growing content
while preserving virtualized history for completed turns.
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
@kilo-code-bot

kilo-code-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental Review

Reviewed changes since 8c3fb65594bcecaaca2483c20be213e98b92de49358c312be3fa4e940f1a8eafd49616b9c9e7156d.

New commit: refactor(transcript-rows): restrict direct partition to only the latest visible turn

The refactored partitionRows logic correctly prevents older streaming turns from being rendered in the direct (non-virtualised) suffix when a newer visible turn exists. Using visible.at(-1)?.turn to identify the candidate turn, then gating on direct.has(turn), is clean and well-scoped. The boundary scan at line 192 still requires row.type === "assistant", so a last-visible turn that has no assistant output safely falls back to fully virtual — confirmed by the existing "skips a held turn without assistant output" test.

The updated test ("does not retain an older turn after a newer visible turn") precisely captures the new invariant and the expected partition output is correct.

Files Reviewed (2 files, incremental)
  • packages/kilo-vscode/webview-ui/src/context/transcript-rows.ts — logic change is correct; edge cases handled
  • packages/kilo-vscode/tests/unit/transcript-rows.test.ts — test rename and updated expectations match the new behaviour

Reviewed by claude-4.6-sonnet-20260217 · 242,250 tokens

Review guidance: REVIEW.md from base branch main

…t.tsx

Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
@imanolmzd-svg
imanolmzd-svg enabled auto-merge (squash) June 12, 2026 08:37
Comment thread packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx Outdated
@imanolmzd-svg
imanolmzd-svg disabled auto-merge June 12, 2026 08:39
…st visible turn

Simplify partitionRows so that older streaming turns are no longer
retained in the direct (non-virtualized) output once a newer turn
becomes visible. The lookup now checks only the final row's turn
against the direct set, ensuring completed assistant turns move back
into the virtualizer immediately when the conversation advances.
@imanolmzd-svg

Copy link
Copy Markdown
Contributor Author

Benchmark results

I benchmarked this PR at 358c312b against base 68fd8ccc in fresh isolated VS Code profiles

The scenario used a long active turn with auto-scroll paused, followed by a queued successor turn. This specifically exercised the handoff where the completed older turn should return to the virtualizer and only the latest assistant turn should render directly.

Metric Base PR Change
Layout-shift events 209 147 -29.7%
Total layout-shift score 2.126 1.846 -13.1%
Unexpected shift score 1.305 1.148 -12.0%
Visible-anchor movement 852 px 717 px -15.8%
Maximum anchor jump 95 px 103 px +8.4%
Script time per mutation 0.159 ms 0.167 ms +5.0%
Task time per mutation 0.665 ms 0.731 ms +9.9%
Long tasks 0 0 unchanged

The partition behavior matched the intended design:

  • While the successor was queued, the active turn remained directly rendered.
  • At handoff, data-direct-count dropped to 0, returning the completed turn to virtualization.
  • Once the new assistant output appeared, data-direct-count became 1 and contained only the latest turn.
  • No older turn remained directly rendered after the newer turn became visible.

Overall, the PR substantially reduced layout-shift frequency, cumulative shift score, and visible-anchor movement. There was a modest scripting/task-time increase, but no long tasks or observable responsiveness regression.

@imanolmzd-svg
imanolmzd-svg merged commit 7955f86 into main Jun 12, 2026
21 checks passed
@imanolmzd-svg
imanolmzd-svg deleted the imanol/improve-virtualized-scroll branch June 12, 2026 10:16
t7tran pushed a commit to t7tran/kilocode that referenced this pull request Aug 14, 2026
* fix(vscode): render streaming assistant rows outside virtualizer to prevent layout jumps

Replace the `keepMounted` index-based approach with a direct rendering
suffix for active assistant turns. Rows belonging to the streaming turn
are partitioned out of the virtualizer and rendered as plain DOM elements,
eliminating measurement instability caused by rapidly growing content
while preserving virtualized history for completed turns.

* Update packages/kilo-vscode/webview-ui/src/components/chat/MessageList.tsx

Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>

* fix(vscode): complete direct row ID rename

* refactor(transcript-rows): restrict direct partition to only the latest visible turn

Simplify partitionRows so that older streaming turns are no longer
retained in the direct (non-virtualized) output once a newer turn
becomes visible. The lookup now checks only the final row's turn
against the direct set, ensuring completed assistant turns move back
into the virtualizer immediately when the conversation advances.

---------

Co-authored-by: kilo-code-bot[bot] <240665456+kilo-code-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants