@vielzeug/keymap 26.10.14 → 26.10.16

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.
@@ -1 +1 @@
1
- {"version":3,"file":"parser.js","names":[],"sources":["../src/parser.ts"],"sourcesContent":["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"],"mappings":"+CAWA,SAAgB,cAAgC,CAC9C,GAAI,OAAO,UAAc,IAAa,MAAO,OAE7C,IAAM,EACH,UAAoE,eAAe,UACpF,UAAU,UACV,GAEF,MAAO,OAAO,KAAK,CAAQ,EAAI,OAAS,MAC1C,CAEA,IAAM,EAAqD,CACzD,IAAK,MACL,IAAK,OACL,QAAS,OACT,QAAS,OACT,KAAM,OACN,KAAM,OACN,IAAK,MACL,OAAQ,MACR,MAAO,QACP,IAAK,MACP,EAEM,EAA8C,CAClD,IAAK,SACL,KAAM,YACN,IAAK,SACL,KAAM,YACN,MAAO,aACP,MAAO,IACP,SAAU,IACV,GAAI,SACN,EAEA,SAAS,gBAAgB,EAAa,EAAuC,CAC3E,IAAM,EAAQ,EACX,KAAK,CAAC,CACN,MAAM,GAAG,CAAC,CACV,OAAO,OAAO,CAAC,CACf,IAAK,GAAS,EAAK,YAAY,CAAC,EAEnC,GAAI,EAAM,SAAW,EAAG,MAAM,IAAI,EAAiB,2BAA2B,EAAI,EAAE,EAEpF,IAAM,EAAuC,CAAE,GAAG,EAAuB,IAAK,CAAO,EAC/E,EAAY,IAAI,IAChB,EAAqB,CAAC,EAE5B,IAAK,IAAM,KAAQ,EAAO,CACxB,IAAM,EAAW,OAAO,OAAO,EAAS,CAAI,EAAI,EAAQ,GAAS,IAAA,GAE7D,EAAU,EAAU,IAAI,CAAQ,EAC/B,EAAS,KAAK,CAAI,CACzB,CAEA,GAAI,EAAS,SAAW,EAAG,MAAM,IAAI,EAAiB,2BAA2B,EAAI,EAAE,EAEvF,GAAI,EAAS,OAAS,EACpB,MAAM,IAAI,EAAiB,6BAA6B,EAAI,oCAAoC,EAGlG,IAAM,EAAS,EAAS,GAGxB,MAAO,CAAE,IAFG,OAAO,OAAO,EAAqB,CAAM,EAAI,EAAoB,GAAW,EAE1E,WAAU,CAC1B,CAEA,SAAgB,UAAU,EAAa,EAA0B,aAAa,EAAwB,CACpG,GAAI,CACF,OAAO,gBAAgB,EAAK,CAAM,CACpC,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAgB,cAAc,EAAa,EAA0B,aAAa,EAAa,CAC7F,IAAM,EAAQ,EAAI,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EAEpD,GAAI,EAAM,SAAW,EAAG,MAAM,IAAI,EAAiB,6CAA6C,EAEhG,OAAO,EAAM,IAAK,GAAS,gBAAgB,EAAM,CAAM,CAAC,CAC1D,CAEA,SAAgB,qBAAqB,EAAwC,CAC3E,OAAO,EACJ,IAAK,GAAS,CACb,IAAM,EAAY,CAAC,GAAG,EAAK,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,EAErD,OAAO,EAAY,GAAG,EAAU,GAAG,EAAK,MAAQ,EAAK,GACvD,CAAC,CAAC,CACD,KAAK,GAAG,CACb,CAEA,SAAgB,UAAU,EAAsB,EAA6B,CAM3E,GAFI,OAAO,EAAM,KAAQ,UAErB,EAAM,IAAI,YAAY,IAAM,EAAK,IAAK,MAAO,GAEjD,GAAM,CAAE,aAAc,EAEtB,OACE,EAAM,SAAW,EAAU,IAAI,KAAK,GACpC,EAAM,UAAY,EAAU,IAAI,MAAM,GACtC,EAAM,UAAY,EAAU,IAAI,MAAM,GACtC,EAAM,WAAa,EAAU,IAAI,OAAO,CAE5C,CASA,SAAgB,SAAS,EAAsB,EAAiB,EAA0B,aAAa,EAAY,CACjH,IAAM,EAAO,UAAU,EAAS,CAAM,EAEtC,OAAO,IAAS,MAAQ,UAAU,EAAO,CAAI,CAC/C"}
1
+ {"version":3,"file":"parser.js","names":["platform","MODIFIER_ALIASES_BASE","SPECIAL_KEY_ALIASES","parts","raw","part","KeymapParseError","aliases","modKey","modifiers","keyParts","modifier","rawKey","steps","step","event","pattern"],"sources":["../src/parser.ts"],"sourcesContent":["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"],"mappings":";;AAWA,SAAgB,eAAgC;CAC9C,IAAI,OAAO,YAAc,KAAa,OAAO;CAE7C,IAAMA,IACH,UAAoE,eAAe,YACpF,UAAU,YACV;CAEF,OAAO,OAAO,KAAKA,CAAQ,IAAI,SAAS;AAC1C;AAEA,IAAMC,IAAqD;CACzD,KAAK;CACL,KAAK;CACL,SAAS;CACT,SAAS;CACT,MAAM;CACN,MAAM;CACN,KAAK;CACL,QAAQ;CACR,OAAO;CACP,KAAK;AACP,GAEMC,IAA8C;CAClD,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,OAAO;CACP,OAAO;CACP,UAAU;CACV,IAAI;AACN;AAEA,SAAS,gBAAgB,GAAa,GAAuC;CAC3E,IAAMC,IAAQC,EACX,KAAK,CAAC,CACN,MAAM,GAAG,CAAC,CACV,OAAO,OAAO,CAAC,CACf,KAAK,MAASC,EAAK,YAAY,CAAC;CAEnC,IAAIF,EAAM,WAAW,GAAG,MAAM,IAAIG,EAAiB,2BAA2BF,EAAI,EAAE;CAEpF,IAAMG,IAAuC;EAAE,GAAGN;EAAuB,KAAKO;CAAO,GAC/EC,oBAAY,IAAI,IAAiB,GACjCC,IAAqB,CAAC;CAE5B,KAAK,IAAML,KAAQF,GAAO;EACxB,IAAMQ,IAAW,OAAO,OAAOJ,GAASF,CAAI,IAAIE,EAAQF,KAAS,KAAA;EAEjE,AAAIM,IAAUF,EAAU,IAAIE,CAAQ,IAC/BD,EAAS,KAAKL,CAAI;CACzB;CAEA,IAAIK,EAAS,WAAW,GAAG,MAAM,IAAIJ,EAAiB,2BAA2BF,EAAI,EAAE;CAEvF,IAAIM,EAAS,SAAS,GACpB,MAAM,IAAIJ,EAAiB,6BAA6BF,EAAI,oCAAoC;CAGlG,IAAMQ,IAASF,EAAS;CAGxB,OAAO;EAAE,KAFG,OAAO,OAAOR,GAAqBU,CAAM,IAAIV,EAAoBU,KAAWA;EAE1E;CAAU;AAC1B;AAEA,SAAgB,UAAU,GAAa,IAA0B,aAAa,GAAwB;CACpG,IAAI;EACF,OAAO,gBAAgBR,GAAKI,CAAM;CACpC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,cAAc,GAAa,IAA0B,aAAa,GAAa;CAC7F,IAAMK,IAAQT,EAAI,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAEpD,IAAIS,EAAM,WAAW,GAAG,MAAM,IAAIP,EAAiB,6CAA6C;CAEhG,OAAOO,EAAM,KAAK,MAAS,gBAAgBC,GAAMN,CAAM,CAAC;AAC1D;AAEA,SAAgB,qBAAqB,GAAwC;CAC3E,OAAOK,EACJ,KAAK,MAAS;EACb,IAAMJ,IAAY,CAAC,GAAGK,EAAK,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG;EAErD,OAAOL,IAAY,GAAGA,EAAU,GAAGK,EAAK,QAAQA,EAAK;CACvD,CAAC,CAAC,CACD,KAAK,GAAG;AACb;AAEA,SAAgB,UAAU,GAAsB,GAA6B;CAM3E,IAFI,OAAOC,EAAM,OAAQ,YAErBA,EAAM,IAAI,YAAY,MAAMD,EAAK,KAAK,OAAO;CAEjD,IAAM,EAAE,iBAAcA;CAEtB,OACEC,EAAM,WAAWN,EAAU,IAAI,KAAK,KACpCM,EAAM,YAAYN,EAAU,IAAI,MAAM,KACtCM,EAAM,YAAYN,EAAU,IAAI,MAAM,KACtCM,EAAM,aAAaN,EAAU,IAAI,OAAO;AAE5C;AASA,SAAgB,SAAS,GAAsB,GAAiB,IAA0B,aAAa,GAAY;CACjH,IAAMK,IAAO,UAAUE,GAASR,CAAM;CAEtC,OAAOM,MAAS,QAAQ,UAAUC,GAAOD,CAAI;AAC/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/keymap",
3
- "version": "26.10.14",
3
+ "version": "26.10.16",
4
4
  "description": "Headless keyboard shortcut manager with chord sequences",
5
5
  "repository": {
6
6
  "type": "git",
@@ -51,9 +51,9 @@
51
51
  "test": "vitest"
52
52
  },
53
53
  "devDependencies": {
54
- "@types/node": "^26.6.2",
54
+ "@types/node": "^26.6.5",
55
55
  "typescript": "^6.0.3",
56
- "vite": "^8.3.0",
57
- "vitest": "^5.0.1"
56
+ "vite": "^8.3.4",
57
+ "vitest": "^5.0.5"
58
58
  }
59
59
  }