Skip to content

Add surface and workspace reorder shortcuts - #8080

Merged
austinywang merged 12 commits into
mainfrom
issue-8041-reorder-shortcuts
Jul 15, 2026
Merged

austinywang merged 12 commits into
mainfrom
issue-8041-reorder-shortcuts

Conversation

@austinywang

@austinywang austinywang commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add customizable shortcuts for moving the selected surface left or right and the selected workspace up or down
  • route shortcuts, menus, command palette hints, context menus, and socket workspace moves through shared adjacent-reorder helpers
  • preserve Bonsplit insertion semantics, edge no-ops, remote tmux synchronization, and pinned workspace tiers
  • document defaults in the config schema and shortcut reference, with English and Japanese localization
  • add behavior coverage for surface ordering, edge clamping, pinned tiers, action registration, and default collisions

Validation

  • arch -arm64 /usr/bin/swift test --package-path Packages/macOS/CmuxSettings (252 tests passed)
  • ./scripts/check-pbxproj.sh
  • ./scripts/lint-pbxproj-test-wiring.sh
  • python3 scripts/check-workspace-package-groups.py --check
  • python3 scripts/check-package-resolved-policy.py
  • Swift parse checks for every changed Swift file
  • JSON parsing and localization/default collision audits

Issue: #8041

Closes #8041


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds customizable shortcuts to reorder the selected surface and workspace across layouts and UIs. Preserves selection, clamps at edges (including extreme offsets), respects pinned tiers, and routes Canvas reorders through CanvasModel with no viewport refresh on clamped moves; implements #8041.

  • New Features

    • Shortcuts: Move Surface Left/Right (⌘⇧⌥[ / ⌘⇧⌥]) and Move Workspace Up/Down (⌃⌥⌘[ / ⌃⌥⌘]); customizable with en/ja labels, docs, schema, and web catalog; command palette IDs palette.moveWorkspaceUp / palette.moveWorkspaceDown mapped.
    • Reordering keeps selection, stays within the focused pane without wrapping, clamps at edges, and doesn’t alter Canvas split order; available via keyboard, menus, command palette, and sockets/tmux.
  • Refactors

    • Centralized adjacent navigation/reorder via shared “move by offset” APIs and handleAdjacentNavigationShortcut; added TabManager helpers and action-visibility filter.
    • Canvas surface reorder now uses CanvasModel.reorderPanel; clamps extreme offsets without overflow, and refreshes the viewport only when order changes.
    • Tests cover clamping (incl. Int.min/Int.max), pin tiers, selection stability, palette mappings, default collisions, and Canvas reorder regressions.

Written for commit 028aa00. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added default shortcuts and View menu commands to move the selected surface left/right and move the selected workspace up/down.
    • Extended adjacent navigation and command-palette mappings for these new actions.
  • Bug Fixes
    • Improved reordering to use relative movement, preserve the current selection, clamp at boundaries, and enforce pinned workspace tiers (including canvas behavior).
  • Documentation
    • Added localized labels and updated the shortcuts reference to include the new actions.
  • Tests
    • Added coverage for movement, edge clamping, selection preservation, pinned-tier rules, canvas vs split-pane behavior, and shortcut/action consistency.

@vercel

vercel Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled Jul 15, 2026 9:12am
cmux-staging Building Building Preview, Comment Jul 15, 2026 9:12am

@coderabbitai

coderabbitai Bot commented Jul 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds four shortcut actions for moving surfaces and workspaces, shared relative-reordering helpers, menu and command-palette wiring, localized shortcut metadata, schema updates, and tests covering ordering, selection, shortcut consistency, and collisions.

Changes

Surface and workspace shortcut reordering

