@remcostoeten/use-shortcut 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-01-22
9
+
10
+ ### Added
11
+
12
+ - Chainable API for fluent shortcut definitions (`$.cmd.key("s").on(handler)`)
13
+ - Cross-platform `mod` modifier (⌘ on Mac, Ctrl on Windows/Linux)
14
+ - Context-aware exceptions with `.except()` for inputs, modals, and custom predicates
15
+ - Full TypeScript intellisense at every step of the chain
16
+ - Platform-aware display formatting (⌘S on Mac, Ctrl+S on Windows)
17
+ - `useShortcut` hook for React with automatic cleanup
18
+ - `createShortcut` for non-React usage
19
+ - CLI for shadcn-style copy-paste installation
20
+ - Shortcut result object with `enable()`, `disable()`, `trigger()`, and `unbind()`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Remco Stoeten
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # @remcostoeten/use-shortcut
2
+
3
+ Chainable keyboard shortcuts for React with **perfect TypeScript intellisense**.
4
+
5
+ ```tsx
6
+ const $ = useShortcut()
7
+
8
+ $.cmd.shift.key("s").on(() => save())
9
+ $.mod.key("k").on(() => search())
10
+ $.key("/").except("typing").on(() => focusSearch())
11
+ ```
12
+
13
+ ## Features
14
+
15
+ - **Chainable API** - Fluent, readable shortcut definitions
16
+ - **Perfect TypeScript** - Intellisense at every step
17
+ - **Cross-platform** - `mod` = ⌘ on Mac, Ctrl on Windows/Linux
18
+ - **Context-aware** - Skip shortcuts in inputs with `.except()`
19
+ - **Zero dependencies** - Only React as peer dependency
20
+ - **Tiny** - ~3KB gzipped
21
+
22
+ ## Installation
23
+
24
+ ### npm/pnpm/bun
25
+
26
+ ```bash
27
+ npm install @remcostoeten/use-shortcut
28
+ pnpm add @remcostoeten/use-shortcut
29
+ bun add @remcostoeten/use-shortcut
30
+ ```
31
+
32
+ ### Copy-paste (shadcn-style)
33
+
34
+ ```bash
35
+ npx @remcostoeten/use-shortcut init
36
+ # or
37
+ bunx @remcostoeten/use-shortcut init
38
+ ```
39
+
40
+ This copies the source files directly into your project at `hooks/use-shortcut/`.
41
+
42
+ ## Quick Start
43
+
44
+ ```tsx
45
+ "use client"
46
+
47
+ import { useShortcut } from "@remcostoeten/use-shortcut"
48
+
49
+ export function App() {
50
+ const $ = useShortcut()
51
+
52
+ $.cmd.key("s").on(() => {
53
+ console.log("Save!")
54
+ })
55
+
56
+ $.mod.key("k").on(() => {
57
+ console.log("Search!")
58
+ })
59
+
60
+ return <div>Press ⌘+S or ⌘+K</div>
61
+ }
62
+ ```
63
+
64
+ ## API
65
+
66
+ ### Modifiers
67
+
68
+ Chain modifiers before calling `.key()`:
69
+
70
+ ```tsx
71
+ $.ctrl.key("s") // Ctrl+S
72
+ $.shift.key("enter") // Shift+Enter
73
+ $.alt.key("n") // Alt+N
74
+ $.cmd.key("k") // ⌘+K (Mac) or Ctrl+K (Windows)
75
+ $.mod.key("k") // Cross-platform: ⌘ on Mac, Ctrl on Windows/Linux
76
+
77
+ // Multiple modifiers
78
+ $.ctrl.shift.key("p") // Ctrl+Shift+P
79
+ $.cmd.shift.alt.key("a") // ⌘+Shift+Alt+A
80
+ ```
81
+
82
+ ### Keys
83
+
84
+ Supports all standard keys:
85
+
86
+ ```tsx
87
+ // Letters
88
+ $.mod.key("s") // a-z
89
+
90
+ // Numbers
91
+ $.mod.key("1") // 0-9
92
+
93
+ // Function keys
94
+ $.key("f1") // f1-f12
95
+
96
+ // Special keys
97
+ $.key("escape") // escape, enter, space, tab
98
+ $.key("backspace") // backspace, delete
99
+ $.mod.key("up") // up, down, left, right
100
+ $.key("home") // home, end, pageup, pagedown
101
+
102
+ // Symbols
103
+ $.mod.key("slash") // slash, backslash, comma, period
104
+ $.mod.key("/") // Also works with actual symbol
105
+ ```
106
+
107
+ ### Exception Handling
108
+
109
+ Skip shortcuts in certain contexts:
110
+
111
+ ```tsx
112
+ // Built-in presets
113
+ $.key("/").except("input").on(handler) // Skip in <input>, <textarea>, <select>
114
+ $.key("/").except("editable").on(handler) // Skip in contenteditable
115
+ $.key("/").except("typing").on(handler) // Skip in any text input
116
+ $.key("escape").except("modal").on(handler) // Skip when modal is open
117
+ $.key("enter").except("disabled").on(handler) // Skip on disabled elements
118
+
119
+ // Multiple presets
120
+ $.key("/").except(["input", "modal"]).on(handler)
121
+
122
+ // Custom predicate
123
+ $.key("k").except((e) => {
124
+ return e.target.classList.contains("no-shortcuts")
125
+ }).on(handler)
126
+ ```
127
+
128
+ ### Handler Options
129
+
130
+ ```tsx
131
+ $.mod.key("s").on(save, {
132
+ preventDefault: true, // Prevent browser default (default: true)
133
+ stopPropagation: false, // Stop event bubbling (default: false)
134
+ delay: 100, // Delay before firing (ms)
135
+ description: "Save doc", // For accessibility
136
+ disabled: false, // Temporarily disable
137
+ })
138
+ ```
139
+
140
+ ### Result Object
141
+
142
+ `.on()` returns a result object:
143
+
144
+ ```tsx
145
+ const save = $.mod.key("s").on(handleSave)
146
+
147
+ save.display // "⌘S" on Mac, "Ctrl+S" on Windows
148
+ save.combo // "cmd+s"
149
+ save.isEnabled // true/false
150
+ save.enable() // Enable the shortcut
151
+ save.disable() // Disable the shortcut
152
+ save.unbind() // Remove the shortcut
153
+ save.trigger() // Programmatically trigger
154
+ ```
155
+
156
+ ### Hook Options
157
+
158
+ ```tsx
159
+ const $ = useShortcut({
160
+ debug: true, // Log all shortcuts to console
161
+ delay: 0, // Global delay for all shortcuts
162
+ ignoreInputs: true, // Ignore in form elements (default: true)
163
+ disabled: false, // Disable all shortcuts
164
+ eventType: "keydown", // or "keyup"
165
+ target: window, // Custom event target
166
+ })
167
+ ```
168
+
169
+ ## Vanilla JS (Non-React)
170
+
171
+ ```tsx
172
+ import { createShortcut } from "@remcostoeten/use-shortcut"
173
+
174
+ const $ = createShortcut()
175
+
176
+ const save = $.mod.key("s").on(() => {
177
+ console.log("Saved!")
178
+ })
179
+
180
+ // Clean up when done
181
+ save.unbind()
182
+ ```
183
+
184
+ ## Display Formatting
185
+
186
+ ```tsx
187
+ import { formatShortcut } from "@remcostoeten/use-shortcut"
188
+
189
+ formatShortcut("cmd+s") // "⌘S" on Mac, "Ctrl+S" on Windows
190
+ formatShortcut("ctrl+shift+p") // "⌃⇧P" on Mac, "Ctrl+Shift+P" on Windows
191
+ ```
192
+
193
+ ## TypeScript
194
+
195
+ Full type definitions with intellisense:
196
+
197
+ ```tsx
198
+ import type {
199
+ ShortcutBuilder,
200
+ ShortcutResult,
201
+ ShortcutHandler,
202
+ HandlerOptions,
203
+ ActionKey,
204
+ ModifierName,
205
+ } from "@remcostoeten/use-shortcut"
206
+ ```
207
+
208
+ ## License
209
+
210
+ MIT © Remco Stoeten
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+
4
+ // cli/index.ts
5
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
6
+ import { join, dirname } from "path";
7
+ import { fileURLToPath } from "url";
8
+ var __filename = fileURLToPath(import.meta.url);
9
+ var __dirname = dirname(__filename);
10
+ var COLORS = {
11
+ reset: "\x1B[0m",
12
+ green: "\x1B[32m",
13
+ cyan: "\x1B[36m",
14
+ yellow: "\x1B[33m",
15
+ dim: "\x1B[2m"
16
+ };
17
+ function log(message, color = COLORS.reset) {
18
+ console.log(`${color}${message}${COLORS.reset}`);
19
+ }
20
+ function getSrcPath() {
21
+ return join(__dirname, "..", "src");
22
+ }
23
+ function getDestPath(targetDir) {
24
+ return join(process.cwd(), targetDir, "use-shortcut");
25
+ }
26
+ var FILES = [
27
+ "index.ts",
28
+ "hook.ts",
29
+ "builder.ts",
30
+ "types.ts",
31
+ "parser.ts",
32
+ "constants.ts",
33
+ "formatter.ts"
34
+ ];
35
+ function init(targetDir = "hooks") {
36
+ const srcPath = getSrcPath();
37
+ const destPath = getDestPath(targetDir);
38
+ log("\n\u{1F3B9} use-shortcut CLI\n", COLORS.cyan);
39
+ if (existsSync(destPath)) {
40
+ log(`\u26A0\uFE0F Directory already exists: ${destPath}`, COLORS.yellow);
41
+ log(" Use --force to overwrite\n");
42
+ return;
43
+ }
44
+ mkdirSync(destPath, { recursive: true });
45
+ for (const file of FILES) {
46
+ const srcFile = join(srcPath, file);
47
+ const destFile = join(destPath, file);
48
+ if (!existsSync(srcFile)) {
49
+ log(`\u26A0\uFE0F Source file not found: ${file}`, COLORS.yellow);
50
+ continue;
51
+ }
52
+ let content = readFileSync(srcFile, "utf-8");
53
+ content = content.replace(/"use client"\n\n/, '"use client"\n\n');
54
+ writeFileSync(destFile, content);
55
+ log(` \u2713 ${file}`, COLORS.green);
56
+ }
57
+ log("\n\u2728 Done! Files copied to:", COLORS.green);
58
+ log(` ${destPath}
59
+ `, COLORS.dim);
60
+ log("Usage:", COLORS.cyan);
61
+ log(` import { useShortcut } from "@/${targetDir}/use-shortcut"
62
+ `, COLORS.dim);
63
+ log(" const $ = useShortcut()", COLORS.dim);
64
+ log(' $.mod.key("k").on(() => console.log("Search!"))\n', COLORS.dim);
65
+ }
66
+ function main() {
67
+ const args = process.argv.slice(2);
68
+ const command = args[0];
69
+ if (command === "init") {
70
+ const targetIndex = args.indexOf("--target");
71
+ const targetDir = targetIndex !== -1 ? args[targetIndex + 1] : "hooks";
72
+ init(targetDir);
73
+ } else {
74
+ log("\n\u{1F3B9} use-shortcut CLI\n", COLORS.cyan);
75
+ log("Commands:", COLORS.yellow);
76
+ log(" init Copy files to your project");
77
+ log(" init --target lib Copy to custom directory\n");
78
+ }
79
+ }
80
+ main();
@@ -0,0 +1,316 @@
1
+ declare const Platform: {
2
+ readonly MAC: "mac";
3
+ readonly WINDOWS: "windows";
4
+ readonly LINUX: "linux";
5
+ };
6
+ type PlatformType = (typeof Platform)[keyof typeof Platform];
7
+ declare function detectPlatform(): PlatformType;
8
+ declare const ModifierKey: {
9
+ readonly META: "meta";
10
+ readonly CTRL: "ctrl";
11
+ readonly ALT: "alt";
12
+ readonly SHIFT: "shift";
13
+ };
14
+ type ModifierKeyType = (typeof ModifierKey)[keyof typeof ModifierKey];
15
+ declare const ModifierAliases: Record<string, ModifierKeyType>;
16
+ declare const SpecialKeyMap: Record<string, string>;
17
+ declare const ModifierDisplaySymbols: Record<PlatformType, Record<ModifierKeyType, string>>;
18
+ declare const ModifierDisplayOrder: Record<PlatformType, ModifierKeyType[]>;
19
+
20
+ /** Lowercase letter keys a-z */
21
+ type AlphaKey = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
22
+ /** Number keys 0-9 */
23
+ type NumericKey = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
24
+ /** Function keys F1-F12 */
25
+ type FunctionKey = "f1" | "f2" | "f3" | "f4" | "f5" | "f6" | "f7" | "f8" | "f9" | "f10" | "f11" | "f12";
26
+ /** Arrow and navigation keys */
27
+ type NavigationKey = "up" | "down" | "left" | "right" | "arrowup" | "arrowdown" | "arrowleft" | "arrowright" | "home" | "end" | "pageup" | "pagedown";
28
+ /** Special action keys like Enter, Escape, Tab */
29
+ type SpecialKey = "enter" | "return" | "escape" | "esc" | "space" | "tab" | "backspace" | "delete" | "del" | "insert";
30
+ /** Symbol and punctuation keys */
31
+ type SymbolKey = "minus" | "plus" | "equal" | "equals" | "bracketleft" | "bracketright" | "backslash" | "slash" | "/" | "comma" | "period" | "semicolon" | "quote" | "backtick";
32
+ /**
33
+ * All valid action keys that can be used with `.key()`
34
+ * @example $.mod.key("s") // "s" is an ActionKey
35
+ */
36
+ type ActionKey = AlphaKey | NumericKey | FunctionKey | NavigationKey | SpecialKey | SymbolKey;
37
+ /** Modifier key names used in the chainable API */
38
+ type ModifierName = "ctrl" | "shift" | "alt" | "cmd" | "mod";
39
+ /** Internal modifier state flags */
40
+ type ModifierFlags = {
41
+ ctrl: boolean;
42
+ shift: boolean;
43
+ alt: boolean;
44
+ cmd: boolean;
45
+ };
46
+ /** Modifier key state from a keyboard event */
47
+ type ModifierState = {
48
+ meta: boolean;
49
+ ctrl: boolean;
50
+ alt: boolean;
51
+ shift: boolean;
52
+ };
53
+ /** Result of parsing a shortcut string */
54
+ type ParsedShortcut = {
55
+ modifiers: ModifierState;
56
+ key: string;
57
+ original: string;
58
+ };
59
+ type EmptyModifiers = {};
60
+ /**
61
+ * Handler function called when a shortcut is triggered
62
+ * @param event - The keyboard event that triggered the shortcut
63
+ */
64
+ type ShortcutHandler = (event: KeyboardEvent) => void;
65
+ /**
66
+ * Custom predicate for excluding shortcuts in certain conditions
67
+ * @param event - The keyboard event to evaluate
68
+ * @returns `true` to skip the shortcut, `false` to allow it
69
+ */
70
+ type ExceptPredicate = (event: KeyboardEvent) => boolean;
71
+ /**
72
+ * Built-in exception presets for common scenarios
73
+ * - `"input"` - Skip when focused on input, textarea, or select
74
+ * - `"editable"` - Skip when focused on contentEditable elements
75
+ * - `"typing"` - Skip in any text input context (combines input + editable)
76
+ * - `"modal"` - Skip when a modal/dialog is open (checks [data-modal] or [role="dialog"])
77
+ * - `"disabled"` - Skip when focused element is disabled
78
+ */
79
+ type ExceptPreset = "input" | "editable" | "typing" | "modal" | "disabled";
80
+ /**
81
+ * Options for shortcut handler registration
82
+ */
83
+ type HandlerOptions = {
84
+ /** Prevent the browser's default action (default: `true`) */
85
+ preventDefault?: boolean;
86
+ /** Stop event propagation */
87
+ stopPropagation?: boolean;
88
+ /** Delay handler execution in milliseconds */
89
+ delay?: number;
90
+ /** Description for documentation/debugging */
91
+ description?: string;
92
+ /** Disable this specific shortcut */
93
+ disabled?: boolean;
94
+ /** Limit shortcut to a specific DOM element */
95
+ scope?: HTMLElement | null;
96
+ /** Conditions to skip the shortcut */
97
+ except?: ExceptPreset | ExceptPreset[] | ExceptPredicate;
98
+ };
99
+ /**
100
+ * Result object returned when registering a shortcut
101
+ * Provides control over the shortcut and display information
102
+ */
103
+ type ShortcutResult = {
104
+ /** Remove the keyboard listener */
105
+ unbind: () => void;
106
+ /** Platform-aware display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows) */
107
+ display: string;
108
+ /** Normalized combo string (e.g., "cmd+s") */
109
+ combo: string;
110
+ /** Programmatically trigger the shortcut handler */
111
+ trigger: () => void;
112
+ /** Whether the shortcut is currently enabled */
113
+ isEnabled: boolean;
114
+ /** Enable the shortcut (after being disabled) */
115
+ enable: () => void;
116
+ /** Temporarily disable the shortcut */
117
+ disable: () => void;
118
+ /** Subscribe to shortcut attempt events (useful for visual feedback) */
119
+ onAttempt?: (callback: (matched: boolean, event: KeyboardEvent) => void) => () => void;
120
+ };
121
+ /**
122
+ * Chainable modifier builder with type-safe exhaustion
123
+ * Each modifier can only be used once in a chain
124
+ */
125
+ type ModifierChain<Used extends Partial<ModifierFlags>> = {
126
+ ctrl: Used["ctrl"] extends true ? never : ModifierChain<Used & {
127
+ ctrl: true;
128
+ }>;
129
+ shift: Used["shift"] extends true ? never : ModifierChain<Used & {
130
+ shift: true;
131
+ }>;
132
+ alt: Used["alt"] extends true ? never : ModifierChain<Used & {
133
+ alt: true;
134
+ }>;
135
+ cmd: Used["cmd"] extends true ? never : ModifierChain<Used & {
136
+ cmd: true;
137
+ }>;
138
+ mod: Used["cmd"] extends true ? never : ModifierChain<Used & {
139
+ cmd: true;
140
+ }>;
141
+ key: <K extends ActionKey>(key: K) => KeyChain<Used, K>;
142
+ };
143
+ /**
144
+ * Chain state after calling `.key()` - ready to attach a handler
145
+ */
146
+ type KeyChain<Used extends Partial<ModifierFlags>, Key extends ActionKey> = {
147
+ /** Attach a handler to this shortcut */
148
+ on: (handler: ShortcutHandler, options?: HandlerOptions) => ShortcutResult;
149
+ /** Attach a handler with inline options */
150
+ handle: (options: HandlerOptions & {
151
+ handler: ShortcutHandler;
152
+ }) => ShortcutResult;
153
+ /** Add exception conditions before attaching handler */
154
+ except: (condition: ExceptPreset | ExceptPreset[] | ExceptPredicate) => KeyChainWithExcept<Used, Key>;
155
+ };
156
+ /**
157
+ * Chain state after calling `.except()` - ready to attach a handler
158
+ */
159
+ type KeyChainWithExcept<Used extends Partial<ModifierFlags>, Key extends ActionKey> = {
160
+ on: (handler: ShortcutHandler, options?: Omit<HandlerOptions, "except">) => ShortcutResult;
161
+ };
162
+ /**
163
+ * The main shortcut builder interface returned by `useShortcut()`
164
+ * @example
165
+ * const $ = useShortcut()
166
+ * $.mod.key("s").on(() => save())
167
+ * $.ctrl.shift.key("p").on(() => openPalette())
168
+ * $.key("/").except("typing").on(() => focusSearch())
169
+ */
170
+ type ShortcutBuilder = ModifierChain<EmptyModifiers> & {
171
+ ctrl: ModifierChain<{
172
+ ctrl: true;
173
+ }>;
174
+ shift: ModifierChain<{
175
+ shift: true;
176
+ }>;
177
+ alt: ModifierChain<{
178
+ alt: true;
179
+ }>;
180
+ cmd: ModifierChain<{
181
+ cmd: true;
182
+ }>;
183
+ mod: ModifierChain<{
184
+ cmd: true;
185
+ }>;
186
+ key: <K extends ActionKey>(key: K) => KeyChain<EmptyModifiers, K>;
187
+ };
188
+ /**
189
+ * Options for the `useShortcut` hook
190
+ */
191
+ type UseShortcutOptions = {
192
+ /** Enable debug logging to console */
193
+ debug?: boolean;
194
+ /** Global delay for all handlers in milliseconds */
195
+ delay?: number;
196
+ /** Skip shortcuts when focused on input elements (default: `true`) */
197
+ ignoreInputs?: boolean;
198
+ /** Target element for keyboard listeners (default: `window`) */
199
+ target?: HTMLElement | Window | null;
200
+ /** Keyboard event type to listen for (default: `"keydown"`) */
201
+ eventType?: "keydown" | "keyup";
202
+ /** Globally disable all shortcuts from this hook */
203
+ disabled?: boolean;
204
+ };
205
+
206
+ /**
207
+ * Parse a shortcut string into its components
208
+ *
209
+ * @param shortcut - Shortcut string (e.g., "cmd+s", "ctrl+shift+p")
210
+ * @returns Parsed shortcut with modifiers, key, and original string
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * const parsed = parseShortcut("cmd+s")
215
+ * // { modifiers: { meta: true, ... }, key: "s", original: "cmd+s" }
216
+ * ```
217
+ */
218
+ declare function parseShortcut(shortcut: string): ParsedShortcut;
219
+ /**
220
+ * Parse multiple shortcut strings
221
+ *
222
+ * @param shortcuts - Single shortcut or array of shortcuts
223
+ * @returns Array of parsed shortcuts
224
+ */
225
+ declare function parseShortcuts(shortcuts: string | string[]): ParsedShortcut[];
226
+ /**
227
+ * Extract modifier state from a keyboard event
228
+ *
229
+ * @param event - The keyboard event
230
+ * @returns Object with meta, ctrl, alt, shift boolean flags
231
+ */
232
+ declare function getModifiersFromEvent(event: KeyboardEvent): ModifierState;
233
+ /**
234
+ * Check if a keyboard event matches a parsed shortcut
235
+ *
236
+ * @param event - The keyboard event to check
237
+ * @param parsed - The parsed shortcut to match against
238
+ * @returns `true` if the event matches the shortcut
239
+ */
240
+ declare function matchesShortcut(event: KeyboardEvent, parsed: ParsedShortcut): boolean;
241
+ /**
242
+ * Check if a keyboard event matches any of the parsed shortcuts
243
+ *
244
+ * @param event - The keyboard event to check
245
+ * @param parsedShortcuts - Array of parsed shortcuts to match against
246
+ * @returns `true` if the event matches any shortcut
247
+ */
248
+ declare function matchesAnyShortcut(event: KeyboardEvent, parsedShortcuts: ParsedShortcut[]): boolean;
249
+
250
+ /**
251
+ * Format a shortcut string for display with platform-aware symbols
252
+ *
253
+ * @param shortcut - Shortcut string (e.g., "cmd+s")
254
+ * @param platform - Optional platform override (default: auto-detect)
255
+ * @returns Formatted display string (e.g., "⌘S" on Mac, "Ctrl+S" on Windows)
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * formatShortcut("cmd+s") // "⌘S" on Mac, "Ctrl+S" on Windows
260
+ * formatShortcut("ctrl+shift+p", "mac") // "⌃⇧P"
261
+ * ```
262
+ */
263
+ declare function formatShortcut(shortcut: string, platform?: PlatformType): string;
264
+ /**
265
+ * Get the modifier key symbols for a platform
266
+ *
267
+ * @param platform - Optional platform override (default: auto-detect)
268
+ * @returns Object mapping modifier keys to display symbols
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * getModifierSymbols("mac") // { meta: "⌘", ctrl: "⌃", alt: "⌥", shift: "⇧" }
273
+ * ```
274
+ */
275
+ declare function getModifierSymbols(platform?: PlatformType): Record<ModifierKeyType, string>;
276
+
277
+ /**
278
+ * React hook for registering chainable keyboard shortcuts
279
+ *
280
+ * @param options - Configuration options for the hook
281
+ * @returns A chainable shortcut builder (`$`)
282
+ *
283
+ * @example
284
+ * ```tsx
285
+ * function App() {
286
+ * const $ = useShortcut()
287
+ *
288
+ * $.mod.key("s").on(() => save())
289
+ * $.ctrl.shift.key("p").on(() => openPalette())
290
+ * $.key("/").except("typing").on(() => focusSearch())
291
+ *
292
+ * return <div>Press ⌘S to save</div>
293
+ * }
294
+ * ```
295
+ */
296
+ declare function useShortcut(options?: UseShortcutOptions): ShortcutBuilder;
297
+ /**
298
+ * Create a shortcut builder for non-React usage
299
+ *
300
+ * Unlike `useShortcut`, this does not auto-cleanup - you must call `.unbind()` manually.
301
+ *
302
+ * @param options - Configuration options
303
+ * @returns A chainable shortcut builder
304
+ *
305
+ * @example
306
+ * ```ts
307
+ * const $ = createShortcut()
308
+ * const save = $.mod.key("s").on(() => save())
309
+ *
310
+ * // Cleanup when done
311
+ * save.unbind()
312
+ * ```
313
+ */
314
+ declare function createShortcut(options?: UseShortcutOptions): ShortcutBuilder;
315
+
316
+ export { type ActionKey, type AlphaKey, type ExceptPredicate, type ExceptPreset, type FunctionKey, type HandlerOptions, type KeyChain, ModifierAliases, type ModifierChain, ModifierDisplayOrder, ModifierDisplaySymbols, type ModifierFlags, ModifierKey, type ModifierName, type ModifierState, type NavigationKey, type NumericKey, type ParsedShortcut, Platform, type ShortcutBuilder, type ShortcutHandler, type ShortcutResult, type SpecialKey, SpecialKeyMap, type SymbolKey, type UseShortcutOptions, createShortcut, detectPlatform, formatShortcut, getModifierSymbols, getModifiersFromEvent, matchesAnyShortcut, matchesShortcut, parseShortcut, parseShortcuts, useShortcut };