🚀 Auto-deploy: Camila atualizado em 26/05/2026 13:07:23
This commit is contained in:
+38
-16
@@ -239,18 +239,36 @@ const initApp = () => {
|
|||||||
loadVoices().then(voices => { cachedVoices = voices; });
|
loadVoices().then(voices => { cachedVoices = voices; });
|
||||||
|
|
||||||
// Escolher melhor voz feminina brasileira disponível
|
// Escolher melhor voz feminina brasileira disponível
|
||||||
|
// Prioridade: Google pt-BR > Microsoft Natural > qualquer feminina > qualquer pt-BR
|
||||||
const getBestBRVoice = () => {
|
const getBestBRVoice = () => {
|
||||||
const brVoices = cachedVoices.filter(v => v.lang.includes('pt'));
|
const brVoices = cachedVoices.filter(v => v.lang === 'pt-BR' || v.lang === 'pt_BR');
|
||||||
// Prioridade: feminina > masculino, local > remote
|
|
||||||
const female = brVoices.find(v =>
|
if (brVoices.length === 0) {
|
||||||
/female|brasil|maria|luciana|fernanda|virtual/i.test(v.name)
|
// Fallback: qualquer voz portuguesa
|
||||||
);
|
const ptVoices = cachedVoices.filter(v => v.lang.startsWith('pt'));
|
||||||
|
return ptVoices[0] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Melhor: vozes Google (muito naturais no Chrome/Android)
|
||||||
|
const google = brVoices.find(v => /google/i.test(v.name) && /femin|female/i.test(v.name));
|
||||||
|
if (google) return google;
|
||||||
|
const googleAny = brVoices.find(v => /google/i.test(v.name));
|
||||||
|
if (googleAny) return googleAny;
|
||||||
|
|
||||||
|
// 2. Microsoft Natural / Online (Edge tem vozes excelentes como Francisca)
|
||||||
|
const msNatural = brVoices.find(v => /natural|online|francisca|thalita|giovanna/i.test(v.name));
|
||||||
|
if (msNatural) return msNatural;
|
||||||
|
|
||||||
|
// 3. Qualquer voz feminina explícita
|
||||||
|
const female = brVoices.find(v => /female|femin|maria|luciana|fernanda|raquel|camila/i.test(v.name));
|
||||||
if (female) return female;
|
if (female) return female;
|
||||||
// fallback: qualquer voz brasileira
|
|
||||||
const brazil = brVoices.find(v => /brasil|brazil|pt-BR/i.test(v.lang));
|
// 4. Vozes locais (melhor que remotas genéricas)
|
||||||
if (brazil) return brazil;
|
const local = brVoices.find(v => v.localService === true);
|
||||||
// fallback: qualquer voz portuguesa
|
if (local) return local;
|
||||||
return brVoices[0] || null;
|
|
||||||
|
// 5. Qualquer voz pt-BR disponível
|
||||||
|
return brVoices[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Falar texto com TTS nativo
|
// Falar texto com TTS nativo
|
||||||
@@ -268,16 +286,20 @@ const initApp = () => {
|
|||||||
|
|
||||||
// Limpar markdown básico antes de falar
|
// Limpar markdown básico antes de falar
|
||||||
const clean = text
|
const clean = text
|
||||||
.replace(/```[\s\S]*?```/g, 'trecho de código ignorado')
|
.replace(/```[\s\S]*?```/g, '') // remover blocos de código inteiros
|
||||||
.replace(/`[^`]+`/g, '')
|
.replace(/`[^`]+`/g, '') // remover inline code
|
||||||
.replace(/[*_#~\[\]]/g, '')
|
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // links → só texto
|
||||||
.replace(/\n+/g, ' ')
|
.replace(/[*_#~\[\]>]/g, '') // remover formatação
|
||||||
|
.replace(/\n+/g, '. ') // quebras viram pausas naturais
|
||||||
|
.replace(/\s{2,}/g, ' ')
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
|
if (!clean) return;
|
||||||
|
|
||||||
const utterance = new window.SpeechSynthesisUtterance(clean);
|
const utterance = new window.SpeechSynthesisUtterance(clean);
|
||||||
utterance.lang = 'pt-BR';
|
utterance.lang = 'pt-BR';
|
||||||
utterance.rate = 0.95;
|
utterance.rate = 1.12; // levemente mais rápida que o padrão
|
||||||
utterance.pitch = 1.05;
|
utterance.pitch = 1.0; // tom natural sem distorção
|
||||||
|
|
||||||
const voice = getBestBRVoice();
|
const voice = getBestBRVoice();
|
||||||
if (voice) utterance.voice = voice;
|
if (voice) utterance.voice = voice;
|
||||||
|
|||||||
Reference in New Issue
Block a user