cmux NEXT: chat tools sit in the tab row; one Browser button opens the chat's page

Merged
TE
16d ago
+339-40

Problem

In the current NEXT nightly, a chat's tools (Preview ▷, Terminal, Browser, Sources ☰, "…") sit in a second row under the pane's tab strip, inset to the transcript column, not at the pane's top right. Leo, 2026-10-20: "I thought we moved that all the way up to the top right." Two buttons also did nearly the same thing. ▷ (Preview) opened the chat's local page beside it, and the globe (Browser) opened a blank New Tab beside it. Clicking the globe while the chat was about localhost:5173 gave an empty browser ("something different was opening here").

Why this surface missed the move: lifting the tools into the tab row already exists (chrome.tabs.rowTools, header/tabRow.ts plus AgentPaneTabRow), but since 2026-10-14 it has been a Debug opt-in that is off by default, so the nightly never used it.

Change

  • Tools in the tab row by default. chrome.tabs.rowTools now defaults to on. A chat's tools sit at the right end of its pane's tab row, and the tabs make room. The Debug setting still turns it off. Nothing else changes: the row is offered only while the pane shows its tabs on top, and Quick Chat keeps its own header.

  • One Browser button. When the chat's last turns named a local page, Browser opens that page beside the chat. Its tooltip names it ("Preview localhost:5173"), and it lights while the page is open. Otherwise Browser opens a plain browser split, as before. A toggle click closes whichever one it opened. Preview is no longer a default button. It is still in the header's right-click menu, and anyone who shows it gets the separate Preview and a plain Browser back. A saved agent-pane/layout.json list is kept as is.

  • The preview card's Open in tab is unchanged on purpose. It already goes through the app-wide link rule (layout.linkPlacement, default: the pane on the right, or a new split there). The card's compact layout and "Load preview" are #22461's.

  • Debug toggles, both on by default:

    • chrome.tabs.rowTools (existing) puts the tools in the tab row.
    • agentChat.browserOpensPreview (new) makes Browser open the chat's page. Turning it off brings back Preview beside a plain Browser for anyone on the default buttons. It applies live through the header state push.

No new strings. The Browser tooltip reuses header.previewAddress.

Before / after

Nightly today (Leo's window, tab strip and tool row only): the tools are a second row under the tabs, inset to the transcript column. The lit globe opened the blank "New Tab" on the right, even though the chat was about a local page.

nightly before

Agent pane page, base fce753a vs this branch. Headless Chromium, the pane's dev entry, a stand-in acpmux daemon. The chat's last reply names http://localhost:5173/. The native tab strip is not in these captures: on head, it fills the empty left part of the top row. (The "Load preview" card under the header is #22461's.)

Base: a 44px row of its own with ▷ Preview, Terminal and Browser. A Browser click sends pane.action {id: "splitBrowserRight"}, which opens a blank browser.
base

Head (row offered, now the default): the tools sit in the 28px tab row at the pane's right end, and there is no separate ▷. Browser's tooltip reads "Preview localhost:5173", and a click sends pane.action {id: "preview", url: "http://localhost:5173/", mode: "toggle"}, which opens the page beside the chat.
head

Gallery entry agent-pane.header-tools, Linux Chromium (big-red), base vs head. The PR's gallery advisory run samples only the first 16 of 43 touched states, so it never reached this entry.

panels-open. Base: Changes, ▷ Preview (lit), Terminal and Browser (lit). Head: the globe alone holds the page; its tooltip reads "Preview localhost:5173".
gallery base panels open dark
gallery head panels open dark

New variant browser-preview (head, light):
gallery head browser preview light

Review (independent subagent)

  • Fixed: in tab-row mode, the header's status slot sits under the App's tabs and is hidden, so with the new default "Disconnected", "Reconnecting" and "Failed" showed nowhere. The warning now leads the tools in that mode, with its reason in the tooltip (ChatHeaderInlineStatus, test chat-header-inline-status).
  • Fixed: closing a lit preview from Browser now sends the page too, as Preview did. If the preview was already closed before the lit state caught up, the click reopens it instead of doing nothing.
  • Known, not new (already true while the setting was opt-in): in a narrow pane, the strip gives the tools at most half the row, so a very wide tool set (PR chip, Changes, Terminal, Browser, ☰, …) can slip under the last tab. The native remote-approval and other-install notices pin to the top of the agent view, so in row mode they start under the strip. These are follow-ups.
  • Intended: the Split Browser Right shortcut and the palette still open a plain browser. Only the header button opens the chat's page, so its tooltip shows no keycap.

Fleet build

cmux-ci job 8138173fc42fbfc0438445a0 succeeded on 46c921b998dd with --backend-mode local, because the shared dev-backend VM cmux-dev-backend-1 was unreachable over Tailscale (REPAIR.md row). That makes it compile and package evidence, not a dogfood build. cmux-ci compile-check --app passed on c7a0776acb0c, which includes the Debug toggle (job 1b93a3c23350a68c9fdee626).

Verification

  • test/chat-header-browser-preview.test.tsx (new), plus the default-list expectations in chat-header-fold and chat-header-customize. Commit 1 holds the tests only and is red on base. Commit 2 is the fix.
  • bun test test/chat-header-*.test.tsx test/chat-menu-*.test.tsx: 19 pass. chat-header-tooltips and chat-header-pull-request-status fail only when the files run together, and they fail the same way on base fce753a089e5. Each passes on its own.
  • vp check on the changed files: clean.
  • agent-pane-tab-row.test.ts (Playwright, CI browser lane) covers the row mode that is now the default.

Agent session: cosmic-gray

🤖 Generated with Claude Code