Skip to content

Fix Canvas keyboard shortcut routing - #6704

Merged
azooz2003-bit merged 13 commits into
mainfrom
task-fix-canvas-keyboard-shortcuts
Jun 24, 2026
Merged

azooz2003-bit merged 13 commits into
mainfrom
task-fix-canvas-keyboard-shortcuts

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jun 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add Canvas shortcut context gating through workspaceCanvasLayout so Canvas-only shortcuts are active only in Canvas layout across runtime, Settings/package metadata, command palette hints, docs, and schema.
  • Route toggleSplitZoom through Canvas overview when the selected workspace is in Canvas layout, while keeping Bonsplit zoom in split layout.
  • Add focused red/green regression coverage for Canvas shortcut when-clauses and Cmd+Shift+Return routing.

Testing

  • ./scripts/setup.sh
  • swift test --package-path Packages/macOS/CmuxSettings --filter Shortcut
  • python3 -m json.tool web/data/cmux.schema.json >/dev/null && python3 -m json.tool web/messages/en.json >/dev/null && python3 -m json.tool web/messages/ja.json >/dev/null
  • ./scripts/lint-pbxproj-test-wiring.sh
  • xcodebuild -project cmux.xcodeproj -scheme cmux-unit -configuration Debug -destination 'platform=macOS' -derivedDataPath /tmp/cmux-canvas-shortcuts-test -only-testing:cmuxTests/KeyboardShortcutContextTests/testCanvasOnlyShortcutDefaultWhenClausesRequireCanvasLayout -only-testing:cmuxTests/AppDelegateEqualizeSplitsShortcutTests/testCmdShiftReturnInCanvasModeDoesNotToggleBonsplitSplitZoom test\n- git diff --check origin/main...HEAD\n- ./scripts/reload-cloud.sh --tag canvsk (fleet unavailable, fell back to local ./scripts/reload.sh --tag canvsk)\n- Tagged preflight: debug.shortcut.simulate {"combo":"cmd+shift+return"} toggled Bonsplit zoom in split layout, then toggled Canvas overview in Canvas layout while Bonsplit panels stayed visible.\n\n## Notes\n- Regression test is commit 1; implementation is commit 2 so CI shows the intended red/green structure.

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


Summary by cubic

Fixes Canvas keyboard shortcut routing and focus. Shortcuts now respect workspaceCanvasLayout; Cmd+Shift+Return toggles the Canvas overview in Canvas (keeps Bonsplit zoom in split layout), and Cmd+0 resets Canvas zoom only in Canvas, not in browser/markdown.

  • Bug Fixes
    • Split shortcuts (.splitRight/.splitDown) and browser splits open a floating Canvas pane beside the focused pane, honoring the requested direction; browser splits focus the address bar.
    • Surface shortcuts in Canvas: Ctrl+1–9 and next/previous navigate surfaces in workspace order (9 = last); no Bonsplit fallback for 0/1-surface cases; router avoids right-sidebar mode conflicts when surface digits are active.
    • Shortcut availability and hints honor workspaceCanvasLayout; canvasZoomReset is Cmd+0 and scoped away from focused browser/markdown; Equalize Splits in Canvas equalizes widths and heights only; docs/schema add Canvas bindings and workspaceCanvasLayout; command palette hints use full context; tests cover gating, directional placement, surface digits, equalize, and Cmd+Shift+Return/Cmd+0 routing.

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

Review in cubic

Summary by CodeRabbit

Release Notes

  • New Features
    • Added shortcut support for canvas layout actions, including pane creation, overview, reveal, zoom/reset, tidy, alignment, equalize, and distribution.
    • Canvas-aware routing now opens canvas browser/terminal panes and toggles the correct canvas overview behavior when in canvas mode.
  • Bug Fixes
    • Shortcut hint availability now evaluates using the full shortcut context (including canvas layout), improving correct when-clause behavior.
  • Documentation
    • Documented the new workspaceCanvasLayout boolean context key and updated examples for per-action when clauses.
  • Tests
    • Added canvas-mode shortcut routing and availability coverage.

