@tanstack/vue-hotkeys 0.6.0 → 0.8.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.
package/README.md CHANGED
@@ -37,7 +37,7 @@
37
37
  # TanStack Hotkeys
38
38
 
39
39
  > [!NOTE]
40
- > TanStack Hotkeys is pre-alpha (prototyping phase). We are actively developing the library and are open to feedback and contributions.
40
+ > TanStack Hotkeys is alpha. We are actively developing the library and are open to feedback and contributions.
41
41
 
42
42
  Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objects, a cross-platform `Mod` key, a singleton Hotkey Manager, and utilities for cheatsheet UIs—built to stay SSR-friendly.
43
43
 
package/dist/index.cjs CHANGED
@@ -7,6 +7,7 @@ const require_useHeldKeys = require('./useHeldKeys.cjs');
7
7
  const require_useHeldKeyCodes = require('./useHeldKeyCodes.cjs');
8
8
  const require_useKeyHold = require('./useKeyHold.cjs');
9
9
  const require_useHotkeySequence = require('./useHotkeySequence.cjs');
10
+ const require_useHotkeySequences = require('./useHotkeySequences.cjs');
10
11
  const require_useHotkeyRecorder = require('./useHotkeyRecorder.cjs');
11
12
  const require_useHotkeySequenceRecorder = require('./useHotkeySequenceRecorder.cjs');
12
13
 
@@ -19,6 +20,7 @@ exports.useHotkey = require_useHotkey.useHotkey;
19
20
  exports.useHotkeyRecorder = require_useHotkeyRecorder.useHotkeyRecorder;
20
21
  exports.useHotkeySequence = require_useHotkeySequence.useHotkeySequence;
21
22
  exports.useHotkeySequenceRecorder = require_useHotkeySequenceRecorder.useHotkeySequenceRecorder;
23
+ exports.useHotkeySequences = require_useHotkeySequences.useHotkeySequences;
22
24
  exports.useHotkeys = require_useHotkeys.useHotkeys;
23
25
  exports.useHotkeysContext = require_HotkeysProviderContext.useHotkeysContext;
24
26
  exports.useKeyHold = require_useKeyHold.useKeyHold;
package/dist/index.d.cts CHANGED
@@ -6,7 +6,8 @@ import { UseHotkeyDefinition, useHotkeys } from "./useHotkeys.cjs";
6
6
  import { useHeldKeys } from "./useHeldKeys.cjs";
7
7
  import { useHeldKeyCodes } from "./useHeldKeyCodes.cjs";
8
8
  import { useKeyHold } from "./useKeyHold.cjs";
9
+ import { UseHotkeySequenceDefinition, useHotkeySequences } from "./useHotkeySequences.cjs";
9
10
  import { VueHotkeyRecorder, useHotkeyRecorder } from "./useHotkeyRecorder.cjs";
10
11
  import { VueHotkeySequenceRecorder, useHotkeySequenceRecorder } from "./useHotkeySequenceRecorder.cjs";
11
12
  export * from "@tanstack/hotkeys";
12
- export { HotkeysProvider, HotkeysProviderOptions, UseHotkeyDefinition, UseHotkeyOptions, UseHotkeySequenceOptions, VueHotkeyRecorder, VueHotkeySequenceRecorder, provideHotkeysContext, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyRecorder, useHotkeySequence, useHotkeySequenceRecorder, useHotkeys, useHotkeysContext, useKeyHold };
13
+ export { HotkeysProvider, HotkeysProviderOptions, UseHotkeyDefinition, UseHotkeyOptions, UseHotkeySequenceDefinition, UseHotkeySequenceOptions, VueHotkeyRecorder, VueHotkeySequenceRecorder, provideHotkeysContext, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyRecorder, useHotkeySequence, useHotkeySequenceRecorder, useHotkeySequences, useHotkeys, useHotkeysContext, useKeyHold };
package/dist/index.d.ts CHANGED
@@ -6,7 +6,8 @@ import { UseHotkeyDefinition, useHotkeys } from "./useHotkeys.js";
6
6
  import { useHeldKeys } from "./useHeldKeys.js";
7
7
  import { useHeldKeyCodes } from "./useHeldKeyCodes.js";
8
8
  import { useKeyHold } from "./useKeyHold.js";
9
+ import { UseHotkeySequenceDefinition, useHotkeySequences } from "./useHotkeySequences.js";
9
10
  import { VueHotkeyRecorder, useHotkeyRecorder } from "./useHotkeyRecorder.js";
10
11
  import { VueHotkeySequenceRecorder, useHotkeySequenceRecorder } from "./useHotkeySequenceRecorder.js";
11
12
  export * from "@tanstack/hotkeys";
12
- export { HotkeysProvider, HotkeysProviderOptions, UseHotkeyDefinition, UseHotkeyOptions, UseHotkeySequenceOptions, VueHotkeyRecorder, VueHotkeySequenceRecorder, provideHotkeysContext, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyRecorder, useHotkeySequence, useHotkeySequenceRecorder, useHotkeys, useHotkeysContext, useKeyHold };
13
+ export { HotkeysProvider, HotkeysProviderOptions, UseHotkeyDefinition, UseHotkeyOptions, UseHotkeySequenceDefinition, UseHotkeySequenceOptions, VueHotkeyRecorder, VueHotkeySequenceRecorder, provideHotkeysContext, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyRecorder, useHotkeySequence, useHotkeySequenceRecorder, useHotkeySequences, useHotkeys, useHotkeysContext, useKeyHold };
package/dist/index.js CHANGED
@@ -6,9 +6,10 @@ import { useHeldKeys } from "./useHeldKeys.js";
6
6
  import { useHeldKeyCodes } from "./useHeldKeyCodes.js";
7
7
  import { useKeyHold } from "./useKeyHold.js";
8
8
  import { useHotkeySequence } from "./useHotkeySequence.js";
9
+ import { useHotkeySequences } from "./useHotkeySequences.js";
9
10
  import { useHotkeyRecorder } from "./useHotkeyRecorder.js";
10
11
  import { useHotkeySequenceRecorder } from "./useHotkeySequenceRecorder.js";
11
12
 
12
13
  export * from "@tanstack/hotkeys"
13
14
 
