use-kbd 0.3.0 → 0.4.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
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
- import { ReactNode, RefObject, CSSProperties, ComponentType } from 'react';
3
+ import { ReactNode, ComponentType, RefObject, SVGProps, CSSProperties } from 'react';
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,19 +18,73 @@ 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.
21
25
  * Single key: [{ key: 'k', modifiers: {...} }]
22
26
  * Sequence: [{ key: '2', ... }, { key: 'w', ... }]
27
+ * @deprecated Use KeySeq for new code
23
28
  */
24
29
  type HotkeySequence = KeyCombination[];
30
+ /**
31
+ * A single element in a key sequence (sum type).
32
+ * - 'key': exact key match (with optional modifiers)
33
+ * - 'digit': matches any single digit 0-9 (\d)
34
+ * - 'digits': matches one or more digits (\d+)
35
+ */
36
+ type SeqElem = {
37
+ type: 'key';
38
+ key: string;
39
+ modifiers: Modifiers;
40
+ } | {
41
+ type: 'digit';
42
+ } | {
43
+ type: 'digits';
44
+ };
45
+ /**
46
+ * A key sequence pattern (array of sequence elements)
47
+ */
48
+ type KeySeq = SeqElem[];
49
+ /**
50
+ * Sequence element with match state (for tracking during input).
51
+ * - 'key': has `matched` flag
52
+ * - 'digit': has captured `value`
53
+ * - 'digits': has captured `value` or in-progress `partial` string
54
+ */
55
+ type SeqElemState = {
56
+ type: 'key';
57
+ key: string;
58
+ modifiers: Modifiers;
59
+ matched?: true;
60
+ } | {
61
+ type: 'digit';
62
+ value?: number;
63
+ } | {
64
+ type: 'digits';
65
+ value?: number;
66
+ partial?: string;
67
+ };
68
+ /**
69
+ * Sequence match state - tracks progress through a sequence with captures
70
+ */
71
+ type SeqMatchState = SeqElemState[];
72
+ /**
73
+ * Extract captured values from a completed sequence match
74
+ */
75
+ declare function extractCaptures(state: SeqMatchState): number[];
76
+ /**
77
+ * Check if a SeqElem is a digit placeholder
78
+ */
79
+ declare function isDigitPlaceholder(elem: SeqElem): elem is {
80
+ type: 'digit';
81
+ } | {
82
+ type: 'digits';
83
+ };
84
+ /**
85
+ * Count digit placeholders in a sequence
86
+ */
87
+ declare function countPlaceholders(seq: KeySeq): number;
25
88
  /**
26
89
  * Platform-aware display format for a key combination or sequence
27
90
  */
