@inklu/keys 1.2.3 → 1.4.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 +39 -39
- package/dist/{attributes-DbxlLdrb.d.ts → attributes-O6bXNKT9.d.ts} +6 -2
- package/dist/attributes.d.ts +1 -1
- package/dist/attributes.js +2 -2
- package/dist/{index-D4UoKAQU.d.ts → index-B5iiRwCy.d.ts} +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{src-DyFUgmho.js → src-DWmUPZSL.js} +35 -15
- package/package.json +45 -44
package/README.md
CHANGED
|
@@ -15,16 +15,16 @@ A command is an addressable action. It has an id, title, and a handler.
|
|
|
15
15
|
import { createCommands, seq } from "@inklu/keys";
|
|
16
16
|
|
|
17
17
|
export const commands = createCommands({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
"palette.open": {
|
|
19
|
+
title: "Open command palette",
|
|
20
|
+
keys: "Mod+K",
|
|
21
|
+
run: () => setPaletteOpen(true),
|
|
22
|
+
},
|
|
23
|
+
"nav.inbox": {
|
|
24
|
+
title: "Go to inbox",
|
|
25
|
+
keys: seq("G", "I"), // press G, then I
|
|
26
|
+
run: () => navigate("/inbox"),
|
|
27
|
+
},
|
|
28
28
|
});
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -37,24 +37,26 @@ import { useCommands, useMountCommands, useShortcut } from "@inklu/keys/react";
|
|
|
37
37
|
import { commands } from "./commands";
|
|
38
38
|
|
|
39
39
|
function App() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
// Starts listening in an effect
|
|
41
|
+
useMountCommands(commands);
|
|
42
|
+
return <ShortcutButton />;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function ShortcutButton() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
// Get live shortcut state
|
|
47
|
+
const shortcut = useShortcut(commands, "palette.open");
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<button
|
|
51
|
+
onClick={() => commands.run("palette.open")}
|
|
52
|
+
aria-keyshortcuts={shortcut.aria || undefined}
|
|
53
|
+
>
|
|
54
|
+
Open Palette
|
|
55
|
+
{shortcut.tokens[0]?.map((token, i) => (
|
|
56
|
+
<kbd key={i}>{token}</kbd>
|
|
57
|
+
))}
|
|
58
|
+
</button>
|
|
59
|
+
);
|
|
58
60
|
}
|
|
59
61
|
```
|
|
60
62
|
|
|
@@ -66,10 +68,10 @@ Use scopes to isolate commands when modals or dialogs are open.
|
|
|
66
68
|
import { useScope } from "@inklu/keys/react";
|
|
67
69
|
|
|
68
70
|
function Dialog({ isOpen }) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
// When exclusive is true, commands outside this scope are silenced
|
|
72
|
+
useScope(commands, "dialog", isOpen, { exclusive: true });
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
return isOpen ? <div className="dialog">...</div> : null;
|
|
73
75
|
}
|
|
74
76
|
```
|
|
75
77
|
|
|
@@ -82,27 +84,27 @@ Exactly one command runs per keypress. The winner is:
|
|
|
82
84
|
1. The command in a named scope, over a global one. `"*"` counts as global here — it means "survives barriers", not "belongs to a region of the UI".
|
|
83
85
|
2. Failing that, the one registered later. Registration order tracks mount order, so the most recently opened surface claims the key.
|
|
84
86
|
|
|
85
|
-
A winner whose `when()` currently returns `false` steps aside rather than swallowing the keypress. This is how `Escape` closes the dialog when one is open and the panel otherwise, without making the dialog `exclusive`. Two commands sharing a chord
|
|
87
|
+
A winner whose `when()` currently returns `false` steps aside rather than swallowing the keypress. This is how `Escape` closes the dialog when one is open and the panel otherwise, without making the dialog `exclusive`. Two commands sharing a chord _in the same scope_ can never separate, so that case warns in development.
|
|
86
88
|
|
|
87
89
|
## 4. Rebinding
|
|
88
90
|
|
|
89
91
|
Overrides are layered over defaults.
|
|
90
92
|
|
|
91
93
|
```ts
|
|
92
|
-
commands.keymap.set("palette.open", "Mod+P");
|
|
94
|
+
commands.keymap.set("palette.open", "Mod+P"); // remap
|
|
93
95
|
commands.keymap.add("palette.open", "Control+P"); // an additional key
|
|
94
|
-
commands.keymap.set("palette.open", null);
|
|
95
|
-
commands.keymap.reset("palette.open");
|
|
96
|
+
commands.keymap.set("palette.open", null); // unbind
|
|
97
|
+
commands.keymap.reset("palette.open"); // reset to default
|
|
96
98
|
```
|
|
97
99
|
|
|
98
100
|
To rebind a command you did not define — one a package shipped — pass `bindings` when creating the registry. An unknown id is a compile error, and `reset` returns here rather than to the definition's `keys`:
|
|
99
101
|
|
|
100
102
|
```ts
|
|
101
103
|
createCommands(commands, {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
bindings: {
|
|
105
|
+
"palette.open": "Mod+P", // replace the shipped default
|
|
106
|
+
"doc.publish": null, // ship it unbound
|
|
107
|
+
},
|
|
106
108
|
});
|
|
107
109
|
```
|
|
108
110
|
|
|
@@ -113,9 +115,7 @@ In React, use the `useRebind` hook for a capture flow:
|
|
|
113
115
|
```tsx
|
|
114
116
|
const rebind = useRebind(commands, "palette.open");
|
|
115
117
|
|
|
116
|
-
<button onClick={rebind.start}>
|
|
117
|
-
{rebind.recording ? "Press keys…" : rebind.display}
|
|
118
|
-
</button>;
|
|
118
|
+
<button onClick={rebind.start}>{rebind.recording ? "Press keys…" : rebind.display}</button>;
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
## API Summary
|
|
@@ -12,6 +12,10 @@ type Binding = Chord | SequenceBinding;
|
|
|
12
12
|
type BindingInput = Binding | ReadonlyArray<Binding>;
|
|
13
13
|
/** JSON-serializable form of a binding. */
|
|
14
14
|
type SerializedBinding = string | Array<string>;
|
|
15
|
+
/** An arbitrary JSON-compatible value, e.g. from `JSON.parse` or persisted storage. */
|
|
16
|
+
type Json = string | number | boolean | null | ReadonlyArray<Json> | {
|
|
17
|
+
readonly [key: string]: Json;
|
|
18
|
+
};
|
|
15
19
|
/** Trigger source for a command. */
|
|
16
20
|
type CommandSource = "keyboard" | "api" | "dom";
|
|
17
21
|
/** Context passed to a command's `run` handler. */
|
|
@@ -138,7 +142,7 @@ interface CommandKeymap<Id extends string = string> {
|
|
|
138
142
|
/** Exports user overrides as JSON-serializable data. */
|
|
139
143
|
export(): SerializedKeymap;
|
|
140
144
|
/** Imports user overrides, replacing current overrides and saving to storage. */
|
|
141
|
-
import(data:
|
|
145
|
+
import(data: Json): void;
|
|
142
146
|
}
|
|
143
147
|
//#endregion
|
|
144
148
|
//#region src/registry.d.ts
|
|
@@ -182,7 +186,7 @@ interface CommandsOptions<Id extends string = string> {
|
|
|
182
186
|
*/
|
|
183
187
|
onRun?: (context: CommandContext) => void;
|
|
184
188
|
/** Error handler called when a command execution fails. Defaults to `console.error`. */
|
|
185
|
-
onError?: (error:
|
|
189
|
+
onError?: (error: Error, context: CommandContext) => void;
|
|
186
190
|
}
|
|
187
191
|
/** Options passed when manually running a command. */
|
|
188
192
|
interface RunOptions {
|
package/dist/attributes.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as observe, t as ObserveOptions } from "./attributes-
|
|
1
|
+
import { n as observe, t as ObserveOptions } from "./attributes-O6bXNKT9.js";
|
|
2
2
|
export { ObserveOptions, observe };
|
package/dist/attributes.js
CHANGED
|
@@ -5,7 +5,7 @@ const KEYS_ATTRIBUTE = "data-command-keys";
|
|
|
5
5
|
* Wires a commands registry to DOM data attributes. Returns a cleanup function.
|
|
6
6
|
*/
|
|
7
7
|
function observe(registry, options = {}) {
|
|
8
|
-
if (
|
|
8
|
+
if (!("document" in globalThis)) return () => {};
|
|
9
9
|
const root = options.root ?? document;
|
|
10
10
|
const onClick = (event) => {
|
|
11
11
|
const target = event.target;
|
|
@@ -45,7 +45,7 @@ function observe(registry, options = {}) {
|
|
|
45
45
|
paintWithin(root);
|
|
46
46
|
});
|
|
47
47
|
let observer;
|
|
48
|
-
if (
|
|
48
|
+
if ("MutationObserver" in globalThis) {
|
|
49
49
|
observer = new MutationObserver((mutations) => {
|
|
50
50
|
for (const mutation of mutations) {
|
|
51
51
|
if (mutation.type === "attributes" && mutation.target instanceof Element) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as SequenceBinding, T as Conflict, a as Commands, b as Chord, v as Binding } from "./attributes-
|
|
1
|
+
import { M as SequenceBinding, T as Conflict, a as Commands, b as Chord, v as Binding } from "./attributes-O6bXNKT9.js";
|
|
2
2
|
import { Hotkey } from "@tanstack/hotkeys";
|
|
3
3
|
//#region src/bindings.d.ts
|
|
4
4
|
/**
|
|
@@ -37,7 +37,7 @@ interface RebindState<Id extends string = string> {
|
|
|
37
37
|
interface RebindOptions<Id extends string = string> {
|
|
38
38
|
/** Capture mode. Defaults to `"chord"`. */
|
|
39
39
|
mode?: RebindMode;
|
|
40
|
-
/** Whether to
|
|
40
|
+
/** Whether to write the recorded (or cleared) binding into the keymap. Defaults to `true`. */
|
|
41
41
|
apply?: boolean;
|
|
42
42
|
/** Whether to append rather than replace existing bindings. Defaults to `false`. */
|
|
43
43
|
add?: boolean;
|
|
@@ -51,7 +51,7 @@ interface RebindOptions<Id extends string = string> {
|
|
|
51
51
|
onRecord?: (binding: Binding, conflicts: ReadonlyArray<Conflict<Id>>) => void;
|
|
52
52
|
/** Callback fired when recording is cancelled. */
|
|
53
53
|
onCancel?: () => void;
|
|
54
|
-
/** Callback fired when
|
|
54
|
+
/** Callback fired when Backspace or Delete clears the binding while recording. */
|
|
55
55
|
onClear?: () => void;
|
|
56
56
|
}
|
|
57
57
|
/** Controller instance for managing a rebinding session. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as Platform, C as CommandDefinition, D as HotkeySequence, E as Hotkey, M as SequenceBinding, N as SerializedBinding, O as KeymapPersistence, S as CommandContext, T as Conflict, _ as formatBinding, a as Commands, b as Chord, c as PendingSequence, d as createCommands, f as CommandKeymap, g as bindingTokens, h as ariaKeyShortcuts, i as CommandId, j as ScopeOptions, k as KeymapStorage, l as RunBlockReason, m as DisplayOptions, n as observe, o as CommandsOptions, p as SerializedKeymap, r as CommandHandle, s as ConflictOptions, t as ObserveOptions, u as RunOptions, v as Binding, w as CommandSource, x as Command, y as BindingInput } from "./attributes-
|
|
2
|
-
import { a as Rebinder, c as seq, i as RebindState, l as validateBinding, n as RebindMode, o as createRebinder, r as RebindOptions, s as BindingValidation, t as ALWAYS_SCOPE } from "./index-
|
|
1
|
+
import { A as Platform, C as CommandDefinition, D as HotkeySequence, E as Hotkey, M as SequenceBinding, N as SerializedBinding, O as KeymapPersistence, S as CommandContext, T as Conflict, _ as formatBinding, a as Commands, b as Chord, c as PendingSequence, d as createCommands, f as CommandKeymap, g as bindingTokens, h as ariaKeyShortcuts, i as CommandId, j as ScopeOptions, k as KeymapStorage, l as RunBlockReason, m as DisplayOptions, n as observe, o as CommandsOptions, p as SerializedKeymap, r as CommandHandle, s as ConflictOptions, t as ObserveOptions, u as RunOptions, v as Binding, w as CommandSource, x as Command, y as BindingInput } from "./attributes-O6bXNKT9.js";
|
|
2
|
+
import { a as Rebinder, c as seq, i as RebindState, l as validateBinding, n as RebindMode, o as createRebinder, r as RebindOptions, s as BindingValidation, t as ALWAYS_SCOPE } from "./index-B5iiRwCy.js";
|
|
3
3
|
export { ALWAYS_SCOPE, type Binding, type BindingInput, type BindingValidation, type Chord, type Command, type CommandContext, type CommandDefinition, type CommandHandle, type CommandId, type CommandKeymap, type CommandSource, type Commands, type CommandsOptions, type Conflict, type ConflictOptions, type DisplayOptions, type Hotkey, type HotkeySequence, type KeymapPersistence, type KeymapStorage, type ObserveOptions, type PendingSequence, type Platform, type RebindMode, type RebindOptions, type RebindState, type Rebinder, type RunBlockReason, type RunOptions, type ScopeOptions, type SequenceBinding, type SerializedBinding, type SerializedKeymap, ariaKeyShortcuts, bindingTokens, createCommands, createRebinder, formatBinding, observe, seq, validateBinding };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { observe } from "./attributes.js";
|
|
2
|
-
import { a as bindingTokens, c as seq, i as ariaKeyShortcuts, n as ALWAYS_SCOPE, o as formatBinding, r as createRebinder, t as createCommands, u as validateBinding } from "./src-
|
|
2
|
+
import { a as bindingTokens, c as seq, i as ariaKeyShortcuts, n as ALWAYS_SCOPE, o as formatBinding, r as createRebinder, t as createCommands, u as validateBinding } from "./src-DWmUPZSL.js";
|
|
3
3
|
export { ALWAYS_SCOPE, ariaKeyShortcuts, bindingTokens, createCommands, createRebinder, formatBinding, observe, seq, validateBinding };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as Platform, C as CommandDefinition, D as HotkeySequence, E as Hotkey, M as SequenceBinding, N as SerializedBinding, O as KeymapPersistence, S as CommandContext, T as Conflict, _ as formatBinding, a as Commands, b as Chord, c as PendingSequence, d as createCommands, f as CommandKeymap, g as bindingTokens, h as ariaKeyShortcuts, i as CommandId, j as ScopeOptions, k as KeymapStorage, l as RunBlockReason, m as DisplayOptions, n as observe, o as CommandsOptions, p as SerializedKeymap, r as CommandHandle, s as ConflictOptions, t as ObserveOptions, u as RunOptions, v as Binding, w as CommandSource, x as Command, y as BindingInput } from "./attributes-
|
|
2
|
-
import { a as Rebinder, c as seq, i as RebindState, l as validateBinding, n as RebindMode, o as createRebinder, r as RebindOptions, s as BindingValidation, t as ALWAYS_SCOPE } from "./index-
|
|
1
|
+
import { A as Platform, C as CommandDefinition, D as HotkeySequence, E as Hotkey, M as SequenceBinding, N as SerializedBinding, O as KeymapPersistence, S as CommandContext, T as Conflict, _ as formatBinding, a as Commands, b as Chord, c as PendingSequence, d as createCommands, f as CommandKeymap, g as bindingTokens, h as ariaKeyShortcuts, i as CommandId, j as ScopeOptions, k as KeymapStorage, l as RunBlockReason, m as DisplayOptions, n as observe, o as CommandsOptions, p as SerializedKeymap, r as CommandHandle, s as ConflictOptions, t as ObserveOptions, u as RunOptions, v as Binding, w as CommandSource, x as Command, y as BindingInput } from "./attributes-O6bXNKT9.js";
|
|
2
|
+
import { a as Rebinder, c as seq, i as RebindState, l as validateBinding, n as RebindMode, o as createRebinder, r as RebindOptions, s as BindingValidation, t as ALWAYS_SCOPE } from "./index-B5iiRwCy.js";
|
|
3
3
|
//#region src/react/hooks.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Mounts a commands registry for the lifecycle of the component.
|
package/dist/react.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { observe } from "./attributes.js";
|
|
3
|
-
import { a as bindingTokens, c as seq, i as ariaKeyShortcuts, l as toBindings, n as ALWAYS_SCOPE, o as formatBinding, r as createRebinder, s as bindingSignature, t as createCommands, u as validateBinding } from "./src-
|
|
3
|
+
import { a as bindingTokens, c as seq, i as ariaKeyShortcuts, l as toBindings, n as ALWAYS_SCOPE, o as formatBinding, r as createRebinder, s as bindingSignature, t as createCommands, u as validateBinding } from "./src-DWmUPZSL.js";
|
|
4
4
|
import { getKeyStateTracker } from "@tanstack/hotkeys";
|
|
5
5
|
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
6
6
|
//#region src/react/hooks.ts
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { HotkeyRecorder, HotkeySequenceRecorder, detectPlatform, formatForDisplay, getHotkeyManager, getSequenceManager, normalizeHotkey, validateHotkey } from "@tanstack/hotkeys";
|
|
2
2
|
//#region src/bindings.ts
|
|
3
3
|
/** Comparing, validating and serializing hotkey bindings. */
|
|
4
|
+
/** Type guard narrowing a JSON value to a string. */
|
|
5
|
+
function isJsonString(value) {
|
|
6
|
+
return typeof value === "string";
|
|
7
|
+
}
|
|
4
8
|
/**
|
|
5
9
|
* Creates a sequence binding from ordered hotkey steps.
|
|
6
10
|
*
|
|
@@ -50,7 +54,7 @@ function serializeBinding(binding) {
|
|
|
50
54
|
}
|
|
51
55
|
/** Deserializes a stored value into a valid binding, or `null` if invalid. */
|
|
52
56
|
function deserializeBinding(value) {
|
|
53
|
-
if (
|
|
57
|
+
if (isJsonString(value)) {
|
|
54
58
|
const trimmed = value.trim();
|
|
55
59
|
return trimmed && isBindingValid(trimmed) ? trimmed : null;
|
|
56
60
|
}
|
|
@@ -188,6 +192,7 @@ function createRebinder(registry, id, options = {}) {
|
|
|
188
192
|
};
|
|
189
193
|
const cleared = () => {
|
|
190
194
|
releaseScope();
|
|
195
|
+
if (options.apply !== false) registry.keymap.set(id, null);
|
|
191
196
|
notify({
|
|
192
197
|
recording: false,
|
|
193
198
|
binding: null,
|
|
@@ -208,7 +213,9 @@ function createRebinder(registry, id, options = {}) {
|
|
|
208
213
|
onClear: cleared
|
|
209
214
|
}) : new HotkeyRecorder({
|
|
210
215
|
ignoreInputs: options.ignoreInputs ?? true,
|
|
211
|
-
onRecord: (hotkey) =>
|
|
216
|
+
onRecord: (hotkey) => {
|
|
217
|
+
if (hotkey) record(hotkey);
|
|
218
|
+
},
|
|
212
219
|
onCancel: cancelled,
|
|
213
220
|
onClear: cleared
|
|
214
221
|
});
|
|
@@ -396,6 +403,10 @@ var Binder = class {
|
|
|
396
403
|
//#region src/keymap.ts
|
|
397
404
|
/** Keymap managing default bindings and user overrides. */
|
|
398
405
|
const DEFAULT_STORAGE_KEY = "inklu.keys";
|
|
406
|
+
/** Type guard narrowing a JSON value to a plain object (excludes arrays and `null`). */
|
|
407
|
+
function isJsonObject(value) {
|
|
408
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
409
|
+
}
|
|
399
410
|
var Keymap = class {
|
|
400
411
|
#defaults = /* @__PURE__ */ new Map();
|
|
401
412
|
#overrides = /* @__PURE__ */ new Map();
|
|
@@ -445,9 +456,7 @@ var Keymap = class {
|
|
|
445
456
|
}
|
|
446
457
|
/** Exports user overrides as a serialized keymap object. */
|
|
447
458
|
export() {
|
|
448
|
-
|
|
449
|
-
for (const [id, bindings] of this.#overrides) data[id] = bindings.map(serializeBinding);
|
|
450
|
-
return data;
|
|
459
|
+
return Object.fromEntries(Array.from(this.#overrides, ([id, bindings]) => [id, bindings.map(serializeBinding)]));
|
|
451
460
|
}
|
|
452
461
|
/** Imports serialized user overrides and saves to storage. */
|
|
453
462
|
import(data) {
|
|
@@ -462,8 +471,10 @@ var Keymap = class {
|
|
|
462
471
|
const raw = storage.getItem(this.#storageKey());
|
|
463
472
|
if (!raw) return;
|
|
464
473
|
this.#read(JSON.parse(raw));
|
|
465
|
-
|
|
466
|
-
|
|
474
|
+
} catch {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
for (const listener of this.#listeners) listener();
|
|
467
478
|
}
|
|
468
479
|
/** Subscribes to keymap changes. Returns an unsubscribe function. */
|
|
469
480
|
subscribe(listener) {
|
|
@@ -473,7 +484,7 @@ var Keymap = class {
|
|
|
473
484
|
};
|
|
474
485
|
}
|
|
475
486
|
#read(data) {
|
|
476
|
-
if (
|
|
487
|
+
if (!isJsonObject(data)) return;
|
|
477
488
|
this.#overrides.clear();
|
|
478
489
|
for (const [id, value] of Object.entries(data)) {
|
|
479
490
|
if (!Array.isArray(value)) continue;
|
|
@@ -638,7 +649,7 @@ var CommandRegistry = class {
|
|
|
638
649
|
try {
|
|
639
650
|
return definition.when() === false ? "guarded" : null;
|
|
640
651
|
} catch (error) {
|
|
641
|
-
this.#reportError(error, {
|
|
652
|
+
this.#reportError(toError(error), {
|
|
642
653
|
id,
|
|
643
654
|
source: "api",
|
|
644
655
|
commands: this
|
|
@@ -660,9 +671,14 @@ var CommandRegistry = class {
|
|
|
660
671
|
if (this.#mounts > 0) this.#binder.sync(id);
|
|
661
672
|
this.#invalidate();
|
|
662
673
|
this.#auditAdded(id);
|
|
674
|
+
let disposed = false;
|
|
663
675
|
return {
|
|
664
|
-
run: (options) => this.run(id, options),
|
|
665
|
-
dispose: () =>
|
|
676
|
+
run: (options) => !disposed && this.run(id, options),
|
|
677
|
+
dispose: () => {
|
|
678
|
+
if (disposed) return;
|
|
679
|
+
disposed = true;
|
|
680
|
+
this.#pop(id, entry);
|
|
681
|
+
}
|
|
666
682
|
};
|
|
667
683
|
}
|
|
668
684
|
remove(loose) {
|
|
@@ -898,13 +914,13 @@ var CommandRegistry = class {
|
|
|
898
914
|
if (this.#options.onRun) try {
|
|
899
915
|
this.#options.onRun(full);
|
|
900
916
|
} catch (error) {
|
|
901
|
-
this.#reportError(error, full);
|
|
917
|
+
this.#reportError(toError(error), full);
|
|
902
918
|
}
|
|
903
919
|
try {
|
|
904
920
|
const result = definition.run(full);
|
|
905
|
-
if (result instanceof Promise) result.catch((
|
|
921
|
+
if (result instanceof Promise) result.catch((cause) => this.#reportError(toError(cause), full));
|
|
906
922
|
} catch (error) {
|
|
907
|
-
this.#reportError(error, full);
|
|
923
|
+
this.#reportError(toError(error), full);
|
|
908
924
|
}
|
|
909
925
|
}
|
|
910
926
|
#reportError(error, context) {
|
|
@@ -1014,7 +1030,11 @@ var CommandRegistry = class {
|
|
|
1014
1030
|
}
|
|
1015
1031
|
};
|
|
1016
1032
|
function describeBinding(binding) {
|
|
1017
|
-
return
|
|
1033
|
+
return isSequence(binding) ? `"${binding.sequence.join(" ")}"` : `"${binding}"`;
|
|
1034
|
+
}
|
|
1035
|
+
/** Normalizes an arbitrary thrown value into a real `Error` at the catch boundary. */
|
|
1036
|
+
function toError(cause) {
|
|
1037
|
+
return cause instanceof Error ? cause : new Error(String(cause));
|
|
1018
1038
|
}
|
|
1019
1039
|
function isDevelopment() {
|
|
1020
1040
|
const environment = globalThis.process?.env?.NODE_ENV;
|
package/package.json
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inklu/keys",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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
|
-
"
|
|
7
|
-
"shortcuts",
|
|
8
|
-
"hotkeys",
|
|
9
|
-
"keybindings",
|
|
6
|
+
"accessibility",
|
|
10
7
|
"command-palette",
|
|
11
8
|
"commands",
|
|
9
|
+
"hotkeys",
|
|
10
|
+
"keybindings",
|
|
11
|
+
"keyboard",
|
|
12
|
+
"react",
|
|
12
13
|
"scopes",
|
|
13
14
|
"sequences",
|
|
14
|
-
"
|
|
15
|
-
"react"
|
|
15
|
+
"shortcuts"
|
|
16
16
|
],
|
|
17
|
+
"homepage": "https://github.com/nkurunziza-saddy/inklu#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/nkurunziza-saddy/inklu/issues"
|
|
20
|
+
},
|
|
17
21
|
"license": "MIT",
|
|
18
22
|
"author": "nkurunziza-saddy",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/nkurunziza-saddy/inklu.git",
|
|
26
|
+
"directory": "packages/keys"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
19
33
|
"type": "module",
|
|
34
|
+
"sideEffects": false,
|
|
20
35
|
"main": "./dist/index.js",
|
|
21
36
|
"module": "./dist/index.js",
|
|
22
37
|
"types": "./dist/index.d.ts",
|
|
@@ -38,61 +53,47 @@
|
|
|
38
53
|
},
|
|
39
54
|
"./package.json": "./package.json"
|
|
40
55
|
},
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
"dist",
|
|
44
|
-
"README.md",
|
|
45
|
-
"LICENSE"
|
|
46
|
-
],
|
|
47
|
-
"engines": {
|
|
48
|
-
"node": ">=18"
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
49
58
|
},
|
|
50
59
|
"dependencies": {
|
|
51
60
|
"@tanstack/hotkeys": "0.8.0"
|
|
52
61
|
},
|
|
53
|
-
"peerDependencies": {
|
|
54
|
-
"react": "^18.0.0 || ^19.0.0"
|
|
55
|
-
},
|
|
56
|
-
"peerDependenciesMeta": {
|
|
57
|
-
"react": {
|
|
58
|
-
"optional": true
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
62
|
"devDependencies": {
|
|
63
|
+
"@inklu/tsconfig": "1.0.0",
|
|
62
64
|
"@testing-library/react": "^16.3.2",
|
|
63
65
|
"@types/react": "^19",
|
|
64
|
-
"@vitest/coverage-v8": "
|
|
66
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
65
67
|
"jsdom": "^29.1.1",
|
|
66
68
|
"react": "19.2.8",
|
|
67
69
|
"react-dom": "19.2.8",
|
|
68
|
-
"tsdown": "^0.22.14",
|
|
69
70
|
"typescript": "^5.8.2",
|
|
70
|
-
"
|
|
71
|
-
"
|
|
71
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
|
|
72
|
+
"vite-plus": "0.3.0",
|
|
73
|
+
"vitest": "4.1.11"
|
|
72
74
|
},
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"url": "git+https://github.com/nkurunziza-saddy/inklu.git",
|
|
76
|
-
"directory": "packages/keys"
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
77
77
|
},
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"react": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
81
82
|
},
|
|
82
|
-
"
|
|
83
|
-
"
|
|
83
|
+
"engines": {
|
|
84
|
+
"node": ">=18"
|
|
84
85
|
},
|
|
85
86
|
"scripts": {
|
|
86
|
-
"build": "
|
|
87
|
+
"build": "vp pack",
|
|
87
88
|
"lint:exports": "publint",
|
|
88
|
-
"dev": "
|
|
89
|
-
"lint": "
|
|
90
|
-
"check": "
|
|
91
|
-
"format": "
|
|
89
|
+
"dev": "vp pack --watch",
|
|
90
|
+
"lint": "vp lint",
|
|
91
|
+
"check": "vp check",
|
|
92
|
+
"format": "vp fmt --write",
|
|
92
93
|
"check-types": "tsc --noEmit",
|
|
93
94
|
"typecheck": "tsc --noEmit",
|
|
94
|
-
"test": "
|
|
95
|
-
"test:watch": "
|
|
96
|
-
"test:coverage": "
|
|
95
|
+
"test": "vp test run",
|
|
96
|
+
"test:watch": "vp test",
|
|
97
|
+
"test:coverage": "vp test run --coverage"
|
|
97
98
|
}
|
|
98
99
|
}
|