@inklu/keys 2.0.1 → 2.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.
@@ -1,10 +1,38 @@
1
- import { FormatDisplayOptions, Hotkey as Hotkey$1, HotkeySequence } from "@tanstack/hotkeys";
1
+ //#region src/chord.d.ts
2
+ /**
3
+ * One chord: its spelling, reading it from a string or a keypress, comparing
4
+ * the two, and writing it back. Everything else in the package handles chords
5
+ * through here, so there is one grammar and one idea of which key was pressed.
6
+ */
7
+ /** Supported platforms for hotkey resolution and display formatting. */
8
+ type Platform = "mac" | "windows" | "linux";
9
+ /**
10
+ * Named keys with a place in the `Key` type, as `KeyboardEvent.key` spells
11
+ * them. Any other name still works as a key; `validateChord` only remarks on it.
12
+ */
13
+ declare const NAMED_KEYS: readonly ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "PageUp", "PageDown", "Enter", "Escape", "Space", "Tab", "Backspace", "Delete", "Insert", "CapsLock", "ContextMenu", "PrintScreen", "ScrollLock", "Pause", "NumLock", "Help", "Clear", "MediaPlayPause", "MediaStop", "MediaTrackNext", "MediaTrackPrevious", "AudioVolumeUp", "AudioVolumeDown", "AudioVolumeMute", "BrowserBack", "BrowserForward", "BrowserRefresh", "BrowserSearch", "BrowserHome"];
14
+ type Letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z";
15
+ type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
16
+ type FunctionKey = `F${Exclude<Digit, "0">}` | `F1${Digit}` | `F2${"0" | "1" | "2" | "3" | "4"}`;
17
+ type Punctuation = "/" | "[" | "]" | "\\" | "=" | "-" | "," | "." | ";" | "`" | "'" | "+" | "?" | "!" | "@" | "#" | "$" | "%" | "^" | "&" | "*" | "(" | ")" | "_" | "{" | "}" | "|" | ":" | '"' | "<" | ">" | "~";
18
+ /**
19
+ * The key of a chord: the character or named key it produces (`"K"`,
20
+ * `"Escape"`), or a physical key by its `KeyboardEvent.code` in brackets
21
+ * (`"[KeyK]"`), which stays put whatever the keyboard layout.
22
+ */
23
+ type Key = Letter | Digit | FunctionKey | Punctuation | (typeof NAMED_KEYS)[number] | `[${string}]`;
24
+ type Held<Modifier extends string> = "" | `${Modifier}+`;
25
+ type Modifiers = `${Held<"Control">}${Held<"Alt">}${Held<"Shift">}${Held<"Meta">}` | `Mod+${Held<"Alt">}${Held<"Shift">}`;
26
+ /**
27
+ * A single hotkey chord, e.g. `"Mod+K"`: modifiers in the order Control, Alt,
28
+ * Shift, Meta, then one key. `Mod` is Command on macOS and Control elsewhere.
29
+ */
30
+ type Chord = `${Modifiers}${Key}`;
31
+ //#endregion
2
32
  //#region src/types.d.ts
3
- /** A single hotkey chord, e.g. `"Mod+K"`. */
4
- type Chord = Hotkey$1;
5
33
  /** A sequence of chords pressed in order, e.g. `G` then `I`. */
6
34
  interface SequenceBinding {
7
- readonly sequence: HotkeySequence;
35
+ readonly sequence: ReadonlyArray<Chord>;
8
36
  }
9
37
  /** A hotkey chord or sequence that triggers a command. */
10
38
  type Binding = Chord | SequenceBinding;
@@ -16,9 +44,16 @@ type SerializedBinding = string | Array<string>;
16
44
  type Json = string | number | boolean | null | ReadonlyArray<Json> | {
17
45
  readonly [key: string]: Json;
18
46
  };