@@ -53,6 +116,8 @@ interface RecordHotkeyResult {
53
116
  pendingKeys: HotkeySequence;
54
117
  /** The key currently being held (for live UI feedback during recording) */
55
118
  activeKeys: KeyCombination | null;
119
+ /** The timeout duration for sequences (ms) */
120
+ sequenceTimeout: number;
56
121
  /**
57
122
  * @deprecated Use `sequence` instead
58
123
  */
@@ -72,7 +137,9 @@ interface RecordHotkeyOptions {
72
137
  onShiftTab?: () => void;
73
138
  /** Prevent default on captured keys (default: true) */
74
139
  preventDefault?: boolean;
75
- /** Timeout in ms before sequence is submitted (default: 1000) */
140
+ /** Timeout in ms before sequence is submitted (default: Infinity, no timeout).
141
+ * Set to 0 for immediate submit (no sequences - first key press is captured).
142
+ * Set to a finite number for auto-submit after that duration. */
76
143
  sequenceTimeout?: number;
77
144
  /** When true, pause the auto-submit timeout (useful for conflict warnings). Default: false */
78
145
  pauseTimeout?: boolean;
@@ -131,10 +198,14 @@ interface SequenceCompletion {
131
198
  * Hotkey definition - maps key combinations/sequences to action names
132
199
  */
133
200
  type HotkeyMap = Record<string, string | string[]>;
201
+ /**
202
+ * Handler function type - can optionally receive captured values
203
+ */
204
+ type HotkeyHandler = (e: KeyboardEvent, captures?: number[]) => void;
134
205
  /**
135
206
  * Handler map - maps action names to handler functions
136
207
  */
137
- type HandlerMap = Record<string, (e: KeyboardEvent) => void>;
208
+ type HandlerMap = Record<string, HotkeyHandler>;
138
209
  interface UseHotkeysOptions {
139
210
  /** Whether hotkeys are enabled (default: true) */
140
211
  enabled?: boolean;
@@ -146,7 +217,7 @@ interface UseHotkeysOptions {
146
217
  stopPropagation?: boolean;
147
218
  /** Enable hotkeys even when focused on input/textarea/select (default: false) */
148
219
  enableOnFormTags?: boolean;
149
- /** Timeout in ms for sequences (default: 1000) */
220
+ /** Timeout in ms for sequences (default: Infinity, no timeout) */
150
221
  sequenceTimeout?: number;
151
222
  /** What happens on timeout: 'submit' executes current sequence, 'cancel' resets (default: 'submit') */
152
223
  onTimeout?: 'submit' | 'cancel';
@@ -183,8 +254,7 @@ interface UseHotkeysResult {
183
254
  * // Sequences
184
255
  * const { pendingKeys, isAwaitingSequence } = useHotkeys(
185
256
  * { '2 w': 'twoWeeks', '2 d': 'twoDays' },
186
- * { twoWeeks: () => setRange('2w'), twoDays: () => setRange('2d') },
187
- * { sequenceTimeout: 1000 }
257
+ * { twoWeeks: () => setRange('2w'), twoDays: () => setRange('2d') }
188
258
  * )
189
259
  * ```
190
260
  */
@@ -399,6 +469,19 @@ interface BindingInfo {
399
469
  */
400
470
  declare function KeybindingEditor({ keymap, defaults, descriptions, onChange, onReset, className, children, }: KeybindingEditorProps): react_jsx_runtime.JSX.Element;
401
471
 
472
+ /**
473
+ * Props for a tooltip wrapper component.
474
+ * Compatible with MUI Tooltip and similar libraries.
475
+ */
476
+ interface TooltipProps {
477
+ title: string;
478
+ children: ReactNode;
479
+ }
480
+ /**
481
+ * A component that wraps children with a tooltip.
482
+ * Default uses native title attribute; can be replaced with MUI Tooltip etc.
483
+ */
484
+ type TooltipComponent = ComponentType<TooltipProps>;
402
485
  interface ShortcutGroup {
403
486
  name: string;
404
487
  shortcuts: Array<{
@@ -474,13 +557,12 @@ interface ShortcutsModalProps {
474
557
  * If not provided, uses closeModal from HotkeysContext.
475
558
  */
476
559
  onClose?: () => void;
477
- /** Hotkey to open modal (default: '?'). Set to empty string to disable. */
478
- openKey?: string;
479
560
  /**
480
- * Whether to auto-register the open hotkey (default: true).
481
- * When using HotkeysContext, the provider already handles this, so set to false.
561
+ * Default keybinding to open shortcuts modal (default: '?').
562
+ * Users can override this in the shortcuts modal.
563
+ * Set to empty string to disable.
482
564
  */
483
- autoRegisterOpen?: boolean;
565
+ defaultBinding?: string;
484
566
  /** Enable editing mode */
485
567
  editable?: boolean;
486
568
  /** Called when a binding changes (required if editable) */
@@ -503,6 +585,14 @@ interface ShortcutsModalProps {
503
585
  title?: string;
504
586
  /** Hint text shown below title (e.g., "Click any key to customize") */
505
587
  hint?: string;
588
+ /** Whether to show actions with no bindings (default: true in editable mode, false otherwise) */
589
+ showUnbound?: boolean;
590
+ /**
591
+ * Custom tooltip component for digit placeholders.
592
+ * Should accept { title: string, children: ReactNode } props.
593
+ * Default uses native title attribute. Can be MUI Tooltip, etc.
594
+ */
595
+ TooltipComponent?: TooltipComponent;
506
596
  }
507
597
  interface ShortcutsModalRenderProps {
508
598
  groups: ShortcutGroup[];
@@ -519,20 +609,31 @@ interface ShortcutsModalRenderProps {
519
609
  reset: () => void;
520
610
  }
521
611
  /**
522
- * Modal component for displaying and optionally editing keyboard shortcuts.
612
+ * Modal for displaying all keyboard shortcuts, organized by group.
613
+ *
614
+ * Opens by default with `?` key. Shows all registered actions and their bindings,
615
+ * grouped by category (e.g., "Navigation", "Edit", "Global").
616
+ *
617
+ * Features:
618
+ * - **Editable bindings**: Click any shortcut to rebind it (when `editable` is true)
619
+ * - **Conflict detection**: Warns when a binding conflicts with existing shortcuts
620
+ * - **Custom group renderers**: Use `groupRenderers` for custom layouts (e.g., two-column for fwd/back pairs)
621
+ * - **Persistence**: Integrates with HotkeysProvider's localStorage persistence
523
622
  *
524
- * Uses CSS classes from styles.css. Override via CSS custom properties:
525
- * --kbd-bg, --kbd-text, --kbd-kbd-bg, etc.
623
+ * Unlike Omnibar (search-first) or LookupModal (type keys to filter), ShortcutsModal
624
+ * shows everything at once in a browsable, organized view.
625
+ *
626
+ * Styled via CSS custom properties: --kbd-bg, --kbd-text, --kbd-kbd-bg, etc.
526
627
  *
527
628
  * @example
528
629
  * ```tsx
529
- * // Read-only display
530
- * <ShortcutsModal
531
- * keymap={HOTKEYS}
532
- * labels={{ 'metric:temp': 'Temperature' }}
533
- * />
630
+ * // Basic usage with HotkeysProvider (recommended)
631
+ * <HotkeysProvider>
632
+ * <App />
633
+ * <ShortcutsModal editable />
634
+ * </HotkeysProvider>
534
635
  *
535
- * // Editable with callbacks
636
+ * // Standalone with explicit props
536
637
  * <ShortcutsModal
537
638
  * keymap={keymap}
538
639
  * defaults={DEFAULT_KEYMAP}
@@ -543,7 +644,7 @@ interface ShortcutsModalRenderProps {
543
644
  * />
544
645
  * ```
545
646
  */
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;
647
+ 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
648
 
548
649
  /**
549
650
  * Configuration for a row in a two-column table
@@ -613,13 +714,12 @@ interface OmnibarProps {
613
714
  * If not provided, uses keymap from HotkeysContext.
614
715
  */
615
716
  keymap?: HotkeyMap;
616
- /** Hotkey to open omnibar (default: 'meta+k'). Set to empty string to disable. */
617
- openKey?: string;
618
717
  /**
619
- * Whether omnibar hotkey is enabled.
620
- * When using HotkeysContext, defaults to false (provider handles it).
718
+ * Default keybinding to open omnibar (default: 'meta+k').
719
+ * Users can override this in the shortcuts modal.
720
+ * Set to empty string to disable.
621
721
  */
622
- enabled?: boolean;
722
+ defaultBinding?: string;
623
723
  /**
624
724
  * Control visibility externally.
625
725
  * If not provided, uses isOmnibarOpen from HotkeysContext.
@@ -663,13 +763,31 @@ interface OmnibarRenderProps {
663
763
  inputRef: RefObject<HTMLInputElement | null>;
664
764
  }
665
765
  /**
666
- * Omnibar/command palette component for searching and executing actions.
766
+ * Command palette for searching and executing actions by name.
767
+ *
768
+ * Opens by default with `⌘K` (macOS) or `Ctrl+K` (Windows/Linux). Type to search
769
+ * across all registered actions by label, then press Enter to execute.
770
+ *
771
+ * Features:
772
+ * - **Fuzzy search**: Matches action labels (e.g., "nav tab" finds "Navigate to Table")
773
+ * - **Keyboard navigation**: Arrow keys to select, Enter to execute, Escape to close
774
+ * - **Binding display**: Shows keyboard shortcuts next to each result
775
+ * - **Sequence support**: Can also match and execute key sequences
776
+ *
777
+ * Unlike ShortcutsModal (shows all shortcuts organized by group) or LookupModal
778
+ * (type keys to filter by binding), Omnibar is search-first by action name/label.
667
779
  *
668
- * Uses CSS classes from styles.css. Override via CSS custom properties:
669
- * --kbd-bg, --kbd-text, --kbd-accent, etc.
780
+ * Styled via CSS custom properties: --kbd-bg, --kbd-text, --kbd-accent, etc.
670
781
  *
671
782
  * @example
672
783
  * ```tsx
784
+ * // Basic usage with HotkeysProvider (recommended)
785
+ * <HotkeysProvider>
786
+ * <App />
787
+ * <Omnibar />
788
+ * </HotkeysProvider>
789
+ *
790
+ * // Standalone with explicit props
673
791
  * <Omnibar
674
792
  * actions={ACTIONS}
675
793
  * handlers={HANDLERS}
@@ -678,8 +796,13 @@ interface OmnibarRenderProps {
678
796
  * />
679
797
  * ```
680
798
  */
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;
799
+ declare function Omnibar({ actions: actionsProp, handlers: handlersProp, keymap: keymapProp, defaultBinding, isOpen: isOpenProp, onOpen: onOpenProp, onClose: onCloseProp, onExecute: onExecuteProp, maxResults, placeholder, children, backdropClassName, omnibarClassName, }: OmnibarProps): react_jsx_runtime.JSX.Element | null;
682
800
 
801
+ /**
802
+ * Check if a key is a shifted symbol (requires Shift on US keyboard).
803
+ * For these keys, shift modifier should be implicit, not shown separately.
804
+ */
805
+ declare function isShiftedSymbol(key: string): boolean;
683
806
  /**
684
807
  * Detect if running on macOS
685
808
  */
@@ -692,11 +815,32 @@ declare function normalizeKey(key: string): string;
692
815
  * Format a key for display (platform-aware)
693
816
  */
694
817
  declare function formatKeyForDisplay(key: string): string;
818
+ /**
819
+ * Sentinel values for digit placeholders in KeyCombination.key
820
+ * These are used during recording to represent placeholder patterns.
821
+ */
822
+ declare const DIGIT_PLACEHOLDER = "__DIGIT__";
823
+ declare const DIGITS_PLACEHOLDER = "__DIGITS__";
824
+ /**
825
+ * Check if a key string is a digit placeholder sentinel value.
826
+ * Used during recording to identify placeholder keys.
827
+ */
828
+ declare function isPlaceholderSentinel(key: string): boolean;
695
829
  /**
696
830
  * Convert a KeyCombination or HotkeySequence to display format
697
831
  */
698
832
  declare function formatCombination(combo: KeyCombination): KeyCombinationDisplay;
699
833
  declare function formatCombination(sequence: HotkeySequence): KeyCombinationDisplay;
834
+ /**
835
+ * Format a binding string for display.
836
+ * Takes a binding like "meta+k" or "2 w" and returns a display string like "⌘K" or "2 W".
837
+ *
838
+ * @example
839
+ * formatBinding('meta+k') // "⌘K" on Mac, "Ctrl+K" on Windows
840
+ * formatBinding('2 w') // "2 W"
841
+ * formatBinding('?') // "?"
842
+ */
843
+ declare function formatBinding(binding: string): string;
700
844
  /**
701
845
  * Check if a key is a modifier key
702
846
  */
@@ -715,6 +859,34 @@ declare function parseHotkeyString(hotkeyStr: string): HotkeySequence;
715
859
  * @deprecated Use parseHotkeyString for sequence support
716
860
  */
717
861
  declare function parseCombinationId(id: string): KeyCombination;
862
+ /**
863
+ * Parse a hotkey string to a KeySeq (new sequence type with digit placeholders).
864
+ * Handles both single keys ("ctrl+k") and sequences ("2 w", "\\d+ d")
865
+ *
866
+ * @example
867
+ * parseKeySeq('\\d+ d') // [{ type: 'digits' }, { type: 'key', key: 'd', ... }]
868
+ * parseKeySeq('ctrl+k') // [{ type: 'key', key: 'k', modifiers: { ctrl: true, ... } }]
869
+ */
870
+ declare function parseKeySeq(hotkeyStr: string): KeySeq;
871
+ /**
872
+ * Format a KeySeq to display format
873
+ */
874
+ declare function formatKeySeq(seq: KeySeq): KeyCombinationDisplay;
875
+ /**
876
+ * Check if a KeySeq contains any digit placeholders
877
+ */
878
+ declare function hasDigitPlaceholders(seq: KeySeq): boolean;
879
+ /**
880
+ * Convert a KeySeq to HotkeySequence (for backwards compatibility).
881
+ * Note: Digit placeholders become literal '\d' or '\d+' keys.
882
+ * This is only useful for legacy code paths.
883
+ */
884
+ declare function keySeqToHotkeySequence(seq: KeySeq): HotkeySequence;
885
+ /**
886
+ * Convert a HotkeySequence to KeySeq (for migration).
887
+ * Note: This does NOT detect digit patterns - use parseKeySeq for that.
888
+ */
889
+ declare function hotkeySequenceToKeySeq(seq: HotkeySequence): KeySeq;
718
890
  /**
719
891
  * Conflict detection result
720
892
  */
@@ -730,6 +902,7 @@ interface KeyConflict {
730
902
  * Find conflicts in a keymap.
731
903
  * Detects:
732
904
  * - Duplicate: multiple actions bound to the exact same key/sequence
905
+ * - Pattern overlap: digit patterns that could match the same input (e.g., "\d d" and "5 d")
733
906
  * - Prefix: one hotkey is a prefix of another (e.g., "2" and "2 w")
734
907
  *
735
908
  * @param keymap - HotkeyMap to check for conflicts
@@ -797,6 +970,23 @@ declare function fuzzyMatch(pattern: string, text: string): FuzzyMatchResult;
797
970
  */
798
971
  declare function searchActions(query: string, actions: ActionRegistry, keymap?: Record<string, string | string[]>): ActionSearchResult[];
799
972
 
973
+ /**
974
+ * Handler function for actions.
975
+ * Optionally receives captured values from digit placeholders in bindings.
976
+ *
977
+ * @example
978
+ * ```tsx
979
+ * // Simple handler (no captures)
980
+ * handler: () => setPage(1)
981
+ *
982
+ * // Handler with captures (e.g., for binding "\d+ j")
983
+ * handler: (e, captures) => {
984
+ * const n = captures?.[0] ?? 1
985
+ * setRow(row + n)
986
+ * }
987
+ * ```
988
+ */
989
+ type ActionHandler = (e?: KeyboardEvent, captures?: number[]) => void;
800
990
  interface ActionConfig {
801
991
  /** Human-readable label for omnibar/modal */
802
992
  label: string;
@@ -806,8 +996,8 @@ interface ActionConfig {
806
996
  defaultBindings?: string[];
807
997
  /** Search keywords for omnibar */
808
998
  keywords?: string[];
809
- /** The action handler */
810
- handler: () => void;
999
+ /** The action handler (optionally receives KeyboardEvent and captured values) */
1000
+ handler: ActionHandler;
811
1001
  /** Whether action is currently enabled (default: true) */
812
1002
  enabled?: boolean;
813
1003
  /** Priority for conflict resolution (higher wins, default: 0) */
@@ -874,12 +1064,14 @@ interface ActionsRegistryValue {
874
1064
  actionRegistry: ActionRegistry;
875
1065
  /** Get all bindings for an action (defaults + overrides) */
876
1066
  getBindingsForAction: (id: string) => string[];
1067
+ /** Get the first binding for an action (convenience for display) */
1068
+ getFirstBindingForAction: (id: string) => string | undefined;
877
1069
  /** User's binding overrides */
878
1070
  overrides: Record<string, string | string[]>;
879
1071
  /** Set a user override for a binding */
880
1072
  setBinding: (actionId: string, key: string) => void;
881
- /** Remove a binding */
882
- removeBinding: (key: string) => void;
1073
+ /** Remove a binding for a specific action */
1074
+ removeBinding: (actionId: string, key: string) => void;
883
1075
  /** Reset all overrides */
884
1076
  resetOverrides: () => void;
885
1077
  }
@@ -900,7 +1092,7 @@ declare function useActionsRegistry(options?: UseActionsRegistryOptions): Action
900
1092
  interface HotkeysConfig {
901
1093
  /** Storage key for persisting user binding overrides */
902
1094
  storageKey?: string;
903
- /** Timeout in ms before a sequence auto-submits (default: 1000) */
1095
+ /** Timeout in ms before a sequence auto-submits (default: Infinity, no timeout) */
904
1096
  sequenceTimeout?: number;
905
1097
  /** When true, keys with conflicts are disabled (default: true) */
906
1098
  disableConflicts?: boolean;
@@ -908,10 +1100,6 @@ interface HotkeysConfig {
908
1100
  minViewportWidth?: number | false;
909
1101
  /** Whether to show hotkey UI on touch-only devices (default: false) */
910
1102
  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
1103
  }
916
1104
  /**
917
1105
  * Context value for hotkeys.
@@ -937,6 +1125,18 @@ interface HotkeysContextValue {
937
1125
  closeOmnibar: () => void;
938
1126
  /** Toggle the omnibar */
939
1127
  toggleOmnibar: () => void;
1128
+ /** Whether currently editing a binding in ShortcutsModal */
1129
+ isEditingBinding: boolean;
1130
+ /** Set editing state (called by ShortcutsModal) */
1131
+ setIsEditingBinding: (value: boolean) => void;
1132
+ /** Lookup modal open state */
1133
+ isLookupOpen: boolean;
1134
+ /** Open the lookup modal */
1135
+ openLookup: () => void;
1136
+ /** Close the lookup modal */
1137
+ closeLookup: () => void;
1138
+ /** Toggle the lookup modal */
1139
+ toggleLookup: () => void;
940
1140
  /** Execute an action by ID */
941
1141
  executeAction: (id: string) => void;
942
1142
  /** Sequence state: pending key combinations */
@@ -955,6 +1155,8 @@ interface HotkeysContextValue {
955
1155
  searchActions: (query: string) => ReturnType<typeof searchActions>;
956
1156
  /** Get sequence completions for pending keys */
957
1157
  getCompletions: (pendingKeys: HotkeySequence) => ReturnType<typeof getSequenceCompletions>;
1158
+ /** Cancel the current sequence */
1159
+ cancelSequence: () => void;
958
1160
  }
959
1161
  interface HotkeysProviderProps {
960
1162
  config?: HotkeysConfig;
@@ -1020,7 +1222,7 @@ declare function useMaybeHotkeysContext(): HotkeysContextValue | null;
1020
1222
  * console.log('Captured:', display.display) // "2 W" or "⌘K"
1021
1223
  * saveKeybinding(display.id) // "2 w" or "meta+k"
1022
1224
  * },
1023
- * sequenceTimeout: 1000,
1225
+ * sequenceTimeout: 800, // custom timeout
1024
1226
  * })
1025
1227
  *
1026
1228
  * return (
@@ -1037,28 +1239,175 @@ declare function useMaybeHotkeysContext(): HotkeysContextValue | null;
1037
1239
  */
1038
1240
  declare function useRecordHotkey(options?: RecordHotkeyOptions): RecordHotkeyResult;
1039
1241
 
1242
+ interface KbdProps {
1243
+ /** Action ID to display binding(s) for */
1244
+ action: string;
1245
+ /** Separator between multiple bindings (default: " / ") */
1246
+ separator?: string;
1247
+ /** Show all bindings instead of just the first (default: false, shows only first) */
1248
+ all?: boolean;
1249
+ /** Fallback content when no bindings exist */
1250
+ fallback?: React.ReactNode;
1251
+ /** Additional className */
1252
+ className?: string;
1253
+ /** Make the kbd clickable to trigger the action */
1254
+ clickable?: boolean;
1255
+ }
1256
+ /**
1257
+ * Display the current binding(s) for an action (clickable by default).
1258
+ *
1259
+ * Automatically updates when users customize their bindings.
1260
+ * Uses SVG icons for modifiers (⌘, ⌥, ⇧, ⌃) and special keys (arrows, enter, etc.)
1261
+ *
1262
+ * @example
1263
+ * ```tsx
1264
+ * // Clickable kbd that triggers the action (default)
1265
+ * <p>Press <Kbd action="help" /> to see shortcuts</p>
1266
+ *
1267
+ * // Non-clickable for pure display (use Key alias or clickable={false})
1268
+ * <p>Navigate with <Key action="next" /> to go to next item</p>
1269
+ *
1270
+ * // Show all bindings (not just the first)
1271
+ * <p>Navigate with <Kbd action="next" all separator=" or " /></p>
1272
+ *
1273
+ * // With fallback when no binding exists
1274
+ * <Kbd action="customAction" fallback="(unbound)" />
1275
+ * ```
1276
+ */
1277
+ declare function Kbd({ action, separator, all, fallback, className, clickable, }: KbdProps): react_jsx_runtime.JSX.Element | null;
1278
+ /**
1279
+ * Non-clickable variant of Kbd for pure display/documentation purposes.
1280
+ * Alias for `<Kbd clickable={false} ... />`
1281
+ */
1282
+ declare function Key(props: Omit<KbdProps, 'clickable'>): react_jsx_runtime.JSX.Element;
1283
+ /**
1284
+ * Display all bindings for an action (shows multiple if they exist).
1285
+ * Alias for `<Kbd all ... />`
1286
+ *
1287
+ * @example
1288
+ * ```tsx
1289
+ * <p>Navigate with <Kbds action="next" separator=" or " /></p>
1290
+ * ```
1291
+ */
1292
+ declare function Kbds(props: Omit<KbdProps, 'all'>): react_jsx_runtime.JSX.Element;
1293
+ type BuiltinKbdProps = Omit<KbdProps, 'action'>;
1294
+ /**
1295
+ * Kbd for the ShortcutsModal trigger (default: `?`).
1296
+ * @example <KbdModal /> // Shows "?" or user's custom binding
1297
+ */
1298
+ declare function KbdModal(props: BuiltinKbdProps): react_jsx_runtime.JSX.Element;
1299
+ /**
1300
+ * Kbd for the Omnibar trigger (default: `⌘K`).
1301
+ * @example <KbdOmnibar /> // Shows "⌘K" or user's custom binding
1302
+ */
1303
+ declare function KbdOmnibar(props: BuiltinKbdProps): react_jsx_runtime.JSX.Element;
1304
+ /**
1305
+ * Kbd for the LookupModal trigger (default: `⌘⇧K`).
1306
+ * @example <KbdLookup /> // Shows "⌘⇧K" or user's custom binding
1307
+ */
1308
+ declare function KbdLookup(props: BuiltinKbdProps): react_jsx_runtime.JSX.Element;
1309
+
1310
+ interface LookupModalProps {
1311
+ /**
1312
+ * Default keybinding to open lookup modal (default: 'meta+shift+k').
1313
+ * Users can override this in the shortcuts modal.
1314
+ * Set to empty string to disable.
1315
+ */
1316
+ defaultBinding?: string;
1317
+ }
1318
+ /**
1319
+ * Modal for browsing and looking up keyboard shortcuts by typing key sequences.
1320
+ *
1321
+ * Unlike SequenceModal (which auto-executes when a complete sequence is entered),
1322
+ * LookupModal lets you browse all available shortcuts and select one to execute.
1323
+ *
1324
+ * - Press keys to filter to matching sequences
1325
+ * - Use arrow keys to navigate results
1326
+ * - Press Enter to execute selected action
1327
+ * - Press Escape to close or clear filter
1328
+ * - Press Backspace to remove last key from filter
1329
+ */
1330
+ declare function LookupModal({ defaultBinding }?: LookupModalProps): react_jsx_runtime.JSX.Element | null;
1331
+
1332
+ /**
1333
+ * Modal that appears during multi-key sequence input (e.g., `g t` for "go to table").
1334
+ *
1335
+ * When a user presses a key that starts a sequence, this modal appears showing:
1336
+ * - The keys pressed so far
1337
+ * - Available completions (what keys can come next)
1338
+ * - A timeout indicator
1339
+ *
1340
+ * Unlike LookupModal (which requires explicit activation and lets you browse/search),
1341
+ * SequenceModal appears automatically when you start typing a sequence and auto-executes
1342
+ * when a complete sequence is entered.
1343
+ *
1344
+ * The modal auto-dismisses if no completion is pressed within the sequence timeout,
1345
+ * or when the user presses Escape, or when a complete sequence is executed.
1346
+ *
1347
+ * @example
1348
+ * ```tsx
1349
+ * // Include in your app (no props needed - uses HotkeysContext)
1350
+ * <HotkeysProvider>
1351
+ * <App />
1352
+ * <SequenceModal />
1353
+ * </HotkeysProvider>
1354
+ * ```
1355
+ */
1040
1356
  declare function SequenceModal(): react_jsx_runtime.JSX.Element | null;
1041
1357
 
1042
- interface ModifierIconProps {
1358
+ interface ModifierIconProps extends SVGProps<SVGSVGElement> {
1043
1359
  className?: string;
1044
1360
  style?: CSSProperties;
1045
1361
  }
1046
1362
  /** Command/Meta key icon (⌘) */
1047
- declare function CommandIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1363
+ declare const Command: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1048
1364
  /** Control key icon (^) - chevron/caret */
1049
- declare function CtrlIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1365
+ declare const Ctrl: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1050
1366
  /** Shift key icon (⇧) - hollow arrow */
1051
- declare function ShiftIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1367
+ declare const Shift: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1052
1368
  /** Option key icon (⌥) - macOS style */
1053
- declare function OptIcon({ className, style }: ModifierIconProps): react_jsx_runtime.JSX.Element;
1369
+ declare const Option: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1054
1370
  /** 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;
1371
+ declare const Alt: react.ForwardRefExoticComponent<Omit<ModifierIconProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
1056
1372
  type ModifierType = 'meta' | 'ctrl' | 'shift' | 'alt' | 'opt';
1057
1373
  /** Get the appropriate icon component for a modifier key */
1058
- declare function getModifierIcon(modifier: ModifierType): ComponentType<ModifierIconProps>;
1374
+ declare function getModifierIcon(modifier: ModifierType): typeof Command;
1059
1375
  /** Render a modifier icon by name */
1060
- declare function ModifierIcon({ modifier, ...props }: ModifierIconProps & {
1376
+ declare const ModifierIcon: react.ForwardRefExoticComponent<Omit<ModifierIconProps & {
1061
1377
  modifier: ModifierType;
1062
- }): react_jsx_runtime.JSX.Element;
1378
+ }, "ref"> & react.RefAttributes<SVGSVGElement>>;
1379
+
1380
+ interface KeyIconProps {
1381
+ className?: string;
1382
+ style?: CSSProperties;
1383
+ }
1384
+ /** Arrow Up icon (↑) */
1385
+ declare function Up({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
1386
+ /** Arrow Down icon (↓) */
1387
+ declare function Down({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
1388
+ /** Arrow Left icon (←) */
1389
+ declare function Left({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
1390
+ /** Arrow Right icon (→) */
1391
+ declare function Right({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
1392
+ /** Enter/Return icon (↵) */
1393
+ declare function Enter({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
1394
+ /** Backspace icon (⌫) */
1395
+ declare function Backspace({ className, style }: KeyIconProps): react_jsx_runtime.JSX.Element;
1396
+ type KeyIconType = 'arrowup' | 'arrowdown' | 'arrowleft' | 'arrowright' | 'enter' | 'backspace';
1397
+ /** Get the icon component for a key, or null if no icon exists */
1398
+ declare function getKeyIcon(key: string): React.ComponentType<KeyIconProps> | null;
1399
+
1400
+ /**
1401
+ * Default timeout for key sequences (no timeout).
1402
+ * Set to a finite number (ms) to auto-submit sequences after that duration.
1403
+ */
1404
+ declare const DEFAULT_SEQUENCE_TIMEOUT: number;
1405
+ /**
1406
+ * Reserved action IDs for built-in UI components.
1407
+ * These are registered automatically by their respective components.
1408
+ */
1409
+ declare const ACTION_MODAL = "__hotkeys:modal";
1410
+ declare const ACTION_OMNIBAR = "__hotkeys:omnibar";
1411
+ declare const ACTION_LOOKUP = "__hotkeys:lookup";
1063
1412
 
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 };
1413
+ 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, 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 OmnibarProps, type OmnibarRenderProps, Option, type RecordHotkeyOptions, type RecordHotkeyResult, type RegisteredAction, 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, parseCombinationId, parseHotkeyString, parseKeySeq, searchActions, useAction, useActions, useActionsRegistry, useEditableHotkeys, useHotkeys, useHotkeysContext, useMaybeHotkeysContext, useOmnibar, useRecordHotkey };