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

Function useVoiceIntegration

src/hooks/useVoiceIntegration.tsx:118–347  ·  view source on GitHub ↗
({
  setInputValueRaw,
  inputValueRef,
  insertTextRef
}: UseVoiceIntegrationArgs)

Source from the content-addressed store, hash-verified

116 interimRange: InterimRange | null;
117};
118export function useVoiceIntegration({
119 setInputValueRaw,
120 inputValueRef,
121 insertTextRef
122}: UseVoiceIntegrationArgs): UseVoiceIntegrationResult {
123 const {
124 addNotification
125 } = useNotifications();
126
127 // Tracks the input content before/after the cursor when voice starts,
128 // so interim transcripts can be inserted at the cursor position without
129 // clobbering surrounding user text.
130 const voicePrefixRef = useRef<string | null>(null);
131 const voiceSuffixRef = useRef<string>('');
132 // Tracks the last input value this hook wrote (via anchor, interim effect,
133 // or handleVoiceTranscript). If inputValueRef.current diverges, the user
134 // submitted or edited — both write paths bail to avoid clobbering. This is
135 // the only guard that correctly handles empty-prefix-empty-suffix: a
136 // startsWith('')/endsWith('') check vacuously passes, and a length check
137 // can't distinguish a cleared input from a never-set one.
138 const lastSetInputRef = useRef<string | null>(null);
139
140 // Strip trailing hold-key chars (and optionally capture the voice
141 // anchor). Called during warmup (to clean up chars that leaked past
142 // stopImmediatePropagation — listener order is not guaranteed) and
143 // on activation (with anchor=true to capture the prefix/suffix around
144 // the cursor for interim transcript placement). The caller passes the
145 // exact count it expects to strip so pre-existing chars at the
146 // boundary are preserved (e.g. the "v" in "hav" when hold-key is "v").
147 // The floor option sets a minimum trailing count to leave behind
148 // (during warmup this is the count we intentionally let through, so
149 // defensive cleanup only removes leaks). Returns the number of
150 // trailing chars remaining after stripping. When nothing changes, no
151 // state update is performed.
152 const stripTrailing = useCallback((maxStrip: number, {
153 char = ' ',
154 anchor = false,
155 floor = 0
156 }: StripOpts = {}) => {
157 const prev = inputValueRef.current;
158 const offset = insertTextRef.current?.cursorOffset ?? prev.length;
159 const beforeCursor = prev.slice(0, offset);
160 const afterCursor = prev.slice(offset);
161 // When the hold key is space, also count full-width spaces (U+3000)
162 // that a CJK IME may have inserted for the same physical key.
163 // U+3000 is BMP single-code-unit so indices align with beforeCursor.
164 const scan = char === ' ' ? normalizeFullWidthSpace(beforeCursor) : beforeCursor;
165 let trailing = 0;
166 while (trailing < scan.length && scan[scan.length - 1 - trailing] === char) {
167 trailing++;
168 }
169 const stripCount = Math.max(0, Math.min(trailing - floor, maxStrip));
170 const remaining = trailing - stripCount;
171 const stripped = beforeCursor.slice(0, beforeCursor.length - stripCount);
172 // When anchoring with a non-space suffix, insert a gap space so the
173 // waveform cursor sits on the gap instead of covering the first
174 // suffix letter. The interim transcript effect maintains this same
175 // structure (prefix + leading + interim + trailing + suffix), so

Callers 1

REPLFunction · 0.85

Calls 6

normalizeFullWidthSpaceFunction · 0.85
featureFunction · 0.85
useVoiceEnabledFunction · 0.85
useVoiceStateFunction · 0.85
maxMethod · 0.80
useNotificationsFunction · 0.50

Tested by

no test coverage detected