@tanstack/hotkeys 0.11.0 → 0.12.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/dist/adapter.d.ts CHANGED
@@ -1,27 +1,116 @@
1
- import { HotkeyCallback, RegisterableHotkey } from "./hotkey.types.js";
2
- import { HotkeyOptions } from "./hotkey-manager.js";
3
- import { HotkeySequence, SequenceOptions } from "./sequence-manager.js";
1
+ import { HotkeyCallback, HotkeyChord, HotkeySequence, RegisterableHotkey, RegisterableHotkeyChord } from "./hotkey.types.js";
2
+ import { HotkeyChordOptions, HotkeyOptions, HotkeySequenceOptions, Target } from "./hotkey-manager.js";
3
+ import { AppHotkey, AppHotkeysStore } from "./app-hotkeys.js";
4
4
  //#region src/adapter.d.ts
5
- /** A hotkey and its callback, with optional per-binding options. */
6
- export interface HotkeyDefinition {
7
- hotkey: RegisterableHotkey;
5
+ /**
6
+ * A hotkey and its callback, with optional per-binding options. Chords accept
7
+ * `requireReset`; sequences accept `timeout`.
8
+ */
9
+ export type HotkeyDefinition = {
10
+ hotkey: RegisterableHotkeyChord;
8
11
  callback: HotkeyCallback;
9
- options?: HotkeyOptions;
10
- }
11
- /** A sequence and its callback, with optional per-binding options. */
12
- export interface HotkeySequenceDefinition {
13
- sequence: HotkeySequence;
12
+ options?: HotkeyChordOptions;
13
+ } | {
14
+ hotkey: HotkeySequence;
14
15
  callback: HotkeyCallback;
15
- options?: SequenceOptions;
16
- }
16
+ options?: HotkeySequenceOptions;
17
+ };
18
+ /** Options shared by every definition. Each option applies only to the kinds that support it. */
19
+ export type HotkeyBindingsCommonOptions = HotkeyChordOptions & HotkeySequenceOptions;
17
20
  /** Reconciles adapter registrations while retaining handles for option updates. */
18
21
  export declare function createHotkeyBindings(): {
19
- update(definitions: Array<HotkeyDefinition>, commonOptions?: HotkeyOptions): void;
20
- destroy: () => void;
22
+ /**
23
+ * Registers new definitions, updates existing ones in place, and removes
24
+ * stale ones. Definitions with an empty sequence or a null target are skipped.
25
+ */
26
+ update(definitions: ReadonlyArray<HotkeyDefinition>, commonOptions?: HotkeyBindingsCommonOptions): void;
27
+ destroy(): void;
21
28
  };