@vercel

vercel Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 24, 2026 12:33am
cmux-staging Building Building Preview, Comment Jun 24, 2026 12:33am

@coderabbitai

coderabbitai Bot commented Jun 23, 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 workspaceCanvasLayout as a new boolean shortcut context key throughout settings, runtime evaluation, and schema. Implements canvas-specific routing for split creation, browser splits, split-zoom toggle, equalize-splits, and surface navigation shortcuts. Extends CanvasPlacer/CanvasModel with a preferredDirection parameter for directional pane placement. Updates the Xcode project, tests, and all 16 locale files.

Changes

Canvas layout shortcut context and routing

Layer / File(s) Summary
workspaceCanvasLayout context key declaration
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutContextKnownKey.swift
Adds the workspaceCanvasLayout enum case and registers it as a boolean-valued key in the valueType switch.
ShortcutContext enum, availability logic, and action mapping
Sources/KeyboardShortcutActionContext.swift, Sources/KeyboardShortcutContext.swift, Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift, Sources/ContentView.swift
Introduces the full ShortcutContext enum with six routing contexts, isAvailable overloads, defaultWhenClause, and overlaps compatibility rules. Wires workspaceCanvasLayout into buildShortcutContext, canvas action default clauses, and command-palette context snapshot evaluation.
Directional canvas pane placement
Packages/macOS/CmuxCanvas/Sources/CmuxCanvas/CanvasPlacer.swift, Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasModel.swift, Sources/Canvas/Workspace+CanvasLayout.swift
Adds preferredDirection to CanvasPlacer.frameForNewPane to reorder cardinal neighbor candidates, threads it through CanvasModel.syncPanes, and extends openNewCanvasPane to accept and forward a direction: CanvasDirection?.
Canvas tab selection and surface navigation
Sources/Canvas/Workspace+CanvasLayout.swift, Sources/Workspace+SurfaceNavigation.swift, Sources/Workspace.swift
Adds selectCanvasTab(at:) with last-tab mapping. Extracts selectNextSurface, selectPreviousSurface, selectSurface(at:), and selectLastSurface into a new extension that branches on .canvas layout; broadens applyTabSelection visibility.
AppDelegate canvas shortcut routing handlers
Sources/AppDelegate+CanvasShortcutRouting.swift, Sources/AppDelegate+EqualizeSplitsShortcut.swift, Sources/AppDelegate.swift
Adds performBrowserSplitShortcut(direction:), performToggleSplitZoomShortcut(tabManager:), and SplitDirection.canvasDirection. Adds canvas equalize branch. Wires split/browser/toggle-zoom shortcuts to canvas paths in AppDelegate; broadens recordGotoSplitZoomIfNeeded and focusBrowserAddressBar access.
Unit and integration tests
cmuxTests/CanvasShortcutContextTests.swift, cmuxTests/AppDelegateSurfaceShortcutRoutingTests.swift
Adds CanvasShortcutContextTests verifying canvas-only action gating and context overlap rules. Adds five canvas-mode AppDelegate tests covering overview routing, directional pane creation, canvas tab selection, and equalize-splits. Updates makeKeyDownEvent to accept a configurable modifiers parameter.
Xcode project registration
cmux.xcodeproj/project.pbxproj
Registers AppDelegate+CanvasShortcutRouting.swift, KeyboardShortcutActionContext.swift, Workspace+SurfaceNavigation.swift, and CanvasShortcutContextTests.swift in PBXBuildFile, PBXFileReference, PBXGroup, and PBXSourcesBuildPhase for the cmux and cmuxTests targets.
Schema allowlist, docs, and i18n
web/data/cmux.schema.json, web/messages/en.json, web/messages/ja.json, web/messages/ar.json, web/messages/bs.json, web/messages/da.json, web/messages/de.json, web/messages/es.json, web/messages/fr.json, web/messages/it.json, web/messages/km.json, web/messages/ko.json, web/messages/no.json, web/messages/pl.json, web/messages/pt-BR.json, web/messages/ru.json, web/messages/th.json, web/messages/tr.json, web/messages/uk.json, web/messages/zh-CN.json, web/messages/zh-TW.json, skills/cmux-settings/references/shortcut-actions.md, Packages/macOS/CmuxSettings/README.md, web/app/[locale]/docs/configuration/page.tsx
Extends schema property allowlist with canvas action ids; adds workspaceCanvasLayout to when predicate descriptions across all 16 locale files. Adds the Canvas section to the shortcut actions reference. Updates the CmuxSettings README when-clause example to use workspaceCanvasLayout.

