MCPcopy
hub / github.com/codeaashu/claude-code / useKeybindings

Function useKeybindings

src/keybindings/useKeybinding.ts:113–196  ·  view source on GitHub ↗
(
  // Handler returning `false` means "not consumed" — the event propagates
  // to later useInput/useKeybindings handlers. Useful for fall-through:
  // e.g. ScrollKeybindingHandler's scroll:line* returns false when the
  // ScrollBox content fits (scroll is a no-op), letting a child component's
  // handler take the wheel event for list navigation instead. Promise<void>
  // is allowed for fire-and-forget async handlers (the `!== false` check
  // only skips propagation for a sync `false`, not a pending Promise).
  handlers: Record<string, () => void | false | Promise<void>>,
  options: Options = {},
)

Source from the content-addressed store, hash-verified

111 * ```
112 */
113export function useKeybindings(
114 // Handler returning `false` means "not consumed" — the event propagates
115 // to later useInput/useKeybindings handlers. Useful for fall-through:
116 // e.g. ScrollKeybindingHandler's scroll:line* returns false when the
117 // ScrollBox content fits (scroll is a no-op), letting a child component's
118 // handler take the wheel event for list navigation instead. Promise<void>
119 // is allowed for fire-and-forget async handlers (the `!== false` check
120 // only skips propagation for a sync `false`, not a pending Promise).
121 handlers: Record<string, () => void | false | Promise<void>>,
122 options: Options = {},
123): void {
124 const { context = 'Global', isActive = true } = options
125 const keybindingContext = useOptionalKeybindingContext()
126
127 // Register all handlers with the context for ChordInterceptor to invoke
128 useEffect(() => {
129 if (!keybindingContext || !isActive) return
130
131 const unregisterFns: Array<() => void> = []
132 for (const [action, handler] of Object.entries(handlers)) {
133 unregisterFns.push(
134 keybindingContext.registerHandler({ action, context, handler }),
135 )
136 }
137
138 return () => {
139 for (const unregister of unregisterFns) {
140 unregister()
141 }
142 }
143 }, [context, handlers, keybindingContext, isActive])
144
145 const handleInput = useCallback(
146 (input: string, key: Key, event: InputEvent) => {
147 // If no keybinding context available, skip resolution
148 if (!keybindingContext) return
149
150 // Build context list: registered active contexts + this context + Global
151 // More specific contexts (registered ones) take precedence over Global
152 const contextsToCheck: KeybindingContextName[] = [
153 ...keybindingContext.activeContexts,
154 context,
155 'Global',
156 ]
157 // Deduplicate while preserving order (first occurrence wins for priority)
158 const uniqueContexts = [...new Set(contextsToCheck)]
159
160 const result = keybindingContext.resolve(input, key, uniqueContexts)
161
162 switch (result.type) {
163 case 'match':
164 // Chord completed (if any) - clear pending state
165 keybindingContext.setPendingChord(null)
166 if (result.action in handlers) {
167 const handler = handlers[result.action]
168 if (handler && handler() !== false) {
169 event.stopImmediatePropagation()
170 }

Callers 15

ModelPickerFunction · 0.85
MessageSelectorFunction · 0.85
ScrollKeybindingHandlerFunction · 0.85
BridgeDialogFunction · 0.85
OnboardingFunction · 0.85
IdeOnboardingDialogFunction · 0.85
TabsFunction · 0.85
PermissionPromptFunction · 0.85
PreviewQuestionViewFunction · 0.85
useFilePermissionDialogFunction · 0.85

Calls 8

useInputFunction · 0.85
entriesMethod · 0.80
registerHandlerMethod · 0.80
handlerFunction · 0.50
pushMethod · 0.45
resolveMethod · 0.45

Tested by

no test coverage detected