From 17e61f4b18c654201ce8f6c3d7b927a91a0dce40 Mon Sep 17 00:00:00 2001 From: dsyoon Date: Wed, 28 Jan 2026 23:04:40 +0900 Subject: [PATCH] Render live STT without finalize --- client/src/App.tsx | 100 ++++++++++++--------------------------------- 1 file changed, 25 insertions(+), 75 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 113160b..2964d82 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -58,20 +58,7 @@ function App() { return () => window.clearInterval(intervalId) }, [isRecording]) - const commitLiveIfAny = async () => { - const text = liveTextRef.current.trim() - if (!text) return - const ts = new Date().toISOString() - setTranscriptLines((prev) => { - const last = prev[prev.length - 1] - if (last && !last.isFinal) { - return [ - ...prev.slice(0, -1), - { ...last, text, ts, isFinal: true }, - ] - } - return [...prev, { id: lineIdRef.current++, ts, text, isFinal: true }] - }) + const persistFinal = async (ts: string, text: string) => { if (!meetingIdRef.current) { pendingUtterancesRef.current.push({ ts, text }) return @@ -83,6 +70,29 @@ function App() { } } + const updateTranscript = (text: string, isFinal: boolean) => { + const trimmed = text.trim() + if (!trimmed) return + const ts = new Date().toISOString() + liveTextRef.current = isFinal ? '' : trimmed + setTranscriptLines((prev) => { + const last = prev[prev.length - 1] + if (last && !last.isFinal) { + return [ + ...prev.slice(0, -1), + { ...last, text: trimmed, ts, isFinal }, + ] + } + if (last && last.isFinal && isFinal && last.text.trim() === trimmed) { + return prev + } + return [...prev, { id: lineIdRef.current++, ts, text: trimmed, isFinal }] + }) + if (isFinal) { + void persistFinal(ts, trimmed) + } + } + const startRecognition = () => { const SpeechRecognitionConstructor = window.SpeechRecognition || window.webkitSpeechRecognition @@ -100,34 +110,10 @@ function App() { recognition.onresult = (event) => { lastResultAtRef.current = Date.now() - let interim = '' for (let i = event.resultIndex; i < event.results.length; i += 1) { const result = event.results[i] const text = result[0].transcript - if (result.isFinal) { - handleFinalTranscript(text) - } else { - interim += text - } - } - const interimText = interim.trim() - if (interimText) { - liveTextRef.current = interimText - setTranscriptLines((prev) => { - const last = prev[prev.length - 1] - if (last && !last.isFinal) { - return [...prev.slice(0, -1), { ...last, text: interimText }] - } - return [ - ...prev, - { - id: lineIdRef.current++, - ts: new Date().toISOString(), - text: interimText, - isFinal: false, - }, - ] - }) + updateTranscript(text, result.isFinal) } } @@ -161,41 +147,6 @@ function App() { } } - const handleFinalTranscript = async (text: string) => { - const trimmed = text.trim() - if (!trimmed) return - lastResultAtRef.current = Date.now() - const ts = new Date().toISOString() - liveTextRef.current = '' - let nextLines: { id: number; ts: string; text: string; isFinal: boolean }[] = [] - setTranscriptLines((prev) => { - const last = prev[prev.length - 1] - if (last && !last.isFinal) { - nextLines = [ - ...prev.slice(0, -1), - { ...last, text: trimmed, ts, isFinal: true }, - ] - return nextLines - } - nextLines = [ - ...prev, - { id: lineIdRef.current++, ts, text: trimmed, isFinal: true }, - ] - return nextLines - }) - - if (!meetingIdRef.current) { - pendingUtterancesRef.current.push({ ts, text: trimmed }) - return - } - try { - await saveUtterance(meetingIdRef.current, trimmed, ts) - } catch (err) { - setErrorMessage((err as Error).message) - } - - } - const handleStart = async () => { setErrorMessage(null) lineIdRef.current = 1 @@ -225,7 +176,6 @@ function App() { if (!meetingIdRef.current) return setErrorMessage(null) recognitionRef.current?.stop() - await commitLiveIfAny() liveTextRef.current = '' setIsRecording(false) isRecordingRef.current = false