The Hotkey That Is Also a Modifier
Gaithano Speak is push-to-talk dictation. You hold a key anywhere on the system, speak, release, and the polished text lands in whatever had focus. The key is Option.
Option is a bad choice, and I could not find a better one. Every unmodified key is somebody's letter and every function key already belongs to the OS. What Option has going for it is that either thumb can reach it from resting position without moving your hands. It is also how you type ⌥← to jump a word, ⌥e for an accent, ⌥Tab in Raycast.
Provisional, then committed
The mechanism that resolves this is a split. optionDown starts audio capture
provisionally and silently: no sound, no overlay, no media pause. Only after a
minimum hold does the dictation commit and become visible. Any other keypress
inside that window abandons the whole thing. The buffer is discarded, the modifier
passes through, and ⌥← does what ⌥← has always done.
The user never sees the provisional state. That is a requirement rather than an implementation detail, because feedback before commit would flash a recording indicator every time somebody typed an accented character.
Then the threshold went to zero
The minimum hold now defaults to zero, which turns the protection off. ⌥-combos start a dictation again.
That was a decision rather than a regression, and I only made it after living with the other version for a while. A half-second wait before recording begins is worse every single time than a collision that happens occasionally and takes one keystroke to undo.
What I did not do was collapse the two states once the window was empty. The provisional/commit split and the abandon path are still there, still timing-driven, still tested. Raising one number brings the old behaviour back. Deleting the machinery because the current default makes it a no-op would have been the easy read of the code and the expensive one to reverse.
The state machine earns its keep here
All of this lives in a pure reducer: handle(event) -> [Effect], with no I/O and no
clock. Events that depend on timing, like minimum hold elapsed or max duration reached,
come back in from timers the coordinator schedules in response to effects. The
coordinator runs effects against real subsystems. The reducer only decides.
That separation exists for the awkward cases. Double-tap to lock. Option-as-modifier passthrough. Rerouting insertion when a password field takes focus mid-dictation. The four different ways a cancel can arrive. Each one is an event and an effect with a test beside it, instead of a condition bolted onto a method that already does five things.
The rule I hold myself to is that new behaviour arrives as an event/effect pair with a test, never as ad-hoc logic in the coordinator. Six months in, it is why the tricky parts are still legible.