@tanstack/lit-hotkeys 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,6 +67,9 @@ Type-safe keyboard shortcuts for the web. Template-string bindings, parsed objec
67
67
  > [!NOTE]
68
68
  > You may know **TanStack Hotkeys** by our adapter names, too!
69
69
  >
70
+ > - [**Alpine Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/alpine/quick-start)
71
+ > - [**Ember Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/ember/quick-start)
72
+ > - [**Octane Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/octane/quick-start)
70
73
  > - [**React Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/react/react-hotkeys)
71
74
  > - [**Preact Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/preact/preact-hotkeys)
72
75
  > - [**Solid Hotkeys**](https://tanstack.com/hotkeys/latest/docs/framework/solid/reference)
@@ -0,0 +1,11 @@
1
+ import { getHotkeyManager } from "@tanstack/hotkeys";
2
+ import { subscribePublishedAppHotkeys as subscribeAppHotkeys } from "@tanstack/hotkeys/adapter";
3
+
4
+ //#region src/controllers/_app-hotkeys.ts
5
+ /** The app hotkeys store published by `provideAppHotkeys`, if any. */
6
+ function getAppHotkeysStore() {
7
+ return getHotkeyManager().appHotkeys.state;
8
+ }
9
+
10
+ //#endregion
11
+ export { getAppHotkeysStore, subscribeAppHotkeys };
@@ -0,0 +1,48 @@
1
+ import { AppHotkey, AppHotkeyListItem, CustomHotkeys, Hotkey, RegisteredAppHotkeys } from "@tanstack/hotkeys";
2
+ import { ReactiveController, ReactiveControllerHost } from "lit";
3
+ //#region src/controllers/app-hotkeys.d.ts
4
+ /**
5
+ * A Lit ReactiveController for app hotkeys provided with `provideAppHotkeys`:
6
+ * the effective app hotkeys, a cheat sheet list with live status, and the
7
+ * user's custom hotkeys. Requests an update when any of them change.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * class CheatSheet extends LitElement {
12
+ * private app = new AppHotkeysController(this)
13
+ *
14
+ * render() {
15
+ * return html`<ul>
16
+ * ${this.app.list.map(
17
+ * ({ appHotkey, status }) => html`<li>${appHotkey.meta?.name}: ${status}</li>`,
18
+ * )}
19
+ * </ul>`
20
+ * }
21
+ * }
22
+ * ```
23
+ */
24
+ export declare class AppHotkeysController implements ReactiveController {
25
+ private _host;
26
+ private _unsubscribe;
27
+ constructor(_host: ReactiveControllerHost);
28
+ /** Effective app hotkeys, typed by `Register`. */
29
+ get appHotkeys(): RegisteredAppHotkeys;
30
+ /** Every app hotkey with its live status, in declaration order. */
31
+ get list(): Array<AppHotkeyListItem<RegisteredAppHotkeys[keyof RegisteredAppHotkeys]>>;
32
+ /** The user's hotkeys that differ from the defaults. */
33
+ get customHotkeys(): CustomHotkeys;
34
+ /**
35
+ * Sets the user's hotkey for an app hotkey. `null` unbinds it.
36
+ * Returns conflicting app hotkeys; when any exist, nothing changes.
37
+ */
38
+ setCustomHotkey(id: keyof RegisteredAppHotkeys & string, hotkey: Hotkey | null): Array<AppHotkey>;
39
+ /** Restores the default hotkey for an app hotkey. */
40
+ resetCustomHotkey(id: keyof RegisteredAppHotkeys & string): void;
41
+ /** Restores every default hotkey. */
42
+ resetCustomHotkeys(): void;
43
+ /** Other app hotkeys that would collide if `id` used `hotkey`. */
44
+ findConflicts(id: keyof RegisteredAppHotkeys & string, hotkey: Hotkey): Array<AppHotkey>;
45
+ hostConnected(): void;
46
+ hostDisconnected(): void;
47
+ }
48
+ //#endregion
@@ -0,0 +1,84 @@
1
+ import { getAppHotkeysStore, subscribeAppHotkeys } from "./_app-hotkeys.js";
2
+ import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
3
+
4
+ //#region src/controllers/app-hotkeys.ts
5
+ function requireStore() {
6
+ const store = getAppHotkeysStore();
7
+ if (!store) throw new Error("AppHotkeysController requires provideAppHotkeys()");
8
+ return store;
9
+ }
10
+ /**
11
+ * A Lit ReactiveController for app hotkeys provided with `provideAppHotkeys`:
12
+ * the effective app hotkeys, a cheat sheet list with live status, and the
13
+ * user's custom hotkeys. Requests an update when any of them change.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * class CheatSheet extends LitElement {
18
+ * private app = new AppHotkeysController(this)
19
+ *
20
+ * render() {
21
+ * return html`<ul>
22
+ * ${this.app.list.map(
23
+ * ({ appHotkey, status }) => html`<li>${appHotkey.meta?.name}: ${status}</li>`,
24
+ * )}
25
+ * </ul>`
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ var AppHotkeysController = class {
31
+ _host;
32
+ _unsubscribe;
33
+ constructor(_host) {
34
+ this._host = _host;
35
+ this._host.addController(this);
36
+ }
37
+ /** Effective app hotkeys, typed by `Register`. */
38
+ get appHotkeys() {
39
+ return requireStore().appHotkeys;
40
+ }
41
+ /** Every app hotkey with its live status, in declaration order. */
42
+ get list() {
43
+ return requireStore().getList(Array.from(getHotkeyManager().registrations.state.values()).map(toHotkeyRegistrationView));
44
+ }
45
+ /** The user's hotkeys that differ from the defaults. */
46
+ get customHotkeys() {
47
+ return requireStore().customHotkeys;
48
+ }
49
+ /**
50
+ * Sets the user's hotkey for an app hotkey. `null` unbinds it.
51
+ * Returns conflicting app hotkeys; when any exist, nothing changes.
52
+ */
53
+ setCustomHotkey(id, hotkey) {
54
+ return requireStore().setCustomHotkey(id, hotkey);
55
+ }
56
+ /** Restores the default hotkey for an app hotkey. */
57
+ resetCustomHotkey(id) {
58
+ requireStore().resetCustomHotkey(id);
59
+ }
60
+ /** Restores every default hotkey. */
61
+ resetCustomHotkeys() {
62
+ requireStore().resetCustomHotkeys();
63
+ }
64
+ /** Other app hotkeys that would collide if `id` used `hotkey`. */
65
+ findConflicts(id, hotkey) {
66
+ return requireStore().findConflicts(id, hotkey);
67
+ }
68
+ hostConnected() {
69
+ const update = () => this._host.requestUpdate();
70
+ const unsubscribeAppHotkeys = subscribeAppHotkeys(update);
71
+ const registrations = getHotkeyManager().registrations.subscribe(update);
72
+ this._unsubscribe = () => {
73
+ unsubscribeAppHotkeys();
74
+ registrations.unsubscribe();
75
+ };
76
+ }
77
+ hostDisconnected() {
78
+ this._unsubscribe?.();
79
+ this._unsubscribe = void 0;
80
+ }
81
+ };
82
+
83
+ //#endregion
84
+ export { AppHotkeysController };
@@ -1,4 +1,4 @@
1
- import { HeldModifierOptions, RegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { AppHotkey, HeldModifierOptions, RegisterableHotkey } from "@tanstack/hotkeys";
2
2
  import { ReactiveController, ReactiveControllerHost } from "lit";
3
3
  //#region src/controllers/hotkey-hint.d.ts
4
4
  /** Modifier hint controller. Getter arguments can read reactive host properties. */
@@ -9,7 +9,7 @@ export declare class HotkeyHintController implements ReactiveController {
9
9
  private unsubscribe?;
10
10
  private visible;
11
11
  /** Attaches to the host; getter arguments can read changing reactive properties. */
12
- constructor(host: ReactiveControllerHost, hotkey: RegisterableHotkey | (() => RegisterableHotkey), options?: HeldModifierOptions | (() => HeldModifierOptions));
12
+ constructor(host: ReactiveControllerHost, hotkey: RegisterableHotkey | AppHotkey | (() => RegisterableHotkey | AppHotkey), options?: HeldModifierOptions | (() => HeldModifierOptions));
13
13
  /** Current visibility; combine with the action’s enabled state when rendering. */
14
14
  get value(): boolean;
15
15
  /** Subscribes on connection and requests renders only when hint visibility changes. */
@@ -1,4 +1,5 @@
1
- import { getKeyStateTracker, matchesHeldModifiers } from "@tanstack/hotkeys";
1
+ import { getAppHotkeysStore, subscribeAppHotkeys } from "./_app-hotkeys.js";
2
+ import { getKeyStateTracker, isAppHotkey, matchesHeldModifiers, resolveAppHotkey } from "@tanstack/hotkeys";
2
3
 
3
4
  //#region src/controllers/hotkey-hint.ts
4
5
  /** Modifier hint controller. Getter arguments can read reactive host properties. */
@@ -22,10 +23,15 @@ var HotkeyHintController = class {
22
23
  /** Subscribes on connection and requests renders only when hint visibility changes. */
23
24
  hostConnected() {
24
25
  this.update();
25
- const subscription = getKeyStateTracker().store.subscribe(() => {
26
+ const refresh = () => {
26
27
  if (this.update()) this.host.requestUpdate();
27
- });
28
- this.unsubscribe = () => subscription.unsubscribe();
28
+ };
29
+ const subscription = getKeyStateTracker().store.subscribe(refresh);
30
+ const unsubscribeAppHotkeys = subscribeAppHotkeys(refresh);
31
+ this.unsubscribe = () => {
32
+ subscription.unsubscribe();
33
+ unsubscribeAppHotkeys();
34
+ };
29
35
  }
30
36
  /** Re-evaluates getter arguments when host properties trigger an update. */
31
37
  hostUpdate() {
@@ -39,7 +45,9 @@ var HotkeyHintController = class {
39
45
  }
40
46
  /** Recomputes visibility and reports whether the host needs another render. */
41
47
  update() {
42
- const next = matchesHeldModifiers(typeof this.hotkey === "function" ? this.hotkey() : this.hotkey, getKeyStateTracker().store.state.heldKeys, typeof this.options === "function" ? this.options() : this.options);
48
+ const value = typeof this.hotkey === "function" ? this.hotkey() : this.hotkey;
49
+ const hotkey = isAppHotkey(value) ? resolveAppHotkey(value, getAppHotkeysStore()).hotkey : value;
50
+ const next = hotkey !== null && matchesHeldModifiers(hotkey, getKeyStateTracker().store.state.heldKeys, typeof this.options === "function" ? this.options() : this.options);
43
51
  const changed = next !== this.visible;
44
52
  this.visible = next;
45
53
  return changed;
@@ -1,12 +1,14 @@
1
- import { Hotkey, HotkeyRecorderOptions } from "@tanstack/hotkeys";
1
+ import { Hotkey, HotkeyChordRecorderOptions, HotkeyRecorderOptions, HotkeySequence, HotkeySequenceRecorderOptions } from "@tanstack/hotkeys";
2
2
  import { ReactiveController, ReactiveControllerHost } from "lit";
3
3
  //#region src/controllers/hotkey-recorder.d.ts
4
4
  /**
5
5
  * A Lit ReactiveController that records keyboard shortcuts.
6
6
  *
7
- * Wraps the framework-agnostic `HotkeyRecorder` class, managing all the
8
- * complexity of capturing keyboard events, converting them to hotkey strings,
9
- * and handling edge cases like Escape to cancel or Backspace/Delete to clear.
7
+ * With the default `maxSteps: 1`, the first valid chord is recorded. With a
8
+ * larger `maxSteps`, each chord adds a step; the recording commits on Enter
9
+ * (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
10
+ * `onRecord` receives a chord (one step) or a sequence. Escape cancels;
11
+ * Backspace/Delete removes the last step or clears.
10
12
  *
11
13
  * @example
12
14
  * ```ts
@@ -41,21 +43,30 @@ export declare class HotkeyRecorderController implements ReactiveController {
41
43
  private _options;
42
44
  /** The recorder instance. */
43
45
  private _recorder;
46
+ private _optionOverrides;
44
47
  /** The unsubscribe function to unsubscribe from the recorder store. */
45
48
  private _unsubscribe;
46
49
  /** Whether recording is currently active. */
47
50
  private _isRecording;
48
- /** The currently recorded hotkey (for live preview). */
51
+ /** The last committed hotkey. */
49
52
  private _recordedHotkey;
53
+ /** Chords recorded so far in a multi-step recording. */
54
+ private _steps;
50
55
  /** Whether recording is currently active. */
51
56
  get isRecording(): boolean;
52
- /** The currently recorded hotkey (for live preview). */
57
+ /** The last committed hotkey: a chord, or a sequence of two or more steps. */
53
58
  get recordedHotkey(): Hotkey | null;
59
+ /** Chords recorded so far in a multi-step recording (for live preview). */
60
+ get steps(): HotkeySequence;
54
61
  /**
55
- * @param _host - The Lit component that owns this controller.
56
- * @param _options - Configuration options for the recorder.
62
+ * @param host - The Lit component that owns this controller.
63
+ * @param options - Options or a getter, read during recording.
57
64
  */
58
- constructor(_host: ReactiveControllerHost, _options: HotkeyRecorderOptions);
65
+ constructor(host: ReactiveControllerHost, options: HotkeyChordRecorderOptions);
66
+ constructor(host: ReactiveControllerHost, options: HotkeySequenceRecorderOptions);
67
+ constructor(host: ReactiveControllerHost, options: () => HotkeySequenceRecorderOptions);
68
+ constructor(host: ReactiveControllerHost, options: () => HotkeyChordRecorderOptions);
69
+ constructor(host: ReactiveControllerHost, options: () => HotkeyRecorderOptions);
59
70
  /** Subscribes to the recorder store and updates the internal state when changes occur. */
60
71
  hostConnected(): void;
61
72
  /** Unsubscribes from the recorder store and destroys the recorder instance to prevent memory leaks. */
@@ -68,5 +79,7 @@ export declare class HotkeyRecorderController implements ReactiveController {
68
79
  stopRecording(): void;
69
80
  /** Cancel recording without saving. */
70
81
  cancelRecording(): void;
82
+ /** Commit the recorded steps now (multi-step recordings). */
83
+ commitRecording(): void;
71
84
  }
72
85
  //#endregion
@@ -4,9 +4,11 @@ import { HotkeyRecorder } from "@tanstack/hotkeys";
4
4
  /**
5
5
  * A Lit ReactiveController that records keyboard shortcuts.
6
6
  *
7
- * Wraps the framework-agnostic `HotkeyRecorder` class, managing all the
8
- * complexity of capturing keyboard events, converting them to hotkey strings,
9
- * and handling edge cases like Escape to cancel or Backspace/Delete to clear.
7
+ * With the default `maxSteps: 1`, the first valid chord is recorded. With a
8
+ * larger `maxSteps`, each chord adds a step; the recording commits on Enter
9
+ * (unless `commitKeys: 'none'`), after `idleTimeoutMs`, or at `maxSteps`, and
10
+ * `onRecord` receives a chord (one step) or a sequence. Escape cancels;
11
+ * Backspace/Delete removes the last step or clears.
10
12
  *
11
13
  * @example
12
14
  * ```ts
@@ -41,37 +43,44 @@ var HotkeyRecorderController = class {
41
43
  _options;
42
44
  /** The recorder instance. */
43
45
  _recorder;
46
+ _optionOverrides = {};
44
47
  /** The unsubscribe function to unsubscribe from the recorder store. */
45
48
  _unsubscribe;
46
49
  /** Whether recording is currently active. */
47
50
  _isRecording = false;
48
- /** The currently recorded hotkey (for live preview). */
51
+ /** The last committed hotkey. */
49
52
  _recordedHotkey = null;
53
+ /** Chords recorded so far in a multi-step recording. */
54
+ _steps = [];
50
55
  /** Whether recording is currently active. */
51
56
  get isRecording() {
52
57
  return this._isRecording;
53
58
  }
54
- /** The currently recorded hotkey (for live preview). */
59
+ /** The last committed hotkey: a chord, or a sequence of two or more steps. */
55
60
  get recordedHotkey() {
56
61
  return this._recordedHotkey;
57
62
  }
58
- /**
59
- * @param _host - The Lit component that owns this controller.
60
- * @param _options - Configuration options for the recorder.
61
- */
63
+ /** Chords recorded so far in a multi-step recording (for live preview). */
64
+ get steps() {
65
+ return this._steps;
66
+ }
62
67
  constructor(_host, _options) {
63
68
  this._host = _host;
64
69
  this._options = _options;
65
- this._recorder = new HotkeyRecorder(_options);
70
+ this._recorder = new HotkeyRecorder(() => ({
71
+ ...typeof this._options === "function" ? this._options() : this._options,
72
+ ...this._optionOverrides
73
+ }));
66
74
  this._host.addController(this);
67
75
  }
68
76
  /** Subscribes to the recorder store and updates the internal state when changes occur. */
69
77
  hostConnected() {
70
78
  const subscription = this._recorder.store.subscribe(() => {
71
- const { isRecording, recordedHotkey } = this._recorder.store.state;
72
- if (isRecording !== this._isRecording || recordedHotkey !== this._recordedHotkey) {
79
+ const { isRecording, recordedHotkey, steps } = this._recorder.store.state;
80
+ if (isRecording !== this._isRecording || recordedHotkey !== this._recordedHotkey || steps !== this._steps) {
73
81
  this._isRecording = isRecording;
74
82
  this._recordedHotkey = recordedHotkey;
83
+ this._steps = steps;
75
84
  this._host.requestUpdate();
76
85
  }
77
86
  });
@@ -84,14 +93,14 @@ var HotkeyRecorderController = class {
84
93
  this._recorder.stop();
85
94
  this._isRecording = false;
86
95
  this._recordedHotkey = null;
96
+ this._steps = [];
87
97
  }
88
98
  /** Updates the recorder options (e.g. callbacks). */
89
99
  setOptions(options) {
90
- this._options = {
91
- ...this._options,
100
+ this._optionOverrides = {
101
+ ...this._optionOverrides,
92
102
  ...options
93
103
  };
94
- this._recorder.setOptions(this._options);
95
104
  }
96
105
  /** Start recording a new hotkey. */
97
106
  startRecording() {
@@ -105,6 +114,10 @@ var HotkeyRecorderController = class {
105
114
  cancelRecording() {
106
115
  this._recorder.cancel();
107
116
  }
117
+ /** Commit the recorded steps now (multi-step recordings). */
118
+ commitRecording() {
119
+ this._recorder.commit();
120
+ }
108
121
  };
109
122
 
110
123
  //#endregion
@@ -1,11 +1,10 @@
1
- import { HotkeyRegistrationView, SequenceRegistrationView } from "@tanstack/hotkeys";
1
+ import { HotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
  import { ReactiveController, ReactiveControllerHost } from "lit";
3
3
  //#region src/controllers/hotkey-registrations.d.ts
4
4
  /**
5
- * A Lit ReactiveController that tracks all hotkey and sequence registrations.
6
- *
7
- * Subscribes to the singleton HotkeyManager and SequenceManager stores and
8
- * triggers host updates whenever registrations change.
5
+ * A Lit ReactiveController that reads every hotkey registration, chords and
6
+ * sequences, from the singleton HotkeyManager and requests an update when
7
+ * they change. Narrow each view with `kind`.
9
8
  *
10
9
  * @example
11
10
  * ```ts
@@ -13,27 +12,21 @@ import { ReactiveController, ReactiveControllerHost } from "lit";
13
12
  * private registrations = new HotkeyRegistrationsController(this)
14
13
  *
15
14
  * render() {
16
- * return html`
17
- * <ul>
18
- * ${this.registrations.hotkeys.map(
19
- * (reg) => html`<li>${reg.options.meta?.name ?? reg.hotkey}</li>`,
20
- * )}
21
- * </ul>
22
- * `
15
+ * return html`<ul>
16
+ * ${this.registrations.hotkeys.map(
17
+ * (reg) => html`<li>${reg.options.meta?.name}: ${formatForDisplay(reg.hotkey)}</li>`,
18
+ * )}
19
+ * </ul>`
23
20
  * }
24
21
  * }
25
22
  * ```
26
23
  */
27
24
  export declare class HotkeyRegistrationsController implements ReactiveController {
28
25
  private _host;
29
- private _unsubscribeHotkeys;
30
- private _unsubscribeSequences;
26
+ private _unsubscribe;
31
27
  private _hotkeys;
32
- private _sequences;
33
- /** All registered hotkeys (public view, no callbacks). */
28
+ /** Every registration, chords and sequences (public view, no callbacks). */
34
29
  get hotkeys(): Array<HotkeyRegistrationView>;
35
- /** All registered sequences. */
36
- get sequences(): Array<SequenceRegistrationView>;
37
30
  constructor(_host: ReactiveControllerHost);
38
31
  hostConnected(): void;
39
32
  hostDisconnected(): void;
@@ -1,11 +1,10 @@
1
- import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
1
+ import { getHotkeyManager, toHotkeyRegistrationView } from "@tanstack/hotkeys";
2
2
 
3
3
  //#region src/controllers/hotkey-registrations.ts
4
4
  /**
5
- * A Lit ReactiveController that tracks all hotkey and sequence registrations.
6
- *
7
- * Subscribes to the singleton HotkeyManager and SequenceManager stores and
8
- * triggers host updates whenever registrations change.
5
+ * A Lit ReactiveController that reads every hotkey registration, chords and
6
+ * sequences, from the singleton HotkeyManager and requests an update when
7
+ * they change. Narrow each view with `kind`.
9
8
  *
10
9
  * @example
11
10
  * ```ts
@@ -13,58 +12,41 @@ import { getHotkeyManager, getSequenceManager, toHotkeyRegistrationView } from "
13
12
  * private registrations = new HotkeyRegistrationsController(this)
14
13
  *
15
14
  * render() {
16
- * return html`
17
- * <ul>
18
- * ${this.registrations.hotkeys.map(
19
- * (reg) => html`<li>${reg.options.meta?.name ?? reg.hotkey}</li>`,
20
- * )}
21
- * </ul>
22
- * `
15
+ * return html`<ul>
16
+ * ${this.registrations.hotkeys.map(
17
+ * (reg) => html`<li>${reg.options.meta?.name}: ${formatForDisplay(reg.hotkey)}</li>`,
18
+ * )}
19
+ * </ul>`
23
20
  * }
24
21
  * }
25
22
  * ```
26
23
  */
27
24
  var HotkeyRegistrationsController = class {
28
25
  _host;
29
- _unsubscribeHotkeys;
30
- _unsubscribeSequences;
26
+ _unsubscribe;
31
27
  _hotkeys = [];
32
- _sequences = [];
33
- /** All registered hotkeys (public view, no callbacks). */
28
+ /** Every registration, chords and sequences (public view, no callbacks). */
34
29
  get hotkeys() {
35
30
  return this._hotkeys;
36
31
  }
37
- /** All registered sequences. */
38
- get sequences() {
39
- return this._sequences;
40
- }
41
32
  constructor(_host) {
42
33
  this._host = _host;
43
34
  this._host.addController(this);
44
35
  }
45
36
  hostConnected() {
46
- const hotkeyManager = getHotkeyManager();
47
- const sequenceManager = getSequenceManager();
48
- const hotkeysSub = hotkeyManager.registrations.subscribe(() => {
49
- this._hotkeys = Array.from(hotkeyManager.registrations.state.values()).map(toHotkeyRegistrationView);
50
- this._host.requestUpdate();
51
- });
52
- const sequencesSub = sequenceManager.registrations.subscribe(() => {
53
- this._sequences = Array.from(sequenceManager.registrations.state.values());
37
+ const manager = getHotkeyManager();
38
+ const read = () => Array.from(manager.registrations.state.values()).map(toHotkeyRegistrationView);
39
+ const subscription = manager.registrations.subscribe(() => {
40
+ this._hotkeys = read();
54
41
  this._host.requestUpdate();
55
42
  });
56
- this._unsubscribeHotkeys = () => hotkeysSub.unsubscribe();
57
- this._unsubscribeSequences = () => sequencesSub.unsubscribe();
58
- this._hotkeys = Array.from(hotkeyManager.registrations.state.values()).map(toHotkeyRegistrationView);
59
- this._sequences = Array.from(sequenceManager.registrations.state.values());
43
+ this._unsubscribe = () => subscription.unsubscribe();
44
+ this._hotkeys = read();
60
45
  }
61
46
  hostDisconnected() {
62
- this._unsubscribeHotkeys?.();
63
- this._unsubscribeSequences?.();
64
- this._unsubscribeHotkeys = void 0;
65
- this._unsubscribeSequences = void 0;
47
+ this._unsubscribe?.();
48
+ this._unsubscribe = void 0;
66
49
  this._hotkeys = [];
67
- this._sequences = [];
68
50
  }
69
51
  };
70
52
 
@@ -1,18 +1,42 @@
1
- import { HotkeyCallback, HotkeyOptions, RegisterableHotkey } from "@tanstack/hotkeys";
1
+ import { AppHotkey, HotkeyCallback, RegisterableHotkey, Target } from "@tanstack/hotkeys";
2
+ import { AdapterAppHotkeyOptions, AdapterChordOptions, AdapterCommonOptions, AdapterHotkeyOptions, AdapterOptionsFor, AdapterSequenceOptions } from "@tanstack/hotkeys/adapter";
2
3
  import { ReactiveController, ReactiveControllerHost } from "lit";
3
4
  //#region src/controllers/hotkey.d.ts
5
+ /** The element to listen on. A `null` target skips registration until it is set. */
6
+ export type HotkeyControllerTarget = Target | null;
7
+ /** Options shared by chords and sequences. */
8
+ export type HotkeyControllerOptions = AdapterHotkeyOptions<HotkeyControllerTarget>;
9
+ /** Options for a chord such as `'Mod+S'`. */
10
+ export type HotkeyControllerChordOptions = AdapterChordOptions<HotkeyControllerTarget>;
11
+ /** Options for a sequence such as `['G', 'G']`. */
12
+ export type HotkeyControllerSequenceOptions = AdapterSequenceOptions<HotkeyControllerTarget>;
13
+ /** Options for every kind; each applies to the kinds that support it. */
14
+ export type HotkeyControllerCommonOptions = AdapterCommonOptions<HotkeyControllerTarget>;
15
+ /** The options type for a hotkey: chord options for chords, sequence options for sequences. */
16
+ export type HotkeyControllerOptionsFor<T extends RegisterableHotkey> = AdapterOptionsFor<T, HotkeyControllerTarget>;
17
+ /** Call-site options for an app hotkey, typed by its declared default kind. */
18
+ export type AppHotkeyControllerOptions<T extends AppHotkey = AppHotkey> = AdapterAppHotkeyOptions<T, HotkeyControllerTarget>;
19
+ type MaybeGetter<T> = T | (() => T);
4
20
  /**
5
- * A Lit ReactiveController that registers a keyboard hotkey when the host
6
- * element is connected and unregisters it when the host is disconnected.
21
+ * A Lit ReactiveController that registers a hotkey when the host element is
22
+ * connected and unregisters it when the host is disconnected. The hotkey can
23
+ * be a chord such as `'Mod+S'`, a sequence such as `['G', 'G']`, or an app
24
+ * hotkey from `defineAppHotkeys`, which uses its custom hotkey from
25
+ * `provideAppHotkeys`.
26
+ *
27
+ * Chords accept `requireReset`; sequences accept `timeout`. Pass a getter for
28
+ * the hotkey or options to re-read them after each host update.
7
29
  *
8
30
  * @example
9
31
  * ```ts
10
32
  * class MyElement extends LitElement {
11
- * private hotkey = new HotkeyController(this, 'Mod+S', () => this.save())
33
+ * private save = new HotkeyController(this, 'Mod+S', () => this.doSave())
34
+ * private top = new HotkeyController(this, ['G', 'G'], () => this.scrollToTop())
12
35
  *
13
36
  * constructor() {
14
37
  * super()
15
- * this.addController(this.hotkey)
38
+ * this.addController(this.save)
39
+ * this.addController(this.top)
16
40
  * }
17
41
  * }
18
42
  * ```
@@ -20,23 +44,25 @@ import { ReactiveController, ReactiveControllerHost } from "lit";
20
44
  export declare class HotkeyController implements ReactiveController {
21
45
  private _host;
22
46
  private _hotkey;
23
- private _callback;
24
47
  private _options;
25
- /** The hotkey registration handle. */
26
- private _registration;
27
- /**
28
- * @param _host - The Lit component that owns this controller (use `this` and pass it to `addController()`).
29
- * @param _hotkey - The key or key combo to listen for (e.g. `'Mod+S'` or a raw hotkey object).
30
- * @param _callback - Function to run when the hotkey is pressed; called with the host as `this`.
31
- * @param _options - Optional registration options (target, platform, enabled, etc.).
32
- */
33
- constructor(_host: ReactiveControllerHost, _hotkey: RegisterableHotkey, _callback: HotkeyCallback, _options?: HotkeyOptions);
48
+ private _bindings;
49
+ private _connected;
50
+ private _boundCallback;
51
+ private _unsubscribe;
34
52
  /**
35
- * Registers the hotkey with the global manager when the host is connected to the DOM.
36
- * Skips registration if no target is available (e.g. no document or options.target is null).
53
+ * @param host - The Lit component that owns this controller. Add the controller with `addController()`.
54
+ * @param hotkey - The chord, sequence, or app hotkey to register, or a getter. A getter is read on connection and after host updates.
55
+ * @param callback - Called with the host as `this`.
56
+ * @param options - Options or a getter. Property getters are read on connection and after host updates.
37
57
  */
58
+ constructor(host: ReactiveControllerHost, hotkey: MaybeGetter<AppHotkey>, callback: HotkeyCallback, options?: MaybeGetter<AppHotkeyControllerOptions>);
59
+ constructor(host: ReactiveControllerHost, hotkey: MaybeGetter<RegisterableHotkey>, callback: HotkeyCallback, options?: MaybeGetter<HotkeyControllerCommonOptions>);
60
+ /** Registers when connected and a target is available. */
38
61
  hostConnected(): void;
39
- /** Unregisters the hotkey when the host is disconnected from the DOM. */
62
+ /** Refreshes options after rendering, when scoped targets are available. */
63
+ hostUpdated(): void;
64
+ private _sync;
65
+ /** Releases registrations when the host disconnects. */
40
66
  hostDisconnected(): void;
41
67
  }
42
68
  //#endregion