Skip to content

Feature: per language shortcuts - #26

Open
bu6n wants to merge 3 commits into
FrigadeHQ:mainfrom
bu6n:feature/per-language-shortcuts
Open

Feature: per language shortcuts#26
bu6n wants to merge 3 commits into
FrigadeHQ:mainfrom
bu6n:feature/per-language-shortcuts

Conversation

@bu6n

@bu6n bu6n commented Aug 5, 2026

Copy link
Copy Markdown

I came accross the same need as #15 and tried to fix it.

The README says yap shouldn't have too much features. I really like how fast yap is to install and use. It's near-perfect, but I use 3 languages daily so I would really like to have this feature.

Let me know if this PR needs more work.

Disclaimer: Claude Opus 5 has done most of the work here, but I think it makes mostly sense. I have tested the feature and it works nicely from my side.

Couple screenshots:

Screenshot 2026-08-05 at 14 00 28 Screenshot 2026-08-05 at 14 00 44

Here's Claude summary for a bit more details:

Closes #15.

Settings now holds a list of dictation profiles — a language plus the shortcut, modifier tap and function-row key that start it. Whichever trigger fires picks the language for that dictation. Optionally the recording window shows a short tag like DE, off by default.

Migration. Existing settings fold into the first profile, which keeps the name the shortcut has always been stored under, so a recorded combination carries over untouched. The old keys stay put for a downgrade, and nothing changes until you add a second language.

Conflicts. Two profiles can't share a trigger — claiming one takes it from whoever held it. Key combinations needed unregistering while a recorder has focus, since a registered combination is claimed system-wide and never reaches the field. The hook is a notification the library posts but doesn't declare public; a test pins it so a dependency bump fails loudly rather than quietly restoring the bug.

Worth flagging: the issue's ⇧ + Right ⌥ isn't expressible today — Carbon hotkeys need a real key, and the modifier-tap monitor only fires on a clean tap with nothing else held. Each language needs a distinct trigger instead (Right ⌥ / Left ⌥). Happy to follow up with combo support if you'd want it.

Two that look like scope creep but aren't:

  • The resolved-locale cache was a single slot cleared on every language change, so alternating between two languages re-resolved on each switch — the exact lag the cache exists to avoid. Now keyed by locale.
  • OnboardingView hardcoded "Press ⌘⇧D to start"; it now reads whatever is bound. Happy to drop this one.

ModifierHotkeyMonitor's state couldn't represent two modifiers held at once, so the clean-tap rules moved into a pure ModifierTapRecognizer, mirroring the existing FunctionKeyTrigger.shouldFire. RecordingCoordinator is otherwise untouched — it takes the language tag through a closure alongside deviceName. With the tag on, the HUD's waveform gives up its oldest ten bars rather than push the buttons off the fixed-width card.

Verification. 90 tests in 15 suites. Exercised by hand over a real 0.1.9 install: settings migrated with the shortcut intact, two languages dictate from two keys, and combinations move between languages correctly.

bu6n added 3 commits August 5, 2026 11:30
Yap had one language and one set of triggers, so dictating in a second language
meant a trip through Settings each time. Settings now holds a list of dictation
profiles — a language plus the shortcut, modifier tap and function-row key that
start it — and whichever trigger fires picks the language for that dictation.
Existing settings migrate into the first profile, so nothing changes until you
add a second.

Closes FrigadeHQ#15.
With a shortcut per language it is not always obvious which one is listening,
so the window can carry a short tag like DE beside "Listening". Off by default,
since it is only worth the room once you dictate in more than one language.
Resolving a language to its locale moves onto the profile along the way, since
the tag is derived from the same place.

The row is a fixed width, so with the tag on the waveform gives up its oldest
ten bars to make room rather than push the confirm and cancel buttons off the
card. With it off the window draws exactly as before.
Recording a key combination that another language already held did nothing at
all. A registered combination is claimed system-wide, so the system routed the
press to the hotkey and the recorder never saw the keys — it stayed empty and
saved nil. Unregister while a recorder has focus, which is the only moment the
field needs those keys for itself, and the combination can be typed in and taken
from whoever held it.

The hook is a notification KeyboardShortcuts posts without declaring it public,
since it is the only signal that fires per field. Anything window-scoped works
once and then stops, because saving a combination re-registers it immediately.
A test pins the notification so a dependency bump fails loudly rather than
quietly bringing the original bug back.
@christianmat

Copy link
Copy Markdown
Contributor

@bu6n thank you for this PR! For now I recommend using a local build for this and linking this PR to an Issue. If we the Issue gets a good amount of traction, I am happy to review and merge this in as a feature. But as you mention above, I am trying to keep this application as lean and clean as possible and only add features that the majority (or a good chunk) of users will use.

@hvitbardur

Copy link
Copy Markdown

I am trying to keep this application as lean and clean as possible

The app already offers language selection. Letting the user switch languages quickly with a hotkey doesn't make it any less lean or clean.

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.

Multiple shortcuts for different dictation languages

3 participants