@vielzeug/keymap 3.0.0 → 26.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/_dev.cjs CHANGED
@@ -1,2 +1,2 @@
1
- function e(e){}exports.warn=e;
1
+ function warn(e){}exports.warn=warn;
2
2
  //# sourceMappingURL=_dev.cjs.map
package/dist/_dev.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __KEYMAP_PROD__?: boolean }).__KEYMAP_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/keymap] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,EAAK,EAAmB,CAExC"}
1
+ {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __KEYMAP_PROD__?: boolean }).__KEYMAP_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/keymap] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,KAAK,EAAmB,CAExC"}
package/dist/_dev.js CHANGED
@@ -1,6 +1,2 @@
1
- //#region src/_dev.ts
2
- function e(e) {}
3
- //#endregion
4
- export { e as warn };
5
-
1
+ function warn(e){}export{warn};
6
2
  //# sourceMappingURL=_dev.js.map
package/dist/_dev.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __KEYMAP_PROD__?: boolean }).__KEYMAP_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/keymap] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":";AAGA,SAAgB,EAAK,GAAmB,CAExC"}
1
+ {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __KEYMAP_PROD__?: boolean }).__KEYMAP_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/keymap] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n"],"mappings":"AAGA,SAAgB,KAAK,EAAmB,CAExC"}
@@ -1,2 +1,2 @@
1
- const e=require("./parser.cjs");function t(t,n){return t.length>n.length?!1:e.canonicalizeShortcut(t)===e.canonicalizeShortcut(n.slice(0,t.length))}function n(n,r,i={}){let{modKey:a,trigger:o=`keydown`}=i;if(n.trim()===``)return[];let s=e.parseShortcut(n,a);return r.filter(e=>e.trigger===o&&(t(s,e.shortcut)||t(e.shortcut,s)))}exports.findShortcutConflicts=n;
1
+ const e=require("./parser.cjs");function isPrefixOf(t,n){return t.length>n.length?!1:e.canonicalizeShortcut(t)===e.canonicalizeShortcut(n.slice(0,t.length))}function findShortcutConflicts(t,n,r={}){let{modKey:i,trigger:a=`keydown`}=r;if(t.trim()===``)return[];let o=e.parseShortcut(t,i);return n.filter(e=>e.trigger===a&&(isPrefixOf(o,e.shortcut)||isPrefixOf(e.shortcut,o)))}exports.findShortcutConflicts=findShortcutConflicts;
2
2
  //# sourceMappingURL=conflicts.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflicts.cjs","names":[],"sources":["../src/conflicts.ts"],"sourcesContent":["import type { ShortcutStep } from './parser';\nimport { canonicalizeShortcut, parseShortcut } from './parser';\nimport type { BindingEntry } from './types';\n\nexport interface ConflictOptions {\n modKey?: 'ctrl' | 'meta';\n trigger?: 'keydown' | 'keyup';\n}\n\nfunction isPrefixOf(shorter: readonly ShortcutStep[], longer: readonly ShortcutStep[]): boolean {\n if (shorter.length > longer.length) return false;\n\n return canonicalizeShortcut(shorter) === canonicalizeShortcut(longer.slice(0, shorter.length));\n}\n\n/**\n * Finds registered bindings that would conflict with a proposed shortcut — an exact duplicate, a\n * shorter binding that would shadow it as a chord prefix, or a longer binding it would itself\n * shadow. Only compares against entries sharing the same `trigger` — `'keydown'` and `'keyup'`\n * chords are matched independently and never conflict with each other.\n *\n * @example\n * const entries = map.listBindings();\n * findShortcutConflicts('g g', entries); // → the entry for 'g', if one is bound\n */\nexport function findShortcutConflicts(\n shortcut: string,\n entries: readonly BindingEntry[],\n options: ConflictOptions = {},\n): BindingEntry[] {\n const { modKey, trigger = 'keydown' } = options;\n\n // An empty/whitespace-only shortcut would be a trivial prefix of every entry — never report it\n // as a conflict.\n if (shortcut.trim() === '') return [];\n\n const steps = parseShortcut(shortcut, modKey);\n\n return entries.filter(\n (entry) => entry.trigger === trigger && (isPrefixOf(steps, entry.shortcut) || isPrefixOf(entry.shortcut, steps)),\n );\n}\n"],"mappings":"gCASA,SAAS,EAAW,EAAkC,EAA0C,CAG9F,OAFI,EAAQ,OAAS,EAAO,OAAe,GAEpC,EAAA,qBAAqB,CAAO,IAAM,EAAA,qBAAqB,EAAO,MAAM,EAAG,EAAQ,MAAM,CAAC,CAC/F,CAYA,SAAgB,EACd,EACA,EACA,EAA2B,CAAC,EACZ,CAChB,GAAM,CAAE,SAAQ,UAAU,WAAc,EAIxC,GAAI,EAAS,KAAK,IAAM,GAAI,MAAO,CAAC,EAEpC,IAAM,EAAQ,EAAA,cAAc,EAAU,CAAM,EAE5C,OAAO,EAAQ,OACZ,GAAU,EAAM,UAAY,IAAY,EAAW,EAAO,EAAM,QAAQ,GAAK,EAAW,EAAM,SAAU,CAAK,EAChH,CACF"}
1
+ {"version":3,"file":"conflicts.cjs","names":[],"sources":["../src/conflicts.ts"],"sourcesContent":["import type { ShortcutStep } from './parser';\nimport { canonicalizeShortcut, parseShortcut } from './parser';\nimport type { BindingEntry } from './types';\n\nexport interface ConflictOptions {\n modKey?: 'ctrl' | 'meta';\n trigger?: 'keydown' | 'keyup';\n}\n\nfunction isPrefixOf(shorter: readonly ShortcutStep[], longer: readonly ShortcutStep[]): boolean {\n if (shorter.length > longer.length) return false;\n\n return canonicalizeShortcut(shorter) === canonicalizeShortcut(longer.slice(0, shorter.length));\n}\n\n/**\n * Finds registered bindings that would conflict with a proposed shortcut — an exact duplicate, a\n * shorter binding that would shadow it as a chord prefix, or a longer binding it would itself\n * shadow. Only compares against entries sharing the same `trigger` — `'keydown'` and `'keyup'`\n * chords are matched independently and never conflict with each other.\n *\n * @example\n * const entries = map.listBindings();\n * findShortcutConflicts('g g', entries); // → the entry for 'g', if one is bound\n */\nexport function findShortcutConflicts(\n shortcut: string,\n entries: readonly BindingEntry[],\n options: ConflictOptions = {},\n): BindingEntry[] {\n const { modKey, trigger = 'keydown' } = options;\n\n // An empty/whitespace-only shortcut would be a trivial prefix of every entry — never report it\n // as a conflict.\n if (shortcut.trim() === '') return [];\n\n const steps = parseShortcut(shortcut, modKey);\n\n return entries.filter(\n (entry) => entry.trigger === trigger && (isPrefixOf(steps, entry.shortcut) || isPrefixOf(entry.shortcut, steps)),\n );\n}\n"],"mappings":"gCASA,SAAS,WAAW,EAAkC,EAA0C,CAG9F,OAFI,EAAQ,OAAS,EAAO,OAAe,GAEpC,EAAA,qBAAqB,CAAO,IAAM,EAAA,qBAAqB,EAAO,MAAM,EAAG,EAAQ,MAAM,CAAC,CAC/F,CAYA,SAAgB,sBACd,EACA,EACA,EAA2B,CAAC,EACZ,CAChB,GAAM,CAAE,SAAQ,UAAU,WAAc,EAIxC,GAAI,EAAS,KAAK,IAAM,GAAI,MAAO,CAAC,EAEpC,IAAM,EAAQ,EAAA,cAAc,EAAU,CAAM,EAE5C,OAAO,EAAQ,OACZ,GAAU,EAAM,UAAY,IAAY,WAAW,EAAO,EAAM,QAAQ,GAAK,WAAW,EAAM,SAAU,CAAK,EAChH,CACF"}
package/dist/conflicts.js CHANGED
@@ -1,15 +1,2 @@
1
- import { canonicalizeShortcut as e, parseShortcut as t } from "./parser.js";
2
- //#region src/conflicts.ts
3
- function n(t, n) {
4
- return t.length > n.length ? !1 : e(t) === e(n.slice(0, t.length));
5
- }
6
- function r(e, r, i = {}) {
7
- let { modKey: a, trigger: o = "keydown" } = i;
8
- if (e.trim() === "") return [];
9
- let s = t(e, a);
10
- return r.filter((e) => e.trigger === o && (n(s, e.shortcut) || n(e.shortcut, s)));
11
- }
12
- //#endregion
13
- export { r as findShortcutConflicts };
14
-
1
+ import{canonicalizeShortcut as e,parseShortcut as t}from"./parser.js";function isPrefixOf(t,n){return t.length>n.length?!1:e(t)===e(n.slice(0,t.length))}function findShortcutConflicts(e,n,r={}){let{modKey:i,trigger:a=`keydown`}=r;if(e.trim()===``)return[];let o=t(e,i);return n.filter(e=>e.trigger===a&&(isPrefixOf(o,e.shortcut)||isPrefixOf(e.shortcut,o)))}export{findShortcutConflicts};
15
2
  //# sourceMappingURL=conflicts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conflicts.js","names":[],"sources":["../src/conflicts.ts"],"sourcesContent":["import type { ShortcutStep } from './parser';\nimport { canonicalizeShortcut, parseShortcut } from './parser';\nimport type { BindingEntry } from './types';\n\nexport interface ConflictOptions {\n modKey?: 'ctrl' | 'meta';\n trigger?: 'keydown' | 'keyup';\n}\n\nfunction isPrefixOf(shorter: readonly ShortcutStep[], longer: readonly ShortcutStep[]): boolean {\n if (shorter.length > longer.length) return false;\n\n return canonicalizeShortcut(shorter) === canonicalizeShortcut(longer.slice(0, shorter.length));\n}\n\n/**\n * Finds registered bindings that would conflict with a proposed shortcut — an exact duplicate, a\n * shorter binding that would shadow it as a chord prefix, or a longer binding it would itself\n * shadow. Only compares against entries sharing the same `trigger` — `'keydown'` and `'keyup'`\n * chords are matched independently and never conflict with each other.\n *\n * @example\n * const entries = map.listBindings();\n * findShortcutConflicts('g g', entries); // → the entry for 'g', if one is bound\n */\nexport function findShortcutConflicts(\n shortcut: string,\n entries: readonly BindingEntry[],\n options: ConflictOptions = {},\n): BindingEntry[] {\n const { modKey, trigger = 'keydown' } = options;\n\n // An empty/whitespace-only shortcut would be a trivial prefix of every entry — never report it\n // as a conflict.\n if (shortcut.trim() === '') return [];\n\n const steps = parseShortcut(shortcut, modKey);\n\n return entries.filter(\n (entry) => entry.trigger === trigger && (isPrefixOf(steps, entry.shortcut) || isPrefixOf(entry.shortcut, steps)),\n );\n}\n"],"mappings":";;AASA,SAAS,EAAW,GAAkC,GAA0C;CAG9F,OAFI,EAAQ,SAAS,EAAO,SAAe,KAEpC,EAAqB,CAAO,MAAM,EAAqB,EAAO,MAAM,GAAG,EAAQ,MAAM,CAAC;AAC/F;AAYA,SAAgB,EACd,GACA,GACA,IAA2B,CAAC,GACZ;CAChB,IAAM,EAAE,WAAQ,aAAU,cAAc;CAIxC,IAAI,EAAS,KAAK,MAAM,IAAI,OAAO,CAAC;CAEpC,IAAM,IAAQ,EAAc,GAAU,CAAM;CAE5C,OAAO,EAAQ,QACZ,MAAU,EAAM,YAAY,MAAY,EAAW,GAAO,EAAM,QAAQ,KAAK,EAAW,EAAM,UAAU,CAAK,EAChH;AACF"}
1
+ {"version":3,"file":"conflicts.js","names":[],"sources":["../src/conflicts.ts"],"sourcesContent":["import type { ShortcutStep } from './parser';\nimport { canonicalizeShortcut, parseShortcut } from './parser';\nimport type { BindingEntry } from './types';\n\nexport interface ConflictOptions {\n modKey?: 'ctrl' | 'meta';\n trigger?: 'keydown' | 'keyup';\n}\n\nfunction isPrefixOf(shorter: readonly ShortcutStep[], longer: readonly ShortcutStep[]): boolean {\n if (shorter.length > longer.length) return false;\n\n return canonicalizeShortcut(shorter) === canonicalizeShortcut(longer.slice(0, shorter.length));\n}\n\n/**\n * Finds registered bindings that would conflict with a proposed shortcut — an exact duplicate, a\n * shorter binding that would shadow it as a chord prefix, or a longer binding it would itself\n * shadow. Only compares against entries sharing the same `trigger` — `'keydown'` and `'keyup'`\n * chords are matched independently and never conflict with each other.\n *\n * @example\n * const entries = map.listBindings();\n * findShortcutConflicts('g g', entries); // → the entry for 'g', if one is bound\n */\nexport function findShortcutConflicts(\n shortcut: string,\n entries: readonly BindingEntry[],\n options: ConflictOptions = {},\n): BindingEntry[] {\n const { modKey, trigger = 'keydown' } = options;\n\n // An empty/whitespace-only shortcut would be a trivial prefix of every entry — never report it\n // as a conflict.\n if (shortcut.trim() === '') return [];\n\n const steps = parseShortcut(shortcut, modKey);\n\n return entries.filter(\n (entry) => entry.trigger === trigger && (isPrefixOf(steps, entry.shortcut) || isPrefixOf(entry.shortcut, steps)),\n );\n}\n"],"mappings":"sEASA,SAAS,WAAW,EAAkC,EAA0C,CAG9F,OAFI,EAAQ,OAAS,EAAO,OAAe,GAEpC,EAAqB,CAAO,IAAM,EAAqB,EAAO,MAAM,EAAG,EAAQ,MAAM,CAAC,CAC/F,CAYA,SAAgB,sBACd,EACA,EACA,EAA2B,CAAC,EACZ,CAChB,GAAM,CAAE,SAAQ,UAAU,WAAc,EAIxC,GAAI,EAAS,KAAK,IAAM,GAAI,MAAO,CAAC,EAEpC,IAAM,EAAQ,EAAc,EAAU,CAAM,EAE5C,OAAO,EAAQ,OACZ,GAAU,EAAM,UAAY,IAAY,WAAW,EAAO,EAAM,QAAQ,GAAK,WAAW,EAAM,SAAU,CAAK,EAChH,CACF"}
package/dist/errors.cjs CHANGED
@@ -1,2 +1,2 @@
1
- var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{};exports.KeymapError=e,exports.KeymapParseError=t;
1
+ var KeymapError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},KeymapParseError=class extends KeymapError{},KeymapConfigError=class extends KeymapError{};exports.KeymapConfigError=KeymapConfigError,exports.KeymapError=KeymapError,exports.KeymapParseError=KeymapParseError;
2
2
  //# sourceMappingURL=errors.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all keymap errors. Use `instanceof KeymapError` to catch any keymap-originated error. */\nexport class KeymapError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */\nexport class KeymapParseError extends KeymapError {}\n"],"mappings":"AACA,IAAa,EAAb,cAAiC,KAAM,CACrC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAsC,CAAY,CAAC"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all keymap errors. Use `instanceof KeymapError` to catch any keymap-originated error. */\nexport class KeymapError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */\nexport class KeymapParseError extends KeymapError {}\n\n/** Thrown when keymap configuration is invalid, such as a non-positive `chordTimeout`. */\nexport class KeymapConfigError extends KeymapError {}\n"],"mappings":"AACA,IAAa,YAAb,cAAiC,KAAM,CACrC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,iBAAb,cAAsC,WAAY,CAAC,EAGtC,kBAAb,cAAuC,WAAY,CAAC"}
