🚀 Auto-deploy: Camila atualizado em 03/06/2026 10:44:31
This commit is contained in:
+37
-29
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user