Sequence Diagram(s)

sequenceDiagram
  participant handleCustomShortcut as AppDelegate.handleCustomShortcut
  participant performToggleSplitZoom as performToggleSplitZoomShortcut
  participant performSplit as performSplitShortcut
  participant performBrowserSplit as performBrowserSplitShortcut
  participant CanvasActionExecutor
  participant Workspace
  participant TabManager

  handleCustomShortcut->>performToggleSplitZoom: .toggleSplitZoom
  alt canvas layout
    performToggleSplitZoom->>CanvasActionExecutor: perform(.toggleOverview)
  else non-canvas
    performToggleSplitZoom->>TabManager: toggleFocusedSplitZoom()
  end

  handleCustomShortcut->>performSplit: direction
  alt canvas layout
    performSplit->>Workspace: openNewCanvasPane(type: .terminal, direction: canvasDirection)
    Workspace->>CanvasModel: syncPanes(preferredDirection: direction)
    CanvasModel->>CanvasPlacer: frameForNewPane(preferredDirection: direction)
  else non-canvas
    performSplit->>TabManager: createTerminalSplit
  end

  handleCustomShortcut->>performBrowserSplit: direction
  alt canvas layout
    performBrowserSplit->>Workspace: openNewCanvasPane(type: .browser, direction: canvasDirection)
  else non-canvas
    performBrowserSplit->>TabManager: createBrowserSplit
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • manaflow-ai/cmux#6105: Both PRs modify canvas pane placement during CanvasModel.syncPanes/CanvasPlacer.frameForNewPane—the main PR adds a preferredDirection parameter and routing for canvas shortcuts, while #6105 changes the same placement loop to cache/update occupied frames to avoid overlaps during sync.
  • manaflow-ai/cmux#6472: Both PRs touch the surface-navigation shortcut flow in AppDelegate—the retrieved PR routes numbered/adjacent shortcuts through the correct window's tabManager, while the main PR adds/updates the underlying Workspace surface-selection methods with canvas-specific behavior.

Suggested reviewers

  • lawrencecchen

