@tanstack/solid-hotkeys 0.12.1 → 0.14.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,76 +1,33 @@
1
- import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
1
+ import { useDefaultHotkeysOptionsSource } from "./defaultOptions.js";
2
2
  import { HotkeyRecorder } from "@tanstack/hotkeys";
3
3
  import { createEffect, onCleanup } from "solid-js";
4
4
  import { useSelector } from "@tanstack/solid-store";
5
5
 
6
6
  //#region src/createHotkeyRecorder.ts
7
- /**
8
- * SolidJS primitive for recording keyboard shortcuts.
9
- *
10
- * This primitive provides a thin wrapper around the framework-agnostic `HotkeyRecorder`
11
- * class, managing all the complexity of capturing keyboard events, converting them
12
- * to hotkey strings, and handling edge cases like Escape to cancel or Backspace/Delete
13
- * to clear.
14
- *
15
- * This primitive uses `useSelector` from `@tanstack/solid-store` to subscribe
16
- * to the recorder's store state (same pattern as useHotkeyRecorder in React).
17
- *
18
- * @param options - Configuration options for the recorder (or accessor function)
19
- * @returns An object with recording state signals and control functions
20
- *
21
- * @example
22
- * ```tsx
23
- * function ShortcutSettings() {
24
- * const [shortcut, setShortcut] = createSignal<Hotkey>('Mod+S')
25
- *
26
- * const recorder = createHotkeyRecorder({
27
- * onRecord: (hotkey) => {
28
- * setShortcut(hotkey)
29
- * },
30
- * onCancel: () => {
31
- * console.log('Recording cancelled')
32
- * },
33
- * })
34
- *
35
- * return (
36
- * <div>
37
- * <button onClick={recorder.startRecording}>
38
- * {recorder.isRecording() ? 'Recording...' : 'Edit Shortcut'}
39
- * </button>
40
- * <Show when={recorder.recordedHotkey()}>
41
- * <div>Recording: {recorder.recordedHotkey()}</div>
42
- * </Show>
43
- * </div>
44
- * )
45
- * }
46
- * ```
47
- */
48
7
  function createHotkeyRecorder(options) {
49
- const defaultOptions = useDefaultHotkeysOptions();
50
- const resolvedOptions = typeof options === "function" ? options() : options;
51
- const mergedOptions = {
52
- ...defaultOptions.hotkeyRecorder,
53
- ...resolvedOptions
54
- };
55
- const recorder = new HotkeyRecorder(mergedOptions);
8
+ const defaultOptions = useDefaultHotkeysOptionsSource();
9
+ const resolve = () => ({
10
+ ...defaultOptions().hotkeyRecorder,
11
+ ...typeof options === "function" ? options() : options
12
+ });
13
+ const recorder = new HotkeyRecorder(resolve());
56
14
  const isRecording = useSelector(recorder.store, (state) => state.isRecording);
15
+ const steps = useSelector(recorder.store, (state) => state.steps);
57
16
  const recordedHotkey = useSelector(recorder.store, (state) => state.recordedHotkey);
58
17
  createEffect(() => {
59
- const resolved = typeof options === "function" ? options() : options;
60
- recorder.setOptions({
61
- ...defaultOptions.hotkeyRecorder,
62
- ...resolved
63
- });
18
+ recorder.setOptions(resolve());
64
19
  });
65
20
  onCleanup(() => {
66
21
  recorder.destroy();
67
22
  });
68
23
  return {
69
24
  isRecording,
25
+ steps,
70
26
  recordedHotkey,
71
27
  startRecording: () => recorder.start(),
72
28
  stopRecording: () => recorder.stop(),
73
- cancelRecording: () => recorder.cancel()
29
+ cancelRecording: () => recorder.cancel(),
30
+ commitRecording: () => recorder.commit()
74
31
  };
75
32
  }
76
33
 
