@remcostoeten/use-shortcut 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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};
@@ -0,0 +1,47 @@
1
+ import { P as ParsedShortcut, e as ModifierState } from './types-yQWKtHDh.js';
2
+
3
+ /**
4
+ * Parse a shortcut string into its components
5
+ *
6
+ * @param shortcut - Shortcut string (e.g., "cmd+s", "ctrl+shift+p")
7
+ * @returns Parsed shortcut with modifiers, key, and original string
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const parsed = parseShortcut("cmd+s")
12
+ * // { modifiers: { meta: true, ... }, key: "s", original: "cmd+s" }
13
+ * ```
14
+ */
15
+ declare function parseShortcut(shortcut: string): ParsedShortcut;
16
+ /**
17
+ * Parse multiple shortcut strings
18
+ *
19
+ * @param shortcuts - Single shortcut or array of shortcuts
20
+ * @returns Array of parsed shortcuts
21
+ */
22
+ declare function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[];
23
+ /**
24
+ * Extract modifier state from a keyboard event
25
+ *
26
+ * @param event - The keyboard event
27
+ * @returns Object with meta, ctrl, alt, shift boolean flags
28
+ */
29
+ declare function getModifiersFromEvent(event: KeyboardEvent): ModifierState;
30
+ /**
31
+ * Check if a keyboard event matches a parsed shortcut
32
+ *
33
+ * @param event - The keyboard event to check
34
+ * @param parsed - The parsed shortcut to match against
35
+ * @returns `true` if the event matches the shortcut
36
+ */
37
+ declare function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean;
38
+ /**
39
+ * Check if a keyboard event matches any of the parsed shortcuts
40
+ *
41
+ * @param event - The keyboard event to check
42
+ * @param parsedShortcuts - Array of parsed shortcuts to match against
43
+ * @returns `true` if the event matches any shortcut
44
+ */
45
+ declare function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean;
46
+
47
+ export { getModifiersFromEvent, matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts };
package/dist/parser.js ADDED
@@ -0,0 +1 @@
1
+ 'use strict';var a={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},d=a,o=null;function m(){if(o)return o;if(typeof navigator>"u")return o=a.WINDOWS,o;let r=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return r.includes("mac")||r.includes("iphone")||r.includes("ipad")||r.includes("ipod")?(o=a.MAC,o):r.includes("linux")||r.includes("android")?(o=a.LINUX,o):(r.includes("win"),o=a.WINDOWS,o)}var e={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},T={command:e.META,cmd:e.META,"\u2318":e.META,meta:e.META,win:e.META,windows:e.META,super:e.META,mod:e.META,control:e.CTRL,ctrl:e.CTRL,"\u2303":e.CTRL,ctl:e.CTRL,alt:e.ALT,option:e.ALT,opt:e.ALT,"\u2325":e.ALT,shift:e.SHIFT,"\u21E7":e.SHIFT,shft:e.SHIFT},u={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"};function y(t){return t===" "?"space":t.toLowerCase()}function A(t){let r=m(),f=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(f.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=f.pop();for(let l of f){let p=T[l];p?l==="mod"?r===d.MAC?n.meta=true:n.ctrl=true:n[p]=true:s=l+s;}let c=u[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function C(t){return (Array.isArray(t)?t:[t]).map(A)}function M(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function S(t,r){let i=M(t),f=y(t.key),n=i.meta===r.modifiers.meta&&i.ctrl===r.modifiers.ctrl&&i.alt===r.modifiers.alt&&i.shift===r.modifiers.shift,s=f===y(r.key);return n&&s}function F(t,r){return r.some(i=>S(t,i))}exports.getModifiersFromEvent=M;exports.matchesAnyShortcut=F;exports.matchesShortcut=S;exports.parseShortcut=A;exports.parseShortcuts=C;
@@ -0,0 +1 @@
1
+ var a={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},d=a,o=null;function m(){if(o)return o;if(typeof navigator>"u")return o=a.WINDOWS,o;let r=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return r.includes("mac")||r.includes("iphone")||r.includes("ipad")||r.includes("ipod")?(o=a.MAC,o):r.includes("linux")||r.includes("android")?(o=a.LINUX,o):(r.includes("win"),o=a.WINDOWS,o)}var e={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},T={command:e.META,cmd:e.META,"\u2318":e.META,meta:e.META,win:e.META,windows:e.META,super:e.META,mod:e.META,control:e.CTRL,ctrl:e.CTRL,"\u2303":e.CTRL,ctl:e.CTRL,alt:e.ALT,option:e.ALT,opt:e.ALT,"\u2325":e.ALT,shift:e.SHIFT,"\u21E7":e.SHIFT,shft:e.SHIFT},u={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"};function y(t){return t===" "?"space":t.toLowerCase()}function A(t){let r=m(),f=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(f.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=f.pop();for(let l of f){let p=T[l];p?l==="mod"?r===d.MAC?n.meta=true:n.ctrl=true:n[p]=true:s=l+s;}let c=u[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function C(t){return (Array.isArray(t)?t:[t]).map(A)}function M(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function S(t,r){let i=M(t),f=y(t.key),n=i.meta===r.modifiers.meta&&i.ctrl===r.modifiers.ctrl&&i.alt===r.modifiers.alt&&i.shift===r.modifiers.shift,s=f===y(r.key);return n&&s}function F(t,r){return r.some(i=>S(t,i))}export{M as getModifiersFromEvent,F as matchesAnyShortcut,S as matchesShortcut,A as parseShortcut,C as parseShortcuts};
@@ -0,0 +1,79 @@
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
+
4
+ /**
5
+ * Registers an object-based shortcut map in one call and returns per-action handles.
6
+ *
7
+ * @param builder - Builder returned by `useShortcut()`
8
+ * @param shortcutMap - Record of action ids to key bindings, handlers, and options
9
+ * @returns A result map with one `ShortcutResult` per shortcut id
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const $ = useShortcut()
14
+ * const results = registerShortcutMap($, {
15
+ * save: { keys: "mod+s", handler: onSave },
16
+ * nav: { keys: ["g", "d"], handler: onGoDashboard },
17
+ * })
18
+ * ```
19
+ */
20
+ declare function registerShortcutMap<T extends ShortcutMap>(builder: ShortcutBuilder, shortcutMap: T): ShortcutMapResult<T>;
21
+ /**
22
+ * React hook for registering chainable keyboard shortcuts
23
+ *
24
+ * @param options - Configuration options for the hook
25
+ * @returns A chainable shortcut builder (`$`)
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const $ = useShortcut({ activeScopes: ["editor"] })
30
+ * $.mod.key("s").on((event) => {
31
+ * event.preventDefault()
32
+ * saveDocument()
33
+ * })
34
+ * ```
35
+ */
36
+ declare function useShortcut(options?: UseShortcutOptions): ShortcutBuilder;
37
+ /**
38
+ * React hook that registers a shortcut map and automatically unbinds on cleanup.
39
+ *
40
+ * @param shortcutMap - Record of action ids to key bindings, handlers, and options
41
+ * @param options - Same options as `useShortcut()`
42
+ * @returns A map of `ShortcutResult` keyed by your shortcut ids
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * const mapResults = useShortcutMap({
47
+ * save: { keys: "mod+s", handler: onSave },
48
+ * close: { keys: "escape", handler: onClose },
49
+ * })
50
+ * ```
51
+ */
52
+ declare function useShortcutMap<T extends ShortcutMap>(shortcutMap: T, options?: UseShortcutOptions): ShortcutMapResult<T>;
53
+ /**
54
+ * Creates an imperative group controller for many shortcut registrations.
55
+ *
56
+ * @returns A `ShortcutGroup` that can add and unbind multiple shortcuts together
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const group = createShortcutGroup()
61
+ * group.add($.mod.key("s").on(onSave))
62
+ * group.add($.key("escape").on(onClose))
63
+ * group.unbindAll()
64
+ * ```
65
+ */
66
+ declare function createShortcutGroup(): ShortcutGroup;
67
+ /**
68
+ * React hook that returns a stable `ShortcutGroup` instance.
69
+ *
70
+ * @returns A memoized `ShortcutGroup` tied to the component lifecycle
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const group = useShortcutGroup()
75
+ * ```
76
+ */
77
+ declare function useShortcutGroup(): ShortcutGroup;
78
+
79
+ export { ShortcutBuilder, ShortcutGroup, ShortcutMap, ShortcutMapResult, UseShortcutOptions, createShortcutGroup, registerShortcutMap, useShortcut, useShortcutGroup, useShortcutMap };
package/dist/react.js ADDED
@@ -0,0 +1 @@
1
+ 'use strict';var react=require('react');var 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 ADDED
@@ -0,0 +1 @@
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};