react-keyhub
Version:
A lightweight, scalable keyboard shortcut manager for React applications with TypeScript support
3 lines (2 loc) • 31.9 kB
JavaScript
import e,{createContext as t,useState as n,useEffect as o,useContext as r,useRef as s,useCallback as c,useMemo as h}from"react";var i,a,u;!function(e){e.GLOBAL="global",e.LOCAL="local"}(i||(i={})),function(e){e.ENABLED="enabled",e.DISABLED="disabled"}(a||(a={})),function(e){e.REGULAR="regular",e.SEQUENCE="sequence"}(u||(u={}));const l=e=>{if(!e)return"";if(e.includes(" "))return e.split(" ").map((e=>l(e))).join(" ");const t=e.toLowerCase().split("+"),n=t.filter((e=>["ctrl","control","alt","option","shift","meta","cmd","command","win","windows"].includes(e))).map((e=>"control"===e?"ctrl":"option"===e?"alt":["cmd","command","win","windows"].includes(e)?"meta":e)),o=t.filter((e=>!["ctrl","control","alt","option","shift","meta","cmd","command","win","windows"].includes(e))).map((e=>"space"===e?"space":"escape"===e?"esc":"arrowup"===e?"up":"arrowdown"===e?"down":"arrowleft"===e?"left":"arrowright"===e?"right":e));return[...Array.from(new Set(n)).sort(),...o].filter(Boolean).join("+")},p=e=>{const t=[];e.ctrlKey&&t.push("ctrl"),e.altKey&&t.push("alt"),e.shiftKey&&t.push("shift"),e.metaKey&&t.push("meta");const n=e.key.toLowerCase();let o=n;return" "===n&&(o="space"),"escape"===n&&(o="esc"),"arrowup"===n&&(o="up"),"arrowdown"===n&&(o="down"),"arrowleft"===n&&(o="left"),"arrowright"===n&&(o="right"),["control","ctrl","alt","shift","meta","command","cmd"].includes(n)?t.sort().join("+"):[...t.sort(),o].join("+")},d=(e,t)=>{let n=null;return function(...o){null!==n&&clearTimeout(n),n=setTimeout((()=>{n=null,e(...o)}),t)}},b=()=>Math.random().toString(36).substring(2,9),y={preventDefault:!0,stopPropagation:!0,target:"undefined"!=typeof document?document:void 0,debounceTime:0,sequenceTimeout:1e3,ignoreInputFields:!0,ignoreModifierOnlyEvents:!0};class m{constructor(e,t={}){this.activeContext=null,this.sequenceBuffer=[],this.sequenceTimer=null,this.paused=!1,this._shortcutIdToKeyCombo=null,this.shortcuts=e,this.subscriptions=new Map,this.shortcutIdToSubscriptions=new Map,this.options=Object.assign(Object.assign({},y),t),this.isListening=!1;const n=e=>{if(this.paused)return;const t=e;if(this.options.ignoreInputFields&&(t.target instanceof HTMLInputElement||t.target instanceof HTMLTextAreaElement||t.target instanceof HTMLElement&&t.target.isContentEditable))return;const n=p(t);this.options.ignoreModifierOnlyEvents&&["ctrl","alt","shift","meta","ctrl+alt","ctrl+shift","ctrl+meta","alt+shift","alt+meta","shift+meta"].includes(n)||(this.handleSequence(n,t),this.emit(n,t))};this.handleKeyDown=this.options.debounceTime>0?d(n,this.options.debounceTime):n,this.startListening()}startListening(){!this.isListening&&this.options.target&&(this.options.target.addEventListener("keydown",this.handleKeyDown),this.isListening=!0)}stopListening(){this.isListening&&this.options.target&&(this.options.target.removeEventListener("keydown",this.handleKeyDown),this.isListening=!1)}pause(){this.paused=!0}resume(){this.paused=!1}setContext(e){this.activeContext=e}getContext(){return this.activeContext}handleSequence(e,t){this.sequenceBuffer.push(e),this.sequenceTimer&&clearTimeout(this.sequenceTimer),this.sequenceTimer=setTimeout((()=>{this.sequenceBuffer=[]}),this.options.sequenceTimeout);const n=this.sequenceBuffer.join(" "),o=Object.entries(this.shortcuts).filter((([e,t])=>"sequence"===t.type&&t.sequence===n));o.length>0&&(o.forEach((([e,n])=>{if("disabled"===n.status)return;if(n.context&&this.activeContext!==n.context)return;this.options.preventDefault&&t.preventDefault(),this.options.stopPropagation&&t.stopPropagation();const o=this.shortcutIdToSubscriptions.get(e)||[];if(o.length>0){const e=o[0];try{e.callback(t)}catch(e){}}else if(n.action)try{n.action(t)}catch(e){}})),this.sequenceBuffer=[],this.sequenceTimer&&(clearTimeout(this.sequenceTimer),this.sequenceTimer=null))}on(e,t){const n=this.shortcuts[e];if(!n)return"test"===process.env.NODE_ENV&&console.warn(`Shortcut "${e}" is not defined`),"";const o="sequence"===n.type?e:n.keyCombo,r=l(o),s=b(),c={id:s,callback:t,priority:n.priority,shortcutId:e};this.subscriptions.has(r)||this.subscriptions.set(r,[]);const h=this.subscriptions.get(r);h.push(c),h.sort(((e,t)=>t.priority-e.priority)),this.shortcutIdToSubscriptions.has(e)||this.shortcutIdToSubscriptions.set(e,[]);const i=this.shortcutIdToSubscriptions.get(e);return i.push(c),i.sort(((e,t)=>t.priority-e.priority)),this._shortcutIdToKeyCombo=this._shortcutIdToKeyCombo||new Map,this._shortcutIdToKeyCombo.set(e,r),s}logSubscriptions(){}off(e){for(const[t,n]of this.subscriptions.entries()){const o=n.findIndex((t=>t.id===e));if(-1!==o){const e=[...n.slice(0,o),...n.slice(o+1)];this.subscriptions.set(t,e)}}for(const[t,n]of this.shortcutIdToSubscriptions.entries()){const o=n.findIndex((t=>t.id===e));if(-1!==o){const e=[...n.slice(0,o),...n.slice(o+1)];this.shortcutIdToSubscriptions.set(t,e);break}}}emit(e,t){const n=l(e),o=[...this.subscriptions.get(n)||[]];if(o.length>0){const e=o[0];this.options.preventDefault&&t.preventDefault(),this.options.stopPropagation&&t.stopPropagation();try{e.callback(t)}catch(e){}return}const r=Object.entries(this.shortcuts).filter((([e,t])=>{if("sequence"===t.type)return!1;return l(t.keyCombo)===n}));if(0!==r.length)for(const[e,n]of r){if("disabled"===n.status)continue;if(n.context&&this.activeContext!==n.context)continue;const o=this.shortcutIdToSubscriptions.get(e)||[];if(o.length>0){const e=o[0];this.options.preventDefault&&t.preventDefault(),this.options.stopPropagation&&t.stopPropagation();try{return void e.callback(t)}catch(e){}}if(n.action){this.options.preventDefault&&t.preventDefault(),this.options.stopPropagation&&t.stopPropagation();try{return void n.action(t)}catch(e){}}}}updateShortcut(e,t){if(!this.shortcuts[e])return void("test"===process.env.NODE_ENV&&console.warn(`Shortcut "${e}" is not defined`));const n=this.shortcuts[e];n.type,this.shortcuts[e]=Object.assign(Object.assign({},n),t)}registerShortcut(e,t){this.shortcuts[e]=t}unregisterShortcut(e){this.shortcuts[e]&&delete this.shortcuts[e]}enableShortcut(e){this.updateShortcut(e,{status:a.ENABLED})}disableShortcut(e){this.updateShortcut(e,{status:a.DISABLED})}getShortcuts(){return Object.assign({},this.shortcuts)}getShortcutsByGroup(e){return Object.entries(this.shortcuts).filter((([t,n])=>n.group===e)).reduce(((e,[t,n])=>(e[t]=n,e)),{})}getShortcutGroups(){const e=new Set;return Object.values(this.shortcuts).forEach((t=>{t.group&&e.add(t.group)})),Array.from(e)}destroy(){this.stopListening(),this.subscriptions.clear(),this.sequenceTimer&&(clearTimeout(this.sequenceTimer),this.sequenceTimer=null)}}const k={save:{keyCombo:"ctrl+s",name:"Save",description:"Save the current document",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"File",type:u.REGULAR},saveAs:{keyCombo:"ctrl+shift+s",name:"Save As",description:"Save the current document with a new name",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"File",type:u.REGULAR},print:{keyCombo:"ctrl+p",name:"Print",description:"Print the current document",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"File",type:u.REGULAR},newWindow:{keyCombo:"ctrl+shift+n",name:"New Window",description:"Open a new window or tab",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"File",type:u.REGULAR},find:{keyCombo:"ctrl+f",name:"Find",description:"Find text in the current document",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},replace:{keyCombo:"ctrl+h",name:"Replace",description:"Replace text in the current document",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},undo:{keyCombo:"ctrl+z",name:"Undo",description:"Undo the last action",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},redo:{keyCombo:"ctrl+y",name:"Redo",description:"Redo the last undone action",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},cut:{keyCombo:"ctrl+x",name:"Cut",description:"Cut the selected content to the clipboard",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},copy:{keyCombo:"ctrl+c",name:"Copy",description:"Copy the selected content to the clipboard",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},paste:{keyCombo:"ctrl+v",name:"Paste",description:"Paste content from the clipboard",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},selectAll:{keyCombo:"ctrl+a",name:"Select All",description:"Select all content in the current document",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Edit",type:u.REGULAR},goToLine:{keyCombo:"ctrl+g",name:"Go to Line",description:"Navigate to a specific line",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Navigation",type:u.REGULAR},goToFile:{keyCombo:"ctrl+p",name:"Go to File",description:"Navigate to a specific file",scope:i.GLOBAL,priority:90,status:a.ENABLED,group:"Navigation",context:"editor",type:u.REGULAR},help:{keyCombo:"f1",name:"Help",description:"Show help information",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Help",type:u.REGULAR},showShortcuts:{keyCombo:"ctrl+/",name:"Show Shortcuts",description:"Show all available keyboard shortcuts",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Help",type:u.REGULAR},gitCommands:{sequence:"g c",name:"Git Commands",description:"Show git commands menu",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Git",type:u.SEQUENCE},gitStatus:{sequence:"g s",name:"Git Status",description:"Show git status",scope:i.GLOBAL,priority:100,status:a.ENABLED,group:"Git",type:u.SEQUENCE},vimUp:{keyCombo:"k",name:"Move Up",description:"Move cursor up",scope:i.LOCAL,priority:100,status:a.ENABLED,group:"Vim Navigation",context:"vim",type:u.REGULAR},vimDown:{keyCombo:"j",name:"Move Down",description:"Move cursor down",scope:i.LOCAL,priority:100,status:a.ENABLED,group:"Vim Navigation",context:"vim",type:u.REGULAR},vimLeft:{keyCombo:"h",name:"Move Left",description:"Move cursor left",scope:i.LOCAL,priority:100,status:a.ENABLED,group:"Vim Navigation",context:"vim",type:u.REGULAR},vimRight:{keyCombo:"l",name:"Move Right",description:"Move cursor right",scope:i.LOCAL,priority:100,status:a.ENABLED,group:"Vim Navigation",context:"vim",type:u.REGULAR}},g=t(null),f=({shortcuts:t=k,options:r={},children:s})=>{const[c]=n((()=>new m(t,r))),h={eventBus:c,shortcuts:t};return o((()=>()=>{c.destroy()}),[c]),e.createElement(g.Provider,{value:h},s)},x=()=>{const e=r(g);if(!e)throw new Error("useKeyHub must be used within a KeyHubProvider");return e.eventBus},E=()=>{const e=r(g);if(!e)throw new Error("useKeyHubShortcuts must be used within a KeyHubProvider");return e.shortcuts},v=()=>x().getShortcuts(),w=e=>x().getShortcutsByGroup(e),L=()=>x().getShortcutGroups(),A=(e,t)=>{const n=x();o((()=>{t?n.enableShortcut(e):n.disableShortcut(e)}),[n,e,t])},C=(e,t)=>{const n=x();o((()=>{n.updateShortcut(e,t)}),[n,e,t])},N=(e,t)=>{const n=x();o((()=>(n.registerShortcut(e,t),()=>{n.unregisterShortcut(e)})),[n,e,t])},S=e=>{const t=x();o((()=>{t.setContext(e)}),[t,e])},B=e=>{const t=x();o((()=>{e?t.pause():t.resume()}),[t,e])};function G(e,t){const n=x(),r=E(),h=s(!1),i=s(t);o((()=>{i.current=t}),[t]);const a=c((t=>{var o;const s=t;if(!n||n.paused)return;const c=r[e];if(!c)return;if("disabled"===c.status)return;const h=null===(o=n.getContext)||void 0===o?void 0:o.call(n);if(c.context&&h!==c.context)return;const a=p(s),u=l(a);let d=!1;if("regular"===c.type){d=l(c.keyCombo)===u}else if("sequence"===c.type)return;if(!d)return;const b=n.options;(null==b?void 0:b.preventDefault)&&s.preventDefault(),(null==b?void 0:b.stopPropagation)&&s.stopPropagation();try{i.current(s)}catch(e){}}),[n,e,r]);return o((()=>{const t=null==n?void 0:n.options,o=null==t?void 0:t.target;if(n&&o)try{return h.current=e in r,h.current?(o.addEventListener("keydown",a),()=>{o.removeEventListener("keydown",a)}):void("test"===process.env.NODE_ENV&&console.warn(`Shortcut "${String(e)}" is not registered. Available shortcuts: ${Object.keys(r).join(", ")}`))}catch(e){return void(h.current=!1)}else h.current=!1}),[n,e,r,a]),h.current}function R(){try{return E()}catch(e){return"test"===process.env.NODE_ENV&&console.warn("Unable to get registered shortcuts:",e),{}}}const D=e=>e.split("+").map((e=>{const t=e.charAt(0).toUpperCase()+e.slice(1);switch(e.toLowerCase()){case"ctrl":return"Ctrl";case"alt":return"Alt";case"shift":return"Shift";case"meta":return navigator.platform.includes("Mac")?"⌘":"Win";case"esc":return"Esc";case"space":return"Space";default:return t}})).join(" + "),O=({children:t})=>e.createElement("kbd",{className:"keyhub-shortcut-sheet-key"},t),T=({shortcut:t})=>{if("sequence"===t.type){const n=t.sequence.split(" ");return e.createElement("div",{className:"keyhub-shortcut-sheet-key-combo"},n.map(((t,n)=>e.createElement(e.Fragment,{key:n},n>0&&e.createElement("span",{className:"keyhub-shortcut-sheet-key-then"},"then"),e.createElement(O,null,D(t))))))}const n=t.keyCombo.split("+");return e.createElement("div",{className:"keyhub-shortcut-sheet-key-combo"},n.map(((t,n)=>e.createElement(e.Fragment,{key:n},n>0&&e.createElement("span",{className:"keyhub-shortcut-sheet-key-plus"},"+"),e.createElement(O,null,D(t))))))},U=({isOpen:t,onClose:r,filter:s={},className:c="",theme:i="light",layout:a="modal"})=>{const u=v(),l=L(),[p,d]=n(s.search||""),[b,y]=n(s.scope||"all"),[m,k]=n(s.group||"all"),[g,f]=n(s.context||"all"),[x,E]=n(l[0]||"all"),[w,A]=n("light");o((()=>{if("auto"===i){const e=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches;A(e?"dark":"light");const t=window.matchMedia("(prefers-color-scheme: dark)"),n=e=>{A(e.matches?"dark":"light")};return t.addEventListener("change",n),()=>t.removeEventListener("change",n)}}),[i]);const C="auto"===i?w:i,N=h((()=>{const e=new Set;return Object.values(u).forEach((t=>{t.context&&e.add(t.context)})),Array.from(e)}),[u]),S=h((()=>Object.entries(u).filter((([e,t])=>{if("all"!==b&&t.scope!==b)return!1;if("all"!==m&&t.group!==m)return!1;if("all"!==g&&t.context!==g)return!1;if("all"!==x&&t.group!==x)return!1;const n=p.toLowerCase();return""===p||t.name.toLowerCase().includes(n)||t.description.toLowerCase().includes(n)||"regular"===t.type&&t.keyCombo.toLowerCase().includes(n)||"sequence"===t.type&&t.sequence.toLowerCase().includes(n)}))),[u,p,b,m,g,x]),B=h((()=>{const e={};return S.forEach((t=>{const n=t[1].group||"Ungrouped";e[n]||(e[n]=[]),e[n].push(t)})),e}),[S]);return t?e.createElement("div",{className:`keyhub-shortcut-sheet keyhub-theme-${C} keyhub-layout-${a} ${c}`},e.createElement("div",{className:"keyhub-shortcut-sheet-overlay",onClick:r}),e.createElement("div",{className:"keyhub-shortcut-sheet-content"},e.createElement("div",{className:"keyhub-shortcut-sheet-header"},e.createElement("h2",null,"Keyboard Shortcuts"),e.createElement("button",{className:"keyhub-shortcut-sheet-close",onClick:r,"aria-label":"Close"},"×")),e.createElement("div",{className:"keyhub-shortcut-sheet-filters"},e.createElement("div",{className:"keyhub-shortcut-sheet-search-container"},e.createElement("input",{type:"text",placeholder:"Search shortcuts...",value:p,onChange:e=>d(e.target.value),className:"keyhub-shortcut-sheet-search"}),p&&e.createElement("button",{className:"keyhub-shortcut-sheet-search-clear",onClick:()=>d(""),"aria-label":"Clear search"},"×")),e.createElement("div",{className:"keyhub-shortcut-sheet-filter-controls"},e.createElement("select",{value:b,onChange:e=>y(e.target.value),className:"keyhub-shortcut-sheet-scope","aria-label":"Filter by scope"},e.createElement("option",{value:"all"},"All Scopes"),e.createElement("option",{value:"global"},"Global"),e.createElement("option",{value:"local"},"Local")),N.length>0&&e.createElement("select",{value:g,onChange:e=>f(e.target.value),className:"keyhub-shortcut-sheet-context","aria-label":"Filter by context"},e.createElement("option",{value:"all"},"All Contexts"),N.map((t=>e.createElement("option",{key:t,value:t},t)))))),l.length>0&&e.createElement("div",{className:"keyhub-shortcut-sheet-tabs"},e.createElement("button",{className:"keyhub-shortcut-sheet-tab "+("all"===x?"active":""),onClick:()=>E("all")},"All"),l.map((t=>e.createElement("button",{key:t,className:"keyhub-shortcut-sheet-tab "+(x===t?"active":""),onClick:()=>E(t)},t)))),e.createElement("div",{className:"keyhub-shortcut-sheet-list"},0===Object.keys(B).length?e.createElement("div",{className:"keyhub-shortcut-sheet-empty"},"No shortcuts found."):e.createElement("div",{className:"keyhub-shortcut-sheet-groups"},Object.entries(B).map((([t,n])=>e.createElement("div",{key:t,className:"keyhub-shortcut-sheet-group"},e.createElement("h3",{className:"keyhub-shortcut-sheet-group-title"},t),e.createElement("div",{className:"keyhub-shortcut-sheet-cards"},n.map((([t,n])=>e.createElement("div",{key:t,className:`keyhub-shortcut-sheet-card keyhub-shortcut-${n.status}`},e.createElement("div",{className:"keyhub-shortcut-sheet-card-header"},e.createElement("h4",{className:"keyhub-shortcut-sheet-card-title"},n.name),n.context&&e.createElement("span",{className:"keyhub-shortcut-sheet-card-context"},n.context)),e.createElement("div",{className:"keyhub-shortcut-sheet-card-description"},n.description),e.createElement("div",{className:"keyhub-shortcut-sheet-card-footer"},e.createElement(T,{shortcut:n}),e.createElement("span",{className:"keyhub-shortcut-sheet-card-scope"},n.scope))))))))))))):null},j="\n.keyhub-shortcut-sheet {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n z-index: 9999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n}\n\n.keyhub-shortcut-sheet-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background-color: rgba(0, 0, 0, 0.6);\n backdrop-filter: blur(4px);\n transition: opacity 0.2s ease;\n}\n\n.keyhub-shortcut-sheet-content {\n position: relative;\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 90%;\n max-width: 1000px;\n max-height: 100vh;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n animation: keyhub-sheet-appear 0.2s ease;\n transition: transform 0.2s ease;\n}\n\n@keyframes keyhub-sheet-appear {\n from {\n opacity: 0;\n transform: scale(0.95);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n/* Light theme */\n.keyhub-theme-light .keyhub-shortcut-sheet-content {\n background-color: white;\n color: #333;\n}\n\n/* Dark theme */\n.keyhub-theme-dark .keyhub-shortcut-sheet-content {\n background-color: #1e1e1e;\n color: #eee;\n}\n\n/* Modal layout */\n.keyhub-layout-modal .keyhub-shortcut-sheet-content {\n width: 90%;\n max-width: 1000px;\n}\n\n/* Sidebar layout */\n.keyhub-layout-sidebar .keyhub-shortcut-sheet-content {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n width: 450px;\n max-width: 90%;\n border-radius: 0;\n border-top-left-radius: 12px;\n border-bottom-left-radius: 12px;\n animation: keyhub-sheet-slide-in 0.3s ease;\n}\n\n@keyframes keyhub-sheet-slide-in {\n from {\n transform: translateX(100%);\n }\n to {\n transform: translateX(0);\n }\n}\n\n.keyhub-shortcut-sheet-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-header {\n border-color: #eee;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-header {\n border-color: #333;\n}\n\n.keyhub-shortcut-sheet-header h2 {\n margin: 0;\n font-size: 22px;\n font-weight: 600;\n}\n\n.keyhub-shortcut-sheet-close {\n background: none;\n border: none;\n font-size: 22px;\n cursor: pointer;\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: all 0.2s;\n padding:0px;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-close {\n color: #666;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-close:hover {\n background-color: #f0f0f0;\n color: #333;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-close {\n color: #aaa;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-close:hover {\n background-color: #333;\n color: #fff;\n}\n\n.keyhub-shortcut-sheet-filters {\n display: flex;\n flex-direction: column;\n padding: 16px 24px;\n gap: 12px;\n border-bottom: 1px solid;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-filters {\n border-color: #eee;\n background-color: #fafafa;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-filters {\n border-color: #333;\n background-color: #252525;\n}\n\n.keyhub-shortcut-sheet-search-container {\n position: relative;\n flex: 1;\n}\n\n.keyhub-shortcut-sheet-search {\n width: 100%;\n padding: 12px 16px;\n border-radius: 8px;\n font-size: 15px;\n border: 1px solid;\n transition: all 0.2s;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-search {\n border-color: #ddd;\n background-color: white;\n color: #333;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-search:focus {\n border-color: #0066cc;\n box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-search {\n border-color: #444;\n background-color: #2a2a2a;\n color: #eee;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-search:focus {\n border-color: #4da3ff;\n box-shadow: 0 0 0 2px rgba(77, 163, 255, 0.2);\n}\n\n.keyhub-shortcut-sheet-search-clear {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n font-size: 18px;\n cursor: pointer;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n transition: all 0.2s;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-search-clear {\n color: #999;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-search-clear:hover {\n background-color: #f0f0f0;\n color: #666;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-search-clear {\n color: #777;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-search-clear:hover {\n background-color: #444;\n color: #aaa;\n}\n\n.keyhub-shortcut-sheet-filter-controls {\n display: flex;\n gap: 12px;\n}\n\n.keyhub-shortcut-sheet-scope,\n.keyhub-shortcut-sheet-context {\n padding: 10px 14px;\n border-radius: 8px;\n font-size: 14px;\n border: 1px solid;\n transition: all 0.2s;\n cursor: pointer;\n appearance: none;\n background-image: url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n padding-right: 32px;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-scope,\n.keyhub-theme-light .keyhub-shortcut-sheet-context {\n border-color: #ddd;\n background-color: white;\n color: #333;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-scope:focus,\n.keyhub-theme-light .keyhub-shortcut-sheet-context:focus {\n border-color: #0066cc;\n box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-scope,\n.keyhub-theme-dark .keyhub-shortcut-sheet-context {\n border-color: #444;\n background-color: #2a2a2a;\n color: #eee;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-scope:focus,\n.keyhub-theme-dark .keyhub-shortcut-sheet-context:focus {\n border-color: #4da3ff;\n box-shadow: 0 0 0 2px rgba(77, 163, 255, 0.2);\n}\n\n.keyhub-shortcut-sheet-tabs {\n display: flex;\n overflow-x: auto;\n padding: 0 24px;\n border-bottom: 1px solid;\n scrollbar-width: thin;\n background-color: transparent;\n min-height: 45px;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-tabs {\n border-color: #eee;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-tabs {\n border-color: #333;\n}\n\n.keyhub-shortcut-sheet-tab {\n padding: 14px 18px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n white-space: nowrap;\n transition: all 0.2s;\n border-radius:0px;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-tab {\n color: #666;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-tab:hover:not(.active) {\n color: #333;\n background-color: #f5f5f5;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-tab {\n color: #aaa;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-tab:hover:not(.active) {\n color: #eee;\n background-color: #333;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-tab.active {\n color: #0066cc;\n border-color: #0066cc;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-tab.active {\n color: #4da3ff;\n border-color: #4da3ff;\n}\n\n.keyhub-shortcut-sheet-list {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n}\n\n.keyhub-shortcut-sheet-empty {\n padding: 40px 24px;\n text-align: center;\n color: #999;\n font-size: 16px;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 12px;\n}\n\n.keyhub-shortcut-sheet-empty:before {\n content: \"🔍\";\n font-size: 32px;\n opacity: 0.7;\n}\n\n.keyhub-shortcut-sheet-groups {\n display: flex;\n flex-direction: column;\n gap: 28px;\n}\n\n.keyhub-shortcut-sheet-group-title {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 600;\n padding-bottom: 8px;\n border-bottom: 1px solid;\n display: flex;\n align-items: center;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-group-title {\n color: #444;\n border-color: #eee;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-group-title {\n color: #ddd;\n border-color: #333;\n}\n\n.keyhub-shortcut-sheet-cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 16px;\n}\n\n.keyhub-shortcut-sheet-card {\n border-radius: 10px;\n padding: 18px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n border: 1px solid;\n transition: all 0.2s;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-card {\n border-color: #eee;\n background-color: #f9f9f9;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-card:hover {\n border-color: #ddd;\n background-color: #f5f5f5;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);\n transform: translateY(-2px);\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-card {\n border-color: #333;\n background-color: #252525;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-card:hover {\n border-color: #444;\n background-color: #2a2a2a;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);\n transform: translateY(-2px);\n}\n\n.keyhub-shortcut-sheet-card-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 8px;\n}\n\n.keyhub-shortcut-sheet-card-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n line-height: 1.3;\n}\n\n.keyhub-shortcut-sheet-card-context {\n font-size: 12px;\n padding: 3px 8px;\n border-radius: 12px;\n white-space: nowrap;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-card-context {\n background-color: #e0e0e0;\n color: #555;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-card-context {\n background-color: #444;\n color: #ccc;\n}\n\n.keyhub-shortcut-sheet-card-description {\n font-size: 14px;\n line-height: 1.5;\n flex-grow: 1;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-card-description {\n color: #666;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-card-description {\n color: #aaa;\n}\n\n.keyhub-shortcut-sheet-card-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 8px;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.keyhub-shortcut-sheet-card-scope {\n font-size: 12px;\n padding: 3px 8px;\n border-radius: 12px;\n text-transform: capitalize;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-card-scope {\n background-color: #e0e0e0;\n color: #555;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-card-scope {\n background-color: #444;\n color: #ccc;\n}\n\n.keyhub-shortcut-sheet-key-combo {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.keyhub-shortcut-sheet-key {\n font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;\n padding: 4px 8px;\n border-radius: 6px;\n font-size: 13px;\n min-width: 24px;\n text-align: center;\n border: 1px solid;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-key {\n background-color: white;\n border-color: #ddd;\n color: #333;\n box-shadow: 0 2px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-key {\n background-color: #333;\n border-color: #444;\n color: #eee;\n box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2), 0 1px 0 rgba(0, 0, 0, 0.2);\n}\n\n.keyhub-shortcut-sheet-key-plus,\n.keyhub-shortcut-sheet-key-then {\n font-size: 13px;\n font-weight: 500;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-key-plus,\n.keyhub-theme-light .keyhub-shortcut-sheet-key-then {\n color: #999;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-key-plus,\n.keyhub-theme-dark .keyhub-shortcut-sheet-key-then {\n color: #777;\n}\n\n.keyhub-shortcut-disabled {\n opacity: 0.6;\n}\n\n/* Scrollbar styling */\n.keyhub-shortcut-sheet-list::-webkit-scrollbar,\n.keyhub-shortcut-sheet-tabs::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-list::-webkit-scrollbar-track,\n.keyhub-theme-light .keyhub-shortcut-sheet-tabs::-webkit-scrollbar-track {\n background: #f1f1f1;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-list::-webkit-scrollbar-thumb,\n.keyhub-theme-light .keyhub-shortcut-sheet-tabs::-webkit-scrollbar-thumb {\n background: #ccc;\n border-radius: 4px;\n}\n\n.keyhub-theme-light .keyhub-shortcut-sheet-list::-webkit-scrollbar-thumb:hover,\n.keyhub-theme-light .keyhub-shortcut-sheet-tabs::-webkit-scrollbar-thumb:hover {\n background: #aaa;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-list::-webkit-scrollbar-track,\n.keyhub-theme-dark .keyhub-shortcut-sheet-tabs::-webkit-scrollbar-track {\n background: #2a2a2a;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-list::-webkit-scrollbar-thumb,\n.keyhub-theme-dark .keyhub-shortcut-sheet-tabs::-webkit-scrollbar-thumb {\n background: #444;\n border-radius: 4px;\n}\n\n.keyhub-theme-dark .keyhub-shortcut-sheet-list::-webkit-scrollbar-thumb:hover,\n.keyhub-theme-dark .keyhub-shortcut-sheet-tabs::-webkit-scrollbar-thumb:hover {\n background: #555;\n}\n\n/* Responsive adjustments */\n@media (max-width: 768px) {\n .keyhub-shortcut-sheet-cards {\n grid-template-columns: 1fr;\n }\n \n .keyhub-shortcut-sheet-filter-controls {\n flex-direction: column;\n }\n \n .keyhub-layout-sidebar .keyhub-shortcut-sheet-content {\n width: 100%;\n max-width: 100%;\n border-radius: 0;\n }\n \n .keyhub-shortcut-sheet-header {\n padding: 16px 20px;\n }\n \n .keyhub-shortcut-sheet-list {\n padding: 16px 20px;\n }\n \n .keyhub-shortcut-sheet-card {\n padding: 16px;\n }\n}\n\n@media (max-width: 480px) {\n .keyhub-shortcut-sheet-header h2 {\n font-size: 18px;\n }\n \n .keyhub-shortcut-sheet-card-footer {\n flex-direction: column;\n align-items: flex-start;\n }\n}\n";export{m as EventBus,f as KeyHubProvider,i as ShortcutScope,U as ShortcutSheet,j as ShortcutSheetStyles,a as ShortcutStatus,u as ShortcutType,d as debounce,k as defaultShortcuts,p as eventToKeyCombo,b as generateId,R as getRegisteredShortcuts,l as normalizeKeyCombo,G as useKey,x as useKeyHub,G as useKeyboardShortcut,G as useShortcut,S as useShortcutContext,L as useShortcutGroups,B as useShortcutPause,N as useShortcutRegister,v as useShortcutSheet,A as useShortcutStatus,C as useShortcutUpdate,w as useShortcutsByGroup};
//# sourceMappingURL=index.esm.js.map