cmux NEXT sidebar: workspaces as tabs prototype (Debug, off): one-line rows, Cmd-Shift-[ / ] step workspaces
Summary
Leo asked for a "workspaces as tabs" mode: a compact, Claude-desktop-style list with one line per workspace (title, status/PR badge, needs-you icon), and ⌘⇧[ / ⌘⇧] moving to the previous/next workspace in sidebar order, wrapping, across groups. This adds it as a prototype behind a Debug Settings tunable, sidebar.workspacesAsTabs, which is off by default.
With the tunable on:
- Every workspace row is one line at any density. It shows the icon, name, working spinner, status glyph (waiting is the needs-you mark), PR badge and unread mark. There's no second line, status block, meta line or tab rows. This is a new row preset (
SidebarLayoutOptions.tabRow) next to minimal mode's. Minimal hides the PR badge, so I didn't reuse it. - ⌘⇧] / ⌘⇧[ run
nextSidebarTab/prevSidebarTabwithscope: "workspaces". That scope walks only workspaces and collapsed groups, in sidebar order across groups, and always wraps, whateversidebar.stepping/sidebar.steppingWrapssay. A collapsed group is one stop: stepping onto it shows its first workspace, as ⌃⌘] does. The keys never land on Home, the App Store or another top item. From Home (opened by a click), ⌘⇧] goes to the first workspace. ⌃⌘] / [ pass no scope and keep followingsidebar.stepping. - Home keeps its own ⌘⇧[ / ]: the bindings'
whenissidebar.workspacesAsTabs && !onHome.
How the keys route: KeyBindingDefaults.workspacesAsTabs holds the two entries. The binding table only includes them while the tunable is on (ActionRegistry.includesWorkspaceTabKeys, which the app points at the tunable), and a table built with the other answer is rebuilt. They come after the Chrome tab keys, so while on they win over nextSurface / prevSurface. KeyRouter sets the sidebar.workspacesAsTabs context key from the same tunable.
Off, nothing changes, in Release builds too. The table, Settings > Keyboard Shortcuts, keybinding.list and their conflict badges are the shipped ones. ⌘⇧[ / ] are Ghostty's default previous_tab / next_tab. Lawrence (2026-10-15) said they must cycle tabs, not workspaces, and this request reached me relayed, not from Leo directly. So nothing moves unless someone turns the Debug toggle on. The action-surfaces export is untouched, and the prepush gate's Ghostty-default-keys check passes.
Testing
WorkspacesAsTabsTests (CmuxNextAppTests) covers:
- Keys, toggle on: ⌘⇧] / [ resolve to
nextSidebarTab/prevSidebarTabwithscope: workspacesin a terminal, a page and an agent chat. - Keys, toggle off: they resolve to
nextSurface/prevSurface. - Home: Home and docked Home resolve the same with the toggle on and off.
- Real item order: with the top block (Home, App Store), a group and default settings:
- the last workspace's ⌘⇧] wraps to the first, and the first's ⌘⇧[ to the last, never to a top item;
- stepping goes through the group's workspace;
- from Home, ⌘⇧] goes to the first workspace;
- it still wraps with
steppingWrapsoff; - ⌃⌘] / [ settings are unchanged.
- Shortcut list: with the toggle off, no row names the toggle, and the ⌘⇧[ / ] rows are only
nextSurface/prevSurface. With it on, the list has exactly two more rows. - Key context: it carries the toggle.
- Row: a busy workspace gives one-line content at both densities, keeping its PR badge and waiting glyph.
scripts/ci/run-swift-testing-suites.sh on the fleet (3 shards):
- Red at 53e1d0ccdcf0 (steps 786355c29329376adcd035fe, bb152f9ed2203b61f52bf285, bb4b4921d4397de73e45233e).
WorkspacesAsTabsTestsfailed on the review findings:- ⌘⇧] from the last workspace went to
itm_home, and ⌘⇧[ from the first toitm_app_store; - with the toggle off, the list showed the two rows with conflict badges;
- the bindings had no scope.
- ⌘⇧] from the last workspace went to
- Green at f3c2bfcec1c3 (steps 4b3d32eec28661435ec40148, 3d44260aa75e8b90e3654820, 5cb02a14f3fcfaab25460581).
WorkspacesAsTabsTestspassed.
Observed / Not run
cmux-ci compile-check --apppassed on f3c2bfcec1c3 (job e79b1b8a950a9f6df2541101).prepush-gate.pyreported no new violations on every push.- Other suites at the green head: 25 failed. 24 of them also fail at base d6dcbed05b13, run the same way (steps 9428fdb4b2847328364a5c88, f76ff359be84f4ffcc793ebb, ca9496bf5411e73f120bb8e3).
- The exception is
MotionMasterSwitchSurfaceTests.aPopupOpensAndClosesStillWithTheMasterSwitchOff(:83, window alpha 1.0, expected 0). It passed at base, at the merged head and at the red head, and this PR touches nothing in popups or motion. A re-run at the same head is under way to tell a flake from a real failure. - Not run: the toggle in a running app, and captures.
Changelog
none (a Debug Settings prototype, off by default)
Proof
No capture: a Debug-only prototype, off by default. A capture will come with a dogfood pass once the tests can run.
Agent session: cosmic-gray
🤖 Generated with Claude Code