package/dist/errors.d.ts CHANGED
@@ -5,4 +5,7 @@ export declare class KeymapError extends Error {
5
5
  /** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */
6
6
  export declare class KeymapParseError extends KeymapError {
7
7
  }
8
+ /** Thrown when keymap configuration is invalid, such as a non-positive `chordTimeout`. */
9
+ export declare class KeymapConfigError extends KeymapError {
10
+ }
8
11
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,sFAAsF;AACtF,qBAAa,gBAAiB,SAAQ,WAAW;CAAG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;CAKjD;AAED,sFAAsF;AACtF,qBAAa,gBAAiB,SAAQ,WAAW;CAAG;AAEpD,0FAA0F;AAC1F,qBAAa,iBAAkB,SAAQ,WAAW;CAAG"}
package/dist/errors.js CHANGED
@@ -1,10 +1,2 @@
1
- //#region src/errors.ts
2
- var e = class extends Error {
3
- constructor(e, t) {
4
- super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
5
- }
6
- }, t = class extends e {};
7
- //#endregion
8
- export { e as KeymapError, t as KeymapParseError };
9
-
1
+ var KeymapError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},KeymapParseError=class extends KeymapError{},KeymapConfigError=class extends KeymapError{};export{KeymapConfigError,KeymapError,KeymapParseError};
10
2
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all keymap errors. Use `instanceof KeymapError` to catch any keymap-originated error. */\nexport class KeymapError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */\nexport class KeymapParseError extends KeymapError {}\n"],"mappings":";AACA,IAAa,IAAb,cAAiC,MAAM;CACrC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAGa,IAAb,cAAsC,EAAY,CAAC"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all keymap errors. Use `instanceof KeymapError` to catch any keymap-originated error. */\nexport class KeymapError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */\nexport class KeymapParseError extends KeymapError {}\n\n/** Thrown when keymap configuration is invalid, such as a non-positive `chordTimeout`. */\nexport class KeymapConfigError extends KeymapError {}\n"],"mappings":"AACA,IAAa,YAAb,cAAiC,KAAM,CACrC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,iBAAb,cAAsC,WAAY,CAAC,EAGtC,kBAAb,cAAuC,WAAY,CAAC"}
package/dist/format.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./parser.cjs");require("./_dev.cjs");var t={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},n={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},r={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},i=[`ctrl`,`alt`,`shift`,`meta`];function a(a,o=e.detectModKey()){if(!a.trim())return``;let s;try{s=e.parseShortcut(a,o)}catch{return`${a}`,``}let c=o===`meta`;return s.map(e=>{let a=i.filter(t=>e.modifiers.has(t)).map(e=>c?n[e]:r[e]),o=t[e.key]??e.key.toUpperCase();return c?`${a.join(``)}${o}`:[...a,o].join(`+`)}).join(` `)}exports.formatShortcut=a;
1
+ const e=require("./parser.cjs");require("./_dev.cjs");var t={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},n={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},r={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},i=[`ctrl`,`alt`,`shift`,`meta`];function formatShortcut(t,n=e.detectModKey()){return t.trim()?formatShortcutParts(t,n).map(e=>e.join(n===`meta`?``:`+`)).join(` `):``}function formatShortcutParts(a,o=e.detectModKey()){if(!a.trim())return`${a}`,[];let s;try{s=e.parseShortcut(a,o)}catch{return`${a}`,[]}let c=o===`meta`;return s.map(e=>{let a=i.filter(t=>e.modifiers.has(t)).map(e=>c?n[e]:r[e]),o=t[e.key]??e.key.toUpperCase();return[...a,o]})}exports.formatShortcut=formatShortcut,exports.formatShortcutParts=formatShortcutParts;
2
2
  //# sourceMappingURL=format.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { detectModKey, parseShortcut } from './parser';\n\nconst KEY_SYMBOLS: Record<string, string> = {\n ' ': 'Space',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n arrowup: '↑',\n backspace: '⌫',\n delete: '⌦',\n end: 'End',\n enter: '↵',\n escape: 'Esc',\n home: 'Home',\n pagedown: 'PgDn',\n pageup: 'PgUp',\n tab: '⇥',\n};\n\nconst MOD_SYMBOLS_MAC: Record<string, string> = {\n alt: '⌥',\n ctrl: '⌃',\n meta: '⌘',\n shift: '⇧',\n};\n\nconst MOD_LABELS_OTHER: Record<string, string> = {\n alt: 'Alt',\n ctrl: 'Ctrl',\n meta: 'Win',\n shift: 'Shift',\n};\n\nconst MOD_ORDER = ['ctrl', 'alt', 'shift', 'meta'] as const;\n\n/**\n * Formats a shortcut string into a human-readable display string.\n *\n * On Mac (when `modKey` is `'meta'`), uses standard Mac symbols (⌘, ⌥, ⇧, ⌃).\n * On other platforms, uses word labels (Ctrl, Alt, Shift, Win).\n *\n * @example\n * formatShortcut('mod+shift+p', 'meta') // '⇧⌘P' (canonical order: ctrl, alt, shift, meta)\n * formatShortcut('mod+shift+p', 'ctrl') // 'Ctrl+Shift+P'\n * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'\n */\nexport function formatShortcut(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string {\n if (!shortcut.trim()) {\n warn(`formatShortcut() received an empty shortcut string`);\n\n return '';\n }\n\n let steps: ReturnType<typeof parseShortcut>;\n\n try {\n steps = parseShortcut(shortcut, modKey);\n } catch {\n warn(`formatShortcut() received an invalid shortcut: \"${shortcut}\"`);\n\n return '';\n }\n\n const isMac = modKey === 'meta';\n\n return steps\n .map((step) => {\n const modParts = MOD_ORDER.filter((m) => step.modifiers.has(m)).map((m) =>\n isMac ? MOD_SYMBOLS_MAC[m] : MOD_LABELS_OTHER[m],\n );\n\n const keyLabel = KEY_SYMBOLS[step.key] ?? step.key.toUpperCase();\n\n return isMac ? `${modParts.join('')}${keyLabel}` : [...modParts, keyLabel].join('+');\n })\n .join(' ');\n}\n"],"mappings":"sDAGA,IAAM,EAAsC,CAC1C,IAAK,QACL,UAAW,IACX,UAAW,IACX,WAAY,IACZ,QAAS,IACT,UAAW,IACX,OAAQ,IACR,IAAK,MACL,MAAO,IACP,OAAQ,MACR,KAAM,OACN,SAAU,OACV,OAAQ,OACR,IAAK,GACP,EAEM,EAA0C,CAC9C,IAAK,IACL,KAAM,IACN,KAAM,IACN,MAAO,GACT,EAEM,EAA2C,CAC/C,IAAK,MACL,KAAM,OACN,KAAM,MACN,MAAO,OACT,EAEM,EAAY,CAAC,OAAQ,MAAO,QAAS,MAAM,EAajD,SAAgB,EAAe,EAAkB,EAA0B,EAAA,aAAa,EAAW,CACjG,GAAI,CAAC,EAAS,KAAK,EAGjB,MAAO,GAGT,IAAI,EAEJ,GAAI,CACF,EAAQ,EAAA,cAAc,EAAU,CAAM,CACxC,MAAQ,CAGN,MAFK,GAAmD,EAAnD,EAEE,EACT,CAEA,IAAM,EAAQ,IAAW,OAEzB,OAAO,EACJ,IAAK,GAAS,CACb,IAAM,EAAW,EAAU,OAAQ,GAAM,EAAK,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,IAAK,GACnE,EAAQ,EAAgB,GAAK,EAAiB,EAChD,EAEM,EAAW,EAAY,EAAK,MAAQ,EAAK,IAAI,YAAY,EAE/D,OAAO,EAAQ,GAAG,EAAS,KAAK,EAAE,IAAI,IAAa,CAAC,GAAG,EAAU,CAAQ,CAAC,CAAC,KAAK,GAAG,CACrF,CAAC,CAAC,CACD,KAAK,GAAG,CACb"}
1
+ {"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { detectModKey, parseShortcut } from './parser';\n\nconst KEY_SYMBOLS: Record<string, string> = {\n ' ': 'Space',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n arrowup: '↑',\n backspace: '⌫',\n delete: '⌦',\n end: 'End',\n enter: '↵',\n escape: 'Esc',\n home: 'Home',\n pagedown: 'PgDn',\n pageup: 'PgUp',\n tab: '⇥',\n};\n\nconst MOD_SYMBOLS_MAC: Record<string, string> = {\n alt: '⌥',\n ctrl: '⌃',\n meta: '⌘',\n shift: '⇧',\n};\n\nconst MOD_LABELS_OTHER: Record<string, string> = {\n alt: 'Alt',\n ctrl: 'Ctrl',\n meta: 'Win',\n shift: 'Shift',\n};\n\nconst MOD_ORDER = ['ctrl', 'alt', 'shift', 'meta'] as const;\n\n/**\n * Formats a shortcut string into a human-readable display string.\n *\n * On Mac (when `modKey` is `'meta'`), uses standard Mac symbols (⌘, ⌥, ⇧, ⌃).\n * On other platforms, uses word labels (Ctrl, Alt, Shift, Win).\n *\n * @example\n * formatShortcut('mod+shift+p', 'meta') // '⇧⌘P' (canonical order: ctrl, alt, shift, meta)\n * formatShortcut('mod+shift+p', 'ctrl') // 'Ctrl+Shift+P'\n * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'\n */\nexport function formatShortcut(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string {\n if (!shortcut.trim()) {\n warn(`formatShortcut() received an empty shortcut string`);\n\n return '';\n }\n\n return formatShortcutParts(shortcut, modKey)\n .map((step) => step.join(modKey === 'meta' ? '' : '+'))\n .join(' ');\n}\n\n/**\n * Formats a shortcut into keycap-sized segments: one array per step (a chord has several\n * steps), one label per keycap — Mac modifier symbols are their own keycaps, other\n * platforms get whole-word labels (`['Ctrl', 'Shift', 'P']`).\n *\n * Made for keycap UIs that need the pieces `formatShortcut` joins into one string, so\n * \"press ⌘ together with Z\" can render as two keycaps while the flat form stays \"⌘Z\".\n *\n * @example\n * formatShortcutParts('mod+z', 'meta') // [['⌘', 'Z']]\n * formatShortcutParts('mod+shift+z', 'ctrl') // [['Ctrl', 'Shift', 'Z']]\n * formatShortcutParts('g h', 'ctrl') // [['G'], ['H']]\n */\nexport function formatShortcutParts(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string[][] {\n if (!shortcut.trim()) {\n warn(`formatShortcutParts() received an empty shortcut string: \"${shortcut}\"`);\n\n return [];\n }\n\n let steps: ReturnType<typeof parseShortcut>;\n\n try {\n steps = parseShortcut(shortcut, modKey);\n } catch {\n warn(`formatShortcutParts() received an invalid shortcut: \"${shortcut}\"`);\n\n return [];\n }\n\n const isMac = modKey === 'meta';\n\n return steps.map((step) => {\n const modParts = MOD_ORDER.filter((m) => step.modifiers.has(m)).map((m) =>\n isMac ? MOD_SYMBOLS_MAC[m] : MOD_LABELS_OTHER[m],\n );\n\n const keyLabel = KEY_SYMBOLS[step.key] ?? step.key.toUpperCase();\n\n return [...modParts, keyLabel];\n });\n}\n"],"mappings":"sDAGA,IAAM,EAAsC,CAC1C,IAAK,QACL,UAAW,IACX,UAAW,IACX,WAAY,IACZ,QAAS,IACT,UAAW,IACX,OAAQ,IACR,IAAK,MACL,MAAO,IACP,OAAQ,MACR,KAAM,OACN,SAAU,OACV,OAAQ,OACR,IAAK,GACP,EAEM,EAA0C,CAC9C,IAAK,IACL,KAAM,IACN,KAAM,IACN,MAAO,GACT,EAEM,EAA2C,CAC/C,IAAK,MACL,KAAM,OACN,KAAM,MACN,MAAO,OACT,EAEM,EAAY,CAAC,OAAQ,MAAO,QAAS,MAAM,EAajD,SAAgB,eAAe,EAAkB,EAA0B,EAAA,aAAa,EAAW,CAOjG,OANK,EAAS,KAAK,EAMZ,oBAAoB,EAAU,CAAM,CAAC,CACzC,IAAK,GAAS,EAAK,KAAK,IAAW,OAAS,GAAK,GAAG,CAAC,CAAC,CACtD,KAAK,GAAG,EALF,EAMX,CAeA,SAAgB,oBAAoB,EAAkB,EAA0B,EAAA,aAAa,EAAe,CAC1G,GAAI,CAAC,EAAS,KAAK,EAGjB,MAFK,GAA6D,EAA7D,EAEE,CAAC,EAGV,IAAI,EAEJ,GAAI,CACF,EAAQ,EAAA,cAAc,EAAU,CAAM,CACxC,MAAQ,CAGN,MAFK,GAAwD,EAAxD,EAEE,CAAC,CACV,CAEA,IAAM,EAAQ,IAAW,OAEzB,OAAO,EAAM,IAAK,GAAS,CACzB,IAAM,EAAW,EAAU,OAAQ,GAAM,EAAK,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,IAAK,GACnE,EAAQ,EAAgB,GAAK,EAAiB,EAChD,EAEM,EAAW,EAAY,EAAK,MAAQ,EAAK,IAAI,YAAY,EAE/D,MAAO,CAAC,GAAG,EAAU,CAAQ,CAC/B,CAAC,CACH"}
package/dist/format.d.ts CHANGED
@@ -10,4 +10,18 @@
10
10
  * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'
11
11
  */
12
12
  export declare function formatShortcut(shortcut: string, modKey?: 'ctrl' | 'meta'): string;
