feat: native chat toolbar menus

Merged
TE
4d ago
+757-51

Native chat toolbar menus and quick-action customization

While chrome.tabs.nativeRowTools is enabled, keep the chat's overflow menu and quick-action customization menus in the native shared tab row. The page publishes the current chat rows (including bounded children and separators) as the reserved chat-menu tool, and native invocation routes through the existing tabRowToolInvoked event. Palette expansion opens a current native row's children without an open-time web round trip. Native quick-action buttons publish their existing customization rows as bounded context menus and invoke the same onCustomize callbacks. The Debug tunable remains off by default and the DOM paths remain the fallback when native publication is unavailable.

Protocol additions:

  • PaneTabRowTool.contextMenu carries bounded right-click rows with checked/destructive/separator metadata.
  • pane.tabRowTools.openMenu {id: "chat-menu", menuItemId?} asks the host to open the already-published chat menu or one row's children.
  • Both additions use the existing host-UI allowlist and strict native bounds (16 tools, 32 rows per level, 256 total rows, depth 2, 64 graphemes/256 UTF-8 bytes per text field).

Validation:

  • bun test webviews/test/native-header-menus.test.ts webviews/test/native-row-tools.test.tsx (11 passed).
  • bun run check (passed; existing UI-review warnings only).
  • bun run typecheck (passed).
  • python3 scripts/cmux-next/prepush-gate.py (passed; app-size advisory only).
  • SwiftFormat lint passed for all touched Swift files. Swift compile/tests require the fleet; request AgentPaneTabRowToolsTests, PaneMoreOverflowButtonTests, and the native toolbar/menu suites.

The web protocol publisher is based on the native-row-tools work from #27135; after that PR lands, this branch's web diff should be reviewed as the menu/customization extension.

Risk: normal. The feature is Debug-gated and default-off.

Leo's words (2026-10-29):

"speaking of that top right buttons section area thing, okay, what do you think about moving it into Swift? Like just going more native with it? Because I just feel like on my fing God, I've ran into so many fing different hacks and stuff just to get this nonsense working. ... we keep running into this nonsense, right? ... And then we could just RPC it or whatever the f*** into TypeScript sometimes. But I feel like, okay, more native buttons and stuff, maybe that might be nicer."

Leo's broader direction (2026-10-28):

"Like you need to be more ambitious, really. Like the whole dynamic workflow situation. I don't know if you already have the UI for that, how good it is, if you guys have tested it out yet. But, you know, we need to have that. And likewise, obviously, like the session info area, like that's where the things can be shown, and also the right tab area. All of these, like better and better UI. Just go ahead, man. I feel like you can do a hell of a lot more. More codex, more Claude, more whatever. We have the capacity, so let's use the fing capacity to go do some more productive fing work, right?"

Agent session: agent session


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


Summary by cubic

Moves the chat overflow menu and quick-action customization menus into the native shared tab row when the chrome.tabs.nativeRowTools Debug flag is on. DOM buttons remain the default and the fallback when the flag is off or native publication fails; native invitations route through the existing tabRowToolInvoked event and the same onCustomize callbacks. The reserved chat-menu tool is published ahead of quick-action buttons under the 16-tool cap so the native overflow menu isn't dropped. Only the selected page's menu requests are honored during a paint hold, menus that exceed native bounds keep the complete DOM fallback, and overflow items hidden by narrow widths remain reachable from the chat menu.

Protocol additions

  • PaneTabRowTool.contextMenu carries bounded right-click rows with checked, destructive, and separator metadata.
  • pane.tabRowTools.openMenu opens the published chat menu or a single row's children, so palette expansion skips the open-time web round trip.
  • Menu request callbacks are owner-checked on the native side so a parked or outgoing page can't present the selected page's menu.