'use client' import { useEffect, useRef, useState, useCallback } from 'react' import { VoiceAPI, VoiceMessage, VoiceTask } from '@/lib/voice/voice-api' import { VoiceIndicator } from './VoiceIndicator' interface VoiceCaptureProps { onTranscript?: (text: string, isFinal: boolean) => void onIntent?: (intent: string, parameters: Record) => void onResponse?: (text: string) => void onTaskCreated?: (task: VoiceTask) => void onError?: (error: Error) => void className?: string } /** * Voice capture component with microphone button * Handles WebSocket connection and audio streaming */ export function VoiceCapture({ onTranscript, onIntent, onResponse, onTaskCreated, onError, className = '', }: VoiceCaptureProps) { const voiceApiRef = useRef(null) const [isInitialized, setIsInitialized] = useState(false) const [isConnected, setIsConnected] = useState(false) const [isListening, setIsListening] = useState(false) const [status, setStatus] = useState('idle') const [audioLevel, setAudioLevel] = useState(0) const [transcript, setTranscript] = useState('') const [error, setError] = useState(null) // Initialize voice API useEffect(() => { const init = async () => { try { const api = new VoiceAPI() await api.initialize() voiceApiRef.current = api // Set up event handlers api.setOnMessage(handleMessage) api.setOnError(handleError) api.setOnStatusChange(handleStatusChange) setIsInitialized(true) } catch (e) { console.error('Failed to initialize voice API:', e) setError('Sprachdienst konnte nicht initialisiert werden') } } init() return () => { voiceApiRef.current?.disconnect() } }, []) const handleMessage = useCallback( (message: VoiceMessage) => { switch (message.type) { case 'transcript': setTranscript(message.text) onTranscript?.(message.text, message.final) break case 'intent': onIntent?.(message.intent, message.parameters) break case 'response': onResponse?.(message.text) break case 'task_created': onTaskCreated?.({ id: message.task_id, session_id: '', type: message.task_type, state: message.state, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), result_available: false, }) break case 'error': setError(message.message) onError?.(new Error(message.message)) break } }, [onTranscript, onIntent, onResponse, onTaskCreated, onError] ) const handleError = useCallback( (error: Error) => { setError(error.message) setIsListening(false) onError?.(error) }, [onError] ) const handleStatusChange = useCallback((newStatus: string) => { setStatus(newStatus) if (newStatus === 'connected') { setIsConnected(true) } else if (newStatus === 'disconnected') { setIsConnected(false) setIsListening(false) } else if (newStatus === 'listening') { setIsListening(true) } else if (newStatus === 'processing') { setIsListening(false) } }, []) const toggleListening = async () => { if (!voiceApiRef.current) return try { setError(null) if (isListening) { // Stop listening voiceApiRef.current.stopCapture() setIsListening(false) } else { // Start listening if (!isConnected) { await voiceApiRef.current.connect() } await voiceApiRef.current.startCapture() setIsListening(true) } } catch (e) { console.error('Failed to toggle listening:', e) setError('Mikrofon konnte nicht aktiviert werden') } } const interrupt = () => { voiceApiRef.current?.interrupt() } if (!isInitialized) { return (
Initialisiere...
) } return (
{/* Error display */} {error && (
{error}
)} {/* Main controls */}
{/* Microphone button */} {/* Status indicator */} {/* Interrupt button (when responding) */} {status === 'responding' && ( )}
{/* Transcript display */} {transcript && (

Erkannt:

{transcript}

)} {/* Instructions */}

{isListening ? 'Sprechen Sie jetzt... Klicken Sie erneut zum Beenden.' : 'Klicken Sie auf das Mikrofon und sprechen Sie Ihren Befehl.'}

) }