@remcostoeten/use-shortcut 1.0.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.js ADDED
@@ -0,0 +1,518 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/constants.ts
6
+ var Platform = {
7
+ MAC: "mac",
8
+ WINDOWS: "windows",
9
+ LINUX: "linux"
10
+ };
11
+ function detectPlatform() {
12
+ if (typeof navigator === "undefined") return Platform.WINDOWS;
13
+ const platform = navigator.platform.toLowerCase();
14
+ if (platform.includes("mac")) return Platform.MAC;
15
+ if (platform.includes("linux")) return Platform.LINUX;
16
+ return Platform.WINDOWS;
17
+ }
18
+ var ModifierKey = {
19
+ META: "meta",
20
+ CTRL: "ctrl",
21
+ ALT: "alt",
22
+ SHIFT: "shift"
23
+ };
24
+ var ModifierAliases = {
25
+ command: ModifierKey.META,
26
+ cmd: ModifierKey.META,
27
+ "\u2318": ModifierKey.META,
28
+ meta: ModifierKey.META,
29
+ win: ModifierKey.META,
30
+ windows: ModifierKey.META,
31
+ super: ModifierKey.META,
32
+ mod: ModifierKey.META,
33
+ control: ModifierKey.CTRL,
34
+ ctrl: ModifierKey.CTRL,
35
+ "\u2303": ModifierKey.CTRL,
36
+ ctl: ModifierKey.CTRL,
37
+ alt: ModifierKey.ALT,
38
+ option: ModifierKey.ALT,
39
+ opt: ModifierKey.ALT,
40
+ "\u2325": ModifierKey.ALT,
41
+ shift: ModifierKey.SHIFT,
42
+ "\u21E7": ModifierKey.SHIFT,
43
+ shft: ModifierKey.SHIFT
44
+ };
45
+ var SpecialKeyMap = {
46
+ up: "ArrowUp",
47
+ down: "ArrowDown",
48
+ left: "ArrowLeft",
49
+ right: "ArrowRight",
50
+ home: "Home",
51
+ end: "End",
52
+ pageup: "PageUp",
53
+ pagedown: "PageDown",
54
+ enter: "Enter",
55
+ return: "Enter",
56
+ space: " ",
57
+ spacebar: " ",
58
+ tab: "Tab",
59
+ backspace: "Backspace",
60
+ delete: "Delete",
61
+ del: "Delete",
62
+ escape: "Escape",
63
+ esc: "Escape",
64
+ f1: "F1",
65
+ f2: "F2",
66
+ f3: "F3",
67
+ f4: "F4",
68
+ f5: "F5",
69
+ f6: "F6",
70
+ f7: "F7",
71
+ f8: "F8",
72
+ f9: "F9",
73
+ f10: "F10",
74
+ f11: "F11",
75
+ f12: "F12",
76
+ plus: "+",
77
+ minus: "-",
78
+ comma: ",",
79
+ period: ".",
80
+ slash: "/",
81
+ backslash: "\\",
82
+ bracket: "[",
83
+ closebracket: "]"
84
+ };
85
+ var ModifierDisplaySymbols = {
86
+ [Platform.MAC]: {
87
+ [ModifierKey.META]: "\u2318",
88
+ [ModifierKey.CTRL]: "\u2303",
89
+ [ModifierKey.ALT]: "\u2325",
90
+ [ModifierKey.SHIFT]: "\u21E7"
91
+ },
92
+ [Platform.WINDOWS]: {
93
+ [ModifierKey.META]: "Ctrl",
94
+ [ModifierKey.CTRL]: "Ctrl",
95
+ [ModifierKey.ALT]: "Alt",
96
+ [ModifierKey.SHIFT]: "Shift"
97
+ },
98
+ [Platform.LINUX]: {
99
+ [ModifierKey.META]: "Super",
100
+ [ModifierKey.CTRL]: "Ctrl",
101
+ [ModifierKey.ALT]: "Alt",
102
+ [ModifierKey.SHIFT]: "Shift"
103
+ }
104
+ };
105
+ var ModifierDisplayOrder = {
106
+ [Platform.MAC]: [ModifierKey.CTRL, ModifierKey.ALT, ModifierKey.SHIFT, ModifierKey.META],
107
+ [Platform.WINDOWS]: [ModifierKey.META, ModifierKey.ALT, ModifierKey.SHIFT, ModifierKey.CTRL],
108
+ [Platform.LINUX]: [ModifierKey.META, ModifierKey.ALT, ModifierKey.SHIFT, ModifierKey.CTRL]
109
+ };
110
+
111
+ // src/parser.ts
112
+ function parseShortcut(shortcut) {
113
+ const platform = detectPlatform();
114
+ const normalized = shortcut.toLowerCase().trim();
115
+ const parts = normalized.split(/[\s+-]+/).filter(Boolean);
116
+ if (parts.length === 0) {
117
+ throw new Error(`Invalid shortcut: "${shortcut}"`);
118
+ }
119
+ const modifiers = {
120
+ meta: false,
121
+ ctrl: false,
122
+ alt: false,
123
+ shift: false
124
+ };
125
+ let key = parts.pop();
126
+ for (const part of parts) {
127
+ const modifierKey = ModifierAliases[part];
128
+ if (modifierKey) {
129
+ if (part === "mod") {
130
+ if (platform === Platform.MAC) {
131
+ modifiers.meta = true;
132
+ } else {
133
+ modifiers.ctrl = true;
134
+ }
135
+ } else {
136
+ modifiers[modifierKey] = true;
137
+ }
138
+ } else {
139
+ key = part + key;
140
+ }
141
+ }
142
+ const normalizedKey = SpecialKeyMap[key] || key;
143
+ return {
144
+ modifiers,
145
+ key: normalizedKey.length === 1 ? normalizedKey.toLowerCase() : normalizedKey,
146
+ original: shortcut
147
+ };
148
+ }
149
+ function parseShortcuts(shortcuts) {
150
+ const shortcutArray = Array.isArray(shortcuts) ? shortcuts : [shortcuts];
151
+ return shortcutArray.map(parseShortcut);
152
+ }
153
+ function getModifiersFromEvent(event) {
154
+ return {
155
+ meta: event.metaKey,
156
+ ctrl: event.ctrlKey,
157
+ alt: event.altKey,
158
+ shift: event.shiftKey
159
+ };
160
+ }
161
+ function matchesShortcut(event, parsed) {
162
+ const eventModifiers = getModifiersFromEvent(event);
163
+ const eventKey = event.key.toLowerCase();
164
+ const modifiersMatch = eventModifiers.meta === parsed.modifiers.meta && eventModifiers.ctrl === parsed.modifiers.ctrl && eventModifiers.alt === parsed.modifiers.alt && eventModifiers.shift === parsed.modifiers.shift;
165
+ const keyMatches = eventKey === parsed.key.toLowerCase();
166
+ return modifiersMatch && keyMatches;
167
+ }
168
+ function matchesAnyShortcut(event, parsedShortcuts) {
169
+ return parsedShortcuts.some((parsed) => matchesShortcut(event, parsed));
170
+ }
171
+
172
+ // src/formatter.ts
173
+ function formatShortcut(shortcut, platform) {
174
+ const targetPlatform = platform ?? detectPlatform();
175
+ const parsed = parseShortcut(shortcut);
176
+ const symbols = ModifierDisplaySymbols[targetPlatform];
177
+ const order = ModifierDisplayOrder[targetPlatform];
178
+ const parts = [];
179
+ for (const modifier of order) {
180
+ if (parsed.modifiers[modifier]) {
181
+ parts.push(symbols[modifier]);
182
+ }
183
+ }
184
+ const displayKey = formatKey(parsed.key, targetPlatform);
185
+ parts.push(displayKey);
186
+ const separator = targetPlatform === Platform.MAC ? "" : "+";
187
+ return parts.join(separator);
188
+ }
189
+ function formatKey(key, platform) {
190
+ const displayNames = {
191
+ ArrowUp: "\u2191",
192
+ ArrowDown: "\u2193",
193
+ ArrowLeft: "\u2190",
194
+ ArrowRight: "\u2192",
195
+ Enter: platform === Platform.MAC ? "\u21A9" : "Enter",
196
+ Tab: platform === Platform.MAC ? "\u21E5" : "Tab",
197
+ Escape: platform === Platform.MAC ? "\u238B" : "Esc",
198
+ Backspace: platform === Platform.MAC ? "\u232B" : "Backspace",
199
+ Delete: platform === Platform.MAC ? "\u2326" : "Del",
200
+ " ": platform === Platform.MAC ? "\u2423" : "Space",
201
+ Home: "Home",
202
+ End: "End",
203
+ PageUp: "PgUp",
204
+ PageDown: "PgDn"
205
+ };
206
+ return displayNames[key] || key.toUpperCase();
207
+ }
208
+ function getModifierSymbols(platform) {
209
+ const targetPlatform = platform ?? detectPlatform();
210
+ return ModifierDisplaySymbols[targetPlatform];
211
+ }
212
+
213
+ // src/builder.ts
214
+ var MODIFIER_KEYS = /* @__PURE__ */ new Set(["ctrl", "shift", "alt", "cmd", "mod"]);
215
+ var IGNORED_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
216
+ var EXCEPT_PREDICATES = {
217
+ input: (e) => {
218
+ const target = e.target;
219
+ return IGNORED_TAGS.has(target.tagName);
220
+ },
221
+ editable: (e) => {
222
+ const target = e.target;
223
+ return target.isContentEditable;
224
+ },
225
+ typing: (e) => {
226
+ const target = e.target;
227
+ return IGNORED_TAGS.has(target.tagName) || target.isContentEditable;
228
+ },
229
+ modal: () => {
230
+ return document.querySelector('[data-modal="true"], [role="dialog"]') !== null;
231
+ },
232
+ disabled: (e) => {
233
+ const target = e.target;
234
+ return target.hasAttribute("disabled") || target.getAttribute("aria-disabled") === "true";
235
+ }
236
+ };
237
+ function shouldExcept(event, except) {
238
+ if (!except) return false;
239
+ if (typeof except === "function") {
240
+ return except(event);
241
+ }
242
+ if (Array.isArray(except)) {
243
+ return except.some((preset) => EXCEPT_PREDICATES[preset]?.(event));
244
+ }
245
+ return EXCEPT_PREDICATES[except]?.(event) ?? false;
246
+ }
247
+ function getActiveModifierTokens(modifiers) {
248
+ const platform = detectPlatform();
249
+ const order = ModifierDisplayOrder[platform];
250
+ return order.filter((key) => {
251
+ if (key === ModifierKey.CTRL) return modifiers.ctrl;
252
+ if (key === ModifierKey.ALT) return modifiers.alt;
253
+ if (key === ModifierKey.SHIFT) return modifiers.shift;
254
+ if (key === ModifierKey.META) return modifiers.cmd;
255
+ return false;
256
+ }).map((key) => {
257
+ if (key === ModifierKey.CTRL) return "ctrl";
258
+ if (key === ModifierKey.ALT) return "alt";
259
+ if (key === ModifierKey.SHIFT) return "shift";
260
+ if (key === ModifierKey.META) return "cmd";
261
+ return "";
262
+ });
263
+ }
264
+ function buildComboString(modifiers, key) {
265
+ const tokens = getActiveModifierTokens(modifiers);
266
+ return [...tokens, key].join("+");
267
+ }
268
+ function formatCombo(modifiers, key) {
269
+ const platform = detectPlatform();
270
+ const symbols = ModifierDisplaySymbols[platform];
271
+ const tokens = getActiveModifierTokens(modifiers);
272
+ const parts = tokens.map((t) => {
273
+ if (t === "ctrl") return symbols[ModifierKey.CTRL];
274
+ if (t === "alt") return symbols[ModifierKey.ALT];
275
+ if (t === "shift") return symbols[ModifierKey.SHIFT];
276
+ if (t === "cmd") return symbols[ModifierKey.META];
277
+ return t;
278
+ });
279
+ parts.push(key.length === 1 ? key.toUpperCase() : key);
280
+ return platform === Platform.MAC ? parts.join("") : parts.join("+");
281
+ }
282
+ function debugLog(debug, ...args) {
283
+ if (debug) {
284
+ console.log("[useShortcut]", ...args);
285
+ }
286
+ }
287
+ function createBinding(state, handler, handlerOptions = {}, registry) {
288
+ const { modifiers, key, options, except: stateExcept } = state;
289
+ if (!key) {
290
+ throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");
291
+ }
292
+ const combo = buildComboString(modifiers, key);
293
+ const display = formatCombo(modifiers, key);
294
+ const parsed = parseShortcut(combo);
295
+ const debug = options.debug ?? false;
296
+ const except = stateExcept ?? handlerOptions.except;
297
+ const existing = registry.listeners.get(combo);
298
+ if (existing) {
299
+ debugLog(debug, "Updating existing shortcut handler:", combo);
300
+ existing.userHandler = handler;
301
+ return {
302
+ unbind: existing.unbind,
303
+ display,
304
+ combo,
305
+ trigger: () => existing.userHandler(new KeyboardEvent("keydown")),
306
+ get isEnabled() {
307
+ return existing.isEnabled;
308
+ },
309
+ enable: () => {
310
+ existing.isEnabled = true;
311
+ },
312
+ disable: () => {
313
+ existing.isEnabled = false;
314
+ },
315
+ onAttempt: (callback) => {
316
+ existing.attemptCallbacks.add(callback);
317
+ return () => existing.attemptCallbacks.delete(callback);
318
+ }
319
+ };
320
+ }
321
+ const isEnabled = !handlerOptions.disabled && !options.disabled;
322
+ const delay = handlerOptions.delay ?? options.delay ?? 0;
323
+ const attemptCallbacks = /* @__PURE__ */ new Set();
324
+ debugLog(debug, "Registering:", combo, "\u2192", display, { modifiers, key, parsed, except: !!except });
325
+ function handleEvent(event) {
326
+ const entry = registry.listeners.get(combo);
327
+ if (!entry?.isEnabled) return;
328
+ if (options.ignoreInputs !== false && !except) {
329
+ const target2 = event.target;
330
+ if (IGNORED_TAGS.has(target2.tagName) || target2.isContentEditable) {
331
+ return;
332
+ }
333
+ }
334
+ if (shouldExcept(event, except)) {
335
+ debugLog(debug, "Skipped due to except condition:", combo);
336
+ return;
337
+ }
338
+ debugLog(debug, "Key pressed:", event.key, {
339
+ ctrl: event.ctrlKey,
340
+ alt: event.altKey,
341
+ shift: event.shiftKey,
342
+ meta: event.metaKey
343
+ });
344
+ const matched = matchesShortcut(event, parsed);
345
+ entry.attemptCallbacks.forEach((cb) => cb(matched, event));
346
+ if (matched) {
347
+ debugLog(debug, "MATCHED:", combo, "\u2192", display);
348
+ if (handlerOptions.preventDefault !== false) {
349
+ event.preventDefault();
350
+ }
351
+ if (handlerOptions.stopPropagation) {
352
+ event.stopPropagation();
353
+ }
354
+ const executeHandler = () => entry.userHandler(event);
355
+ if (delay > 0) {
356
+ debugLog(debug, "Delaying execution by", delay, "ms");
357
+ setTimeout(executeHandler, delay);
358
+ } else {
359
+ executeHandler();
360
+ }
361
+ }
362
+ }
363
+ const target = options.target ?? (typeof window !== "undefined" ? window : null);
364
+ const eventType = options.eventType ?? "keydown";
365
+ if (target) {
366
+ target.addEventListener(eventType, handleEvent);
367
+ debugLog(debug, "Listener attached for:", combo);
368
+ }
369
+ function unbind() {
370
+ if (target) {
371
+ target.removeEventListener(eventType, handleEvent);
372
+ registry.listeners.delete(combo);
373
+ debugLog(debug, "Unregistered:", combo);
374
+ }
375
+ }
376
+ registry.listeners.set(combo, {
377
+ listener: handleEvent,
378
+ userHandler: handler,
379
+ unbind,
380
+ isEnabled,
381
+ attemptCallbacks
382
+ });
383
+ return {
384
+ unbind,
385
+ display,
386
+ combo,
387
+ trigger: () => handler(new KeyboardEvent(eventType)),
388
+ get isEnabled() {
389
+ return registry.listeners.get(combo)?.isEnabled ?? false;
390
+ },
391
+ enable: () => {
392
+ const entry = registry.listeners.get(combo);
393
+ if (entry) entry.isEnabled = true;
394
+ },
395
+ disable: () => {
396
+ const entry = registry.listeners.get(combo);
397
+ if (entry) entry.isEnabled = false;
398
+ },
399
+ onAttempt: (callback) => {
400
+ const entry = registry.listeners.get(combo);
401
+ if (entry) {
402
+ entry.attemptCallbacks.add(callback);
403
+ return () => entry.attemptCallbacks.delete(callback);
404
+ }
405
+ return () => {
406
+ };
407
+ }
408
+ };
409
+ }
410
+ function createShortcutBuilder(options = {}) {
411
+ const registry = {
412
+ listeners: /* @__PURE__ */ new Map(),
413
+ options
414
+ };
415
+ debugLog(options.debug, "Builder created with options:", options);
416
+ function createProxy(currentState) {
417
+ return new Proxy({}, {
418
+ get(_, prop) {
419
+ if (prop === "__debug") {
420
+ return currentState.options.debug;
421
+ }
422
+ if (MODIFIER_KEYS.has(prop)) {
423
+ const platform = detectPlatform();
424
+ const modKey = prop === "mod" ? platform === Platform.MAC ? "cmd" : "ctrl" : prop;
425
+ const newState = {
426
+ ...currentState,
427
+ modifiers: { ...currentState.modifiers, [modKey]: true }
428
+ };
429
+ debugLog(currentState.options.debug, `Chain: +${prop} \u2192`, newState.modifiers);
430
+ return createProxy(newState);
431
+ }
432
+ if (prop === "key") {
433
+ return (key) => {
434
+ const newState = {
435
+ ...currentState,
436
+ key
437
+ };
438
+ debugLog(currentState.options.debug, `Chain: .key("${key}")`);
439
+ return createProxy(newState);
440
+ };
441
+ }
442
+ if (prop === "except") {
443
+ return (condition) => {
444
+ const newState = {
445
+ ...currentState,
446
+ except: condition
447
+ };
448
+ debugLog(currentState.options.debug, `Chain: .except()`, condition);
449
+ return createProxy(newState);
450
+ };
451
+ }
452
+ if (prop === "on") {
453
+ return (handler, handlerOptions) => {
454
+ return createBinding(currentState, handler, handlerOptions, registry);
455
+ };
456
+ }
457
+ if (prop === "handle") {
458
+ return (opts) => {
459
+ const { handler, ...rest } = opts;
460
+ return createBinding(currentState, handler, rest, registry);
461
+ };
462
+ }
463
+ return void 0;
464
+ }
465
+ });
466
+ }
467
+ const initialState = {
468
+ modifiers: {},
469
+ key: null,
470
+ options
471
+ };
472
+ return {
473
+ builder: createProxy(initialState),
474
+ registry
475
+ };
476
+ }
477
+
478
+ // src/hook.ts
479
+ function useShortcut(options = {}) {
480
+ const optionsRef = react.useRef(options);
481
+ optionsRef.current = options;
482
+ const { builder, registry } = react.useMemo(() => {
483
+ return createShortcutBuilder(optionsRef.current);
484
+ }, []);
485
+ react.useEffect(() => {
486
+ registry.options = optionsRef.current;
487
+ });
488
+ react.useEffect(() => {
489
+ return () => {
490
+ registry.listeners.forEach((entry) => entry.unbind());
491
+ registry.listeners.clear();
492
+ };
493
+ }, [registry]);
494
+ return builder;
495
+ }
496
+ function createShortcut(options = {}) {
497
+ const { builder } = createShortcutBuilder(options);
498
+ return builder;
499
+ }
500
+
501
+ exports.ModifierAliases = ModifierAliases;
502
+ exports.ModifierDisplayOrder = ModifierDisplayOrder;
503
+ exports.ModifierDisplaySymbols = ModifierDisplaySymbols;
504
+ exports.ModifierKey = ModifierKey;
505
+ exports.Platform = Platform;
506
+ exports.SpecialKeyMap = SpecialKeyMap;
507
+ exports.createShortcut = createShortcut;
508
+ exports.detectPlatform = detectPlatform;
509
+ exports.formatShortcut = formatShortcut;
510
+ exports.getModifierSymbols = getModifierSymbols;
511
+ exports.getModifiersFromEvent = getModifiersFromEvent;
512
+ exports.matchesAnyShortcut = matchesAnyShortcut;
513
+ exports.matchesShortcut = matchesShortcut;
514
+ exports.parseShortcut = parseShortcut;
515
+ exports.parseShortcuts = parseShortcuts;
516
+ exports.useShortcut = useShortcut;
517
+ //# sourceMappingURL=index.js.map
518
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/parser.ts","../src/formatter.ts","../src/builder.ts","../src/hook.ts"],"names":["target","useRef","useMemo","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["export const Platform = {\n MAC: \"mac\",\n WINDOWS: \"windows\",\n LINUX: \"linux\",\n} as const\n\nexport type PlatformType = (typeof Platform)[keyof typeof Platform]\n\nexport function detectPlatform(): PlatformType {\n if (typeof navigator === \"undefined\") return Platform.WINDOWS\n const platform = navigator.platform.toLowerCase()\n if (platform.includes(\"mac\")) return Platform.MAC\n if (platform.includes(\"linux\")) return Platform.LINUX\n return Platform.WINDOWS\n}\n\nexport const ModifierKey = {\n META: \"meta\",\n CTRL: \"ctrl\",\n ALT: \"alt\",\n SHIFT: \"shift\",\n} as const\n\nexport type ModifierKeyType = (typeof ModifierKey)[keyof typeof ModifierKey]\n\nexport const ModifierAliases: Record<string, ModifierKeyType> = {\n command: ModifierKey.META,\n cmd: ModifierKey.META,\n \"⌘\": ModifierKey.META,\n meta: ModifierKey.META,\n win: ModifierKey.META,\n windows: ModifierKey.META,\n super: ModifierKey.META,\n mod: ModifierKey.META,\n control: ModifierKey.CTRL,\n ctrl: ModifierKey.CTRL,\n \"⌃\": ModifierKey.CTRL,\n ctl: ModifierKey.CTRL,\n alt: ModifierKey.ALT,\n option: ModifierKey.ALT,\n opt: ModifierKey.ALT,\n \"⌥\": ModifierKey.ALT,\n shift: ModifierKey.SHIFT,\n \"⇧\": ModifierKey.SHIFT,\n shft: ModifierKey.SHIFT,\n} as const\n\nexport const SpecialKeyMap: Record<string, string> = {\n up: \"ArrowUp\",\n down: \"ArrowDown\",\n left: \"ArrowLeft\",\n right: \"ArrowRight\",\n home: \"Home\",\n end: \"End\",\n pageup: \"PageUp\",\n pagedown: \"PageDown\",\n enter: \"Enter\",\n return: \"Enter\",\n space: \" \",\n spacebar: \" \",\n tab: \"Tab\",\n backspace: \"Backspace\",\n delete: \"Delete\",\n del: \"Delete\",\n escape: \"Escape\",\n esc: \"Escape\",\n f1: \"F1\",\n f2: \"F2\",\n f3: \"F3\",\n f4: \"F4\",\n f5: \"F5\",\n f6: \"F6\",\n f7: \"F7\",\n f8: \"F8\",\n f9: \"F9\",\n f10: \"F10\",\n f11: \"F11\",\n f12: \"F12\",\n plus: \"+\",\n minus: \"-\",\n comma: \",\",\n period: \".\",\n slash: \"/\",\n backslash: \"\\\\\",\n bracket: \"[\",\n closebracket: \"]\",\n} as const\n\nexport const ModifierDisplaySymbols: Record<PlatformType, Record<ModifierKeyType, string>> = {\n [Platform.MAC]: {\n [ModifierKey.META]: \"⌘\",\n [ModifierKey.CTRL]: \"⌃\",\n [ModifierKey.ALT]: \"⌥\",\n [ModifierKey.SHIFT]: \"⇧\",\n },\n [Platform.WINDOWS]: {\n [ModifierKey.META]: \"Ctrl\",\n [ModifierKey.CTRL]: \"Ctrl\",\n [ModifierKey.ALT]: \"Alt\",\n [ModifierKey.SHIFT]: \"Shift\",\n },\n [Platform.LINUX]: {\n [ModifierKey.META]: \"Super\",\n [ModifierKey.CTRL]: \"Ctrl\",\n [ModifierKey.ALT]: \"Alt\",\n [ModifierKey.SHIFT]: \"Shift\",\n },\n} as const\n\nexport const ModifierDisplayOrder: Record<PlatformType, ModifierKeyType[]> = {\n [Platform.MAC]: [ModifierKey.CTRL, ModifierKey.ALT, ModifierKey.SHIFT, ModifierKey.META],\n [Platform.WINDOWS]: [ModifierKey.META, ModifierKey.ALT, ModifierKey.SHIFT, ModifierKey.CTRL],\n [Platform.LINUX]: [ModifierKey.META, ModifierKey.ALT, ModifierKey.SHIFT, ModifierKey.CTRL],\n} as const\n","import { ModifierAliases, SpecialKeyMap, detectPlatform, Platform } from \"./constants\"\nimport type { ModifierState, ParsedShortcut } from \"./types\"\n\n/**\n * Parse a shortcut string into its components\n *\n * @param shortcut - Shortcut string (e.g., \"cmd+s\", \"ctrl+shift+p\")\n * @returns Parsed shortcut with modifiers, key, and original string\n *\n * @example\n * ```ts\n * const parsed = parseShortcut(\"cmd+s\")\n * // { modifiers: { meta: true, ... }, key: \"s\", original: \"cmd+s\" }\n * ```\n */\nexport function parseShortcut(shortcut: string): ParsedShortcut {\n const platform = detectPlatform()\n const normalized = shortcut.toLowerCase().trim()\n const parts = normalized.split(/[\\s+-]+/).filter(Boolean)\n\n if (parts.length === 0) {\n throw new Error(`Invalid shortcut: \"${shortcut}\"`)\n }\n\n const modifiers: ModifierState = {\n meta: false,\n ctrl: false,\n alt: false,\n shift: false,\n }\n\n let key = parts.pop()!\n\n for (const part of parts) {\n const modifierKey = ModifierAliases[part]\n\n if (modifierKey) {\n if (part === \"mod\") {\n if (platform === Platform.MAC) {\n modifiers.meta = true\n } else {\n modifiers.ctrl = true\n }\n } else {\n modifiers[modifierKey] = true\n }\n } else {\n key = part + key\n }\n }\n\n const normalizedKey = SpecialKeyMap[key] || key\n\n return {\n modifiers,\n key: normalizedKey.length === 1 ? normalizedKey.toLowerCase() : normalizedKey,\n original: shortcut,\n }\n}\n\n/**\n * Parse multiple shortcut strings\n *\n * @param shortcuts - Single shortcut or array of shortcuts\n * @returns Array of parsed shortcuts\n */\nexport function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[] {\n const shortcutArray = Array.isArray(shortcuts) ? shortcuts : [shortcuts]\n return shortcutArray.map(parseShortcut)\n}\n\n/**\n * Extract modifier state from a keyboard event\n *\n * @param event - The keyboard event\n * @returns Object with meta, ctrl, alt, shift boolean flags\n */\nexport function getModifiersFromEvent(event: KeyboardEvent): ModifierState {\n return {\n meta: event.metaKey,\n ctrl: event.ctrlKey,\n alt: event.altKey,\n shift: event.shiftKey,\n }\n}\n\n/**\n * Check if a keyboard event matches a parsed shortcut\n *\n * @param event - The keyboard event to check\n * @param parsed - The parsed shortcut to match against\n * @returns `true` if the event matches the shortcut\n */\nexport function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean {\n const eventModifiers = getModifiersFromEvent(event)\n const eventKey = event.key.toLowerCase()\n\n const modifiersMatch =\n eventModifiers.meta === parsed.modifiers.meta &&\n eventModifiers.ctrl === parsed.modifiers.ctrl &&\n eventModifiers.alt === parsed.modifiers.alt &&\n eventModifiers.shift === parsed.modifiers.shift\n\n const keyMatches = eventKey === parsed.key.toLowerCase()\n\n return modifiersMatch && keyMatches\n}\n\n/**\n * Check if a keyboard event matches any of the parsed shortcuts\n *\n * @param event - The keyboard event to check\n * @param parsedShortcuts - Array of parsed shortcuts to match against\n * @returns `true` if the event matches any shortcut\n */\nexport function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean {\n return parsedShortcuts.some((parsed) => matchesShortcut(event, parsed))\n}\n\n","import {\n ModifierDisplayOrder,\n ModifierDisplaySymbols,\n Platform,\n detectPlatform,\n type ModifierKeyType,\n type PlatformType,\n} from \"./constants\"\nimport { parseShortcut } from \"./parser\"\n\n/**\n * Format a shortcut string for display with platform-aware symbols\n *\n * @param shortcut - Shortcut string (e.g., \"cmd+s\")\n * @param platform - Optional platform override (default: auto-detect)\n * @returns Formatted display string (e.g., \"⌘S\" on Mac, \"Ctrl+S\" on Windows)\n *\n * @example\n * ```ts\n * formatShortcut(\"cmd+s\") // \"⌘S\" on Mac, \"Ctrl+S\" on Windows\n * formatShortcut(\"ctrl+shift+p\", \"mac\") // \"⌃⇧P\"\n * ```\n */\nexport function formatShortcut(shortcut: string, platform?: PlatformType): string {\n const targetPlatform = platform ?? detectPlatform()\n const parsed = parseShortcut(shortcut)\n const symbols = ModifierDisplaySymbols[targetPlatform]\n const order = ModifierDisplayOrder[targetPlatform]\n\n const parts: string[] = []\n\n for (const modifier of order) {\n if (parsed.modifiers[modifier]) {\n parts.push(symbols[modifier])\n }\n }\n\n const displayKey = formatKey(parsed.key, targetPlatform)\n parts.push(displayKey)\n\n const separator = targetPlatform === Platform.MAC ? \"\" : \"+\"\n\n return parts.join(separator)\n}\n\nfunction formatKey(key: string, platform: PlatformType): string {\n const displayNames: Record<string, string> = {\n ArrowUp: \"↑\",\n ArrowDown: \"↓\",\n ArrowLeft: \"←\",\n ArrowRight: \"→\",\n Enter: platform === Platform.MAC ? \"↩\" : \"Enter\",\n Tab: platform === Platform.MAC ? \"⇥\" : \"Tab\",\n Escape: platform === Platform.MAC ? \"⎋\" : \"Esc\",\n Backspace: platform === Platform.MAC ? \"⌫\" : \"Backspace\",\n Delete: platform === Platform.MAC ? \"⌦\" : \"Del\",\n \" \": platform === Platform.MAC ? \"␣\" : \"Space\",\n Home: \"Home\",\n End: \"End\",\n PageUp: \"PgUp\",\n PageDown: \"PgDn\",\n }\n\n return displayNames[key] || key.toUpperCase()\n}\n\n/**\n * Get the modifier key symbols for a platform\n *\n * @param platform - Optional platform override (default: auto-detect)\n * @returns Object mapping modifier keys to display symbols\n *\n * @example\n * ```ts\n * getModifierSymbols(\"mac\") // { meta: \"⌘\", ctrl: \"⌃\", alt: \"⌥\", shift: \"⇧\" }\n * ```\n */\nexport function getModifierSymbols(platform?: PlatformType): Record<ModifierKeyType, string> {\n const targetPlatform = platform ?? detectPlatform()\n return ModifierDisplaySymbols[targetPlatform]\n}\n\n","import {\n detectPlatform,\n Platform,\n ModifierDisplaySymbols,\n ModifierKey,\n ModifierDisplayOrder,\n} from \"./constants\"\nimport type { ModifierKeyType } from \"./constants\"\nimport { parseShortcut, matchesShortcut } from \"./parser\"\nimport type {\n ActionKey,\n ModifierFlags,\n ShortcutHandler,\n HandlerOptions,\n ShortcutResult,\n UseShortcutOptions,\n ExceptPreset,\n ExceptPredicate,\n ShortcutBuilder as IShortcutBuilder,\n} from \"./types\"\n\nconst MODIFIER_KEYS = new Set([\"ctrl\", \"shift\", \"alt\", \"cmd\", \"mod\"])\nconst IGNORED_TAGS = new Set([\"INPUT\", \"TEXTAREA\", \"SELECT\"])\n\nconst EXCEPT_PREDICATES: Record<ExceptPreset, ExceptPredicate> = {\n input: (e) => {\n const target = e.target as HTMLElement\n return IGNORED_TAGS.has(target.tagName)\n },\n editable: (e) => {\n const target = e.target as HTMLElement\n return target.isContentEditable\n },\n typing: (e) => {\n const target = e.target as HTMLElement\n return IGNORED_TAGS.has(target.tagName) || target.isContentEditable\n },\n modal: () => {\n return document.querySelector('[data-modal=\"true\"], [role=\"dialog\"]') !== null\n },\n disabled: (e) => {\n const target = e.target as HTMLElement\n return target.hasAttribute(\"disabled\") || target.getAttribute(\"aria-disabled\") === \"true\"\n },\n}\n\nfunction shouldExcept(event: KeyboardEvent, except?: ExceptPreset | ExceptPreset[] | ExceptPredicate): boolean {\n if (!except) return false\n\n if (typeof except === \"function\") {\n return except(event)\n }\n\n if (Array.isArray(except)) {\n return except.some((preset) => EXCEPT_PREDICATES[preset]?.(event))\n }\n\n return EXCEPT_PREDICATES[except]?.(event) ?? false\n}\n\ntype BuilderState = {\n modifiers: Partial<ModifierFlags>\n key: ActionKey | null\n options: UseShortcutOptions\n except?: ExceptPreset | ExceptPreset[] | ExceptPredicate\n}\n\ntype ShortcutRegistry = {\n listeners: Map<\n string,\n {\n listener: (e: KeyboardEvent) => void\n userHandler: ShortcutHandler\n unbind: () => void\n isEnabled: boolean\n attemptCallbacks: Set<(matched: boolean, event: KeyboardEvent) => void>\n }\n >\n options: UseShortcutOptions\n}\n\nfunction getActiveModifierTokens(modifiers: Partial<ModifierFlags>): string[] {\n const platform = detectPlatform()\n const order = ModifierDisplayOrder[platform]\n\n return order\n .filter((key) => {\n if (key === ModifierKey.CTRL) return modifiers.ctrl\n if (key === ModifierKey.ALT) return modifiers.alt\n if (key === ModifierKey.SHIFT) return modifiers.shift\n if (key === ModifierKey.META) return modifiers.cmd\n return false\n })\n .map((key) => {\n if (key === ModifierKey.CTRL) return \"ctrl\"\n if (key === ModifierKey.ALT) return \"alt\"\n if (key === ModifierKey.SHIFT) return \"shift\"\n if (key === ModifierKey.META) return \"cmd\"\n return \"\"\n })\n}\n\nfunction buildComboString(modifiers: Partial<ModifierFlags>, key: ActionKey): string {\n const tokens = getActiveModifierTokens(modifiers)\n return [...tokens, key].join(\"+\")\n}\n\nfunction formatCombo(modifiers: Partial<ModifierFlags>, key: ActionKey): string {\n const platform = detectPlatform()\n const symbols = ModifierDisplaySymbols[platform]\n const tokens = getActiveModifierTokens(modifiers)\n\n const parts = tokens.map((t) => {\n if (t === \"ctrl\") return symbols[ModifierKey.CTRL]\n if (t === \"alt\") return symbols[ModifierKey.ALT]\n if (t === \"shift\") return symbols[ModifierKey.SHIFT]\n if (t === \"cmd\") return symbols[ModifierKey.META]\n return t\n })\n\n parts.push(key.length === 1 ? key.toUpperCase() : key)\n\n return platform === Platform.MAC ? parts.join(\"\") : parts.join(\"+\")\n}\n\nfunction debugLog(debug: boolean | undefined, ...args: unknown[]) {\n if (debug) {\n console.log(\"[useShortcut]\", ...args)\n }\n}\n\nfunction createBinding(\n state: BuilderState,\n handler: ShortcutHandler,\n handlerOptions: HandlerOptions = {},\n registry: ShortcutRegistry,\n): ShortcutResult {\n const { modifiers, key, options, except: stateExcept } = state\n\n if (!key) {\n throw new Error(\"[useShortcut] No key specified. Use .key() to set the action key.\")\n }\n\n const combo = buildComboString(modifiers, key)\n const display = formatCombo(modifiers, key)\n const parsed = parseShortcut(combo)\n const debug = options.debug ?? false\n const except = stateExcept ?? handlerOptions.except\n\n const existing = registry.listeners.get(combo)\n if (existing) {\n debugLog(debug, \"Updating existing shortcut handler:\", combo)\n existing.userHandler = handler\n return {\n unbind: existing.unbind,\n display,\n combo,\n trigger: () => existing.userHandler(new KeyboardEvent(\"keydown\")),\n get isEnabled() {\n return existing.isEnabled\n },\n enable: () => {\n existing.isEnabled = true\n },\n disable: () => {\n existing.isEnabled = false\n },\n onAttempt: (callback) => {\n existing.attemptCallbacks.add(callback)\n return () => existing.attemptCallbacks.delete(callback)\n },\n }\n }\n\n const isEnabled = !handlerOptions.disabled && !options.disabled\n const delay = handlerOptions.delay ?? options.delay ?? 0\n const attemptCallbacks = new Set<(matched: boolean, event: KeyboardEvent) => void>()\n\n debugLog(debug, \"Registering:\", combo, \"→\", display, { modifiers, key, parsed, except: !!except })\n\n function handleEvent(event: KeyboardEvent) {\n const entry = registry.listeners.get(combo)\n if (!entry?.isEnabled) return\n\n if (options.ignoreInputs !== false && !except) {\n const target = event.target as HTMLElement\n if (IGNORED_TAGS.has(target.tagName) || target.isContentEditable) {\n return\n }\n }\n\n if (shouldExcept(event, except)) {\n debugLog(debug, \"Skipped due to except condition:\", combo)\n return\n }\n\n debugLog(debug, \"Key pressed:\", event.key, {\n ctrl: event.ctrlKey,\n alt: event.altKey,\n shift: event.shiftKey,\n meta: event.metaKey,\n })\n\n const matched = matchesShortcut(event, parsed)\n\n entry.attemptCallbacks.forEach((cb) => cb(matched, event))\n\n if (matched) {\n debugLog(debug, \"MATCHED:\", combo, \"→\", display)\n\n if (handlerOptions.preventDefault !== false) {\n event.preventDefault()\n }\n\n if (handlerOptions.stopPropagation) {\n event.stopPropagation()\n }\n\n const executeHandler = () => entry.userHandler(event)\n\n if (delay > 0) {\n debugLog(debug, \"Delaying execution by\", delay, \"ms\")\n setTimeout(executeHandler, delay)\n } else {\n executeHandler()\n }\n }\n }\n\n const target = options.target ?? (typeof window !== \"undefined\" ? window : null)\n const eventType = options.eventType ?? \"keydown\"\n\n if (target) {\n target.addEventListener(eventType, handleEvent as EventListener)\n debugLog(debug, \"Listener attached for:\", combo)\n }\n\n function unbind() {\n if (target) {\n target.removeEventListener(eventType, handleEvent as EventListener)\n registry.listeners.delete(combo)\n debugLog(debug, \"Unregistered:\", combo)\n }\n }\n\n registry.listeners.set(combo, {\n listener: handleEvent,\n userHandler: handler,\n unbind,\n isEnabled,\n attemptCallbacks,\n })\n\n return {\n unbind,\n display,\n combo,\n trigger: () => handler(new KeyboardEvent(eventType)),\n get isEnabled() {\n return registry.listeners.get(combo)?.isEnabled ?? false\n },\n enable: () => {\n const entry = registry.listeners.get(combo)\n if (entry) entry.isEnabled = true\n },\n disable: () => {\n const entry = registry.listeners.get(combo)\n if (entry) entry.isEnabled = false\n },\n onAttempt: (callback) => {\n const entry = registry.listeners.get(combo)\n if (entry) {\n entry.attemptCallbacks.add(callback)\n return () => entry.attemptCallbacks.delete(callback)\n }\n return () => { }\n },\n }\n}\n\nexport function createShortcutBuilder(options: UseShortcutOptions = {}): {\n builder: IShortcutBuilder\n registry: ShortcutRegistry\n} {\n const registry: ShortcutRegistry = {\n listeners: new Map(),\n options,\n }\n\n debugLog(options.debug, \"Builder created with options:\", options)\n\n function createProxy(currentState: BuilderState): IShortcutBuilder {\n return new Proxy({} as IShortcutBuilder, {\n get(_, prop: string) {\n if (prop === \"__debug\") {\n return currentState.options.debug\n }\n\n if (MODIFIER_KEYS.has(prop)) {\n const platform = detectPlatform()\n const modKey = prop === \"mod\" ? (platform === Platform.MAC ? \"cmd\" : \"ctrl\") : prop\n\n const newState: BuilderState = {\n ...currentState,\n modifiers: { ...currentState.modifiers, [modKey]: true },\n }\n\n debugLog(currentState.options.debug, `Chain: +${prop} →`, newState.modifiers)\n\n return createProxy(newState)\n }\n\n if (prop === \"key\") {\n return (key: ActionKey) => {\n const newState: BuilderState = {\n ...currentState,\n key,\n }\n\n debugLog(currentState.options.debug, `Chain: .key(\"${key}\")`)\n\n return createProxy(newState)\n }\n }\n\n if (prop === \"except\") {\n return (condition: ExceptPreset | ExceptPreset[] | ExceptPredicate) => {\n const newState: BuilderState = {\n ...currentState,\n except: condition,\n }\n\n debugLog(currentState.options.debug, `Chain: .except()`, condition)\n\n return createProxy(newState)\n }\n }\n\n if (prop === \"on\") {\n return (handler: ShortcutHandler, handlerOptions?: HandlerOptions) => {\n return createBinding(currentState, handler, handlerOptions, registry)\n }\n }\n\n if (prop === \"handle\") {\n return (opts: HandlerOptions & { handler: ShortcutHandler }) => {\n const { handler, ...rest } = opts\n return createBinding(currentState, handler, rest, registry)\n }\n }\n\n return undefined\n },\n })\n }\n\n const initialState: BuilderState = {\n modifiers: {},\n key: null,\n options,\n }\n\n return {\n builder: createProxy(initialState),\n registry,\n }\n}\n","\"use client\"\n\nimport { useEffect, useRef, useMemo } from \"react\"\nimport { createShortcutBuilder } from \"./builder\"\nimport type { ShortcutBuilder, UseShortcutOptions } from \"./types\"\n\n/**\n * React hook for registering chainable keyboard shortcuts\n *\n * @param options - Configuration options for the hook\n * @returns A chainable shortcut builder (`$`)\n *\n * @example\n * ```tsx\n * function App() {\n * const $ = useShortcut()\n *\n * $.mod.key(\"s\").on(() => save())\n * $.ctrl.shift.key(\"p\").on(() => openPalette())\n * $.key(\"/\").except(\"typing\").on(() => focusSearch())\n *\n * return <div>Press ⌘S to save</div>\n * }\n * ```\n */\nexport function useShortcut(options: UseShortcutOptions = {}): ShortcutBuilder {\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const { builder, registry } = useMemo(() => {\n return createShortcutBuilder(optionsRef.current)\n }, [])\n\n useEffect(() => {\n registry.options = optionsRef.current\n })\n\n useEffect(() => {\n return () => {\n registry.listeners.forEach((entry) => entry.unbind())\n registry.listeners.clear()\n }\n }, [registry])\n\n return builder as ShortcutBuilder\n}\n\n/**\n * Create a shortcut builder for non-React usage\n *\n * Unlike `useShortcut`, this does not auto-cleanup - you must call `.unbind()` manually.\n *\n * @param options - Configuration options\n * @returns A chainable shortcut builder\n *\n * @example\n * ```ts\n * const $ = createShortcut()\n * const save = $.mod.key(\"s\").on(() => save())\n *\n * // Cleanup when done\n * save.unbind()\n * ```\n */\nexport function createShortcut(options: UseShortcutOptions = {}): ShortcutBuilder {\n const { builder } = createShortcutBuilder(options)\n return builder as ShortcutBuilder\n}\n\n"]}