@inklu/keys 1.2.1 → 1.3.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.
@@ -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: unknown): void;
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: unknown, context: CommandContext) => void;
189
+ onError?: (error: Error, context: CommandContext) => void;
186
190
  }
187
191
  /** Options passed when manually running a command. */
188
192
  interface RunOptions {
@@ -1,2 +1,2 @@
1
- import { n as observe, t as ObserveOptions } from "./attributes-DbxlLdrb.js";
1
+ import { n as observe, t as ObserveOptions } from "./attributes-O6bXNKT9.js";
2
2
  export { ObserveOptions, observe };
@@ -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 (typeof document === "undefined") return () => {};
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 (typeof MutationObserver !== "undefined") {
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-DbxlLdrb.js";
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
  /**
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-DbxlLdrb.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-D4UoKAQU.js";
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-nIJJ9qsY.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-DyFUgmho.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-DifrdRcK.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-DbxlLdrb.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-D4UoKAQU.js";
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-nIJJ9qsY.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-DyFUgmho.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-DifrdRcK.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 (typeof value === "string") {
57
+ if (isJsonString(value)) {
54
58
  const trimmed = value.trim();
55
59
  return trimmed && isBindingValid(trimmed) ? trimmed : null;
56
60
  }
@@ -396,6 +400,10 @@ var Binder = class {
396
400
  //#region src/keymap.ts
397
401
  /** Keymap managing default bindings and user overrides. */
398
402
  const DEFAULT_STORAGE_KEY = "inklu.keys";
403
+ /** Type guard narrowing a JSON value to a plain object (excludes arrays and `null`). */
404
+ function isJsonObject(value) {
405
+ return typeof value === "object" && value !== null && !Array.isArray(value);
406
+ }
399
407
  var Keymap = class {
400
408
  #defaults = /* @__PURE__ */ new Map();
401
409
  #overrides = /* @__PURE__ */ new Map();
@@ -445,9 +453,7 @@ var Keymap = class {
445
453
  }
446
454
  /** Exports user overrides as a serialized keymap object. */
447
455
  export() {
448
- const data = {};
449
- for (const [id, bindings] of this.#overrides) data[id] = bindings.map(serializeBinding);
450
- return data;
456
+ return Object.fromEntries(Array.from(this.#overrides, ([id, bindings]) => [id, bindings.map(serializeBinding)]));
451
457
  }
452
458
  /** Imports serialized user overrides and saves to storage. */
453
459
  import(data) {
@@ -473,7 +479,7 @@ var Keymap = class {
473
479
  };
474
480
  }
475
481
  #read(data) {
476
- if (typeof data !== "object" || data === null) return;
482
+ if (!isJsonObject(data)) return;
477
483
  this.#overrides.clear();
478
484
  for (const [id, value] of Object.entries(data)) {
479
485
  if (!Array.isArray(value)) continue;
@@ -638,7 +644,7 @@ var CommandRegistry = class {
638
644
  try {
639
645
  return definition.when() === false ? "guarded" : null;
640
646
  } catch (error) {
641
- this.#reportError(error, {
647
+ this.#reportError(toError(error), {
642
648
  id,
643
649
  source: "api",
644
650
  commands: this
@@ -898,13 +904,13 @@ var CommandRegistry = class {
898
904
  if (this.#options.onRun) try {
899
905
  this.#options.onRun(full);
900
906
  } catch (error) {
901
- this.#reportError(error, full);
907
+ this.#reportError(toError(error), full);
902
908
  }
903
909
  try {
904
910
  const result = definition.run(full);
905
- if (result instanceof Promise) result.catch((error) => this.#reportError(error, full));
911
+ if (result instanceof Promise) result.catch((cause) => this.#reportError(toError(cause), full));
906
912
  } catch (error) {
907
- this.#reportError(error, full);
913
+ this.#reportError(toError(error), full);
908
914
  }
909
915
  }
910
916
  #reportError(error, context) {
@@ -1014,7 +1020,11 @@ var CommandRegistry = class {
1014
1020
  }
1015
1021
  };
1016
1022
  function describeBinding(binding) {
1017
- return typeof binding === "string" ? `"${binding}"` : `"${binding.sequence.join(" ")}"`;
1023
+ return isSequence(binding) ? `"${binding.sequence.join(" ")}"` : `"${binding}"`;
1024
+ }
1025
+ /** Normalizes an arbitrary thrown value into a real `Error` at the catch boundary. */
1026
+ function toError(cause) {
1027
+ return cause instanceof Error ? cause : new Error(String(cause));
1018
1028
  }
1019
1029
  function isDevelopment() {
1020
1030
  const environment = globalThis.process?.env?.NODE_ENV;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inklu/keys",
3
- "version": "1.2.1",
3
+ "version": "1.3.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
  "keyboard",