@tanstack/hotkeys 0.22.1 → 0.23.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/_server.js +21 -0
- package/dist/adapter.d.ts +10 -0
- package/dist/adapter.js +18 -3
- package/dist/format.js +8 -2
- package/dist/index.d.ts +2 -2
- package/dist/match.d.ts +2 -1
- package/dist/platform.d.ts +16 -4
- package/dist/platform.js +26 -9
- package/package.json +1 -1
- package/skills/hotkeys-core/SKILL.md +1 -0
package/dist/_server.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/_server.ts
|
|
2
|
+
const SERVER_AGENT = /^(?:Node\.js\/|Deno\/|Bun\/|Cloudflare-Workers)/;
|
|
3
|
+
/** Whether this runs on a server: no `navigator`, or a server runtime's. */
|
|
4
|
+
function isServerRuntime() {
|
|
5
|
+
return typeof navigator === "undefined" || SERVER_AGENT.test(String(navigator.userAgent));
|
|
6
|
+
}
|
|
7
|
+
let warned = false;
|
|
8
|
+
/**
|
|
9
|
+
* Warns once, in development, when a label's platform is detected during
|
|
10
|
+
* server rendering. The server then renders 'linux' labels, which differ for
|
|
11
|
+
* users on macOS and Windows after hydration.
|
|
12
|
+
*/
|
|
13
|
+
function warnServerPlatform() {
|
|
14
|
+
if (process.env.NODE_ENV !== "production" && !warned && isServerRuntime()) {
|
|
15
|
+
warned = true;
|
|
16
|
+
console.warn("[TanStack Hotkeys] A hotkey label was formatted during server rendering without a platform, so it uses 'linux'. Mac and Windows users get a different label after hydration. Read the platform from the request with `detectPlatform(userAgent)`, set it as the `platform` of your hotkeys provider or scope, and format labels with your adapter's formatter, such as `useFormatForDisplay`. Or render the labels only in the browser. See the server rendering section of the Displaying Hotkeys guide.");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { isServerRuntime, warnServerPlatform };
|
package/dist/adapter.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Store, createStore } from "./_store.js";
|
|
|
2
2
|
import { HotkeyCallback, HotkeyChord, HotkeySequence, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence } from "./hotkey.types.js";
|
|
3
3
|
import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyOptions, HotkeySequenceOptions, Target } from "./hotkey-manager.js";
|
|
4
4
|
import { AppHotkey, AppHotkeysStore } from "./app-hotkeys.js";
|
|
5
|
+
import { HotkeyPlatform } from "./platform.js";
|
|
5
6
|
import { definedOptions, withDefaults } from "./_registration.js";
|
|
6
7
|
import { createAppHotkeysStore } from "@tanstack/hotkeys";
|
|
7
8
|
//#region src/adapter.d.ts
|
|
@@ -147,11 +148,20 @@ export interface HotkeysProviderScopeProps {
|
|
|
147
148
|
/**
|
|
148
149
|
* Merges a provider's default options over its parent's. A provider with a
|
|
149
150
|
* `scope` sets `hotkey.scope`, so hotkeys registered inside it use that scope.
|
|
151
|
+
* A provider's `platform` is inherited, and is the default platform for its
|
|
152
|
+
* hotkeys and recorders; `hotkey.platform` and `hotkeyRecorder.platform` win.
|
|
150
153
|
*/
|
|
151
154
|
export declare function mergeHotkeysProviderDefaults<T extends {
|
|
155
|
+
platform?: HotkeyPlatform;
|
|
152
156
|
hotkey?: object;
|
|
153
157
|
hotkeyRecorder?: object;
|
|
154
158
|
}>(parent: T | undefined, own: T | undefined, scope: string | undefined): T;
|
|
159
|
+
/**
|
|
160
|
+
* The platform to render with during server rendering and hydration when no
|
|
161
|
+
* provider sets one, for a `useSyncExternalStore` server snapshot. Warns once
|
|
162
|
+
* in development on the server.
|
|
163
|
+
*/
|
|
164
|
+
export declare function getServerPlatformSnapshot(): HotkeyPlatform;
|
|
155
165
|
/**
|
|
156
166
|
* A key for a provider's scope activation, or `null` when the scope should not
|
|
157
167
|
* be active. Equal keys mean the same activation, so adapters reactivate only
|
package/dist/adapter.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { warnServerPlatform } from "./_server.js";
|
|
1
2
|
import { chordHasKey, defaultHotkeyOptions, definedOptions, withDefaults } from "./_registration.js";
|
|
2
3
|
import { createStore } from "./_store.js";
|
|
3
4
|
import { createAppHotkeysStore, detectPlatform, getAppHotkeyRegistrationOptions, getHotkeyManager, isAppHotkey, isHotkeySequence, normalizeHotkey, parseHotkey, resolveAppHotkey } from "@tanstack/hotkeys";
|
|
@@ -122,17 +123,31 @@ function subscribePublishedAppHotkeys(onChange) {
|
|
|
122
123
|
/**
|
|
123
124
|
* Merges a provider's default options over its parent's. A provider with a
|
|
124
125
|
* `scope` sets `hotkey.scope`, so hotkeys registered inside it use that scope.
|
|
126
|
+
* A provider's `platform` is inherited, and is the default platform for its
|
|
127
|
+
* hotkeys and recorders; `hotkey.platform` and `hotkeyRecorder.platform` win.
|
|
125
128
|
*/
|
|
126
129
|
function mergeHotkeysProviderDefaults(parent, own, scope) {
|
|
130
|
+
const parentPlatform = { platform: parent?.platform };
|
|
131
|
+
const ownPlatform = { platform: own?.platform };
|
|
127
132
|
return {
|
|
133
|
+
...definedOptions(parentPlatform, ownPlatform),
|
|
128
134
|
hotkey: {
|
|
129
|
-
...definedOptions(parent?.hotkey, own?.hotkey),
|
|
135
|
+
...definedOptions(parentPlatform, parent?.hotkey, ownPlatform, own?.hotkey),
|
|
130
136
|
...scope && { scope }
|
|
131
137
|
},
|
|
132
|
-
hotkeyRecorder: definedOptions(parent?.hotkeyRecorder, own?.hotkeyRecorder)
|
|
138
|
+
hotkeyRecorder: definedOptions(parentPlatform, parent?.hotkeyRecorder, ownPlatform, own?.hotkeyRecorder)
|
|
133
139
|
};
|
|
134
140
|
}
|
|
135
141
|
/**
|
|
142
|
+
* The platform to render with during server rendering and hydration when no
|
|
143
|
+
* provider sets one, for a `useSyncExternalStore` server snapshot. Warns once
|
|
144
|
+
* in development on the server.
|
|
145
|
+
*/
|
|
146
|
+
function getServerPlatformSnapshot() {
|
|
147
|
+
warnServerPlatform();
|
|
148
|
+
return "linux";
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
136
151
|
* A key for a provider's scope activation, or `null` when the scope should not
|
|
137
152
|
* be active. Equal keys mean the same activation, so adapters reactivate only
|
|
138
153
|
* when the key changes, not when an inline `exclusive` object is recreated.
|
|
@@ -159,4 +174,4 @@ function activateHotkeysProviderScope(key) {
|
|
|
159
174
|
}
|
|
160
175
|
|
|
161
176
|
//#endregion
|
|
162
|
-
export { activateHotkeysProviderScope, createAppHotkeysStore, createHotkeyBindings, createStore, definedOptions, getHotkeysProviderScopeKey, mergeHotkeysProviderDefaults, prepareHotkeyDefinitions, subscribePublishedAppHotkeys, withDefaults };
|
|
177
|
+
export { activateHotkeysProviderScope, createAppHotkeysStore, createHotkeyBindings, createStore, definedOptions, getHotkeysProviderScopeKey, getServerPlatformSnapshot, mergeHotkeysProviderDefaults, prepareHotkeyDefinitions, subscribePublishedAppHotkeys, withDefaults };
|
package/dist/format.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MODIFIER_ORDER, normalizeKeyName } from "./constants.js";
|
|
2
2
|
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";
|
|
3
4
|
import { detectPlatform } from "./platform.js";
|
|
4
5
|
import { assertLogicalKey, codeToLogicalKey, modifierAlias, physicalCodeOf, splitChordParts } from "./_keyboard-event.js";
|
|
5
6
|
import { normalizeHotkey } from "./parse.js";
|
|
@@ -23,16 +24,21 @@ function formatForDisplay(hotkey, options = {}) {
|
|
|
23
24
|
const parts = formatChordParts(hotkey, options);
|
|
24
25
|
return options.parts ? parts : parts.join(chordSeparator(options));
|
|
25
26
|
}
|
|
27
|
+
/** Detects the platform for a label, warning in development on a server. */
|
|
28
|
+
function detectDisplayPlatform() {
|
|
29
|
+
warnServerPlatform();
|
|
30
|
+
return detectPlatform();
|
|
31
|
+
}
|
|
26
32
|
function modifierSymbolsEnabled(options) {
|
|
27
33
|
const symbols = options.useSymbols ?? true;
|
|
28
34
|
return typeof symbols === "boolean" ? symbols : symbols.modifiers ?? true;
|
|
29
35
|
}
|
|
30
36
|
function chordSeparator(options) {
|
|
31
|
-
const platform = options.platform ??
|
|
37
|
+
const platform = options.platform ?? detectDisplayPlatform();
|
|
32
38
|
return options.separatorToken ?? (platform === "mac" && modifierSymbolsEnabled(options) ? " " : "+");
|
|
33
39
|
}
|
|
34
40
|
function formatChordParts(hotkey, options) {
|
|
35
|
-
const platform = options.platform ??
|
|
41
|
+
const platform = options.platform ?? detectDisplayPlatform();
|
|
36
42
|
const symbols = options.useSymbols ?? true;
|
|
37
43
|
const modifierSymbols = modifierSymbolsEnabled(options);
|
|
38
44
|
const keySymbols = typeof symbols === "boolean" ? symbols : symbols.keys ?? true;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { CanonicalModifier, EditingKey, FunctionKey, IndividualKey, Key, LetterK
|
|
|
2
2
|
import { ConflictBehavior, FourModifierHotkeyChord, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyMeta, HotkeySequence, HotkeySequenceCallbackContext, ParsedHotkeyChord, ParsedModifiers, RawHotkeyChord, RawModifiers, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, SingleModifierHotkeyChord, ThreeModifierHotkeyChord, TwoModifierHotkeyChord } from "./hotkey.types.js";
|
|
3
3
|
import { ActivateHotkeyScopeOptions, HotkeyChordOptions, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyManager, HotkeyOptions, HotkeyOptionsFor, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequenceOptions, HotkeySequenceRegistration, HotkeySequenceRegistrationView, Target, getHotkeyManager, toHotkeyRegistrationView } from "./hotkey-manager.js";
|
|
4
4
|
import { AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CustomHotkeys, DefinedAppHotkeys, Register, RegisteredAppHotkeys, defineAppHotkeys, getAppHotkeyRegistrationOptions, isAppHotkey, isCustomHotkey, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey } from "./app-hotkeys.js";
|
|
5
|
+
import { HotkeyPlatform, detectPlatform, resolveModifier } from "./platform.js";
|
|
5
6
|
import { 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 } from "./constants.js";
|
|
6
7
|
import { KEY_DISPLAY_SYMBOLS, LINUX_MODIFIER_LABELS, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, PUNCTUATION_KEY_DISPLAY_LABELS, WINDOWS_MODIFIER_LABELS } from "./display-labels.js";
|
|
7
|
-
import { detectPlatform, resolveModifier } from "./platform.js";
|
|
8
8
|
import { DisplayHotkey, DisplayHotkeyChord, FormatDisplayOptions, formatForDisplay, formatHotkey } from "./format.js";
|
|
9
9
|
import { hasNonModifierKey, isHotkeySequence, isModifierKey, normalizeHotkey, parseHotkey, parseKeyboardEvent, simplifyHotkey } from "./parse.js";
|
|
10
10
|
import { ValidatableHotkey, ValidationResult, assertValidHotkey, checkHotkey, validateHotkey } from "./validate.js";
|
|
@@ -14,4 +14,4 @@ import { HotkeyConflict, HotkeyConflictOptions, findHotkeyConflicts } from "./co
|
|
|
14
14
|
import { KeyStateTracker, KeyStateTrackerState, getKeyStateTracker } from "./key-state-tracker.js";
|
|
15
15
|
import { HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, RecorderKeyMode, RecorderOptions, RecorderRejection } from "./hotkey-recorder.js";
|
|
16
16
|
import { createAppHotkeysStore } from "./_app-hotkeys-factory.js";
|
|
17
|
-
export { ALL_KEYS, ActivateHotkeyScopeOptions, AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CanonicalModifier, ConflictBehavior, CreateHotkeyChordHandlerOptions, CustomHotkeys, DEFAULT_SEQUENCE_TIMEOUT, DefinedAppHotkeys, DisplayHotkey, DisplayHotkeyChord, EDITING_KEYS, EditingKey, FUNCTION_KEYS, FormatDisplayOptions, FourModifierHotkeyChord, FunctionKey, HeldModifierOptions, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyChordOptions, HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyConflict, HotkeyConflictOptions, HotkeyManager, HotkeyMeta, HotkeyOptions, HotkeyOptionsFor, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequence, HotkeySequenceCallbackContext, HotkeySequenceOptions, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, HotkeySequenceRegistration, HotkeySequenceRegistrationView, IndividualKey, KEY_DISPLAY_SYMBOLS, Key, KeyStateTracker, KeyStateTrackerState, KeyboardEventMatch, LETTER_KEYS, LINUX_MODIFIER_LABELS, LetterKey, LogicalKey, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, Modifier, MultiHotkeyChordHandler, NAVIGATION_KEYS, NUMBER_KEYS, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, ParsedHotkeyChord, ParsedModifiers, PhysicalKey, PhysicalKeyCode, PunctuationKey, RawHotkeyChord, RawModifiers, RecorderKeyMode, RecorderOptions, RecorderRejection, Register, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, RegisteredAppHotkeys, SingleModifierHotkeyChord, Target, ThreeModifierHotkeyChord, TwoModifierHotkeyChord, ValidatableHotkey, ValidationResult, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createAppHotkeysStore, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
|
|
17
|
+
export { ALL_KEYS, ActivateHotkeyScopeOptions, AppHotkey, AppHotkeyChordOptions, AppHotkeyDefaultKind, AppHotkeyId, AppHotkeyInput, AppHotkeyListItem, AppHotkeyOptions, AppHotkeySequenceOptions, AppHotkeyStatus, AppHotkeys, AppHotkeysState, AppHotkeysStore, AppHotkeysStoreOptions, CanonicalModifier, ConflictBehavior, CreateHotkeyChordHandlerOptions, CustomHotkeys, DEFAULT_SEQUENCE_TIMEOUT, DefinedAppHotkeys, DisplayHotkey, DisplayHotkeyChord, EDITING_KEYS, EditingKey, FUNCTION_KEYS, FormatDisplayOptions, FourModifierHotkeyChord, FunctionKey, HeldModifierOptions, Hotkey, HotkeyCallback, HotkeyCallbackContext, HotkeyCallbackContextFor, HotkeyChord, HotkeyChordCallbackContext, HotkeyChordOptions, HotkeyChordRecorderOptions, HotkeyChordRecorderValidationContext, HotkeyChordRegistration, HotkeyChordRegistrationView, HotkeyConflict, HotkeyConflictOptions, HotkeyManager, HotkeyMeta, HotkeyOptions, HotkeyOptionsFor, HotkeyPlatform, HotkeyRecorder, HotkeyRecorderCommitKeys, HotkeyRecorderOptions, HotkeyRecorderState, HotkeyRecorderValidationContext, HotkeyRegistration, HotkeyRegistrationHandle, HotkeyRegistrationStatus, HotkeyRegistrationView, HotkeyScopesState, HotkeySequence, HotkeySequenceCallbackContext, HotkeySequenceOptions, HotkeySequenceRecorderOptions, HotkeySequenceRecorderValidationContext, HotkeySequenceRegistration, HotkeySequenceRegistrationView, IndividualKey, KEY_DISPLAY_SYMBOLS, Key, KeyStateTracker, KeyStateTrackerState, KeyboardEventMatch, LETTER_KEYS, LINUX_MODIFIER_LABELS, LetterKey, LogicalKey, MAC_MODIFIER_LABELS, MAC_MODIFIER_SYMBOLS, MODIFIER_ALIASES, MODIFIER_KEYS, MODIFIER_ORDER, Modifier, MultiHotkeyChordHandler, NAVIGATION_KEYS, NUMBER_KEYS, NamedKey, NavigationKey, NonPunctuationKey, NormalizedKeyboardEvent, NumberKey, PUNCTUATION_CODE_MAP, PUNCTUATION_KEYS, PUNCTUATION_KEY_DISPLAY_LABELS, ParsedHotkeyChord, ParsedModifiers, PhysicalKey, PhysicalKeyCode, PunctuationKey, RawHotkeyChord, RawModifiers, RecorderKeyMode, RecorderOptions, RecorderRejection, Register, RegisterableHotkey, RegisterableHotkeyChord, RegisterableHotkeySequence, RegisteredAppHotkeys, SingleModifierHotkeyChord, Target, ThreeModifierHotkeyChord, TwoModifierHotkeyChord, ValidatableHotkey, ValidationResult, WINDOWS_MODIFIER_LABELS, areHotkeysEqual, assertValidHotkey, checkHotkey, createAppHotkeysStore, createHotkeyChordHandler, createMultiHotkeyChordHandler, createSequenceMatcher, defineAppHotkeys, detectPlatform, findHotkeyConflicts, formatForDisplay, formatHotkey, getAppHotkeyRegistrationOptions, getHotkeyManager, getKeyStateTracker, hasNonModifierKey, isAppHotkey, isCustomHotkey, isHotkeySequence, isModifierKey, isSingleLetterKey, matchesHeldModifiers, matchesKeyboardEvent, normalizeHotkey, normalizeKeyName, parseHotkey, parseKeyboardEvent, provideAppHotkeys, publishAppHotkeysStore, resolveAppHotkey, resolveModifier, simplifyHotkey, toHotkeyRegistrationView, validateHotkey };
|
package/dist/match.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HotkeyCallback, HotkeyChord, HotkeyChordCallbackContext, ParsedHotkeyChord, RegisterableHotkey, RegisterableHotkeySequence } from "./hotkey.types.js";
|
|
2
|
+
import { HotkeyPlatform } from "./platform.js";
|
|
2
3
|
//#region src/match.d.ts
|
|
3
4
|
export interface KeyboardEventMatch {
|
|
4
5
|
matched: boolean;
|
|
@@ -96,7 +97,7 @@ export declare function createMultiHotkeyChordHandler(handlers: MultiHotkeyChord
|
|
|
96
97
|
* Aliases resolve on the supplied platform. A physical code and a logical key
|
|
97
98
|
* remain distinct even when one keyboard event could match both.
|
|
98
99
|
*/
|
|
99
|
-
export declare function areHotkeysEqual(left: RegisterableHotkey, right: RegisterableHotkey, platform?:
|
|
100
|
+
export declare function areHotkeysEqual(left: RegisterableHotkey, right: RegisterableHotkey, platform?: HotkeyPlatform): boolean;
|
|
100
101
|
/** Default timeout between sequence steps (in milliseconds). */
|
|
101
102
|
export declare const DEFAULT_SEQUENCE_TIMEOUT = 1000;
|
|
102
103
|
/**
|
package/dist/platform.d.ts
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
1
|
import { CanonicalModifier } from "./key.types.js";
|
|
2
2
|
//#region src/platform.d.ts
|
|
3
|
+
/** A platform for resolving `Mod` and formatting labels. */
|
|
4
|
+
export type HotkeyPlatform = 'mac' | 'windows' | 'linux';
|
|
3
5
|
/**
|
|
4
|
-
* Detects
|
|
6
|
+
* Detects a platform from a user agent string, or from the browser's
|
|
7
|
+
* `navigator` when called without one.
|
|
5
8
|
*
|
|
6
9
|
* Used internally to resolve platform-adaptive modifiers like 'Mod' (Command on Mac,
|
|
7
|
-
* Control elsewhere) and for platform-specific hotkey formatting.
|
|
10
|
+
* Control elsewhere) and for platform-specific hotkey formatting. During server
|
|
11
|
+
* rendering, pass the request's `User-Agent` header to detect the user's
|
|
12
|
+
* platform instead of the server's.
|
|
8
13
|
*
|
|
14
|
+
* @param userAgent - A user agent string, such as a request's `User-Agent` header.
|
|
15
|
+
* When omitted or `null` (a missing header), reads `navigator`.
|
|
9
16
|
* @returns The detected platform: 'mac', 'windows', or 'linux'
|
|
10
|
-
* @remarks
|
|
17
|
+
* @remarks Without a user agent, returns 'linux' on a server runtime (no
|
|
18
|
+
* `navigator`, or Node, Deno, Bun, or Cloudflare Workers), where `navigator`
|
|
19
|
+
* describes the server.
|
|
11
20
|
*
|
|
12
21
|
* @example
|
|
13
22
|
* ```ts
|
|
14
23
|
* const platform = detectPlatform() // 'mac' | 'windows' | 'linux'
|
|
15
24
|
* const modifier = resolveModifier('Mod', platform) // 'Meta' on Mac, 'Control' elsewhere
|
|
25
|
+
*
|
|
26
|
+
* // On the server
|
|
27
|
+
* detectPlatform(request.headers.get('user-agent'))
|
|
16
28
|
* ```
|
|
17
29
|
*/
|
|
18
|
-
export declare function detectPlatform(
|
|
30
|
+
export declare function detectPlatform(userAgent?: string | null): HotkeyPlatform;
|
|
19
31
|
/**
|
|
20
32
|
* Resolves the platform-adaptive 'Mod' modifier to the appropriate canonical modifier.
|
|
21
33
|
*
|
package/dist/platform.js
CHANGED
|
@@ -1,25 +1,42 @@
|
|
|
1
|
+
import { isServerRuntime } from "./_server.js";
|
|
2
|
+
|
|
1
3
|
//#region src/platform.ts
|
|
2
4
|
/**
|
|
3
|
-
* Detects
|
|
5
|
+
* Detects a platform from a user agent string, or from the browser's
|
|
6
|
+
* `navigator` when called without one.
|
|
4
7
|
*
|
|
5
8
|
* Used internally to resolve platform-adaptive modifiers like 'Mod' (Command on Mac,
|
|
6
|
-
* Control elsewhere) and for platform-specific hotkey formatting.
|
|
9
|
+
* Control elsewhere) and for platform-specific hotkey formatting. During server
|
|
10
|
+
* rendering, pass the request's `User-Agent` header to detect the user's
|
|
11
|
+
* platform instead of the server's.
|
|
7
12
|
*
|
|
13
|
+
* @param userAgent - A user agent string, such as a request's `User-Agent` header.
|
|
14
|
+
* When omitted or `null` (a missing header), reads `navigator`.
|
|
8
15
|
* @returns The detected platform: 'mac', 'windows', or 'linux'
|
|
9
|
-
* @remarks
|
|
16
|
+
* @remarks Without a user agent, returns 'linux' on a server runtime (no
|
|
17
|
+
* `navigator`, or Node, Deno, Bun, or Cloudflare Workers), where `navigator`
|
|
18
|
+
* describes the server.
|
|
10
19
|
*
|
|
11
20
|
* @example
|
|
12
21
|
* ```ts
|
|
13
22
|
* const platform = detectPlatform() // 'mac' | 'windows' | 'linux'
|
|
14
23
|
* const modifier = resolveModifier('Mod', platform) // 'Meta' on Mac, 'Control' elsewhere
|
|
24
|
+
*
|
|
25
|
+
* // On the server
|
|
26
|
+
* detectPlatform(request.headers.get('user-agent'))
|
|
15
27
|
* ```
|
|
16
28
|
*/
|
|
17
|
-
function detectPlatform() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
function detectPlatform(userAgent) {
|
|
30
|
+
let platform = "";
|
|
31
|
+
let agent = userAgent;
|
|
32
|
+
if (agent == null) {
|
|
33
|
+
if (isServerRuntime()) return "linux";
|
|
34
|
+
platform = navigator.platform?.toLowerCase() ?? "";
|
|
35
|
+
agent = navigator.userAgent ?? "";
|
|
36
|
+
}
|
|
37
|
+
agent = agent.toLowerCase();
|
|
38
|
+
if (platform.includes("mac") || agent.includes("mac")) return "mac";
|
|
39
|
+
if (platform.includes("win") || agent.includes("win")) return "windows";
|
|
23
40
|
return "linux";
|
|
24
41
|
}
|
|
25
42
|
/**
|
package/package.json
CHANGED
|
@@ -66,6 +66,7 @@ When nesting scopes, stacking dialogs, or keeping some hotkeys working during an
|
|
|
66
66
|
- **Single-character shortcuts need an off switch.** For WCAG 2.1.4, `K`, `?`, and `['G', 'H']` need a user setting passed as `enabled`, or an app hotkey users can remap.
|
|
67
67
|
- **react-hotkeys-hook names don't carry over.** `useHotkeys` and `HotkeysProvider` share names, not signatures: there's no dependency array, `preventDefault` is on by default, and sequences are arrays. Remove the old library, so two libraries don't handle the same keys.
|
|
68
68
|
- **Install only the adapter.** A separately installed `@tanstack/hotkeys` at another version is a second copy, and scopes don't apply across copies.
|
|
69
|
+
- **Server-rendered labels need the user's platform.** On a server, `formatForDisplay` renders Linux labels, because the server can't see the user's OS. Read the request's `User-Agent` header, pass `detectPlatform(userAgent)` as the platform of the adapter's provider or scope, and format labels with the adapter's formatter, such as `useFormatForDisplay`. Those pages then depend on the user agent, so keep them out of shared caches or add the platform to the cache key.
|
|
69
70
|
|
|
70
71
|
When writing tests for hotkeys, read [testing](references/testing.md).
|
|
71
72
|
|