@tanstack/react-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,54 +1,58 @@
1
- import { Hotkey, HotkeyRecorderOptions } from "@tanstack/hotkeys";
1
+ import { Hotkey, HotkeyChord, HotkeyChordRecorderOptions, HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
2
2
  //#region src/useHotkeyRecorder.d.ts
3
- export interface ReactHotkeyRecorder {
3
+ export interface ReactHotkeyRecorder<THotkey extends Hotkey = Hotkey> {
4
4
  /** Whether recording is currently active */
5
5
  isRecording: boolean;
6
- /** The currently recorded hotkey (for live preview) */
7
- recordedHotkey: Hotkey | null;
6
+ /** Chords recorded so far in a multi-step recording (for live preview) */
7
+ steps: HotkeySequence;
8
+ /** The last committed hotkey */
9
+ recordedHotkey: THotkey | null;
8
10
  /** Start recording a new hotkey */
9
11
  startRecording: () => void;
10
- /** Stop recording (same as cancel) */
12
+ /** Stop recording without calling onCancel */
11
13
  stopRecording: () => void;
12
- /** Cancel recording without saving */
14
+ /** Cancel recording and call onCancel */
13
15
  cancelRecording: () => void;
16
+ /** Commit the recorded steps now (multi-step recordings) */
17
+ commitRecording: () => void;
14
18
  }
15
19
  /**
16
20
  * React hook for recording keyboard shortcuts.
17
21
  *
18
- * This hook provides a thin wrapper around the framework-agnostic `HotkeyRecorder`
19
- * class, managing all the complexity of capturing keyboard events, converting them
20
- * to hotkey strings, and handling edge cases like Escape to cancel or Backspace/Delete
21
- * to clear.
22
+ * With the default `maxSteps: 1`, the first valid chord is recorded and
23
+ * `onRecord` receives a chord such as `'Mod+S'`. With a larger `maxSteps`, each
24
+ * chord adds a step; the recording commits on Enter (unless
25
+ * `commitKeys: 'none'`), after `idleTimeoutMs`, or when it reaches `maxSteps`,
26
+ * and `onRecord` receives a chord (one step) or a sequence.
27
+ *
28
+ * Escape cancels. Backspace/Delete removes the last step, or calls `onClear`
29
+ * when nothing is recorded.
22
30
  *
23
31
  * @param options - Configuration options for the recorder
24
- * @returns An object with recording state and control functions
32
+ * @returns The recorder state and controls
25
33
  *
26
34
  * @example
27
35
  * ```tsx
28
- * function ShortcutSettings() {
36
+ * function ShortcutInput() {
29
37
  * const [shortcut, setShortcut] = useState<Hotkey>('Mod+S')
30
- *
31
- * const recorder = useHotkeyRecorder({
32
- * onRecord: (hotkey) => {
33
- * setShortcut(hotkey)
34
- * },
35
- * onCancel: () => {
36
- * console.log('Recording cancelled')
37
- * },
38
- * })
38
+ * const recorder = useHotkeyRecorder({ onRecord: setShortcut })
39
39
  *
40
40
  * return (
41
- * <div>
42
- * <button onClick={recorder.startRecording}>
43
- * {recorder.isRecording ? 'Recording...' : 'Edit Shortcut'}
44
- * </button>
45
- * {recorder.recordedHotkey && (
46
- * <div>Recording: {recorder.recordedHotkey}</div>
47
- * )}
48
- * </div>
41
+ * <button onClick={recorder.startRecording}>
42
+ * {recorder.isRecording ? 'Press keys…' : formatForDisplay(shortcut)}
43
+ * </button>
49
44
  * )
50
45
  * }
51
46
  * ```
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * const recorder = useHotkeyRecorder({
51
+ * maxSteps: 3,
52
+ * onRecord: (hotkey) => setShortcut(hotkey), // 'Mod+S' or ['G', 'H']
53
+ * })
54
+ * ```
52
55
  */
53
- export declare function useHotkeyRecorder(options: HotkeyRecorderOptions): ReactHotkeyRecorder;
56
+ export declare function useHotkeyRecorder(options: HotkeyChordRecorderOptions): ReactHotkeyRecorder<HotkeyChord>;
57
+ export declare function useHotkeyRecorder(options: HotkeySequenceRecorderOptions): ReactHotkeyRecorder;
54
58
  //#endregion
@@ -4,44 +4,6 @@ import { useEffect, useRef } from "react";
4
4
  import { useSelector } from "@tanstack/react-store";
5
5
 
6
6
  //#region src/useHotkeyRecorder.ts
7
- /**
8
- * React hook for recording keyboard shortcuts.
9
- *
10
- * This hook 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
- * @param options - Configuration options for the recorder
16
- * @returns An object with recording state and control functions
17
- *
18
- * @example
19
- * ```tsx
20
- * function ShortcutSettings() {
21
- * const [shortcut, setShortcut] = useState<Hotkey>('Mod+S')
22
- *
23
- * const recorder = useHotkeyRecorder({
24
- * onRecord: (hotkey) => {
25
- * setShortcut(hotkey)
26
- * },
27
- * onCancel: () => {
28
- * console.log('Recording cancelled')
29
- * },
30
- * })
31
- *
32
- * return (
33
- * <div>
34
- * <button onClick={recorder.startRecording}>
35
- * {recorder.isRecording ? 'Recording...' : 'Edit Shortcut'}
36
- * </button>
37
- * {recorder.recordedHotkey && (
38
- * <div>Recording: {recorder.recordedHotkey}</div>
39
- * )}
40
- * </div>
41
- * )
42
- * }
43
- * ```
44
- */
45
7
  function useHotkeyRecorder(options) {
46
8
  const mergedOptions = {
47
9
  ...useDefaultHotkeysOptions().hotkeyRecorder,
@@ -50,8 +12,10 @@ function useHotkeyRecorder(options) {
50
12
  const recorderRef = useRef(null);
51
13
  if (!recorderRef.current) recorderRef.current = new HotkeyRecorder(mergedOptions);
52
14
  recorderRef.current.setOptions(mergedOptions);
53
- const isRecording = useSelector(recorderRef.current.store, (state) => state.isRecording);
54
- const recordedHotkey = useSelector(recorderRef.current.store, (state) => state.recordedHotkey);
15
+ const recorder = recorderRef.current;
16
+ const isRecording = useSelector(recorder.store, (state) => state.isRecording);
17
+ const steps = useSelector(recorder.store, (state) => state.steps);
18
+ const recordedHotkey = useSelector(recorder.store, (state) => state.recordedHotkey);
55
19
  useEffect(() => {
56
20
  return () => {
57
21
  recorderRef.current?.destroy();
@@ -59,10 +23,12 @@ function useHotkeyRecorder(options) {
59
23
  }, []);
60
24
  return {
61
25
  isRecording,
26
+ steps,
62
27
  recordedHotkey,
63
28
  startRecording: () => recorderRef.current?.start(),
64
29
  stopRecording: () => recorderRef.current?.stop(),
65
- cancelRecording: () => recorderRef.current?.cancel()
30
+ cancelRecording: () => recorderRef.current?.cancel(),
31
+ commitRecording: () => recorderRef.current?.commit()
66
32
  };
67
33
  }
68
34
 
@@ -1,39 +1,24 @@
1
- import { HotkeyRegistrationView, SequenceRegistrationView } from "@tanstack/hotkeys";
1
+ import { HotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  //#region src/useHotkeyRegistrations.d.ts
3
3
  /**
4
- * Return type for useHotkeyRegistrations.
5
- */
6
- export interface HotkeyRegistrationsResult {
7
- /** All registered hotkeys (public view, no callbacks) */
8
- hotkeys: Array<HotkeyRegistrationView>;
9
- /** All registered sequences */
10
- sequences: Array<SequenceRegistrationView>;
11
- }
12
- /**
13
- * React hook that reactively reads all hotkey and sequence registrations
14
- * from the singleton managers.
4
+ * React hook that reactively reads every hotkey registration, chords and
5
+ * sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
15
6
  *
16
- * This is a standalone hook that does NOT require the HotkeysProvider.
17
- * It subscribes to both HotkeyManager and SequenceManager stores and
18
- * re-renders when registrations change.
7
+ * This hook does NOT require the HotkeysProvider. It re-renders when
8
+ * registrations change, including trigger counts and sequence progress.
19
9
  *
20
- * @returns Object with `hotkeys` and `sequences` arrays
10
+ * @returns All registrations, without callbacks
21
11
  *
22
12
  * @example
23
13
  * ```tsx
24
14
  * function ShortcutPalette() {
25
- * const { hotkeys, sequences } = useHotkeyRegistrations()
15
+ * const registrations = useHotkeyRegistrations()
26
16
  *
27
17
  * return (
28
18
  * <ul>
29
- * {hotkeys.map((reg) => (
30
- * <li key={reg.id}>
31
- * {reg.options.meta?.name ?? reg.hotkey}
32
- * </li>
33
- * ))}
34
- * {sequences.map((reg) => (
19
+ * {registrations.map((reg) => (
35
20
  * <li key={reg.id}>
36
- * {reg.options.meta?.name ?? reg.sequence.join(' ')}
21
+ * {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
37
22
  * </li>
38
23
  * ))}
39
24
  * </ul>
@@ -41,5 +26,5 @@ export interface HotkeyRegistrationsResult {
41
26
  * }
42
27
  * ```
43
28
  */
44
- export declare function useHotkeyRegistrations(): HotkeyRegistrationsResult;
29
+ export declare function useHotkeyRegistrations(): Array<HotkeyRegistrationView>;
45
30
  //#endregion
@@ -1,32 +1,26 @@
1
- import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
1
+ import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  import { useSelector } from "@tanstack/react-store";
3
3
 
4
4
  //#region src/useHotkeyRegistrations.ts
5
5
  /**
6
- * React hook that reactively reads all hotkey and sequence registrations
7
- * from the singleton managers.
6
+ * React hook 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 hook that does NOT require the HotkeysProvider.
10
- * It subscribes to both HotkeyManager and SequenceManager stores and
11
- * re-renders when registrations change.
9
+ * This hook does NOT require the HotkeysProvider. It re-renders when
10
+ * registrations change, including trigger counts and sequence progress.
12
11
  *
13
- * @returns Object with `hotkeys` and `sequences` arrays
12
+ * @returns All registrations, without callbacks
14
13
  *
15
14
  * @example
16
15
  * ```tsx
17
16
  * function ShortcutPalette() {
18
- * const { hotkeys, sequences } = useHotkeyRegistrations()
17
+ * const registrations = useHotkeyRegistrations()
19
18
  *
20
19
  * return (
21
20
  * <ul>
22
- * {hotkeys.map((reg) => (
21
+ * {registrations.map((reg) => (
23
22
  * <li key={reg.id}>
24
- * {reg.options.meta?.name ?? reg.hotkey}
25
- * </li>
26
- * ))}
27
- * {sequences.map((reg) => (
28
- * <li key={reg.id}>
29
- * {reg.options.meta?.name ?? reg.sequence.join(' ')}
23
+ * {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
30
24
  * </li>
31
25
  * ))}
32
26
  * </ul>
@@ -35,12 +29,7 @@ import { useSelector } from "@tanstack/react-store";
35
29
  * ```
36
30
  */
37
31
  function useHotkeyRegistrations() {
38
- const hotkeyManager = getHotkeyManager();
39
- const sequenceManager = getSequenceManager();
40
- return {
41
- hotkeys: useSelector(hotkeyManager.registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView)),
42
- sequences: useSelector(sequenceManager.registrations, (state) => Array.from(state.values()))
43
- };
32
+ return useSelector(getHotkeyManager().registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView));
44
33
  }
45
34
 
46
35
  //#endregion
@@ -1,41 +1,32 @@
1
- import { UseHotkeyOptions } from "./useHotkey.js";
2
- import { HotkeyCallback, RegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { UseHotkeyCommonOptions, UseHotkeyTarget } from "./useHotkey.js";
2
+ import { AdapterHotkeyDefinition } from "@tanstack/hotkeys/adapter";
3
3
  //#region src/useHotkeys.d.ts
4
4
  /**
5
- * A single hotkey definition for use with `useHotkeys`.
5
+ * One hotkey for `useHotkeys`. Chords accept `requireReset`; sequences accept
6
+ * `timeout`; hotkeys of unknown kind accept only shared options.
6
7
  */
7
- export interface UseHotkeyDefinition {
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?: UseHotkeyOptions;
14
- }
8
+ export type UseHotkeyDefinition = AdapterHotkeyDefinition<UseHotkeyTarget>;
15
9
  /**
16
- * React hook for registering multiple keyboard hotkeys at once.
10
+ * React hook for registering several hotkeys at once: chords, sequences, and
11
+ * app hotkeys. Accepts a dynamic array, so it is safe to use with
12
+ * variable-length lists without violating the rules of hooks.
17
13
  *
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 without violating the rules of hooks.
14
+ * Options merge in this order: provider defaults, app hotkey declaration,
15
+ * commonOptions, per-hotkey options. Each option applies only to the kinds
16
+ * that support it. Callbacks and options sync on every render.
21
17
  *
22
- * Options are merged in this order:
23
- * HotkeysProvider defaults < commonOptions < per-definition options
18
+ * Per-row `enabled: false` keeps that registration (visible in devtools) and
19
+ * only suppresses firing. Unbound app hotkeys and empty sequences register nothing.
24
20
  *
25
- * Callbacks and options are synced on every render to avoid stale closures.
26
- *
27
- * @param hotkeys - Array of hotkey definitions to register
28
- * @param commonOptions - Shared options applied to all hotkeys (overridden by per-definition options).
29
- * Per-row `enabled: false` still registers that hotkey: `HotkeyManager` suppresses execution only (the row
30
- * stays in the store and appears in TanStack Hotkeys devtools). Toggling `enabled` updates the existing handle
31
- * via `setOptions` (no unregister/re-register churn).
21
+ * @param hotkeys - Hotkeys to register
22
+ * @param commonOptions - Options applied to every hotkey (overridden by per-hotkey options)
32
23
  *
33
24
  * @example
34
25
  * ```tsx
35
26
  * function Editor() {
36
27
  * useHotkeys([
37
28
  * { hotkey: 'Mod+S', callback: () => save() },
38
- * { hotkey: 'Mod+Z', callback: () => undo() },
29
+ * { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
39
30
  * { hotkey: 'Escape', callback: () => close() },
40
31
  * ])
41
32
  * }
@@ -56,5 +47,5 @@ export interface UseHotkeyDefinition {
56
47
  * }
57
48
  * ```
58
49
  */
59
- export declare function useHotkeys(hotkeys: Array<UseHotkeyDefinition>, commonOptions?: UseHotkeyOptions): void;
50
+ export declare function useHotkeys(hotkeys: ReadonlyArray<UseHotkeyDefinition>, commonOptions?: UseHotkeyCommonOptions): void;
60
51
  //#endregion
@@ -1,33 +1,31 @@
1
- import { useDefaultHotkeysOptions } from "./HotkeysProvider.js";
2
- import { isRef } from "./utils.js";
3
- import { detectPlatform, getHotkeyManager, normalizeRegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { useHotkeysContext } from "./HotkeysProvider.js";
2
+ import { useAppHotkeysStoreState } from "./_appHotkeysStore.js";
3
+ import { resolveTarget } from "./utils.js";
4
4
  import { useEffect, useRef } from "react";
5
+ import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkeys/adapter";
5
6
 
6
7
  //#region src/useHotkeys.ts
7
8
  /**
8
- * React hook for registering multiple keyboard hotkeys at once.
9
+ * React hook for registering several hotkeys at once: chords, sequences, and
10
+ * app hotkeys. Accepts a dynamic array, so it is safe to use with
11
+ * variable-length lists without violating the rules of hooks.
9
12
  *
10
- * Uses the singleton HotkeyManager for efficient event handling.
11
- * Accepts a dynamic array of hotkey definitions, making it safe to use
12
- * with variable-length lists without violating the rules of hooks.
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. Callbacks and options sync on every render.
13
16
  *
14
- * Options are merged in this order:
15
- * HotkeysProvider defaults < commonOptions < per-definition options
17
+ * Per-row `enabled: false` keeps that registration (visible in devtools) and
18
+ * only suppresses firing. Unbound app hotkeys and empty sequences register nothing.
16
19
  *
17
- * Callbacks and options are synced on every render to avoid stale closures.
18
- *
19
- * @param hotkeys - Array of hotkey definitions to register
20
- * @param commonOptions - Shared options applied to all hotkeys (overridden by per-definition options).
21
- * Per-row `enabled: false` still registers that hotkey: `HotkeyManager` suppresses execution only (the row
22
- * stays in the store and appears in TanStack Hotkeys devtools). Toggling `enabled` updates the existing handle
23
- * via `setOptions` (no unregister/re-register churn).
20
+ * @param hotkeys - Hotkeys to register
21
+ * @param commonOptions - Options applied to every hotkey (overridden by per-hotkey options)
24
22
  *
25
23
  * @example
26
24
  * ```tsx
27
25
  * function Editor() {
28
26
  * useHotkeys([
29
27
  * { hotkey: 'Mod+S', callback: () => save() },
30
- * { hotkey: 'Mod+Z', callback: () => undo() },
28
+ * { hotkey: ['G', 'G'], callback: () => scrollToTop(), options: { timeout: 500 } },
31
29
  * { hotkey: 'Escape', callback: () => close() },
32
30
  * ])
33
31
  * }
@@ -49,72 +47,33 @@ import { useEffect, useRef } from "react";
49
47
  * ```
50
48
  */
51
49
  function useHotkeys(hotkeys, commonOptions = {}) {
52
- const defaultOptions = useDefaultHotkeysOptions().hotkey;
53
- const manager = getHotkeyManager();
54
- const platform = commonOptions.platform ?? defaultOptions?.platform ?? detectPlatform();
55
- const registrationsRef = useRef(/* @__PURE__ */ new Map());
56
- const hotkeysRef = useRef(hotkeys);
57
- const hotkeyStringsRef = useRef([]);
58
- const commonOptionsRef = useRef(commonOptions);
59
- const defaultOptionsRef = useRef(defaultOptions);
60
- const managerRef = useRef(manager);
61
- const hotkeyStrings = hotkeys.map((def) => normalizeRegisterableHotkey(def.hotkey, platform));
62
- hotkeysRef.current = hotkeys;
63
- hotkeyStringsRef.current = hotkeyStrings;
64
- commonOptionsRef.current = commonOptions;
65
- defaultOptionsRef.current = defaultOptions;
66
- managerRef.current = manager;
50
+ const defaults = useHotkeysContext()?.defaultOptions.hotkey;
51
+ const appHotkeysStore = useAppHotkeysStoreState();
52
+ const latestRef = useRef({
53
+ hotkeys,
54
+ commonOptions,
55
+ defaults,
56
+ appHotkeysStore
57
+ });
58
+ latestRef.current = {
59
+ hotkeys,
60
+ commonOptions,
61
+ defaults,
62
+ appHotkeysStore
63
+ };
64
+ const bindingsRef = useRef(null);
67
65
  useEffect(() => {
68
- const prevRegistrations = registrationsRef.current;
69
- const nextRegistrations = /* @__PURE__ */ new Map();
70
- const rows = [];
71
- for (let i = 0; i < hotkeysRef.current.length; i++) {
72
- const def = hotkeysRef.current[i];
73
- const hotkeyStr = hotkeyStringsRef.current[i];
74
- const mergedOptions = {
75
- ...defaultOptionsRef.current,
76
- ...commonOptionsRef.current,
77
- ...def.options
78
- };
79
- const resolvedTarget = isRef(mergedOptions.target) ? mergedOptions.target.current : mergedOptions.target ?? (typeof document !== "undefined" ? document : null);
80
- if (!resolvedTarget) continue;
81
- const registrationKey = `${i}:${hotkeyStr}`;
82
- rows.push({
83
- registrationKey,
84
- def,
85
- hotkeyStr,
86
- mergedOptions,
87
- resolvedTarget
88
- });
89
- }
90
- const nextKeys = new Set(rows.map((r) => r.registrationKey));
91
- for (const [key, record] of prevRegistrations) if (!nextKeys.has(key) && record.handle.isActive) record.handle.unregister();
92
- for (const row of rows) {
93
- const { registrationKey, def, hotkeyStr, mergedOptions, resolvedTarget } = row;
94
- const existing = prevRegistrations.get(registrationKey);
95
- if (existing?.handle.isActive && existing.target === resolvedTarget) {
96
- existing.handle.callback = def.callback;
97
- const { target: _target, ...optionsWithoutTarget } = mergedOptions;
98
- existing.handle.setOptions(optionsWithoutTarget);
99
- nextRegistrations.set(registrationKey, existing);
100
- continue;
101
- }
102
- if (existing?.handle.isActive) existing.handle.unregister();
103
- const handle = managerRef.current.register(hotkeyStr, def.callback, {
104
- ...mergedOptions,
105
- target: resolvedTarget
106
- });
107
- nextRegistrations.set(registrationKey, {
108
- handle,
109
- target: resolvedTarget
110
- });
111
- }
112
- registrationsRef.current = nextRegistrations;
66
+ bindingsRef.current ??= createHotkeyBindings();
67
+ const { hotkeys, ...context } = latestRef.current;
68
+ bindingsRef.current.update(prepareHotkeyDefinitions(hotkeys, {
69
+ ...context,
70
+ resolveTarget
71
+ }));
113
72
  });
114
73
  useEffect(() => {
115
74
  return () => {
116
- for (const { handle } of registrationsRef.current.values()) if (handle.isActive) handle.unregister();
117
- registrationsRef.current = /* @__PURE__ */ new Map();
75
+ bindingsRef.current?.destroy();
76
+ bindingsRef.current = null;
118
77
  };
119
78
  }, []);
120
79
  }
package/dist/utils.js CHANGED
@@ -5,6 +5,13 @@
5
5
  function isRef(value) {
6
6
  return value !== null && typeof value === "object" && "current" in value;
7
7
  }
8
+ /**
9
+ * Resolves a hook target. A ref that is still null skips registration;
10
+ * a null target means document.
11
+ */
12
+ function resolveTarget(target) {
13
+ return isRef(target) ? target.current : target ?? void 0;
14
+ }
8
15
 
9
16
  //#endregion
10
- export { isRef };
17
+ export { resolveTarget };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-hotkeys",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "React adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -33,8 +33,8 @@
33
33
  "dist"
34
34
  ],
35
35
  "dependencies": {
36
- "@tanstack/hotkeys": "0.10.1",
37
- "@tanstack/react-store": "^0.11.1"
36
+ "@tanstack/hotkeys": "0.12.0",
37
+ "@tanstack/react-store": "^0.11.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@eslint-react/eslint-plugin": "^4.2.3",
@@ -1,55 +0,0 @@
1
- import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
2
- //#region src/useHotkeySequence.d.ts
3
- export interface UseHotkeySequenceOptions extends Omit<SequenceOptions, 'target'> {
4
- /**
5
- * The DOM element to attach the event listener to.
6
- * Can be a React ref, direct DOM element, or null.
7
- * Defaults to document.
8
- */
9
- target?: React.RefObject<HTMLElement | null> | HTMLElement | Document | Window | null;
10
- }
11
- /**
12
- * React hook for registering a keyboard shortcut sequence (Vim-style).
13
- *
14
- * This hook allows you to register multi-key sequences like 'g g' or 'd d'
15
- * that trigger when the full sequence is pressed within a timeout.
16
- *
17
- * Each step may include modifiers. You can chain the same modifier across
18
- * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
19
- * Control, Alt, or Meta pressed alone) are ignored while matching—they do not
20
- * advance the sequence or reset progress.
21
- *
22
- * @param sequence - Array of hotkey strings that form the sequence
23
- * @param callback - Function to call when the sequence is completed
24
- * @param options - Options for the sequence behavior. `enabled: false` keeps the registration (visible in devtools)
25
- * and only suppresses firing; the hook updates the existing handle instead of unregistering.
26
- *
27
- * @example
28
- * ```tsx
29
- * function VimEditor() {
30
- * // 'g g' to go to top
31
- * useHotkeySequence(['G', 'G'], () => {
32
- * scrollToTop()
33
- * })
34
- *
35
- * // 'd d' to delete line
36
- * useHotkeySequence(['D', 'D'], () => {
37
- * deleteLine()
38
- * })
39
- *
40
- * // 'd i w' to delete inner word
41
- * useHotkeySequence(['D', 'I', 'W'], () => {
42
- * deleteInnerWord()
43
- * }, { timeout: 500 })
44
- *
45
- * // Same modifier on consecutive steps (bare Shift between chords is ignored)
46
- * useHotkeySequence(['Shift+R', 'Shift+T'], () => {
47
- * nextAction()
48
- * })
49
- *
50
- * return <div>...</div>
51
- * }
52
- * ```
53
- */
54
- export declare function useHotkeySequence(sequence: HotkeySequence, callback: HotkeyCallback, options?: UseHotkeySequenceOptions): void;
55
- //#endregion