🐇 A new key was born today,
workspaceCanvasLayout lights the way!
Panes now leap in the right direction,
Canvas shortcuts pass each inspection. 🎨
Hoppity hop, the context is set —
No split confusion, no wrong-route fret! ✨


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 No Test Or Debug Seam In Production Source ❌ Error recordGotoSplitZoomIfNeeded(tabManager:) in Sources/AppDelegate.swift was widened from private to non-private and is only called from a #if DEBUG block in production source, exposing a test... Move test observation into test target via @testable import after widening only internal state, or isolate the test recording facility in a dedicated debug file. Cite PR#6452 as reference.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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
Title check ✅ Passed The title "Fix Canvas keyboard shortcut routing" directly describes the main change—routing Canvas shortcuts through context gating, which is the primary objective of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR introduces Canvas shortcut routing with proper Swift 6 actor isolation: new ShortcutContext enum is a value type (implicitly Sendable), all new methods are extensions on existing @MainActor type...
Cmux Swift Blocking Runtime ✅ Passed PR introduces no new blocking/timing-based synchronization (semaphores, locks, sleep, asyncAfter, main.sync) in production Swift code. All new files and test files contain clean, non-blocking imple...
Cmux Browser Automation Off-Main ✅ Passed PR does not modify TerminalController.swift or ControlCommandExecutionPolicy.swift; no browser socket automation changes made. Check applies only when browser.* socket command routing is modified.
Cmux Expensive Synchronous Load ✅ Passed No expensive synchronous agent-history loads were added to shortcut evaluation paths. The only new operation in buildShortcutContext is workspace.layoutMode == .canvas, a lightweight enum propert...
Cmux Cache Substitution Correctness ✅ Passed The shortcutEventFocusContextCache is transient (not persisted), event-scoped (cleared per-event), and used only for shortcut routing decisions, not persistence/undo paths. Cache cannot affect sess...
Cmux No Hacky Sleeps ✅ Passed PR contains no new sleep, setTimeout, setInterval, polling, or timing-based race workarounds in production non-Swift code. Changes are exclusively Swift code + documentation/schema/localization data.
Cmux Algorithmic Complexity ✅ Passed PR does not violate algorithmic complexity rules. New code uses O(1) tab selection (direct index/last access), bounded grid scan (hardcoded 8×16), and Set-based duplicate detection. Existing nested...
Cmux Swift Concurrency ✅ Passed All new/modified Swift code in the PR consists of synchronous, simple delegation/routing functions with no legacy async patterns: no fire-and-forget Tasks, DispatchQueue usage, Combine objects, or...
Cmux Swift @Concurrent ✅ Passed PR introduces only synchronous Swift functions; no async functions, @concurrent annotations, or concurrency isolation changes; pre-existing async functions untouched.
Cmux Swift File And Package Boundaries ✅ Passed PR adds 4 small focused Swift files (86, 33, 108, 61 lines) with single responsibilities (shortcut routing, navigation) and achieves net-negative changes to large files (AppDelegate -34, ContentVie...
Cmux Swiftpm Lockfiles ✅ Passed PR includes root Xcode Package.resolved with cmux.xcodeproj changes; cmux-owned package .gitignore files don't ignore Package.resolved; no new external SwiftPM dependencies introduced without lockf...
Cmux Swift Logging ✅ Passed All production code additions/changes comply with logging rules: debug-only cmuxDebugLog calls properly guarded with #if DEBUG, no unguarded print/NSLog, no ad hoc logging.
Cmux User-Facing Error Privacy ✅ Passed All user-facing content follows privacy rules: debug logs are guarded #if DEBUG, new terminology is safe/generic (workspaceCanvasLayout, canvas actions), no credentials/tokens/vendor names exposed,...
Cmux Full Internationalization ✅ Passed PR fully complies with internationalization requirements: all 20 locales in web/i18n/routing.ts have been updated with workspaceCanvasLayout in web/messages/*.json; Swift files contain no user-faci...
Cmux Swiftui State Layout ✅ Passed PR does not violate SwiftUI state layout rules. No new @ObservableObject/@Published/@StateObject, no new GeometryReader usage, no store references in lazy boundaries, and no render-time state mutat...
Cmux Architecture Rethink ✅ Passed PR adds Canvas shortcut routing through clean architectural patterns: unified single-path handlers (performSplitShortcut, performToggleSplitZoomShortcut) with layoutMode branches, no timing/blockin...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds canvas shortcut routing and context gating without creating new standalone windows; canvas panes are SwiftUI views within existing workspace canvas view, not NSWindow/NSPanel/NSWindowContro...
Cmux Source Artifacts ✅ Passed All 35+ modified files are intentional sources (Swift code, tests, docs, config) or localization catalogs; no local artifacts, generated logs, caches, build output, or package-manager downloads det...
Cmux No Ambient Global State ✅ Passed All new code uses extension methods on existing types (AppDelegate, Workspace, SplitDirection, KeyboardShortcutSettings.Action); no top-level functions, global state, singletons, or static-only nam...
Description check ✅ Passed The PR description covers the summary, testing approach, and notes. However, the description lacks explicit sections for a demo video link and a complete checklist.
✨ 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 task-fix-canvas-keyboard-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.

@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: 1

🤖 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 `@Sources/KeyboardShortcutContext.swift`:
- Line 38: The `canvasLayout` case in the KeyboardShortcutContext needs to be
marked as overlapping with focus contexts. Update the `overlaps(_:)` method to
return true when comparing `canvasLayout` with focus-related context cases (such
as browser, markdown, sidebar, or other focus-scoped contexts), since canvas
layout is orthogonal to focus and can coexist with these other contexts. This
ensures that conflict detection properly acknowledges both when-clauses can
match simultaneously and routing order determines the winner.
🪄 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

Run ID: b7b22c7d-4c97-4983-ab90-50e8de97e148

📥 Commits

Reviewing files that changed from the base of the PR and between 1d98dc7 and 02b61f6.

📒 Files selected for processing (13)
  • Packages/macOS/CmuxSettings/README.md
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
  • Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutContextKnownKey.swift
  • Sources/AppDelegate.swift
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutContext.swift
  • cmuxTests/AppDelegateEqualizeSplitsShortcutTests.swift
  • cmuxTests/KeyboardShortcutContextTests.swift
  • skills/cmux-settings/references/shortcut-actions.md
  • web/app/[locale]/docs/configuration/page.tsx
  • web/data/cmux.schema.json
  • web/messages/en.json
  • web/messages/ja.json

Comment thread Sources/KeyboardShortcutContext.swift Outdated
case browserPanel
case markdownPanel
case rightSidebarFocus
case canvasLayout

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

Mark canvasLayout as overlapping focus contexts.

workspaceCanvasLayout is orthogonal to focus: in a canvas workspace, browser, markdown, sidebar, or non-browser contexts can also be true. Leaving overlaps(_:) unchanged lets conflict detection accept the same chord for a canvas action and a focus-scoped action even though both when-clauses can match, so routing order decides the winner.

Proposed fix
         func overlaps(_ other: ShortcutContext) -> Bool {
             if self == .application || other == .application {
                 return true
             }
             if self == other {
                 return true
             }
+            if self == .canvasLayout || other == .canvasLayout {
+                return true
+            }
             // A focused markdown viewer also satisfies `.nonBrowserPanel`, so the
             // two contexts can be active at the same time. Treat them as
             // overlapping so shortcut conflict detection rejects a chord bound to

Also applies to: 150-155

🤖 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/KeyboardShortcutContext.swift` at line 38, The `canvasLayout` case in
the KeyboardShortcutContext needs to be marked as overlapping with focus
contexts. Update the `overlaps(_:)` method to return true when comparing
`canvasLayout` with focus-related context cases (such as browser, markdown,
sidebar, or other focus-scoped contexts), since canvas layout is orthogonal to
focus and can coexist with these other contexts. This ensures that conflict
detection properly acknowledges both when-clauses can match simultaneously and
routing order determines the winner.

@greptile-apps

greptile-apps Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR gates Canvas-only keyboard shortcuts behind the new workspaceCanvasLayout context key, routes Cmd+Shift+Return to toggle Canvas overview (not Bonsplit split-zoom) when the workspace is in canvas layout, and rebinds canvasZoomReset from Cmd+Option+0 to Cmd+0. The change is co-ordinated across the runtime, CmuxSettings package, command palette, schema, docs, and all 20 web locale files.

  • Context key: workspaceCanvasLayout is added to ShortcutContextKnownKey, populated in buildShortcutContext, and carries through both the event-driven shortcut path and the command-palette hint path via the new isAvailable(commandPaletteContext:) overload.
  • Routing: performToggleSplitZoomShortcut and the terminal/browser split handlers are extracted to AppDelegate+CanvasShortcutRouting.swift and branch on layoutMode == .canvas; surface navigation is similarly extracted to Workspace+SurfaceNavigation.swift with canvas order replacing per-pane tab order.
  • Tests: CanvasShortcutContextTests and expanded AppDelegateSurfaceShortcutRoutingTests cover when-clause gating, Cmd+Shift+Return routing, directional pane creation, digit selection (including 9-vs-last), equalize-splits, and Cmd+0 zoom-reset in canvas mode.

Confidence Score: 5/5

The routing logic is well-isolated behind the new workspaceCanvasLayout context key, the two implementation layers produce matching when-clauses, and every changed shortcut path has direct regression coverage.

The context-gating, routing branches, and command-palette hint path are all consistent. All locale files were updated. The canvasZoomReset rebinding to Cmd+0 is correctly scoped away from browser and markdown panels. Tests cover both negative and positive paths for every new routing decision.

No files require special attention.

Important Files Changed

Filename Overview
Sources/AppDelegate+CanvasShortcutRouting.swift New file extracting browser-split and toggle-split-zoom shortcut handlers with canvas layout branching; production callers only, no test seams.
Sources/KeyboardShortcutActionContext.swift New file relocating ShortcutContext enum and adding canvasLayout/canvasLayoutOutsideFocusedContent cases with correct overlaps() and defaultWhenClause() implementations.
Sources/KeyboardShortcutContext.swift Adds workspaceCanvasLayout to buildShortcutContext; ShortcutContext enum moved to KeyboardShortcutActionContext.swift.
Sources/Workspace+SurfaceNavigation.swift New file extracting surface navigation from Workspace.swift, branching on canvas layout for workspace-order navigation rather than per-pane tabs.
Sources/Canvas/Workspace+CanvasLayout.swift selectAdjacentCanvasTab rewritten for workspace-wide surface order; openNewCanvasPane gains preferredDirection and uses anchor panel as placement focus with matching preferred size.
Packages/macOS/CmuxCanvas/Sources/CmuxCanvas/CanvasPlacer.swift frameForNewPane gains preferredDirection parameter; preferred direction tried first among cardinal neighbors without changing the fallback logic.
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutContextKnownKey.swift Adds workspaceCanvasLayout as a Bool context key; correctly placed in the valueType switch.
Sources/AppDelegate+EqualizeSplitsShortcut.swift Canvas branch added: equalize-splits in canvas mode calls equalizeWidths + equalizeHeights via CanvasActionExecutor and returns early.
Sources/ContentView.swift Command palette shortcut hint now uses isAvailable(commandPaletteContext:) which reads workspaceCanvasLayout for correct canvas-mode hint availability.
cmuxTests/CanvasShortcutContextTests.swift New test suite covering when-clause gating, overlap assertions, and Cmd+0/Cmd+Shift+Return routing.
cmuxTests/AppDelegateSurfaceShortcutRoutingTests.swift Expanded with canvas-mode tests; Cmd+Shift+Return routing test now also asserts overviewToggleCount == 1.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Key Event] --> B[shortcutEventFocusContext]
    B --> C{workspaceCanvasLayout?}
    C -- true --> D[canvasSurfaceDigitShortcutIsActive?]
    D -- true --> E[selectSurface canvas order]
    D -- false --> F[rightSidebarModeShortcut?]
    F -- yes --> G[Focus sidebar]
    F -- no --> H[matchConfiguredShortcut router]
    C -- false --> F
    H --> I{toggleSplitZoom?}
    I -- canvas layout --> J[CanvasActionExecutor toggleOverview]
    I -- split layout --> K[toggleFocusedSplitZoom]
    H --> L{splitRight / splitDown?}
    L -- canvas layout --> M[openNewCanvasPane preferred direction]
    L -- split layout --> N[createSplit / createBrowserSplit]
    H --> O{equalizeSplits?}
    O -- canvas layout --> P[equalizeWidths + equalizeHeights]
    O -- split layout --> Q[bonsplit equalize]
    H --> R{canvasZoomReset Cmd+0?}
    R -- canvas no browser/markdown --> S[CanvasViewport resetZoom]
    R -- browser focused --> T[browserZoomReset]
    R -- markdown focused --> U[markdownZoomReset]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Key Event] --> B[shortcutEventFocusContext]
    B --> C{workspaceCanvasLayout?}
    C -- true --> D[canvasSurfaceDigitShortcutIsActive?]
    D -- true --> E[selectSurface canvas order]
    D -- false --> F[rightSidebarModeShortcut?]
    F -- yes --> G[Focus sidebar]
    F -- no --> H[matchConfiguredShortcut router]
    C -- false --> F
    H --> I{toggleSplitZoom?}
    I -- canvas layout --> J[CanvasActionExecutor toggleOverview]
    I -- split layout --> K[toggleFocusedSplitZoom]
    H --> L{splitRight / splitDown?}
    L -- canvas layout --> M[openNewCanvasPane preferred direction]
    L -- split layout --> N[createSplit / createBrowserSplit]
    H --> O{equalizeSplits?}
    O -- canvas layout --> P[equalizeWidths + equalizeHeights]
    O -- split layout --> Q[bonsplit equalize]
    H --> R{canvasZoomReset Cmd+0?}
    R -- canvas no browser/markdown --> S[CanvasViewport resetZoom]
    R -- browser focused --> T[browserZoomReset]
    R -- markdown focused --> U[markdownZoomReset]
Loading

Reviews (8): Last reviewed commit: "Keep Canvas zoom test hook private" | Re-trigger Greptile

Comment thread web/messages/en.json
},
"shortcuts": {
"when": "Optional per-action context predicates (VS Code-style `when` clauses), keyed by cmux action id. Each value is a boolean expression over context keys combined with !, &&, ||, and parentheses. Boolean keys: sidebarFocus, browserFocus, markdownFocus, terminalFocus, commandPaletteVisible, terminalFindVisible. Typed keys support comparisons: the string sidebarMode (files, find, sessions, feed, or dock) and the integers paneCount and workspaceCount. Comparison operators are ==, !=, =~ (regex), <, <=, >, >=, and `in [a, b]`; an unknown or absent key reads as false. The boolean literals true and false are also accepted; `key == false` is the same as `!key`. The action's shortcut only fires (and only conflicts with other shortcuts) when the clause holds. Examples: { \"selectWorkspaceByNumber\": \"!sidebarFocus\" } selects workspaces with Ctrl+1–9 everywhere except when the right sidebar is focused; { \"selectSurfaceByNumber\": \"sidebarMode == 'find' && paneCount > 1\" } scopes a binding to the Find sidebar when the workspace has multiple panes.",
"when": "Optional per-action context predicates (VS Code-style `when` clauses), keyed by cmux action id. Each value is a boolean expression over context keys combined with !, &&, ||, and parentheses. Boolean keys: sidebarFocus, browserFocus, markdownFocus, terminalFocus, commandPaletteVisible, terminalFindVisible, workspaceCanvasLayout. Typed keys support comparisons: the string sidebarMode (files, find, sessions, feed, or dock) and the integers paneCount and workspaceCount. Comparison operators are ==, !=, =~ (regex), <, <=, >, >=, and `in [a, b]`; an unknown or absent key reads as false. The boolean literals true and false are also accepted; `key == false` is the same as `!key`. The action's shortcut only fires (and only conflicts with other shortcuts) when the clause holds. Examples: { \"selectWorkspaceByNumber\": \"!sidebarFocus\" } selects workspaces with Ctrl+1–9 everywhere except when the right sidebar is focused; { \"selectSurfaceByNumber\": \"sidebarMode == 'find' && paneCount > 1\" } scopes a binding to the Find sidebar when the workspace has multiple panes.",

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 Missing locale updates for workspaceCanvasLayout in 18 message files

The shortcuts.when description key was updated in en.json and ja.json to include workspaceCanvasLayout in the boolean-keys list, but 18 other locale files (ar, bs, da, de, es, fr, it, km, ko, no, pl, pt-BR, ru, th, tr, uk, zh-CN, zh-TW) still have the old text ending with terminalFindVisible only. Users reading the docs in any of those locales will not see workspaceCanvasLayout listed among the available boolean context keys and will be unable to use it in their shortcuts.when clauses without guessing.

Rule Used: Flag production user-facing text that is not fully... (source)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Current head db2b10f updates all supported locale message files. Verification: 20 returns 20, and passes.

— Claude Code

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in the current head d695c60: verified every web/messages/*.json shortcuts.when string now includes workspaceCanvasLayout, and the latest Greptile review for d695c60 reports all locale files updated with no files requiring special attention.

— Claude Code

Comment on lines +89 to +98
#if DEBUG
XCTAssertTrue(appDelegate.debugHandleCustomShortcut(event: event))
#else
XCTFail("debugHandleCustomShortcut is only available in DEBUG")
#endif
XCTAssertFalse(
workspace.bonsplitController.isSplitZoomed,
"In canvas mode, the split-zoom shortcut should drive canvas overview instead of Bonsplit zoom"
)
}

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.

P2 Test only guards the negative case; canvas overview firing is unverified

testCmdShiftReturnInCanvasModeDoesNotToggleBonsplitSplitZoom asserts that bonsplitController.isSplitZoomed stays false after the shortcut fires, but it does not assert that canvas overview actually toggled. If workspace.canvasModel.viewport is nil at test time (common for a newly created window before layout), CanvasActionExecutor.perform(.toggleOverview) silently returns false, debugHandleCustomShortcut still returns true, and all assertions pass — hiding the regression the test intends to catch. A complementary assertion on the canvas overview state (or XCTAssertTrue(canvasExecutorResult)) is needed to make the positive path observable.

@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: 2

🤖 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 `@Sources/AppDelegate.swift`:
- Around line 14567-14574: The canvas browser split path using
`openNewCanvasPane(...)` returns immediately without calling
`focusBrowserAddressBar(panelId:)`, while the non-canvas path applies this
function. This creates inconsistent omnibar focus behavior between layouts.
Refactor the canvas path to capture the result from `openNewCanvasPane(...)`
and, when successful, ensure `focusBrowserAddressBar(panelId:)` is called with
the appropriate panel identifier before returning, so both canvas and non-canvas
paths maintain consistent focus behavior.

In `@Sources/Canvas/Workspace`+CanvasLayout.swift:
- Around line 193-203: The selectCanvasTab(at:) function incorrectly applies the
1-indexed shortcut convention (9 = last) directly to its zero-based parameter,
causing index 8 to always select the last tab regardless of actual tab count.
Remove the special case logic that maps index 8 to tabs.count - 1 in
selectCanvasTab(at:), keeping the function purely zero-based, and move the index
conversion logic (handling 9 = last) to the caller (likely a shortcut handler or
Workspace.selectSurface(at:)) so it performs the necessary conversion before
invoking selectCanvasTab(at:).
🪄 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

Run ID: af074529-7462-4ff7-8de9-74b6548493e2

📥 Commits

Reviewing files that changed from the base of the PR and between 02b61f6 and 0fca436.

📒 Files selected for processing (13)
  • Packages/macOS/CmuxCanvas/Sources/CmuxCanvas/CanvasPlacer.swift
  • Packages/macOS/CmuxCanvasUI/Sources/CmuxCanvasUI/CanvasModel.swift
  • Sources/AppDelegate+CanvasShortcutRouting.swift
  • Sources/AppDelegate+EqualizeSplitsShortcut.swift
  • Sources/AppDelegate.swift
  • Sources/Canvas/Workspace+CanvasLayout.swift
  • Sources/ContentView.swift
  • Sources/KeyboardShortcutActionContext.swift
  • Sources/KeyboardShortcutContext.swift
  • Sources/Workspace.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxTests/AppDelegateEqualizeSplitsShortcutTests.swift
  • cmuxTests/CanvasShortcutContextTests.swift

Comment thread Sources/AppDelegate.swift Outdated
Comment thread Sources/Canvas/Workspace+CanvasLayout.swift Outdated
@azooz2003-bit
azooz2003-bit merged commit 73581e9 into main Jun 24, 2026
37 checks passed
@azooz2003-bit
azooz2003-bit deleted the task-fix-canvas-keyboard-shortcuts branch June 24, 2026 03:20

This branch was successfully deployed

1 active deployment
Preview – cmux — d695c603 Deployed Jun 24, 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.

1 participant