13
+ /**
14
+ * Formats a shortcut into keycap-sized segments: one array per step (a chord has several
15
+ * steps), one label per keycap — Mac modifier symbols are their own keycaps, other
16
+ * platforms get whole-word labels (`['Ctrl', 'Shift', 'P']`).
17
+ *
18
+ * Made for keycap UIs that need the pieces `formatShortcut` joins into one string, so
19
+ * "press ⌘ together with Z" can render as two keycaps while the flat form stays "⌘Z".
20
+ *
21
+ * @example
22
+ * formatShortcutParts('mod+z', 'meta') // [['⌘', 'Z']]
23
+ * formatShortcutParts('mod+shift+z', 'ctrl') // [['Ctrl', 'Shift', 'Z']]
24
+ * formatShortcutParts('g h', 'ctrl') // [['G'], ['H']]
25
+ */
26
+ export declare function formatShortcutParts(shortcut: string, modKey?: 'ctrl' | 'meta'): string[][];
13
27
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAoCA;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,MAAuB,GAAG,MAAM,CA8BjG"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAoCA;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,MAAuB,GAAG,MAAM,CAUjG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,MAAuB,GAAG,MAAM,EAAE,EAAE,CA4B1G"}
package/dist/format.js CHANGED
@@ -1,52 +1,2 @@
1
- import { detectModKey as e, parseShortcut as t } from "./parser.js";
2
- import "./_dev.js";
3
- //#region src/format.ts
4
- var n = {
5
- " ": "Space",
6
- arrowdown: "↓",
7
- arrowleft: "←",
8
- arrowright: "→",
9
- arrowup: "↑",
10
- backspace: "⌫",
11
- delete: "⌦",
12
- end: "End",
13
- enter: "↵",
14
- escape: "Esc",
15
- home: "Home",
16
- pagedown: "PgDn",
17
- pageup: "PgUp",
18
- tab: "⇥"
19
- }, r = {
20
- alt: "⌥",
21
- ctrl: "⌃",
22
- meta: "⌘",
23
- shift: "⇧"
24
- }, i = {
25
- alt: "Alt",
26
- ctrl: "Ctrl",
27
- meta: "Win",
28
- shift: "Shift"
29
- }, a = [
30
- "ctrl",
31
- "alt",
32
- "shift",
33
- "meta"
34
- ];
35
- function o(o, s = e()) {
36
- if (!o.trim()) return "";
37
- let c;
38
- try {
39
- c = t(o, s);
40
- } catch {
41
- return `${o}`, "";
42
- }
43
- let l = s === "meta";
44
- return c.map((e) => {
45
- let t = a.filter((t) => e.modifiers.has(t)).map((e) => l ? r[e] : i[e]), o = n[e.key] ?? e.key.toUpperCase();
46
- return l ? `${t.join("")}${o}` : [...t, o].join("+");
47
- }).join(" ");
48
- }
49
- //#endregion
50
- export { o as formatShortcut };
51
-
1
+ import{detectModKey as e,parseShortcut as t}from"./parser.js";import"./_dev.js";var n={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},r={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},i={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},a=[`ctrl`,`alt`,`shift`,`meta`];function formatShortcut(t,n=e()){return t.trim()?formatShortcutParts(t,n).map(e=>e.join(n===`meta`?``:`+`)).join(` `):``}function formatShortcutParts(o,s=e()){if(!o.trim())return`${o}`,[];let c;try{c=t(o,s)}catch{return`${o}`,[]}let l=s===`meta`;return c.map(e=>{let t=a.filter(t=>e.modifiers.has(t)).map(e=>l?r[e]:i[e]),o=n[e.key]??e.key.toUpperCase();return[...t,o]})}export{formatShortcut,formatShortcutParts};
52
2
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { detectModKey, parseShortcut } from './parser';\n\nconst KEY_SYMBOLS: Record<string, string> = {\n ' ': 'Space',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n arrowup: '↑',\n backspace: '⌫',\n delete: '⌦',\n end: 'End',\n enter: '↵',\n escape: 'Esc',\n home: 'Home',\n pagedown: 'PgDn',\n pageup: 'PgUp',\n tab: '⇥',\n};\n\nconst MOD_SYMBOLS_MAC: Record<string, string> = {\n alt: '⌥',\n ctrl: '⌃',\n meta: '⌘',\n shift: '⇧',\n};\n\nconst MOD_LABELS_OTHER: Record<string, string> = {\n alt: 'Alt',\n ctrl: 'Ctrl',\n meta: 'Win',\n shift: 'Shift',\n};\n\nconst MOD_ORDER = ['ctrl', 'alt', 'shift', 'meta'] as const;\n\n/**\n * Formats a shortcut string into a human-readable display string.\n *\n * On Mac (when `modKey` is `'meta'`), uses standard Mac symbols (⌘, ⌥, ⇧, ⌃).\n * On other platforms, uses word labels (Ctrl, Alt, Shift, Win).\n *\n * @example\n * formatShortcut('mod+shift+p', 'meta') // '⇧⌘P' (canonical order: ctrl, alt, shift, meta)\n * formatShortcut('mod+shift+p', 'ctrl') // 'Ctrl+Shift+P'\n * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'\n */\nexport function formatShortcut(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string {\n if (!shortcut.trim()) {\n warn(`formatShortcut() received an empty shortcut string`);\n\n return '';\n }\n\n let steps: ReturnType<typeof parseShortcut>;\n\n try {\n steps = parseShortcut(shortcut, modKey);\n } catch {\n warn(`formatShortcut() received an invalid shortcut: \"${shortcut}\"`);\n\n return '';\n }\n\n const isMac = modKey === 'meta';\n\n return steps\n .map((step) => {\n const modParts = MOD_ORDER.filter((m) => step.modifiers.has(m)).map((m) =>\n isMac ? MOD_SYMBOLS_MAC[m] : MOD_LABELS_OTHER[m],\n );\n\n const keyLabel = KEY_SYMBOLS[step.key] ?? step.key.toUpperCase();\n\n return isMac ? `${modParts.join('')}${keyLabel}` : [...modParts, keyLabel].join('+');\n })\n .join(' ');\n}\n"],"mappings":";;;AAGA,IAAM,IAAsC;CAC1C,KAAK;CACL,WAAW;CACX,WAAW;CACX,YAAY;CACZ,SAAS;CACT,WAAW;CACX,QAAQ;CACR,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,UAAU;CACV,QAAQ;CACR,KAAK;AACP,GAEM,IAA0C;CAC9C,KAAK;CACL,MAAM;CACN,MAAM;CACN,OAAO;AACT,GAEM,IAA2C;CAC/C,KAAK;CACL,MAAM;CACN,MAAM;CACN,OAAO;AACT,GAEM,IAAY;CAAC;CAAQ;CAAO;CAAS;AAAM;AAajD,SAAgB,EAAe,GAAkB,IAA0B,EAAa,GAAW;CACjG,IAAI,CAAC,EAAS,KAAK,GAGjB,OAAO;CAGT,IAAI;CAEJ,IAAI;EACF,IAAQ,EAAc,GAAU,CAAM;CACxC,QAAQ;EAGN,OAFK,GAAmD,EAAnD,GAEE;CACT;CAEA,IAAM,IAAQ,MAAW;CAEzB,OAAO,EACJ,KAAK,MAAS;EACb,IAAM,IAAW,EAAU,QAAQ,MAAM,EAAK,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,MACnE,IAAQ,EAAgB,KAAK,EAAiB,EAChD,GAEM,IAAW,EAAY,EAAK,QAAQ,EAAK,IAAI,YAAY;EAE/D,OAAO,IAAQ,GAAG,EAAS,KAAK,EAAE,IAAI,MAAa,CAAC,GAAG,GAAU,CAAQ,CAAC,CAAC,KAAK,GAAG;CACrF,CAAC,CAAC,CACD,KAAK,GAAG;AACb"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { warn } from './_dev';\nimport { detectModKey, parseShortcut } from './parser';\n\nconst KEY_SYMBOLS: Record<string, string> = {\n ' ': 'Space',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n arrowup: '↑',\n backspace: '⌫',\n delete: '⌦',\n end: 'End',\n enter: '↵',\n escape: 'Esc',\n home: 'Home',\n pagedown: 'PgDn',\n pageup: 'PgUp',\n tab: '⇥',\n};\n\nconst MOD_SYMBOLS_MAC: Record<string, string> = {\n alt: '⌥',\n ctrl: '⌃',\n meta: '⌘',\n shift: '⇧',\n};\n\nconst MOD_LABELS_OTHER: Record<string, string> = {\n alt: 'Alt',\n ctrl: 'Ctrl',\n meta: 'Win',\n shift: 'Shift',\n};\n\nconst MOD_ORDER = ['ctrl', 'alt', 'shift', 'meta'] as const;\n\n/**\n * Formats a shortcut string into a human-readable display string.\n *\n * On Mac (when `modKey` is `'meta'`), uses standard Mac symbols (⌘, ⌥, ⇧, ⌃).\n * On other platforms, uses word labels (Ctrl, Alt, Shift, Win).\n *\n * @example\n * formatShortcut('mod+shift+p', 'meta') // '⇧⌘P' (canonical order: ctrl, alt, shift, meta)\n * formatShortcut('mod+shift+p', 'ctrl') // 'Ctrl+Shift+P'\n * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'\n */\nexport function formatShortcut(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string {\n if (!shortcut.trim()) {\n warn(`formatShortcut() received an empty shortcut string`);\n\n return '';\n }\n\n return formatShortcutParts(shortcut, modKey)\n .map((step) => step.join(modKey === 'meta' ? '' : '+'))\n .join(' ');\n}\n\n/**\n * Formats a shortcut into keycap-sized segments: one array per step (a chord has several\n * steps), one label per keycap — Mac modifier symbols are their own keycaps, other\n * platforms get whole-word labels (`['Ctrl', 'Shift', 'P']`).\n *\n * Made for keycap UIs that need the pieces `formatShortcut` joins into one string, so\n * \"press ⌘ together with Z\" can render as two keycaps while the flat form stays \"⌘Z\".\n *\n * @example\n * formatShortcutParts('mod+z', 'meta') // [['⌘', 'Z']]\n * formatShortcutParts('mod+shift+z', 'ctrl') // [['Ctrl', 'Shift', 'Z']]\n * formatShortcutParts('g h', 'ctrl') // [['G'], ['H']]\n */\nexport function formatShortcutParts(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string[][] {\n if (!shortcut.trim()) {\n warn(`formatShortcutParts() received an empty shortcut string: \"${shortcut}\"`);\n\n return [];\n }\n\n let steps: ReturnType<typeof parseShortcut>;\n\n try {\n steps = parseShortcut(shortcut, modKey);\n } catch {\n warn(`formatShortcutParts() received an invalid shortcut: \"${shortcut}\"`);\n\n return [];\n }\n\n const isMac = modKey === 'meta';\n\n return steps.map((step) => {\n const modParts = MOD_ORDER.filter((m) => step.modifiers.has(m)).map((m) =>\n isMac ? MOD_SYMBOLS_MAC[m] : MOD_LABELS_OTHER[m],\n );\n\n const keyLabel = KEY_SYMBOLS[step.key] ?? step.key.toUpperCase();\n\n return [...modParts, keyLabel];\n });\n}\n"],"mappings":"gFAGA,IAAM,EAAsC,CAC1C,IAAK,QACL,UAAW,IACX,UAAW,IACX,WAAY,IACZ,QAAS,IACT,UAAW,IACX,OAAQ,IACR,IAAK,MACL,MAAO,IACP,OAAQ,MACR,KAAM,OACN,SAAU,OACV,OAAQ,OACR,IAAK,GACP,EAEM,EAA0C,CAC9C,IAAK,IACL,KAAM,IACN,KAAM,IACN,MAAO,GACT,EAEM,EAA2C,CAC/C,IAAK,MACL,KAAM,OACN,KAAM,MACN,MAAO,OACT,EAEM,EAAY,CAAC,OAAQ,MAAO,QAAS,MAAM,EAajD,SAAgB,eAAe,EAAkB,EAA0B,EAAa,EAAW,CAOjG,OANK,EAAS,KAAK,EAMZ,oBAAoB,EAAU,CAAM,CAAC,CACzC,IAAK,GAAS,EAAK,KAAK,IAAW,OAAS,GAAK,GAAG,CAAC,CAAC,CACtD,KAAK,GAAG,EALF,EAMX,CAeA,SAAgB,oBAAoB,EAAkB,EAA0B,EAAa,EAAe,CAC1G,GAAI,CAAC,EAAS,KAAK,EAGjB,MAFK,GAA6D,EAA7D,EAEE,CAAC,EAGV,IAAI,EAEJ,GAAI,CACF,EAAQ,EAAc,EAAU,CAAM,CACxC,MAAQ,CAGN,MAFK,GAAwD,EAAxD,EAEE,CAAC,CACV,CAEA,IAAM,EAAQ,IAAW,OAEzB,OAAO,EAAM,IAAK,GAAS,CACzB,IAAM,EAAW,EAAU,OAAQ,GAAM,EAAK,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,IAAK,GACnE,EAAQ,EAAgB,GAAK,EAAiB,EAChD,EAEM,EAAW,EAAY,EAAK,MAAQ,EAAK,IAAI,YAAY,EAE/D,MAAO,CAAC,GAAG,EAAU,CAAQ,CAC/B,CAAC,CACH"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./conflicts.cjs"),n=require("./format.cjs"),r=require("./keymap.cjs");exports.KeymapError=e.KeymapError,exports.KeymapParseError=e.KeymapParseError,exports.createKeymap=r.createKeymap,exports.findShortcutConflicts=t.findShortcutConflicts,exports.formatShortcut=n.formatShortcut;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./parser.cjs"),n=require("./conflicts.cjs"),r=require("./format.cjs"),i=require("./keymap.cjs");exports.KeymapConfigError=e.KeymapConfigError,exports.KeymapError=e.KeymapError,exports.KeymapParseError=e.KeymapParseError,exports.createKeymap=i.createKeymap,exports.detectModKey=t.detectModKey,exports.findShortcutConflicts=n.findShortcutConflicts,exports.formatShortcut=r.formatShortcut,exports.formatShortcutParts=r.formatShortcutParts,exports.matchKey=t.matchKey;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export type { ConflictOptions } from './conflicts';
2
2
  export { findShortcutConflicts } from './conflicts';
3
- export { KeymapError, KeymapParseError } from './errors';
4
- export { formatShortcut } from './format';
3
+ export { KeymapConfigError, KeymapError, KeymapParseError } from './errors';
4
+ export { formatShortcut, formatShortcutParts } from './format';
5
5
  export { createKeymap } from './keymap';
6
+ export { detectModKey, matchKey } from './parser';
6
7
  export type { Binding, BindingEntry, Handler, Keymap, KeymapEvent, KeymapOptions, When } from './types';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAClD,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1 @@
