Repository navigation
fix(vscode): keep session header height stable while streaming - #14154
Conversation
The header grew in steps as a turn streamed: the activity timeline appeared, then the context bar, then the token line, which also jumped height when the async model usage replaced the plain row with a collapsible trigger. Each step pushed the transcript down. Show the graph section as soon as the header is visible and keep every row at its final height, so the timeline, context bar, and token counts fill in place without moving the transcript.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Incremental pass over commit Previous Review Summaries (5 snapshots, latest commit 8c22dd4)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 8c22dd4)Status: No Issues Found | Recommendation: Merge Files Reviewed (13 files)
Incremental pass over commit Previous review (commit fdacb1c)Status: No Issues Found | Recommendation: Merge Files Reviewed (5 files)
Previous review (commit 39e935e)Status: No Issues Found | Recommendation: Merge Files Reviewed (5 files)
Previous review (commit 121b2ab)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (4 files)
The incremental commit Fix these issues in Kilo Cloud Previous review (commit f77ae0b)Status: 3 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (4 files)
Verified the skeleton geometry against Reviewed by deepseek-v4.1-flash · Input: 56.5K · Output: 7.1K · Cached: 354.2K Review guidance: REVIEW.md from base branch |
Review feedback: the skeleton placeholders could persist forever. The timeline skeleton only cleared once an assistant part arrived, and the context skeleton never cleared for models without a context limit, so both kept animating on idle sessions. Gate the graph section on a running turn or timeline data, and show each skeleton only while a turn is in flight. Without a context limit the context row stays hidden, as it did before. Add a Storybook story for the first-turn loading state so the skeleton rows get a visual regression baseline.
Gating the graph section on the turn running made the header 85px taller when a turn started, which moved the transcript on every turn boundary. session-dock-stability.spec.ts asserts that swap keeps the transcript still, and it failed on CI. Render the graph section whenever the header is shown so its height is constant, and keep the skeletons as loading states that only render while a turn is running. Rows without data stay empty instead of animating forever. Give the TaskHeader stories visible messages, parts, and token counts so they render the loaded header. Without them the fixtures showed loading skeletons, which also changed the task-todo-list docs screenshot.
The visual regression job renders this story slightly differently on each run, so its baseline keeps flipping by a few pixels without any source change and the auto-update guard fails the job. Its baseline already changed in earlier baseline-only commits on main. The story renders only DiffPanel and is unrelated to the session header, so skip it like the other non-deterministic diff-panel stories.
What Problem This Solves
The session header grew in steps while a turn streamed. The activity timeline only mounted once an assistant part existed, the context bar only mounted once token counts and a model limit resolved, and the token line only mounted once usage resolved. The token line also changed height when async per-model usage arrived and swapped the plain row for a collapsible trigger. Each step pushed the transcript down, so the header visibly skipped during the first seconds of a turn.
Why This Change Was Made
The header rows had independent mount conditions but no reserved space, so partial data produced partial layout.
The graph section now always reserves the height of all three rows, so the header has one constant height whenever it is shown and the transcript does not move. The skeleton rows are loading states only: each one renders while a turn is running, and a row with no data stays empty instead. Without a configured context limit the context row stays empty, as it did before, instead of animating forever.
Reserving the height rather than adding it when the turn starts also preserves the existing invariant in
session-dock-stability.spec.ts, where the transcript viewport and the last message position must not move across the working/idle swap. Gating the section on the turn running failed that spec by 85px.The token row normalization also removes a pre-existing inconsistency: the plain row and the collapsible trigger rendered at different heights (16px vs 28px), which caused a shift even after all data had loaded.
User Impact
The session header settles at its final height as soon as a session has a message, and the timeline, context bar, and token counts fill in place while a turn runs. The transcript no longer moves on a turn boundary. While a turn is running, rows that are still loading show skeleton rows rather than an empty block or a partially drawn header.
Evidence
Skeleton state while the first turn streams (timeline bars, context track, token row):
Checks, all run in
packages/kilo-vscode:bun run typecheck: 0 errorsbun run lint: cleanbun run build-storybook: buildsbunx playwright test: 127 passed, 18 skipped (the skipped ones are the Linux-only screenshot comparisons)The new
TaskHeader — loading, first turnstory renders the skeleton rows with only a user message, giving the visual regression suite a baseline for this state. Its graph section is 85px, with 5 timeline bars, the context track, and 3 token pills.Limitations: the streaming state was not reproduced in an isolated VS Code self-test because it requires a paid model request. The state in the screenshot comes from normal use.