MCPcopy Index your code
hub / github.com/codeaashu/claude-code / finishRecording

Function finishRecording

src/hooks/useVoice.ts:322–522  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

320 }, [setVoiceState])
321
322 function finishRecording(): void {
323 logForDebugging(
324 '[voice] finishRecording: stopping recording, transitioning to processing',
325 )
326 // Session ending — stale any in-flight attempt so its late onError
327 // (conn 2 responding after user released key) doesn't double-fire on
328 // top of the "check network" message below.
329 attemptGenRef.current++
330 // Capture focusTriggered BEFORE clearing it — needed as an event dimension
331 // so BigQuery can filter out passive focus-mode auto-recordings (user focused
332 // terminal without speaking → ambient noise sets hadAudioSignal=true → false
333 // silent-drop signature). focusFlushedCharsRef fixes transcriptChars accuracy
334 // for sessions WITH speech; focusTriggered enables filtering sessions WITHOUT.
335 const focusTriggered = focusTriggeredRef.current
336 focusTriggeredRef.current = false
337 updateState('processing')
338 voiceModule?.stopRecording()
339 // Capture duration BEFORE the finalize round-trip so that the WebSocket
340 // wait time is not included (otherwise a quick tap looks like > 2s).
341 // All ref-backed values are captured here, BEFORE the async boundary —
342 // a keypress during the finalize wait can start a new session and reset
343 // these refs (e.g. focusFlushedCharsRef = 0 in startRecordingSession),
344 // reproducing the silent-drop false-positive this ref exists to prevent.
345 const recordingDurationMs = Date.now() - recordingStartRef.current
346 const hadAudioSignal = hasAudioSignalRef.current
347 const retried = retryUsedRef.current
348 const focusFlushedChars = focusFlushedCharsRef.current
349 // wsConnected distinguishes "backend received audio but dropped it" (the
350 // bug backend PR #287008 fixes) from "WS handshake never completed" —
351 // in the latter case audio is still in audioBuffer, never reached the
352 // server, but hasAudioSignalRef is already true from ambient noise.
353 const wsConnected = everConnectedRef.current
354 // Capture generation BEFORE the .then() — if a new session starts during
355 // the finalize wait, sessionGenRef has already advanced by the time the
356 // continuation runs, so capturing inside the .then() would yield the new
357 // session's gen and every staleness check would be a no-op.
358 const myGen = sessionGenRef.current
359 const isStale = () => sessionGenRef.current !== myGen
360 logForDebugging('[voice] Recording stopped')
361
362 // Send finalize and wait for the WebSocket to close before reading the
363 // accumulated transcript. The close handler promotes any unreported
364 // interim text to final, so we must wait for it to fire.
365 const finalizePromise: Promise<FinalizeSource | undefined> =
366 connectionRef.current
367 ? connectionRef.current.finalize()
368 : Promise.resolve(undefined)
369
370 void finalizePromise
371 .then(async finalizeSource => {
372 if (isStale()) return
373 // Silent-drop replay: when the server accepted audio (wsConnected),
374 // the mic captured real signal (hadAudioSignal), but finalize timed
375 // out with zero transcript — the ~1% session-sticky CE-pod bug.
376 // Replay the buffered audio on a fresh connection once. A 250ms
377 // backoff clears the same-pod rapid-reconnect race (same gap as the
378 // early-error retry path below).
379 if (

Callers 4

armFocusSilenceTimerFunction · 0.85
useVoiceFunction · 0.85
startRecordingSessionFunction · 0.85
attemptConnectFunction · 0.85

Calls 15

logForDebuggingFunction · 0.85
updateStateFunction · 0.85
isStaleFunction · 0.85
logEventFunction · 0.85
normalizeLanguageForSTTFunction · 0.85
getInitialSettingsFunction · 0.85
getVoiceKeytermsFunction · 0.85
connectVoiceStreamFunction · 0.85
toErrorFunction · 0.85
sendMethod · 0.65
sleepFunction · 0.50
resolveFunction · 0.50

Tested by

no test coverage detected