1
- import { KeymapError as e, KeymapParseError as t } from "./errors.js";
2
- import { findShortcutConflicts as n } from "./conflicts.js";
3
- import { formatShortcut as r } from "./format.js";
4
- import { createKeymap as i } from "./keymap.js";
5
- export { e as KeymapError, t as KeymapParseError, i as createKeymap, n as findShortcutConflicts, r as formatShortcut };
1
+ import{KeymapConfigError as e,KeymapError as t,KeymapParseError as n}from"./errors.js";import{detectModKey as r,matchKey as i}from"./parser.js";import{findShortcutConflicts as a}from"./conflicts.js";import{formatShortcut as o,formatShortcutParts as s}from"./format.js";import{createKeymap as c}from"./keymap.js";export{e as KeymapConfigError,t as KeymapError,n as KeymapParseError,c as createKeymap,r as detectModKey,a as findShortcutConflicts,o as formatShortcut,s as formatShortcutParts,i as matchKey};
package/dist/keymap.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{};function n(){if(typeof navigator>`u`)return`ctrl`;let e=navigator.userAgentData?.platform??navigator.platform??``;return/mac/i.test(e)?`meta`:`ctrl`}var r={alt:`alt`,cmd:`meta`,command:`meta`,control:`ctrl`,ctrl:`ctrl`,meta:`meta`,opt:`alt`,option:`alt`,shift:`shift`,win:`meta`},i={del:`delete`,down:`arrowdown`,esc:`escape`,left:`arrowleft`,right:`arrowright`,space:` `,spacebar:` `,up:`arrowup`};function a(e,n){let a=e.trim().split(`+`).filter(Boolean).map(e=>e.toLowerCase());if(a.length===0)throw new t(`Invalid shortcut step: "${e}"`);let o={...r,mod:n},s=new Set,c=[];for(let e of a){let t=Object.hasOwn(o,e)?o[e]:void 0;t?s.add(t):c.push(e)}if(c.length===0)throw new t(`Invalid shortcut step: "${e}"`);if(c.length>1)throw new t(`Ambiguous shortcut step: "${e}" — multiple non-modifier keys found`);let l=c[0];return{key:Object.hasOwn(i,l)?i[l]:l,modifiers:s}}function o(e,r=n()){let i=e.trim().split(/\s+/).filter(Boolean);if(i.length===0)throw new t(`Shortcut must contain at least one key step`);return i.map(e=>a(e,r))}function s(e){return e.map(e=>{let t=[...e.modifiers].sort().join(`+`);return t?`${t}+${e.key}`:e.key}).join(` `)}function c(e,t){if(typeof e.key!=`string`||e.key.toLowerCase()!==t.key)return!1;let{modifiers:n}=t;return e.altKey===n.has(`alt`)&&e.ctrlKey===n.has(`ctrl`)&&e.metaKey===n.has(`meta`)&&e.shiftKey===n.has(`shift`)}function l(e,t){return e.length>t.length?!1:s(e)===s(t.slice(0,e.length))}function u(e,t,n={}){let{modKey:r,trigger:i=`keydown`}=n;if(e.trim()===``)return[];let a=o(e,r);return t.filter(e=>e.trigger===i&&(l(a,e.shortcut)||l(e.shortcut,a)))}var d={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},f={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},p={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},m=[`ctrl`,`alt`,`shift`,`meta`];function h(e,t=n()){if(!e.trim())return``;let r;try{r=o(e,t)}catch{return`${e}`,``}let i=t===`meta`;return r.map(e=>{let t=m.filter(t=>e.modifiers.has(t)).map(e=>i?f[e]:p[e]),n=d[e.key]??e.key.toUpperCase();return i?`${t.join(``)}${n}`:[...t,n].join(`+`)}).join(` `)}var g=()=>{};function _(e,t,n){let r=0,i=[],a;function o(e=!0){a!==void 0&&clearTimeout(a);let t=r>0;a=void 0,r=0,i=[],e&&t&&n.onCancel()}function s(l){let u=(r===0?e():i).filter(e=>{let t=e.shortcut[r];return t!==void 0&&c(l,t)&&(!e.when||e.when(l))});if(u.length===0){let e=r!==0;return o(),e?s(l):{type:`none`}}a!==void 0&&clearTimeout(a),a=void 0;let d=u.find(e=>e.shortcut.length===r+1);if(d)return o(!1),{binding:d,type:`match`};let f=r===0;return i=u,r+=1,n.onProgress(u[0].shortcut.slice(0,r),f),a=setTimeout(()=>{n.onTimeout(),o(!1)},t),{bindings:u,type:`pending`}}return{advance:s,reset:o}}function v(t=[],r={}){let{chordTimeout:i=1e3,modKey:a=n(),when:s}=r,c=Number.isFinite(i)&&i>0?i:1e3;c!==i&&`${i}`;let l=new Map,u=new Map,d=new Set,f=new AbortController,p=[],m=[],h=!1;function v(){if(h)throw new e(`Keymap is disposed`)}function y(){p=[],m=[];for(let e of l.values())e.trigger===`keydown`?p.push(e):m.push(e);for(let e of u.values())e.keydown.reset(),e.keyup.reset()}function b(e){let t={handler:e.handler,id:e.id,preventDefault:e.preventDefault??!0,shortcut:o(e.shortcut,a),stopPropagation:e.stopPropagation??!1,trigger:e.trigger??`keydown`,when:e.when};return l.set(t.id,t),y(),t}function x(e){let t=l.delete(e);return t&&y(),t}function S(e){return{id:e.id,preventDefault:e.preventDefault,shortcut:e.shortcut.map(e=>({key:e.key,modifiers:new Set(e.modifiers)})),stopPropagation:e.stopPropagation,trigger:e.trigger}}function C(e){if(d.size!==0)for(let t of d)try{t(e)}catch{}}function w(e,t,n){return r=>{let i=(typeof r.composedPath==`function`?r.composedPath():[]).find(e=>u.has(e));if(i&&i!==e)return;let a=r;if(h)return;if(s&&!s(a)){n.reset();return}let o=n.advance(a);if(o.type===`none`)return;let c=o.type===`pending`?o.bindings:[o.binding];c.some(e=>e.preventDefault)&&a.preventDefault(),c.some(e=>e.stopPropagation)&&a.stopPropagation(),o.type!==`pending`&&(d.size>0&&C({binding:S(o.binding),target:e,trigger:t,type:`match`}),o.binding.handler(a))}}for(let e of t)b(e);return{bind(e){v();let t=b(e);return()=>{!h&&l.get(t.id)===t&&x(t.id)}},get disposalSignal(){return f.signal},dispose(){if(!h){h=!0;for(let[e,t]of u)e.removeEventListener(`keydown`,t.onKeydown),e.removeEventListener(`keyup`,t.onKeyup),t.keydown.reset(),t.keyup.reset();C({type:`dispose`}),f.abort(),u.clear(),l.clear(),p=[],m=[],d.clear()}},get disposed(){return h},listBindings(){return[...l.values()].map(S)},mount(e){v();let t=u.get(e);if(!t){let n=t=>({onCancel:()=>{d.size>0&&C({target:e,trigger:t,type:`chord-cancel`})},onProgress:(n,r)=>{if(d.size===0)return;let i=n.map(e=>({key:e.key,modifiers:new Set(e.modifiers)}));C(r?{step:i[0],target:e,trigger:t,type:`chord-start`}:{steps:i,target:e,trigger:t,type:`chord-progress`})},onTimeout:()=>{d.size>0&&C({target:e,trigger:t,type:`chord-timeout`})}}),r=_(()=>p,c,n(`keydown`)),i=_(()=>m,c,n(`keyup`)),a=w(e,`keydown`,r),o=w(e,`keyup`,i);t={keydown:r,keyup:i,onKeydown:a,onKeyup:o,refs:0},u.set(e,t),e.addEventListener(`keydown`,a),e.addEventListener(`keyup`,o)}t.refs+=1;let n=!1;return()=>{n||(n=!0,--t.refs,!(t?.refs>0)&&(e.removeEventListener(`keydown`,t?.onKeydown),e.removeEventListener(`keyup`,t?.onKeyup),t?.keydown.reset(),t?.keyup.reset(),u.delete(e)))}},[Symbol.dispose](){this.dispose()},tap(e,t){let n=t?.signal?AbortSignal.any([f.signal,t.signal]):f.signal;if(n.aborted)return g;d.add(e);let r=()=>d.delete(e);return n.addEventListener(`abort`,r,{once:!0}),()=>{d.delete(e),n.removeEventListener(`abort`,r)}},unbind(e){v(),x(e)||`${e}`}}}exports.KeymapError=e,exports.KeymapParseError=t,exports.createKeymap=v,exports.findShortcutConflicts=u,exports.formatShortcut=h;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var KeymapError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},KeymapParseError=class extends KeymapError{},KeymapConfigError=class extends KeymapError{};function detectModKey(){if(typeof navigator>`u`)return`ctrl`;let e=navigator.userAgentData?.platform??navigator.platform??``;return/mac/i.test(e)?`meta`:`ctrl`}var e={alt:`alt`,cmd:`meta`,command:`meta`,control:`ctrl`,ctrl:`ctrl`,meta:`meta`,opt:`alt`,option:`alt`,shift:`shift`,win:`meta`},t={del:`delete`,down:`arrowdown`,esc:`escape`,left:`arrowleft`,right:`arrowright`,space:` `,spacebar:` `,up:`arrowup`};function parseStepStrict(n,r){let i=n.trim().split(`+`).filter(Boolean).map(e=>e.toLowerCase());if(i.length===0)throw new KeymapParseError(`Invalid shortcut step: "${n}"`);let a={...e,mod:r},o=new Set,s=[];for(let e of i){let t=Object.hasOwn(a,e)?a[e]:void 0;t?o.add(t):s.push(e)}if(s.length===0)throw new KeymapParseError(`Invalid shortcut step: "${n}"`);if(s.length>1)throw new KeymapParseError(`Ambiguous shortcut step: "${n}" — multiple non-modifier keys found`);let c=s[0];return{key:Object.hasOwn(t,c)?t[c]:c,modifiers:o}}function parseStep(e,t=detectModKey()){try{return parseStepStrict(e,t)}catch{return null}}function parseShortcut(e,t=detectModKey()){let n=e.trim().split(/\s+/).filter(Boolean);if(n.length===0)throw new KeymapParseError(`Shortcut must contain at least one key step`);return n.map(e=>parseStepStrict(e,t))}function canonicalizeShortcut(e){return e.map(e=>{let t=[...e.modifiers].sort().join(`+`);return t?`${t}+${e.key}`:e.key}).join(` `)}function matchStep(e,t){if(typeof e.key!=`string`||e.key.toLowerCase()!==t.key)return!1;let{modifiers:n}=t;return e.altKey===n.has(`alt`)&&e.ctrlKey===n.has(`ctrl`)&&e.metaKey===n.has(`meta`)&&e.shiftKey===n.has(`shift`)}function matchKey(e,t,n=detectModKey()){let r=parseStep(t,n);return r!==null&&matchStep(e,r)}function isPrefixOf(e,t){return e.length>t.length?!1:canonicalizeShortcut(e)===canonicalizeShortcut(t.slice(0,e.length))}function findShortcutConflicts(e,t,n={}){let{modKey:r,trigger:i=`keydown`}=n;if(e.trim()===``)return[];let a=parseShortcut(e,r);return t.filter(e=>e.trigger===i&&(isPrefixOf(a,e.shortcut)||isPrefixOf(e.shortcut,a)))}var n={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},r={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},i={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},a=[`ctrl`,`alt`,`shift`,`meta`];function formatShortcut(e,t=detectModKey()){return e.trim()?formatShortcutParts(e,t).map(e=>e.join(t===`meta`?``:`+`)).join(` `):``}function formatShortcutParts(e,t=detectModKey()){if(!e.trim())return`${e}`,[];let o;try{o=parseShortcut(e,t)}catch{return`${e}`,[]}let s=t===`meta`;return o.map(e=>{let t=a.filter(t=>e.modifiers.has(t)).map(e=>s?r[e]:i[e]),o=n[e.key]??e.key.toUpperCase();return[...t,o]})}var noop=()=>{};function createChordTracker(e,t,n){let r=0,i=[],a;function reset(e=!0){a!==void 0&&clearTimeout(a);let t=r>0;a=void 0,r=0,i=[],e&&t&&n.onCancel()}function advance(o){let s=(r===0?e():i).filter(e=>{let t=e.shortcut[r];return t!==void 0&&matchStep(o,t)&&(!e.when||e.when(o))});if(s.length===0){let e=r!==0;return reset(),e?advance(o):{type:`none`}}a!==void 0&&clearTimeout(a),a=void 0;let c=s.find(e=>e.shortcut.length===r+1);if(c)return reset(!1),{binding:c,type:`match`};let l=r===0;return i=s,r+=1,n.onProgress(s[0].shortcut.slice(0,r),l),a=setTimeout(()=>{n.onTimeout(),reset(!1)},t),{bindings:s,type:`pending`}}return{advance,reset}}function createKeymap(e=[],t={}){let{chordTimeout:n=1e3,modKey:r=detectModKey(),when:i}=t;if(!Number.isFinite(n)||n<=0)throw new KeymapConfigError(`chordTimeout must be a positive finite number; received ${n}.`);let a=new Map,o=new Map,s=new Set,c=new AbortController,l=[],u=[],d=!1;function assertActive(){if(d)throw new KeymapError(`Keymap is disposed`)}function rebuildTriggerCaches(){l=[],u=[];for(let e of a.values())e.trigger===`keydown`?l.push(e):u.push(e);for(let e of o.values())e.keydown.reset(),e.keyup.reset()}function addBinding(e){let t={handler:e.handler,id:e.id,preventDefault:e.preventDefault??!0,shortcut:parseShortcut(e.shortcut,r),stopPropagation:e.stopPropagation??!1,trigger:e.trigger??`keydown`,when:e.when};return a.set(t.id,t),rebuildTriggerCaches(),t}function removeById(e){let t=a.delete(e);return t&&rebuildTriggerCaches(),t}function toBindingEntry(e){return{id:e.id,preventDefault:e.preventDefault,shortcut:e.shortcut.map(e=>({key:e.key,modifiers:new Set(e.modifiers)})),stopPropagation:e.stopPropagation,trigger:e.trigger}}function emitTap(e){if(s.size!==0)for(let t of s)try{t(e)}catch{}}function makeHandler(e,t,n){return r=>{let a=(typeof r.composedPath==`function`?r.composedPath():[]).find(e=>o.has(e));if(a&&a!==e)return;let c=r;if(d)return;if(i&&!i(c)){n.reset();return}let l=n.advance(c);if(l.type===`none`)return;let u=l.type===`pending`?l.bindings:[l.binding];u.some(e=>e.preventDefault)&&c.preventDefault(),u.some(e=>e.stopPropagation)&&c.stopPropagation(),l.type!==`pending`&&(s.size>0&&emitTap({binding:toBindingEntry(l.binding),target:e,trigger:t,type:`match`}),l.binding.handler(c))}}for(let t of e)addBinding(t);return{bind(e){assertActive();let t=addBinding(e);return()=>{!d&&a.get(t.id)===t&&removeById(t.id)}},get disposalSignal(){return c.signal},dispose(){if(!d){d=!0;for(let[e,t]of o)e.removeEventListener(`keydown`,t.onKeydown),e.removeEventListener(`keyup`,t.onKeyup),t.keydown.reset(),t.keyup.reset();emitTap({type:`dispose`}),c.abort(),o.clear(),a.clear(),l=[],u=[],s.clear()}},get disposed(){return d},listBindings(){return[...a.values()].map(toBindingEntry)},mount(e){assertActive();let t=o.get(e);if(!t){let createCallbacks=t=>({onCancel:()=>{s.size>0&&emitTap({target:e,trigger:t,type:`chord-cancel`})},onProgress:(n,r)=>{if(s.size===0)return;let i=n.map(e=>({key:e.key,modifiers:new Set(e.modifiers)}));emitTap(r?{step:i[0],target:e,trigger:t,type:`chord-start`}:{steps:i,target:e,trigger:t,type:`chord-progress`})},onTimeout:()=>{s.size>0&&emitTap({target:e,trigger:t,type:`chord-timeout`})}}),r=createChordTracker(()=>l,n,createCallbacks(`keydown`)),i=createChordTracker(()=>u,n,createCallbacks(`keyup`)),a=makeHandler(e,`keydown`,r),c=makeHandler(e,`keyup`,i);t={keydown:r,keyup:i,onKeydown:a,onKeyup:c,refs:0},o.set(e,t),e.addEventListener(`keydown`,a),e.addEventListener(`keyup`,c)}t.refs+=1;let r=!1;return()=>{r||(r=!0,--t.refs,!(t?.refs>0)&&(e.removeEventListener(`keydown`,t?.onKeydown),e.removeEventListener(`keyup`,t?.onKeyup),t?.keydown.reset(),t?.keyup.reset(),o.delete(e)))}},[Symbol.dispose](){this.dispose()},tap(e,t){let n=t?.signal?AbortSignal.any([c.signal,t.signal]):c.signal;if(n.aborted)return noop;s.add(e);let onAbort=()=>s.delete(e);return n.addEventListener(`abort`,onAbort,{once:!0}),()=>{s.delete(e),n.removeEventListener(`abort`,onAbort)}},unbind(e){assertActive(),removeById(e)||`${e}`}}}exports.KeymapConfigError=KeymapConfigError,exports.KeymapError=KeymapError,exports.KeymapParseError=KeymapParseError,exports.createKeymap=createKeymap,exports.detectModKey=detectModKey,exports.findShortcutConflicts=findShortcutConflicts,exports.formatShortcut=formatShortcut,exports.formatShortcutParts=formatShortcutParts,exports.matchKey=matchKey;
2
2
  //# sourceMappingURL=keymap.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"keymap.cjs","names":[],"sources":["../src/errors.ts","../src/parser.ts","../src/conflicts.ts","../src/_dev.ts","../src/format.ts","../src/keymap.ts"],"sourcesContent":["/** Base class for all keymap errors. Use `instanceof KeymapError` to catch any keymap-originated error. */\nexport class KeymapError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */\nexport class KeymapParseError extends KeymapError {}\n","import { KeymapParseError } from './errors';\n\nexport type ModifierKey = 'alt' | 'ctrl' | 'meta' | 'shift';\n\nexport type ShortcutStep = {\n key: string;\n modifiers: Set<ModifierKey>;\n};\n\nexport type Shortcut = ShortcutStep[];\n\nexport function detectModKey(): 'ctrl' | 'meta' {\n if (typeof navigator === 'undefined') return 'ctrl';\n\n const platform =\n (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ??\n navigator.platform ??\n '';\n\n return /mac/i.test(platform) ? 'meta' : 'ctrl';\n}\n\nconst MODIFIER_ALIASES_BASE: Record<string, ModifierKey> = {\n alt: 'alt',\n cmd: 'meta',\n command: 'meta',\n control: 'ctrl',\n ctrl: 'ctrl',\n meta: 'meta',\n opt: 'alt',\n option: 'alt',\n shift: 'shift',\n win: 'meta',\n};\n\nconst SPECIAL_KEY_ALIASES: Record<string, string> = {\n del: 'delete',\n down: 'arrowdown',\n esc: 'escape',\n left: 'arrowleft',\n right: 'arrowright',\n space: ' ',\n spacebar: ' ',\n up: 'arrowup',\n};\n\nfunction parseStepStrict(raw: string, modKey: 'ctrl' | 'meta'): ShortcutStep {\n const parts = raw\n .trim()\n .split('+')\n .filter(Boolean)\n .map((part) => part.toLowerCase());\n\n if (parts.length === 0) throw new KeymapParseError(`Invalid shortcut step: \"${raw}\"`);\n\n const aliases: Record<string, ModifierKey> = { ...MODIFIER_ALIASES_BASE, mod: modKey };\n const modifiers = new Set<ModifierKey>();\n const keyParts: string[] = [];\n\n for (const part of parts) {\n const modifier = Object.hasOwn(aliases, part) ? aliases[part]! : undefined;\n\n if (modifier) modifiers.add(modifier);\n else keyParts.push(part);\n }\n\n if (keyParts.length === 0) throw new KeymapParseError(`Invalid shortcut step: \"${raw}\"`);\n\n if (keyParts.length > 1) {\n throw new KeymapParseError(`Ambiguous shortcut step: \"${raw}\" — multiple non-modifier keys found`);\n }\n\n const rawKey = keyParts[0]!;\n const key = Object.hasOwn(SPECIAL_KEY_ALIASES, rawKey) ? SPECIAL_KEY_ALIASES[rawKey]! : rawKey;\n\n return { key, modifiers };\n}\n\nexport function parseStep(raw: string, modKey: 'ctrl' | 'meta' = detectModKey()): ShortcutStep | null {\n try {\n return parseStepStrict(raw, modKey);\n } catch {\n return null;\n }\n}\n\nexport function parseShortcut(raw: string, modKey: 'ctrl' | 'meta' = detectModKey()): Shortcut {\n const steps = raw.trim().split(/\\s+/).filter(Boolean);\n\n if (steps.length === 0) throw new KeymapParseError('Shortcut must contain at least one key step');\n\n return steps.map((step) => parseStepStrict(step, modKey));\n}\n\nexport function canonicalizeShortcut(steps: readonly ShortcutStep[]): string {\n return steps\n .map((step) => {\n const modifiers = [...step.modifiers].sort().join('+');\n\n return modifiers ? `${modifiers}+${step.key}` : step.key;\n })\n .join(' ');\n}\n\nexport function matchStep(event: KeyboardEvent, step: ShortcutStep): boolean {\n // Headless usage (SSR, non-DOM `EventTarget`s, hand-built test events) means `event` isn't\n // guaranteed to be a real `KeyboardEvent` at runtime even though the type says so — treat a\n // missing/non-string `key` as \"doesn't match\" rather than throwing inside the event listener.\n if (typeof event.key !== 'string') return false;\n\n if (event.key.toLowerCase() !== step.key) return false;\n\n const { modifiers } = step;\n\n return (\n event.altKey === modifiers.has('alt') &&\n event.ctrlKey === modifiers.has('ctrl') &&\n event.metaKey === modifiers.has('meta') &&\n event.shiftKey === modifiers.has('shift')\n );\n}\n","import type { ShortcutStep } from './parser';\nimport { canonicalizeShortcut, parseShortcut } from './parser';\nimport type { BindingEntry } from './types';\n\nexport interface ConflictOptions {\n modKey?: 'ctrl' | 'meta';\n trigger?: 'keydown' | 'keyup';\n}\n\nfunction isPrefixOf(shorter: readonly ShortcutStep[], longer: readonly ShortcutStep[]): boolean {\n if (shorter.length > longer.length) return false;\n\n return canonicalizeShortcut(shorter) === canonicalizeShortcut(longer.slice(0, shorter.length));\n}\n\n/**\n * Finds registered bindings that would conflict with a proposed shortcut — an exact duplicate, a\n * shorter binding that would shadow it as a chord prefix, or a longer binding it would itself\n * shadow. Only compares against entries sharing the same `trigger` — `'keydown'` and `'keyup'`\n * chords are matched independently and never conflict with each other.\n *\n * @example\n * const entries = map.listBindings();\n * findShortcutConflicts('g g', entries); // → the entry for 'g', if one is bound\n */\nexport function findShortcutConflicts(\n shortcut: string,\n entries: readonly BindingEntry[],\n options: ConflictOptions = {},\n): BindingEntry[] {\n const { modKey, trigger = 'keydown' } = options;\n\n // An empty/whitespace-only shortcut would be a trivial prefix of every entry — never report it\n // as a conflict.\n if (shortcut.trim() === '') return [];\n\n const steps = parseShortcut(shortcut, modKey);\n\n return entries.filter(\n (entry) => entry.trigger === trigger && (isPrefixOf(steps, entry.shortcut) || isPrefixOf(entry.shortcut, steps)),\n );\n}\n","const isDev = !(globalThis as { __KEYMAP_PROD__?: boolean }).__KEYMAP_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/keymap] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import { warn } from './_dev';\nimport { detectModKey, parseShortcut } from './parser';\n\nconst KEY_SYMBOLS: Record<string, string> = {\n ' ': 'Space',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n arrowup: '↑',\n backspace: '⌫',\n delete: '⌦',\n end: 'End',\n enter: '↵',\n escape: 'Esc',\n home: 'Home',\n pagedown: 'PgDn',\n pageup: 'PgUp',\n tab: '⇥',\n};\n\nconst MOD_SYMBOLS_MAC: Record<string, string> = {\n alt: '⌥',\n ctrl: '⌃',\n meta: '⌘',\n shift: '⇧',\n};\n\nconst MOD_LABELS_OTHER: Record<string, string> = {\n alt: 'Alt',\n ctrl: 'Ctrl',\n meta: 'Win',\n shift: 'Shift',\n};\n\nconst MOD_ORDER = ['ctrl', 'alt', 'shift', 'meta'] as const;\n\n/**\n * Formats a shortcut string into a human-readable display string.\n *\n * On Mac (when `modKey` is `'meta'`), uses standard Mac symbols (⌘, ⌥, ⇧, ⌃).\n * On other platforms, uses word labels (Ctrl, Alt, Shift, Win).\n *\n * @example\n * formatShortcut('mod+shift+p', 'meta') // '⇧⌘P' (canonical order: ctrl, alt, shift, meta)\n * formatShortcut('mod+shift+p', 'ctrl') // 'Ctrl+Shift+P'\n * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'\n */\nexport function formatShortcut(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string {\n if (!shortcut.trim()) {\n warn(`formatShortcut() received an empty shortcut string`);\n\n return '';\n }\n\n let steps: ReturnType<typeof parseShortcut>;\n\n try {\n steps = parseShortcut(shortcut, modKey);\n } catch {\n warn(`formatShortcut() received an invalid shortcut: \"${shortcut}\"`);\n\n return '';\n }\n\n const isMac = modKey === 'meta';\n\n return steps\n .map((step) => {\n const modParts = MOD_ORDER.filter((m) => step.modifiers.has(m)).map((m) =>\n isMac ? MOD_SYMBOLS_MAC[m] : MOD_LABELS_OTHER[m],\n );\n\n const keyLabel = KEY_SYMBOLS[step.key] ?? step.key.toUpperCase();\n\n return isMac ? `${modParts.join('')}${keyLabel}` : [...modParts, keyLabel].join('+');\n })\n .join(' ');\n}\n","import { warn } from './_dev';\nimport { KeymapError } from './errors';\nimport type { Shortcut } from './parser';\nimport { detectModKey, matchStep, parseShortcut } from './parser';\nimport type { Binding, BindingEntry, Handler, Keymap, KeymapEvent, KeymapOptions, When } from './types';\n\ntype ParsedBinding = {\n id: string;\n handler: Handler;\n shortcut: Shortcut;\n trigger: 'keydown' | 'keyup';\n when?: When;\n preventDefault: boolean;\n stopPropagation: boolean;\n};\n\ntype ChordAdvanceResult =\n | { type: 'none' }\n | { bindings: ParsedBinding[]; type: 'pending' }\n | { binding: ParsedBinding; type: 'match' };\ntype ChordTrackerCallbacks = {\n onCancel: () => void;\n onProgress: (steps: Shortcut, started: boolean) => void;\n onTimeout: () => void;\n};\ntype ChordTracker = ReturnType<typeof createChordTracker>;\ntype MountedTarget = {\n keydown: ChordTracker;\n keyup: ChordTracker;\n onKeydown: EventListener;\n onKeyup: EventListener;\n refs: number;\n};\n\nconst noop = () => {};\n\nfunction createChordTracker(\n getBindings: () => ParsedBinding[],\n chordTimeout: number,\n callbacks: ChordTrackerCallbacks,\n) {\n let pendingIndex = 0;\n let candidates: ParsedBinding[] = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n function reset(notify = true): void {\n if (timer !== undefined) clearTimeout(timer);\n\n const hadPending = pendingIndex > 0;\n\n timer = undefined;\n pendingIndex = 0;\n candidates = [];\n\n if (notify && hadPending) callbacks.onCancel();\n }\n\n function advance(event: KeyboardEvent): ChordAdvanceResult {\n const pool = pendingIndex === 0 ? getBindings() : candidates;\n const matched = pool.filter((binding) => {\n const step = binding.shortcut[pendingIndex];\n\n return step !== undefined && matchStep(event, step) && (!binding.when || binding.when(event));\n });\n\n if (matched.length === 0) {\n const retryFromRoot = pendingIndex !== 0;\n\n reset();\n\n return retryFromRoot ? advance(event) : { type: 'none' };\n }\n\n if (timer !== undefined) clearTimeout(timer);\n\n timer = undefined;\n\n const completed = matched.find((binding) => binding.shortcut.length === pendingIndex + 1);\n\n if (completed) {\n reset(false);\n\n return { binding: completed, type: 'match' };\n }\n\n const started = pendingIndex === 0;\n\n candidates = matched;\n pendingIndex += 1;\n callbacks.onProgress(matched[0]!.shortcut.slice(0, pendingIndex), started);\n timer = setTimeout(() => {\n callbacks.onTimeout();\n reset(false);\n }, chordTimeout);\n\n return { bindings: matched, type: 'pending' };\n }\n\n return { advance, reset };\n}\n\n/**\n * Creates a headless keyboard shortcut manager with target-local chord state.\n *\n * Pass an ordered array of `Binding` objects (each with an explicit `id`,\n * `shortcut`, and `handler`), then call `.mount(target)` to attach to any\n * `EventTarget`. Supports chord sequences (e.g. `\"ctrl+k ctrl+s\"`), per-binding\n * `when` guards, `trigger` (keydown/keyup), and per-binding\n * `preventDefault`/`stopPropagation`. Dynamic `bind`/`unbind` use the binding\n * `id`, so duplicate shortcuts can coexist with different ids.\n *\n * @example\n * const map = createKeymap([\n * { id: 'save', shortcut: 'mod+k mod+s', handler: save },\n * { id: 'palette', shortcut: 'mod+shift+p', handler: openPalette },\n * { id: 'top', shortcut: 'g g', handler: goToTop },\n * { id: 'close', shortcut: 'escape', handler: closePanel, when: (event) => !isEditableTarget(event.target) },\n * { id: 'play', shortcut: 'space', handler: togglePlay, trigger: 'keyup' },\n * ], { modKey: 'ctrl' });\n * const unmount = map.mount(document);\n */\nexport function createKeymap(initialBindings: readonly Binding[] = [], options: KeymapOptions = {}): Keymap {\n const { chordTimeout: rawChordTimeout = 1000, modKey = detectModKey(), when: globalWhen } = options;\n const chordTimeout = Number.isFinite(rawChordTimeout) && rawChordTimeout > 0 ? rawChordTimeout : 1000;\n\n if (chordTimeout !== rawChordTimeout) {\n warn(`chordTimeout must be a positive finite number; received ${rawChordTimeout}. Using default of 1000ms.`);\n }\n\n // Ordered map keyed by binding id — preserves insertion order and supports\n // duplicate shortcuts with different ids.\n const bindings = new Map<string, ParsedBinding>();\n const mounted = new Map<EventTarget, MountedTarget>();\n const tappers = new Set<(event: KeymapEvent) => void>();\n const disposalController = new AbortController();\n let bindingsDown: ParsedBinding[] = [];\n let bindingsUp: ParsedBinding[] = [];\n let disposed = false;\n\n function assertActive(): void {\n if (disposed) throw new KeymapError('Keymap is disposed');\n }\n\n function rebuildTriggerCaches(): void {\n bindingsDown = [];\n bindingsUp = [];\n\n for (const binding of bindings.values()) {\n if (binding.trigger === 'keydown') bindingsDown.push(binding);\n else bindingsUp.push(binding);\n }\n\n for (const record of mounted.values()) {\n record.keydown.reset();\n record.keyup.reset();\n }\n }\n\n function addBinding(binding: Binding): ParsedBinding {\n const parsedBinding: ParsedBinding = {\n handler: binding.handler,\n id: binding.id,\n preventDefault: binding.preventDefault ?? true,\n shortcut: parseShortcut(binding.shortcut, modKey),\n stopPropagation: binding.stopPropagation ?? false,\n trigger: binding.trigger ?? 'keydown',\n when: binding.when,\n };\n\n bindings.set(parsedBinding.id, parsedBinding);\n rebuildTriggerCaches();\n\n return parsedBinding;\n }\n\n function removeById(id: string): boolean {\n const existed = bindings.delete(id);\n\n if (existed) rebuildTriggerCaches();\n\n return existed;\n }\n\n function toBindingEntry(binding: ParsedBinding): BindingEntry {\n return {\n id: binding.id,\n preventDefault: binding.preventDefault,\n shortcut: binding.shortcut.map((step) => ({ key: step.key, modifiers: new Set(step.modifiers) })),\n stopPropagation: binding.stopPropagation,\n trigger: binding.trigger,\n };\n }\n\n function emitTap(event: KeymapEvent): void {\n if (tappers.size === 0) return;\n\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {}\n }\n }\n\n function makeHandler(target: EventTarget, trigger: 'keydown' | 'keyup', chord: ChordTracker): EventListener {\n return (event) => {\n const path = typeof event.composedPath === 'function' ? event.composedPath() : [];\n const nearestMountedTarget = path.find((pathTarget) => mounted.has(pathTarget));\n\n if (nearestMountedTarget && nearestMountedTarget !== target) return;\n\n const keyboardEvent = event as KeyboardEvent;\n\n if (disposed) return;\n\n if (globalWhen && !globalWhen(keyboardEvent)) {\n chord.reset();\n\n return;\n }\n\n const result = chord.advance(keyboardEvent);\n\n if (result.type === 'none') return;\n\n const matchedBindings = result.type === 'pending' ? result.bindings : [result.binding];\n\n if (matchedBindings.some((binding) => binding.preventDefault)) keyboardEvent.preventDefault();\n if (matchedBindings.some((binding) => binding.stopPropagation)) keyboardEvent.stopPropagation();\n\n if (result.type === 'pending') return;\n\n if (tappers.size > 0) {\n emitTap({ binding: toBindingEntry(result.binding), target, trigger, type: 'match' });\n }\n\n result.binding.handler(keyboardEvent);\n };\n }\n\n for (const binding of initialBindings) addBinding(binding);\n\n return {\n bind(binding: Binding): () => void {\n assertActive();\n\n const parsedBinding = addBinding(binding);\n\n return () => {\n if (!disposed && bindings.get(parsedBinding.id) === parsedBinding) removeById(parsedBinding.id);\n };\n },\n\n get disposalSignal(): AbortSignal {\n return disposalController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n\n for (const [target, record] of mounted) {\n target.removeEventListener('keydown', record.onKeydown);\n target.removeEventListener('keyup', record.onKeyup);\n record.keydown.reset();\n record.keyup.reset();\n }\n\n emitTap({ type: 'dispose' });\n disposalController.abort();\n mounted.clear();\n bindings.clear();\n bindingsDown = [];\n bindingsUp = [];\n tappers.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n listBindings(): readonly BindingEntry[] {\n return [...bindings.values()].map(toBindingEntry);\n },\n\n mount(target: EventTarget): () => void {\n assertActive();\n\n let record = mounted.get(target);\n\n if (!record) {\n const createCallbacks = (trigger: 'keydown' | 'keyup'): ChordTrackerCallbacks => ({\n onCancel: () => {\n if (tappers.size > 0) emitTap({ target, trigger, type: 'chord-cancel' });\n },\n onProgress: (steps, started) => {\n if (tappers.size === 0) return;\n\n const snapshot = steps.map((step) => ({ key: step.key, modifiers: new Set(step.modifiers) }));\n\n if (started) emitTap({ step: snapshot[0]!, target, trigger, type: 'chord-start' });\n else emitTap({ steps: snapshot, target, trigger, type: 'chord-progress' });\n },\n onTimeout: () => {\n if (tappers.size > 0) emitTap({ target, trigger, type: 'chord-timeout' });\n },\n });\n const keydown = createChordTracker(() => bindingsDown, chordTimeout, createCallbacks('keydown'));\n const keyup = createChordTracker(() => bindingsUp, chordTimeout, createCallbacks('keyup'));\n const onKeydown = makeHandler(target, 'keydown', keydown);\n const onKeyup = makeHandler(target, 'keyup', keyup);\n\n record = { keydown, keyup, onKeydown, onKeyup, refs: 0 };\n mounted.set(target, record);\n target.addEventListener('keydown', onKeydown);\n target.addEventListener('keyup', onKeyup);\n }\n\n record.refs += 1;\n\n let unmounted = false;\n\n return () => {\n if (unmounted) return;\n\n unmounted = true;\n record!.refs -= 1;\n\n if (record?.refs > 0) return;\n\n target.removeEventListener('keydown', record?.onKeydown);\n target.removeEventListener('keyup', record?.onKeyup);\n record?.keydown.reset();\n record?.keyup.reset();\n mounted.delete(target);\n };\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n\n tap(handler: (event: KeymapEvent) => void, options?: { signal?: AbortSignal }): () => void {\n const signal = options?.signal\n ? AbortSignal.any([disposalController.signal, options.signal])\n : disposalController.signal;\n\n if (signal.aborted) return noop;\n\n tappers.add(handler);\n\n const onAbort = () => tappers.delete(handler);\n\n signal.addEventListener('abort', onAbort, { once: true });\n\n return () => {\n tappers.delete(handler);\n signal.removeEventListener('abort', onAbort);\n };\n },\n\n unbind(id: string): void {\n assertActive();\n\n if (!removeById(id)) {\n warn(`unbind() called for unknown id: \"${id}\"`);\n }\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"keymap.cjs","names":[],"sources":["../src/errors.ts","../src/parser.ts","../src/conflicts.ts","../src/_dev.ts","../src/format.ts","../src/keymap.ts"],"sourcesContent":["/** Base class for all keymap errors. Use `instanceof KeymapError` to catch any keymap-originated error. */\nexport class KeymapError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a shortcut string cannot be parsed — ambiguous or invalid key step. */\nexport class KeymapParseError extends KeymapError {}\n\n/** Thrown when keymap configuration is invalid, such as a non-positive `chordTimeout`. */\nexport class KeymapConfigError extends KeymapError {}\n","import { KeymapParseError } from './errors';\n\nexport type ModifierKey = 'alt' | 'ctrl' | 'meta' | 'shift';\n\nexport type ShortcutStep = {\n key: string;\n modifiers: Set<ModifierKey>;\n};\n\nexport type Shortcut = ShortcutStep[];\n\nexport function detectModKey(): 'ctrl' | 'meta' {\n if (typeof navigator === 'undefined') return 'ctrl';\n\n const platform =\n (navigator as Navigator & { userAgentData?: { platform?: string } }).userAgentData?.platform ??\n navigator.platform ??\n '';\n\n return /mac/i.test(platform) ? 'meta' : 'ctrl';\n}\n\nconst MODIFIER_ALIASES_BASE: Record<string, ModifierKey> = {\n alt: 'alt',\n cmd: 'meta',\n command: 'meta',\n control: 'ctrl',\n ctrl: 'ctrl',\n meta: 'meta',\n opt: 'alt',\n option: 'alt',\n shift: 'shift',\n win: 'meta',\n};\n\nconst SPECIAL_KEY_ALIASES: Record<string, string> = {\n del: 'delete',\n down: 'arrowdown',\n esc: 'escape',\n left: 'arrowleft',\n right: 'arrowright',\n space: ' ',\n spacebar: ' ',\n up: 'arrowup',\n};\n\nfunction parseStepStrict(raw: string, modKey: 'ctrl' | 'meta'): ShortcutStep {\n const parts = raw\n .trim()\n .split('+')\n .filter(Boolean)\n .map((part) => part.toLowerCase());\n\n if (parts.length === 0) throw new KeymapParseError(`Invalid shortcut step: \"${raw}\"`);\n\n const aliases: Record<string, ModifierKey> = { ...MODIFIER_ALIASES_BASE, mod: modKey };\n const modifiers = new Set<ModifierKey>();\n const keyParts: string[] = [];\n\n for (const part of parts) {\n const modifier = Object.hasOwn(aliases, part) ? aliases[part]! : undefined;\n\n if (modifier) modifiers.add(modifier);\n else keyParts.push(part);\n }\n\n if (keyParts.length === 0) throw new KeymapParseError(`Invalid shortcut step: \"${raw}\"`);\n\n if (keyParts.length > 1) {\n throw new KeymapParseError(`Ambiguous shortcut step: \"${raw}\" — multiple non-modifier keys found`);\n }\n\n const rawKey = keyParts[0]!;\n const key = Object.hasOwn(SPECIAL_KEY_ALIASES, rawKey) ? SPECIAL_KEY_ALIASES[rawKey]! : rawKey;\n\n return { key, modifiers };\n}\n\nexport function parseStep(raw: string, modKey: 'ctrl' | 'meta' = detectModKey()): ShortcutStep | null {\n try {\n return parseStepStrict(raw, modKey);\n } catch {\n return null;\n }\n}\n\nexport function parseShortcut(raw: string, modKey: 'ctrl' | 'meta' = detectModKey()): Shortcut {\n const steps = raw.trim().split(/\\s+/).filter(Boolean);\n\n if (steps.length === 0) throw new KeymapParseError('Shortcut must contain at least one key step');\n\n return steps.map((step) => parseStepStrict(step, modKey));\n}\n\nexport function canonicalizeShortcut(steps: readonly ShortcutStep[]): string {\n return steps\n .map((step) => {\n const modifiers = [...step.modifiers].sort().join('+');\n\n return modifiers ? `${modifiers}+${step.key}` : step.key;\n })\n .join(' ');\n}\n\nexport function matchStep(event: KeyboardEvent, step: ShortcutStep): boolean {\n // Headless usage (SSR, non-DOM `EventTarget`s, hand-built test events) means `event` isn't\n // guaranteed to be a real `KeyboardEvent` at runtime even though the type says so — treat a\n // missing/non-string `key` as \"doesn't match\" rather than throwing inside the event listener.\n if (typeof event.key !== 'string') return false;\n\n if (event.key.toLowerCase() !== step.key) return false;\n\n const { modifiers } = step;\n\n return (\n event.altKey === modifiers.has('alt') &&\n event.ctrlKey === modifiers.has('ctrl') &&\n event.metaKey === modifiers.has('meta') &&\n event.shiftKey === modifiers.has('shift')\n );\n}\n\n/**\n * Match a keyboard event against a single shortcut pattern such as `'ArrowDown'`,\n * `'esc'`, `'mod+k'`, or `'shift+Home'`. Aliases and `mod` resolve through the same\n * parser `createKeymap` uses, and modifier state must match exactly: `ArrowDown`\n * does not match Ctrl+ArrowDown. Returns `false` for unparseable patterns instead\n * of throwing, so it is safe inside event listeners.\n */\nexport function matchKey(event: KeyboardEvent, pattern: string, modKey: 'ctrl' | 'meta' = detectModKey()): boolean {\n const step = parseStep(pattern, modKey);\n\n return step !== null && matchStep(event, step);\n}\n","import type { ShortcutStep } from './parser';\nimport { canonicalizeShortcut, parseShortcut } from './parser';\nimport type { BindingEntry } from './types';\n\nexport interface ConflictOptions {\n modKey?: 'ctrl' | 'meta';\n trigger?: 'keydown' | 'keyup';\n}\n\nfunction isPrefixOf(shorter: readonly ShortcutStep[], longer: readonly ShortcutStep[]): boolean {\n if (shorter.length > longer.length) return false;\n\n return canonicalizeShortcut(shorter) === canonicalizeShortcut(longer.slice(0, shorter.length));\n}\n\n/**\n * Finds registered bindings that would conflict with a proposed shortcut — an exact duplicate, a\n * shorter binding that would shadow it as a chord prefix, or a longer binding it would itself\n * shadow. Only compares against entries sharing the same `trigger` — `'keydown'` and `'keyup'`\n * chords are matched independently and never conflict with each other.\n *\n * @example\n * const entries = map.listBindings();\n * findShortcutConflicts('g g', entries); // → the entry for 'g', if one is bound\n */\nexport function findShortcutConflicts(\n shortcut: string,\n entries: readonly BindingEntry[],\n options: ConflictOptions = {},\n): BindingEntry[] {\n const { modKey, trigger = 'keydown' } = options;\n\n // An empty/whitespace-only shortcut would be a trivial prefix of every entry — never report it\n // as a conflict.\n if (shortcut.trim() === '') return [];\n\n const steps = parseShortcut(shortcut, modKey);\n\n return entries.filter(\n (entry) => entry.trigger === trigger && (isPrefixOf(steps, entry.shortcut) || isPrefixOf(entry.shortcut, steps)),\n );\n}\n","const isDev = !(globalThis as { __KEYMAP_PROD__?: boolean }).__KEYMAP_PROD__;\n\n/** @internal */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/keymap] ${msg}`);\n}\n\n/** @internal — Run fn only in dev builds. Use when dev-only logic goes beyond a single warn() / error() call. */\nexport function devOnly(fn: () => void): void {\n if (isDev) fn();\n}\n","import { warn } from './_dev';\nimport { detectModKey, parseShortcut } from './parser';\n\nconst KEY_SYMBOLS: Record<string, string> = {\n ' ': 'Space',\n arrowdown: '↓',\n arrowleft: '←',\n arrowright: '→',\n arrowup: '↑',\n backspace: '⌫',\n delete: '⌦',\n end: 'End',\n enter: '↵',\n escape: 'Esc',\n home: 'Home',\n pagedown: 'PgDn',\n pageup: 'PgUp',\n tab: '⇥',\n};\n\nconst MOD_SYMBOLS_MAC: Record<string, string> = {\n alt: '⌥',\n ctrl: '⌃',\n meta: '⌘',\n shift: '⇧',\n};\n\nconst MOD_LABELS_OTHER: Record<string, string> = {\n alt: 'Alt',\n ctrl: 'Ctrl',\n meta: 'Win',\n shift: 'Shift',\n};\n\nconst MOD_ORDER = ['ctrl', 'alt', 'shift', 'meta'] as const;\n\n/**\n * Formats a shortcut string into a human-readable display string.\n *\n * On Mac (when `modKey` is `'meta'`), uses standard Mac symbols (⌘, ⌥, ⇧, ⌃).\n * On other platforms, uses word labels (Ctrl, Alt, Shift, Win).\n *\n * @example\n * formatShortcut('mod+shift+p', 'meta') // '⇧⌘P' (canonical order: ctrl, alt, shift, meta)\n * formatShortcut('mod+shift+p', 'ctrl') // 'Ctrl+Shift+P'\n * formatShortcut('ctrl+k ctrl+s', 'meta') // '⌃K ⌃S'\n */\nexport function formatShortcut(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string {\n if (!shortcut.trim()) {\n warn(`formatShortcut() received an empty shortcut string`);\n\n return '';\n }\n\n return formatShortcutParts(shortcut, modKey)\n .map((step) => step.join(modKey === 'meta' ? '' : '+'))\n .join(' ');\n}\n\n/**\n * Formats a shortcut into keycap-sized segments: one array per step (a chord has several\n * steps), one label per keycap — Mac modifier symbols are their own keycaps, other\n * platforms get whole-word labels (`['Ctrl', 'Shift', 'P']`).\n *\n * Made for keycap UIs that need the pieces `formatShortcut` joins into one string, so\n * \"press ⌘ together with Z\" can render as two keycaps while the flat form stays \"⌘Z\".\n *\n * @example\n * formatShortcutParts('mod+z', 'meta') // [['⌘', 'Z']]\n * formatShortcutParts('mod+shift+z', 'ctrl') // [['Ctrl', 'Shift', 'Z']]\n * formatShortcutParts('g h', 'ctrl') // [['G'], ['H']]\n */\nexport function formatShortcutParts(shortcut: string, modKey: 'ctrl' | 'meta' = detectModKey()): string[][] {\n if (!shortcut.trim()) {\n warn(`formatShortcutParts() received an empty shortcut string: \"${shortcut}\"`);\n\n return [];\n }\n\n let steps: ReturnType<typeof parseShortcut>;\n\n try {\n steps = parseShortcut(shortcut, modKey);\n } catch {\n warn(`formatShortcutParts() received an invalid shortcut: \"${shortcut}\"`);\n\n return [];\n }\n\n const isMac = modKey === 'meta';\n\n return steps.map((step) => {\n const modParts = MOD_ORDER.filter((m) => step.modifiers.has(m)).map((m) =>\n isMac ? MOD_SYMBOLS_MAC[m] : MOD_LABELS_OTHER[m],\n );\n\n const keyLabel = KEY_SYMBOLS[step.key] ?? step.key.toUpperCase();\n\n return [...modParts, keyLabel];\n });\n}\n","import { warn } from './_dev';\nimport { KeymapConfigError, KeymapError } from './errors';\nimport type { Shortcut } from './parser';\nimport { detectModKey, matchStep, parseShortcut } from './parser';\nimport type { Binding, BindingEntry, Handler, Keymap, KeymapEvent, KeymapOptions, When } from './types';\n\ntype ParsedBinding = {\n id: string;\n handler: Handler;\n shortcut: Shortcut;\n trigger: 'keydown' | 'keyup';\n when?: When;\n preventDefault: boolean;\n stopPropagation: boolean;\n};\n\ntype ChordAdvanceResult =\n | { type: 'none' }\n | { bindings: ParsedBinding[]; type: 'pending' }\n | { binding: ParsedBinding; type: 'match' };\ntype ChordTrackerCallbacks = {\n onCancel: () => void;\n onProgress: (steps: Shortcut, started: boolean) => void;\n onTimeout: () => void;\n};\ntype ChordTracker = ReturnType<typeof createChordTracker>;\ntype MountedTarget = {\n keydown: ChordTracker;\n keyup: ChordTracker;\n onKeydown: EventListener;\n onKeyup: EventListener;\n refs: number;\n};\n\nconst noop = () => {};\n\nfunction createChordTracker(\n getBindings: () => ParsedBinding[],\n chordTimeout: number,\n callbacks: ChordTrackerCallbacks,\n) {\n let pendingIndex = 0;\n let candidates: ParsedBinding[] = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n function reset(notify = true): void {\n if (timer !== undefined) clearTimeout(timer);\n\n const hadPending = pendingIndex > 0;\n\n timer = undefined;\n pendingIndex = 0;\n candidates = [];\n\n if (notify && hadPending) callbacks.onCancel();\n }\n\n function advance(event: KeyboardEvent): ChordAdvanceResult {\n const pool = pendingIndex === 0 ? getBindings() : candidates;\n const matched = pool.filter((binding) => {\n const step = binding.shortcut[pendingIndex];\n\n return step !== undefined && matchStep(event, step) && (!binding.when || binding.when(event));\n });\n\n if (matched.length === 0) {\n const retryFromRoot = pendingIndex !== 0;\n\n reset();\n\n return retryFromRoot ? advance(event) : { type: 'none' };\n }\n\n if (timer !== undefined) clearTimeout(timer);\n\n timer = undefined;\n\n const completed = matched.find((binding) => binding.shortcut.length === pendingIndex + 1);\n\n if (completed) {\n reset(false);\n\n return { binding: completed, type: 'match' };\n }\n\n const started = pendingIndex === 0;\n\n candidates = matched;\n pendingIndex += 1;\n callbacks.onProgress(matched[0]!.shortcut.slice(0, pendingIndex), started);\n timer = setTimeout(() => {\n callbacks.onTimeout();\n reset(false);\n }, chordTimeout);\n\n return { bindings: matched, type: 'pending' };\n }\n\n return { advance, reset };\n}\n\n/**\n * Creates a headless keyboard shortcut manager with target-local chord state.\n *\n * Pass an ordered array of `Binding` objects (each with an explicit `id`,\n * `shortcut`, and `handler`), then call `.mount(target)` to attach to any\n * `EventTarget`. Supports chord sequences (e.g. `\"ctrl+k ctrl+s\"`), per-binding\n * `when` guards, `trigger` (keydown/keyup), and per-binding\n * `preventDefault`/`stopPropagation`. Dynamic `bind`/`unbind` use the binding\n * `id`, so duplicate shortcuts can coexist with different ids.\n *\n * @example\n * const map = createKeymap([\n * { id: 'save', shortcut: 'mod+k mod+s', handler: save },\n * { id: 'palette', shortcut: 'mod+shift+p', handler: openPalette },\n * { id: 'top', shortcut: 'g g', handler: goToTop },\n * { id: 'close', shortcut: 'escape', handler: closePanel, when: (event) => !isEditableTarget(event.target) },\n * { id: 'play', shortcut: 'space', handler: togglePlay, trigger: 'keyup' },\n * ], { modKey: 'ctrl' });\n * const unmount = map.mount(document);\n */\nexport function createKeymap(initialBindings: readonly Binding[] = [], options: KeymapOptions = {}): Keymap {\n const { chordTimeout = 1000, modKey = detectModKey(), when: globalWhen } = options;\n\n if (!Number.isFinite(chordTimeout) || chordTimeout <= 0) {\n throw new KeymapConfigError(`chordTimeout must be a positive finite number; received ${chordTimeout}.`);\n }\n\n // Ordered map keyed by binding id — preserves insertion order and supports\n // duplicate shortcuts with different ids.\n const bindings = new Map<string, ParsedBinding>();\n const mounted = new Map<EventTarget, MountedTarget>();\n const tappers = new Set<(event: KeymapEvent) => void>();\n const disposalController = new AbortController();\n let bindingsDown: ParsedBinding[] = [];\n let bindingsUp: ParsedBinding[] = [];\n let disposed = false;\n\n function assertActive(): void {\n if (disposed) throw new KeymapError('Keymap is disposed');\n }\n\n function rebuildTriggerCaches(): void {\n bindingsDown = [];\n bindingsUp = [];\n\n for (const binding of bindings.values()) {\n if (binding.trigger === 'keydown') bindingsDown.push(binding);\n else bindingsUp.push(binding);\n }\n\n for (const record of mounted.values()) {\n record.keydown.reset();\n record.keyup.reset();\n }\n }\n\n function addBinding(binding: Binding): ParsedBinding {\n const parsedBinding: ParsedBinding = {\n handler: binding.handler,\n id: binding.id,\n preventDefault: binding.preventDefault ?? true,\n shortcut: parseShortcut(binding.shortcut, modKey),\n stopPropagation: binding.stopPropagation ?? false,\n trigger: binding.trigger ?? 'keydown',\n when: binding.when,\n };\n\n bindings.set(parsedBinding.id, parsedBinding);\n rebuildTriggerCaches();\n\n return parsedBinding;\n }\n\n function removeById(id: string): boolean {\n const existed = bindings.delete(id);\n\n if (existed) rebuildTriggerCaches();\n\n return existed;\n }\n\n function toBindingEntry(binding: ParsedBinding): BindingEntry {\n return {\n id: binding.id,\n preventDefault: binding.preventDefault,\n shortcut: binding.shortcut.map((step) => ({ key: step.key, modifiers: new Set(step.modifiers) })),\n stopPropagation: binding.stopPropagation,\n trigger: binding.trigger,\n };\n }\n\n function emitTap(event: KeymapEvent): void {\n if (tappers.size === 0) return;\n\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {}\n }\n }\n\n function makeHandler(target: EventTarget, trigger: 'keydown' | 'keyup', chord: ChordTracker): EventListener {\n return (event) => {\n const path = typeof event.composedPath === 'function' ? event.composedPath() : [];\n const nearestMountedTarget = path.find((pathTarget) => mounted.has(pathTarget));\n\n if (nearestMountedTarget && nearestMountedTarget !== target) return;\n\n const keyboardEvent = event as KeyboardEvent;\n\n if (disposed) return;\n\n if (globalWhen && !globalWhen(keyboardEvent)) {\n chord.reset();\n\n return;\n }\n\n const result = chord.advance(keyboardEvent);\n\n if (result.type === 'none') return;\n\n const matchedBindings = result.type === 'pending' ? result.bindings : [result.binding];\n\n if (matchedBindings.some((binding) => binding.preventDefault)) keyboardEvent.preventDefault();\n if (matchedBindings.some((binding) => binding.stopPropagation)) keyboardEvent.stopPropagation();\n\n if (result.type === 'pending') return;\n\n if (tappers.size > 0) {\n emitTap({ binding: toBindingEntry(result.binding), target, trigger, type: 'match' });\n }\n\n result.binding.handler(keyboardEvent);\n };\n }\n\n for (const binding of initialBindings) addBinding(binding);\n\n return {\n bind(binding: Binding): () => void {\n assertActive();\n\n const parsedBinding = addBinding(binding);\n\n return () => {\n if (!disposed && bindings.get(parsedBinding.id) === parsedBinding) removeById(parsedBinding.id);\n };\n },\n\n get disposalSignal(): AbortSignal {\n return disposalController.signal;\n },\n\n dispose(): void {\n if (disposed) return;\n\n disposed = true;\n\n for (const [target, record] of mounted) {\n target.removeEventListener('keydown', record.onKeydown);\n target.removeEventListener('keyup', record.onKeyup);\n record.keydown.reset();\n record.keyup.reset();\n }\n\n emitTap({ type: 'dispose' });\n disposalController.abort();\n mounted.clear();\n bindings.clear();\n bindingsDown = [];\n bindingsUp = [];\n tappers.clear();\n },\n\n get disposed(): boolean {\n return disposed;\n },\n\n listBindings(): readonly BindingEntry[] {\n return [...bindings.values()].map(toBindingEntry);\n },\n\n mount(target: EventTarget): () => void {\n assertActive();\n\n let record = mounted.get(target);\n\n if (!record) {\n const createCallbacks = (trigger: 'keydown' | 'keyup'): ChordTrackerCallbacks => ({\n onCancel: () => {\n if (tappers.size > 0) emitTap({ target, trigger, type: 'chord-cancel' });\n },\n onProgress: (steps, started) => {\n if (tappers.size === 0) return;\n\n const snapshot = steps.map((step) => ({ key: step.key, modifiers: new Set(step.modifiers) }));\n\n if (started) emitTap({ step: snapshot[0]!, target, trigger, type: 'chord-start' });\n else emitTap({ steps: snapshot, target, trigger, type: 'chord-progress' });\n },\n onTimeout: () => {\n if (tappers.size > 0) emitTap({ target, trigger, type: 'chord-timeout' });\n },\n });\n const keydown = createChordTracker(() => bindingsDown, chordTimeout, createCallbacks('keydown'));\n const keyup = createChordTracker(() => bindingsUp, chordTimeout, createCallbacks('keyup'));\n const onKeydown = makeHandler(target, 'keydown', keydown);\n const onKeyup = makeHandler(target, 'keyup', keyup);\n\n record = { keydown, keyup, onKeydown, onKeyup, refs: 0 };\n mounted.set(target, record);\n target.addEventListener('keydown', onKeydown);\n target.addEventListener('keyup', onKeyup);\n }\n\n record.refs += 1;\n\n let unmounted = false;\n\n return () => {\n if (unmounted) return;\n\n unmounted = true;\n record!.refs -= 1;\n\n if (record?.refs > 0) return;\n\n target.removeEventListener('keydown', record?.onKeydown);\n target.removeEventListener('keyup', record?.onKeyup);\n record?.keydown.reset();\n record?.keyup.reset();\n mounted.delete(target);\n };\n },\n\n [Symbol.dispose](): void {\n this.dispose();\n },\n\n tap(handler: (event: KeymapEvent) => void, options?: { signal?: AbortSignal }): () => void {\n const signal = options?.signal\n ? AbortSignal.any([disposalController.signal, options.signal])\n : disposalController.signal;\n\n if (signal.aborted) return noop;\n\n tappers.add(handler);\n\n const onAbort = () => tappers.delete(handler);\n\n signal.addEventListener('abort', onAbort, { once: true });\n\n return () => {\n tappers.delete(handler);\n signal.removeEventListener('abort', onAbort);\n };\n },\n\n unbind(id: string): void {\n assertActive();\n\n if (!removeById(id)) {\n warn(`unbind() called for unknown id: \"${id}\"`);\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"keymap.d.ts","sourceRoot":"","sources":["../src/keymap.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAyB,MAAM,EAAe,aAAa,EAAQ,MAAM,SAAS,CAAC;AAiGxG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,eAAe,GAAE,SAAS,OAAO,EAAO,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAwP1G"}
