🚀 Auto-deploy: Camila atualizado em 03/06/2026 10:44:31

This commit is contained in:
2026-06-03 10:44:31 +00:00
parent e41e3b6cbf
commit 0f3bf4c2dd
+37 -29
View File
@@ -775,27 +775,14 @@ const initApp = () => {
// TTS — Speech Synthesis nativa (voz do navegador) // TTS — Speech Synthesis nativa (voz do navegador)
// ========================================================================== // ==========================================================================
let cachedVoices = []; let currentSpeakingBtn = null;
const loadVoices = () => { // Selecionar melhor voz pt-BR disponível no dispositivo de forma dinâmica
const voices = window.speechSynthesis.getVoices();
if (voices.length > 0) {
cachedVoices = voices;
console.log('Vozes pt-BR disponíveis:', cachedVoices.filter(v => v.lang.startsWith('pt')).map(v => v.name));
} else {
window.speechSynthesis.addEventListener('voiceschanged', () => {
cachedVoices = window.speechSynthesis.getVoices();
console.log('Vozes pt-BR disponíveis:', cachedVoices.filter(v => v.lang.startsWith('pt')).map(v => v.name));
}, { once: true });
}
};
loadVoices();
// Selecionar melhor voz pt-BR disponível no dispositivo
const getBestVoice = () => { const getBestVoice = () => {
const ptBR = cachedVoices.filter(v => v.lang === 'pt-BR' || v.lang === 'pt_BR'); const voices = window.speechSynthesis.getVoices();
const ptBR = voices.filter(v => v.lang === 'pt-BR' || v.lang === 'pt_BR');
if (ptBR.length === 0) { if (ptBR.length === 0) {
const pt = cachedVoices.filter(v => v.lang.startsWith('pt')); const pt = voices.filter(v => v.lang.startsWith('pt'));
return pt[0] || null; return pt[0] || null;
} }
@@ -819,19 +806,34 @@ const initApp = () => {
return ptBR[0]; return ptBR[0];
}; };
// Falar texto com TTS nativa const resetSpeakBtnState = (btn) => {
const speakText = (text, btn) => {
// Se já está falando, parar
if (window.speechSynthesis.speaking) {
window.speechSynthesis.cancel();
if (btn) { if (btn) {
btn.classList.remove('speaking'); btn.classList.remove('speaking');
const span = btn.querySelector('span'); const span = btn.querySelector('span');
if (span) span.textContent = 'Ouvir'; if (span) span.textContent = 'Ouvir';
} }
};
// Falar texto com TTS nativa
const speakText = (text, btn) => {
if (!text) return;
// Se clicar no botão que já está ativamente falando, parar a reprodução
if (window.speechSynthesis.speaking && currentSpeakingBtn === btn) {
window.speechSynthesis.cancel();
resetSpeakBtnState(btn);
currentSpeakingBtn = null;
return; return;
} }
// Se houver outra fala em andamento ou fila travada, limpa tudo e reseta o botão antigo
window.speechSynthesis.cancel();
if (currentSpeakingBtn) {
resetSpeakBtnState(currentSpeakingBtn);
}
currentSpeakingBtn = btn;
// Limpar markdown antes de falar // Limpar markdown antes de falar
const clean = text const clean = text
.replace(/```[\s\S]*?```/g, '') .replace(/```[\s\S]*?```/g, '')
@@ -842,7 +844,10 @@ const initApp = () => {
.replace(/\s{2,}/g, ' ') .replace(/\s{2,}/g, ' ')
.trim(); .trim();
if (!clean) return; if (!clean) {
currentSpeakingBtn = null;
return;
}
const utterance = new SpeechSynthesisUtterance(clean); const utterance = new SpeechSynthesisUtterance(clean);
utterance.lang = 'pt-BR'; utterance.lang = 'pt-BR';
@@ -860,14 +865,17 @@ const initApp = () => {
} }
}; };
utterance.onend = utterance.onerror = () => { const handleStop = (event) => {
if (btn) { console.log('TTS finalizado ou com erro:', event.type);
btn.classList.remove('speaking'); if (currentSpeakingBtn === btn) {
const span = btn.querySelector('span'); resetSpeakBtnState(btn);
if (span) span.textContent = 'Ouvir'; currentSpeakingBtn = null;
} }
}; };
utterance.onend = handleStop;
utterance.onerror = handleStop;
window.speechSynthesis.speak(utterance); window.speechSynthesis.speak(utterance);
}; };