@tanstack/preact-hotkeys 0.13.1 → 0.14.1

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 PreactHotkeyRecorder {
3
+ export interface PreactHotkeyRecorder<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
  * Preact 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): PreactHotkeyRecorder;
56
+ export declare function useHotkeyRecorder(options: HotkeyChordRecorderOptions): PreactHotkeyRecorder<HotkeyChord>;
57
+ export declare function useHotkeyRecorder(options: HotkeySequenceRecorderOptions): PreactHotkeyRecorder;
54
58
  //#endregion
@@ -4,44 +4,6 @@ import { useEffect, useRef } from "preact/hooks";
4
4
  import { useSelector } from "@tanstack/preact-store";
5
5
 
6
6
  //#region src/useHotkeyRecorder.ts
7
- /**
8
- * Preact 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,23 +1,30 @@
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
- * Preact hook that reactively reads all hotkey and sequence registrations
14
- * from the singleton managers.
4
+ * Preact hook that reactively reads every hotkey registration, chords and
5
+ * sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
6
+ *
7
+ * This hook does NOT require the HotkeysProvider. It re-renders when
8
+ * registrations change, including trigger counts and sequence progress.
9
+ *
10
+ * @returns All registrations, without callbacks
15
11
  *
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.
12
+ * @example
13
+ * ```tsx
14
+ * function ShortcutPalette() {
15
+ * const registrations = useHotkeyRegistrations()
19
16
  *
20
- * @returns Object with `hotkeys` and `sequences` arrays
17
+ * return (
18
+ * <ul>
19
+ * {registrations.map((reg) => (
20
+ * <li key={reg.id}>
21
+ * {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
22
+ * </li>
23
+ * ))}
24
+ * </ul>
25
+ * )
26
+ * }
27
+ * ```
21
28
  */
22
- export declare function useHotkeyRegistrations(): HotkeyRegistrationsResult;
29
+ export declare function useHotkeyRegistrations(): Array<HotkeyRegistrationView>;
23
30
  //#endregion
@@ -1,27 +1,35 @@
1
- import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
2
- import { useMemo } from "preact/hooks";
1
+ import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
3
2
  import { useSelector } from "@tanstack/preact-store";
4
3
 
5
4
  //#region src/useHotkeyRegistrations.ts
6
5
  /**
7
- * Preact hook that reactively reads all hotkey and sequence registrations
8
- * from the singleton managers.
6
+ * Preact hook that reactively reads every hotkey registration, chords and
7
+ * sequences, from the singleton HotkeyManager. Narrow each view with `kind`.
9
8
  *
10
- * This is a standalone hook that does NOT require the HotkeysProvider.
11
- * It subscribes to both HotkeyManager and SequenceManager stores and
12
- * 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.
13
11
  *
14
- * @returns Object with `hotkeys` and `sequences` arrays
12
+ * @returns All registrations, without callbacks
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * function ShortcutPalette() {
17
+ * const registrations = useHotkeyRegistrations()
18
+ *
19
+ * return (
20
+ * <ul>
21
+ * {registrations.map((reg) => (
22
+ * <li key={reg.id}>
23
+ * {reg.options.meta?.name}: {formatForDisplay(reg.hotkey)}
24
+ * </li>
25
+ * ))}
26
+ * </ul>
27
+ * )
28
+ * }
29
+ * ```
15
30
  */
16
31
  function useHotkeyRegistrations() {
17
- const hotkeyManager = getHotkeyManager();
18
- const sequenceManager = getSequenceManager();
19
- const hotkeyState = useSelector(hotkeyManager.registrations);
20
- const sequenceState = useSelector(sequenceManager.registrations);
21
- return {
22
- hotkeys: useMemo(() => Array.from(hotkeyState.values()).map(toHotkeyRegistrationView), [hotkeyState]),
23
- sequences: useMemo(() => Array.from(sequenceState.values()), [sequenceState])
24
- };
32
+ return useSelector(getHotkeyManager().registrations, (state) => Array.from(state.values()).map(toHotkeyRegistrationView));
25
33
  }
26
34
 
27
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
- * Preact hook for registering multiple keyboard hotkeys at once.
10
+ * Preact 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 "preact/hooks";
5
+ import { createHotkeyBindings, prepareHotkeyDefinitions } from "@tanstack/hotkeys/adapter";
5
6
 
6
7
  //#region src/useHotkeys.ts
7
8
  /**
8
- * Preact hook for registering multiple keyboard hotkeys at once.
9
+ * Preact 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 "preact/hooks";
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/preact-hotkeys",
3
- "version": "0.13.1",
3
+ "version": "0.14.1",
4
4
  "description": "Preact adapter for TanStack Hotkeys",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -33,7 +33,7 @@
33
33
  "dist"
34
34
  ],
35
35
  "dependencies": {
36
- "@tanstack/hotkeys": "0.11.0",
36
+ "@tanstack/hotkeys": "0.12.1",
37
37
  "@tanstack/preact-store": "^0.13.5"
38
38
  },
39
39
  "devDependencies": {
@@ -1,56 +0,0 @@
1
- import { HotkeyCallback, HotkeySequence, SequenceOptions } from "@tanstack/hotkeys";
2
- import { RefObject } from "preact";
3
- //#region src/useHotkeySequence.d.ts
4
- export interface UseHotkeySequenceOptions extends Omit<SequenceOptions, 'target'> {
5
- /**
6
- * The DOM element to attach the event listener to.
7
- * Can be a Preact ref, direct DOM element, or null.
8
- * Defaults to document.
9
- */
10
- target?: RefObject<HTMLElement | null> | HTMLElement | Document | Window | null;
11
- }
12
- /**
13
- * Preact hook for registering a keyboard shortcut sequence (Vim-style).
14
- *
15
- * This hook allows you to register multi-key sequences like 'g g' or 'd d'
16
- * that trigger when the full sequence is pressed within a timeout.
17
- *
18
- * Each step may include modifiers. You can chain the same modifier across
19
- * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,
20
- * Control, Alt, or Meta pressed alone) are ignored while matching—they do not
21
- * advance the sequence or reset progress.
22
- *
23
- * @param sequence - Array of hotkey strings that form the sequence
24
- * @param callback - Function to call when the sequence is completed
25
- * @param options - Options for the sequence behavior. `enabled: false` keeps the registration (visible in devtools)
26
- * and only suppresses firing; the hook updates the existing handle instead of unregistering.
27
- *
28
- * @example
29
- * ```tsx
30
- * function VimEditor() {
31
- * // 'g g' to go to top
32
- * useHotkeySequence(['G', 'G'], () => {
33
- * scrollToTop()
34
- * })
35
- *
36
- * // 'd d' to delete line
37
- * useHotkeySequence(['D', 'D'], () => {
38
- * deleteLine()
39
- * })
40
- *
41
- * // 'd i w' to delete inner word
42
- * useHotkeySequence(['D', 'I', 'W'], () => {
43
- * deleteInnerWord()
44
- * }, { timeout: 500 })
45
- *
46
- * // Same modifier on consecutive steps (bare Shift between chords is ignored)
47
- * useHotkeySequence(['Shift+R', 'Shift+T'], () => {
48
- * nextAction()
49
- * })
50
- *
51
- * return <div>...</div>
52
- * }
53
- * ```
54
- */
55
- export declare function useHotkeySequence(sequence: HotkeySequence, callback: HotkeyCallback, options?: UseHotkeySequenceOptions): void;
56
- //#endregion