0067: A dictation shortcut you record

On this page

Status: accepted

Context

Dictation (ADR 0057) offered fixed gestures: double-tap or hold fn or Control-Option on macOS, and Ctrl+Win on Windows. Those collide with other uses: macOS's Press 🌐 key to and its own Dictation shortcut, keyboards without an Apple fn key, Windows tools that take Ctrl+Win. People asked to choose their own keys.

Decision

ShortcutKey gains custom, and the shortcut holds the recorded keys in custom: modifiers held (control, alt, shift, meta, either side) and a key by its KeyboardEvent.code (KeyD, F13, AltRight). A modifier's name as the key means that key alone on its side; no key means the modifiers together. The recorded keys stay in config.toml when the user goes back to a fixed shortcut.

  • Names, not key codes. The app records in its own window, where the browser names keys the same way on both systems. ai::keys maps each name to its macOS key code and Windows virtual key; the voice helper turns what the keyboard sends back into names.
  • The same gesture. ai::gesture::Keys hears every key by name and tells the gesture when exactly the recorded keys are down, so double-tap, hold, chords that never count and esc work as before. On Windows a recorded shortcut double-taps or holds like macOS; Ctrl+Win+Space stays Ctrl+Win's.
  • Rules that keep typing working, checked in the app and again by leashd (CustomKeys::check): a key that types alone (letters, digits, Space, arrows) needs a modifier other than Shift; Escape, Caps Lock and fn can't be used; one modifier alone must be on its side.
  • An active tap only when needed. The macOS tap stays listen-only for modifiers and the fixed gestures. A recorded key that isn't a modifier (⌃⇧D, F13) would also reach the app in front, so for it the tap is active and drops only that key's events while its modifiers match; macOS then needs Accessibility as well as Input Monitoring. The Windows hook already could drop keys, and sends its mask key before Alt or Win comes up after the shortcut, as for Ctrl+Win, so menus and Start stay shut.
  • Clashes are named, not refused. Before saving, Settings lists the system and app shortcuts the keys would collide with from a table of each system's defaults (Spotlight, input sources, screenshots, Mission Control, macOS Dictation's double presses, Win shortcuts, Narrator's and the On-Screen Keyboard's Ctrl+Win chords, copy and paste). The user may still use them.

Consequences

  • People with a taken fn or Ctrl+Win, or a keyboard without fn, can dictate without changing system settings.
  • The clash list is Short Leash's knowledge of the defaults, not the system's current settings: a shortcut the user changed in macOS or a third-party app's global shortcut isn't named.
  • An active tap can hold up typing if the helper stalls; macOS turns a slow tap off and Short Leash turns it back on, and only recorded shortcuts with a non-modifier key use one.
  • Keys the browser doesn't report (fn, media keys) can't be recorded; fn stays a fixed choice.