Context
0084 ported the keyboard package askbd, and
0294 renamed it @stackra/shortcuts. The package
handles single-key bindings. Users expect chords, which are sequences of keys such as mod+e mod+e. A chord needs state
across key presses, a timeout and conflict checks before a build. TanStack Hotkeys already provides type-safe bindings, a
cross-platform Mod key, sequences with modifiers and timeouts, scopes, key-state tracking and recording. The package
builds on it rather than writing its own matcher.
Decision
-
Chords.
@stackra/shortcutsaccepts a sequence of keys as well as a single key.mod+e mod+eis one example. The sequence is written in thekeysoption ofShortcut({ keys }). - Waiting for the second key. After the first key of a chord, the shortcut status shows that the key was pressed and that the package waits for the second key. The wait ends after 1.5 seconds. If the second key is unknown, the status shows that the pair is not a command.
- Gen checks. Gen fails on a conflict between two bindings, and on a chord that shadows a single-key binding.
-
Display.
@stackra/shortcutsformats chord labels with a per-platform formatter, which writes⌘on Apple platforms andCtrlelsewhere. The palette UI of each app displays those labels, and the help shows them too. The palette itself is still built per app (0084). -
The driver. On web and desktop, the driver is TanStack Hotkeys, and only
@stackra/shortcutsuses it. Apps never import it. The exact beta pin lands with the driver work, not with this decision. The dependency steward checks the pinned version’s age and licence before it lands. -
The package’s own API. The API of
@stackra/shortcuts, the chord hint UI, gen’s conflict checks and the label formatter sit above the driver. A breaking change in the beta then stays inside one package. - Scope of this decision. Chords are in-app bindings only. The global shortcuts of 0281 are unchanged: they stay in the Electron main process, with their own config and conflict checks.
- Mobile. Hardware keyboards on mobile get a native driver later. This ADR does not add one.
Consequences
- A chord is a binding kind that gen checks at build time, so a conflict fails the build and does not reach a user.
- A breaking change in the beta driver costs one package’s change, not a change in each app.
- The label that a user sees in the palette and the help is the label the binding uses, per platform.
- Apps keep using the package’s API, so they do not learn TanStack Hotkeys.
Enforcement
- planned — the gen conflict and shadowing checks of decision 3.
- planned — tests in
@stackra/shortcutsfor the 1.5 second wait, the unknown-second-key status and the label formatter per platform. - planned — an oxlint boundary rule that refuses the TanStack Hotkeys import outside
@stackra/shortcuts.