Agent chat: the composer keeps a thin scrollbar while its draft overflows

Merged
TE
16d ago
+287-3

Leo, 2026-10-21: "STOP HIDING THEN SHOWING THEN HIDING THE SCROLLBAR. IF THERE ARE THINGS TO SCROLL, JUST KEEP THE SCROLLBAR IN THE COMPOSER, LIKE CLAUDE DOES."

The agent chat's prompt (.acpmux-md-field) scrolled with the engine's own scroller. WebKit's overlay scroller shows while the field scrolls and fades out a moment later, so a long draft's scrollbar flashed on every line typed and went away when you stopped.

Now the prompt has the shared minimal scroller: MINIMAL_SCROLLER_ATTRIBUTE (data-cmux-minimal-scroller) and MINIMAL_SCROLLER_CSS in webviews/src/scrollers.ts, installed with the other shared scroller rules by installScrollers. It draws a thin thumb (--cmux-scroller-size, --cmux-scroller-thumb) with no track, the whole time the content overflows. Typing, focus, hover and resize never fade it. Its track stops --cmux-minimal-scroller-inset short of each end, clear of the card's rounded corners. A draft that fits has no scrollbar (overflow: auto). The style uses theme tokens only.

This is the smallest fix, the composer only. The transcript and the sidebar move to the same style, and an opt-in auto-hide variant gets added, in follow-ups (chat-load and sidebar-rows lanes). Other code can opt in by putting the attribute on its scroll container.

Base merge and the reserved lane (orphan-sweep, head ee2217df9ff)

After one merge of feat-cmux-next, composer-draft-wrap.test.ts failed in WebKit at 300 px: the minimal scroller is a classic WebKit scrollbar, so when a draft first overflowed its 8 px lane took layout width and the text rewrapped (8 lines became 9, and the caret left the view). The minimal scroller now reserves its lane (scrollbar-gutter: stable), and the agent composer takes that lane out of its right padding in both looks, so the draft keeps base's width whether or not it overflows. The lane stays empty while the draft fits. The Home composer (pages/home-channels/styles.css) uses the same field and does not adjust its padding, so its text area is 8 px narrower than before.

Test

webviews/test/composer-persistent-scrollbar.test.ts, in Chromium (scrollbars on) and WebKit. It pastes past the prompt's eight lines, then reads the scrollbar lane after every painted frame (cmuxAfterPaint) and samples the drawn thumb through typing, a 2.5 s idle, blur, focus and the pointer leaving. Every frame must have a thin lane (1 to 10 px) and every sample a thumb with no track. Then it cuts the draft back to one line and expects no scrollbar on any frame.

cmux-ci run --class light --script scripts/ci/run-webviews-tests.sh --env CMUX_BROWSER_TESTS=1 --env CMUX_WEB_TEST_FILES=test/composer-persistent-scrollbar.test.ts

Observed / Not run

  • Observed: the fleet runs above. cmux-ci compile-check --app passed on the head. scripts/web-lint.sh and prepush-gate.py ran locally. The web lint's only reds come from the sparse worktree (no Packages/, no cmux-tui/, no generated strings), and the same checks are red on base in that checkout. None of them is in a changed file.
  • Not run: a native capture. No capture: a webview CSS change; the gallery compare covers it.

Changelog

  • Fixed: The agent chat composer keeps a thin scrollbar visible while a long draft overflows, instead of flashing it on each line typed.

Agent session: cosmic-gray

๐Ÿค– Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes the agent chat composer so a long draft keeps a thin scrollbar visible while it overflows, instead of flashing it on each line typed and fading out at rest.

The prompt (.acpmux-md-field) now uses the shared minimal scroller (MINIMAL_SCROLLER_ATTRIBUTE / MINIMAL_SCROLLER_CSS in webviews/src/scrollers.ts), which draws a thin thumb on no track the whole time content overflows, clear of the rounded corners.

  • The lane is always reserved with overflow-y: scroll โ€” WebKit ignores scrollbar-gutter on a box with a custom scrollbar โ€” and is taken out of the composer's right padding, so drafts keep their width and never rewrap when the scrollbar appears or disappears. The Home composer uses the same field without this adjustment, so its text area is 8 px narrower.
  • Adds a browser test covering Chromium and WebKit that verifies the thumb stays on every painted frame through typing, idle, blur, focus, and pointer leave, and disappears when the draft fits.
  • Other scroll containers can opt in by adding the attribute.