Skip to content

Restore ⌘W and ⌘Q after the menu built-in collision fix#461

Merged
sbertix merged 1 commit into
mainfrom
sbertix/fix-hotkeys-injection
Jun 22, 2026
Merged

Restore ⌘W and ⌘Q after the menu built-in collision fix#461
sbertix merged 1 commit into
mainfrom
sbertix/fix-hotkeys-injection

Conversation

@sbertix

@sbertix sbertix commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

#459 (which closed #450) introduced a regression: it broke ⌘W (Close Terminal) and ⌘Q (Quit), which stopped closing and quitting and instead fell through to the terminal. This restores them while keeping the #459 behavior intact.

Background

#450 reported that a Ghostty binding colliding with a non-remappable macOS menu built-in fired both: ⌥⌘H bound to goto_split:left also triggered Hide Others. #459 fixed that by resolving the specific app-owned menu item for a chord and dispatching only that item, skipping the system built-ins.

The bug

To dispatch the resolved item, #459 called the parent menu's update() and then performActionForItem(at:). For SwiftUI-backed command items (Close Terminal, Quit), update() makes SwiftUI rebuild the menu and replace the NSMenuItem, so index(of:) returned -1, the dispatch silently failed, and the chord fell through to keyDown. ⌘W and ⌘Q stopped working.

The fix

  • Forward to the menu only when the chord resolves to an app-owned item, so Ghostty-only shortcuts like ⌘⇧, (reload_config) are not eaten by AppKit's menu-matching quirks.
  • Common case: dispatch through the native NSMenu.performKeyEquivalent(with:) path, which fires SwiftUI command items reliably (no update(), no stale index).
  • Built-in collision case: fire the resolved item directly via NSApp.sendAction so the built-in cannot also fire and the app action's own side effects still run. A close_surface remapped onto a built-in chord (e.g. ⌘M) keeps its explicit-close bookkeeping instead of falling through to Ghostty, which would reattach a zmx-backed surface rather than close it.
  • A chord with no forwardable item (e.g. ⌥⌘H Hide Others vs a goto_split binding) still falls through to Ghostty so the terminal binding wins, keeping the ⌘⌥H ("Hide Others") fires even when the chord is bound to a terminal action #450 fix.

Tests

Added unit coverage for the extracted matcher and dispatch helpers: exact-modifier matching, implicit-shift fold, system-managed conflict detection, and direct-dispatch on conflict. Full suite green (2056 tests).

#459 dispatched a resolved menu item via `performActionForItem(at:)` after
calling the parent menu's `update()`. For SwiftUI-backed command items
(Close Terminal, Quit) `update()` makes SwiftUI rebuild the menu and replace
the item, so `index(of:)` returned -1, the dispatch silently failed, and the
chord fell through to the terminal. ⌘W and ⌘Q stopped closing and quitting.

Forward to the menu only when the chord resolves to an app-owned item, so
Ghostty-only shortcuts like ⌘⇧, are not eaten by AppKit's menu-matching
quirks. For the common case dispatch through the native
`NSMenu.performKeyEquivalent(with:)` path, which fires SwiftUI command items
reliably. When the chord also collides with a non-remappable macOS built-in
(Hide, Minimize, ...), fire the resolved item directly via `NSApp.sendAction`
so the built-in can't fire too and the app action's own side effects still
run: a remapped close_surface keeps its explicit-close bookkeeping instead of
falling through to Ghostty, which would reattach a zmx surface rather than
close it. A chord with no forwardable item (e.g. ⌥⌘H Hide Others vs a
goto_split binding) still falls through to Ghostty so the terminal binding
wins, keeping the #459 fix intact.
@sbertix sbertix enabled auto-merge (squash) June 22, 2026 22:30
@tuist

tuist Bot commented Jun 22, 2026

Copy link
Copy Markdown

🛠️ Tuist Run Report 🛠️

Builds 🔨

Scheme Status Duration Commit
supacode 2m 20s f3b8d0107

@sbertix sbertix merged commit cb5670f into main Jun 22, 2026
1 check passed
@sbertix sbertix deleted the sbertix/fix-hotkeys-injection branch June 22, 2026 22:37
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.

⌘⌥H ("Hide Others") fires even when the chord is bound to a terminal action

1 participant