import { useState, useEffect, useRef } from "react"; import { CHARACTERS } from "../assets/characters"; import CharacterBackdrop from "./CharacterBackdrop"; import { useAudio } from "../hooks/useAudio"; import { evaluateAttempt } from "../utils/api"; import { friendlyPhoneme, phonemeExample } from "../utils/phonemeMap"; import { displayPhoneme } from "../utils/phonemeMapIndic"; import { t } from "../utils/i18n"; import { getTheme, getSurface } from "../utils/themes"; export default function PracticeScreen({ character, language = "english", wordData, sessionId, attemptNumber, attemptHistory = [], onResult, onSwitchCharacter, darkMode }) { const [phase, setPhase] = useState("listen"); const [playingChar, setPlayingChar] = useState(false); const [playingChild, setPlayingChild] = useState(false); const { isRecording, audioBlob, audioUrl, startRecording, stopRecording, reset } = useAudio(); const char = CHARACTERS[character]; const th = getTheme(character, darkMode); const [showSwitcher, setShowSwitcher] = useState(false); const [imageIndex, setImageIndex] = useState(0); useEffect(() => { document.body.style.background = th.bg; document.body.style.transition = "background 0.5s ease"; }, [th.bg]); const playWord = async (speed = 1.0) => { setPlayingChar(true); try { const form = new FormData(); form.append("word", wordData.word); form.append("speed", String(speed)); form.append("language", language); form.append("character", character); const res = await fetch("https://anabaena-vaaksiddhi.hf.space/speak/word", { method: "POST", body: form }); const blob = await res.blob(); const url = URL.createObjectURL(blob); const audio = new Audio(url); audio.play(); audio.onended = () => setPlayingChar(false); } catch { setPlayingChar(false); } }; const playChildAudio = () => { if (!audioUrl) return; setPlayingChild(true); const audio = new Audio(audioUrl); audio.play(); audio.onended = () => setPlayingChild(false); }; const handleRecord = async () => { setPhase("record"); await startRecording(); }; const handleSubmit = async () => { if (!audioBlob) return; setPhase("loading"); try { const result = await evaluateAttempt({ audio: audioBlob, targetWord: wordData.word, character, language: wordData.language || "english", condition: "autism", attemptNumber, sessionId, attemptHistory, }); const newHistory = [...attemptHistory, result]; onResult({ ...result, attemptNumber, attemptHistory: newHistory, childAudioUrl: audioUrl }); } catch (err) { console.error(err); setPhase("record"); } }; const imageUrl = wordData?.image_base64 ? `data:image/png;base64,${wordData.image_base64}` : null; return (
{char.name} {char.name}
Attempt {attemptNumber}
{showSwitcher && (

Switch character

{Object.values(CHARACTERS).map(c => ( ))}
)}
{(() => { const imgs = wordData?.images?.length > 0 ? wordData.images : imageUrl ? [{ label: wordData?.word, image_base64: wordData?.image_base64 }] : []; const idx = Math.min(imageIndex, Math.max(imgs.length - 1, 0)); const current = imgs[idx]; return (
{current?.pair ? ( <>
{current.label_1} {current.label_1}
+ {current.image_base64_2 && (
{current.label_2} {current.label_2}
)} ) : current ? ( {current.label} ) : (
🖼️
)}
{current && !current.pair &&

{current.label}

} {imgs.length > 1 && (
{imgs.map((_, i) => (
setImageIndex(i)} style={{ width: i === idx ? "16px" : "7px", height: "7px", borderRadius: "4px", background: i === idx ? th.accent : `${th.accent}33`, transition: "all 0.2s", cursor: "pointer" }} /> ))}
)}
); })()}

Target Word

{wordData?.word}

{(wordData?.phonemes || []).map((p, i) => (
{language === "english" ? friendlyPhoneme(p) : (displayPhoneme(p, language)?.label || p)} {p}
))}
{playingChar &&

Playing...

} {phase === "listen" && ( )} {phase === "record" && isRecording && (

Recording... tap to stop

)} {phase === "record" && !isRecording && audioBlob && (
)} {phase === "loading" && (

Analysing your voice...

)}
); }