@tanstack/hotkeys 0.23.0 → 0.25.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/_app-hotkeys-factory.d.ts +1 -3
- package/dist/_app-hotkeys-factory.js +2 -3
- package/dist/_event-target.js +40 -7
- package/dist/_keyboard-event.js +8 -5
- package/dist/_manager-ref.js +1 -2
- package/dist/_match.js +5 -3
- package/dist/_named-keys.d.ts +1 -3
- package/dist/_named-keys.js +1 -2
- package/dist/_recorder-chord.js +7 -13
- package/dist/_recording-guard.js +1 -2
- package/dist/_registration.d.ts +1 -3
- package/dist/_registration.js +11 -6
- package/dist/_server.js +3 -4
- package/dist/_store.d.ts +1 -3
- package/dist/_store.js +1 -2
- package/dist/_target-listeners.js +11 -3
- package/dist/adapter.d.ts +10 -8
- package/dist/adapter.js +9 -5
- package/dist/app-hotkeys.d.ts +4 -5
- package/dist/app-hotkeys.js +10 -21
- package/dist/conflicts.d.ts +2 -4
- package/dist/conflicts.js +5 -6
- package/dist/constants.d.ts +2 -4
- package/dist/constants.js +1 -2
- package/dist/display-labels.d.ts +1 -3
- package/dist/display-labels.js +1 -2
- package/dist/format.d.ts +2 -4
- package/dist/format.js +4 -5
- package/dist/hint.d.ts +1 -3
- package/dist/hint.js +1 -2
- package/dist/hotkey-manager.d.ts +23 -4
- package/dist/hotkey-manager.js +138 -30
- package/dist/hotkey-recorder.d.ts +19 -7
- package/dist/hotkey-recorder.js +8 -11
- package/dist/hotkey.types.d.ts +1 -3
- package/dist/index.d.ts +8 -8
- package/dist/index.js +3 -3
- package/dist/key-state-tracker.d.ts +1 -3
- package/dist/key-state-tracker.js +2 -3
- package/dist/key.types.d.ts +5 -6
- package/dist/match.d.ts +4 -5
- package/dist/match.js +7 -7
- package/dist/parse.d.ts +2 -3
- package/dist/parse.js +10 -19
- package/dist/platform.d.ts +1 -3
- package/dist/platform.js +1 -2
- package/dist/validate.d.ts +33 -13
- package/dist/validate.js +51 -34
- package/dist/vendor/@tanstack/store/dist/store.d.ts +0 -2
- package/dist/vendor/@tanstack/store/dist/types.d.ts +0 -2
- package/package.json +4 -4
- package/skills/app-hotkeys/SKILL.md +1 -1
- package/skills/hotkeys-core/SKILL.md +3 -1
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CanonicalModifier, EditingKey, FunctionKey, LetterKey, NavigationKey, NumberKey, PunctuationKey } from "./key.types.js";
|
|
2
|
-
//#region src/constants.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Canonical order for modifiers in normalized hotkey strings.
|
|
5
4
|
*
|
|
@@ -118,7 +117,7 @@ export declare const PUNCTUATION_CODE_MAP: Record<string, string>;
|
|
|
118
117
|
* @see {@link EDITING_KEYS}
|
|
119
118
|
* @see {@link PUNCTUATION_KEYS}
|
|
120
119
|
*/
|
|
121
|
-
export declare const ALL_KEYS: Set<"CapsLock" | "ContextMenu" | "Convert" | "KanaMode" | "NonConvert" | "Help" | "Insert" | "NumLock" | "Fn" | "FnLock" | "PrintScreen" | "ScrollLock" | "Pause" | "BrowserBack" | "BrowserFavorites" | "BrowserForward" | "BrowserHome" | "BrowserRefresh" | "BrowserSearch" | "BrowserStop" | "Eject" | "LaunchMail" | "MediaPlayPause" | "MediaStop" | "MediaTrackNext" | "MediaTrackPrevious" | "Power" | "AudioVolumeDown" | "AudioVolumeMute" | "AudioVolumeUp" | "WakeUp" | "Hyper" | "Super" | "Again" | "Copy" | "Cut" | "Find" | "Open" | "Paste" | "Props" | "Select" | "Undo" | "Hiragana" | "Katakana" | "Clear" | "Cancel" | "Standby" | "LaunchApplication1" | "LaunchApplication2" | "LaunchMediaPlayer" | "HangulMode" | "HanjaMode" | "Zenkaku" | "Hankaku" | "ZenkakuHankaku"
|
|
120
|
+
export declare const ALL_KEYS: Set<LetterKey | NumberKey | FunctionKey | NavigationKey | EditingKey | PunctuationKey | "CapsLock" | "ContextMenu" | "Convert" | "KanaMode" | "NonConvert" | "Help" | "Insert" | "NumLock" | "Fn" | "FnLock" | "PrintScreen" | "ScrollLock" | "Pause" | "BrowserBack" | "BrowserFavorites" | "BrowserForward" | "BrowserHome" | "BrowserRefresh" | "BrowserSearch" | "BrowserStop" | "Eject" | "LaunchMail" | "MediaPlayPause" | "MediaStop" | "MediaTrackNext" | "MediaTrackPrevious" | "Power" | "AudioVolumeDown" | "AudioVolumeMute" | "AudioVolumeUp" | "WakeUp" | "Hyper" | "Super" | "Again" | "Copy" | "Cut" | "Find" | "Open" | "Paste" | "Props" | "Select" | "Undo" | "Hiragana" | "Katakana" | "Clear" | "Cancel" | "Standby" | "LaunchApplication1" | "LaunchApplication2" | "LaunchMediaPlayer" | "HangulMode" | "HanjaMode" | "Zenkaku" | "Hankaku" | "ZenkakuHankaku">;
|
|
122
121
|
/** Whether a key is a single Unicode letter, such as `'a'` or `'é'`. */
|
|
123
122
|
export declare function isSingleLetterKey(key: string): boolean;
|
|
124
123
|
/**
|
|
@@ -136,5 +135,4 @@ export declare function isSingleLetterKey(key: string): boolean;
|
|
|
136
135
|
* normalizeKeyName('ArrowUp') // 'ArrowUp' (already canonical)
|
|
137
136
|
* ```
|
|
138
137
|
*/
|
|
139
|
-
export declare function normalizeKeyName(key: string): string;
|
|
140
|
-
//#endregion
|
|
138
|
+
export declare function normalizeKeyName(key: string): string;
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EDITING_KEY_NAMES, LOGICAL_ONLY_NAMED_KEYS, NAVIGATION_KEY_NAMES, SHARED_NAMED_KEYS } from "./_named-keys.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// src/constants.ts
|
|
4
4
|
/**
|
|
5
5
|
* Canonical order for modifiers in normalized hotkey strings.
|
|
6
6
|
*
|
|
@@ -193,5 +193,4 @@ function normalizeKeyName(key) {
|
|
|
193
193
|
return key === " " ? "Space" : key;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
//#endregion
|
|
197
196
|
export { ALL_KEYS, EDITING_KEYS, FUNCTION_KEYS, LETTER_KEYS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, NAVIGATION_KEYS, NUMBER_KEYS, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, isSingleLetterKey, normalizeKeyName };
|
package/dist/display-labels.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CanonicalModifier } from "./key.types.js";
|
|
2
|
-
//#region src/display-labels.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Modifier key symbols for macOS display.
|
|
5
4
|
*
|
|
@@ -86,5 +85,4 @@ export declare const KEY_DISPLAY_SYMBOLS: {
|
|
|
86
85
|
readonly Delete: "⌦";
|
|
87
86
|
readonly Tab: "⇥";
|
|
88
87
|
readonly Space: "␣";
|
|
89
|
-
};
|
|
90
|
-
//#endregion
|
|
88
|
+
};
|
package/dist/display-labels.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/display-labels.ts
|
|
2
2
|
/**
|
|
3
3
|
* Modifier key symbols for macOS display.
|
|
4
4
|
*
|
|
@@ -108,5 +108,4 @@ const KEY_DISPLAY_SYMBOLS = {
|
|
|
108
108
|
Space: "␣"
|
|
109
109
|
};
|
|
110
110
|
|
|
111
|
-
//#endregion
|
|
112
111
|
export { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS };
|
package/dist/format.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { CanonicalModifier } from "./key.types.js";
|
|
2
1
|
import { ParsedHotkeyChord, RegisterableHotkeyChord } from "./hotkey.types.js";
|
|
3
|
-
|
|
2
|
+
import { CanonicalModifier } from "./key.types.js";
|
|
4
3
|
/**
|
|
5
4
|
* Converts a parsed hotkey back to a string, with each platform modifier
|
|
6
5
|
* spelled out, such as `Control+Shift+S`. A parsed chord returns a string, and
|
|
@@ -105,5 +104,4 @@ export interface FormatDisplayOptions {
|
|
|
105
104
|
separatorToken?: string | null;
|
|
106
105
|
/** Separator between sequence steps. Default: a space. */
|
|
107
106
|
sequenceSeparator?: string;
|
|
108
|
-
}
|
|
109
|
-
//#endregion
|
|
107
|
+
}
|
package/dist/format.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { assertLogicalKey, codeToLogicalKey, modifierAlias, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
|
|
2
|
+
import { warnServerPlatform } from "./_server.js";
|
|
1
3
|
import { MODIFIER_ORDER, normalizeKeyName } from "./constants.js";
|
|
2
4
|
import { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS } from "./display-labels.js";
|
|
3
|
-
import { warnServerPlatform } from "./_server.js";
|
|
4
|
-
import { detectPlatform } from "./platform.js";
|
|
5
|
-
import { assertLogicalKey, codeToLogicalKey, modifierAlias, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
|
|
6
5
|
import { normalizeHotkey } from "./parse.js";
|
|
6
|
+
import { detectPlatform } from "./platform.js";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// src/format.ts
|
|
9
9
|
function formatHotkey(parsed) {
|
|
10
10
|
return Array.isArray(parsed) ? parsed.map((step) => formatParsedChord(step)) : formatParsedChord(parsed);
|
|
11
11
|
}
|
|
@@ -72,5 +72,4 @@ function formatKey(segment, platform, modifierSymbols, keySymbols, options) {
|
|
|
72
72
|
return numpad ? `Numpad ${display.replace(/([a-z])([A-Z])/g, "$1 $2")}` : display;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
//#endregion
|
|
76
75
|
export { formatForDisplay, formatHotkey };
|
package/dist/hint.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { RegisterableHotkey } from "./hotkey.types.js";
|
|
2
|
-
//#region src/hint.d.ts
|
|
3
2
|
export interface HeldModifierOptions {
|
|
4
3
|
/** Resolve Mod for this platform. Defaults to platform detection. */
|
|
5
4
|
platform?: 'mac' | 'windows' | 'linux';
|
|
@@ -18,5 +17,4 @@ export interface HeldModifierOptions {
|
|
|
18
17
|
* matchesHeldModifiers('Alt+Shift+[KeyK]', ['Alt']) // true
|
|
19
18
|
* matchesHeldModifiers('Alt+Shift+[KeyK]', ['Alt', 'Control']) // false
|
|
20
19
|
*/
|
|
21
|
-
export declare function matchesHeldModifiers(hotkey: RegisterableHotkey | null | undefined, heldKeys: ReadonlyArray<string>, options?: HeldModifierOptions): boolean;
|
|
22
|
-
//#endregion
|
|
20
|
+
export declare function matchesHeldModifiers(hotkey: RegisterableHotkey | null | undefined, heldKeys: ReadonlyArray<string>, options?: HeldModifierOptions): boolean;
|
package/dist/hint.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MODIFIER_KEYS, normalizeKeyName } from "./constants.js";
|
|
2
2
|
import { isHotkeySequence, parseHotkey } from "./parse.js";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// src/hint.ts
|
|
5
5
|
/**
|
|
6
6
|
* Returns whether held modifiers should reveal a shortcut hint.
|
|
7
7
|
* By default, any nonempty subset of the binding's modifiers qualifies; an
|
|
@@ -24,5 +24,4 @@ function matchesHeldModifiers(hotkey, heldKeys, options = {}) {
|
|
|
24
24
|
return held.length > 0 && held.every((key) => modifiers.includes(key)) && (!options.exact || held.length === modifiers.length);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
//#endregion
|
|
28
27
|
export { matchesHeldModifiers };
|
package/dist/hotkey-manager.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Store } from "./_store.js";
|
|
2
|
-
import { ConflictBehavior, HotkeyCallback, HotkeyCallbackContextFor, HotkeyChord, HotkeyMeta, HotkeySequence, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
|
|
3
2
|
import { AppHotkey, AppHotkeysStore } from "./app-hotkeys.js";
|
|
4
|
-
|
|
3
|
+
import { ConflictBehavior, HotkeyCallback, HotkeyCallbackContextFor, HotkeyChord, HotkeyMeta, HotkeySequence, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
|
|
5
4
|
/** An element, document, or window that hotkeys can listen on. */
|
|
6
5
|
export type Target = HTMLElement | Document | Window;
|
|
7
6
|
/**
|
|
@@ -24,8 +23,19 @@ export interface HotkeyOptions {
|
|
|
24
23
|
* existing handle via `setOptions` rather than unregistering. Defaults to `true`.
|
|
25
24
|
*/
|
|
26
25
|
enabled?: boolean;
|
|
27
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* The event type to listen for. Defaults to 'keydown'. A 'keyup' chord fires
|
|
28
|
+
* when its key or one of its modifiers is released, whichever comes first,
|
|
29
|
+
* unless another key was pressed in between. `preventDefault` applies to
|
|
30
|
+
* its keydown too.
|
|
31
|
+
*/
|
|
28
32
|
eventType?: 'keydown' | 'keyup';
|
|
33
|
+
/**
|
|
34
|
+
* Skip a key press that a listener outside TanStack Hotkeys already handled
|
|
35
|
+
* by calling `preventDefault()`, such as a component library's dialog that
|
|
36
|
+
* closes on Escape. Defaults to `false`.
|
|
37
|
+
*/
|
|
38
|
+
ignoreDefaultPrevented?: boolean;
|
|
29
39
|
/**
|
|
30
40
|
* Skip keys that a focused control inside the target uses itself: typing in
|
|
31
41
|
* text inputs, textarea, select, and contenteditable; Enter/Space on buttons
|
|
@@ -70,6 +80,16 @@ export interface HotkeyOptions {
|
|
|
70
80
|
scope?: string;
|
|
71
81
|
/** Id of the app hotkey this registration implements. Adapters set this when registering an app hotkey from `defineAppHotkeys`. */
|
|
72
82
|
appHotkeyId?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Run after key handlers that a framework calls from one listener on its
|
|
85
|
+
* root or the document, such as React's `onKeyDown`, so handlers inside the
|
|
86
|
+
* target run first and can stop the hotkey. An element target then listens
|
|
87
|
+
* on its document, or on its closed shadow root, instead of the element. A
|
|
88
|
+
* stop by an earlier listener there skips the hotkey. Applies only to the
|
|
89
|
+
* bubble phase.
|
|
90
|
+
* Adapters for frameworks that delegate key events set this.
|
|
91
|
+
*/
|
|
92
|
+
afterDelegatedHandlers?: boolean;
|
|
73
93
|
}
|
|
74
94
|
/** Options for a single chord such as `'Mod+S'`. */
|
|
75
95
|
export interface HotkeyChordOptions extends HotkeyOptions {
|
|
@@ -400,5 +420,4 @@ export declare class HotkeyManager {
|
|
|
400
420
|
* Convenience function for accessing the manager.
|
|
401
421
|
*/
|
|
402
422
|
export declare function getHotkeyManager(): HotkeyManager;
|
|
403
|
-
//#endregion
|
|
404
423
|
export type { ConflictBehavior };
|
package/dist/hotkey-manager.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { detectPlatform } from "./platform.js";
|
|
1
|
+
import { closesNativeLayer, getTargetDocument, isDocumentLevel, shouldIgnoreInputEvent, targetHasFocus } from "./_event-target.js";
|
|
3
2
|
import { isKeyNameShape, keysEqual, normalizeKeyboardEvent } from "./_keyboard-event.js";
|
|
4
|
-
import {
|
|
5
|
-
import { formatHotkey } from "./format.js";
|
|
3
|
+
import { managerRef } from "./_manager-ref.js";
|
|
6
4
|
import { matchKeyboardEvent, parsedChordsEqual } from "./_match.js";
|
|
5
|
+
import { isRecordingEvent } from "./_recording-guard.js";
|
|
7
6
|
import { GLOBAL_SCOPE, chordHasKey, defaultHotkeyOptions, definedOptions, getDefaultIgnoreInputs, handleConflict, optionsEqual } from "./_registration.js";
|
|
8
|
-
import { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual } from "./match.js";
|
|
9
|
-
import { getTargetDocument, isDocumentLevel, shouldIgnoreInputEvent, targetHasFocus } from "./_event-target.js";
|
|
10
7
|
import { createStore } from "./_store.js";
|
|
11
8
|
import { TargetListeners } from "./_target-listeners.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
9
|
+
import { normalizeKeyName } from "./constants.js";
|
|
10
|
+
import { formatHotkey } from "./format.js";
|
|
11
|
+
import { DEFAULT_SEQUENCE_TIMEOUT, areHotkeysEqual } from "./match.js";
|
|
12
|
+
import { isHotkeySequence, isModifierKey, parseHotkey, simplifyHotkey } from "./parse.js";
|
|
13
|
+
import { detectPlatform } from "./platform.js";
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// src/hotkey-manager.ts
|
|
16
16
|
/** Whether a registration can fire now. Without a manager, scopes don't apply. */
|
|
17
17
|
function getRegistrationStatus(options, manager) {
|
|
18
18
|
if (options.enabled === false) return "disabled";
|
|
@@ -91,6 +91,23 @@ function optionsForKind(kind, options) {
|
|
|
91
91
|
const { requireReset: _requireReset, ...rest } = options;
|
|
92
92
|
return rest;
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Where a registration listens: its target, or for an element target that
|
|
96
|
+
* runs after delegated handlers in the bubble phase, the node where handlers
|
|
97
|
+
* delegated around it have already run. That is its document, unless the
|
|
98
|
+
* element is in a closed shadow root, which hides it from listeners outside,
|
|
99
|
+
* or in a tree outside any document, which events never leave.
|
|
100
|
+
*/
|
|
101
|
+
function listenerTarget({ target, options }) {
|
|
102
|
+
if (!options.afterDelegatedHandlers || options.capture || isDocumentLevel(target)) return target;
|
|
103
|
+
let root = target.getRootNode();
|
|
104
|
+
while ("host" in root && root.mode === "open") root = root.host.getRootNode();
|
|
105
|
+
return root;
|
|
106
|
+
}
|
|
107
|
+
/** Whether a registration's shared listener moves after listeners added before it joins. */
|
|
108
|
+
function movesAfterDelegatedHandlers(options) {
|
|
109
|
+
return !!options.afterDelegatedHandlers && !options.capture;
|
|
110
|
+
}
|
|
94
111
|
/** Steps with Mod already resolved for the registration's platform. */
|
|
95
112
|
function resolvedSteps(registration) {
|
|
96
113
|
return registration.kind === "chord" ? [registration.parsedChord] : registration.parsedSequence;
|
|
@@ -173,8 +190,20 @@ var HotkeyManager = class HotkeyManager {
|
|
|
173
190
|
/** Scopes that can fire while a scope is exclusive, or `null` when none is. */
|
|
174
191
|
#liveScopes = null;
|
|
175
192
|
#listeners = new TargetListeners((...args) => this.#processTargetEvent(...args));
|
|
193
|
+
/** Where each registration listens, kept so cleanup finds a target that moved documents. */
|
|
194
|
+
#listenerTargets = /* @__PURE__ */ new Map();
|
|
176
195
|
/** Events that continue a sequence in progress, decided before any listener runs. */
|
|
177
196
|
#sequenceOwned = /* @__PURE__ */ new WeakMap();
|
|
197
|
+
/**
|
|
198
|
+
* Keyup chords whose keydown matched. Releasing one of the chord's
|
|
199
|
+
* modifiers fires them, because the key's own keyup may come after the
|
|
200
|
+
* modifier's or, on macOS while Command is held, not at all. `null` marks a
|
|
201
|
+
* press that another key cancelled or that `ignoreDefaultPrevented`
|
|
202
|
+
* skipped, so its release doesn't fire.
|
|
203
|
+
*/
|
|
204
|
+
#pendingReleases = /* @__PURE__ */ new Map();
|
|
205
|
+
/** Events this manager prevented, so its own prevention never skips a hotkey. */
|
|
206
|
+
#prevented = /* @__PURE__ */ new WeakSet();
|
|
178
207
|
/** Physical keys whose keydown a sequence owned, so their repeats and keyup are owned too. */
|
|
179
208
|
#sequenceOwnedKeys = /* @__PURE__ */ new Set();
|
|
180
209
|
/** Timers that clear a sequence's progress once its timeout passes. */
|
|
@@ -219,7 +248,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
219
248
|
if (scope === "global") return () => {};
|
|
220
249
|
const { exclusive } = options;
|
|
221
250
|
const allow = exclusive ? exclusive === true ? [] : exclusive.allow : null;
|
|
222
|
-
if (process.env.NODE_ENV !== "production" && allow && [...this.#scopeActivations].some((other) => other.allow && other.scope === scope)) console.warn(`Exclusive
|
|
251
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && allow && [...this.#scopeActivations].some((other) => other.allow && other.scope === scope)) console.warn(`[hotkeys] Exclusive scope '${scope}' is already active`);
|
|
223
252
|
const activation = {
|
|
224
253
|
scope,
|
|
225
254
|
parent: options.parent ?? "global",
|
|
@@ -250,7 +279,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
250
279
|
const parents = /* @__PURE__ */ new Map();
|
|
251
280
|
const latest = /* @__PURE__ */ new Map();
|
|
252
281
|
for (const activation of this.#scopeActivations) {
|
|
253
|
-
parents.set(activation.scope, activation.parent);
|
|
282
|
+
if (activation.parent !== activation.scope) parents.set(activation.scope, activation.parent);
|
|
254
283
|
latest.set(activation.scope, Math.max(latest.get(activation.scope) ?? 0, activation.order));
|
|
255
284
|
}
|
|
256
285
|
const depth = (scope) => {
|
|
@@ -311,7 +340,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
311
340
|
#handledByLiveHotkey(event, eventType) {
|
|
312
341
|
const path = event.composedPath();
|
|
313
342
|
for (const registration of this.registrations.state.values()) {
|
|
314
|
-
if (!this.#isLive(registration) || registration.options.eventType !== eventType || !path.includes(registration.target) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, registration.target)) continue;
|
|
343
|
+
if (!this.#isLive(registration) || registration.options.eventType !== eventType || this.#skips(registration, event) || !path.includes(registration.target) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, registration.target)) continue;
|
|
315
344
|
if ((registration.kind === "chord" ? [registration.parsedChord] : [registration.parsedSequence[registration.currentIndex], registration.parsedSequence[0]]).some((step) => matchKeyboardEvent(event, step, registration.options.platform).matched)) return true;
|
|
316
345
|
}
|
|
317
346
|
return false;
|
|
@@ -362,12 +391,14 @@ var HotkeyManager = class HotkeyManager {
|
|
|
362
391
|
}
|
|
363
392
|
const steps = registration.kind === "sequence" ? registration.parsedSequence : [registration.parsedChord];
|
|
364
393
|
if (!steps.every(chordHasKey)) return inactiveHandle(callback);
|
|
365
|
-
if (process.env.NODE_ENV !== "production") {
|
|
366
|
-
for (const { key } of steps) if (key !== void 0 && !isKeyNameShape(key)) console.warn(`Unknown key '${key}' in '${formatForMessage(registration.hotkey)}'
|
|
394
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
395
|
+
for (const { key } of steps) if (key !== void 0 && !isKeyNameShape(key)) console.warn(`[hotkeys] Unknown key '${key}' in '${formatForMessage(registration.hotkey)}' (names are case-sensitive)`);
|
|
367
396
|
}
|
|
368
397
|
const conflicting = this.#findConflictingRegistration(registration);
|
|
369
398
|
if (conflicting) handleConflict(conflicting.id, formatForMessage(registration.hotkey), options.conflictBehavior ?? "warn", (conflictId) => this.#unregister(conflictId));
|
|
370
|
-
|
|
399
|
+
const listenOn = listenerTarget(registration);
|
|
400
|
+
this.#listeners.add(listenOn, !!registration.options.capture, id, movesAfterDelegatedHandlers(registration.options));
|
|
401
|
+
this.#listenerTargets.set(id, listenOn);
|
|
371
402
|
this.registrations.setState((prev) => new Map(prev).set(id, registration));
|
|
372
403
|
const manager = this;
|
|
373
404
|
return {
|
|
@@ -399,10 +430,16 @@ var HotkeyManager = class HotkeyManager {
|
|
|
399
430
|
else delete options[key];
|
|
400
431
|
if (optionsEqual(reg.options, options)) return prev;
|
|
401
432
|
const capture = !!reg.options.capture;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
433
|
+
const from = manager.#listenerTargets.get(id) ?? reg.target;
|
|
434
|
+
const to = listenerTarget({
|
|
435
|
+
target: reg.target,
|
|
436
|
+
options
|
|
437
|
+
});
|
|
438
|
+
if (capture !== !!options.capture || from !== to) {
|
|
439
|
+
manager.#listeners.remove(from, capture, id);
|
|
440
|
+
manager.#listeners.add(to, !!options.capture, id, movesAfterDelegatedHandlers(options));
|
|
441
|
+
manager.#listenerTargets.set(id, to);
|
|
442
|
+
} else if (movesAfterDelegatedHandlers(options) && !movesAfterDelegatedHandlers(reg.options)) manager.#listeners.add(to, capture, id, true);
|
|
406
443
|
return new Map(prev).set(id, {
|
|
407
444
|
...reg,
|
|
408
445
|
options
|
|
@@ -420,13 +457,14 @@ var HotkeyManager = class HotkeyManager {
|
|
|
420
457
|
#unregister(id) {
|
|
421
458
|
const registration = this.registrations.state.get(id);
|
|
422
459
|
if (!registration) return;
|
|
423
|
-
const target = registration.target;
|
|
424
460
|
this.registrations.setState((prev) => {
|
|
425
461
|
const next = new Map(prev);
|
|
426
462
|
next.delete(id);
|
|
427
463
|
return next;
|
|
428
464
|
});
|
|
429
|
-
this.#listeners.remove(target, !!registration.options.capture, id);
|
|
465
|
+
this.#listeners.remove(this.#listenerTargets.get(id) ?? registration.target, !!registration.options.capture, id);
|
|
466
|
+
this.#listenerTargets.delete(id);
|
|
467
|
+
this.#pendingReleases.delete(id);
|
|
430
468
|
this.#clearSequenceReset(id);
|
|
431
469
|
}
|
|
432
470
|
/**
|
|
@@ -478,6 +516,30 @@ var HotkeyManager = class HotkeyManager {
|
|
|
478
516
|
*/
|
|
479
517
|
#processTargetEvent(event, target, eventType, capture, ids, first) {
|
|
480
518
|
if (first) this.#sequenceOwned.set(event, this.#isOwnedBySequence(event, eventType));
|
|
519
|
+
if (capture) {
|
|
520
|
+
this.#processRound(event, target, eventType, capture, ids);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const rounds = /* @__PURE__ */ new Map();
|
|
524
|
+
for (const id of ids) {
|
|
525
|
+
const registration = this.registrations.state.get(id);
|
|
526
|
+
if (!registration) continue;
|
|
527
|
+
const round = rounds.get(registration.target) ?? /* @__PURE__ */ new Set();
|
|
528
|
+
rounds.set(registration.target, round.add(id));
|
|
529
|
+
}
|
|
530
|
+
const path = rounds.size > 1 || !rounds.has(target) ? event.composedPath() : [];
|
|
531
|
+
const order = [...rounds.keys()].filter((other) => other === target || path.includes(other)).sort((a, b) => a === target ? 1 : b === target ? -1 : path.indexOf(a) - path.indexOf(b));
|
|
532
|
+
const arrivedStopped = event.cancelBubble;
|
|
533
|
+
for (const roundTarget of order) {
|
|
534
|
+
const round = /* @__PURE__ */ new Set();
|
|
535
|
+
for (const id of rounds.get(roundTarget)) {
|
|
536
|
+
const delegated = this.registrations.state.get(id)?.options.afterDelegatedHandlers;
|
|
537
|
+
if (!event.cancelBubble || !delegated && arrivedStopped) round.add(id);
|
|
538
|
+
}
|
|
539
|
+
this.#processRound(event, roundTarget, eventType, capture, round);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
#processRound(event, target, eventType, capture, ids) {
|
|
481
543
|
this.#processChords(event, target, eventType, capture, this.#registrationsIn(ids, capture, "chord"));
|
|
482
544
|
this.#processSequences(event, target, eventType, capture, this.#registrationsIn(ids, capture, "sequence"));
|
|
483
545
|
}
|
|
@@ -488,15 +550,47 @@ var HotkeyManager = class HotkeyManager {
|
|
|
488
550
|
registration.activeMatch = void 0;
|
|
489
551
|
}
|
|
490
552
|
}
|
|
553
|
+
if (eventType === "keydown" && !event.repeat) {
|
|
554
|
+
for (const { id } of registrations) if (this.#pendingReleases.get(id)) this.#pendingReleases.set(id, null);
|
|
555
|
+
}
|
|
491
556
|
if (isRecordingEvent(event) || this.#sequenceOwned.get(event)) return;
|
|
492
557
|
const matches = /* @__PURE__ */ new Map();
|
|
493
558
|
let bestScore = 0;
|
|
494
559
|
let reserved = false;
|
|
560
|
+
const eventKey = normalizeKeyName(event.key);
|
|
495
561
|
for (const registration of registrations) {
|
|
496
562
|
const live = this.#isLive(registration);
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
563
|
+
const { eventType: firesOn, preventDefault } = registration.options;
|
|
564
|
+
const press = eventType === "keydown" && firesOn === "keyup";
|
|
565
|
+
const skipped = this.#skips(registration, event);
|
|
566
|
+
const { parsedChord, id } = registration;
|
|
567
|
+
const { platform } = registration.options;
|
|
568
|
+
let result;
|
|
569
|
+
let released;
|
|
570
|
+
if (eventType === "keyup" && firesOn === "keyup" && this.#pendingReleases.has(id)) {
|
|
571
|
+
result = matchKeyboardEvent(event, parsedChord, platform);
|
|
572
|
+
if (result.matched || parsedChord.modifiers.includes(eventKey) && !event.getModifierState(eventKey)) {
|
|
573
|
+
released = this.#pendingReleases.get(id);
|
|
574
|
+
this.#pendingReleases.delete(id);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (!live && preventDefault !== "always" || firesOn !== eventType && !press || ignoresInputs(registration) && shouldIgnoreInputEvent(event, target)) continue;
|
|
578
|
+
result ??= matchKeyboardEvent(event, parsedChord, platform);
|
|
579
|
+
if (press) {
|
|
580
|
+
if (result.matched) {
|
|
581
|
+
if (skipped) this.#pendingReleases.set(id, null);
|
|
582
|
+
else {
|
|
583
|
+
if (preventDefault) this.#prevent(event);
|
|
584
|
+
if (live) this.#pendingReleases.set(id, result);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
if (released !== void 0) {
|
|
590
|
+
if (!released || skipped) continue;
|
|
591
|
+
if (!result.matched) result = released;
|
|
592
|
+
}
|
|
593
|
+
if (skipped || !result.matched) continue;
|
|
500
594
|
if (!live) {
|
|
501
595
|
reserved = true;
|
|
502
596
|
continue;
|
|
@@ -504,7 +598,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
504
598
|
matches.set(registration.id, result);
|
|
505
599
|
bestScore = Math.max(bestScore, result.score);
|
|
506
600
|
}
|
|
507
|
-
if (reserved && matches.size === 0 && !this.#handledByLiveHotkey(event, eventType)) event
|
|
601
|
+
if (reserved && matches.size === 0 && !this.#handledByLiveHotkey(event, eventType)) this.#prevent(event);
|
|
508
602
|
let bestRank = -1;
|
|
509
603
|
for (const registration of registrations) if (matches.get(registration.id)?.score === bestScore) bestRank = Math.max(bestRank, this.#scopeRank(registration.options.scope));
|
|
510
604
|
for (const snapshot of registrations) {
|
|
@@ -548,7 +642,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
548
642
|
const path = event.composedPath();
|
|
549
643
|
const now = Date.now();
|
|
550
644
|
for (const registration of this.registrations.state.values()) {
|
|
551
|
-
if (registration.kind !== "sequence" || registration.currentIndex === 0 || !this.#isLive(registration) || registration.options.eventType !== eventType || now - registration.lastKeyTime > (registration.options.timeout ?? 1e3)) continue;
|
|
645
|
+
if (registration.kind !== "sequence" || registration.currentIndex === 0 || !this.#isLive(registration) || registration.options.eventType !== eventType || this.#skips(registration, event) || now - registration.lastKeyTime > (registration.options.timeout ?? 1e3)) continue;
|
|
552
646
|
const target = registration.target;
|
|
553
647
|
if (path.includes(isDocumentLevel(target) ? getTargetDocument(target) : target) && !(ignoresInputs(registration) && shouldIgnoreInputEvent(event, target)) && matchKeyboardEvent(event, registration.parsedSequence[registration.currentIndex], registration.options.platform).matched) return true;
|
|
554
648
|
}
|
|
@@ -573,7 +667,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
573
667
|
const candidates = /* @__PURE__ */ new Map();
|
|
574
668
|
let bestScore = 0;
|
|
575
669
|
for (const registration of registrations) {
|
|
576
|
-
if (!this.#isLive(registration) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, target) || registration.options.eventType !== eventType) continue;
|
|
670
|
+
if (!this.#isLive(registration) || ignoresInputs(registration) && shouldIgnoreInputEvent(event, target) || registration.options.eventType !== eventType || this.#skips(registration, event)) continue;
|
|
577
671
|
const timeout = registration.options.timeout ?? 1e3;
|
|
578
672
|
if (registration.currentIndex > 0 && now - registration.lastKeyTime > timeout) {
|
|
579
673
|
registration.currentIndex = 0;
|
|
@@ -605,7 +699,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
605
699
|
this.#consume(registration, event);
|
|
606
700
|
registration.currentIndex = 0;
|
|
607
701
|
completed.push(registration);
|
|
608
|
-
} else if (registration.options.preventDefault) event
|
|
702
|
+
} else if (registration.options.preventDefault) this.#prevent(event);
|
|
609
703
|
} else if (registration.currentIndex > 0) {
|
|
610
704
|
registration.currentIndex = 0;
|
|
611
705
|
changed = true;
|
|
@@ -624,9 +718,21 @@ var HotkeyManager = class HotkeyManager {
|
|
|
624
718
|
if (registration) this.#fire(registration, event);
|
|
625
719
|
}
|
|
626
720
|
}
|
|
721
|
+
#prevent(event) {
|
|
722
|
+
if (!event.defaultPrevented) this.#prevented.add(event);
|
|
723
|
+
event.preventDefault();
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Whether the registration skips the event: Escape belongs to an open modal
|
|
727
|
+
* dialog or popover outside its target, or another listener prevented the
|
|
728
|
+
* event and the registration skips such events.
|
|
729
|
+
*/
|
|
730
|
+
#skips(registration, event) {
|
|
731
|
+
return closesNativeLayer(event, registration.target) || !!registration.options.ignoreDefaultPrevented && event.defaultPrevented && !this.#prevented.has(event);
|
|
732
|
+
}
|
|
627
733
|
/** Applies a matched registration's preventDefault and stopPropagation options. */
|
|
628
734
|
#consume(registration, event) {
|
|
629
|
-
if (registration.options.preventDefault) event
|
|
735
|
+
if (registration.options.preventDefault) this.#prevent(event);
|
|
630
736
|
if (registration.options.stopPropagation) event.stopPropagation();
|
|
631
737
|
}
|
|
632
738
|
/** Counts a trigger, notifies subscribers (such as devtools), and runs the callback. */
|
|
@@ -634,7 +740,9 @@ var HotkeyManager = class HotkeyManager {
|
|
|
634
740
|
registration.triggerCount++;
|
|
635
741
|
if (registration.kind === "sequence") registration.hasFired = true;
|
|
636
742
|
this.#notify();
|
|
743
|
+
const prevented = event.defaultPrevented;
|
|
637
744
|
registration.callback(event, context);
|
|
745
|
+
if (!prevented && event.defaultPrevented) this.#prevented.add(event);
|
|
638
746
|
}
|
|
639
747
|
#callbackContext(registration) {
|
|
640
748
|
return registration.kind === "chord" ? {
|
|
@@ -719,6 +827,7 @@ var HotkeyManager = class HotkeyManager {
|
|
|
719
827
|
for (const timer of this.#sequenceResetTimers.values()) clearTimeout(timer);
|
|
720
828
|
this.#sequenceResetTimers.clear();
|
|
721
829
|
this.#listeners.destroy();
|
|
830
|
+
this.#listenerTargets.clear();
|
|
722
831
|
this.registrations.setState(() => /* @__PURE__ */ new Map());
|
|
723
832
|
this.appHotkeys.setState(() => null);
|
|
724
833
|
}
|
|
@@ -731,5 +840,4 @@ function getHotkeyManager() {
|
|
|
731
840
|
return HotkeyManager.getInstance();
|
|
732
841
|
}
|
|
733
842
|
|
|
734
|
-
//#endregion
|
|
735
843
|
export { HotkeyManager, getHotkeyManager, toHotkeyRegistrationView };
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { Store } from "./_store.js";
|
|
2
|
-
import { Hotkey, HotkeyChord, HotkeySequence, ParsedHotkeyChord } from "./hotkey.types.js";
|
|
3
2
|
import { HotkeyConflict, HotkeyConflictOptions } from "./conflicts.js";
|
|
4
|
-
|
|
3
|
+
import { Hotkey, HotkeyChord, HotkeySequence, ParsedHotkeyChord } from "./hotkey.types.js";
|
|
5
4
|
/** Event property used to record a binding. Physical codes are the default. */
|
|
6
5
|
export type RecorderKeyMode = 'code' | 'key';
|
|
6
|
+
/**
|
|
7
|
+
* Why the recorder rejected a key press or a recording:
|
|
8
|
+
*
|
|
9
|
+
* - `'alt-graph'`: AltGr typed a character, which physical recording can't
|
|
10
|
+
* record. Record by key to accept characters.
|
|
11
|
+
* - `'missing-code'`: the event has no usable physical key code.
|
|
12
|
+
* - `'whitespace'`: the key is a whitespace character, which a hotkey can't
|
|
13
|
+
* hold.
|
|
14
|
+
* - `'validation'`: your `validate` callback rejected the recording.
|
|
15
|
+
* - `'conflict'`: the recording conflicts with registered hotkeys.
|
|
16
|
+
*/
|
|
17
|
+
export type RecorderRejectionReason = 'alt-graph' | 'missing-code' | 'whitespace' | 'validation' | 'conflict';
|
|
7
18
|
export interface RecorderRejection {
|
|
8
|
-
reason:
|
|
9
|
-
message
|
|
19
|
+
reason: RecorderRejectionReason;
|
|
20
|
+
/** The message your `validate` callback returned, when it returned one. */
|
|
21
|
+
message?: string;
|
|
10
22
|
/** The rejected chord or sequence, when one was recorded. */
|
|
11
23
|
hotkey?: Hotkey;
|
|
12
24
|
conflicts?: Array<HotkeyConflict>;
|
|
@@ -74,7 +86,7 @@ interface HotkeyRecorderBaseOptions extends RecorderOptions {
|
|
|
74
86
|
export interface HotkeyChordRecorderOptions extends HotkeyRecorderBaseOptions {
|
|
75
87
|
/** Maximum number of chords to record. Default: 1 */
|
|
76
88
|
maxSteps?: 1;
|
|
77
|
-
/** Return true to accept, or false
|
|
89
|
+
/** Return true to accept, or false or a message to reject while staying in recording mode. The message is passed to `onReject`. */
|
|
78
90
|
validate?: (hotkey: HotkeyChord, context: HotkeyChordRecorderValidationContext) => boolean | string;
|
|
79
91
|
/** Callback when a chord is successfully recorded */
|
|
80
92
|
onRecord: (hotkey: HotkeyChord) => void;
|
|
@@ -86,7 +98,7 @@ export interface HotkeyChordRecorderOptions extends HotkeyRecorderBaseOptions {
|
|
|
86
98
|
export interface HotkeySequenceRecorderOptions extends HotkeyRecorderBaseOptions {
|
|
87
99
|
/** Maximum number of chords to record. Reaching it commits automatically. Use `Infinity` for no limit. */
|
|
88
100
|
maxSteps: number;
|
|
89
|
-
/** Return true to accept, or false
|
|
101
|
+
/** Return true to accept, or false or a message to reject while staying in recording mode. The message is passed to `onReject`. */
|
|
90
102
|
validate?: (hotkey: Hotkey, context: HotkeyRecorderValidationContext) => boolean | string;
|
|
91
103
|
/** Callback when a hotkey is successfully recorded */
|
|
92
104
|
onRecord: (hotkey: Hotkey) => void;
|
|
@@ -178,4 +190,4 @@ export declare class HotkeyRecorder {
|
|
|
178
190
|
*/
|
|
179
191
|
destroy(): void;
|
|
180
192
|
}
|
|
181
|
-
|
|
193
|
+
export {};
|
package/dist/hotkey-recorder.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { detectPlatform } from "./platform.js";
|
|
2
|
-
import { normalizeKeyboardEvent, splitChordParts } from "./_keyboard-event.js";
|
|
3
|
-
import { isModifierKey, parseHotkey, simplifyHotkey } from "./parse.js";
|
|
4
1
|
import { isTypingEvent } from "./_event-target.js";
|
|
5
|
-
import {
|
|
2
|
+
import { normalizeKeyboardEvent, splitChordParts } from "./_keyboard-event.js";
|
|
3
|
+
import { chordRejection, hotkeyChordFromKeydown } from "./_recorder-chord.js";
|
|
6
4
|
import { beginRecording, captureRecordingEvent, endRecording } from "./_recording-guard.js";
|
|
5
|
+
import { createStore } from "./_store.js";
|
|
7
6
|
import { findHotkeyConflicts } from "./conflicts.js";
|
|
8
|
-
import {
|
|
7
|
+
import { isModifierKey, parseHotkey, simplifyHotkey } from "./parse.js";
|
|
8
|
+
import { detectPlatform } from "./platform.js";
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// src/hotkey-recorder.ts
|
|
11
11
|
const initialState = {
|
|
12
12
|
isRecording: false,
|
|
13
13
|
steps: [],
|
|
@@ -157,8 +157,7 @@ var HotkeyRecorder = class {
|
|
|
157
157
|
if (chord === null) return;
|
|
158
158
|
if (splitChordParts(chord).some((part) => !part.trim())) {
|
|
159
159
|
options.onReject?.({
|
|
160
|
-
reason: "
|
|
161
|
-
message: "Whitespace keys cannot be recorded",
|
|
160
|
+
reason: "whitespace",
|
|
162
161
|
hotkey: chord
|
|
163
162
|
});
|
|
164
163
|
return;
|
|
@@ -211,7 +210,7 @@ var HotkeyRecorder = class {
|
|
|
211
210
|
if (accepted !== void 0 && accepted !== true) {
|
|
212
211
|
rejectAndKeepRecording({
|
|
213
212
|
reason: "validation",
|
|
214
|
-
|
|
213
|
+
...typeof accepted === "string" && { message: accepted },
|
|
215
214
|
hotkey
|
|
216
215
|
});
|
|
217
216
|
return;
|
|
@@ -225,7 +224,6 @@ var HotkeyRecorder = class {
|
|
|
225
224
|
if (conflicts.length) {
|
|
226
225
|
rejectAndKeepRecording({
|
|
227
226
|
reason: "conflict",
|
|
228
|
-
message: `This ${single ? "shortcut" : "sequence"} conflicts with a registered hotkey.`,
|
|
229
227
|
hotkey,
|
|
230
228
|
conflicts
|
|
231
229
|
});
|
|
@@ -286,5 +284,4 @@ var HotkeyRecorder = class {
|
|
|
286
284
|
}
|
|
287
285
|
};
|
|
288
286
|
|
|
289
|
-
//#endregion
|
|
290
287
|
export { HotkeyRecorder };
|
package/dist/hotkey.types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CanonicalModifier, Key, LogicalKey } from "./key.types.js";
|
|
2
|
-
//#region src/hotkey.types.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Single modifier + key combinations.
|
|
5
4
|
* Uses canonical modifiers (4) + Mod (1) = 5 modifiers.
|
|
@@ -269,5 +268,4 @@ export interface HotkeyMeta {
|
|
|
269
268
|
* - `'replace'` - Unregister the existing registration and register the new one
|
|
270
269
|
* - `'allow'` - Allow multiple registrations without warning
|
|
271
270
|
*/
|
|
272
|
-
export type ConflictBehavior = 'warn' | 'error' | 'replace' | 'allow';
|
|
273
|
-
//#endregion
|
|
271
|
+
export type ConflictBehavior = 'warn' | 'error' | 'replace' | 'allow';
|