@@ -1,23 +1,20 @@
1
- import { HotkeyRegistrationView, SequenceRegistrationView } from "@tanstack/hotkeys";
1
+ import { HotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  //#region src/createHotkeyRegistrations.d.ts
3
3
  /**
4
- * Return type for createHotkeyRegistrations.
5
- */
6
- export interface HotkeyRegistrationsResult {
7
- /** Accessor for all registered hotkeys (public view, no callbacks) */
8
- hotkeys: () => Array<HotkeyRegistrationView>;
9
- /** Accessor for all registered sequences */
10
- sequences: () => Array<SequenceRegistrationView>;
11
- }
12
- /**
13
- * Solid primitive that reactively reads all hotkey and sequence registrations
14
- * from the singleton managers.
4
+ * Solid primitive that reactively reads every hotkey registration, chords and
5
+ * sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
6
+ *
7
+ * This primitive does NOT require the HotkeysProvider.
15
8
  *
16
- * This is a standalone primitive that does NOT require the HotkeysProvider.
17
- * It subscribes to both HotkeyManager and SequenceManager stores and
18
- * triggers reactivity when registrations change.
9
+ * @returns An accessor for all registrations, without callbacks
19
10
  *
20
- * @returns Object with `hotkeys` and `sequences` signal accessors
11
+ * @example
12
+ * ```tsx
13
+ * const registrations = createHotkeyRegistrations()
14
+ * <For each={registrations()}>
15
+ * {(reg) => <li>{reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}</li>}
16
+ * </For>
17
+ * ```
21
18
  */
22
- export declare function createHotkeyRegistrations(): HotkeyRegistrationsResult;
19
+ export declare function createHotkeyRegistrations(): () => Array<HotkeyRegistrationView>;
23
20
  //#endregion
@@ -1,24 +1,25 @@
1
- import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
1
+ import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  import { useSelector } from "@tanstack/solid-store";
3
3
 
4
4
  //#region src/createHotkeyRegistrations.ts
5
5
  /**
6
- * Solid primitive that reactively reads all hotkey and sequence registrations
7
- * from the singleton managers.
6
+ * Solid primitive that reactively reads every hotkey registration, chords and
7
+ * sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
8
8
  *
9
- * This is a standalone primitive that does NOT require the HotkeysProvider.
10
- * It subscribes to both HotkeyManager and SequenceManager stores and
11
- * triggers reactivity when registrations change.
9
+ * This primitive does NOT require the HotkeysProvider.
12
10
  *
13
- * @returns Object with `hotkeys` and `sequences` signal accessors
11
+ * @returns An accessor for all registrations, without callbacks
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const registrations = createHotkeyRegistrations()
16
+ * <For each={registrations()}>
17
+ * {(reg) => <li>{reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}</li>}
18
+ * </For>
19
+ * ```
14
20
  */
15
21
  function createHotkeyRegistrations() {
16
- const hotkeyManager = getHotkeyManager();
17
- const sequenceManager = getSequenceManager();
18
- return {
19
- hotkeys: useSelector(hotkeyManager.registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView)),
20
- sequences: useSelector(sequenceManager.registrations, (state) => Array.from(state.values()))
21
- };
22
+ return useSelector(getHotkeyManager().registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView));
22
23
  }
23
24
 
24
25
  //#endregion