14
- export { HotkeysProvider, provideHotkeysContext, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyRecorder, useHotkeySequence, useHotkeySequenceRecorder, useHotkeys, useHotkeysContext, useKeyHold };
15
+ export { HotkeysProvider, provideHotkeysContext, useDefaultHotkeysOptions, useHeldKeyCodes, useHeldKeys, useHotkey, useHotkeyRecorder, useHotkeySequence, useHotkeySequenceRecorder, useHotkeySequences, useHotkeys, useHotkeysContext, useKeyHold };
@@ -71,6 +71,8 @@ function useHotkey(hotkey, callback, options = {}) {
71
71
  const defaultOptions = require_HotkeysProviderContext.useDefaultHotkeysOptions();
72
72
  const manager = (0, _tanstack_hotkeys.getHotkeyManager)();
73
73
  let registration = null;
74
+ let lastHotkeyString = null;
75
+ let lastTarget = null;
74
76
  const stopWatcher = (0, vue.watch)(() => {
75
77
  const resolvedHotkey = resolveMaybeRefOrGetter(hotkey);
76
78
  const resolvedOptions = resolveMaybeRefOrGetter(options);
@@ -85,19 +87,31 @@ function useHotkey(hotkey, callback, options = {}) {
85
87
  resolvedTarget: mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target)
86
88
  };
87
89
  }, ({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {
88
- const platform = mergedOptions.platform ?? (0, _tanstack_hotkeys.detectPlatform)();
89
- const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : (0, _tanstack_hotkeys.formatHotkey)((0, _tanstack_hotkeys.rawHotkeyToParsedHotkey)(resolvedHotkey, platform));
90
- const finalTarget = resolvedTarget ?? (typeof document !== "undefined" ? document : null);
91
- if (!finalTarget) return;
92
- if (registration?.isActive) {
93
- registration.unregister();
94
- registration = null;
90
+ const hotkeyString = (0, _tanstack_hotkeys.normalizeRegisterableHotkey)(resolvedHotkey, mergedOptions.platform ?? (0, _tanstack_hotkeys.detectPlatform)());
91
+ const finalTarget = resolvedTarget === void 0 ? typeof document !== "undefined" ? document : null : resolvedTarget;
92
+ if (!finalTarget) {
93
+ if (registration?.isActive) {
94
+ registration.unregister();
95
+ registration = null;
96
+ }
97
+ lastHotkeyString = null;
98
+ lastTarget = null;
99
+ return;
95
100
  }
96
101
  const { target: _target, enabled: _enabled, ...restOptions } = mergedOptions;
97
102
  const optionsWithoutTarget = {
98
103
  ...restOptions,
99
104
  ...resolvedEnabled === void 0 ? {} : { enabled: resolvedEnabled }
100
105
  };
106
+ if (registration?.isActive && lastHotkeyString === hotkeyString && lastTarget === finalTarget) {
107
+ registration.callback = callback;
108
+ registration.setOptions(optionsWithoutTarget);
109
+ return;
110
+ }
111
+ if (registration?.isActive) {
112
+ registration.unregister();
113
+ registration = null;
114
+ }
101
115
  registration = manager.register(hotkeyString, callback, {
102
116
  ...optionsWithoutTarget,
103
117
  target: finalTarget
@@ -106,6 +120,8 @@ function useHotkey(hotkey, callback, options = {}) {
106
120
  registration.callback = callback;
107
121
  registration.setOptions(optionsWithoutTarget);
108
122
  }
123
+ lastHotkeyString = hotkeyString;
124
+ lastTarget = finalTarget;
109
125
  }, { immediate: true });
110
126
  (0, vue.onUnmounted)(() => {
111
127
  stopWatcher();
@@ -1 +1 @@
1
- {"version":3,"file":"useHotkey.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkey.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n formatHotkey,\n getHotkeyManager,\n rawHotkeyToParsedHotkey,\n} from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\nexport interface UseHotkeyOptions extends Omit<\n HotkeyOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the hotkey is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * This composable automatically tracks reactive dependencies and updates\n * the registration when options or the callback change.\n *\n * @param hotkey - The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform)\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const count = ref(0)\n *\n * // Callback always has access to latest count value\n * useHotkey('Mod+S', (event, { hotkey }) => {\n * console.log(`Save triggered, count is ${count.value}`)\n * handleSave()\n * })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const isOpen = ref(false)\n *\n * // enabled option is reactive\n * useHotkey('Escape', () => {\n * isOpen.value = false\n * }, { enabled: isOpen })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const editorRef = ref<HTMLDivElement | null>(null)\n *\n * // Scoped to a specific element\n * useHotkey('Mod+S', () => {\n * save()\n * }, { target: editorRef })\n * </script>\n *\n * <template>\n * <div ref=\"editorRef\">...</div>\n * </template>\n * ```\n */\nexport function useHotkey(\n hotkey: MaybeRefOrGetter<RegisterableHotkey>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeyOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n let registration: HotkeyRegistrationHandle | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedHotkey = resolveMaybeRefOrGetter(hotkey)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as UseHotkeyOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedHotkey,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString: Hotkey =\n typeof resolvedHotkey === 'string'\n ? resolvedHotkey\n : (formatHotkey(\n rawHotkeyToParsedHotkey(resolvedHotkey as any, platform),\n ) as Hotkey)\n\n // Resolve target\n const finalTarget =\n resolvedTarget ?? (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n return\n }\n\n // Unregister previous registration if it exists\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n // Register the hotkey\n registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n // Update callback and options\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmGA,SAAgB,UACd,QACA,UACA,UAA8C,EAAE,EAC1C;CACN,MAAM,iBAAiBA,yDAA0B;CACjD,MAAM,mDAA4B;CAElC,IAAI,eAAgD;CAGpD,MAAM,mCACE;EACJ,MAAM,iBAAiB,wBAAwB,OAAO;EACtD,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,gBAAgB,eAAe,iBAAiB,qBAAqB;EAEtE,MAAM,WAAW,cAAc,mDAA4B;EAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,oGAE0B,gBAAuB,SAAS,CACzD;EAGP,MAAM,cACJ,mBAAmB,OAAO,aAAa,cAAc,WAAW;AAElE,MAAI,CAAC,YACH;AAIF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;EAIjB,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAGD,iBAAe,QAAQ,SAAS,cAAc,UAAU;GACtD,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;IAGjD,EAAE,WAAW,MAAM,CACpB;AAGD,4BAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,kBAAS,MAAM"}
1
+ {"version":3,"file":"useHotkey.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkey.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n getHotkeyManager,\n normalizeRegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\nexport interface UseHotkeyOptions extends Omit<\n HotkeyOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the hotkey is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * This composable automatically tracks reactive dependencies and updates\n * the registration when options or the callback change.\n *\n * @param hotkey - The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform)\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const count = ref(0)\n *\n * // Callback always has access to latest count value\n * useHotkey('Mod+S', (event, { hotkey }) => {\n * console.log(`Save triggered, count is ${count.value}`)\n * handleSave()\n * })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const isOpen = ref(false)\n *\n * // enabled option is reactive\n * useHotkey('Escape', () => {\n * isOpen.value = false\n * }, { enabled: isOpen })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const editorRef = ref<HTMLDivElement | null>(null)\n *\n * // Scoped to a specific element\n * useHotkey('Mod+S', () => {\n * save()\n * }, { target: editorRef })\n * </script>\n *\n * <template>\n * <div ref=\"editorRef\">...</div>\n * </template>\n * ```\n */\nexport function useHotkey(\n hotkey: MaybeRefOrGetter<RegisterableHotkey>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeyOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n let registration: HotkeyRegistrationHandle | null = null\n let lastHotkeyString: Hotkey | null = null\n let lastTarget: HTMLElement | Document | Window | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedHotkey = resolveMaybeRefOrGetter(hotkey)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as UseHotkeyOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedHotkey,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, platform)\n\n // Resolve target\n const finalTarget =\n resolvedTarget === undefined\n ? typeof document !== 'undefined'\n ? document\n : null\n : resolvedTarget\n\n if (!finalTarget) {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastHotkeyString = null\n lastTarget = null\n return\n }\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n if (\n registration?.isActive &&\n lastHotkeyString === hotkeyString &&\n lastTarget === finalTarget\n ) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n return\n }\n\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n lastHotkeyString = hotkeyString\n lastTarget = finalTarget\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,SAAgB,UACd,QACA,UACA,UAA8C,EAAE,EAC1C;CACN,MAAM,iBAAiBA,yDAA0B;CACjD,MAAM,mDAA4B;CAElC,IAAI,eAAgD;CACpD,IAAI,mBAAkC;CACtC,IAAI,aAAqD;CAGzD,MAAM,mCACE;EACJ,MAAM,iBAAiB,wBAAwB,OAAO;EACtD,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,gBAAgB,eAAe,iBAAiB,qBAAqB;EAGtE,MAAM,kEAA2C,gBADhC,cAAc,mDAA4B,CACe;EAG1E,MAAM,cACJ,mBAAmB,SACf,OAAO,aAAa,cAClB,WACA,OACF;AAEN,MAAI,CAAC,aAAa;AAChB,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;AAEjB,sBAAmB;AACnB,gBAAa;AACb;;EAIF,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAED,MACE,cAAc,YACd,qBAAqB,gBACrB,eAAe,aACf;AACA,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;AAC7C;;AAGF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAGjB,iBAAe,QAAQ,SAAS,cAAc,UAAU;GACtD,GAAG;GACH,QAAQ;GACT,CAAC;AAEF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAG/C,qBAAmB;AACnB,eAAa;IAEf,EAAE,WAAW,MAAM,CACpB;AAGD,4BAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,kBAAS,MAAM"}
package/dist/useHotkey.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useDefaultHotkeysOptions } from "./HotkeysProviderContext.js";
2
- import { detectPlatform, formatHotkey, getHotkeyManager, rawHotkeyToParsedHotkey } from "@tanstack/hotkeys";
2
+ import { detectPlatform, getHotkeyManager, normalizeRegisterableHotkey } from "@tanstack/hotkeys";
3
3
  import { onUnmounted, unref, watch } from "vue";
4
4
 
5
5
  //#region src/useHotkey.ts
@@ -71,6 +71,8 @@ function useHotkey(hotkey, callback, options = {}) {
71
71
  const defaultOptions = useDefaultHotkeysOptions();
72
72
  const manager = getHotkeyManager();
73
73
  let registration = null;
74
+ let lastHotkeyString = null;
75
+ let lastTarget = null;
74
76
  const stopWatcher = watch(() => {
75
77
  const resolvedHotkey = resolveMaybeRefOrGetter(hotkey);
76
78
  const resolvedOptions = resolveMaybeRefOrGetter(options);
@@ -85,19 +87,31 @@ function useHotkey(hotkey, callback, options = {}) {
85
87
  resolvedTarget: mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target)
86
88
  };
87
89
  }, ({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {
88
- const platform = mergedOptions.platform ?? detectPlatform();
89
- const hotkeyString = typeof resolvedHotkey === "string" ? resolvedHotkey : formatHotkey(rawHotkeyToParsedHotkey(resolvedHotkey, platform));
90
- const finalTarget = resolvedTarget ?? (typeof document !== "undefined" ? document : null);
91
- if (!finalTarget) return;
92
- if (registration?.isActive) {
93
- registration.unregister();
94
- registration = null;
90
+ const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, mergedOptions.platform ?? detectPlatform());
91
+ const finalTarget = resolvedTarget === void 0 ? typeof document !== "undefined" ? document : null : resolvedTarget;
92
+ if (!finalTarget) {
93
+ if (registration?.isActive) {
94
+ registration.unregister();
95
+ registration = null;
96
+ }
97
+ lastHotkeyString = null;
98
+ lastTarget = null;
99
+ return;
95
100
  }
96
101
  const { target: _target, enabled: _enabled, ...restOptions } = mergedOptions;
97
102
  const optionsWithoutTarget = {
98
103
  ...restOptions,
99
104
  ...resolvedEnabled === void 0 ? {} : { enabled: resolvedEnabled }
100
105
  };
106
+ if (registration?.isActive && lastHotkeyString === hotkeyString && lastTarget === finalTarget) {
107
+ registration.callback = callback;
108
+ registration.setOptions(optionsWithoutTarget);
109
+ return;
110
+ }
111
+ if (registration?.isActive) {
112
+ registration.unregister();
113
+ registration = null;
114
+ }
101
115
  registration = manager.register(hotkeyString, callback, {
102
116
  ...optionsWithoutTarget,
103
117
  target: finalTarget
@@ -106,6 +120,8 @@ function useHotkey(hotkey, callback, options = {}) {
106
120
  registration.callback = callback;
107
121
  registration.setOptions(optionsWithoutTarget);
108
122
  }
123
+ lastHotkeyString = hotkeyString;
124
+ lastTarget = finalTarget;
109
125
  }, { immediate: true });
110
126
  onUnmounted(() => {
111
127
  stopWatcher();
@@ -1 +1 @@
1
- {"version":3,"file":"useHotkey.js","names":[],"sources":["../src/useHotkey.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n formatHotkey,\n getHotkeyManager,\n rawHotkeyToParsedHotkey,\n} from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\nexport interface UseHotkeyOptions extends Omit<\n HotkeyOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the hotkey is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * This composable automatically tracks reactive dependencies and updates\n * the registration when options or the callback change.\n *\n * @param hotkey - The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform)\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const count = ref(0)\n *\n * // Callback always has access to latest count value\n * useHotkey('Mod+S', (event, { hotkey }) => {\n * console.log(`Save triggered, count is ${count.value}`)\n * handleSave()\n * })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const isOpen = ref(false)\n *\n * // enabled option is reactive\n * useHotkey('Escape', () => {\n * isOpen.value = false\n * }, { enabled: isOpen })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const editorRef = ref<HTMLDivElement | null>(null)\n *\n * // Scoped to a specific element\n * useHotkey('Mod+S', () => {\n * save()\n * }, { target: editorRef })\n * </script>\n *\n * <template>\n * <div ref=\"editorRef\">...</div>\n * </template>\n * ```\n */\nexport function useHotkey(\n hotkey: MaybeRefOrGetter<RegisterableHotkey>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeyOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n let registration: HotkeyRegistrationHandle | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedHotkey = resolveMaybeRefOrGetter(hotkey)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as UseHotkeyOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedHotkey,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString: Hotkey =\n typeof resolvedHotkey === 'string'\n ? resolvedHotkey\n : (formatHotkey(\n rawHotkeyToParsedHotkey(resolvedHotkey as any, platform),\n ) as Hotkey)\n\n // Resolve target\n const finalTarget =\n resolvedTarget ?? (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n return\n }\n\n // Unregister previous registration if it exists\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n // Register the hotkey\n registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n // Update callback and options\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmGA,SAAgB,UACd,QACA,UACA,UAA8C,EAAE,EAC1C;CACN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,kBAAkB;CAElC,IAAI,eAAgD;CAGpD,MAAM,cAAc,YACZ;EACJ,MAAM,iBAAiB,wBAAwB,OAAO;EACtD,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,gBAAgB,eAAe,iBAAiB,qBAAqB;EAEtE,MAAM,WAAW,cAAc,YAAY,gBAAgB;EAC3D,MAAM,eACJ,OAAO,mBAAmB,WACtB,iBACC,aACC,wBAAwB,gBAAuB,SAAS,CACzD;EAGP,MAAM,cACJ,mBAAmB,OAAO,aAAa,cAAc,WAAW;AAElE,MAAI,CAAC,YACH;AAIF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;EAIjB,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAGD,iBAAe,QAAQ,SAAS,cAAc,UAAU;GACtD,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;IAGjD,EAAE,WAAW,MAAM,CACpB;AAGD,mBAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
1
+ {"version":3,"file":"useHotkey.js","names":[],"sources":["../src/useHotkey.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport {\n detectPlatform,\n getHotkeyManager,\n normalizeRegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type {\n Hotkey,\n HotkeyCallback,\n HotkeyOptions,\n HotkeyRegistrationHandle,\n RegisterableHotkey,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\nexport interface UseHotkeyOptions extends Omit<\n HotkeyOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the hotkey is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard hotkey.\n *\n * Uses the singleton HotkeyManager for efficient event handling.\n * The callback receives both the keyboard event and a context object\n * containing the hotkey string and parsed hotkey.\n *\n * This composable automatically tracks reactive dependencies and updates\n * the registration when options or the callback change.\n *\n * @param hotkey - The hotkey string (e.g., 'Mod+S', 'Escape') or RawHotkey object (supports `mod` for cross-platform)\n * @param callback - The function to call when the hotkey is pressed\n * @param options - Options for the hotkey behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const count = ref(0)\n *\n * // Callback always has access to latest count value\n * useHotkey('Mod+S', (event, { hotkey }) => {\n * console.log(`Save triggered, count is ${count.value}`)\n * handleSave()\n * })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const isOpen = ref(false)\n *\n * // enabled option is reactive\n * useHotkey('Escape', () => {\n * isOpen.value = false\n * }, { enabled: isOpen })\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { ref } from 'vue'\n * import { useHotkey } from '@tanstack/vue-hotkeys'\n *\n * const editorRef = ref<HTMLDivElement | null>(null)\n *\n * // Scoped to a specific element\n * useHotkey('Mod+S', () => {\n * save()\n * }, { target: editorRef })\n * </script>\n *\n * <template>\n * <div ref=\"editorRef\">...</div>\n * </template>\n * ```\n */\nexport function useHotkey(\n hotkey: MaybeRefOrGetter<RegisterableHotkey>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeyOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getHotkeyManager()\n\n let registration: HotkeyRegistrationHandle | null = null\n let lastHotkeyString: Hotkey | null = null\n let lastTarget: HTMLElement | Document | Window | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedHotkey = resolveMaybeRefOrGetter(hotkey)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkey,\n ...resolvedOptions,\n } as UseHotkeyOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedHotkey,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedHotkey, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n // Normalize to hotkey string\n const platform = mergedOptions.platform ?? detectPlatform()\n const hotkeyString = normalizeRegisterableHotkey(resolvedHotkey, platform)\n\n // Resolve target\n const finalTarget =\n resolvedTarget === undefined\n ? typeof document !== 'undefined'\n ? document\n : null\n : resolvedTarget\n\n if (!finalTarget) {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastHotkeyString = null\n lastTarget = null\n return\n }\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n if (\n registration?.isActive &&\n lastHotkeyString === hotkeyString &&\n lastTarget === finalTarget\n ) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n return\n }\n\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n registration = manager.register(hotkeyString, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n lastHotkeyString = hotkeyString\n lastTarget = finalTarget\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,SAAgB,UACd,QACA,UACA,UAA8C,EAAE,EAC1C;CACN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,kBAAkB;CAElC,IAAI,eAAgD;CACpD,IAAI,mBAAkC;CACtC,IAAI,aAAqD;CAGzD,MAAM,cAAc,YACZ;EACJ,MAAM,iBAAiB,wBAAwB,OAAO;EACtD,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,gBAAgB,eAAe,iBAAiB,qBAAqB;EAGtE,MAAM,eAAe,4BAA4B,gBADhC,cAAc,YAAY,gBAAgB,CACe;EAG1E,MAAM,cACJ,mBAAmB,SACf,OAAO,aAAa,cAClB,WACA,OACF;AAEN,MAAI,CAAC,aAAa;AAChB,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;AAEjB,sBAAmB;AACnB,gBAAa;AACb;;EAIF,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAED,MACE,cAAc,YACd,qBAAqB,gBACrB,eAAe,aACf;AACA,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;AAC7C;;AAGF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAGjB,iBAAe,QAAQ,SAAS,cAAc,UAAU;GACtD,GAAG;GACH,QAAQ;GACT,CAAC;AAEF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAG/C,qBAAmB;AACnB,eAAa;IAEf,EAAE,WAAW,MAAM,CACpB;AAGD,mBAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
@@ -53,6 +53,8 @@ function useHotkeySequence(sequence, callback, options = {}) {
53
53
  const defaultOptions = require_HotkeysProviderContext.useDefaultHotkeysOptions();
54
54
  const manager = (0, _tanstack_hotkeys.getSequenceManager)();
55
55
  let registration = null;
56
+ let lastSequenceKey = null;
57
+ let lastTarget = null;
56
58
  const stopWatcher = (0, vue.watch)(() => {
57
59
  const resolvedSequence = resolveMaybeRefOrGetter(sequence);
58
60
  const resolvedOptions = resolveMaybeRefOrGetter(options);
@@ -67,18 +69,31 @@ function useHotkeySequence(sequence, callback, options = {}) {
67
69
  resolvedTarget: mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target)
68
70
  };
69
71
  }, ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {
70
- if (resolvedEnabled === false || resolvedSequence.length === 0) return;
71
- const finalTarget = resolvedTarget ?? (typeof document !== "undefined" ? document : null);
72
- if (!finalTarget) return;
73
- if (registration?.isActive) {
74
- registration.unregister();
75
- registration = null;
72
+ const finalTarget = resolvedTarget === void 0 ? typeof document !== "undefined" ? document : null : resolvedTarget;
73
+ if (resolvedSequence.length === 0 || !finalTarget) {
74
+ if (registration?.isActive) {
75
+ registration.unregister();
76
+ registration = null;
77
+ }
78
+ lastSequenceKey = null;
79
+ lastTarget = null;
80
+ return;
76
81
  }
82
+ const sequenceKey = (0, _tanstack_hotkeys.formatHotkeySequence)(resolvedSequence);
77
83
  const { target: _target, enabled: _enabled, ...restOptions } = mergedOptions;
78
84
  const optionsWithoutTarget = {
79
85
  ...restOptions,
80
86
  ...resolvedEnabled === void 0 ? {} : { enabled: resolvedEnabled }
81
87
  };
88
+ if (registration?.isActive && lastSequenceKey === sequenceKey && lastTarget === finalTarget) {
89
+ registration.callback = callback;
90
+ registration.setOptions(optionsWithoutTarget);
91
+ return;
92
+ }
93
+ if (registration?.isActive) {
94
+ registration.unregister();
95
+ registration = null;
96
+ }
82
97
  registration = manager.register(resolvedSequence, callback, {
83
98
  ...optionsWithoutTarget,
84
99
  target: finalTarget
@@ -87,6 +102,8 @@ function useHotkeySequence(sequence, callback, options = {}) {
87
102
  registration.callback = callback;
88
103
  registration.setOptions(optionsWithoutTarget);
89
104
  }
105
+ lastSequenceKey = sequenceKey;
106
+ lastTarget = finalTarget;
90
107
  }, { immediate: true });
91
108
  (0, vue.onUnmounted)(() => {
92
109
  stopWatcher();
@@ -1 +1 @@
1
- {"version":3,"file":"useHotkeySequence.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkeySequence.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { MaybeRefOrGetter } from 'vue'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\nexport interface UseHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the sequence is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard shortcut sequence (Vim-style).\n *\n * This composable allows you to register multi-key sequences like 'g g' or 'd d'\n * that trigger when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeySequence } from '@tanstack/vue-hotkeys'\n *\n * // 'g g' to go to top\n * useHotkeySequence(['G', 'G'], () => {\n * scrollToTop()\n * })\n *\n * // 'd d' to delete line\n * useHotkeySequence(['D', 'D'], () => {\n * deleteLine()\n * })\n *\n * // 'd i w' to delete inner word\n * useHotkeySequence(['D', 'I', 'W'], () => {\n * deleteInnerWord()\n * }, { timeout: 500 })\n *\n * // Same modifier on consecutive steps (bare Shift between chords is ignored)\n * useHotkeySequence(['Shift+R', 'Shift+T'], () => {\n * nextAction()\n * })\n * </script>\n *\n * <template>\n * <div>...</div>\n * </template>\n * ```\n */\nexport function useHotkeySequence(\n sequence: MaybeRefOrGetter<HotkeySequence>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n let registration: SequenceRegistrationHandle | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedSequence = resolveMaybeRefOrGetter(sequence)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as UseHotkeySequenceOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedSequence,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n if (resolvedEnabled === false || resolvedSequence.length === 0) {\n return\n }\n\n // Resolve target\n const finalTarget =\n resolvedTarget ?? (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n return\n }\n\n // Unregister previous registration if it exists\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n // Register the sequence\n registration = manager.register(resolvedSequence, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n // Update callback and options\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,kBACd,UACA,UACA,UAAsD,EAAE,EAClD;CACN,MAAM,iBAAiBA,yDAA0B;CACjD,MAAM,qDAA8B;CAEpC,IAAI,eAAkD;CAGtD,MAAM,mCACE;EACJ,MAAM,mBAAmB,wBAAwB,SAAS;EAC1D,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,kBAAkB,eAAe,iBAAiB,qBAAqB;AACxE,MAAI,oBAAoB,SAAS,iBAAiB,WAAW,EAC3D;EAIF,MAAM,cACJ,mBAAmB,OAAO,aAAa,cAAc,WAAW;AAElE,MAAI,CAAC,YACH;AAIF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;EAIjB,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAGD,iBAAe,QAAQ,SAAS,kBAAkB,UAAU;GAC1D,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;IAGjD,EAAE,WAAW,MAAM,CACpB;AAGD,4BAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,kBAAS,MAAM"}
1
+ {"version":3,"file":"useHotkeySequence.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkeySequence.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { MaybeRefOrGetter } from 'vue'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\nexport interface UseHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the sequence is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard shortcut sequence (Vim-style).\n *\n * This composable allows you to register multi-key sequences like 'g g' or 'd d'\n * that trigger when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeySequence } from '@tanstack/vue-hotkeys'\n *\n * // 'g g' to go to top\n * useHotkeySequence(['G', 'G'], () => {\n * scrollToTop()\n * })\n *\n * // 'd d' to delete line\n * useHotkeySequence(['D', 'D'], () => {\n * deleteLine()\n * })\n *\n * // 'd i w' to delete inner word\n * useHotkeySequence(['D', 'I', 'W'], () => {\n * deleteInnerWord()\n * }, { timeout: 500 })\n *\n * // Same modifier on consecutive steps (bare Shift between chords is ignored)\n * useHotkeySequence(['Shift+R', 'Shift+T'], () => {\n * nextAction()\n * })\n * </script>\n *\n * <template>\n * <div>...</div>\n * </template>\n * ```\n */\nexport function useHotkeySequence(\n sequence: MaybeRefOrGetter<HotkeySequence>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n let registration: SequenceRegistrationHandle | null = null\n let lastSequenceKey: string | null = null\n let lastTarget: HTMLElement | Document | Window | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedSequence = resolveMaybeRefOrGetter(sequence)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as UseHotkeySequenceOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedSequence,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n const finalTarget =\n resolvedTarget === undefined\n ? typeof document !== 'undefined'\n ? document\n : null\n : resolvedTarget\n\n if (resolvedSequence.length === 0 || !finalTarget) {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastSequenceKey = null\n lastTarget = null\n return\n }\n\n const sequenceKey = formatHotkeySequence(resolvedSequence)\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n if (\n registration?.isActive &&\n lastSequenceKey === sequenceKey &&\n lastTarget === finalTarget\n ) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n return\n }\n\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n registration = manager.register(resolvedSequence, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n lastSequenceKey = sequenceKey\n lastTarget = finalTarget\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,kBACd,UACA,UACA,UAAsD,EAAE,EAClD;CACN,MAAM,iBAAiBA,yDAA0B;CACjD,MAAM,qDAA8B;CAEpC,IAAI,eAAkD;CACtD,IAAI,kBAAiC;CACrC,IAAI,aAAqD;CAGzD,MAAM,mCACE;EACJ,MAAM,mBAAmB,wBAAwB,SAAS;EAC1D,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,kBAAkB,eAAe,iBAAiB,qBAAqB;EACxE,MAAM,cACJ,mBAAmB,SACf,OAAO,aAAa,cAClB,WACA,OACF;AAEN,MAAI,iBAAiB,WAAW,KAAK,CAAC,aAAa;AACjD,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;AAEjB,qBAAkB;AAClB,gBAAa;AACb;;EAGF,MAAM,0DAAmC,iBAAiB;EAG1D,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAED,MACE,cAAc,YACd,oBAAoB,eACpB,eAAe,aACf;AACA,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;AAC7C;;AAGF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAGjB,iBAAe,QAAQ,SAAS,kBAAkB,UAAU;GAC1D,GAAG;GACH,QAAQ;GACT,CAAC;AAEF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAG/C,oBAAkB;AAClB,eAAa;IAEf,EAAE,WAAW,MAAM,CACpB;AAGD,4BAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,kBAAS,MAAM"}
@@ -1,5 +1,5 @@
1
1
  import { useDefaultHotkeysOptions } from "./HotkeysProviderContext.js";
2
- import { getSequenceManager } from "@tanstack/hotkeys";
2
+ import { formatHotkeySequence, getSequenceManager } from "@tanstack/hotkeys";
3
3
  import { onUnmounted, unref, watch } from "vue";
4
4
 
5
5
  //#region src/useHotkeySequence.ts
@@ -53,6 +53,8 @@ function useHotkeySequence(sequence, callback, options = {}) {
53
53
  const defaultOptions = useDefaultHotkeysOptions();
54
54
  const manager = getSequenceManager();
55
55
  let registration = null;
56
+ let lastSequenceKey = null;
57
+ let lastTarget = null;
56
58
  const stopWatcher = watch(() => {
57
59
  const resolvedSequence = resolveMaybeRefOrGetter(sequence);
58
60
  const resolvedOptions = resolveMaybeRefOrGetter(options);
@@ -67,18 +69,31 @@ function useHotkeySequence(sequence, callback, options = {}) {
67
69
  resolvedTarget: mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target)
68
70
  };
69
71
  }, ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {
70
- if (resolvedEnabled === false || resolvedSequence.length === 0) return;
71
- const finalTarget = resolvedTarget ?? (typeof document !== "undefined" ? document : null);
72
- if (!finalTarget) return;
73
- if (registration?.isActive) {
74
- registration.unregister();
75
- registration = null;
72
+ const finalTarget = resolvedTarget === void 0 ? typeof document !== "undefined" ? document : null : resolvedTarget;
73
+ if (resolvedSequence.length === 0 || !finalTarget) {
74
+ if (registration?.isActive) {
75
+ registration.unregister();
76
+ registration = null;
77
+ }
78
+ lastSequenceKey = null;
79
+ lastTarget = null;
80
+ return;
76
81
  }
82
+ const sequenceKey = formatHotkeySequence(resolvedSequence);
77
83
  const { target: _target, enabled: _enabled, ...restOptions } = mergedOptions;
78
84
  const optionsWithoutTarget = {
79
85
  ...restOptions,
80
86
  ...resolvedEnabled === void 0 ? {} : { enabled: resolvedEnabled }
81
87
  };
88
+ if (registration?.isActive && lastSequenceKey === sequenceKey && lastTarget === finalTarget) {
89
+ registration.callback = callback;
90
+ registration.setOptions(optionsWithoutTarget);
91
+ return;
92
+ }
93
+ if (registration?.isActive) {
94
+ registration.unregister();
95
+ registration = null;
96
+ }
82
97
  registration = manager.register(resolvedSequence, callback, {
83
98
  ...optionsWithoutTarget,
84
99
  target: finalTarget
@@ -87,6 +102,8 @@ function useHotkeySequence(sequence, callback, options = {}) {
87
102
  registration.callback = callback;
88
103
  registration.setOptions(optionsWithoutTarget);
89
104
  }
105
+ lastSequenceKey = sequenceKey;
106
+ lastTarget = finalTarget;
90
107
  }, { immediate: true });
91
108
  onUnmounted(() => {
92
109
  stopWatcher();
@@ -1 +1 @@
1
- {"version":3,"file":"useHotkeySequence.js","names":[],"sources":["../src/useHotkeySequence.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { MaybeRefOrGetter } from 'vue'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\nexport interface UseHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the sequence is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard shortcut sequence (Vim-style).\n *\n * This composable allows you to register multi-key sequences like 'g g' or 'd d'\n * that trigger when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeySequence } from '@tanstack/vue-hotkeys'\n *\n * // 'g g' to go to top\n * useHotkeySequence(['G', 'G'], () => {\n * scrollToTop()\n * })\n *\n * // 'd d' to delete line\n * useHotkeySequence(['D', 'D'], () => {\n * deleteLine()\n * })\n *\n * // 'd i w' to delete inner word\n * useHotkeySequence(['D', 'I', 'W'], () => {\n * deleteInnerWord()\n * }, { timeout: 500 })\n *\n * // Same modifier on consecutive steps (bare Shift between chords is ignored)\n * useHotkeySequence(['Shift+R', 'Shift+T'], () => {\n * nextAction()\n * })\n * </script>\n *\n * <template>\n * <div>...</div>\n * </template>\n * ```\n */\nexport function useHotkeySequence(\n sequence: MaybeRefOrGetter<HotkeySequence>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n let registration: SequenceRegistrationHandle | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedSequence = resolveMaybeRefOrGetter(sequence)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as UseHotkeySequenceOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedSequence,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n if (resolvedEnabled === false || resolvedSequence.length === 0) {\n return\n }\n\n // Resolve target\n const finalTarget =\n resolvedTarget ?? (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n return\n }\n\n // Unregister previous registration if it exists\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n // Register the sequence\n registration = manager.register(resolvedSequence, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n // Update callback and options\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,kBACd,UACA,UACA,UAAsD,EAAE,EAClD;CACN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,oBAAoB;CAEpC,IAAI,eAAkD;CAGtD,MAAM,cAAc,YACZ;EACJ,MAAM,mBAAmB,wBAAwB,SAAS;EAC1D,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,kBAAkB,eAAe,iBAAiB,qBAAqB;AACxE,MAAI,oBAAoB,SAAS,iBAAiB,WAAW,EAC3D;EAIF,MAAM,cACJ,mBAAmB,OAAO,aAAa,cAAc,WAAW;AAElE,MAAI,CAAC,YACH;AAIF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;EAIjB,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAGD,iBAAe,QAAQ,SAAS,kBAAkB,UAAU;GAC1D,GAAG;GACH,QAAQ;GACT,CAAC;AAGF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;IAGjD,EAAE,WAAW,MAAM,CACpB;AAGD,mBAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
1
+ {"version":3,"file":"useHotkeySequence.js","names":[],"sources":["../src/useHotkeySequence.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { MaybeRefOrGetter } from 'vue'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceOptions,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\n\nexport interface UseHotkeySequenceOptions extends Omit<\n SequenceOptions,\n 'enabled' | 'target'\n> {\n /**\n * Whether the sequence is active.\n * Can be a Ref, a getter function, or a boolean value.\n * Defaults to true.\n */\n enabled?: MaybeRefOrGetter<boolean>\n /**\n * The DOM element to attach the event listener to.\n * Can be a Ref, a getter function, direct DOM element, or null.\n * Defaults to document.\n */\n target?: MaybeRefOrGetter<HTMLElement | Document | Window | null>\n}\n\n/**\n * Vue composable for registering a keyboard shortcut sequence (Vim-style).\n *\n * This composable allows you to register multi-key sequences like 'g g' or 'd d'\n * that trigger when the full sequence is pressed within a timeout.\n *\n * Each step may include modifiers. You can chain the same modifier across\n * steps (e.g. `Shift+R` then `Shift+T`). Modifier-only keydown events (Shift,\n * Control, Alt, or Meta pressed alone) are ignored while matching—they do not\n * advance the sequence or reset progress.\n *\n * @param sequence - Array of hotkey strings that form the sequence\n * @param callback - Function to call when the sequence is completed\n * @param options - Options for the sequence behavior\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeySequence } from '@tanstack/vue-hotkeys'\n *\n * // 'g g' to go to top\n * useHotkeySequence(['G', 'G'], () => {\n * scrollToTop()\n * })\n *\n * // 'd d' to delete line\n * useHotkeySequence(['D', 'D'], () => {\n * deleteLine()\n * })\n *\n * // 'd i w' to delete inner word\n * useHotkeySequence(['D', 'I', 'W'], () => {\n * deleteInnerWord()\n * }, { timeout: 500 })\n *\n * // Same modifier on consecutive steps (bare Shift between chords is ignored)\n * useHotkeySequence(['Shift+R', 'Shift+T'], () => {\n * nextAction()\n * })\n * </script>\n *\n * <template>\n * <div>...</div>\n * </template>\n * ```\n */\nexport function useHotkeySequence(\n sequence: MaybeRefOrGetter<HotkeySequence>,\n callback: HotkeyCallback,\n options: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},\n): void {\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n let registration: SequenceRegistrationHandle | null = null\n let lastSequenceKey: string | null = null\n let lastTarget: HTMLElement | Document | Window | null = null\n\n // Watch for changes to reactive dependencies\n const stopWatcher = watch(\n () => {\n const resolvedSequence = resolveMaybeRefOrGetter(sequence)\n const resolvedOptions = resolveMaybeRefOrGetter(options)\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedOptions,\n } as UseHotkeySequenceOptions\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target)\n\n return {\n resolvedSequence,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n },\n ({ resolvedSequence, mergedOptions, resolvedEnabled, resolvedTarget }) => {\n const finalTarget =\n resolvedTarget === undefined\n ? typeof document !== 'undefined'\n ? document\n : null\n : resolvedTarget\n\n if (resolvedSequence.length === 0 || !finalTarget) {\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n lastSequenceKey = null\n lastTarget = null\n return\n }\n\n const sequenceKey = formatHotkeySequence(resolvedSequence)\n\n // Extract options without target (target is handled separately)\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(resolvedEnabled === undefined ? {} : { enabled: resolvedEnabled }),\n }\n\n if (\n registration?.isActive &&\n lastSequenceKey === sequenceKey &&\n lastTarget === finalTarget\n ) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n return\n }\n\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n\n registration = manager.register(resolvedSequence, callback, {\n ...optionsWithoutTarget,\n target: finalTarget,\n })\n\n if (registration.isActive) {\n registration.callback = callback\n registration.setOptions(optionsWithoutTarget)\n }\n\n lastSequenceKey = sequenceKey\n lastTarget = finalTarget\n },\n { immediate: true },\n )\n\n // Cleanup on unmount\n onUnmounted(() => {\n stopWatcher()\n if (registration?.isActive) {\n registration.unregister()\n registration = null\n }\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,kBACd,UACA,UACA,UAAsD,EAAE,EAClD;CACN,MAAM,iBAAiB,0BAA0B;CACjD,MAAM,UAAU,oBAAoB;CAEpC,IAAI,eAAkD;CACtD,IAAI,kBAAiC;CACrC,IAAI,aAAqD;CAGzD,MAAM,cAAc,YACZ;EACJ,MAAM,mBAAmB,wBAAwB,SAAS;EAC1D,MAAM,kBAAkB,wBAAwB,QAAQ;EACxD,MAAM,gBAAgB;GACpB,GAAG,eAAe;GAClB,GAAG;GACJ;AAUD,SAAO;GACL;GACA;GACA,iBAXA,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GAUlD,gBARA,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAO;GAOlD;KAEF,EAAE,kBAAkB,eAAe,iBAAiB,qBAAqB;EACxE,MAAM,cACJ,mBAAmB,SACf,OAAO,aAAa,cAClB,WACA,OACF;AAEN,MAAI,iBAAiB,WAAW,KAAK,CAAC,aAAa;AACjD,OAAI,cAAc,UAAU;AAC1B,iBAAa,YAAY;AACzB,mBAAe;;AAEjB,qBAAkB;AAClB,gBAAa;AACb;;EAGF,MAAM,cAAc,qBAAqB,iBAAiB;EAG1D,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD;EACJ,MAAM,uBAAuB;GAC3B,GAAG;GACH,GAAI,oBAAoB,SAAY,EAAE,GAAG,EAAE,SAAS,iBAAiB;GACtE;AAED,MACE,cAAc,YACd,oBAAoB,eACpB,eAAe,aACf;AACA,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;AAC7C;;AAGF,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;AAGjB,iBAAe,QAAQ,SAAS,kBAAkB,UAAU;GAC1D,GAAG;GACH,QAAQ;GACT,CAAC;AAEF,MAAI,aAAa,UAAU;AACzB,gBAAa,WAAW;AACxB,gBAAa,WAAW,qBAAqB;;AAG/C,oBAAkB;AAClB,eAAa;IAEf,EAAE,WAAW,MAAM,CACpB;AAGD,mBAAkB;AAChB,eAAa;AACb,MAAI,cAAc,UAAU;AAC1B,gBAAa,YAAY;AACzB,kBAAe;;GAEjB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,GAAG,MAAM,MAAM"}
@@ -0,0 +1,140 @@
1
+ const require_HotkeysProviderContext = require('./HotkeysProviderContext.cjs');
2
+ let _tanstack_hotkeys = require("@tanstack/hotkeys");
3
+ let vue = require("vue");
4
+
5
+ //#region src/useHotkeySequences.ts
6
+ /**
7
+ * Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).
8
+ *
9
+ * Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref
10
+ * that returns one, so you can register variable-length lists safely.
11
+ *
12
+ * Options are merged in this order:
13
+ * HotkeysProvider defaults < commonOptions < per-definition options
14
+ *
15
+ * Definitions with an empty `sequence` are skipped (no registration).
16
+ *
17
+ * @param definitions - Array of sequence definitions, or a getter/ref
18
+ * @param commonOptions - Shared options applied to all sequences (overridden by per-definition options)
19
+ *
20
+ * @example
21
+ * ```vue
22
+ * <script setup>
23
+ * import { useHotkeySequences } from '@tanstack/vue-hotkeys'
24
+ *
25
+ * useHotkeySequences([
26
+ * { sequence: ['G', 'G'], callback: () => scrollToTop() },
27
+ * { sequence: ['D', 'D'], callback: () => deleteLine() },
28
+ * ])
29
+ * <\/script>
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```vue
34
+ * <script setup>
35
+ * import { computed } from 'vue'
36
+ * import { useHotkeySequences } from '@tanstack/vue-hotkeys'
37
+ *
38
+ * const items = computed(() => [...])
39
+ * useHotkeySequences(
40
+ * () => items.value.map((item) => ({
41
+ * sequence: item.chords,
42
+ * callback: item.action,
43
+ * options: { enabled: item.enabled },
44
+ * })),
45
+ * { preventDefault: true },
46
+ * )
47
+ * <\/script>
48
+ * ```
49
+ */
50
+ function useHotkeySequences(definitions, commonOptions = {}) {
51
+ const defaultOptions = require_HotkeysProviderContext.useDefaultHotkeysOptions();
52
+ const manager = (0, _tanstack_hotkeys.getSequenceManager)();
53
+ const registrations = /* @__PURE__ */ new Map();
54
+ const stopWatcher = (0, vue.watch)(() => {
55
+ const resolvedDefinitions = resolveMaybeRefOrGetter(definitions);
56
+ const resolvedCommonOptions = resolveMaybeRefOrGetter(commonOptions);
57
+ return resolvedDefinitions.map((def, i) => {
58
+ const resolvedSequence = resolveMaybeRefOrGetter(def.sequence);
59
+ const resolvedDefOptions = def.options ? resolveMaybeRefOrGetter(def.options) : {};
60
+ const mergedOptions = {
61
+ ...defaultOptions.hotkeySequence,
62
+ ...resolvedCommonOptions,
63
+ ...resolvedDefOptions
64
+ };
65
+ const sequenceString = (0, _tanstack_hotkeys.formatHotkeySequence)(resolvedSequence);
66
+ const resolvedEnabled = mergedOptions.enabled === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.enabled);
67
+ const resolvedTarget = mergedOptions.target === void 0 ? void 0 : resolveMaybeRefOrGetter(mergedOptions.target);
68
+ return {
69
+ index: i,
70
+ resolvedSequence,
71
+ sequenceString,
72
+ callback: def.callback,
73
+ mergedOptions,
74
+ resolvedEnabled,
75
+ resolvedTarget
76
+ };
77
+ });
78
+ }, (resolved) => {
79
+ const nextKeys = /* @__PURE__ */ new Set();
80
+ const prepared = [];
81
+ for (const entry of resolved) {
82
+ if (entry.resolvedSequence.length === 0) continue;
83
+ const finalTarget = entry.resolvedTarget ?? (typeof document !== "undefined" ? document : null);
84
+ if (!finalTarget) continue;
85
+ const registrationKey = `${entry.index}:${entry.sequenceString}`;
86
+ nextKeys.add(registrationKey);
87
+ prepared.push({
88
+ registrationKey,
89
+ entry,
90
+ finalTarget
91
+ });
92
+ }
93
+ for (const [key, record] of [...registrations.entries()]) if (!nextKeys.has(key)) {
94
+ if (record.handle.isActive) record.handle.unregister();
95
+ registrations.delete(key);
96
+ }
97
+ for (const { registrationKey, entry, finalTarget } of prepared) {
98
+ const existing = registrations.get(registrationKey);
99
+ if (existing?.handle.isActive && existing.target === finalTarget) {
100
+ existing.handle.callback = entry.callback;
101
+ const { target: _target, enabled: _enabled, ...restOptions } = entry.mergedOptions;
102
+ const optionsWithoutTarget = {
103
+ ...restOptions,
104
+ ...entry.resolvedEnabled === void 0 ? {} : { enabled: entry.resolvedEnabled }
105
+ };
106
+ existing.handle.setOptions(optionsWithoutTarget);
107
+ continue;
108
+ }
109
+ if (existing) {
110
+ if (existing.handle.isActive) existing.handle.unregister();
111
+ registrations.delete(registrationKey);
112
+ }
113
+ const { target: _target, enabled: _enabled, ...restOptions } = entry.mergedOptions;
114
+ const optionsWithoutTarget = {
115
+ ...restOptions,
116
+ ...entry.resolvedEnabled === void 0 ? {} : { enabled: entry.resolvedEnabled }
117
+ };
118
+ const handle = manager.register(entry.resolvedSequence, entry.callback, {
119
+ ...optionsWithoutTarget,
120
+ target: finalTarget
121
+ });
122
+ registrations.set(registrationKey, {
123
+ handle,
124
+ target: finalTarget
125
+ });
126
+ }
127
+ }, { immediate: true });
128
+ (0, vue.onUnmounted)(() => {
129
+ stopWatcher();
130
+ for (const { handle } of registrations.values()) if (handle.isActive) handle.unregister();
131
+ registrations.clear();
132
+ });
133
+ }
134
+ function resolveMaybeRefOrGetter(value) {
135
+ return typeof value === "function" ? value() : (0, vue.unref)(value);
136
+ }
137
+
138
+ //#endregion
139
+ exports.useHotkeySequences = useHotkeySequences;
140
+ //# sourceMappingURL=useHotkeySequences.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHotkeySequences.cjs","names":["useDefaultHotkeysOptions"],"sources":["../src/useHotkeySequences.ts"],"sourcesContent":["import { onUnmounted, unref, watch } from 'vue'\nimport { formatHotkeySequence, getSequenceManager } from '@tanstack/hotkeys'\nimport { useDefaultHotkeysOptions } from './HotkeysProviderContext'\nimport type { UseHotkeySequenceOptions } from './useHotkeySequence'\nimport type {\n HotkeyCallback,\n HotkeySequence,\n SequenceRegistrationHandle,\n} from '@tanstack/hotkeys'\nimport type { MaybeRefOrGetter } from 'vue'\n\n/**\n * A single sequence definition for use with `useHotkeySequences`.\n */\nexport interface UseHotkeySequenceDefinition {\n /** Array of hotkey strings that form the sequence */\n sequence: MaybeRefOrGetter<HotkeySequence>\n /** The function to call when the sequence is completed */\n callback: HotkeyCallback\n /** Per-sequence options (merged on top of commonOptions) */\n options?: MaybeRefOrGetter<UseHotkeySequenceOptions>\n}\n\n/**\n * Vue composable for registering multiple keyboard shortcut sequences at once (Vim-style).\n *\n * Uses the singleton SequenceManager. Accepts a dynamic array of definitions, or a getter/ref\n * that returns one, so you can register variable-length lists safely.\n *\n * Options are merged in this order:\n * HotkeysProvider defaults < commonOptions < per-definition options\n *\n * Definitions with an empty `sequence` are skipped (no registration).\n *\n * @param definitions - Array of sequence definitions, or a getter/ref\n * @param commonOptions - Shared options applied to all sequences (overridden by per-definition options)\n *\n * @example\n * ```vue\n * <script setup>\n * import { useHotkeySequences } from '@tanstack/vue-hotkeys'\n *\n * useHotkeySequences([\n * { sequence: ['G', 'G'], callback: () => scrollToTop() },\n * { sequence: ['D', 'D'], callback: () => deleteLine() },\n * ])\n * </script>\n * ```\n *\n * @example\n * ```vue\n * <script setup>\n * import { computed } from 'vue'\n * import { useHotkeySequences } from '@tanstack/vue-hotkeys'\n *\n * const items = computed(() => [...])\n * useHotkeySequences(\n * () => items.value.map((item) => ({\n * sequence: item.chords,\n * callback: item.action,\n * options: { enabled: item.enabled },\n * })),\n * { preventDefault: true },\n * )\n * </script>\n * ```\n */\nexport function useHotkeySequences(\n definitions: MaybeRefOrGetter<Array<UseHotkeySequenceDefinition>>,\n commonOptions: MaybeRefOrGetter<UseHotkeySequenceOptions> = {},\n): void {\n type RegistrationRecord = {\n handle: SequenceRegistrationHandle\n target: Document | HTMLElement | Window\n }\n\n const defaultOptions = useDefaultHotkeysOptions()\n const manager = getSequenceManager()\n\n const registrations = new Map<string, RegistrationRecord>()\n\n const stopWatcher = watch(\n () => {\n const resolvedDefinitions = resolveMaybeRefOrGetter(definitions)\n const resolvedCommonOptions = resolveMaybeRefOrGetter(commonOptions)\n\n return resolvedDefinitions.map((def, i) => {\n const resolvedSequence = resolveMaybeRefOrGetter(def.sequence)\n const resolvedDefOptions = def.options\n ? resolveMaybeRefOrGetter(def.options)\n : {}\n\n const mergedOptions = {\n ...defaultOptions.hotkeySequence,\n ...resolvedCommonOptions,\n ...resolvedDefOptions,\n } as UseHotkeySequenceOptions\n\n const sequenceString = formatHotkeySequence(resolvedSequence)\n\n const resolvedEnabled =\n mergedOptions.enabled === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.enabled)\n const resolvedTarget =\n mergedOptions.target === undefined\n ? undefined\n : resolveMaybeRefOrGetter(mergedOptions.target as any)\n\n return {\n index: i,\n resolvedSequence,\n sequenceString,\n callback: def.callback,\n mergedOptions,\n resolvedEnabled,\n resolvedTarget,\n }\n })\n },\n (resolved) => {\n const nextKeys = new Set<string>()\n const prepared: Array<{\n registrationKey: string\n entry: (typeof resolved)[number]\n finalTarget: Document | HTMLElement | Window\n }> = []\n\n for (const entry of resolved) {\n if (entry.resolvedSequence.length === 0) {\n continue\n }\n\n const finalTarget =\n entry.resolvedTarget ??\n (typeof document !== 'undefined' ? document : null)\n\n if (!finalTarget) {\n continue\n }\n\n const registrationKey = `${entry.index}:${entry.sequenceString}`\n nextKeys.add(registrationKey)\n prepared.push({ registrationKey, entry, finalTarget })\n }\n\n for (const [key, record] of [...registrations.entries()]) {\n if (!nextKeys.has(key)) {\n if (record.handle.isActive) {\n record.handle.unregister()\n }\n registrations.delete(key)\n }\n }\n\n for (const { registrationKey, entry, finalTarget } of prepared) {\n const existing = registrations.get(registrationKey)\n if (existing?.handle.isActive && existing.target === finalTarget) {\n existing.handle.callback = entry.callback\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n existing.handle.setOptions(optionsWithoutTarget)\n continue\n }\n\n if (existing) {\n if (existing.handle.isActive) {\n existing.handle.unregister()\n }\n registrations.delete(registrationKey)\n }\n\n const {\n target: _target,\n enabled: _enabled,\n ...restOptions\n } = entry.mergedOptions\n const optionsWithoutTarget = {\n ...restOptions,\n ...(entry.resolvedEnabled === undefined\n ? {}\n : { enabled: entry.resolvedEnabled }),\n }\n\n const handle = manager.register(\n entry.resolvedSequence,\n entry.callback,\n {\n ...optionsWithoutTarget,\n target: finalTarget,\n },\n )\n registrations.set(registrationKey, { handle, target: finalTarget })\n }\n },\n { immediate: true },\n )\n\n onUnmounted(() => {\n stopWatcher()\n for (const { handle } of registrations.values()) {\n if (handle.isActive) {\n handle.unregister()\n }\n }\n registrations.clear()\n })\n}\n\nfunction resolveMaybeRefOrGetter<T>(value: MaybeRefOrGetter<T>): T {\n return typeof value === 'function' ? (value as () => T)() : unref(value)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,SAAgB,mBACd,aACA,gBAA4D,EAAE,EACxD;CAMN,MAAM,iBAAiBA,yDAA0B;CACjD,MAAM,qDAA8B;CAEpC,MAAM,gCAAgB,IAAI,KAAiC;CAE3D,MAAM,mCACE;EACJ,MAAM,sBAAsB,wBAAwB,YAAY;EAChE,MAAM,wBAAwB,wBAAwB,cAAc;AAEpE,SAAO,oBAAoB,KAAK,KAAK,MAAM;GACzC,MAAM,mBAAmB,wBAAwB,IAAI,SAAS;GAC9D,MAAM,qBAAqB,IAAI,UAC3B,wBAAwB,IAAI,QAAQ,GACpC,EAAE;GAEN,MAAM,gBAAgB;IACpB,GAAG,eAAe;IAClB,GAAG;IACH,GAAG;IACJ;GAED,MAAM,6DAAsC,iBAAiB;GAE7D,MAAM,kBACJ,cAAc,YAAY,SACtB,SACA,wBAAwB,cAAc,QAAQ;GACpD,MAAM,iBACJ,cAAc,WAAW,SACrB,SACA,wBAAwB,cAAc,OAAc;AAE1D,UAAO;IACL,OAAO;IACP;IACA;IACA,UAAU,IAAI;IACd;IACA;IACA;IACD;IACD;KAEH,aAAa;EACZ,MAAM,2BAAW,IAAI,KAAa;EAClC,MAAM,WAID,EAAE;AAEP,OAAK,MAAM,SAAS,UAAU;AAC5B,OAAI,MAAM,iBAAiB,WAAW,EACpC;GAGF,MAAM,cACJ,MAAM,mBACL,OAAO,aAAa,cAAc,WAAW;AAEhD,OAAI,CAAC,YACH;GAGF,MAAM,kBAAkB,GAAG,MAAM,MAAM,GAAG,MAAM;AAChD,YAAS,IAAI,gBAAgB;AAC7B,YAAS,KAAK;IAAE;IAAiB;IAAO;IAAa,CAAC;;AAGxD,OAAK,MAAM,CAAC,KAAK,WAAW,CAAC,GAAG,cAAc,SAAS,CAAC,CACtD,KAAI,CAAC,SAAS,IAAI,IAAI,EAAE;AACtB,OAAI,OAAO,OAAO,SAChB,QAAO,OAAO,YAAY;AAE5B,iBAAc,OAAO,IAAI;;AAI7B,OAAK,MAAM,EAAE,iBAAiB,OAAO,iBAAiB,UAAU;GAC9D,MAAM,WAAW,cAAc,IAAI,gBAAgB;AACnD,OAAI,UAAU,OAAO,YAAY,SAAS,WAAW,aAAa;AAChE,aAAS,OAAO,WAAW,MAAM;IACjC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;IACV,MAAM,uBAAuB;KAC3B,GAAG;KACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;KACvC;AACD,aAAS,OAAO,WAAW,qBAAqB;AAChD;;AAGF,OAAI,UAAU;AACZ,QAAI,SAAS,OAAO,SAClB,UAAS,OAAO,YAAY;AAE9B,kBAAc,OAAO,gBAAgB;;GAGvC,MAAM,EACJ,QAAQ,SACR,SAAS,UACT,GAAG,gBACD,MAAM;GACV,MAAM,uBAAuB;IAC3B,GAAG;IACH,GAAI,MAAM,oBAAoB,SAC1B,EAAE,GACF,EAAE,SAAS,MAAM,iBAAiB;IACvC;GAED,MAAM,SAAS,QAAQ,SACrB,MAAM,kBACN,MAAM,UACN;IACE,GAAG;IACH,QAAQ;IACT,CACF;AACD,iBAAc,IAAI,iBAAiB;IAAE;IAAQ,QAAQ;IAAa,CAAC;;IAGvE,EAAE,WAAW,MAAM,CACpB;AAED,4BAAkB;AAChB,eAAa;AACb,OAAK,MAAM,EAAE,YAAY,cAAc,QAAQ,CAC7C,KAAI,OAAO,SACT,QAAO,YAAY;AAGvB,gBAAc,OAAO;GACrB;;AAGJ,SAAS,wBAA2B,OAA+B;AACjE,QAAO,OAAO,UAAU,aAAc,OAAmB,kBAAS,MAAM"}