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

Function useVoiceKeybindingHandler

src/hooks/useVoiceIntegration.tsx:373–668  ·  view source on GitHub ↗
({
  voiceHandleKeyEvent,
  stripTrailing,
  resetAnchor,
  isActive
}: {
  voiceHandleKeyEvent: (fallbackMs?: number) => void;
  stripTrailing: (maxStrip: number, opts?: StripOpts) => number;
  resetAnchor: () => void;
  isActive: boolean;
})

Source from the content-addressed store, hash-verified

371 * (discrete sequences, no hold). Validation warns on these.
372 */
373export function useVoiceKeybindingHandler({
374 voiceHandleKeyEvent,
375 stripTrailing,
376 resetAnchor,
377 isActive
378}: {
379 voiceHandleKeyEvent: (fallbackMs?: number) => void;
380 stripTrailing: (maxStrip: number, opts?: StripOpts) => number;
381 resetAnchor: () => void;
382 isActive: boolean;
383}): {
384 handleKeyDown: (e: KeyboardEvent) => void;
385} {
386 const getVoiceState = useGetVoiceState();
387 const setVoiceState = useSetVoiceState();
388 const keybindingContext = useOptionalKeybindingContext();
389 const isModalOverlayActive = useIsModalOverlayActive();
390 // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
391 const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false;
392 const voiceState = feature('VOICE_MODE') ?
393 // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
394 useVoiceState(s => s.voiceState) : 'idle';
395
396 // Find the configured key for voice:pushToTalk from keybinding context.
397 // Forward iteration with last-wins (matching the resolver): if a later
398 // Chat binding overrides the same chord with null or a different
399 // action, the voice binding is discarded and null is returned — the
400 // user explicitly disabled hold-to-talk via binding override, so
401 // don't second-guess them with a fallback. The DEFAULT is only used
402 // when there's no provider at all. Context filter is required — space
403 // is also bound in Settings/Confirmation/Plugin (select:accept etc.);
404 // without the filter those would null out the default.
405 const voiceKeystroke = useMemo((): ParsedKeystroke | null => {
406 if (!keybindingContext) return DEFAULT_VOICE_KEYSTROKE;
407 let result: ParsedKeystroke | null = null;
408 for (const binding of keybindingContext.bindings) {
409 if (binding.context !== 'Chat') continue;
410 if (binding.chord.length !== 1) continue;
411 const ks = binding.chord[0];
412 if (!ks) continue;
413 if (binding.action === 'voice:pushToTalk') {
414 result = ks;
415 } else if (result !== null && keystrokesEqual(ks, result)) {
416 // A later binding overrides this chord (null unbind or reassignment)
417 result = null;
418 }
419 }
420 return result;
421 }, [keybindingContext]);
422
423 // If the binding is a bare (unmodified) single printable char, terminal
424 // auto-repeat may batch N keystrokes into one input event (e.g. "vvv"),
425 // and the char flows into the text input — we need flow-through + strip.
426 // Modifier combos (meta+k, ctrl+x) also auto-repeat (the letter part
427 // repeats) but don't insert text, so they're swallowed from the first
428 // press with no stripping needed. matchesKeyboardEvent handles those.
429 const bareChar = voiceKeystroke !== null && voiceKeystroke.key.length === 1 && !voiceKeystroke.ctrl && !voiceKeystroke.alt && !voiceKeystroke.shift && !voiceKeystroke.meta && !voiceKeystroke.super ? voiceKeystroke.key : null;
430 const rapidCountRef = useRef(0);

Callers 1

VoiceKeybindingHandlerFunction · 0.85

Calls 12

useGetVoiceStateFunction · 0.85
useSetVoiceStateFunction · 0.85
useIsModalOverlayActiveFunction · 0.85
featureFunction · 0.85
useVoiceEnabledFunction · 0.85
useVoiceStateFunction · 0.85
keystrokesEqualFunction · 0.85
useInputFunction · 0.85
handleKeyDownFunction · 0.70

Tested by

no test coverage detected