Skip to content

Fix foreground scope for Global Search shortcut - #8699

Merged
austinywang merged 141 commits into
mainfrom
issue-8561-global-search-background-hotkey
Jul 27, 2026
Merged

austinywang merged 141 commits into
mainfrom
issue-8561-global-search-background-hotkey

Conversation

@austinywang

@austinywang austinywang commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Make Global Search application-scoped: its configurable shortcut is handled by cmux's AppKit local key monitor only while cmux is active.
  • Keep Carbon registration exclusive to the explicitly enabled Show/Hide All Windows hotkey.
  • Route menu, foreground shortcut, visible-popover toggle, settings, persistence, and conflict validation through shared policy/action paths.
  • Preserve remaps, chords, focused input/editor ownership, settings live reload, and input-source changes.
  • Audit the same global-input mechanisms: the only production global NSEvent monitor is mouse-only, there is no production keyboard event tap, and Show/Hide All Windows is the sole RegisterEventHotKey owner.

Fixes #8561

Testing

Honest infrastructure caveats

Demo Video

  • Edited 32-second full-display review copy: artifacts/issue-8561-global-search-background-hotkey/final-interaction-5f811c13/final-cua-5f811c13/recording-v4/edited.mp4 (SHA-256 338b15c5526ec079607578c60a643861125d3e8d880a73aa6c06d01568bfe572)
  • Raw uncut 118.75-second full-display recording: artifacts/issue-8561-global-search-background-hotkey/final-interaction-5f811c13/final-cua-5f811c13/recording-v4/recording.mov (SHA-256 953ca36e8a283b1505462fb40662587f9ccc48111d773da5b8d5a4c0e9de28ad)
  • Contact sheet and machine-readable action/state ledger are beside the recordings under final-cua-5f811c13.

The requested /cmux-assets root was read-only on this machine, so the verified branch-scoped worktree artifact fallback was used.

Review Trigger (Copy/Paste as PR comment)

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • I tested the change locally and on isolated macOS runners
  • I added or updated behavior-level tests
  • I updated shortcut/configuration documentation in English and Japanese
  • Review bots evaluated the final SHA
  • All code review bot comments are resolved
  • All human review comments are resolved

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented Jul 22, 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

Global Search is reclassified as application-scoped, while Show/Hide All Windows remains the sole system-wide hotkey. Registration, shortcut routing, persistence, parsing, tests, documentation, and foreground UI validation are updated accordingly.

Changes

Shortcut scope and routing

