Skip to content

Fix Cmd+grave show/hide global hotkey - #6477

Merged
austinywang merged 6 commits into
mainfrom
issue-6435-cmd-grave-global-hotkey
Jun 22, 2026
Merged

austinywang merged 6 commits into
mainfrom
issue-6435-cmd-grave-global-hotkey

Conversation

@austinywang

@austinywang austinywang commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor

Root cause

showHideAllWindows could persist a Cmd+grave binding from Settings / cmux.json, but runtime registration dropped it before calling RegisterEventHotKey. The hardcoded system-wide conflict list always reserved Cmd+grave and Cmd+Shift+grave as macOS window-cycling shortcuts, and SystemWideHotkeyController.refreshRegistration re-runs that conflict check before registering.

I verified the registration path rather than assuming the OS rejects it: a local Carbon probe for keyCode 50 returned status=0 for Cmd+grave, Cmd+Shift+grave, and Opt+grave. Ghostty’s working implementation uses a CGEvent tap for global keybinds, but cmux’s immediate failure was self-inflicted: the configured shortcut was never allowed to reach Carbon registration.

Fix

Allow the Cmd+grave window-cycle pair only for the showHideAllWindows system-wide action. The existing hardcoded reservations remain in place for other global hotkeys, including Global Search, and Cmd+. is still rejected for Show/Hide because it is AppKit’s standard cancel keystroke.

Why this should not break other hotkeys

The change only filters the hardcoded Cmd+grave/Cmd+Shift+grave reservation when the current action being validated is showHideAllWindows. Other system-wide actions still see that reservation, existing cmux action conflict checks still run, and the Carbon keyCode/modifier mapping path is unchanged.

