Agent chat: growing the composer never moves the transcript or its scrollbar
Summary
Typing a newline in the agent chat's composer (Shift-Return, or a soft wrap) made the transcript's scrollbar jump (Leo, 2026-10-20).
Root cause: the bottom stack floats over the transcript's foot, and VirtualTranscript keeps room under the last row equal to the stack's live height (rowsHeight = rows + THREAD_TOP + dockSpace). Every composer line therefore changed the scroller's content height. Scrolled up, the reader's offset and rows stayed put, but scrollHeight grew by a line each time, so the scrollbar's thumb moved and shrank on each newline (and WebKit flashes the overlay scroller on a content resize). The full-height minimap, sized from height - dockSpace, rescaled every mark on each line too.
Fix (App.tsx): the room under the last row follows the stack only while the view follows the latest row. Scrolled up, it stays as it was, so the scroller's offset, height and scrollbar don't change, and neither does the minimap. Back at the latest row, the room is the stack's again and the last row sits above the grown card. A followed view still stays at its end on every frame as the composer grows, as before. Jump to latest, the mask under the stack, and scroll-into-view still use the stack's live height.
Follow-up to #22819 (bottom stack is a no-scroll zone), built on #22272 and #22858.
Changelog
Fixed: In agent chat, typing a newline in the composer while scrolled up no longer moves the transcript's scrollbar.
Testing
webviews/test/agent-pane-scrollbar-full-height.test.tshas a new case, "a composer that grows leaves a reader scrolled up where they were, scrollbar and all, on every frame". It scrolls up, grows the composer by four Shift-Return lines and a wrapped line, and samples every painted frame:scrollTop,scrollHeightandclientHeightstay unchanged. It then scrolls back to the end and checks that the latest row sits above the grown card. The existing case "a composer that grows keeps the followed end above it on every frame" covers the pinned view (distance from end stays 0 on every frame).- I ran these on huge-blue (Linux, Playwright Chromium;
CMUX_BROWSER_TESTS=1). The new case fails on base (scrollHeightgrew on every line) and passes with the fix, twice. Related suites pass with the fix: agent-pane-bottom-stack, chat-column, follow-live, pr-dock, queue-steer, thread-minimap-, thread-scroll-map, transcript-scroll-restore. - agent-pane-stream-jitter "webkit: scrolling up while a reply streams is never pulled back" fails the same way on base on that host. agent-pane-wheel-pass-through hit a beforeEach timeout on that host on base too.
tsc --noEmit,vp fmt --checkandvp lintpass on the changed files.
Observed / Not run
- Not run on cosmic-gray (too loaded), and not run in the macOS app's WKWebView. CI's browser lane is the check.
Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes agent chat so growing the composer (newlines or soft wraps) no longer moves the transcript's scrollbar when the reader is scrolled up.
- The room kept under the last row for the floating bottom stack was the stack's live height, so every composer line changed the scroller's content height.
- The room now follows the stack only while the view follows the latest row; scrolled up, it stays as it was, leaving the scrollbar and minimap unchanged.
- Back at the latest row, the room is the stack's again and the latest row sits above the grown card; a followed view still stays at its end.