1
+ {"version":3,"file":"keymap.d.ts","sourceRoot":"","sources":["../src/keymap.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAyB,MAAM,EAAe,aAAa,EAAQ,MAAM,SAAS,CAAC;AAiGxG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,eAAe,GAAE,SAAS,OAAO,EAAO,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAuP1G"}
@@ -1,2 +1,2 @@
1
- var Keymap=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},n=class extends t{};function r(){if(typeof navigator>`u`)return`ctrl`;let e=navigator.userAgentData?.platform??navigator.platform??``;return/mac/i.test(e)?`meta`:`ctrl`}var i={alt:`alt`,cmd:`meta`,command:`meta`,control:`ctrl`,ctrl:`ctrl`,meta:`meta`,opt:`alt`,option:`alt`,shift:`shift`,win:`meta`},a={del:`delete`,down:`arrowdown`,esc:`escape`,left:`arrowleft`,right:`arrowright`,space:` `,spacebar:` `,up:`arrowup`};function o(e,t){let r=e.trim().split(`+`).filter(Boolean).map(e=>e.toLowerCase());if(r.length===0)throw new n(`Invalid shortcut step: "${e}"`);let o={...i,mod:t},s=new Set,c=[];for(let e of r){let t=Object.hasOwn(o,e)?o[e]:void 0;t?s.add(t):c.push(e)}if(c.length===0)throw new n(`Invalid shortcut step: "${e}"`);if(c.length>1)throw new n(`Ambiguous shortcut step: "${e}" — multiple non-modifier keys found`);let l=c[0];return{key:Object.hasOwn(a,l)?a[l]:l,modifiers:s}}function s(e,t=r()){let i=e.trim().split(/\s+/).filter(Boolean);if(i.length===0)throw new n(`Shortcut must contain at least one key step`);return i.map(e=>o(e,t))}function c(e){return e.map(e=>{let t=[...e.modifiers].sort().join(`+`);return t?`${t}+${e.key}`:e.key}).join(` `)}function l(e,t){if(typeof e.key!=`string`||e.key.toLowerCase()!==t.key)return!1;let{modifiers:n}=t;return e.altKey===n.has(`alt`)&&e.ctrlKey===n.has(`ctrl`)&&e.metaKey===n.has(`meta`)&&e.shiftKey===n.has(`shift`)}function u(e,t){return e.length>t.length?!1:c(e)===c(t.slice(0,e.length))}function d(e,t,n={}){let{modKey:r,trigger:i=`keydown`}=n;if(e.trim()===``)return[];let a=s(e,r);return t.filter(e=>e.trigger===i&&(u(a,e.shortcut)||u(e.shortcut,a)))}var f={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},p={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},m={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},h=[`ctrl`,`alt`,`shift`,`meta`];function g(e,t=r()){if(!e.trim())return``;let n;try{n=s(e,t)}catch{return`${e}`,``}let i=t===`meta`;return n.map(e=>{let t=h.filter(t=>e.modifiers.has(t)).map(e=>i?p[e]:m[e]),n=f[e.key]??e.key.toUpperCase();return i?`${t.join(``)}${n}`:[...t,n].join(`+`)}).join(` `)}var _=()=>{};function v(e,t,n){let r=0,i=[],a;function o(e=!0){a!==void 0&&clearTimeout(a);let t=r>0;a=void 0,r=0,i=[],e&&t&&n.onCancel()}function s(c){let u=(r===0?e():i).filter(e=>{let t=e.shortcut[r];return t!==void 0&&l(c,t)&&(!e.when||e.when(c))});if(u.length===0){let e=r!==0;return o(),e?s(c):{type:`none`}}a!==void 0&&clearTimeout(a),a=void 0;let d=u.find(e=>e.shortcut.length===r+1);if(d)return o(!1),{binding:d,type:`match`};let f=r===0;return i=u,r+=1,n.onProgress(u[0].shortcut.slice(0,r),f),a=setTimeout(()=>{n.onTimeout(),o(!1)},t),{bindings:u,type:`pending`}}return{advance:s,reset:o}}function y(e=[],n={}){let{chordTimeout:i=1e3,modKey:a=r(),when:o}=n,c=Number.isFinite(i)&&i>0?i:1e3;c!==i&&`${i}`;let l=new Map,u=new Map,d=new Set,f=new AbortController,p=[],m=[],h=!1;function g(){if(h)throw new t(`Keymap is disposed`)}function y(){p=[],m=[];for(let e of l.values())e.trigger===`keydown`?p.push(e):m.push(e);for(let e of u.values())e.keydown.reset(),e.keyup.reset()}function b(e){let t={handler:e.handler,id:e.id,preventDefault:e.preventDefault??!0,shortcut:s(e.shortcut,a),stopPropagation:e.stopPropagation??!1,trigger:e.trigger??`keydown`,when:e.when};return l.set(t.id,t),y(),t}function x(e){let t=l.delete(e);return t&&y(),t}function S(e){return{id:e.id,preventDefault:e.preventDefault,shortcut:e.shortcut.map(e=>({key:e.key,modifiers:new Set(e.modifiers)})),stopPropagation:e.stopPropagation,trigger:e.trigger}}function C(e){if(d.size!==0)for(let t of d)try{t(e)}catch{}}function w(e,t,n){return r=>{let i=(typeof r.composedPath==`function`?r.composedPath():[]).find(e=>u.has(e));if(i&&i!==e)return;let a=r;if(h)return;if(o&&!o(a)){n.reset();return}let s=n.advance(a);if(s.type===`none`)return;let c=s.type===`pending`?s.bindings:[s.binding];c.some(e=>e.preventDefault)&&a.preventDefault(),c.some(e=>e.stopPropagation)&&a.stopPropagation(),s.type!==`pending`&&(d.size>0&&C({binding:S(s.binding),target:e,trigger:t,type:`match`}),s.binding.handler(a))}}for(let t of e)b(t);return{bind(e){g();let t=b(e);return()=>{!h&&l.get(t.id)===t&&x(t.id)}},get disposalSignal(){return f.signal},dispose(){if(!h){h=!0;for(let[e,t]of u)e.removeEventListener(`keydown`,t.onKeydown),e.removeEventListener(`keyup`,t.onKeyup),t.keydown.reset(),t.keyup.reset();C({type:`dispose`}),f.abort(),u.clear(),l.clear(),p=[],m=[],d.clear()}},get disposed(){return h},listBindings(){return[...l.values()].map(S)},mount(e){g();let t=u.get(e);if(!t){let n=t=>({onCancel:()=>{d.size>0&&C({target:e,trigger:t,type:`chord-cancel`})},onProgress:(n,r)=>{if(d.size===0)return;let i=n.map(e=>({key:e.key,modifiers:new Set(e.modifiers)}));C(r?{step:i[0],target:e,trigger:t,type:`chord-start`}:{steps:i,target:e,trigger:t,type:`chord-progress`})},onTimeout:()=>{d.size>0&&C({target:e,trigger:t,type:`chord-timeout`})}}),r=v(()=>p,c,n(`keydown`)),i=v(()=>m,c,n(`keyup`)),a=w(e,`keydown`,r),o=w(e,`keyup`,i);t={keydown:r,keyup:i,onKeydown:a,onKeyup:o,refs:0},u.set(e,t),e.addEventListener(`keydown`,a),e.addEventListener(`keyup`,o)}t.refs+=1;let n=!1;return()=>{n||(n=!0,--t.refs,!(t?.refs>0)&&(e.removeEventListener(`keydown`,t?.onKeydown),e.removeEventListener(`keyup`,t?.onKeyup),t?.keydown.reset(),t?.keyup.reset(),u.delete(e)))}},[Symbol.dispose](){this.dispose()},tap(e,t){let n=t?.signal?AbortSignal.any([f.signal,t.signal]):f.signal;if(n.aborted)return _;d.add(e);let r=()=>d.delete(e);return n.addEventListener(`abort`,r,{once:!0}),()=>{d.delete(e),n.removeEventListener(`abort`,r)}},unbind(e){g(),x(e)||`${e}`}}}return e.KeymapError=t,e.KeymapParseError=n,e.createKeymap=y,e.findShortcutConflicts=d,e.formatShortcut=g,e})({});
1
+ var Keymap=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var KeymapError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},KeymapParseError=class extends KeymapError{},KeymapConfigError=class extends KeymapError{};function detectModKey(){if(typeof navigator>`u`)return`ctrl`;let e=navigator.userAgentData?.platform??navigator.platform??``;return/mac/i.test(e)?`meta`:`ctrl`}var t={alt:`alt`,cmd:`meta`,command:`meta`,control:`ctrl`,ctrl:`ctrl`,meta:`meta`,opt:`alt`,option:`alt`,shift:`shift`,win:`meta`},n={del:`delete`,down:`arrowdown`,esc:`escape`,left:`arrowleft`,right:`arrowright`,space:` `,spacebar:` `,up:`arrowup`};function parseStepStrict(e,r){let i=e.trim().split(`+`).filter(Boolean).map(e=>e.toLowerCase());if(i.length===0)throw new KeymapParseError(`Invalid shortcut step: "${e}"`);let a={...t,mod:r},o=new Set,s=[];for(let e of i){let t=Object.hasOwn(a,e)?a[e]:void 0;t?o.add(t):s.push(e)}if(s.length===0)throw new KeymapParseError(`Invalid shortcut step: "${e}"`);if(s.length>1)throw new KeymapParseError(`Ambiguous shortcut step: "${e}" — multiple non-modifier keys found`);let c=s[0];return{key:Object.hasOwn(n,c)?n[c]:c,modifiers:o}}function parseStep(e,t=detectModKey()){try{return parseStepStrict(e,t)}catch{return null}}function parseShortcut(e,t=detectModKey()){let n=e.trim().split(/\s+/).filter(Boolean);if(n.length===0)throw new KeymapParseError(`Shortcut must contain at least one key step`);return n.map(e=>parseStepStrict(e,t))}function canonicalizeShortcut(e){return e.map(e=>{let t=[...e.modifiers].sort().join(`+`);return t?`${t}+${e.key}`:e.key}).join(` `)}function matchStep(e,t){if(typeof e.key!=`string`||e.key.toLowerCase()!==t.key)return!1;let{modifiers:n}=t;return e.altKey===n.has(`alt`)&&e.ctrlKey===n.has(`ctrl`)&&e.metaKey===n.has(`meta`)&&e.shiftKey===n.has(`shift`)}function matchKey(e,t,n=detectModKey()){let r=parseStep(t,n);return r!==null&&matchStep(e,r)}function isPrefixOf(e,t){return e.length>t.length?!1:canonicalizeShortcut(e)===canonicalizeShortcut(t.slice(0,e.length))}function findShortcutConflicts(e,t,n={}){let{modKey:r,trigger:i=`keydown`}=n;if(e.trim()===``)return[];let a=parseShortcut(e,r);return t.filter(e=>e.trigger===i&&(isPrefixOf(a,e.shortcut)||isPrefixOf(e.shortcut,a)))}var r={" ":`Space`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,arrowup:`↑`,backspace:`⌫`,delete:`⌦`,end:`End`,enter:`↵`,escape:`Esc`,home:`Home`,pagedown:`PgDn`,pageup:`PgUp`,tab:`⇥`},i={alt:`⌥`,ctrl:`⌃`,meta:`⌘`,shift:`⇧`},a={alt:`Alt`,ctrl:`Ctrl`,meta:`Win`,shift:`Shift`},o=[`ctrl`,`alt`,`shift`,`meta`];function formatShortcut(e,t=detectModKey()){return e.trim()?formatShortcutParts(e,t).map(e=>e.join(t===`meta`?``:`+`)).join(` `):``}function formatShortcutParts(e,t=detectModKey()){if(!e.trim())return`${e}`,[];let n;try{n=parseShortcut(e,t)}catch{return`${e}`,[]}let s=t===`meta`;return n.map(e=>{let t=o.filter(t=>e.modifiers.has(t)).map(e=>s?i[e]:a[e]),n=r[e.key]??e.key.toUpperCase();return[...t,n]})}var noop=()=>{};function createChordTracker(e,t,n){let r=0,i=[],a;function reset(e=!0){a!==void 0&&clearTimeout(a);let t=r>0;a=void 0,r=0,i=[],e&&t&&n.onCancel()}function advance(o){let s=(r===0?e():i).filter(e=>{let t=e.shortcut[r];return t!==void 0&&matchStep(o,t)&&(!e.when||e.when(o))});if(s.length===0){let e=r!==0;return reset(),e?advance(o):{type:`none`}}a!==void 0&&clearTimeout(a),a=void 0;let c=s.find(e=>e.shortcut.length===r+1);if(c)return reset(!1),{binding:c,type:`match`};let l=r===0;return i=s,r+=1,n.onProgress(s[0].shortcut.slice(0,r),l),a=setTimeout(()=>{n.onTimeout(),reset(!1)},t),{bindings:s,type:`pending`}}return{advance,reset}}function createKeymap(e=[],t={}){let{chordTimeout:n=1e3,modKey:r=detectModKey(),when:i}=t;if(!Number.isFinite(n)||n<=0)throw new KeymapConfigError(`chordTimeout must be a positive finite number; received ${n}.`);let a=new Map,o=new Map,s=new Set,c=new AbortController,l=[],u=[],d=!1;function assertActive(){if(d)throw new KeymapError(`Keymap is disposed`)}function rebuildTriggerCaches(){l=[],u=[];for(let e of a.values())e.trigger===`keydown`?l.push(e):u.push(e);for(let e of o.values())e.keydown.reset(),e.keyup.reset()}function addBinding(e){let t={handler:e.handler,id:e.id,preventDefault:e.preventDefault??!0,shortcut:parseShortcut(e.shortcut,r),stopPropagation:e.stopPropagation??!1,trigger:e.trigger??`keydown`,when:e.when};return a.set(t.id,t),rebuildTriggerCaches(),t}function removeById(e){let t=a.delete(e);return t&&rebuildTriggerCaches(),t}function toBindingEntry(e){return{id:e.id,preventDefault:e.preventDefault,shortcut:e.shortcut.map(e=>({key:e.key,modifiers:new Set(e.modifiers)})),stopPropagation:e.stopPropagation,trigger:e.trigger}}function emitTap(e){if(s.size!==0)for(let t of s)try{t(e)}catch{}}function makeHandler(e,t,n){return r=>{let a=(typeof r.composedPath==`function`?r.composedPath():[]).find(e=>o.has(e));if(a&&a!==e)return;let c=r;if(d)return;if(i&&!i(c)){n.reset();return}let l=n.advance(c);if(l.type===`none`)return;let u=l.type===`pending`?l.bindings:[l.binding];u.some(e=>e.preventDefault)&&c.preventDefault(),u.some(e=>e.stopPropagation)&&c.stopPropagation(),l.type!==`pending`&&(s.size>0&&emitTap({binding:toBindingEntry(l.binding),target:e,trigger:t,type:`match`}),l.binding.handler(c))}}for(let t of e)addBinding(t);return{bind(e){assertActive();let t=addBinding(e);return()=>{!d&&a.get(t.id)===t&&removeById(t.id)}},get disposalSignal(){return c.signal},dispose(){if(!d){d=!0;for(let[e,t]of o)e.removeEventListener(`keydown`,t.onKeydown),e.removeEventListener(`keyup`,t.onKeyup),t.keydown.reset(),t.keyup.reset();emitTap({type:`dispose`}),c.abort(),o.clear(),a.clear(),l=[],u=[],s.clear()}},get disposed(){return d},listBindings(){return[...a.values()].map(toBindingEntry)},mount(e){assertActive();let t=o.get(e);if(!t){let createCallbacks=t=>({onCancel:()=>{s.size>0&&emitTap({target:e,trigger:t,type:`chord-cancel`})},onProgress:(n,r)=>{if(s.size===0)return;let i=n.map(e=>({key:e.key,modifiers:new Set(e.modifiers)}));emitTap(r?{step:i[0],target:e,trigger:t,type:`chord-start`}:{steps:i,target:e,trigger:t,type:`chord-progress`})},onTimeout:()=>{s.size>0&&emitTap({target:e,trigger:t,type:`chord-timeout`})}}),r=createChordTracker(()=>l,n,createCallbacks(`keydown`)),i=createChordTracker(()=>u,n,createCallbacks(`keyup`)),a=makeHandler(e,`keydown`,r),c=makeHandler(e,`keyup`,i);t={keydown:r,keyup:i,onKeydown:a,onKeyup:c,refs:0},o.set(e,t),e.addEventListener(`keydown`,a),e.addEventListener(`keyup`,c)}t.refs+=1;let r=!1;return()=>{r||(r=!0,--t.refs,!(t?.refs>0)&&(e.removeEventListener(`keydown`,t?.onKeydown),e.removeEventListener(`keyup`,t?.onKeyup),t?.keydown.reset(),t?.keyup.reset(),o.delete(e)))}},[Symbol.dispose](){this.dispose()},tap(e,t){let n=t?.signal?AbortSignal.any([c.signal,t.signal]):c.signal;if(n.aborted)return noop;s.add(e);let onAbort=()=>s.delete(e);return n.addEventListener(`abort`,onAbort,{once:!0}),()=>{s.delete(e),n.removeEventListener(`abort`,onAbort)}},unbind(e){assertActive(),removeById(e)||`${e}`}}}return e.KeymapConfigError=KeymapConfigError,e.KeymapError=KeymapError,e.KeymapParseError=KeymapParseError,e.createKeymap=createKeymap,e.detectModKey=detectModKey,e.findShortcutConflicts=findShortcutConflicts,e.formatShortcut=formatShortcut,e.formatShortcutParts=formatShortcutParts,e.matchKey=matchKey,e})({});
2
2
  //# sourceMappingURL=keymap.iife.js.map