diff --git a/htdocs/ai/js/ai_assistant.js b/htdocs/ai/js/ai_assistant.js
index 2ac67cfba97..a0dc7f0632f 100644
--- a/htdocs/ai/js/ai_assistant.js
+++ b/htdocs/ai/js/ai_assistant.js
@@ -715,7 +715,10 @@ export function initAiAssistant(container) {
cloudRecognition.lang = navigator.language || 'en-US';
cloudRecognition.onstart = () => { setMicState('listening'); statusBar.innerText = t('Listening'); };
- cloudRecognition.onend = () => { if (isRecording && engineSelect.value === 'cloud') stopRecording(); };
+ // When recognition ends without a result (user stopped, or silence
+ // timeout), return to idle instead of looping on stopRecording (which
+ // left the button stuck in the orange 'processing' state).
+ cloudRecognition.onend = () => { if (engineSelect.value === 'cloud' && isRecording) { resetUI(); } };
cloudRecognition.onerror = (event) => {
console.error('Speech recognition error:', event.error);
let errorMsg = t('Error') + ": " + event.error;
@@ -730,7 +733,7 @@ export function initAiAssistant(container) {
cloudRecognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
input.value = transcript;
- setMicState('idle');
+ resetUI(); // clears isRecording/isProcessing so onend won't re-fire a reset
statusBar.innerText = t('Transcribed');
handleQuery();
};
@@ -936,7 +939,7 @@ export function initAiAssistant(container) {
micBtn.classList.remove('listening', 'processing', 'cancelling');
micBtn.disabled = false;
if (state === 'listening') { micBtn.classList.add('listening'); micBtn.innerHTML = ''; }
- else if (state === 'processing') { micBtn.classList.add('processing'); micBtn.innerHTML = ''; }
+ else if (state === 'processing') { micBtn.classList.add('processing'); micBtn.innerHTML = ''; }
else { micBtn.innerHTML = ''; micBtn.title = "Start Recording"; }
}