Restore ⌘W and ⌘Q after the menu built-in collision fix#461
Merged
Conversation
#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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
⌥⌘Hbound togoto_split:leftalso 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 thenperformActionForItem(at:). For SwiftUI-backed command items (Close Terminal, Quit),update()makes SwiftUI rebuild the menu and replace theNSMenuItem, soindex(of:)returned-1, the dispatch silently failed, and the chord fell through tokeyDown.⌘Wand⌘Qstopped working.The fix
⌘⇧,(reload_config) are not eaten by AppKit's menu-matching quirks.NSMenu.performKeyEquivalent(with:)path, which fires SwiftUI command items reliably (noupdate(), no stale index).NSApp.sendActionso the built-in cannot also fire and the app action's own side effects still run. Aclose_surfaceremapped 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.⌥⌘HHide Others vs agoto_splitbinding) 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).