added visual waiting steps
All checks were successful
Deploy FluentGerman.ai / deploy (push) Successful in 52s
All checks were successful
Deploy FluentGerman.ai / deploy (push) Successful in 52s
This commit is contained in:
@@ -160,7 +160,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
const text = inputEl.value.trim();
|
const text = inputEl.value.trim();
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
|
|
||||||
const wasVoice = voice.lastInputWasVoice;
|
|
||||||
voice.lastInputWasVoice = false;
|
voice.lastInputWasVoice = false;
|
||||||
|
|
||||||
inputEl.value = '';
|
inputEl.value = '';
|
||||||
@@ -169,7 +168,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
appendMessage('user', text);
|
appendMessage('user', text);
|
||||||
history.push({ role: 'user', content: text });
|
history.push({ role: 'user', content: text });
|
||||||
|
|
||||||
const assistantEl = appendMessage('assistant', '');
|
const assistantEl = appendMessage('assistant', voiceModeOn ? 'Thinking...' : '');
|
||||||
let fullResponse = '';
|
let fullResponse = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -188,9 +187,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
// Special handling for Voice Mode: Buffer text, wait for TTS, then show & play
|
// Special handling for Voice Mode: Buffer text, wait for TTS, then show & play
|
||||||
if (voiceModeOn) {
|
if (voiceModeOn) {
|
||||||
// Show thinking state
|
// "Thinking..." is already shown from appendMessage above
|
||||||
assistantEl.innerHTML = '<span class="thinking-dots">Thinking<span>.</span><span>.</span><span>.</span></span>';
|
|
||||||
assistantEl.classList.add('message-thinking');
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
|
|||||||
@@ -147,6 +147,8 @@ class VoiceManager {
|
|||||||
stopRecording() {
|
stopRecording() {
|
||||||
if (this.mode === 'api') {
|
if (this.mode === 'api') {
|
||||||
if (this.mediaRecorder?.state === 'recording') {
|
if (this.mediaRecorder?.state === 'recording') {
|
||||||
|
// Show processing state immediately (don't wait for onstop callback)
|
||||||
|
if (this.onProcessing) this.onProcessing(true);
|
||||||
this.mediaRecorder.stop();
|
this.mediaRecorder.stop();
|
||||||
} else {
|
} else {
|
||||||
this.isRecording = false;
|
this.isRecording = false;
|
||||||
@@ -240,17 +242,20 @@ class VoiceManager {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
audio.onended = () => {
|
audio.onended = () => {
|
||||||
if (avatarContainer) avatarContainer.classList.remove('speaking');
|
if (avatarContainer) avatarContainer.classList.remove('speaking');
|
||||||
|
URL.revokeObjectURL(audioUrl);
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
// Handle errors during playback (e.g. format issues)
|
// Handle errors during playback (e.g. format issues)
|
||||||
audio.onerror = () => {
|
audio.onerror = () => {
|
||||||
if (avatarContainer) avatarContainer.classList.remove('speaking');
|
if (avatarContainer) avatarContainer.classList.remove('speaking');
|
||||||
|
URL.revokeObjectURL(audioUrl);
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Playback failed", e);
|
console.error("Playback failed", e);
|
||||||
if (avatarContainer) avatarContainer.classList.remove('speaking');
|
if (avatarContainer) avatarContainer.classList.remove('speaking');
|
||||||
|
URL.revokeObjectURL(audioUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user