@tanstack/hotkeys 0.24.0 → 0.25.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 (53) hide show
  1. package/dist/_app-hotkeys-factory.d.ts +1 -3
  2. package/dist/_app-hotkeys-factory.js +2 -3
  3. package/dist/_event-target.js +25 -15
  4. package/dist/_keyboard-event.js +8 -5
  5. package/dist/_manager-ref.js +1 -2
  6. package/dist/_match.js +5 -3
  7. package/dist/_named-keys.d.ts +1 -3
  8. package/dist/_named-keys.js +1 -2
  9. package/dist/_recorder-chord.js +7 -13
  10. package/dist/_recording-guard.js +1 -2
  11. package/dist/_registration.d.ts +1 -3
  12. package/dist/_registration.js +11 -6
  13. package/dist/_server.js +2 -3
  14. package/dist/_store.d.ts +1 -3
  15. package/dist/_store.js +1 -2
  16. package/dist/_target-listeners.js +1 -2
  17. package/dist/adapter.d.ts +3 -5
  18. package/dist/adapter.js +2 -3
  19. package/dist/app-hotkeys.d.ts +4 -5
  20. package/dist/app-hotkeys.js +10 -21
  21. package/dist/conflicts.d.ts +2 -4
  22. package/dist/conflicts.js +5 -6
  23. package/dist/constants.d.ts +2 -4
  24. package/dist/constants.js +1 -2
  25. package/dist/display-labels.d.ts +1 -3
  26. package/dist/display-labels.js +1 -2
  27. package/dist/format.d.ts +2 -4
  28. package/dist/format.js +4 -5
  29. package/dist/hint.d.ts +1 -3
  30. package/dist/hint.js +1 -2
  31. package/dist/hotkey-manager.d.ts +1 -3
  32. package/dist/hotkey-manager.js +12 -13
  33. package/dist/hotkey-recorder.d.ts +19 -7
  34. package/dist/hotkey-recorder.js +8 -11
  35. package/dist/hotkey.types.d.ts +1 -3
  36. package/dist/index.d.ts +8 -8
  37. package/dist/index.js +3 -3
  38. package/dist/key-state-tracker.d.ts +1 -3
  39. package/dist/key-state-tracker.js +2 -3
  40. package/dist/key.types.d.ts +5 -6
  41. package/dist/match.d.ts +4 -5
  42. package/dist/match.js +7 -7
  43. package/dist/parse.d.ts +2 -3
  44. package/dist/parse.js +10 -19
  45. package/dist/platform.d.ts +1 -3
  46. package/dist/platform.js +1 -2
  47. package/dist/validate.d.ts +33 -13
  48. package/dist/validate.js +51 -34
  49. package/dist/vendor/@tanstack/store/dist/store.d.ts +0 -2
  50. package/dist/vendor/@tanstack/store/dist/types.d.ts +0 -2
  51. package/package.json +4 -4
  52. package/skills/app-hotkeys/SKILL.md +1 -1
  53. package/skills/hotkeys-core/SKILL.md +1 -1
@@ -1,5 +1,3 @@
1
1
  import { AppHotkeys, AppHotkeysStore, AppHotkeysStoreOptions } from "./app-hotkeys.js";
2
- //#region src/_app-hotkeys-factory.d.ts
3
2
  /** Creates the store for app hotkeys from `defineAppHotkeys`. */
4
- export declare function createAppHotkeysStore<T extends AppHotkeys>(appHotkeys: T, options: AppHotkeysStoreOptions<T>): AppHotkeysStore<T>;
5
- //#endregion
3
+ export declare function createAppHotkeysStore<T extends AppHotkeys>(appHotkeys: T, options: AppHotkeysStoreOptions<T>): AppHotkeysStore<T>;
@@ -1,13 +1,12 @@
1
- //#region src/_app-hotkeys-factory.ts
1
+ // src/_app-hotkeys-factory.ts
2
2
  let factory;
3
3
  function setAppHotkeysStoreFactory(create) {
4
4
  factory = create;
5
5
  }
6
6
  /** Creates the store for app hotkeys from `defineAppHotkeys`. */
7
7
  function createAppHotkeysStore(appHotkeys, options) {
8
- if (!factory) throw new Error("[hotkeys] App hotkeys must come from defineAppHotkeys.");
8
+ if (!factory) throw new Error("[hotkeys] App hotkeys must come from defineAppHotkeys");
9
9
  return factory(appHotkeys, options);
10
10
  }
11
11
 
12
- //#endregion
13
12
  export { createAppHotkeysStore, setAppHotkeysStoreFactory };
@@ -1,6 +1,7 @@
1
+ import { codeToLogicalKey } from "./_keyboard-event.js";
1
2
  import { normalizeKeyName } from "./constants.js";
2
3
 
3
- //#region src/_event-target.ts
4
+ // src/_event-target.ts
4
5
  function isElement(node) {
5
6
  return node?.nodeType === 1;
6
7
  }
