@tanstack/hotkeys 0.9.0 → 0.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.
Files changed (148) hide show
  1. package/dist/_event-target.js +1 -2
  2. package/dist/_keyboard-event.js +1 -2
  3. package/dist/_match.js +1 -2
  4. package/dist/_named-keys.d.ts +3 -5
  5. package/dist/_named-keys.js +1 -2
  6. package/dist/_recorder-chord.js +1 -2
  7. package/dist/_recording-guard.js +1 -2
  8. package/dist/_registration.js +1 -2
  9. package/dist/conflicts.d.ts +4 -7
  10. package/dist/conflicts.js +1 -2
  11. package/dist/constants.d.ts +14 -17
  12. package/dist/constants.js +8 -9
  13. package/dist/display-labels.d.ts +7 -10
  14. package/dist/display-labels.js +1 -2
  15. package/dist/format.d.ts +9 -12
  16. package/dist/format.js +9 -7
  17. package/dist/hint.d.ts +3 -6
  18. package/dist/hint.js +1 -2
  19. package/dist/hotkey-manager.d.ts +8 -10
  20. package/dist/hotkey-manager.js +21 -3
  21. package/dist/hotkey-recorder.d.ts +4 -7
  22. package/dist/hotkey-recorder.js +9 -6
  23. package/dist/hotkey-sequence-recorder.d.ts +5 -8
  24. package/dist/hotkey-sequence-recorder.js +6 -7
  25. package/dist/hotkey.types.d.ts +15 -18
  26. package/dist/key-state-tracker.d.ts +4 -7
  27. package/dist/key-state-tracker.js +6 -3
  28. package/dist/key.types.d.ts +17 -20
  29. package/dist/match.d.ts +8 -11
  30. package/dist/match.js +1 -2
  31. package/dist/parse.d.ts +11 -14
  32. package/dist/parse.js +5 -4
  33. package/dist/platform.d.ts +3 -6
  34. package/dist/platform.js +1 -2
  35. package/dist/recorder-options.d.ts +6 -9
  36. package/dist/sequence-manager.d.ts +11 -14
  37. package/dist/sequence-manager.js +12 -7
  38. package/dist/validate.d.ts +5 -8
  39. package/dist/validate.js +1 -2
  40. package/package.json +8 -13
  41. package/dist/_event-target.cjs +0 -73
  42. package/dist/_event-target.cjs.map +0 -1
  43. package/dist/_event-target.js.map +0 -1
  44. package/dist/_keyboard-event.cjs +0 -83
  45. package/dist/_keyboard-event.cjs.map +0 -1
  46. package/dist/_keyboard-event.js.map +0 -1
  47. package/dist/_match.cjs +0 -49
  48. package/dist/_match.cjs.map +0 -1
  49. package/dist/_match.js.map +0 -1
  50. package/dist/_named-keys.cjs +0 -68
  51. package/dist/_named-keys.cjs.map +0 -1
  52. package/dist/_named-keys.d.cts +0 -8
  53. package/dist/_named-keys.js.map +0 -1
  54. package/dist/_recorder-chord.cjs +0 -44
  55. package/dist/_recorder-chord.cjs.map +0 -1
  56. package/dist/_recorder-chord.js.map +0 -1
  57. package/dist/_recording-guard.cjs +0 -59
  58. package/dist/_recording-guard.cjs.map +0 -1
  59. package/dist/_recording-guard.js.map +0 -1
  60. package/dist/_registration.cjs +0 -54
  61. package/dist/_registration.cjs.map +0 -1
  62. package/dist/_registration.js.map +0 -1
  63. package/dist/conflicts.cjs +0 -55
  64. package/dist/conflicts.cjs.map +0 -1
  65. package/dist/conflicts.d.cts +0 -39
  66. package/dist/conflicts.js.map +0 -1
  67. package/dist/constants.cjs +0 -410
  68. package/dist/constants.cjs.map +0 -1
  69. package/dist/constants.d.cts +0 -165
  70. package/dist/constants.js.map +0 -1
  71. package/dist/display-labels.cjs +0 -119
  72. package/dist/display-labels.cjs.map +0 -1
  73. package/dist/display-labels.d.cts +0 -93
  74. package/dist/display-labels.js.map +0 -1
  75. package/dist/format.cjs +0 -85
  76. package/dist/format.cjs.map +0 -1
  77. package/dist/format.d.cts +0 -99
  78. package/dist/format.js.map +0 -1
  79. package/dist/hint.cjs +0 -24
  80. package/dist/hint.cjs.map +0 -1
  81. package/dist/hint.d.cts +0 -23
  82. package/dist/hint.js.map +0 -1
  83. package/dist/hotkey-manager.cjs +0 -414
  84. package/dist/hotkey-manager.cjs.map +0 -1
  85. package/dist/hotkey-manager.d.cts +0 -234
  86. package/dist/hotkey-manager.js.map +0 -1
  87. package/dist/hotkey-recorder.cjs +0 -235
  88. package/dist/hotkey-recorder.cjs.map +0 -1
  89. package/dist/hotkey-recorder.d.cts +0 -119
  90. package/dist/hotkey-recorder.js.map +0 -1
  91. package/dist/hotkey-sequence-recorder.cjs +0 -255
  92. package/dist/hotkey-sequence-recorder.cjs.map +0 -1
  93. package/dist/hotkey-sequence-recorder.d.cts +0 -86
  94. package/dist/hotkey-sequence-recorder.js.map +0 -1
  95. package/dist/hotkey.types.d.cts +0 -236
  96. package/dist/index.cjs +0 -71
  97. package/dist/index.d.cts +0 -18
  98. package/dist/key-state-tracker.cjs +0 -197
  99. package/dist/key-state-tracker.cjs.map +0 -1
  100. package/dist/key-state-tracker.d.cts +0 -107
  101. package/dist/key-state-tracker.js.map +0 -1
  102. package/dist/key.types.d.cts +0 -82
  103. package/dist/match.cjs +0 -144
  104. package/dist/match.cjs.map +0 -1
  105. package/dist/match.d.cts +0 -101
  106. package/dist/match.js.map +0 -1
  107. package/dist/parse.cjs +0 -245
  108. package/dist/parse.cjs.map +0 -1
  109. package/dist/parse.d.cts +0 -134
  110. package/dist/parse.js.map +0 -1
  111. package/dist/platform.cjs +0 -55
  112. package/dist/platform.cjs.map +0 -1
  113. package/dist/platform.d.cts +0 -44
  114. package/dist/platform.js.map +0 -1
  115. package/dist/recorder-options.d.cts +0 -34
  116. package/dist/sequence-manager.cjs +0 -441
  117. package/dist/sequence-manager.cjs.map +0 -1
  118. package/dist/sequence-manager.d.cts +0 -172
  119. package/dist/sequence-manager.js.map +0 -1
  120. package/dist/validate.cjs +0 -112
  121. package/dist/validate.cjs.map +0 -1
  122. package/dist/validate.d.cts +0 -67
  123. package/dist/validate.js.map +0 -1
  124. package/src/_event-target.ts +0 -157
  125. package/src/_keyboard-event.ts +0 -101
  126. package/src/_match.ts +0 -96
  127. package/src/_named-keys.ts +0 -62
  128. package/src/_recorder-chord.ts +0 -76
  129. package/src/_recording-guard.ts +0 -63
  130. package/src/_registration.ts +0 -79
  131. package/src/conflicts.ts +0 -125
  132. package/src/constants.ts +0 -455
  133. package/src/display-labels.ts +0 -123
  134. package/src/format.ts +0 -201
  135. package/src/hint.ts +0 -42
  136. package/src/hotkey-manager.ts +0 -814
  137. package/src/hotkey-recorder.ts +0 -348
  138. package/src/hotkey-sequence-recorder.ts +0 -410
  139. package/src/hotkey.types.ts +0 -269
  140. package/src/index.ts +0 -17
  141. package/src/key-state-tracker.ts +0 -261
  142. package/src/key.types.ts +0 -268
  143. package/src/match.ts +0 -210
  144. package/src/parse.ts +0 -369
  145. package/src/platform.ts +0 -66
  146. package/src/recorder-options.ts +0 -34
  147. package/src/sequence-manager.ts +0 -769
  148. package/src/validate.ts +0 -179
