@remcostoeten/use-shortcut 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,6 +40,7 @@ function App() {
40
40
  const $ = useShortcut()
41
41
 
42
42
  $.mod.key("k").on(() => openPalette(), { preventDefault: true })
43
+ $.bind("mod+p").on(() => openProjects())
43
44
  $.key("escape").on(() => closePalette())
44
45
 
45
46
  return <div>Press Cmd/Ctrl+K</div>
@@ -54,9 +55,49 @@ Main React exports:
54
55
  - `createShortcutGroup()`
55
56
  - `useShortcutGroup()`
56
57
 
58
+ ## Bound combo example
59
+
60
+ Use `.bind()` when your shortcuts already exist as strings in config or user
61
+ settings.
62
+
63
+ ```tsx
64
+ import { useShortcut } from "@remcostoeten/use-shortcut/react"
65
+
66
+ const appShortcuts = {
67
+ openCommandPalette: {
68
+ combo: "mod+k",
69
+ description: "Open command palette",
70
+ },
71
+ closeDialog: {
72
+ combo: ["escape", "mod+d"],
73
+ description: "Close dialog",
74
+ },
75
+ }
76
+
77
+ function App() {
78
+ const $ = useShortcut()
79
+
80
+ $.bind(appShortcuts.openCommandPalette.combo).on(() => {
81
+ openPalette()
82
+ }, {
83
+ description: appShortcuts.openCommandPalette.description,
84
+ preventDefault: true,
85
+ })
86
+
87
+ $.bind(appShortcuts.closeDialog.combo).on(() => {
88
+ closeDialog()
89
+ }, {
90
+ description: appShortcuts.closeDialog.description,
91
+ })
92
+
93
+ return <div>Shortcuts ready</div>
94
+ }
95
+ ```
96
+
57
97
  ## Features
58
98
 
59
99
  - Chainable shortcut builder: `$.mod.key("k").on(handler)`
100
+ - Pre-bound combos with `$.bind("mod+k").on(handler)`
60
101
  - Bulk shortcut maps: `useShortcutMap()` and `registerShortcutMap()`
61
102
  - Modifier support: `ctrl`, `shift`, `alt`, `cmd`, `mod`
62
103
  - Sequence support: `$.key("g").then("d")`
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { ModifierAliases, ModifierDisplayOrder, ModifierDisplaySymbols, ModifierKey, Platform, SpecialKeyMap, detectPlatform } from './constants.js';
2
- export { A as ActionKey, a as AlphaKey, E as ExceptPredicate, b as ExceptPreset, F as FunctionKey, H as HandlerOptions, K as KeyChain, M as ModifierChain, c as ModifierFlags, d as ModifierName, e as ModifierState, N as NavigationKey, f as NumericKey, P as ParsedShortcut, S as ShortcutAttemptDebugEvent, g as ShortcutAttemptStatus, h as ShortcutBuilder, i as ShortcutConflict, j as ShortcutDebugEvent, k as ShortcutDebugInput, l as ShortcutDebugOptions, m as ShortcutDebugStep, n as ShortcutDebugToken, o as ShortcutDebugTokenStatus, p as ShortcutGroup, q as ShortcutHandler, r as ShortcutMap, s as ShortcutMapEntry, t as ShortcutMapResult, u as ShortcutRecordingOptions, v as ShortcutResult, w as ShortcutScope, x as SpecialKey, y as SymbolKey, U as UseShortcutOptions } from './types-Cg7uyv1m.js';
2
+ export { A as ActionKey, a as AlphaKey, E as ExceptPredicate, b as ExceptPreset, F as FunctionKey, H as HandlerOptions, K as KeyChain, M as ModifierChain, c as ModifierFlags, d as ModifierName, e as ModifierState, N as NavigationKey, f as NumericKey, P as ParsedShortcut, S as ShortcutAttemptDebugEvent, g as ShortcutAttemptStatus, h as ShortcutBuilder, i as ShortcutConflict, j as ShortcutDebugEvent, k as ShortcutDebugInput, l as ShortcutDebugOptions, m as ShortcutDebugStep, n as ShortcutDebugToken, o as ShortcutDebugTokenStatus, p as ShortcutGroup, q as ShortcutHandler, r as ShortcutMap, s as ShortcutMapEntry, t as ShortcutMapResult, u as ShortcutRecordingOptions, v as ShortcutResult, w as ShortcutScope, x as SpecialKey, y as SymbolKey, U as UseShortcutOptions } from './types-yQWKtHDh.js';
3
3
  export { matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts } from './parser.js';
4
4
  export { formatShortcut } from './formatter.js';
5
5
  export { createShortcutGroup, registerShortcutMap, useShortcut, useShortcutGroup, useShortcutMap } from './react.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var react=require('react');var m={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=m,y=null;function E(){if(y)return y;if(typeof navigator>"u")return y=m.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=m.MAC,y):e.includes("linux")||e.includes("android")?(y=m.LINUX,y):(e.includes("win"),y=m.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},F={command:p.META,cmd:p.META,"\u2318":p.META,meta:p.META,win:p.META,windows:p.META,super:p.META,mod:p.META,control:p.CTRL,ctrl:p.CTRL,"\u2303":p.CTRL,ctl:p.CTRL,alt:p.ALT,option:p.ALT,opt:p.ALT,"\u2325":p.ALT,shift:p.SHIFT,"\u21E7":p.SHIFT,shft:p.SHIFT},N={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"},U={[m.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[m.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[m.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},L={[m.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[m.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[m.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function G(t){return t===" "?"space":t.toLowerCase()}function k(t){let e=E(),o=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(o.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=o.pop();for(let i of o){let u=F[i];u?i==="mod"?e===R.MAC?n.meta=true:n.ctrl=true:n[u]=true:s=i+s;}let c=N[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function gt(t){return (Array.isArray(t)?t:[t]).map(k)}function yt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function P(t,e){let r=yt(t),o=G(t.key),n=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,s=o===G(e.key);return n&&s}function bt(t,e){return e.some(r=>P(t,r))}var X={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Et={...X,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Tt={...X,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function q(t,e){let r=e??E(),o=k(t),n=U[r],s=L[r],c=[];for(let f of s)o.modifiers[f]&&c.push(n[f]);let i=Mt(o.key,r);c.push(i);let u=r===m.MAC?"":"+";return c.join(u)}function Mt(t,e){return (e===m.MAC?Et:Tt)[t]||t.toUpperCase()}function At(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function z(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function Y(t){if(t)return t===true?{console:true}:t}function v(t){let e=Y(t);return e?e.console!==false:false}function h(t,...e){v(t)&&console.log("[useShortcut]",...e);}function J(t,e){return {key:t.key,code:t.code,location:t.location,repeat:t.repeat,keyCode:"keyCode"in t?t.keyCode:void 0,which:"which"in t?t.which:void 0,combo:e,modifiers:{meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}}function Ct(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Q(t,e,r){return t.map((o,n)=>{let s=e[n];if(!s)return {index:n,expected:o,status:"pending",tokens:[]};let c=new Set(z(o)),i=new Set(t.slice(n+1).flatMap(z)),u=z(s).map((l,b,_)=>({token:l,kind:At(l)||b<_.length-1?"modifier":"key",status:Ct(l,c,i)}));if(s===o)return {index:n,expected:o,actual:s,status:r||n<e.length-1?"match":"partial",tokens:u};let f=t.slice(n+1).includes(s)?"wrong-order":"mismatch";return {index:n,expected:o,actual:s,status:f,tokens:u}})}function V(t,e,r,o){if(o)return "matched";let n=t.slice(0,r);return n.length>0&&n.every(s=>s.status==="match"||s.status==="partial")?r<e?"partial":"mismatch":n.some(s=>s.status==="wrong-order")?"wrong-order":"mismatch"}function Z(t,e){if(!v(t))return;let r=Y(t),o=[];if(r?.includeCode&&e.input.code&&o.push(`code=${e.input.code}`),r?.includeLocation&&o.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&o.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&o.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...o);return}for(let n of e.attempts)console.log("[useShortcut]",n.status.toUpperCase(),`${e.input.combo} -> ${n.combo}`,...o);}function xt(t){let e=E(),r=L[e],o=[];for(let n of r)n===p.CTRL&&t.ctrl&&o.push("ctrl"),n===p.ALT&&t.alt&&o.push("alt"),n===p.SHIFT&&t.shift&&o.push("shift"),n===p.META&&t.cmd&&o.push("cmd");return o}function tt(t,e){return [...xt(t),e].join("+")}function et(t){return t.map(e=>q(e)).join(" then ")}function A(t){let e=[];t.modifiers.ctrl&&e.push("ctrl"),t.modifiers.alt&&e.push("alt"),t.modifiers.shift&&e.push("shift"),t.modifiers.meta&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function $(t){let e=[];t.ctrlKey&&e.push("ctrl"),t.altKey&&e.push("alt"),t.shiftKey&&e.push("shift"),t.metaKey&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function rt(t){return $(t)}function ot(t,e){if(t.length>e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}function nt(t,e){let r=t.map(A),o=e.map(A),n=r.join(" "),s=o.join(" ");return n===s?"exact":ot(r,o)||ot(o,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var K=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)},editable:t=>t.target instanceof HTMLElement?t.target.isContentEditable:false,typing:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)||e.isContentEditable},modal:()=>typeof document>"u"||typeof document.querySelector!="function"?false:document.querySelector('[data-modal="true"], [role="dialog"]')!==null,disabled:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true"}};function ct(t,e){return e?typeof e=="function"?e(t):Array.isArray(e)?e.some(r=>it[r]?.(t)):it[e]?.(t)??false:false}function C(t){return t?(Array.isArray(t)?t:[t]).map(e=>e.trim()).filter(Boolean):[]}function ut(t,e){if(t.size===0)return true;for(let r of t)if(e.has(r))return true;return false}function at(t){let e=t.key.toLowerCase();return e==="shift"||e==="control"||e==="alt"||e==="meta"}function wt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function kt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let o=rt(e),n=J(e,o),s=[],c=new Set,i=t.debugListeners.size>0||v(r.debug)||[...t.listeners.values()].some(l=>l.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(o);if(u)for(let l of u)c.add(l);for(let l of t.activeSequenceCombos)c.add(l);if(i)for(let l of t.listeners.keys())c.add(l);for(let l of c){let b=t.listeners.get(l);if(!b)continue;let _=wt(b);for(let a of _){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(K.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){h(r.debug,"Skipped due to except condition:",l);continue}let O=a.parsedSteps[a.progress],T=Date.now();a.progress>0&&T-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&T-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let H=a.progress,M=false;P(e,O)?(a.progress+=1,a.lastMatchedAt=T,a.progress===a.parsedSteps.length&&(M=true,a.progress=0)):a.progress>0&&P(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=T):a.progress=0,a.lastDebugAt=T,a.debugHistory.push(o),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),D=Q(a.expectedSteps,g,M),B={combo:a.combo,display:a.display,description:a.description,status:V(D,a.expectedSteps.length,g.length,M),matched:M,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:H,input:n,steps:D};s.push(B);for(let S of a.attemptCallbacks)S(M,e,B);if(!M)continue;h(r.debug,"MATCHED:",l),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(h(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(l):t.activeSequenceCombos.delete(l);}let f={input:n,attempts:s};if(t.debugListeners.size>0)for(let l of t.debugListeners)l(f);Z(r.debug,f);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",o=n=>kt(t,n);e.addEventListener(r,o),t.listener=o,t.listenerTarget=e,t.listenerEventType=r,h(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,h(t.options.debug,"Listener detached"));}function j(t,e,r={},o){let{options:n,except:s}=t,c=t.steps;if(c.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let i=c.map(d=>k(d)),u=i.map(A).join(" "),f=et(c),l=n.debug??false,b=s??r.except;for(let[d,S]of o.listeners.entries())for(let x of S){if(d===u)continue;let w=nt(i,x.parsedSteps);w&&st(o,{combo:u,existingCombo:d,reason:w});}let _=!r.disabled&&!n.disabled,a=r.delay??n.delay??0,O=r.sequenceTimeout??n.sequenceTimeout??800,T=new Set(C(t.scopes??r.scopes)),H=i.map(A),M=new Set;h(l,"Registering:",u,"\u2192",f,{parsedSteps:i,except:!!b,scopes:[...T]});let g={id:o.nextId++,userHandler:e,isEnabled:_,combo:u,display:f,description:r.description,attemptCallbacks:M,parsedSteps:i,expectedSteps:H,scopes:T,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:O,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},D=o.listeners.get(u);if(D)D.push(g);else {o.listeners.set(u,[g]);let d=A(i[0]),S=o.firstStepIndex.get(d);S?S.add(u):o.firstStepIndex.set(d,new Set([u]));}return pt(o),{unbind:()=>{let d=o.listeners.get(u);if(!d)return;let S=d.filter(x=>x.id!==g.id);if(S.length===0){o.listeners.delete(u),o.activeSequenceCombos.delete(u);let x=A(i[0]),w=o.firstStepIndex.get(x);w&&(w.delete(u),w.size===0&&o.firstStepIndex.delete(x)),h(l,"Unregistered:",u);}else o.listeners.set(u,S);o.listeners.size===0&&lt(o);},display:f,combo:u,trigger:()=>e(new KeyboardEvent(o.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function ft(t){return (e={})=>new Promise((r,o)=>{let n=e.target??t.target??(typeof window<"u"?window:null),s=e.eventType??t.eventType??"keydown";if(!n){o(new Error("[useShortcut] Cannot record shortcut without a target."));return}let c,i=f=>{let l=f;at(l)||(l.preventDefault(),n.removeEventListener(s,i),c&&clearTimeout(c),r($(l)));};n.addEventListener(s,i);let u=e.timeoutMs;u&&u>0&&(c=setTimeout(()=>{n.removeEventListener(s,i),o(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var _t=new Set(["ctrl","shift","alt","cmd","mod"]);function dt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};h(t.debug,"Builder created with options:",t);function r(n){return new Proxy({},{get(s,c){if(c==="__debug")return n.options.debug;if(_t.has(c)){let i=E(),u=c==="mod"?i===R.MAC?"cmd":"ctrl":c,f={...n,modifiers:{...n.modifiers,[u]:true}};return h(n.options.debug,`Chain: +${c} \u2192`,f.modifiers),r(f)}if(c==="in")return i=>{let u=[...C(n.scopes),...C(i)],f={...n,scopes:u};return r(f)};if(c==="setScopes")return i=>{e.activeScopes=new Set(C(i));};if(c==="enableScope")return i=>{i?.trim()&&e.activeScopes.add(i.trim());};if(c==="disableScope")return i=>{i?.trim()&&e.activeScopes.delete(i.trim());};if(c==="getScopes")return ()=>[...e.activeScopes];if(c==="isScopeActive")return i=>e.activeScopes.has(i);if(c==="onDebug")return i=>(e.debugListeners.add(i),()=>e.debugListeners.delete(i));if(c==="record")return ft(e.options);if(c==="key")return i=>{let u=tt(n.modifiers,i),f={...n,modifiers:{},steps:[...n.steps,u]};return h(n.options.debug,`Chain: .key("${i}")`),r(f)};if(c==="then")return i=>{let u=String(i).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let f={...n,steps:[...n.steps,u]};return h(n.options.debug,`Chain: .then("${u}")`),r(f)};if(c==="except")return i=>{let u={...n,except:i};return h(n.options.debug,"Chain: .except()",i),r(u)};if(c==="on")return (i,u)=>j(n,i,u,e);if(c==="handle")return i=>{let{handler:u,...f}=i;return j(n,u,f,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Rt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function Lt(t,e){let r=Object.keys(t),o=Object.keys(e);if(r.length!==o.length)return false;for(let n of r){let s=t[n],c=e[n];if(!c||!Rt(s.keys,c.keys)||s.handler!==c.handler||s.options!==c.options)return false}return true}function Pt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function vt(t,e){let r=e.toLowerCase().split("+").map(s=>s.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let o=r.pop(),n=t;for(let s of r){if(s==="ctrl"||s==="control"){n=n.ctrl;continue}if(s==="shift"){n=n.shift;continue}if(s==="alt"||s==="option"){n=n.alt;continue}if(s==="cmd"||s==="command"||s==="meta"){n=n.cmd;continue}if(s==="mod"){n=n.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${s}" in step "${e}"`)}return n.key(o)}function mt(t,e){let r={};for(let o of Object.keys(e)){let n=e[o],s=Pt(n.keys);if(s.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(o)}" has no key steps`);let c=vt(t,s[0]);for(let i of s.slice(1))c=c.then(i);r[o]=c.on(n.handler,n.options);}return r}function ht(t={}){let e=react.useRef(t);e.current=t;let{builder:r,registry:o}=react.useMemo(()=>dt(e.current),[]);return react.useEffect(()=>{if(o.options=e.current,e.current.activeScopes!==void 0){let n=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];o.activeScopes=new Set(n.map(s=>s.trim()).filter(Boolean));}},[o,t]),react.useEffect(()=>()=>{o.listeners.clear(),o.firstStepIndex.clear(),o.activeSequenceCombos.clear(),o.listener&&o.listenerTarget&&(o.listenerTarget.removeEventListener(o.listenerEventType,o.listener),o.listener=null,o.listenerTarget=null);},[o]),r}function Kt(t,e={}){let r=ht(e),o=react.useRef(t);Lt(o.current,t)||(o.current=t);let n=o.current,s=react.useRef({});return react.useEffect(()=>{let c=mt(r,n),i=s.current;for(let u of Object.keys(i))delete i[u];return Object.assign(i,c),()=>{for(let u of Object.values(c))u.unbind();for(let u of Object.keys(i))delete i[u];}},[r,n]),s.current}function St(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function It(){let t=react.useRef(null);return t.current||(t.current=St()),t.current}exports.ModifierAliases=F;exports.ModifierDisplayOrder=L;exports.ModifierDisplaySymbols=U;exports.ModifierKey=p;exports.Platform=R;exports.SpecialKeyMap=N;exports.createShortcutGroup=St;exports.detectPlatform=E;exports.formatShortcut=q;exports.matchesAnyShortcut=bt;exports.matchesShortcut=P;exports.parseShortcut=k;exports.parseShortcuts=gt;exports.registerShortcutMap=mt;exports.useShortcut=ht;exports.useShortcutGroup=It;exports.useShortcutMap=Kt;
1
+ 'use strict';var react=require('react');var h={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},D=h,y=null;function E(){if(y)return y;if(typeof navigator>"u")return y=h.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=h.MAC,y):e.includes("linux")||e.includes("android")?(y=h.LINUX,y):(e.includes("win"),y=h.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},F={command:p.META,cmd:p.META,"\u2318":p.META,meta:p.META,win:p.META,windows:p.META,super:p.META,mod:p.META,control:p.CTRL,ctrl:p.CTRL,"\u2303":p.CTRL,ctl:p.CTRL,alt:p.ALT,option:p.ALT,opt:p.ALT,"\u2325":p.ALT,shift:p.SHIFT,"\u21E7":p.SHIFT,shft:p.SHIFT},N={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"},U={[h.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[h.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[h.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},L={[h.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[h.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[h.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function G(t){return t===" "?"space":t.toLowerCase()}function k(t){let e=E(),n=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(n.length===0)throw new Error(`Invalid shortcut: "${t}"`);let o={meta:false,ctrl:false,alt:false,shift:false},i=n.pop();for(let c of n){let u=F[c];u?c==="mod"?e===D.MAC?o.meta=true:o.ctrl=true:o[u]=true:i=c+i;}let s=N[i]||i;return {modifiers:o,key:s.length===1?s.toLowerCase():s,original:t}}function yt(t){return (Array.isArray(t)?t:[t]).map(k)}function bt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function v(t,e){let r=bt(t),n=G(t.key),o=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,i=n===G(e.key);return o&&i}function Et(t,e){return e.some(r=>v(t,r))}var X={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Tt={...X,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Mt={...X,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function q(t,e){let r=e??E(),n=k(t),o=U[r],i=L[r],s=[];for(let l of i)n.modifiers[l]&&s.push(o[l]);let c=At(n.key,r);s.push(c);let u=r===h.MAC?"":"+";return s.join(u)}function At(t,e){return (e===h.MAC?Tt:Mt)[t]||t.toUpperCase()}function Ct(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function $(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function Y(t){if(t)return t===true?{console:true}:t}function P(t){let e=Y(t);return e?e.console!==false:false}function m(t,...e){P(t)&&console.log("[useShortcut]",...e);}function J(t,e){return {key:t.key,code:t.code,location:t.location,repeat:t.repeat,keyCode:"keyCode"in t?t.keyCode:void 0,which:"which"in t?t.which:void 0,combo:e,modifiers:{meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}}function xt(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Q(t,e,r){return t.map((n,o)=>{let i=e[o];if(!i)return {index:o,expected:n,status:"pending",tokens:[]};let s=new Set($(n)),c=new Set(t.slice(o+1).flatMap($)),u=$(i).map((f,b,_)=>({token:f,kind:Ct(f)||b<_.length-1?"modifier":"key",status:xt(f,s,c)}));if(i===n)return {index:o,expected:n,actual:i,status:r||o<e.length-1?"match":"partial",tokens:u};let l=t.slice(o+1).includes(i)?"wrong-order":"mismatch";return {index:o,expected:n,actual:i,status:l,tokens:u}})}function V(t,e,r,n){if(n)return "matched";let o=t.slice(0,r);return o.length>0&&o.every(i=>i.status==="match"||i.status==="partial")?r<e?"partial":"mismatch":o.some(i=>i.status==="wrong-order")?"wrong-order":"mismatch"}function Z(t,e){if(!P(t))return;let r=Y(t),n=[];if(r?.includeCode&&e.input.code&&n.push(`code=${e.input.code}`),r?.includeLocation&&n.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&n.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&n.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...n);return}for(let o of e.attempts)console.log("[useShortcut]",o.status.toUpperCase(),`${e.input.combo} -> ${o.combo}`,...n);}function wt(t){let e=E(),r=L[e],n=[];for(let o of r)o===p.CTRL&&t.ctrl&&n.push("ctrl"),o===p.ALT&&t.alt&&n.push("alt"),o===p.SHIFT&&t.shift&&n.push("shift"),o===p.META&&t.cmd&&n.push("cmd");return n}function tt(t,e){return [...wt(t),e].join("+")}function et(t){return t.map(e=>q(e)).join(" then ")}function A(t){let e=[];t.modifiers.ctrl&&e.push("ctrl"),t.modifiers.alt&&e.push("alt"),t.modifiers.shift&&e.push("shift"),t.modifiers.meta&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function j(t){let e=[];t.ctrlKey&&e.push("ctrl"),t.altKey&&e.push("alt"),t.shiftKey&&e.push("shift"),t.metaKey&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function rt(t){return j(t)}function ot(t,e){if(t.length>e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}function nt(t,e){let r=t.map(A),n=e.map(A),o=r.join(" "),i=n.join(" ");return o===i?"exact":ot(r,n)||ot(n,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var K=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)},editable:t=>t.target instanceof HTMLElement?t.target.isContentEditable:false,typing:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)||e.isContentEditable},modal:()=>typeof document>"u"||typeof document.querySelector!="function"?false:document.querySelector('[data-modal="true"], [role="dialog"]')!==null,disabled:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true"}};function ct(t,e){return e?typeof e=="function"?e(t):Array.isArray(e)?e.some(r=>it[r]?.(t)):it[e]?.(t)??false:false}function C(t){return t?(Array.isArray(t)?t:[t]).map(e=>e.trim()).filter(Boolean):[]}function ut(t,e){if(t.size===0)return true;for(let r of t)if(e.has(r))return true;return false}function at(t){let e=t.key.toLowerCase();return e==="shift"||e==="control"||e==="alt"||e==="meta"}function kt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function _t(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let n=rt(e),o=J(e,n),i=[],s=new Set,c=t.debugListeners.size>0||P(r.debug)||[...t.listeners.values()].some(f=>f.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(n);if(u)for(let f of u)s.add(f);for(let f of t.activeSequenceCombos)s.add(f);if(c)for(let f of t.listeners.keys())s.add(f);for(let f of s){let b=t.listeners.get(f);if(!b)continue;let _=kt(b);for(let a of _){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(K.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){m(r.debug,"Skipped due to except condition:",f);continue}let H=a.parsedSteps[a.progress],T=Date.now();a.progress>0&&T-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&T-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let O=a.progress,M=false;v(e,H)?(a.progress+=1,a.lastMatchedAt=T,a.progress===a.parsedSteps.length&&(M=true,a.progress=0)):a.progress>0&&v(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=T):a.progress=0,a.lastDebugAt=T,a.debugHistory.push(n),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),R=Q(a.expectedSteps,g,M),B={combo:a.combo,display:a.display,description:a.description,status:V(R,a.expectedSteps.length,g.length,M),matched:M,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:O,input:o,steps:R};i.push(B);for(let S of a.attemptCallbacks)S(M,e,B);if(!M)continue;m(r.debug,"MATCHED:",f),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(m(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(f):t.activeSequenceCombos.delete(f);}let l={input:o,attempts:i};if(t.debugListeners.size>0)for(let f of t.debugListeners)f(l);Z(r.debug,l);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",n=o=>_t(t,o);e.addEventListener(r,n),t.listener=n,t.listenerTarget=e,t.listenerEventType=r,m(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,m(t.options.debug,"Listener detached"));}function ft(t,e,r,n){let{options:o,except:i}=t,s=t.steps;if(s.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let c=s.map(d=>k(d)),u=c.map(A).join(" "),l=et(s),f=o.debug??false,b=i??r.except;for(let[d,S]of n.listeners.entries())for(let x of S){if(d===u)continue;let w=nt(c,x.parsedSteps);w&&st(n,{combo:u,existingCombo:d,reason:w});}let _=!r.disabled&&!o.disabled,a=r.delay??o.delay??0,H=r.sequenceTimeout??o.sequenceTimeout??800,T=new Set(C(t.scopes??r.scopes)),O=c.map(A),M=new Set;m(f,"Registering:",u,"\u2192",l,{parsedSteps:c,except:!!b,scopes:[...T]});let g={id:n.nextId++,userHandler:e,isEnabled:_,combo:u,display:l,description:r.description,attemptCallbacks:M,parsedSteps:c,expectedSteps:O,scopes:T,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:H,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},R=n.listeners.get(u);if(R)R.push(g);else {n.listeners.set(u,[g]);let d=A(c[0]),S=n.firstStepIndex.get(d);S?S.add(u):n.firstStepIndex.set(d,new Set([u]));}return pt(n),{unbind:()=>{let d=n.listeners.get(u);if(!d)return;let S=d.filter(x=>x.id!==g.id);if(S.length===0){n.listeners.delete(u),n.activeSequenceCombos.delete(u);let x=A(c[0]),w=n.firstStepIndex.get(x);w&&(w.delete(u),w.size===0&&n.firstStepIndex.delete(x)),m(f,"Unregistered:",u);}else n.listeners.set(u,S);n.listeners.size===0&&lt(n);},display:l,combo:u,trigger:()=>e(new KeyboardEvent(n.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function z(t,e,r={},n){let o=t.boundCombos?.filter(s=>s.trim());if(!o||o.length<=1)return ft(t,e,r,n);let i=o.map(s=>{let c={...t,steps:[s]};return ft(c,e,r,n)});return {unbind:()=>{for(let s of i)s.unbind();},display:i.map(s=>s.display).join(" / "),combo:i.map(s=>s.combo).join(" | "),trigger:()=>{for(let s of i)s.trigger();},get isEnabled(){return i.some(s=>s.isEnabled)},enable:()=>{for(let s of i)s.enable();},disable:()=>{for(let s of i)s.disable();},onAttempt:s=>{let c=i.map(u=>u.onAttempt?.(s)).filter(u=>!!u);return ()=>{for(let u of c)u();}}}}function dt(t){return (e={})=>new Promise((r,n)=>{let o=e.target??t.target??(typeof window<"u"?window:null),i=e.eventType??t.eventType??"keydown";if(!o){n(new Error("[useShortcut] Cannot record shortcut without a target."));return}let s,c=l=>{let f=l;at(f)||(f.preventDefault(),o.removeEventListener(i,c),s&&clearTimeout(s),r(j(f)));};o.addEventListener(i,c);let u=e.timeoutMs;u&&u>0&&(s=setTimeout(()=>{o.removeEventListener(i,c),n(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var Rt=new Set(["ctrl","shift","alt","cmd","mod"]);function mt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};m(t.debug,"Builder created with options:",t);function r(o){return new Proxy({},{get(i,s){if(s==="__debug")return o.options.debug;if(Rt.has(s)){let c=E(),u=s==="mod"?c===D.MAC?"cmd":"ctrl":s,l={...o,modifiers:{...o.modifiers,[u]:true}};return m(o.options.debug,`Chain: +${s} \u2192`,l.modifiers),r(l)}if(s==="in")return c=>{let u=[...C(o.scopes),...C(c)],l={...o,scopes:u};return r(l)};if(s==="setScopes")return c=>{e.activeScopes=new Set(C(c));};if(s==="enableScope")return c=>{c?.trim()&&e.activeScopes.add(c.trim());};if(s==="disableScope")return c=>{c?.trim()&&e.activeScopes.delete(c.trim());};if(s==="getScopes")return ()=>[...e.activeScopes];if(s==="isScopeActive")return c=>e.activeScopes.has(c);if(s==="onDebug")return c=>(e.debugListeners.add(c),()=>e.debugListeners.delete(c));if(s==="record")return dt(e.options);if(s==="key")return c=>{let u=tt(o.modifiers,c),l={...o,modifiers:{},boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .key("${c}")`),r(l)};if(s==="bind")return c=>{let u=Array.isArray(c)?c:[c],l={...o,modifiers:{},boundCombos:u,steps:u};return m(o.options.debug,`Chain: .bind("${u.join('", "')}")`),r(l)};if(s==="then")return c=>{let u=String(c).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let l={...o,boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .then("${u}")`),r(l)};if(s==="except")return c=>{let u={...o,except:c};return m(o.options.debug,"Chain: .except()",c),r(u)};if(s==="on")return (c,u)=>z(o,c,u,e);if(s==="handle")return c=>{let{handler:u,...l}=c;return z(o,u,l,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Lt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function vt(t,e){let r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return false;for(let o of r){let i=t[o],s=e[o];if(!s||!Lt(i.keys,s.keys)||i.handler!==s.handler||i.options!==s.options)return false}return true}function Pt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function Kt(t,e){let r=e.toLowerCase().split("+").map(i=>i.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let n=r.pop(),o=t;for(let i of r){if(i==="ctrl"||i==="control"){o=o.ctrl;continue}if(i==="shift"){o=o.shift;continue}if(i==="alt"||i==="option"){o=o.alt;continue}if(i==="cmd"||i==="command"||i==="meta"){o=o.cmd;continue}if(i==="mod"){o=o.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${i}" in step "${e}"`)}return o.key(n)}function ht(t,e){let r={};for(let n of Object.keys(e)){let o=e[n],i=Pt(o.keys);if(i.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(n)}" has no key steps`);let s=Kt(t,i[0]);for(let c of i.slice(1))s=s.then(c);r[n]=s.on(o.handler,o.options);}return r}function St(t={}){let e=react.useRef(t);e.current=t;let{builder:r,registry:n}=react.useMemo(()=>mt(e.current),[]);return react.useEffect(()=>{if(n.options=e.current,e.current.activeScopes!==void 0){let o=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];n.activeScopes=new Set(o.map(i=>i.trim()).filter(Boolean));}},[n,t]),react.useEffect(()=>()=>{n.listeners.clear(),n.firstStepIndex.clear(),n.activeSequenceCombos.clear(),n.listener&&n.listenerTarget&&(n.listenerTarget.removeEventListener(n.listenerEventType,n.listener),n.listener=null,n.listenerTarget=null);},[n]),r}function It(t,e={}){let r=St(e),n=react.useRef(t);vt(n.current,t)||(n.current=t);let o=n.current,i=react.useRef({});return react.useEffect(()=>{let s=ht(r,o),c=i.current;for(let u of Object.keys(c))delete c[u];return Object.assign(c,s),()=>{for(let u of Object.values(s))u.unbind();for(let u of Object.keys(c))delete c[u];}},[r,o]),i.current}function gt(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function Ht(){let t=react.useRef(null);return t.current||(t.current=gt()),t.current}exports.ModifierAliases=F;exports.ModifierDisplayOrder=L;exports.ModifierDisplaySymbols=U;exports.ModifierKey=p;exports.Platform=D;exports.SpecialKeyMap=N;exports.createShortcutGroup=gt;exports.detectPlatform=E;exports.formatShortcut=q;exports.matchesAnyShortcut=Et;exports.matchesShortcut=v;exports.parseShortcut=k;exports.parseShortcuts=yt;exports.registerShortcutMap=ht;exports.useShortcut=St;exports.useShortcutGroup=Ht;exports.useShortcutMap=It;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {useRef,useMemo,useEffect}from'react';var m={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=m,y=null;function E(){if(y)return y;if(typeof navigator>"u")return y=m.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=m.MAC,y):e.includes("linux")||e.includes("android")?(y=m.LINUX,y):(e.includes("win"),y=m.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},F={command:p.META,cmd:p.META,"\u2318":p.META,meta:p.META,win:p.META,windows:p.META,super:p.META,mod:p.META,control:p.CTRL,ctrl:p.CTRL,"\u2303":p.CTRL,ctl:p.CTRL,alt:p.ALT,option:p.ALT,opt:p.ALT,"\u2325":p.ALT,shift:p.SHIFT,"\u21E7":p.SHIFT,shft:p.SHIFT},N={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"},U={[m.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[m.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[m.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},L={[m.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[m.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[m.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function G(t){return t===" "?"space":t.toLowerCase()}function k(t){let e=E(),o=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(o.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=o.pop();for(let i of o){let u=F[i];u?i==="mod"?e===R.MAC?n.meta=true:n.ctrl=true:n[u]=true:s=i+s;}let c=N[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function gt(t){return (Array.isArray(t)?t:[t]).map(k)}function yt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function P(t,e){let r=yt(t),o=G(t.key),n=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,s=o===G(e.key);return n&&s}function bt(t,e){return e.some(r=>P(t,r))}var X={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Et={...X,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Tt={...X,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function q(t,e){let r=e??E(),o=k(t),n=U[r],s=L[r],c=[];for(let f of s)o.modifiers[f]&&c.push(n[f]);let i=Mt(o.key,r);c.push(i);let u=r===m.MAC?"":"+";return c.join(u)}function Mt(t,e){return (e===m.MAC?Et:Tt)[t]||t.toUpperCase()}function At(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function z(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function Y(t){if(t)return t===true?{console:true}:t}function v(t){let e=Y(t);return e?e.console!==false:false}function h(t,...e){v(t)&&console.log("[useShortcut]",...e);}function J(t,e){return {key:t.key,code:t.code,location:t.location,repeat:t.repeat,keyCode:"keyCode"in t?t.keyCode:void 0,which:"which"in t?t.which:void 0,combo:e,modifiers:{meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}}function Ct(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Q(t,e,r){return t.map((o,n)=>{let s=e[n];if(!s)return {index:n,expected:o,status:"pending",tokens:[]};let c=new Set(z(o)),i=new Set(t.slice(n+1).flatMap(z)),u=z(s).map((l,b,_)=>({token:l,kind:At(l)||b<_.length-1?"modifier":"key",status:Ct(l,c,i)}));if(s===o)return {index:n,expected:o,actual:s,status:r||n<e.length-1?"match":"partial",tokens:u};let f=t.slice(n+1).includes(s)?"wrong-order":"mismatch";return {index:n,expected:o,actual:s,status:f,tokens:u}})}function V(t,e,r,o){if(o)return "matched";let n=t.slice(0,r);return n.length>0&&n.every(s=>s.status==="match"||s.status==="partial")?r<e?"partial":"mismatch":n.some(s=>s.status==="wrong-order")?"wrong-order":"mismatch"}function Z(t,e){if(!v(t))return;let r=Y(t),o=[];if(r?.includeCode&&e.input.code&&o.push(`code=${e.input.code}`),r?.includeLocation&&o.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&o.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&o.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...o);return}for(let n of e.attempts)console.log("[useShortcut]",n.status.toUpperCase(),`${e.input.combo} -> ${n.combo}`,...o);}function xt(t){let e=E(),r=L[e],o=[];for(let n of r)n===p.CTRL&&t.ctrl&&o.push("ctrl"),n===p.ALT&&t.alt&&o.push("alt"),n===p.SHIFT&&t.shift&&o.push("shift"),n===p.META&&t.cmd&&o.push("cmd");return o}function tt(t,e){return [...xt(t),e].join("+")}function et(t){return t.map(e=>q(e)).join(" then ")}function A(t){let e=[];t.modifiers.ctrl&&e.push("ctrl"),t.modifiers.alt&&e.push("alt"),t.modifiers.shift&&e.push("shift"),t.modifiers.meta&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function $(t){let e=[];t.ctrlKey&&e.push("ctrl"),t.altKey&&e.push("alt"),t.shiftKey&&e.push("shift"),t.metaKey&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function rt(t){return $(t)}function ot(t,e){if(t.length>e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}function nt(t,e){let r=t.map(A),o=e.map(A),n=r.join(" "),s=o.join(" ");return n===s?"exact":ot(r,o)||ot(o,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var K=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)},editable:t=>t.target instanceof HTMLElement?t.target.isContentEditable:false,typing:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)||e.isContentEditable},modal:()=>typeof document>"u"||typeof document.querySelector!="function"?false:document.querySelector('[data-modal="true"], [role="dialog"]')!==null,disabled:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true"}};function ct(t,e){return e?typeof e=="function"?e(t):Array.isArray(e)?e.some(r=>it[r]?.(t)):it[e]?.(t)??false:false}function C(t){return t?(Array.isArray(t)?t:[t]).map(e=>e.trim()).filter(Boolean):[]}function ut(t,e){if(t.size===0)return true;for(let r of t)if(e.has(r))return true;return false}function at(t){let e=t.key.toLowerCase();return e==="shift"||e==="control"||e==="alt"||e==="meta"}function wt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function kt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let o=rt(e),n=J(e,o),s=[],c=new Set,i=t.debugListeners.size>0||v(r.debug)||[...t.listeners.values()].some(l=>l.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(o);if(u)for(let l of u)c.add(l);for(let l of t.activeSequenceCombos)c.add(l);if(i)for(let l of t.listeners.keys())c.add(l);for(let l of c){let b=t.listeners.get(l);if(!b)continue;let _=wt(b);for(let a of _){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(K.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){h(r.debug,"Skipped due to except condition:",l);continue}let O=a.parsedSteps[a.progress],T=Date.now();a.progress>0&&T-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&T-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let H=a.progress,M=false;P(e,O)?(a.progress+=1,a.lastMatchedAt=T,a.progress===a.parsedSteps.length&&(M=true,a.progress=0)):a.progress>0&&P(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=T):a.progress=0,a.lastDebugAt=T,a.debugHistory.push(o),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),D=Q(a.expectedSteps,g,M),B={combo:a.combo,display:a.display,description:a.description,status:V(D,a.expectedSteps.length,g.length,M),matched:M,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:H,input:n,steps:D};s.push(B);for(let S of a.attemptCallbacks)S(M,e,B);if(!M)continue;h(r.debug,"MATCHED:",l),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(h(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(l):t.activeSequenceCombos.delete(l);}let f={input:n,attempts:s};if(t.debugListeners.size>0)for(let l of t.debugListeners)l(f);Z(r.debug,f);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",o=n=>kt(t,n);e.addEventListener(r,o),t.listener=o,t.listenerTarget=e,t.listenerEventType=r,h(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,h(t.options.debug,"Listener detached"));}function j(t,e,r={},o){let{options:n,except:s}=t,c=t.steps;if(c.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let i=c.map(d=>k(d)),u=i.map(A).join(" "),f=et(c),l=n.debug??false,b=s??r.except;for(let[d,S]of o.listeners.entries())for(let x of S){if(d===u)continue;let w=nt(i,x.parsedSteps);w&&st(o,{combo:u,existingCombo:d,reason:w});}let _=!r.disabled&&!n.disabled,a=r.delay??n.delay??0,O=r.sequenceTimeout??n.sequenceTimeout??800,T=new Set(C(t.scopes??r.scopes)),H=i.map(A),M=new Set;h(l,"Registering:",u,"\u2192",f,{parsedSteps:i,except:!!b,scopes:[...T]});let g={id:o.nextId++,userHandler:e,isEnabled:_,combo:u,display:f,description:r.description,attemptCallbacks:M,parsedSteps:i,expectedSteps:H,scopes:T,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:O,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},D=o.listeners.get(u);if(D)D.push(g);else {o.listeners.set(u,[g]);let d=A(i[0]),S=o.firstStepIndex.get(d);S?S.add(u):o.firstStepIndex.set(d,new Set([u]));}return pt(o),{unbind:()=>{let d=o.listeners.get(u);if(!d)return;let S=d.filter(x=>x.id!==g.id);if(S.length===0){o.listeners.delete(u),o.activeSequenceCombos.delete(u);let x=A(i[0]),w=o.firstStepIndex.get(x);w&&(w.delete(u),w.size===0&&o.firstStepIndex.delete(x)),h(l,"Unregistered:",u);}else o.listeners.set(u,S);o.listeners.size===0&&lt(o);},display:f,combo:u,trigger:()=>e(new KeyboardEvent(o.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function ft(t){return (e={})=>new Promise((r,o)=>{let n=e.target??t.target??(typeof window<"u"?window:null),s=e.eventType??t.eventType??"keydown";if(!n){o(new Error("[useShortcut] Cannot record shortcut without a target."));return}let c,i=f=>{let l=f;at(l)||(l.preventDefault(),n.removeEventListener(s,i),c&&clearTimeout(c),r($(l)));};n.addEventListener(s,i);let u=e.timeoutMs;u&&u>0&&(c=setTimeout(()=>{n.removeEventListener(s,i),o(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var _t=new Set(["ctrl","shift","alt","cmd","mod"]);function dt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};h(t.debug,"Builder created with options:",t);function r(n){return new Proxy({},{get(s,c){if(c==="__debug")return n.options.debug;if(_t.has(c)){let i=E(),u=c==="mod"?i===R.MAC?"cmd":"ctrl":c,f={...n,modifiers:{...n.modifiers,[u]:true}};return h(n.options.debug,`Chain: +${c} \u2192`,f.modifiers),r(f)}if(c==="in")return i=>{let u=[...C(n.scopes),...C(i)],f={...n,scopes:u};return r(f)};if(c==="setScopes")return i=>{e.activeScopes=new Set(C(i));};if(c==="enableScope")return i=>{i?.trim()&&e.activeScopes.add(i.trim());};if(c==="disableScope")return i=>{i?.trim()&&e.activeScopes.delete(i.trim());};if(c==="getScopes")return ()=>[...e.activeScopes];if(c==="isScopeActive")return i=>e.activeScopes.has(i);if(c==="onDebug")return i=>(e.debugListeners.add(i),()=>e.debugListeners.delete(i));if(c==="record")return ft(e.options);if(c==="key")return i=>{let u=tt(n.modifiers,i),f={...n,modifiers:{},steps:[...n.steps,u]};return h(n.options.debug,`Chain: .key("${i}")`),r(f)};if(c==="then")return i=>{let u=String(i).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let f={...n,steps:[...n.steps,u]};return h(n.options.debug,`Chain: .then("${u}")`),r(f)};if(c==="except")return i=>{let u={...n,except:i};return h(n.options.debug,"Chain: .except()",i),r(u)};if(c==="on")return (i,u)=>j(n,i,u,e);if(c==="handle")return i=>{let{handler:u,...f}=i;return j(n,u,f,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Rt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function Lt(t,e){let r=Object.keys(t),o=Object.keys(e);if(r.length!==o.length)return false;for(let n of r){let s=t[n],c=e[n];if(!c||!Rt(s.keys,c.keys)||s.handler!==c.handler||s.options!==c.options)return false}return true}function Pt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function vt(t,e){let r=e.toLowerCase().split("+").map(s=>s.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let o=r.pop(),n=t;for(let s of r){if(s==="ctrl"||s==="control"){n=n.ctrl;continue}if(s==="shift"){n=n.shift;continue}if(s==="alt"||s==="option"){n=n.alt;continue}if(s==="cmd"||s==="command"||s==="meta"){n=n.cmd;continue}if(s==="mod"){n=n.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${s}" in step "${e}"`)}return n.key(o)}function mt(t,e){let r={};for(let o of Object.keys(e)){let n=e[o],s=Pt(n.keys);if(s.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(o)}" has no key steps`);let c=vt(t,s[0]);for(let i of s.slice(1))c=c.then(i);r[o]=c.on(n.handler,n.options);}return r}function ht(t={}){let e=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 Kt(t,e={}){let r=ht(e),o=useRef(t);Lt(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 It(){let t=useRef(null);return t.current||(t.current=St()),t.current}export{F as ModifierAliases,L as ModifierDisplayOrder,U as ModifierDisplaySymbols,p as ModifierKey,R as Platform,N as SpecialKeyMap,St as createShortcutGroup,E as detectPlatform,q as formatShortcut,bt as matchesAnyShortcut,P as matchesShortcut,k as parseShortcut,gt as parseShortcuts,mt as registerShortcutMap,ht as useShortcut,It as useShortcutGroup,Kt as useShortcutMap};
1
+ import {useRef,useMemo,useEffect}from'react';var h={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},D=h,y=null;function E(){if(y)return y;if(typeof navigator>"u")return y=h.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=h.MAC,y):e.includes("linux")||e.includes("android")?(y=h.LINUX,y):(e.includes("win"),y=h.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},F={command:p.META,cmd:p.META,"\u2318":p.META,meta:p.META,win:p.META,windows:p.META,super:p.META,mod:p.META,control:p.CTRL,ctrl:p.CTRL,"\u2303":p.CTRL,ctl:p.CTRL,alt:p.ALT,option:p.ALT,opt:p.ALT,"\u2325":p.ALT,shift:p.SHIFT,"\u21E7":p.SHIFT,shft:p.SHIFT},N={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"},U={[h.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[h.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[h.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},L={[h.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[h.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[h.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function G(t){return t===" "?"space":t.toLowerCase()}function k(t){let e=E(),n=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(n.length===0)throw new Error(`Invalid shortcut: "${t}"`);let o={meta:false,ctrl:false,alt:false,shift:false},i=n.pop();for(let c of n){let u=F[c];u?c==="mod"?e===D.MAC?o.meta=true:o.ctrl=true:o[u]=true:i=c+i;}let s=N[i]||i;return {modifiers:o,key:s.length===1?s.toLowerCase():s,original:t}}function yt(t){return (Array.isArray(t)?t:[t]).map(k)}function bt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function v(t,e){let r=bt(t),n=G(t.key),o=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,i=n===G(e.key);return o&&i}function Et(t,e){return e.some(r=>v(t,r))}var X={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Tt={...X,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},Mt={...X,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function q(t,e){let r=e??E(),n=k(t),o=U[r],i=L[r],s=[];for(let l of i)n.modifiers[l]&&s.push(o[l]);let c=At(n.key,r);s.push(c);let u=r===h.MAC?"":"+";return s.join(u)}function At(t,e){return (e===h.MAC?Tt:Mt)[t]||t.toUpperCase()}function Ct(t){return t==="ctrl"||t==="alt"||t==="shift"||t==="cmd"}function $(t){return t.split("+").map(e=>e.trim()).filter(Boolean)}function Y(t){if(t)return t===true?{console:true}:t}function P(t){let e=Y(t);return e?e.console!==false:false}function m(t,...e){P(t)&&console.log("[useShortcut]",...e);}function J(t,e){return {key:t.key,code:t.code,location:t.location,repeat:t.repeat,keyCode:"keyCode"in t?t.keyCode:void 0,which:"which"in t?t.which:void 0,combo:e,modifiers:{meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}}function xt(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Q(t,e,r){return t.map((n,o)=>{let i=e[o];if(!i)return {index:o,expected:n,status:"pending",tokens:[]};let s=new Set($(n)),c=new Set(t.slice(o+1).flatMap($)),u=$(i).map((f,b,_)=>({token:f,kind:Ct(f)||b<_.length-1?"modifier":"key",status:xt(f,s,c)}));if(i===n)return {index:o,expected:n,actual:i,status:r||o<e.length-1?"match":"partial",tokens:u};let l=t.slice(o+1).includes(i)?"wrong-order":"mismatch";return {index:o,expected:n,actual:i,status:l,tokens:u}})}function V(t,e,r,n){if(n)return "matched";let o=t.slice(0,r);return o.length>0&&o.every(i=>i.status==="match"||i.status==="partial")?r<e?"partial":"mismatch":o.some(i=>i.status==="wrong-order")?"wrong-order":"mismatch"}function Z(t,e){if(!P(t))return;let r=Y(t),n=[];if(r?.includeCode&&e.input.code&&n.push(`code=${e.input.code}`),r?.includeLocation&&n.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&n.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&n.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...n);return}for(let o of e.attempts)console.log("[useShortcut]",o.status.toUpperCase(),`${e.input.combo} -> ${o.combo}`,...n);}function wt(t){let e=E(),r=L[e],n=[];for(let o of r)o===p.CTRL&&t.ctrl&&n.push("ctrl"),o===p.ALT&&t.alt&&n.push("alt"),o===p.SHIFT&&t.shift&&n.push("shift"),o===p.META&&t.cmd&&n.push("cmd");return n}function tt(t,e){return [...wt(t),e].join("+")}function et(t){return t.map(e=>q(e)).join(" then ")}function A(t){let e=[];t.modifiers.ctrl&&e.push("ctrl"),t.modifiers.alt&&e.push("alt"),t.modifiers.shift&&e.push("shift"),t.modifiers.meta&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function j(t){let e=[];t.ctrlKey&&e.push("ctrl"),t.altKey&&e.push("alt"),t.shiftKey&&e.push("shift"),t.metaKey&&e.push("cmd");let r=t.key===" "||t.key==="Spacebar"?"space":t.key.toLowerCase();return [...e,r].join("+")}function rt(t){return j(t)}function ot(t,e){if(t.length>e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}function nt(t,e){let r=t.map(A),n=e.map(A),o=r.join(" "),i=n.join(" ");return o===i?"exact":ot(r,n)||ot(n,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var K=new Set(["INPUT","TEXTAREA","SELECT"]),it={input:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)},editable:t=>t.target instanceof HTMLElement?t.target.isContentEditable:false,typing:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return K.has(e.tagName)||e.isContentEditable},modal:()=>typeof document>"u"||typeof document.querySelector!="function"?false:document.querySelector('[data-modal="true"], [role="dialog"]')!==null,disabled:t=>{if(!(t.target instanceof HTMLElement))return false;let e=t.target;return e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true"}};function ct(t,e){return e?typeof e=="function"?e(t):Array.isArray(e)?e.some(r=>it[r]?.(t)):it[e]?.(t)??false:false}function C(t){return t?(Array.isArray(t)?t:[t]).map(e=>e.trim()).filter(Boolean):[]}function ut(t,e){if(t.size===0)return true;for(let r of t)if(e.has(r))return true;return false}function at(t){let e=t.key.toLowerCase();return e==="shift"||e==="control"||e==="alt"||e==="meta"}function kt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function _t(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let n=rt(e),o=J(e,n),i=[],s=new Set,c=t.debugListeners.size>0||P(r.debug)||[...t.listeners.values()].some(f=>f.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(n);if(u)for(let f of u)s.add(f);for(let f of t.activeSequenceCombos)s.add(f);if(c)for(let f of t.listeners.keys())s.add(f);for(let f of s){let b=t.listeners.get(f);if(!b)continue;let _=kt(b);for(let a of _){if(!a.isEnabled||!ut(a.scopes,t.activeScopes))continue;if(r.ignoreInputs!==false&&!a.except){let S=e.target;if(S&&(K.has(S.tagName)||S.isContentEditable))continue}if(ct(e,a.except)){m(r.debug,"Skipped due to except condition:",f);continue}let H=a.parsedSteps[a.progress],T=Date.now();a.progress>0&&T-a.lastMatchedAt>a.sequenceTimeout&&(a.progress=0),a.debugHistory.length>0&&T-a.lastDebugAt>a.sequenceTimeout&&(a.debugHistory=[]);let O=a.progress,M=false;v(e,H)?(a.progress+=1,a.lastMatchedAt=T,a.progress===a.parsedSteps.length&&(M=true,a.progress=0)):a.progress>0&&v(e,a.parsedSteps[0])?(a.progress=1,a.lastMatchedAt=T):a.progress=0,a.lastDebugAt=T,a.debugHistory.push(n),a.debugHistory.length>a.expectedSteps.length&&a.debugHistory.shift();let g=a.debugHistory.slice(-a.expectedSteps.length),R=Q(a.expectedSteps,g,M),B={combo:a.combo,display:a.display,description:a.description,status:V(R,a.expectedSteps.length,g.length,M),matched:M,progress:a.progress,expectedSteps:a.expectedSteps,actualSteps:g,stepIndex:O,input:o,steps:R};i.push(B);for(let S of a.attemptCallbacks)S(M,e,B);if(!M)continue;m(r.debug,"MATCHED:",f),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(m(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(f):t.activeSequenceCombos.delete(f);}let l={input:o,attempts:i};if(t.debugListeners.size>0)for(let f of t.debugListeners)f(l);Z(r.debug,l);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",n=o=>_t(t,o);e.addEventListener(r,n),t.listener=n,t.listenerTarget=e,t.listenerEventType=r,m(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,m(t.options.debug,"Listener detached"));}function ft(t,e,r,n){let{options:o,except:i}=t,s=t.steps;if(s.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let c=s.map(d=>k(d)),u=c.map(A).join(" "),l=et(s),f=o.debug??false,b=i??r.except;for(let[d,S]of n.listeners.entries())for(let x of S){if(d===u)continue;let w=nt(c,x.parsedSteps);w&&st(n,{combo:u,existingCombo:d,reason:w});}let _=!r.disabled&&!o.disabled,a=r.delay??o.delay??0,H=r.sequenceTimeout??o.sequenceTimeout??800,T=new Set(C(t.scopes??r.scopes)),O=c.map(A),M=new Set;m(f,"Registering:",u,"\u2192",l,{parsedSteps:c,except:!!b,scopes:[...T]});let g={id:n.nextId++,userHandler:e,isEnabled:_,combo:u,display:l,description:r.description,attemptCallbacks:M,parsedSteps:c,expectedSteps:O,scopes:T,progress:0,lastMatchedAt:0,debugHistory:[],lastDebugAt:0,except:b,delay:a,sequenceTimeout:H,preventDefault:r.preventDefault!==false,stopPropagation:r.stopPropagation??false,stopOnMatch:r.stopOnMatch??false,priority:r.priority??0},R=n.listeners.get(u);if(R)R.push(g);else {n.listeners.set(u,[g]);let d=A(c[0]),S=n.firstStepIndex.get(d);S?S.add(u):n.firstStepIndex.set(d,new Set([u]));}return pt(n),{unbind:()=>{let d=n.listeners.get(u);if(!d)return;let S=d.filter(x=>x.id!==g.id);if(S.length===0){n.listeners.delete(u),n.activeSequenceCombos.delete(u);let x=A(c[0]),w=n.firstStepIndex.get(x);w&&(w.delete(u),w.size===0&&n.firstStepIndex.delete(x)),m(f,"Unregistered:",u);}else n.listeners.set(u,S);n.listeners.size===0&&lt(n);},display:l,combo:u,trigger:()=>e(new KeyboardEvent(n.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function z(t,e,r={},n){let o=t.boundCombos?.filter(s=>s.trim());if(!o||o.length<=1)return ft(t,e,r,n);let i=o.map(s=>{let c={...t,steps:[s]};return ft(c,e,r,n)});return {unbind:()=>{for(let s of i)s.unbind();},display:i.map(s=>s.display).join(" / "),combo:i.map(s=>s.combo).join(" | "),trigger:()=>{for(let s of i)s.trigger();},get isEnabled(){return i.some(s=>s.isEnabled)},enable:()=>{for(let s of i)s.enable();},disable:()=>{for(let s of i)s.disable();},onAttempt:s=>{let c=i.map(u=>u.onAttempt?.(s)).filter(u=>!!u);return ()=>{for(let u of c)u();}}}}function dt(t){return (e={})=>new Promise((r,n)=>{let o=e.target??t.target??(typeof window<"u"?window:null),i=e.eventType??t.eventType??"keydown";if(!o){n(new Error("[useShortcut] Cannot record shortcut without a target."));return}let s,c=l=>{let f=l;at(f)||(f.preventDefault(),o.removeEventListener(i,c),s&&clearTimeout(s),r(j(f)));};o.addEventListener(i,c);let u=e.timeoutMs;u&&u>0&&(s=setTimeout(()=>{o.removeEventListener(i,c),n(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var Rt=new Set(["ctrl","shift","alt","cmd","mod"]);function mt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};m(t.debug,"Builder created with options:",t);function r(o){return new Proxy({},{get(i,s){if(s==="__debug")return o.options.debug;if(Rt.has(s)){let c=E(),u=s==="mod"?c===D.MAC?"cmd":"ctrl":s,l={...o,modifiers:{...o.modifiers,[u]:true}};return m(o.options.debug,`Chain: +${s} \u2192`,l.modifiers),r(l)}if(s==="in")return c=>{let u=[...C(o.scopes),...C(c)],l={...o,scopes:u};return r(l)};if(s==="setScopes")return c=>{e.activeScopes=new Set(C(c));};if(s==="enableScope")return c=>{c?.trim()&&e.activeScopes.add(c.trim());};if(s==="disableScope")return c=>{c?.trim()&&e.activeScopes.delete(c.trim());};if(s==="getScopes")return ()=>[...e.activeScopes];if(s==="isScopeActive")return c=>e.activeScopes.has(c);if(s==="onDebug")return c=>(e.debugListeners.add(c),()=>e.debugListeners.delete(c));if(s==="record")return dt(e.options);if(s==="key")return c=>{let u=tt(o.modifiers,c),l={...o,modifiers:{},boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .key("${c}")`),r(l)};if(s==="bind")return c=>{let u=Array.isArray(c)?c:[c],l={...o,modifiers:{},boundCombos:u,steps:u};return m(o.options.debug,`Chain: .bind("${u.join('", "')}")`),r(l)};if(s==="then")return c=>{let u=String(c).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let l={...o,boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .then("${u}")`),r(l)};if(s==="except")return c=>{let u={...o,except:c};return m(o.options.debug,"Chain: .except()",c),r(u)};if(s==="on")return (c,u)=>z(o,c,u,e);if(s==="handle")return c=>{let{handler:u,...l}=c;return z(o,u,l,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Lt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function vt(t,e){let r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return false;for(let o of r){let i=t[o],s=e[o];if(!s||!Lt(i.keys,s.keys)||i.handler!==s.handler||i.options!==s.options)return false}return true}function Pt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function Kt(t,e){let r=e.toLowerCase().split("+").map(i=>i.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let n=r.pop(),o=t;for(let i of r){if(i==="ctrl"||i==="control"){o=o.ctrl;continue}if(i==="shift"){o=o.shift;continue}if(i==="alt"||i==="option"){o=o.alt;continue}if(i==="cmd"||i==="command"||i==="meta"){o=o.cmd;continue}if(i==="mod"){o=o.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${i}" in step "${e}"`)}return o.key(n)}function ht(t,e){let r={};for(let n of Object.keys(e)){let o=e[n],i=Pt(o.keys);if(i.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(n)}" has no key steps`);let s=Kt(t,i[0]);for(let c of i.slice(1))s=s.then(c);r[n]=s.on(o.handler,o.options);}return r}function St(t={}){let e=useRef(t);e.current=t;let{builder:r,registry:n}=useMemo(()=>mt(e.current),[]);return useEffect(()=>{if(n.options=e.current,e.current.activeScopes!==void 0){let o=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];n.activeScopes=new Set(o.map(i=>i.trim()).filter(Boolean));}},[n,t]),useEffect(()=>()=>{n.listeners.clear(),n.firstStepIndex.clear(),n.activeSequenceCombos.clear(),n.listener&&n.listenerTarget&&(n.listenerTarget.removeEventListener(n.listenerEventType,n.listener),n.listener=null,n.listenerTarget=null);},[n]),r}function It(t,e={}){let r=St(e),n=useRef(t);vt(n.current,t)||(n.current=t);let o=n.current,i=useRef({});return useEffect(()=>{let s=ht(r,o),c=i.current;for(let u of Object.keys(c))delete c[u];return Object.assign(c,s),()=>{for(let u of Object.values(s))u.unbind();for(let u of Object.keys(c))delete c[u];}},[r,o]),i.current}function gt(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function Ht(){let t=useRef(null);return t.current||(t.current=gt()),t.current}export{F as ModifierAliases,L as ModifierDisplayOrder,U as ModifierDisplaySymbols,p as ModifierKey,D as Platform,N as SpecialKeyMap,gt as createShortcutGroup,E as detectPlatform,q as formatShortcut,Et as matchesAnyShortcut,v as matchesShortcut,k as parseShortcut,yt as parseShortcuts,ht as registerShortcutMap,St as useShortcut,Ht as useShortcutGroup,It as useShortcutMap};
package/dist/parser.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { P as ParsedShortcut, e as ModifierState } from './types-Cg7uyv1m.js';
1
+ import { P as ParsedShortcut, e as ModifierState } from './types-yQWKtHDh.js';
2
2
 
3
3
  /**
4
4
  * Parse a shortcut string into its components
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { p as ShortcutGroup, r as ShortcutMap, h as ShortcutBuilder, t as ShortcutMapResult, U as UseShortcutOptions } from './types-yQWKtHDh.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-yQWKtHDh.js';
3
3
 
4
4
  /**
5
5
  * Registers an object-based shortcut map in one call and returns per-action handles.
package/dist/react.js CHANGED
@@ -1 +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&&lt(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;
1
+ 'use strict';var react=require('react');var h={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=h,y=null;function A(){if(y)return y;if(typeof navigator>"u")return y=h.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=h.MAC,y):e.includes("linux")||e.includes("android")?(y=h.LINUX,y):(e.includes("win"),y=h.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},$={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},j={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:"]"},z={[h.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[h.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[h.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},D={[h.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[h.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[h.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function W(t){return t===" "?"space":t.toLowerCase()}function L(t){let e=A(),n=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(n.length===0)throw new Error(`Invalid shortcut: "${t}"`);let o={meta:false,ctrl:false,alt:false,shift:false},i=n.pop();for(let c of n){let u=$[c];u?c==="mod"?e===R.MAC?o.meta=true:o.ctrl=true:o[u]=true:i=c+i;}let s=j[i]||i;return {modifiers:o,key:s.length===1?s.toLowerCase():s,original:t}}function yt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function B(t,e){let r=yt(t),n=W(t.key),o=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,i=n===W(e.key);return o&&i}function bt(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 m(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 Et(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Y(t,e,r){return t.map((n,o)=>{let i=e[o];if(!i)return {index:o,expected:n,status:"pending",tokens:[]};let s=new Set(F(n)),c=new Set(t.slice(o+1).flatMap(F)),u=F(i).map((f,b,k)=>({token:f,kind:bt(f)||b<k.length-1?"modifier":"key",status:Et(f,s,c)}));if(i===n)return {index:o,expected:n,actual:i,status:r||o<e.length-1?"match":"partial",tokens:u};let l=t.slice(o+1).includes(i)?"wrong-order":"mismatch";return {index:o,expected:n,actual:i,status:l,tokens:u}})}function J(t,e,r,n){if(n)return "matched";let o=t.slice(0,r);return o.length>0&&o.every(i=>i.status==="match"||i.status==="partial")?r<e?"partial":"mismatch":o.some(i=>i.status==="wrong-order")?"wrong-order":"mismatch"}function Q(t,e){if(!v(t))return;let r=G(t),n=[];if(r?.includeCode&&e.input.code&&n.push(`code=${e.input.code}`),r?.includeLocation&&n.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&n.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&n.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...n);return}for(let o of e.attempts)console.log("[useShortcut]",o.status.toUpperCase(),`${e.input.combo} -> ${o.combo}`,...n);}var V={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Tt={...V,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},At={...V,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function Z(t,e){let r=A(),n=L(t),o=z[r],i=D[r],s=[];for(let l of i)n.modifiers[l]&&s.push(o[l]);let c=Mt(n.key,r);s.push(c);let u=r===h.MAC?"":"+";return s.join(u)}function Mt(t,e){return (e===h.MAC?Tt:At)[t]||t.toUpperCase()}function Ct(t){let e=A(),r=D[e],n=[];for(let o of r)o===p.CTRL&&t.ctrl&&n.push("ctrl"),o===p.ALT&&t.alt&&n.push("alt"),o===p.SHIFT&&t.shift&&n.push("shift"),o===p.META&&t.cmd&&n.push("cmd");return n}function tt(t,e){return [...Ct(t),e].join("+")}function et(t){return t.map(e=>Z(e)).join(" then ")}function M(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(M),n=e.map(M),o=r.join(" "),i=n.join(" ");return o===i?"exact":ot(r,n)||ot(n,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var 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 wt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function xt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let n=rt(e),o=X(e,n),i=[],s=new Set,c=t.debugListeners.size>0||v(r.debug)||[...t.listeners.values()].some(f=>f.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(n);if(u)for(let f of u)s.add(f);for(let f of t.activeSequenceCombos)s.add(f);if(c)for(let f of t.listeners.keys())s.add(f);for(let f of s){let b=t.listeners.get(f);if(!b)continue;let k=wt(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)){m(r.debug,"Skipped due to except condition:",f);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(n),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:o,steps:_};i.push(O);for(let S of a.attemptCallbacks)S(T,e,O);if(!T)continue;m(r.debug,"MATCHED:",f),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(m(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(f):t.activeSequenceCombos.delete(f);}let l={input:o,attempts:i};if(t.debugListeners.size>0)for(let f of t.debugListeners)f(l);Q(r.debug,l);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",n=o=>xt(t,o);e.addEventListener(r,n),t.listener=n,t.listenerTarget=e,t.listenerEventType=r,m(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,m(t.options.debug,"Listener detached"));}function ft(t,e,r,n){let{options:o,except:i}=t,s=t.steps;if(s.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let c=s.map(d=>L(d)),u=c.map(M).join(" "),l=et(s),f=o.debug??false,b=i??r.except;for(let[d,S]of n.listeners.entries())for(let w of S){if(d===u)continue;let x=nt(c,w.parsedSteps);x&&st(n,{combo:u,existingCombo:d,reason:x});}let k=!r.disabled&&!o.disabled,a=r.delay??o.delay??0,I=r.sequenceTimeout??o.sequenceTimeout??800,E=new Set(C(t.scopes??r.scopes)),H=c.map(M),T=new Set;m(f,"Registering:",u,"\u2192",l,{parsedSteps:c,except:!!b,scopes:[...E]});let g={id:n.nextId++,userHandler:e,isEnabled:k,combo:u,display:l,description:r.description,attemptCallbacks:T,parsedSteps:c,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},_=n.listeners.get(u);if(_)_.push(g);else {n.listeners.set(u,[g]);let d=M(c[0]),S=n.firstStepIndex.get(d);S?S.add(u):n.firstStepIndex.set(d,new Set([u]));}return pt(n),{unbind:()=>{let d=n.listeners.get(u);if(!d)return;let S=d.filter(w=>w.id!==g.id);if(S.length===0){n.listeners.delete(u),n.activeSequenceCombos.delete(u);let w=M(c[0]),x=n.firstStepIndex.get(w);x&&(x.delete(u),x.size===0&&n.firstStepIndex.delete(w)),m(f,"Unregistered:",u);}else n.listeners.set(u,S);n.listeners.size===0&&lt(n);},display:l,combo:u,trigger:()=>e(new KeyboardEvent(n.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function U(t,e,r={},n){let o=t.boundCombos?.filter(s=>s.trim());if(!o||o.length<=1)return ft(t,e,r,n);let i=o.map(s=>{let c={...t,steps:[s]};return ft(c,e,r,n)});return {unbind:()=>{for(let s of i)s.unbind();},display:i.map(s=>s.display).join(" / "),combo:i.map(s=>s.combo).join(" | "),trigger:()=>{for(let s of i)s.trigger();},get isEnabled(){return i.some(s=>s.isEnabled)},enable:()=>{for(let s of i)s.enable();},disable:()=>{for(let s of i)s.disable();},onAttempt:s=>{let c=i.map(u=>u.onAttempt?.(s)).filter(u=>!!u);return ()=>{for(let u of c)u();}}}}function dt(t){return (e={})=>new Promise((r,n)=>{let o=e.target??t.target??(typeof window<"u"?window:null),i=e.eventType??t.eventType??"keydown";if(!o){n(new Error("[useShortcut] Cannot record shortcut without a target."));return}let s,c=l=>{let f=l;at(f)||(f.preventDefault(),o.removeEventListener(i,c),s&&clearTimeout(s),r(N(f)));};o.addEventListener(i,c);let u=e.timeoutMs;u&&u>0&&(s=setTimeout(()=>{o.removeEventListener(i,c),n(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var kt=new Set(["ctrl","shift","alt","cmd","mod"]);function mt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};m(t.debug,"Builder created with options:",t);function r(o){return new Proxy({},{get(i,s){if(s==="__debug")return o.options.debug;if(kt.has(s)){let c=A(),u=s==="mod"?c===R.MAC?"cmd":"ctrl":s,l={...o,modifiers:{...o.modifiers,[u]:true}};return m(o.options.debug,`Chain: +${s} \u2192`,l.modifiers),r(l)}if(s==="in")return c=>{let u=[...C(o.scopes),...C(c)],l={...o,scopes:u};return r(l)};if(s==="setScopes")return c=>{e.activeScopes=new Set(C(c));};if(s==="enableScope")return c=>{c?.trim()&&e.activeScopes.add(c.trim());};if(s==="disableScope")return c=>{c?.trim()&&e.activeScopes.delete(c.trim());};if(s==="getScopes")return ()=>[...e.activeScopes];if(s==="isScopeActive")return c=>e.activeScopes.has(c);if(s==="onDebug")return c=>(e.debugListeners.add(c),()=>e.debugListeners.delete(c));if(s==="record")return dt(e.options);if(s==="key")return c=>{let u=tt(o.modifiers,c),l={...o,modifiers:{},boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .key("${c}")`),r(l)};if(s==="bind")return c=>{let u=Array.isArray(c)?c:[c],l={...o,modifiers:{},boundCombos:u,steps:u};return m(o.options.debug,`Chain: .bind("${u.join('", "')}")`),r(l)};if(s==="then")return c=>{let u=String(c).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let l={...o,boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .then("${u}")`),r(l)};if(s==="except")return c=>{let u={...o,except:c};return m(o.options.debug,"Chain: .except()",c),r(u)};if(s==="on")return (c,u)=>U(o,c,u,e);if(s==="handle")return c=>{let{handler:u,...l}=c;return U(o,u,l,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Rt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function Dt(t,e){let r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return false;for(let o of r){let i=t[o],s=e[o];if(!s||!Rt(i.keys,s.keys)||i.handler!==s.handler||i.options!==s.options)return false}return true}function Lt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function vt(t,e){let r=e.toLowerCase().split("+").map(i=>i.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let n=r.pop(),o=t;for(let i of r){if(i==="ctrl"||i==="control"){o=o.ctrl;continue}if(i==="shift"){o=o.shift;continue}if(i==="alt"||i==="option"){o=o.alt;continue}if(i==="cmd"||i==="command"||i==="meta"){o=o.cmd;continue}if(i==="mod"){o=o.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${i}" in step "${e}"`)}return o.key(n)}function ht(t,e){let r={};for(let n of Object.keys(e)){let o=e[n],i=Lt(o.keys);if(i.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(n)}" has no key steps`);let s=vt(t,i[0]);for(let c of i.slice(1))s=s.then(c);r[n]=s.on(o.handler,o.options);}return r}function St(t={}){let e=react.useRef(t);e.current=t;let{builder:r,registry:n}=react.useMemo(()=>mt(e.current),[]);return react.useEffect(()=>{if(n.options=e.current,e.current.activeScopes!==void 0){let o=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];n.activeScopes=new Set(o.map(i=>i.trim()).filter(Boolean));}},[n,t]),react.useEffect(()=>()=>{n.listeners.clear(),n.firstStepIndex.clear(),n.activeSequenceCombos.clear(),n.listener&&n.listenerTarget&&(n.listenerTarget.removeEventListener(n.listenerEventType,n.listener),n.listener=null,n.listenerTarget=null);},[n]),r}function Pt(t,e={}){let r=St(e),n=react.useRef(t);Dt(n.current,t)||(n.current=t);let o=n.current,i=react.useRef({});return react.useEffect(()=>{let s=ht(r,o),c=i.current;for(let u of Object.keys(c))delete c[u];return Object.assign(c,s),()=>{for(let u of Object.values(s))u.unbind();for(let u of Object.keys(c))delete c[u];}},[r,o]),i.current}function gt(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function Kt(){let t=react.useRef(null);return t.current||(t.current=gt()),t.current}exports.createShortcutGroup=gt;exports.registerShortcutMap=ht;exports.useShortcut=St;exports.useShortcutGroup=Kt;exports.useShortcutMap=Pt;
package/dist/react.mjs CHANGED
@@ -1 +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&&lt(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
+ import {useRef,useMemo,useEffect}from'react';var h={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},R=h,y=null;function A(){if(y)return y;if(typeof navigator>"u")return y=h.WINDOWS,y;let e=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return e.includes("mac")||e.includes("iphone")||e.includes("ipad")||e.includes("ipod")?(y=h.MAC,y):e.includes("linux")||e.includes("android")?(y=h.LINUX,y):(e.includes("win"),y=h.WINDOWS,y)}var p={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},$={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},j={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:"]"},z={[h.MAC]:{[p.META]:"\u2318",[p.CTRL]:"\u2303",[p.ALT]:"\u2325",[p.SHIFT]:"\u21E7"},[h.WINDOWS]:{[p.META]:"Ctrl",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"},[h.LINUX]:{[p.META]:"Super",[p.CTRL]:"Ctrl",[p.ALT]:"Alt",[p.SHIFT]:"Shift"}},D={[h.MAC]:[p.CTRL,p.ALT,p.SHIFT,p.META],[h.WINDOWS]:[p.META,p.ALT,p.SHIFT,p.CTRL],[h.LINUX]:[p.META,p.ALT,p.SHIFT,p.CTRL]};function W(t){return t===" "?"space":t.toLowerCase()}function L(t){let e=A(),n=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(n.length===0)throw new Error(`Invalid shortcut: "${t}"`);let o={meta:false,ctrl:false,alt:false,shift:false},i=n.pop();for(let c of n){let u=$[c];u?c==="mod"?e===R.MAC?o.meta=true:o.ctrl=true:o[u]=true:i=c+i;}let s=j[i]||i;return {modifiers:o,key:s.length===1?s.toLowerCase():s,original:t}}function yt(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function B(t,e){let r=yt(t),n=W(t.key),o=r.meta===e.modifiers.meta&&r.ctrl===e.modifiers.ctrl&&r.alt===e.modifiers.alt&&r.shift===e.modifiers.shift,i=n===W(e.key);return o&&i}function bt(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 m(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 Et(t,e,r){return e.has(t)?"match":r.has(t)?"wrong-order":"mismatch"}function Y(t,e,r){return t.map((n,o)=>{let i=e[o];if(!i)return {index:o,expected:n,status:"pending",tokens:[]};let s=new Set(F(n)),c=new Set(t.slice(o+1).flatMap(F)),u=F(i).map((f,b,k)=>({token:f,kind:bt(f)||b<k.length-1?"modifier":"key",status:Et(f,s,c)}));if(i===n)return {index:o,expected:n,actual:i,status:r||o<e.length-1?"match":"partial",tokens:u};let l=t.slice(o+1).includes(i)?"wrong-order":"mismatch";return {index:o,expected:n,actual:i,status:l,tokens:u}})}function J(t,e,r,n){if(n)return "matched";let o=t.slice(0,r);return o.length>0&&o.every(i=>i.status==="match"||i.status==="partial")?r<e?"partial":"mismatch":o.some(i=>i.status==="wrong-order")?"wrong-order":"mismatch"}function Q(t,e){if(!v(t))return;let r=G(t),n=[];if(r?.includeCode&&e.input.code&&n.push(`code=${e.input.code}`),r?.includeLocation&&n.push(`location=${String(e.input.location)}`),r?.includeKeyCode&&(typeof e.input.keyCode=="number"&&n.push(`keyCode=${String(e.input.keyCode)}`),typeof e.input.which=="number"&&n.push(`which=${String(e.input.which)}`)),e.attempts.length===0){console.log("[useShortcut]","key",e.input.combo,...n);return}for(let o of e.attempts)console.log("[useShortcut]",o.status.toUpperCase(),`${e.input.combo} -> ${o.combo}`,...n);}var V={ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Home:"Home",End:"End",PageUp:"PgUp",PageDown:"PgDn"},Tt={...V,Enter:"\u21A9",Tab:"\u21E5",Escape:"\u238B",Backspace:"\u232B",Delete:"\u2326"," ":"\u2423"},At={...V,Enter:"Enter",Tab:"Tab",Escape:"Esc",Backspace:"Backspace",Delete:"Del"," ":"Space"};function Z(t,e){let r=A(),n=L(t),o=z[r],i=D[r],s=[];for(let l of i)n.modifiers[l]&&s.push(o[l]);let c=Mt(n.key,r);s.push(c);let u=r===h.MAC?"":"+";return s.join(u)}function Mt(t,e){return (e===h.MAC?Tt:At)[t]||t.toUpperCase()}function Ct(t){let e=A(),r=D[e],n=[];for(let o of r)o===p.CTRL&&t.ctrl&&n.push("ctrl"),o===p.ALT&&t.alt&&n.push("alt"),o===p.SHIFT&&t.shift&&n.push("shift"),o===p.META&&t.cmd&&n.push("cmd");return n}function tt(t,e){return [...Ct(t),e].join("+")}function et(t){return t.map(e=>Z(e)).join(" then ")}function M(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(M),n=e.map(M),o=r.join(" "),i=n.join(" ");return o===i?"exact":ot(r,n)||ot(n,r)?"sequence-prefix":null}function st(t,e){let r=t.options.conflictWarnings??true;if(t.options.onConflict){t.options.onConflict(e);return}r&&console.warn(`[useShortcut] Conflict detected (${e.reason}) between "${e.combo}" and "${e.existingCombo}"`);}var 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 wt(t){return t.length<=1?t:[...t].sort((e,r)=>r.priority!==e.priority?r.priority-e.priority:e.id-r.id)}function xt(t,e){let r=t.options;if(r.disabled||r.eventFilter&&!r.eventFilter(e))return;let n=rt(e),o=X(e,n),i=[],s=new Set,c=t.debugListeners.size>0||v(r.debug)||[...t.listeners.values()].some(f=>f.some(b=>b.attemptCallbacks.size>0)),u=t.firstStepIndex.get(n);if(u)for(let f of u)s.add(f);for(let f of t.activeSequenceCombos)s.add(f);if(c)for(let f of t.listeners.keys())s.add(f);for(let f of s){let b=t.listeners.get(f);if(!b)continue;let k=wt(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)){m(r.debug,"Skipped due to except condition:",f);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(n),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:o,steps:_};i.push(O);for(let S of a.attemptCallbacks)S(T,e,O);if(!T)continue;m(r.debug,"MATCHED:",f),a.preventDefault&&e.preventDefault(),a.stopPropagation&&e.stopPropagation();let d=()=>a.userHandler(e);if(a.delay>0?(m(r.debug,"Delaying execution by",a.delay,"ms"),setTimeout(d,a.delay)):d(),a.stopOnMatch)break}b.some(a=>a.progress>0)?t.activeSequenceCombos.add(f):t.activeSequenceCombos.delete(f);}let l={input:o,attempts:i};if(t.debugListeners.size>0)for(let f of t.debugListeners)f(l);Q(r.debug,l);}function pt(t){if(t.listener)return;let e=t.options.target??(typeof window<"u"?window:null);if(!e)return;let r=t.options.eventType??"keydown",n=o=>xt(t,o);e.addEventListener(r,n),t.listener=n,t.listenerTarget=e,t.listenerEventType=r,m(t.options.debug,"Listener attached");}function lt(t){!t.listener||!t.listenerTarget||(t.listenerTarget.removeEventListener(t.listenerEventType,t.listener),t.listener=null,t.listenerTarget=null,m(t.options.debug,"Listener detached"));}function ft(t,e,r,n){let{options:o,except:i}=t,s=t.steps;if(s.length===0)throw new Error("[useShortcut] No key specified. Use .key() to set the action key.");let c=s.map(d=>L(d)),u=c.map(M).join(" "),l=et(s),f=o.debug??false,b=i??r.except;for(let[d,S]of n.listeners.entries())for(let w of S){if(d===u)continue;let x=nt(c,w.parsedSteps);x&&st(n,{combo:u,existingCombo:d,reason:x});}let k=!r.disabled&&!o.disabled,a=r.delay??o.delay??0,I=r.sequenceTimeout??o.sequenceTimeout??800,E=new Set(C(t.scopes??r.scopes)),H=c.map(M),T=new Set;m(f,"Registering:",u,"\u2192",l,{parsedSteps:c,except:!!b,scopes:[...E]});let g={id:n.nextId++,userHandler:e,isEnabled:k,combo:u,display:l,description:r.description,attemptCallbacks:T,parsedSteps:c,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},_=n.listeners.get(u);if(_)_.push(g);else {n.listeners.set(u,[g]);let d=M(c[0]),S=n.firstStepIndex.get(d);S?S.add(u):n.firstStepIndex.set(d,new Set([u]));}return pt(n),{unbind:()=>{let d=n.listeners.get(u);if(!d)return;let S=d.filter(w=>w.id!==g.id);if(S.length===0){n.listeners.delete(u),n.activeSequenceCombos.delete(u);let w=M(c[0]),x=n.firstStepIndex.get(w);x&&(x.delete(u),x.size===0&&n.firstStepIndex.delete(w)),m(f,"Unregistered:",u);}else n.listeners.set(u,S);n.listeners.size===0&&lt(n);},display:l,combo:u,trigger:()=>e(new KeyboardEvent(n.options.eventType??"keydown")),get isEnabled(){return g.isEnabled},enable:()=>{g.isEnabled=true;},disable:()=>{g.isEnabled=false;},onAttempt:d=>(g.attemptCallbacks.add(d),()=>g.attemptCallbacks.delete(d))}}function U(t,e,r={},n){let o=t.boundCombos?.filter(s=>s.trim());if(!o||o.length<=1)return ft(t,e,r,n);let i=o.map(s=>{let c={...t,steps:[s]};return ft(c,e,r,n)});return {unbind:()=>{for(let s of i)s.unbind();},display:i.map(s=>s.display).join(" / "),combo:i.map(s=>s.combo).join(" | "),trigger:()=>{for(let s of i)s.trigger();},get isEnabled(){return i.some(s=>s.isEnabled)},enable:()=>{for(let s of i)s.enable();},disable:()=>{for(let s of i)s.disable();},onAttempt:s=>{let c=i.map(u=>u.onAttempt?.(s)).filter(u=>!!u);return ()=>{for(let u of c)u();}}}}function dt(t){return (e={})=>new Promise((r,n)=>{let o=e.target??t.target??(typeof window<"u"?window:null),i=e.eventType??t.eventType??"keydown";if(!o){n(new Error("[useShortcut] Cannot record shortcut without a target."));return}let s,c=l=>{let f=l;at(f)||(f.preventDefault(),o.removeEventListener(i,c),s&&clearTimeout(s),r(N(f)));};o.addEventListener(i,c);let u=e.timeoutMs;u&&u>0&&(s=setTimeout(()=>{o.removeEventListener(i,c),n(new Error(`[useShortcut] Recording timed out after ${u}ms.`));},u));})}var kt=new Set(["ctrl","shift","alt","cmd","mod"]);function mt(t={}){let e={listeners:new Map,firstStepIndex:new Map,activeSequenceCombos:new Set,options:t,activeScopes:new Set(C(t.activeScopes)),nextId:1,debugListeners:new Set,listener:null,listenerTarget:null,listenerEventType:t.eventType??"keydown"};m(t.debug,"Builder created with options:",t);function r(o){return new Proxy({},{get(i,s){if(s==="__debug")return o.options.debug;if(kt.has(s)){let c=A(),u=s==="mod"?c===R.MAC?"cmd":"ctrl":s,l={...o,modifiers:{...o.modifiers,[u]:true}};return m(o.options.debug,`Chain: +${s} \u2192`,l.modifiers),r(l)}if(s==="in")return c=>{let u=[...C(o.scopes),...C(c)],l={...o,scopes:u};return r(l)};if(s==="setScopes")return c=>{e.activeScopes=new Set(C(c));};if(s==="enableScope")return c=>{c?.trim()&&e.activeScopes.add(c.trim());};if(s==="disableScope")return c=>{c?.trim()&&e.activeScopes.delete(c.trim());};if(s==="getScopes")return ()=>[...e.activeScopes];if(s==="isScopeActive")return c=>e.activeScopes.has(c);if(s==="onDebug")return c=>(e.debugListeners.add(c),()=>e.debugListeners.delete(c));if(s==="record")return dt(e.options);if(s==="key")return c=>{let u=tt(o.modifiers,c),l={...o,modifiers:{},boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .key("${c}")`),r(l)};if(s==="bind")return c=>{let u=Array.isArray(c)?c:[c],l={...o,modifiers:{},boundCombos:u,steps:u};return m(o.options.debug,`Chain: .bind("${u.join('", "')}")`),r(l)};if(s==="then")return c=>{let u=String(c).trim().toLowerCase();if(!u)throw new Error("[useShortcut] .then() requires a non-empty key or shortcut step.");let l={...o,boundCombos:void 0,steps:[...o.steps,u]};return m(o.options.debug,`Chain: .then("${u}")`),r(l)};if(s==="except")return c=>{let u={...o,except:c};return m(o.options.debug,"Chain: .except()",c),r(u)};if(s==="on")return (c,u)=>U(o,c,u,e);if(s==="handle")return c=>{let{handler:u,...l}=c;return U(o,u,l,e)}}})}return {builder:r({modifiers:{},steps:[],options:t}),registry:e}}function Rt(t,e){if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return false;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r])return false;return true}return !Array.isArray(t)&&!Array.isArray(e)?t===e:false}function Dt(t,e){let r=Object.keys(t),n=Object.keys(e);if(r.length!==n.length)return false;for(let o of r){let i=t[o],s=e[o];if(!s||!Rt(i.keys,s.keys)||i.handler!==s.handler||i.options!==s.options)return false}return true}function Lt(t){if(Array.isArray(t))return t.map(r=>r.trim()).filter(Boolean);let e=t.trim();return e?e.includes(" then ")?e.split(/\s+then\s+/i).map(r=>r.trim()).filter(Boolean):e.includes(" ")&&!e.includes("+")?e.split(/\s+/).map(r=>r.trim()).filter(Boolean):[e]:[]}function vt(t,e){let r=e.toLowerCase().split("+").map(i=>i.trim()).filter(Boolean);if(r.length===0)throw new Error("[useShortcutMap] Invalid step: empty shortcut step");let n=r.pop(),o=t;for(let i of r){if(i==="ctrl"||i==="control"){o=o.ctrl;continue}if(i==="shift"){o=o.shift;continue}if(i==="alt"||i==="option"){o=o.alt;continue}if(i==="cmd"||i==="command"||i==="meta"){o=o.cmd;continue}if(i==="mod"){o=o.mod;continue}throw new Error(`[useShortcutMap] Unsupported modifier token "${i}" in step "${e}"`)}return o.key(n)}function ht(t,e){let r={};for(let n of Object.keys(e)){let o=e[n],i=Lt(o.keys);if(i.length===0)throw new Error(`[useShortcutMap] Shortcut "${String(n)}" has no key steps`);let s=vt(t,i[0]);for(let c of i.slice(1))s=s.then(c);r[n]=s.on(o.handler,o.options);}return r}function St(t={}){let e=useRef(t);e.current=t;let{builder:r,registry:n}=useMemo(()=>mt(e.current),[]);return useEffect(()=>{if(n.options=e.current,e.current.activeScopes!==void 0){let o=Array.isArray(e.current.activeScopes)?e.current.activeScopes:[e.current.activeScopes];n.activeScopes=new Set(o.map(i=>i.trim()).filter(Boolean));}},[n,t]),useEffect(()=>()=>{n.listeners.clear(),n.firstStepIndex.clear(),n.activeSequenceCombos.clear(),n.listener&&n.listenerTarget&&(n.listenerTarget.removeEventListener(n.listenerEventType,n.listener),n.listener=null,n.listenerTarget=null);},[n]),r}function Pt(t,e={}){let r=St(e),n=useRef(t);Dt(n.current,t)||(n.current=t);let o=n.current,i=useRef({});return useEffect(()=>{let s=ht(r,o),c=i.current;for(let u of Object.keys(c))delete c[u];return Object.assign(c,s),()=>{for(let u of Object.values(s))u.unbind();for(let u of Object.keys(c))delete c[u];}},[r,o]),i.current}function gt(){let t=[];return {add:(...e)=>{t.push(...e);},addMany:e=>{if(Array.isArray(e)){t.push(...e);return}t.push(...Object.values(e));},unbindAll:()=>{for(let e of t)e.unbind();t.length=0;},clear:()=>{t.length=0;},getResults:()=>[...t]}}function Kt(){let t=useRef(null);return t.current||(t.current=gt()),t.current}export{gt as createShortcutGroup,ht as registerShortcutMap,St as useShortcut,Kt as useShortcutGroup,Pt as useShortcutMap};
@@ -205,6 +205,8 @@ type KeyChain<Key extends string> = {
205
205
  handle: (options: HandlerOptions & {
206
206
  handler: ShortcutHandler;
207
207
  }) => ShortcutResult;
208
+ /** Bind a combo string mid-chain */
209
+ bind: (combo: string | string[]) => KeyChain<any>;
208
210
  /** Add exception conditions before attaching handler */
209
211
  except: (condition: ExceptPreset | ExceptPreset[] | ExceptPredicate) => KeyChainWithExcept<Key>;
210
212
  /** Add required named scopes */
@@ -217,6 +219,8 @@ type KeyChain<Key extends string> = {
217
219
  */
218
220
  type KeyChainWithExcept<Key extends string> = {
219
221
  on: (handler: ShortcutHandler, options?: Omit<HandlerOptions, "except">) => ShortcutResult;
222
+ /** Bind a combo string mid-chain */
223
+ bind: (combo: string | string[]) => KeyChainWithExcept<Key>;
220
224
  in: (scopes: ShortcutScope) => KeyChainWithExcept<Key>;
221
225
  then: <K extends ActionKey | string>(key: K) => KeyChainWithExcept<`${Key} ${K}`>;
222
226
  };
@@ -246,6 +250,8 @@ type ShortcutBuilder = ModifierChain<EmptyModifiers> & {
246
250
  cmd: true;
247
251
  }>;
248
252
  key: <K extends ActionKey>(key: K) => KeyChain<K>;
253
+ /** Bind a pre-defined combo string */
254
+ bind: (combo: string | string[]) => KeyChain<any>;
249
255
  /** Set required scopes for upcoming chain calls */
250
256
  in: (scopes: ShortcutScope) => ShortcutBuilder;
251
257
  /** Update active scopes at runtime */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcostoeten/use-shortcut",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Tiny, chainable React keyboard shortcuts with sequences, scopes, and typed debug hooks.",
5
5
  "author": "Remco Stoeten",
6
6
  "license": "MIT",