@remcostoeten/use-shortcut 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -34
- 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 -415
- package/dist/index.js +1 -946
- package/dist/index.mjs +1 -928
- 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/{index.d.mts → types-Cg7uyv1m.d.ts} +65 -166
- package/package.json +49 -9
- package/CHANGELOG.md +0 -66
- package/dist/cli/index.mjs +0 -455
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { P as ParsedShortcut, e as ModifierState } from './types-Cg7uyv1m.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse a shortcut string into its components
|
|
5
|
+
*
|
|
6
|
+
* @param shortcut - Shortcut string (e.g., "cmd+s", "ctrl+shift+p")
|
|
7
|
+
* @returns Parsed shortcut with modifiers, key, and original string
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const parsed = parseShortcut("cmd+s")
|
|
12
|
+
* // { modifiers: { meta: true, ... }, key: "s", original: "cmd+s" }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare function parseShortcut(shortcut: string): ParsedShortcut;
|
|
16
|
+
/**
|
|
17
|
+
* Parse multiple shortcut strings
|
|
18
|
+
*
|
|
19
|
+
* @param shortcuts - Single shortcut or array of shortcuts
|
|
20
|
+
* @returns Array of parsed shortcuts
|
|
21
|
+
*/
|
|
22
|
+
declare function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[];
|
|
23
|
+
/**
|
|
24
|
+
* Extract modifier state from a keyboard event
|
|
25
|
+
*
|
|
26
|
+
* @param event - The keyboard event
|
|
27
|
+
* @returns Object with meta, ctrl, alt, shift boolean flags
|
|
28
|
+
*/
|
|
29
|
+
declare function getModifiersFromEvent(event: KeyboardEvent): ModifierState;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a keyboard event matches a parsed shortcut
|
|
32
|
+
*
|
|
33
|
+
* @param event - The keyboard event to check
|
|
34
|
+
* @param parsed - The parsed shortcut to match against
|
|
35
|
+
* @returns `true` if the event matches the shortcut
|
|
36
|
+
*/
|
|
37
|
+
declare function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a keyboard event matches any of the parsed shortcuts
|
|
40
|
+
*
|
|
41
|
+
* @param event - The keyboard event to check
|
|
42
|
+
* @param parsedShortcuts - Array of parsed shortcuts to match against
|
|
43
|
+
* @returns `true` if the event matches any shortcut
|
|
44
|
+
*/
|
|
45
|
+
declare function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean;
|
|
46
|
+
|
|
47
|
+
export { getModifiersFromEvent, matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts };
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var a={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},d=a,o=null;function m(){if(o)return o;if(typeof navigator>"u")return o=a.WINDOWS,o;let r=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return r.includes("mac")||r.includes("iphone")||r.includes("ipad")||r.includes("ipod")?(o=a.MAC,o):r.includes("linux")||r.includes("android")?(o=a.LINUX,o):(r.includes("win"),o=a.WINDOWS,o)}var e={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},T={command:e.META,cmd:e.META,"\u2318":e.META,meta:e.META,win:e.META,windows:e.META,super:e.META,mod:e.META,control:e.CTRL,ctrl:e.CTRL,"\u2303":e.CTRL,ctl:e.CTRL,alt:e.ALT,option:e.ALT,opt:e.ALT,"\u2325":e.ALT,shift:e.SHIFT,"\u21E7":e.SHIFT,shft:e.SHIFT},u={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:"]"};function y(t){return t===" "?"space":t.toLowerCase()}function A(t){let r=m(),f=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(f.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=f.pop();for(let l of f){let p=T[l];p?l==="mod"?r===d.MAC?n.meta=true:n.ctrl=true:n[p]=true:s=l+s;}let c=u[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function C(t){return (Array.isArray(t)?t:[t]).map(A)}function M(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function S(t,r){let i=M(t),f=y(t.key),n=i.meta===r.modifiers.meta&&i.ctrl===r.modifiers.ctrl&&i.alt===r.modifiers.alt&&i.shift===r.modifiers.shift,s=f===y(r.key);return n&&s}function F(t,r){return r.some(i=>S(t,i))}exports.getModifiersFromEvent=M;exports.matchesAnyShortcut=F;exports.matchesShortcut=S;exports.parseShortcut=A;exports.parseShortcuts=C;
|
package/dist/parser.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},d=a,o=null;function m(){if(o)return o;if(typeof navigator>"u")return o=a.WINDOWS,o;let r=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return r.includes("mac")||r.includes("iphone")||r.includes("ipad")||r.includes("ipod")?(o=a.MAC,o):r.includes("linux")||r.includes("android")?(o=a.LINUX,o):(r.includes("win"),o=a.WINDOWS,o)}var e={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},T={command:e.META,cmd:e.META,"\u2318":e.META,meta:e.META,win:e.META,windows:e.META,super:e.META,mod:e.META,control:e.CTRL,ctrl:e.CTRL,"\u2303":e.CTRL,ctl:e.CTRL,alt:e.ALT,option:e.ALT,opt:e.ALT,"\u2325":e.ALT,shift:e.SHIFT,"\u21E7":e.SHIFT,shft:e.SHIFT},u={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:"]"};function y(t){return t===" "?"space":t.toLowerCase()}function A(t){let r=m(),f=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(f.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=f.pop();for(let l of f){let p=T[l];p?l==="mod"?r===d.MAC?n.meta=true:n.ctrl=true:n[p]=true:s=l+s;}let c=u[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function C(t){return (Array.isArray(t)?t:[t]).map(A)}function M(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function S(t,r){let i=M(t),f=y(t.key),n=i.meta===r.modifiers.meta&&i.ctrl===r.modifiers.ctrl&&i.alt===r.modifiers.alt&&i.shift===r.modifiers.shift,s=f===y(r.key);return n&&s}function F(t,r){return r.some(i=>S(t,i))}export{M as getModifiersFromEvent,F as matchesAnyShortcut,S as matchesShortcut,A as parseShortcut,C as parseShortcuts};
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { p as ShortcutGroup, r as ShortcutMap, h as ShortcutBuilder, t as ShortcutMapResult, U as UseShortcutOptions } from './types-Cg7uyv1m.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, N as NavigationKey, f as NumericKey, S as ShortcutAttemptDebugEvent, g as ShortcutAttemptStatus, i as ShortcutConflict, j as ShortcutDebugEvent, k as ShortcutDebugInput, l as ShortcutDebugOptions, m as ShortcutDebugStep, n as ShortcutDebugToken, o as ShortcutDebugTokenStatus, q as ShortcutHandler, s as ShortcutMapEntry, u as ShortcutRecordingOptions, v as ShortcutResult, w as ShortcutScope, x as SpecialKey, y as SymbolKey } from './types-Cg7uyv1m.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Registers an object-based shortcut map in one call and returns per-action handles.
|
|
6
|
+
*
|
|
7
|
+
* @param builder - Builder returned by `useShortcut()`
|
|
8
|
+
* @param shortcutMap - Record of action ids to key bindings, handlers, and options
|
|
9
|
+
* @returns A result map with one `ShortcutResult` per shortcut id
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const $ = useShortcut()
|
|
14
|
+
* const results = registerShortcutMap($, {
|
|
15
|
+
* save: { keys: "mod+s", handler: onSave },
|
|
16
|
+
* nav: { keys: ["g", "d"], handler: onGoDashboard },
|
|
17
|
+
* })
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function registerShortcutMap<T extends ShortcutMap>(builder: ShortcutBuilder, shortcutMap: T): ShortcutMapResult<T>;
|
|
21
|
+
/**
|
|
22
|
+
* React hook for registering chainable keyboard shortcuts
|
|
23
|
+
*
|
|
24
|
+
* @param options - Configuration options for the hook
|
|
25
|
+
* @returns A chainable shortcut builder (`$`)
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const $ = useShortcut({ activeScopes: ["editor"] })
|
|
30
|
+
* $.mod.key("s").on((event) => {
|
|
31
|
+
* event.preventDefault()
|
|
32
|
+
* saveDocument()
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
declare function useShortcut(options?: UseShortcutOptions): ShortcutBuilder;
|
|
37
|
+
/**
|
|
38
|
+
* React hook that registers a shortcut map and automatically unbinds on cleanup.
|
|
39
|
+
*
|
|
40
|
+
* @param shortcutMap - Record of action ids to key bindings, handlers, and options
|
|
41
|
+
* @param options - Same options as `useShortcut()`
|
|
42
|
+
* @returns A map of `ShortcutResult` keyed by your shortcut ids
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const mapResults = useShortcutMap({
|
|
47
|
+
* save: { keys: "mod+s", handler: onSave },
|
|
48
|
+
* close: { keys: "escape", handler: onClose },
|
|
49
|
+
* })
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
declare function useShortcutMap<T extends ShortcutMap>(shortcutMap: T, options?: UseShortcutOptions): ShortcutMapResult<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Creates an imperative group controller for many shortcut registrations.
|
|
55
|
+
*
|
|
56
|
+
* @returns A `ShortcutGroup` that can add and unbind multiple shortcuts together
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const group = createShortcutGroup()
|
|
61
|
+
* group.add($.mod.key("s").on(onSave))
|
|
62
|
+
* group.add($.key("escape").on(onClose))
|
|
63
|
+
* group.unbindAll()
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
declare function createShortcutGroup(): ShortcutGroup;
|
|
67
|
+
/**
|
|
68
|
+
* React hook that returns a stable `ShortcutGroup` instance.
|
|
69
|
+
*
|
|
70
|
+
* @returns A memoized `ShortcutGroup` tied to the component lifecycle
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const group = useShortcutGroup()
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function useShortcutGroup(): ShortcutGroup;
|
|
78
|
+
|
|
79
|
+
export { ShortcutBuilder, ShortcutGroup, ShortcutMap, ShortcutMapResult, UseShortcutOptions, createShortcutGroup, registerShortcutMap, useShortcut, useShortcutGroup, useShortcutMap };
|
package/dist/react.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var react=require('react');var m={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=m,y=null;function M(){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"},z={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},$={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:"]"},j={[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"}},D={[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 W(t){return t===" "?"space":t.toLowerCase()}function L(t){let e=M(),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=z[i];u?i==="mod"?e===R.MAC?n.meta=true:n.ctrl=true:n[u]=true:s=i+s;}let c=$[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function gt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function B(t,e){let r=gt(t),o=W(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===W(e.key);return n&&s}function yt(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function F(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function G(t){if(t)return t===true?{console:true}:t}function v(t){let e=G(t);return e?e.console!==false:false}function h(t,...e){v(t)&&console.log("[useShortcut]",...e);}function X(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 bt(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Y(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(F(o)),i=new Set(t.slice(n+1).flatMap(F)),u=F(s).map((l,b,k)=>({token:l,kind:yt(l)||b<k.length-1?"modifier":"key",status:bt(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 J(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 Q(t,e){if(!v(t))return;let r=G(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);}var V={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Et={...V,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Tt={...V,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function Z(t,e){let r=M(),o=L(t),n=j[r],s=D[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){let e=M(),r=D[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 [...At(t),e].join("+")}function et(t){return t.map(e=>Z(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 N(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 N(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 P=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return P.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 P.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 Ct(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function wt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let o=rt(e),n=X(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 k=Ct(b);for(let a of k){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(P.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){h(r.debug,"Skipped due to except condition:",l);continue}let I=a.parsedSteps[a.progress],E=Date.now();a.progress>0&&E-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&E-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let H=a.progress,T=false;B(e,I)?(a.progress+=1,a.lastMatchedAt=E,a.progress===a.parsedSteps.length&&(T=true,a.progress=0)):a.progress>0&&B(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=E):a.progress=0,a.lastDebugAt=E,a.debugHistory.push(o),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),_=Y(a.expectedSteps,g,T),O={combo:a.combo,display:a.display,description:a.description,status:J(_,a.expectedSteps.length,g.length,T),matched:T,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:H,input:n,steps:_};s.push(O);for(let S of a.attemptCallbacks)S(T,e,O);if(!T)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);Q(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=>wt(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 U(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=>L(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 w of S){if(d===u)continue;let x=nt(i,w.parsedSteps);x&&st(o,{combo:u,existingCombo:d,reason:x});}let k=!r.disabled&&!n.disabled,a=r.delay??n.delay??0,I=r.sequenceTimeout??n.sequenceTimeout??800,E=new Set(C(t.scopes??r.scopes)),H=i.map(A),T=new Set;h(l,"Registering:",u,"\u2192",f,{parsedSteps:i,except:!!b,scopes:[...E]});let g={id:o.nextId++,userHandler:e,isEnabled:k,combo:u,display:f,description:r.description,attemptCallbacks:T,parsedSteps:i,expectedSteps:H,scopes:E,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:I,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},_=o.listeners.get(u);if(_)_.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(w=>w.id!==g.id);if(S.length===0){o.listeners.delete(u),o.activeSequenceCombos.delete(u);let w=A(i[0]),x=o.firstStepIndex.get(w);x&&(x.delete(u),x.size===0&&o.firstStepIndex.delete(w)),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(N(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 xt=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(xt.has(c)){let i=M(),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)=>U(n,i,u,e);if(c==="handle")return i=>{let{handler:u,...f}=i;return U(n,u,f,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function _t(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 Rt(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||!_t(s.keys,c.keys)||s.handler!==c.handler||s.options!==c.options)return false}return true}function Dt(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 Lt(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=Dt(n.keys);if(s.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(o)}" has no key steps`);let c=Lt(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 vt(t,e={}){let r=ht(e),o=react.useRef(t);Rt(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 Pt(){let t=react.useRef(null);return t.current||(t.current=St()),t.current}exports.createShortcutGroup=St;exports.registerShortcutMap=mt;exports.useShortcut=ht;exports.useShortcutGroup=Pt;exports.useShortcutMap=vt;
|
package/dist/react.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {useRef,useMemo,useEffect}from'react';var m={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=m,y=null;function M(){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"},z={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},$={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:"]"},j={[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"}},D={[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 W(t){return t===" "?"space":t.toLowerCase()}function L(t){let e=M(),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=z[i];u?i==="mod"?e===R.MAC?n.meta=true:n.ctrl=true:n[u]=true:s=i+s;}let c=$[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function gt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function B(t,e){let r=gt(t),o=W(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===W(e.key);return n&&s}function yt(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function F(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function G(t){if(t)return t===true?{console:true}:t}function v(t){let e=G(t);return e?e.console!==false:false}function h(t,...e){v(t)&&console.log("[useShortcut]",...e);}function X(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 bt(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Y(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(F(o)),i=new Set(t.slice(n+1).flatMap(F)),u=F(s).map((l,b,k)=>({token:l,kind:yt(l)||b<k.length-1?"modifier":"key",status:bt(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 J(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 Q(t,e){if(!v(t))return;let r=G(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);}var V={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Et={...V,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Tt={...V,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function Z(t,e){let r=M(),o=L(t),n=j[r],s=D[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){let e=M(),r=D[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 [...At(t),e].join("+")}function et(t){return t.map(e=>Z(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 N(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 N(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 P=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return P.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 P.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 Ct(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function wt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let o=rt(e),n=X(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 k=Ct(b);for(let a of k){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(P.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){h(r.debug,"Skipped due to except condition:",l);continue}let I=a.parsedSteps[a.progress],E=Date.now();a.progress>0&&E-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&E-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let H=a.progress,T=false;B(e,I)?(a.progress+=1,a.lastMatchedAt=E,a.progress===a.parsedSteps.length&&(T=true,a.progress=0)):a.progress>0&&B(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=E):a.progress=0,a.lastDebugAt=E,a.debugHistory.push(o),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),_=Y(a.expectedSteps,g,T),O={combo:a.combo,display:a.display,description:a.description,status:J(_,a.expectedSteps.length,g.length,T),matched:T,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:H,input:n,steps:_};s.push(O);for(let S of a.attemptCallbacks)S(T,e,O);if(!T)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);Q(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=>wt(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 U(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=>L(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 w of S){if(d===u)continue;let x=nt(i,w.parsedSteps);x&&st(o,{combo:u,existingCombo:d,reason:x});}let k=!r.disabled&&!n.disabled,a=r.delay??n.delay??0,I=r.sequenceTimeout??n.sequenceTimeout??800,E=new Set(C(t.scopes??r.scopes)),H=i.map(A),T=new Set;h(l,"Registering:",u,"\u2192",f,{parsedSteps:i,except:!!b,scopes:[...E]});let g={id:o.nextId++,userHandler:e,isEnabled:k,combo:u,display:f,description:r.description,attemptCallbacks:T,parsedSteps:i,expectedSteps:H,scopes:E,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:I,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},_=o.listeners.get(u);if(_)_.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(w=>w.id!==g.id);if(S.length===0){o.listeners.delete(u),o.activeSequenceCombos.delete(u);let w=A(i[0]),x=o.firstStepIndex.get(w);x&&(x.delete(u),x.size===0&&o.firstStepIndex.delete(w)),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(N(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 xt=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(xt.has(c)){let i=M(),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)=>U(n,i,u,e);if(c==="handle")return i=>{let{handler:u,...f}=i;return U(n,u,f,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function _t(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 Rt(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||!_t(s.keys,c.keys)||s.handler!==c.handler||s.options!==c.options)return false}return true}function Dt(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 Lt(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=Dt(n.keys);if(s.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(o)}" has no key steps`);let c=Lt(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=useRef(t);e.current=t;let{builder:r,registry:o}=useMemo(()=>dt(e.current),[]);return 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]),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 vt(t,e={}){let r=ht(e),o=useRef(t);Rt(o.current,t)||(o.current=t);let n=o.current,s=useRef({});return 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 Pt(){let t=useRef(null);return t.current||(t.current=St()),t.current}export{St as createShortcutGroup,mt as registerShortcutMap,ht as useShortcut,Pt as useShortcutGroup,vt as useShortcutMap};
|
|
@@ -1,38 +1,3 @@
|
|
|
1
|
-
/** Supported runtime OS identifiers used by formatter and parser normalization. */
|
|
2
|
-
declare const OS: {
|
|
3
|
-
readonly MAC: "mac";
|
|
4
|
-
readonly WINDOWS: "windows";
|
|
5
|
-
readonly LINUX: "linux";
|
|
6
|
-
};
|
|
7
|
-
type PlatformType = (typeof OS)[keyof typeof OS];
|
|
8
|
-
/** Public platform constant alias (`Platform.MAC`, `Platform.WINDOWS`, `Platform.LINUX`). */
|
|
9
|
-
declare const Platform: {
|
|
10
|
-
readonly MAC: "mac";
|
|
11
|
-
readonly WINDOWS: "windows";
|
|
12
|
-
readonly LINUX: "linux";
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Detect the current OS platform for modifier normalization and display formatting.
|
|
16
|
-
* Result is memoized for the page lifecycle.
|
|
17
|
-
*/
|
|
18
|
-
declare function detectPlatform(): PlatformType;
|
|
19
|
-
/** Canonical modifier token names used internally across parsing/formatting. */
|
|
20
|
-
declare const ModifierKey: {
|
|
21
|
-
readonly META: "meta";
|
|
22
|
-
readonly CTRL: "ctrl";
|
|
23
|
-
readonly ALT: "alt";
|
|
24
|
-
readonly SHIFT: "shift";
|
|
25
|
-
};
|
|
26
|
-
type ModifierKeyType = (typeof ModifierKey)[keyof typeof ModifierKey];
|
|
27
|
-
/** Alias map from user-facing modifier tokens to canonical modifier keys. */
|
|
28
|
-
declare const ModifierAliases: Record<string, ModifierKeyType>;
|
|
29
|
-
/** Alias map from human shortcut key tokens to `KeyboardEvent.key`-compatible values. */
|
|
30
|
-
declare const SpecialKeyMap: Record<string, string>;
|
|
31
|
-
/** Platform-specific display labels/symbols for modifier keys. */
|
|
32
|
-
declare const ModifierDisplaySymbols: Record<PlatformType, Record<ModifierKeyType, string>>;
|
|
33
|
-
/** Platform-specific canonical order for modifier rendering and combo normalization. */
|
|
34
|
-
declare const ModifierDisplayOrder: Record<PlatformType, ModifierKeyType[]>;
|
|
35
|
-
|
|
36
1
|
/** Lowercase letter keys a-z */
|
|
37
2
|
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
3
|
/** Number keys 0-9 */
|
|
@@ -101,6 +66,65 @@ type ShortcutConflict = {
|
|
|
101
66
|
existingCombo: string;
|
|
102
67
|
reason: "exact" | "sequence-prefix";
|
|
103
68
|
};
|
|
69
|
+
/** High-level match status for one shortcut attempt against the current keyboard input. */
|
|
70
|
+
type ShortcutAttemptStatus = "matched" | "partial" | "wrong-order" | "mismatch";
|
|
71
|
+
/** Token-level verdict for modifiers and keys inside debug attempt payloads. */
|
|
72
|
+
type ShortcutDebugTokenStatus = "match" | "wrong-order" | "mismatch";
|
|
73
|
+
/** Debug metadata for one expected token in a shortcut step. */
|
|
74
|
+
type ShortcutDebugToken = {
|
|
75
|
+
token: string;
|
|
76
|
+
kind: "modifier" | "key";
|
|
77
|
+
status: ShortcutDebugTokenStatus;
|
|
78
|
+
};
|
|
79
|
+
/** Debug metadata for one step in a combo or multi-step shortcut sequence. */
|
|
80
|
+
type ShortcutDebugStep = {
|
|
81
|
+
index: number;
|
|
82
|
+
expected: string;
|
|
83
|
+
actual?: string;
|
|
84
|
+
status: "match" | "partial" | "pending" | "wrong-order" | "mismatch";
|
|
85
|
+
tokens: ShortcutDebugToken[];
|
|
86
|
+
};
|
|
87
|
+
/** Normalized view of the keyboard input that triggered debug processing. */
|
|
88
|
+
type ShortcutDebugInput = {
|
|
89
|
+
key: string;
|
|
90
|
+
code: string;
|
|
91
|
+
location: number;
|
|
92
|
+
repeat: boolean;
|
|
93
|
+
keyCode?: number;
|
|
94
|
+
which?: number;
|
|
95
|
+
combo: string;
|
|
96
|
+
modifiers: ModifierState;
|
|
97
|
+
};
|
|
98
|
+
/** Per-shortcut debug payload describing how one registered shortcut was evaluated. */
|
|
99
|
+
type ShortcutAttemptDebugEvent = {
|
|
100
|
+
combo: string;
|
|
101
|
+
display: string;
|
|
102
|
+
description?: string;
|
|
103
|
+
status: ShortcutAttemptStatus;
|
|
104
|
+
matched: boolean;
|
|
105
|
+
progress: number;
|
|
106
|
+
expectedSteps: string[];
|
|
107
|
+
actualSteps: string[];
|
|
108
|
+
stepIndex: number;
|
|
109
|
+
input: ShortcutDebugInput;
|
|
110
|
+
steps: ShortcutDebugStep[];
|
|
111
|
+
};
|
|
112
|
+
/** Global debug payload emitted for every processed keyboard event. */
|
|
113
|
+
type ShortcutDebugEvent = {
|
|
114
|
+
input: ShortcutDebugInput;
|
|
115
|
+
attempts: ShortcutAttemptDebugEvent[];
|
|
116
|
+
};
|
|
117
|
+
/** Runtime debug configuration for console/debug-stream metadata. */
|
|
118
|
+
type ShortcutDebugOptions = {
|
|
119
|
+
/** Log shortcut attempts to the console (default: true) */
|
|
120
|
+
console?: boolean;
|
|
121
|
+
/** Include `KeyboardEvent.code` in console output */
|
|
122
|
+
includeCode?: boolean;
|
|
123
|
+
/** Include `KeyboardEvent.location` in console output */
|
|
124
|
+
includeLocation?: boolean;
|
|
125
|
+
/** Include deprecated numeric key metadata in console output when available */
|
|
126
|
+
includeKeyCode?: boolean;
|
|
127
|
+
};
|
|
104
128
|
/**
|
|
105
129
|
* Options for shortcut handler registration
|
|
106
130
|
*/
|
|
@@ -146,7 +170,7 @@ type ShortcutResult = {
|
|
|
146
170
|
/** Temporarily disable the shortcut */
|
|
147
171
|
disable: () => void;
|
|
148
172
|
/** Subscribe to shortcut attempt events (useful for visual feedback) */
|
|
149
|
-
onAttempt?: (callback: (matched: boolean, event: KeyboardEvent) => void) => () => void;
|
|
173
|
+
onAttempt?: (callback: (matched: boolean, event: KeyboardEvent, details?: ShortcutAttemptDebugEvent) => void) => () => void;
|
|
150
174
|
};
|
|
151
175
|
/**
|
|
152
176
|
* Chainable modifier builder with type-safe exhaustion
|
|
@@ -234,6 +258,8 @@ type ShortcutBuilder = ModifierChain<EmptyModifiers> & {
|
|
|
234
258
|
getScopes: () => string[];
|
|
235
259
|
/** Check if a scope is active */
|
|
236
260
|
isScopeActive: (scope: string) => boolean;
|
|
261
|
+
/** Subscribe to every keyboard input evaluated by this shortcut registry */
|
|
262
|
+
onDebug: (callback: (event: ShortcutDebugEvent) => void) => () => void;
|
|
237
263
|
/** Record the next key combo */
|
|
238
264
|
record: (options?: ShortcutRecordingOptions) => Promise<string>;
|
|
239
265
|
};
|
|
@@ -241,8 +267,8 @@ type ShortcutBuilder = ModifierChain<EmptyModifiers> & {
|
|
|
241
267
|
* Options for the `useShortcut` hook
|
|
242
268
|
*/
|
|
243
269
|
type UseShortcutOptions = {
|
|
244
|
-
/** Enable debug logging to console */
|
|
245
|
-
debug?: boolean;
|
|
270
|
+
/** Enable debug logging to console or configure structured debug output */
|
|
271
|
+
debug?: boolean | ShortcutDebugOptions;
|
|
246
272
|
/** Global delay for all handlers in milliseconds */
|
|
247
273
|
delay?: number;
|
|
248
274
|
/** Skip shortcuts when focused on input elements (default: `true`) */
|
|
@@ -285,131 +311,4 @@ type ShortcutGroup = {
|
|
|
285
311
|
getResults: () => ShortcutResult[];
|
|
286
312
|
};
|
|
287
313
|
|
|
288
|
-
|
|
289
|
-
* Parse a shortcut string into its components
|
|
290
|
-
*
|
|
291
|
-
* @param shortcut - Shortcut string (e.g., "cmd+s", "ctrl+shift+p")
|
|
292
|
-
* @returns Parsed shortcut with modifiers, key, and original string
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```ts
|
|
296
|
-
* const parsed = parseShortcut("cmd+s")
|
|
297
|
-
* // { modifiers: { meta: true, ... }, key: "s", original: "cmd+s" }
|
|
298
|
-
* ```
|
|
299
|
-
*/
|
|
300
|
-
declare function parseShortcut(shortcut: string): ParsedShortcut;
|
|
301
|
-
/**
|
|
302
|
-
* Parse multiple shortcut strings
|
|
303
|
-
*
|
|
304
|
-
* @param shortcuts - Single shortcut or array of shortcuts
|
|
305
|
-
* @returns Array of parsed shortcuts
|
|
306
|
-
*/
|
|
307
|
-
declare function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[];
|
|
308
|
-
/**
|
|
309
|
-
* Check if a keyboard event matches a parsed shortcut
|
|
310
|
-
*
|
|
311
|
-
* @param event - The keyboard event to check
|
|
312
|
-
* @param parsed - The parsed shortcut to match against
|
|
313
|
-
* @returns `true` if the event matches the shortcut
|
|
314
|
-
*/
|
|
315
|
-
declare function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean;
|
|
316
|
-
/**
|
|
317
|
-
* Check if a keyboard event matches any of the parsed shortcuts
|
|
318
|
-
*
|
|
319
|
-
* @param event - The keyboard event to check
|
|
320
|
-
* @param parsedShortcuts - Array of parsed shortcuts to match against
|
|
321
|
-
* @returns `true` if the event matches any shortcut
|
|
322
|
-
*/
|
|
323
|
-
declare function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean;
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Format a shortcut string for display with platform-aware symbols
|
|
327
|
-
*
|
|
328
|
-
* @param shortcut - Shortcut string (e.g., "cmd+s")
|
|
329
|
-
* @param platform - Optional platform override (default: auto-detect)
|
|
330
|
-
* @returns Formatted display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows)
|
|
331
|
-
*
|
|
332
|
-
* @example
|
|
333
|
-
* ```ts
|
|
334
|
-
* formatShortcut("cmd+s") // "⌘S" on Mac, "Ctrl+S" on Windows
|
|
335
|
-
* formatShortcut("ctrl+shift+p", "mac") // "⌃⇧P"
|
|
336
|
-
* ```
|
|
337
|
-
*/
|
|
338
|
-
declare function formatShortcut(shortcut: string, platform?: PlatformType): string;
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Registers an object-based shortcut map in one call and returns per-action handles.
|
|
342
|
-
*
|
|
343
|
-
* @param builder - Builder returned by `useShortcut()`
|
|
344
|
-
* @param shortcutMap - Record of action ids to key bindings, handlers, and options
|
|
345
|
-
* @returns A result map with one `ShortcutResult` per shortcut id
|
|
346
|
-
*
|
|
347
|
-
* @example
|
|
348
|
-
* ```ts
|
|
349
|
-
* const $ = useShortcut()
|
|
350
|
-
* const results = registerShortcutMap($, {
|
|
351
|
-
* save: { keys: "mod+s", handler: onSave },
|
|
352
|
-
* nav: { keys: ["g", "d"], handler: onGoDashboard },
|
|
353
|
-
* })
|
|
354
|
-
* ```
|
|
355
|
-
*/
|
|
356
|
-
declare function registerShortcutMap<T extends ShortcutMap>(builder: ShortcutBuilder, shortcutMap: T): ShortcutMapResult<T>;
|
|
357
|
-
/**
|
|
358
|
-
* React hook for registering chainable keyboard shortcuts
|
|
359
|
-
*
|
|
360
|
-
* @param options - Configuration options for the hook
|
|
361
|
-
* @returns A chainable shortcut builder (`$`)
|
|
362
|
-
*
|
|
363
|
-
* @example
|
|
364
|
-
* ```ts
|
|
365
|
-
* const $ = useShortcut({ activeScopes: ["editor"] })
|
|
366
|
-
* $.mod.key("s").on((event) => {
|
|
367
|
-
* event.preventDefault()
|
|
368
|
-
* saveDocument()
|
|
369
|
-
* })
|
|
370
|
-
* ```
|
|
371
|
-
*/
|
|
372
|
-
declare function useShortcut(options?: UseShortcutOptions): ShortcutBuilder;
|
|
373
|
-
/**
|
|
374
|
-
* React hook that registers a shortcut map and automatically unbinds on cleanup.
|
|
375
|
-
*
|
|
376
|
-
* @param shortcutMap - Record of action ids to key bindings, handlers, and options
|
|
377
|
-
* @param options - Same options as `useShortcut()`
|
|
378
|
-
* @returns A map of `ShortcutResult` keyed by your shortcut ids
|
|
379
|
-
*
|
|
380
|
-
* @example
|
|
381
|
-
* ```ts
|
|
382
|
-
* const mapResults = useShortcutMap({
|
|
383
|
-
* save: { keys: "mod+s", handler: onSave },
|
|
384
|
-
* close: { keys: "escape", handler: onClose },
|
|
385
|
-
* })
|
|
386
|
-
* ```
|
|
387
|
-
*/
|
|
388
|
-
declare function useShortcutMap<T extends ShortcutMap>(shortcutMap: T, options?: UseShortcutOptions): ShortcutMapResult<T>;
|
|
389
|
-
/**
|
|
390
|
-
* Creates an imperative group controller for many shortcut registrations.
|
|
391
|
-
*
|
|
392
|
-
* @returns A `ShortcutGroup` that can add and unbind multiple shortcuts together
|
|
393
|
-
*
|
|
394
|
-
* @example
|
|
395
|
-
* ```ts
|
|
396
|
-
* const group = createShortcutGroup()
|
|
397
|
-
* group.add($.mod.key("s").on(onSave))
|
|
398
|
-
* group.add($.key("escape").on(onClose))
|
|
399
|
-
* group.unbindAll()
|
|
400
|
-
* ```
|
|
401
|
-
*/
|
|
402
|
-
declare function createShortcutGroup(): ShortcutGroup;
|
|
403
|
-
/**
|
|
404
|
-
* React hook that returns a stable `ShortcutGroup` instance.
|
|
405
|
-
*
|
|
406
|
-
* @returns A memoized `ShortcutGroup` tied to the component lifecycle
|
|
407
|
-
*
|
|
408
|
-
* @example
|
|
409
|
-
* ```ts
|
|
410
|
-
* const group = useShortcutGroup()
|
|
411
|
-
* ```
|
|
412
|
-
*/
|
|
413
|
-
declare function useShortcutGroup(): ShortcutGroup;
|
|
414
|
-
|
|
415
|
-
export { type ActionKey, type AlphaKey, type ExceptPredicate, type ExceptPreset, type FunctionKey, type HandlerOptions, type KeyChain, ModifierAliases, type ModifierChain, ModifierDisplayOrder, ModifierDisplaySymbols, type ModifierFlags, ModifierKey, type ModifierName, type ModifierState, type NavigationKey, type NumericKey, type ParsedShortcut, Platform, type ShortcutBuilder, type ShortcutConflict, type ShortcutGroup, type ShortcutHandler, type ShortcutMap, type ShortcutMapEntry, type ShortcutMapResult, type ShortcutRecordingOptions, type ShortcutResult, type ShortcutScope, type SpecialKey, SpecialKeyMap, type SymbolKey, type UseShortcutOptions, createShortcutGroup, detectPlatform, formatShortcut, matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts, registerShortcutMap, useShortcut, useShortcutGroup, useShortcutMap };
|
|
314
|
+
export type { ActionKey as A, ExceptPredicate as E, FunctionKey as F, HandlerOptions as H, KeyChain as K, ModifierChain as M, NavigationKey as N, ParsedShortcut as P, ShortcutAttemptDebugEvent as S, UseShortcutOptions as U, AlphaKey as a, ExceptPreset as b, ModifierFlags as c, ModifierName as d, ModifierState as e, NumericKey as f, ShortcutAttemptStatus as g, ShortcutBuilder as h, ShortcutConflict as i, ShortcutDebugEvent as j, ShortcutDebugInput as k, ShortcutDebugOptions as l, ShortcutDebugStep as m, ShortcutDebugToken as n, ShortcutDebugTokenStatus as o, ShortcutGroup as p, ShortcutHandler as q, ShortcutMap as r, ShortcutMapEntry as s, ShortcutMapResult as t, ShortcutRecordingOptions as u, ShortcutResult as v, ShortcutScope as w, SpecialKey as x, SymbolKey as y };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remcostoeten/use-shortcut",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "Tiny, chainable React keyboard shortcuts with sequences, scopes, and typed debug hooks.",
|
|
5
5
|
"author": "Remco Stoeten",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/remcostoeten/use-shortcut#readme",
|
|
@@ -23,29 +23,65 @@
|
|
|
23
23
|
"chainable"
|
|
24
24
|
],
|
|
25
25
|
"sideEffects": false,
|
|
26
|
-
"bin": {
|
|
27
|
-
"use-shortcut": "dist/cli/index.mjs"
|
|
28
|
-
},
|
|
29
26
|
"main": "./dist/index.js",
|
|
30
27
|
"module": "./dist/index.mjs",
|
|
31
28
|
"types": "./dist/index.d.ts",
|
|
32
29
|
"exports": {
|
|
33
30
|
".": {
|
|
34
31
|
"import": {
|
|
35
|
-
"types": "./dist/index.d.
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
36
33
|
"default": "./dist/index.mjs"
|
|
37
34
|
},
|
|
38
35
|
"require": {
|
|
39
36
|
"types": "./dist/index.d.ts",
|
|
40
37
|
"default": "./dist/index.js"
|
|
41
38
|
}
|
|
39
|
+
},
|
|
40
|
+
"./react": {
|
|
41
|
+
"import": {
|
|
42
|
+
"types": "./dist/react.d.ts",
|
|
43
|
+
"default": "./dist/react.mjs"
|
|
44
|
+
},
|
|
45
|
+
"require": {
|
|
46
|
+
"types": "./dist/react.d.ts",
|
|
47
|
+
"default": "./dist/react.js"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"./parser": {
|
|
51
|
+
"import": {
|
|
52
|
+
"types": "./dist/parser.d.ts",
|
|
53
|
+
"default": "./dist/parser.mjs"
|
|
54
|
+
},
|
|
55
|
+
"require": {
|
|
56
|
+
"types": "./dist/parser.d.ts",
|
|
57
|
+
"default": "./dist/parser.js"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"./formatter": {
|
|
61
|
+
"import": {
|
|
62
|
+
"types": "./dist/formatter.d.ts",
|
|
63
|
+
"default": "./dist/formatter.mjs"
|
|
64
|
+
},
|
|
65
|
+
"require": {
|
|
66
|
+
"types": "./dist/formatter.d.ts",
|
|
67
|
+
"default": "./dist/formatter.js"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"./constants": {
|
|
71
|
+
"import": {
|
|
72
|
+
"types": "./dist/constants.d.ts",
|
|
73
|
+
"default": "./dist/constants.mjs"
|
|
74
|
+
},
|
|
75
|
+
"require": {
|
|
76
|
+
"types": "./dist/constants.d.ts",
|
|
77
|
+
"default": "./dist/constants.js"
|
|
78
|
+
}
|
|
42
79
|
}
|
|
43
80
|
},
|
|
44
81
|
"files": [
|
|
45
82
|
"dist",
|
|
46
83
|
"README.md",
|
|
47
|
-
"LICENSE"
|
|
48
|
-
"CHANGELOG.md"
|
|
84
|
+
"LICENSE"
|
|
49
85
|
],
|
|
50
86
|
"engines": {
|
|
51
87
|
"node": ">=18.0.0"
|
|
@@ -54,12 +90,16 @@
|
|
|
54
90
|
"access": "public"
|
|
55
91
|
},
|
|
56
92
|
"scripts": {
|
|
57
|
-
"build": "tsup",
|
|
93
|
+
"build": "tsup && node scripts/prune-dist.mjs",
|
|
58
94
|
"dev": "tsup --watch",
|
|
59
95
|
"test": "vitest run --environment jsdom",
|
|
96
|
+
"test:watch": "vitest --environment jsdom",
|
|
97
|
+
"test:entrypoints": "vitest run --environment jsdom src/__tests__/entrypoints.test.ts",
|
|
98
|
+
"test:features": "vitest run --environment jsdom src/__tests__/features.test.ts",
|
|
60
99
|
"typecheck": "tsc --noEmit",
|
|
61
100
|
"docs:api": "node scripts/generate-api-reference.mjs",
|
|
62
101
|
"docs:check": "bun run docs:api && node scripts/docs-check.mjs",
|
|
102
|
+
"verify": "bun run typecheck && bun run test && bun run build && bun run docs:check",
|
|
63
103
|
"clean": "rm -rf dist",
|
|
64
104
|
"prepublishOnly": "npm run build"
|
|
65
105
|
},
|