@remcostoeten/use-shortcut 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,415 +1,5 @@
1
- /** Supported runtime OS identifiers used by formatter and parser normalization. */
2
- declare const OS: {
3
- readonly MAC: "mac";
4
- readonly WINDOWS: "windows";
5
- readonly LINUX: "linux";
6
- };
7
- type PlatformType = (typeof OS)[keyof typeof OS];
8
- /** Public platform constant alias (`Platform.MAC`, `Platform.WINDOWS`, `Platform.LINUX`). */
9
- declare const Platform: {
10
- readonly MAC: "mac";
11
- readonly WINDOWS: "windows";
12
- readonly LINUX: "linux";
13
- };
14
- /**
15
- * Detect the current OS platform for modifier normalization and display formatting.
16
- * Result is memoized for the page lifecycle.
17
- */
18
- declare function detectPlatform(): PlatformType;
19
- /** Canonical modifier token names used internally across parsing/formatting. */
20
- declare const ModifierKey: {
21
- readonly META: "meta";
22
- readonly CTRL: "ctrl";
23
- readonly ALT: "alt";
24
- readonly SHIFT: "shift";
25
- };
26
- type ModifierKeyType = (typeof ModifierKey)[keyof typeof ModifierKey];
27
- /** Alias map from user-facing modifier tokens to canonical modifier keys. */
28
- declare const ModifierAliases: Record<string, ModifierKeyType>;
29
- /** Alias map from human shortcut key tokens to `KeyboardEvent.key`-compatible values. */
30
- declare const SpecialKeyMap: Record<string, string>;
31
- /** Platform-specific display labels/symbols for modifier keys. */
32
- declare const ModifierDisplaySymbols: Record<PlatformType, Record<ModifierKeyType, string>>;
33
- /** Platform-specific canonical order for modifier rendering and combo normalization. */
34
- declare const ModifierDisplayOrder: Record<PlatformType, ModifierKeyType[]>;
35
-
36
- /** Lowercase letter keys a-z */
37
- type AlphaKey = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
38
- /** Number keys 0-9 */
39
- type NumericKey = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
40
- /** Function keys F1-F12 */
41
- type FunctionKey = "f1" | "f2" | "f3" | "f4" | "f5" | "f6" | "f7" | "f8" | "f9" | "f10" | "f11" | "f12";
42
- /** Arrow and navigation keys */
43
- type NavigationKey = "up" | "down" | "left" | "right" | "arrowup" | "arrowdown" | "arrowleft" | "arrowright" | "home" | "end" | "pageup" | "pagedown";
44
- /** Special action keys like Enter, Escape, Tab */
45
- type SpecialKey = "enter" | "return" | "escape" | "esc" | "space" | "tab" | "backspace" | "delete" | "del" | "insert";
46
- /** Symbol and punctuation keys */
47
- type SymbolKey = "minus" | "plus" | "equal" | "equals" | "bracketleft" | "bracketright" | "backslash" | "slash" | "/" | "comma" | "period" | "semicolon" | "quote" | "backtick";
48
- /**
49
- * All valid action keys that can be used with `.key()`
50
- * @example $.mod.key("s") // "s" is an ActionKey
51
- */
52
- type ActionKey = AlphaKey | NumericKey | FunctionKey | NavigationKey | SpecialKey | SymbolKey;
53
- /** Modifier key names used in the chainable API */
54
- type ModifierName = "ctrl" | "shift" | "alt" | "cmd" | "mod";
55
- /** Internal modifier state flags */
56
- type ModifierFlags = {
57
- ctrl: boolean;
58
- shift: boolean;
59
- alt: boolean;
60
- cmd: boolean;
61
- };
62
- /** Modifier key state from a keyboard event */
63
- type ModifierState = {
64
- meta: boolean;
65
- ctrl: boolean;
66
- alt: boolean;
67
- shift: boolean;
68
- };
69
- /** Result of parsing a shortcut string */
70
- type ParsedShortcut = {
71
- modifiers: ModifierState;
72
- key: string;
73
- original: string;
74
- };
75
- type EmptyModifiers = {};
76
- /**
77
- * Handler function called when a shortcut is triggered
78
- * @param event - The keyboard event that triggered the shortcut
79
- */
80
- type ShortcutHandler = (event: KeyboardEvent) => void;
81
- /**
82
- * Custom predicate for excluding shortcuts in certain conditions
83
- * @param event - The keyboard event to evaluate
84
- * @returns `true` to skip the shortcut, `false` to allow it
85
- */
86
- type ExceptPredicate = (event: KeyboardEvent) => boolean;
87
- /**
88
- * Built-in exception presets for common scenarios
89
- * - "input" - Skip when focused on input, textarea, or select
90
- * - "editable" - Skip when focused on contentEditable elements
91
- * - "typing" - Skip in any text input context (combines input + editable)
92
- * - "modal" - Skip when a modal/dialog is open (checks [data-modal] or [role="dialog"])
93
- * - "disabled" - Skip when focused element is disabled
94
- */
95
- type ExceptPreset = "input" | "editable" | "typing" | "modal" | "disabled";
96
- /** Scope selector used to enable/disable subsets of shortcuts at runtime. */
97
- type ShortcutScope = string | string[];
98
- /** Conflict metadata emitted when two registered shortcuts overlap. */
99
- type ShortcutConflict = {
100
- combo: string;
101
- existingCombo: string;
102
- reason: "exact" | "sequence-prefix";
103
- };
104
- /**
105
- * Options for shortcut handler registration
106
- */
107
- type HandlerOptions = {
108
- /** Prevent the browser's default action (default: `true`) */
109
- preventDefault?: boolean;
110
- /** Stop event propagation */
111
- stopPropagation?: boolean;
112
- /** Delay handler execution in milliseconds */
113
- delay?: number;
114
- /** Description for documentation/debugging */
115
- description?: string;
116
- /** Disable this specific shortcut */
117
- disabled?: boolean;
118
- /** Conditions to skip the shortcut */
119
- except?: ExceptPreset | ExceptPreset[] | ExceptPredicate;
120
- /** Required named scopes that must be active */
121
- scopes?: ShortcutScope;
122
- /** Timeout in ms for multi-step sequences */
123
- sequenceTimeout?: number;
124
- /** Higher priority handlers run first (default: 0) */
125
- priority?: number;
126
- /** Stop evaluating other handlers for this combo when matched */
127
- stopOnMatch?: boolean;
128
- };
129
- /**
130
- * Result object returned when registering a shortcut
131
- * Provides control over the shortcut and display information
132
- */
133
- type ShortcutResult = {
134
- /** Remove the keyboard listener */
135
- unbind: () => void;
136
- /** Platform-aware display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows) */
137
- display: string;
138
- /** Normalized combo string (e.g., "cmd+s" or "g d") */
139
- combo: string;
140
- /** Programmatically trigger the shortcut handler */
141
- trigger: () => void;
142
- /** Whether the shortcut is currently enabled */
143
- isEnabled: boolean;
144
- /** Enable the shortcut (after being disabled) */
145
- enable: () => void;
146
- /** Temporarily disable the shortcut */
147
- disable: () => void;
148
- /** Subscribe to shortcut attempt events (useful for visual feedback) */
149
- onAttempt?: (callback: (matched: boolean, event: KeyboardEvent) => void) => () => void;
150
- };
151
- /**
152
- * Chainable modifier builder with type-safe exhaustion
153
- * Each modifier can only be used once in a chain
154
- */
155
- type ModifierChain<Used extends Partial<ModifierFlags>> = {
156
- ctrl: Used["ctrl"] extends true ? never : ModifierChain<Used & {
157
- ctrl: true;
158
- }>;
159
- shift: Used["shift"] extends true ? never : ModifierChain<Used & {
160
- shift: true;
161
- }>;
162
- alt: Used["alt"] extends true ? never : ModifierChain<Used & {
163
- alt: true;
164
- }>;
165
- cmd: Used["cmd"] extends true ? never : ModifierChain<Used & {
166
- cmd: true;
167
- }>;
168
- mod: Used["cmd"] extends true ? never : ModifierChain<Used & {
169
- cmd: true;
170
- }>;
171
- key: <K extends ActionKey>(key: K) => KeyChain<K>;
172
- in: (scopes: ShortcutScope) => ModifierChain<Used>;
173
- };
174
- /**
175
- * Chain state after calling `.key()` - ready to attach a handler
176
- */
177
- type KeyChain<Key extends string> = {
178
- /** Attach a handler to this shortcut */
179
- on: (handler: ShortcutHandler, options?: HandlerOptions) => ShortcutResult;
180
- /** Attach a handler with inline options */
181
- handle: (options: HandlerOptions & {
182
- handler: ShortcutHandler;
183
- }) => ShortcutResult;
184
- /** Add exception conditions before attaching handler */
185
- except: (condition: ExceptPreset | ExceptPreset[] | ExceptPredicate) => KeyChainWithExcept<Key>;
186
- /** Add required named scopes */
187
- in: (scopes: ShortcutScope) => KeyChain<Key>;
188
- /** Add the next step in a sequence */
189
- then: <K extends ActionKey | string>(key: K) => KeyChain<`${Key} ${K}`>;
190
- };
191
- /**
192
- * Chain state after calling `.except()` - ready to attach handler
193
- */
194
- type KeyChainWithExcept<Key extends string> = {
195
- on: (handler: ShortcutHandler, options?: Omit<HandlerOptions, "except">) => ShortcutResult;
196
- in: (scopes: ShortcutScope) => KeyChainWithExcept<Key>;
197
- then: <K extends ActionKey | string>(key: K) => KeyChainWithExcept<`${Key} ${K}`>;
198
- };
199
- /** Options for `ShortcutBuilder.record()` and low-level recording flows. */
200
- type ShortcutRecordingOptions = {
201
- target?: HTMLElement | Window | null;
202
- eventType?: "keydown" | "keyup";
203
- timeoutMs?: number;
204
- };
205
- /**
206
- * The main shortcut builder interface returned by `useShortcut()`
207
- */
208
- type ShortcutBuilder = ModifierChain<EmptyModifiers> & {
209
- ctrl: ModifierChain<{
210
- ctrl: true;
211
- }>;
212
- shift: ModifierChain<{
213
- shift: true;
214
- }>;
215
- alt: ModifierChain<{
216
- alt: true;
217
- }>;
218
- cmd: ModifierChain<{
219
- cmd: true;
220
- }>;
221
- mod: ModifierChain<{
222
- cmd: true;
223
- }>;
224
- key: <K extends ActionKey>(key: K) => KeyChain<K>;
225
- /** Set required scopes for upcoming chain calls */
226
- in: (scopes: ShortcutScope) => ShortcutBuilder;
227
- /** Update active scopes at runtime */
228
- setScopes: (scopes: ShortcutScope) => void;
229
- /** Enable one scope */
230
- enableScope: (scope: string) => void;
231
- /** Disable one scope */
232
- disableScope: (scope: string) => void;
233
- /** Return currently active scopes */
234
- getScopes: () => string[];
235
- /** Check if a scope is active */
236
- isScopeActive: (scope: string) => boolean;
237
- /** Record the next key combo */
238
- record: (options?: ShortcutRecordingOptions) => Promise<string>;
239
- };
240
- /**
241
- * Options for the `useShortcut` hook
242
- */
243
- type UseShortcutOptions = {
244
- /** Enable debug logging to console */
245
- debug?: boolean;
246
- /** Global delay for all handlers in milliseconds */
247
- delay?: number;
248
- /** Skip shortcuts when focused on input elements (default: `true`) */
249
- ignoreInputs?: boolean;
250
- /** Target element for keyboard listeners (default: `window`) */
251
- target?: HTMLElement | Window | null;
252
- /** Keyboard event type to listen for (default: "keydown") */
253
- eventType?: "keydown" | "keyup";
254
- /** Globally disable all shortcuts from this hook */
255
- disabled?: boolean;
256
- /** Active named scopes. Shortcuts with scopes only run when at least one matches. */
257
- activeScopes?: ShortcutScope;
258
- /** Global timeout in ms for sequence completion */
259
- sequenceTimeout?: number;
260
- /** Warn when conflicting shortcuts are registered (default: true) */
261
- conflictWarnings?: boolean;
262
- /** Custom conflict callback */
263
- onConflict?: (conflict: ShortcutConflict) => void;
264
- /** Global event filter; return false to skip all shortcuts for the event */
265
- eventFilter?: (event: KeyboardEvent) => boolean;
266
- };
267
- /** Single shortcut-map entry used by `registerShortcutMap` and `useShortcutMap`. */
268
- type ShortcutMapEntry = {
269
- keys: string | string[];
270
- handler: ShortcutHandler;
271
- options?: HandlerOptions;
272
- };
273
- /** Bulk registration shape mapping action ids to key+handler definitions. */
274
- type ShortcutMap = Record<string, ShortcutMapEntry>;
275
- /** Return type for map registrations, keyed by the same ids as the source map. */
276
- type ShortcutMapResult<T extends ShortcutMap = ShortcutMap> = {
277
- [K in keyof T]: ShortcutResult;
278
- };
279
- /** Imperative grouping controller for binding/unbinding many shortcut registrations together. */
280
- type ShortcutGroup = {
281
- add: (...results: ShortcutResult[]) => void;
282
- addMany: (results: ShortcutResult[] | Record<string, ShortcutResult>) => void;
283
- unbindAll: () => void;
284
- clear: () => void;
285
- getResults: () => ShortcutResult[];
286
- };
287
-
288
- /**
289
- * Parse a shortcut string into its components
290
- *
291
- * @param shortcut - Shortcut string (e.g., "cmd+s", "ctrl+shift+p")
292
- * @returns Parsed shortcut with modifiers, key, and original string
293
- *
294
- * @example
295
- * ```ts
296
- * const parsed = parseShortcut("cmd+s")
297
- * // { modifiers: { meta: true, ... }, key: "s", original: "cmd+s" }
298
- * ```
299
- */
300
- declare function parseShortcut(shortcut: string): ParsedShortcut;
301
- /**
302
- * Parse multiple shortcut strings
303
- *
304
- * @param shortcuts - Single shortcut or array of shortcuts
305
- * @returns Array of parsed shortcuts
306
- */
307
- declare function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[];
308
- /**
309
- * Check if a keyboard event matches a parsed shortcut
310
- *
311
- * @param event - The keyboard event to check
312
- * @param parsed - The parsed shortcut to match against
313
- * @returns `true` if the event matches the shortcut
314
- */
315
- declare function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean;
316
- /**
317
- * Check if a keyboard event matches any of the parsed shortcuts
318
- *
319
- * @param event - The keyboard event to check
320
- * @param parsedShortcuts - Array of parsed shortcuts to match against
321
- * @returns `true` if the event matches any shortcut
322
- */
323
- declare function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean;
324
-
325
- /**
326
- * Format a shortcut string for display with platform-aware symbols
327
- *
328
- * @param shortcut - Shortcut string (e.g., "cmd+s")
329
- * @param platform - Optional platform override (default: auto-detect)
330
- * @returns Formatted display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows)
331
- *
332
- * @example
333
- * ```ts
334
- * formatShortcut("cmd+s") // "⌘S" on Mac, "Ctrl+S" on Windows
335
- * formatShortcut("ctrl+shift+p", "mac") // "⌃⇧P"
336
- * ```
337
- */
338
- declare function formatShortcut(shortcut: string, platform?: PlatformType): string;
339
-
340
- /**
341
- * Registers an object-based shortcut map in one call and returns per-action handles.
342
- *
343
- * @param builder - Builder returned by `useShortcut()`
344
- * @param shortcutMap - Record of action ids to key bindings, handlers, and options
345
- * @returns A result map with one `ShortcutResult` per shortcut id
346
- *
347
- * @example
348
- * ```ts
349
- * const $ = useShortcut()
350
- * const results = registerShortcutMap($, {
351
- * save: { keys: "mod+s", handler: onSave },
352
- * nav: { keys: ["g", "d"], handler: onGoDashboard },
353
- * })
354
- * ```
355
- */
356
- declare function registerShortcutMap<T extends ShortcutMap>(builder: ShortcutBuilder, shortcutMap: T): ShortcutMapResult<T>;
357
- /**
358
- * React hook for registering chainable keyboard shortcuts
359
- *
360
- * @param options - Configuration options for the hook
361
- * @returns A chainable shortcut builder (`$`)
362
- *
363
- * @example
364
- * ```ts
365
- * const $ = useShortcut({ activeScopes: ["editor"] })
366
- * $.mod.key("s").on((event) => {
367
- * event.preventDefault()
368
- * saveDocument()
369
- * })
370
- * ```
371
- */
372
- declare function useShortcut(options?: UseShortcutOptions): ShortcutBuilder;
373
- /**
374
- * React hook that registers a shortcut map and automatically unbinds on cleanup.
375
- *
376
- * @param shortcutMap - Record of action ids to key bindings, handlers, and options
377
- * @param options - Same options as `useShortcut()`
378
- * @returns A map of `ShortcutResult` keyed by your shortcut ids
379
- *
380
- * @example
381
- * ```ts
382
- * const mapResults = useShortcutMap({
383
- * save: { keys: "mod+s", handler: onSave },
384
- * close: { keys: "escape", handler: onClose },
385
- * })
386
- * ```
387
- */
388
- declare function useShortcutMap<T extends ShortcutMap>(shortcutMap: T, options?: UseShortcutOptions): ShortcutMapResult<T>;
389
- /**
390
- * Creates an imperative group controller for many shortcut registrations.
391
- *
392
- * @returns A `ShortcutGroup` that can add and unbind multiple shortcuts together
393
- *
394
- * @example
395
- * ```ts
396
- * const group = createShortcutGroup()
397
- * group.add($.mod.key("s").on(onSave))
398
- * group.add($.key("escape").on(onClose))
399
- * group.unbindAll()
400
- * ```
401
- */
402
- declare function createShortcutGroup(): ShortcutGroup;
403
- /**
404
- * React hook that returns a stable `ShortcutGroup` instance.
405
- *
406
- * @returns A memoized `ShortcutGroup` tied to the component lifecycle
407
- *
408
- * @example
409
- * ```ts
410
- * const group = useShortcutGroup()
411
- * ```
412
- */
413
- declare function useShortcutGroup(): ShortcutGroup;
414
-
415
- export { type ActionKey, type AlphaKey, type ExceptPredicate, type ExceptPreset, type FunctionKey, type HandlerOptions, type KeyChain, ModifierAliases, type ModifierChain, ModifierDisplayOrder, ModifierDisplaySymbols, type ModifierFlags, ModifierKey, type ModifierName, type ModifierState, type NavigationKey, type NumericKey, type ParsedShortcut, Platform, type ShortcutBuilder, type ShortcutConflict, type ShortcutGroup, type ShortcutHandler, type ShortcutMap, type ShortcutMapEntry, type ShortcutMapResult, type ShortcutRecordingOptions, type ShortcutResult, type ShortcutScope, type SpecialKey, SpecialKeyMap, type SymbolKey, type UseShortcutOptions, createShortcutGroup, detectPlatform, formatShortcut, matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts, registerShortcutMap, useShortcut, useShortcutGroup, useShortcutMap };
1
+ export { ModifierAliases, ModifierDisplayOrder, ModifierDisplaySymbols, ModifierKey, Platform, SpecialKeyMap, detectPlatform } from './constants.js';
2
+ export { A as ActionKey, a as AlphaKey, E as ExceptPredicate, b as ExceptPreset, F as FunctionKey, H as HandlerOptions, K as KeyChain, M as ModifierChain, c as ModifierFlags, d as ModifierName, e as ModifierState, N as NavigationKey, f as NumericKey, P as ParsedShortcut, S as ShortcutAttemptDebugEvent, g as ShortcutAttemptStatus, h as ShortcutBuilder, i as ShortcutConflict, j as ShortcutDebugEvent, k as ShortcutDebugInput, l as ShortcutDebugOptions, m as ShortcutDebugStep, n as ShortcutDebugToken, o as ShortcutDebugTokenStatus, p as ShortcutGroup, q as ShortcutHandler, r as ShortcutMap, s as ShortcutMapEntry, t as ShortcutMapResult, u as ShortcutRecordingOptions, v as ShortcutResult, w as ShortcutScope, x as SpecialKey, y as SymbolKey, U as UseShortcutOptions } from './types-Cg7uyv1m.js';
3
+ export { matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts } from './parser.js';
4
+ export { formatShortcut } from './formatter.js';
5
+ export { createShortcutGroup, registerShortcutMap, useShortcut, useShortcutGroup, useShortcutMap } from './react.js';