import React, { useState, useRef } from 'react'; import { Mic, Square, Send } from 'lucide-react'; import { apiService } from '../services/api'; interface VoiceInputProps { onProcessComplete?: () => void; } export function VoiceInput({ onProcessComplete }: VoiceInputProps) { const [isRecording, setIsRecording] = useState(false); const [textInput, setTextInput] = useState(''); const [processing, setProcessing] = useState(false); const [error, setError] = useState(null); const [result, setResult] = useState(null); const mediaRecorderRef = useRef(null); const audioChunksRef = useRef([]); const startRecording = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); const mediaRecorder = new MediaRecorder(stream); mediaRecorderRef.current = mediaRecorder; audioChunksRef.current = []; mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { audioChunksRef.current.push(event.data); } }; mediaRecorder.onstop = async () => { const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' }); await processAudio(audioBlob); // Stop all tracks stream.getTracks().forEach(track => track.stop()); }; mediaRecorder.start(); setIsRecording(true); setError(null); } catch (err) { console.error('Failed to start recording:', err); setError('无法访问麦克风'); } }; const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); setIsRecording(false); } }; const processAudio = async (audioBlob: Blob) => { setProcessing(true); setError(null); try { // Convert to supported format (mp3) const file = new File([audioBlob], 'recording.webm', { type: 'audio/webm' }); const response = await apiService.processInput(file); setResult(response); if (onProcessComplete) { onProcessComplete(); } } catch (err: any) { console.error('Failed to process audio:', err); setError(err.message || '处理失败'); } finally { setProcessing(false); } }; const processText = async () => { if (!textInput.trim()) return; setProcessing(true); setError(null); try { const response = await apiService.processInput(undefined, textInput); setResult(response); setTextInput(''); if (onProcessComplete) { onProcessComplete(); } } catch (err: any) { console.error('Failed to process text:', err); setError(err.message || '处理失败'); } finally { setProcessing(false); } }; return (
{/* Voice Recording */}
{isRecording && ( 录音中... )}
{/* Text Input */}
setTextInput(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && processText()} placeholder="或者输入文字..." disabled={processing || isRecording} className=" flex-1 px-4 py-3 rounded-2xl bg-white/80 backdrop-blur-sm border border-slate-200 focus:outline-none focus:ring-2 focus:ring-purple-300 disabled:opacity-50 disabled:cursor-not-allowed " />
{/* Processing Indicator */} {processing && (
处理中...
)} {/* Error Message */} {error && (
{error}
)} {/* Result Display */} {result && (

处理结果

{result.mood && (
情绪
{result.mood.type} (强度: {result.mood.intensity}/10)
{result.mood.keywords.length > 0 && (
{result.mood.keywords.map((kw: string, i: number) => ( {kw} ))}
)}
)} {result.inspirations.length > 0 && (
灵感
{result.inspirations.map((insp: any, i: number) => (
{insp.core_idea}
{insp.tags.map((tag: string, j: number) => ( {tag} ))}
))}
)} {result.todos.length > 0 && (
待办
{result.todos.map((todo: any, i: number) => (
{todo.task} {todo.time && ({todo.time})}
))}
)}
)}
); }