@@ -220,11 +221,11 @@ function getOwnedKeys(element) {
220
221
  * are never owned; Enter and Space are owned only without modifiers, while
221
222
  * navigation and typeahead keys allow Shift (for example, to extend a selection).
222
223
  */
223
- function ownsKey(element, event, key) {
224
+ function ownsKey(element, event, key, produced) {
224
225
  if (event.ctrlKey || event.metaKey || event.altKey) return false;
225
226
  const owned = getOwnedKeys(element);
226
227
  if (key === "Enter" || key === "Space") return !event.shiftKey && owned.includes(key);
227
- return owned.includes(key) || owned.includes(TYPEAHEAD) && /^.$/u.test(event.key);
228
+ return owned.includes(key) || owned.includes(TYPEAHEAD) && /^.$/u.test(produced);
228
229
  }
229
230
  /**
230
231
  * Finds the first element on the event's way out that blocks it, walking out
@@ -252,6 +253,11 @@ function blocks({ elements, index }, target) {
252
253
  }
253
254
  /** Open modal dialogs and popovers that close on Escape, per event. */
254
255
  const layerCache = /* @__PURE__ */ new WeakMap();
256
+ /** Whether `node` is `layer` or inside it, across shadow roots. */
257
+ function isInside(node, layer) {
258
+ for (; node; node = composedParent(node)) if (node === layer) return true;
259
+ return false;
260
+ }
255
261
  /**
256
262
  * Returns whether Escape will close a modal `<dialog>` or an open popover that
257
263
  * `target` isn't inside. The browser closes it only when Escape's keydown
@@ -261,17 +267,21 @@ function closesNativeLayer(event, target) {
261
267
  if (event.type !== "keydown" || event.key !== "Escape") return false;
262
268
  let layers = layerCache.get(event);
263
269
  if (!layers) {
264
- const found = [];
265
- for (const selector of ["dialog:modal", ":popover-open"]) try {
266
- found.push(...getTargetDocument(target).querySelectorAll(selector));
267
- } catch {}
268
- layers = found.filter((layer) => layer.popover !== "manual");
270
+ const doc = getTargetDocument(target);
271
+ const focused = getDeepActiveElement(doc);
272
+ const query = (selector) => {
273
+ try {
274
+ return [...doc.querySelectorAll(selector)];
275
+ } catch {
276
+ return [];
277
+ }
278
+ };
279
+ const dialogs = query("dialog:modal");
280
+ const holding = dialogs.filter((dialog) => isInside(focused, dialog));
281
+ layers = [...holding.length ? holding : dialogs, ...query(":popover-open").filter((layer) => layer.popover !== "manual")];
269
282
  layerCache.set(event, layers);
270
283
  }
271
- return layers.some((layer) => {
272
- for (let node = isDocumentLevel(target) ? null : target; node; node = composedParent(node)) if (node === layer) return false;
273
- return true;
274
- });
284
+ return layers.some((layer) => isDocumentLevel(target) || !isInside(target, layer));
275
285
  }
276
286
  const ignoreCache = /* @__PURE__ */ new WeakMap();
277
287
  /**
@@ -287,8 +297,9 @@ const ignoreCache = /* @__PURE__ */ new WeakMap();
287
297
  function shouldIgnoreInputEvent(event, target) {
288
298
  let blocker = ignoreCache.get(event);
289
299
  if (!blocker) {
290
- const key = normalizeKeyName(event.key);
291
- blocker = findBlocker(event, getTargetDocument(target), (element) => isInputElement(element) || ownsKey(element, event, key));
300
+ const produced = event.key === "Unidentified" ? codeToLogicalKey(event.code) ?? event.code : event.key;
301
+ const key = normalizeKeyName(produced);
302
+ blocker = findBlocker(event, getTargetDocument(target), (element) => isInputElement(element) || ownsKey(element, event, key, produced));
292
303
  ignoreCache.set(event, blocker);
293
304
  }
294
305
  return blocks(blocker, target);
@@ -302,5 +313,4 @@ function isTypingEvent(event, target) {
302
313
  return blocks(findBlocker(event, getTargetDocument(target), isInputElement), target);
303
314
  }
304
315
 
305
- //#endregion
306
316
  export { closesNativeLayer, getTargetDocument, isDocumentLevel, isInputElement, isTypingEvent, shouldIgnoreInputEvent, targetHasFocus };
@@ -1,6 +1,6 @@
1
1
  import { MODIFIER_ALIASES, PUNCTUATION_CODE_MAP, normalizeKeyName } from "./constants.js";
2
2
 
3
- //#region src/_keyboard-event.ts
3
+ // src/_keyboard-event.ts
4
4
  /** Normalizes Unicode key names and modifier flags while retaining physical code and IME state. */
5
5
  function normalizeKeyboardEvent(event, platform) {
6
6
  let altGraph = false;
@@ -22,7 +22,7 @@ function normalizeKeyboardEvent(event, platform) {
22
22
  meta: Boolean(event.metaKey),
23
23
  altGraph: altGraph && !controlAlt,
24
24
  location: event.location,
25
- isComposing: Boolean(event.isComposing) || event.keyCode === 229 || key === "Process" || key === "Unidentified"
25
+ isComposing: Boolean(event.isComposing) || event.keyCode === 229 || key === "Process" || key === "Unidentified" && (!event.code || event.code === "Unidentified" || isModifierCode(event.code))
26
26
  };
27
27
  }
28
28
  /**
@@ -77,6 +77,10 @@ function isCodeName(code) {
77
77
  function physicalCodeOf(token) {
78
78
  return token.startsWith("[") && token.endsWith("]") && isCodeName(token.slice(1, -1)) ? token.slice(1, -1) : void 0;
79
79
  }
80
+ /** Whether a `KeyboardEvent.code` is a modifier key, such as `ShiftLeft`. */
81
+ function isModifierCode(code) {
82
+ return /^(Control|Shift|Alt|Meta)(Left|Right)$/.test(code);
83
+ }
80
84
  /** Splits modifier tokens while retaining a trailing literal plus: `Control++` → `['Control', '+']`. */
81
85
  function splitChordParts(hotkey) {
82
86
  if (!hotkey.endsWith("+")) return hotkey.split("+");
@@ -105,7 +109,7 @@ function keysEqual(left, right) {
105
109
  }
106
110
  /** Rejects reserved physical-token syntax in logical object fields while allowing Unicode characters. */
107
111
  function assertLogicalKey(key) {
108
- if (/^\[.*\]$/s.test(key.trim())) throw new Error("Logical key must not contain a bracketed physical code. Use the code field instead.");
112
+ if (/^\[.*\]$/s.test(key.trim())) throw new Error(`[hotkeys] Put '${key}' in the code field, not key`);
109
113
  }
110
114
  /**
111
115
  * Whether a key is written like a key the library knows: one character,
@@ -116,5 +120,4 @@ function isKeyNameShape(key) {
116
120
  return /^(?:.|F(?:[1-9]|1\d|2[0-4])|[A-Z][a-z][A-Za-z\d]*)$/su.test(key);
117
121
  }
118
122
 
119
- //#endregion
120
- export { altGraphTypedCharacter, assertLogicalKey, codeToLogicalKey, isAsciiLetter, isCodeName, isKeyNameShape, isLatinLetter, isPrintableKey, keysEqual, modifierAlias, normalizeKeyboardEvent, physicalCodeOf, splitChordParts };
123
+ export { altGraphTypedCharacter, assertLogicalKey, codeToLogicalKey, isAsciiLetter, isCodeName, isKeyNameShape, isLatinLetter, isModifierCode, isPrintableKey, keysEqual, modifierAlias, normalizeKeyboardEvent, physicalCodeOf, splitChordParts };
@@ -1,4 +1,4 @@
1
- //#region src/_manager-ref.ts
1
+ // src/_manager-ref.ts
2
2
  /**
3
3
  * The HotkeyManager singleton, once created. Code that only reads the live
4
4
  * registry, such as conflict checks, reads this instead of importing the
@@ -6,5 +6,4 @@
6
6
  */
7
7
  const managerRef = { current: null };
8
8
 
9
- //#endregion
10
9
  export { managerRef };
package/dist/_match.js CHANGED
@@ -1,8 +1,8 @@
1
- import { detectPlatform } from "./platform.js";
2
1
  import { altGraphTypedCharacter, codeToLogicalKey, isAsciiLetter, isLatinLetter, isPrintableKey, keysEqual, normalizeKeyboardEvent } from "./_keyboard-event.js";
3
2
  import { parseHotkey } from "./parse.js";
3
+ import { detectPlatform } from "./platform.js";
4
4
 
5
- //#region src/_match.ts
5
+ // src/_match.ts
6
6
  /** Matches an event and reports the winning candidate and release identity. */
7
7
  function matchKeyboardEvent(event, hotkey, platform = detectPlatform()) {
8
8
  const parsed = typeof hotkey === "string" ? parseHotkey(hotkey, platform) : hotkey;
@@ -44,6 +44,8 @@ function matchKeyboardEvent(event, hotkey, platform = detectPlatform()) {
44
44
  * output that can't name a shortcut falls back:
45
45
  * - non-Latin letters and symbols, such as Russian `ы` or macOS Option's `–`
46
46
  * - dead keys
47
+ * - `Unidentified` keys, except on letter positions, which layouts such as
48
+ * AZERTY rearrange. Chrome on Linux Wayland reports Ctrl+` this way.
47
49
  * - anything on the digit row, where AZERTY types `&` and `é` for 1 and 2
48
50
  * - ASCII symbols typed with Shift, such as `!` for Shift+1
49
51
  * - Latin letters on punctuation keys with Control or Command, so German
@@ -54,6 +56,7 @@ function matchKeyboardEvent(event, hotkey, platform = detectPlatform()) {
54
56
  * sits. AZERTY's `+` on the US `/` position is `+`, not `/`.
55
57
  */
56
58
  function positionStandsIn({ key, code, ctrl, meta, shift, alt }) {
59
+ if (key === "Unidentified") return !code.startsWith("Key");
57
60
  if (key !== "Dead" && !isPrintableKey(key)) return false;
58
61
  if (code.startsWith("Digit")) return true;
59
62
  if (isAsciiLetter(key)) return false;
@@ -77,5 +80,4 @@ function parsedChordsEqual(a, b) {
77
80
  return 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 : keysEqual(a.key, b.key));
78
81
  }
79
82
 
80
- //#endregion
81
83
  export { matchKeyboardEvent, parsedChordsEqual };
@@ -1,6 +1,4 @@
1
- //#region src/_named-keys.d.ts
2
1
  /** Names defined for both KeyboardEvent.key and KeyboardEvent.code, beyond the basic key groups. */
3
2
  export declare const SHARED_NAMED_KEYS: readonly ["CapsLock", "ContextMenu", "Convert", "KanaMode", "NonConvert", "Help", "Insert", "NumLock", "Fn", "FnLock", "PrintScreen", "ScrollLock", "Pause", "BrowserBack", "BrowserFavorites", "BrowserForward", "BrowserHome", "BrowserRefresh", "BrowserSearch", "BrowserStop", "Eject", "LaunchMail", "MediaPlayPause", "MediaStop", "MediaTrackNext", "MediaTrackPrevious", "Power", "AudioVolumeDown", "AudioVolumeMute", "AudioVolumeUp", "WakeUp", "Hyper", "Super", "Again", "Copy", "Cut", "Find", "Open", "Paste", "Props", "Select", "Undo", "Hiragana", "Katakana"];
4
3
  /** Logical counterparts whose physical positions use different names (for example Sleep and Lang1). */
5
- export declare const LOGICAL_ONLY_NAMED_KEYS: readonly ["Clear", "Cancel", "Standby", "LaunchApplication1", "LaunchApplication2", "LaunchMediaPlayer", "HangulMode", "HanjaMode", "Zenkaku", "Hankaku", "ZenkakuHankaku"];
6
- //#endregion
4
+ export declare const LOGICAL_ONLY_NAMED_KEYS: readonly ["Clear", "Cancel", "Standby", "LaunchApplication1", "LaunchApplication2", "LaunchMediaPlayer", "HangulMode", "HanjaMode", "Zenkaku", "Hankaku", "ZenkakuHankaku"];
@@ -1,4 +1,4 @@
1
- //#region src/_named-keys.ts
1
+ // src/_named-keys.ts
2
2
  /** Names defined for both KeyboardEvent.key and KeyboardEvent.code, beyond the basic key groups. */
3
3
  const SHARED_NAMED_KEYS = [
4
4
  "CapsLock",
@@ -81,5 +81,4 @@ const EDITING_KEY_NAMES = [
81
81
  "Delete"
82
82
  ];
83
83
 
84
- //#endregion
85
84
  export { EDITING_KEY_NAMES, LOGICAL_ONLY_NAMED_KEYS, NAVIGATION_KEY_NAMES, SHARED_NAMED_KEYS };
@@ -1,11 +1,12 @@
1
- import { normalizeKeyName } from "./constants.js";
2
1
  import { isCodeName, normalizeKeyboardEvent } from "./_keyboard-event.js";
2
+ import { normalizeKeyName } from "./constants.js";
3
3
  import { isModifierKey, normalizeHotkey, parseKeyboardEvent } from "./parse.js";
4
4
 
5
- //#region src/_recorder-chord.ts
5
+ // src/_recorder-chord.ts
6
6
  /**
7
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.
8
+ * Returns null for composition, modifier-only input, unusable physical input,
9
+ * or an `Unidentified` key in key mode.
9
10
  * Key mode retains the produced character; neither mode guesses layout letters.
10
11
  */
11
12
  function hotkeyChordFromKeydown(event, platform, recordBy = "code") {
@@ -20,7 +21,7 @@ function hotkeyChordFromKeydown(event, platform, recordBy = "code") {
20
21
  code: event.code
21
22
  }, platform);
22
23
  }
23
- return parsed.key ? normalizeHotkey(parsed, platform) : null;
24
+ return parsed.key && parsed.key !== "Unidentified" ? normalizeHotkey(parsed, platform) : null;
24
25
  }
25
26
  /**
26
27
  * Explains why a physical chord cannot be recorded before syntax/policy checks.
@@ -28,19 +29,12 @@ function hotkeyChordFromKeydown(event, platform, recordBy = "code") {
28
29
  */
29
30
  function chordRejection(event, options) {
30
31
  if (options.recordBy === "key") return void 0;
31
- if (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 (!hasUsableCode(event)) return {
36
- reason: "missing-code",
37
- message: "This event has no usable physical key code."
38
- };
32
+ if (normalizeKeyboardEvent(event, options.platform).altGraph) return { reason: "alt-graph" };
33
+ if (!hasUsableCode(event)) return { reason: "missing-code" };
39
34
  }
40
35
  /** Whether the event reports a real physical key code to record. */
41
36
  function hasUsableCode(event) {
42
37
  return isCodeName(event.code) && event.code !== "Unidentified";
43
38
  }
44
39
 
45
- //#endregion
46
40
  export { chordRejection, hotkeyChordFromKeydown };
@@ -1,6 +1,6 @@
1
1
  import { MODIFIER_KEYS, normalizeKeyName } from "./constants.js";
2
2
 
3
- //#region src/_recording-guard.ts
3
+ // src/_recording-guard.ts
4
4
  const sessions = /* @__PURE__ */ new Set();
5
5
  const captured = /* @__PURE__ */ new WeakSet();
6
6
  const pending = /* @__PURE__ */ new Map();
@@ -51,5 +51,4 @@ function isRecordingEvent(event) {
51
51
  return sessions.size > 0 || captured.has(event) || pending.has(identity(event));
52
52
  }
53
53
 
54
- //#endregion
55
54
  export { beginRecording, captureRecordingEvent, endRecording, isRecordingEvent };
@@ -1,4 +1,3 @@
1
- //#region src/_registration.d.ts
2
1
  /**
3
2
  * Merges option layers left to right. A key set to `undefined` keeps the
4
3
  * earlier layer's value, so an unset option keeps its default. Getters are
@@ -10,5 +9,4 @@ export declare function definedOptions<T extends object>(...layers: ReadonlyArra
10
9
  * `undefined` as "clear". An unset option takes its default, or stays
11
10
  * `undefined` to clear an earlier value when it has no default.
12
11
  */
13
- export declare function withDefaults<T extends object>(defaults: object | undefined, options: T | undefined): T;
14
- //#endregion
12
+ export declare function withDefaults<T extends object>(defaults: object | undefined, options: T | undefined): T;
@@ -1,4 +1,6 @@
1
- //#region src/_registration.ts
1
+ import { isModifierCode, modifierAlias } from "./_keyboard-event.js";
2
+
3
+ // src/_registration.ts
2
4
  /** The scope of hotkeys that do not name one. It is always active unless an exclusive scope is active. */
3
5
  const GLOBAL_SCOPE = "global";
4
6
  /**
@@ -42,9 +44,13 @@ function withDefaults(defaults, options) {
42
44
  ...definedOptions(defaults, options)
43
45
  };
44
46
  }
45
- /** Whether a parsed chord has a key. An empty one would match key-less events. */
47
+ /**
48
+ * Whether a parsed chord has a key that isn't a modifier. A chord without one
49
+ * registers nothing: an empty key would match key-less events, and a
50
+ * modifier-only chord isn't supported.
51
+ */
46
52
  function chordHasKey(chord) {
47
- return !!(chord.code || chord.key);
53
+ return chord.code !== void 0 ? !!chord.code && !isModifierCode(chord.code) : !!chord.key && !modifierAlias(chord.key);
48
54
  }
49
55
  /**
50
56
  * Resolves `ignoreInputs: 'auto'` from the hotkey (the first step of a sequence).
@@ -66,10 +72,10 @@ function getDefaultIgnoreInputs(parsedChord) {
66
72
  function handleConflict(conflictingId, keyDisplay, conflictBehavior, unregister) {
67
73
  if (conflictBehavior === "allow") return;
68
74
  if (conflictBehavior === "warn") {
69
- if (process.env.NODE_ENV !== "production") console.warn(`'${keyDisplay}' is already registered. Multiple handlers may be triggered. Use conflictBehavior: 'replace' to replace the existing handler, or conflictBehavior: 'allow' to suppress this warning.`);
75
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") console.warn(`[hotkeys] '${keyDisplay}' is already registered`);
70
76
  return;
71
77
  }
72
- 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.`);
78
+ if (conflictBehavior === "error") throw new Error(`[hotkeys] '${keyDisplay}' is already registered`);
73
79
  unregister(conflictingId);
74
80
  }
75
81
  /** Compare updates without publishing unchanged options, including fresh metadata objects. */
@@ -80,5 +86,4 @@ function optionsEqual(left, right) {
80
86
  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]));
81
87
  }
82
88
 
83
- //#endregion
84
89
  export { GLOBAL_SCOPE, chordHasKey, defaultHotkeyOptions, definedOptions, getDefaultIgnoreInputs, handleConflict, optionsEqual, withDefaults };
package/dist/_server.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/_server.ts
1
+ // src/_server.ts
2
2
  const SERVER_AGENT = /^(?:Node\.js\/|Deno\/|Bun\/|Cloudflare-Workers)/;
3
3
  /** Whether this runs on a server: no `navigator`, or a server runtime's. */
4
4
  function isServerRuntime() {
@@ -13,9 +13,8 @@ let warned = false;
13
13
  function warnServerPlatform() {
14
14
  if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && !warned && isServerRuntime()) {
15
15
  warned = true;
16
- console.warn("[TanStack Hotkeys] A hotkey label was formatted during server rendering without a platform, so it uses 'linux'. Mac and Windows users get a different label after hydration. Read the platform from the request with `detectPlatform(userAgent)`, set it as the `platform` of your hotkeys provider or scope, and format labels with your adapter's formatter, such as `useFormatForDisplay`. Or render the labels only in the browser. See the server rendering section of the Displaying Hotkeys guide.");
16
+ console.warn("[hotkeys] Server-rendered label uses 'linux'. Set the provider's platform with detectPlatform(userAgent)");
17
17
  }
18
18
  }
19
19
 
20
- //#endregion
21
20
  export { isServerRuntime, warnServerPlatform };
package/dist/_store.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Store as Store$1 } from "./vendor/@tanstack/store/dist/store.js";
2
- //#region src/_store.d.ts
3
2
  /**
4
3
  * The part of TanStack Store's `Store` that TanStack Hotkeys uses. Its stores
5
4
  * work with TanStack Store's framework adapters, such as `useSelector` from
@@ -10,5 +9,4 @@ export type Store<T> = Pick<Store$1<T>, 'state' | 'get' | 'setState' | 'subscrib
10
9
  * Creates a store. Listeners run synchronously after each change, and an
11
10
  * update to an equal value (by `Object.is`) notifies no one.
12
11
  */