@@ -1,44 +0,0 @@
1
- const require_constants = require('./constants.cjs');
2
- const require__keyboard_event = require('./_keyboard-event.cjs');
3
- const require_parse = require('./parse.cjs');
4
-
5
- //#region src/_recorder-chord.ts
6
- /**
7
- * Converts a keydown to a replayable chord, preserving physical codes by default.
8
- * Returns null for composition, modifier-only input, or unusable physical input.
9
- * Key mode retains the produced character; neither mode guesses layout letters.
10
- */
11
- function hotkeyChordFromKeydown(event, platform, recordBy = "code") {
12
- const normalized = require__keyboard_event.normalizeKeyboardEvent(event, platform);
13
- if (normalized.isComposing || require_parse.isModifierKey(require_constants.normalizeKeyName(event.key)) || event.key === "AltGraph") return null;
14
- if (recordBy === "code" && (!/^[A-Za-z][A-Za-z0-9]*$/.test(event.code) || event.code === "Unidentified" || normalized.altGraph)) return null;
15
- const parsed = require_parse.parseKeyboardEvent(event, platform);
16
- if (recordBy === "code") {
17
- const { key: _key, code: _code, ...modifiers } = parsed;
18
- return require_parse.normalizeHotkeyFromParsed({
19
- ...modifiers,
20
- code: event.code
21
- }, platform);
22
- }
23
- return parsed.key ? require_parse.normalizeHotkeyFromParsed(parsed, platform) : null;
24
- }
25
- /**
26
- * Explains why a physical chord cannot be recorded before syntax/policy checks.
27
- * Logical mode deliberately accepts produced characters instead of requiring a code.
28
- */
29
- function chordRejection(event, options) {
30
- if (options.recordBy === "key") return void 0;
31
- if (require__keyboard_event.normalizeKeyboardEvent(event, options.platform).altGraph) return {
32
- reason: "alt-graph",
33
- message: "AltGraph character entry cannot be recorded as a physical shortcut. Use key mode for characters."
34
- };
35
- if (!/^[A-Za-z][A-Za-z0-9]*$/.test(event.code) || event.code === "Unidentified") return {
36
- reason: "missing-code",
37
- message: "This event has no usable physical key code."
38
- };
39
- }
40
-
41
- //#endregion
42
- exports.chordRejection = chordRejection;
43
- exports.hotkeyChordFromKeydown = hotkeyChordFromKeydown;
44
- //# sourceMappingURL=_recorder-chord.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_recorder-chord.cjs","names":["normalizeKeyboardEvent","isModifierKey","normalizeKeyName","parseKeyboardEvent","normalizeHotkeyFromParsed"],"sources":["../src/_recorder-chord.ts"],"sourcesContent":["import { normalizeKeyName } from './constants'\nimport { normalizeKeyboardEvent } from './_keyboard-event'\nimport {\n isModifierKey,\n normalizeHotkeyFromParsed,\n parseKeyboardEvent,\n} from './parse'\nimport type {\n RecorderKeyMode,\n RecorderOptions,\n RecorderRejection,\n} from './recorder-options'\nimport type { Hotkey } from './hotkey.types'\n\n/**\n * Converts a keydown to a replayable chord, preserving physical codes by default.\n * Returns null for composition, modifier-only input, or unusable physical input.\n * Key mode retains the produced character; neither mode guesses layout letters.\n */\nexport function hotkeyChordFromKeydown(\n event: KeyboardEvent,\n platform: 'mac' | 'windows' | 'linux',\n recordBy: RecorderKeyMode = 'code',\n): Hotkey | null {\n const normalized = normalizeKeyboardEvent(event, platform)\n if (\n normalized.isComposing ||\n isModifierKey(normalizeKeyName(event.key)) ||\n event.key === 'AltGraph'\n )\n return null\n if (\n recordBy === 'code' &&\n (!/^[A-Za-z][A-Za-z0-9]*$/.test(event.code) ||\n event.code === 'Unidentified' ||\n normalized.altGraph)\n )\n return null\n const parsed = parseKeyboardEvent(event, platform)\n if (recordBy === 'code') {\n // Remove logical identity rather than placing a physical code in the key field.\n const { key: _key, code: _code, ...modifiers } = parsed\n return normalizeHotkeyFromParsed(\n { ...modifiers, code: event.code },\n platform,\n )\n }\n return parsed.key ? normalizeHotkeyFromParsed(parsed, platform) : null\n}\n\n/**\n * Explains why a physical chord cannot be recorded before syntax/policy checks.\n * Logical mode deliberately accepts produced characters instead of requiring a code.\n */\nexport function chordRejection(\n event: KeyboardEvent,\n options: RecorderOptions,\n): RecorderRejection | undefined {\n if (options.recordBy === 'key') return undefined\n // AltGr is character entry, often reported as synthetic Ctrl+Alt; macOS Option is handled separately.\n if (normalizeKeyboardEvent(event, options.platform).altGraph)\n return {\n reason: 'alt-graph',\n message:\n 'AltGraph character entry cannot be recorded as a physical shortcut. Use key mode for characters.',\n }\n if (\n !/^[A-Za-z][A-Za-z0-9]*$/.test(event.code) ||\n event.code === 'Unidentified'\n )\n return {\n reason: 'missing-code',\n message: 'This event has no usable physical key code.',\n }\n return undefined\n}\n"],"mappings":";;;;;;;;;;AAmBA,SAAgB,uBACd,OACA,UACA,WAA4B,QACb;CACf,MAAM,aAAaA,+CAAuB,OAAO,SAAS;AAC1D,KACE,WAAW,eACXC,4BAAcC,mCAAiB,MAAM,IAAI,CAAC,IAC1C,MAAM,QAAQ,WAEd,QAAO;AACT,KACE,aAAa,WACZ,CAAC,yBAAyB,KAAK,MAAM,KAAK,IACzC,MAAM,SAAS,kBACf,WAAW,UAEb,QAAO;CACT,MAAM,SAASC,iCAAmB,OAAO,SAAS;AAClD,KAAI,aAAa,QAAQ;EAEvB,MAAM,EAAE,KAAK,MAAM,MAAM,OAAO,GAAG,cAAc;AACjD,SAAOC,wCACL;GAAE,GAAG;GAAW,MAAM,MAAM;GAAM,EAClC,SACD;;AAEH,QAAO,OAAO,MAAMA,wCAA0B,QAAQ,SAAS,GAAG;;;;;;AAOpE,SAAgB,eACd,OACA,SAC+B;AAC/B,KAAI,QAAQ,aAAa,MAAO,QAAO;AAEvC,KAAIJ,+CAAuB,OAAO,QAAQ,SAAS,CAAC,SAClD,QAAO;EACL,QAAQ;EACR,SACE;EACH;AACH,KACE,CAAC,yBAAyB,KAAK,MAAM,KAAK,IAC1C,MAAM,SAAS,eAEf,QAAO;EACL,QAAQ;EACR,SAAS;EACV"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_recorder-chord.js","names":[],"sources":["../src/_recorder-chord.ts"],"sourcesContent":["import { normalizeKeyName } from './constants'\nimport { normalizeKeyboardEvent } from './_keyboard-event'\nimport {\n isModifierKey,\n normalizeHotkeyFromParsed,\n parseKeyboardEvent,\n} from './parse'\nimport type {\n RecorderKeyMode,\n RecorderOptions,\n RecorderRejection,\n} from './recorder-options'\nimport type { Hotkey } from './hotkey.types'\n\n/**\n * Converts a keydown to a replayable chord, preserving physical codes by default.\n * Returns null for composition, modifier-only input, or unusable physical input.\n * Key mode retains the produced character; neither mode guesses layout letters.\n */\nexport function hotkeyChordFromKeydown(\n event: KeyboardEvent,\n platform: 'mac' | 'windows' | 'linux',\n recordBy: RecorderKeyMode = 'code',\n): Hotkey | null {\n const normalized = normalizeKeyboardEvent(event, platform)\n if (\n normalized.isComposing ||\n isModifierKey(normalizeKeyName(event.key)) ||\n event.key === 'AltGraph'\n )\n return null\n if (\n recordBy === 'code' &&\n (!/^[A-Za-z][A-Za-z0-9]*$/.test(event.code) ||\n event.code === 'Unidentified' ||\n normalized.altGraph)\n )\n return null\n const parsed = parseKeyboardEvent(event, platform)\n if (recordBy === 'code') {\n // Remove logical identity rather than placing a physical code in the key field.\n const { key: _key, code: _code, ...modifiers } = parsed\n return normalizeHotkeyFromParsed(\n { ...modifiers, code: event.code },\n platform,\n )\n }\n return parsed.key ? normalizeHotkeyFromParsed(parsed, platform) : null\n}\n\n/**\n * Explains why a physical chord cannot be recorded before syntax/policy checks.\n * Logical mode deliberately accepts produced characters instead of requiring a code.\n */\nexport function chordRejection(\n event: KeyboardEvent,\n options: RecorderOptions,\n): RecorderRejection | undefined {\n if (options.recordBy === 'key') return undefined\n // AltGr is character entry, often reported as synthetic Ctrl+Alt; macOS Option is handled separately.\n if (normalizeKeyboardEvent(event, options.platform).altGraph)\n return {\n reason: 'alt-graph',\n message:\n 'AltGraph character entry cannot be recorded as a physical shortcut. Use key mode for characters.',\n }\n if (\n !/^[A-Za-z][A-Za-z0-9]*$/.test(event.code) ||\n event.code === 'Unidentified'\n )\n return {\n reason: 'missing-code',\n message: 'This event has no usable physical key code.',\n }\n return undefined\n}\n"],"mappings":";;;;;;;;;;AAmBA,SAAgB,uBACd,OACA,UACA,WAA4B,QACb;CACf,MAAM,aAAa,uBAAuB,OAAO,SAAS;AAC1D,KACE,WAAW,eACX,cAAc,iBAAiB,MAAM,IAAI,CAAC,IAC1C,MAAM,QAAQ,WAEd,QAAO;AACT,KACE,aAAa,WACZ,CAAC,yBAAyB,KAAK,MAAM,KAAK,IACzC,MAAM,SAAS,kBACf,WAAW,UAEb,QAAO;CACT,MAAM,SAAS,mBAAmB,OAAO,SAAS;AAClD,KAAI,aAAa,QAAQ;EAEvB,MAAM,EAAE,KAAK,MAAM,MAAM,OAAO,GAAG,cAAc;AACjD,SAAO,0BACL;GAAE,GAAG;GAAW,MAAM,MAAM;GAAM,EAClC,SACD;;AAEH,QAAO,OAAO,MAAM,0BAA0B,QAAQ,SAAS,GAAG;;;;;;AAOpE,SAAgB,eACd,OACA,SAC+B;AAC/B,KAAI,QAAQ,aAAa,MAAO,QAAO;AAEvC,KAAI,uBAAuB,OAAO,QAAQ,SAAS,CAAC,SAClD,QAAO;EACL,QAAQ;EACR,SACE;EACH;AACH,KACE,CAAC,yBAAyB,KAAK,MAAM,KAAK,IAC1C,MAAM,SAAS,eAEf,QAAO;EACL,QAAQ;EACR,SAAS;EACV"}
@@ -1,59 +0,0 @@
1
- const require_constants = require('./constants.cjs');
2
-
3
- //#region src/_recording-guard.ts
4
- const sessions = /* @__PURE__ */ new Set();
5
- const captured = /* @__PURE__ */ new WeakSet();
6
- const pending = /* @__PURE__ */ new Map();
7
- let listening = false;
8
- const identity = (event) => event.code || `key:${event.key}`;
9
- /** Consumes recorded key releases and removes guards for completed chords. */
10
- function release(event) {
11
- if (pending.delete(identity(event))) captured.add(event);
12
- if (require_constants.MODIFIER_KEYS.has(require_constants.normalizeKeyName(event.key))) {
13
- for (const [id, key] of pending) if (!require_constants.MODIFIER_KEYS.has(key)) pending.delete(id);
14
- }
15
- cleanup();
16
- }
17
- /** Clears pending releases when focus loss prevents the browser from delivering them. */
18
- function blur() {
19
- pending.clear();
20
- cleanup();
21
- }
22
- /** Detaches shared listeners once neither active sessions nor pending releases need them. */
23
- function cleanup() {
24
- if (listening && sessions.size === 0 && pending.size === 0) {
25
- document.removeEventListener("keyup", release, true);
26
- window.removeEventListener("blur", blur);
27
- listening = false;
28
- }
29
- }
30
- /** Pauses application shortcut dispatch and starts tracking releases for this recorder. */
31
- function beginRecording(owner) {
32
- sessions.add(owner);
33
- if (!listening && typeof document !== "undefined") {
34
- document.addEventListener("keyup", release, true);
35
- window.addEventListener("blur", blur);
36
- listening = true;
37
- }
38
- }
39
- /** Ends a session while retaining guards for keys still held after commit or cancellation. */
40
- function endRecording(owner) {
41
- sessions.delete(owner);
42
- cleanup();
43
- }
44
- /** Marks a consumed keydown and guards subsequent repeats and its release. */
45
- function captureRecordingEvent(event) {
46
- captured.add(event);
47
- pending.set(identity(event), require_constants.normalizeKeyName(event.key));
48
- }
49
- /** Whether managers should suppress callbacks for this recording session or captured chord. */
50
- function isRecordingEvent(event) {
51
- return sessions.size > 0 || captured.has(event) || pending.has(identity(event));
52
- }
53
-
54
- //#endregion
55
- exports.beginRecording = beginRecording;
56
- exports.captureRecordingEvent = captureRecordingEvent;
57
- exports.endRecording = endRecording;
58
- exports.isRecordingEvent = isRecordingEvent;
59
- //# sourceMappingURL=_recording-guard.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_recording-guard.cjs","names":["MODIFIER_KEYS","normalizeKeyName"],"sources":["../src/_recording-guard.ts"],"sourcesContent":["import { MODIFIER_KEYS, normalizeKeyName } from './constants'\n\n// Shared only by recorders and dispatchers. Does not own bindings or preferences.\n// Multiple recorder instances may be active; dispatch resumes after the last one ends.\nconst sessions = new Set<object>()\n// Remember consumed event objects through the remainder of their DOM dispatch.\nconst captured = new WeakSet<KeyboardEvent>()\n// Captured physical identities stay blocked through repeats and their eventual keyup.\nconst pending = new Map<string, string>()\nlet listening = false\n// Prefer the stable physical identity when modifier release changes event.key.\nconst identity = (event: KeyboardEvent) => event.code || `key:${event.key}`\n\n/** Consumes recorded key releases and removes guards for completed chords. */\nfunction release(event: KeyboardEvent): void {\n if (pending.delete(identity(event))) captured.add(event)\n // macOS can swallow the main key's keyup while Command is held.\n // Releasing a modifier ends that chord, as in KeyStateTracker.\n if (MODIFIER_KEYS.has(normalizeKeyName(event.key))) {\n for (const [id, key] of pending) {\n if (!MODIFIER_KEYS.has(key)) pending.delete(id)\n }\n }\n cleanup()\n}\n/** Clears pending releases when focus loss prevents the browser from delivering them. */\nfunction blur(): void {\n pending.clear()\n cleanup()\n}\n/** Detaches shared listeners once neither active sessions nor pending releases need them. */\nfunction cleanup(): void {\n if (listening && sessions.size === 0 && pending.size === 0) {\n document.removeEventListener('keyup', release, true)\n window.removeEventListener('blur', blur)\n listening = false\n }\n}\n/** Pauses application shortcut dispatch and starts tracking releases for this recorder. */\nexport function beginRecording(owner: object): void {\n sessions.add(owner)\n if (!listening && typeof document !== 'undefined') {\n document.addEventListener('keyup', release, true)\n window.addEventListener('blur', blur)\n listening = true\n }\n}\n/** Ends a session while retaining guards for keys still held after commit or cancellation. */\nexport function endRecording(owner: object): void {\n sessions.delete(owner)\n cleanup()\n}\n/** Marks a consumed keydown and guards subsequent repeats and its release. */\nexport function captureRecordingEvent(event: KeyboardEvent): void {\n captured.add(event)\n pending.set(identity(event), normalizeKeyName(event.key))\n}\n/** Whether managers should suppress callbacks for this recording session or captured chord. */\nexport function isRecordingEvent(event: KeyboardEvent): boolean {\n return (\n sessions.size > 0 || captured.has(event) || pending.has(identity(event))\n )\n}\n"],"mappings":";;;AAIA,MAAM,2BAAW,IAAI,KAAa;AAElC,MAAM,2BAAW,IAAI,SAAwB;AAE7C,MAAM,0BAAU,IAAI,KAAqB;AACzC,IAAI,YAAY;AAEhB,MAAM,YAAY,UAAyB,MAAM,QAAQ,OAAO,MAAM;;AAGtE,SAAS,QAAQ,OAA4B;AAC3C,KAAI,QAAQ,OAAO,SAAS,MAAM,CAAC,CAAE,UAAS,IAAI,MAAM;AAGxD,KAAIA,gCAAc,IAAIC,mCAAiB,MAAM,IAAI,CAAC,EAChD;OAAK,MAAM,CAAC,IAAI,QAAQ,QACtB,KAAI,CAACD,gCAAc,IAAI,IAAI,CAAE,SAAQ,OAAO,GAAG;;AAGnD,UAAS;;;AAGX,SAAS,OAAa;AACpB,SAAQ,OAAO;AACf,UAAS;;;AAGX,SAAS,UAAgB;AACvB,KAAI,aAAa,SAAS,SAAS,KAAK,QAAQ,SAAS,GAAG;AAC1D,WAAS,oBAAoB,SAAS,SAAS,KAAK;AACpD,SAAO,oBAAoB,QAAQ,KAAK;AACxC,cAAY;;;;AAIhB,SAAgB,eAAe,OAAqB;AAClD,UAAS,IAAI,MAAM;AACnB,KAAI,CAAC,aAAa,OAAO,aAAa,aAAa;AACjD,WAAS,iBAAiB,SAAS,SAAS,KAAK;AACjD,SAAO,iBAAiB,QAAQ,KAAK;AACrC,cAAY;;;;AAIhB,SAAgB,aAAa,OAAqB;AAChD,UAAS,OAAO,MAAM;AACtB,UAAS;;;AAGX,SAAgB,sBAAsB,OAA4B;AAChE,UAAS,IAAI,MAAM;AACnB,SAAQ,IAAI,SAAS,MAAM,EAAEC,mCAAiB,MAAM,IAAI,CAAC;;;AAG3D,SAAgB,iBAAiB,OAA+B;AAC9D,QACE,SAAS,OAAO,KAAK,SAAS,IAAI,MAAM,IAAI,QAAQ,IAAI,SAAS,MAAM,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_recording-guard.js","names":[],"sources":["../src/_recording-guard.ts"],"sourcesContent":["import { MODIFIER_KEYS, normalizeKeyName } from './constants'\n\n// Shared only by recorders and dispatchers. Does not own bindings or preferences.\n// Multiple recorder instances may be active; dispatch resumes after the last one ends.\nconst sessions = new Set<object>()\n// Remember consumed event objects through the remainder of their DOM dispatch.\nconst captured = new WeakSet<KeyboardEvent>()\n// Captured physical identities stay blocked through repeats and their eventual keyup.\nconst pending = new Map<string, string>()\nlet listening = false\n// Prefer the stable physical identity when modifier release changes event.key.\nconst identity = (event: KeyboardEvent) => event.code || `key:${event.key}`\n\n/** Consumes recorded key releases and removes guards for completed chords. */\nfunction release(event: KeyboardEvent): void {\n if (pending.delete(identity(event))) captured.add(event)\n // macOS can swallow the main key's keyup while Command is held.\n // Releasing a modifier ends that chord, as in KeyStateTracker.\n if (MODIFIER_KEYS.has(normalizeKeyName(event.key))) {\n for (const [id, key] of pending) {\n if (!MODIFIER_KEYS.has(key)) pending.delete(id)\n }\n }\n cleanup()\n}\n/** Clears pending releases when focus loss prevents the browser from delivering them. */\nfunction blur(): void {\n pending.clear()\n cleanup()\n}\n/** Detaches shared listeners once neither active sessions nor pending releases need them. */\nfunction cleanup(): void {\n if (listening && sessions.size === 0 && pending.size === 0) {\n document.removeEventListener('keyup', release, true)\n window.removeEventListener('blur', blur)\n listening = false\n }\n}\n/** Pauses application shortcut dispatch and starts tracking releases for this recorder. */\nexport function beginRecording(owner: object): void {\n sessions.add(owner)\n if (!listening && typeof document !== 'undefined') {\n document.addEventListener('keyup', release, true)\n window.addEventListener('blur', blur)\n listening = true\n }\n}\n/** Ends a session while retaining guards for keys still held after commit or cancellation. */\nexport function endRecording(owner: object): void {\n sessions.delete(owner)\n cleanup()\n}\n/** Marks a consumed keydown and guards subsequent repeats and its release. */\nexport function captureRecordingEvent(event: KeyboardEvent): void {\n captured.add(event)\n pending.set(identity(event), normalizeKeyName(event.key))\n}\n/** Whether managers should suppress callbacks for this recording session or captured chord. */\nexport function isRecordingEvent(event: KeyboardEvent): boolean {\n return (\n sessions.size > 0 || captured.has(event) || pending.has(identity(event))\n )\n}\n"],"mappings":";;;AAIA,MAAM,2BAAW,IAAI,KAAa;AAElC,MAAM,2BAAW,IAAI,SAAwB;AAE7C,MAAM,0BAAU,IAAI,KAAqB;AACzC,IAAI,YAAY;AAEhB,MAAM,YAAY,UAAyB,MAAM,QAAQ,OAAO,MAAM;;AAGtE,SAAS,QAAQ,OAA4B;AAC3C,KAAI,QAAQ,OAAO,SAAS,MAAM,CAAC,CAAE,UAAS,IAAI,MAAM;AAGxD,KAAI,cAAc,IAAI,iBAAiB,MAAM,IAAI,CAAC,EAChD;OAAK,MAAM,CAAC,IAAI,QAAQ,QACtB,KAAI,CAAC,cAAc,IAAI,IAAI,CAAE,SAAQ,OAAO,GAAG;;AAGnD,UAAS;;;AAGX,SAAS,OAAa;AACpB,SAAQ,OAAO;AACf,UAAS;;;AAGX,SAAS,UAAgB;AACvB,KAAI,aAAa,SAAS,SAAS,KAAK,QAAQ,SAAS,GAAG;AAC1D,WAAS,oBAAoB,SAAS,SAAS,KAAK;AACpD,SAAO,oBAAoB,QAAQ,KAAK;AACxC,cAAY;;;;AAIhB,SAAgB,eAAe,OAAqB;AAClD,UAAS,IAAI,MAAM;AACnB,KAAI,CAAC,aAAa,OAAO,aAAa,aAAa;AACjD,WAAS,iBAAiB,SAAS,SAAS,KAAK;AACjD,SAAO,iBAAiB,QAAQ,KAAK;AACrC,cAAY;;;;AAIhB,SAAgB,aAAa,OAAqB;AAChD,UAAS,OAAO,MAAM;AACtB,UAAS;;;AAGX,SAAgB,sBAAsB,OAA4B;AAChE,UAAS,IAAI,MAAM;AACnB,SAAQ,IAAI,SAAS,MAAM,EAAE,iBAAiB,MAAM,IAAI,CAAC;;;AAG3D,SAAgB,iBAAiB,OAA+B;AAC9D,QACE,SAAS,OAAO,KAAK,SAAS,IAAI,MAAM,IAAI,QAAQ,IAAI,SAAS,MAAM,CAAC"}
@@ -1,54 +0,0 @@
1
-
2
- //#region src/_registration.ts
3
- /**
4
- * Default options for hotkey/sequence registration.
5
- * Omitted: platform, target (resolved at registration), requireReset (HotkeyManager only).
6
- */
7
- const defaultHotkeyOptions = {
8
- preventDefault: true,
9
- stopPropagation: true,
10
- eventType: "keydown",
11
- enabled: true,
12
- ignoreInputs: true,
13
- conflictBehavior: "warn"
14
- };
15
- /**
16
- * Computes the default ignoreInputs value based on the hotkey.
17
- * Ctrl/Meta shortcuts and Escape fire in inputs; single keys and Shift/Alt combos are ignored.
18
- */
19
- function getDefaultIgnoreInputs(parsedHotkey) {
20
- if (parsedHotkey.ctrl || parsedHotkey.meta) return false;
21
- if (parsedHotkey.key === "Escape" || parsedHotkey.code === "Escape") return false;
22
- return true;
23
- }
24
- /**
25
- * Handles conflicts between registrations based on conflict behavior.
26
- *
27
- * @param conflictingId - The ID of the conflicting registration
28
- * @param keyDisplay - Display string for the conflicting key/sequence (for error messages)
29
- * @param conflictBehavior - How to handle the conflict
30
- * @param unregister - Function to unregister by ID
31
- */
32
- function handleConflict(conflictingId, keyDisplay, conflictBehavior, unregister) {
33
- if (conflictBehavior === "allow") return;
34
- if (conflictBehavior === "warn") {
35
- console.warn(`'${keyDisplay}' is already registered. Multiple handlers will be triggered. Use conflictBehavior: 'replace' to replace the existing handler, or conflictBehavior: 'allow' to suppress this warning.`);
36
- return;
37
- }
38
- if (conflictBehavior === "error") throw new Error(`'${keyDisplay}' is already registered. Use conflictBehavior: 'replace' to replace the existing handler, or conflictBehavior: 'allow' to allow multiple registrations.`);
39
- unregister(conflictingId);
40
- }
41
- /** Compare updates without publishing unchanged options, including fresh metadata objects. */
42
- function optionsEqual(left, right) {
43
- const a = left;
44
- const b = right;
45
- const keys = Object.keys(a);
46
- return keys.length === Object.keys(b).length && keys.every((key) => key === "meta" && a[key] && b[key] ? optionsEqual(a[key], b[key]) : Object.is(a[key], b[key]));
47
- }
48
-
49
- //#endregion
50
- exports.defaultHotkeyOptions = defaultHotkeyOptions;
51
- exports.getDefaultIgnoreInputs = getDefaultIgnoreInputs;
52
- exports.handleConflict = handleConflict;
53
- exports.optionsEqual = optionsEqual;
54
- //# sourceMappingURL=_registration.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_registration.cjs","names":[],"sources":["../src/_registration.ts"],"sourcesContent":["import type { ConflictBehavior, ParsedHotkey } from './hotkey.types'\n\n/**\n * Default options for hotkey/sequence registration.\n * Omitted: platform, target (resolved at registration), requireReset (HotkeyManager only).\n */\nexport const defaultHotkeyOptions = {\n preventDefault: true,\n stopPropagation: true,\n eventType: 'keydown' as const,\n enabled: true,\n ignoreInputs: true,\n conflictBehavior: 'warn' as ConflictBehavior,\n}\n\n/**\n * Computes the default ignoreInputs value based on the hotkey.\n * Ctrl/Meta shortcuts and Escape fire in inputs; single keys and Shift/Alt combos are ignored.\n */\nexport function getDefaultIgnoreInputs(parsedHotkey: ParsedHotkey): boolean {\n if (parsedHotkey.ctrl || parsedHotkey.meta) return false // Mod+S, Ctrl+C, etc.\n if (parsedHotkey.key === 'Escape' || parsedHotkey.code === 'Escape')\n return false // Close modal, etc.\n return true // Single keys, Shift+key, Alt+key\n}\n\n/**\n * Handles conflicts between registrations based on conflict behavior.\n *\n * @param conflictingId - The ID of the conflicting registration\n * @param keyDisplay - Display string for the conflicting key/sequence (for error messages)\n * @param conflictBehavior - How to handle the conflict\n * @param unregister - Function to unregister by ID\n */\nexport function handleConflict(\n conflictingId: string,\n keyDisplay: string,\n conflictBehavior: ConflictBehavior,\n unregister: (id: string) => void,\n): void {\n if (conflictBehavior === 'allow') {\n return\n }\n\n if (conflictBehavior === 'warn') {\n console.warn(\n `'${keyDisplay}' is already registered. Multiple handlers will be triggered. ` +\n `Use conflictBehavior: 'replace' to replace the existing handler, ` +\n `or conflictBehavior: 'allow' to suppress this warning.`,\n )\n return\n }\n\n if (conflictBehavior === 'error') {\n throw new Error(\n `'${keyDisplay}' is already registered. ` +\n `Use conflictBehavior: 'replace' to replace the existing handler, ` +\n `or conflictBehavior: 'allow' to allow multiple registrations.`,\n )\n }\n\n // At this point, conflictBehavior must be 'replace'\n unregister(conflictingId)\n}\n\n/** Compare updates without publishing unchanged options, including fresh metadata objects. */\nexport function optionsEqual(left: object, right: object): boolean {\n const a = left as Record<string, unknown>\n const b = right as Record<string, unknown>\n const keys = Object.keys(a)\n return (\n keys.length === Object.keys(b).length &&\n keys.every((key) =>\n key === 'meta' && a[key] && b[key]\n ? optionsEqual(a[key] as object, b[key] as object)\n : Object.is(a[key], b[key]),\n )\n )\n}\n"],"mappings":";;;;;;AAMA,MAAa,uBAAuB;CAClC,gBAAgB;CAChB,iBAAiB;CACjB,WAAW;CACX,SAAS;CACT,cAAc;CACd,kBAAkB;CACnB;;;;;AAMD,SAAgB,uBAAuB,cAAqC;AAC1E,KAAI,aAAa,QAAQ,aAAa,KAAM,QAAO;AACnD,KAAI,aAAa,QAAQ,YAAY,aAAa,SAAS,SACzD,QAAO;AACT,QAAO;;;;;;;;;;AAWT,SAAgB,eACd,eACA,YACA,kBACA,YACM;AACN,KAAI,qBAAqB,QACvB;AAGF,KAAI,qBAAqB,QAAQ;AAC/B,UAAQ,KACN,IAAI,WAAW,uLAGhB;AACD;;AAGF,KAAI,qBAAqB,QACvB,OAAM,IAAI,MACR,IAAI,WAAW,yJAGhB;AAIH,YAAW,cAAc;;;AAI3B,SAAgB,aAAa,MAAc,OAAwB;CACjE,MAAM,IAAI;CACV,MAAM,IAAI;CACV,MAAM,OAAO,OAAO,KAAK,EAAE;AAC3B,QACE,KAAK,WAAW,OAAO,KAAK,EAAE,CAAC,UAC/B,KAAK,OAAO,QACV,QAAQ,UAAU,EAAE,QAAQ,EAAE,OAC1B,aAAa,EAAE,MAAgB,EAAE,KAAe,GAChD,OAAO,GAAG,EAAE,MAAM,EAAE,KAAK,CAC9B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_registration.js","names":[],"sources":["../src/_registration.ts"],"sourcesContent":["import type { ConflictBehavior, ParsedHotkey } from './hotkey.types'\n\n/**\n * Default options for hotkey/sequence registration.\n * Omitted: platform, target (resolved at registration), requireReset (HotkeyManager only).\n */\nexport const defaultHotkeyOptions = {\n preventDefault: true,\n stopPropagation: true,\n eventType: 'keydown' as const,\n enabled: true,\n ignoreInputs: true,\n conflictBehavior: 'warn' as ConflictBehavior,\n}\n\n/**\n * Computes the default ignoreInputs value based on the hotkey.\n * Ctrl/Meta shortcuts and Escape fire in inputs; single keys and Shift/Alt combos are ignored.\n */\nexport function getDefaultIgnoreInputs(parsedHotkey: ParsedHotkey): boolean {\n if (parsedHotkey.ctrl || parsedHotkey.meta) return false // Mod+S, Ctrl+C, etc.\n if (parsedHotkey.key === 'Escape' || parsedHotkey.code === 'Escape')\n return false // Close modal, etc.\n return true // Single keys, Shift+key, Alt+key\n}\n\n/**\n * Handles conflicts between registrations based on conflict behavior.\n *\n * @param conflictingId - The ID of the conflicting registration\n * @param keyDisplay - Display string for the conflicting key/sequence (for error messages)\n * @param conflictBehavior - How to handle the conflict\n * @param unregister - Function to unregister by ID\n */\nexport function handleConflict(\n conflictingId: string,\n keyDisplay: string,\n conflictBehavior: ConflictBehavior,\n unregister: (id: string) => void,\n): void {\n if (conflictBehavior === 'allow') {\n return\n }\n\n if (conflictBehavior === 'warn') {\n console.warn(\n `'${keyDisplay}' is already registered. Multiple handlers will be triggered. ` +\n `Use conflictBehavior: 'replace' to replace the existing handler, ` +\n `or conflictBehavior: 'allow' to suppress this warning.`,\n )\n return\n }\n\n if (conflictBehavior === 'error') {\n throw new Error(\n `'${keyDisplay}' is already registered. ` +\n `Use conflictBehavior: 'replace' to replace the existing handler, ` +\n `or conflictBehavior: 'allow' to allow multiple registrations.`,\n )\n }\n\n // At this point, conflictBehavior must be 'replace'\n unregister(conflictingId)\n}\n\n/** Compare updates without publishing unchanged options, including fresh metadata objects. */\nexport function optionsEqual(left: object, right: object): boolean {\n const a = left as Record<string, unknown>\n const b = right as Record<string, unknown>\n const keys = Object.keys(a)\n return (\n keys.length === Object.keys(b).length &&\n keys.every((key) =>\n key === 'meta' && a[key] && b[key]\n ? optionsEqual(a[key] as object, b[key] as object)\n : Object.is(a[key], b[key]),\n )\n )\n}\n"],"mappings":";;;;;AAMA,MAAa,uBAAuB;CAClC,gBAAgB;CAChB,iBAAiB;CACjB,WAAW;CACX,SAAS;CACT,cAAc;CACd,kBAAkB;CACnB;;;;;AAMD,SAAgB,uBAAuB,cAAqC;AAC1E,KAAI,aAAa,QAAQ,aAAa,KAAM,QAAO;AACnD,KAAI,aAAa,QAAQ,YAAY,aAAa,SAAS,SACzD,QAAO;AACT,QAAO;;;;;;;;;;AAWT,SAAgB,eACd,eACA,YACA,kBACA,YACM;AACN,KAAI,qBAAqB,QACvB;AAGF,KAAI,qBAAqB,QAAQ;AAC/B,UAAQ,KACN,IAAI,WAAW,uLAGhB;AACD;;AAGF,KAAI,qBAAqB,QACvB,OAAM,IAAI,MACR,IAAI,WAAW,yJAGhB;AAIH,YAAW,cAAc;;;AAI3B,SAAgB,aAAa,MAAc,OAAwB;CACjE,MAAM,IAAI;CACV,MAAM,IAAI;CACV,MAAM,OAAO,OAAO,KAAK,EAAE;AAC3B,QACE,KAAK,WAAW,OAAO,KAAK,EAAE,CAAC,UAC/B,KAAK,OAAO,QACV,QAAQ,UAAU,EAAE,QAAQ,EAAE,OAC1B,aAAa,EAAE,MAAgB,EAAE,KAAe,GAChD,OAAO,GAAG,EAAE,MAAM,EAAE,KAAK,CAC9B"}
@@ -1,55 +0,0 @@
1
- const require_platform = require('./platform.cjs');
2
- const require__keyboard_event = require('./_keyboard-event.cjs');
3
- const require_parse = require('./parse.cjs');
4
- const require_match = require('./match.cjs');
5
- const require_hotkey_manager = require('./hotkey-manager.cjs');
6
- const require_sequence_manager = require('./sequence-manager.cjs');
7
-
8
- //#region src/conflicts.ts
9
- /** Whether two targets share a document and one contains the other (or is its window). */
10
- function targetsOverlap(a, b) {
11
- if (a === b) return true;
12
- const documentOf = (target) => {
13
- if ("document" in target) return target.document;
14
- if (target.nodeType === 9) return target;
15
- return target.ownerDocument;
16
- };
17
- if (documentOf(a) !== documentOf(b)) return false;
18
- if (!("contains" in a) || !("contains" in b)) return true;
19
- return a.contains(b) || b.contains(a);
20
- }
21
- /**
22
- * Find equivalent bindings and sequence-prefix conflicts in the live registry.
23
- * With source events, also checks observed logical/physical overlap. Does not
24
- * discover unmounted handlers, external listeners, or other keyboard layouts.
25
- */
26
- function findHotkeyConflicts(candidate, options = {}) {
27
- const steps = Array.isArray(candidate) ? candidate : [candidate];
28
- if (steps.length === 0) return [];
29
- const platform = options.platform ?? require_platform.detectPlatform();
30
- const parsed = steps.map((step) => require_parse.parseRegisterableHotkey(step, platform));
31
- const target = options.target ?? (typeof document === "undefined" ? void 0 : document);
32
- const conflicts = [];
33
- const matches = (registration, bindings) => {
34
- if (!options.includeDisabled && registration.options.enabled === false || (registration.options.eventType ?? "keydown") !== (options.eventType ?? "keydown") || options.excludeIds?.includes(registration.id) || options.exclude?.(registration) || options.scope !== "all" && target && !targetsOverlap(target, registration.target)) return false;
35
- return parsed.slice(0, Math.min(parsed.length, bindings.length)).every((a, index) => {
36
- const b = require_parse.parseRegisterableHotkey(bindings[index], registration.options.platform);
37
- const same = a.ctrl === b.ctrl && a.alt === b.alt && a.shift === b.shift && a.meta === b.meta && (a.code !== void 0 || b.code !== void 0 ? a.code === b.code : require__keyboard_event.keysEqual(a.key, b.key));
38
- const event = options.events?.[index];
39
- return same || event !== void 0 && require_match.matchesKeyboardEvent(event, a, platform) && require_match.matchesKeyboardEvent(event, b, registration.options.platform);
40
- });
41
- };
42
- for (const registration of require_hotkey_manager.getHotkeyManager().registrations.state.values()) if (matches(registration, [registration.hotkey])) conflicts.push({
43
- type: "hotkey",
44
- registration: require_hotkey_manager.toHotkeyRegistrationView(registration)
45
- });
46
- for (const registration of require_sequence_manager.getSequenceManager().registrations.state.values()) if (matches(registration, registration.sequence)) conflicts.push({
47
- type: "sequence",
48
- registration
49
- });
50
- return conflicts;
51
- }
52
-
53
- //#endregion
54
- exports.findHotkeyConflicts = findHotkeyConflicts;
55
- //# sourceMappingURL=conflicts.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conflicts.cjs","names":["detectPlatform","parseRegisterableHotkey","keysEqual","matchesKeyboardEvent","getHotkeyManager","toHotkeyRegistrationView","getSequenceManager"],"sources":["../src/conflicts.ts"],"sourcesContent":["import { detectPlatform } from './platform'\nimport { parseRegisterableHotkey } from './parse'\nimport { keysEqual } from './_keyboard-event'\nimport { matchesKeyboardEvent } from './match'\nimport { getHotkeyManager, toHotkeyRegistrationView } from './hotkey-manager'\nimport { getSequenceManager } from './sequence-manager'\nimport type { HotkeyRegistrationView } from './hotkey-manager'\nimport type { SequenceRegistrationView } from './sequence-manager'\nimport type { RegisterableHotkey } from './hotkey.types'\n\nexport type HotkeyConflict =\n | { type: 'hotkey'; registration: HotkeyRegistrationView }\n | { type: 'sequence'; registration: SequenceRegistrationView }\n\nexport interface HotkeyConflictOptions {\n /** Intended registration target. Defaults to document; disjoint targets are excluded. */\n target?: HTMLElement | Document | Window\n /** Check all targets instead of overlapping targets. Default: overlapping. */\n scope?: 'overlapping' | 'all'\n /** Intended event type. Default: keydown. */\n eventType?: 'keydown' | 'keyup'\n /** Include disabled registrations. Default: false. */\n includeDisabled?: boolean\n /** IDs of registrations being edited. */\n excludeIds?: ReadonlyArray<string>\n /** Additional app-specific exclusions, such as all instances of one action. */\n exclude?: (\n registration: HotkeyRegistrationView | SequenceRegistrationView,\n ) => boolean\n /** Platform used to resolve Mod in the candidate. Registrations retain their own platform. */\n platform?: 'mac' | 'windows' | 'linux'\n /** Source events allow detecting physical/logical overlap on the recorded layout. */\n events?: ReadonlyArray<KeyboardEvent>\n}\n\n/** Whether two targets share a document and one contains the other (or is its window). */\nfunction targetsOverlap(\n a: HTMLElement | Document | Window,\n b: HTMLElement | Document | Window,\n): boolean {\n if (a === b) return true\n // Resolve the owning document without relying on cross-frame instanceof checks.\n const documentOf = (target: typeof a): Document | null => {\n if ('document' in target) return target.document\n if (target.nodeType === 9) return target as Document\n return target.ownerDocument\n }\n if (documentOf(a) !== documentOf(b)) return false\n if (!('contains' in a) || !('contains' in b)) return true\n return a.contains(b as Node) || b.contains(a as Node)\n}\n\n/**\n * Find equivalent bindings and sequence-prefix conflicts in the live registry.\n * With source events, also checks observed logical/physical overlap. Does not\n * discover unmounted handlers, external listeners, or other keyboard layouts.\n */\nexport function findHotkeyConflicts(\n candidate: RegisterableHotkey | ReadonlyArray<RegisterableHotkey>,\n options: HotkeyConflictOptions = {},\n): Array<HotkeyConflict> {\n const steps: ReadonlyArray<RegisterableHotkey> = Array.isArray(candidate)\n ? candidate\n : [candidate as RegisterableHotkey]\n if (steps.length === 0) return []\n const platform = options.platform ?? detectPlatform()\n const parsed = steps.map((step) => parseRegisterableHotkey(step, platform))\n const target =\n options.target ?? (typeof document === 'undefined' ? undefined : document)\n const conflicts: Array<HotkeyConflict> = []\n // Apply scope policy before comparing equivalent bindings or observed event overlap.\n const matches = (\n registration: HotkeyRegistrationView | SequenceRegistrationView,\n bindings: ReadonlyArray<RegisterableHotkey>,\n ) => {\n if (\n (!options.includeDisabled && registration.options.enabled === false) ||\n (registration.options.eventType ?? 'keydown') !==\n (options.eventType ?? 'keydown') ||\n options.excludeIds?.includes(registration.id) ||\n options.exclude?.(registration) ||\n (options.scope !== 'all' &&\n target &&\n !targetsOverlap(target, registration.target))\n )\n return false\n // Compare only the common prefix: a shorter sequence can collide with a longer one.\n return parsed\n .slice(0, Math.min(parsed.length, bindings.length))\n .every((a, index) => {\n const b = parseRegisterableHotkey(\n bindings[index]!,\n registration.options.platform,\n )\n const same =\n a.ctrl === b.ctrl &&\n a.alt === b.alt &&\n a.shift === b.shift &&\n a.meta === b.meta &&\n (a.code !== undefined || b.code !== undefined\n ? a.code === b.code\n : keysEqual(a.key, b.key))\n // A source event can establish code/key overlap without guessing its layout.\n const event = options.events?.[index]\n return (\n same ||\n (event !== undefined &&\n matchesKeyboardEvent(event, a, platform) &&\n matchesKeyboardEvent(event, b, registration.options.platform))\n )\n })\n }\n for (const registration of getHotkeyManager().registrations.state.values()) {\n if (matches(registration, [registration.hotkey]))\n conflicts.push({\n type: 'hotkey',\n registration: toHotkeyRegistrationView(registration),\n })\n }\n for (const registration of getSequenceManager().registrations.state.values()) {\n if (matches(registration, registration.sequence))\n conflicts.push({ type: 'sequence', registration })\n }\n return conflicts\n}\n"],"mappings":";;;;;;;;;AAoCA,SAAS,eACP,GACA,GACS;AACT,KAAI,MAAM,EAAG,QAAO;CAEpB,MAAM,cAAc,WAAsC;AACxD,MAAI,cAAc,OAAQ,QAAO,OAAO;AACxC,MAAI,OAAO,aAAa,EAAG,QAAO;AAClC,SAAO,OAAO;;AAEhB,KAAI,WAAW,EAAE,KAAK,WAAW,EAAE,CAAE,QAAO;AAC5C,KAAI,EAAE,cAAc,MAAM,EAAE,cAAc,GAAI,QAAO;AACrD,QAAO,EAAE,SAAS,EAAU,IAAI,EAAE,SAAS,EAAU;;;;;;;AAQvD,SAAgB,oBACd,WACA,UAAiC,EAAE,EACZ;CACvB,MAAM,QAA2C,MAAM,QAAQ,UAAU,GACrE,YACA,CAAC,UAAgC;AACrC,KAAI,MAAM,WAAW,EAAG,QAAO,EAAE;CACjC,MAAM,WAAW,QAAQ,YAAYA,iCAAgB;CACrD,MAAM,SAAS,MAAM,KAAK,SAASC,sCAAwB,MAAM,SAAS,CAAC;CAC3E,MAAM,SACJ,QAAQ,WAAW,OAAO,aAAa,cAAc,SAAY;CACnE,MAAM,YAAmC,EAAE;CAE3C,MAAM,WACJ,cACA,aACG;AACH,MACG,CAAC,QAAQ,mBAAmB,aAAa,QAAQ,YAAY,UAC7D,aAAa,QAAQ,aAAa,gBAChC,QAAQ,aAAa,cACxB,QAAQ,YAAY,SAAS,aAAa,GAAG,IAC7C,QAAQ,UAAU,aAAa,IAC9B,QAAQ,UAAU,SACjB,UACA,CAAC,eAAe,QAAQ,aAAa,OAAO,CAE9C,QAAO;AAET,SAAO,OACJ,MAAM,GAAG,KAAK,IAAI,OAAO,QAAQ,SAAS,OAAO,CAAC,CAClD,OAAO,GAAG,UAAU;GACnB,MAAM,IAAIA,sCACR,SAAS,QACT,aAAa,QAAQ,SACtB;GACD,MAAM,OACJ,EAAE,SAAS,EAAE,QACb,EAAE,QAAQ,EAAE,OACZ,EAAE,UAAU,EAAE,SACd,EAAE,SAAS,EAAE,SACZ,EAAE,SAAS,UAAa,EAAE,SAAS,SAChC,EAAE,SAAS,EAAE,OACbC,kCAAU,EAAE,KAAK,EAAE,IAAI;GAE7B,MAAM,QAAQ,QAAQ,SAAS;AAC/B,UACE,QACC,UAAU,UACTC,mCAAqB,OAAO,GAAG,SAAS,IACxCA,mCAAqB,OAAO,GAAG,aAAa,QAAQ,SAAS;IAEjE;;AAEN,MAAK,MAAM,gBAAgBC,yCAAkB,CAAC,cAAc,MAAM,QAAQ,CACxE,KAAI,QAAQ,cAAc,CAAC,aAAa,OAAO,CAAC,CAC9C,WAAU,KAAK;EACb,MAAM;EACN,cAAcC,gDAAyB,aAAa;EACrD,CAAC;AAEN,MAAK,MAAM,gBAAgBC,6CAAoB,CAAC,cAAc,MAAM,QAAQ,CAC1E,KAAI,QAAQ,cAAc,aAAa,SAAS,CAC9C,WAAU,KAAK;EAAE,MAAM;EAAY;EAAc,CAAC;AAEtD,QAAO"}
@@ -1,39 +0,0 @@
1
- import { RegisterableHotkey } from "./hotkey.types.cjs";
2
- import { HotkeyRegistrationView } from "./hotkey-manager.cjs";
3
- import { SequenceRegistrationView } from "./sequence-manager.cjs";
4
-
5
- //#region src/conflicts.d.ts
6
- type HotkeyConflict = {
7
- type: 'hotkey';
8
- registration: HotkeyRegistrationView;
9
- } | {
10
- type: 'sequence';
11
- registration: SequenceRegistrationView;
12
- };
13
- interface HotkeyConflictOptions {
14
- /** Intended registration target. Defaults to document; disjoint targets are excluded. */
15
- target?: HTMLElement | Document | Window;
16
- /** Check all targets instead of overlapping targets. Default: overlapping. */
17
- scope?: 'overlapping' | 'all';
18
- /** Intended event type. Default: keydown. */
19
- eventType?: 'keydown' | 'keyup';
20
- /** Include disabled registrations. Default: false. */
21
- includeDisabled?: boolean;
22
- /** IDs of registrations being edited. */
23
- excludeIds?: ReadonlyArray<string>;
24
- /** Additional app-specific exclusions, such as all instances of one action. */
25
- exclude?: (registration: HotkeyRegistrationView | SequenceRegistrationView) => boolean;
26
- /** Platform used to resolve Mod in the candidate. Registrations retain their own platform. */
27
- platform?: 'mac' | 'windows' | 'linux';
28
- /** Source events allow detecting physical/logical overlap on the recorded layout. */
29
- events?: ReadonlyArray<KeyboardEvent>;
30
- }
31
- /**
32
- * Find equivalent bindings and sequence-prefix conflicts in the live registry.
33
- * With source events, also checks observed logical/physical overlap. Does not
34
- * discover unmounted handlers, external listeners, or other keyboard layouts.
35
- */
36
- declare function findHotkeyConflicts(candidate: RegisterableHotkey | ReadonlyArray<RegisterableHotkey>, options?: HotkeyConflictOptions): Array<HotkeyConflict>;
37
- //#endregion
38
- export { HotkeyConflict, HotkeyConflictOptions, findHotkeyConflicts };
39
- //# sourceMappingURL=conflicts.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conflicts.js","names":[],"sources":["../src/conflicts.ts"],"sourcesContent":["import { detectPlatform } from './platform'\nimport { parseRegisterableHotkey } from './parse'\nimport { keysEqual } from './_keyboard-event'\nimport { matchesKeyboardEvent } from './match'\nimport { getHotkeyManager, toHotkeyRegistrationView } from './hotkey-manager'\nimport { getSequenceManager } from './sequence-manager'\nimport type { HotkeyRegistrationView } from './hotkey-manager'\nimport type { SequenceRegistrationView } from './sequence-manager'\nimport type { RegisterableHotkey } from './hotkey.types'\n\nexport type HotkeyConflict =\n | { type: 'hotkey'; registration: HotkeyRegistrationView }\n | { type: 'sequence'; registration: SequenceRegistrationView }\n\nexport interface HotkeyConflictOptions {\n /** Intended registration target. Defaults to document; disjoint targets are excluded. */\n target?: HTMLElement | Document | Window\n /** Check all targets instead of overlapping targets. Default: overlapping. */\n scope?: 'overlapping' | 'all'\n /** Intended event type. Default: keydown. */\n eventType?: 'keydown' | 'keyup'\n /** Include disabled registrations. Default: false. */\n includeDisabled?: boolean\n /** IDs of registrations being edited. */\n excludeIds?: ReadonlyArray<string>\n /** Additional app-specific exclusions, such as all instances of one action. */\n exclude?: (\n registration: HotkeyRegistrationView | SequenceRegistrationView,\n ) => boolean\n /** Platform used to resolve Mod in the candidate. Registrations retain their own platform. */\n platform?: 'mac' | 'windows' | 'linux'\n /** Source events allow detecting physical/logical overlap on the recorded layout. */\n events?: ReadonlyArray<KeyboardEvent>\n}\n\n/** Whether two targets share a document and one contains the other (or is its window). */\nfunction targetsOverlap(\n a: HTMLElement | Document | Window,\n b: HTMLElement | Document | Window,\n): boolean {\n if (a === b) return true\n // Resolve the owning document without relying on cross-frame instanceof checks.\n const documentOf = (target: typeof a): Document | null => {\n if ('document' in target) return target.document\n if (target.nodeType === 9) return target as Document\n return target.ownerDocument\n }\n if (documentOf(a) !== documentOf(b)) return false\n if (!('contains' in a) || !('contains' in b)) return true\n return a.contains(b as Node) || b.contains(a as Node)\n}\n\n/**\n * Find equivalent bindings and sequence-prefix conflicts in the live registry.\n * With source events, also checks observed logical/physical overlap. Does not\n * discover unmounted handlers, external listeners, or other keyboard layouts.\n */\nexport function findHotkeyConflicts(\n candidate: RegisterableHotkey | ReadonlyArray<RegisterableHotkey>,\n options: HotkeyConflictOptions = {},\n): Array<HotkeyConflict> {\n const steps: ReadonlyArray<RegisterableHotkey> = Array.isArray(candidate)\n ? candidate\n : [candidate as RegisterableHotkey]\n if (steps.length === 0) return []\n const platform = options.platform ?? detectPlatform()\n const parsed = steps.map((step) => parseRegisterableHotkey(step, platform))\n const target =\n options.target ?? (typeof document === 'undefined' ? undefined : document)\n const conflicts: Array<HotkeyConflict> = []\n // Apply scope policy before comparing equivalent bindings or observed event overlap.\n const matches = (\n registration: HotkeyRegistrationView | SequenceRegistrationView,\n bindings: ReadonlyArray<RegisterableHotkey>,\n ) => {\n if (\n (!options.includeDisabled && registration.options.enabled === false) ||\n (registration.options.eventType ?? 'keydown') !==\n (options.eventType ?? 'keydown') ||\n options.excludeIds?.includes(registration.id) ||\n options.exclude?.(registration) ||\n (options.scope !== 'all' &&\n target &&\n !targetsOverlap(target, registration.target))\n )\n return false\n // Compare only the common prefix: a shorter sequence can collide with a longer one.\n return parsed\n .slice(0, Math.min(parsed.length, bindings.length))\n .every((a, index) => {\n const b = parseRegisterableHotkey(\n bindings[index]!,\n registration.options.platform,\n )\n const same =\n a.ctrl === b.ctrl &&\n a.alt === b.alt &&\n a.shift === b.shift &&\n a.meta === b.meta &&\n (a.code !== undefined || b.code !== undefined\n ? a.code === b.code\n : keysEqual(a.key, b.key))\n // A source event can establish code/key overlap without guessing its layout.\n const event = options.events?.[index]\n return (\n same ||\n (event !== undefined &&\n matchesKeyboardEvent(event, a, platform) &&\n matchesKeyboardEvent(event, b, registration.options.platform))\n )\n })\n }\n for (const registration of getHotkeyManager().registrations.state.values()) {\n if (matches(registration, [registration.hotkey]))\n conflicts.push({\n type: 'hotkey',\n registration: toHotkeyRegistrationView(registration),\n })\n }\n for (const registration of getSequenceManager().registrations.state.values()) {\n if (matches(registration, registration.sequence))\n conflicts.push({ type: 'sequence', registration })\n }\n return conflicts\n}\n"],"mappings":";;;;;;;;;AAoCA,SAAS,eACP,GACA,GACS;AACT,KAAI,MAAM,EAAG,QAAO;CAEpB,MAAM,cAAc,WAAsC;AACxD,MAAI,cAAc,OAAQ,QAAO,OAAO;AACxC,MAAI,OAAO,aAAa,EAAG,QAAO;AAClC,SAAO,OAAO;;AAEhB,KAAI,WAAW,EAAE,KAAK,WAAW,EAAE,CAAE,QAAO;AAC5C,KAAI,EAAE,cAAc,MAAM,EAAE,cAAc,GAAI,QAAO;AACrD,QAAO,EAAE,SAAS,EAAU,IAAI,EAAE,SAAS,EAAU;;;;;;;AAQvD,SAAgB,oBACd,WACA,UAAiC,EAAE,EACZ;CACvB,MAAM,QAA2C,MAAM,QAAQ,UAAU,GACrE,YACA,CAAC,UAAgC;AACrC,KAAI,MAAM,WAAW,EAAG,QAAO,EAAE;CACjC,MAAM,WAAW,QAAQ,YAAY,gBAAgB;CACrD,MAAM,SAAS,MAAM,KAAK,SAAS,wBAAwB,MAAM,SAAS,CAAC;CAC3E,MAAM,SACJ,QAAQ,WAAW,OAAO,aAAa,cAAc,SAAY;CACnE,MAAM,YAAmC,EAAE;CAE3C,MAAM,WACJ,cACA,aACG;AACH,MACG,CAAC,QAAQ,mBAAmB,aAAa,QAAQ,YAAY,UAC7D,aAAa,QAAQ,aAAa,gBAChC,QAAQ,aAAa,cACxB,QAAQ,YAAY,SAAS,aAAa,GAAG,IAC7C,QAAQ,UAAU,aAAa,IAC9B,QAAQ,UAAU,SACjB,UACA,CAAC,eAAe,QAAQ,aAAa,OAAO,CAE9C,QAAO;AAET,SAAO,OACJ,MAAM,GAAG,KAAK,IAAI,OAAO,QAAQ,SAAS,OAAO,CAAC,CAClD,OAAO,GAAG,UAAU;GACnB,MAAM,IAAI,wBACR,SAAS,QACT,aAAa,QAAQ,SACtB;GACD,MAAM,OACJ,EAAE,SAAS,EAAE,QACb,EAAE,QAAQ,EAAE,OACZ,EAAE,UAAU,EAAE,SACd,EAAE,SAAS,EAAE,SACZ,EAAE,SAAS,UAAa,EAAE,SAAS,SAChC,EAAE,SAAS,EAAE,OACb,UAAU,EAAE,KAAK,EAAE,IAAI;GAE7B,MAAM,QAAQ,QAAQ,SAAS;AAC/B,UACE,QACC,UAAU,UACT,qBAAqB,OAAO,GAAG,SAAS,IACxC,qBAAqB,OAAO,GAAG,aAAa,QAAQ,SAAS;IAEjE;;AAEN,MAAK,MAAM,gBAAgB,kBAAkB,CAAC,cAAc,MAAM,QAAQ,CACxE,KAAI,QAAQ,cAAc,CAAC,aAAa,OAAO,CAAC,CAC9C,WAAU,KAAK;EACb,MAAM;EACN,cAAc,yBAAyB,aAAa;EACrD,CAAC;AAEN,MAAK,MAAM,gBAAgB,oBAAoB,CAAC,cAAc,MAAM,QAAQ,CAC1E,KAAI,QAAQ,cAAc,aAAa,SAAS,CAC9C,WAAU,KAAK;EAAE,MAAM;EAAY;EAAc,CAAC;AAEtD,QAAO"}