@remcostoeten/use-shortcut 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -40
- package/dist/constants.d.ts +36 -0
- package/dist/constants.js +1 -0
- package/dist/constants.mjs +1 -0
- package/dist/formatter.d.ts +30 -0
- package/dist/formatter.js +1 -0
- package/dist/formatter.mjs +1 -0
- package/dist/index.d.ts +5 -468
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/parser.d.ts +47 -0
- package/dist/parser.js +1 -0
- package/dist/parser.mjs +1 -0
- package/dist/react.d.ts +79 -0
- package/dist/react.js +1 -0
- package/dist/react.mjs +1 -0
- package/dist/types-yQWKtHDh.d.ts +320 -0
- package/package.json +46 -5
- package/dist/cli/index.mjs +0 -469
package/dist/index.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&<(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&<(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
ADDED
|
@@ -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;
|
package/dist/parser.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a={MAC:"mac",WINDOWS:"windows",LINUX:"linux"},d=a,o=null;function m(){if(o)return o;if(typeof navigator>"u")return o=a.WINDOWS,o;let r=(navigator.userAgentData?.platform?.toLowerCase()??navigator.platform??navigator.userAgent??"").toLowerCase();return r.includes("mac")||r.includes("iphone")||r.includes("ipad")||r.includes("ipod")?(o=a.MAC,o):r.includes("linux")||r.includes("android")?(o=a.LINUX,o):(r.includes("win"),o=a.WINDOWS,o)}var e={META:"meta",CTRL:"ctrl",ALT:"alt",SHIFT:"shift"},T={command:e.META,cmd:e.META,"\u2318":e.META,meta:e.META,win:e.META,windows:e.META,super:e.META,mod:e.META,control:e.CTRL,ctrl:e.CTRL,"\u2303":e.CTRL,ctl:e.CTRL,alt:e.ALT,option:e.ALT,opt:e.ALT,"\u2325":e.ALT,shift:e.SHIFT,"\u21E7":e.SHIFT,shft:e.SHIFT},u={up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",pageup:"PageUp",pagedown:"PageDown",enter:"Enter",return:"Enter",space:" ",spacebar:" ",tab:"Tab",backspace:"Backspace",delete:"Delete",del:"Delete",escape:"Escape",esc:"Escape",f1:"F1",f2:"F2",f3:"F3",f4:"F4",f5:"F5",f6:"F6",f7:"F7",f8:"F8",f9:"F9",f10:"F10",f11:"F11",f12:"F12",plus:"+",minus:"-",comma:",",period:".",slash:"/",backslash:"\\",bracket:"[",closebracket:"]"};function y(t){return t===" "?"space":t.toLowerCase()}function A(t){let r=m(),f=t.toLowerCase().trim().split(/[\s+-]+/).filter(Boolean);if(f.length===0)throw new Error(`Invalid shortcut: "${t}"`);let n={meta:false,ctrl:false,alt:false,shift:false},s=f.pop();for(let l of f){let p=T[l];p?l==="mod"?r===d.MAC?n.meta=true:n.ctrl=true:n[p]=true:s=l+s;}let c=u[s]||s;return {modifiers:n,key:c.length===1?c.toLowerCase():c,original:t}}function C(t){return (Array.isArray(t)?t:[t]).map(A)}function M(t){return {meta:t.metaKey,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey}}function S(t,r){let i=M(t),f=y(t.key),n=i.meta===r.modifiers.meta&&i.ctrl===r.modifiers.ctrl&&i.alt===r.modifiers.alt&&i.shift===r.modifiers.shift,s=f===y(r.key);return n&&s}function F(t,r){return r.some(i=>S(t,i))}export{M as getModifiersFromEvent,F as matchesAnyShortcut,S as matchesShortcut,A as parseShortcut,C as parseShortcuts};
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { p as ShortcutGroup, r as ShortcutMap, h as ShortcutBuilder, t as ShortcutMapResult, U as UseShortcutOptions } from './types-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&<(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&<(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};
|