13
- export declare function createStore<T>(initialState: T): Store<T>;
14
- //#endregion
12
+ export declare function createStore<T>(initialState: T): Store<T>;
package/dist/_store.js CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/_store.ts
1
+ // src/_store.ts
2
2
  const queue = /* @__PURE__ */ new Set();
3
3
  function notify(listeners) {
4
4
  for (const listener of listeners) queue.add(listener);
@@ -39,5 +39,4 @@ function createStore(initialState) {
39
39
  };
40
40
  }
41
41
 
42
- //#endregion
43
42
  export { createStore };
@@ -1,4 +1,4 @@
1
- //#region src/_target-listeners.ts
1
+ // src/_target-listeners.ts
2
2
  /** Shares keyboard listeners within each target and capture phase. */
3
3
  var TargetListeners = class {
4
4
  dispatch;
@@ -75,5 +75,4 @@ var TargetListeners = class {
75
75
  }
76
76
  };
77
77
 
78
- //#endregion
79
78
  export { TargetListeners };
package/dist/adapter.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- import { Store, createStore } from "./_store.js";
2
- import { HotkeyCallback, HotkeyChord, HotkeySequence, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
3
- import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyOptions, HotkeySequenceOptions, Target } from "./hotkey-manager.js";
4
1
  import { AppHotkey, AppHotkeysStore } from "./app-hotkeys.js";