Layer / File(s) Summary
Shortcut contracts and defaults
Packages/macOS/CmuxSettings/..., Sources/KeyboardShortcutSettings.swift, Resources/Localizable.xcstrings, web/data/*, skills/cmux-settings/...
Defines four navigation actions with defaults, labels, localization, schema support, web metadata, and settings exposure.
Relative movement implementation and UI wiring
Sources/Workspace+SurfaceNavigation.swift, Sources/TabManager+AdjacentWorkspaceReordering.swift, Sources/AppDelegate*.swift, Sources/ContentView*.swift, Sources/cmuxApp*.swift, Sources/TerminalController.swift
Adds clamped relative surface/workspace movement and routes keyboard, palette, menu, context-menu, and API commands through shared helpers.
Behavior validation and project integration
cmuxTests/ReorderShortcutActionTests.swift, cmux.xcodeproj/project.pbxproj
Adds tests for ordering, pinned tiers, selection preservation, shortcut consistency, command mappings, and project membership.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AppDelegate
  participant Workspace
  participant TabManager
  User->>AppDelegate: Press movement shortcut
  AppDelegate->>Workspace: moveSelectedSurface(by: offset)
  AppDelegate->>TabManager: moveSelectedWorkspace(by: offset)
  Workspace->>Workspace: reorderSurface(panelId:by:)
  TabManager->>TabManager: reorderWorkspace(tabId:by:)
Loading

Possibly related PRs

  • manaflow-ai/cmux#7981: Both changes extend shared shortcut-action infrastructure with new action cases.

Suggested reviewers: azooz2003-bit


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Full Internationalization ❌ Error New shortcut labels in Resources/Localizable.xcstrings and web/data/cmux-shortcuts.ts ship only en/ja; the catalog already supports 19 locales. Add translations for every locale already present in Resources/Localizable.xcstrings and update the web shortcut data/messages for all locales in web/i18n/routing.ts.
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (23 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement #8041 by adding shortcut actions and wiring them to adjacent surface/workspace reorder logic.
Out of Scope Changes check ✅ Passed All added refactors and tests support the reorder-shortcut feature; no clearly unrelated code changes stand out.
Cmux Swift Actor Isolation ✅ Passed New shortcuts/reorder helpers live on @MainActor UI types or pure value enums; no new Sendable shared mutable refs or background UI-store access were introduced.
Cmux Swift Blocking Runtime ✅ Passed The PR only adds shared reorder wiring and shortcut/menu routing; no new blocking waits, sleeps, main-queue syncs, or locks appear in the changed production Swift.
Cmux Browser Automation Off-Main ✅ Passed PR doesn't change browser.* socket routing or policy files; only unrelated workspace reorder code in TerminalController changed, and ControlCommandExecutionPolicy is untouched.
Cmux Expensive Synchronous Load ✅ Passed Diff only adds shortcut/reorder helpers and wiring; no RestorableAgentSessionIndex load or large agent-history parse was added to main-actor interactive paths.
Cmux Cache Substitution Correctness ✅ Passed Changed code only adds live reorder helpers and shortcut/menu/socket wiring; no persistence/history/snapshot path substitutes cached values.
Cmux No Hacky Sleeps ✅ Passed No changed TS/JS/shell runtime code introduces sleep, delay, polling, or timers; the only changed TS file is static shortcut data and the schema is declarative.
Cmux Algorithmic Complexity ✅ Passed New offset-based helpers add only linear lookups per action; no nested scans, repeated sorts/filters, or batch rescans were introduced in hot paths.
Cmux Swift Concurrency ✅ Passed PR diff adds only synchronous reorder helpers/call sites; no new DispatchQueue, Combine, completion-handler, or fire-and-forget Task patterns appear.
Cmux Swift @Concurrent ✅ Passed The PR only adds synchronous, UI-bound reorder helpers; no new @concurrent or nonisolated-async code, and no heavy async UI call sites were introduced.
Cmux Swift File And Package Boundaries ✅ Passed PASS: New production Swift files are small, single-purpose helpers; oversized files were only lightly touched; core reorder logic was split into package/app extensions and glue.
Cmux Swiftpm Lockfiles ✅ Passed Diff only changes Sources/Workspace+SurfaceNavigation.swift; no Package.resolved, .gitignore, workflow, or Xcode project/package-reference edits.
Cmux Swift Logging ✅ Passed No diff-added production logging: the changed runtime Swift files add shortcut/reorder helpers only, with no print/debugPrint/dump/NSLog or new Logger constants.
Cmux User-Facing Error Privacy ✅ Passed PR adds shortcut wiring and generic labels only; the only user-facing error diff removes a generic "Workspace not found" message, with no sensitive details exposed.
Cmux Swiftui State Layout ✅ Passed PR only rewires shortcuts/menus; no new ObservableObject/@published, GeometryReader, lazy-row store refs, or render-time state writes were introduced.
Cmux Architecture Rethink ✅ Passed The diff adds shared reorder helpers and routes shortcuts/menus/socket paths through them; no sleeps, observers, locks, or split ownership were introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: The only changed file is Sources/Workspace+SurfaceNavigation.swift, which contains no NSWindow/NSPanel/WindowGroup code and no cmuxAuxiliaryWindowIdentifiers changes.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/config/docs/localization/test assets; no temp, build, cache, or other artifact paths appear in the diff.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The only changed production Sources Swift file just adjusts canvas viewport refresh logic; no #if DEBUG, test-only names, or widened test seams appear.
Cmux No Ambient Global State ✅ Passed New behavior lives on AppDelegate, TabManager, Workspace, cmuxApp, and ShortcutAction extensions; no new file-scope mutable state or singleton was added.
Title check ✅ Passed The title clearly matches the main change: new surface and workspace reorder shortcuts.
Description check ✅ Passed It covers the required summary and testing details, with the remaining template sections mostly present or non-critical.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8041-reorder-shortcuts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 14, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds four customizable keyboard shortcuts — Move Surface Left/Right and Move Workspace Up/Down — and routes all entry points (keyboard, menus, command palette, socket) through shared adjacent-reorder helpers on TabManager and Workspace. It replaces three separate copies of the move-and-preserve-selection pattern with a single implementation that correctly clamps at tier/layout edges.

  • TabManager.reorderWorkspace(tabId:by:) and moveSelectedWorkspace(by:) centralize what was duplicated in ContentView, cmuxApp, and TerminalController; bounds/tier clamping is delegated to the existing WorkspaceReorderCoordinator.
  • Workspace.reorderSurface(panelId:by:) handles both Canvas (via new CanvasModel.reorderPanel(_:by:)) and split-pane paths, converting final-position offsets to Bonsplit insertion indices and suppressing viewport.modelDidChangeExternally on edge no-ops.
  • Localization is complete: Resources/Localizable.xcstrings carries both en and ja translations for all four action labels, and the web shortcut catalog and JSON schema are updated to match.

Confidence Score: 5/5

Safe to merge — well-bounded refactor of duplicated move logic with no observable behavior change for existing callers and solid new test coverage.

All three layout/entry-point paths (canvas, split, socket) are covered by tests. Bounds clamping is handled by the existing WorkspaceReorderCoordinator. No new concurrency patterns, no new ambient state, and all user-facing strings carry en/ja translations.

No files require special attention.

Important Files Changed

Filename Overview
Sources/TabManager+AdjacentWorkspaceReordering.swift New file: shared adjacent-reorder helpers moveSelectedWorkspace(by:) and reorderWorkspace(tabId:by:) that centralize what was previously duplicated across ContentView, cmuxApp, and TerminalController.
Sources/Workspace+SurfaceNavigation.swift Adds moveSelectedSurface(by:) and reorderSurface(panelId:by:) handling both canvas and split layout modes. Edge-clamps correctly; split path converts final-position to insertion index to match Bonsplit semantics.
Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasModel.swift Adds reorderPanel(_:by:) with clamped destination index and revision bump; returns true for edge no-ops without mutating the layout.
Sources/AppDelegate+AdjacentNavigationShortcut.swift New file: consolidates surface navigation and new surface/workspace reorder shortcuts into handleAdjacentNavigationShortcut.
Sources/KeyboardShortcutSettings.swift Adds moveSurfaceLeft/Right and moveWorkspaceUp/Down actions with default shortcuts and localized labels.
Sources/TerminalController.swift Simplifies move_up/move_down socket commands to use the new reorderWorkspace(tabId:by:) helper.
Sources/ContentView.swift Removes duplicated moveSelectedWorkspace(by:) and updates TabItemView.moveBy to delegate to the shared helper.
cmuxTests/ReorderShortcutActionTests.swift New test suite covering surface move clamping, no-ops, canvas vs split isolation, pin-tier ordering, selection preservation, and shortcut collision checks.
Resources/Localizable.xcstrings Adds en and ja translations for all four new action labels.
web/data/cmux-shortcuts.ts Adds moveWorkspaceUp/Down and moveSurfaceLeft/Right entries with correct default combo glyphs and both en and ja descriptions.

Reviews (8): Last reviewed commit: "Add Canvas reorder overflow regression t..." | Re-trigger Greptile

Comment on lines 4490 to 4496
case "move_up":
guard let currentIndex = tabManager.tabs.firstIndex(where: { $0.id == workspace.id }) else {
result = .err(code: "not_found", message: "Workspace not found", data: nil)
return
}
_ = tabManager.reorderWorkspace(tabId: workspace.id, toIndex: max(currentIndex - 1, 0))
_ = tabManager.reorderWorkspace(tabId: workspace.id, by: -1)
finish(["index": v2OrNull(tabManager.tabs.firstIndex(where: { $0.id == workspace.id }))])

case "move_down":
guard let currentIndex = tabManager.tabs.firstIndex(where: { $0.id == workspace.id }) else {
result = .err(code: "not_found", message: "Workspace not found", data: nil)
return
}
_ = tabManager.reorderWorkspace(tabId: workspace.id, toIndex: min(currentIndex + 1, tabManager.tabs.count - 1))
_ = tabManager.reorderWorkspace(tabId: workspace.id, by: 1)
finish(["index": v2OrNull(tabManager.tabs.firstIndex(where: { $0.id == workspace.id }))])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 not_found error path silently removed for move_up/move_down

The old implementation guarded against the workspace being absent from tabManager.tabs and returned result = .err(code: "not_found", …) before calling finish. The refactored code drops that guard entirely: if reorderWorkspace(tabId:by:) returns false because the workspace is not in tabs, execution still falls through to finish(["index": v2OrNull(nil)]), sending a success response with null index instead of an error. Adjacent commands close_above and close_below (a few lines below) still retain their not_found guards, making this an inconsistency in the socket API contract. Any automation client that distinguishes the error vs. success-with-null response will silently get wrong behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The behavior remains not_found for an unknown workspace. The enclosing v2MainSync closure already resolves workspace with tabManager.tabs.first(where:) and returns not_found before entering this switch. This is one synchronous @mainactor closure with no suspension point or intervening mutation, so the removed per-case lookup was redundant; reorderWorkspace(tabId:by:) receives the same workspace ID that the outer guard just proved is present. I am leaving the shared reorder path unchanged.

— Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The outer guard in v2WorkspaceAction already resolves workspace from tabManager.tabs inside the same @mainactor v2MainSync closure before entering the switch. Since no suspension occurs between that guard and move_up/move_down, the workspace cannot disappear concurrently; the removed inner guard was redundant. A genuinely missing workspace still returns not_found from the outer guard, while reorderWorkspace returning false here only represents an edge/tier no-op and preserves the existing success contract.

— Claude Code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — the @MainActor guarantee with no suspension between the outer resolution and the switch makes the inner guards provably redundant. The not_found contract is fully preserved by the outer guard, and reorderWorkspace returning false correctly maps to an edge/tier no-op rather than a missing-workspace error. I'll withdraw the concern.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/ContentView+RightSidebarCommandPalette.swift (1)

919-924: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the new shortcut labels to every supported locale. Resources/Localizable.xcstrings already includes ar, bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, and zh-Hant, but shortcut.moveSurfaceLeft.label, shortcut.moveSurfaceRight.label, shortcut.moveWorkspaceDown.label, and shortcut.moveWorkspaceUp.label only have en and ja.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/ContentView`+RightSidebarCommandPalette.swift around lines 919 - 924,
Update the localized entries for shortcut.moveSurfaceLeft.label,
shortcut.moveSurfaceRight.label, shortcut.moveWorkspaceDown.label, and
shortcut.moveWorkspaceUp.label in Localizable.xcstrings so every supported
locale has a translation, including ar, bs, da, de, es, fr, it, km, ko, nb, pl,
pt-BR, ru, th, tr, uk, zh-Hans, and zh-Hant, while preserving the existing en
and ja values.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Sources/ContentView`+RightSidebarCommandPalette.swift:
- Around line 919-924: Update the localized entries for
shortcut.moveSurfaceLeft.label, shortcut.moveSurfaceRight.label,
shortcut.moveWorkspaceDown.label, and shortcut.moveWorkspaceUp.label in
Localizable.xcstrings so every supported locale has a translation, including ar,
bs, da, de, es, fr, it, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, and
zh-Hant, while preserving the existing en and ja values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 14184649-a1af-46e0-a909-0e3a1cccc703

📥 Commits

Reviewing files that changed from the base of the PR and between 1c22c55 and 6d5d8d2.

📒 Files selected for processing (16)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate.swift
  • Sources/ContentView+RightSidebarCommandPalette.swift
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettings.swift
  • Sources/TabManager+AdjacentWorkspaceReordering.swift
  • Sources/TerminalController.swift
  • Sources/Workspace+SurfaceNavigation.swift
  • Sources/cmuxApp.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/ReorderShortcutActionTests.swift
  • skills/cmux-settings/references/shortcut-actions.md
  • web/data/cmux-shortcuts.ts
  • web/data/cmux.schema.json

cmux reload-cloud added 3 commits July 14, 2026 20:36
…ortcuts

# Conflicts:
#	Packages/Shared/CmuxIrohTransport/Tests/CmuxIrohTransportTests/CmxIrohRelayCredentialCoordinatorTests.swift
@austinywang
austinywang merged commit 1ff14b8 into main Jul 15, 2026
23 of 26 checks passed
@vercel
vercel Bot temporarily deployed to Preview – cmux July 15, 2026 09:11 Inactive
austinywang pushed a commit to mvanhorn/cmux that referenced this pull request Jul 15, 2026
reorderPanel(_:by:) computed currentIndex + offset before clamping, so
offset = Int.max / Int.min (exactly what
reorderPanelClampsExtremeOffsetsWithoutOverflow drives) trapped with a
Swift runtime arithmetic-overflow crash and killed the whole
CmuxCanvasUI test process before it printed its summary. Saturate via
addingReportingOverflow, then clamp as before.

This landed broken in manaflow-ai#8080,
which merged while the repo's CI workflows were manually disabled, and
has broken swift-package-tests on main since.
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jul 16, 2026
* Add workspace and surface reorder shortcuts

* Split shortcut helpers out of oversized files

* Add Canvas surface reorder regression test

* Route surface reordering through Canvas layout

* Add no-op Canvas reorder regression test

* Skip Canvas refresh for clamped surface moves

* Fix relay test determinism guard

* Add Canvas reorder overflow regression test

---------

Co-authored-by: cmux reload-cloud <cmux-reload-cloud@users.noreply.github.com>
(cherry picked from commit 1ff14b8)
hhsw2015 pushed a commit to hhsw2015/cmux that referenced this pull request Jul 16, 2026
reorderPanel(_:by:) computed currentIndex + offset before clamping, so
offset = Int.max / Int.min (exactly what
reorderPanelClampsExtremeOffsetsWithoutOverflow drives) trapped with a
Swift runtime arithmetic-overflow crash and killed the whole
CmuxCanvasUI test process before it printed its summary. Saturate via
addingReportingOverflow, then clamp as before.

This landed broken in manaflow-ai#8080,
which merged while the repo's CI workflows were manually disabled, and
has broken swift-package-tests on main since.

(cherry picked from commit 6a00674)

This branch was previously deployed

1 inactive deployment
Preview – cmux — 028aa00c Deployed Jul 15, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose keyboard shortcut for surface/workspace reorder (reuse existing reorderSurface/reorderWorkspace)

1 participant