/** * A canonicalized key string such as `'Ctrl+Shift+P'`, with sequences joined by * single spaces. Produced by `parseKey`/`eventToCanonical` and used verbatim as * the registry's map key. An alias for `string`: it names intent in signatures * and carries no runtime behavior of its own. */ type CanonicalKey = string; /** A shortcut callback. Receives the original `keydown` that completed the sequence. */ type ShortcutHandler = (event: KeyboardEvent) => void; /** Per-registration options accepted by `engine.register` and `useShortcut`. */ type ShortcutOptions = { /** Human-readable label shown in the popup and cheatsheet. */ description?: string; /** Let the shortcut fire while focus is in a text field. Defaults to `false`. */ enableOnInputs?: boolean; /** Higher wins when several entries share the same key string. Defaults to `0`. */ priority?: number; /** Set `false` to disable the shortcut without unregistering. Defaults to `true`. */ enabled?: boolean; /** Keep the shortcut reachable under an active exclusive layer. Defaults to `false`. */ global?: boolean; /** * Layer this registration belongs to. Normally stamped for you by `pushLayer` * / ``. Must be a non-negative integer; anything else warns and * falls back to `0`, which is also the default. */ level?: number; }; /** * A registration once the engine has canonicalized its keys and applied every * default — the resolved record the registry stores and `getAllActive()` * returns. The engine builds these; consumers pass `ShortcutOptions` instead. */ type ShortcutEntry = { id: string; keys: string; handler: ShortcutHandler; description: string | undefined; enableOnInputs: boolean; priority: number; enabled: boolean; level: number; global: boolean; }; /** The canonicalized, defaults-applied form of a `registerGroup` call. */ type GroupEntry = { id: string; prefix: string; description: string; priority: number; level: number; }; /** One popup row: a key that can be pressed next from the current prefix. */ type WhichKeyCandidate = { /** Full key string this row stands for — the pressed prefix plus `nextKey`. */ readonly keys: string; /** The single key to press next, e.g. `'h'` under the prefix `'g'`. */ readonly nextKey: string; /** Label for the row: the group's description, or the leaf shortcut's. */ readonly description: string | undefined; /** True when further keys follow this one, i.e. the row is a prefix, not a leaf. */ readonly isGroup: boolean; }; /** The React-shaped view of `WhichKeySnapshot.popup` returned by `useWhichKeyState`. */ type WhichKeyState = { /** Whether the popup should be rendered. */ readonly visible: boolean; /** Keys pressed so far in the pending sequence. Enforced read-only. */ readonly currentSequence: readonly string[]; /** Rows to offer for the pending sequence. Enforced read-only. */ readonly candidates: readonly WhichKeyCandidate[]; /** Aborts the pending sequence and hides the popup. */ readonly cancel: () => void; }; /** Comparator over full canonical key strings; same contract as `Array.prototype.sort`. */ type KeyComparator = (a: string, b: string) => number; /** * The type of the `sortKeys` option: a built-in ordering or a custom * comparator. Defaults to `'registration'`, which leaves candidates in the * order they were registered. */ type SortMode = 'alphabetical' | 'registration' | KeyComparator; /** * Stores every registration in priority-sorted buckets keyed by canonical key * string (and by group prefix), so several components can bind the same key at * once. * * A bucket's winner is resolved by level, then priority, then latest * registration. An active `exclusive` layer raises a reachability floor: an * entry below its level is unreachable unless it was registered with * `global: true`. * * Exported for diagnostics, where `getAllActive()` is the supported read. The * mutators are driven by the engine — calling them directly bypasses its * bookkeeping. */ declare class ShortcutRegistry { private shortcuts; private groups; private layers; private blockLevelCache; private _version; /** * Monotonic mutation counter, starting at `0`. Bumped by every registration, * every layer activation or deactivation, and any unregistration that * actually removes an entry, and never by a read. Intended as a * memoisation key for a derived view over the registry. */ get version(): number; activateLayer(id: string, level: number, exclusive: boolean): void; deactivateLayer(id: string): void; nextLevel(): number; private blockLevel; private isReachable; register(entry: ShortcutEntry): void; unregister(id: string): void; registerGroup(entry: GroupEntry): void; unregisterGroup(id: string): void; getActive(keys: string): ShortcutEntry | undefined; getActiveGroup(prefix: string): GroupEntry | undefined; /** * Every shortcut currently winning its bucket — what would actually fire * right now, once level, priority, and layer blocking are resolved. The array * is freshly built, but the entries are the live registration objects. */ getAllActive(): ShortcutEntry[]; getActiveCandidates(prefix: string): WhichKeyCandidate[]; /** * Cheap existence check for `getActiveCandidates(prefix).length > 0`. * Runs on every keystroke, so it allocates nothing and exits on the first hit. */ hasCandidates(prefix: string): boolean; private findActive; } /** * Options accepted by `createWhichKey`. Every field is optional, and an invalid * value warns and falls back to its default rather than throwing. */ type WhichKeyOptions = { /** * Milliseconds of inactivity before a partial sequence is cancelled. Defaults * to `500`; a non-finite, negative, or `setTimeout`-overflowing value warns * and falls back to `500`. */ timeoutMs?: number; /** * Key that toggles the cheatsheet. Defaults to `'?'`. `null` disables the * built-in help shortcut silently; `''`, or a string `parseKey` cannot parse, * disables it with a warning. */ helpKey?: string | null; /** Order of candidates in the popup and cheatsheet. Defaults to `'registration'`. */ sortKeys?: SortMode; /** Node `start()` installs the `keydown` listener on. Defaults to `document`. */ target?: Document | HTMLElement; }; /** One cheatsheet row: a shortcut's canonical key string and its label. */ type CheatsheetEntry = { readonly keys: string; readonly description: string | undefined; }; /** * A first key and the shortcuts filed under it. `description` is the label from * a matching `registerGroup`, or `undefined` when no group was registered. */ type CheatsheetGroup = { readonly prefix: string; readonly description: string | undefined; readonly entries: readonly CheatsheetEntry[]; }; /** * The cheatsheet view model returned by `engine.getCheatsheetModel()`. A * shortcut is `standalone` only when it is the sole entry filed under its first * key, its whole key string is that one key, and no group label is registered * for it; everything else is bucketed by first key into `groups`. */ type CheatsheetModel = { readonly standalone: readonly CheatsheetEntry[]; readonly groups: readonly CheatsheetGroup[]; }; /** * Immutable view of the engine's UI state, handed to `subscribe` listeners and * returned by `getSnapshot()`. * * Two invariants consumers depend on: the object identity is stable between * emits (`getSnapshot()` returns the cached snapshot, which is what keeps * `useSyncExternalStore` from re-rendering forever), and the whole tree — * `currentSequence` and `candidates` included — must be treated as read-only. */ type WhichKeySnapshot = { readonly popup: { readonly visible: boolean; readonly currentSequence: readonly string[]; readonly candidates: readonly WhichKeyCandidate[]; }; readonly cheatsheet: { readonly visible: boolean; }; }; /** * Handle returned by `pushLayer`. Its `register` and `registerGroup` behave * like the engine methods of the same name but stamp this handle's `level` onto * each registration and track it, so `pop()` can unregister everything the * handle created and then deactivate the layer. `pop()` is idempotent. */ type LayerHandle = { readonly level: number; register: WhichKeyEngine['register']; registerGroup: WhichKeyEngine['registerGroup']; pop(): void; }; /** The object returned by `createWhichKey`. */ type WhichKeyEngine = { /** * Registers a shortcut and returns its unregister function. An unparseable * `keys` string or a non-function handler warns and returns a no-op * unregister rather than throwing. */ register(keys: string, handler: ShortcutHandler, options?: ShortcutOptions): () => void; /** * Labels a key prefix in the popup and cheatsheet, and returns an unregister * function. An unparseable prefix warns and returns a no-op. */ registerGroup(prefix: string, options: { description: string; priority?: number; level?: number; }): () => void; /** * Activates a layer at an explicit level without owning any registrations, * and returns an idempotent deactivate function. Prefer `pushLayer` unless * you are managing registration lifetimes yourself. */ activateLayer(level: number, exclusive: boolean): () => void; /** Pushes a layer and returns a handle owning everything registered through it. */ pushLayer(options?: { exclusive?: boolean; level?: number; }): LayerHandle; /** Attaches the `keydown` listener to the configured target. Idempotent. */ start(): void; /** Detaches the listener and cancels any in-progress sequence. Idempotent. */ stop(): void; /** Subscribes to state changes; returns an unsubscribe function. */ subscribe(listener: (snapshot: WhichKeySnapshot) => void): () => void; /** * Returns the current snapshot. The identity is cached and changes only when * the state does, so it is safe to hand straight to `useSyncExternalStore`. */ getSnapshot(): WhichKeySnapshot; /** Opens the cheatsheet; no-op if already open. */ openCheatsheet(): void; /** Closes the cheatsheet; no-op if already closed. */ closeCheatsheet(): void; /** Toggles the cheatsheet. */ toggleCheatsheet(): void; /** Cancels any in-progress key sequence and hides the popup. */ cancel(): void; /** Builds the cheatsheet view model — useful for a custom cheatsheet UI. */ getCheatsheetModel(): CheatsheetModel; /** * The underlying registry. Advanced use only, and effectively read-only: the * supported read is `getAllActive()`, and calling the mutators directly * bypasses the engine's bookkeeping. */ readonly registry: ShortcutRegistry; }; /** * Creates a which-key engine: a registry, a matcher, and the snapshot store the * renderers subscribe to. * * No `keydown` listener is bound until `start()` is called. Invalid options * soft-fail — a bad `timeoutMs` warns and falls back to `500`, a bad `helpKey` * warns and leaves the built-in help shortcut disabled — because the React * provider calls this from its render body, where a throw would take down the * consumer's tree. */ declare const createWhichKey: (options?: WhichKeyOptions) => WhichKeyEngine; export { type CanonicalKey as C, type GroupEntry as G, type KeyComparator as K, type LayerHandle as L, type SortMode as S, type WhichKeyEngine as W, type ShortcutHandler as a, type ShortcutOptions as b, type WhichKeyState as c, type CheatsheetEntry as d, type CheatsheetGroup as e, type CheatsheetModel as f, type ShortcutEntry as g, ShortcutRegistry as h, type WhichKeyCandidate as i, type WhichKeyOptions as j, type WhichKeySnapshot as k, createWhichKey as l };