import { useState, useEffect, createContext, useContext, useCallback, type ElementType, } from "react"; type ScriptClass = | "text-ipa" | "text-rtl" | "text-cjk" | "text-thai" | "text-cyrillic" | "text-latin"; interface ScriptContextType { getClass: (text: string) => ScriptClass; } const ScriptContext = createContext({ getClass: (text: string) => "text-latin" as ScriptClass, }); const getScriptClass = (text: string): ScriptClass => { // You can combine these with includes() if text has multiple scripts if (/[\u0591-\u07FF\u200F\u202B]/.test(text)) return "text-rtl"; if (/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff]/.test(text)) return "text-cjk"; if (/[\u0E00-\u0E7F]/.test(text)) return "text-thai"; if (/[\u0400-\u04FF]/.test(text)) return "text-cyrillic"; if (/[\u0250-\u02AF\u1D00-\u1D7F\u1D80-\u1DBF\u1DC0-\u1DFF]/.test(text)) return "text-ipa"; return "text-latin"; // default }; export const ScripProvider: React.FC<{ children: React.ReactNode }> = ({ children, }) => { const getClass = useCallback((text: string) => { return getScriptClass(text); }, []); return ( {children} ); }; export const useScript = () => useContext(ScriptContext); type TextElementProps = { children?: React.ReactNode; className?: string; }; const createTextElement = (Component: ElementType) => { return function TextElement({ children, className, ...rest }: TextElementProps & React.ComponentPropsWithoutRef) { const writingSystemClass = typeof children === "string" ? getScriptClass(children) : "text-latin"; return ( {children} ); }; }; // Create all the text elements you need export const Span = createTextElement("span"); export const P = createTextElement("p"); export const H1 = createTextElement("h1"); export const H2 = createTextElement("h2"); export const H3 = createTextElement("h3"); export const H4 = createTextElement("h4"); export const H5 = createTextElement("h5"); export const H6 = createTextElement("h6"); export const Label = createTextElement("label"); export const Small = createTextElement("small"); interface Voice { default: boolean; lang: string; localService: boolean; name: string; voiceURI: string; } export const useSpeechSynthesis = () => { const [voices, setVoices] = useState([]); const [speaking, setSpeaking] = useState(false); console.log({ voices }, "voices hook"); useEffect(() => { // Function to get voices const updateVoices = () => { // Some browsers need a small delay for voices to be available setTimeout(() => { const availableVoices = window.speechSynthesis.getVoices(); if (availableVoices.length > 0) { setVoices(availableVoices); } }, 100); }; // Get initial voices updateVoices(); window.speechSynthesis.addEventListener("voiceschanged", updateVoices); // Cleanup return () => { window.speechSynthesis.removeEventListener("voiceschanged", updateVoices); }; }, []); const speak = (text: string, voiceName?: string) => { const utterance = new SpeechSynthesisUtterance(text); if (voiceName) { const voice = voices.find((v) => v.name === voiceName); if (voice) utterance.voice = voice; } utterance.onstart = () => setSpeaking(true); utterance.onend = () => setSpeaking(false); utterance.onerror = () => setSpeaking(false); window.speechSynthesis.speak(utterance); }; const stop = () => { window.speechSynthesis.cancel(); setSpeaking(false); }; return { voices, speaking, speak, stop, }; }; // Example usage in a component: const SpeechComponent = () => { const { voices, speaking, speak, stop } = useSpeechSynthesis(); const [selectedVoice, setSelectedVoice] = useState(""); return (
{speaking && }
); };