Shortcut
Headless Shortcut; covers VS Code keybindings, React Aria useKeyboard, GitHub Hotkey.
| Package | @vizejs/ui/shortcut |
| Maturity | stable |
| Own the source | vize lib pull shortcut |
| Requires | — |
| Aliases | keyboard shortcuts, hotkeys, keybindings, kbd formatting |
| Covers | VS Code keybindings, React Aria useKeyboard, GitHub Hotkey |
API
createShortcutRegistry
Create an SSR-safe, scope-aware keyboard shortcut registry. The registry resolves Mod for one platform, routes chords and multi-chord sequences to the winning binding, and never touches the DOM on the server: with no ambient document it stays detached until a target appears or events are fed through ShortcutRegistry.input. Call ShortcutRegistry.dispose when using this factory outside a Vue effect scope.
function createShortcutRegistry(options: ShortcutRegistryOptions = {}): ShortcutRegistry
useShortcutRegistry
Create a shortcut registry disposed with the current Vue effect scope.
function useShortcutRegistry(options: ShortcutRegistryOptions = {}): ShortcutRegistry
getShortcutKeycaps
Break a shortcut into display keycaps for <kbd> rendering. The result carries one array per sequence step; each step lists modifier keycaps in platform order followed by the key keycap. Formatting is deterministic for a given platform, so pass an explicit platform when the output is server-rendered.
function getShortcutKeycaps( shortcut: string | ShortcutSequence, options: ShortcutFormatOptions = {}, ): readonly (readonly string[])[]
formatShortcut
Format a shortcut as one display string, e.g. ⇧⌘K or Ctrl+Shift+K. Apple symbol keycaps join without separators; every other combination joins with +. Sequence steps are joined with a single space.
function formatShortcut( shortcut: string | ShortcutSequence, options: ShortcutFormatOptions = {}, ): string
detectShortcutPlatform
Detect the modifier layout of the current environment. Server rendering has no ambient keyboard, so the detector deterministically reports standard there; pass an explicit platform when formatting keycaps into server-rendered markup.
function detectShortcutPlatform(): ShortcutPlatform
readPlatform
function readPlatform(platform: ShortcutPlatform | undefined): ShortcutPlatform
isCharacterKeyValue
True when the key value is a single character rather than a named key.
function isCharacterKeyValue(value: string): boolean
parseShortcut
Parse a shortcut pattern into a normalized chord sequence. Chord steps are separated by whitespace and each step joins modifiers and one key with +, e.g. "Mod+K", "Ctrl+Shift+P", or "G D". Mod resolves to Meta on Apple layouts and Control elsewhere.
function parseShortcut( pattern: string, options: ShortcutParseOptions = {}, ): ShortcutSequence
toShortcutSequence
Validate and freeze a caller-supplied sequence without re-parsing.
function toShortcutSequence( value: string | ShortcutSequence, platform: ShortcutPlatform, ): ShortcutSequence
serializeShortcut
Stable identity string used for routing and conflict detection.
function serializeShortcut(sequence: ShortcutSequence): string
isModifierOnlyEvent
True when the event is a lone modifier press that can never end a chord.
function isModifierOnlyEvent(event: KeyboardEvent): boolean
matchesShortcutChord
True when the event satisfies the chord's key and exact modifier state.
function matchesShortcutChord(event: KeyboardEvent, chord: ShortcutChord): boolean
chordFromEvent
Build the normalized chord described by one keyboard event.
function chordFromEvent(event: KeyboardEvent): ShortcutChord
readOptionalBoolean
function readOptionalBoolean(value: boolean | undefined, name: string): boolean
validateBindingStatics
function validateBindingStatics(options: ShortcutBindingOptions): void
readWhen
function readWhen(binding: InternalShortcutBinding): boolean
chordEquals
function chordEquals(left: ShortcutChord, right: ShortcutChord): boolean
isEditableEventTarget
Detect text-editing targets across light and shadow DOM retargeting.
function isEditableEventTarget(event: KeyboardEvent): boolean
routeChord
Route one accepted chord against the registered bindings. Exact matches win over pending extensions. Among exact matches the binding in the most recently activated scope wins, and within one scope the most recently registered binding wins.
function routeChord( bindings: Iterable<InternalShortcutBinding>, activeScopes: readonly string[], next: readonly ShortcutChord[], context: { readonly isEditable: boolean; readonly isRepeat: boolean }, ): ShortcutRouteDecision
toBindingInfo
function toBindingInfo(binding: InternalShortcutBinding): ShortcutBindingInfo
collectConflicts
Group same-scope bindings that claim one identical normalized sequence.
function collectConflicts( bindings: Iterable<InternalShortcutBinding>, ): readonly ShortcutConflict[]
Behavior
Normative state × input → outcome table for @vizejs/ui/shortcut. Every row is
exercised by src/families/interaction/shortcut/shortcut*.test.ts; compile-only assertions live in
src/families/interaction/shortcut/shortcut.types.test-d.ts.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| S1 | registered chord | matching keydown with exact modifiers | handler receives an immutable match and the native action is canceled | chord test |
| S2 | registered chord | keydown with extra or missing modifiers | nothing dispatches and the event keeps its native action | chord test |
| S3 | Mod pattern |
parsed on apple and standard platforms | Meta and Control resolve respectively and match accordingly | platform test |
| S4 | registered sequence | first chord of the sequence | pending state records the step and later chords complete the handler | sequence test |
| S5 | pending sequence | timeout elapses or a non-continuing key | pending clears; the key is retried as a fresh sequence start | sequence-reset test |
| S6 | shadowed shortcut | scope activated above global | the deepest active scope wins; release restores the earlier routing | scope test |
| S7 | scoped binding, scope inactive | matching keydown | nothing dispatches | scope test |
| S8 | conflicting registrations | identical normalized sequence in one scope | getConflicts groups them; the latest registration wins routing |
conflict test |
| S9 | binding with when gate |
gate resolves false | binding is skipped without consuming the event | enablement test |
| S10 | text-editing target | printable shortcut without allowInEditable |
binding is skipped so typing is preserved, opted-in bindings dispatch | editable-target test |
| S11 | held key | auto-repeated keydown | only allowRepeat chords re-dispatch |
repeat test |
| S12 | modifier-only or IME input | lone modifier keydown or composing event | pending state is preserved and nothing dispatches | filter test |
| S13 | reactive target | target ref resolves or changes | native listeners move to the new target, including shadow roots | target test |
| S14 | reactive disabled | disabled becomes true | pending clears synchronously and later input is ignored | disabled test |
| S15 | invalid pattern or options | malformed pattern, chord, or option value | stable runtime diagnostics reject the misuse | diagnostics test |
| S16 | any pattern | formatted for display | keycaps follow platform order and style deterministically | format test |
| S17 | active registry | dispose or Vue scope stop | listeners and timers release and imperative calls become terminal | lifecycle test |
| S18 | concurrent SSR requests | identical consumers | byte-identical markup contains no listeners or scheduled timers | SSR test |
| S19 | SSR followed by hydration | matching keydown | host identity remains and the shortcut dispatches without warnings | hydration test |
| S20 | DOM, SSR, and Vapor lanes | authored consumer compiles | every renderer accepts the same registry props and formatted keycaps | renderer gate |
| S21 | root and subpath consumer | only shortcut is retained | equal CSS-free bundles exclude unrelated component families | tree-shaking gate |
| S22 | public TypeScript API | mutation or invalid options | compile-only assertions reject misuse | type declaration test |
Accessibility obligation
Shortcuts are accelerators, never the only path: every action reachable
through a shortcut must stay reachable through visible, focusable controls.
Bindings skip text-editing targets by default so shortcuts cannot steal
typing, single-character shortcuts should stay opt-in per WCAG 2.2 §2.1.4
(Character Key Shortcuts), and consumers should surface formatShortcut
output near the control (for example in <kbd> or aria-keyshortcuts) so
users can discover the accelerator. Platform detection is deterministic on
the server; pass an explicit platform when rendering keycaps into SSR markup.