import React, { useState, useCallback } from 'react'; import { Scenario, UserSettings, AnalysisResult } from '../types'; import { GoogleGenAI } from '@google/genai'; import { SYSTEM_SUMMARY_ANALYSIS_PROMPT, CBT_ANALYSIS_SCHEMA } from '../constants'; import { useLiveSession } from '../hooks/useLiveSession'; import { AlertCircle, Settings2, X, BrainCircuit, Zap, Info } from 'lucide-react'; import { Button } from './ui/button'; // Sub-components import AudioVisualizer from './AudioVisualizer'; import TranscriptPanel from './TranscriptPanel'; import LiveInsightPanel from './LiveInsightPanel'; import SessionReport from './SessionReport'; import ConnectionState from './VoiceSession/ConnectionState'; import AnalyzingState from './VoiceSession/AnalyzingState'; import VoiceHeader from './VoiceSession/VoiceHeader'; import VoiceControls from './VoiceSession/VoiceControls'; import AudioDeviceSelector from './AudioDeviceSelector'; interface VoiceSessionProps { scenario: Scenario; onExit: () => void; voiceSettings: UserSettings; onUpdateSettings: (settings: UserSettings) => void; } const VoiceSession: React.FC = ({ scenario, onExit, voiceSettings, onUpdateSettings }) => { const [showTranscript, setShowTranscript] = useState(false); const [showDeviceSelector, setShowDeviceSelector] = useState(false); const [hasStarted, setHasStarted] = useState(false); const [finalReport, setFinalReport] = useState(null); const { status, setStatus, isMuted, toggleMute, audioLevel, aiAudioLevel, isAiSpeaking, isUserSpeaking, messages, liveInsight, isAnalyzingTurn, isSilenceDetected, sessionPromiseRef, startSession } = useLiveSession(scenario, voiceSettings); const activeLevel = isAiSpeaking ? aiAudioLevel : audioLevel; const handleStart = useCallback(() => { setHasStarted(true); startSession(); }, [startSession]); const handleFinishSession = useCallback(async () => { setStatus('analyzing'); if (sessionPromiseRef.current) { sessionPromiseRef.current.then(session => session.close()); } try { const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); const transcriptText = messages.map(m => `[${m.role.toUpperCase()}]: ${m.text}`).join('\n'); const summaryPrompt = `${SYSTEM_SUMMARY_ANALYSIS_PROMPT} SCENARIO CONTEXT: ${scenario.title} - ${scenario.description}`; const result = await ai.models.generateContent({ model: 'gemini-3-flash-preview', config: { systemInstruction: summaryPrompt, responseMimeType: 'application/json', responseSchema: CBT_ANALYSIS_SCHEMA, }, contents: [{ role: 'user', parts: [{ text: `SESSION TRANSCRIPT:\n${transcriptText}` }] }] }); if (result.text) { setFinalReport(JSON.parse(result.text)); setStatus('finished'); } else { throw new Error("Analysis failed"); } } catch (err) { console.error("Post-session analysis failure", err); onExit(); } }, [messages, scenario, setStatus, sessionPromiseRef, onExit]); if (status === 'connecting') return ; if (status === 'analyzing') return ; if (status === 'finished' && finalReport) { return ; } return (
{/* Main Interaction Area */}
{/* Animated Background Aura */}
{!hasStarted && status === 'active' ? (

Neural Link Established

The simulation is ready. Press start to begin your interaction with {scenario.characterName}.

Greeting Hint: Say "Hello {scenario.characterName}" or just wait for them to speak first.

) : ( <> {isSilenceDetected && !isMuted && (

No Audio Detected

We can't hear you. Please check your microphone or select a different input device.

)} {/* Speaker Indicator Badge */}
{isAiSpeaking ? 'AI Speaking' : (isUserSpeaking ? 'You Speaking' : 'Waiting...')}

{isAiSpeaking ? `Vocalizing Response...` : (isMuted ? 'Microphone Muted' : 'Listening...')}

Clinical Fidelity Level: 99.8%

)}
{/* Device Selector Overlay */} {showDeviceSelector && (

Audio Input

{ onUpdateSettings({ ...voiceSettings, preferredAudioDeviceId: deviceId }); // Don't close immediately so user sees the checkmark setTimeout(() => setShowDeviceSelector(false), 300); }} />

Changing your input device will restart the current neural link.

)} setShowTranscript(prev => !prev)} /> {showTranscript && setShowTranscript(false)} />}
{/* Real-time Insights Module */}
); }; export default React.memo(VoiceSession);