import { useRef, useState } from 'react' import { Box, Button, Card, Chip, CircularProgress, IconButton, TextField, Typography } from '@mui/material' import { Mic, MicOff, Sparkles, X } from 'lucide-react' interface Props { text: string onTextChange: (s: string) => void onAiSubmit: () => void isAnalyzing: boolean isAutoGen: boolean } const EXAMPLES = [ 'Büro 800–1000 m² Zürich-West, ab Sept. 2025, max. CHF 45/m², ÖV-Anbindung', 'Retail-Fläche 200–400 m² Bern Innenstadt, Erdgeschoss, max. CHF 150/m², sofort', 'Lagerhalle 2000–3000 m² Basel, Rampe, 12 m Deckenhöhe, max. CHF 15/m²', ] const isSpeechSupported = typeof window !== 'undefined' && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) export function VoiceNeedInput({ text, onTextChange, onAiSubmit, isAnalyzing, isAutoGen }: Props) { const [isRecording, setIsRecording] = useState(false) const [interimText, setInterimText] = useState('') const recognitionRef = useRef(null) const accumulatedRef = useRef('') function startRecording() { const SpeechAPI = (window as any).SpeechRecognition ?? (window as any).webkitSpeechRecognition if (!SpeechAPI) return accumulatedRef.current = text const rec = new SpeechAPI() rec.lang = 'de-DE' rec.continuous = true rec.interimResults = true rec.onresult = (e: any) => { let finalPart = '' let interimPart = '' for (let i = e.resultIndex; i < e.results.length; i++) { const t = e.results[i][0].transcript if (e.results[i].isFinal) finalPart += t else interimPart += t } if (finalPart) { accumulatedRef.current = (accumulatedRef.current + ' ' + finalPart).trim() onTextChange(accumulatedRef.current) } setInterimText(interimPart) } rec.onend = () => { setIsRecording(false) setInterimText('') if (accumulatedRef.current.length >= 15) onAiSubmit() } rec.onerror = () => { setIsRecording(false); setInterimText('') } rec.start() recognitionRef.current = rec setIsRecording(true) } function stopRecording() { recognitionRef.current?.stop() } // Show interim text inside the field while recording const displayValue = isRecording && interimText ? (text + (text ? ' ' : '') + interimText) : text return ( Bedarf beschreiben Schreiben oder sprechen — die KI extrahiert alle Kriterien automatisch {isRecording ? ( ) : isAnalyzing ? ( Analysiert… ) : isAutoGen && text ? ( ⚡ auto-synchronisiert ) : null} {/* Textarea + mic */} { setInterimText('') onTextChange(e.target.value) }} disabled={isRecording || isAnalyzing} slotProps={{ htmlInput: { maxLength: 2000 } }} sx={{ '& .MuiOutlinedInput-root': { pr: '52px', bgcolor: isAutoGen && !isRecording ? '#f0f7ff' : 'transparent', transition: 'background-color 0.2s', '& textarea': { color: isRecording && interimText ? '#64748b' : 'inherit' }, }, }} /> {isRecording ? ( ) : ( )} {/* Actions row */} {/* Example prompts */} {EXAMPLES.map((ex, i) => ( onTextChange(ex)} sx={{ fontSize: 10, height: 20 }} /> ))} {text && !isRecording && ( onTextChange('')} sx={{ color: '#94a3b8', p: 0.5 }}> )} ) }