🎤 feat: Sistema de conversa por voz - auto-TTS quando mensagem vem do microfone, envio automático, melhor visibilidade do ícone
This commit is contained in:
+24
-3
@@ -288,6 +288,7 @@ const initApp = () => {
|
|||||||
// Variáveis de Reconhecimento de Voz
|
// Variáveis de Reconhecimento de Voz
|
||||||
let recognition = null;
|
let recognition = null;
|
||||||
let isListening = false;
|
let isListening = false;
|
||||||
|
let lastInputWasVoice = false; // Rastrear se a última mensagem veio do microfone
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// THEME TOGGLE — Claro/Escuro
|
// THEME TOGGLE — Claro/Escuro
|
||||||
@@ -488,13 +489,14 @@ const initApp = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// VOZ — WEB SPEECH API (ditado)
|
// VOZ — WEB SPEECH API (ditado + conversa por voz)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
const initVoiceRecognition = () => {
|
const initVoiceRecognition = () => {
|
||||||
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
|
||||||
if (!SpeechRecognition) {
|
if (!SpeechRecognition) {
|
||||||
btnVoice.style.display = 'none';
|
btnVoice.style.display = 'none';
|
||||||
|
console.warn('SpeechRecognition não suportado neste navegador.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,6 +511,16 @@ const initApp = () => {
|
|||||||
userInput.value = transcript;
|
userInput.value = transcript;
|
||||||
userInput.dispatchEvent(new Event('input'));
|
userInput.dispatchEvent(new Event('input'));
|
||||||
setVoiceState(false);
|
setVoiceState(false);
|
||||||
|
|
||||||
|
// Marca que o input veio do microfone para auto-responder por voz
|
||||||
|
lastInputWasVoice = true;
|
||||||
|
|
||||||
|
// Envia automaticamente a mensagem ditada
|
||||||
|
if (transcript.trim()) {
|
||||||
|
setTimeout(() => {
|
||||||
|
chatForm.dispatchEvent(new Event('submit', { cancelable: true }));
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
recognition.onerror = (event) => {
|
recognition.onerror = (event) => {
|
||||||
@@ -525,10 +537,10 @@ const initApp = () => {
|
|||||||
isListening = listening;
|
isListening = listening;
|
||||||
if (listening) {
|
if (listening) {
|
||||||
btnVoice.classList.add('listening');
|
btnVoice.classList.add('listening');
|
||||||
btnVoice.setAttribute('title', 'Clique para parar');
|
btnVoice.setAttribute('title', 'Clique para parar de ouvir');
|
||||||
} else {
|
} else {
|
||||||
btnVoice.classList.remove('listening');
|
btnVoice.classList.remove('listening');
|
||||||
btnVoice.setAttribute('title', 'Ditar mensagem');
|
btnVoice.setAttribute('title', 'Conversar por voz');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1685,6 +1697,15 @@ const initApp = () => {
|
|||||||
|
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
|
|
||||||
|
// AUTO-TTS: Se a mensagem veio do microfone, ler a resposta em voz alta automaticamente
|
||||||
|
if (lastInputWasVoice && assistantContent && !isMediaResponse) {
|
||||||
|
console.log('[VOZ] Mensagem veio do microfone — respondendo por voz automaticamente.');
|
||||||
|
setTimeout(() => {
|
||||||
|
speakText(assistantContent, speakBtn);
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
lastInputWasVoice = false;
|
||||||
|
|
||||||
// Salvar resposta no estado e localStorage
|
// Salvar resposta no estado e localStorage
|
||||||
const assistantMsg = { role: 'assistant', content: assistantContent };
|
const assistantMsg = { role: 'assistant', content: assistantContent };
|
||||||
chat.messages.push(assistantMsg);
|
chat.messages.push(assistantMsg);
|
||||||
|
|||||||
+10
-7
@@ -1223,10 +1223,11 @@ code {
|
|||||||
.btn-voice {
|
.btn-voice {
|
||||||
background: none !important;
|
background: none !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
color: var(--text-muted) !important;
|
color: var(--text-secondary, #a3a3a3) !important;
|
||||||
border-radius: 50% !important;
|
border-radius: 50% !important;
|
||||||
width: 32px !important;
|
width: 36px !important;
|
||||||
height: 32px !important;
|
height: 36px !important;
|
||||||
|
min-width: 36px !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1237,17 +1238,19 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.btn-voice svg {
|
.btn-voice svg {
|
||||||
width: 18px;
|
width: 20px;
|
||||||
height: 18px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-voice:hover {
|
.btn-voice:hover {
|
||||||
color: var(--text-primary) !important;
|
color: var(--brand-green, #10a37f) !important;
|
||||||
transform: scale(1.05);
|
background-color: rgba(16, 163, 127, 0.1) !important;
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-voice.listening {
|
.btn-voice.listening {
|
||||||
color: #ef4444 !important;
|
color: #ef4444 !important;
|
||||||
|
background-color: rgba(239, 68, 68, 0.12) !important;
|
||||||
animation: pulse-voice 1.5s ease-in-out infinite;
|
animation: pulse-voice 1.5s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user