47
+ /** Display options for formatting hotkeys. */
48
+ interface DisplayOptions {
49
+ /** Platform to write for. Defaults to the one detected. */
50
+ platform?: Platform;
51
+ /** Symbols such as `⌘` and `↵` rather than names such as `Cmd`. Defaults to `true`. */
52
+ useSymbols?: boolean;
53
+ }
19
54
  /** Trigger source for a command. */
20
55
  type CommandSource = "keyboard" | "api" | "dom";
21
- /** Context passed to a command's `run` handler. */
56
+ /** Context passed to a command's handler. */
22
57
  interface CommandContext<Id extends string = string> {
23
58
  /** ID of the command being run. */
24
59
  readonly id: Id;
@@ -34,15 +69,16 @@ interface CommandContext<Id extends string = string> {
34
69
  * @example
35
70
  * ```ts
36
71
  * run: ({ commands }) => {
37
- * commands.setEnabled("doc.publish", false);
38
72
  * commands.run("doc.save");
39
73
  * }
40
74
  * ```
41
75
  */
42
76
  readonly commands: Commands<Id>;
43
77
  }
78
+ /** What a command does when it runs. */
79
+ type CommandHandler<Id extends string = string> = (context: CommandContext<Id>) => void | Promise<void>;
44
80
  /**
45
- * Definition of a command and its default configuration.
81
+ * A command: what it is called, which keys reach it, and where it is live.
46
82
  *
47
83
  * `Id` is the registry's id union. `createCommands` fills it in, so
48
84
  * `commands.run(...)` inside a handler is checked against the registry's own
@@ -67,10 +103,6 @@ interface CommandDefinition<Id extends string = string> {
67
103
  keys?: BindingInput;
68
104
  /** Scope required for this command to fire. Omit for global, or `"*"` to bypass exclusive scopes. */
69
105
  scope?: string;
70
- /** Guard function evaluated before running. */
71
- when?: () => boolean;
72
- /** Whether the command is enabled. Defaults to `true`. */
73
- enabled?: boolean;
74
106
  /**
75
107
  * Prevent default event behavior when run. Defaults to `true`. For a
76
108
  * sequence this also covers each step as it matches, so a first step such
@@ -78,20 +110,23 @@ interface CommandDefinition<Id extends string = string> {
78
110
  */
79
111
  preventDefault?: boolean;
80
112
  /**
81
- * Whether to ignore key events from text inputs, textareas and
82
- * contenteditable elements. When omitted, TanStack Hotkeys decides per
83
- * binding: chords holding Control or Meta (so `Mod+…`) and bare `Escape`
84
- * still fire inside inputs, while everything else — bare keys, Shift/Alt
85
- * chords, and sequences starting with one — is ignored there, so typing is
86
- * never hijacked.
113
+ * Whether to ignore key events from text inputs, textareas, selects and
114
+ * contenteditable elements. When omitted it is decided per binding: chords
115
+ * holding Control or Meta (so `Mod+…`) and bare `Escape` still fire inside
116
+ * inputs, while everything else — bare keys, Shift/Alt chords, and
117
+ * sequences starting with one — is ignored there, so typing is never
118
+ * hijacked.
87
119
  */
88
120
  ignoreInputs?: boolean;
89
- /** Require key release before firing again (prevents auto-repeat). Defaults to `false`. */
90
- requireReset?: boolean;
91
121
  /** Timeout in milliseconds between sequence steps. Defaults to `1000`. */
92
122
  sequenceTimeout?: number;
93
- /** Handler executed when the command runs. */
94
- run: (context: CommandContext<Id>) => void | Promise<void>;
123
+ /**
124
+ * What the command does, when that needs nothing a component owns. Leave
125
+ * it out and attach the behaviour where the state lives, with
126
+ * `commands.handle()` or `useCommandHandler`: a command with no handler
127
+ * cannot run and leaves its key alone.
128
+ */
129
+ run?: CommandHandler<Id>;
95
130
  }
96
131
  /** Resolved command snapshot including effective bindings, display tokens, and reachability. */
