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/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, CSSProperties, ComponentType } from 'react';
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
- * @deprecated Use `sequence` instead
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: 1000) */
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, (e: KeyboardEvent) => void>;
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: 1000) */
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
- /** Search results (filtered and sorted) */
497
+ /** Local action search results (filtered and sorted) */
273
498
  results: ActionSearchResult[];
274
- /** Currently selected result index */
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
- * Whether to auto-register the open hotkey (default: true).
481
- * When using HotkeysContext, the provider already handles this, so set to false.
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
- autoRegisterOpen?: boolean;
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 component for displaying and optionally editing keyboard shortcuts.
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
- * Uses CSS classes from styles.css. Override via CSS custom properties:
525
- * --kbd-bg, --kbd-text, --kbd-kbd-bg, etc.
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
- * // Read-only display
530
- * <ShortcutsModal
531
- * keymap={HOTKEYS}
532
- * labels={{ 'metric:temp': 'Temperature' }}
533
- * />
795
+ * // Basic usage with HotkeysProvider (recommended)
796
+ * <HotkeysProvider>
797
+ * <App />
798
+ * <ShortcutsModal editable />
799
+ * </HotkeysProvider>
534
800
  *
535
- * // Editable with callbacks
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, openKey, autoRegisterOpen, editable, onBindingChange, onBindingAdd, onBindingRemove, onReset, multipleBindings, children, backdropClassName, modalClassName, title, hint, }: ShortcutsModalProps): react_jsx_runtime.JSX.Element | null;
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
- * Whether omnibar hotkey is enabled.
620
- * When using HotkeysContext, defaults to false (provider handles it).
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
- enabled?: boolean;
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 an action is executed.
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
- * Omnibar/command palette component for searching and executing actions.
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
- * Uses CSS classes from styles.css. Override via CSS custom properties:
669
- * --kbd-bg, --kbd-text, --kbd-accent, etc.
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, openKey, enabled: enabledProp, isOpen: isOpenProp, onOpen: onOpenProp, onClose: onCloseProp, onExecute: onExecuteProp, maxResults, placeholder, children, backdropClassName, omnibarClassName, }: OmnibarProps): react_jsx_runtime.JSX.Element | null;
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
- * Detect if running on macOS
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 combination ID back to a KeyCombination (single key only)
715
- * @deprecated Use parseHotkeyString for sequence support
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 parseCombinationId(id: string): KeyCombination;
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 = { '2 w': 'twoWeeks', '2 d': 'twoDays', 't': 'temp' }
754
- * const pending = parseHotkeyString('2')
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: 'w', fullSequence: '2 w', actions: ['twoWeeks'], ... },
759
- * // { nextKeys: 'd', fullSequence: '2 d', actions: ['twoDays'], ... },
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: () => void;
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: 1000) */
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: 1000,
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 function CommandIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1584
+ declare const Command: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1048
1585
  /** Control key icon (^) - chevron/caret */
1049
- declare function CtrlIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1586
+ declare const Ctrl: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1050
1587
  /** Shift key icon (⇧) - hollow arrow */
1051
- declare function ShiftIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1588
+ declare const Shift: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1052
1589
  /** Option key icon (⌥) - macOS style */
1053
- declare function OptIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
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 function AltIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
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): ComponentType<ModifierIconProps>;
1595
+ declare function getModifierIcon(modifier: ModifierType): typeof Command;
1059
1596
  /** Render a modifier icon by name */
1060
- declare function ModifierIcon({ modifier, ...props }: ModifierIconProps & {
1597
+ declare const ModifierIcon: react.ForwardRefExoticComponent<Omit<ModifierIconProps & {
1061
1598
  modifier: ModifierType;
1062
- }): react_jsx_runtime.JSX.Element;
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, AltIcon, type BindingInfo, CommandIcon, CtrlIcon, type FuzzyMatchResult, type GroupRenderer, type GroupRendererProps, type HandlerMap, type HotkeyMap, type HotkeySequence, type HotkeysConfig, type HotkeysContextValue, HotkeysProvider, type HotkeysProviderProps, type KeyCombination, type KeyCombinationDisplay, type KeyConflict, KeybindingEditor, type KeybindingEditorProps, type KeybindingEditorRenderProps, ModifierIcon, type ModifierIconProps, type ModifierType, Omnibar, type OmnibarProps, type OmnibarRenderProps, OptIcon, type RecordHotkeyOptions, type RecordHotkeyResult, type RegisteredAction, type SequenceCompletion, SequenceModal, ShiftIcon, type ShortcutGroup, ShortcutsModal, type ShortcutsModalProps, type ShortcutsModalRenderProps, type TwoColumnConfig, type TwoColumnRow, type UseEditableHotkeysOptions, type UseEditableHotkeysResult, type UseHotkeysOptions, type UseHotkeysResult, type UseOmnibarOptions, type UseOmnibarResult, createTwoColumnRenderer, findConflicts, formatCombination, formatKeyForDisplay, fuzzyMatch, getActionBindings, getConflictsArray, getModifierIcon, getSequenceCompletions, hasConflicts, isMac, isModifierKey, isSequence, normalizeKey, parseCombinationId, parseHotkeyString, searchActions, useAction, useActions, useActionsRegistry, useEditableHotkeys, useHotkeys, useHotkeysContext, useMaybeHotkeysContext, useOmnibar, useRecordHotkey };
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 };