@tanstack/hotkeys 0.23.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 +40 -7
  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 +3 -4
  14. package/dist/_store.d.ts +1 -3
  15. package/dist/_store.js +1 -2
  16. package/dist/_target-listeners.js +11 -3
  17. package/dist/adapter.d.ts +10 -8
  18. package/dist/adapter.js +9 -5
  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 +23 -4
  32. package/dist/hotkey-manager.js +138 -30
  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 +3 -1
package/dist/index.d.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { CanonicalModifier, EditingKey, FunctionKey, IndividualKey, Key, LetterKey, LogicalKey, Modifier, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PhysicalKey, PhysicalKeyCode, PunctuationKey } from "./key.types.js";
2
- import { ConflictBehavior, FourModifierHotkeyChord, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyMeta, HotkeySequence, HotkeySequenceCallbackContext, ParsedHotkeyChord, ParsedModifiers, RawHotkeyChord, RawModifiers, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, SingleModifierHotkeyChord, ThreeModifierHotkeyChord, TwoModifierHotkeyChord } from "./hotkey.types.js";
3
- import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyManager, HotkeyOptions, HotkeyOptionsFor, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequenceOptions, HotkeySequenceRegistration, HotkeySequenceRegistrationView, Target, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
4
- import { AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CustomHotkeys, DefinedAppHotkeys, Register, RegisteredAppHotkeys, defineAppHotkeys, getAppHotkeyRegistrationOptions, isAppHotkey, isCustomHotkey, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey } from "./app-hotkeys.js";
5
- import { HotkeyPlatform, detectPlatform, resolveModifier } from "./platform.js";
6
1
  import { 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 } from "./constants.js";
7
2
  import { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS } from "./display-labels.js";
3
+ import { HotkeyPlatform, detectPlatform, resolveModifier } from "./platform.js";
4
+ import { CanonicalModifier, EditingKey, FunctionKey, IndividualKey, Key, LetterKey, LogicalKey, Modifier, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PhysicalKey, PhysicalKeyCode, PunctuationKey } from "./key.types.js";
5
+ import { ConflictBehavior, FourModifierHotkeyChord, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyMeta, HotkeySequence, HotkeySequenceCallbackContext, ParsedHotkeyChord, ParsedModifiers, RawHotkeyChord, RawModifiers, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, SingleModifierHotkeyChord, ThreeModifierHotkeyChord, TwoModifierHotkeyChord } from "./hotkey.types.js";
8
6
  import { DisplayHotkey, DisplayHotkeyChord, FormatDisplayOptions, formatForDisplay, formatHotkey } from "./format.js";
9
7
  import { hasNonModifierKey, isHotkeySequence, isModifierKey, normalizeHotkey, parseHotkey, parseKeyboardEvent, simplifyHotkey } from "./parse.js";
10
- import { ValidatableHotkey, ValidationResult, assertValidHotkey, checkHotkey, validateHotkey } from "./validate.js";
8
+ import { HotkeyIssue, HotkeyIssueReason, ValidatableHotkey, ValidationResult, assertValidHotkey, checkHotkey, validateHotkey } from "./validate.js";
11
9
  import { CreateHotkeyChordHandlerOptions, DEFAULT_SEQUENCE_TIMEOUT, KeyboardEventMatch, MultiHotkeyChordHandler, areHotkeysEqual, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, matchesKeyboardEvent } from "./match.js";
12
10
  import { HeldModifierOptions, matchesHeldModifiers } from "./hint.js";
13
11
  import { HotkeyConflict, HotkeyConflictOptions, findHotkeyConflicts } from "./conflicts.js";
12
+ import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyManager, HotkeyOptions, HotkeyOptionsFor, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequenceOptions, HotkeySequenceRegistration, HotkeySequenceRegistrationView, Target, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
13
+ import { AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CustomHotkeys, DefinedAppHotkeys, Register, RegisteredAppHotkeys, defineAppHotkeys, getAppHotkeyRegistrationOptions, isAppHotkey, isCustomHotkey, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey } from "./app-hotkeys.js";
14
14
  import { KeyStateTracker, KeyStateTrackerState, getKeyStateTracker } from "./key-state-tracker.js";
15
- import { HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, RecorderKeyMode, RecorderOptions, RecorderRejection } from "./hotkey-recorder.js";
15
+ import { HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, RecorderKeyMode, RecorderOptions, RecorderRejection, RecorderRejectionReason } from "./hotkey-recorder.js";
16
16
  import { createAppHotkeysStore } from "./_app-hotkeys-factory.js";
