UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

187 lines (183 loc) 5.36 kB
import { useAccessibilityStore } from "./chunk-7CD5FIDV.mjs"; // src/hooks/useTTS.ts import { useCallback, useEffect, useMemo, useRef, useState } from "react"; // src/components/AccessibilityWidget/tts/WebSpeechProvider.ts var WebSpeechProvider = class { synth; currentUtterance = null; constructor(synth = getSynth()) { this.synth = synth; } isSupported() { return this.synth !== null; } async getVoices() { if (!this.synth) return []; const synth = this.synth; const collect = () => synth.getVoices().map((v) => ({ id: v.voiceURI, name: v.name, lang: v.lang, isLocal: v.localService })); const initial = collect(); if (initial.length > 0) return initial; return new Promise((resolve) => { const onChange = () => { synth.removeEventListener("voiceschanged", onChange); resolve(collect()); }; synth.addEventListener("voiceschanged", onChange); setTimeout(() => { synth.removeEventListener("voiceschanged", onChange); resolve(collect()); }, 1500); }); } speak(text, options = {}, events = {}) { if (!this.synth) { events.onError?.("S\xEDntese de voz n\xE3o dispon\xEDvel neste navegador."); return; } const trimmed = text.trim(); if (!trimmed) return; this.synth.cancel(); const utterance = new SpeechSynthesisUtterance(trimmed); utterance.rate = options.rate ?? 1; utterance.pitch = options.pitch ?? 1; const allVoices = this.synth.getVoices(); let chosenVoice; if (options.voiceId) { chosenVoice = allVoices.find((v) => v.voiceURI === options.voiceId); } else if (options.lang) { chosenVoice = allVoices.find( (v) => v.lang.toLowerCase().startsWith(options.lang.toLowerCase()) ); } if (chosenVoice) { utterance.voice = chosenVoice; utterance.lang = chosenVoice.lang; } utterance.onstart = () => events.onStart?.(); utterance.onend = () => { this.currentUtterance = null; events.onEnd?.(); }; utterance.onpause = () => events.onPause?.(); utterance.onresume = () => events.onResume?.(); utterance.onerror = (event) => { this.currentUtterance = null; events.onError?.(event.error || "Erro desconhecido na s\xEDntese."); }; this.currentUtterance = utterance; this.synth.speak(utterance); } pause() { this.synth?.pause(); } resume() { this.synth?.resume(); } stop() { this.synth?.cancel(); this.currentUtterance = null; } }; var getSynth = () => { if (typeof globalThis === "undefined") return null; const synth = globalThis.speechSynthesis; return synth ?? null; }; // src/hooks/useTTS.ts var useTTS = (providerOverride) => { const setTTSStatus = useAccessibilityStore((s) => s.setTTSStatus); const ttsRate = useAccessibilityStore((s) => s.ttsRate); const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId); const provider = useMemo( () => providerOverride ?? new WebSpeechProvider(), [providerOverride] ); const [voices, setVoices] = useState([]); const rateRef = useRef(ttsRate); const voiceIdRef = useRef(ttsVoiceId); rateRef.current = ttsRate; voiceIdRef.current = ttsVoiceId; const isActiveSpeakerRef = useRef(false); useEffect(() => { let mounted = true; if (provider.isSupported()) { provider.getVoices().then((list) => { if (mounted) setVoices(list); }).catch(() => void 0); } return () => { mounted = false; if (isActiveSpeakerRef.current) { provider.stop(); setTTSStatus("idle"); isActiveSpeakerRef.current = false; } }; }, [provider, setTTSStatus]); const hasPortugueseVoice = voices.some( (v) => v.lang.toLowerCase().startsWith("pt") ); const speak = useCallback( (text) => { isActiveSpeakerRef.current = true; provider.speak( text, { rate: rateRef.current, voiceId: voiceIdRef.current ?? void 0 // Não fixamos `lang: 'pt-BR'` aqui — em sistemas sem voz pt-BR // instalada, o browser fica tentando achar uma e nunca emite // áudio. O provider usa o `voice.lang` quando há voz escolhida. }, { onStart: () => setTTSStatus("speaking"), onEnd: () => { isActiveSpeakerRef.current = false; setTTSStatus("idle"); }, onPause: () => setTTSStatus("paused"), onResume: () => setTTSStatus("speaking"), onError: () => { isActiveSpeakerRef.current = false; setTTSStatus("idle"); } } ); }, [provider, setTTSStatus] ); const pause = useCallback(() => { provider.pause(); setTTSStatus("paused"); }, [provider, setTTSStatus]); const resume = useCallback(() => { provider.resume(); setTTSStatus("speaking"); }, [provider, setTTSStatus]); const stop = useCallback(() => { isActiveSpeakerRef.current = false; provider.stop(); setTTSStatus("idle"); }, [provider, setTTSStatus]); return { isSupported: provider.isSupported(), voices, hasPortugueseVoice, speak, pause, resume, stop }; }; export { WebSpeechProvider, useTTS }; //# sourceMappingURL=chunk-4PIITPCV.mjs.map