Verification

  • Added a regression test that the reporter’s exact shape (key: "", keyCode: 50, command: true) maps to CarbonHotKeyRegistration(keyCode: 50, modifiers: cmdKey)and is accepted forshowHideAllWindows`.
  • Added coverage that Global Search still rejects the same Cmd+grave reservation.
  • Probed RegisterEventHotKey(50, cmdKey, ...) locally with a tiny Swift snippet: Carbon returned noErr for Cmd+grave.
  • Did not run a local cmux build or local tests per task instruction. The true end-to-end proof needs a running tagged app plus a global keypress from another app, so CI and later dogfood are the validation path.

Closes #6435


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


Summary by cubic

Fixes the global Cmd+show/hide hotkey so it registers and works system-wide on macOS. OnlyshowHideAllWindows can use Cmd+ and Cmd+Shift+`; other actions stay reserved, and Cmd+. remains blocked.

  • Bug Fixes
    • Filtered the hardcoded reservation so Cmd+/Cmd+Shift+ for showHideAllWindows aren’t treated as conflicts; others stay reserved.
    • Moved hotkey policy tests to Swift Testing with isolated UserDefaults and settings store, added teardown to restore state, and covered acceptance for showHideAllWindows, rejection for Global Search, and Carbon keyCode/modifier mapping.

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

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved system-wide keyboard shortcut conflict handling by applying action-specific filtering for command+grave (`) hotkeys—ensuring show/hide all windows accepts valid variants while other actions still respect reserved-shortcut rules.
  • Tests

    • Added a new test suite covering system-wide shortcut policy behavior, including acceptance of command+grave (with and without shift) for show/hide all windows and rejection of command+grave for global search.

@vercel

vercel Bot commented Jun 20, 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 20, 2026 6:23am
cmux-staging Building Building Preview, Comment Jun 20, 2026 6:23am

@coderabbitai

coderabbitai Bot commented Jun 20, 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

reservedSystemWideHotkeyShortcuts(excluding:) in KeyboardShortcutSettings.swift now filters hardcodedSystemWideHotkeyConflicts to exclude Command+Grave entries when the excluded action is .showHideAllWindows. A new test suite, SystemWideHotkeyShortcutPolicyTests, is added and wired into the Xcode project to verify this policy for both showHideAllWindows and globalSearch.

Changes

Cmd+` Hotkey Conflict Fix

Layer / File(s) Summary
Conditional conflict filtering
Sources/KeyboardShortcutSettings.swift
reservedSystemWideHotkeyShortcuts(excluding:) filters hardcodedSystemWideHotkeyConflicts to remove Command+Grave (no Option/Control) entries when the current action is .showHideAllWindows, leaving all other actions unaffected.
Policy tests and project wiring
cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift, cmux.xcodeproj/project.pbxproj
New SystemWideHotkeyShortcutPolicyTests suite manages isolated defaults snapshots and asserts that showHideAllWindows accepts Cmd+\`` (with and without shift) while globalSearchrejects it asreservedBySystem. Helpers manage shortcut construction and defaults capture/restoration. The test file is registered as a PBXBuildFile/PBXFileReference, added to the cmuxUITestsgroup, and included in the target'sPBXSourcesBuildPhase`.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • manaflow-ai/cmux#3908: Directly overlaps with globalSearch system-wide hotkey conflict handling logic in KeyboardShortcutSettings.swift, the same function modified in this PR.

Suggested reviewers

  • lawrencecchen

🐇 Oh, backtick, backtick, where did you go?
Lost in the conflicts, put on quite a show!
Now showHideAllWindows knows you're its own,
While globalSearch must leave you alone.
Cmd+` shall cycle windows with glee—
A bunny fixed the bug, hop hop, yippee! 🎉

🚥 Pre-merge checks | ✅ 22 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (22 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concisely describes the main fix: enabling Cmd+grave for the show/hide global hotkey on macOS.
Linked Issues check ✅ Passed The PR fully addresses issue #6435 by filtering hardcoded Cmd+grave/Cmd+Shift+grave reservations for showHideAllWindows only, enabling the grave keystroke to reach Carbon registration. Tests confirm the exact reporter configuration works and other actions remain unaffected.
Out of Scope Changes check ✅ Passed All changes are narrowly scoped to the hotkey policy fix: filtering the conflict list for showHideAllWindows, adding test infrastructure, and adding comprehensive tests for the grave keystroke behavior.
Cmux Swift Actor Isolation ✅ Passed Production change only refines filtering logic in an existing private function; uses only value types (struct); no new declarations of Sendable, service protocols, or mutable reference types; test...
Cmux Swift Blocking Runtime ✅ Passed The PR introduces zero blocking or timing-based synchronization in production code. Sources/KeyboardShortcutSettings.swift only adds a deterministic filter operation to an existing append call with...
Cmux Expensive Synchronous Load ✅ Passed PR makes only two changes: (1) a simple array.filter() on 5-element static constant in KeyboardShortcutSettings.swift, and (2) a new @MainActor test suite using lightweight UserDefaults/temp files...
Cmux Cache Substitution Correctness ✅ Passed The change modifies validation logic only. hardcodedSystemWideHotkeyConflicts is a compile-time constant, never persisted. The modified reservedSystemWideHotkeyShortcuts() function returns fres...
Cmux No Hacky Sleeps ✅ Passed This check applies only to TypeScript, JavaScript, shell, and non-Swift build/runtime scripts. The PR only modifies Swift code and Xcode project files, which are explicitly out of scope.
Cmux Algorithmic Complexity ✅ Passed The PR modifies a filter on hardcodedSystemWideHotkeyConflicts (5-item constant) and iterates over Action.allCases (~136 fixed enum cases). Both are explicitly bounded tiny fixed-size collectio...
Cmux Swift Concurrency ✅ Passed PR introduces no new legacy async patterns. The Task in KeyboardShortcutSettings is a justified OS callback boundary crossing; test file uses modern Swift Testing without Dispatch/Combine/callbacks.
Cmux Swift @Concurrent ✅ Passed All Swift changes comply with concurrent annotation rules: KeyboardShortcutSettings.swift has no async functions (only modified filter logic), and new test class SystemWideHotkeyShortcutPolicyTests...
Cmux Swift File And Package Boundaries ✅ Passed PR adds only 1 line to oversized KeyboardShortcutSettings.swift as focused bug fix, adds 105-line test file under 400-line limit, maintains clear responsibilities and extraction paths.
Cmux Swiftpm Lockfiles ✅ Passed Xcode project SwiftPM package-reference changes in cmux.xcodeproj/project.pbxproj are accompanied by the root lockfile cmux.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved, meet...
Cmux Swift Logging ✅ Passed The PR modifies production Swift code to filter a hardcoded conflict list and adds test code, but introduces no print/debugPrint/dump/NSLog calls, file logging, or logging violations per swift-logg...
Cmux User-Facing Error Privacy ✅ Passed No user-facing errors, alerts, or messages were added or modified. The PR contains only internal logic changes (a filter condition) and test code. Developer comments reference only technical detail...
Cmux Full Internationalization ✅ Passed PR modifies only logic (filter condition) in production code with no new user-facing text, localization keys, or string catalog changes; test file is properly excluded from i18n requirements.
Cmux Swiftui State Layout ✅ Passed PR contains no SwiftUI Views or state patterns. Changes are in model-layer enum (KeyboardShortcutSettings) and tests, which do not involve @StateObject, @Published, @Observable, or state management.
Cmux Architecture Rethink ✅ Passed Small correctness fix with clear owner (reservedSystemWideHotkeyShortcuts) and invariant; no timing repairs, new state, or architectural debt.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR does not introduce or materially change standalone cmux-owned windows. Changes only modify keyboard shortcut validation logic and add a test-only fixture, both explicitly allowed by the rule.
Cmux Source Artifacts ✅ Passed All changed files are legitimate source artifacts: Sources/KeyboardShortcutSettings.swift (hand-written source code), cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift (hand-written test), and cm...
Cmux No Test Or Debug Seam In Production Source ✅ Passed The only production code change in Sources/KeyboardShortcutSettings.swift is a filter narrowing the hardcoded Cmd+grave conflict reservation for .showHideAllWindows—legitimate product behavior, not...
Description check ✅ Passed The pull request provides a comprehensive description covering the root cause, fix, verification steps, and why the change is safe.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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-6435-cmd-grave-global-hotkey

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 and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 20, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a self-inflicted bug where showHideAllWindows could never be bound to Cmd+grave because the hardcoded system-wide conflict list always reserved it — before Carbon registration was even attempted. The fix narrows that reservation so it applies to all actions except showHideAllWindows, while keeping Cmd+. and Ctrl+Tab reserved unconditionally.

  • Production change (Sources/KeyboardShortcutSettings.swift): a single .filter call in reservedSystemWideHotkeyShortcuts that strips the Cmd+grave / Cmd+Shift+grave entries from hardcodedSystemWideHotkeyConflicts only when currentAction == .showHideAllWindows; all other entries and all other actions are unaffected.
  • New test suite (cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift): Swift Testing suite covering the reporter's exact shortcut shape (keyCode 50, cmdKey) accepted for showHideAllWindows in both the unshifted and shifted directions, plus a regression test confirming globalSearch still rejects Cmd+grave.
  • Project file (cmux.xcodeproj/project.pbxproj): adds the new test file to the test target's Sources build phase only.

Confidence Score: 5/5

Safe to merge — the production change is a single, well-bounded filter predicate that only touches one code path, and the logic is verified correct for every entry in the hardcoded conflict list.

The filter expression correctly removes only the two Cmd+grave entries (unshifted and shifted) from the reserved list for showHideAllWindows, while preserving all other entries unconditionally. Ctrl+Tab entries are kept because $0.control is true; Cmd+. is kept because $0.key != ""is true. All other actions see the full reserved list unchanged. The new test suite exercises both accepted directions forshowHideAllWindowsand the regression case forglobalSearch`, using the same isolated settings scaffold already established in the test target.

No files require special attention.

Important Files Changed

Filename Overview
Sources/KeyboardShortcutSettings.swift One-line filter added to reservedSystemWideHotkeyShortcuts that strips the Cmd+grave and Cmd+Shift+grave entries from hardcodedSystemWideHotkeyConflicts when the action under validation is showHideAllWindows; all other entries and all other actions are unaffected.
cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift New Swift Testing suite covering the fix: showHideAllWindows accepts both Cmd+grave and Cmd+Shift+grave (with Carbon keyCode/modifier mapping verified), globalSearch still rejects Cmd+grave; uses the existing installIsolatedTestFileStore scaffold already present in the test target.
cmux.xcodeproj/project.pbxproj Adds PBXBuildFile and PBXFileReference entries for the new test file and wires it into the test target's Sources build phase; no production target membership or dependency changes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["reservedSystemWideHotkeyShortcuts(excluding: currentAction)"] --> B["Add per-action configured shortcuts"]
    B --> C["Filter hardcodedSystemWideHotkeyConflicts"]
    C --> D{currentAction == showHideAllWindows?}
    D -- No --> E["All hardcoded conflicts reserved\n(Ctrl+Tab, Cmd+grave, Cmd+Shift+grave, Cmd+.)"]
    D -- Yes --> F{"entry.key == '`' &&\nentry.command &&\n!entry.option &&\n!entry.control?"}
    F -- Yes --> G["Removed from reserved\n(Cmd+grave, Cmd+Shift+grave allowed)"]
    F -- No --> H["Kept in reserved\n(Ctrl+Tab, Cmd+. still blocked)"]
    E --> I["Return reserved list"]
    G --> I
    H --> I
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["reservedSystemWideHotkeyShortcuts(excluding: currentAction)"] --> B["Add per-action configured shortcuts"]
    B --> C["Filter hardcodedSystemWideHotkeyConflicts"]
    C --> D{currentAction == showHideAllWindows?}
    D -- No --> E["All hardcoded conflicts reserved\n(Ctrl+Tab, Cmd+grave, Cmd+Shift+grave, Cmd+.)"]
    D -- Yes --> F{"entry.key == '`' &&\nentry.command &&\n!entry.option &&\n!entry.control?"}
    F -- Yes --> G["Removed from reserved\n(Cmd+grave, Cmd+Shift+grave allowed)"]
    F -- No --> H["Kept in reserved\n(Ctrl+Tab, Cmd+. still blocked)"]
    E --> I["Return reserved list"]
    G --> I
    H --> I
Loading

Reviews (5): Last reviewed commit: "test: fix hotkey policy test teardown" | Re-trigger Greptile

Comment thread cmuxTests/GlobalSearchShortcutSettingsTests.swift Outdated
@austinywang
austinywang force-pushed the issue-6435-cmd-grave-global-hotkey branch from b64fa89 to a24bc2b Compare June 20, 2026 03:42

@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)
cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift (1)

