@buildwithdarsh/keyboardjs 1.0.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 ADDED
@@ -0,0 +1,168 @@
1
+ > This project is made with the help of Claude (1M context).
2
+
3
+ <div align="center">
4
+
5
+ <br />
6
+
7
+ <img src="https://img.shields.io/badge/keyboardjs-v1.0.0-30d158?style=for-the-badge&labelColor=000000" alt="version" />
8
+ <img src="https://img.shields.io/badge/gzip-~4KB-ff9f0a?style=for-the-badge&labelColor=000000" alt="gzip" />
9
+ <img src="https://img.shields.io/badge/dependencies-0-bf5af2?style=for-the-badge&labelColor=000000" alt="deps" />
10
+ <img src="https://img.shields.io/badge/license-MIT-ff375f?style=for-the-badge&labelColor=000000" alt="license" />
11
+
12
+ <br /><br />
13
+
14
+ # KeyBoardJS
15
+
16
+ **The tiny keyboard library.**
17
+
18
+ Hotkeys, sequences, chords, scopes, layouts, and a recorder — all in one script tag. Zero dependencies, TypeScript first.
19
+
20
+ </div>
21
+
22
+ ---
23
+
24
+ ## Why KeyBoardJS?
25
+
26
+ | | hotkeys-js | Mousetrap | **KeyBoardJS** |
27
+ |---|---|---|---|
28
+ | Hotkeys | Yes | Yes | **Yes** |
29
+ | Key sequences (`g g`) | No | Yes | **Yes** |
30
+ | Chords (hold `j+k`) | No | No | **Yes** |
31
+ | Named scopes | Partial | No | **Yes** (stackable) |
32
+ | Layout remap (Dvorak/Colemak) | No | No | **Yes** |
33
+ | Record + replay | No | No | **Yes** |
34
+ | TypeScript-first | No | No | **Yes** |
35
+ | Gzip size | ~3KB | ~3KB | **~4KB (everything)** |
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ```html
42
+ <script src="https://keyboardjs.work.withdarsh.com/dist/keyboard.umd.js"></script>
43
+ <script>
44
+ Keyboard.init();
45
+
46
+ Keyboard.on('ctrl+k', () => openPalette());
47
+ Keyboard.on(['g', 'g'], () => window.scrollTo(0, 0));
48
+ Keyboard.chord(['j', 'k'], () => exitInsertMode());
49
+ </script>
50
+ ```
51
+
52
+ Or with a bundler:
53
+
54
+ ```ts
55
+ import Kb from 'KeyBoardJS';
56
+
57
+ Kb.init();
58
+ Kb.on('shift+?', () => showHelp());
59
+ ```
60
+
61
+ ---
62
+
63
+ ## API
64
+
65
+ ### `Kb.init(opts?)`
66
+ Attach listeners to `window` (or a custom `EventTarget`).
67
+
68
+ ### `Kb.on(combo, handler, opts?)`
69
+ Register a hotkey. Accepts `'ctrl+k'` style strings.
70
+
71
+ ```js
72
+ Kb.on('ctrl+shift+p', (e) => { ... }, {
73
+ scope: 'editor', // only fire in this scope
74
+ allowInInput: false, // ignore when typing in inputs
75
+ preventDefault: true, // stop default browser behaviour
76
+ });
77
+ ```
78
+
79
+ ### `Kb.on([k1, k2, ...], handler, opts?)`
80
+ Register a **sequence**. Fires when keys are pressed in order within `timeout` ms.
81
+
82
+ ```js
83
+ Kb.on(['g', 'g'], gotoTop, { timeout: 800 });
84
+ ```
85
+
86
+ ### `Kb.chord([k1, k2, ...], handler, opts?)`
87
+ Register a **chord** — keys pressed simultaneously within `window` ms.
88
+
89
+ ```js
90
+ Kb.chord(['j', 'k'], exitInsertMode, { window: 200 });
91
+ ```
92
+
93
+ ### Scopes
94
+
95
+ ```js
96
+ Kb.scope('editor'); // activate
97
+ Kb.scope(); // read current
98
+ const pop = Kb.pushScope('modal');
99
+ pop(); // restore previous
100
+ ```
101
+
102
+ Bindings with `scope: 'global'` always fire. Others only fire in the active scope.
103
+
104
+ ### Layouts
105
+
106
+ ```js
107
+ Kb.layout('dvorak'); // or 'colemak' | 'qwerty'
108
+ ```
109
+
110
+ ### Recorder
111
+
112
+ ```js
113
+ Kb.record();
114
+ // ... user types ...
115
+ const frames = Kb.stop();
116
+ await Kb.replay(frames, 2); // 2x speed
117
+ ```
118
+
119
+ ### Misc
120
+
121
+ ```js
122
+ Kb.off('ctrl+k'); // remove
123
+ Kb.clear(); // remove all
124
+ Kb.list(); // [{ combo, scope }, ...] — good for help dialogs
125
+ Kb.format('ctrl+k'); // "Ctrl + K"
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Platform support (Mac / Windows / Linux)
131
+
132
+ Use `mod` (a.k.a. `cmdOrCtrl`) for bindings that should work the same on every OS:
133
+
134
+ ```js
135
+ Kb.on('mod+k', openPalette); // ⌘K on Mac, Ctrl+K on Windows/Linux
136
+ Kb.on('mod+shift+p', command); // ⇧⌘P / Ctrl+Shift+P
137
+ ```
138
+
139
+ Inspect the detected platform:
140
+
141
+ ```js
142
+ Kb.platform // 'mac' | 'windows' | 'linux' | 'other'
143
+ Kb.isMac // true on macOS / iPadOS / iOS
144
+ Kb.modKey // 'meta' (Mac) or 'control' (PC)
145
+ Kb.format('mod+shift+k') // "⇧⌘K" on Mac, "Ctrl + Shift + K" on PC
146
+ ```
147
+
148
+ `Kb.format()` returns Mac glyphs (`⌘ ⌥ ⇧ ⌃ ↵ ⌫ ⇥`) on Apple platforms and word labels (`Ctrl + Shift + K`) elsewhere — drop straight into tooltips and help dialogs.
149
+
150
+ ## Key names
151
+
152
+ Case-insensitive. Aliases supported:
153
+
154
+ - `mod` / `cmdOrCtrl` / `commandOrControl` — **platform-aware** (Cmd on Mac, Ctrl elsewhere)
155
+ - `ctrl` / `control`
156
+ - `cmd` / `meta` / `command` / `win`
157
+ - `alt` / `option` / `opt`
158
+ - `esc` / `escape`
159
+ - `return` / `enter`
160
+ - `space` / `spacebar`
161
+ - `up` / `down` / `left` / `right` (arrow keys)
162
+ - `pgup` / `pgdn`, `del`, `ins`
163
+
164
+ ---
165
+
166
+ ## License
167
+
168
+ MIT © Darsh Gupta
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Key normalization utilities.
3
+ * Converts between human-readable names ("ctrl+k", "enter") and the
4
+ * underlying KeyboardEvent representation.
5
+ */
6
+ declare const MODIFIER_ORDER: readonly ["control", "alt", "shift", "meta"];
7
+ type Modifier = (typeof MODIFIER_ORDER)[number];
8
+ interface ParsedCombo {
9
+ key: string;
10
+ mods: Set<Modifier>;
11
+ }
12
+
13
+ interface ShortcutOptions {
14
+ /** Scope name this binding belongs to — only fires when scope is active */
15
+ scope?: string;
16
+ /** Prevent default browser behaviour when matched (default: true) */
17
+ preventDefault?: boolean;
18
+ /** Fire even when typing in input/textarea/contenteditable (default: false) */
19
+ allowInInput?: boolean;
20
+ /** Fire on keyup instead of keydown */
21
+ onKeyup?: boolean;
22
+ }
23
+
24
+ interface SequenceOptions {
25
+ scope?: string;
26
+ /** Reset timeout in ms between keys (default: 1000) */
27
+ timeout?: number;
28
+ }
29
+
30
+ interface ChordOptions {
31
+ scope?: string;
32
+ /** Max time between first and last key press in ms (default: 250) */
33
+ window?: number;
34
+ }
35
+
36
+ type LayoutName = 'qwerty' | 'dvorak' | 'colemak';
37
+
38
+ interface RecordedKey {
39
+ combo: ParsedCombo;
40
+ label: string;
41
+ /** ms offset from start of recording */
42
+ at: number;
43
+ }
44
+
45
+ /**
46
+ * Platform detection — runs once at module load.
47
+ * Determines how `mod` maps to a physical modifier and how combos
48
+ * are pretty-printed.
49
+ */
50
+ type Platform = 'mac' | 'windows' | 'linux' | 'other';
51
+
52
+ interface InitOptions {
53
+ /** EventTarget to attach listeners to (default: window) */
54
+ target?: EventTarget;
55
+ /** Default scope name when starting (default: "global") */
56
+ scope?: string;
57
+ }
58
+ declare class Keyboard {
59
+ private _ev;
60
+ private _shortcuts;
61
+ private _sequences;
62
+ private _chords;
63
+ private _layouts;
64
+ private _scope;
65
+ private _recorder;
66
+ private _target;
67
+ private _onKeydown;
68
+ private _onKeyup;
69
+ private _onBlur;
70
+ private _started;
71
+ init(opts?: InitOptions): void;
72
+ destroy(): void;
73
+ /** Register a hotkey. Accepts a single combo string or an array for sequences. */
74
+ on(combo: string, handler: (e: KeyboardEvent) => void, opts?: ShortcutOptions): () => void;
75
+ on(sequence: string[], handler: () => void, opts?: SequenceOptions): () => void;
76
+ /** Remove all bindings for a combo. */
77
+ off(combo: string): void;
78
+ /** Register a chord — keys held simultaneously. */
79
+ chord(keys: string[], handler: () => void, opts?: ChordOptions): () => void;
80
+ /** List every registered shortcut — useful for building help dialogs. */
81
+ list(): Array<{
82
+ combo: string;
83
+ scope: string;
84
+ }>;
85
+ clear(): void;
86
+ scope(name?: string, opts?: {
87
+ exclusive?: boolean;
88
+ }): string;
89
+ /**
90
+ * Push a scope onto the stack. When `exclusive: true`, global bindings
91
+ * are suppressed while this scope is active — ideal for modals.
92
+ */
93
+ pushScope(name: string, opts?: {
94
+ exclusive?: boolean;
95
+ }): () => void;
96
+ popScope(name?: string): void;
97
+ layout(name?: LayoutName): LayoutName;
98
+ record(): void;
99
+ stop(): RecordedKey[];
100
+ /**
101
+ * Replay a recording — dispatches synthetic KeyboardEvents at the original
102
+ * inter-key timing (or faster with `speed`). Emits a 'replay' event per key.
103
+ */
104
+ replay(recording: RecordedKey[], speed?: number): Promise<void>;
105
+ onEvent(event: string, fn: (...args: unknown[]) => void): () => void;
106
+ /** Detected platform: 'mac' | 'windows' | 'linux' | 'other'. */
107
+ get platform(): Platform;
108
+ /** True on macOS / iPadOS / iOS. */
109
+ get isMac(): boolean;
110
+ /**
111
+ * The physical modifier that `mod` resolves to on this platform.
112
+ * 'meta' on Mac, 'control' on Windows/Linux.
113
+ * Use when building help UI: `Kb.modKey // "meta" | "control"`.
114
+ */
115
+ get modKey(): 'meta' | 'control';
116
+ format(combo: string): string;
117
+ private _handleKey;
118
+ private _isEditable;
119
+ }
120
+ declare const keyboard: Keyboard;
121
+
122
+ export { keyboard as default };
123
+ export type { ChordOptions, InitOptions, LayoutName, Platform, RecordedKey, SequenceOptions, ShortcutOptions };
@@ -0,0 +1,2 @@
1
+ class t{_=new Map;on(t,e){let s=this._.get(t);return s||(s=new Set,this._.set(t,s)),s.add(e),()=>this.off(t,e)}off(t,e){e?this._.get(t)?.delete(e):this._.delete(t)}emit(t,...e){const s=this._.get(t);if(s)for(const t of s)try{t(...e)}catch(t){console.error("[keyboard.js]",t)}}}const e=function(){if("undefined"==typeof navigator)return"other";const t=navigator.userAgent,e=(navigator.platform||"").toLowerCase();return/mac|iphone|ipad|ipod/i.test(e)||/mac/i.test(t)?"mac":/win/i.test(e)||/windows/i.test(t)?"windows":/linux|x11/i.test(e)||/linux/i.test(t)?"linux":"other"}(),s="mac"===e,n=s?"meta":"control",o={control:"⌃",alt:"⌥",shift:"⇧",meta:"⌘"},i={control:"Ctrl",alt:"Alt",shift:"Shift",meta:"Win"},r={enter:"↵",backspace:"⌫",delete:"⌦",tab:"⇥",escape:"esc",arrowup:"↑",arrowdown:"↓",arrowleft:"←",arrowright:"→"," ":"Space"},h={ctrl:"control",cmd:"meta",command:"meta",win:"meta",windows:"meta",super:"meta",opt:"alt",option:"alt",return:"enter",esc:"escape",space:" ",spacebar:" ",plus:"+",up:"arrowup",down:"arrowdown",left:"arrowleft",right:"arrowright",del:"delete",ins:"insert",pgup:"pageup",pgdn:"pagedown"},c=["control","alt","shift","meta"];function a(t){const e=t.trim().toLowerCase();return"mod"===e||"commandorcontrol"===e||"cmdorctrl"===e?n:h[e]??e}function l(t){const e=t.split("+").map(t=>t.trim()).filter(t=>t.length>0);if(0===e.length)return{key:"",mods:new Set};const s=new Set;let n="";for(const t of e){const e=a(t);c.includes(e)?s.add(e):n=e}return{key:n,mods:s}}function u(t){return`${c.filter(e=>t.mods.has(e)).join("+")}|${t.key}`}function f(t){const e=new Set;t.ctrlKey&&e.add("control"),t.altKey&&e.add("alt"),t.shiftKey&&e.add("shift"),t.metaKey&&e.add("meta");let s=t.key.toLowerCase();return c.includes(s)&&(s=""),{key:s,mods:e}}function d(t){const e=[],n=s?o:i;for(const s of c)t.mods.has(s)&&e.push(n[s]??s);return t.key&&e.push((s?r[t.key]:void 0)??(1===t.key.length?t.key.toUpperCase():t.key)),e.join(s?"":" + ")}const w={scope:"global",preventDefault:!0,allowInInput:!1,onKeyup:!1};class p{K=new Map;add(t,e,s){const n=l(t),o=u(n),i={combo:n,sig:o,handler:e,opts:{...w,...s,scope:s?.scope??"global"}};let r=this.K.get(o);return r||(r=new Set,this.K.set(o,r)),r.add(i),()=>this.remove(i)}remove(t){const e=this.K.get(t.sig);e&&(e.delete(t),0===e.size&&this.K.delete(t.sig))}removeByCombo(t){const e=u(l(t));this.K.delete(e)}clear(){this.K.clear()}match(t,e){const s=f(t);if(!s.key)return[];const n=u(s),o=this.K.get(n);if(!o)return[];const i=[];for(const t of o)("keyup"!==e||t.opts.onKeyup)&&("keydown"===e&&t.opts.onKeyup||i.push(t));return i}list(){const t=[];for(const e of this.K.values())for(const s of e)t.push(s);return t}}const m={scope:"global",timeout:1e3};class y{K=new Set;S=[];add(t,e,s){const n={sigs:t.map(t=>u(l(t))),handler:e,opts:{...m,...s}};return this.K.add(n),()=>{this.K.delete(n)}}clear(){this.K.clear(),this.S=[]}feed(t,e){const s=f(t);if(!s.key)return[];const n=u(s),o=Date.now(),i=this.T();this.S=this.S.filter(t=>o-t.at<=i),this.S.push({sig:n,at:o});const r=[];for(const t of this.K){if(t.opts.scope!==e&&"global"!==t.opts.scope)continue;if(this.S.length<t.sigs.length)continue;const s=this.S.slice(-t.sigs.length);o-(s[0]?.at??0)<=t.opts.timeout&&(s.every((e,s)=>e.sig===t.sigs[s])&&r.push(t))}return r.length>0&&(this.S=[]),r}T(){let t=1/0;for(const e of this.K)e.opts.timeout<t&&(t=e.opts.timeout);return t===1/0?1e3:t}}const b={scope:"global",window:250};class g{K=new Set;M=new Map;add(t,e,s){const n={keys:new Set(t.map(a)),handler:e,opts:{...b,...s}};return this.K.add(n),()=>{this.K.delete(n)}}clear(){this.K.clear(),this.M.clear()}down(t,e){const s=a(t.key);this.M.has(s)||this.M.set(s,Date.now());const n=Date.now(),o=[];for(const t of this.K){if(t.opts.scope!==e&&"global"!==t.opts.scope)continue;if(t.keys.size!==this.M.size)continue;let s=!0,i=n;for(const e of t.keys){const t=this.M.get(e);if(void 0===t){s=!1;break}t<i&&(i=t)}s&&(n-i>t.opts.window||o.push(t))}return o}up(t){this.M.delete(a(t.key))}reset(){this.M.clear()}}const k={q:"'",w:",",e:".",r:"p",t:"y",y:"f",u:"g",i:"c",o:"r",p:"l",a:"a",s:"o",d:"e",f:"u",g:"i",h:"d",j:"h",k:"t",l:"n",z:";",x:"q",c:"j",v:"k",b:"x",n:"b",m:"m"},v={q:"q",w:"w",e:"f",r:"p",t:"g",y:"j",u:"l",i:"u",o:"y",p:";",a:"a",s:"r",d:"s",f:"t",g:"d",h:"h",j:"n",k:"e",l:"i",z:"z",x:"x",c:"c",v:"v",b:"b",n:"k",m:"m"};class _{D="qwerty";set(t){this.D=t}get name(){return this.D}translate(t){const e=t.toLowerCase();return"qwerty"===this.D?e:("dvorak"===this.D?k:v)[e]??e}}class K{A=[{name:"global",exclusive:!1}];get active(){return this.A[this.A.length-1]?.name??"global"}get isExclusive(){return this.A[this.A.length-1]?.exclusive??!1}set(t,e){this.A=[{name:t,exclusive:e?.exclusive??!1}]}push(t,e){return this.A.push({name:t,exclusive:e?.exclusive??!1}),()=>this.pop(t)}pop(t){if(!t)return void(this.A.length>1&&this.A.pop());const e=this.A.map(t=>t.name).lastIndexOf(t);e>0&&this.A.splice(e,1)}reset(){this.A=[{name:"global",exclusive:!1}]}matches(t){return t===this.active||"global"===t&&!this.isExclusive}}class x{D=!1;C=0;S=[];get recording(){return this.D}start(){this.S=[],this.C=Date.now(),this.D=!0}stop(){return this.D=!1,this.S.slice()}capture(t){if(!this.D)return;const e=f(t);e.key&&this.S.push({combo:e,label:d(e),at:Date.now()-this.C})}async replay(t,e=window,s=1){if(0===t.length)return;const n=t[0];if(!n)return;const o=n.at;for(const n of t){const t=Math.max(0,(n.at-o)/s);await new Promise(e=>setTimeout(e,t));const i=new KeyboardEvent("keydown",{key:n.combo.key,ctrlKey:n.combo.mods.has("control"),altKey:n.combo.mods.has("alt"),shiftKey:n.combo.mods.has("shift"),metaKey:n.combo.mods.has("meta"),bubbles:!0,cancelable:!0});e.dispatchEvent(i)}}}const S=new class{I=new t;P=new p;B=new y;L=new g;$=new _;H=new K;N=new x;R=null;U=null;W=null;X=null;F=!1;init(t){this.F||(this.R=t?.target??window,t?.scope&&this.H.set(t.scope),this.U=t=>this.G(t,"keydown"),this.W=t=>this.G(t,"keyup"),this.X=()=>this.L.reset(),this.R.addEventListener("keydown",this.U),this.R.addEventListener("keyup",this.W),window.addEventListener("blur",this.X),this.F=!0)}destroy(){this.F&&this.R&&(this.U&&this.R.removeEventListener("keydown",this.U),this.W&&this.R.removeEventListener("keyup",this.W),this.X&&window.removeEventListener("blur",this.X),this.F=!1)}on(t,e,s){return Array.isArray(t)?this.B.add(t,e,s):this.P.add(t,e,s)}off(t){this.P.removeByCombo(t)}chord(t,e,s){return this.L.add(t,e,s)}list(){return this.P.list().map(t=>({combo:d(t.combo),scope:t.opts.scope}))}clear(){this.P.clear(),this.B.clear(),this.L.clear()}scope(t,e){return t&&this.H.set(t,e),this.H.active}pushScope(t,e){return this.H.push(t,e)}popScope(t){this.H.pop(t)}layout(t){return t&&this.$.set(t),this.$.name}record(){this.N.start()}stop(){return this.N.stop()}async replay(t,e=1){if(0===t.length)return;const s=t[0];if(!s)return;const n=s.at,o=this.R??window;for(const s of t){const t=Math.max(0,(s.at-n)/e);await new Promise(e=>setTimeout(e,t)),this.I.emit("replay",s);const i=new KeyboardEvent("keydown",{key:s.combo.key,ctrlKey:s.combo.mods.has("control"),altKey:s.combo.mods.has("alt"),shiftKey:s.combo.mods.has("shift"),metaKey:s.combo.mods.has("meta"),bubbles:!0,cancelable:!0});o.dispatchEvent(i)}}onEvent(t,e){return this.I.on(t,e)}get platform(){return e}get isMac(){return s}get modKey(){return n}format(t){return d(l(t))}G(t,e){if("keydown"===e&&this.N.capture(t),"keyup"===e)return void this.L.up(t);this.I.emit("keydown",t);const s=this.J(t.target),n=this.L.down(t,this.H.active);for(const t of n)t.handler(),this.I.emit("chord",t);const o=this.P.match(t,e);let i=!1;for(const e of o)this.H.matches(e.opts.scope)&&(s&&!e.opts.allowInInput||(e.opts.preventDefault&&t.preventDefault(),e.handler(t),this.I.emit("shortcut",e),i=!0));const r=this.B.feed(t,this.H.active);for(const t of r)s||(t.handler(),this.I.emit("sequence",t),i=!0);i&&this.I.emit("handled",t)}J(t){if(!(t instanceof HTMLElement))return!1;const e=t.tagName;return"INPUT"===e||"TEXTAREA"===e||"SELECT"===e||t.isContentEditable}};export{S as default};
2
+ //# sourceMappingURL=keyboard.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.esm.js","sources":["../src/events/events.ts","../src/utils/platform.ts","../src/utils/keys.ts","../src/shortcuts/shortcuts.ts","../src/sequences/sequences.ts","../src/chords/chords.ts","../src/layouts/layouts.ts","../src/scope/scope.ts","../src/recorder/recorder.ts","../src/index.ts"],"sourcesContent":["type Listener = (...args: unknown[]) => void;\n\n/** Tiny pub/sub for KeyBoard's internal lifecycle events. */\nexport class KbEvents {\n private _map = new Map<string, Set<Listener>>();\n\n on(event: string, fn: Listener): () => void {\n let set = this._map.get(event);\n if (!set) {\n set = new Set();\n this._map.set(event, set);\n }\n set.add(fn);\n return () => this.off(event, fn);\n }\n\n off(event: string, fn?: Listener): void {\n if (!fn) {\n this._map.delete(event);\n return;\n }\n this._map.get(event)?.delete(fn);\n }\n\n emit(event: string, ...args: unknown[]): void {\n const set = this._map.get(event);\n if (!set) return;\n for (const fn of set) {\n try { fn(...args); } catch (e) { console.error('[keyboard.js]', e); }\n }\n }\n}\n","/**\n * Platform detection — runs once at module load.\n * Determines how `mod` maps to a physical modifier and how combos\n * are pretty-printed.\n */\n\nexport type Platform = 'mac' | 'windows' | 'linux' | 'other';\n\nfunction detect(): Platform {\n if (typeof navigator === 'undefined') return 'other';\n const ua = navigator.userAgent;\n const plat = (navigator.platform || '').toLowerCase();\n\n // Apple devices report \"MacIntel\", \"iPhone\", \"iPad\", \"iPod\".\n // On iPadOS 13+ the platform string is \"MacIntel\" but `maxTouchPoints > 1`.\n if (/mac|iphone|ipad|ipod/i.test(plat)) return 'mac';\n if (/mac/i.test(ua)) return 'mac';\n if (/win/i.test(plat) || /windows/i.test(ua)) return 'windows';\n if (/linux|x11/i.test(plat) || /linux/i.test(ua)) return 'linux';\n return 'other';\n}\n\nexport const PLATFORM: Platform = detect();\nexport const IS_MAC = PLATFORM === 'mac';\n\n/** The physical modifier that `mod` (a.k.a. \"CommandOrControl\") resolves to. */\nexport const MOD_MODIFIER: 'meta' | 'control' = IS_MAC ? 'meta' : 'control';\n\n/** Mac glyphs for the four modifiers. */\nexport const MAC_SYMBOLS: Record<string, string> = {\n control: '\\u2303', // ⌃\n alt: '\\u2325', // ⌥\n shift: '\\u21E7', // ⇧\n meta: '\\u2318', // ⌘\n};\n\n/** Human-readable labels for non-Mac platforms. */\nexport const PC_LABELS: Record<string, string> = {\n control: 'Ctrl',\n alt: 'Alt',\n shift: 'Shift',\n meta: 'Win',\n};\n\n/** Special-key glyphs on Mac (\"enter\" → ↵, etc.) */\nexport const MAC_KEY_SYMBOLS: Record<string, string> = {\n enter: '\\u21B5', // ↵\n backspace: '\\u232B', // ⌫\n delete: '\\u2326', // ⌦\n tab: '\\u21E5', // ⇥\n escape: 'esc',\n arrowup: '\\u2191',\n arrowdown: '\\u2193',\n arrowleft: '\\u2190',\n arrowright: '\\u2192',\n ' ': 'Space',\n};\n","/**\n * Key normalization utilities.\n * Converts between human-readable names (\"ctrl+k\", \"enter\") and the\n * underlying KeyboardEvent representation.\n */\n\nimport { MAC_KEY_SYMBOLS, MAC_SYMBOLS, MOD_MODIFIER, PC_LABELS, IS_MAC } from './platform';\n\nconst ALIAS: Record<string, string> = {\n ctrl: 'control',\n cmd: 'meta',\n command: 'meta',\n win: 'meta',\n windows: 'meta',\n super: 'meta',\n opt: 'alt',\n option: 'alt',\n return: 'enter',\n esc: 'escape',\n space: ' ',\n spacebar: ' ',\n plus: '+',\n up: 'arrowup',\n down: 'arrowdown',\n left: 'arrowleft',\n right: 'arrowright',\n del: 'delete',\n ins: 'insert',\n pgup: 'pageup',\n pgdn: 'pagedown',\n};\n\nconst MODIFIER_ORDER = ['control', 'alt', 'shift', 'meta'] as const;\nexport type Modifier = (typeof MODIFIER_ORDER)[number];\n\nexport interface ParsedCombo {\n key: string;\n mods: Set<Modifier>;\n}\n\n/** Normalize a single key token to its canonical form. */\nexport function normKey(raw: string): string {\n const k = raw.trim().toLowerCase();\n // `mod` is the cross-platform modifier — Cmd on Mac, Ctrl elsewhere.\n // VSCode, CodeMirror, and Electron use the same convention.\n if (k === 'mod' || k === 'commandorcontrol' || k === 'cmdorctrl') {\n return MOD_MODIFIER;\n }\n return ALIAS[k] ?? k;\n}\n\n/** Parse \"ctrl+shift+k\" into a ParsedCombo. */\nexport function parseCombo(input: string): ParsedCombo {\n const parts = input\n .split('+')\n .map((p) => p.trim())\n .filter((p) => p.length > 0);\n\n if (parts.length === 0) {\n return { key: '', mods: new Set() };\n }\n\n const mods = new Set<Modifier>();\n let key = '';\n\n for (const raw of parts) {\n const n = normKey(raw);\n if (MODIFIER_ORDER.includes(n as Modifier)) {\n mods.add(n as Modifier);\n } else {\n key = n;\n }\n }\n\n return { key, mods };\n}\n\n/** Build a comparable signature: \"control+shift|k\" */\nexport function comboSig(combo: ParsedCombo): string {\n const mods = MODIFIER_ORDER.filter((m) => combo.mods.has(m)).join('+');\n return `${mods}|${combo.key}`;\n}\n\n/** Extract a ParsedCombo from a live KeyboardEvent. */\nexport function eventCombo(ev: KeyboardEvent): ParsedCombo {\n const mods = new Set<Modifier>();\n if (ev.ctrlKey) mods.add('control');\n if (ev.altKey) mods.add('alt');\n if (ev.shiftKey) mods.add('shift');\n if (ev.metaKey) mods.add('meta');\n\n let key = ev.key.toLowerCase();\n // Strip modifier self-reports so \"shift\" alone doesn't clash with \"shift+a\"\n if (MODIFIER_ORDER.includes(key as Modifier)) key = '';\n\n return { key, mods };\n}\n\n/**\n * Pretty-print a combo for display.\n * Mac: uses glyphs with no separator (\"⌘K\").\n * PC: uses words with \" + \" separator (\"Ctrl + K\").\n */\nexport function formatCombo(combo: ParsedCombo): string {\n const parts: string[] = [];\n const labels = IS_MAC ? MAC_SYMBOLS : PC_LABELS;\n for (const m of MODIFIER_ORDER) {\n if (combo.mods.has(m)) parts.push(labels[m] ?? m);\n }\n if (combo.key) {\n const special = IS_MAC ? MAC_KEY_SYMBOLS[combo.key] : undefined;\n parts.push(special ?? (combo.key.length === 1 ? combo.key.toUpperCase() : combo.key));\n }\n return parts.join(IS_MAC ? '' : ' + ');\n}\n","import { comboSig, eventCombo, parseCombo } from '../utils/keys';\nimport type { ParsedCombo } from '../utils/keys';\n\nexport interface ShortcutOptions {\n /** Scope name this binding belongs to — only fires when scope is active */\n scope?: string;\n /** Prevent default browser behaviour when matched (default: true) */\n preventDefault?: boolean;\n /** Fire even when typing in input/textarea/contenteditable (default: false) */\n allowInInput?: boolean;\n /** Fire on keyup instead of keydown */\n onKeyup?: boolean;\n}\n\nexport interface ShortcutBinding {\n combo: ParsedCombo;\n sig: string;\n handler: (e: KeyboardEvent) => void;\n opts: Required<Omit<ShortcutOptions, 'scope'>> & { scope: string };\n}\n\nexport interface ShortcutMatch {\n binding: ShortcutBinding;\n}\n\nconst DEFAULT_OPTS: Required<Omit<ShortcutOptions, 'scope'>> & { scope: string } = {\n scope: 'global',\n preventDefault: true,\n allowInInput: false,\n onKeyup: false,\n};\n\nexport class Shortcuts {\n private _bindings = new Map<string, Set<ShortcutBinding>>();\n\n add(combo: string, handler: (e: KeyboardEvent) => void, opts?: ShortcutOptions): () => void {\n const parsed = parseCombo(combo);\n const sig = comboSig(parsed);\n const binding: ShortcutBinding = {\n combo: parsed,\n sig,\n handler,\n opts: { ...DEFAULT_OPTS, ...opts, scope: opts?.scope ?? 'global' },\n };\n let set = this._bindings.get(sig);\n if (!set) {\n set = new Set();\n this._bindings.set(sig, set);\n }\n set.add(binding);\n return () => this.remove(binding);\n }\n\n remove(binding: ShortcutBinding): void {\n const set = this._bindings.get(binding.sig);\n if (!set) return;\n set.delete(binding);\n if (set.size === 0) this._bindings.delete(binding.sig);\n }\n\n removeByCombo(combo: string): void {\n const sig = comboSig(parseCombo(combo));\n this._bindings.delete(sig);\n }\n\n clear(): void {\n this._bindings.clear();\n }\n\n /** Find all matching bindings for a key event — caller filters by scope. */\n match(ev: KeyboardEvent, type: 'keydown' | 'keyup'): ShortcutBinding[] {\n const combo = eventCombo(ev);\n if (!combo.key) return [];\n const sig = comboSig(combo);\n const set = this._bindings.get(sig);\n if (!set) return [];\n const out: ShortcutBinding[] = [];\n for (const b of set) {\n if (type === 'keyup' && !b.opts.onKeyup) continue;\n if (type === 'keydown' && b.opts.onKeyup) continue;\n out.push(b);\n }\n return out;\n }\n\n list(): ShortcutBinding[] {\n const out: ShortcutBinding[] = [];\n for (const set of this._bindings.values()) {\n for (const b of set) out.push(b);\n }\n return out;\n }\n}\n","import { comboSig, eventCombo, parseCombo } from '../utils/keys';\n\nexport interface SequenceOptions {\n scope?: string;\n /** Reset timeout in ms between keys (default: 1000) */\n timeout?: number;\n}\n\nexport interface SequenceBinding {\n sigs: string[];\n handler: () => void;\n opts: Required<SequenceOptions>;\n}\n\nconst DEFAULT_OPTS: Required<SequenceOptions> = {\n scope: 'global',\n timeout: 1000,\n};\n\n/**\n * Detects ordered key sequences like `g g` or `ctrl+k ctrl+s`.\n * Each token in the input array represents one step of the sequence.\n */\nexport class Sequences {\n private _bindings = new Set<SequenceBinding>();\n private _buffer: { sig: string; at: number }[] = [];\n\n add(sequence: string[], handler: () => void, opts?: SequenceOptions): () => void {\n const sigs = sequence.map((s) => comboSig(parseCombo(s)));\n const binding: SequenceBinding = {\n sigs,\n handler,\n opts: { ...DEFAULT_OPTS, ...opts },\n };\n this._bindings.add(binding);\n return () => { this._bindings.delete(binding); };\n }\n\n clear(): void {\n this._bindings.clear();\n this._buffer = [];\n }\n\n /** Feed a key event — returns list of sequences that fired. */\n feed(ev: KeyboardEvent, activeScope: string): SequenceBinding[] {\n const combo = eventCombo(ev);\n if (!combo.key) return [];\n const sig = comboSig(combo);\n const now = Date.now();\n\n // Prune expired entries from buffer (using the shortest timeout among bindings)\n const minTimeout = this._minTimeout();\n this._buffer = this._buffer.filter((entry) => now - entry.at <= minTimeout);\n\n this._buffer.push({ sig, at: now });\n\n const fired: SequenceBinding[] = [];\n for (const b of this._bindings) {\n if (b.opts.scope !== activeScope && b.opts.scope !== 'global') continue;\n if (this._buffer.length < b.sigs.length) continue;\n const tail = this._buffer.slice(-b.sigs.length);\n const within = now - (tail[0]?.at ?? 0) <= b.opts.timeout;\n if (!within) continue;\n const matches = tail.every((entry, i) => entry.sig === b.sigs[i]);\n if (matches) fired.push(b);\n }\n\n if (fired.length > 0) this._buffer = [];\n return fired;\n }\n\n private _minTimeout(): number {\n let min = Infinity;\n for (const b of this._bindings) {\n if (b.opts.timeout < min) min = b.opts.timeout;\n }\n return min === Infinity ? 1000 : min;\n }\n}\n","import { normKey } from '../utils/keys';\n\nexport interface ChordOptions {\n scope?: string;\n /** Max time between first and last key press in ms (default: 250) */\n window?: number;\n}\n\nexport interface ChordBinding {\n keys: Set<string>;\n handler: () => void;\n opts: Required<ChordOptions>;\n}\n\nconst DEFAULT_OPTS: Required<ChordOptions> = {\n scope: 'global',\n window: 250,\n};\n\n/**\n * Detects simultaneous key presses (e.g. \"j+k\" pressed together).\n * Differs from shortcuts in that it has no modifier constraint — any\n * combination of plain keys held within a short window counts.\n */\nexport class Chords {\n private _bindings = new Set<ChordBinding>();\n private _held = new Map<string, number>();\n\n add(keys: string[], handler: () => void, opts?: ChordOptions): () => void {\n const binding: ChordBinding = {\n keys: new Set(keys.map(normKey)),\n handler,\n opts: { ...DEFAULT_OPTS, ...opts },\n };\n this._bindings.add(binding);\n return () => { this._bindings.delete(binding); };\n }\n\n clear(): void {\n this._bindings.clear();\n this._held.clear();\n }\n\n down(ev: KeyboardEvent, activeScope: string): ChordBinding[] {\n const key = normKey(ev.key);\n if (!this._held.has(key)) this._held.set(key, Date.now());\n\n const now = Date.now();\n const fired: ChordBinding[] = [];\n\n for (const b of this._bindings) {\n if (b.opts.scope !== activeScope && b.opts.scope !== 'global') continue;\n if (b.keys.size !== this._held.size) continue;\n\n let allMatch = true;\n let earliest = now;\n for (const k of b.keys) {\n const t = this._held.get(k);\n if (t === undefined) { allMatch = false; break; }\n if (t < earliest) earliest = t;\n }\n if (!allMatch) continue;\n if (now - earliest > b.opts.window) continue;\n fired.push(b);\n }\n return fired;\n }\n\n up(ev: KeyboardEvent): void {\n this._held.delete(normKey(ev.key));\n }\n\n reset(): void {\n this._held.clear();\n }\n}\n","export type LayoutName = 'qwerty' | 'dvorak' | 'colemak';\n\n/** Mapping from QWERTY physical key → Dvorak character. */\nconst DVORAK: Record<string, string> = {\n q: \"'\", w: ',', e: '.', r: 'p', t: 'y', y: 'f', u: 'g', i: 'c', o: 'r', p: 'l',\n a: 'a', s: 'o', d: 'e', f: 'u', g: 'i', h: 'd', j: 'h', k: 't', l: 'n',\n z: ';', x: 'q', c: 'j', v: 'k', b: 'x', n: 'b', m: 'm',\n};\n\nconst COLEMAK: Record<string, string> = {\n q: 'q', w: 'w', e: 'f', r: 'p', t: 'g', y: 'j', u: 'l', i: 'u', o: 'y', p: ';',\n a: 'a', s: 'r', d: 's', f: 't', g: 'd', h: 'h', j: 'n', k: 'e', l: 'i',\n z: 'z', x: 'x', c: 'c', v: 'v', b: 'b', n: 'k', m: 'm',\n};\n\n/**\n * Layout remapper — translates a user's physical key press into the\n * character they *intend* to produce under a different layout.\n * Useful when letting users define shortcuts that survive layout changes.\n */\nexport class Layouts {\n private _active: LayoutName = 'qwerty';\n\n set(layout: LayoutName): void {\n this._active = layout;\n }\n\n get name(): LayoutName {\n return this._active;\n }\n\n /** Translate a key character from QWERTY-physical to the active layout. */\n translate(key: string): string {\n const k = key.toLowerCase();\n if (this._active === 'qwerty') return k;\n const map = this._active === 'dvorak' ? DVORAK : COLEMAK;\n return map[k] ?? k;\n }\n}\n","interface Frame {\n name: string;\n exclusive: boolean;\n}\n\n/**\n * Scope manager — lets shortcuts be grouped by context (e.g. \"editor\",\n * \"modal\", \"global\").\n *\n * Normally, bindings with `scope: 'global'` always fire and scope-specific\n * bindings only fire when that scope is active. An `exclusive` scope\n * suppresses global bindings while active — useful for modals where\n * underlying shortcuts should stop firing.\n */\nexport class Scope {\n private _stack: Frame[] = [{ name: 'global', exclusive: false }];\n\n get active(): string {\n return this._stack[this._stack.length - 1]?.name ?? 'global';\n }\n\n get isExclusive(): boolean {\n return this._stack[this._stack.length - 1]?.exclusive ?? false;\n }\n\n /** Replace the current scope. */\n set(name: string, opts?: { exclusive?: boolean }): void {\n this._stack = [{ name, exclusive: opts?.exclusive ?? false }];\n }\n\n /** Push a temporary scope — useful for modals. */\n push(name: string, opts?: { exclusive?: boolean }): () => void {\n const frame: Frame = { name, exclusive: opts?.exclusive ?? false };\n this._stack.push(frame);\n return () => this.pop(name);\n }\n\n /** Pop either the specific scope or the topmost. */\n pop(name?: string): void {\n if (!name) {\n if (this._stack.length > 1) this._stack.pop();\n return;\n }\n const idx = this._stack.map((f) => f.name).lastIndexOf(name);\n if (idx > 0) this._stack.splice(idx, 1);\n }\n\n reset(): void {\n this._stack = [{ name: 'global', exclusive: false }];\n }\n\n matches(scope: string): boolean {\n const active = this.active;\n if (scope === active) return true;\n if (scope === 'global') return !this.isExclusive;\n return false;\n }\n}\n","import { eventCombo, formatCombo } from '../utils/keys';\nimport type { ParsedCombo } from '../utils/keys';\n\nexport interface RecordedKey {\n combo: ParsedCombo;\n label: string;\n /** ms offset from start of recording */\n at: number;\n}\n\n/**\n * Records and replays key events.\n * `replay` dispatches synthetic KeyboardEvents onto a target (default: window)\n * preserving the original inter-key timing.\n */\nexport class Recorder {\n private _active = false;\n private _start = 0;\n private _buffer: RecordedKey[] = [];\n\n get recording(): boolean {\n return this._active;\n }\n\n start(): void {\n this._buffer = [];\n this._start = Date.now();\n this._active = true;\n }\n\n stop(): RecordedKey[] {\n this._active = false;\n return this._buffer.slice();\n }\n\n capture(ev: KeyboardEvent): void {\n if (!this._active) return;\n const combo = eventCombo(ev);\n if (!combo.key) return;\n this._buffer.push({\n combo,\n label: formatCombo(combo),\n at: Date.now() - this._start,\n });\n }\n\n async replay(\n recording: RecordedKey[],\n target: EventTarget = window,\n speed = 1,\n ): Promise<void> {\n if (recording.length === 0) return;\n const first = recording[0];\n if (!first) return;\n const baseOffset = first.at;\n\n for (const entry of recording) {\n const wait = Math.max(0, (entry.at - baseOffset) / speed);\n await new Promise((r) => setTimeout(r, wait));\n const ev = new KeyboardEvent('keydown', {\n key: entry.combo.key,\n ctrlKey: entry.combo.mods.has('control'),\n altKey: entry.combo.mods.has('alt'),\n shiftKey: entry.combo.mods.has('shift'),\n metaKey: entry.combo.mods.has('meta'),\n bubbles: true,\n cancelable: true,\n });\n target.dispatchEvent(ev);\n }\n }\n}\n","import { KbEvents } from './events/events';\nimport { Shortcuts } from './shortcuts/shortcuts';\nimport type { ShortcutOptions } from './shortcuts/shortcuts';\nimport { Sequences } from './sequences/sequences';\nimport type { SequenceOptions } from './sequences/sequences';\nimport { Chords } from './chords/chords';\nimport type { ChordOptions } from './chords/chords';\nimport { Layouts } from './layouts/layouts';\nimport type { LayoutName } from './layouts/layouts';\nimport { Scope } from './scope/scope';\nimport { Recorder } from './recorder/recorder';\nimport type { RecordedKey } from './recorder/recorder';\nimport { formatCombo, parseCombo } from './utils/keys';\nimport { PLATFORM, IS_MAC, MOD_MODIFIER } from './utils/platform';\nimport type { Platform } from './utils/platform';\n\nexport interface InitOptions {\n /** EventTarget to attach listeners to (default: window) */\n target?: EventTarget;\n /** Default scope name when starting (default: \"global\") */\n scope?: string;\n}\n\nclass Keyboard {\n private _ev = new KbEvents();\n private _shortcuts = new Shortcuts();\n private _sequences = new Sequences();\n private _chords = new Chords();\n private _layouts = new Layouts();\n private _scope = new Scope();\n private _recorder = new Recorder();\n\n private _target: EventTarget | null = null;\n private _onKeydown: ((e: Event) => void) | null = null;\n private _onKeyup: ((e: Event) => void) | null = null;\n private _onBlur: (() => void) | null = null;\n private _started = false;\n\n // --- lifecycle ---\n\n init(opts?: InitOptions): void {\n if (this._started) return;\n this._target = opts?.target ?? window;\n if (opts?.scope) this._scope.set(opts.scope);\n\n this._onKeydown = (ev) => this._handleKey(ev as KeyboardEvent, 'keydown');\n this._onKeyup = (ev) => this._handleKey(ev as KeyboardEvent, 'keyup');\n this._onBlur = () => this._chords.reset();\n\n this._target.addEventListener('keydown', this._onKeydown);\n this._target.addEventListener('keyup', this._onKeyup);\n window.addEventListener('blur', this._onBlur);\n this._started = true;\n }\n\n destroy(): void {\n if (!this._started || !this._target) return;\n if (this._onKeydown) this._target.removeEventListener('keydown', this._onKeydown);\n if (this._onKeyup) this._target.removeEventListener('keyup', this._onKeyup);\n if (this._onBlur) window.removeEventListener('blur', this._onBlur);\n this._started = false;\n }\n\n // --- shortcut API ---\n\n /** Register a hotkey. Accepts a single combo string or an array for sequences. */\n on(\n combo: string,\n handler: (e: KeyboardEvent) => void,\n opts?: ShortcutOptions,\n ): () => void;\n on(\n sequence: string[],\n handler: () => void,\n opts?: SequenceOptions,\n ): () => void;\n on(\n combo: string | string[],\n handler: ((e: KeyboardEvent) => void) | (() => void),\n opts?: ShortcutOptions | SequenceOptions,\n ): () => void {\n if (Array.isArray(combo)) {\n return this._sequences.add(combo, handler as () => void, opts as SequenceOptions);\n }\n return this._shortcuts.add(combo, handler as (e: KeyboardEvent) => void, opts);\n }\n\n /** Remove all bindings for a combo. */\n off(combo: string): void {\n this._shortcuts.removeByCombo(combo);\n }\n\n /** Register a chord — keys held simultaneously. */\n chord(keys: string[], handler: () => void, opts?: ChordOptions): () => void {\n return this._chords.add(keys, handler, opts);\n }\n\n /** List every registered shortcut — useful for building help dialogs. */\n list(): Array<{ combo: string; scope: string }> {\n return this._shortcuts.list().map((b) => ({\n combo: formatCombo(b.combo),\n scope: b.opts.scope,\n }));\n }\n\n clear(): void {\n this._shortcuts.clear();\n this._sequences.clear();\n this._chords.clear();\n }\n\n // --- scope ---\n\n scope(name?: string, opts?: { exclusive?: boolean }): string {\n if (name) this._scope.set(name, opts);\n return this._scope.active;\n }\n\n /**\n * Push a scope onto the stack. When `exclusive: true`, global bindings\n * are suppressed while this scope is active — ideal for modals.\n */\n pushScope(name: string, opts?: { exclusive?: boolean }): () => void {\n return this._scope.push(name, opts);\n }\n\n popScope(name?: string): void {\n this._scope.pop(name);\n }\n\n // --- layout ---\n\n layout(name?: LayoutName): LayoutName {\n if (name) this._layouts.set(name);\n return this._layouts.name;\n }\n\n // --- recorder ---\n\n record(): void { this._recorder.start(); }\n stop(): RecordedKey[] { return this._recorder.stop(); }\n\n /**\n * Replay a recording — dispatches synthetic KeyboardEvents at the original\n * inter-key timing (or faster with `speed`). Emits a 'replay' event per key.\n */\n async replay(recording: RecordedKey[], speed = 1): Promise<void> {\n if (recording.length === 0) return;\n const first = recording[0];\n if (!first) return;\n const baseOffset = first.at;\n const target = this._target ?? window;\n\n for (const entry of recording) {\n const wait = Math.max(0, (entry.at - baseOffset) / speed);\n await new Promise((r) => setTimeout(r, wait));\n this._ev.emit('replay', entry);\n const ev = new KeyboardEvent('keydown', {\n key: entry.combo.key,\n ctrlKey: entry.combo.mods.has('control'),\n altKey: entry.combo.mods.has('alt'),\n shiftKey: entry.combo.mods.has('shift'),\n metaKey: entry.combo.mods.has('meta'),\n bubbles: true,\n cancelable: true,\n });\n target.dispatchEvent(ev);\n }\n }\n\n // --- events (library-internal lifecycle) ---\n\n onEvent(event: string, fn: (...args: unknown[]) => void): () => void {\n return this._ev.on(event, fn);\n }\n\n // --- platform ---\n\n /** Detected platform: 'mac' | 'windows' | 'linux' | 'other'. */\n get platform(): Platform { return PLATFORM; }\n\n /** True on macOS / iPadOS / iOS. */\n get isMac(): boolean { return IS_MAC; }\n\n /**\n * The physical modifier that `mod` resolves to on this platform.\n * 'meta' on Mac, 'control' on Windows/Linux.\n * Use when building help UI: `Kb.modKey // \"meta\" | \"control\"`.\n */\n get modKey(): 'meta' | 'control' { return MOD_MODIFIER; }\n\n // --- helpers ---\n\n format(combo: string): string {\n return formatCombo(parseCombo(combo));\n }\n\n // --- internal ---\n\n private _handleKey(ev: KeyboardEvent, type: 'keydown' | 'keyup'): void {\n if (type === 'keydown') this._recorder.capture(ev);\n if (type === 'keyup') { this._chords.up(ev); return; }\n\n // Raw stream — fires for every keydown even if no binding matches\n this._ev.emit('keydown', ev);\n\n const inInput = this._isEditable(ev.target);\n\n // 1. Chord detection (simultaneous keys)\n const chords = this._chords.down(ev, this._scope.active);\n for (const c of chords) {\n c.handler();\n this._ev.emit('chord', c);\n }\n\n // 2. Shortcut matching\n const matches = this._shortcuts.match(ev, type);\n let handled = false;\n for (const b of matches) {\n if (!this._scope.matches(b.opts.scope)) continue;\n if (inInput && !b.opts.allowInInput) continue;\n if (b.opts.preventDefault) ev.preventDefault();\n b.handler(ev);\n this._ev.emit('shortcut', b);\n handled = true;\n }\n\n // 3. Sequence detection — independent of shortcut matching\n const fired = this._sequences.feed(ev, this._scope.active);\n for (const s of fired) {\n if (inInput) continue;\n s.handler();\n this._ev.emit('sequence', s);\n handled = true;\n }\n\n if (handled) this._ev.emit('handled', ev);\n }\n\n private _isEditable(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n const tag = target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n return target.isContentEditable;\n }\n}\n\nconst keyboard = new Keyboard();\nexport default keyboard;\n\nexport type {\n ShortcutOptions, SequenceOptions, ChordOptions,\n LayoutName, RecordedKey, Platform,\n};\n"],"names":["KbEvents","_map","Map","on","event","fn","set","this","get","Set","add","off","delete","emit","args","e","console","error","PLATFORM","navigator","ua","userAgent","plat","platform","toLowerCase","test","detect","IS_MAC","MOD_MODIFIER","MAC_SYMBOLS","control","alt","shift","meta","PC_LABELS","MAC_KEY_SYMBOLS","enter","backspace","tab","escape","arrowup","arrowdown","arrowleft","arrowright","ALIAS","ctrl","cmd","command","win","windows","super","opt","option","return","esc","space","spacebar","plus","up","down","left","right","del","ins","pgup","pgdn","MODIFIER_ORDER","normKey","raw","k","trim","parseCombo","input","parts","split","map","p","filter","length","key","mods","n","includes","comboSig","combo","m","has","join","eventCombo","ev","ctrlKey","altKey","shiftKey","metaKey","formatCombo","labels","push","undefined","toUpperCase","DEFAULT_OPTS","scope","preventDefault","allowInInput","onKeyup","Shortcuts","_bindings","handler","opts","parsed","sig","binding","remove","size","removeByCombo","clear","match","type","out","b","list","values","timeout","Sequences","_buffer","sequence","sigs","s","feed","activeScope","now","Date","minTimeout","_minTimeout","entry","at","fired","tail","slice","every","i","min","Infinity","window","Chords","_held","keys","allMatch","earliest","t","reset","DVORAK","q","w","r","y","u","o","a","d","f","g","h","j","l","z","x","c","v","COLEMAK","Layouts","_active","layout","name","translate","Scope","_stack","exclusive","active","isExclusive","pop","idx","lastIndexOf","splice","matches","Recorder","_start","recording","start","stop","capture","label","replay","target","speed","first","baseOffset","wait","Math","max","Promise","setTimeout","KeyboardEvent","bubbles","cancelable","dispatchEvent","keyboard","_ev","_shortcuts","_sequences","_chords","_layouts","_scope","_recorder","_target","_onKeydown","_onKeyup","_onBlur","_started","init","_handleKey","addEventListener","destroy","removeEventListener","Array","isArray","chord","pushScope","popScope","record","onEvent","isMac","modKey","format","inInput","_isEditable","chords","handled","HTMLElement","tag","tagName","isContentEditable"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Keyboard=e()}(this,function(){"use strict";class t{_=new Map;on(t,e){let s=this._.get(t);return s||(s=new Set,this._.set(t,s)),s.add(e),()=>this.off(t,e)}off(t,e){e?this._.get(t)?.delete(e):this._.delete(t)}emit(t,...e){const s=this._.get(t);if(s)for(const t of s)try{t(...e)}catch(t){console.error("[keyboard.js]",t)}}}const e=function(){if("undefined"==typeof navigator)return"other";const t=navigator.userAgent,e=(navigator.platform||"").toLowerCase();return/mac|iphone|ipad|ipod/i.test(e)||/mac/i.test(t)?"mac":/win/i.test(e)||/windows/i.test(t)?"windows":/linux|x11/i.test(e)||/linux/i.test(t)?"linux":"other"}(),s="mac"===e,n=s?"meta":"control",o={control:"⌃",alt:"⌥",shift:"⇧",meta:"⌘"},i={control:"Ctrl",alt:"Alt",shift:"Shift",meta:"Win"},r={enter:"↵",backspace:"⌫",delete:"⌦",tab:"⇥",escape:"esc",arrowup:"↑",arrowdown:"↓",arrowleft:"←",arrowright:"→"," ":"Space"},c={ctrl:"control",cmd:"meta",command:"meta",win:"meta",windows:"meta",super:"meta",opt:"alt",option:"alt",return:"enter",esc:"escape",space:" ",spacebar:" ",plus:"+",up:"arrowup",down:"arrowdown",left:"arrowleft",right:"arrowright",del:"delete",ins:"insert",pgup:"pageup",pgdn:"pagedown"},h=["control","alt","shift","meta"];function a(t){const e=t.trim().toLowerCase();return"mod"===e||"commandorcontrol"===e||"cmdorctrl"===e?n:c[e]??e}function l(t){const e=t.split("+").map(t=>t.trim()).filter(t=>t.length>0);if(0===e.length)return{key:"",mods:new Set};const s=new Set;let n="";for(const t of e){const e=a(t);h.includes(e)?s.add(e):n=e}return{key:n,mods:s}}function u(t){return`${h.filter(e=>t.mods.has(e)).join("+")}|${t.key}`}function f(t){const e=new Set;t.ctrlKey&&e.add("control"),t.altKey&&e.add("alt"),t.shiftKey&&e.add("shift"),t.metaKey&&e.add("meta");let s=t.key.toLowerCase();return h.includes(s)&&(s=""),{key:s,mods:e}}function d(t){const e=[],n=s?o:i;for(const s of h)t.mods.has(s)&&e.push(n[s]??s);return t.key&&e.push((s?r[t.key]:void 0)??(1===t.key.length?t.key.toUpperCase():t.key)),e.join(s?"":" + ")}const w={scope:"global",preventDefault:!0,allowInInput:!1,onKeyup:!1};class p{K=new Map;add(t,e,s){const n=l(t),o=u(n),i={combo:n,sig:o,handler:e,opts:{...w,...s,scope:s?.scope??"global"}};let r=this.K.get(o);return r||(r=new Set,this.K.set(o,r)),r.add(i),()=>this.remove(i)}remove(t){const e=this.K.get(t.sig);e&&(e.delete(t),0===e.size&&this.K.delete(t.sig))}removeByCombo(t){const e=u(l(t));this.K.delete(e)}clear(){this.K.clear()}match(t,e){const s=f(t);if(!s.key)return[];const n=u(s),o=this.K.get(n);if(!o)return[];const i=[];for(const t of o)("keyup"!==e||t.opts.onKeyup)&&("keydown"===e&&t.opts.onKeyup||i.push(t));return i}list(){const t=[];for(const e of this.K.values())for(const s of e)t.push(s);return t}}const m={scope:"global",timeout:1e3};class y{K=new Set;S=[];add(t,e,s){const n={sigs:t.map(t=>u(l(t))),handler:e,opts:{...m,...s}};return this.K.add(n),()=>{this.K.delete(n)}}clear(){this.K.clear(),this.S=[]}feed(t,e){const s=f(t);if(!s.key)return[];const n=u(s),o=Date.now(),i=this.T();this.S=this.S.filter(t=>o-t.at<=i),this.S.push({sig:n,at:o});const r=[];for(const t of this.K){if(t.opts.scope!==e&&"global"!==t.opts.scope)continue;if(this.S.length<t.sigs.length)continue;const s=this.S.slice(-t.sigs.length);o-(s[0]?.at??0)<=t.opts.timeout&&(s.every((e,s)=>e.sig===t.sigs[s])&&r.push(t))}return r.length>0&&(this.S=[]),r}T(){let t=1/0;for(const e of this.K)e.opts.timeout<t&&(t=e.opts.timeout);return t===1/0?1e3:t}}const b={scope:"global",window:250};class g{K=new Set;M=new Map;add(t,e,s){const n={keys:new Set(t.map(a)),handler:e,opts:{...b,...s}};return this.K.add(n),()=>{this.K.delete(n)}}clear(){this.K.clear(),this.M.clear()}down(t,e){const s=a(t.key);this.M.has(s)||this.M.set(s,Date.now());const n=Date.now(),o=[];for(const t of this.K){if(t.opts.scope!==e&&"global"!==t.opts.scope)continue;if(t.keys.size!==this.M.size)continue;let s=!0,i=n;for(const e of t.keys){const t=this.M.get(e);if(void 0===t){s=!1;break}t<i&&(i=t)}s&&(n-i>t.opts.window||o.push(t))}return o}up(t){this.M.delete(a(t.key))}reset(){this.M.clear()}}const k={q:"'",w:",",e:".",r:"p",t:"y",y:"f",u:"g",i:"c",o:"r",p:"l",a:"a",s:"o",d:"e",f:"u",g:"i",h:"d",j:"h",k:"t",l:"n",z:";",x:"q",c:"j",v:"k",b:"x",n:"b",m:"m"},v={q:"q",w:"w",e:"f",r:"p",t:"g",y:"j",u:"l",i:"u",o:"y",p:";",a:"a",s:"r",d:"s",f:"t",g:"d",h:"h",j:"n",k:"e",l:"i",z:"z",x:"x",c:"c",v:"v",b:"b",n:"k",m:"m"};class _{D="qwerty";set(t){this.D=t}get name(){return this.D}translate(t){const e=t.toLowerCase();return"qwerty"===this.D?e:("dvorak"===this.D?k:v)[e]??e}}class K{A=[{name:"global",exclusive:!1}];get active(){return this.A[this.A.length-1]?.name??"global"}get isExclusive(){return this.A[this.A.length-1]?.exclusive??!1}set(t,e){this.A=[{name:t,exclusive:e?.exclusive??!1}]}push(t,e){return this.A.push({name:t,exclusive:e?.exclusive??!1}),()=>this.pop(t)}pop(t){if(!t)return void(this.A.length>1&&this.A.pop());const e=this.A.map(t=>t.name).lastIndexOf(t);e>0&&this.A.splice(e,1)}reset(){this.A=[{name:"global",exclusive:!1}]}matches(t){return t===this.active||"global"===t&&!this.isExclusive}}class x{D=!1;C=0;S=[];get recording(){return this.D}start(){this.S=[],this.C=Date.now(),this.D=!0}stop(){return this.D=!1,this.S.slice()}capture(t){if(!this.D)return;const e=f(t);e.key&&this.S.push({combo:e,label:d(e),at:Date.now()-this.C})}async replay(t,e=window,s=1){if(0===t.length)return;const n=t[0];if(!n)return;const o=n.at;for(const n of t){const t=Math.max(0,(n.at-o)/s);await new Promise(e=>setTimeout(e,t));const i=new KeyboardEvent("keydown",{key:n.combo.key,ctrlKey:n.combo.mods.has("control"),altKey:n.combo.mods.has("alt"),shiftKey:n.combo.mods.has("shift"),metaKey:n.combo.mods.has("meta"),bubbles:!0,cancelable:!0});e.dispatchEvent(i)}}}return new class{I=new t;P=new p;B=new y;L=new g;$=new _;H=new K;N=new x;R=null;U=null;W=null;X=null;F=!1;init(t){this.F||(this.R=t?.target??window,t?.scope&&this.H.set(t.scope),this.U=t=>this.G(t,"keydown"),this.W=t=>this.G(t,"keyup"),this.X=()=>this.L.reset(),this.R.addEventListener("keydown",this.U),this.R.addEventListener("keyup",this.W),window.addEventListener("blur",this.X),this.F=!0)}destroy(){this.F&&this.R&&(this.U&&this.R.removeEventListener("keydown",this.U),this.W&&this.R.removeEventListener("keyup",this.W),this.X&&window.removeEventListener("blur",this.X),this.F=!1)}on(t,e,s){return Array.isArray(t)?this.B.add(t,e,s):this.P.add(t,e,s)}off(t){this.P.removeByCombo(t)}chord(t,e,s){return this.L.add(t,e,s)}list(){return this.P.list().map(t=>({combo:d(t.combo),scope:t.opts.scope}))}clear(){this.P.clear(),this.B.clear(),this.L.clear()}scope(t,e){return t&&this.H.set(t,e),this.H.active}pushScope(t,e){return this.H.push(t,e)}popScope(t){this.H.pop(t)}layout(t){return t&&this.$.set(t),this.$.name}record(){this.N.start()}stop(){return this.N.stop()}async replay(t,e=1){if(0===t.length)return;const s=t[0];if(!s)return;const n=s.at,o=this.R??window;for(const s of t){const t=Math.max(0,(s.at-n)/e);await new Promise(e=>setTimeout(e,t)),this.I.emit("replay",s);const i=new KeyboardEvent("keydown",{key:s.combo.key,ctrlKey:s.combo.mods.has("control"),altKey:s.combo.mods.has("alt"),shiftKey:s.combo.mods.has("shift"),metaKey:s.combo.mods.has("meta"),bubbles:!0,cancelable:!0});o.dispatchEvent(i)}}onEvent(t,e){return this.I.on(t,e)}get platform(){return e}get isMac(){return s}get modKey(){return n}format(t){return d(l(t))}G(t,e){if("keydown"===e&&this.N.capture(t),"keyup"===e)return void this.L.up(t);this.I.emit("keydown",t);const s=this.J(t.target),n=this.L.down(t,this.H.active);for(const t of n)t.handler(),this.I.emit("chord",t);const o=this.P.match(t,e);let i=!1;for(const e of o)this.H.matches(e.opts.scope)&&(s&&!e.opts.allowInInput||(e.opts.preventDefault&&t.preventDefault(),e.handler(t),this.I.emit("shortcut",e),i=!0));const r=this.B.feed(t,this.H.active);for(const t of r)s||(t.handler(),this.I.emit("sequence",t),i=!0);i&&this.I.emit("handled",t)}J(t){if(!(t instanceof HTMLElement))return!1;const e=t.tagName;return"INPUT"===e||"TEXTAREA"===e||"SELECT"===e||t.isContentEditable}}});
2
+ //# sourceMappingURL=keyboard.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard.umd.js","sources":["../src/events/events.ts","../src/utils/platform.ts","../src/utils/keys.ts","../src/shortcuts/shortcuts.ts","../src/sequences/sequences.ts","../src/chords/chords.ts","../src/layouts/layouts.ts","../src/scope/scope.ts","../src/recorder/recorder.ts","../src/index.ts"],"sourcesContent":["type Listener = (...args: unknown[]) => void;\n\n/** Tiny pub/sub for KeyBoard's internal lifecycle events. */\nexport class KbEvents {\n private _map = new Map<string, Set<Listener>>();\n\n on(event: string, fn: Listener): () => void {\n let set = this._map.get(event);\n if (!set) {\n set = new Set();\n this._map.set(event, set);\n }\n set.add(fn);\n return () => this.off(event, fn);\n }\n\n off(event: string, fn?: Listener): void {\n if (!fn) {\n this._map.delete(event);\n return;\n }\n this._map.get(event)?.delete(fn);\n }\n\n emit(event: string, ...args: unknown[]): void {\n const set = this._map.get(event);\n if (!set) return;\n for (const fn of set) {\n try { fn(...args); } catch (e) { console.error('[keyboard.js]', e); }\n }\n }\n}\n","/**\n * Platform detection — runs once at module load.\n * Determines how `mod` maps to a physical modifier and how combos\n * are pretty-printed.\n */\n\nexport type Platform = 'mac' | 'windows' | 'linux' | 'other';\n\nfunction detect(): Platform {\n if (typeof navigator === 'undefined') return 'other';\n const ua = navigator.userAgent;\n const plat = (navigator.platform || '').toLowerCase();\n\n // Apple devices report \"MacIntel\", \"iPhone\", \"iPad\", \"iPod\".\n // On iPadOS 13+ the platform string is \"MacIntel\" but `maxTouchPoints > 1`.\n if (/mac|iphone|ipad|ipod/i.test(plat)) return 'mac';\n if (/mac/i.test(ua)) return 'mac';\n if (/win/i.test(plat) || /windows/i.test(ua)) return 'windows';\n if (/linux|x11/i.test(plat) || /linux/i.test(ua)) return 'linux';\n return 'other';\n}\n\nexport const PLATFORM: Platform = detect();\nexport const IS_MAC = PLATFORM === 'mac';\n\n/** The physical modifier that `mod` (a.k.a. \"CommandOrControl\") resolves to. */\nexport const MOD_MODIFIER: 'meta' | 'control' = IS_MAC ? 'meta' : 'control';\n\n/** Mac glyphs for the four modifiers. */\nexport const MAC_SYMBOLS: Record<string, string> = {\n control: '\\u2303', // ⌃\n alt: '\\u2325', // ⌥\n shift: '\\u21E7', // ⇧\n meta: '\\u2318', // ⌘\n};\n\n/** Human-readable labels for non-Mac platforms. */\nexport const PC_LABELS: Record<string, string> = {\n control: 'Ctrl',\n alt: 'Alt',\n shift: 'Shift',\n meta: 'Win',\n};\n\n/** Special-key glyphs on Mac (\"enter\" → ↵, etc.) */\nexport const MAC_KEY_SYMBOLS: Record<string, string> = {\n enter: '\\u21B5', // ↵\n backspace: '\\u232B', // ⌫\n delete: '\\u2326', // ⌦\n tab: '\\u21E5', // ⇥\n escape: 'esc',\n arrowup: '\\u2191',\n arrowdown: '\\u2193',\n arrowleft: '\\u2190',\n arrowright: '\\u2192',\n ' ': 'Space',\n};\n","/**\n * Key normalization utilities.\n * Converts between human-readable names (\"ctrl+k\", \"enter\") and the\n * underlying KeyboardEvent representation.\n */\n\nimport { MAC_KEY_SYMBOLS, MAC_SYMBOLS, MOD_MODIFIER, PC_LABELS, IS_MAC } from './platform';\n\nconst ALIAS: Record<string, string> = {\n ctrl: 'control',\n cmd: 'meta',\n command: 'meta',\n win: 'meta',\n windows: 'meta',\n super: 'meta',\n opt: 'alt',\n option: 'alt',\n return: 'enter',\n esc: 'escape',\n space: ' ',\n spacebar: ' ',\n plus: '+',\n up: 'arrowup',\n down: 'arrowdown',\n left: 'arrowleft',\n right: 'arrowright',\n del: 'delete',\n ins: 'insert',\n pgup: 'pageup',\n pgdn: 'pagedown',\n};\n\nconst MODIFIER_ORDER = ['control', 'alt', 'shift', 'meta'] as const;\nexport type Modifier = (typeof MODIFIER_ORDER)[number];\n\nexport interface ParsedCombo {\n key: string;\n mods: Set<Modifier>;\n}\n\n/** Normalize a single key token to its canonical form. */\nexport function normKey(raw: string): string {\n const k = raw.trim().toLowerCase();\n // `mod` is the cross-platform modifier — Cmd on Mac, Ctrl elsewhere.\n // VSCode, CodeMirror, and Electron use the same convention.\n if (k === 'mod' || k === 'commandorcontrol' || k === 'cmdorctrl') {\n return MOD_MODIFIER;\n }\n return ALIAS[k] ?? k;\n}\n\n/** Parse \"ctrl+shift+k\" into a ParsedCombo. */\nexport function parseCombo(input: string): ParsedCombo {\n const parts = input\n .split('+')\n .map((p) => p.trim())\n .filter((p) => p.length > 0);\n\n if (parts.length === 0) {\n return { key: '', mods: new Set() };\n }\n\n const mods = new Set<Modifier>();\n let key = '';\n\n for (const raw of parts) {\n const n = normKey(raw);\n if (MODIFIER_ORDER.includes(n as Modifier)) {\n mods.add(n as Modifier);\n } else {\n key = n;\n }\n }\n\n return { key, mods };\n}\n\n/** Build a comparable signature: \"control+shift|k\" */\nexport function comboSig(combo: ParsedCombo): string {\n const mods = MODIFIER_ORDER.filter((m) => combo.mods.has(m)).join('+');\n return `${mods}|${combo.key}`;\n}\n\n/** Extract a ParsedCombo from a live KeyboardEvent. */\nexport function eventCombo(ev: KeyboardEvent): ParsedCombo {\n const mods = new Set<Modifier>();\n if (ev.ctrlKey) mods.add('control');\n if (ev.altKey) mods.add('alt');\n if (ev.shiftKey) mods.add('shift');\n if (ev.metaKey) mods.add('meta');\n\n let key = ev.key.toLowerCase();\n // Strip modifier self-reports so \"shift\" alone doesn't clash with \"shift+a\"\n if (MODIFIER_ORDER.includes(key as Modifier)) key = '';\n\n return { key, mods };\n}\n\n/**\n * Pretty-print a combo for display.\n * Mac: uses glyphs with no separator (\"⌘K\").\n * PC: uses words with \" + \" separator (\"Ctrl + K\").\n */\nexport function formatCombo(combo: ParsedCombo): string {\n const parts: string[] = [];\n const labels = IS_MAC ? MAC_SYMBOLS : PC_LABELS;\n for (const m of MODIFIER_ORDER) {\n if (combo.mods.has(m)) parts.push(labels[m] ?? m);\n }\n if (combo.key) {\n const special = IS_MAC ? MAC_KEY_SYMBOLS[combo.key] : undefined;\n parts.push(special ?? (combo.key.length === 1 ? combo.key.toUpperCase() : combo.key));\n }\n return parts.join(IS_MAC ? '' : ' + ');\n}\n","import { comboSig, eventCombo, parseCombo } from '../utils/keys';\nimport type { ParsedCombo } from '../utils/keys';\n\nexport interface ShortcutOptions {\n /** Scope name this binding belongs to — only fires when scope is active */\n scope?: string;\n /** Prevent default browser behaviour when matched (default: true) */\n preventDefault?: boolean;\n /** Fire even when typing in input/textarea/contenteditable (default: false) */\n allowInInput?: boolean;\n /** Fire on keyup instead of keydown */\n onKeyup?: boolean;\n}\n\nexport interface ShortcutBinding {\n combo: ParsedCombo;\n sig: string;\n handler: (e: KeyboardEvent) => void;\n opts: Required<Omit<ShortcutOptions, 'scope'>> & { scope: string };\n}\n\nexport interface ShortcutMatch {\n binding: ShortcutBinding;\n}\n\nconst DEFAULT_OPTS: Required<Omit<ShortcutOptions, 'scope'>> & { scope: string } = {\n scope: 'global',\n preventDefault: true,\n allowInInput: false,\n onKeyup: false,\n};\n\nexport class Shortcuts {\n private _bindings = new Map<string, Set<ShortcutBinding>>();\n\n add(combo: string, handler: (e: KeyboardEvent) => void, opts?: ShortcutOptions): () => void {\n const parsed = parseCombo(combo);\n const sig = comboSig(parsed);\n const binding: ShortcutBinding = {\n combo: parsed,\n sig,\n handler,\n opts: { ...DEFAULT_OPTS, ...opts, scope: opts?.scope ?? 'global' },\n };\n let set = this._bindings.get(sig);\n if (!set) {\n set = new Set();\n this._bindings.set(sig, set);\n }\n set.add(binding);\n return () => this.remove(binding);\n }\n\n remove(binding: ShortcutBinding): void {\n const set = this._bindings.get(binding.sig);\n if (!set) return;\n set.delete(binding);\n if (set.size === 0) this._bindings.delete(binding.sig);\n }\n\n removeByCombo(combo: string): void {\n const sig = comboSig(parseCombo(combo));\n this._bindings.delete(sig);\n }\n\n clear(): void {\n this._bindings.clear();\n }\n\n /** Find all matching bindings for a key event — caller filters by scope. */\n match(ev: KeyboardEvent, type: 'keydown' | 'keyup'): ShortcutBinding[] {\n const combo = eventCombo(ev);\n if (!combo.key) return [];\n const sig = comboSig(combo);\n const set = this._bindings.get(sig);\n if (!set) return [];\n const out: ShortcutBinding[] = [];\n for (const b of set) {\n if (type === 'keyup' && !b.opts.onKeyup) continue;\n if (type === 'keydown' && b.opts.onKeyup) continue;\n out.push(b);\n }\n return out;\n }\n\n list(): ShortcutBinding[] {\n const out: ShortcutBinding[] = [];\n for (const set of this._bindings.values()) {\n for (const b of set) out.push(b);\n }\n return out;\n }\n}\n","import { comboSig, eventCombo, parseCombo } from '../utils/keys';\n\nexport interface SequenceOptions {\n scope?: string;\n /** Reset timeout in ms between keys (default: 1000) */\n timeout?: number;\n}\n\nexport interface SequenceBinding {\n sigs: string[];\n handler: () => void;\n opts: Required<SequenceOptions>;\n}\n\nconst DEFAULT_OPTS: Required<SequenceOptions> = {\n scope: 'global',\n timeout: 1000,\n};\n\n/**\n * Detects ordered key sequences like `g g` or `ctrl+k ctrl+s`.\n * Each token in the input array represents one step of the sequence.\n */\nexport class Sequences {\n private _bindings = new Set<SequenceBinding>();\n private _buffer: { sig: string; at: number }[] = [];\n\n add(sequence: string[], handler: () => void, opts?: SequenceOptions): () => void {\n const sigs = sequence.map((s) => comboSig(parseCombo(s)));\n const binding: SequenceBinding = {\n sigs,\n handler,\n opts: { ...DEFAULT_OPTS, ...opts },\n };\n this._bindings.add(binding);\n return () => { this._bindings.delete(binding); };\n }\n\n clear(): void {\n this._bindings.clear();\n this._buffer = [];\n }\n\n /** Feed a key event — returns list of sequences that fired. */\n feed(ev: KeyboardEvent, activeScope: string): SequenceBinding[] {\n const combo = eventCombo(ev);\n if (!combo.key) return [];\n const sig = comboSig(combo);\n const now = Date.now();\n\n // Prune expired entries from buffer (using the shortest timeout among bindings)\n const minTimeout = this._minTimeout();\n this._buffer = this._buffer.filter((entry) => now - entry.at <= minTimeout);\n\n this._buffer.push({ sig, at: now });\n\n const fired: SequenceBinding[] = [];\n for (const b of this._bindings) {\n if (b.opts.scope !== activeScope && b.opts.scope !== 'global') continue;\n if (this._buffer.length < b.sigs.length) continue;\n const tail = this._buffer.slice(-b.sigs.length);\n const within = now - (tail[0]?.at ?? 0) <= b.opts.timeout;\n if (!within) continue;\n const matches = tail.every((entry, i) => entry.sig === b.sigs[i]);\n if (matches) fired.push(b);\n }\n\n if (fired.length > 0) this._buffer = [];\n return fired;\n }\n\n private _minTimeout(): number {\n let min = Infinity;\n for (const b of this._bindings) {\n if (b.opts.timeout < min) min = b.opts.timeout;\n }\n return min === Infinity ? 1000 : min;\n }\n}\n","import { normKey } from '../utils/keys';\n\nexport interface ChordOptions {\n scope?: string;\n /** Max time between first and last key press in ms (default: 250) */\n window?: number;\n}\n\nexport interface ChordBinding {\n keys: Set<string>;\n handler: () => void;\n opts: Required<ChordOptions>;\n}\n\nconst DEFAULT_OPTS: Required<ChordOptions> = {\n scope: 'global',\n window: 250,\n};\n\n/**\n * Detects simultaneous key presses (e.g. \"j+k\" pressed together).\n * Differs from shortcuts in that it has no modifier constraint — any\n * combination of plain keys held within a short window counts.\n */\nexport class Chords {\n private _bindings = new Set<ChordBinding>();\n private _held = new Map<string, number>();\n\n add(keys: string[], handler: () => void, opts?: ChordOptions): () => void {\n const binding: ChordBinding = {\n keys: new Set(keys.map(normKey)),\n handler,\n opts: { ...DEFAULT_OPTS, ...opts },\n };\n this._bindings.add(binding);\n return () => { this._bindings.delete(binding); };\n }\n\n clear(): void {\n this._bindings.clear();\n this._held.clear();\n }\n\n down(ev: KeyboardEvent, activeScope: string): ChordBinding[] {\n const key = normKey(ev.key);\n if (!this._held.has(key)) this._held.set(key, Date.now());\n\n const now = Date.now();\n const fired: ChordBinding[] = [];\n\n for (const b of this._bindings) {\n if (b.opts.scope !== activeScope && b.opts.scope !== 'global') continue;\n if (b.keys.size !== this._held.size) continue;\n\n let allMatch = true;\n let earliest = now;\n for (const k of b.keys) {\n const t = this._held.get(k);\n if (t === undefined) { allMatch = false; break; }\n if (t < earliest) earliest = t;\n }\n if (!allMatch) continue;\n if (now - earliest > b.opts.window) continue;\n fired.push(b);\n }\n return fired;\n }\n\n up(ev: KeyboardEvent): void {\n this._held.delete(normKey(ev.key));\n }\n\n reset(): void {\n this._held.clear();\n }\n}\n","export type LayoutName = 'qwerty' | 'dvorak' | 'colemak';\n\n/** Mapping from QWERTY physical key → Dvorak character. */\nconst DVORAK: Record<string, string> = {\n q: \"'\", w: ',', e: '.', r: 'p', t: 'y', y: 'f', u: 'g', i: 'c', o: 'r', p: 'l',\n a: 'a', s: 'o', d: 'e', f: 'u', g: 'i', h: 'd', j: 'h', k: 't', l: 'n',\n z: ';', x: 'q', c: 'j', v: 'k', b: 'x', n: 'b', m: 'm',\n};\n\nconst COLEMAK: Record<string, string> = {\n q: 'q', w: 'w', e: 'f', r: 'p', t: 'g', y: 'j', u: 'l', i: 'u', o: 'y', p: ';',\n a: 'a', s: 'r', d: 's', f: 't', g: 'd', h: 'h', j: 'n', k: 'e', l: 'i',\n z: 'z', x: 'x', c: 'c', v: 'v', b: 'b', n: 'k', m: 'm',\n};\n\n/**\n * Layout remapper — translates a user's physical key press into the\n * character they *intend* to produce under a different layout.\n * Useful when letting users define shortcuts that survive layout changes.\n */\nexport class Layouts {\n private _active: LayoutName = 'qwerty';\n\n set(layout: LayoutName): void {\n this._active = layout;\n }\n\n get name(): LayoutName {\n return this._active;\n }\n\n /** Translate a key character from QWERTY-physical to the active layout. */\n translate(key: string): string {\n const k = key.toLowerCase();\n if (this._active === 'qwerty') return k;\n const map = this._active === 'dvorak' ? DVORAK : COLEMAK;\n return map[k] ?? k;\n }\n}\n","interface Frame {\n name: string;\n exclusive: boolean;\n}\n\n/**\n * Scope manager — lets shortcuts be grouped by context (e.g. \"editor\",\n * \"modal\", \"global\").\n *\n * Normally, bindings with `scope: 'global'` always fire and scope-specific\n * bindings only fire when that scope is active. An `exclusive` scope\n * suppresses global bindings while active — useful for modals where\n * underlying shortcuts should stop firing.\n */\nexport class Scope {\n private _stack: Frame[] = [{ name: 'global', exclusive: false }];\n\n get active(): string {\n return this._stack[this._stack.length - 1]?.name ?? 'global';\n }\n\n get isExclusive(): boolean {\n return this._stack[this._stack.length - 1]?.exclusive ?? false;\n }\n\n /** Replace the current scope. */\n set(name: string, opts?: { exclusive?: boolean }): void {\n this._stack = [{ name, exclusive: opts?.exclusive ?? false }];\n }\n\n /** Push a temporary scope — useful for modals. */\n push(name: string, opts?: { exclusive?: boolean }): () => void {\n const frame: Frame = { name, exclusive: opts?.exclusive ?? false };\n this._stack.push(frame);\n return () => this.pop(name);\n }\n\n /** Pop either the specific scope or the topmost. */\n pop(name?: string): void {\n if (!name) {\n if (this._stack.length > 1) this._stack.pop();\n return;\n }\n const idx = this._stack.map((f) => f.name).lastIndexOf(name);\n if (idx > 0) this._stack.splice(idx, 1);\n }\n\n reset(): void {\n this._stack = [{ name: 'global', exclusive: false }];\n }\n\n matches(scope: string): boolean {\n const active = this.active;\n if (scope === active) return true;\n if (scope === 'global') return !this.isExclusive;\n return false;\n }\n}\n","import { eventCombo, formatCombo } from '../utils/keys';\nimport type { ParsedCombo } from '../utils/keys';\n\nexport interface RecordedKey {\n combo: ParsedCombo;\n label: string;\n /** ms offset from start of recording */\n at: number;\n}\n\n/**\n * Records and replays key events.\n * `replay` dispatches synthetic KeyboardEvents onto a target (default: window)\n * preserving the original inter-key timing.\n */\nexport class Recorder {\n private _active = false;\n private _start = 0;\n private _buffer: RecordedKey[] = [];\n\n get recording(): boolean {\n return this._active;\n }\n\n start(): void {\n this._buffer = [];\n this._start = Date.now();\n this._active = true;\n }\n\n stop(): RecordedKey[] {\n this._active = false;\n return this._buffer.slice();\n }\n\n capture(ev: KeyboardEvent): void {\n if (!this._active) return;\n const combo = eventCombo(ev);\n if (!combo.key) return;\n this._buffer.push({\n combo,\n label: formatCombo(combo),\n at: Date.now() - this._start,\n });\n }\n\n async replay(\n recording: RecordedKey[],\n target: EventTarget = window,\n speed = 1,\n ): Promise<void> {\n if (recording.length === 0) return;\n const first = recording[0];\n if (!first) return;\n const baseOffset = first.at;\n\n for (const entry of recording) {\n const wait = Math.max(0, (entry.at - baseOffset) / speed);\n await new Promise((r) => setTimeout(r, wait));\n const ev = new KeyboardEvent('keydown', {\n key: entry.combo.key,\n ctrlKey: entry.combo.mods.has('control'),\n altKey: entry.combo.mods.has('alt'),\n shiftKey: entry.combo.mods.has('shift'),\n metaKey: entry.combo.mods.has('meta'),\n bubbles: true,\n cancelable: true,\n });\n target.dispatchEvent(ev);\n }\n }\n}\n","import { KbEvents } from './events/events';\nimport { Shortcuts } from './shortcuts/shortcuts';\nimport type { ShortcutOptions } from './shortcuts/shortcuts';\nimport { Sequences } from './sequences/sequences';\nimport type { SequenceOptions } from './sequences/sequences';\nimport { Chords } from './chords/chords';\nimport type { ChordOptions } from './chords/chords';\nimport { Layouts } from './layouts/layouts';\nimport type { LayoutName } from './layouts/layouts';\nimport { Scope } from './scope/scope';\nimport { Recorder } from './recorder/recorder';\nimport type { RecordedKey } from './recorder/recorder';\nimport { formatCombo, parseCombo } from './utils/keys';\nimport { PLATFORM, IS_MAC, MOD_MODIFIER } from './utils/platform';\nimport type { Platform } from './utils/platform';\n\nexport interface InitOptions {\n /** EventTarget to attach listeners to (default: window) */\n target?: EventTarget;\n /** Default scope name when starting (default: \"global\") */\n scope?: string;\n}\n\nclass Keyboard {\n private _ev = new KbEvents();\n private _shortcuts = new Shortcuts();\n private _sequences = new Sequences();\n private _chords = new Chords();\n private _layouts = new Layouts();\n private _scope = new Scope();\n private _recorder = new Recorder();\n\n private _target: EventTarget | null = null;\n private _onKeydown: ((e: Event) => void) | null = null;\n private _onKeyup: ((e: Event) => void) | null = null;\n private _onBlur: (() => void) | null = null;\n private _started = false;\n\n // --- lifecycle ---\n\n init(opts?: InitOptions): void {\n if (this._started) return;\n this._target = opts?.target ?? window;\n if (opts?.scope) this._scope.set(opts.scope);\n\n this._onKeydown = (ev) => this._handleKey(ev as KeyboardEvent, 'keydown');\n this._onKeyup = (ev) => this._handleKey(ev as KeyboardEvent, 'keyup');\n this._onBlur = () => this._chords.reset();\n\n this._target.addEventListener('keydown', this._onKeydown);\n this._target.addEventListener('keyup', this._onKeyup);\n window.addEventListener('blur', this._onBlur);\n this._started = true;\n }\n\n destroy(): void {\n if (!this._started || !this._target) return;\n if (this._onKeydown) this._target.removeEventListener('keydown', this._onKeydown);\n if (this._onKeyup) this._target.removeEventListener('keyup', this._onKeyup);\n if (this._onBlur) window.removeEventListener('blur', this._onBlur);\n this._started = false;\n }\n\n // --- shortcut API ---\n\n /** Register a hotkey. Accepts a single combo string or an array for sequences. */\n on(\n combo: string,\n handler: (e: KeyboardEvent) => void,\n opts?: ShortcutOptions,\n ): () => void;\n on(\n sequence: string[],\n handler: () => void,\n opts?: SequenceOptions,\n ): () => void;\n on(\n combo: string | string[],\n handler: ((e: KeyboardEvent) => void) | (() => void),\n opts?: ShortcutOptions | SequenceOptions,\n ): () => void {\n if (Array.isArray(combo)) {\n return this._sequences.add(combo, handler as () => void, opts as SequenceOptions);\n }\n return this._shortcuts.add(combo, handler as (e: KeyboardEvent) => void, opts);\n }\n\n /** Remove all bindings for a combo. */\n off(combo: string): void {\n this._shortcuts.removeByCombo(combo);\n }\n\n /** Register a chord — keys held simultaneously. */\n chord(keys: string[], handler: () => void, opts?: ChordOptions): () => void {\n return this._chords.add(keys, handler, opts);\n }\n\n /** List every registered shortcut — useful for building help dialogs. */\n list(): Array<{ combo: string; scope: string }> {\n return this._shortcuts.list().map((b) => ({\n combo: formatCombo(b.combo),\n scope: b.opts.scope,\n }));\n }\n\n clear(): void {\n this._shortcuts.clear();\n this._sequences.clear();\n this._chords.clear();\n }\n\n // --- scope ---\n\n scope(name?: string, opts?: { exclusive?: boolean }): string {\n if (name) this._scope.set(name, opts);\n return this._scope.active;\n }\n\n /**\n * Push a scope onto the stack. When `exclusive: true`, global bindings\n * are suppressed while this scope is active — ideal for modals.\n */\n pushScope(name: string, opts?: { exclusive?: boolean }): () => void {\n return this._scope.push(name, opts);\n }\n\n popScope(name?: string): void {\n this._scope.pop(name);\n }\n\n // --- layout ---\n\n layout(name?: LayoutName): LayoutName {\n if (name) this._layouts.set(name);\n return this._layouts.name;\n }\n\n // --- recorder ---\n\n record(): void { this._recorder.start(); }\n stop(): RecordedKey[] { return this._recorder.stop(); }\n\n /**\n * Replay a recording — dispatches synthetic KeyboardEvents at the original\n * inter-key timing (or faster with `speed`). Emits a 'replay' event per key.\n */\n async replay(recording: RecordedKey[], speed = 1): Promise<void> {\n if (recording.length === 0) return;\n const first = recording[0];\n if (!first) return;\n const baseOffset = first.at;\n const target = this._target ?? window;\n\n for (const entry of recording) {\n const wait = Math.max(0, (entry.at - baseOffset) / speed);\n await new Promise((r) => setTimeout(r, wait));\n this._ev.emit('replay', entry);\n const ev = new KeyboardEvent('keydown', {\n key: entry.combo.key,\n ctrlKey: entry.combo.mods.has('control'),\n altKey: entry.combo.mods.has('alt'),\n shiftKey: entry.combo.mods.has('shift'),\n metaKey: entry.combo.mods.has('meta'),\n bubbles: true,\n cancelable: true,\n });\n target.dispatchEvent(ev);\n }\n }\n\n // --- events (library-internal lifecycle) ---\n\n onEvent(event: string, fn: (...args: unknown[]) => void): () => void {\n return this._ev.on(event, fn);\n }\n\n // --- platform ---\n\n /** Detected platform: 'mac' | 'windows' | 'linux' | 'other'. */\n get platform(): Platform { return PLATFORM; }\n\n /** True on macOS / iPadOS / iOS. */\n get isMac(): boolean { return IS_MAC; }\n\n /**\n * The physical modifier that `mod` resolves to on this platform.\n * 'meta' on Mac, 'control' on Windows/Linux.\n * Use when building help UI: `Kb.modKey // \"meta\" | \"control\"`.\n */\n get modKey(): 'meta' | 'control' { return MOD_MODIFIER; }\n\n // --- helpers ---\n\n format(combo: string): string {\n return formatCombo(parseCombo(combo));\n }\n\n // --- internal ---\n\n private _handleKey(ev: KeyboardEvent, type: 'keydown' | 'keyup'): void {\n if (type === 'keydown') this._recorder.capture(ev);\n if (type === 'keyup') { this._chords.up(ev); return; }\n\n // Raw stream — fires for every keydown even if no binding matches\n this._ev.emit('keydown', ev);\n\n const inInput = this._isEditable(ev.target);\n\n // 1. Chord detection (simultaneous keys)\n const chords = this._chords.down(ev, this._scope.active);\n for (const c of chords) {\n c.handler();\n this._ev.emit('chord', c);\n }\n\n // 2. Shortcut matching\n const matches = this._shortcuts.match(ev, type);\n let handled = false;\n for (const b of matches) {\n if (!this._scope.matches(b.opts.scope)) continue;\n if (inInput && !b.opts.allowInInput) continue;\n if (b.opts.preventDefault) ev.preventDefault();\n b.handler(ev);\n this._ev.emit('shortcut', b);\n handled = true;\n }\n\n // 3. Sequence detection — independent of shortcut matching\n const fired = this._sequences.feed(ev, this._scope.active);\n for (const s of fired) {\n if (inInput) continue;\n s.handler();\n this._ev.emit('sequence', s);\n handled = true;\n }\n\n if (handled) this._ev.emit('handled', ev);\n }\n\n private _isEditable(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n const tag = target.tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return true;\n return target.isContentEditable;\n }\n}\n\nconst keyboard = new Keyboard();\nexport default keyboard;\n\nexport type {\n ShortcutOptions, SequenceOptions, ChordOptions,\n LayoutName, RecordedKey, Platform,\n};\n"],"names":["KbEvents","_map","Map","on","event","fn","set","this","get","Set","add","off","delete","emit","args","e","console","error","PLATFORM","navigator","ua","userAgent","plat","platform","toLowerCase","test","detect","IS_MAC","MOD_MODIFIER","MAC_SYMBOLS","control","alt","shift","meta","PC_LABELS","MAC_KEY_SYMBOLS","enter","backspace","tab","escape","arrowup","arrowdown","arrowleft","arrowright","ALIAS","ctrl","cmd","command","win","windows","super","opt","option","return","esc","space","spacebar","plus","up","down","left","right","del","ins","pgup","pgdn","MODIFIER_ORDER","normKey","raw","k","trim","parseCombo","input","parts","split","map","p","filter","length","key","mods","n","includes","comboSig","combo","m","has","join","eventCombo","ev","ctrlKey","altKey","shiftKey","metaKey","formatCombo","labels","push","undefined","toUpperCase","DEFAULT_OPTS","scope","preventDefault","allowInInput","onKeyup","Shortcuts","_bindings","handler","opts","parsed","sig","binding","remove","size","removeByCombo","clear","match","type","out","b","list","values","timeout","Sequences","_buffer","sequence","sigs","s","feed","activeScope","now","Date","minTimeout","_minTimeout","entry","at","fired","tail","slice","every","i","min","Infinity","window","Chords","_held","keys","allMatch","earliest","t","reset","DVORAK","q","w","r","y","u","o","a","d","f","g","h","j","l","z","x","c","v","COLEMAK","Layouts","_active","layout","name","translate","Scope","_stack","exclusive","active","isExclusive","pop","idx","lastIndexOf","splice","matches","Recorder","_start","recording","start","stop","capture","label","replay","target","speed","first","baseOffset","wait","Math","max","Promise","setTimeout","KeyboardEvent","bubbles","cancelable","dispatchEvent","_ev","_shortcuts","_sequences","_chords","_layouts","_scope","_recorder","_target","_onKeydown","_onKeyup","_onBlur","_started","init","_handleKey","addEventListener","destroy","removeEventListener","Array","isArray","chord","pushScope","popScope","record","onEvent","isMac","modKey","format","inInput","_isEditable","chords","handled","HTMLElement","tag","tagName","isContentEditable"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@buildwithdarsh/keyboardjs",
3
+ "version": "1.0.0",
4
+ "description": "A tiny, modular keyboard input library — hotkeys, sequences, chords, scopes, layouts, recorder",
5
+ "main": "dist/keyboard.umd.js",
6
+ "module": "dist/keyboard.esm.js",
7
+ "types": "dist/keyboard.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/keyboard.d.ts",
11
+ "import": "./dist/keyboard.esm.js",
12
+ "require": "./dist/keyboard.umd.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/keyboard.umd.js",
17
+ "dist/keyboard.umd.js.map",
18
+ "dist/keyboard.esm.js",
19
+ "dist/keyboard.esm.js.map",
20
+ "dist/keyboard.d.ts"
21
+ ],
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "scripts": {
26
+ "build": "rollup -c && rm -rf example/dist && cp -r dist example/dist",
27
+ "typecheck": "tsc --noEmit",
28
+ "dev": "rollup -c -w"
29
+ },
30
+ "keywords": [
31
+ "keyboard",
32
+ "hotkeys",
33
+ "shortcuts",
34
+ "keybindings",
35
+ "sequences",
36
+ "chords",
37
+ "layouts"
38
+ ],
39
+ "author": "Darsh Gupta",
40
+ "license": "MIT",
41
+ "type": "module",
42
+ "devDependencies": {
43
+ "@rollup/plugin-terser": "^1.0.0",
44
+ "@rollup/plugin-typescript": "^12.1.2",
45
+ "rollup": "^4.34.8",
46
+ "rollup-plugin-dts": "^6.1.1",
47
+ "tslib": "^2.8.1",
48
+ "typescript": "^5.7.3"
49
+ }
50
+ }