@tanstack/hotkeys 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/{manager.utils.js → _event-target.cjs} +5 -41
- package/dist/_event-target.cjs.map +1 -0
- package/dist/{manager.utils.cjs → _event-target.js} +3 -47
- package/dist/_event-target.js.map +1 -0
- package/dist/_keyboard-event.cjs +83 -0
- package/dist/_keyboard-event.cjs.map +1 -0
- package/dist/_keyboard-event.js +76 -0
- package/dist/_keyboard-event.js.map +1 -0
- package/dist/_match.cjs +49 -0
- package/dist/_match.cjs.map +1 -0
- package/dist/_match.js +49 -0
- package/dist/_match.js.map +1 -0
- package/dist/_named-keys.cjs +68 -0
- package/dist/_named-keys.cjs.map +1 -0
- package/dist/_named-keys.d.cts +8 -0
- package/dist/_named-keys.d.ts +8 -0
- package/dist/_named-keys.js +66 -0
- package/dist/_named-keys.js.map +1 -0
- package/dist/_recorder-chord.cjs +44 -0
- package/dist/_recorder-chord.cjs.map +1 -0
- package/dist/_recorder-chord.js +43 -0
- package/dist/_recorder-chord.js.map +1 -0
- package/dist/_recording-guard.cjs +59 -0
- package/dist/_recording-guard.cjs.map +1 -0
- package/dist/_recording-guard.js +56 -0
- package/dist/_recording-guard.js.map +1 -0
- package/dist/_registration.cjs +54 -0
- package/dist/_registration.cjs.map +1 -0
- package/dist/_registration.js +50 -0
- package/dist/_registration.js.map +1 -0
- package/dist/conflicts.cjs +55 -0
- package/dist/conflicts.cjs.map +1 -0
- package/dist/conflicts.d.cts +39 -0
- package/dist/conflicts.d.ts +39 -0
- package/dist/conflicts.js +55 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/constants.cjs +46 -171
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +4 -129
- package/dist/constants.d.ts +4 -129
- package/dist/constants.js +48 -164
- package/dist/constants.js.map +1 -1
- package/dist/display-labels.cjs +119 -0
- package/dist/display-labels.cjs.map +1 -0
- package/dist/display-labels.d.cts +93 -0
- package/dist/display-labels.d.ts +93 -0
- package/dist/display-labels.js +113 -0
- package/dist/display-labels.js.map +1 -0
- package/dist/format.cjs +30 -36
- package/dist/format.cjs.map +1 -1
- package/dist/format.d.cts +46 -7
- package/dist/format.d.ts +46 -7
- package/dist/format.js +31 -37
- package/dist/format.js.map +1 -1
- package/dist/hint.cjs +24 -0
- package/dist/hint.cjs.map +1 -0
- package/dist/hint.d.cts +23 -0
- package/dist/hint.d.ts +23 -0
- package/dist/hint.js +24 -0
- package/dist/hint.js.map +1 -0
- package/dist/hotkey-manager.cjs +53 -22
- package/dist/hotkey-manager.cjs.map +1 -1
- package/dist/hotkey-manager.d.cts +6 -2
- package/dist/hotkey-manager.d.ts +6 -2
- package/dist/hotkey-manager.js +49 -18
- package/dist/hotkey-manager.js.map +1 -1
- package/dist/hotkey-recorder.cjs +67 -11
- package/dist/hotkey-recorder.cjs.map +1 -1
- package/dist/hotkey-recorder.d.cts +6 -3
- package/dist/hotkey-recorder.d.ts +6 -3
- package/dist/hotkey-recorder.js +66 -10
- package/dist/hotkey-recorder.js.map +1 -1
- package/dist/hotkey-sequence-recorder.cjs +85 -22
- package/dist/hotkey-sequence-recorder.cjs.map +1 -1
- package/dist/hotkey-sequence-recorder.d.cts +9 -2
- package/dist/hotkey-sequence-recorder.d.ts +9 -2
- package/dist/hotkey-sequence-recorder.js +84 -21
- package/dist/hotkey-sequence-recorder.js.map +1 -1
- package/dist/{hotkey.d.ts → hotkey.types.d.cts} +55 -120
- package/dist/{hotkey.d.cts → hotkey.types.d.ts} +55 -120
- package/dist/index.cjs +18 -10
- package/dist/index.d.cts +14 -9
- package/dist/index.d.ts +14 -9
- package/dist/index.js +10 -6
- package/dist/key-state-tracker.cjs +24 -24
- package/dist/key-state-tracker.cjs.map +1 -1
- package/dist/key-state-tracker.js +24 -24
- package/dist/key-state-tracker.js.map +1 -1
- package/dist/key.types.d.cts +82 -0
- package/dist/key.types.d.ts +82 -0
- package/dist/match.cjs +21 -30
- package/dist/match.cjs.map +1 -1
- package/dist/match.d.cts +19 -3
- package/dist/match.d.ts +19 -3
- package/dist/match.js +19 -29
- package/dist/match.js.map +1 -1
- package/dist/parse.cjs +43 -26
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.d.cts +10 -4
- package/dist/parse.d.ts +10 -4
- package/dist/parse.js +35 -19
- package/dist/parse.js.map +1 -1
- package/dist/platform.cjs +55 -0
- package/dist/platform.cjs.map +1 -0
- package/dist/platform.d.cts +44 -0
- package/dist/platform.d.ts +44 -0
- package/dist/platform.js +53 -0
- package/dist/platform.js.map +1 -0
- package/dist/recorder-options.d.cts +34 -0
- package/dist/recorder-options.d.ts +34 -0
- package/dist/sequence-manager.cjs +54 -28
- package/dist/sequence-manager.cjs.map +1 -1
- package/dist/sequence-manager.d.cts +18 -3
- package/dist/sequence-manager.d.ts +18 -3
- package/dist/sequence-manager.js +49 -23
- package/dist/sequence-manager.js.map +1 -1
- package/dist/validate.cjs +6 -10
- package/dist/validate.cjs.map +1 -1
- package/dist/validate.d.cts +13 -2
- package/dist/validate.d.ts +13 -2
- package/dist/validate.js +7 -11
- package/dist/validate.js.map +1 -1
- package/package.json +2 -2
- package/src/{manager.utils.ts → _event-target.ts} +0 -74
- package/src/_keyboard-event.ts +101 -0
- package/src/_match.ts +96 -0
- package/src/_named-keys.ts +62 -0
- package/src/_recorder-chord.ts +76 -0
- package/src/_recording-guard.ts +63 -0
- package/src/_registration.ts +79 -0
- package/src/conflicts.ts +125 -0
- package/src/constants.ts +48 -196
- package/src/display-labels.ts +123 -0
- package/src/format.ts +111 -52
- package/src/hint.ts +42 -0
- package/src/hotkey-manager.ts +95 -40
- package/src/hotkey-recorder.ts +92 -16
- package/src/hotkey-sequence-recorder.ts +110 -28
- package/src/{hotkey.ts → hotkey.types.ts} +57 -224
- package/src/index.ts +11 -5
- package/src/key-state-tracker.ts +36 -24
- package/src/key.types.ts +268 -0
- package/src/match.ts +41 -90
- package/src/parse.ts +67 -26
- package/src/platform.ts +66 -0
- package/src/recorder-options.ts +34 -0
- package/src/sequence-manager.ts +77 -42
- package/src/validate.ts +29 -21
- package/dist/manager.utils.cjs.map +0 -1
- package/dist/manager.utils.d.cts +0 -13
- package/dist/manager.utils.d.ts +0 -13
- package/dist/manager.utils.js.map +0 -1
- package/dist/recorder-chord.cjs +0 -18
- package/dist/recorder-chord.cjs.map +0 -1
- package/dist/recorder-chord.js +0 -18
- package/dist/recorder-chord.js.map +0 -1
- package/src/recorder-chord.ts +0 -25
|
@@ -1,157 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* All supported modifier key names, including aliases.
|
|
3
|
-
* - Control/Ctrl: The Control key
|
|
4
|
-
* - Shift: The Shift key
|
|
5
|
-
* - Alt/Option: The Alt key (Option on macOS)
|
|
6
|
-
* - Command/Cmd: The Command key (macOS only)
|
|
7
|
-
* - CommandOrControl/Mod: Command on macOS, Control on other platforms
|
|
8
|
-
*/
|
|
9
|
-
export type Modifier =
|
|
10
|
-
| 'Control'
|
|
11
|
-
| 'Ctrl'
|
|
12
|
-
| 'Shift'
|
|
13
|
-
| 'Alt'
|
|
14
|
-
| 'Option'
|
|
15
|
-
| 'Command'
|
|
16
|
-
| 'Cmd'
|
|
17
|
-
| 'CommandOrControl'
|
|
18
|
-
| 'Mod'
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Canonical modifier names that map to KeyboardEvent properties.
|
|
22
|
-
*/
|
|
23
|
-
export type CanonicalModifier = 'Control' | 'Shift' | 'Alt' | 'Meta'
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Letter keys A-Z (case-insensitive in matching).
|
|
27
|
-
*/
|
|
28
|
-
export type LetterKey =
|
|
29
|
-
| 'A'
|
|
30
|
-
| 'B'
|
|
31
|
-
| 'C'
|
|
32
|
-
| 'D'
|
|
33
|
-
| 'E'
|
|
34
|
-
| 'F'
|
|
35
|
-
| 'G'
|
|
36
|
-
| 'H'
|
|
37
|
-
| 'I'
|
|
38
|
-
| 'J'
|
|
39
|
-
| 'K'
|
|
40
|
-
| 'L'
|
|
41
|
-
| 'M'
|
|
42
|
-
| 'N'
|
|
43
|
-
| 'O'
|
|
44
|
-
| 'P'
|
|
45
|
-
| 'Q'
|
|
46
|
-
| 'R'
|
|
47
|
-
| 'S'
|
|
48
|
-
| 'T'
|
|
49
|
-
| 'U'
|
|
50
|
-
| 'V'
|
|
51
|
-
| 'W'
|
|
52
|
-
| 'X'
|
|
53
|
-
| 'Y'
|
|
54
|
-
| 'Z'
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Number keys 0-9.
|
|
58
|
-
*/
|
|
59
|
-
export type NumberKey =
|
|
60
|
-
| '0'
|
|
61
|
-
| '1'
|
|
62
|
-
| '2'
|
|
63
|
-
| '3'
|
|
64
|
-
| '4'
|
|
65
|
-
| '5'
|
|
66
|
-
| '6'
|
|
67
|
-
| '7'
|
|
68
|
-
| '8'
|
|
69
|
-
| '9'
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Function keys F1-F12.
|
|
73
|
-
*/
|
|
74
|
-
export type FunctionKey =
|
|
75
|
-
| 'F1'
|
|
76
|
-
| 'F2'
|
|
77
|
-
| 'F3'
|
|
78
|
-
| 'F4'
|
|
79
|
-
| 'F5'
|
|
80
|
-
| 'F6'
|
|
81
|
-
| 'F7'
|
|
82
|
-
| 'F8'
|
|
83
|
-
| 'F9'
|
|
84
|
-
| 'F10'
|
|
85
|
-
| 'F11'
|
|
86
|
-
| 'F12'
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Navigation keys for cursor movement.
|
|
90
|
-
*/
|
|
91
|
-
export type NavigationKey =
|
|
92
|
-
| 'ArrowUp'
|
|
93
|
-
| 'ArrowDown'
|
|
94
|
-
| 'ArrowLeft'
|
|
95
|
-
| 'ArrowRight'
|
|
96
|
-
| 'Home'
|
|
97
|
-
| 'End'
|
|
98
|
-
| 'PageUp'
|
|
99
|
-
| 'PageDown'
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Editing and special keys.
|
|
103
|
-
*/
|
|
104
|
-
export type EditingKey =
|
|
105
|
-
| 'Enter'
|
|
106
|
-
| 'Escape'
|
|
107
|
-
| 'Space'
|
|
108
|
-
| 'Tab'
|
|
109
|
-
| 'Backspace'
|
|
110
|
-
| 'Delete'
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Punctuation keys commonly used in keyboard shortcuts.
|
|
114
|
-
* These are the literal characters as they appear in KeyboardEvent.key
|
|
115
|
-
* (layout-dependent, typically US keyboard layout).
|
|
116
|
-
*/
|
|
117
|
-
export type PunctuationKey =
|
|
118
|
-
| '/'
|
|
119
|
-
| '['
|
|
120
|
-
| ']'
|
|
121
|
-
| '\\'
|
|
122
|
-
| '='
|
|
123
|
-
| '-'
|
|
124
|
-
| ','
|
|
125
|
-
| '.'
|
|
126
|
-
| ';'
|
|
127
|
-
| '`'
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Keys that don't change their value when Shift is pressed.
|
|
131
|
-
* These keys produce the same `KeyboardEvent.key` value whether Shift is held or not.
|
|
132
|
-
*
|
|
133
|
-
* Excludes NumberKey (Shift+1 produces '!' on US layout) and PunctuationKey
|
|
134
|
-
* (Shift+',' produces '<' on US layout).
|
|
135
|
-
*
|
|
136
|
-
* Used in hotkey type definitions to prevent layout-dependent issues when Shift
|
|
137
|
-
* is part of the modifier combination.
|
|
138
|
-
*/
|
|
139
|
-
type NonPunctuationKey =
|
|
140
|
-
| LetterKey
|
|
141
|
-
| NumberKey
|
|
142
|
-
| EditingKey
|
|
143
|
-
| NavigationKey
|
|
144
|
-
| FunctionKey
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* All supported non-modifier keys.
|
|
148
|
-
*/
|
|
149
|
-
export type Key = NonPunctuationKey | PunctuationKey
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Includes both modifier keys and regular keys.
|
|
153
|
-
*/
|
|
154
|
-
export type IndividualKey = CanonicalModifier | Key
|
|
1
|
+
import type { CanonicalModifier, Key, LogicalKey } from './key.types'
|
|
155
2
|
|
|
156
3
|
// =============================================================================
|
|
157
4
|
// Hotkey Types
|
|
@@ -160,7 +7,6 @@ export type IndividualKey = CanonicalModifier | Key
|
|
|
160
7
|
/**
|
|
161
8
|
* Single modifier + key combinations.
|
|
162
9
|
* Uses canonical modifiers (4) + Mod (1) = 5 modifiers.
|
|
163
|
-
* Shift combinations exclude PunctuationKey to avoid layout-dependent issues.
|
|
164
10
|
*
|
|
165
11
|
* The `Mod` modifier is platform-adaptive:
|
|
166
12
|
* - **macOS**: Resolves to `Meta` (Command key ⌘)
|
|
@@ -169,16 +15,15 @@ export type IndividualKey = CanonicalModifier | Key
|
|
|
169
15
|
* This enables cross-platform hotkey definitions that automatically adapt to the platform.
|
|
170
16
|
* For example, `Mod+S` becomes `Command+S` on Mac and `Ctrl+S` on Windows/Linux.
|
|
171
17
|
*/
|
|
172
|
-
type SingleModifierHotkey =
|
|
18
|
+
export type SingleModifierHotkey =
|
|
173
19
|
| `Control+${Key}`
|
|
174
20
|
| `Alt+${Key}`
|
|
175
|
-
| `Shift+${
|
|
21
|
+
| `Shift+${Key}`
|
|
176
22
|
| `Meta+${Key}`
|
|
177
23
|
| `Mod+${Key}`
|
|
178
24
|
|
|
179
25
|
/**
|
|
180
26
|
* Two modifier + key combinations.
|
|
181
|
-
* Shift combinations exclude Numbers and PunctuationKeys to avoid layout-dependent issues.
|
|
182
27
|
*
|
|
183
28
|
* **Platform-adaptive `Mod` combinations:**
|
|
184
29
|
* - `Mod+Alt` and `Mod+Shift` are included (safe on all platforms)
|
|
@@ -186,19 +31,18 @@ type SingleModifierHotkey =
|
|
|
186
31
|
* - `Mod+Control` duplicates `Control` on Windows/Linux (Mod = Control)
|
|
187
32
|
* - `Mod+Meta` duplicates `Meta` on macOS (Mod = Meta)
|
|
188
33
|
*/
|
|
189
|
-
type TwoModifierHotkey =
|
|
34
|
+
export type TwoModifierHotkey =
|
|
190
35
|
| `Control+Alt+${Key}`
|
|
191
|
-
| `Control+Shift+${
|
|
36
|
+
| `Control+Shift+${Key}`
|
|
192
37
|
| `Control+Meta+${Key}`
|
|
193
|
-
| `Alt+Shift+${
|
|
38
|
+
| `Alt+Shift+${Key}`
|
|
194
39
|
| `Alt+Meta+${Key}`
|
|
195
|
-
| `Shift+Meta+${
|
|
40
|
+
| `Shift+Meta+${Key}`
|
|
196
41
|
| `Mod+Alt+${Key}`
|
|
197
|
-
| `Mod+Shift+${
|
|
42
|
+
| `Mod+Shift+${Key}`
|
|
198
43
|
|
|
199
44
|
/**
|
|
200
45
|
* Three modifier + key combinations.
|
|
201
|
-
* Shift combinations exclude Numbers and PunctuationKeys to avoid layout-dependent issues.
|
|
202
46
|
*
|
|
203
47
|
* **Platform-adaptive `Mod` combinations:**
|
|
204
48
|
* - `Mod+Alt+Shift` is included (safe on all platforms)
|
|
@@ -206,16 +50,15 @@ type TwoModifierHotkey =
|
|
|
206
50
|
* - `Mod+Control+Shift` duplicates `Control` on Windows/Linux (Mod = Control)
|
|
207
51
|
* - `Mod+Shift+Meta` duplicates `Meta` on macOS (Mod = Meta)
|
|
208
52
|
*/
|
|
209
|
-
type ThreeModifierHotkey =
|
|
210
|
-
| `Control+Alt+Shift+${
|
|
53
|
+
export type ThreeModifierHotkey =
|
|
54
|
+
| `Control+Alt+Shift+${Key}`
|
|
211
55
|
| `Control+Alt+Meta+${Key}`
|
|
212
|
-
| `Control+Shift+Meta+${
|
|
213
|
-
| `Alt+Shift+Meta+${
|
|
214
|
-
| `Mod+Alt+Shift+${
|
|
56
|
+
| `Control+Shift+Meta+${Key}`
|
|
57
|
+
| `Alt+Shift+Meta+${Key}`
|
|
58
|
+
| `Mod+Alt+Shift+${Key}`
|
|
215
59
|
|
|
216
60
|
/**
|
|
217
61
|
* Four modifier + key combinations.
|
|
218
|
-
* Shift combinations exclude Numbers and PunctuationKeys to avoid layout-dependent issues.
|
|
219
62
|
*
|
|
220
63
|
* Only the canonical `Control+Alt+Shift+Meta` combination is included.
|
|
221
64
|
*
|
|
@@ -226,7 +69,7 @@ type ThreeModifierHotkey =
|
|
|
226
69
|
* - `Mod+Control+Alt+Shift` → duplicates `Control` on Windows/Linux
|
|
227
70
|
* - `Mod+Alt+Shift+Meta` → duplicates `Meta` on macOS
|
|
228
71
|
*/
|
|
229
|
-
type FourModifierHotkey = `Control+Alt+Shift+Meta+${
|
|
72
|
+
export type FourModifierHotkey = `Control+Alt+Shift+Meta+${Key}`
|
|
230
73
|
|
|
231
74
|
/**
|
|
232
75
|
* A type-safe hotkey string.
|
|
@@ -287,39 +130,35 @@ export type Hotkey =
|
|
|
287
130
|
| ThreeModifierHotkey
|
|
288
131
|
| FourModifierHotkey
|
|
289
132
|
|
|
290
|
-
/**
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
* This interface provides a flexible fallback when the `Hotkey` type doesn't
|
|
294
|
-
* fit your use case. You can pass a `ParsedHotkey` directly to hotkey functions
|
|
295
|
-
* instead of a hotkey string, allowing for more dynamic or complex scenarios
|
|
296
|
-
* that aren't covered by the type-safe `Hotkey` union.
|
|
297
|
-
*
|
|
298
|
-
* @example
|
|
299
|
-
* ```ts
|
|
300
|
-
* // Type-safe hotkey string
|
|
301
|
-
* useHotkey('Mod+S', handler)
|
|
302
|
-
*
|
|
303
|
-
* // Fallback: parsed hotkey for dynamic scenarios
|
|
304
|
-
* const parsed = parseHotkey(userInput)
|
|
305
|
-
* useHotkey(parsed, handler) // Works even if userInput isn't in Hotkey type
|
|
306
|
-
* ```
|
|
307
|
-
*/
|
|
308
|
-
export interface ParsedHotkey {
|
|
309
|
-
/** The non-modifier key (e.g., 'S', 'Escape', 'F1', '/', '['). Can be any string for flexibility. */
|
|
310
|
-
key: Key | (string & {})
|
|
311
|
-
/** Whether the Control key is required */
|
|
133
|
+
/** Resolved modifier flags shared by both parsed binding variants. */
|
|
134
|
+
export interface ParsedModifiers {
|
|
135
|
+
/** Whether Control is required. */
|
|
312
136
|
ctrl: boolean
|
|
313
|
-
/** Whether
|
|
137
|
+
/** Whether Shift is required. */
|
|
314
138
|
shift: boolean
|
|
315
|
-
/** Whether
|
|
139
|
+
/** Whether Alt is required. */
|
|
316
140
|
alt: boolean
|
|
317
|
-
/** Whether
|
|
141
|
+
/** Whether Meta (Command) is required. */
|
|
318
142
|
meta: boolean
|
|
319
|
-
/**
|
|
143
|
+
/** Required modifiers in canonical order. */
|
|
320
144
|
modifiers: Array<CanonicalModifier>
|
|
321
145
|
}
|
|
322
146
|
|
|
147
|
+
/**
|
|
148
|
+
* A parsed binding matches either a logical key or a physical code, never both.
|
|
149
|
+
* Check `code !== undefined` to narrow to a physical binding. Its logical
|
|
150
|
+
* character is unknown without a keyboard event and is not stored in `key`.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* parseHotkey('Alt+S') // { key: 'S', alt: true, ... }
|
|
154
|
+
* parseHotkey('Alt+[KeyS]') // { code: 'KeyS', alt: true, ... } — no key property
|
|
155
|
+
*/
|
|
156
|
+
export type ParsedHotkey = ParsedModifiers &
|
|
157
|
+
(
|
|
158
|
+
| { key: LogicalKey | (string & {}); code?: never }
|
|
159
|
+
| { code: string; key?: never }
|
|
160
|
+
)
|
|
161
|
+
|
|
323
162
|
/**
|
|
324
163
|
* A raw hotkey object for programmatic registration.
|
|
325
164
|
*
|
|
@@ -338,11 +177,13 @@ export interface ParsedHotkey {
|
|
|
338
177
|
* useHotkey({ key: 'Escape' }, handler) // Escape (no modifiers)
|
|
339
178
|
* useHotkey({ key: 'A', shift: true, meta: true }, handler) // Shift+Meta+A
|
|
340
179
|
* useHotkey({ key: 'S', mod: true, shift: true }, handler) // Mod+Shift+S
|
|
180
|
+
* useHotkey({ code: 'KeyW', mod: true }, handler) // Physical Mod+KeyW
|
|
181
|
+
* useHotkey({ code: 'NumpadAdd', mod: true }, handler) // Physical numpad plus
|
|
341
182
|
* ```
|
|
342
183
|
*/
|
|
343
|
-
|
|
184
|
+
/** Modifier flags shared by logical-key and physical-code registrations. */
|
|
185
|
+
export interface RawModifiers {
|
|
344
186
|
/** The non-modifier key (e.g., 'S', 'Escape', 'F1'). */
|
|
345
|
-
key: Key | (string & {})
|
|
346
187
|
/** Platform-adaptive modifier: Command on macOS, Control on Windows/Linux. Defaults to false. */
|
|
347
188
|
mod?: boolean
|
|
348
189
|
/** Whether the Control key is required. Defaults to false. */
|
|
@@ -355,36 +196,16 @@ export interface RawHotkey {
|
|
|
355
196
|
meta?: boolean
|
|
356
197
|
}
|
|
357
198
|
|
|
199
|
+
export type RawHotkey =
|
|
200
|
+
| ({ key: LogicalKey | (string & {}); code?: never } & RawModifiers)
|
|
201
|
+
| ({ code: string; key?: never } & RawModifiers)
|
|
202
|
+
|
|
358
203
|
/**
|
|
359
204
|
* A hotkey that can be passed to `HotkeyManager.register()` and `useHotkey()`.
|
|
360
205
|
* Either a type-safe string (`Hotkey`) or a raw object (`RawHotkey`).
|
|
361
206
|
*/
|
|
362
207
|
export type RegisterableHotkey = Hotkey | RawHotkey
|
|
363
208
|
|
|
364
|
-
/**
|
|
365
|
-
* Options for formatting hotkeys for display.
|
|
366
|
-
*/
|
|
367
|
-
export interface FormatDisplayOptions {
|
|
368
|
-
/** The target platform. Defaults to auto-detection. */
|
|
369
|
-
platform?: 'mac' | 'windows' | 'linux'
|
|
370
|
-
/** Whether to use symbols for the display. Defaults to true. */
|
|
371
|
-
useSymbols?: boolean
|
|
372
|
-
/** Override the separator between display tokens. Defaults to platform-specific formatting when null/undefined. */
|
|
373
|
-
separatorToken?: string | null
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Result of validating a hotkey string.
|
|
378
|
-
*/
|
|
379
|
-
export interface ValidationResult {
|
|
380
|
-
/** Whether the hotkey is valid (can still have warnings) */
|
|
381
|
-
valid: boolean
|
|
382
|
-
/** Warning messages about potential issues */
|
|
383
|
-
warnings: Array<string>
|
|
384
|
-
/** Error messages about invalid syntax */
|
|
385
|
-
errors: Array<string>
|
|
386
|
-
}
|
|
387
|
-
|
|
388
209
|
/**
|
|
389
210
|
* Context passed to hotkey callbacks along with the keyboard event.
|
|
390
211
|
*/
|
|
@@ -416,7 +237,7 @@ export type HotkeyCallback = (
|
|
|
416
237
|
|
|
417
238
|
/**
|
|
418
239
|
* Metadata for hotkey and sequence registrations.
|
|
419
|
-
* Includes `name` and `
|
|
240
|
+
* Includes `name`, `description`, and `group` by default. Extend via declaration merging:
|
|
420
241
|
*
|
|
421
242
|
* @example
|
|
422
243
|
* ```ts
|
|
@@ -433,4 +254,16 @@ export interface HotkeyMeta {
|
|
|
433
254
|
name?: string
|
|
434
255
|
/** Description of what this hotkey does */
|
|
435
256
|
description?: string
|
|
257
|
+
/** Optional display group. Does not affect matching or scope. */
|
|
258
|
+
group?: string
|
|
436
259
|
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Behavior when registering a hotkey/sequence that conflicts with an existing registration.
|
|
263
|
+
*
|
|
264
|
+
* - `'warn'` - Log a warning to the console but allow both registrations (default)
|
|
265
|
+
* - `'error'` - Throw an error and prevent the new registration
|
|
266
|
+
* - `'replace'` - Unregister the existing registration and register the new one
|
|
267
|
+
* - `'allow'` - Allow multiple registrations without warning
|
|
268
|
+
*/
|
|
269
|
+
export type ConflictBehavior = 'warn' | 'error' | 'replace' | 'allow'
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
export * from './constants'
|
|
2
|
+
export * from './display-labels'
|
|
3
|
+
export * from './platform'
|
|
4
|
+
export * from './key.types'
|
|
5
|
+
export * from './hotkey.types'
|
|
2
6
|
export * from './format'
|
|
3
|
-
export * from './
|
|
7
|
+
export * from './parse'
|
|
8
|
+
export * from './validate'
|
|
9
|
+
export * from './match'
|
|
10
|
+
export * from './hint'
|
|
11
|
+
export * from './conflicts'
|
|
4
12
|
export * from './hotkey-manager'
|
|
13
|
+
export * from './sequence-manager'
|
|
5
14
|
export * from './key-state-tracker'
|
|
6
|
-
export * from './
|
|
7
|
-
export * from './parse'
|
|
15
|
+
export * from './recorder-options'
|
|
8
16
|
export * from './hotkey-recorder'
|
|
9
17
|
export * from './hotkey-sequence-recorder'
|
|
10
|
-
export * from './sequence-manager'
|
|
11
|
-
export * from './validate'
|
package/src/key-state-tracker.ts
CHANGED
|
@@ -71,8 +71,7 @@ export class KeyStateTracker {
|
|
|
71
71
|
getDefaultKeyStateTrackerState(),
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
-
#
|
|
75
|
-
#heldCodesMap: Map<string, string> = new Map()
|
|
74
|
+
#heldEntries: Map<string, { key: string; code: string }> = new Map()
|
|
76
75
|
#keydownListener: ((event: KeyboardEvent) => void) | null = null
|
|
77
76
|
#keyupListener: ((event: KeyboardEvent) => void) | null = null
|
|
78
77
|
#blurListener: (() => void) | null = null
|
|
@@ -111,18 +110,23 @@ export class KeyStateTracker {
|
|
|
111
110
|
|
|
112
111
|
this.#keydownListener = (event: KeyboardEvent) => {
|
|
113
112
|
const key = normalizeKeyName(event.key)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
this.#
|
|
113
|
+
const identity = event.code || `key:${key}`
|
|
114
|
+
if (!this.#heldEntries.has(identity)) {
|
|
115
|
+
this.#heldEntries.set(identity, { key, code: event.code })
|
|
117
116
|
this.#syncState()
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
this.#keyupListener = (event: KeyboardEvent) => {
|
|
122
121
|
const key = normalizeKeyName(event.key)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
const identity = event.code || `key:${key}`
|
|
123
|
+
this.#heldEntries.delete(identity)
|
|
124
|
+
|
|
125
|
+
// Code-less keyup events can only be paired by their logical key.
|
|
126
|
+
if (!event.code) {
|
|
127
|
+
for (const [entryIdentity, entry] of this.#heldEntries) {
|
|
128
|
+
if (entry.key === key) this.#heldEntries.delete(entryIdentity)
|
|
129
|
+
}
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
// When a modifier key is released, clear any non-modifier keys still
|
|
@@ -130,10 +134,9 @@ export class KeyStateTracker {
|
|
|
130
134
|
// (e.g. Cmd+S) and swallows the keyup event for the non-modifier key,
|
|
131
135
|
// leaving it permanently stuck in the held set.
|
|
132
136
|
if (MODIFIER_KEYS.has(key)) {
|
|
133
|
-
for (const
|
|
134
|
-
if (!MODIFIER_KEYS.has(
|
|
135
|
-
this.#
|
|
136
|
-
this.#heldCodesMap.delete(heldKey)
|
|
137
|
+
for (const [entryIdentity, entry] of this.#heldEntries) {
|
|
138
|
+
if (!MODIFIER_KEYS.has(entry.key)) {
|
|
139
|
+
this.#heldEntries.delete(entryIdentity)
|
|
137
140
|
}
|
|
138
141
|
}
|
|
139
142
|
}
|
|
@@ -143,15 +146,14 @@ export class KeyStateTracker {
|
|
|
143
146
|
|
|
144
147
|
// Clear all keys when window loses focus (keys might be released while not focused)
|
|
145
148
|
this.#blurListener = () => {
|
|
146
|
-
if (this.#
|
|
147
|
-
this.#
|
|
148
|
-
this.#heldCodesMap.clear()
|
|
149
|
+
if (this.#heldEntries.size > 0) {
|
|
150
|
+
this.#heldEntries.clear()
|
|
149
151
|
this.#syncState()
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
document.addEventListener('keydown', this.#keydownListener)
|
|
154
|
-
document.addEventListener('keyup', this.#keyupListener)
|
|
155
|
+
document.addEventListener('keydown', this.#keydownListener, true)
|
|
156
|
+
document.addEventListener('keyup', this.#keyupListener, true)
|
|
155
157
|
window.addEventListener('blur', this.#blurListener)
|
|
156
158
|
}
|
|
157
159
|
|
|
@@ -159,9 +161,18 @@ export class KeyStateTracker {
|
|
|
159
161
|
* Syncs the internal Set to the Store state.
|
|
160
162
|
*/
|
|
161
163
|
#syncState(): void {
|
|
164
|
+
const heldKeys = Array.from(
|
|
165
|
+
new Set(Array.from(this.#heldEntries.values(), (entry) => entry.key)),
|
|
166
|
+
)
|
|
167
|
+
const heldCodes = Object.fromEntries(
|
|
168
|
+
Array.from(this.#heldEntries.values(), (entry) => [
|
|
169
|
+
entry.key,
|
|
170
|
+
entry.code,
|
|
171
|
+
]),
|
|
172
|
+
)
|
|
162
173
|
this.store.setState(() => ({
|
|
163
|
-
heldKeys
|
|
164
|
-
heldCodes
|
|
174
|
+
heldKeys,
|
|
175
|
+
heldCodes,
|
|
165
176
|
}))
|
|
166
177
|
}
|
|
167
178
|
|
|
@@ -174,12 +185,12 @@ export class KeyStateTracker {
|
|
|
174
185
|
}
|
|
175
186
|
|
|
176
187
|
if (this.#keydownListener) {
|
|
177
|
-
document.removeEventListener('keydown', this.#keydownListener)
|
|
188
|
+
document.removeEventListener('keydown', this.#keydownListener, true)
|
|
178
189
|
this.#keydownListener = null
|
|
179
190
|
}
|
|
180
191
|
|
|
181
192
|
if (this.#keyupListener) {
|
|
182
|
-
document.removeEventListener('keyup', this.#keyupListener)
|
|
193
|
+
document.removeEventListener('keyup', this.#keyupListener, true)
|
|
183
194
|
this.#keyupListener = null
|
|
184
195
|
}
|
|
185
196
|
|
|
@@ -206,7 +217,9 @@ export class KeyStateTracker {
|
|
|
206
217
|
*/
|
|
207
218
|
isKeyHeld(key: string): boolean {
|
|
208
219
|
const normalizedKey = normalizeKeyName(key)
|
|
209
|
-
return this.#
|
|
220
|
+
return Array.from(this.#heldEntries.values()).some(
|
|
221
|
+
(entry) => entry.key === normalizedKey,
|
|
222
|
+
)
|
|
210
223
|
}
|
|
211
224
|
|
|
212
225
|
/**
|
|
@@ -234,8 +247,7 @@ export class KeyStateTracker {
|
|
|
234
247
|
*/
|
|
235
248
|
destroy(): void {
|
|
236
249
|
this.#removeListeners()
|
|
237
|
-
this.#
|
|
238
|
-
this.#heldCodesMap.clear()
|
|
250
|
+
this.#heldEntries.clear()
|
|
239
251
|
this.store.setState(() => getDefaultKeyStateTrackerState())
|
|
240
252
|
}
|
|
241
253
|
}
|