Layer / File(s) Summary
Shortcut scope contracts
Sources/KeyboardShortcutSettings.swift, Sources/KeyboardShortcutSettingsFileStore.swift, skills/cmux-keyboard-shortcuts/SKILL.md, web/data/cmux-shortcuts.ts
System-wide handling is limited to Show/Hide All Windows; Global Search normalization, persistence, parsing documentation, and scope descriptions now reflect application scope.
Single system-wide hotkey registration
Sources/KeyboardShortcutSettings.swift
SystemWideHotkeyController uses one Carbon hotkey registration and toggles application visibility only.
Application-scoped Global Search routing
Sources/AppDelegate.swift, Sources/App/MenuBarExtraController.swift, Sources/App/ShortcutBareStartRouting.swift
Global Search is routed through AppDelegate, excluded from system-wide chord handling, and delegated from the menu-bar action.
Shortcut policy and foreground validation
cmuxTests/*, cmuxUITests/GlobalSearchForegroundScopeUITests.swift, Sources/Search/MenubarSearchPopover.swift, cmux.xcodeproj/project.pbxproj
Tests validate parsing, system-wide policy, and foreground behavior; the search field receives an accessibility identifier and the UI test is added to the project.

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

Sequence Diagram(s)

sequenceDiagram
  participant KeyboardEvent
  participant AppDelegate
  participant GlobalSearchPalette
  KeyboardEvent->>AppDelegate: Match configured globalSearch shortcut
  AppDelegate->>GlobalSearchPalette: toggleGlobalSearchPalette()
Loading

Possibly related issues

  • manaflow-ai/cmux-dev-artifacts#5425 — Adds foreground-scope UI coverage for the Global Search shortcut behavior addressed by this change.
  • manaflow-ai/cmux-dev-artifacts#5426 — Adds a failing-test scenario covering Global Search foreground scoping.
🚥 Pre-merge checks | ✅ 24 | ❌ 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 (24 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address #8561 by making Global Search app-scoped and leaving only the enabled system-wide hotkey to Carbon registration.
Out of Scope Changes check ✅ Passed The touched code, docs, and tests all support the foreground-scope shortcut fix and do not introduce unrelated scope.
Cmux Swift Actor Isolation ✅ Passed Changed prod code keeps UI work on @MainActor and uses Task { @MainActor } for hotkey callbacks; no new Sendable/shared-mutable actor debt appears.
Cmux Swift Blocking Runtime ✅ Passed PASS: The production diff only changes shortcut routing/policy; no new sleeps, semaphores, sync waits, or locks. The only waiter added is XCTWaiter in a UI test helper.
Cmux Browser Automation Off-Main ✅ Passed The checked-out commit only adds a UI test; no browser.* socket automation files or routing paths were changed.
Cmux Expensive Synchronous Load ✅ Passed Touched production Swift changes only reroute shortcut handling; no added RestorableAgentSessionIndex.load() or agent-log/JSON parsing on main or interactive paths.
Cmux Cache Substitution Correctness ✅ Passed PASS: the new bare-start cache is routing-only, falls back on cold lookup, and invalidates on KeyboardShortcutSettings.didChangeNotification.
Cmux No Hacky Sleeps ✅ Passed No covered non-Swift runtime diff adds fixed sleeps, polling, or delayed dispatch; only deterministic Swift XCTest waits were introduced.
Cmux Algorithmic Complexity ✅ Passed The diff only reroutes shortcut handling and narrows the system-wide set; no new nested scans over scalable user data were introduced, and the remaining Action.allCases scans are fixed-size.
Cmux Swift Concurrency ✅ Passed The diff only renames/reroutes shortcut handling and adds XCTest UI waits; no new DispatchQueue, Task, Combine, or completion-handler async patterns were introduced.
Cmux Swift @Concurrent ✅ Passed The diff only adjusts synchronous shortcut routing/UI tests; no changed Swift hunk adds @concurrent, nonisolated async, or unsafe UI-isolated heavy async work.
Cmux Swift Package Boundaries ✅ Passed The touched Swift changes are app-lifecycle/UI glue and Carbon/AppKit hotkey routing; they’re tightly coupled to AppDelegate, menu wiring, and SwiftUI views, not standalone reusable domain logic.
Cmux Swiftpm Lockfiles ✅ Passed The PR only adds a UI test to cmux.xcodeproj; no SwiftPM package refs, Package.resolved, or .gitignore lockfile changes are present.
Cmux Swift Logging ✅ Passed PASS: The only logging hunk is a #if DEBUG cmuxDebugLog in KeyboardShortcutSettings.swift; no new production print/NSLog/file logging or unsafe Logger changes were introduced.
Cmux User-Facing Error Privacy ✅ Passed No changed user-facing error/alert/recovery copy leaks vendor or internal details; the patch only adjusts shortcut logic, docs, and tests.
Cmux Full Internationalization ✅ Passed No new unlocalized user-facing Swift text was added; the only copy change is existing en/ja web data, while other edits are tests, docs, or internal routing.
Cmux Swiftui State Layout ✅ Passed The only SwiftUI diff is an accessibilityIdentifier on an existing TextField; no new ObservableObject/@published, GeometryReader, lazy-row store refs, or render-time writes.
Cmux Architecture Rethink ✅ Passed Global Search is routed through one AppDelegate-owned action, and the Carbon controller now owns only show/hide windows; no new timing hacks or split owners appear.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: the PR only changes an NSPopover-backed global search UI and shortcut routing/tests; no new NSWindow/NSPanel/WindowGroup or cmux.* identifier ownership changes.
Cmux Source Artifacts ✅ Passed No changed path matches the rule’s artifact buckets; all diffs are intentional source, tests, docs, config, or localization data.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No production Sources/ diff adds test/debug-only seams; the changes are a shortcut refactor and a UI-test accessibility identifier.
Cmux No Ambient Global State ✅ Passed No new top-level globals or singletons were introduced; AppDelegate.shared and SystemWideHotkeyController.shared already existed pre-patch.
Title check ✅ Passed The title clearly summarizes the main change: restricting Global Search to the foreground.
Description check ✅ Passed The description follows the template with summary, testing, demo video, review trigger, and checklist sections filled in.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8561-global-search-background-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.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates Global Search from a system-wide Carbon RegisterEventHotKey to an AppKit local key monitor, scoping it to foreground-only receipt while preserving the existing showHideAllWindows as the sole Carbon owner.

  • Scope change: SystemWideHotkeyController is simplified to a single hotkey/action pair for showHideAllWindows; globalSearch is retired from Carbon and is now handled by a new AppDelegate+VisibleGlobalSearchShortcutRouting extension that routes foreground key events through the shared shortcut policy.
  • Policy unification: A new ShortcutBindingPolicyResult type in the CmuxSettings package centralizes per-action validation (chord allowed, bare-first-stroke, media-key rejection, system-reserved like Cmd+.) so the app target, settings UI, and persistence layer share one truth.
  • Observer modernisation: KeyboardShortcutSettingsObserver is rewritten with @Observable (replacing ObservableObject/@Published), gains a cached globalSearchShortcut slot updated on settings-change and input-source-change notifications, and injects all dependencies for testing.

Confidence Score: 5/5

Safe to merge — the architectural change is internally consistent, all edge cases are covered by the existing policy and test suites, and no correctness-critical paths were left unguarded.

The migration from Carbon to AppKit for Global Search is well-bounded: SystemWideHotkeyController is narrowed to a single action, the new routing extension rolls back chord state on non-applicable events, the CmuxSettings package policy centralises per-action validation that was previously scattered, and the managedShortcutActions set correctly propagates managed-but-explicitly-null state through multi-source merging. The @StateObject to @State change for the now-@observable observer is correct. No production blocking primitives, ambient global state, test seams, or unlocalized strings were introduced.

Files Needing Attention: No files require special attention. The most complex path — routeVisibleGlobalSearchShortcutFromLocalMonitor's speculative chord-state promotion with rollback — is straightforward to audit and is covered by the visible-popover test suite.

Important Files Changed

Filename Overview
Sources/KeyboardShortcutSettings.swift Major refactor: removes globalSearch from SystemWideHotkeyController (Carbon), splits globalSearch and showHideAllWindows handling in resolvedRecordedShortcutIgnoringConflicts, adds isSystemWideHotkey predicate, and fixes legacy migration data-read ordering. Logic is sound.
Sources/KeyboardShortcutSettingsObserver.swift New file: replaces ObservableObject-based observer with @observable, adds cached globalSearchShortcut slot, and observes input-source-change distributed notifications. All dependencies are injected; deinit properly removes observers.
Sources/App/AppDelegate+VisibleGlobalSearchShortcutRouting.swift New file: implements speculative chord-state promotion with rollback for the visible palette's local monitor, and a shared routeVisibleGlobalSearchShortcut used by both the palette monitor and handleCustomShortcut. Logic is consistent and correct.
Sources/AppDelegate.swift Significant reorganisation of handleCustomShortcut: IME marked-text bypass promoted above command-palette check, global search routing inserted before generic chord arming, and browserResponderHasMarkedText renamed to shortcutResponderHasMarkedText. PendingConfiguredShortcutChord widened to internal for use by the new routing extension.
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutBindingPolicyResult.swift New file: defines ShortcutBindingPolicyResult enum and effectivePersistedShortcut resolution logic shared across Settings UI, persistence, and app target. showHideAllWindows fails closed when candidate is invalid; other actions fall back to the built-in default.
Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction+Validation.swift New file: per-action media-key and system-reserved (Cmd+.) rejection predicates. Replaces the hardcoded Cmd+. entry in the app-target's hardcodedSystemWideHotkeyConflicts array, keeping the protection in the shared package where all consumers see it.
Sources/KeyboardShortcutSettings+PersistedShortcutPolicy.swift New file: bridges app-target action validation to the shared CmuxSettings package policy, implements effectivePersistedShortcut for the app target, and defines the conflict arbitration rule (globalSearch yields to showHideAllWindows Carbon registration). Clear and well-isolated.
Sources/KeyboardShortcutSettingsFileStore.swift Adds managedShortcutActions set to ResolvedSettingsSnapshot so managed-but-explicitly-null state is tracked separately from the shortcuts dictionary, enabling isManagedByFile to return true even when the stored binding is nil. fillMissingSettings correctly propagates this state during multi-source merging.
Sources/Search/MenubarSearchPopover.swift Palette's local monitor now delegates to routeVisibleGlobalSearchShortcutFromLocalMonitor before falling through to handleKeyEvent; arrow-key result navigation is guarded to bare arrow (no modifiers) to avoid eating modifier+arrow word-nav shortcuts.
Sources/Search/GlobalSearchKeyEvent+QueryEditing.swift New file: queryOwnsEditingShortcut encapsulates text-field ownership rules (nav/deletion keys, Cmd-editing characters, Ctrl-Emacs, Option/bare printable input), replacing the narrower isTextEditingCommand in MenubarSearchPopover.
Sources/KeyboardShortcutSettings+SystemWideHotkeyConflicts.swift Extracts systemWideHotkeyConflicts (previously private) with an alsoExcluding parameter so globalSearch can be excluded when validating showHideAllWindows candidates. Removes Cmd+. from hardcodedSystemWideHotkeyConflicts; protection is now in ShortcutAction+Validation.swift in CmuxSettings.
Sources/cmuxApp.swift @StateObject replaced with @State for KeyboardShortcutSettingsObserver.shared — correct because the type is now @observable rather than ObservableObject.
web/data/cmux-shortcuts.ts Updates globalSearch note from system-wide hotkey to when cmux is active to reflect the new foreground-only scope. Both English and Japanese locales correctly updated.

Sequence Diagram

sequenceDiagram
    participant User
    participant Carbon as Carbon (OS)
    participant LocalMon as NSEvent LocalMonitor
    participant PopoverMon as MenubarSearchPopover Monitor
    participant AppDel as AppDelegate (handleCustomShortcut)
    participant Observer as KeyboardShortcutSettingsObserver
    participant Palette as GlobalSearchCoordinator

    Note over Carbon: showHideAllWindows only
    User->>Carbon: Configured hotkey (background)
    Carbon->>AppDel: toggleApplicationVisibilityFromGlobalHotkey()

    Note over LocalMon: globalSearch — foreground only
    User->>LocalMon: Key event (cmux active)
    LocalMon->>AppDel: handleCustomShortcut(event)
    AppDel->>Observer: globalSearchShortcut (cached)
    Observer-->>AppDel: StoredShortcut
    AppDel->>AppDel: routeVisibleGlobalSearchShortcut()
    alt Palette visible
        AppDel->>Palette: isPaletteVisible() → true
        alt queryOwnsEditingShortcut
            AppDel-->>LocalMon: .queryOwnsEvent → pass through
        else shortcut matches
            AppDel->>Palette: toggleGlobalSearchPalette() (close)
            AppDel-->>LocalMon: .handled → consume
        end
    else Palette not visible
        AppDel->>Palette: toggleGlobalSearchPalette() (open)
        AppDel-->>LocalMon: return true (consume)
    end

    Note over PopoverMon: Visible palette's own local monitor
    User->>PopoverMon: Key event (palette open)
    PopoverMon->>AppDel: routeVisibleGlobalSearchShortcutFromLocalMonitor()
    AppDel-->>PopoverMon: .handled / .queryOwnsEvent / .notApplicable
Loading

Reviews (109): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

@cursor

cursor Bot commented Jul 23, 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.

@cursor

cursor Bot commented Jul 23, 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.

@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 `@cmuxUITests/GlobalSearchForegroundScopeUITests.swift`:
- Around line 30-60: Ensure
testBackgroundGlobalSearchShortcutIsDeliveredToFinder captures the diagnostic
screenshot on every exit by registering attachScreenshot(named:
"background-shortcut-delivered-to-finder") with defer at the start of the test,
and remove the later conditional call so assertion failures still trigger
capture.
🪄 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: 581c3f3d-5559-498a-a427-9e78afd05821

📥 Commits

Reviewing files that changed from the base of the PR and between 4a2aaca and 15dd2f9.

📒 Files selected for processing (1)
  • cmuxUITests/GlobalSearchForegroundScopeUITests.swift

Comment thread cmuxUITests/GlobalSearchForegroundScopeUITests.swift Outdated
@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.

@cursor

cursor Bot commented Jul 26, 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.

Comment thread Sources/Search/GlobalSearchKeyEvent+QueryEditing.swift
@cursor

cursor Bot commented Jul 26, 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.

cmux reload-cloud added 6 commits July 26, 2026 13:26
…rch-background-hotkey

# Conflicts:
#	Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
#	Packages/macOS/CmuxSettingsUI/Sources/CmuxSettingsUI/Bindings/ShortcutListModel.swift
#	Packages/macOS/CmuxSettingsUI/Tests/CmuxSettingsUITests/ShortcutListModelTests.swift
#	Sources/AppDelegate.swift
…rch-background-hotkey

# Conflicts:
#	skills/cmux-keyboard-shortcuts/SKILL.md
@cursor

cursor Bot commented Jul 27, 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.

cmux reload-cloud added 3 commits July 27, 2026 13:03
…rch-background-hotkey

# Conflicts:
#	Packages/macOS/CmuxSettings/Sources/CmuxSettings/Values/ShortcutAction.swift
#	Sources/KeyboardShortcutSettings.swift
@austinywang
austinywang merged commit 5115654 into main Jul 27, 2026
14 of 15 checks passed
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.

Global search shortcuts triggers even when in the background – breaks it in other apps

1 participant