Now: Getting KeyTips ready for release

KeyTips · 30 Oct 2026

Why a keyboard shortcut needs a state machine

Tapping Option is easy to describe and surprisingly hard to detect. Here's the small design that makes sequential shortcuts safe.

“Tap Option, then type H, V, V” sounds simple. But Option is also a key people hold while typing, and a shortcut tool sits between your keyboard and your live workbook. If it guesses wrong, a stray letter lands in a cell.

KeyTips handles this with a small state machine. It has three states, and it’s always in exactly one:

State Meaning What moves it on
Idle Nothing is happening, and keys pass straight through Option goes down, moving it to armed
Armed Option is down, and this might become a tap Releasing Option moves it to active; anything else returns it to idle
Active The tap happened, and it’s collecting a sequence A full match runs the command and returns to idle

If you’ve built a month-end checklist with statuses, you already know this idea. Each item is in one status, and only certain events can move it to the next.

A tap has to be a clean tap

The armed state is what separates a tap from a hold. If you press Option and then another key, as you would to type a special character, it isn’t a tap. The tool goes back to idle and your typing is left alone.

A typo shouldn’t reach the cell

In active mode, a key that doesn’t match anything is swallowed and you stay in the mode:

if holdPrefixes.contains(candidate) || map.commands.contains(where: { $0.keys.hasPrefix(candidate) }) {
    state = .active(prefix: candidate)
}
// An unknown key stays in the mode, so a typo can't land in a cell.
return .swallow

Press the wrong letter, and nothing is typed into your model. It’s a small line that protects something a finance person cares about a lot.

Getting back out

Esc removes the last key typed, and at the start of a sequence it exits. Press another key, such as an arrow, Return or a Command shortcut, and the sequence ends and that key goes through as normal.

Why keep it separate

This logic has no connection to macOS at all. It takes inputs and returns decisions, so it can be tested without opening Office. A test can feed it “Option down, Option up, H” and check the result in an instant.

It’s the same reason a good model keeps inputs and calculations apart: you can check the logic without touching anything live.

← All tips