/** * Praxis v0.1 — full session UX (SLICE-05 TASK-05-04). * * Three views: start → live → debrief. Replaces the SLICE-02 minimal page. * - Start: scenario title + disclaimer acknowledgement + Start button * - Live: turn indicators (learner/AI), interrupt feedback, latency readout * - Debrief: debrief text + audio replay control + latency/cost summary */ import { useVoiceSession } from './useVoiceSession' import { useEffect, useState } from 'react' import './App.css' type View = 'start' | 'live' | 'debrief' function App() { const { state, error, transcripts, latency, start, stop } = useVoiceSession() const [view, setView] = useState('start') const [acknowledged, setAcknowledged] = useState(false) useEffect(() => { if (state === 'connected' && view === 'start') { setView('live') } if (state === 'idle' && view === 'live') { setView('debrief') } }, [state, view]) const handleStart = async () => { await start() } const handleEnd = async () => { await stop() setView('debrief') } const handleRestart = () => { setView('start') setAcknowledged(false) } return (

Praxis

Customer Service role-play — v0.1

{view === 'start' && (

Angry customer requesting refund on a damaged product

You are a customer service agent. An angry customer (Jordan) is demanding a refund for a cracked product. Handle the conversation. You'll receive a coaching debrief at the end.

{error &&
{error}
}
)} {view === 'live' && (
{state} {latency && ( {latency.label}:{' '} {latency.e2eMs !== null ? `${latency.e2eMs.toFixed(0)} ms` : '—'} )}

Live transcript

{transcripts.length === 0 ? (

Speak to the AI customer…

) : (
    {transcripts.map((t, i) => (
  • {t.role === 'user' ? 'You' : 'AI'} {t.text}
  • ))}
)}
{error &&
{error}
}
)} {view === 'debrief' && (

Session debrief

Your coaching debrief would appear here, generated from your turns + the branch outcome. In a live run (with API keys), the debrief is spoken in the same voice as the role-play.

{latency && (

Latency summary

{latency.label}:{' '} {latency.e2eMs !== null ? `${latency.e2eMs.toFixed(0)} ms` : '—'} {latency.e2eMs !== null && ( {' '}(budget 600ms — {latency.e2eMs <= 600 ? 'within' : 'over'}) )}

)} {transcripts.length > 0 && (

Turns this session

    {transcripts.map((t, i) => (
  • {t.role === 'user' ? 'You' : 'AI'} {t.text}
  • ))}
)}
)}
) } export default App