@remcostoeten/use-shortcut 2.1.0 → 2.3.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/README.md +120 -40
- package/dist/constants.d.ts +36 -0
- package/dist/constants.js +1 -0
- package/dist/constants.mjs +1 -0
- package/dist/formatter.d.ts +30 -0
- package/dist/formatter.js +1 -0
- package/dist/formatter.mjs +1 -0
- package/dist/index.d.ts +5 -468
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/parser.d.ts +47 -0
- package/dist/parser.js +1 -0
- package/dist/parser.mjs +1 -0
- package/dist/react.d.ts +79 -0
- package/dist/react.js +1 -0
- package/dist/react.mjs +1 -0
- package/dist/types-yQWKtHDh.d.ts +320 -0
- package/package.json +46 -5
- package/dist/cli/index.mjs +0 -469
package/dist/index.d.ts
CHANGED
|
@@ -1,468 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
type ShortcutAttemptStatus = "matched" | "partial" | "wrong-order" | "mismatch";
|
|
105
|
-
type ShortcutDebugTokenStatus = "match" | "wrong-order" | "mismatch";
|
|
106
|
-
type ShortcutDebugToken = {
|
|
107
|
-
token: string;
|
|
108
|
-
kind: "modifier" | "key";
|
|
109
|
-
status: ShortcutDebugTokenStatus;
|
|
110
|
-
};
|
|
111
|
-
type ShortcutDebugStep = {
|
|
112
|
-
index: number;
|
|
113
|
-
expected: string;
|
|
114
|
-
actual?: string;
|
|
115
|
-
status: "match" | "partial" | "pending" | "wrong-order" | "mismatch";
|
|
116
|
-
tokens: ShortcutDebugToken[];
|
|
117
|
-
};
|
|
118
|
-
type ShortcutDebugInput = {
|
|
119
|
-
key: string;
|
|
120
|
-
code: string;
|
|
121
|
-
location: number;
|
|
122
|
-
repeat: boolean;
|
|
123
|
-
keyCode?: number;
|
|
124
|
-
which?: number;
|
|
125
|
-
combo: string;
|
|
126
|
-
modifiers: ModifierState;
|
|
127
|
-
};
|
|
128
|
-
type ShortcutAttemptDebugEvent = {
|
|
129
|
-
combo: string;
|
|
130
|
-
display: string;
|
|
131
|
-
description?: string;
|
|
132
|
-
status: ShortcutAttemptStatus;
|
|
133
|
-
matched: boolean;
|
|
134
|
-
progress: number;
|
|
135
|
-
expectedSteps: string[];
|
|
136
|
-
actualSteps: string[];
|
|
137
|
-
stepIndex: number;
|
|
138
|
-
input: ShortcutDebugInput;
|
|
139
|
-
steps: ShortcutDebugStep[];
|
|
140
|
-
};
|
|
141
|
-
type ShortcutDebugEvent = {
|
|
142
|
-
input: ShortcutDebugInput;
|
|
143
|
-
attempts: ShortcutAttemptDebugEvent[];
|
|
144
|
-
};
|
|
145
|
-
type ShortcutDebugOptions = {
|
|
146
|
-
/** Log shortcut attempts to the console (default: true) */
|
|
147
|
-
console?: boolean;
|
|
148
|
-
/** Include `KeyboardEvent.code` in console output */
|
|
149
|
-
includeCode?: boolean;
|
|
150
|
-
/** Include `KeyboardEvent.location` in console output */
|
|
151
|
-
includeLocation?: boolean;
|
|
152
|
-
/** Include deprecated numeric key metadata in console output when available */
|
|
153
|
-
includeKeyCode?: boolean;
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* Options for shortcut handler registration
|
|
157
|
-
*/
|
|
158
|
-
type HandlerOptions = {
|
|
159
|
-
/** Prevent the browser's default action (default: `true`) */
|
|
160
|
-
preventDefault?: boolean;
|
|
161
|
-
/** Stop event propagation */
|
|
162
|
-
stopPropagation?: boolean;
|
|
163
|
-
/** Delay handler execution in milliseconds */
|
|
164
|
-
delay?: number;
|
|
165
|
-
/** Description for documentation/debugging */
|
|
166
|
-
description?: string;
|
|
167
|
-
/** Disable this specific shortcut */
|
|
168
|
-
disabled?: boolean;
|
|
169
|
-
/** Conditions to skip the shortcut */
|
|
170
|
-
except?: ExceptPreset | ExceptPreset[] | ExceptPredicate;
|
|
171
|
-
/** Required named scopes that must be active */
|
|
172
|
-
scopes?: ShortcutScope;
|
|
173
|
-
/** Timeout in ms for multi-step sequences */
|
|
174
|
-
sequenceTimeout?: number;
|
|
175
|
-
/** Higher priority handlers run first (default: 0) */
|
|
176
|
-
priority?: number;
|
|
177
|
-
/** Stop evaluating other handlers for this combo when matched */
|
|
178
|
-
stopOnMatch?: boolean;
|
|
179
|
-
};
|
|
180
|
-
/**
|
|
181
|
-
* Result object returned when registering a shortcut
|
|
182
|
-
* Provides control over the shortcut and display information
|
|
183
|
-
*/
|
|
184
|
-
type ShortcutResult = {
|
|
185
|
-
/** Remove the keyboard listener */
|
|
186
|
-
unbind: () => void;
|
|
187
|
-
/** Platform-aware display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows) */
|
|
188
|
-
display: string;
|
|
189
|
-
/** Normalized combo string (e.g., "cmd+s" or "g d") */
|
|
190
|
-
combo: string;
|
|
191
|
-
/** Programmatically trigger the shortcut handler */
|
|
192
|
-
trigger: () => void;
|
|
193
|
-
/** Whether the shortcut is currently enabled */
|
|
194
|
-
isEnabled: boolean;
|
|
195
|
-
/** Enable the shortcut (after being disabled) */
|
|
196
|
-
enable: () => void;
|
|
197
|
-
/** Temporarily disable the shortcut */
|
|
198
|
-
disable: () => void;
|
|
199
|
-
/** Subscribe to shortcut attempt events (useful for visual feedback) */
|
|
200
|
-
onAttempt?: (callback: (matched: boolean, event: KeyboardEvent, details?: ShortcutAttemptDebugEvent) => void) => () => void;
|
|
201
|
-
};
|
|
202
|
-
/**
|
|
203
|
-
* Chainable modifier builder with type-safe exhaustion
|
|
204
|
-
* Each modifier can only be used once in a chain
|
|
205
|
-
*/
|
|
206
|
-
type ModifierChain<Used extends Partial<ModifierFlags>> = {
|
|
207
|
-
ctrl: Used["ctrl"] extends true ? never : ModifierChain<Used & {
|
|
208
|
-
ctrl: true;
|
|
209
|
-
}>;
|
|
210
|
-
shift: Used["shift"] extends true ? never : ModifierChain<Used & {
|
|
211
|
-
shift: true;
|
|
212
|
-
}>;
|
|
213
|
-
alt: Used["alt"] extends true ? never : ModifierChain<Used & {
|
|
214
|
-
alt: true;
|
|
215
|
-
}>;
|
|
216
|
-
cmd: Used["cmd"] extends true ? never : ModifierChain<Used & {
|
|
217
|
-
cmd: true;
|
|
218
|
-
}>;
|
|
219
|
-
mod: Used["cmd"] extends true ? never : ModifierChain<Used & {
|
|
220
|
-
cmd: true;
|
|
221
|
-
}>;
|
|
222
|
-
key: <K extends ActionKey>(key: K) => KeyChain<K>;
|
|
223
|
-
in: (scopes: ShortcutScope) => ModifierChain<Used>;
|
|
224
|
-
};
|
|
225
|
-
/**
|
|
226
|
-
* Chain state after calling `.key()` - ready to attach a handler
|
|
227
|
-
*/
|
|
228
|
-
type KeyChain<Key extends string> = {
|
|
229
|
-
/** Attach a handler to this shortcut */
|
|
230
|
-
on: (handler: ShortcutHandler, options?: HandlerOptions) => ShortcutResult;
|
|
231
|
-
/** Attach a handler with inline options */
|
|
232
|
-
handle: (options: HandlerOptions & {
|
|
233
|
-
handler: ShortcutHandler;
|
|
234
|
-
}) => ShortcutResult;
|
|
235
|
-
/** Add exception conditions before attaching handler */
|
|
236
|
-
except: (condition: ExceptPreset | ExceptPreset[] | ExceptPredicate) => KeyChainWithExcept<Key>;
|
|
237
|
-
/** Add required named scopes */
|
|
238
|
-
in: (scopes: ShortcutScope) => KeyChain<Key>;
|
|
239
|
-
/** Add the next step in a sequence */
|
|
240
|
-
then: <K extends ActionKey | string>(key: K) => KeyChain<`${Key} ${K}`>;
|
|
241
|
-
};
|
|
242
|
-
/**
|
|
243
|
-
* Chain state after calling `.except()` - ready to attach handler
|
|
244
|
-
*/
|
|
245
|
-
type KeyChainWithExcept<Key extends string> = {
|
|
246
|
-
on: (handler: ShortcutHandler, options?: Omit<HandlerOptions, "except">) => ShortcutResult;
|
|
247
|
-
in: (scopes: ShortcutScope) => KeyChainWithExcept<Key>;
|
|
248
|
-
then: <K extends ActionKey | string>(key: K) => KeyChainWithExcept<`${Key} ${K}`>;
|
|
249
|
-
};
|
|
250
|
-
/** Options for `ShortcutBuilder.record()` and low-level recording flows. */
|
|
251
|
-
type ShortcutRecordingOptions = {
|
|
252
|
-
target?: HTMLElement | Window | null;
|
|
253
|
-
eventType?: "keydown" | "keyup";
|
|
254
|
-
timeoutMs?: number;
|
|
255
|
-
};
|
|
256
|
-
/**
|
|
257
|
-
* The main shortcut builder interface returned by `useShortcut()`
|
|
258
|
-
*/
|
|
259
|
-
type ShortcutBuilder = ModifierChain<EmptyModifiers> & {
|
|
260
|
-
ctrl: ModifierChain<{
|
|
261
|
-
ctrl: true;
|
|
262
|
-
}>;
|
|
263
|
-
shift: ModifierChain<{
|
|
264
|
-
shift: true;
|
|
265
|
-
}>;
|
|
266
|
-
alt: ModifierChain<{
|
|
267
|
-
alt: true;
|
|
268
|
-
}>;
|
|
269
|
-
cmd: ModifierChain<{
|
|
270
|
-
cmd: true;
|
|
271
|
-
}>;
|
|
272
|
-
mod: ModifierChain<{
|
|
273
|
-
cmd: true;
|
|
274
|
-
}>;
|
|
275
|
-
key: <K extends ActionKey>(key: K) => KeyChain<K>;
|
|
276
|
-
/** Set required scopes for upcoming chain calls */
|
|
277
|
-
in: (scopes: ShortcutScope) => ShortcutBuilder;
|
|
278
|
-
/** Update active scopes at runtime */
|
|
279
|
-
setScopes: (scopes: ShortcutScope) => void;
|
|
280
|
-
/** Enable one scope */
|
|
281
|
-
enableScope: (scope: string) => void;
|
|
282
|
-
/** Disable one scope */
|
|
283
|
-
disableScope: (scope: string) => void;
|
|
284
|
-
/** Return currently active scopes */
|
|
285
|
-
getScopes: () => string[];
|
|
286
|
-
/** Check if a scope is active */
|
|
287
|
-
isScopeActive: (scope: string) => boolean;
|
|
288
|
-
/** Subscribe to every keyboard input evaluated by this shortcut registry */
|
|
289
|
-
onDebug: (callback: (event: ShortcutDebugEvent) => void) => () => void;
|
|
290
|
-
/** Record the next key combo */
|
|
291
|
-
record: (options?: ShortcutRecordingOptions) => Promise<string>;
|
|
292
|
-
};
|
|
293
|
-
/**
|
|
294
|
-
* Options for the `useShortcut` hook
|
|
295
|
-
*/
|
|
296
|
-
type UseShortcutOptions = {
|
|
297
|
-
/** Enable debug logging to console or configure structured debug output */
|
|
298
|
-
debug?: boolean | ShortcutDebugOptions;
|
|
299
|
-
/** Global delay for all handlers in milliseconds */
|
|
300
|
-
delay?: number;
|
|
301
|
-
/** Skip shortcuts when focused on input elements (default: `true`) */
|
|
302
|
-
ignoreInputs?: boolean;
|
|
303
|
-
/** Target element for keyboard listeners (default: `window`) */
|
|
304
|
-
target?: HTMLElement | Window | null;
|
|
305
|
-
/** Keyboard event type to listen for (default: "keydown") */
|
|
306
|
-
eventType?: "keydown" | "keyup";
|
|
307
|
-
/** Globally disable all shortcuts from this hook */
|
|
308
|
-
disabled?: boolean;
|
|
309
|
-
/** Active named scopes. Shortcuts with scopes only run when at least one matches. */
|
|
310
|
-
activeScopes?: ShortcutScope;
|
|
311
|
-
/** Global timeout in ms for sequence completion */
|
|
312
|
-
sequenceTimeout?: number;
|
|
313
|
-
/** Warn when conflicting shortcuts are registered (default: true) */
|
|
314
|
-
conflictWarnings?: boolean;
|
|
315
|
-
/** Custom conflict callback */
|
|
316
|
-
onConflict?: (conflict: ShortcutConflict) => void;
|
|
317
|
-
/** Global event filter; return false to skip all shortcuts for the event */
|
|
318
|
-
eventFilter?: (event: KeyboardEvent) => boolean;
|
|
319
|
-
};
|
|
320
|
-
/** Single shortcut-map entry used by `registerShortcutMap` and `useShortcutMap`. */
|
|
321
|
-
type ShortcutMapEntry = {
|
|
322
|
-
keys: string | string[];
|
|
323
|
-
handler: ShortcutHandler;
|
|
324
|
-
options?: HandlerOptions;
|
|
325
|
-
};
|
|
326
|
-
/** Bulk registration shape mapping action ids to key+handler definitions. */
|
|
327
|
-
type ShortcutMap = Record<string, ShortcutMapEntry>;
|
|
328
|
-
/** Return type for map registrations, keyed by the same ids as the source map. */
|
|
329
|
-
type ShortcutMapResult<T extends ShortcutMap = ShortcutMap> = {
|
|
330
|
-
[K in keyof T]: ShortcutResult;
|
|
331
|
-
};
|
|
332
|
-
/** Imperative grouping controller for binding/unbinding many shortcut registrations together. */
|
|
333
|
-
type ShortcutGroup = {
|
|
334
|
-
add: (...results: ShortcutResult[]) => void;
|
|
335
|
-
addMany: (results: ShortcutResult[] | Record<string, ShortcutResult>) => void;
|
|
336
|
-
unbindAll: () => void;
|
|
337
|
-
clear: () => void;
|
|
338
|
-
getResults: () => ShortcutResult[];
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Parse a shortcut string into its components
|
|
343
|
-
*
|
|
344
|
-
* @param shortcut - Shortcut string (e.g., "cmd+s", "ctrl+shift+p")
|
|
345
|
-
* @returns Parsed shortcut with modifiers, key, and original string
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* ```ts
|
|
349
|
-
* const parsed = parseShortcut("cmd+s")
|
|
350
|
-
* // { modifiers: { meta: true, ... }, key: "s", original: "cmd+s" }
|
|
351
|
-
* ```
|
|
352
|
-
*/
|
|
353
|
-
declare function parseShortcut(shortcut: string): ParsedShortcut;
|
|
354
|
-
/**
|
|
355
|
-
* Parse multiple shortcut strings
|
|
356
|
-
*
|
|
357
|
-
* @param shortcuts - Single shortcut or array of shortcuts
|
|
358
|
-
* @returns Array of parsed shortcuts
|
|
359
|
-
*/
|
|
360
|
-
declare function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[];
|
|
361
|
-
/**
|
|
362
|
-
* Check if a keyboard event matches a parsed shortcut
|
|
363
|
-
*
|
|
364
|
-
* @param event - The keyboard event to check
|
|
365
|
-
* @param parsed - The parsed shortcut to match against
|
|
366
|
-
* @returns `true` if the event matches the shortcut
|
|
367
|
-
*/
|
|
368
|
-
declare function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean;
|
|
369
|
-
/**
|
|
370
|
-
* Check if a keyboard event matches any of the parsed shortcuts
|
|
371
|
-
*
|
|
372
|
-
* @param event - The keyboard event to check
|
|
373
|
-
* @param parsedShortcuts - Array of parsed shortcuts to match against
|
|
374
|
-
* @returns `true` if the event matches any shortcut
|
|
375
|
-
*/
|
|
376
|
-
declare function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean;
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Format a shortcut string for display with platform-aware symbols
|
|
380
|
-
*
|
|
381
|
-
* @param shortcut - Shortcut string (e.g., "cmd+s")
|
|
382
|
-
* @param platform - Optional platform override (default: auto-detect)
|
|
383
|
-
* @returns Formatted display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows)
|
|
384
|
-
*
|
|
385
|
-
* @example
|
|
386
|
-
* ```ts
|
|
387
|
-
* formatShortcut("cmd+s") // "⌘S" on Mac, "Ctrl+S" on Windows
|
|
388
|
-
* formatShortcut("ctrl+shift+p", "mac") // "⌃⇧P"
|
|
389
|
-
* ```
|
|
390
|
-
*/
|
|
391
|
-
declare function formatShortcut(shortcut: string, platform?: PlatformType): string;
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Registers an object-based shortcut map in one call and returns per-action handles.
|
|
395
|
-
*
|
|
396
|
-
* @param builder - Builder returned by `useShortcut()`
|
|
397
|
-
* @param shortcutMap - Record of action ids to key bindings, handlers, and options
|
|
398
|
-
* @returns A result map with one `ShortcutResult` per shortcut id
|
|
399
|
-
*
|
|
400
|
-
* @example
|
|
401
|
-
* ```ts
|
|
402
|
-
* const $ = useShortcut()
|
|
403
|
-
* const results = registerShortcutMap($, {
|
|
404
|
-
* save: { keys: "mod+s", handler: onSave },
|
|
405
|
-
* nav: { keys: ["g", "d"], handler: onGoDashboard },
|
|
406
|
-
* })
|
|
407
|
-
* ```
|
|
408
|
-
*/
|
|
409
|
-
declare function registerShortcutMap<T extends ShortcutMap>(builder: ShortcutBuilder, shortcutMap: T): ShortcutMapResult<T>;
|
|
410
|
-
/**
|
|
411
|
-
* React hook for registering chainable keyboard shortcuts
|
|
412
|
-
*
|
|
413
|
-
* @param options - Configuration options for the hook
|
|
414
|
-
* @returns A chainable shortcut builder (`$`)
|
|
415
|
-
*
|
|
416
|
-
* @example
|
|
417
|
-
* ```ts
|
|
418
|
-
* const $ = useShortcut({ activeScopes: ["editor"] })
|
|
419
|
-
* $.mod.key("s").on((event) => {
|
|
420
|
-
* event.preventDefault()
|
|
421
|
-
* saveDocument()
|
|
422
|
-
* })
|
|
423
|
-
* ```
|
|
424
|
-
*/
|
|
425
|
-
declare function useShortcut(options?: UseShortcutOptions): ShortcutBuilder;
|
|
426
|
-
/**
|
|
427
|
-
* React hook that registers a shortcut map and automatically unbinds on cleanup.
|
|
428
|
-
*
|
|
429
|
-
* @param shortcutMap - Record of action ids to key bindings, handlers, and options
|
|
430
|
-
* @param options - Same options as `useShortcut()`
|
|
431
|
-
* @returns A map of `ShortcutResult` keyed by your shortcut ids
|
|
432
|
-
*
|
|
433
|
-
* @example
|
|
434
|
-
* ```ts
|
|
435
|
-
* const mapResults = useShortcutMap({
|
|
436
|
-
* save: { keys: "mod+s", handler: onSave },
|
|
437
|
-
* close: { keys: "escape", handler: onClose },
|
|
438
|
-
* })
|
|
439
|
-
* ```
|
|
440
|
-
*/
|
|
441
|
-
declare function useShortcutMap<T extends ShortcutMap>(shortcutMap: T, options?: UseShortcutOptions): ShortcutMapResult<T>;
|
|
442
|
-
/**
|
|
443
|
-
* Creates an imperative group controller for many shortcut registrations.
|
|
444
|
-
*
|
|
445
|
-
* @returns A `ShortcutGroup` that can add and unbind multiple shortcuts together
|
|
446
|
-
*
|
|
447
|
-
* @example
|
|
448
|
-
* ```ts
|
|
449
|
-
* const group = createShortcutGroup()
|
|
450
|
-
* group.add($.mod.key("s").on(onSave))
|
|
451
|
-
* group.add($.key("escape").on(onClose))
|
|
452
|
-
* group.unbindAll()
|
|
453
|
-
* ```
|
|
454
|
-
*/
|
|
455
|
-
declare function createShortcutGroup(): ShortcutGroup;
|
|
456
|
-
/**
|
|
457
|
-
* React hook that returns a stable `ShortcutGroup` instance.
|
|
458
|
-
*
|
|
459
|
-
* @returns A memoized `ShortcutGroup` tied to the component lifecycle
|
|
460
|
-
*
|
|
461
|
-
* @example
|
|
462
|
-
* ```ts
|
|
463
|
-
* const group = useShortcutGroup()
|
|
464
|
-
* ```
|
|
465
|
-
*/
|
|
466
|
-
declare function useShortcutGroup(): ShortcutGroup;
|
|
467
|
-
|
|
468
|
-
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 ShortcutAttemptDebugEvent, type ShortcutAttemptStatus, type ShortcutBuilder, type ShortcutConflict, type ShortcutDebugEvent, type ShortcutDebugInput, type ShortcutDebugOptions, type ShortcutDebugStep, type ShortcutDebugToken, type ShortcutDebugTokenStatus, 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-yQWKtHDh.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';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var react=require('react');var m={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=m,y=null;function E(){if(y)return y;if(typeof navigator>"u")return y=m.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=m.MAC,y):e.includes("linux")||e.includes("android")?(y=m.LINUX,y):(e.includes("win"),y=m.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},F={command:p.META,cmd:p.META,"\u2318":p.META,meta:p.META,win:p.META,windows:p.META,super:p.META,mod:p.META,control:p.CTRL,ctrl:p.CTRL,"\u2303":p.CTRL,ctl:p.CTRL,alt:p.ALT,option:p.ALT,opt:p.ALT,"\u2325":p.ALT,shift:p.SHIFT,"\u21E7":p.SHIFT,shft:p.SHIFT},N={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"},U={[m.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[m.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[m.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},L={[m.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[m.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[m.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function G(t){return t===" "?"space":t.toLowerCase()}function k(t){let e=E(),o=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(o.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=o.pop();for(let i of o){let u=F[i];u?i==="mod"?e===R.MAC?n.meta=true:n.ctrl=true:n[u]=true:s=i+s;}let c=N[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function gt(t){return (Array.isArray(t)?t:[t]).map(k)}function yt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function P(t,e){let r=yt(t),o=G(t.key),n=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,s=o===G(e.key);return n&&s}function bt(t,e){return e.some(r=>P(t,r))}var X={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Et={...X,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Tt={...X,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function q(t,e){let r=e??E(),o=k(t),n=U[r],s=L[r],c=[];for(let f of s)o.modifiers[f]&&c.push(n[f]);let i=Mt(o.key,r);c.push(i);let u=r===m.MAC?"":"+";return c.join(u)}function Mt(t,e){return (e===m.MAC?Et:Tt)[t]||t.toUpperCase()}function At(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function z(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function Y(t){if(t)return t===true?{console:true}:t}function v(t){let e=Y(t);return e?e.console!==false:false}function h(t,...e){v(t)&&console.log("[useShortcut]",...e);}function J(t,e){return {key:t.key,code:t.code,location:t.location,repeat:t.repeat,keyCode:"keyCode"in t?t.keyCode:void 0,which:"which"in t?t.which:void 0,combo:e,modifiers:{meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}}function Ct(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Q(t,e,r){return t.map((o,n)=>{let s=e[n];if(!s)return {index:n,expected:o,status:"pending",tokens:[]};let c=new Set(z(o)),i=new Set(t.slice(n+1).flatMap(z)),u=z(s).map((l,b,_)=>({token:l,kind:At(l)||b<_.length-1?"modifier":"key",status:Ct(l,c,i)}));if(s===o)return {index:n,expected:o,actual:s,status:r||n<e.length-1?"match":"partial",tokens:u};let f=t.slice(n+1).includes(s)?"wrong-order":"mismatch";return {index:n,expected:o,actual:s,status:f,tokens:u}})}function V(t,e,r,o){if(o)return "matched";let n=t.slice(0,r);return n.length>0&&n.every(s=>s.status==="match"||s.status==="partial")?r<e?"partial":"mismatch":n.some(s=>s.status==="wrong-order")?"wrong-order":"mismatch"}function Z(t,e){if(!v(t))return;let r=Y(t),o=[];if(r?.includeCode&&e.input.code&&o.push(`code=${e.input.code}`),r?.includeLocation&&o.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&o.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&o.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...o);return}for(let n of e.attempts)console.log("[useShortcut]",n.status.toUpperCase(),`${e.input.combo} -> ${n.combo}`,...o);}function xt(t){let e=E(),r=L[e],o=[];for(let n of r)n===p.CTRL&&t.ctrl&&o.push("ctrl"),n===p.ALT&&t.alt&&o.push("alt"),n===p.SHIFT&&t.shift&&o.push("shift"),n===p.META&&t.cmd&&o.push("cmd");return o}function tt(t,e){return [...xt(t),e].join("+")}function et(t){return t.map(e=>q(e)).join(" then ")}function A(t){let e=[];t.modifiers.ctrl&&e.push("ctrl"),t.modifiers.alt&&e.push("alt"),t.modifiers.shift&&e.push("shift"),t.modifiers.meta&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function $(t){let e=[];t.ctrlKey&&e.push("ctrl"),t.altKey&&e.push("alt"),t.shiftKey&&e.push("shift"),t.metaKey&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function rt(t){return $(t)}function ot(t,e){if(t.length>e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}function nt(t,e){let r=t.map(A),o=e.map(A),n=r.join(" "),s=o.join(" ");return n===s?"exact":ot(r,o)||ot(o,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var K=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)},editable:t=>t.target instanceof HTMLElement?t.target.isContentEditable:false,typing:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)||e.isContentEditable},modal:()=>typeof document>"u"||typeof document.querySelector!="function"?false:document.querySelector('[data-modal="true"], [role="dialog"]')!==null,disabled:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true"}};function ct(t,e){return e?typeof e=="function"?e(t):Array.isArray(e)?e.some(r=>it[r]?.(t)):it[e]?.(t)??false:false}function C(t){return t?(Array.isArray(t)?t:[t]).map(e=>e.trim()).filter(Boolean):[]}function ut(t,e){if(t.size===0)return true;for(let r of t)if(e.has(r))return true;return false}function at(t){let e=t.key.toLowerCase();return e==="shift"||e==="control"||e==="alt"||e==="meta"}function wt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function kt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let o=rt(e),n=J(e,o),s=[],c=new Set,i=t.debugListeners.size>0||v(r.debug)||[...t.listeners.values()].some(l=>l.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(o);if(u)for(let l of u)c.add(l);for(let l of t.activeSequenceCombos)c.add(l);if(i)for(let l of t.listeners.keys())c.add(l);for(let l of c){let b=t.listeners.get(l);if(!b)continue;let _=wt(b);for(let a of _){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(K.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){h(r.debug,"Skipped due to except condition:",l);continue}let O=a.parsedSteps[a.progress],T=Date.now();a.progress>0&&T-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&T-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let H=a.progress,M=false;P(e,O)?(a.progress+=1,a.lastMatchedAt=T,a.progress===a.parsedSteps.length&&(M=true,a.progress=0)):a.progress>0&&P(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=T):a.progress=0,a.lastDebugAt=T,a.debugHistory.push(o),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),D=Q(a.expectedSteps,g,M),B={combo:a.combo,display:a.display,description:a.description,status:V(D,a.expectedSteps.length,g.length,M),matched:M,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:H,input:n,steps:D};s.push(B);for(let S of a.attemptCallbacks)S(M,e,B);if(!M)continue;h(r.debug,"MATCHED:",l),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(h(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(l):t.activeSequenceCombos.delete(l);}let f={input:n,attempts:s};if(t.debugListeners.size>0)for(let l of t.debugListeners)l(f);Z(r.debug,f);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",o=n=>kt(t,n);e.addEventListener(r,o),t.listener=o,t.listenerTarget=e,t.listenerEventType=r,h(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,h(t.options.debug,"Listener detached"));}function j(t,e,r={},o){let{options:n,except:s}=t,c=t.steps;if(c.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let i=c.map(d=>k(d)),u=i.map(A).join(" "),f=et(c),l=n.debug??false,b=s??r.except;for(let[d,S]of o.listeners.entries())for(let x of S){if(d===u)continue;let w=nt(i,x.parsedSteps);w&&st(o,{combo:u,existingCombo:d,reason:w});}let _=!r.disabled&&!n.disabled,a=r.delay??n.delay??0,O=r.sequenceTimeout??n.sequenceTimeout??800,T=new Set(C(t.scopes??r.scopes)),H=i.map(A),M=new Set;h(l,"Registering:",u,"\u2192",f,{parsedSteps:i,except:!!b,scopes:[...T]});let g={id:o.nextId++,userHandler:e,isEnabled:_,combo:u,display:f,description:r.description,attemptCallbacks:M,parsedSteps:i,expectedSteps:H,scopes:T,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:O,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},D=o.listeners.get(u);if(D)D.push(g);else {o.listeners.set(u,[g]);let d=A(i[0]),S=o.firstStepIndex.get(d);S?S.add(u):o.firstStepIndex.set(d,new Set([u]));}return pt(o),{unbind:()=>{let d=o.listeners.get(u);if(!d)return;let S=d.filter(x=>x.id!==g.id);if(S.length===0){o.listeners.delete(u),o.activeSequenceCombos.delete(u);let x=A(i[0]),w=o.firstStepIndex.get(x);w&&(w.delete(u),w.size===0&&o.firstStepIndex.delete(x)),h(l,"Unregistered:",u);}else o.listeners.set(u,S);o.listeners.size===0&<(o);},display:f,combo:u,trigger:()=>e(new KeyboardEvent(o.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function ft(t){return (e={})=>new Promise((r,o)=>{let n=e.target??t.target??(typeof window<"u"?window:null),s=e.eventType??t.eventType??"keydown";if(!n){o(new Error("[useShortcut] Cannot record shortcut without a target."));return}let c,i=f=>{let l=f;at(l)||(l.preventDefault(),n.removeEventListener(s,i),c&&clearTimeout(c),r($(l)));};n.addEventListener(s,i);let u=e.timeoutMs;u&&u>0&&(c=setTimeout(()=>{n.removeEventListener(s,i),o(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var _t=new Set(["ctrl","shift","alt","cmd","mod"]);function dt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};h(t.debug,"Builder created with options:",t);function r(n){return new Proxy({},{get(s,c){if(c==="__debug")return n.options.debug;if(_t.has(c)){let i=E(),u=c==="mod"?i===R.MAC?"cmd":"ctrl":c,f={...n,modifiers:{...n.modifiers,[u]:true}};return h(n.options.debug,`Chain: +${c} \u2192`,f.modifiers),r(f)}if(c==="in")return i=>{let u=[...C(n.scopes),...C(i)],f={...n,scopes:u};return r(f)};if(c==="setScopes")return i=>{e.activeScopes=new Set(C(i));};if(c==="enableScope")return i=>{i?.trim()&&e.activeScopes.add(i.trim());};if(c==="disableScope")return i=>{i?.trim()&&e.activeScopes.delete(i.trim());};if(c==="getScopes")return ()=>[...e.activeScopes];if(c==="isScopeActive")return i=>e.activeScopes.has(i);if(c==="onDebug")return i=>(e.debugListeners.add(i),()=>e.debugListeners.delete(i));if(c==="record")return ft(e.options);if(c==="key")return i=>{let u=tt(n.modifiers,i),f={...n,modifiers:{},steps:[...n.steps,u]};return h(n.options.debug,`Chain: .key("${i}")`),r(f)};if(c==="then")return i=>{let u=String(i).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let f={...n,steps:[...n.steps,u]};return h(n.options.debug,`Chain: .then("${u}")`),r(f)};if(c==="except")return i=>{let u={...n,except:i};return h(n.options.debug,"Chain: .except()",i),r(u)};if(c==="on")return (i,u)=>j(n,i,u,e);if(c==="handle")return i=>{let{handler:u,...f}=i;return j(n,u,f,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Rt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function Lt(t,e){let r=Object.keys(t),o=Object.keys(e);if(r.length!==o.length)return false;for(let n of r){let s=t[n],c=e[n];if(!c||!Rt(s.keys,c.keys)||s.handler!==c.handler||s.options!==c.options)return false}return true}function Pt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function vt(t,e){let r=e.toLowerCase().split("+").map(s=>s.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let o=r.pop(),n=t;for(let s of r){if(s==="ctrl"||s==="control"){n=n.ctrl;continue}if(s==="shift"){n=n.shift;continue}if(s==="alt"||s==="option"){n=n.alt;continue}if(s==="cmd"||s==="command"||s==="meta"){n=n.cmd;continue}if(s==="mod"){n=n.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${s}" in step "${e}"`)}return n.key(o)}function mt(t,e){let r={};for(let o of Object.keys(e)){let n=e[o],s=Pt(n.keys);if(s.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(o)}" has no key steps`);let c=vt(t,s[0]);for(let i of s.slice(1))c=c.then(i);r[o]=c.on(n.handler,n.options);}return r}function ht(t={}){let e=react.useRef(t);e.current=t;let{builder:r,registry:o}=react.useMemo(()=>dt(e.current),[]);return react.useEffect(()=>{if(o.options=e.current,e.current.activeScopes!==void 0){let n=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];o.activeScopes=new Set(n.map(s=>s.trim()).filter(Boolean));}},[o,t]),react.useEffect(()=>()=>{o.listeners.clear(),o.firstStepIndex.clear(),o.activeSequenceCombos.clear(),o.listener&&o.listenerTarget&&(o.listenerTarget.removeEventListener(o.listenerEventType,o.listener),o.listener=null,o.listenerTarget=null);},[o]),r}function Kt(t,e={}){let r=ht(e),o=react.useRef(t);Lt(o.current,t)||(o.current=t);let n=o.current,s=react.useRef({});return react.useEffect(()=>{let c=mt(r,n),i=s.current;for(let u of Object.keys(i))delete i[u];return Object.assign(i,c),()=>{for(let u of Object.values(c))u.unbind();for(let u of Object.keys(i))delete i[u];}},[r,n]),s.current}function St(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function It(){let t=react.useRef(null);return t.current||(t.current=St()),t.current}exports.ModifierAliases=F;exports.ModifierDisplayOrder=L;exports.ModifierDisplaySymbols=U;exports.ModifierKey=p;exports.Platform=R;exports.SpecialKeyMap=N;exports.createShortcutGroup=St;exports.detectPlatform=E;exports.formatShortcut=q;exports.matchesAnyShortcut=bt;exports.matchesShortcut=P;exports.parseShortcut=k;exports.parseShortcuts=gt;exports.registerShortcutMap=mt;exports.useShortcut=ht;exports.useShortcutGroup=It;exports.useShortcutMap=Kt;
|
|
1
|
+
'use strict';var react=require('react');var h={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},D=h,y=null;function E(){if(y)return y;if(typeof navigator>"u")return y=h.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=h.MAC,y):e.includes("linux")||e.includes("android")?(y=h.LINUX,y):(e.includes("win"),y=h.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},F={command:p.META,cmd:p.META,"\u2318":p.META,meta:p.META,win:p.META,windows:p.META,super:p.META,mod:p.META,control:p.CTRL,ctrl:p.CTRL,"\u2303":p.CTRL,ctl:p.CTRL,alt:p.ALT,option:p.ALT,opt:p.ALT,"\u2325":p.ALT,shift:p.SHIFT,"\u21E7":p.SHIFT,shft:p.SHIFT},N={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"},U={[h.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[h.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[h.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},L={[h.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[h.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[h.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function G(t){return t===" "?"space":t.toLowerCase()}function k(t){let e=E(),n=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(n.length===0)throw new Error(`Invalid shortcut: "${t}"`);let o={meta:false,ctrl:false,alt:false,shift:false},i=n.pop();for(let c of n){let u=F[c];u?c==="mod"?e===D.MAC?o.meta=true:o.ctrl=true:o[u]=true:i=c+i;}let s=N[i]||i;return {modifiers:o,key:s.length===1?s.toLowerCase():s,original:t}}function yt(t){return (Array.isArray(t)?t:[t]).map(k)}function bt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function v(t,e){let r=bt(t),n=G(t.key),o=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,i=n===G(e.key);return o&&i}function Et(t,e){return e.some(r=>v(t,r))}var X={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Tt={...X,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Mt={...X,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function q(t,e){let r=e??E(),n=k(t),o=U[r],i=L[r],s=[];for(let l of i)n.modifiers[l]&&s.push(o[l]);let c=At(n.key,r);s.push(c);let u=r===h.MAC?"":"+";return s.join(u)}function At(t,e){return (e===h.MAC?Tt:Mt)[t]||t.toUpperCase()}function Ct(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function $(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function Y(t){if(t)return t===true?{console:true}:t}function P(t){let e=Y(t);return e?e.console!==false:false}function m(t,...e){P(t)&&console.log("[useShortcut]",...e);}function J(t,e){return {key:t.key,code:t.code,location:t.location,repeat:t.repeat,keyCode:"keyCode"in t?t.keyCode:void 0,which:"which"in t?t.which:void 0,combo:e,modifiers:{meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}}function xt(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Q(t,e,r){return t.map((n,o)=>{let i=e[o];if(!i)return {index:o,expected:n,status:"pending",tokens:[]};let s=new Set($(n)),c=new Set(t.slice(o+1).flatMap($)),u=$(i).map((f,b,_)=>({token:f,kind:Ct(f)||b<_.length-1?"modifier":"key",status:xt(f,s,c)}));if(i===n)return {index:o,expected:n,actual:i,status:r||o<e.length-1?"match":"partial",tokens:u};let l=t.slice(o+1).includes(i)?"wrong-order":"mismatch";return {index:o,expected:n,actual:i,status:l,tokens:u}})}function V(t,e,r,n){if(n)return "matched";let o=t.slice(0,r);return o.length>0&&o.every(i=>i.status==="match"||i.status==="partial")?r<e?"partial":"mismatch":o.some(i=>i.status==="wrong-order")?"wrong-order":"mismatch"}function Z(t,e){if(!P(t))return;let r=Y(t),n=[];if(r?.includeCode&&e.input.code&&n.push(`code=${e.input.code}`),r?.includeLocation&&n.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&n.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&n.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...n);return}for(let o of e.attempts)console.log("[useShortcut]",o.status.toUpperCase(),`${e.input.combo} -> ${o.combo}`,...n);}function wt(t){let e=E(),r=L[e],n=[];for(let o of r)o===p.CTRL&&t.ctrl&&n.push("ctrl"),o===p.ALT&&t.alt&&n.push("alt"),o===p.SHIFT&&t.shift&&n.push("shift"),o===p.META&&t.cmd&&n.push("cmd");return n}function tt(t,e){return [...wt(t),e].join("+")}function et(t){return t.map(e=>q(e)).join(" then ")}function A(t){let e=[];t.modifiers.ctrl&&e.push("ctrl"),t.modifiers.alt&&e.push("alt"),t.modifiers.shift&&e.push("shift"),t.modifiers.meta&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function j(t){let e=[];t.ctrlKey&&e.push("ctrl"),t.altKey&&e.push("alt"),t.shiftKey&&e.push("shift"),t.metaKey&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function rt(t){return j(t)}function ot(t,e){if(t.length>e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}function nt(t,e){let r=t.map(A),n=e.map(A),o=r.join(" "),i=n.join(" ");return o===i?"exact":ot(r,n)||ot(n,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var K=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)},editable:t=>t.target instanceof HTMLElement?t.target.isContentEditable:false,typing:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)||e.isContentEditable},modal:()=>typeof document>"u"||typeof document.querySelector!="function"?false:document.querySelector('[data-modal="true"], [role="dialog"]')!==null,disabled:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true"}};function ct(t,e){return e?typeof e=="function"?e(t):Array.isArray(e)?e.some(r=>it[r]?.(t)):it[e]?.(t)??false:false}function C(t){return t?(Array.isArray(t)?t:[t]).map(e=>e.trim()).filter(Boolean):[]}function ut(t,e){if(t.size===0)return true;for(let r of t)if(e.has(r))return true;return false}function at(t){let e=t.key.toLowerCase();return e==="shift"||e==="control"||e==="alt"||e==="meta"}function kt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function _t(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let n=rt(e),o=J(e,n),i=[],s=new Set,c=t.debugListeners.size>0||P(r.debug)||[...t.listeners.values()].some(f=>f.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(n);if(u)for(let f of u)s.add(f);for(let f of t.activeSequenceCombos)s.add(f);if(c)for(let f of t.listeners.keys())s.add(f);for(let f of s){let b=t.listeners.get(f);if(!b)continue;let _=kt(b);for(let a of _){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(K.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){m(r.debug,"Skipped due to except condition:",f);continue}let H=a.parsedSteps[a.progress],T=Date.now();a.progress>0&&T-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&T-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let O=a.progress,M=false;v(e,H)?(a.progress+=1,a.lastMatchedAt=T,a.progress===a.parsedSteps.length&&(M=true,a.progress=0)):a.progress>0&&v(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=T):a.progress=0,a.lastDebugAt=T,a.debugHistory.push(n),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),R=Q(a.expectedSteps,g,M),B={combo:a.combo,display:a.display,description:a.description,status:V(R,a.expectedSteps.length,g.length,M),matched:M,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:O,input:o,steps:R};i.push(B);for(let S of a.attemptCallbacks)S(M,e,B);if(!M)continue;m(r.debug,"MATCHED:",f),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(m(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(f):t.activeSequenceCombos.delete(f);}let l={input:o,attempts:i};if(t.debugListeners.size>0)for(let f of t.debugListeners)f(l);Z(r.debug,l);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",n=o=>_t(t,o);e.addEventListener(r,n),t.listener=n,t.listenerTarget=e,t.listenerEventType=r,m(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,m(t.options.debug,"Listener detached"));}function ft(t,e,r,n){let{options:o,except:i}=t,s=t.steps;if(s.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let c=s.map(d=>k(d)),u=c.map(A).join(" "),l=et(s),f=o.debug??false,b=i??r.except;for(let[d,S]of n.listeners.entries())for(let x of S){if(d===u)continue;let w=nt(c,x.parsedSteps);w&&st(n,{combo:u,existingCombo:d,reason:w});}let _=!r.disabled&&!o.disabled,a=r.delay??o.delay??0,H=r.sequenceTimeout??o.sequenceTimeout??800,T=new Set(C(t.scopes??r.scopes)),O=c.map(A),M=new Set;m(f,"Registering:",u,"\u2192",l,{parsedSteps:c,except:!!b,scopes:[...T]});let g={id:n.nextId++,userHandler:e,isEnabled:_,combo:u,display:l,description:r.description,attemptCallbacks:M,parsedSteps:c,expectedSteps:O,scopes:T,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:H,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},R=n.listeners.get(u);if(R)R.push(g);else {n.listeners.set(u,[g]);let d=A(c[0]),S=n.firstStepIndex.get(d);S?S.add(u):n.firstStepIndex.set(d,new Set([u]));}return pt(n),{unbind:()=>{let d=n.listeners.get(u);if(!d)return;let S=d.filter(x=>x.id!==g.id);if(S.length===0){n.listeners.delete(u),n.activeSequenceCombos.delete(u);let x=A(c[0]),w=n.firstStepIndex.get(x);w&&(w.delete(u),w.size===0&&n.firstStepIndex.delete(x)),m(f,"Unregistered:",u);}else n.listeners.set(u,S);n.listeners.size===0&<(n);},display:l,combo:u,trigger:()=>e(new KeyboardEvent(n.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function z(t,e,r={},n){let o=t.boundCombos?.filter(s=>s.trim());if(!o||o.length<=1)return ft(t,e,r,n);let i=o.map(s=>{let c={...t,steps:[s]};return ft(c,e,r,n)});return {unbind:()=>{for(let s of i)s.unbind();},display:i.map(s=>s.display).join(" / "),combo:i.map(s=>s.combo).join(" | "),trigger:()=>{for(let s of i)s.trigger();},get isEnabled(){return i.some(s=>s.isEnabled)},enable:()=>{for(let s of i)s.enable();},disable:()=>{for(let s of i)s.disable();},onAttempt:s=>{let c=i.map(u=>u.onAttempt?.(s)).filter(u=>!!u);return ()=>{for(let u of c)u();}}}}function dt(t){return (e={})=>new Promise((r,n)=>{let o=e.target??t.target??(typeof window<"u"?window:null),i=e.eventType??t.eventType??"keydown";if(!o){n(new Error("[useShortcut] Cannot record shortcut without a target."));return}let s,c=l=>{let f=l;at(f)||(f.preventDefault(),o.removeEventListener(i,c),s&&clearTimeout(s),r(j(f)));};o.addEventListener(i,c);let u=e.timeoutMs;u&&u>0&&(s=setTimeout(()=>{o.removeEventListener(i,c),n(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var Rt=new Set(["ctrl","shift","alt","cmd","mod"]);function mt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};m(t.debug,"Builder created with options:",t);function r(o){return new Proxy({},{get(i,s){if(s==="__debug")return o.options.debug;if(Rt.has(s)){let c=E(),u=s==="mod"?c===D.MAC?"cmd":"ctrl":s,l={...o,modifiers:{...o.modifiers,[u]:true}};return m(o.options.debug,`Chain: +${s} \u2192`,l.modifiers),r(l)}if(s==="in")return c=>{let u=[...C(o.scopes),...C(c)],l={...o,scopes:u};return r(l)};if(s==="setScopes")return c=>{e.activeScopes=new Set(C(c));};if(s==="enableScope")return c=>{c?.trim()&&e.activeScopes.add(c.trim());};if(s==="disableScope")return c=>{c?.trim()&&e.activeScopes.delete(c.trim());};if(s==="getScopes")return ()=>[...e.activeScopes];if(s==="isScopeActive")return c=>e.activeScopes.has(c);if(s==="onDebug")return c=>(e.debugListeners.add(c),()=>e.debugListeners.delete(c));if(s==="record")return dt(e.options);if(s==="key")return c=>{let u=tt(o.modifiers,c),l={...o,modifiers:{},boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .key("${c}")`),r(l)};if(s==="bind")return c=>{let u=Array.isArray(c)?c:[c],l={...o,modifiers:{},boundCombos:u,steps:u};return m(o.options.debug,`Chain: .bind("${u.join('", "')}")`),r(l)};if(s==="then")return c=>{let u=String(c).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let l={...o,boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .then("${u}")`),r(l)};if(s==="except")return c=>{let u={...o,except:c};return m(o.options.debug,"Chain: .except()",c),r(u)};if(s==="on")return (c,u)=>z(o,c,u,e);if(s==="handle")return c=>{let{handler:u,...l}=c;return z(o,u,l,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Lt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function vt(t,e){let r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return false;for(let o of r){let i=t[o],s=e[o];if(!s||!Lt(i.keys,s.keys)||i.handler!==s.handler||i.options!==s.options)return false}return true}function Pt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function Kt(t,e){let r=e.toLowerCase().split("+").map(i=>i.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let n=r.pop(),o=t;for(let i of r){if(i==="ctrl"||i==="control"){o=o.ctrl;continue}if(i==="shift"){o=o.shift;continue}if(i==="alt"||i==="option"){o=o.alt;continue}if(i==="cmd"||i==="command"||i==="meta"){o=o.cmd;continue}if(i==="mod"){o=o.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${i}" in step "${e}"`)}return o.key(n)}function ht(t,e){let r={};for(let n of Object.keys(e)){let o=e[n],i=Pt(o.keys);if(i.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(n)}" has no key steps`);let s=Kt(t,i[0]);for(let c of i.slice(1))s=s.then(c);r[n]=s.on(o.handler,o.options);}return r}function St(t={}){let e=react.useRef(t);e.current=t;let{builder:r,registry:n}=react.useMemo(()=>mt(e.current),[]);return react.useEffect(()=>{if(n.options=e.current,e.current.activeScopes!==void 0){let o=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];n.activeScopes=new Set(o.map(i=>i.trim()).filter(Boolean));}},[n,t]),react.useEffect(()=>()=>{n.listeners.clear(),n.firstStepIndex.clear(),n.activeSequenceCombos.clear(),n.listener&&n.listenerTarget&&(n.listenerTarget.removeEventListener(n.listenerEventType,n.listener),n.listener=null,n.listenerTarget=null);},[n]),r}function It(t,e={}){let r=St(e),n=react.useRef(t);vt(n.current,t)||(n.current=t);let o=n.current,i=react.useRef({});return react.useEffect(()=>{let s=ht(r,o),c=i.current;for(let u of Object.keys(c))delete c[u];return Object.assign(c,s),()=>{for(let u of Object.values(s))u.unbind();for(let u of Object.keys(c))delete c[u];}},[r,o]),i.current}function gt(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function Ht(){let t=react.useRef(null);return t.current||(t.current=gt()),t.current}exports.ModifierAliases=F;exports.ModifierDisplayOrder=L;exports.ModifierDisplaySymbols=U;exports.ModifierKey=p;exports.Platform=D;exports.SpecialKeyMap=N;exports.createShortcutGroup=gt;exports.detectPlatform=E;exports.formatShortcut=q;exports.matchesAnyShortcut=Et;exports.matchesShortcut=v;exports.parseShortcut=k;exports.parseShortcuts=yt;exports.registerShortcutMap=ht;exports.useShortcut=St;exports.useShortcutGroup=Ht;exports.useShortcutMap=It;
|