6ammedical / components /ChatInput.tsx
vesakkivignesh's picture
hello
25f6ee4
import React, { useState, useEffect, useRef } from 'react';
interface ChatInputProps {
onSendMessage: (text: string) => void;
isLoading: boolean;
}
// Check for SpeechRecognition API
// FIX: Cast window to `any` to access non-standard SpeechRecognition APIs without TypeScript errors.
const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
const recognition = SpeechRecognition ? new SpeechRecognition() : null;
if (recognition) {
recognition.continuous = false;
recognition.lang = 'en-US';
recognition.interimResults = false;
}
const ChatInput: React.FC<ChatInputProps> = ({ onSendMessage, isLoading }) => {
const [text, setText] = useState('');
const [isListening, setIsListening] = useState(false);
const recognitionRef = useRef(recognition);
useEffect(() => {
const currentRecognition = recognitionRef.current;
if (!currentRecognition) return;
// FIX: Use `any` for the event type as SpeechRecognitionEvent is not defined in this project's TS config.
const handleResult = (event: any) => {
const transcript = event.results[0][0].transcript;
setText(transcript);
handleSend(); // Auto-send after transcription
};
const handleEnd = () => {
setIsListening(false);
};
// FIX: Use `any` for the event type as SpeechRecognitionError (or the correct SpeechRecognitionErrorEvent) is not defined.
const handleError = (event: any) => {
console.error('Speech recognition error', event.error);
setIsListening(false);
}
currentRecognition.addEventListener('result', handleResult);
currentRecognition.addEventListener('end', handleEnd);
currentRecognition.addEventListener('error', handleError);
return () => {
currentRecognition.removeEventListener('result', handleResult);
currentRecognition.removeEventListener('end', handleEnd);
currentRecognition.removeEventListener('error', handleError);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleSend = () => {
if (text.trim() && !isLoading) {
onSendMessage(text);
setText('');
}
};
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleSend();
}
};
const toggleListening = () => {
if (!recognitionRef.current) return;
if (isListening) {
recognitionRef.current.stop();
} else {
recognitionRef.current.start();
}
setIsListening(!isListening);
};
return (
<footer className="bg-white border-t border-gray-200 p-4">
<div className="max-w-4xl mx-auto flex items-center space-x-3">
{recognition && (
<button
onClick={toggleListening}
disabled={isLoading}
className={`flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center transition-colors duration-200 ${
isListening ? 'bg-red-500 text-white' : 'bg-gray-200 text-gray-600 hover:bg-gray-300'
} disabled:opacity-50 disabled:cursor-not-allowed`}
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
</svg>
</button>
)}
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Type your medical question..."
className="w-full p-3 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-500 transition-shadow"
disabled={isLoading}
/>
<button
onClick={handleSend}
disabled={isLoading || !text.trim()}
className="bg-blue-500 text-white rounded-full p-3 flex-shrink-0 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:bg-blue-300 disabled:cursor-not-allowed transition-colors"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</button>
</div>
</footer>
);
};
export default ChatInput;