56-63: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider testing Cmd+Shift+` rejection as well.

The acceptance test (lines 32-54) verifies both Cmd+` and Cmd+Shift+` for showHideAllWindows. For symmetry and completeness, this rejection test could also verify that globalSearch rejects the shifted variant.

✨ Optional: Add shifted variant test
 `@Test` func globalSearchStillRejectsCommandGraveWindowCyclingHotkey() {
     let shortcut = commandGraveShortcut()
 
     `#expect`(
         KeyboardShortcutSettings.Action.globalSearch.normalizedRecordedShortcutResult(shortcut) ==
             .rejected(.reservedBySystem)
     )
+
+    let shiftedShortcut = commandGraveShortcut(shift: true)
+
+    `#expect`(
+        KeyboardShortcutSettings.Action.globalSearch.normalizedRecordedShortcutResult(shiftedShortcut) ==
+            .rejected(.reservedBySystem)
+    )
 }
🤖 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/SystemWideHotkeyShortcutPolicyTests.swift` around lines 56 - 63,
The test `globalSearchStillRejectsCommandGraveWindowCyclingHotkey` currently
only verifies rejection of the unshifted Cmd+` variant, but for consistency with
the acceptance test (which checks both Cmd+` and Cmd+Shift+` for
`showHideAllWindows`), this test should also verify that `globalSearch` rejects
the shifted Cmd+Shift+` variant. Add an additional test case or expand the
existing test to check that the shifted variant is also rejected with
`.rejected(.reservedBySystem)` result.
🤖 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 `@cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift`:
- Around line 56-63: The test
`globalSearchStillRejectsCommandGraveWindowCyclingHotkey` currently only
verifies rejection of the unshifted Cmd+` variant, but for consistency with the
acceptance test (which checks both Cmd+` and Cmd+Shift+` for
`showHideAllWindows`), this test should also verify that `globalSearch` rejects
the shifted Cmd+Shift+` variant. Add an additional test case or expand the
existing test to check that the shifted variant is also rejected with
`.rejected(.reservedBySystem)` result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0e2a1973-2388-49f0-9cf3-f4b98318c9d3

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf3b00 and 6592e63.

📒 Files selected for processing (1)
  • cmuxTests/SystemWideHotkeyShortcutPolicyTests.swift

@austinywang
austinywang merged commit 95a34f9 into main Jun 22, 2026
29 checks passed

This branch was successfully deployed

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

Cmd + grave / ` global hotkey does not trigger Show/Hide All Windows on macOS

1 participant