Skip to content

fix(app): use the subtle scrollbar in menus - #57926

Open
kitlangton wants to merge 6 commits into
betafrom
menu-scrollbars
Open

kitlangton wants to merge 6 commits into
betafrom
menu-scrollbars

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

The background-activity menu above the composer (“15 subagents”) scrolled with the native scrollbar. It now uses the same ScrollView as the session timeline: a thin thumb with no track that fades in on hover or while scrolling.

Changes

  • ScrollView gets focusable={false} for menus and lists that own focus and arrow keys. The viewport then has no tabindex, role="region" or key handling, so menu roles and arrow-key navigation stay with Kobalte.
  • Activity menu: rows render inside <ScrollView focusable={false}>, and the panel's grid, padding and scroll padding move to the viewport. The existing onFocusIn → scrollIntoView still keeps the active row in view.
  • The same swap, list only, for the other scrolling menus and popovers in the audit table below.
  • Lint cleanup in the touched files, needed for lint:changed: ScrollView forwards events through a typed forward helper instead of as any.

Audit (pages half in #57943)

Surface Before After PR
Background activity menu (subagents) native subtle #57926
Workspace submenus (new session, location missing, details panel) native subtle #57926
Recently closed tabs menu native subtle #57926
Details panel service menus and summary popover native subtle #57926
Model picker variant flyout native subtle #57926
Select dropdowns, typeahead lists native subtle #57926
Line comment @-mention list native subtle #57926
Provider account switcher (Settings → Providers) native subtle #57926
Settings sidebar native subtle #57943
Project settings → Extensions tabs native subtle #57943
Side panel new tab view native subtle #57943
Inbox list and working shelf native subtle #57943
Home native subtle #57943
New session "preparing" view native subtle #57943
Review panel diff (both axes) native subtle #57943
Browser new tab page native subtle #57943
Beta notes, free models, execute details dialogs native subtle #57943
Error page, error details, SSH prompt native subtle #57943
Timeline, ⌘K palette, model list, location dialog, composer, review file list subtle unchanged —
Settings content, dialogs, tab strips, @ and / suggestions, tool output, code blocks hidden unchanged —

Still native, and why

  • Terminal: xterm manages its own viewport and scrolling.
  • File trees (Files panel, folder picker): @pierre/trees scrolls a virtualized list inside a shadow root. This needs a hook in the library.
  • Code and diff horizontal scroll, Office, spreadsheet and artifact viewers: each has its own 2D scroll and zoom code.
  • Markdown and GenUI tables, execute-tool input, error and source boxes: inline timeline content that needs a wrapper in each renderer. Follow-up.
  • Queue panel: drag-to-reorder auto-scroll and an overflow that turns on and off.
  • Connect-server screen, provider-connect console list, WSL lists (Windows only), mobile drawer (touch), debug motion panel: rarely seen or platform-specific, and each needs larger CSS changes.
  • Legacy line comment (session-ui): its CSS is injected into diff shadow roots, where the ScrollView styles don't load.

Verification

  • Static-bundle stills of the ManySubagents story, before and after, idle and hovered (headless Chromium paints no overlay scrollbar, so the native bar doesn't show in the before stills). In both builds, wheel scrolling works (scrollTop 80), the panel keeps role="menu" and contains no region, and arrow-key behavior is the same.
  • bun typecheck (app, ui), scroll-view.test.ts, bun run lint:changed origin/beta.
  • CI: unit, build and lint pass. The remaining red e2e tests (app-1, app-2, app-components) are the same ones that fail on beta. The activity-panel geometry and long-MCP-list tests failed on earlier heads and now pass. The MCP test now reads scroll metrics from the ScrollView viewport.
  • For feat(app): try the CSS orb activity chip behind an experiment #57893: the panel's grid, gap and padding now live on .scroll-view__viewport, so its [data-orb] column-gap override needs to target the viewport. Git also flags a whitespace-only conflict in activity-chips.tsx.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks sound. I couldn't render the menus here (no browser in the sandbox), so I checked by reading the code and running the type checks and tests: typecheck passes in packages/ui and packages/app, and scroll-view.test.ts passes.

  • The .scroll-view root and viewport already set min-height: 0, so both menus still clamp to their max height and scroll.
  • The workspace submenu's list ref now points at the viewport, so arrow keys still move focus from the search field into the list.
  • Dropping .filter(Boolean) is safe: createResizeObserver filters out null targets itself.
  • With focusable={false}, menu roles and arrow-key navigation stay with Kobalte.

Public API is unchanged. The only addition is an optional focusable prop on the internal @opencode/ui ScrollView (backwards compatible).

No blocking concerns.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant