hotkey-router 0.0.2 โ 0.1.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/LICENSE +284 -284
- package/README.md +370 -331
- package/dist/hotkey-router.cjs +105 -24
- package/dist/hotkey-router.cjs.map +2 -2
- package/dist/hotkey-router.js +105 -24
- package/dist/hotkey-router.js.map +2 -2
- package/dist/hotkey-router.min.js +2 -2
- package/package.json +52 -58
package/README.md
CHANGED
|
@@ -1,331 +1,370 @@
|
|
|
1
|
-
# hotkey-router
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/hotkey-router)
|
|
4
|
-
[](https://bundlephobia.com/package/hotkey-router)
|
|
5
|
-
[](https://github.com/iWhatty/hotkey-router-js)
|
|
6
|
-
|
|
7
|
-
**A tiny, deterministic keyboard routing engine for modern web apps.**
|
|
8
|
-
|
|
9
|
-
Hotkey Router is not a key utility.
|
|
10
|
-
It is a predictable, plugin-first routing layer for keyboard shortcuts.
|
|
11
|
-
|
|
12
|
-
* โก O(1) dispatch
|
|
13
|
-
* ๐งฉ Plugin-safe lifecycle management
|
|
14
|
-
* ๐ฏ Deterministic winner selection (priority + recency)
|
|
15
|
-
* ๐ Input-safe by default
|
|
16
|
-
* ๐งช Testable via `trigger()`
|
|
17
|
-
* ๐ฆ
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
hotkeys
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
##
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
* `
|
|
322
|
-
* `
|
|
323
|
-
* `
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
1
|
+
# hotkey-router
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/hotkey-router)
|
|
4
|
+
[](https://bundlephobia.com/package/hotkey-router)
|
|
5
|
+
[](https://github.com/iWhatty/hotkey-router-js)
|
|
6
|
+
|
|
7
|
+
**A tiny, deterministic keyboard routing engine for modern web apps.**
|
|
8
|
+
|
|
9
|
+
Hotkey Router is not a key utility.
|
|
10
|
+
It is a predictable, plugin-first routing layer for keyboard shortcuts.
|
|
11
|
+
|
|
12
|
+
* โก O(1) dispatch
|
|
13
|
+
* ๐งฉ Plugin-safe lifecycle management
|
|
14
|
+
* ๐ฏ Deterministic winner selection (priority + recency)
|
|
15
|
+
* ๐ Input-safe by default
|
|
16
|
+
* ๐งช Testable via `trigger()`
|
|
17
|
+
* ๐ฆ 4.9 kB minified
|
|
18
|
+
* ๐ 2.2 kB minified + gzipped
|
|
19
|
+
* ๐ซ Zero dependencies
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Philosophy
|
|
24
|
+
|
|
25
|
+
Hotkey Router follows three core rules:
|
|
26
|
+
|
|
27
|
+
1. **Predictable routing** โ Highest priority wins. Ties go to the most recently bound handler.
|
|
28
|
+
2. **Safe composition** โ Plugins can register and unregister without affecting others.
|
|
29
|
+
3. **Modern only** โ Built for modern browsers using `KeyboardEvent.key`.
|
|
30
|
+
|
|
31
|
+
No keycodes. No legacy IE hacks. No hidden global scope state.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install hotkey-router
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### ESM
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import hotkeys from 'hotkey-router'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### CommonJS
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const hotkeys = require('hotkey-router')
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### CDN (ESM)
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
import hotkeys from 'https://cdn.jsdelivr.net/npm/hotkey-router/dist/hotkey-router.min.js'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
# Basic Usage
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
import hotkeys from 'hotkey-router'
|
|
65
|
+
|
|
66
|
+
// Simple keydown
|
|
67
|
+
hotkeys.bind('ctrl+k', () => {
|
|
68
|
+
openCommandPalette()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// Keyup using " up" suffix
|
|
72
|
+
hotkeys.bind('ctrl+p up', () => {
|
|
73
|
+
console.log('Released CTRL+P')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// AHK-style modifiers also supported
|
|
77
|
+
hotkeys.bind('^k', () => {
|
|
78
|
+
openCommandPalette() // ctrl+k
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Bare-modifier bindings (Alt-as-mode UX)
|
|
82
|
+
hotkeys.bind('alt', enterSelectMode) // fires on Alt keydown
|
|
83
|
+
hotkeys.bind('alt up', exitSelectMode) // fires on Alt keyup
|
|
84
|
+
|
|
85
|
+
// Layout-stable matching via KeyboardEvent.code (cross-platform Alt+letter
|
|
86
|
+
// โ works on macOS where Option remaps Alt+X -> โ)
|
|
87
|
+
hotkeys.bind('alt+code:KeyX', deleteHovered, null, { preventDefault: true })
|
|
88
|
+
|
|
89
|
+
// Plugin grouping
|
|
90
|
+
hotkeys.registerPlugin('docs', {
|
|
91
|
+
'ctrl+f': openSearch,
|
|
92
|
+
'escape': closeSearch,
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Bare-modifier bindings (v0.1.0+)
|
|
97
|
+
|
|
98
|
+
Some UX patterns are driven by a bare modifier rather than a chord โ e.g. "hold Alt to enter select mode, release Alt to exit."
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
hotkeys.bind('alt', onAltDown) // fires on Alt keydown
|
|
102
|
+
hotkeys.bind('alt up', onAltUp) // fires on Alt keyup
|
|
103
|
+
hotkeys.bind('ctrl', onCtrlDown) // any single modifier supported
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Notes:
|
|
107
|
+
- Only **single** bare modifiers are supported. Multi-modifier bare bindings (`'ctrl+alt'`) throw โ add a base key for those.
|
|
108
|
+
- Default `repeat: false` applies, so a held modifier fires only once on keydown.
|
|
109
|
+
- Loose match: a bare-Alt binding fires whenever the Alt key is the one being pressed/released, regardless of which other modifier flags are also set. Add a `when` filter for exact-set semantics.
|
|
110
|
+
|
|
111
|
+
## Code-based matching (v0.1.0+)
|
|
112
|
+
|
|
113
|
+
`KeyboardEvent.key` is layout- and modifier-dependent โ Alt+X gives `โ` on macOS, `x` on Linux/Windows. For shortcuts that should be stable across platforms, bind to `KeyboardEvent.code` instead:
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
hotkeys.bind('alt+code:KeyX', deleteHovered) // matches the physical X key
|
|
117
|
+
hotkeys.bind('ctrl+code:Digit1', goToTab1) // matches digit row, not numpad
|
|
118
|
+
hotkeys.bind('!code:KeyX', deleteHovered) // AHK shorthand also works
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The `code:` value is **case-sensitive** (matches the camelCase `KeyboardEvent.code` spec values: `KeyA`, `Digit1`, `ArrowLeft`, etc.). Only one `code:` token per binding is allowed; multiple `code:` tokens throw a parse error.
|
|
122
|
+
|
|
123
|
+
Both key-based and code-based bindings can coexist; the standard priority + recency rules pick the winner.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
# Routing Model
|
|
128
|
+
|
|
129
|
+
When multiple handlers match the same hotkey:
|
|
130
|
+
|
|
131
|
+
1. Highest `priority` wins
|
|
132
|
+
2. If equal priority โ newest binding wins
|
|
133
|
+
|
|
134
|
+
This makes modal overrides simple:
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
hotkeys.bind('escape', closeModal, null, {
|
|
138
|
+
priority: 100,
|
|
139
|
+
preventDefault: true,
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
# API
|
|
146
|
+
|
|
147
|
+
## `bind(hotkey, handler, plugin?, options?)`
|
|
148
|
+
|
|
149
|
+
Register a hotkey.
|
|
150
|
+
|
|
151
|
+
Returns an `off()` function.
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
const off = hotkeys.bind('mod+k', openPalette)
|
|
155
|
+
|
|
156
|
+
// Later
|
|
157
|
+
off()
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Options
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
{
|
|
164
|
+
preventDefault?: boolean
|
|
165
|
+
stopPropagation?: boolean
|
|
166
|
+
stopImmediatePropagation?: boolean
|
|
167
|
+
repeat?: boolean // default false on keydown
|
|
168
|
+
once?: boolean
|
|
169
|
+
when?: (event) => boolean
|
|
170
|
+
allowIn?: (event) => boolean
|
|
171
|
+
priority?: number // default 0
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Examples
|
|
176
|
+
|
|
177
|
+
Ignore repeat keydown (default behavior):
|
|
178
|
+
|
|
179
|
+
```js
|
|
180
|
+
hotkeys.bind('j', nextItem)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Allow inside inputs:
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
hotkeys.bind('mod+k', openPalette, null, {
|
|
187
|
+
allowIn: () => true,
|
|
188
|
+
preventDefault: true,
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Conditional binding:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
hotkeys.bind('delete', deleteItem, null, {
|
|
196
|
+
when: () => selectionCount() > 0,
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Run once:
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
hotkeys.bind('ctrl+s', saveDraft, null, { once: true })
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## `unbind(hotkey, handler?)`
|
|
209
|
+
|
|
210
|
+
Remove bindings.
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
hotkeys.unbind('ctrl+k')
|
|
214
|
+
hotkeys.unbind('ctrl+k', openPalette)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## `registerPlugin(name, map)`
|
|
220
|
+
|
|
221
|
+
Batch register hotkeys under a plugin namespace.
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
const unregister = hotkeys.registerPlugin('files', {
|
|
225
|
+
'mod+o': openFile,
|
|
226
|
+
'delete': deleteFile,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
// Later
|
|
230
|
+
unregister()
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Plugin cleanup is isolated โ removing one plugin never affects other bindings.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## `unregisterPlugin(name)`
|
|
238
|
+
|
|
239
|
+
Remove all hotkeys associated with a plugin.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## `pause()` / `resume()`
|
|
244
|
+
|
|
245
|
+
Temporarily disable or re-enable all routing.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## `ignoreInput(boolean = true)`
|
|
250
|
+
|
|
251
|
+
By default, hotkeys do **not** fire inside:
|
|
252
|
+
|
|
253
|
+
* `<input>`
|
|
254
|
+
* `<textarea>`
|
|
255
|
+
* `<select>`
|
|
256
|
+
* `[contenteditable]`
|
|
257
|
+
* `role="textbox"`
|
|
258
|
+
|
|
259
|
+
Override per-binding with `allowIn()`.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## `init(options?)`
|
|
264
|
+
|
|
265
|
+
Manually attach listeners.
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
hotkeys.init({
|
|
269
|
+
target: window,
|
|
270
|
+
capture: false,
|
|
271
|
+
})
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Auto-initializes on `window` by default (browser environments).
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## `destroy()`
|
|
279
|
+
|
|
280
|
+
Removes all listeners and clears internal state.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## `trigger(hotkey, options?)`
|
|
285
|
+
|
|
286
|
+
Programmatically trigger a hotkey.
|
|
287
|
+
Useful for testing.
|
|
288
|
+
|
|
289
|
+
```js
|
|
290
|
+
hotkeys.trigger('ctrl+k')
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Returns `true` if a handler ran.
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
# Supported Syntax
|
|
298
|
+
|
|
299
|
+
## Standard
|
|
300
|
+
|
|
301
|
+
* `ctrl+k`
|
|
302
|
+
* `shift+a`
|
|
303
|
+
* `ctrl+k up`
|
|
304
|
+
* `mod+s` (meta on macOS, ctrl elsewhere)
|
|
305
|
+
* `ctrl++` or `ctrl+plus`
|
|
306
|
+
|
|
307
|
+
## AHK-Style Prefix Modifiers
|
|
308
|
+
|
|
309
|
+
* `^k` โ `ctrl+k`
|
|
310
|
+
* `!k` โ `alt+k`
|
|
311
|
+
* `+k` โ `shift+k`
|
|
312
|
+
* `#k` โ `meta+k`
|
|
313
|
+
* `^!k` โ `ctrl+alt+k`
|
|
314
|
+
|
|
315
|
+
Modifiers must appear before the base key.
|
|
316
|
+
|
|
317
|
+
## Aliases Supported
|
|
318
|
+
|
|
319
|
+
### Modifiers
|
|
320
|
+
|
|
321
|
+
* `ctrl`, `control`, `โ`
|
|
322
|
+
* `shift`, `โง`, `+`
|
|
323
|
+
* `alt`, `option`, `โฅ`, `!`
|
|
324
|
+
* `meta`, `cmd`, `command`, `win`, `โ`, `#`
|
|
325
|
+
* `mod` (meta on macOS, ctrl elsewhere)
|
|
326
|
+
|
|
327
|
+
### Navigation / Special Keys
|
|
328
|
+
|
|
329
|
+
* `escape`, `esc`
|
|
330
|
+
* `enter`, `return`
|
|
331
|
+
* `space`
|
|
332
|
+
* `tab`
|
|
333
|
+
* `backspace`
|
|
334
|
+
* `delete`, `del`
|
|
335
|
+
* `home`, `end`
|
|
336
|
+
* `pageup`, `pgup`
|
|
337
|
+
* `pagedown`, `pgdn`
|
|
338
|
+
* `up`, `down`, `left`, `right`
|
|
339
|
+
* `f1`โ`f19`
|
|
340
|
+
|
|
341
|
+
Keys are case-insensitive.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
# What This Is Not
|
|
346
|
+
|
|
347
|
+
* Not a keycode polyfill
|
|
348
|
+
* Not a legacy browser shim
|
|
349
|
+
* Not a global scope manager
|
|
350
|
+
* Not a VSCode-style sequence engine
|
|
351
|
+
|
|
352
|
+
This is a small, deterministic routing layer for modern applications.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
# Browser Support
|
|
357
|
+
|
|
358
|
+
Modern browsers supporting:
|
|
359
|
+
|
|
360
|
+
* `KeyboardEvent.key`
|
|
361
|
+
* `Map`
|
|
362
|
+
* `addEventListener`
|
|
363
|
+
|
|
364
|
+
Chrome, Firefox, Safari, Edge.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
# License
|
|
369
|
+
|
|
370
|
+
See LICENSE file for details.
|