Support modifier+key combo hotkeys for quick dictation - #204
Merged
Conversation
Member
Author
|
@jatinkrmalik PTAL! |
Quick dictation was limited to a single key (a lone modifier or a regular key). Adds ⌘Space/⌃Space/⌥Space-style combos while keeping every existing single-key hotkey working exactly as before. - New HotKeyCombo/HotKeyModifiers model for required-modifiers + base key - HotKeyManager's event tap gates the base key on an exact modifier match and force-stops push-to-talk if a required modifier releases early - Settings/Onboarding hotkey recorder captures full combos, tracking held modifiers by physical key (not shared flag) and seeding from live key state so a modifier already held when recording starts is handled correctly - Guards against autorepeat swallowing unrelated keystrokes (e.g. plain spacebar use elsewhere) when a Space-based combo is configured
The recorder used NSEvent.addLocalMonitorForEvents, which only sees keystrokes macOS has already declined to handle itself. Combos the system reserves therefore never arrived — ⌃Space and ⌃⌥Space are bound to input-source switching, ⌘Space to Spotlight — and ⌘-based combos are routed to menu key equivalents first, so whole categories of shortcut (including ⌃⇧Space) could not be recorded at all. The runtime engine already solves this: HotKeyManager uses a session CGEventTap at head-insert, which sees every keystroke before the system does. That asymmetry was the bug — triggering worked while recording did not. The recorder now uses the same mechanism, and consumes the key-down while recording so the shortcut being recorded doesn't also fire its normal action. - Extracts capture into HotKeyComboRecorder, leaving the NSView as a lifecycle shell (start on appear, cancel on window resign) - Falls back to the local monitor when the tap can't be created (no Accessibility permission) so recording degrades rather than dying - Disables the tap synchronously on teardown and defers run loop source removal, so the run loop is never mutated from inside its callback
Mr-Sunglasses
force-pushed
the
feat/hotkey-combo
branch
from
August 12, 2026 05:56
b373e28 to
f973f30
Compare
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.
closes: #132
Summary
HotKeyCombo/HotKeyModifiersmodel (Sources/VocaMac/Models/HotKeyModifiers.swift) represents "required modifiers + base key," stored via a new@AppStorage("vocamac.hotKeyModifiers")field alongside the existinghotKeyCode.HotKeyManager's global event tap now gates a combo's base key on an exact modifier match, and force-stops push-to-talk if a required modifier is released before the base key.CGEventSource.keyState) so a modifier already held down when "Record" is clicked is handled correctly.Test plan
swift build— clean, no warnings introducedswift test— all pre-existing and new tests pass (full suite run 3x for stability; excluded a pre-existing flaky/segfaultingAudioEnginehardware-access suite that reproduces identically on unmodifiedmain)