92 lines
2.6 KiB
TypeScript
92 lines
2.6 KiB
TypeScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import { Mic, MicOff, Loader2 } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
|
|
interface VoiceInputProps {
|
|
onTranscribed: (text: string) => void;
|
|
className?: string;
|
|
}
|
|
|
|
export default function VoiceInput({ onTranscribed, className = "" }: VoiceInputProps) {
|
|
const [isListening, setIsListening] = useState(false);
|
|
const [isSupported, setIsSupported] = useState(true);
|
|
const recognitionRef = useRef<any>(null);
|
|
|
|
useEffect(() => {
|
|
const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
|
|
|
|
if (!SpeechRecognition) {
|
|
setIsSupported(false);
|
|
return;
|
|
}
|
|
|
|
const recognition = new SpeechRecognition();
|
|
recognition.lang = 'de-DE';
|
|
recognition.interimResults = false;
|
|
recognition.continuous = false;
|
|
|
|
recognition.onresult = (event: any) => {
|
|
const text = event.results[0][0].transcript;
|
|
onTranscribed(text);
|
|
setIsListening(false);
|
|
};
|
|
|
|
recognition.onerror = (event: any) => {
|
|
console.error('Speech recognition error:', event.error);
|
|
toast.error(`Spracherkennung Fehler: ${event.error}`);
|
|
setIsListening(false);
|
|
};
|
|
|
|
recognition.onend = () => {
|
|
setIsListening(false);
|
|
};
|
|
|
|
recognitionRef.current = recognition;
|
|
}, [onTranscribed]);
|
|
|
|
const toggleListening = () => {
|
|
if (!isSupported) {
|
|
toast.error("Spracherkennung wird von diesem Browser nicht unterstützt.");
|
|
return;
|
|
}
|
|
|
|
if (isListening) {
|
|
recognitionRef.current?.stop();
|
|
setIsListening(false);
|
|
} else {
|
|
try {
|
|
recognitionRef.current?.start();
|
|
setIsListening(true);
|
|
} catch (e) {
|
|
console.error(e);
|
|
setIsListening(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
return (
|
|
<button
|
|
onClick={toggleListening}
|
|
disabled={!isSupported}
|
|
className={`
|
|
relative p-2 rounded-full transition-all duration-200
|
|
${isListening
|
|
? 'bg-red-500 text-white animate-pulse ring-4 ring-red-500/20'
|
|
: 'bg-slate-100 text-slate-600 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700'
|
|
}
|
|
${className}
|
|
`}
|
|
title={isListening ? "Stoppen" : "Spracheingabe starten"}
|
|
>
|
|
{isListening ? (
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
|
) : (
|
|
<Mic className="w-5 h-5" />
|
|
)}
|
|
|
|
{!isSupported && (
|
|
<div className="absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full border-2 border-white dark:border-slate-900" />
|
|
)}
|
|
</button>
|
|
);
|
|
} |