17
- export { ALL_KEYS, ActivateHotkeyScopeOptions, AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CanonicalModifier, ConflictBehavior, CreateHotkeyChordHandlerOptions, CustomHotkeys, DEFAULT_SEQUENCE_TIMEOUT, DefinedAppHotkeys, DisplayHotkey, DisplayHotkeyChord, EDITING_KEYS, EditingKey, FUNCTION_KEYS, FormatDisplayOptions, FourModifierHotkeyChord, FunctionKey, HeldModifierOptions, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyChordOptions, HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyConflict, HotkeyConflictOptions, HotkeyManager, HotkeyMeta, HotkeyOptions, HotkeyOptionsFor, HotkeyPlatform, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequence, HotkeySequenceCallbackContext, HotkeySequenceOptions, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, HotkeySequenceRegistration, HotkeySequenceRegistrationView, IndividualKey, KEY_DISPLAY_SYMBOLS, Key, KeyStateTracker, KeyStateTrackerState, KeyboardEventMatch, LETTER_KEYS, LINUX_MODIFIER_LABELS, LetterKey, LogicalKey, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, Modifier, MultiHotkeyChordHandler, NAVIGATION_KEYS, NUMBER_KEYS, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, ParsedHotkeyChord, ParsedModifiers, PhysicalKey, PhysicalKeyCode, PunctuationKey, RawHotkeyChord, RawModifiers, RecorderKeyMode, RecorderOptions, RecorderRejection, Register, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, RegisteredAppHotkeys, SingleModifierHotkeyChord, Target, ThreeModifierHotkeyChord, TwoModifierHotkeyChord, ValidatableHotkey, ValidationResult, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createAppHotkeysStore, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
17
+ export { ALL_KEYS, ActivateHotkeyScopeOptions, AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CanonicalModifier, ConflictBehavior, CreateHotkeyChordHandlerOptions, CustomHotkeys, DEFAULT_SEQUENCE_TIMEOUT, DefinedAppHotkeys, DisplayHotkey, DisplayHotkeyChord, EDITING_KEYS, EditingKey, FUNCTION_KEYS, FormatDisplayOptions, FourModifierHotkeyChord, FunctionKey, HeldModifierOptions, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyChordOptions, HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyConflict, HotkeyConflictOptions, HotkeyIssue, HotkeyIssueReason, HotkeyManager, HotkeyMeta, HotkeyOptions, HotkeyOptionsFor, HotkeyPlatform, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequence, HotkeySequenceCallbackContext, HotkeySequenceOptions, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, HotkeySequenceRegistration, HotkeySequenceRegistrationView, IndividualKey, KEY_DISPLAY_SYMBOLS, Key, KeyStateTracker, KeyStateTrackerState, KeyboardEventMatch, LETTER_KEYS, LINUX_MODIFIER_LABELS, LetterKey, LogicalKey, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, Modifier, MultiHotkeyChordHandler, NAVIGATION_KEYS, NUMBER_KEYS, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, ParsedHotkeyChord, ParsedModifiers, PhysicalKey, PhysicalKeyCode, PunctuationKey, RawHotkeyChord, RawModifiers, RecorderKeyMode, RecorderOptions, RecorderRejection, RecorderRejectionReason, Register, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, RegisteredAppHotkeys, SingleModifierHotkeyChord, Target, ThreeModifierHotkeyChord, TwoModifierHotkeyChord, ValidatableHotkey, ValidationResult, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createAppHotkeysStore, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import { 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 } from "./constants.js";
2
2
  import { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS } from "./display-labels.js";
3
3
  import { detectPlatform, resolveModifier } from "./platform.js";
4
- import { hasNonModifierKey, isHotkeySequence, isModifierKey, normalizeHotkey, parseHotkey, parseKeyboardEvent, simplifyHotkey } from "./parse.js";
5
4
  import { formatForDisplay, formatHotkey } from "./format.js";
5
+ import { hasNonModifierKey, isHotkeySequence, isModifierKey, normalizeHotkey, parseHotkey, parseKeyboardEvent, simplifyHotkey } from "./parse.js";
6
6
  import { assertValidHotkey, checkHotkey, validateHotkey } from "./validate.js";
7
7
  import { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, matchesKeyboardEvent } from "./match.js";
8
8
  import { matchesHeldModifiers } from "./hint.js";
9
- import { HotkeyManager, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
10
9
  import { findHotkeyConflicts } from "./conflicts.js";
11
- import { createAppHotkeysStore } from "./_app-hotkeys-factory.js";
10
+ import { HotkeyManager, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
12
11
  import { AppHotkeysStore, defineAppHotkeys, getAppHotkeyRegistrationOptions, isAppHotkey, isCustomHotkey, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey } from "./app-hotkeys.js";
13
12
  import { KeyStateTracker, getKeyStateTracker } from "./key-state-tracker.js";
14
13
  import { HotkeyRecorder } from "./hotkey-recorder.js";
14
+ import { createAppHotkeysStore } from "./_app-hotkeys-factory.js";
15
15
 
16
16
  export { ALL_KEYS, AppHotkeysStore, DEFAULT_SEQUENCE_TIMEOUT, EDITING_KEYS, FUNCTION_KEYS, HotkeyManager, HotkeyRecorder, KEY_DISPLAY_SYMBOLS, KeyStateTracker, LETTER_KEYS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, NAVIGATION_KEYS, NUMBER_KEYS, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createAppHotkeysStore, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
@@ -1,5 +1,4 @@
1
1
  import { Store } from "./_store.js";
2
- //#region src/key-state-tracker.d.ts
3
2
  /**
4
3
  * State interface for the KeyStateTracker.
5
4
  */
@@ -100,5 +99,4 @@ export declare class KeyStateTracker {
100
99
  * Gets the singleton KeyStateTracker instance.
101
100
  * Convenience function for accessing the tracker.
102
101
  */
103
- export declare function getKeyStateTracker(): KeyStateTracker;
104
- //#endregion
102
+ export declare function getKeyStateTracker(): KeyStateTracker;
@@ -1,7 +1,7 @@
1
- import { MODIFIER_KEYS, normalizeKeyName } from "./constants.js";
2
1
  import { createStore } from "./_store.js";
2
+ import { MODIFIER_KEYS, normalizeKeyName } from "./constants.js";
3
3
 
4
- //#region src/key-state-tracker.ts
4
+ // src/key-state-tracker.ts
5
5
  /**
6
6
  * Returns the default state for KeyStateTracker.
7
7
  */
@@ -252,5 +252,4 @@ function getKeyStateTracker() {
252
252
  return KeyStateTracker.getInstance();
253
253
  }
254
254
 
255
- //#endregion
256
255
  export { KeyStateTracker, getKeyStateTracker };
@@ -1,5 +1,4 @@
1
1
  import { LOGICAL_ONLY_NAMED_KEYS, SHARED_NAMED_KEYS } from "./_named-keys.js";
2
- //#region src/key.types.d.ts
3
2
  /**
4
3
  * All supported modifier key names, including aliases.
5
4
  * - Control/Ctrl: The Control key
@@ -44,12 +43,13 @@ export type NamedKey = (typeof SHARED_NAMED_KEYS)[number] | (typeof LOGICAL_ONLY
44
43
  export type NonPunctuationKey = LetterKey | NumberKey | EditingKey | NavigationKey | FunctionKey | NamedKey;
45
44
  /**
46
45
  * Supported physical KeyboardEvent.code names, independent of keyboard layout.
47
- * Letter, digit, numpad, and sided modifier names use finite template unions.
48
- * `Unidentified` is excluded because it cannot identify a physical key.
46
+ * Letter, digit, and numpad names use finite template unions. Modifier codes
47
+ * such as `ShiftLeft` are excluded, because modifier-only hotkeys aren't
48
+ * supported. `Unidentified` is excluded because it cannot identify a physical key.
49
49
  *
50
50
  * @see https://www.w3.org/TR/uievents-code/
51
51
  */
52
- export type PhysicalKeyCode = `Key${LetterKey}` | `Digit${NumberKey}` | `Numpad${NumberKey}` | `${'Alt' | 'Control' | 'Meta' | 'Shift'}${'Left' | 'Right'}` | EditingKey | NavigationKey | FunctionKey | (typeof SHARED_NAMED_KEYS)[number] | 'Backquote' | 'Backslash' | 'BracketLeft' | 'BracketRight' | 'Comma' | 'Equal' | 'IntlBackslash' | 'IntlRo' | 'IntlYen' | 'Minus' | 'Period' | 'Quote' | 'Semicolon' | 'Slash' | `Lang${1 | 2 | 3 | 4 | 5}` | `Numpad${'Add' | 'Backspace' | 'Clear' | 'ClearEntry' | 'Comma' | 'Decimal' | 'Divide' | 'Enter' | 'Equal' | 'Hash' | 'MemoryAdd' | 'MemoryClear' | 'MemoryRecall' | 'MemoryStore' | 'MemorySubtract' | 'Multiply' | 'ParenLeft' | 'ParenRight' | 'Star' | 'Subtract'}` | 'LaunchApp1' | 'LaunchApp2' | 'MediaSelect' | 'Sleep' | 'Turbo' | 'Abort' | 'Resume' | 'Suspend';
52
+ export type PhysicalKeyCode = `Key${LetterKey}` | `Digit${NumberKey}` | `Numpad${NumberKey}` | EditingKey | NavigationKey | FunctionKey | (typeof SHARED_NAMED_KEYS)[number] | 'Backquote' | 'Backslash' | 'BracketLeft' | 'BracketRight' | 'Comma' | 'Equal' | 'IntlBackslash' | 'IntlRo' | 'IntlYen' | 'Minus' | 'Period' | 'Quote' | 'Semicolon' | 'Slash' | `Lang${1 | 2 | 3 | 4 | 5}` | `Numpad${'Add' | 'Backspace' | 'Clear' | 'ClearEntry' | 'Comma' | 'Decimal' | 'Divide' | 'Enter' | 'Equal' | 'Hash' | 'MemoryAdd' | 'MemoryClear' | 'MemoryRecall' | 'MemoryStore' | 'MemorySubtract' | 'Multiply' | 'ParenLeft' | 'ParenRight' | 'Star' | 'Subtract'}` | 'LaunchApp1' | 'LaunchApp2' | 'MediaSelect' | 'Sleep' | 'Turbo' | 'Abort' | 'Resume' | 'Suspend';
53
53
  /**
54
54
  * An explicit physical key token. All codes use brackets, including names such
55
55
  * as `[Enter]` that also exist as logical keys. Invalid code names are rejected
@@ -75,5 +75,4 @@ export interface NormalizedKeyboardEvent {
75
75
  altGraph: boolean;
76
76
  location: number;
77
77
  isComposing: boolean;
78
- }
79
- //#endregion
78
+ }
package/dist/match.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { HotkeyCallback, HotkeyChord, HotkeyChordCallbackContext, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeySequence } from "./hotkey.types.js";
2
1
  import { HotkeyPlatform } from "./platform.js";
3
- //#region src/match.d.ts
2
+ import { HotkeyCallback, HotkeyChord, HotkeyChordCallbackContext, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeySequence } from "./hotkey.types.js";
4
3
  export interface KeyboardEventMatch {
5
4
  matched: boolean;
6
5
  score: 0 | 1 | 2 | 3;
@@ -22,7 +21,8 @@ export interface KeyboardEventMatch {
22
21
  * Also handles "dead key" events where `event.key` is `'Dead'` instead of the expected
23
22
  * character. This commonly occurs on macOS with Option+letter combinations (e.g., Option+E,
24
23
  * Option+I, Option+U, Option+N) and on Windows/Linux with international keyboard layouts.
25
- * In these cases, `event.code` is used to determine the physical key.
24
+ * In these cases, `event.code` is used to determine the physical key. An
25
+ * `'Unidentified'` key also falls back to `code`, except on letter keys.
26
26
  *
27
27
  * @param event - The KeyboardEvent to check
28
28
  * @param chord - The chord string or parsed chord to match against
@@ -129,5 +129,4 @@ export declare function createSequenceMatcher(sequence: RegisterableHotkeySequen
129
129
  match: (event: KeyboardEvent) => boolean;
130
130
  reset: () => void;
131
131
  getProgress: () => number;
132
- };
133
- //#endregion
132
+ };
package/dist/match.js CHANGED
@@ -1,12 +1,12 @@
1
- import { normalizeKeyName } from "./constants.js";
2
- import { detectPlatform } from "./platform.js";
3
1
  import { normalizeKeyboardEvent } from "./_keyboard-event.js";
4
- import { isHotkeySequence, isModifierKey, parseHotkey } from "./parse.js";
5
- import { formatHotkey } from "./format.js";
6
2
  import { matchKeyboardEvent, parsedChordsEqual } from "./_match.js";
7
3
  import { chordHasKey } from "./_registration.js";
4
+ import { normalizeKeyName } from "./constants.js";
5
+ import { formatHotkey } from "./format.js";
6
+ import { isHotkeySequence, isModifierKey, parseHotkey } from "./parse.js";
7
+ import { detectPlatform } from "./platform.js";
8
8
 
9
- //#region src/match.ts
9
+ // src/match.ts
10
10
  /**
11
11
  * Checks if a KeyboardEvent matches a hotkey.
12
12
  *
@@ -19,7 +19,8 @@ import { chordHasKey } from "./_registration.js";
19
19
  * Also handles "dead key" events where `event.key` is `'Dead'` instead of the expected
20
20
  * character. This commonly occurs on macOS with Option+letter combinations (e.g., Option+E,
21
21
  * Option+I, Option+U, Option+N) and on Windows/Linux with international keyboard layouts.
22
- * In these cases, `event.code` is used to determine the physical key.
22
+ * In these cases, `event.code` is used to determine the physical key. An
23
+ * `'Unidentified'` key also falls back to `code`, except on letter keys.
23
24
  *
24
25
  * @param event - The KeyboardEvent to check
25
26
  * @param chord - The chord string or parsed chord to match against
@@ -193,5 +194,4 @@ function createSequenceMatcher(sequence, options = {}) {
193
194
  };
194
195
  }
195
196
 
196
- //#endregion
197
197
  export { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, matchesKeyboardEvent };
package/dist/parse.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { Key } from "./key.types.js";
2
1
  import { Hotkey, HotkeyChord, HotkeySequence, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
3
- //#region src/parse.d.ts
2
+ import { Key } from "./key.types.js";
4
3
  type Platform = 'mac' | 'windows' | 'linux';
5
4
  /** A chord in any form: a string, a chord object, or an already-parsed chord. */
6
5
  type AnyChord = RegisterableHotkeyChord | ParsedHotkeyChord | (string & {});
@@ -103,4 +102,4 @@ export declare function parseKeyboardEvent(event: KeyboardEvent, platform?: Plat
103
102
  * ```
104
103
  */
105
104
  export declare function hasNonModifierKey(chord: HotkeyChord | ParsedHotkeyChord | (string & {}), platform?: Platform): boolean;
106
- //#endregion
105
+ export {};
package/dist/parse.js CHANGED
@@ -1,8 +1,9 @@
1
+ import { assertLogicalKey, modifierAlias, normalizeKeyboardEvent, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
2
+ import { chordHasKey } from "./_registration.js";
1
3
  import { MODIFIER_ORDER, normalizeKeyName } from "./constants.js";
2
4
  import { detectPlatform, resolveModifier } from "./platform.js";
3
- import { assertLogicalKey, modifierAlias, normalizeKeyboardEvent, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
4
5
 
5
- //#region src/parse.ts
6
+ // src/parse.ts
6
7
  /** Builds a ParsedHotkeyChord, listing held modifiers in canonical order. */
7
8
  function toParsedChord(identity, ctrl, shift, alt, meta) {
8
9
  const held = {
@@ -31,21 +32,14 @@ function simplifyHotkey(hotkey) {
31
32
  return isHotkeySequence(hotkey) && hotkey.length === 1 ? hotkey[0] : hotkey;
32
33
  }
33
34
  function parseChordString(chord, platform) {
34
- const parts = splitChordParts(chord);
35
+ const parts = splitChordParts(chord).map((part) => part.trim());
36
+ const key = normalizeKeyName(parts.pop());
35
37
  const modifiers = /* @__PURE__ */ new Set();
36
- let key = "";
37
- for (let i = 0; i < parts.length; i++) {
38
- const part = parts[i].trim();
39
- if (i === parts.length - 1) key = normalizeKeyName(part);
40
- else {
41
- const alias = modifierAlias(part);
42
- if (alias) {
43
- const resolved = resolveModifier(alias, platform);
44
- modifiers.add(resolved);
45
- } else if (parts.length === 1) key = normalizeKeyName(part);
46
- }
38
+ for (const part of parts) {
39
+ const alias = modifierAlias(part);
40
+ if (!alias) return toParsedChord({ key: "" }, false, false, false, false);
41
+ modifiers.add(resolveModifier(alias, platform));
47
42
  }
48
- if (!key && parts.length > 0) key = normalizeKeyName(parts[parts.length - 1].trim());
49
43
  const code = physicalCodeOf(key);
50
44
  return toParsedChord(code !== void 0 ? { code } : { key }, modifiers.has("Control"), modifiers.has("Shift"), modifiers.has("Alt"), modifiers.has("Meta"));
51
45
  }
@@ -145,10 +139,7 @@ function parseKeyboardEvent(event, platform) {
145
139
  * ```
146
140
  */
147
141
  function hasNonModifierKey(chord, platform = detectPlatform()) {
148
- const parsed = typeof chord === "string" ? parseChordString(chord, platform) : chord;
149
- if (parsed.code !== void 0) return parsed.code.length > 0 && !/^(Control|Shift|Alt|Meta)(Left|Right)$/.test(parsed.code);
150
- return !isModifierKey(parsed.key) && parsed.key.length > 0;
142
+ return chordHasKey(typeof chord === "string" ? parseChordString(chord, platform) : chord);
151
143
  }
152
144
 
153
- //#endregion
154
145
  export { hasNonModifierKey, isHotkeySequence, isModifierKey, normalizeHotkey, parseHotkey, parseKeyboardEvent, simplifyHotkey };
@@ -1,5 +1,4 @@
1
1
  import { CanonicalModifier } from "./key.types.js";
2
- //#region src/platform.d.ts
3
2
  /** A platform for resolving `Mod` and formatting labels. */
4
3
  export type HotkeyPlatform = 'mac' | 'windows' | 'linux';
5
4
  /**
@@ -49,5 +48,4 @@ export declare function detectPlatform(userAgent?: string | null): HotkeyPlatfor
49
48
  * resolveModifier('Control', 'mac') // 'Control' (unchanged)
50
49
  * ```
51
50
  */
52
- export declare function resolveModifier(modifier: CanonicalModifier | 'Mod', platform?: 'mac' | 'windows' | 'linux'): CanonicalModifier;
53
- //#endregion
51
+ export declare function resolveModifier(modifier: CanonicalModifier | 'Mod', platform?: 'mac' | 'windows' | 'linux'): CanonicalModifier;
package/dist/platform.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isServerRuntime } from "./_server.js";
2
2
 
3
- //#region src/platform.ts
3
+ // src/platform.ts
4
4
  /**
5
5
  * Detects a platform from a user agent string, or from the browser's
6
6
  * `navigator` when called without one.
@@ -65,5 +65,4 @@ function resolveModifier(modifier, platform = detectPlatform()) {
65
65
  return modifier;
66
66
  }
67
67
 
68
- //#endregion
69
68
  export { detectPlatform, resolveModifier };
@@ -1,14 +1,15 @@
1
1
  import { Hotkey } from "./hotkey.types.js";
2
- //#region src/validate.d.ts
3
2
  /** A hotkey to validate: a chord string, a sequence, or untyped input such as saved data. */
4
3
  export type ValidatableHotkey = Hotkey | (string & {}) | ReadonlyArray<string>;
5
4
  /**
6
5
  * Validates a hotkey and returns any warnings or errors.
7
- * Sequences are validated step by step, and messages name the failing step.
6
+ * Each issue has a `reason` code, so apps can write their own messages.
7
+ * Issues in a sequence carry the index of their `step`.
8
8
  *
9
9
  * Checks for:
10
10
  * - Valid syntax (modifier+...+key format)
11
11
  * - Known modifiers
12
+ * - A key that isn't a modifier
12
13
  * - Known keys
13
14
  *
14
15
  * @param hotkey - The hotkey string to validate
@@ -20,10 +21,10 @@ export type ValidatableHotkey = Hotkey | (string & {}) | ReadonlyArray<string>;
20
21
  * // { valid: true, warnings: [], errors: [] }
21
22
  *
22
23
  * validateHotkey('')
23
- * // { valid: false, warnings: [], errors: ['Hotkey cannot be empty'] }
24
+ * // { valid: false, warnings: [], errors: [{ reason: 'empty' }] }
24
25
  *
25
26
  * validateHotkey(['G', 'Foo+H'])
26
- * // { valid: false, warnings: [], errors: ["Step 2: Unknown modifier: 'Foo'"] }
27
+ * // { valid: false, warnings: [], errors: [{ reason: 'unknown-modifier', part: 'Foo', step: 1 }] }
27
28
  * ```
28
29
  */
29
30
  export declare function validateHotkey(hotkey: ValidatableHotkey): ValidationResult;
@@ -37,7 +38,7 @@ export declare function validateHotkey(hotkey: ValidatableHotkey): ValidationRes
37
38
  * @example
38
39
  * ```ts
39
40
  * assertValidHotkey('Mod+S') // OK
40
- * assertValidHotkey('') // Throws Error: Invalid hotkey: Hotkey cannot be empty
41
+ * assertValidHotkey('') // Throws Error: [hotkeys] Invalid hotkey '': empty
41
42
  * ```
42
43
  */
43
44
  export declare function assertValidHotkey(hotkey: ValidatableHotkey): void;
@@ -51,24 +52,43 @@ export declare function assertValidHotkey(hotkey: ValidatableHotkey): void;
51
52
  * @example
52
53
  * ```ts
53
54
  * checkHotkey('Control+escape')
54
- * // Console: Hotkey 'Control+escape' warnings: Unknown key: 'escape'. Key names are case-sensitive, such as 'Escape' and 'F1'.
55
+ * // Console: [hotkeys] Hotkey 'Control+escape' may never fire: unknown-key 'escape'
55
56
  * // Returns: true
56
57
  *
57
58
  * checkHotkey('Ctl+K')
58
- * // Console: Hotkey 'Ctl+K' errors: Unknown modifier: 'Ctl'
59
+ * // Console: [hotkeys] Invalid hotkey 'Ctl+K': unknown-modifier 'Ctl'
59
60
  * // Returns: false
60
61
  * ```
61
62
  */
62
63
  export declare function checkHotkey(hotkey: ValidatableHotkey): boolean;
64
+ /**
65
+ * Why a hotkey is invalid or may never fire:
66
+ *
67
+ * - `'empty'`: the hotkey or sequence is empty.
68
+ * - `'empty-part'`: a part between `+` signs is empty or whitespace.
69
+ * - `'unknown-modifier'`: a modifier isn't one the library knows, such as `Ctl`.
70
+ * - `'invalid-physical-key'`: a bracketed key isn't a `KeyboardEvent.code`.
71
+ * - `'modifier-only'`: the key is a modifier, such as `Shift` or `[ShiftLeft]`.
72
+ * - `'unknown-key'` (warning): the key isn't written like a known key name.
73
+ * Key names are case-sensitive, so `escape` never matches `Escape`.
74
+ */
75
+ export type HotkeyIssueReason = 'empty' | 'empty-part' | 'unknown-modifier' | 'invalid-physical-key' | 'modifier-only' | 'unknown-key';
76
+ /** One problem found by `validateHotkey`. */
77
+ export interface HotkeyIssue {
78
+ reason: HotkeyIssueReason;
79
+ /** The part of the chord with the problem, such as `'Ctl'` in `'Ctl+K'`. */
80
+ part?: string;
81
+ /** For a sequence, the index of the step with the problem. */
82
+ step?: number;
83
+ }
63
84
  /**
64
85
  * Result of validating a hotkey string.
65
86
  */
66
87
  export interface ValidationResult {
67
88
  /** Whether the hotkey is valid (can still have warnings) */
68
89
  valid: boolean;
69
- /** Warning messages about potential issues */
70
- warnings: Array<string>;
71
- /** Error messages about invalid syntax */
72
- errors: Array<string>;
73
- }
74
- //#endregion
90
+ /** Problems that may stop the hotkey from firing, such as an unknown key */
91
+ warnings: Array<HotkeyIssue>;
92
+ /** Problems that stop the hotkey from registering */
93
+ errors: Array<HotkeyIssue>;
94
+ }
package/dist/validate.js CHANGED
@@ -1,13 +1,15 @@
1
- import { isKeyNameShape, modifierAlias, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
1
+ import { isKeyNameShape, isModifierCode, modifierAlias, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
2
2
 
3
- //#region src/validate.ts
3
+ // src/validate.ts
4
4
  /**
5
5
  * Validates a hotkey and returns any warnings or errors.
6
- * Sequences are validated step by step, and messages name the failing step.
6
+ * Each issue has a `reason` code, so apps can write their own messages.
7
+ * Issues in a sequence carry the index of their `step`.
7
8
  *
8
9
  * Checks for:
9
10
  * - Valid syntax (modifier+...+key format)
10
11
  * - Known modifiers
12
+ * - A key that isn't a modifier
11
13
  * - Known keys
12
14
  *
13
15
  * @param hotkey - The hotkey string to validate
@@ -19,55 +21,71 @@ import { isKeyNameShape, modifierAlias, physicalCodeOf, splitChordParts } from "
19
21
  * // { valid: true, warnings: [], errors: [] }
20
22
  *
21
23
  * validateHotkey('')
22
- * // { valid: false, warnings: [], errors: ['Hotkey cannot be empty'] }
24
+ * // { valid: false, warnings: [], errors: [{ reason: 'empty' }] }
23
25
  *
24
26
  * validateHotkey(['G', 'Foo+H'])
25
- * // { valid: false, warnings: [], errors: ["Step 2: Unknown modifier: 'Foo'"] }
27
+ * // { valid: false, warnings: [], errors: [{ reason: 'unknown-modifier', part: 'Foo', step: 1 }] }
26
28
  * ```
27
29
  */
28
30
  function validateHotkey(hotkey) {
29
31
  if (typeof hotkey !== "string") {
30
- if (hotkey.length === 0) return {
31
- valid: false,
32
- warnings: [],
33
- errors: ["Sequence cannot be empty"]
34
- };
32
+ if (hotkey.length === 0) return invalid("empty");
35
33
  if (hotkey.length === 1) return validateChord(hotkey[0]);
36
34
  const results = hotkey.map(validateChord);
37
- const prefixed = (key) => results.flatMap((result, i) => result[key].map((message) => `Step ${i + 1}: ${message}`));
35
+ const withStep = (key) => results.flatMap((result, step) => result[key].map((issue) => ({
36
+ ...issue,
37
+ step
38
+ })));
38
39
  return {
39
40
  valid: results.every((result) => result.valid),
40
- warnings: prefixed("warnings"),
41
- errors: prefixed("errors")
41
+ warnings: withStep("warnings"),
42
+ errors: withStep("errors")
42
43
  };
43
44
  }
44
45
  return validateChord(hotkey);
45
46
  }
46
- function validateChord(hotkey) {
47
- const warnings = [];
48
- const errors = [];
49
- if (!hotkey || hotkey.trim() === "") return {
47
+ function invalid(reason) {
48
+ return {
50
49
  valid: false,
51
50
  warnings: [],
52
- errors: ["Hotkey cannot be empty"]
51
+ errors: [{ reason }]
53
52
  };
53
+ }
54
+ function validateChord(hotkey) {
55
+ const warnings = [];
56
+ const errors = [];
57
+ if (!hotkey || hotkey.trim() === "") return invalid("empty");
54
58
  const parts = splitChordParts(hotkey).map((p) => p.trim());
55
- if (parts.length === 0 || parts.some((p) => p === "")) return {
56
- valid: false,
57
- warnings: [],
58
- errors: ["Invalid hotkey format: empty parts detected"]
59
- };
59
+ if (parts.length === 0 || parts.some((p) => p === "")) return invalid("empty-part");
60
60
  const modifierParts = parts.slice(0, -1);
61
61
  const keyPart = parts[parts.length - 1];
62
- for (const modifier of modifierParts) if (!modifierAlias(modifier)) errors.push(`Unknown modifier: '${modifier}'`);
63
- if (keyPart.length > 1 && (keyPart.startsWith("[") || keyPart.endsWith("]")) && physicalCodeOf(keyPart) === void 0) errors.push("Invalid physical key: use a bracketed KeyboardEvent.code, such as [KeyS]");
64
- if (physicalCodeOf(keyPart) === void 0 && !isKeyNameShape(keyPart)) warnings.push(`Unknown key: '${keyPart}'. Key names are case-sensitive, such as 'Escape' and 'F1'.`);
62
+ for (const modifier of modifierParts) if (!modifierAlias(modifier)) errors.push({
63
+ reason: "unknown-modifier",
64
+ part: modifier
65
+ });
66
+ const code = physicalCodeOf(keyPart);
67
+ if (keyPart.length > 1 && (keyPart.startsWith("[") || keyPart.endsWith("]")) && code === void 0) errors.push({
68
+ reason: "invalid-physical-key",
69
+ part: keyPart
70
+ });
71
+ if (code !== void 0 ? isModifierCode(code) : modifierAlias(keyPart)) errors.push({
72
+ reason: "modifier-only",
73
+ part: keyPart
74
+ });
75
+ if (code === void 0 && !isKeyNameShape(keyPart)) warnings.push({
76
+ reason: "unknown-key",
77
+ part: keyPart
78
+ });
65
79
  return {
66
80
  valid: errors.length === 0,
67
81
  warnings,
68
82
  errors
69
83
  };
70
84
  }
85
+ /** Lists issues for a console message, such as `unknown-modifier 'Ctl'`. */
86
+ function describeIssues(issues) {
87
+ return issues.map(({ reason, part, step }) => (step === void 0 ? "" : `step ${step + 1} `) + reason + (part === void 0 ? "" : ` '${part}'`)).join(", ");
88
+ }
71
89
  /**
72
90
  * Validates a hotkey and throws an error if invalid.
73
91
  * Useful for development-time validation.
@@ -78,12 +96,12 @@ function validateChord(hotkey) {
78
96
  * @example
79
97
  * ```ts
80
98
  * assertValidHotkey('Mod+S') // OK
81
- * assertValidHotkey('') // Throws Error: Invalid hotkey: Hotkey cannot be empty
99
+ * assertValidHotkey('') // Throws Error: [hotkeys] Invalid hotkey '': empty
82
100
  * ```
83
101
  */
84
102
  function assertValidHotkey(hotkey) {
85
103
  const result = validateHotkey(hotkey);
86
- if (!result.valid) throw new Error(`Invalid hotkey '${String(hotkey)}': ${result.errors.join(", ")}`);
104
+ if (!result.valid) throw new Error(`[hotkeys] Invalid hotkey '${String(hotkey)}': ${describeIssues(result.errors)}`);
87
105
  }
88
106
  /**
89
107
  * Validates a hotkey and logs errors and warnings to the console.
@@ -95,22 +113,21 @@ function assertValidHotkey(hotkey) {
95
113
  * @example
96
114
  * ```ts
97
115
  * checkHotkey('Control+escape')
98
- * // Console: Hotkey 'Control+escape' warnings: Unknown key: 'escape'. Key names are case-sensitive, such as 'Escape' and 'F1'.
116
+ * // Console: [hotkeys] Hotkey 'Control+escape' may never fire: unknown-key 'escape'
99
117
  * // Returns: true
100
118
  *
101
119
  * checkHotkey('Ctl+K')
102
- * // Console: Hotkey 'Ctl+K' errors: Unknown modifier: 'Ctl'
120
+ * // Console: [hotkeys] Invalid hotkey 'Ctl+K': unknown-modifier 'Ctl'
103
121
  * // Returns: false
104
122
  * ```
105
123
  */
106
124
  function checkHotkey(hotkey) {
107
125
  const result = validateHotkey(hotkey);
108
- if (process.env.NODE_ENV !== "production") {
109
- if (result.errors.length > 0) console.error(`Hotkey '${String(hotkey)}' errors:`, result.errors.join("; "));
110
- if (result.warnings.length > 0) console.warn(`Hotkey '${String(hotkey)}' warnings:`, result.warnings.join("; "));
126
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
127
+ if (result.errors.length > 0) console.error(`[hotkeys] Invalid hotkey '${String(hotkey)}': ${describeIssues(result.errors)}`);
128
+ if (result.warnings.length > 0) console.warn(`[hotkeys] Hotkey '${String(hotkey)}' may never fire: ${describeIssues(result.warnings)}`);
111
129
  }
112
130
  return result.valid;
113
131
  }
114
132
 
115
- //#endregion
116
133
  export { assertValidHotkey, checkHotkey, validateHotkey };
@@ -1,5 +1,4 @@
1
1
  import { Observer, Subscription } from "./types.js";
2
- //#region ../../node_modules/.pnpm/@tanstack+store@0.11.2/node_modules/@tanstack/store/dist/store.d.ts
3
2
  //#region src/store.d.ts
4
3
  type StoreAction = (...args: Array<any>) => any;
5
4
  type StoreActionMap = Record<string, StoreAction>;
@@ -19,5 +18,4 @@ declare class Store<T, TActions extends StoreActionMap = never> {
19
18
  get(): T;
20
19
  subscribe(observerOrFn: Observer<T> | ((value: T) => void)): Subscription;
21
20
  }
22
- //#endregion
23
21
  export { Store, StoreAction, StoreActionMap, StoreActionsFactory };
@@ -1,4 +1,3 @@
1
- //#region ../../node_modules/.pnpm/@tanstack+store@0.11.2/node_modules/@tanstack/store/dist/types.d.ts
2
1
  type Observer<T> = {
3
2
  next?: (value: T) => void;
4
3
  error?: (err: unknown) => void;
@@ -7,5 +6,4 @@ type Observer<T> = {
7
6
  interface Subscription {
8
7
  unsubscribe: () => void;
9
8
  }
10
- //#endregion
11
9
  export { Observer, Subscription };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/hotkeys",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Type-safe, framework-agnostic keyboard hotkey management for the browser",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -47,9 +47,9 @@
47
47
  },
48
48
  "scripts": {
49
49
  "clean": "premove ./build ./dist",
50
- "lint": "eslint ./src",
51
- "lint:fix": "eslint ./src --fix",
52
- "test:eslint": "eslint ./src",
50
+ "lint": "oxlint ./src",
51
+ "lint:fix": "oxlint ./src --fix",
52
+ "test:oxlint": "oxlint ./src",
53
53
  "test:lib": "vitest",
54
54
  "test:lib:dev": "pnpm test:lib --watch",
55
55
  "test:types": "tsc",
@@ -52,7 +52,7 @@ Augment `Register` in the adapter module you import from, such as `declare modul
52
52
 
53
53
  - **Register the app hotkey, not its value.** Pass `appHotkeys['doc.save']`. Passing `.hotkey` registers the declared default, so the user's rebinding never applies, and the list reports the shortcut as `'inactive'`.
54
54
  - **Save what `onCustomHotkeysChange` receives.** It holds only the shortcuts the user changed, so users still get new defaults. Saving every effective hotkey freezes today's defaults into their data.
55
- - **Ids are a storage contract.** Saved data is keyed by id, and unknown ids are dropped with a warning, so renaming an id discards users' customizations.
55
+ - **Ids are a storage contract.** Saved data is keyed by id, and unknown ids are silently dropped, so renaming an id discards users' customizations.
56
56
  - **`setCustomHotkey` returns conflicts and changes nothing when there are any.** Check the result before reporting success, or reject the key press earlier by calling `findConflicts` in the recorder's `validate`.
57
57
  - **Declare scopes on app hotkeys that share keys.** An app hotkey without a scope, or in `'global'`, conflicts with every other app hotkey on the same keys, so rebinding is rejected.
58
58
  - **Conflict checks treat physical and logical bindings as different.** A recorded `Mod+[KeyP]` doesn't conflict with a declared `Mod+P`, although Command+P runs both. Declaring rebindable defaults in the recorder's form, such as `Mod+[KeyP]`, makes them comparable.
@@ -59,10 +59,12 @@ When nesting scopes, stacking dialogs, or keeping some hotkeys working during an
59
59
  - **A disabled hotkey doesn't block the browser.** With `enabled: false`, `Mod+S` opens the Save Page dialog. Add `preventDefault: 'always'` to keep the action blocked while the hotkey can't fire, whether it's disabled or its scope is off. Skip it for hotkeys turned off to give the key back to the browser, such as `Mod+C`, `Mod+Z`, `Tab`, or single keys behind a user's off switch. Don't keep the hotkey enabled and return early from the callback; the hotkey would also stay `active` in app hotkey lists and show its hint.
60
60
  - **Modifiers match exactly.** `ArrowDown` never fires while Shift is held, so register `Shift+ArrowDown` instead of checking `event.shiftKey`. Shift that a symbol needs is the exception: `?` matches Shift+/.
61
61
  - **Chords repeat while held.** Set `requireReset: true` on toggles, or holding the keys flickers them.
62
- - **Key names are case-sensitive.** Write `Escape`, `ArrowUp`, `F1`, and `+` as the types spell them; `escape`, `Esc`, `f1`, and `Plus` never match. Only letters match in any case. Hotkeys from plain JavaScript or saved data skip the types, so check them with `validateHotkey`.
62
+ - **Key names are case-sensitive.** Write `Escape`, `ArrowUp`, `F1`, and `+` as the types spell them; `escape`, `Esc`, `f1`, and `Plus` never match. Only letters match in any case. Hotkeys from plain JavaScript or saved data skip the types, so check them with `validateHotkey`. Its errors and warnings are reason codes such as `{ reason: 'unknown-key', part: 'escape' }`, not text, so map them to your own messages. A modifier-only hotkey, or one with an unknown modifier such as `Ctl+K`, registers nothing.
63
63
  - **Letters follow the typed character.** For position-based controls such as WASD, use physical bindings such as `'[KeyW]'`. Keep letters for mnemonic commands such as `Mod+S`.
64
64
  - **Each open exclusive scope needs its own name.** A reusable dialog with a fixed scope name breaks when one dialog opens another: both share one scope and both handle `Escape`. Build the name per instance.
65
65
  - **A chord on a sequence's first key runs every time the sequence starts.** With `G` and `['G', 'G']`, the first `G` runs the chord. Later keys belong to the sequence. To show "waiting for the next key", read `matchedStepCount` from the adapter's registrations list.
66
+ - **A sequence that starts a longer one doesn't wait for it.** With `['G', 'G']` and `['G', 'G', 'G']`, three G presses run both, with no warning. Pick sequences where neither one starts the other.
67
+ - **Component libraries handle Escape too.** A global `Escape` hotkey can run alongside a library's dropdown or dialog closing, or keep a library's overlay open. Native modal dialogs and popovers get Escape on their own, except `popover="manual"`: hotkeys outside them skip it. Set `ignoreDefaultPrevented: true` in the adapter's shared defaults (on each hotkey in Lit) for libraries that call `preventDefault()` before the hotkey runs, such as Radix UI and shadcn/ui in React. The option can't see a `preventDefault()` that comes after the hotkey, as in Bits UI, so give open overlays an exclusive scope for those and the rest. The sensible defaults guide lists libraries per framework.
66
68
  - **Single-character shortcuts need an off switch.** For WCAG 2.1.4, `K`, `?`, and `['G', 'H']` need a user setting passed as `enabled`, or an app hotkey users can remap.
67
69
  - **react-hotkeys-hook names don't carry over.** `useHotkeys` and `HotkeysProvider` share names, not signatures: there's no dependency array, `preventDefault` is on by default, and sequences are arrays. Remove the old library, so two libraries don't handle the same keys.
68
70
  - **Install only the adapter.** A separately installed `@tanstack/hotkeys` at another version is a second copy, and scopes don't apply across copies.