cmux NEXT composer: a click on the model button opens its menu and it stays open

Merged
TE
16d ago
+264-4

Leo, on nightly 3a63d6d, in his long claude-sr2 chat: clicking the Opus 5.5 Medium model button did nothing.

Likely cause (not yet confirmed in the app's WKWebView). ui/Menu opens a menu on the mouse press's pointerdown (beginPointer). In the app's WebKit, the press's mousedown follows, and Base UI's Menu.Trigger also toggles on mousedown (useClick with event: 'mousedown'). MenuButton only called preventDefault() on that mousedown while the menu was open. That kept Base UI from reopening a closed menu, but it doesn't stop Base UI's handler. When React has already drawn the opened menu by the time the mousedown arrives (a busy page, a long running chat), Base UI reads the menu as open and closes it a frame later. The menu flashes and is gone, so the click looks dead.

Fix. webviews/src/ui/Menu.tsx: a mouse press marks that it already opened or closed the menu. That press's mousedown takes the mark and calls preventBaseUIHandler() (and preventDefault()), so each press toggles the menu once. This covers every ui/Menu chip (model, mode, +, header menus); the model button is where Leo hit it.

No overlay is involved. On the nightly bundle in headless Chromium and WebKit (Playwright, 300 to 700 turn mock chat, a running turn, a long draft, CPU throttled 8x, pane widths from 360 to 1100), document.elementFromPoint at the button's centre always hits the button. Playwright's engines don't send the follow-up mousedown after a cancelled pointerdown, so the bug never shows there. The test reproduces the app's event order directly.

Not proven in the app. The mode button is the same ui/Menu shape and opened for Leo on the same nightly, which argues against this being the whole story. If WKWebView doesn't send the mousedown after a cancelled pointerdown, this fix is harmless but doesn't explain Leo's dead click. Before closing the issue, confirm it on a nightly with this PR, or with a WebKit event log from the real pane.

Changelog

  • Fixed: A click on the composer's model button (and its other menu buttons) opens the menu instead of closing it right away.

Regression test

cd webviews && bun test test/model-button-opens.test.tsx

The test mounts the real composer pickers in Leo's chat's state (claude-sr2, 704 turns, a running turn, a queued prompt, 30% context). It clicks the model button with WebKit's event order (pointerdown, then mousedown after React has drawn, pointerup, mouseup, click), then asserts that the menu ("Model · this session") is open and still open a frame later. It also asserts that a second press closes it, and that a plain ui/Menu chip behaves the same.

Observed / Not run

  • Observed on huge-blue: the new test is red on base and green on head. On head, these also pass: menu-pointer-close-focus, model-menu-compact, composer-chip-press, popover-trigger-press-marker, home-composer-menu, agent-options-menu and reasoning-menu-check (35 pass, 0 fail; browser-lane cases skipped without CMUX_BROWSER_TESTS). bun run check passes; its only warning, system blue in ui/importCards.css, is a base file.
  • Observed: the Playwright hit-test probe on the 3a63d6d bundle (above). The button is never covered.
  • Not run: the real app's WKWebView. I could not drive Leo's running nightly page, and agents don't build the Mac app locally. cmux-ci compile-check --app was started on the head.

No capture: webview-only change (press handling, no visual change); no gallery state differs.

Agent session: cosmic-gray

🤖 Generated with Claude Code