Agent chat: the gutter beside every part of the floating stack scrolls the transcript

Merged
TE
15d ago
+434-15

Summary

In the agent chat, when the bottom stack floats over the transcript, the wheel in the gutter beside the composer's card already scrolls the transcript (Leo 2026-10-20: "if it's not in the composer it should just scroll"). Beside the stack's other parts it did not. The question card, the pull request dock, error cards and the agents pill are bands as wide as the pane, with their card inset. The wheel in a band's gutters or empty room hit the band and scrolled nothing.

The fix keeps the bands catching the pointer. The transcript rows under the stack are only masked (.acpmux-scroll[data-docked]), so a band that passed the pointer through would let a hover, click or drag in its empty room reach rows no one can see: link hover cards, hidden links, tool rows, text selection. Instead, wheelPassThrough.ts forwards a wheel whose target is a band itself (a direct child of the floating .acpmux-dock, not the composer and not the dictation notice) to the transcript, the way it already forwards the wheel over Jump to latest. It does this only when the band can't scroll itself (scrollHeight <= clientHeight), so a pull request dock or question card that overflows in a short pane keeps its own wheel, and a wheel there never scrolls both. Because the bands take the pointer, those two parts' scrollbars can be dragged again. The composer is unchanged: it passes the pointer through around its card and queue.

Changelog

  • Fixed: In an agent chat, scrolling beside the question card, the pull request bar, an error card or the agents pill now scrolls the conversation.

Testing

CMUX_BROWSER_TESTS=1 bun test test/agent-pane-dock-gutters.test.ts (CI web / react-apps-check, Chromium and WebKit). One chat shows every part of the stack at once:

  • the question (a pending permission)
  • the pull request dock (3 PRs)
  • an error card (Edit CLAUDE.md, which the browser host refuses)
  • the agents pill (2 running subagents)
  • the queue (2 prompts) and the composer's card
  • later in the test, the dictation notice

For each part, the pointer 8 px left of the card reaches the band (for the composer's queue and card, the transcript) and the wheel there scrolls the transcript both ways. The same holds in the empty room right of a PR chip and right of the agents pill, where elementFromPoint must not be inside .acpmux-scroll. The wheel over a card moves nothing. The agents list dialog takes the pointer. In a 440 px pane, the wheel over the overflowing PR dock scrolls only the dock, and for each overflowing part the point 3 px in from the band's right edge (its scrollbar) reaches the band.

  • First approach (bands pass the pointer through), test-only head 1d4d573a9be: red in both engines. Fixed at 3b3c868da4c (PASS). Review found the hidden-row problem above.
  • Test for the reviewed approach, test-only head 9895107d616: red in both engines:
    • The gutter beside the question card reached transcript, not band (line 250).
    • The pill's gutter with its list open reached transcript (line 309).
    • The PR dock's scrollbar reached transcript (line 342).
  • Fix 02fff408cf0, then test-only follow-ups at 8ea0f9aa647 (the question card asserts only that the transcript stays still) and 560891d7f66 (each wheel waits until the transcript holds still, after a WebKit run measured a glide that was still moving). On 560891d7f66: PASS test/agent-pane-dock-gutters.test.ts (react-apps-check shard 3, both engines).

Observed / Not run

Observed: the CI logs above. On 02fff408cf0 the two main cases passed in both engines. The short-pane case failed only on the question card's own scroll: the wheel over its rows moves neither the card nor the transcript. Wheels over a band's contents are handled the same way as on base, so this isn't caused by this PR. The test now asks only the PR dock to scroll alone, and asks the question card only that the transcript stays still and its scrollbar takes the pointer.

On 560891d7f66, the remaining react-apps-check reds are secret-card and timeouts in composer-draft-wrap and composer-row-layout. Earlier heads also had reds in files this PR doesn't touch:

  • agent-pane-scrollbar-full-height
  • gallery-agent-pane-entries, gallery coverage
  • pane-english (run-board)
  • the + is its glyph and Add tooltip cases
  • pet picker and Chiefs list cases
  • the motion categories case
  • timeouts in composer-draft-wrap, composer-type-scale and composer-row-layout

Not run: nothing ran locally; browser tests run only on CI. The recap card (behind a Debug toggle) and the folder-choice, worktree and btw notices (shown only before a chat starts or with side sessions on) aren't in the fixture. They are bands of the same shape and get the same handling. Dragging a scrollbar is checked only through elementFromPoint, not with a real drag.

Agent session: cosmic-gray

🤖 Generated with Claude Code