97
132
  interface Command<Id extends string = string> {
@@ -122,15 +157,11 @@ interface Command<Id extends string = string> {
122
157
  * text or `aria-description`, and the only accessible form of a sequence.
123
158
  */
124
159
  readonly spoken: string;
125
- /** Whether the command is enabled. */
126
- readonly enabled: boolean;
127
- /** Whether the command is enabled and in an active scope. */
160
+ /** Whether its keys work right now: it has a handler and its scope is active. */
128
161
  readonly reachable: boolean;
129
162
  /** Whether the command has a user keymap override. */
130
163
  readonly overridden: boolean;
131
164
  }
132
- /** Supported platforms for hotkey resolution and display formatting. */
133
- type Platform = "mac" | "windows" | "linux";
134
165
  /** Storage interface for persisting keymap overrides. */
135
166
  interface KeymapStorage {
136
167
  getItem: (key: string) => string | null;
@@ -144,83 +175,36 @@ interface KeymapPersistence {
144
175
  /** Getter returning the storage implementation lazily. */
145
176
  storage: () => KeymapStorage | null;
146
177
  }
147
- /**
148
- * How two bindings collide: `"exact"` when they are the same keys, `"prefix"`
149
- * when one is the start of the other — chord `G` against `G then I` — so the
150
- * shorter one fires on the way to the longer one.
151
- */
152
- type ConflictKind = "exact" | "prefix";
153
- /** Details of a conflict between commands whose bindings collide in overlapping scopes. */
154
- interface Conflict<Id extends string = string> {
155
- /** ID of the conflicting command. */
156
- readonly id: Id;
157
- /** The conflicting command's binding. */
158
- readonly binding: Binding;
159
- /** Whether the bindings are identical or one is a prefix of the other. */
160
- readonly kind: ConflictKind;
161
- /** Formatted display representation of the binding. */
162
- readonly display: string;
163
- /** Overlapping scope where conflict occurs, if any. */
164
- readonly scope?: string;
165
- }
166
- /** Options for activating a scope. */
167
- interface ScopeOptions {
168
- /** Shadow commands in lower scopes, including global commands. */
169
- exclusive?: boolean;
170
- /**
171
- * Also shadow `"*"` commands, which otherwise survive every barrier.
172
- * Implies `exclusive`. Meant for surfaces that must own the whole keyboard,
173
- * such as recording a new shortcut.
174
- */
175
- strict?: boolean;
176
- }
177
- //#endregion
178
- //#region src/display.d.ts
179
- /** Display options for formatting hotkeys. */
180
- type DisplayOptions = Omit<FormatDisplayOptions, "parts">;
181
- /** Formats a binding for display on the target platform (e.g. `⌘K` or `Ctrl+K`). */
182
- declare function formatBinding(binding: Binding, options?: DisplayOptions): string;
183
- /** Splits a binding into display tokens for rendering `<kbd>` elements. */
184
- declare function bindingTokens(binding: Binding, options?: DisplayOptions): Array<string>;
185
- /**
186
- * Phrases a binding for a screen reader: `"Control+K"` for a chord,
187
- * `"G then I"` for a sequence. `aria-keyshortcuts` cannot express a sequence
188
- * (a space there means "or"), so this is how a sequence gets announced — put it
189
- * in visually hidden text or `aria-description`.
190
- */
191
- declare function spokenBinding(binding: Binding, platform?: Platform): string;
192
- /** Generates an `aria-keyshortcuts` attribute string for a binding. */
193
- declare function ariaKeyShortcuts(binding: Binding, platform?: Platform): string;
194
- //#endregion
195
- //#region src/keymap.d.ts
196
178
  /** One command's stored override: an array replaces the default, `{ add }` extends it. */
197
179
  type SerializedOverride = Array<SerializedBinding> | {
198
180
  add: Array<SerializedBinding>;
199
181
  };
200
- /** Persisted user overrides, keyed by command id. */
201
- interface SerializedKeymap {
202
- /** Format version, so a release can skip a format it does not know instead of misreading it. */
182
+ /**
183
+ * Persisted user overrides, keyed by command id. `version` lets a release
184
+ * skip a format it does not know instead of misreading it.
185
+ */
186
+ type SerializedKeymap = {
203
187
  version: 1;
204
188
  overrides: Record<string, SerializedOverride>;
205
- }
189
+ };
206
190
  /** Keymap interface for querying, modifying, and persisting keybindings. */
207
191
  interface CommandKeymap<Id extends string = string> {
208
192
  /** Active bindings in effect: the default, with any user override applied. */
209
- get(id: Id | (string & {})): ReadonlyArray<Binding>;
193
+ get(id: Id): ReadonlyArray<Binding>;
210
194
  /**
211
195
  * Replaces a command's bindings. Passing `null` unbinds it without
212
196
  * restoring the default; passing exactly the default forgets the override,
213
197
  * so the command follows the default again if the app changes it.
214
198
  */
215
- set(id: Id | (string & {}), input: BindingInput | null): void;
199
+ set(id: Id, input: BindingInput | null): void;
216
200
  /**
217
201
  * Appends an additional binding. On a command the user has not replaced,
218
202
  * it is stored on top of the default rather than as a copy of it, so a
219
203
  * default the app changes later still applies alongside the added keys.
220
204
  */
221
- add(id: Id | (string & {}), input: BindingInput): void;
205
+ add(id: Id, input: BindingInput): void;
222
206
  /** Resets user overrides back to defaults for a specific command or all commands. */
223
- reset(id?: Id | (string & {})): void;
207
+ reset(id?: Id): void;
224
208
  /** Exports user overrides as JSON-serializable data. */
225
209
  export(): SerializedKeymap;
226
210
  /**
@@ -229,8 +213,68 @@ interface CommandKeymap<Id extends string = string> {
229
213
  */
230
214
  import(data: Json): void;
231
215
  }
232
- //#endregion
233
- //#region src/registry.d.ts
216
+ /**
217
+ * How two bindings collide: `"exact"` when they are the same keys, `"prefix"`
218
+ * when one is the start of the other — chord `G` against `G then I` — so the
219
+ * shorter one fires on the way to the longer one.
220
+ */
221
+ type ConflictKind = "exact" | "prefix";
222
+ /** Details of a conflict between commands whose bindings collide in overlapping scopes. */
223
+ interface Conflict<Id extends string = string> {
224
+ /** ID of the conflicting command. */
225
+ readonly id: Id;
226
+ /** The conflicting command's binding. */
227
+ readonly binding: Binding;
228
+ /** Whether the bindings are identical or one is a prefix of the other. */
229
+ readonly kind: ConflictKind;
230
+ /** Formatted display representation of the binding. */
231
+ readonly display: string;
232
+ /** Overlapping scope where conflict occurs, if any. */
233
+ readonly scope?: string;
234
+ }
235
+ /** Options for conflict detection. */
236
+ interface ConflictOptions<Id extends string = string> {
237
+ /** Command ID to ignore during conflict checking. */
238
+ ignore?: Id;
239
+ /** Scope to evaluate conflicts in. Defaults to global. */
240
+ scope?: string;
241
+ }
242
+ /** Options for activating a scope. */
243
+ interface ScopeOptions {
244
+ /** Silence the keys of commands in lower scopes, including global commands. */
245
+ exclusive?: boolean;
246
+ }
247
+ /**
248
+ * Receives each keypress while the keyboard is captured: the event, and the
249
+ * chord it expresses (`null` for a modifier on its own). It decides what the
250
+ * key means, including whether to `preventDefault()` it.
251
+ */
252
+ type KeyCapture = (event: KeyboardEvent, chord: Chord | null) => void;
253
+ /** Options passed when manually running a command. */
254
+ interface RunOptions {
255
+ /** Trigger source. Defaults to `"api"`. */
256
+ source?: CommandSource;
257
+ /** Originating DOM event. */
258
+ event?: Event;
259
+ }
260
+ /**
261
+ * Why a command cannot run: its id is not registered, nothing has attached a
262
+ * handler to it, or its scope is not open.
263
+ */
264
+ type RunBlockReason = "unknown" | "unhandled" | "out-of-scope";
265
+ /** Represents a partially completed key sequence. */
266
+ interface PendingSequence<Id extends string = string> {
267
+ /** Command ID. */
268
+ readonly id: Id;
269
+ /** Sequence binding being typed. */
270
+ readonly binding: SequenceBinding;
271
+ /** Number of steps matched so far. */
272
+ readonly matched: number;
273
+ /** Total steps in the sequence. */
274
+ readonly total: number;
275
+ /** Formatted remaining steps. */
276
+ readonly remaining: ReadonlyArray<string>;
277
+ }
234
278
  /** Configuration options for a commands registry. */
235
279
  interface CommandsOptions<Id extends string = string> {
236
280
  /**
@@ -284,90 +328,58 @@ interface CommandsOptions<Id extends string = string> {
284
328
  */
285
329
  onRun?: (context: CommandContext<Id>) => void;
286
330
  /**
287
- * Error handler called when a command, its `when()` guard, or `onRun`
288
- * throws. Defaults to `console.error`.
331
+ * Error handler called when a handler or `onRun` throws. Defaults to
332
+ * `console.error`.
289
333
  */
290
334
  onError?: (error: Error, context: CommandContext<Id>) => void;
291
335
  }
292
- /** Options passed when manually running a command. */
293
- interface RunOptions {
294
- /** Trigger source. Defaults to `"api"`. */
295
- source?: CommandSource;
296
- /** Originating DOM event. */
297
- event?: Event;
298
- }
299
- /** Options for conflict detection. */
300
- interface ConflictOptions<Id extends string = string> {
301
- /** Command ID to ignore during conflict checking. */
302
- ignore?: Id;
303
- /** Scope to evaluate conflicts in. Defaults to global. */
304
- scope?: string;
305
- }
306
- /** Reason why a command cannot run. */
307
- type RunBlockReason = "unknown" | "disabled" | "out-of-scope" | "guarded";
308
- /** Represents a partially completed key sequence. */
309
- interface PendingSequence<Id extends string = string> {
310
- /** Command ID. */
311
- readonly id: Id;
312
- /** Sequence binding being typed. */
313
- readonly binding: SequenceBinding;
314
- /** Number of steps matched so far. */
315
- readonly matched: number;
316
- /** Total steps in the sequence. */
317
- readonly total: number;
318
- /** Formatted remaining steps. */
319
- readonly remaining: ReadonlyArray<string>;
320
- }
321
- /** Handle for a dynamically registered runtime command. */
322
- interface CommandHandle {
323
- /** Runs the command. Returns `true` if executed. */
324
- run: (options?: RunOptions) => boolean;
325
- /** Disposes and unregisters the command. */
326
- dispose: () => void;
327
- }
328
- /** Public interface for the commands registry. */
336
+ /** The commands registry. */
329
337
  interface Commands<Id extends string = string> {
330
338
  /** Keymap holding default and user bindings. */
331
339
  readonly keymap: CommandKeymap<Id>;
332
340
  /** Retrieves a command's snapshot, or `undefined` if unregistered. */
333
- get(id: Id | (string & {})): Command<Id> | undefined;
334
- /** Returns all registered commands in definition order. */
341
+ get(id: Id): Command<Id> | undefined;
342
+ /** Returns all commands in definition order. */
335
343
  list(): ReadonlyArray<Command<Id>>;
336
344
  /**
337
- * The command list as the server renders it: the commands passed to
338
- * `createCommands` with their default bindings (after `options.bindings`),
339
- * formatted for the server platform — no runtime `add()`, scope, user
340
- * override or `setEnabled` applied. Referentially stable. This is the
341
- * `getServerSnapshot` the React hooks hand `useSyncExternalStore`, so a
342
- * subtree that hydrates after the client state moved on still matches the
343
- * server's HTML.
345
+ * The list as the server rendered it: default bindings in the
346
+ * platform-neutral form, no scope, user override or attached handler
347
+ * applied. Referentially stable. Hand it to a framework as the server
348
+ * snapshot (`useSyncExternalStore`'s third argument), so a subtree that
349
+ * hydrates after the client state moved on still matches the server's HTML.
344
350
  */
345
351
  serverList(): ReadonlyArray<Command<Id>>;
346
- /** Runs a command by ID. Returns `true` if executed. */
352
+ /**
353
+ * Runs a command by ID. Returns `true` if executed. A direct call is not
354
+ * a keypress, so it passes `exclusive` barriers: a palette can run what it
355
+ * lists while its own scope silences the keyboard.
356
+ */
347
357
  run(id: Id, options?: RunOptions): boolean;
348
- /** Checks whether a command can run currently. */
349
- canRun(id: Id | (string & {})): boolean;
350
- /** Returns why a command cannot run, or `null` if executable. */
351
- whyNotRun(id: Id | (string & {})): RunBlockReason | null;
352
- /** Enables or disables a command. */
353
- setEnabled(id: Id | (string & {}), enabled: boolean): void;
354
- /** Registers a command at runtime. */
355
- add(id: Id | (string & {}), definition: CommandDefinition<Id>): CommandHandle;
356
- /** Removes a command registration. */
357
- remove(id: Id | (string & {})): void;
358
+ /** Checks whether `run` would execute the command right now. */
359
+ canRun(id: Id): boolean;
360
+ /** Returns why `run` would refuse the command, or `null` if it would run. */
361
+ whyNotRun(id: Id): RunBlockReason | null;
362
+ /**
363
+ * Attaches what a command does until the returned function is called.
364
+ * The most recently attached handler runs; disposing it hands the command
365
+ * back to the one before, or to the definition's own `run`.
366
+ */
367
+ handle(id: Id, run: CommandHandler<Id>): () => void;
358
368
  /** Activates a scope until the returned disposal function is called. */
359
369
  pushScope(name: string, options?: ScopeOptions): () => void;
370
+ /**
371
+ * Takes the whole keyboard until the returned function is called: every
372
+ * keypress goes to `onKey` first and no command's keys are live, `"*"`
373
+ * ones included. This is what recording a new shortcut is built on.
374
+ */
375
+ capture(onKey: KeyCapture): () => void;
360
376
  /**
361
377
  * Finds commands in overlapping scopes whose bindings collide with the given
362
378
  * one — the same keys (`kind: "exact"`), or one a prefix of the other, like
363
379
  * chord `G` against `G then I` (`kind: "prefix"`).
364
380
  */
365
381
  conflicts(binding: BindingInput, options?: ConflictOptions<Id>): Array<Conflict<Id>>;
366
- /** Formats a command's primary binding for display. */
367
- display(id: Id | (string & {})): string;
368
- /** Splits a command's primary binding into `<kbd>` tokens. */
369
- tokens(id: Id | (string & {})): Array<string>;
370
- /** Returns currently active partially-typed sequences. */
382
+ /** Returns the partially-typed sequences. Tracked only while something is subscribed. */
371
383
  pending(): ReadonlyArray<PendingSequence<Id>>;
372
384
  /** Subscribes to sequence progress changes. */
373
385
  subscribePending(listener: () => void): () => void;
@@ -379,48 +391,8 @@ interface Commands<Id extends string = string> {
379
391
  readonly singleKeyShortcuts: boolean;
380
392
  /** Turns single-key shortcuts on or off for every command (WCAG 2.1.4). */
381
393
  setSingleKeyShortcuts(enabled: boolean): void;
382
- /** Tears down listeners and clears all command registrations. */
394
+ /** Tears down listeners and clears all commands. */
383
395
  destroy(): void;
384
396
  }
385
- /**
386
- * Creates a command registry.
387
- *
388
- * @example
389
- * ```ts
390
- * export const commands = createCommands({
391
- * "palette.open": {
392
- * title: "Open command palette",
393
- * keys: "Mod+K",
394
- * run: () => setPaletteOpen(true),
395
- * },
396
- * "nav.inbox": {
397
- * title: "Go to inbox",
398
- * keys: seq("G", "I"),
399
- * run: () => navigate("/inbox"),
400
- * },
401
- * });
402
- * ```
403
- */
404
- declare function createCommands<Id extends string>(definitions: { [K in Id]: CommandDefinition<Id>; }, options?: CommandsOptions<NoInfer<Id>>): Commands<Id>;
405
- /**
406
- * Extracts command ID union from a Commands registry type.
407
- *
408
- * @example
409
- * ```ts
410
- * export const commands = createCommands({ … });
411
- * export type AppCommandId = CommandId<typeof commands>;
412
- * ```
413
- */
414
- type CommandId<C> = C extends Commands<infer Id> ? Id : never;
415
- //#endregion
416
- //#region src/attributes.d.ts
417
- interface ObserveOptions {
418
- /** Root document or element to delegate from. Defaults to `document`. */
419
- root?: Document | Element;
420
- }
421
- /**
422
- * Wires a commands registry to DOM data attributes. Returns a cleanup function.
423
- */
424
- declare function observe<Id extends string>(registry: Commands<Id>, options?: ObserveOptions): () => void;
425
397
  //#endregion
426
- export { HotkeySequence as A, Command as C, Conflict as D, CommandSource as E, SequenceBinding as F, SerializedBinding as I, KeymapStorage as M, Platform as N, ConflictKind as O, ScopeOptions as P, Chord as S, CommandDefinition as T, bindingTokens as _, Commands as a, Binding as b, PendingSequence as c, createCommands as d, CommandKeymap as f, ariaKeyShortcuts as g, DisplayOptions as h, CommandId as i, KeymapPersistence as j, Hotkey$1 as k, RunBlockReason as l, SerializedOverride as m, observe as n, CommandsOptions as o, SerializedKeymap as p, CommandHandle as r, ConflictOptions as s, ObserveOptions as t, RunOptions as u, formatBinding as v, CommandContext as w, BindingInput as x, spokenBinding as y };
398
+ export { SequenceBinding as C, Chord as D, SerializedOverride as E, Key as O, ScopeOptions as S, SerializedKeymap as T, KeymapPersistence as _, CommandDefinition as a, RunBlockReason as b, CommandSource as c, Conflict as d, ConflictKind as f, KeyCapture as g, Json as h, CommandContext as i, Platform as k, Commands as l, DisplayOptions as m, BindingInput as n, CommandHandler as o, ConflictOptions as p, Command as r, CommandKeymap as s, Binding as t, CommandsOptions as u, KeymapStorage as v, SerializedBinding as w, RunOptions as x, PendingSequence as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inklu/keys",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "A command system for the web — typed commands, portable keybindings, scopes, sequences, conflict detection, rebinding, and platform-aware display.",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -41,12 +41,11 @@
41
41
  "publishConfig": {
42
42
  "access": "public"
43
43
  },
44
- "dependencies": {
45
- "@tanstack/hotkeys": "^0.10.0"
46
- },
47
44
  "devDependencies": {
48
45
  "@inklu/tsconfig": "1.0.0",
46
+ "@playwright/test": "^1.63.0",
49
47
  "@testing-library/react": "^16.3.3",
48
+ "@types/node": "^26.6.2",
50
49
  "@types/react": "^19.3.0",
51
50
  "@types/react-dom": "^19.3.0",
52
51
  "jsdom": "^30.1.1",
@@ -67,6 +66,7 @@
67
66
  "scripts": {
68
67
  "build": "vp pack",
69
68
  "dev": "vp pack --watch",
70
- "test": "vp test"
69
+ "test": "vp test",
70
+ "test:e2e": "playwright test"
71
71
  }
72
72
  }