22
- /** Reconciles sequence bindings; empty sequences and explicit null targets are skipped. */
23
- export declare function createHotkeySequenceBindings(): {
24
- update(definitions: Array<HotkeySequenceDefinition>, commonOptions?: SequenceOptions): void;
25
- destroy: () => void;
29
+ /** Options with a framework-specific target, such as a ref. */
30
+ export type WithTarget<TOptions, TTarget> = Omit<TOptions, 'target' | 'appHotkeyId'> & {
31
+ /** The element to listen on. Defaults to document. */
32
+ target?: TTarget;
26
33
  };
34
+ /** Options shared by chords and sequences, with a framework target. */
35
+ export type AdapterHotkeyOptions<TTarget> = WithTarget<HotkeyOptions, TTarget>;
36
+ /** Options for a chord such as `'Mod+S'`, with a framework target. */
37
+ export type AdapterChordOptions<TTarget> = WithTarget<HotkeyChordOptions, TTarget>;
38
+ /** Options for a sequence such as `['G', 'G']`, with a framework target. */
39
+ export type AdapterSequenceOptions<TTarget> = WithTarget<HotkeySequenceOptions, TTarget>;
40
+ /** Options applied to every hotkey, such as defaults. Each applies to the kinds that support it. */
41
+ export type AdapterCommonOptions<TTarget> = AdapterChordOptions<TTarget> & AdapterSequenceOptions<TTarget>;
42
+ /** Chord options for chords, sequence options for sequences. */
43
+ export type AdapterOptionsFor<T extends RegisterableHotkey, TTarget> = [T] extends [never] ? AdapterCommonOptions<TTarget> : T extends readonly [unknown] ? AdapterChordOptions<TTarget> : T extends ReadonlyArray<unknown> ? AdapterSequenceOptions<TTarget> : AdapterChordOptions<TTarget>;
44
+ /**
45
+ * Call-site options for an app hotkey. `meta` comes from the declaration.
46
+ * Kind-specific options follow the declared default: `requireReset` for a
47
+ * chord, `timeout` for a sequence, both for an app hotkey without a default.
48
+ * If the user rebinds it to the other kind, options for the old kind no
49
+ * longer apply.
50
+ */
51
+ export type AdapterAppHotkeyOptions<T extends AppHotkey, TTarget> = Omit<[T['defaultHotkey']] extends [HotkeySequence] ? AdapterSequenceOptions<TTarget> : [T['defaultHotkey']] extends [HotkeyChord] ? AdapterChordOptions<TTarget> : AdapterCommonOptions<TTarget>, 'meta'>;
52
+ /**
53
+ * One hotkey and its callback for adapter APIs such as `useHotkeys`. Chords
54
+ * accept `requireReset`; sequences accept `timeout`; hotkeys of unknown kind
55
+ * accept only shared options.
56
+ */
57
+ export type AdapterHotkeyDefinition<TTarget> = {
58
+ /** A chord string such as `'Mod+S'`, or a RawHotkey object */
59
+ hotkey: RegisterableHotkeyChord;
60
+ /** The function to call when the hotkey is pressed */
61
+ callback: HotkeyCallback;
62
+ /** Per-hotkey options (merged on top of common options) */
63
+ options?: AdapterChordOptions<TTarget>;
64
+ } | {
65
+ /** A sequence such as `['G', 'G']` */
66
+ hotkey: HotkeySequence;
67
+ callback: HotkeyCallback;
68
+ options?: AdapterSequenceOptions<TTarget>;
69
+ } | {
70
+ /**
71
+ * An app hotkey from `defineAppHotkeys`. Kind-specific options apply
72
+ * only while its effective hotkey has that kind.
73
+ */
74
+ hotkey: AppHotkey;
75
+ callback: HotkeyCallback;
76
+ options?: Omit<AdapterCommonOptions<TTarget>, 'meta'>;
77
+ } | {
78
+ /**
79
+ * A hotkey whose kind is not known statically, such as user data typed
80
+ * `Hotkey`. Accepts only shared options.
81
+ */
82
+ hotkey: RegisterableHotkey;
83
+ callback: HotkeyCallback;
84
+ options?: AdapterHotkeyOptions<TTarget>;
85
+ };
86
+ /** Context for `prepareHotkeyDefinitions`. */
87
+ export interface PrepareHotkeyDefinitionsOptions<TTarget> {
88
+ /** The provider's app hotkeys store, so app hotkeys use custom hotkeys. */
89
+ appHotkeysStore?: AppHotkeysStore | null;
90
+ /** Provider defaults, applied first. */
91
+ defaults?: AdapterCommonOptions<TTarget>;
92
+ /** Options for every definition, applied after app hotkey declarations. */
93
+ commonOptions?: AdapterCommonOptions<TTarget>;
94
+ /**
95
+ * Resolves a framework target, such as a ref. Return `undefined` for the
96
+ * document or `null` to skip registration until the target exists.
97
+ * Without it, targets pass through and `null` means document.
98
+ */
99
+ resolveTarget?: (target: TTarget) => Target | null | undefined;
100
+ }
101
+ /**
102
+ * Converts adapter definitions into `createHotkeyBindings` definitions.
103
+ * Options merge in this order: defaults, app hotkey declaration, common
104
+ * options, per-definition options. App hotkeys resolve their effective hotkey
105
+ * from the store, and an unbound app hotkey keeps its position but registers
106
+ * nothing.
107
+ */
108
+ export declare function prepareHotkeyDefinitions<TTarget>(definitions: ReadonlyArray<AdapterHotkeyDefinition<TTarget>>, options?: PrepareHotkeyDefinitionsOptions<TTarget>): Array<HotkeyDefinition>;
109
+ /**
110
+ * Calls `onChange` when the store published with `publishAppHotkeysStore` or
111
+ * `provideAppHotkeys` changes, or when its app hotkeys or custom hotkeys
112
+ * change. For adapters that resolve app hotkeys without a provider. Returns an
113
+ * unsubscribe function.
114
+ */
115
+ export declare function subscribePublishedAppHotkeys(onChange: () => void): () => void;
27
116
  //#endregion
package/dist/adapter.js CHANGED
@@ -1,20 +1,51 @@
1
1
  import { detectPlatform } from "./platform.js";
2
- import { normalizeRegisterableHotkey, parseHotkey } from "./parse.js";
2
+ import { isHotkeySequence, normalizeRegisterableHotkey, parseHotkey } from "./parse.js";
3
3
  import { defaultHotkeyOptions, getDefaultIgnoreInputs } from "./_registration.js";
4
4
  import { getHotkeyManager } from "./hotkey-manager.js";
5
- import { DEFAULT_SEQUENCE_TIMEOUT, getSequenceManager } from "./sequence-manager.js";
5
+ import { getAppHotkeyRegistrationOptions, isAppHotkey, resolveAppHotkey } from "./app-hotkeys.js";
6
6
 
7
7
  //#region src/adapter.ts
8
- function createBindings() {
8
+ /** Reconciles adapter registrations while retaining handles for option updates. */
9
+ function createHotkeyBindings() {
9
10
  const registrations = /* @__PURE__ */ new Map();
10
11
  return {
11
- update(bindings) {
12
- const keys = new Set(bindings.map((binding) => binding.key));
12
+ /**
13
+ * Registers new definitions, updates existing ones in place, and removes
14
+ * stale ones. Definitions with an empty sequence or a null target are skipped.
15
+ */
16
+ update(definitions, commonOptions = {}) {
17
+ const prepared = [];
18
+ definitions.forEach((definition, index) => {
19
+ const merged = {
20
+ ...commonOptions,
21
+ ...definition.options
22
+ };
23
+ const options = {
24
+ ...defaultHotkeyOptions,
25
+ ...merged,
26
+ ignoreInputs: merged.ignoreInputs,
27
+ target: "target" in merged ? merged.target ?? null : typeof document === "undefined" ? null : document,
28
+ platform: merged.platform ?? detectPlatform()
29
+ };
30
+ if (!options.target || isHotkeySequence(definition.hotkey) && definition.hotkey.length === 0) return;
31
+ const hotkey = normalizeRegisterableHotkey(definition.hotkey, options.platform);
32
+ const first = isHotkeySequence(hotkey) ? hotkey[0] : hotkey;
33
+ options.ignoreInputs ??= getDefaultIgnoreInputs(parseHotkey(first, options.platform));
34
+ if (isHotkeySequence(hotkey)) delete options.requireReset;
35
+ else delete options.timeout;
36
+ prepared.push({
37
+ key: `${index}:${isHotkeySequence(hotkey) ? hotkey.join(" ") : hotkey}`,
38
+ callback: definition.callback,
39
+ options,
40
+ register: () => getHotkeyManager().register(hotkey, definition.callback, options)
41
+ });
42
+ });
43
+ const keys = new Set(prepared.map((binding) => binding.key));
13
44
  for (const [key, registration] of registrations) if (!keys.has(key)) {
14
45
  registration.handle.unregister();
15
46
  registrations.delete(key);
16
47
  }
17
- for (const binding of bindings) {
48
+ for (const binding of prepared) {
18
49
  const previous = registrations.get(binding.key);
19
50
  const { target, platform } = binding.options;
20
51
  if (previous?.handle.isActive && previous.target === target && previous.platform === platform) {
@@ -42,68 +73,62 @@ function createBindings() {
42
73
  }
43
74
  };
44
75
  }
45
- function resolveOptions(options) {
46
- return {
47
- ...defaultHotkeyOptions,
48
- requireReset: false,
49
- ...options,
50
- ignoreInputs: options.ignoreInputs,
51
- target: "target" in options ? options.target ?? null : typeof document === "undefined" ? null : document,
52
- platform: options.platform ?? detectPlatform()
53
- };
76
+ /**
77
+ * Converts adapter definitions into `createHotkeyBindings` definitions.
78
+ * Options merge in this order: defaults, app hotkey declaration, common
79
+ * options, per-definition options. App hotkeys resolve their effective hotkey
80
+ * from the store, and an unbound app hotkey keeps its position but registers
81
+ * nothing.
82
+ */
83
+ function prepareHotkeyDefinitions(definitions, options = {}) {
84
+ return definitions.map((definition) => {
85
+ let hotkey = definition.hotkey;
86
+ let declared = {};
87
+ if (isAppHotkey(hotkey)) {
88
+ const appHotkey = resolveAppHotkey(hotkey, options.appHotkeysStore);
89
+ declared = getAppHotkeyRegistrationOptions(appHotkey);
90
+ hotkey = appHotkey.hotkey ?? [];
91
+ }
92
+ const { target, ...merged } = {
93
+ ...options.defaults,
94
+ ...declared,
95
+ ...options.commonOptions,
96
+ ...definition.options
97
+ };
98
+ const resolved = target === void 0 ? void 0 : options.resolveTarget ? options.resolveTarget(target) : target ?? void 0;
99
+ return {
100
+ hotkey,
101
+ callback: definition.callback,
102
+ options: resolved === void 0 ? merged : {
103
+ ...merged,
104
+ target: resolved
105
+ }
106
+ };
107
+ });
54
108
  }
55
- /** Reconciles adapter registrations while retaining handles for option updates. */
56
- function createHotkeyBindings() {
57
- const bindings = createBindings();
58
- return {
59
- update(definitions, commonOptions = {}) {
60
- const prepared = [];
61
- definitions.forEach((definition, index) => {
62
- const options = resolveOptions({
63
- ...commonOptions,
64
- ...definition.options
65
- });
66
- if (!options.target) return;
67
- const hotkey = normalizeRegisterableHotkey(definition.hotkey, options.platform);
68
- options.ignoreInputs ??= getDefaultIgnoreInputs(parseHotkey(hotkey, options.platform));
69
- prepared.push({
70
- key: `${index}:${hotkey}`,
71
- callback: definition.callback,
72
- options,
73
- register: () => getHotkeyManager().register(hotkey, definition.callback, options)
74
- });
75
- });
76
- bindings.update(prepared);
77
- },
78
- destroy: bindings.destroy
109
+ /**
110
+ * Calls `onChange` when the store published with `publishAppHotkeysStore` or
111
+ * `provideAppHotkeys` changes, or when its app hotkeys or custom hotkeys
112
+ * change. For adapters that resolve app hotkeys without a provider. Returns an
113
+ * unsubscribe function.
114
+ */
115
+ function subscribePublishedAppHotkeys(onChange) {
116
+ const slot = getHotkeyManager().appHotkeys;
117
+ let inner;
118
+ const follow = () => {
119
+ inner?.unsubscribe();
120
+ inner = slot.state?.store.subscribe(onChange);
79
121
  };
80
- }
81
- /** Reconciles sequence bindings; empty sequences and explicit null targets are skipped. */
82
- function createHotkeySequenceBindings() {
83
- const bindings = createBindings();
84
- return {
85
- update(definitions, commonOptions = {}) {
86
- const prepared = [];
87
- definitions.forEach((definition, index) => {
88
- const options = resolveOptions({
89
- timeout: DEFAULT_SEQUENCE_TIMEOUT,
90
- ...commonOptions,
91
- ...definition.options
92
- });
93
- if (!options.target || definition.sequence.length === 0) return;
94
- options.ignoreInputs ??= getDefaultIgnoreInputs(parseHotkey(definition.sequence[0], options.platform));
95
- prepared.push({
96
- key: `${index}:${JSON.stringify(definition.sequence)}`,
97
- callback: definition.callback,
98
- options,
99
- register: () => getSequenceManager().register(definition.sequence, definition.callback, options)
100
- });
101
- });
102
- bindings.update(prepared);
103
- },
104
- destroy: bindings.destroy
122
+ follow();
123
+ const outer = slot.subscribe(() => {
124
+ follow();
125
+ onChange();
126
+ });
127
+ return () => {
128
+ outer.unsubscribe();
129
+ inner?.unsubscribe();
105
130
  };
106
131
  }
107
132
 
108
133
  //#endregion
109
- export { createHotkeyBindings, createHotkeySequenceBindings };
134
+ export { createHotkeyBindings, prepareHotkeyDefinitions, subscribePublishedAppHotkeys };
@@ -0,0 +1,239 @@
1
+ import { Hotkey, HotkeyChord, HotkeyMeta, HotkeySequence } from "./hotkey.types.js";
2
+ import { HotkeyChordOptions, HotkeyRegistrationView, HotkeySequenceOptions } from "./hotkey-manager.js";
3
+ import { Store } from "@tanstack/store";
4
+ //#region src/app-hotkeys.d.ts
5
+ type Platform = 'mac' | 'windows' | 'linux';
6
+ type DeclaredOptionKeys = 'conflictBehavior' | 'eventType' | 'ignoreInputs' | 'preventDefault' | 'stopPropagation';
7
+ /** Declared options for an app hotkey whose default is a chord. */
8
+ export type AppHotkeyChordOptions = Pick<HotkeyChordOptions, DeclaredOptionKeys | 'requireReset'>;
9
+ /** Declared options for an app hotkey whose default is a sequence. */
10
+ export type AppHotkeySequenceOptions = Pick<HotkeySequenceOptions, DeclaredOptionKeys | 'timeout'>;
11
+ /** Every declared option. Kind-specific options apply only while the effective hotkey has that kind. */
12
+ export type AppHotkeyOptions = AppHotkeyChordOptions & AppHotkeySequenceOptions;
13
+ /** One declaration passed to `defineAppHotkeys`. */
14
+ export interface AppHotkeyInput {
15
+ /** Default hotkey: a chord, a sequence, or `null` for an action without a default. */
16
+ hotkey: Hotkey | null;
17
+ /** Display metadata for cheat sheets and settings screens. */
18
+ meta?: HotkeyMeta;
19
+ /**
20
+ * Label for where this hotkey applies, such as `'editor'`. App hotkeys with
21
+ * different contexts may share a hotkey; one without a context conflicts with
22
+ * any app hotkey that shares its hotkey. It does not affect matching.
23
+ */
24
+ context?: string;
25
+ /**
26
+ * Options that describe the hotkey itself. Chord defaults accept
27
+ * `requireReset`; sequence defaults accept `timeout`.
28
+ */
29
+ options?: AppHotkeyOptions;
30
+ }
31
+ /**
32
+ * A declared app hotkey. It never holds a callback: register it with
33
+ * `useHotkey(appHotkey, callback)` or the equivalent adapter API.
34
+ *
35
+ * `TDefault` is the kind of the declared default (`HotkeyChord`,
36
+ * `HotkeySequence`, or `null`). Adapters use it to type kind-specific options.
37
+ */
38
+ export interface AppHotkey<TId extends string = string, TDefault extends HotkeyChord | HotkeySequence | null = HotkeyChord | HotkeySequence | null> {
39
+ /** Brand that distinguishes app hotkeys from raw hotkey objects. */
40
+ readonly '~appHotkey': true;
41
+ /** Stable id. Saved custom hotkeys are keyed by this value. */
42
+ readonly id: TId;
43
+ /** Effective hotkey: the user's custom hotkey, or the default. `null` means unbound. */
44
+ readonly hotkey: Hotkey | null;
45
+ /** Hotkey declared in `defineAppHotkeys`. */
46
+ readonly defaultHotkey: TDefault;
47
+ readonly meta?: HotkeyMeta;
48
+ readonly context?: string;
49
+ readonly options?: AppHotkeyOptions;
50
+ }
51
+ /** A map of app hotkeys keyed by id. */
52
+ export type AppHotkeys = Readonly<Record<string, AppHotkey>>;
53
+ type AllowedOptions<THotkey> = THotkey extends HotkeySequence ? AppHotkeySequenceOptions : THotkey extends null ? AppHotkeyOptions : AppHotkeyChordOptions;
54
+ type StrictOptions<TOptions, TAllowed> = TOptions extends object ? { [P in Exclude<keyof TOptions, keyof TAllowed>]: never; } & TAllowed : TAllowed;
55
+ /** Rejects options that do not apply to each declaration's default kind. */
56
+ type ValidatedAppHotkeyInputs<T> = { [K in keyof T]: T[K] extends {
57
+ hotkey: infer THotkey;
58
+ } ? Omit<T[K], 'options'> & {
59
+ options?: StrictOptions<T[K] extends {
60
+ options: infer TOptions;
61
+ } ? TOptions : {}, AllowedOptions<THotkey>>;
62
+ } : never; };
63
+ /** The kind of a declared default hotkey; a one-step sequence is a chord. */
64
+ export type AppHotkeyDefaultKind<THotkey> = THotkey extends null ? null : THotkey extends readonly [unknown] ? HotkeyChord : THotkey extends ReadonlyArray<unknown> ? HotkeySequence : HotkeyChord;
65
+ /** The app hotkeys returned by `defineAppHotkeys` for a declaration object. */
66
+ export type DefinedAppHotkeys<T extends Record<string, AppHotkeyInput>> = { readonly [K in keyof T & string]: AppHotkey<K, AppHotkeyDefaultKind<T[K]['hotkey']>>; };
67
+ /**
68
+ * Register your app hotkeys to type `useAppHotkeys()` and `CustomHotkeys`.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * declare module '@tanstack/react-hotkeys' {
73
+ * interface Register {
74
+ * appHotkeys: typeof appHotkeys
75
+ * }
76
+ * }
77
+ * ```
78
+ */
79
+ export interface Register {}
80
+ /** The app hotkeys type from `Register`, or a generic map when none is registered. */
81
+ export type RegisteredAppHotkeys = Register extends {
82
+ appHotkeys: infer T extends AppHotkeys;
83
+ } ? T : AppHotkeys;
84
+ /** Ids of the registered app hotkeys. */
85
+ export type AppHotkeyId = keyof RegisteredAppHotkeys & string;
86
+ /**
87
+ * The user's hotkeys that differ from the declared defaults, keyed by app
88
+ * hotkey id. This is the shape to save. `null` means the user unbound it.
89
+ */
90
+ export type CustomHotkeys<T extends AppHotkeys = RegisteredAppHotkeys> = { -readonly [K in keyof T]?: Hotkey | null; };
91
+ /**
92
+ * Declares every app hotkey up front, without registering any, so cheat
93
+ * sheets and settings screens can list hotkeys whose components have not
94
+ * mounted. Pass the result to `HotkeysProvider` as `appHotkeys`.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * export const appHotkeys = defineAppHotkeys({
99
+ * 'doc.save': { hotkey: 'Mod+S', meta: { name: 'Save', group: 'Document' } },
100
+ * 'nav.home': { hotkey: ['G', 'H'], meta: { name: 'Go home' }, options: { timeout: 800 } },
101
+ * 'palette.open': { hotkey: null, meta: { name: 'Command palette' } },
102
+ * })
103
+ * ```
104
+ */
105
+ export declare function defineAppHotkeys<const T extends Record<string, AppHotkeyInput>>(definitions: T & ValidatedAppHotkeyInputs<T>): DefinedAppHotkeys<T>;
106
+ /** Whether a value is an app hotkey created by `defineAppHotkeys`. */
107
+ export declare function isAppHotkey(value: unknown): value is AppHotkey;
108
+ /** Whether an app hotkey's effective hotkey differs from its default. */
109
+ export declare function isCustomHotkey(appHotkey: AppHotkey, platform?: Platform): boolean;
110
+ /**
111
+ * Status of an app hotkey in the running app.
112
+ * - `'active'`: at least one enabled registration
113
+ * - `'disabled'`: registered, but every registration has `enabled: false`
114
+ * - `'inactive'`: has a hotkey, but nothing has registered it
115
+ * - `'unbound'`: no hotkey assigned
116
+ */
117
+ export type AppHotkeyStatus = 'active' | 'disabled' | 'inactive' | 'unbound';
118
+ /** One row of an app hotkey cheat sheet. */
119
+ export interface AppHotkeyListItem<T extends AppHotkey = AppHotkey> {
120
+ appHotkey: T;
121
+ status: AppHotkeyStatus;
122
+ /** Whether the effective hotkey differs from the declared default. */
123
+ isCustom: boolean;
124
+ /** Live registrations of this app hotkey (for example, one per list row). */
125
+ registrations: Array<HotkeyRegistrationView>;
126
+ }
127
+ /** State of an `AppHotkeysStore`. */
128
+ export interface AppHotkeysState<T extends AppHotkeys = AppHotkeys> {
129
+ /** Effective app hotkeys: declarations with custom hotkeys applied. */
130
+ appHotkeys: T;
131
+ /** The user's hotkeys that differ from the defaults. */
132
+ customHotkeys: CustomHotkeys<T>;
133
+ }
134
+ /** Options for `AppHotkeysStore`. */
135
+ export interface AppHotkeysStoreOptions<T extends AppHotkeys = AppHotkeys> {
136
+ /** Starting custom hotkeys, such as saved data. Invalid entries are ignored with a warning. */
137
+ customHotkeys?: CustomHotkeys<T> | null;
138
+ /** Called with the custom hotkeys after every change, ready to save. */
139
+ onCustomHotkeysChange?: (customHotkeys: CustomHotkeys<T>) => void;
140
+ /** Platform used to compare hotkeys. Defaults to detection. */
141
+ platform?: Platform;
142
+ }
143
+ /**
144
+ * Holds declared app hotkeys and the user's custom hotkeys. Framework
145
+ * providers create one and expose it through hooks; devtools read it from
146
+ * `HotkeyManager.appHotkeys`.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const store = new AppHotkeysStore(appHotkeys, {
151
+ * customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'),
152
+ * onCustomHotkeysChange: (custom) =>
153
+ * localStorage.setItem('hotkeys', JSON.stringify(custom)),
154
+ * })
155
+ * store.setCustomHotkey('doc.save', 'Mod+Shift+S')
156
+ * ```
157
+ */
158
+ export declare class AppHotkeysStore<T extends AppHotkeys = AppHotkeys> {
159
+ #private;
160
+ readonly store: Store<AppHotkeysState<T>>;
161
+ constructor(appHotkeys: T, options?: AppHotkeysStoreOptions<T>);
162
+ /** Effective app hotkeys. */
163
+ get appHotkeys(): T;
164
+ /** The user's hotkeys that differ from the defaults. */
165
+ get customHotkeys(): CustomHotkeys<T>;
166
+ /** Updates callbacks and other options without resetting custom hotkeys. */
167
+ setOptions(options: Omit<AppHotkeysStoreOptions<T>, 'customHotkeys'>): void;
168
+ /**
169
+ * Replaces the declarations, for example after hot module replacement.
170
+ * Custom hotkeys for ids that still exist are kept.
171
+ */
172
+ setAppHotkeys(appHotkeys: T): void;
173
+ /** Effective app hotkey for an id, or `undefined` when it is not declared. */
174
+ get(id: string): AppHotkey | undefined;
175
+ /**
176
+ * Other app hotkeys that would collide if `id` used `hotkey`. App hotkeys
177
+ * collide when their contexts match or either has no context, and their
178
+ * hotkeys are equal or one sequence starts with the other.
179
+ */
180
+ findConflicts(id: keyof T & string, hotkey: Hotkey): Array<AppHotkey>;
181
+ /**
182
+ * Sets the user's hotkey for an app hotkey. `null` unbinds it. A hotkey
183
+ * equal to the default removes the customization.
184
+ *
185
+ * @returns Conflicting app hotkeys. When any exist, nothing changes.
186
+ */
187
+ setCustomHotkey(id: keyof T & string, hotkey: Hotkey | null): Array<AppHotkey>;
188
+ /** Restores the default hotkey for an app hotkey. */
189
+ resetCustomHotkey(id: keyof T & string): void;
190
+ /** Restores every default hotkey. */
191
+ resetCustomHotkeys(): void;
192
+ /**
193
+ * Joins app hotkeys with live registrations, in declaration order.
194
+ * Adapters call this with the manager's registration views.
195
+ */
196
+ getList(registrations: Iterable<HotkeyRegistrationView>): Array<AppHotkeyListItem<T[keyof T]>>;
197
+ }
198
+ /**
199
+ * Returns the provider's version of an app hotkey, so custom hotkeys apply
200
+ * even when a component imports the declaration directly. Falls back to the
201
+ * given app hotkey when no store declares it.
202
+ */
203
+ export declare function resolveAppHotkey(appHotkey: AppHotkey, store: AppHotkeysStore | null | undefined): AppHotkey;
204
+ /**
205
+ * Options that adapters pass to the manager when registering an app hotkey:
206
+ * declared options, meta, and the app hotkey id. The manager drops options
207
+ * that do not apply to the effective hotkey's kind.
208
+ */
209
+ export declare function getAppHotkeyRegistrationOptions(appHotkey: AppHotkey): AppHotkeyOptions & {
210
+ meta?: HotkeyMeta;
211
+ appHotkeyId: string;
212
+ };
213
+ /**
214
+ * Publishes a provider's store on the HotkeyManager so devtools can list
215
+ * declared hotkeys. Returns a cleanup function that unpublishes it if it is
216
+ * still the published store.
217
+ */
218
+ export declare function publishAppHotkeysStore(store: AppHotkeysStore): () => void;
219
+ /**
220
+ * Creates an app hotkeys store and publishes it on the HotkeyManager, for
221
+ * adapters without a provider component (such as Lit and Alpine). Hotkey
222
+ * APIs in those adapters resolve app hotkeys and custom hotkeys from the
223
+ * published store. Call it once at startup. Returns the store and a function
224
+ * that unpublishes it.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * provideAppHotkeys(appHotkeys, {
229
+ * customHotkeys: JSON.parse(localStorage.getItem('hotkeys') ?? '{}'),
230
+ * onCustomHotkeysChange: (custom) =>
231
+ * localStorage.setItem('hotkeys', JSON.stringify(custom)),
232
+ * })
233
+ * ```
234
+ */
235
+ export declare function provideAppHotkeys<T extends AppHotkeys>(appHotkeys: T, options?: AppHotkeysStoreOptions<T>): {
236
+ store: AppHotkeysStore<T>;
237
+ dispose: () => void;
238
+ };
239
+ //#endregion