Skip to content

Move active surface between panes and create missing splits (#8752) - #8845

Closed
kyl33r wants to merge 4 commits into
manaflow-ai:mainfrom
kyl33r:feat/8752-move-surface-between-panes
Closed

kyl33r wants to merge 4 commits into
manaflow-ai:mainfrom
kyl33r:feat/8752-move-surface-between-panes

Conversation

@kyl33r

@kyl33r kyl33r commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Move the active terminal or browser surface to the previous/next pane or to the pane on the left, right, above, or below.
  • Reuse an existing adjacent pane when one exists.
  • When a directional destination is missing, split the source pane 50/50 in that direction and move the live surface into the new pane.
  • Preserve the source region's orthogonal extent: up/down retain the parent column width; left/right retain the parent row height.
  • Preserve terminal/browser object identity, runtime state, selection, and focus.
  • Keep a valid source pane by replacing Bonsplit's placeholder with a real terminal when the moved surface was the source's only surface.
  • Expose all six actions through configurable shortcuts, the Command Palette, and the View menu.

Behavior

  • Previous/next follows spatial pane order and wraps across existing panes.
  • Left/right/up/down uses Bonsplit adjacency when a destination exists.
  • Left/right/up/down creates an equal directional split when no destination exists.
  • Existing-destination insertion occurs immediately after the destination pane's selected surface.
  • Canvas and remote-tmux mirrors do not mutate the local pane tree.
  • Dock-owned keyboard focus does not move a background workspace surface.
  • New shortcuts remain unbound by default to avoid conflicts with pane-focus shortcuts.

Verification

  • Focused Xcode suite:
    • ./scripts/test-unit.sh -skipPackageUpdates test -only-testing:cmuxTests/MoveSurfaceBetweenPanesTests
    • ** TEST SUCCEEDED **
  • Coverage includes all four directional split positions, 50/50 divider ratios, asymmetric parent-extent preservation in all directions, terminal identity, browser identity, replacement-terminal repair, existing-pane movement, previous/next wrapping, Canvas, remote-tmux guards, shortcut metadata, and production shortcut routing.
  • Tagged Debug build: ./scripts/reload.sh --tag move-pane --launch
  • Tagged app launched and responded through CMUX_TAG=move-pane scripts/cmux-debug-cli.sh workspace list.
  • git diff --check, Swift parser validation, PBX validation, and test-wiring lint passed.
  • Final human interactive verification and demo recording.

Related issue

Supersedes #8751, which GitHub closed automatically when its head branch was renamed to include the issue number.

Closes #8752


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


Summary by cubic

Adds actions to move the active surface between panes. Reuses adjacent panes or creates a 50/50 split when missing, while preserving identity, focus, and pane extents. Six unbound actions are available via shortcuts, Command Palette, and the View menu; closes #8752.

  • New Features

    • Previous/Next pane moves follow stable spatial order and wrap.
    • Left/Right/Up/Down use Bonsplit adjacency; if none, split the source pane 50/50 in that direction and move the live surface.
    • Preserve orthogonal extent; insert after the destination pane’s selected tab; keep the source pane valid by repairing the moving-tab split with a real terminal when needed.
    • No-ops under Canvas layout and remote tmux mirrors.
    • Shortcut labels: “Move Surface Left/Right” → “Reorder Surface Left/Right”.
    • Six new unbound actions: move to previous/next pane and to pane left/right/up/down; available in shortcuts, Command Palette, and View menu.
    • Localizations updated for new labels and actions.
    • Tests cover directional splits, extent preservation, identity/focus, previous/next wrapping, Canvas/remote guards, and shortcut/command metadata.
  • Bug Fixes

    • Play a system beep when a surface move cannot be completed (e.g., disallowed context or Dock-focused cases).

Written for commit a8381b2. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added commands and keyboard shortcuts to move the selected surface to the previous/next pane and adjacent panes (left/right/up/down).
    • When a target adjacent pane isn’t available, the move can create the needed split automatically.
    • Extended command palette and menu entries, including updated “Reorder Surface …” labels (with localization).
  • Bug Fixes
    • Improved focus and selection preservation when moving surfaces between panes.
    • Added audible feedback when a pane move can’t be performed.
  • Tests
    • Added coverage for directional movement, pane splitting behavior, and shortcut/command-palette integration.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds configurable actions to move the active surface to previous, next, or adjacent panes. Movement is exposed through keyboard shortcuts, the command palette, and the View menu, with directional splits created when needed and tests covering movement, focus, layout restrictions, and routing.

Changes

Surface movement

Layer / File(s) Summary
Shortcut and command contracts
Packages/macOS/CmuxSettings/..., Sources/KeyboardShortcutSettings.swift, Resources/Localizable.xcstrings, Sources/ContentView+RightSidebarCommandPalette.swift
Adds six surface-to-pane actions, navigation grouping, localized labels, unbound defaults, and command-palette mappings.
Workspace surface movement
Sources/Workspace+SurfaceNavigation.swift, Sources/Workspace.swift, Sources/TabManager.swift
Adds adjacent and spatial pane movement APIs, insertion ordering, validation, and directional split creation.
Shortcut, palette, and menu wiring
Sources/AppDelegate+AdjacentNavigationShortcut.swift, Sources/ContentView.swift, Sources/cmuxApp.swift
Routes shortcuts and commands to pane movement operations and beeps when movement fails or Dock focus blocks routing.
Movement behavior validation
cmuxTests/MoveSurfaceBetweenPanes.swift, cmux.xcodeproj/project.pbxproj
Registers tests covering transfers, focus, pane collapse, spatial ordering, split creation, unsupported layouts, metadata, and shortcut routing.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AppDelegate
  participant TabManager
  participant Workspace
  participant Bonsplit
  User->>AppDelegate: Trigger surface-to-pane shortcut
  AppDelegate->>TabManager: Request surface movement
  TabManager->>Workspace: Forward movement request
  Workspace->>Bonsplit: Move surface or create directional split
  Bonsplit-->>Workspace: Movement result
  Workspace-->>TabManager: Return success or failure
  TabManager-->>AppDelegate: Report result
Loading

Possibly related PRs

Suggested reviewers: azooz2003-bit, lawrencecchen, austinywang


Important

Pre-merge checks failed

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

❌ Failed checks (5 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Concurrency ❌ Error New signing-secret cache schedules keychain/file lookup on DispatchQueue.global, adding a fresh background-queue async path where an async/actor cache would fit. Replace the callback/schedule cache with an async actor-backed secret loader (or Task-backed cache) and keep any UI hop at the call site only.
Cmux Swift Package Boundaries ❌ Error The pane-move logic was added in app-target Sources/Workspace*.swift/TabManager.swift, but it’s reusable workspace-domain code and fits the existing CmuxWorkspaces seam. Extract the move-planning/insertion logic into Packages/macOS/CmuxWorkspaces (e.g. a WorkspaceSurfaceMovementPlanner over WorkspaceSurfaceTreeReading); keep only menu/AppKit routing in the app target.
Cmux Swift Logging ❌ Error New file-scoped hiddenMacsLog = Logger(...) was added in an @MainActor MobileShellComposite extension file without nonisolated. Change it to nonisolated private let hiddenMacsLog = Logger(...) or reuse an existing nonisolated logger.
Cmux Full Internationalization ❌ Error The new shortcut labels are localized in Swift, but Resources/Localizable.xcstrings adds them only in en/ja while the catalog supports 20 locales. Add translations for every touched key in all supported locales (ar, bs, da, de, es, fr, it, ja, km, ko, nb, pl, pt-BR, ru, th, tr, uk, zh-Hans, zh-Hant).
Cmux No Test Or Debug Seam In Production Source ❌ Error Production sidebar cells add dropIndicatorPaintForTesting DEBUG accessors with no production caller, which is a new test/debug seam. Remove the ForTesting accessors from Sources or move the observation into Tests/ via @testable import; keep any debug-only probing in a dedicated debug-only file.
Docstring Coverage ⚠️ Warning Docstring coverage is 20.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The implementation matches the linked issue's surface-move, split, focus, guard, and shortcut requirements.
Out of Scope Changes check ✅ Passed I don't see unrelated code changes; the diff stays focused on surface movement, UI exposure, and tests.
Cmux Swift Actor Isolation ✅ Passed New move-surface APIs stay on @MainActor Workspace/TabManager, sidebar actions are @MainActor, and shortcut enums remain plain Sendable value types.
Cmux Swift Blocking Runtime ✅ Passed PASS: The new surface-move paths only add guards, direct calls, and NSSound.beep; no new semaphores, sleeps, syncs, locks, or polling were introduced.
Cmux Browser Automation Off-Main ✅ Passed No changed browser automation command was routed through main actor; the patch only adds worker-routed iroh_diag policy/tests, leaving browser.* routing untouched.
Cmux Expensive Synchronous Load ✅ Passed New JSON/JSONL loaders are nonisolated/async or run in Task.detached, and no added RestorableAgentSessionIndex.load() landed on main/UI paths.
Cmux Cache Substitution Correctness ✅ Passed PASS: The only cache use is signingSecretCache, which has cold fallback/preload/readiness gating; the pane-move and snapshot changes don’t swap fresh reads for stale cache.
Cmux No Hacky Sleeps ✅ Passed Only fixed delay is in .github/workflows/ios-testflight.yml, which the rule excludes; no timers/sleeps appear in changed production TS/JS/sh files.
Cmux Algorithmic Complexity ✅ Passed New surface-move paths use single-pass pane/tab lookups; the only nested workspace scan I found in ContentView.swift pre-existed and wasn’t expanded.
Cmux Swift @Concurrent ✅ Passed PASS: The PR only adds synchronous surface-move routing helpers; no new nonisolated async work or @concurrent annotations were introduced.
Cmux Swiftpm Lockfiles ✅ Passed No .gitignore or Package.resolved files changed, and the Package.swift edits add only local path deps with no new external pins or Xcode package-reference changes.
Cmux User-Facing Error Privacy ✅ Passed New user-facing copy is generic (“Move/Reorder Surface …”); failures only use NSSound.beep(), with no vendor/internal IDs or raw upstream errors exposed.
Cmux Swiftui State Layout ✅ Passed Feature diff only adds menu/command-palette wiring in ContentView.swift and cmuxApp.swift; no new ObservableObject/@published, GeometryReader, lazy-row store refs, or render-time writes.
Cmux Architecture Rethink ✅ Passed The move behavior stays centralized in Workspace/TabManager, and the diff adds no sleeps, polling, locks, observers, or split lifecycle ownership.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only adds surface-move actions and menu/shortcut wiring; no new NSWindow/NSPanel/WindowGroup code or cmuxAuxiliaryWindowIdentifiers changes were introduced.
Cmux Source Artifacts ✅ Passed All changed paths are source, tests, localization, or project config; none match artifact/scratch/build-output patterns in the review rule.
Cmux No Ambient Global State ✅ Passed No new ambient global surface was added; the PR adds methods/extensions on existing types, and the only top-level boundary is the existing @main app entry.
Title check ✅ Passed The title clearly describes the main feature: moving the active surface between panes and creating missing splits.
Description check ✅ Passed The description covers summary and verification well, but it omits the demo video, review-trigger block, and checklist sections.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@cursor

cursor Bot commented Jul 24, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@greptile-apps

greptile-apps Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds six new actions — "Move Surface to Previous/Next Pane" and "Move Surface to Pane Left/Right/Up/Down" — to the existing surface-navigation system, delegating to Bonsplit adjacency when a destination pane exists and creating a 50/50 directional split when it does not. The shortcut labels for the existing "Move Surface Left/Right" tab-reordering actions are also renamed to "Reorder Surface Left/Right" to clarify the distinction.

  • New moveSelectedSurfaceToAdjacentPane / moveSelectedSurfaceToPane methods added to Workspace+SurfaceNavigation.swift along with a shared insertionIndexAfterSelectedTab helper; existing private moveSurfaceToAdjacentPane widened to internal so the cross-file extension can call it.
  • All six new actions are unbound by default (nil/.unbound); exposed consistently through shortcut dispatch, the Command Palette (palette.move… command IDs), and the View menu; Canvas layout and remote-tmux mirrors are early-guarded as no-ops.
  • Localization is complete for en and ja; 376-line Swift Testing suite added covering directional splits, 50/50 divider ratios, asymmetric extent preservation, terminal/browser identity, source-pane repair, previous/next wrapping, Canvas/remote guards, and shortcut metadata.

Confidence Score: 5/5

Safe to merge — all six new pane-move actions are unbound by default, both code paths (existing-pane move and split-creation) are guarded against Canvas layout and remote-tmux mirrors, and the test suite covers the full directional matrix.

The implementation is narrowly scoped: two new workspace methods plus thin wiring through TabManager, the shortcut handler, Command Palette, and View menu. Canvas and remote-tmux guards are present at the entry point in each path. The 50/50 split-creation path reuses Bonsplit's movingTab split API, which already handles source-pane repair. Localization is complete for both supported locales. No new ambient globals, no blocking primitives, and no test seams were introduced in production source.

No files require special attention.

Important Files Changed

Filename Overview
Sources/Workspace+SurfaceNavigation.swift Core implementation: adds two new public move helpers and a shared insertionIndexAfterSelectedTab util. Logic for spatial-order wrapping and index computation is correct; Canvas/remote-tmux guards present.
Sources/Workspace.swift Expands moveSurfaceToAdjacentPane from private to internal (needed for cross-file extension), adds Canvas/remote-tmux guards, split-creation path, and clearSplitZoom before all branch points.
Sources/AppDelegate+AdjacentNavigationShortcut.swift Adds six new matchConfiguredDirectionalShortcut branches with a shared performSurfacePaneMoveShortcut helper that guards on dock focus and beeps on failure; extracts repeated preferredMainWindowContextForShortcutRouting call into a single routedTabManager local.
Sources/KeyboardShortcutSettings.swift Registers six new .Action cases; all six new actions return .unbound default; labels for moveSurfaceLeft/moveSurfaceRight renamed to Reorder Surface Left/Right.
Resources/Localizable.xcstrings Adds six new localized string keys for the new actions and updates the two renamed reorder labels; both en and ja translations present for every entry.
Sources/ContentView.swift Registers six new Command Palette contributions (with hasFocusedPanel when guard) and six registry.register command handlers that beep on failure; pattern is consistent with adjacent existing registrations.
Sources/cmuxApp.swift Adds six new View-menu splitCommandButton entries mirroring the Command Palette actions; beep-on-failure pattern matches the adjacent command palette handlers.
Sources/TabManager.swift Adds two thin @discardableResult forwarding methods over selectedWorkspace; straightforward delegation with optional chaining.
Sources/ContentView+RightSidebarCommandPalette.swift Maps six new palette.move command IDs to the corresponding ShortcutAction cases for shortcut-highlight display; all six entries present and correctly named.
cmuxTests/MoveSurfaceBetweenPanesTests.swift 376-line Swift Testing suite; covers existing-pane and split-creation paths for all four directions, identity preservation, source-pane repair, previous/next wrapping, Canvas/remote guards, and shortcut metadata.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Shortcut / Menu / Command Palette"] --> B["TabManager.moveSelectedSurfaceToAdjacentPane or moveSelectedSurfaceToPane"]
    B --> C["Workspace methods"]
    C --> D{Layout mode?}
    D -- Canvas --> E["return false (no-op)"]
    D -- Remote tmux --> E
    D -- Normal --> F{Adjacent pane exists?}
    F -- Yes --> G["moveSurface to target pane"]
    F -- No --> H["bonsplitController.splitPane 50/50"]
    H --> I["focusPane + selectTab + focusPanel"]
    G --> J["Success: return true"]
    I --> J
Loading

Reviews (2): Last reviewed commit: "Merge branch 'manaflow-ai:main' into fea..." | Re-trigger Greptile

@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.

Actionable comments posted: 3

🤖 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.

Inline comments:
In `@cmuxTests/MoveSurfaceBetweenPanesTests.swift`:
- Around line 308-334: Extend MoveSurfaceBetweenPanesTests with a test covering
performSurfacePaneMoveShortcut when the Dock owns keyboard focus: trigger a
pane-move keypress while Dock focus is active, then assert the main-panel pane
tree remains unchanged. Reuse the existing test setup and pane-tree comparison
patterns used for canvas-layout and remote-tmux-mirror guard coverage.

In `@Resources/Localizable.xcstrings`:
- Around line 196270-196276: Update the affected localization entries and their
source labels to “Move Surface Left” and “Move Surface Right” rather than
“Reorder Surface Left/Right.” Restore the corresponding Japanese translations to
describe moving the surface between panes, keeping the labels aligned with the
existing pane-movement action semantics.

In `@Sources/ContentView.swift`:
- Around line 8506-8541: Route all pane-movement handlers through the shared
Dock-aware, window-routed action instead of directly calling tab managers.
Update the six palette handlers in Sources/ContentView.swift lines 8506-8541 and
the corresponding menu handlers in Sources/cmuxApp.swift lines 935-964 to invoke
that same action, preserving failure feedback while preventing movement when
Dock owns focus.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c8d9f9de-b3e8-49c2-b641-ff754d03a0c4

📥 Commits

Reviewing files that changed from the base of the PR and between 915e12c and 1f8f4a2.

📒 Files selected for processing (13)
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Defaults.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
  • Resources/Localizable.xcstrings
  • Sources/AppDelegate+AdjacentNavigationShortcut.swift
  • Sources/ContentView+RightSidebarCommandPalette.swift
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutSettings.swift
  • Sources/TabManager.swift
  • Sources/Workspace+SurfaceNavigation.swift
  • Sources/Workspace.swift
  • Sources/cmuxApp.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/MoveSurfaceBetweenPanesTests.swift

Comment on lines +308 to +334
@Test func shortcutAndCommandPaletteMetadataIsCompleteAndUnambiguous() throws {
let paneMoveActions: [KeyboardShortcutSettings.Action] = [
.moveSurfaceToPreviousPane,
.moveSurfaceToNextPane,
.moveSurfaceToPaneLeft,
.moveSurfaceToPaneRight,
.moveSurfaceToPaneUp,
.moveSurfaceToPaneDown,
]

for action in paneMoveActions {
#expect(KeyboardShortcutSettings.publicShortcutActions.contains(action))
#expect(KeyboardShortcutSettings.settingsVisibleActions.contains(action))
let settingsAction = try #require(ShortcutAction(rawValue: action.rawValue))
#expect(settingsAction.displayName == action.label)
#expect(settingsAction.defaultStroke == nil)
#expect(action.defaultShortcut == .unbound)
}

#expect(ContentView.commandPaletteShortcutAction(forCommandID: "palette.moveSurfaceToPreviousPane") == .moveSurfaceToPreviousPane)
#expect(ContentView.commandPaletteShortcutAction(forCommandID: "palette.moveSurfaceToNextPane") == .moveSurfaceToNextPane)
#expect(ContentView.commandPaletteShortcutAction(forCommandID: "palette.moveSurfaceToPaneLeft") == .moveSurfaceToPaneLeft)
#expect(ContentView.commandPaletteShortcutAction(forCommandID: "palette.moveSurfaceToPaneRight") == .moveSurfaceToPaneRight)
#expect(ContentView.commandPaletteShortcutAction(forCommandID: "palette.moveSurfaceToPaneUp") == .moveSurfaceToPaneUp)
#expect(ContentView.commandPaletteShortcutAction(forCommandID: "palette.moveSurfaceToPaneDown") == .moveSurfaceToPaneDown)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether the Dock-focus guard for pane-move shortcuts has dedicated test coverage elsewhere.
rg -n "performFocusedDockShortcut" -C3 cmuxTests
rg -n "moveSurfaceToPane|moveSurfaceToPreviousPane|moveSurfaceToNextPane" cmuxTests -g '!MoveSurfaceBetweenPanesTests.swift'

Repository: manaflow-ai/cmux

Length of output: 154


🏁 Script executed:

#!/bin/bash
set -u

echo "Repository files matching relevant names:"
fd -a 'MoveSurfaceBetweenPanesTests|AdjacentNavigationShortcut|MoveSurface|PaneMove|DashboardWindow' . | sed 's#^\./##' | head -200

echo
echo "Search for performFocusedDockShortcut outside cmuxTests:"
rg -n "performFocusedDockShortcut|focusedDockShortcut|Dock.own|keyboard focus|focus.*Dock|surface.moveToPane|moveSurfaceToPane|moveSurfaceToPreviousPane" -S . --glob '!cmuxTests/**' --glob '!web/**' --glob '!Packages/**/Tests/**' | head -300

echo
echo "Search relevant tests in cmuxTests:"
rg -n "moveSurfaceToPane|moveSurfaceToPreviousPane|moveSurfaceToNextPane|performFocusedDockShortcut|focusedDockShortcut|Dock" cmuxTests -C 3 --glob '*.swift' | head -300

Repository: manaflow-ai/cmux

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -u

echo "Relevant Swift test/source files (limit names):"
git ls-files | rg '(^|/)(cmuxTests/MoveSurfaceBetweenPanesTests\.swift|AppDelegate\+AdjacentNavigationShortcut\.swift|Move|Pane|Surface|Shortcut).*\.swift$' | head -300

echo
echo "Targeted Dock/pane-move references outside huge binary assets:"
rg -n "performFocusedDockShortcut|focusedDockShortcut|dockFocus|Dock.*focus|moveSurfaceToPane|moveSurfaceToPreviousPane|moveSurfaceToNextPane|move_surface_to|AdjacentNavigationShortcut|move surface" -S \
  --glob '!cmuxTests/**' --glob '!Packages/**/Tests/**' --glob '!web/**' \
  --glob '*.swift' --glob '*.json' --glob '*.md' --glob '*.xcstrings' \
  . | head -300

echo
echo "Test references outside MoveSurfaceBetweenPanesTests.swift:"
rg -n "performFocusedDockShortcut|focusedDockShortcut|Dock.*focus|moveSurfaceToPane|moveSurface to pane|AdjacentNavigationShortcut|moveSurfaceToPreviousPane|moveSurfaceToNextPane" cmuxTests -S --glob '*.swift' | head -300

echo
echo "Search only test function/comments for Dock:"
rg -n "function .*dock|Dock|dock|performFocusedDockShortcut|keyboard focus|background workspace|move background|unbound" cmuxTests -S --glob '*.swift' | head -300

Repository: manaflow-ai/cmux

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -u

echo "Inspect AppDelegate+AdjacentNavigationShortcut.swift:"
cat -n Sources/AppDelegate+AdjacentNavigationShortcut.swift | sed -n '1,120p'

echo
echo "Inspect DockShortcutRouting implementation:"
cat -n Sources/AppDelegate+DockShortcutRouting.swift | sed -n '1,150p'

echo
echo "Locate MoveSurfaceBetweenPanesTests around guards:"
cat -n cmuxTests/MoveSurfaceBetweenPanesTests.swift | sed -n '240,360p'

echo
echo "Focused Dock shortcut tests in MoveSurfaceBetweenPanesTests.swift:"
rg -n "focusedDock|Dock.*focus|background|unbound|handleAdjacentNavigationShortcut|moveSurface" cmuxTests/MoveSurfaceBetweenPanesTests.swift -C 4

echo
echo "Focused Dock shortcut tests elsewhere in cmuxTests:"
rg -n "focusedDock|Dock.*focus|background|unbound|handleAdjacentNavigationShortcut|panes move|moveSurfaceToPane|moveSurfaceToPreviousPane|moveSurfaceToNextPane|moveSurface" cmuxTests -S --glob '*.swift' | head -400

Repository: manaflow-ai/cmux

Length of output: 50372


Add coverage for the Dock-focus pane-move guard.

performSurfacePaneMoveShortcut guards Dock focus before every pane-move shortcut, but the test suite only exercises canvas-layout and remote-tmux-mirror guards. Add a test that a pane move keypress leaves the main-panel pane tree unchanged when the Dock owns keyboard focus.

🤖 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 `@cmuxTests/MoveSurfaceBetweenPanesTests.swift` around lines 308 - 334, Extend
MoveSurfaceBetweenPanesTests with a test covering performSurfacePaneMoveShortcut
when the Dock owns keyboard focus: trigger a pane-move keypress while Dock focus
is active, then assert the main-panel pane tree remains unchanged. Reuse the
existing test setup and pane-tree comparison patterns used for canvas-layout and
remote-tmux-mirror guard coverage.

Comment on lines +196270 to +196276
"value": "Reorder Surface Left"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "サーフェスを左へ移動"
"value": "サーフェスを左へ並べ替え"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the labels aligned with pane movement.

The PR objective and neighboring keys describe moving the active surface to another pane, but these labels now say “Reorder Surface,” which implies rearranging surfaces within the current pane. Restore “Move Surface Left/Right” and the corresponding Japanese translations unless the underlying actions intentionally have different semantics.

Suggested fix
- "value": "Reorder Surface Left"
+ "value": "Move Surface Left"

- "value": "サーフェスを左へ並べ替え"
+ "value": "サーフェスを左へ移動"

- "value": "Reorder Surface Right"
+ "value": "Move Surface Right"

- "value": "サーフェスを右へ並べ替え"
+ "value": "サーフェスを右へ移動"

Also applies to: 196287-196293

🤖 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 `@Resources/Localizable.xcstrings` around lines 196270 - 196276, Update the
affected localization entries and their source labels to “Move Surface Left” and
“Move Surface Right” rather than “Reorder Surface Left/Right.” Restore the
corresponding Japanese translations to describe moving the surface between
panes, keeping the labels aligned with the existing pane-movement action
semantics.

Comment thread Sources/ContentView.swift
Comment on lines +8506 to +8541
registry.register(commandId: "palette.moveSurfaceToPreviousPane") {
guard tabManager.moveSelectedSurfaceToPane(offset: -1) else {
NSSound.beep()
return
}
}
registry.register(commandId: "palette.moveSurfaceToNextPane") {
guard tabManager.moveSelectedSurfaceToPane(offset: 1) else {
NSSound.beep()
return
}
}
registry.register(commandId: "palette.moveSurfaceToPaneLeft") {
guard tabManager.moveSelectedSurfaceToAdjacentPane(.left) else {
NSSound.beep()
return
}
}
registry.register(commandId: "palette.moveSurfaceToPaneRight") {
guard tabManager.moveSelectedSurfaceToAdjacentPane(.right) else {
NSSound.beep()
return
}
}
registry.register(commandId: "palette.moveSurfaceToPaneUp") {
guard tabManager.moveSelectedSurfaceToAdjacentPane(.up) else {
NSSound.beep()
return
}
}
registry.register(commandId: "palette.moveSurfaceToPaneDown") {
guard tabManager.moveSelectedSurfaceToAdjacentPane(.down) else {
NSSound.beep()
return
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Route pane moves through one Dock-aware action path.

The shortcut path blocks pane movement when Dock owns focus, but these palette and menu handlers directly mutate the active workspace. Invoking either while Dock is active can move a background surface. Centralize the operation behind the same Dock-aware, window-routed action.

  • Sources/ContentView.swift#L8506-L8541: replace direct tabManager moves with the shared action.
  • Sources/cmuxApp.swift#L935-L964: invoke that same shared action rather than activeTabManager directly.

As per coding guidelines, “Do not wire the same behavior separately through multiple surfaces; use one shared action path.” The PR objective also requires avoiding movement while Dock owns keyboard focus.

📍 Affects 2 files
  • Sources/ContentView.swift#L8506-L8541 (this comment)
  • Sources/cmuxApp.swift#L935-L964
🤖 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.swift` around lines 8506 - 8541, Route all pane-movement
handlers through the shared Dock-aware, window-routed action instead of directly
calling tab managers. Update the six palette handlers in
Sources/ContentView.swift lines 8506-8541 and the corresponding menu handlers in
Sources/cmuxApp.swift lines 935-964 to invoke that same action, preserving
failure feedback while preventing movement when Dock owns focus.

Source: Coding guidelines

@kyl33r

kyl33r commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

need more attention to this so that this can be released as soon as possible

kyl33r and others added 2 commits July 24, 2026 07:30
…ShortcutAction.swift


add missing doc comments

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@teamleaderleo

Copy link
Copy Markdown
Collaborator

The requested pane-movement behavior is already on main in e53143d, and issue #8752 is closed. Closing this superseded PR.

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.

Move the active surface between panes and create missing directional splits

2 participants