use-kbd 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -36
- package/dist/index.cjs +2145 -409
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +646 -76
- package/dist/index.d.ts +646 -76
- package/dist/index.js +2108 -405
- package/dist/index.js.map +1 -1
- package/package.json +6 -8
- package/src/styles.css +302 -67
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as react from 'react';
|
|
3
|
-
import { ReactNode, RefObject,
|
|
2
|
+
import { ReactNode, ComponentType, RefObject, SVGProps, CSSProperties } from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Modifier keys state
|
|
7
|
+
*/
|
|
8
|
+
interface Modifiers {
|
|
9
|
+
ctrl: boolean;
|
|
10
|
+
alt: boolean;
|
|
11
|
+
shift: boolean;
|
|
12
|
+
meta: boolean;
|
|
13
|
+
}
|
|
5
14
|
/**
|
|
6
15
|
* Represents a single key press (possibly with modifiers)
|
|
7
16
|
*/
|
|
@@ -9,12 +18,7 @@ interface KeyCombination {
|
|
|
9
18
|
/** The main key (lowercase, e.g., 'k', 'enter', 'arrowup') */
|
|
10
19
|
key: string;
|
|
11
20
|
/** Modifier keys pressed */
|
|
12
|
-
modifiers:
|
|
13
|
-
ctrl: boolean;
|
|
14
|
-
alt: boolean;
|
|
15
|
-
shift: boolean;
|
|
16
|
-
meta: boolean;
|
|
17
|
-
};
|
|
21
|
+
modifiers: Modifiers;
|
|
18
22
|
}
|
|
19
23
|
/**
|
|
20
24
|
* Represents a hotkey - either a single key or a sequence of keys.
|
|
@@ -22,6 +26,64 @@ interface KeyCombination {
|
|
|
22
26
|
* Sequence: [{ key: '2', ... }, { key: 'w', ... }]
|
|
23
27
|
*/
|
|
24
28
|
type HotkeySequence = KeyCombination[];
|
|
29
|
+
/**
|
|
30
|
+
* A single element in a key sequence (sum type).
|
|
31
|
+
* - 'key': exact key match (with optional modifiers)
|
|
32
|
+
* - 'digit': matches any single digit 0-9 (\d)
|
|
33
|
+
* - 'digits': matches one or more digits (\d+)
|
|
34
|
+
*/
|
|
35
|
+
type SeqElem = {
|
|
36
|
+
type: 'key';
|
|
37
|
+
key: string;
|
|
38
|
+
modifiers: Modifiers;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'digit';
|
|
41
|
+
} | {
|
|
42
|
+
type: 'digits';
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* A key sequence pattern (array of sequence elements)
|
|
46
|
+
*/
|
|
47
|
+
type KeySeq = SeqElem[];
|
|
48
|
+
/**
|
|
49
|
+
* Sequence element with match state (for tracking during input).
|
|
50
|
+
* - 'key': has `matched` flag
|
|
51
|
+
* - 'digit': has captured `value`
|
|
52
|
+
* - 'digits': has captured `value` or in-progress `partial` string
|
|
53
|
+
*/
|
|
54
|
+
type SeqElemState = {
|
|
55
|
+
type: 'key';
|
|
56
|
+
key: string;
|
|
57
|
+
modifiers: Modifiers;
|
|
58
|
+
matched?: true;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'digit';
|
|
61
|
+
value?: number;
|
|
62
|
+
} | {
|
|
63
|
+
type: 'digits';
|
|
64
|
+
value?: number;
|
|
65
|
+
partial?: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Sequence match state - tracks progress through a sequence with captures
|
|
69
|
+
*/
|
|
70
|
+
type SeqMatchState = SeqElemState[];
|
|
71
|
+
/**
|
|
72
|
+
* Extract captured values from a completed sequence match
|
|
73
|
+
*/
|
|
74
|
+
declare function extractCaptures(state: SeqMatchState): number[];
|
|
75
|
+
/**
|
|
76
|
+
* Check if a SeqElem is a digit placeholder
|
|
77
|
+
*/
|
|
78
|
+
declare function isDigitPlaceholder(elem: SeqElem): elem is {
|
|
79
|
+
type: 'digit';
|
|
80
|
+
} | {
|
|
81
|
+
type: 'digits';
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Count digit placeholders in a sequence
|
|
85
|
+
*/
|
|
86
|
+
declare function countPlaceholders(seq: KeySeq): number;
|
|
25
87
|
/**
|
|
26
88
|
* Platform-aware display format for a key combination or sequence
|
|
27
89
|
*/
|
|
@@ -53,10 +115,8 @@ interface RecordHotkeyResult {
|
|
|
53
115
|
pendingKeys: HotkeySequence;
|
|
54
116
|
/** The key currently being held (for live UI feedback during recording) */
|
|
55
117
|
activeKeys: KeyCombination | null;
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
*/
|
|
59
|
-
combination: KeyCombination | null;
|
|
118
|
+
/** The timeout duration for sequences (ms) */
|
|
119
|
+
sequenceTimeout: number;
|
|
60
120
|
}
|
|
61
121
|
/**
|
|
62
122
|
* Options for useRecordHotkey
|
|
@@ -72,7 +132,9 @@ interface RecordHotkeyOptions {
|
|
|
72
132
|
onShiftTab?: () => void;
|
|
73
133
|
/** Prevent default on captured keys (default: true) */
|
|
74
134
|
preventDefault?: boolean;
|
|
75
|
-
/** Timeout in ms before sequence is submitted (default:
|
|
135
|
+
/** Timeout in ms before sequence is submitted (default: Infinity, no timeout).
|
|
136
|
+
* Set to 0 for immediate submit (no sequences - first key press is captured).
|
|
137
|
+
* Set to a finite number for auto-submit after that duration. */
|
|
76
138
|
sequenceTimeout?: number;
|
|
77
139
|
/** When true, pause the auto-submit timeout (useful for conflict warnings). Default: false */
|
|
78
140
|
pauseTimeout?: boolean;
|
|
@@ -93,6 +155,8 @@ interface ActionDefinition {
|
|
|
93
155
|
icon?: string;
|
|
94
156
|
/** Whether the action is currently enabled (default: true) */
|
|
95
157
|
enabled?: boolean;
|
|
158
|
+
/** Hide from ShortcutsModal (still searchable in omnibar) */
|
|
159
|
+
hideFromModal?: boolean;
|
|
96
160
|
}
|
|
97
161
|
/**
|
|
98
162
|
* Registry of all available actions
|
|
@@ -117,24 +181,116 @@ interface ActionSearchResult {
|
|
|
117
181
|
* A possible completion for a partially-typed sequence
|
|
118
182
|
*/
|
|
119
183
|
interface SequenceCompletion {
|
|
120
|
-
/** The next key(s) needed to complete this sequence */
|
|
184
|
+
/** The next key(s) needed to complete this sequence (empty string if complete) */
|
|
121
185
|
nextKeys: string;
|
|
186
|
+
/** Structured next keys for rendering with icons (undefined if complete) */
|
|
187
|
+
nextKeySeq?: KeySeq;
|
|
122
188
|
/** The full hotkey string */
|
|
123
189
|
fullSequence: string;
|
|
124
190
|
/** Display format for the full sequence */
|
|
125
191
|
display: KeyCombinationDisplay;
|
|
126
192
|
/** Actions triggered by this sequence */
|
|
127
193
|
actions: string[];
|
|
194
|
+
/** Whether the sequence is already complete (can be executed now with Enter) */
|
|
195
|
+
isComplete: boolean;
|
|
196
|
+
/** Captured digit values from \d and \d+ placeholders */
|
|
197
|
+
captures?: number[];
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Base fields for all omnibar entries
|
|
201
|
+
*/
|
|
202
|
+
interface OmnibarEntryBase {
|
|
203
|
+
/** Unique identifier for this entry */
|
|
204
|
+
id: string;
|
|
205
|
+
/** Display label */
|
|
206
|
+
label: string;
|
|
207
|
+
/** Optional description (shown below label) */
|
|
208
|
+
description?: string;
|
|
209
|
+
/** Group name for organizing results */
|
|
210
|
+
group?: string;
|
|
211
|
+
/** Additional search keywords */
|
|
212
|
+
keywords?: string[];
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Omnibar entry that navigates to a URL when selected
|
|
216
|
+
*/
|
|
217
|
+
interface OmnibarLinkEntry extends OmnibarEntryBase {
|
|
218
|
+
/** URL to navigate to */
|
|
219
|
+
href: string;
|
|
220
|
+
handler?: never;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Omnibar entry that executes a handler when selected
|
|
224
|
+
*/
|
|
225
|
+
interface OmnibarActionEntry extends OmnibarEntryBase {
|
|
226
|
+
/** Handler to execute (can close over data) */
|
|
227
|
+
handler: () => void;
|
|
228
|
+
href?: never;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* An entry returned from a remote omnibar endpoint.
|
|
232
|
+
* Must have either `href` (for navigation) or `handler` (for custom action).
|
|
233
|
+
*/
|
|
234
|
+
type OmnibarEntry = OmnibarLinkEntry | OmnibarActionEntry;
|
|
235
|
+
/**
|
|
236
|
+
* Pagination parameters passed to endpoint fetch function
|
|
237
|
+
*/
|
|
238
|
+
interface EndpointPagination {
|
|
239
|
+
/** Starting offset (0-indexed) */
|
|
240
|
+
offset: number;
|
|
241
|
+
/** Maximum number of entries to return */
|
|
242
|
+
limit: number;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Response from an endpoint fetch, including pagination metadata
|
|
246
|
+
*/
|
|
247
|
+
interface EndpointResponse {
|
|
248
|
+
/** Entries for this page */
|
|
249
|
+
entries: OmnibarEntry[];
|
|
250
|
+
/** Total count if known (enables "X of Y" display) */
|
|
251
|
+
total?: number;
|
|
252
|
+
/** Whether more results exist (fallback when total is expensive to compute) */
|
|
253
|
+
hasMore?: boolean;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Pagination mode for an endpoint
|
|
257
|
+
* - 'scroll': Fetch more when scrolling near bottom (IntersectionObserver)
|
|
258
|
+
* - 'buttons': Show pagination controls at bottom of endpoint's group
|
|
259
|
+
* - 'none': Single page, no pagination (default)
|
|
260
|
+
*/
|
|
261
|
+
type EndpointPaginationMode = 'scroll' | 'buttons' | 'none';
|
|
262
|
+
/**
|
|
263
|
+
* Configuration for a remote omnibar endpoint
|
|
264
|
+
*/
|
|
265
|
+
interface OmnibarEndpointConfig {
|
|
266
|
+
/** Fetch function that returns entries for a query */
|
|
267
|
+
fetch: (query: string, signal: AbortSignal, pagination: EndpointPagination) => Promise<EndpointResponse>;
|
|
268
|
+
/** Default group for entries from this endpoint */
|
|
269
|
+
group?: string;
|
|
270
|
+
/** Priority for result ordering (higher = shown first, default: 0, local actions: 100) */
|
|
271
|
+
priority?: number;
|
|
272
|
+
/** Minimum query length before fetching (default: 2) */
|
|
273
|
+
minQueryLength?: number;
|
|
274
|
+
/** Whether this endpoint is enabled (default: true) */
|
|
275
|
+
enabled?: boolean;
|
|
276
|
+
/** Number of results per page (default: 10) */
|
|
277
|
+
pageSize?: number;
|
|
278
|
+
/** Pagination mode (default: 'none') */
|
|
279
|
+
pagination?: EndpointPaginationMode;
|
|
128
280
|
}
|
|
129
281
|
|
|
130
282
|
/**
|
|
131
283
|
* Hotkey definition - maps key combinations/sequences to action names
|
|
132
284
|
*/
|
|
133
285
|
type HotkeyMap = Record<string, string | string[]>;
|
|
286
|
+
/**
|
|
287
|
+
* Handler function type - can optionally receive captured values
|
|
288
|
+
*/
|
|
289
|
+
type HotkeyHandler = (e: KeyboardEvent, captures?: number[]) => void;
|
|
134
290
|
/**
|
|
135
291
|
* Handler map - maps action names to handler functions
|
|
136
292
|
*/
|
|
137
|
-
type HandlerMap = Record<string,
|
|
293
|
+
type HandlerMap = Record<string, HotkeyHandler>;
|
|
138
294
|
interface UseHotkeysOptions {
|
|
139
295
|
/** Whether hotkeys are enabled (default: true) */
|
|
140
296
|
enabled?: boolean;
|
|
@@ -146,7 +302,7 @@ interface UseHotkeysOptions {
|
|
|
146
302
|
stopPropagation?: boolean;
|
|
147
303
|
/** Enable hotkeys even when focused on input/textarea/select (default: false) */
|
|
148
304
|
enableOnFormTags?: boolean;
|
|
149
|
-
/** Timeout in ms for sequences (default:
|
|
305
|
+
/** Timeout in ms for sequences (default: Infinity, no timeout) */
|
|
150
306
|
sequenceTimeout?: number;
|
|
151
307
|
/** What happens on timeout: 'submit' executes current sequence, 'cancel' resets (default: 'submit') */
|
|
152
308
|
onTimeout?: 'submit' | 'cancel';
|
|
@@ -183,8 +339,7 @@ interface UseHotkeysResult {
|
|
|
183
339
|
* // Sequences
|
|
184
340
|
* const { pendingKeys, isAwaitingSequence } = useHotkeys(
|
|
185
341
|
* { '2 w': 'twoWeeks', '2 d': 'twoDays' },
|
|
186
|
-
* { twoWeeks: () => setRange('2w'), twoDays: () => setRange('2d') }
|
|
187
|
-
* { sequenceTimeout: 1000 }
|
|
342
|
+
* { twoWeeks: () => setRange('2w'), twoDays: () => setRange('2d') }
|
|
188
343
|
* )
|
|
189
344
|
* ```
|
|
190
345
|
*/
|
|
@@ -236,6 +391,70 @@ interface UseEditableHotkeysResult {
|
|
|
236
391
|
*/
|
|
237
392
|
declare function useEditableHotkeys(defaults: HotkeyMap, handlers: HandlerMap, options?: UseEditableHotkeysOptions): UseEditableHotkeysResult;
|
|
238
393
|
|
|
394
|
+
interface RegisteredEndpoint {
|
|
395
|
+
id: string;
|
|
396
|
+
config: OmnibarEndpointConfig;
|
|
397
|
+
registeredAt: number;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Result from querying an endpoint
|
|
401
|
+
*/
|
|
402
|
+
interface EndpointQueryResult {
|
|
403
|
+
endpointId: string;
|
|
404
|
+
entries: OmnibarEntry[];
|
|
405
|
+
/** Total count from endpoint (if provided) */
|
|
406
|
+
total?: number;
|
|
407
|
+
/** Whether endpoint has more results (if provided) */
|
|
408
|
+
hasMore?: boolean;
|
|
409
|
+
error?: Error;
|
|
410
|
+
}
|
|
411
|
+
interface OmnibarEndpointsRegistryValue {
|
|
412
|
+
/** Register an endpoint. Called by useOmnibarEndpoint on mount. */
|
|
413
|
+
register: (id: string, config: OmnibarEndpointConfig) => void;
|
|
414
|
+
/** Unregister an endpoint. Called by useOmnibarEndpoint on unmount. */
|
|
415
|
+
unregister: (id: string) => void;
|
|
416
|
+
/** Currently registered endpoints */
|
|
417
|
+
endpoints: Map<string, RegisteredEndpoint>;
|
|
418
|
+
/** Query all registered endpoints (initial page) */
|
|
419
|
+
queryAll: (query: string, signal: AbortSignal) => Promise<EndpointQueryResult[]>;
|
|
420
|
+
/** Query a single endpoint with specific pagination (for load-more) */
|
|
421
|
+
queryEndpoint: (endpointId: string, query: string, pagination: EndpointPagination, signal: AbortSignal) => Promise<EndpointQueryResult | null>;
|
|
422
|
+
}
|
|
423
|
+
declare const OmnibarEndpointsRegistryContext: react.Context<OmnibarEndpointsRegistryValue | null>;
|
|
424
|
+
/**
|
|
425
|
+
* Hook to create an omnibar endpoints registry.
|
|
426
|
+
* Used internally by HotkeysProvider.
|
|
427
|
+
*/
|
|
428
|
+
declare function useOmnibarEndpointsRegistry(): OmnibarEndpointsRegistryValue;
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Result from remote endpoint, normalized for display
|
|
432
|
+
*/
|
|
433
|
+
interface RemoteOmnibarResult {
|
|
434
|
+
/** Unique ID (prefixed with endpoint ID) */
|
|
435
|
+
id: string;
|
|
436
|
+
/** Entry data from endpoint */
|
|
437
|
+
entry: OmnibarEntry;
|
|
438
|
+
/** Endpoint ID this came from */
|
|
439
|
+
endpointId: string;
|
|
440
|
+
/** Priority from endpoint config */
|
|
441
|
+
priority: number;
|
|
442
|
+
/** Fuzzy match score */
|
|
443
|
+
score: number;
|
|
444
|
+
/** Matched ranges in label for highlighting */
|
|
445
|
+
labelMatches: Array<[number, number]>;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Pagination info for an endpoint's results group
|
|
449
|
+
*/
|
|
450
|
+
interface EndpointPaginationInfo {
|
|
451
|
+
endpointId: string;
|
|
452
|
+
loaded: number;
|
|
453
|
+
total?: number;
|
|
454
|
+
hasMore: boolean;
|
|
455
|
+
isLoading: boolean;
|
|
456
|
+
mode: EndpointPaginationMode;
|
|
457
|
+
}
|
|
239
458
|
interface UseOmnibarOptions {
|
|
240
459
|
/** Registry of available actions */
|
|
241
460
|
actions: ActionRegistry;
|
|
@@ -249,12 +468,18 @@ interface UseOmnibarOptions {
|
|
|
249
468
|
enabled?: boolean;
|
|
250
469
|
/** Called when an action is executed (if handlers not provided, or in addition to) */
|
|
251
470
|
onExecute?: (actionId: string) => void;
|
|
471
|
+
/** Called when a remote entry is executed */
|
|
472
|
+
onExecuteRemote?: (entry: OmnibarEntry) => void;
|
|
252
473
|
/** Called when omnibar opens */
|
|
253
474
|
onOpen?: () => void;
|
|
254
475
|
/** Called when omnibar closes */
|
|
255
476
|
onClose?: () => void;
|
|
256
477
|
/** Maximum number of results to show (default: 10) */
|
|
257
478
|
maxResults?: number;
|
|
479
|
+
/** Remote endpoints registry (optional - enables remote search) */
|
|
480
|
+
endpointsRegistry?: OmnibarEndpointsRegistryValue;
|
|
481
|
+
/** Debounce time for remote queries in ms (default: 150) */
|
|
482
|
+
debounceMs?: number;
|
|
258
483
|
}
|
|
259
484
|
interface UseOmnibarResult {
|
|
260
485
|
/** Whether omnibar is open */
|
|
@@ -269,10 +494,20 @@ interface UseOmnibarResult {
|
|
|
269
494
|
query: string;
|
|
270
495
|
/** Set the search query */
|
|
271
496
|
setQuery: (query: string) => void;
|
|
272
|
-
/**
|
|
497
|
+
/** Local action search results (filtered and sorted) */
|
|
273
498
|
results: ActionSearchResult[];
|
|
274
|
-
/**
|
|
499
|
+
/** Remote endpoint results */
|
|
500
|
+
remoteResults: RemoteOmnibarResult[];
|
|
501
|
+
/** Whether any remote endpoint is loading (initial or more) */
|
|
502
|
+
isLoadingRemote: boolean;
|
|
503
|
+
/** Pagination info per endpoint */
|
|
504
|
+
endpointPagination: Map<string, EndpointPaginationInfo>;
|
|
505
|
+
/** Load more results for a specific endpoint */
|
|
506
|
+
loadMore: (endpointId: string) => void;
|
|
507
|
+
/** Currently selected result index (across local + remote) */
|
|
275
508
|
selectedIndex: number;
|
|
509
|
+
/** Total number of results (local + remote) */
|
|
510
|
+
totalResults: number;
|
|
276
511
|
/** Select the next result */
|
|
277
512
|
selectNext: () => void;
|
|
278
513
|
/** Select the previous result */
|
|
@@ -399,6 +634,19 @@ interface BindingInfo {
|
|
|
399
634
|
*/
|
|
400
635
|
declare function KeybindingEditor({ keymap, defaults, descriptions, onChange, onReset, className, children, }: KeybindingEditorProps): react_jsx_runtime.JSX.Element;
|
|
401
636
|
|
|
637
|
+
/**
|
|
638
|
+
* Props for a tooltip wrapper component.
|
|
639
|
+
* Compatible with MUI Tooltip and similar libraries.
|
|
640
|
+
*/
|
|
641
|
+
interface TooltipProps {
|
|
642
|
+
title: string;
|
|
643
|
+
children: ReactNode;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* A component that wraps children with a tooltip.
|
|
647
|
+
* Default uses native title attribute; can be replaced with MUI Tooltip etc.
|
|
648
|
+
*/
|
|
649
|
+
type TooltipComponent = ComponentType<TooltipProps>;
|
|
402
650
|
interface ShortcutGroup {
|
|
403
651
|
name: string;
|
|
404
652
|
shortcuts: Array<{
|
|
@@ -474,13 +722,12 @@ interface ShortcutsModalProps {
|
|
|
474
722
|
* If not provided, uses closeModal from HotkeysContext.
|
|
475
723
|
*/
|
|
476
724
|
onClose?: () => void;
|
|
477
|
-
/** Hotkey to open modal (default: '?'). Set to empty string to disable. */
|
|
478
|
-
openKey?: string;
|
|
479
725
|
/**
|
|
480
|
-
*
|
|
481
|
-
*
|
|
726
|
+
* Default keybinding to open shortcuts modal (default: '?').
|
|
727
|
+
* Users can override this in the shortcuts modal.
|
|
728
|
+
* Set to empty string to disable.
|
|
482
729
|
*/
|
|
483
|
-
|
|
730
|
+
defaultBinding?: string;
|
|
484
731
|
/** Enable editing mode */
|
|
485
732
|
editable?: boolean;
|
|
486
733
|
/** Called when a binding changes (required if editable) */
|
|
@@ -503,6 +750,14 @@ interface ShortcutsModalProps {
|
|
|
503
750
|
title?: string;
|
|
504
751
|
/** Hint text shown below title (e.g., "Click any key to customize") */
|
|
505
752
|
hint?: string;
|
|
753
|
+
/** Whether to show actions with no bindings (default: true in editable mode, false otherwise) */
|
|
754
|
+
showUnbound?: boolean;
|
|
755
|
+
/**
|
|
756
|
+
* Custom tooltip component for digit placeholders.
|
|
757
|
+
* Should accept { title: string, children: ReactNode } props.
|
|
758
|
+
* Default uses native title attribute. Can be MUI Tooltip, etc.
|
|
759
|
+
*/
|
|
760
|
+
TooltipComponent?: TooltipComponent;
|
|
506
761
|
}
|
|
507
762
|
interface ShortcutsModalRenderProps {
|
|
508
763
|
groups: ShortcutGroup[];
|
|
@@ -519,20 +774,31 @@ interface ShortcutsModalRenderProps {
|
|
|
519
774
|
reset: () => void;
|
|
520
775
|
}
|
|
521
776
|
/**
|
|
522
|
-
* Modal
|
|
777
|
+
* Modal for displaying all keyboard shortcuts, organized by group.
|
|
778
|
+
*
|
|
779
|
+
* Opens by default with `?` key. Shows all registered actions and their bindings,
|
|
780
|
+
* grouped by category (e.g., "Navigation", "Edit", "Global").
|
|
523
781
|
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
782
|
+
* Features:
|
|
783
|
+
* - **Editable bindings**: Click any shortcut to rebind it (when `editable` is true)
|
|
784
|
+
* - **Conflict detection**: Warns when a binding conflicts with existing shortcuts
|
|
785
|
+
* - **Custom group renderers**: Use `groupRenderers` for custom layouts (e.g., two-column for fwd/back pairs)
|
|
786
|
+
* - **Persistence**: Integrates with HotkeysProvider's localStorage persistence
|
|
787
|
+
*
|
|
788
|
+
* Unlike Omnibar (search-first) or LookupModal (type keys to filter), ShortcutsModal
|
|
789
|
+
* shows everything at once in a browsable, organized view.
|
|
790
|
+
*
|
|
791
|
+
* Styled via CSS custom properties: --kbd-bg, --kbd-text, --kbd-kbd-bg, etc.
|
|
526
792
|
*
|
|
527
793
|
* @example
|
|
528
794
|
* ```tsx
|
|
529
|
-
* //
|
|
530
|
-
* <
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
795
|
+
* // Basic usage with HotkeysProvider (recommended)
|
|
796
|
+
* <HotkeysProvider>
|
|
797
|
+
* <App />
|
|
798
|
+
* <ShortcutsModal editable />
|
|
799
|
+
* </HotkeysProvider>
|
|
534
800
|
*
|
|
535
|
-
* //
|
|
801
|
+
* // Standalone with explicit props
|
|
536
802
|
* <ShortcutsModal
|
|
537
803
|
* keymap={keymap}
|
|
538
804
|
* defaults={DEFAULT_KEYMAP}
|
|
@@ -543,7 +809,7 @@ interface ShortcutsModalRenderProps {
|
|
|
543
809
|
* />
|
|
544
810
|
* ```
|
|
545
811
|
*/
|
|
546
|
-
declare function ShortcutsModal({ keymap: keymapProp, defaults: defaultsProp, labels: labelsProp, descriptions: descriptionsProp, groups: groupNamesProp, groupOrder, groupRenderers, isOpen: isOpenProp, onClose: onCloseProp,
|
|
812
|
+
declare function ShortcutsModal({ keymap: keymapProp, defaults: defaultsProp, labels: labelsProp, descriptions: descriptionsProp, groups: groupNamesProp, groupOrder, groupRenderers, isOpen: isOpenProp, onClose: onCloseProp, defaultBinding, editable, onBindingChange, onBindingAdd, onBindingRemove, onReset, multipleBindings, children, backdropClassName, modalClassName, title, hint, showUnbound, TooltipComponent: TooltipComponentProp, }: ShortcutsModalProps): react_jsx_runtime.JSX.Element | null;
|
|
547
813
|
|
|
548
814
|
/**
|
|
549
815
|
* Configuration for a row in a two-column table
|
|
@@ -613,13 +879,12 @@ interface OmnibarProps {
|
|
|
613
879
|
* If not provided, uses keymap from HotkeysContext.
|
|
614
880
|
*/
|
|
615
881
|
keymap?: HotkeyMap;
|
|
616
|
-
/** Hotkey to open omnibar (default: 'meta+k'). Set to empty string to disable. */
|
|
617
|
-
openKey?: string;
|
|
618
882
|
/**
|
|
619
|
-
*
|
|
620
|
-
*
|
|
883
|
+
* Default keybinding to open omnibar (default: 'meta+k').
|
|
884
|
+
* Users can override this in the shortcuts modal.
|
|
885
|
+
* Set to empty string to disable.
|
|
621
886
|
*/
|
|
622
|
-
|
|
887
|
+
defaultBinding?: string;
|
|
623
888
|
/**
|
|
624
889
|
* Control visibility externally.
|
|
625
890
|
* If not provided, uses isOmnibarOpen from HotkeysContext.
|
|
@@ -633,10 +898,15 @@ interface OmnibarProps {
|
|
|
633
898
|
*/
|
|
634
899
|
onClose?: () => void;
|
|
635
900
|
/**
|
|
636
|
-
* Called when
|
|
901
|
+
* Called when a local action is executed.
|
|
637
902
|
* If not provided, uses executeAction from HotkeysContext.
|
|
638
903
|
*/
|
|
639
904
|
onExecute?: (actionId: string) => void;
|
|
905
|
+
/**
|
|
906
|
+
* Called when a remote omnibar entry is executed.
|
|
907
|
+
* Use this to handle navigation for entries with `href`.
|
|
908
|
+
*/
|
|
909
|
+
onExecuteRemote?: (entry: OmnibarEntry) => void;
|
|
640
910
|
/** Maximum number of results to show (default: 10) */
|
|
641
911
|
maxResults?: number;
|
|
642
912
|
/** Placeholder text for input (default: 'Type a command...') */
|
|
@@ -651,8 +921,20 @@ interface OmnibarProps {
|
|
|
651
921
|
interface OmnibarRenderProps {
|
|
652
922
|
query: string;
|
|
653
923
|
setQuery: (query: string) => void;
|
|
924
|
+
/** Local action search results */
|
|
654
925
|
results: ActionSearchResult[];
|
|
926
|
+
/** Remote endpoint results */
|
|
927
|
+
remoteResults: RemoteOmnibarResult[];
|
|
928
|
+
/** Whether remote endpoints are being queried */
|
|
929
|
+
isLoadingRemote: boolean;
|
|
930
|
+
/** Pagination info per endpoint */
|
|
931
|
+
endpointPagination: Map<string, EndpointPaginationInfo>;
|
|
932
|
+
/** Load more results for a specific endpoint */
|
|
933
|
+
loadMore: (endpointId: string) => void;
|
|
934
|
+
/** Currently selected index (across local + remote) */
|
|
655
935
|
selectedIndex: number;
|
|
936
|
+
/** Total number of results (local + remote) */
|
|
937
|
+
totalResults: number;
|
|
656
938
|
selectNext: () => void;
|
|
657
939
|
selectPrev: () => void;
|
|
658
940
|
execute: (actionId?: string) => void;
|
|
@@ -663,13 +945,31 @@ interface OmnibarRenderProps {
|
|
|
663
945
|
inputRef: RefObject<HTMLInputElement | null>;
|
|
664
946
|
}
|
|
665
947
|
/**
|
|
666
|
-
*
|
|
948
|
+
* Command palette for searching and executing actions by name.
|
|
949
|
+
*
|
|
950
|
+
* Opens by default with `⌘K` (macOS) or `Ctrl+K` (Windows/Linux). Type to search
|
|
951
|
+
* across all registered actions by label, then press Enter to execute.
|
|
667
952
|
*
|
|
668
|
-
*
|
|
669
|
-
*
|
|
953
|
+
* Features:
|
|
954
|
+
* - **Fuzzy search**: Matches action labels (e.g., "nav tab" finds "Navigate to Table")
|
|
955
|
+
* - **Keyboard navigation**: Arrow keys to select, Enter to execute, Escape to close
|
|
956
|
+
* - **Binding display**: Shows keyboard shortcuts next to each result
|
|
957
|
+
* - **Sequence support**: Can also match and execute key sequences
|
|
958
|
+
*
|
|
959
|
+
* Unlike ShortcutsModal (shows all shortcuts organized by group) or LookupModal
|
|
960
|
+
* (type keys to filter by binding), Omnibar is search-first by action name/label.
|
|
961
|
+
*
|
|
962
|
+
* Styled via CSS custom properties: --kbd-bg, --kbd-text, --kbd-accent, etc.
|
|
670
963
|
*
|
|
671
964
|
* @example
|
|
672
965
|
* ```tsx
|
|
966
|
+
* // Basic usage with HotkeysProvider (recommended)
|
|
967
|
+
* <HotkeysProvider>
|
|
968
|
+
* <App />
|
|
969
|
+
* <Omnibar />
|
|
970
|
+
* </HotkeysProvider>
|
|
971
|
+
*
|
|
972
|
+
* // Standalone with explicit props
|
|
673
973
|
* <Omnibar
|
|
674
974
|
* actions={ACTIONS}
|
|
675
975
|
* handlers={HANDLERS}
|
|
@@ -678,10 +978,15 @@ interface OmnibarRenderProps {
|
|
|
678
978
|
* />
|
|
679
979
|
* ```
|
|
680
980
|
*/
|
|
681
|
-
declare function Omnibar({ actions: actionsProp, handlers: handlersProp, keymap: keymapProp,
|
|
981
|
+
declare function Omnibar({ actions: actionsProp, handlers: handlersProp, keymap: keymapProp, defaultBinding, isOpen: isOpenProp, onOpen: onOpenProp, onClose: onCloseProp, onExecute: onExecuteProp, onExecuteRemote: onExecuteRemoteProp, maxResults, placeholder, children, backdropClassName, omnibarClassName, }: OmnibarProps): react_jsx_runtime.JSX.Element | null;
|
|
682
982
|
|
|
683
983
|
/**
|
|
684
|
-
*
|
|
984
|
+
* Check if a key is a shifted symbol (requires Shift on US keyboard).
|
|
985
|
+
* For these keys, shift modifier should be implicit, not shown separately.
|
|
986
|
+
*/
|
|
987
|
+
declare function isShiftedSymbol(key: string): boolean;
|
|
988
|
+
/**
|
|
989
|
+
* Detect if running on macOS/iOS
|
|
685
990
|
*/
|
|
686
991
|
declare function isMac(): boolean;
|
|
687
992
|
/**
|
|
@@ -692,11 +997,32 @@ declare function normalizeKey(key: string): string;
|
|
|
692
997
|
* Format a key for display (platform-aware)
|
|
693
998
|
*/
|
|
694
999
|
declare function formatKeyForDisplay(key: string): string;
|
|
1000
|
+
/**
|
|
1001
|
+
* Sentinel values for digit placeholders in KeyCombination.key
|
|
1002
|
+
* These are used during recording to represent placeholder patterns.
|
|
1003
|
+
*/
|
|
1004
|
+
declare const DIGIT_PLACEHOLDER = "__DIGIT__";
|
|
1005
|
+
declare const DIGITS_PLACEHOLDER = "__DIGITS__";
|
|
1006
|
+
/**
|
|
1007
|
+
* Check if a key string is a digit placeholder sentinel value.
|
|
1008
|
+
* Used during recording to identify placeholder keys.
|
|
1009
|
+
*/
|
|
1010
|
+
declare function isPlaceholderSentinel(key: string): boolean;
|
|
695
1011
|
/**
|
|
696
1012
|
* Convert a KeyCombination or HotkeySequence to display format
|
|
697
1013
|
*/
|
|
698
1014
|
declare function formatCombination(combo: KeyCombination): KeyCombinationDisplay;
|
|
699
1015
|
declare function formatCombination(sequence: HotkeySequence): KeyCombinationDisplay;
|
|
1016
|
+
/**
|
|
1017
|
+
* Format a binding string for display.
|
|
1018
|
+
* Takes a binding like "meta+k" or "2 w" and returns a display string like "⌘K" or "2 W".
|
|
1019
|
+
*
|
|
1020
|
+
* @example
|
|
1021
|
+
* formatBinding('meta+k') // "⌘K" on Mac, "Ctrl+K" on Windows
|
|
1022
|
+
* formatBinding('2 w') // "2 W"
|
|
1023
|
+
* formatBinding('?') // "?"
|
|
1024
|
+
*/
|
|
1025
|
+
declare function formatBinding(binding: string): string;
|
|
700
1026
|
/**
|
|
701
1027
|
* Check if a key is a modifier key
|
|
702
1028
|
*/
|
|
@@ -711,10 +1037,33 @@ declare function isSequence(hotkeyStr: string): boolean;
|
|
|
711
1037
|
*/
|
|
712
1038
|
declare function parseHotkeyString(hotkeyStr: string): HotkeySequence;
|
|
713
1039
|
/**
|
|
714
|
-
* Parse a
|
|
715
|
-
*
|
|
1040
|
+
* Parse a hotkey string to a KeySeq (new sequence type with digit placeholders).
|
|
1041
|
+
* Handles both single keys ("ctrl+k") and sequences ("2 w", "\\d+ d")
|
|
1042
|
+
*
|
|
1043
|
+
* @example
|
|
1044
|
+
* parseKeySeq('\\d+ d') // [{ type: 'digits' }, { type: 'key', key: 'd', ... }]
|
|
1045
|
+
* parseKeySeq('ctrl+k') // [{ type: 'key', key: 'k', modifiers: { ctrl: true, ... } }]
|
|
1046
|
+
*/
|
|
1047
|
+
declare function parseKeySeq(hotkeyStr: string): KeySeq;
|
|
1048
|
+
/**
|
|
1049
|
+
* Format a KeySeq to display format
|
|
1050
|
+
*/
|
|
1051
|
+
declare function formatKeySeq(seq: KeySeq): KeyCombinationDisplay;
|
|
1052
|
+
/**
|
|
1053
|
+
* Check if a KeySeq contains any digit placeholders
|
|
1054
|
+
*/
|
|
1055
|
+
declare function hasDigitPlaceholders(seq: KeySeq): boolean;
|
|
1056
|
+
/**
|
|
1057
|
+
* Convert a KeySeq to HotkeySequence (for backwards compatibility).
|
|
1058
|
+
* Note: Digit placeholders become literal '\d' or '\d+' keys.
|
|
1059
|
+
* This is only useful for legacy code paths.
|
|
1060
|
+
*/
|
|
1061
|
+
declare function keySeqToHotkeySequence(seq: KeySeq): HotkeySequence;
|
|
1062
|
+
/**
|
|
1063
|
+
* Convert a HotkeySequence to KeySeq (for migration).
|
|
1064
|
+
* Note: This does NOT detect digit patterns - use parseKeySeq for that.
|
|
716
1065
|
*/
|
|
717
|
-
declare function
|
|
1066
|
+
declare function hotkeySequenceToKeySeq(seq: HotkeySequence): KeySeq;
|
|
718
1067
|
/**
|
|
719
1068
|
* Conflict detection result
|
|
720
1069
|
*/
|
|
@@ -730,6 +1079,7 @@ interface KeyConflict {
|
|
|
730
1079
|
* Find conflicts in a keymap.
|
|
731
1080
|
* Detects:
|
|
732
1081
|
* - Duplicate: multiple actions bound to the exact same key/sequence
|
|
1082
|
+
* - Pattern overlap: digit patterns that could match the same input (e.g., "\d d" and "5 d")
|
|
733
1083
|
* - Prefix: one hotkey is a prefix of another (e.g., "2" and "2 w")
|
|
734
1084
|
*
|
|
735
1085
|
* @param keymap - HotkeyMap to check for conflicts
|
|
@@ -747,16 +1097,17 @@ declare function getConflictsArray(keymap: Record<string, string | string[]>): K
|
|
|
747
1097
|
|
|
748
1098
|
/**
|
|
749
1099
|
* Get possible completions for a partially-typed sequence.
|
|
1100
|
+
* Returns both exact matches (isComplete: true) and continuations (isComplete: false).
|
|
750
1101
|
*
|
|
751
1102
|
* @example
|
|
752
1103
|
* ```tsx
|
|
753
|
-
* const keymap = { '
|
|
754
|
-
* const pending = parseHotkeyString('
|
|
1104
|
+
* const keymap = { 'h': 'humidity', 'h \\d+': 'nHours', '2 w': 'twoWeeks' }
|
|
1105
|
+
* const pending = parseHotkeyString('h')
|
|
755
1106
|
* const completions = getSequenceCompletions(pending, keymap)
|
|
756
1107
|
* // Returns:
|
|
757
1108
|
* // [
|
|
758
|
-
* // { nextKeys: '
|
|
759
|
-
* // { nextKeys: '
|
|
1109
|
+
* // { nextKeys: '', fullSequence: 'h', actions: ['humidity'], isComplete: true },
|
|
1110
|
+
* // { nextKeys: '⟨##⟩', fullSequence: 'h \\d+', actions: ['nHours'], isComplete: false },
|
|
760
1111
|
* // ]
|
|
761
1112
|
* ```
|
|
762
1113
|
*/
|
|
@@ -797,6 +1148,23 @@ declare function fuzzyMatch(pattern: string, text: string): FuzzyMatchResult;
|
|
|
797
1148
|
*/
|
|
798
1149
|
declare function searchActions(query: string, actions: ActionRegistry, keymap?: Record<string, string | string[]>): ActionSearchResult[];
|
|
799
1150
|
|
|
1151
|
+
/**
|
|
1152
|
+
* Handler function for actions.
|
|
1153
|
+
* Optionally receives captured values from digit placeholders in bindings.
|
|
1154
|
+
*
|
|
1155
|
+
* @example
|
|
1156
|
+
* ```tsx
|
|
1157
|
+
* // Simple handler (no captures)
|
|
1158
|
+
* handler: () => setPage(1)
|
|
1159
|
+
*
|
|
1160
|
+
* // Handler with captures (e.g., for binding "\d+ j")
|
|
1161
|
+
* handler: (e, captures) => {
|
|
1162
|
+
* const n = captures?.[0] ?? 1
|
|
1163
|
+
* setRow(row + n)
|
|
1164
|
+
* }
|
|
1165
|
+
* ```
|
|
1166
|
+
*/
|
|
1167
|
+
type ActionHandler = (e?: KeyboardEvent, captures?: number[]) => void;
|
|
800
1168
|
interface ActionConfig {
|
|
801
1169
|
/** Human-readable label for omnibar/modal */
|
|
802
1170
|
label: string;
|
|
@@ -806,12 +1174,14 @@ interface ActionConfig {
|
|
|
806
1174
|
defaultBindings?: string[];
|
|
807
1175
|
/** Search keywords for omnibar */
|
|
808
1176
|
keywords?: string[];
|
|
809
|
-
/** The action handler */
|
|
810
|
-
handler:
|
|
1177
|
+
/** The action handler (optionally receives KeyboardEvent and captured values) */
|
|
1178
|
+
handler: ActionHandler;
|
|
811
1179
|
/** Whether action is currently enabled (default: true) */
|
|
812
1180
|
enabled?: boolean;
|
|
813
1181
|
/** Priority for conflict resolution (higher wins, default: 0) */
|
|
814
1182
|
priority?: number;
|
|
1183
|
+
/** Hide from ShortcutsModal (still searchable in omnibar) */
|
|
1184
|
+
hideFromModal?: boolean;
|
|
815
1185
|
}
|
|
816
1186
|
/**
|
|
817
1187
|
* Register an action with the hotkeys system.
|
|
@@ -864,8 +1234,8 @@ interface ActionsRegistryValue {
|
|
|
864
1234
|
register: (id: string, config: ActionConfig) => void;
|
|
865
1235
|
/** Unregister an action. Called by useAction on unmount. */
|
|
866
1236
|
unregister: (id: string) => void;
|
|
867
|
-
/** Execute an action by ID */
|
|
868
|
-
execute: (id: string) => void;
|
|
1237
|
+
/** Execute an action by ID, optionally with captured digit values */
|
|
1238
|
+
execute: (id: string, captures?: number[]) => void;
|
|
869
1239
|
/** Currently registered actions */
|
|
870
1240
|
actions: Map<string, RegisteredAction>;
|
|
871
1241
|
/** Computed keymap from registered actions + user overrides */
|
|
@@ -874,12 +1244,14 @@ interface ActionsRegistryValue {
|
|
|
874
1244
|
actionRegistry: ActionRegistry;
|
|
875
1245
|
/** Get all bindings for an action (defaults + overrides) */
|
|
876
1246
|
getBindingsForAction: (id: string) => string[];
|
|
1247
|
+
/** Get the first binding for an action (convenience for display) */
|
|
1248
|
+
getFirstBindingForAction: (id: string) => string | undefined;
|
|
877
1249
|
/** User's binding overrides */
|
|
878
1250
|
overrides: Record<string, string | string[]>;
|
|
879
1251
|
/** Set a user override for a binding */
|
|
880
1252
|
setBinding: (actionId: string, key: string) => void;
|
|
881
|
-
/** Remove a binding */
|
|
882
|
-
removeBinding: (key: string) => void;
|
|
1253
|
+
/** Remove a binding for a specific action */
|
|
1254
|
+
removeBinding: (actionId: string, key: string) => void;
|
|
883
1255
|
/** Reset all overrides */
|
|
884
1256
|
resetOverrides: () => void;
|
|
885
1257
|
}
|
|
@@ -900,7 +1272,7 @@ declare function useActionsRegistry(options?: UseActionsRegistryOptions): Action
|
|
|
900
1272
|
interface HotkeysConfig {
|
|
901
1273
|
/** Storage key for persisting user binding overrides */
|
|
902
1274
|
storageKey?: string;
|
|
903
|
-
/** Timeout in ms before a sequence auto-submits (default:
|
|
1275
|
+
/** Timeout in ms before a sequence auto-submits (default: Infinity, no timeout) */
|
|
904
1276
|
sequenceTimeout?: number;
|
|
905
1277
|
/** When true, keys with conflicts are disabled (default: true) */
|
|
906
1278
|
disableConflicts?: boolean;
|
|
@@ -908,10 +1280,6 @@ interface HotkeysConfig {
|
|
|
908
1280
|
minViewportWidth?: number | false;
|
|
909
1281
|
/** Whether to show hotkey UI on touch-only devices (default: false) */
|
|
910
1282
|
enableOnTouch?: boolean;
|
|
911
|
-
/** Key sequence to open shortcuts modal (false to disable) */
|
|
912
|
-
modalTrigger?: string | false;
|
|
913
|
-
/** Key sequence to open omnibar (false to disable) */
|
|
914
|
-
omnibarTrigger?: string | false;
|
|
915
1283
|
}
|
|
916
1284
|
/**
|
|
917
1285
|
* Context value for hotkeys.
|
|
@@ -919,6 +1287,8 @@ interface HotkeysConfig {
|
|
|
919
1287
|
interface HotkeysContextValue {
|
|
920
1288
|
/** The actions registry */
|
|
921
1289
|
registry: ActionsRegistryValue;
|
|
1290
|
+
/** The omnibar endpoints registry */
|
|
1291
|
+
endpointsRegistry: OmnibarEndpointsRegistryValue;
|
|
922
1292
|
/** Whether hotkeys are enabled (based on viewport/touch) */
|
|
923
1293
|
isEnabled: boolean;
|
|
924
1294
|
/** Modal open state */
|
|
@@ -937,8 +1307,20 @@ interface HotkeysContextValue {
|
|
|
937
1307
|
closeOmnibar: () => void;
|
|
938
1308
|
/** Toggle the omnibar */
|
|
939
1309
|
toggleOmnibar: () => void;
|
|
1310
|
+
/** Whether currently editing a binding in ShortcutsModal */
|
|
1311
|
+
isEditingBinding: boolean;
|
|
1312
|
+
/** Set editing state (called by ShortcutsModal) */
|
|
1313
|
+
setIsEditingBinding: (value: boolean) => void;
|
|
1314
|
+
/** Lookup modal open state */
|
|
1315
|
+
isLookupOpen: boolean;
|
|
1316
|
+
/** Open the lookup modal */
|
|
1317
|
+
openLookup: () => void;
|
|
1318
|
+
/** Close the lookup modal */
|
|
1319
|
+
closeLookup: () => void;
|
|
1320
|
+
/** Toggle the lookup modal */
|
|
1321
|
+
toggleLookup: () => void;
|
|
940
1322
|
/** Execute an action by ID */
|
|
941
|
-
executeAction: (id: string) => void;
|
|
1323
|
+
executeAction: (id: string, captures?: number[]) => void;
|
|
942
1324
|
/** Sequence state: pending key combinations */
|
|
943
1325
|
pendingKeys: HotkeySequence;
|
|
944
1326
|
/** Sequence state: whether waiting for more keys */
|
|
@@ -955,6 +1337,8 @@ interface HotkeysContextValue {
|
|
|
955
1337
|
searchActions: (query: string) => ReturnType<typeof searchActions>;
|
|
956
1338
|
/** Get sequence completions for pending keys */
|
|
957
1339
|
getCompletions: (pendingKeys: HotkeySequence) => ReturnType<typeof getSequenceCompletions>;
|
|
1340
|
+
/** Cancel the current sequence */
|
|
1341
|
+
cancelSequence: () => void;
|
|
958
1342
|
}
|
|
959
1343
|
interface HotkeysProviderProps {
|
|
960
1344
|
config?: HotkeysConfig;
|
|
@@ -1003,6 +1387,44 @@ declare function useHotkeysContext(): HotkeysContextValue;
|
|
|
1003
1387
|
*/
|
|
1004
1388
|
declare function useMaybeHotkeysContext(): HotkeysContextValue | null;
|
|
1005
1389
|
|
|
1390
|
+
/**
|
|
1391
|
+
* Register a remote omnibar endpoint.
|
|
1392
|
+
*
|
|
1393
|
+
* Endpoints are automatically unregistered when the component unmounts,
|
|
1394
|
+
* making this ideal for colocating search providers with their data context.
|
|
1395
|
+
*
|
|
1396
|
+
* @example
|
|
1397
|
+
* ```tsx
|
|
1398
|
+
* function UsersPage() {
|
|
1399
|
+
* const navigate = useNavigate()
|
|
1400
|
+
*
|
|
1401
|
+
* useOmnibarEndpoint('users', {
|
|
1402
|
+
* fetch: async (query, signal, pagination) => {
|
|
1403
|
+
* const res = await fetch(`/api/users?q=${query}&offset=${pagination.offset}&limit=${pagination.limit}`, { signal })
|
|
1404
|
+
* const { users, total } = await res.json()
|
|
1405
|
+
* return {
|
|
1406
|
+
* entries: users.map(u => ({
|
|
1407
|
+
* id: `user:${u.id}`,
|
|
1408
|
+
* label: u.name,
|
|
1409
|
+
* description: u.email,
|
|
1410
|
+
* handler: () => navigate(`/users/${u.id}`),
|
|
1411
|
+
* })),
|
|
1412
|
+
* total,
|
|
1413
|
+
* hasMore: pagination.offset + users.length < total,
|
|
1414
|
+
* }
|
|
1415
|
+
* },
|
|
1416
|
+
* group: 'Users',
|
|
1417
|
+
* priority: 10,
|
|
1418
|
+
* pageSize: 10,
|
|
1419
|
+
* pagination: 'scroll',
|
|
1420
|
+
* })
|
|
1421
|
+
*
|
|
1422
|
+
* return <UsersList />
|
|
1423
|
+
* }
|
|
1424
|
+
* ```
|
|
1425
|
+
*/
|
|
1426
|
+
declare function useOmnibarEndpoint(id: string, config: OmnibarEndpointConfig): void;
|
|
1427
|
+
|
|
1006
1428
|
/**
|
|
1007
1429
|
* Hook to record a keyboard shortcut (single key or sequence) from user input.
|
|
1008
1430
|
*
|
|
@@ -1020,7 +1442,7 @@ declare function useMaybeHotkeysContext(): HotkeysContextValue | null;
|
|
|
1020
1442
|
* console.log('Captured:', display.display) // "2 W" or "⌘K"
|
|
1021
1443
|
* saveKeybinding(display.id) // "2 w" or "meta+k"
|
|
1022
1444
|
* },
|
|
1023
|
-
* sequenceTimeout:
|
|
1445
|
+
* sequenceTimeout: 800, // custom timeout
|
|
1024
1446
|
* })
|
|
1025
1447
|
*
|
|
1026
1448
|
* return (
|
|
@@ -1037,28 +1459,176 @@ declare function useMaybeHotkeysContext(): HotkeysContextValue | null;
|
|
|
1037
1459
|
*/
|
|
1038
1460
|
declare function useRecordHotkey(options?: RecordHotkeyOptions): RecordHotkeyResult;
|
|
1039
1461
|
|
|
1462
|
+
interface KbdProps {
|
|
1463
|
+
/** Action ID to display binding(s) for */
|
|
1464
|
+
action: string;
|
|
1465
|
+
/** Separator between multiple bindings (default: " / ") */
|
|
1466
|
+
separator?: string;
|
|
1467
|
+
/** Show all bindings instead of just the first (default: false, shows only first) */
|
|
1468
|
+
all?: boolean;
|
|
1469
|
+
/** Fallback content when no bindings exist */
|
|
1470
|
+
fallback?: React.ReactNode;
|
|
1471
|
+
/** Additional className */
|
|
1472
|
+
className?: string;
|
|
1473
|
+
/** Make the kbd clickable to trigger the action */
|
|
1474
|
+
clickable?: boolean;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Display the current binding(s) for an action (clickable by default).
|
|
1478
|
+
*
|
|
1479
|
+
* Automatically updates when users customize their bindings.
|
|
1480
|
+
* Uses SVG icons for modifiers (⌘, ⌥, ⇧, ⌃) and special keys (arrows, enter, etc.)
|
|
1481
|
+
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* ```tsx
|
|
1484
|
+
* // Clickable kbd that triggers the action (default)
|
|
1485
|
+
* <p>Press <Kbd action="help" /> to see shortcuts</p>
|
|
1486
|
+
*
|
|
1487
|
+
* // Non-clickable for pure display (use Key alias or clickable={false})
|
|
1488
|
+
* <p>Navigate with <Key action="next" /> to go to next item</p>
|
|
1489
|
+
*
|
|
1490
|
+
* // Show all bindings (not just the first)
|
|
1491
|
+
* <p>Navigate with <Kbd action="next" all separator=" or " /></p>
|
|
1492
|
+
*
|
|
1493
|
+
* // With fallback when no binding exists
|
|
1494
|
+
* <Kbd action="customAction" fallback="(unbound)" />
|
|
1495
|
+
* ```
|
|
1496
|
+
*/
|
|
1497
|
+
declare function Kbd({ action, separator, all, fallback, className, clickable, }: KbdProps): react_jsx_runtime.JSX.Element | null;
|
|
1498
|
+
/**
|
|
1499
|
+
* Non-clickable variant of Kbd for pure display/documentation purposes.
|
|
1500
|
+
* Alias for `<Kbd clickable={false} ... />`
|
|
1501
|
+
*/
|
|
1502
|
+
declare function Key(props: Omit<KbdProps, 'clickable'>): react_jsx_runtime.JSX.Element;
|
|
1503
|
+
/**
|
|
1504
|
+
* Display all bindings for an action (shows multiple if they exist).
|
|
1505
|
+
* Alias for `<Kbd all ... />`
|
|
1506
|
+
*
|
|
1507
|
+
* @example
|
|
1508
|
+
* ```tsx
|
|
1509
|
+
* <p>Navigate with <Kbds action="next" separator=" or " /></p>
|
|
1510
|
+
* ```
|
|
1511
|
+
*/
|
|
1512
|
+
declare function Kbds(props: Omit<KbdProps, 'all'>): react_jsx_runtime.JSX.Element;
|
|
1513
|
+
type BuiltinKbdProps = Omit<KbdProps, 'action'>;
|
|
1514
|
+
/**
|
|
1515
|
+
* Kbd for the ShortcutsModal trigger (default: `?`).
|
|
1516
|
+
* @example <KbdModal /> // Shows "?" or user's custom binding
|
|
1517
|
+
*/
|
|
1518
|
+
declare function KbdModal(props: BuiltinKbdProps): react_jsx_runtime.JSX.Element;
|
|
1519
|
+
/**
|
|
1520
|
+
* Kbd for the Omnibar trigger (default: `⌘K`).
|
|
1521
|
+
* @example <KbdOmnibar /> // Shows "⌘K" or user's custom binding
|
|
1522
|
+
*/
|
|
1523
|
+
declare function KbdOmnibar(props: BuiltinKbdProps): react_jsx_runtime.JSX.Element;
|
|
1524
|
+
/**
|
|
1525
|
+
* Kbd for the LookupModal trigger (default: `⌘⇧K`).
|
|
1526
|
+
* @example <KbdLookup /> // Shows "⌘⇧K" or user's custom binding
|
|
1527
|
+
*/
|
|
1528
|
+
declare function KbdLookup(props: BuiltinKbdProps): react_jsx_runtime.JSX.Element;
|
|
1529
|
+
|
|
1530
|
+
interface LookupModalProps {
|
|
1531
|
+
/**
|
|
1532
|
+
* Default keybinding to open lookup modal (default: 'meta+shift+k').
|
|
1533
|
+
* Users can override this in the shortcuts modal.
|
|
1534
|
+
* Set to empty string to disable.
|
|
1535
|
+
*/
|
|
1536
|
+
defaultBinding?: string;
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Modal for browsing and looking up keyboard shortcuts by typing key sequences.
|
|
1540
|
+
*
|
|
1541
|
+
* Unlike SequenceModal (which auto-executes when a complete sequence is entered),
|
|
1542
|
+
* LookupModal lets you browse all available shortcuts and select one to execute.
|
|
1543
|
+
*
|
|
1544
|
+
* - Press keys to filter to matching sequences
|
|
1545
|
+
* - Use arrow keys to navigate results
|
|
1546
|
+
* - Press Enter to execute selected action
|
|
1547
|
+
* - Press Escape to close or clear filter
|
|
1548
|
+
* - Press Backspace to remove last key from filter
|
|
1549
|
+
*/
|
|
1550
|
+
declare function LookupModal({ defaultBinding }?: LookupModalProps): react_jsx_runtime.JSX.Element | null;
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* Modal that appears during multi-key sequence input (e.g., `g t` for "go to table").
|
|
1554
|
+
*
|
|
1555
|
+
* When a user presses a key that starts a sequence, this modal appears showing:
|
|
1556
|
+
* - The keys pressed so far
|
|
1557
|
+
* - Available completions (what keys can come next)
|
|
1558
|
+
* - A timeout indicator (only shown when exactly one completion exists)
|
|
1559
|
+
*
|
|
1560
|
+
* Features:
|
|
1561
|
+
* - Arrow keys navigate between completions (cancels auto-timeout)
|
|
1562
|
+
* - Enter executes the selected completion (even for digit patterns - handler gets undefined captures)
|
|
1563
|
+
* - Escape cancels the sequence
|
|
1564
|
+
*
|
|
1565
|
+
* Unlike LookupModal (which requires explicit activation and lets you browse/search),
|
|
1566
|
+
* SequenceModal appears automatically when you start typing a sequence.
|
|
1567
|
+
*
|
|
1568
|
+
* @example
|
|
1569
|
+
* ```tsx
|
|
1570
|
+
* // Include in your app (no props needed - uses HotkeysContext)
|
|
1571
|
+
* <HotkeysProvider>
|
|
1572
|
+
* <App />
|
|
1573
|
+
* <SequenceModal />
|
|
1574
|
+
* </HotkeysProvider>
|
|
1575
|
+
* ```
|
|
1576
|
+
*/
|
|
1040
1577
|
declare function SequenceModal(): react_jsx_runtime.JSX.Element | null;
|
|
1041
1578
|
|
|
1042
|
-
interface ModifierIconProps {
|
|
1579
|
+
interface ModifierIconProps extends SVGProps<SVGSVGElement> {
|
|
1043
1580
|
className?: string;
|
|
1044
1581
|
style?: CSSProperties;
|
|
1045
1582
|
}
|
|
1046
1583
|
/** Command/Meta key icon (⌘) */
|
|
1047
|
-
declare
|
|
1584
|
+
declare const Command: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
|
|
1048
1585
|
/** Control key icon (^) - chevron/caret */
|
|
1049
|
-
declare
|
|
1586
|
+
declare const Ctrl: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
|
|
1050
1587
|
/** Shift key icon (⇧) - hollow arrow */
|
|
1051
|
-
declare
|
|
1588
|
+
declare const Shift: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
|
|
1052
1589
|
/** Option key icon (⌥) - macOS style */
|
|
1053
|
-
declare
|
|
1590
|
+
declare const Option: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
|
|
1054
1591
|
/** Alt key icon (⎇) - Windows style, though "Alt" text is more common on Windows */
|
|
1055
|
-
declare
|
|
1592
|
+
declare const Alt: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
|
|
1056
1593
|
type ModifierType = 'meta' | 'ctrl' | 'shift' | 'alt' | 'opt';
|
|
1057
1594
|
/** Get the appropriate icon component for a modifier key */
|
|
1058
|
-
declare function getModifierIcon(modifier: ModifierType):
|
|
1595
|
+
declare function getModifierIcon(modifier: ModifierType): typeof Command;
|
|
1059
1596
|
/** Render a modifier icon by name */
|
|
1060
|
-
declare
|
|
1597
|
+
declare const ModifierIcon: react.ForwardRefExoticComponent<Omit<ModifierIconProps & {
|
|
1061
1598
|
modifier: ModifierType;
|
|
1062
|
-
}
|
|
1599
|
+
}, "ref"> & react.RefAttributes<SVGSVGElement>>;
|
|
1600
|
+
|
|
1601
|
+
interface KeyIconProps {
|
|
1602
|
+
className?: string;
|
|
1603
|
+
style?: CSSProperties;
|
|
1604
|
+
}
|
|
1605
|
+
/** Arrow Up icon (↑) */
|
|
1606
|
+
declare function Up({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
|
|
1607
|
+
/** Arrow Down icon (↓) */
|
|
1608
|
+
declare function Down({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
|
|
1609
|
+
/** Arrow Left icon (←) */
|
|
1610
|
+
declare function Left({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
|
|
1611
|
+
/** Arrow Right icon (→) */
|
|
1612
|
+
declare function Right({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
|
|
1613
|
+
/** Enter/Return icon (↵) */
|
|
1614
|
+
declare function Enter({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
|
|
1615
|
+
/** Backspace icon (⌫) */
|
|
1616
|
+
declare function Backspace({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
|
|
1617
|
+
type KeyIconType = 'arrowup' | 'arrowdown' | 'arrowleft' | 'arrowright' | 'enter' | 'backspace' | 'tab';
|
|
1618
|
+
/** Get the icon component for a key, or null if no icon exists */
|
|
1619
|
+
declare function getKeyIcon(key: string): ComponentType<KeyIconProps> | null;
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Default timeout for key sequences (no timeout).
|
|
1623
|
+
* Set to a finite number (ms) to auto-submit sequences after that duration.
|
|
1624
|
+
*/
|
|
1625
|
+
declare const DEFAULT_SEQUENCE_TIMEOUT: number;
|
|
1626
|
+
/**
|
|
1627
|
+
* Reserved action IDs for built-in UI components.
|
|
1628
|
+
* These are registered automatically by their respective components.
|
|
1629
|
+
*/
|
|
1630
|
+
declare const ACTION_MODAL = "__hotkeys:modal";
|
|
1631
|
+
declare const ACTION_OMNIBAR = "__hotkeys:omnibar";
|
|
1632
|
+
declare const ACTION_LOOKUP = "__hotkeys:lookup";
|
|
1063
1633
|
|
|
1064
|
-
export { type ActionConfig, type ActionDefinition, type ActionRegistry, type ActionSearchResult, ActionsRegistryContext, type ActionsRegistryValue,
|
|
1634
|
+
export { ACTION_LOOKUP, ACTION_MODAL, ACTION_OMNIBAR, type ActionConfig, type ActionDefinition, type ActionHandler, type ActionRegistry, type ActionSearchResult, ActionsRegistryContext, type ActionsRegistryValue, Alt, Backspace, type BindingInfo, Command, Ctrl, DEFAULT_SEQUENCE_TIMEOUT, DIGITS_PLACEHOLDER, DIGIT_PLACEHOLDER, Down, type EndpointPagination, type EndpointPaginationInfo, type EndpointPaginationMode, type EndpointQueryResult, type EndpointResponse, Enter, type FuzzyMatchResult, type GroupRenderer, type GroupRendererProps, type HandlerMap, type HotkeyHandler, type HotkeyMap, type HotkeySequence, type HotkeysConfig, type HotkeysContextValue, HotkeysProvider, type HotkeysProviderProps, Kbd, KbdLookup, KbdModal, KbdOmnibar, type KbdProps, Kbds, Key, type KeyCombination, type KeyCombinationDisplay, type KeyConflict, type KeyIconProps, type KeyIconType, type KeySeq, KeybindingEditor, type KeybindingEditorProps, type KeybindingEditorRenderProps, Left, LookupModal, ModifierIcon, type ModifierIconProps, type ModifierType, type Modifiers, Omnibar, type OmnibarActionEntry, type OmnibarEndpointConfig, OmnibarEndpointsRegistryContext, type OmnibarEndpointsRegistryValue, type OmnibarEntry, type OmnibarEntryBase, type OmnibarLinkEntry, type OmnibarProps, type OmnibarRenderProps, Option, type RecordHotkeyOptions, type RecordHotkeyResult, type RegisteredAction, type RegisteredEndpoint, type RemoteOmnibarResult, Right, type SeqElem, type SeqElemState, type SeqMatchState, type SequenceCompletion, SequenceModal, Shift, type ShortcutGroup, ShortcutsModal, type ShortcutsModalProps, type ShortcutsModalRenderProps, type TooltipComponent, type TooltipProps, type TwoColumnConfig, type TwoColumnRow, Up, type UseEditableHotkeysOptions, type UseEditableHotkeysResult, type UseHotkeysOptions, type UseHotkeysResult, type UseOmnibarOptions, type UseOmnibarResult, countPlaceholders, createTwoColumnRenderer, extractCaptures, findConflicts, formatBinding, formatCombination, formatKeyForDisplay, formatKeySeq, fuzzyMatch, getActionBindings, getConflictsArray, getKeyIcon, getModifierIcon, getSequenceCompletions, hasConflicts, hasDigitPlaceholders, hotkeySequenceToKeySeq, isDigitPlaceholder, isMac, isModifierKey, isPlaceholderSentinel, isSequence, isShiftedSymbol, keySeqToHotkeySequence, normalizeKey, parseHotkeyString, parseKeySeq, searchActions, useAction, useActions, useActionsRegistry, useEditableHotkeys, useHotkeys, useHotkeysContext, useMaybeHotkeysContext, useOmnibar, useOmnibarEndpoint, useOmnibarEndpointsRegistry, useRecordHotkey };
|