This commit is contained in:
2026-03-09 15:04:34 +01:00
parent 26cf79a0f7
commit 1aeb5c164b

View File

@@ -146,14 +146,11 @@ function createRecognition() {
if (!SpeechRecognition) return null; if (!SpeechRecognition) return null;
const r = new SpeechRecognition(); const r = new SpeechRecognition();
r.lang = 'de-DE'; r.lang = 'de-DE';
r.continuous = true; r.continuous = false; // one phrase per session — avoids all Android e.resultIndex bugs
r.interimResults = true; r.interimResults = true; // stream words within the phrase
r.maxAlternatives = 1; r.maxAlternatives = 1;
// Android Chrome bug: onresult re-fires old results with e.resultIndex=0. let committed = false; // guard: each session commits at most one final result
// highestFinalIndex guards against processing the same final result twice.
let highestFinalIndex = -1;
let sessionFinal = ''; // final text committed within this recognition session
r.onstart = () => { r.onstart = () => {
recordBtn.classList.add('recording'); recordBtn.classList.add('recording');
@@ -162,35 +159,27 @@ function createRecognition() {
}; };
r.onresult = (e) => { r.onresult = (e) => {
let interim = ''; if (committed) return;
for (let i = 0; i < e.results.length; i++) { const result = e.results[0];
if (e.results[i].isFinal) { const text = result[0].transcript;
if (i > highestFinalIndex) { if (result.isFinal) {
sessionFinal += (sessionFinal ? ' ' : '') + e.results[i][0].transcript; committed = true;
highestFinalIndex = i; state.finalTranscript += (state.finalTranscript ? ' ' : '') + text;
} state.transcript = state.finalTranscript;
} else { } else {
interim = e.results[i][0].transcript; // only the latest interim matters // interim — show but don't commit
} state.transcript = (state.finalTranscript ? state.finalTranscript + ' ' : '') + text;
} }
const base = state.finalTranscript; if (state.transcript.trim().split(/\s+/).length >= MAX_RECORD_WORDS) {
const combined = base + (base && sessionFinal ? ' ' : '') + sessionFinal;
const display = combined + (interim ? (combined ? ' ' : '') + interim : '');
if (display.trim().split(/\s+/).length >= MAX_RECORD_WORDS) {
stopRecording(); stopRecording();
return; return;
} }
updateTranscriptBox(display); updateTranscriptBox(state.transcript);
state.transcript = display; checkBtn.disabled = !state.transcript.trim();
checkBtn.disabled = !display.trim();
}; };
r.onend = () => { r.onend = () => {
if (!state.isRecording) return; if (!state.isRecording) return;
// Merge this session's final text into the persistent accumulator before restarting
if (sessionFinal) {
state.finalTranscript += (state.finalTranscript ? ' ' : '') + sessionFinal;
}
setTimeout(() => { setTimeout(() => {
if (!state.isRecording) return; if (!state.isRecording) return;
recognition = createRecognition(); recognition = createRecognition();
@@ -198,7 +187,6 @@ function createRecognition() {
}, 100); }, 100);
}; };
// 'aborted' fires during normal restart cycle — ignore it
r.onerror = (e) => { r.onerror = (e) => {
if (e.error !== 'no-speech' && e.error !== 'aborted') stopRecording(); if (e.error !== 'no-speech' && e.error !== 'aborted') stopRecording();
}; };