🚀 Auto-deploy: Camila atualizado em 03/06/2026 10:44:31
This commit is contained in:
+39
-31
@@ -775,27 +775,14 @@ const initApp = () => {
|
||||
// TTS — Speech Synthesis nativa (voz do navegador)
|
||||
// ==========================================================================
|
||||
|
||||
let cachedVoices = [];
|
||||
let currentSpeakingBtn = null;
|
||||
|
||||
const loadVoices = () => {
|
||||
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
|
||||
// Selecionar melhor voz pt-BR disponível no dispositivo de forma dinâmica
|
||||
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) {
|
||||
const pt = cachedVoices.filter(v => v.lang.startsWith('pt'));
|
||||
const pt = voices.filter(v => v.lang.startsWith('pt'));
|
||||
return pt[0] || null;
|
||||
}
|
||||
|
||||
@@ -819,19 +806,34 @@ const initApp = () => {
|
||||
return ptBR[0];
|
||||
};
|
||||
|
||||
const resetSpeakBtnState = (btn) => {
|
||||
if (btn) {
|
||||
btn.classList.remove('speaking');
|
||||
const span = btn.querySelector('span');
|
||||
if (span) span.textContent = 'Ouvir';
|
||||
}
|
||||
};
|
||||
|
||||
// Falar texto com TTS nativa
|
||||
const speakText = (text, btn) => {
|
||||
// Se já está falando, parar
|
||||
if (window.speechSynthesis.speaking) {
|
||||
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();
|
||||
if (btn) {
|
||||
btn.classList.remove('speaking');
|
||||
const span = btn.querySelector('span');
|
||||
if (span) span.textContent = 'Ouvir';
|
||||
}
|
||||
resetSpeakBtnState(btn);
|
||||
currentSpeakingBtn = null;
|
||||
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
|
||||
const clean = text
|
||||
.replace(/```[\s\S]*?```/g, '')
|
||||
@@ -842,7 +844,10 @@ const initApp = () => {
|
||||
.replace(/\s{2,}/g, ' ')
|
||||
.trim();
|
||||
|
||||
if (!clean) return;
|
||||
if (!clean) {
|
||||
currentSpeakingBtn = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const utterance = new SpeechSynthesisUtterance(clean);
|
||||
utterance.lang = 'pt-BR';
|
||||
@@ -860,14 +865,17 @@ const initApp = () => {
|
||||
}
|
||||
};
|
||||
|
||||
utterance.onend = utterance.onerror = () => {
|
||||
if (btn) {
|
||||
btn.classList.remove('speaking');
|
||||
const span = btn.querySelector('span');
|
||||
if (span) span.textContent = 'Ouvir';
|
||||
const handleStop = (event) => {
|
||||
console.log('TTS finalizado ou com erro:', event.type);
|
||||
if (currentSpeakingBtn === btn) {
|
||||
resetSpeakBtnState(btn);
|
||||
currentSpeakingBtn = null;
|
||||
}
|
||||
};
|
||||
|
||||
utterance.onend = handleStop;
|
||||
utterance.onerror = handleStop;
|
||||
|
||||
window.speechSynthesis.speak(utterance);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user