2
+ import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyOptions, HotkeySequenceOptions, Target } from "./hotkey-manager.js";
3
+ import { HotkeyCallback, HotkeyChord, HotkeySequence, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
5
4
  import { HotkeyPlatform } from "./platform.js";
5
+ import { Store, createStore } from "./_store.js";
6
6
  import { definedOptions, withDefaults } from "./_registration.js";
7
7
  import { createAppHotkeysStore } from "@tanstack/hotkeys";
8
- //#region src/adapter.d.ts
9
8
  /**
10
9
  * A hotkey and its callback, with optional per-binding options. Chords accept
11
10
  * `requireReset`; sequences accept `timeout`.
@@ -184,5 +183,4 @@ export declare function getHotkeysProviderScopeKey(options: {
184
183
  * Returns a function that releases the activation.
185
184
  */
186
185
  export declare function activateHotkeysProviderScope(key: string): () => void;
187
- //#endregion
188
186
  export { type Store, createAppHotkeysStore, createStore, definedOptions, withDefaults };
package/dist/adapter.js CHANGED
@@ -1,9 +1,9 @@
1
- import { warnServerPlatform } from "./_server.js";
2
1
  import { chordHasKey, defaultHotkeyOptions, definedOptions, withDefaults } from "./_registration.js";
2
+ import { warnServerPlatform } from "./_server.js";
3
3
  import { createStore } from "./_store.js";
4
4
  import { createAppHotkeysStore, detectPlatform, getAppHotkeyRegistrationOptions, getHotkeyManager, isAppHotkey, isHotkeySequence, normalizeHotkey, parseHotkey, resolveAppHotkey } from "@tanstack/hotkeys";
5
5
 
6
- //#region src/adapter.ts
6
+ // src/adapter.ts
7
7
  /**
8
8
  * Reconciles adapter registrations while retaining handles for option updates.
9
9
  * Adapters for frameworks that delegate key events, such as React, pass
@@ -178,5 +178,4 @@ function activateHotkeysProviderScope(key) {
178
178
  });
179
179
  }
180
180
 
181
- //#endregion
182
181
  export { activateHotkeysProviderScope, createAppHotkeysStore, createHotkeyBindings, createStore, definedOptions, getHotkeysProviderScopeKey, getServerPlatformSnapshot, mergeHotkeysProviderDefaults, prepareHotkeyDefinitions, subscribePublishedAppHotkeys, withDefaults };
@@ -1,7 +1,6 @@
1
1
  import { Store } from "./_store.js";
2
- import { Hotkey, HotkeyChord, HotkeyMeta, HotkeySequence } from "./hotkey.types.js";
3
2
  import { HotkeyChordOptions, HotkeyRegistrationView, HotkeySequenceOptions } from "./hotkey-manager.js";
4
- //#region src/app-hotkeys.d.ts
3
+ import { Hotkey, HotkeyChord, HotkeyMeta, HotkeySequence } from "./hotkey.types.js";
5
4
  type Platform = 'mac' | 'windows' | 'linux';
6
5
  type DeclaredOptionKeys = 'capture' | 'conflictBehavior' | 'eventType' | 'ignoreInputs' | 'preventDefault' | 'stopPropagation';
7
6
  /** Declared options for an app hotkey whose default is a chord. */
@@ -136,8 +135,8 @@ export interface AppHotkeysState<T extends AppHotkeys = AppHotkeys> {
136
135
  /** Options for `AppHotkeysStore`. */
137
136
  export interface AppHotkeysStoreOptions<T extends AppHotkeys = AppHotkeys> {
138
137
  /**
139
- * The user's saved custom hotkeys. Invalid entries are ignored with a
140
- * warning. Passing a different value to `setOptions` later, such as data
138
+ * The user's saved custom hotkeys. Unknown ids and invalid entries are
139
+ * ignored. Passing a different value to `setOptions` later, such as data
141
140
  * that finished loading or another user's data, replaces the current
142
141
  * custom hotkeys. `undefined` leaves them unchanged; a change to `null`
143
142
  * clears them.
@@ -254,4 +253,4 @@ export declare function provideAppHotkeys<T extends AppHotkeys>(appHotkeys: T, o
254
253
  store: AppHotkeysStore<T>;
255
254
  dispose: () => void;
256
255
  };
257
- //#endregion
256
+ export {};
@@ -1,13 +1,13 @@
1
- import { detectPlatform } from "./platform.js";
2
- import { isHotkeySequence, simplifyHotkey } from "./parse.js";
3
- import { validateHotkey } from "./validate.js";
1
+ import { setAppHotkeysStoreFactory } from "./_app-hotkeys-factory.js";
4
2
  import { GLOBAL_SCOPE } from "./_registration.js";
5
- import { areHotkeysEqual } from "./match.js";
6
3
  import { createStore } from "./_store.js";
7
4
  import { getHotkeyManager } from "./hotkey-manager.js";
8
- import { setAppHotkeysStoreFactory } from "./_app-hotkeys-factory.js";
5
+ import { areHotkeysEqual } from "./match.js";
6
+ import { isHotkeySequence, simplifyHotkey } from "./parse.js";
7
+ import { detectPlatform } from "./platform.js";
8
+ import { validateHotkey } from "./validate.js";
9
9
 
10
- //#region src/app-hotkeys.ts
10
+ // src/app-hotkeys.ts
11
11
  /**
12
12
  * Declares every app hotkey up front, without registering any, so cheat
13
13
  * sheets and settings screens can list hotkeys whose components have not
@@ -69,22 +69,15 @@ function applyCustomHotkeys(appHotkeys, customHotkeys) {
69
69
  }
70
70
  return result;
71
71
  }
72
- /** Keeps only valid custom hotkeys for known ids, warning about the rest. */
72
+ /** Keeps only valid custom hotkeys for known ids and silently drops the rest. */
73
73
  function sanitizeCustomHotkeys(appHotkeys, saved) {
74
74
  const result = {};
75
75
  if (typeof saved !== "object" || saved === null) return result;
76
76
  for (const [id, value] of Object.entries(saved)) {
77
77
  if (value === void 0) continue;
78
- if (!Object.hasOwn(appHotkeys, id)) {
79
- if (process.env.NODE_ENV !== "production") console.warn(`Ignoring custom hotkey for unknown app hotkey '${id}'`);
80
- continue;
81
- }
78
+ if (!Object.hasOwn(appHotkeys, id)) continue;
82
79
  const hotkey = parseCustomHotkey(value);
83
- if (hotkey === void 0) {
84
- if (process.env.NODE_ENV !== "production") console.warn(`Ignoring invalid custom hotkey for app hotkey '${id}':`, value);
85
- continue;
86
- }
87
- result[id] = hotkey;
80
+ if (hotkey !== void 0) result[id] = hotkey;
88
81
  }
89
82
  return result;
90
83
  }
@@ -232,10 +225,7 @@ var AppHotkeysStore = class {
232
225
  */
233
226
  setCustomHotkey(id, hotkey) {
234
227
  const appHotkey = this.get(id);
235
- if (!appHotkey) {
236
- if (process.env.NODE_ENV !== "production") console.warn(`Cannot set a custom hotkey for unknown app hotkey '${id}'`);
237
- return [];
238
- }
228
+ if (!appHotkey) return [];
239
229
  const next = hotkey === null ? null : simplifyHotkey(hotkey);
240
230
  if (next !== null) {
241
231
  const conflicts = this.findConflicts(id, next);
@@ -346,5 +336,4 @@ function provideAppHotkeys(appHotkeys, options = {}) {
346
336
  };
347
337
  }
348
338
 
349
- //#endregion
350
339
  export { AppHotkeysStore, defineAppHotkeys, getAppHotkeyRegistrationOptions, isAppHotkey, isCustomHotkey, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey };
@@ -1,6 +1,5 @@
1
- import { RegisterableHotkey } from "./hotkey.types.js";
2
1
  import { HotkeyRegistrationView } from "./hotkey-manager.js";
3
- //#region src/conflicts.d.ts
2
+ import { RegisterableHotkey } from "./hotkey.types.js";
4
3
  /** A live registration that overlaps a candidate hotkey. */
5
4
  export interface HotkeyConflict {
6
5
  registration: HotkeyRegistrationView;
@@ -28,5 +27,4 @@ export interface HotkeyConflictOptions {
28
27
  * With source events, also checks observed logical/physical overlap. Does not
29
28
  * discover unmounted handlers, external listeners, or other keyboard layouts.
30
29
  */
31
- export declare function findHotkeyConflicts(candidate: RegisterableHotkey, options?: HotkeyConflictOptions): Array<HotkeyConflict>;
32
- //#endregion
30
+ export declare function findHotkeyConflicts(candidate: RegisterableHotkey, options?: HotkeyConflictOptions): Array<HotkeyConflict>;
package/dist/conflicts.js CHANGED
@@ -1,12 +1,12 @@
1
- import { detectPlatform } from "./platform.js";
2
- import { isHotkeySequence, parseHotkey } from "./parse.js";
3
- import { parsedChordsEqual } from "./_match.js";
4
- import { matchesKeyboardEvent } from "./match.js";
5
1
  import { getTargetDocument } from "./_event-target.js";
6
2
  import { managerRef } from "./_manager-ref.js";
3
+ import { parsedChordsEqual } from "./_match.js";
7
4
  import { toHotkeyRegistrationView } from "./hotkey-manager.js";
5
+ import { matchesKeyboardEvent } from "./match.js";
6
+ import { isHotkeySequence, parseHotkey } from "./parse.js";
7
+ import { detectPlatform } from "./platform.js";
8
8
 
9
- //#region src/conflicts.ts
9
+ // src/conflicts.ts
10
10
  /** Whether two targets share a document and one contains the other (or is its window). */
11
11
  function targetsOverlap(a, b) {
12
12
  if (a === b) return true;
@@ -42,5 +42,4 @@ function findHotkeyConflicts(candidate, options = {}) {
42
42
  return conflicts;
43
43
  }
44
44
 
45
- //#endregion
46
45
  export { findHotkeyConflicts };
@@ -1,5 +1,4 @@
1
1
  import { CanonicalModifier, EditingKey, FunctionKey, LetterKey, NavigationKey, NumberKey, PunctuationKey } from "./key.types.js";
2
- //#region src/constants.d.ts
3
2
  /**
4
3
  * Canonical order for modifiers in normalized hotkey strings.
5
4
  *
@@ -118,7 +117,7 @@ export declare const PUNCTUATION_CODE_MAP: Record<string, string>;
118
117
  * @see {@link EDITING_KEYS}
119
118
  * @see {@link PUNCTUATION_KEYS}
120
119
  */
121
- export declare const ALL_KEYS: Set<"CapsLock" | "ContextMenu" | "Convert" | "KanaMode" | "NonConvert" | "Help" | "Insert" | "NumLock" | "Fn" | "FnLock" | "PrintScreen" | "ScrollLock" | "Pause" | "BrowserBack" | "BrowserFavorites" | "BrowserForward" | "BrowserHome" | "BrowserRefresh" | "BrowserSearch" | "BrowserStop" | "Eject" | "LaunchMail" | "MediaPlayPause" | "MediaStop" | "MediaTrackNext" | "MediaTrackPrevious" | "Power" | "AudioVolumeDown" | "AudioVolumeMute" | "AudioVolumeUp" | "WakeUp" | "Hyper" | "Super" | "Again" | "Copy" | "Cut" | "Find" | "Open" | "Paste" | "Props" | "Select" | "Undo" | "Hiragana" | "Katakana" | "Clear" | "Cancel" | "Standby" | "LaunchApplication1" | "LaunchApplication2" | "LaunchMediaPlayer" | "HangulMode" | "HanjaMode" | "Zenkaku" | "Hankaku" | "ZenkakuHankaku" | LetterKey | NumberKey | FunctionKey | NavigationKey | EditingKey | PunctuationKey>;
120
+ export declare const ALL_KEYS: Set<LetterKey | NumberKey | FunctionKey | NavigationKey | EditingKey | PunctuationKey | "CapsLock" | "ContextMenu" | "Convert" | "KanaMode" | "NonConvert" | "Help" | "Insert" | "NumLock" | "Fn" | "FnLock" | "PrintScreen" | "ScrollLock" | "Pause" | "BrowserBack" | "BrowserFavorites" | "BrowserForward" | "BrowserHome" | "BrowserRefresh" | "BrowserSearch" | "BrowserStop" | "Eject" | "LaunchMail" | "MediaPlayPause" | "MediaStop" | "MediaTrackNext" | "MediaTrackPrevious" | "Power" | "AudioVolumeDown" | "AudioVolumeMute" | "AudioVolumeUp" | "WakeUp" | "Hyper" | "Super" | "Again" | "Copy" | "Cut" | "Find" | "Open" | "Paste" | "Props" | "Select" | "Undo" | "Hiragana" | "Katakana" | "Clear" | "Cancel" | "Standby" | "LaunchApplication1" | "LaunchApplication2" | "LaunchMediaPlayer" | "HangulMode" | "HanjaMode" | "Zenkaku" | "Hankaku" | "ZenkakuHankaku">;
122
121
  /** Whether a key is a single Unicode letter, such as `'a'` or `'é'`. */
123
122
  export declare function isSingleLetterKey(key: string): boolean;
124
123
  /**
@@ -136,5 +135,4 @@ export declare function isSingleLetterKey(key: string): boolean;
136
135
  * normalizeKeyName('ArrowUp') // 'ArrowUp' (already canonical)
137
136
  * ```
138
137
  */
139
- export declare function normalizeKeyName(key: string): string;
140
- //#endregion
138
+ export declare function normalizeKeyName(key: string): string;
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EDITING_KEY_NAMES, LOGICAL_ONLY_NAMED_KEYS, NAVIGATION_KEY_NAMES, SHARED_NAMED_KEYS } from "./_named-keys.js";
2
2
 
3
- //#region src/constants.ts
3
+ // src/constants.ts
4
4
  /**
5
5
  * Canonical order for modifiers in normalized hotkey strings.
6
6
  *
@@ -193,5 +193,4 @@ function normalizeKeyName(key) {
193
193
  return key === " " ? "Space" : key;
194
194
  }
195
195
 
196
- //#endregion
197
196
  export { ALL_KEYS, EDITING_KEYS, FUNCTION_KEYS, LETTER_KEYS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, NAVIGATION_KEYS, NUMBER_KEYS, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, isSingleLetterKey, normalizeKeyName };
@@ -1,5 +1,4 @@
1
1
  import { CanonicalModifier } from "./key.types.js";
2
- //#region src/display-labels.d.ts
3
2
  /**
4
3
  * Modifier key symbols for macOS display.
5
4
  *
@@ -86,5 +85,4 @@ export declare const KEY_DISPLAY_SYMBOLS: {
86
85
  readonly Delete: "⌦";
87
86
  readonly Tab: "⇥";
88
87
  readonly Space: "␣";
89
- };
90
- //#endregion
88
+ };
@@ -1,4 +1,4 @@
1
- //#region src/display-labels.ts
1
+ // src/display-labels.ts
2
2
  /**
3
3
  * Modifier key symbols for macOS display.
4
4
  *
@@ -108,5 +108,4 @@ const KEY_DISPLAY_SYMBOLS = {
108
108
  Space: "␣"
109
109
  };
110
110
 
111
- //#endregion
112
111
  export { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS };