feat(whisper): add dropdown preset keybinding selector for speech-to-text#447
Open
monotykamary wants to merge 7 commits into
Open
feat(whisper): add dropdown preset keybinding selector for speech-to-text#447monotykamary wants to merge 7 commits into
monotykamary wants to merge 7 commits into
Conversation
…text Replace the single HotkeyRecorder + 'Set to Fn' button in Whisper hotkey settings with a dropdown offering preset keybinding options. This solves the problem where standalone modifier keys (Option, Command, etc.) could not be captured by the recorder since browser keydown events treat them as modifiers rather than keys. Preset options include: Fn (hold-to-talk), Option (hold-to-talk), F5, F6, Option+Space, Option+., Command+., Control+Space, Shift+Space. A 'Custom…' option in the dropdown reveals the HotkeyRecorder for advanced key combos. Backend changes generalize the native CGEventTap-based Fn speak-toggle watcher to support any standalone modifier key as a hold-to-talk trigger (Option/Alt keyCode 58, Command keyCode 55, etc.). The hotkey-hold-monitor.swift binary already handles flagsChanged events for modifier keys, so no native code changes were needed. All 9 locale files updated with new i18n keys (custom, holdToTalkHint) replacing the removed keys (setToFn, fnHint, fnHintBefore, fnHintAfter).
…ix Custom auto-record - Replace the 9 preset list (F5, F6, combos) with the 3 hold-to-talk modifier families: Fn, Left/Right Option, Left/Right Command - Add right-side modifier key codes (61=RightOption, 54=RightCommand) to STANDALONE_MODIFIER_KEYCODES and parseHoldShortcutConfig key map - Update normalizeAccelerator to recognize left/right modifier tokens as modifiers when used in combos (e.g. LeftOption+Space → Alt+Space) - Update isStandaloneAlt/isStandaloneCmd detection for left/right variants - Format display: ⌥←/⌥→/⌘←/⌘→ with directional arrows for side info - Fix Custom option: blur the <select> when Custom is chosen so it stops intercepting keyboard events, and add autoRecord prop to HotkeyRecorder so it auto-starts recording and grabs focus on mount
…ix hooks order The useState for whisperCustomMode was placed after the early return (!settings), causing React to throw 'Rendered more hooks than during the previous render' when settings loaded asynchronously.
The recorder visibility was gated on whisperPresetValue which only reflects the stored hotkey — but selecting 'Custom…' doesn't change the stored value, so the recorder never appeared. Use whisperSelectValue instead, which accounts for the user's explicit Custom selection.
Keep whisperCustomMode=true until the stored hotkey actually updates via IPC instead of clearing it in the recorder's onChange (which fires before the async settings save completes). A 150ms timeout fallback handles the edge case where the recorded value equals the current stored value.
The useEffect for clearing whisperCustomMode was placed after the component-level early return guard, causing the hooks order mismatch again. Derive whisperSpeakToggleHotkey from settings? before the guard and move the effect up with the other hooks.
The previous 150ms timeout-based useEffect fired immediately when custom mode was entered, snapping the dropdown back before the user could record anything. Now custom mode only clears when the HotkeyRecorder's onChange resolves (i.e. after IPC save completes).
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
There was no easy way to change the keybinding for speech-to-text beyond the default Fn key. The HotkeyRecorder component cannot capture standalone modifier keys (Option, Command, etc.) because browser
keydownevents treat them as modifiers rather than keys, making them impossible to record.This PR adds a dropdown with preset keybinding options in the Whisper hotkey settings, with full backend support for standalone modifier keys as hold-to-talk triggers.
Changes
UI —
src/renderer/src/settings/AITab.tsx<select>dropdown with 5 hold-to-talk preset options:UI —
src/renderer/src/settings/HotkeyRecorder.tsxautoRecordprop: when set, the recorder auto-starts in recording mode and grabs focus on mount<select>retained focus and intercepted keyboard events, preventing the recorder from receiving inputDisplay —
src/renderer/src/utils/hyper-key.ts⌥←,⌥→,⌘←,⌘→Backend —
src/main/main.tsSTANDALONE_MODIFIER_KEYCODESmap with both left and right modifier key codes (alt→58, leftoption→58, rightoption→61, command→55, leftcommand→55, rightcommand→54, control→59, shift→56 — macOS virtual key codes)isStandaloneModifierShortcut()andneedsNativeHoldWatcher()helper functionsparseHoldShortcutConfig()key code map with left/right modifier keys, setting the corresponding modifier flag when the key token is a standalone modifier (matching the existing Fn pattern)fnSpeakToggleCurrentShortcutvariable (was previously hardcoded to'Fn'), so the watcher adapts to whichever key the user selectssyncFnSpeakToggleWatcher()to activate for any shortcut needing a native watcher (Fn, Alt/Option, left or right) and properly restart when the shortcut changesregisterCommandHotkeys()andupdateCommandHotkeyIPC handler to skip ElectronglobalShortcutfor standalone modifier shortcuts on the speak-toggle command (handled by native CGEventTap instead)normalizeAccelerator()to recognize left/right modifier tokens as modifiers when used in combos (e.g.LeftOption+Space→Alt+Space)"unavailable"errorNo native code changes needed
The
hotkey-hold-monitor.swiftbinary already handlesflagsChangedevents for modifier keys and accepts any key code + modifier combination via CLI args.Locales — all 9 locale files
setToFn,fnHint,fnHintBefore,fnHintAftercustom("Custom…"),holdToTalkHint(explanatory help text)en.jsonHow to Test