@@ -1,54 +1,27 @@
1
- import { CreateHotkeyOptions } from "./createHotkey.js";
2
- import { HotkeyCallback, RegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { CreateHotkeyCommonOptions, CreateHotkeyTarget } from "./createHotkey.js";
2
+ import { AdapterHotkeyDefinition } from "@tanstack/hotkeys/adapter";
3
3
  //#region src/createHotkeys.d.ts
4
4
  /**
5
- * A single hotkey definition for use with `createHotkeys`.
5
+ * One hotkey for `createHotkeys`. Chords accept `requireReset`; sequences
6
+ * accept `timeout`; hotkeys of unknown kind accept only shared options.
6
7
  */
7
- export interface CreateHotkeyDefinition {
8
- /** The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object */
9
- hotkey: RegisterableHotkey;
10
- /** The function to call when the hotkey is pressed */
11
- callback: HotkeyCallback;
12
- /** Per-hotkey options (merged on top of commonOptions) */
13
- options?: CreateHotkeyOptions;
14
- }
8
+ export type CreateHotkeyDefinition = AdapterHotkeyDefinition<CreateHotkeyTarget>;
15
9
  /**
16
- * SolidJS primitive for registering multiple keyboard hotkeys at once.
10
+ * Solid primitive for registering several hotkeys at once: chords, sequences,
11
+ * and app hotkeys. Pass an accessor for a reactive list.
17
12
  *
18
- * Uses the singleton HotkeyManager for efficient event handling.
19
- * Accepts a dynamic array of hotkey definitions, making it safe to use
20
- * with variable-length lists.
21
- *
22
- * Options are merged in this order:
23
- * HotkeysProvider defaults < commonOptions < per-definition options
24
- *
25
- * @param hotkeys - Array of hotkey definitions, or accessor returning them
26
- * @param commonOptions - Shared options applied to all hotkeys, or accessor
27
- *
28
- * @example
29
- * ```tsx
30
- * function Editor() {
31
- * createHotkeys([
32
- * { hotkey: 'Mod+S', callback: () => save() },
33
- * { hotkey: 'Mod+Z', callback: () => undo() },
34
- * { hotkey: 'Escape', callback: () => close() },
35
- * ])
36
- * }
37
- * ```
13
+ * Options merge in this order: provider defaults, app hotkey declaration,
14
+ * commonOptions, per-hotkey options. Each option applies only to the kinds
15
+ * that support it. Unbound app hotkeys and empty sequences register nothing.
16
+ * A `null` target skips registration until it is set.
38
17
  *
39
18
  * @example
40
19
  * ```tsx
41
- * function MenuShortcuts(props) {
42
- * createHotkeys(
43
- * () => props.items.map((item) => ({
44
- * hotkey: item.shortcut,
45
- * callback: item.action,
46
- * options: { enabled: item.enabled },
47
- * })),
48
- * { preventDefault: true },
49
- * )
50
- * }
20
+ * createHotkeys([
21
+ * { hotkey: 'Mod+S', callback: () => save() },
22
+ * { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
23
+ * ])
51
24
  * ```
52
25
  */
53
- export declare function createHotkeys(hotkeys: Array<CreateHotkeyDefinition> | (() => Array<CreateHotkeyDefinition>), commonOptions?: CreateHotkeyOptions | (() => CreateHotkeyOptions)): void;
26
+ export declare function createHotkeys(hotkeys: ReadonlyArray<CreateHotkeyDefinition> | (() => ReadonlyArray<CreateHotkeyDefinition>), commonOptions?: CreateHotkeyCommonOptions | (() => CreateHotkeyCommonOptions)): void;
54
27
  //#endregion
@@ -1,109 +1,39 @@
1
- import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
2
- import { detectPlatform, getHotkeyManager, normalizeRegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { useAppHotkeysStoreSource, useDefaultHotkeysOptionsSource } from "./defaultOptions.js";
3
2
  import { createEffect, onCleanup, untrack } from "solid-js";
3
+ import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkeys/adapter";
4
4
 
5
5
  //#region src/createHotkeys.ts
6
6
  /**
7
- * SolidJS primitive for registering multiple keyboard hotkeys at once.
7
+ * Solid primitive for registering several hotkeys at once: chords, sequences,
8
+ * and app hotkeys. Pass an accessor for a reactive list.
8
9
  *
9
- * Uses the singleton HotkeyManager for efficient event handling.
10
- * Accepts a dynamic array of hotkey definitions, making it safe to use
11
- * with variable-length lists.
12
- *
13
- * Options are merged in this order:
14
- * HotkeysProvider defaults < commonOptions < per-definition options
15
- *
16
- * @param hotkeys - Array of hotkey definitions, or accessor returning them
17
- * @param commonOptions - Shared options applied to all hotkeys, or accessor
10
+ * Options merge in this order: provider defaults, app hotkey declaration,
11
+ * commonOptions, per-hotkey options. Each option applies only to the kinds
12
+ * that support it. Unbound app hotkeys and empty sequences register nothing.
13
+ * A `null` target skips registration until it is set.
18
14
  *
19
15
  * @example
20
16
  * ```tsx
21
- * function Editor() {
22
- * createHotkeys([
23
- * { hotkey: 'Mod+S', callback: () => save() },
24
- * { hotkey: 'Mod+Z', callback: () => undo() },
25
- * { hotkey: 'Escape', callback: () => close() },
26
- * ])
27
- * }
28
- * ```
29
- *
30
- * @example
31
- * ```tsx
32
- * function MenuShortcuts(props) {
33
- * createHotkeys(
34
- * () => props.items.map((item) => ({
35
- * hotkey: item.shortcut,
36
- * callback: item.action,
37
- * options: { enabled: item.enabled },
38
- * })),
39
- * { preventDefault: true },
40
- * )
41
- * }
17
+ * createHotkeys([
18
+ * { hotkey: 'Mod+S', callback: () => save() },
19
+ * { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
20
+ * ])
42
21
  * ```
43
22
  */
44
23
  function createHotkeys(hotkeys, commonOptions = {}) {
45
- const defaultOptions = useDefaultHotkeysOptions();
46
- const manager = getHotkeyManager();
47
- const registrations = /* @__PURE__ */ new Map();
48
- onCleanup(() => {
49
- for (const { handle } of registrations.values()) if (handle.isActive) handle.unregister();
50
- registrations.clear();
51
- });
24
+ const defaultOptions = useDefaultHotkeysOptionsSource();
25
+ const appHotkeys = useAppHotkeysStoreSource();
26
+ const bindings = createHotkeyBindings();
27
+ onCleanup(() => bindings.destroy());
52
28
  createEffect(() => {
53
- const resolvedHotkeys = typeof hotkeys === "function" ? hotkeys() : hotkeys;
54
- const resolvedCommonOptions = typeof commonOptions === "function" ? commonOptions() : commonOptions;
55
- const nextKeys = /* @__PURE__ */ new Set();
56
- const prepared = [];
57
- for (let i = 0; i < resolvedHotkeys.length; i++) {
58
- const def = resolvedHotkeys[i];
59
- const resolvedDefOptions = def.options ?? {};
60
- const mergedOptions = {
61
- ...defaultOptions.hotkey,
62
- ...resolvedCommonOptions,
63
- ...resolvedDefOptions
64
- };
65
- const platform = mergedOptions.platform ?? detectPlatform();
66
- const hotkeyString = normalizeRegisterableHotkey(def.hotkey, platform);
67
- const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
68
- if (!resolvedTarget) continue;
69
- const registrationKey = `${i}:${hotkeyString}`;
70
- nextKeys.add(registrationKey);
71
- prepared.push({
72
- registrationKey,
73
- def,
74
- mergedOptions,
75
- hotkeyString,
76
- resolvedTarget
77
- });
78
- }
79
- untrack(() => {
80
- for (const [key, record] of [...registrations.entries()]) if (!nextKeys.has(key)) {
81
- if (record.handle.isActive) record.handle.unregister();
82
- registrations.delete(key);
83
- }
84
- for (const p of prepared) {
85
- const existing = registrations.get(p.registrationKey);
86
- if (existing?.handle.isActive && existing.target === p.resolvedTarget) {
87
- existing.handle.callback = p.def.callback;
88
- const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
89
- existing.handle.setOptions(optionsWithoutTarget);
90
- continue;
91
- }
92
- if (existing) {
93
- if (existing.handle.isActive) existing.handle.unregister();
94
- registrations.delete(p.registrationKey);
95
- }
96
- const { target: _target, ...optionsWithoutTarget } = p.mergedOptions;
97
- const handle = manager.register(p.hotkeyString, p.def.callback, {
98
- ...optionsWithoutTarget,
99
- target: p.resolvedTarget
100
- });
101
- registrations.set(p.registrationKey, {
102
- handle,
103
- target: p.resolvedTarget
104
- });
105
- }
29
+ appHotkeys.state();
30
+ const definitions = prepareHotkeyDefinitions(typeof hotkeys === "function" ? hotkeys() : hotkeys, {
31
+ appHotkeysStore: appHotkeys.store,
32
+ defaults: defaultOptions().hotkey,
33
+ commonOptions: typeof commonOptions === "function" ? commonOptions() : commonOptions,
34
+ resolveTarget: (target) => target
106
35
  });
36
+ untrack(() => bindings.update(definitions));
107
37
  });
108
38
  }
109
39
 
@@ -0,0 +1,24 @@
1
+ import { useHotkeysContext } from "./HotkeysProvider.js";
2
+ import { Store, useSelector } from "@tanstack/solid-store";
3
+
4
+ //#region src/defaultOptions.ts
5
+ /** Captures the context once and reads current defaults inside reactive computations. */
6
+ function useDefaultHotkeysOptionsSource() {
7
+ const context = useHotkeysContext();
8
+ return () => context?.defaultOptions ?? {};
9
+ }
10
+ const NO_APP_HOTKEYS = new Store(null);
11
+ /**
12
+ * The provider's app hotkeys store and an accessor that tracks its state, so
13
+ * computations re-run when app hotkeys or custom hotkeys change.
14
+ */
15
+ function useAppHotkeysStoreSource() {
16
+ const store = useHotkeysContext()?.appHotkeysStore ?? null;
17
+ return {
18
+ store,
19
+ state: useSelector(store?.store ?? NO_APP_HOTKEYS, (value) => value)
20
+ };
21
+ }
22
+
23
+ //#endregion
24
+ export { useAppHotkeysStoreSource, useDefaultHotkeysOptionsSource };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,12 @@
1
- import { CreateHotkeyOptions, createHotkey } from "./createHotkey.js";
2
- import { CreateHotkeySequenceOptions, createHotkeySequence } from "./createHotkeySequence.js";
1
+ import { CreateAppHotkeyOptions, CreateHotkeyChordOptions, CreateHotkeyCommonOptions, CreateHotkeyOptions, CreateHotkeyOptionsFor, CreateHotkeySequenceOptions, CreateHotkeyTarget, createHotkey } from "./createHotkey.js";
3
2
  import { HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, useDefaultHotkeysOptions, useHotkeysContext } from "./HotkeysProvider.js";
4
3
  import { CreateHotkeyDefinition, createHotkeys } from "./createHotkeys.js";
4
+ import { SolidHotkeyRecorder, createHotkeyRecorder } from "./createHotkeyRecorder.js";
5
+ import { createHotkeyRegistrations } from "./createHotkeyRegistrations.js";
6
+ import { createHotkeyHint } from "./createHotkeyHint.js";
7
+ import { UseCustomHotkeysResult, createAppHotkeyList, useAppHotkeys, useCustomHotkeys } from "./appHotkeys.js";
5
8
  import { createHeldKeys } from "./createHeldKeys.js";
6
9
  import { createHeldKeyCodes } from "./createHeldKeyCodes.js";
7
10
  import { createKeyHold } from "./createKeyHold.js";
8
- import { CreateHotkeySequenceDefinition, createHotkeySequences } from "./createHotkeySequences.js";
9
- import { SolidHotkeyRecorder, createHotkeyRecorder } from "./createHotkeyRecorder.js";
10
- import { HotkeyRegistrationsResult, createHotkeyRegistrations } from "./createHotkeyRegistrations.js";
11
- import { SolidHotkeySequenceRecorder, createHotkeySequenceRecorder } from "./createHotkeySequenceRecorder.js";
12
- import { createHotkeyHint } from "./createHotkeyHint.js";
13
11
  export * from "@tanstack/hotkeys";
14
- export { CreateHotkeyDefinition, CreateHotkeyOptions, CreateHotkeySequenceDefinition, CreateHotkeySequenceOptions, HotkeyRegistrationsResult, HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, SolidHotkeyRecorder, SolidHotkeySequenceRecorder, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations, createHotkeySequence, createHotkeySequenceRecorder, createHotkeySequences, createHotkeys, createKeyHold, useDefaultHotkeysOptions, useHotkeysContext };
12
+ export { CreateAppHotkeyOptions, CreateHotkeyChordOptions, CreateHotkeyCommonOptions, CreateHotkeyDefinition, CreateHotkeyOptions, CreateHotkeyOptionsFor, CreateHotkeySequenceOptions, CreateHotkeyTarget, HotkeysProvider, HotkeysProviderOptions, HotkeysProviderProps, SolidHotkeyRecorder, UseCustomHotkeysResult, createAppHotkeyList, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations, createHotkeys, createKeyHold, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useHotkeysContext };
package/dist/index.js CHANGED
@@ -1,16 +1,14 @@
1
1
  import { HotkeysProvider, useDefaultHotkeysOptions, useHotkeysContext } from "./HotkeysProvider.js";
2
- import { createHotkey } from "./createHotkey.js";
3
2
  import { createHotkeys } from "./createHotkeys.js";
4
- import { createHeldKeys } from "./createHeldKeys.js";
5
- import { createHeldKeyCodes } from "./createHeldKeyCodes.js";
6
- import { createKeyHold } from "./createKeyHold.js";
7
- import { createHotkeySequence } from "./createHotkeySequence.js";
8
- import { createHotkeySequences } from "./createHotkeySequences.js";
3
+ import { createHotkey } from "./createHotkey.js";
9
4
  import { createHotkeyRecorder } from "./createHotkeyRecorder.js";
10
5
  import { createHotkeyRegistrations } from "./createHotkeyRegistrations.js";
11
- import { createHotkeySequenceRecorder } from "./createHotkeySequenceRecorder.js";
12
6
  import { createHotkeyHint } from "./createHotkeyHint.js";
7
+ import { createAppHotkeyList, useAppHotkeys, useCustomHotkeys } from "./appHotkeys.js";
8
+ import { createHeldKeys } from "./createHeldKeys.js";
9
+ import { createHeldKeyCodes } from "./createHeldKeyCodes.js";
10
+ import { createKeyHold } from "./createKeyHold.js";
13
11
 
14
12
  export * from "@tanstack/hotkeys"
15
13
 
16
- export { HotkeysProvider, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations, createHotkeySequence, createHotkeySequenceRecorder, createHotkeySequences, createHotkeys, createKeyHold, useDefaultHotkeysOptions, useHotkeysContext };
14
+ export { HotkeysProvider, createAppHotkeyList, createHeldKeyCodes, createHeldKeys, createHotkey, createHotkeyHint, createHotkeyRecorder, createHotkeyRegistrations, createHotkeys, createKeyHold, useAppHotkeys, useCustomHotkeys, useDefaultHotkeysOptions, useHotkeysContext };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/solid-hotkeys",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "SolidJS adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -36,8 +36,8 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@tanstack/hotkeys": "0.10.1",
40
- "@tanstack/solid-store": "^0.11.1"
39
+ "@tanstack/hotkeys": "0.12.0",
40
+ "@tanstack/solid-store": "^0.11.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@solidjs/testing-library": "^0.8.10",
@@ -1,53 +0,0 @@
1
- import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
2
- //#region src/createHotkeySequence.d.ts
3
- export interface CreateHotkeySequenceOptions extends Omit<SequenceOptions, 'target'> {
4
- /**
5
- * The DOM element to attach the event listener to.
6
- * Can be a direct DOM element, an accessor, or null. Defaults to document.
7
- */
8
- target?: HTMLElement | Document | Window | null;
9
- }
10
- /**
11
- * SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).
12
- *
13
- * This primitive allows you to register multi-key sequences like 'g g' or 'd d'
14
- * that trigger when the full sequence is pressed within a timeout.
15
- *
16
- * Each step may include modifiers. You can chain the same modifier across
17
- * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
18
- * Control, Alt, or Meta pressed alone) are ignored while matching—they do not
19
- * advance the sequence or reset progress.
20
- *
21
- * @param sequence - Array of hotkey strings that form the sequence (or accessor function)
22
- * @param callback - Function to call when the sequence is completed
23
- * @param options - Options for the sequence behavior (or accessor function)
24
- *
25
- * @example
26
- * ```tsx
27
- * function VimEditor() {
28
- * // 'g g' to go to top
29
- * createHotkeySequence(['G', 'G'], () => {
30
- * scrollToTop()
31
- * })
32
- *
33
- * // 'd d' to delete line
34
- * createHotkeySequence(['D', 'D'], () => {
35
- * deleteLine()
36
- * })
37
- *
38
- * // 'd i w' to delete inner word
39
- * createHotkeySequence(['D', 'I', 'W'], () => {
40
- * deleteInnerWord()
41
- * }, { timeout: 500 })
42
- *
43
- * // Same modifier on consecutive steps (bare Shift between chords is ignored)
44
- * createHotkeySequence(['Shift+R', 'Shift+T'], () => {
45
- * nextAction()
46
- * })
47
- *
48
- * return <div>...</div>
49
- * }
50
- * ```
51
- */
52
- export declare function createHotkeySequence(sequence: HotkeySequence | (() => HotkeySequence), callback: HotkeyCallback, options?: CreateHotkeySequenceOptions | (() => CreateHotkeySequenceOptions)): void;
53
- //#endregion
@@ -1,106 +0,0 @@
1
- import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
2
- import { formatHotkeySequence, getSequenceManager } from "@tanstack/hotkeys";
3
- import { createEffect, onCleanup, untrack } from "solid-js";
4
-
5
- //#region src/createHotkeySequence.ts
6
- /**
7
- * SolidJS primitive for registering a keyboard shortcut sequence (Vim-style).
8
- *
9
- * This primitive allows you to register multi-key sequences like 'g g' or 'd d'
10
- * that trigger when the full sequence is pressed within a timeout.
11
- *
12
- * Each step may include modifiers. You can chain the same modifier across
13
- * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
14
- * Control, Alt, or Meta pressed alone) are ignored while matching—they do not
15
- * advance the sequence or reset progress.
16
- *
17
- * @param sequence - Array of hotkey strings that form the sequence (or accessor function)
18
- * @param callback - Function to call when the sequence is completed
19
- * @param options - Options for the sequence behavior (or accessor function)
20
- *
21
- * @example
22
- * ```tsx
23
- * function VimEditor() {
24
- * // 'g g' to go to top
25
- * createHotkeySequence(['G', 'G'], () => {
26
- * scrollToTop()
27
- * })
28
- *
29
- * // 'd d' to delete line
30
- * createHotkeySequence(['D', 'D'], () => {
31
- * deleteLine()
32
- * })
33
- *
34
- * // 'd i w' to delete inner word
35
- * createHotkeySequence(['D', 'I', 'W'], () => {
36
- * deleteInnerWord()
37
- * }, { timeout: 500 })
38
- *
39
- * // Same modifier on consecutive steps (bare Shift between chords is ignored)
40
- * createHotkeySequence(['Shift+R', 'Shift+T'], () => {
41
- * nextAction()
42
- * })
43
- *
44
- * return <div>...</div>
45
- * }
46
- * ```
47
- */
48
- function createHotkeySequence(sequence, callback, options = {}) {
49
- const defaultOptions = useDefaultHotkeysOptions();
50
- const manager = getSequenceManager();
51
- let registration = null;
52
- let lastSequenceKey = null;
53
- let lastTarget = null;
54
- onCleanup(() => {
55
- if (registration?.isActive) {
56
- registration.unregister();
57
- registration = null;
58
- }
59
- lastSequenceKey = null;
60
- lastTarget = null;
61
- });
62
- createEffect(() => {
63
- const resolvedSequence = typeof sequence === "function" ? sequence() : sequence;
64
- const resolvedOptions = typeof options === "function" ? options() : options;
65
- const mergedOptions = {
66
- ...defaultOptions.hotkeySequence,
67
- ...resolvedOptions
68
- };
69
- const { target: _target, ...optionsWithoutTarget } = mergedOptions;
70
- const resolvedTarget = "target" in mergedOptions ? mergedOptions.target ?? null : typeof document !== "undefined" ? document : null;
71
- if (resolvedSequence.length === 0 || !resolvedTarget) {
72
- if (registration?.isActive) {
73
- registration.unregister();
74
- registration = null;
75
- }
76
- lastSequenceKey = null;
77
- lastTarget = null;
78
- return;
79
- }
80
- const sequenceKey = formatHotkeySequence(resolvedSequence);
81
- untrack(() => {
82
- if (registration?.isActive && lastSequenceKey === sequenceKey && lastTarget === resolvedTarget) {
83
- registration.callback = callback;
84
- registration.setOptions(optionsWithoutTarget);
85
- return;
86
- }
87
- if (registration?.isActive) {
88
- registration.unregister();
89
- registration = null;
90
- }
91
- registration = manager.register(resolvedSequence, callback, {
92
- ...mergedOptions,
93
- target: resolvedTarget
94
- });
95
- if (registration.isActive) {
96
- registration.callback = callback;
97
- registration.setOptions(optionsWithoutTarget);
98
- }
99
- lastSequenceKey = sequenceKey;
100
- lastTarget = resolvedTarget;
101
- });
102
- });
103
- }
104
-
105
- //#endregion
106
- export { createHotkeySequence };
@@ -1,16 +0,0 @@
1
- import { HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
2
- //#region src/createHotkeySequenceRecorder.d.ts
3
- export interface SolidHotkeySequenceRecorder {
4
- isRecording: () => boolean;
5
- steps: () => HotkeySequence;
6
- recordedSequence: () => HotkeySequence | null;
7
- startRecording: () => void;
8
- stopRecording: () => void;
9
- cancelRecording: () => void;
10
- commitRecording: () => void;
11
- }
12
- /**
13
- * SolidJS primitive for recording multi-chord sequences (Vim-style shortcuts).
14
- */
15
- export declare function createHotkeySequenceRecorder(options: HotkeySequenceRecorderOptions | (() => HotkeySequenceRecorderOptions)): SolidHotkeySequenceRecorder;
16
- //#endregion