import { useState, useCallback, useEffect, useRef } from "react"; import type { Persona, Affect, ChatMessage, LatencyLog } from "./types"; import { resetSession, checkHealth } from "./lib/api"; import { useWebcam } from "./hooks/useWebcam"; import { useSensing } from "./hooks/useSensing"; import { PersonaSelector } from "./components/PersonaSelector"; import { ChatPanel } from "./components/ChatPanel"; import { WebcamSensing } from "./components/WebcamSensing"; import { SensingStatus } from "./components/SensingStatus"; import { LatencyMetrics } from "./components/LatencyMetrics"; import "./App.css"; function App() { const [persona, setPersona] = useState(null); const [messages, setMessages] = useState([]); const [latency, setLatency] = useState(null); const [webcamEnabled, setWebcamEnabled] = useState(false); const [affectOverride, setAffectOverride] = useState(null); const [backendReady, setBackendReady] = useState(false); const healthPoll = useRef>(undefined); useEffect(() => { async function poll() { const ready = await checkHealth(); if (ready) { setBackendReady(true); clearInterval(healthPoll.current); } } poll(); healthPoll.current = setInterval(poll, 2000); return () => clearInterval(healthPoll.current); }, []); const { sensing, ready, initError, init, processFrame, clearAirWrittenText, clearHeadSignal, resetCalibration, } = useSensing(); const onFrame = useCallback( (video: HTMLVideoElement, timestamp: number) => { processFrame(video, timestamp); }, [processFrame] ); const { videoRef, active, error } = useWebcam({ enabled: webcamEnabled && ready, onFrame, }); async function handleWebcamToggle() { if (!webcamEnabled) { const ok = await init(); if (ok) setWebcamEnabled(true); } else { setWebcamEnabled(false); resetCalibration(); } } async function handlePersonaSelect(p: Persona) { setPersona(p); setMessages([]); setLatency(null); try { await resetSession(p.id); } catch { // Session reset failed — non-critical, continue with fresh UI state } } return (
); } export default App;