| import { useState, useRef, useEffect } from 'react'; |
| import { Mic, MicOff } from 'lucide-react'; |
|
|
| const ChatVoiceButton = ({ onInput }) => { |
| const [isListening, setIsListening] = useState(false); |
| const recognitionRef = useRef(null); |
| const silenceTimerRef = useRef(null); |
|
|
| const stopSilenceTimer = () => { |
| if (silenceTimerRef.current) { |
| clearTimeout(silenceTimerRef.current); |
| silenceTimerRef.current = null; |
| } |
| }; |
|
|
| const startSilenceTimer = () => { |
| stopSilenceTimer(); |
| silenceTimerRef.current = setTimeout(() => { |
| console.log("Chat Silence timeout. Stopping."); |
| if (recognitionRef.current) recognitionRef.current.stop(); |
| }, 6000); |
| }; |
|
|
| const onInputRef = useRef(onInput); |
|
|
| useEffect(() => { |
| onInputRef.current = onInput; |
| }, [onInput]); |
|
|
| useEffect(() => { |
| if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) { |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; |
| recognitionRef.current = new SpeechRecognition(); |
| recognitionRef.current.continuous = true; |
| recognitionRef.current.interimResults = true; |
| recognitionRef.current.lang = 'en-US'; |
|
|
| recognitionRef.current.onstart = () => { |
| setIsListening(true); |
| startSilenceTimer(); |
| }; |
|
|
| recognitionRef.current.onend = () => { |
| setIsListening(false); |
| stopSilenceTimer(); |
| }; |
|
|
| recognitionRef.current.onerror = (event) => { |
| console.error("Speech recognition error", event.error); |
| setIsListening(false); |
| stopSilenceTimer(); |
| }; |
|
|
| recognitionRef.current.onresult = (event) => { |
| startSilenceTimer(); |
| let finalTranscript = ''; |
| let interimTranscript = ''; |
|
|
| for (let i = event.resultIndex; i < event.results.length; ++i) { |
| if (event.results[i].isFinal) { |
| finalTranscript += event.results[i][0].transcript; |
| } else { |
| interimTranscript += event.results[i][0].transcript; |
| } |
| } |
|
|
| if (onInputRef.current) { |
| onInputRef.current(finalTranscript || interimTranscript, event.results[event.results.length - 1].isFinal); |
| } |
| }; |
| } |
|
|
| return () => { |
| if (recognitionRef.current) recognitionRef.current.stop(); |
| stopSilenceTimer(); |
| }; |
| }, []); |
|
|
| const toggleListening = () => { |
| if (!recognitionRef.current) { |
| alert("Voice not supported in this browser."); |
| return; |
| } |
|
|
| if (isListening) { |
| recognitionRef.current.stop(); |
| } else { |
| recognitionRef.current.start(); |
| } |
| }; |
|
|
| return ( |
| <button |
| onClick={toggleListening} |
| title="Voice Input" |
| style={{ |
| background: isListening ? 'rgba(239, 68, 68, 0.2)' : 'transparent', |
| color: isListening ? '#ef4444' : 'rgba(255,255,255,0.5)', |
| border: isListening ? '1px solid #ef4444' : 'none', |
| width: '36px', height: '36px', |
| borderRadius: '50%', |
| display: 'flex', alignItems: 'center', justifyContent: 'center', |
| cursor: 'pointer', |
| transition: 'all 0.2s', |
| marginRight: '0.5rem' |
| }} |
| > |
| {isListening ? <MicOff size={18} /> : <Mic size={18} />} |
| </button> |
| ); |
| }; |
|
|
| export default ChatVoiceButton; |
|
|