Repository navigation
cmux NEXT: vertical tabs per workspace or for all workspaces - #32127
Conversation
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by web and size checks: green Default changesThis pull request changes 42 defaults at
Signed off on this commit by Merge receipt: merged as Regenerate the snapshot with Shipped in:
|
4eb7add to
db85dfe
Compare
db85dfe to
71fe602
Compare
Leo asked: “I DIDNT FUCKING MEAN AS A GLOBAL DEFAULT I MEANT JUST HAVING THE VERTICAL TAB FUCKING OPTION AND THEN BEING ABLE TO SET IT AS A DEFAULT FOR ALL WORKSPACERS”. Vertical tabs are available as an option, per-workspace choices persist, and the all-workspaces default remains off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-2fba32c80c8682a93d18
71fe602 to
fa51dcb
Compare
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-2fba32c80c8682a93d18
|
Review (codex gpt-6-astra high, next-review): BLOCK at 8c7f7c6 BLOCK at 8c7f7c6
|
|
Found 8 test failures on Blacksmith runners: Failures
|
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
Sidebar-Look: no visual change Agent-Session: https://cmux.com/deeplink/agent?id=aid-2fba32c80c8682a93d18
|
Review (Claude Opus 5.5 medium): SHIP at ba62977 I read the full diff from merge-base 5f937c4 to this exact head, plus the PaneController apply/observe, PaneTabBarHandlers, and fullscreen paths. The feature meets the ask: vertical tabs are a per-workspace option, and tabs.verticalTabs remains off by default. No blocking findings. Follow-ups:
The exporter test red is base-owned: CloudApprovalAnsweredElsewhereTests.swift cannot find CloudAppOpFailure, and this PR changes no Cloud files. The fleet compile-check for this exact head passed CmuxNext and cmux-tui. |
|
The tip arch-ratchet red from this PR (stateful_singletons +1, assume_isolated_outside_adapters +2, observer_sites +1, app_lines +273 over its parent) is fixed forward in #32414: the vertical tabs store is built once in AppServices and injected, the list uses MenuTracking, and the logic moves to CmuxNextSettings, CmuxNextActions and CmuxNextTabs with the same behavior. |
…ted; its logic moves out of the App (#32127 arch rows) (#32414) * base red: vertical tabs' store is built once in AppServices and injected; its logic moves out of the App (#32127 arch rows) Fixes the feat tip arch-ratchet red from #32127 (a9c5b0d, STATUS 6861). Over its parent, #32127 raised four rows: stateful_singletons +1, assume_isolated_outside_adapters +2, observer_sites +1 and app_lines +273. The tip verdict named only the first one. - stateful_singletons: `WorkspaceVerticalTabs.shared` is gone. AppServices constructs one store (`workspaceVerticalTabs`, next to `paneTabBars`) and passes it to the actions and to each pane's snapshot. - assume_isolated_outside_adapters, observer_sites: the vertical list follows the end of its menu through CmuxNextCompat's MenuTracking, as the strip's TabMenuTracking does. It has no deinit, as TabStripView has none: leaving the window cancels its observation tasks, and DemandTimer and MenuTracking clean up when they are released. - app_lines -273: the store, PaneVerticalTabs and the actions (VerticalTabsActions with a VerticalTabsHost that AppActionContext implements) move to CmuxNextSettings, with their two strings. The tab bar's empty-area menu becomes ActionRegistry.makeTabStripMenu in CmuxNextActions. The list's show and layout move to VerticalTabListView in CmuxNextTabs. The sibling per-pane stores PaneTabBarChoices and PaneFullWidthTabs move to CmuxNextSettings too. Behavior stays the same. apply() no longer sets hidesStrip before showStrip sets it to the same value. arch-ratchet --base 3b6996e --head a9c5b0d: refused (the four rows above). arch-ratchet --base c9835aa --head this commit: no rise; stateful_singletons -1, assume_isolated_outside_adapters -2, observer_sites -1, app_lines -273. * base red vtabs: the moved vertical tabs and tab bar stores and actions stay on the main actor in CmuxNextSettings (nonisolated by default) * base red vtabs: the two setting rows bind typed effects in CmuxNextSettings (the App's throwing run: bind is App-only); a managed setting refuses as before
…tension (#32456) * cmux-next tests: VerticalTabListViewTests closes its windows (base red from #32127) WindowCleanupCoverageTests fails at the tip: #32127 added VerticalTabListViewTests, which makes an NSWindow per test without .closesItsWindows and only ordered it out. Each test now closes its window in its teardown (closeAfterTest), and the suite takes the trait. * test(next): the window cleanup scan must see tests in an extension of a type without .closesItsWindows (fails: the scan skips extensions) * test(next): the window cleanup scan counts a top-level extension's tests as its type's suite WindowCleanupCoverageTests read only struct/class/enum/actor declarations, so tests in a top-level extension were invisible: a suite whose tests live in `extension Parent { ... }` could open windows while Parent lacks .closesItsWindows, and the scan said nothing. The scan now reads top-level extensions too, collects the types declared with the trait across every test file, and reports an extension holding tests whose type is not one of them.
… (ActionSurfaceParityTests) (#33092) everyTargetKindMenuShowsItsActions failed at ActionSurfaceParityTests.swift:79 with gaps newTab.default (tab) and reopenClosedBrowserPanel (tab). a9c5b0d (#32127, vertical tabs) added the tab bar's empty-area menu (ActionMenuContext.tabStrip, whose target is the pane) and placed both there, as Chrome's and Edge's tab strip menus do. A placement replaces an action's menu exemption, so both lost theirs (newTab.default's duplicateOfDefault was removed; reopenClosedBrowserPanel's noObject no longer applies), and the parity rule found a tab action placed only in a pane menu. The rows work: the app passes the pane, and newTab.default's handler takes a pane target as its place (TabLifecycle.newTabOfPaneKind, namesPane); Reopen Closed Tab reopens the key window's newest closed group whatever the target. What the rule could not say is that the tab bar's rows act on what its pane holds. ActionMenuContext.contentKinds now says it, for the tab bar only ([.tab]); every other menu keeps the rule as it was (a pane's own menu still does not count for a tab action). Culprit: a9c5b0d (#32127). Red: fleet job 2bf90b6c18ca136f3fb6c15d at feat 11dfe91.
|
Swift-logic note (REWRITE R-SWIFT-MIRRORS): this PR added Why this matters: the tip's Nothing to change in this merged PR. REWRITE R-SWIFT-MIRRORS tracks the move (design: https://cmux-orchestrator-1.tail137216.ts.net/resize-videos/rewrites/R-SWIFT-MIRRORS/DESIGN.md, section 11). |
…ic red since 25c69ee (#33986) * swift-logic-classes: classify the false counts of the tip's swift-logic red since 25c69ee The tip watcher's swift_logic_lines row has been red since 2026-11-02 05:38Z (last green 25c69ee: 415218). Part of the rise is not new Swift logic: declarations whose reviewed row did not follow a module move, and view, strings, tunables and debug code that the default rule counts in full because no row lists it. This commit corrects the table only (no code change). Each row carries its reason. - 24 rows follow their declaration's module move (class, shares and reason kept): DesktopNotifier and NotificationPermission* to CmuxNextNotifications (#32438), LinkHintController to CmuxNextBrowser (#32537), FirstFrameGate, LinkDragItem, LinkDropPlan, StaticURL, PageInstallCounter, AgentDialogLine, NewTabLayoutChoice, RunningPrograms, AgentChatMute, CloudAppOp*, PaneFullWidthTabs, PaneTabBarChoices, WindowRegistryStore. LOGIC rows stay LOGIC. - 3 stale rows follow a move that also changed the kind (enum to struct): WhatsNewTunables, NewTabTunables, NotificationPermissionStrings; the stale CloudMoveStrings enum row is replaced by its struct. - 48 new PLATFORM rows: the vertical tab list's AppKit drawing, pointer, accessibility and pane extensions (#32127; its row layout math, VerticalTabListLayout, stays counted), Core Animation avatar looks (#33395), view hosts (BrowserPageBars, TerminalStatusLabel, SidebarContainerView+PaneReach, AgentPaneView+MoveCover), the person click check of consent prompts, the glyph bitmap cache, the thin Swift side of the Rust Cloud start rules (CloudStartCore), the at-cap dialog spec, localized strings, Debug Settings tunables, and DEBUG, bench or DEV playground and fixture code. Not changed: every real rise stays counted (for example NativeDeviceEnrollment, NativeCloudLinkChild, GitHubConnections, VerticalTabListLayout, DisplayBackdropBinding, CreationInputBuffer); REWRITE R-SWIFT-MIRRORS names the wave that removes each. The green reference 415218 is not changed. swift_logic_lines at 2b3d7cd (python3 -I scripts/cmux-next/ swift-logic-lines.py --rev 2b3d7cd --json, then with --table of this commit): 431786 -> 427957 (-3829). mirror_lines 71688 unchanged. table_rows_missing 36 -> 8 (stale rows of deleted declarations); table_rows_invalid 0. scripts/cmux-next/tests/test_swift_logic_lines.py: 16 tests OK. * swift-logic-classes: the review's three rows count their rules; reasons say what the code is Review of #33986 (FIX at cb155fb, comment 6610083921): three rows put a rule under PLATFORM. Each now counts it: - RefusalStrings+Spaces: MIXED 0/46. Its spaceDelete (lines 14-19) is a Swift copy of the daemon's own rule (cmux-tui-core state/room_delete.rs:148 and personal_mutations.rs:241: the default room cannot be deleted), so its share is MIRROR and it counts in mirror_lines; the localized strings stay platform. - TerminalStatusLabel: MIXED 65/0. Lines 17-23, 46-47, 49-76 and 88-131 (81 of 125) are the reveal-wait state machine. - LinkHintController: MIXED 35/0. #32537 moved and rewrote it; who gets each key while the page answers (hold, release, replay marks, key kinds, the 500 ms answer bound, outcome handling: about 87 of 251 lines) is logic for REWRITE R-INPUT. Text: the reasons of the strings and tunables rows say what the code is and cite the table's own precedent rows, not a "table rule" no document has; the AgentPaneView #1 row of AgentPaneView+MoveCover describes the Chromium move it holds; LinkHintStrings moved to its own file, not to another module; CloudStartCore's reason names its one hand-copied fallback line. Three stale rows follow their moved code: PersonalGroupLifecycle (LOGIC, now a struct in CmuxNextAppCore), the NSResponder extension (PLATFORM, now in CmuxNextFocusUI), and DiffTabReuse (LOGIC, now enum DiffTabChange and an extension of DiffOpenSource in CmuxNextDiff; both rows LOGIC). swift_logic_lines at 2b3d7cd with this table: 428126 (base table 431786; the first version of this PR 427957); mirror_lines 71694. At feat f07e8d9: 433351 -> 429651. table_rows_missing 5, invalid 0; scripts/cmux-next/tests/test_swift_logic_lines.py: OK. * swift-logic-classes: LinkHintController counts the key routing its reason names; textInputHasKeyboard is listed Re-review of #33986 (FIX at 791c494, comment 6611048817): - LinkHintController: MIXED 35/0 -> 57/0. The reason already named hold, release, replay marks, key kinds, the answer bound and outcome handling as logic; those are lines 22-24, 28-31, 34-35, 45-46, 52-77, 79-85, 98-105, 107-130, 132-140, 142-149, 180-200, 202-210 and 217-237: 144 of 251 lines. The reason now lists them. - extension NSWindow in CmuxNextFocusUI/TypedTextInput.swift (#31865): new LOGIC row. textInputHasKeyboard is the rule which input has the person's keyboard; the view-shaped default (an NSWindow extension) counted its 13 lines as 0. - Reasons cite repository facts only (crates, files, PRs), not plan names outside the repository; the strings rows no longer quote a row count that changes. swift_logic_lines at 2b3d7cd with this table: 428194 (base table 431786). At feat f07e8d9: 429719. mirror_lines 71694 / 71698. table_rows_missing 5, invalid 0. scripts/cmux-next/tests/ test_swift_logic_lines.py: OK.
|
Swift logic follow-up (tip watcher swift-logic row, STATUS 13027 "SWIFT LOGIC CULPRITS"; lead order). This PR raised
plans/cmux-next/layer-ownership.md says Rust owns every state, list and rule and Swift is the platform shell. Please move the new logic of this PR in a follow-up: to Rust (the crate that owns the area; the owner lists in STATUS 12147-12165 name the target per area), or at least out of the app into its package. If a declaration above is platform code (AppKit, Core Animation, OS calls, strings, tokens, DEBUG tooling), reply here and the table gets a PLATFORM row with your reason instead. The swift-logic row is a report, not a gate: nothing blocks on this comment. |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Adds vertical tabs to cmux NEXT. Each workspace can turn vertical tabs on or off from its tab menu, and
tabs.verticalTabsremains an off-by-default setting that can make vertical tabs the default for all workspaces. The strip and list share one menu, and the sidebar does not list the same workspace tabs twice.Leo's ask (2026-10-21): “I DIDNT FING MEAN AS A GLOBAL DEFAULT I MEANT JUST HAVING THE VERTICAL TAB FING OPTION AND THEN BEING ABLE TO SET IT AS A DEFAULT FOR ALL WORKSPACERS”.
Verification
cmux-cicompile-check passed for exact heada32b50df134304a02b38f21cdc663795cc98d3b3(CmuxNext and cmux-tui).cache_http_400after the script exited 0.Changelog
Leo-ask: ask-c1e514d084
Agent session: agent session