import { useState, useRef, useEffect, useCallback } from 'react' export default function InputBar({ isLoading, hasDocument, onSendText, onFileSelect, onTranscript }) { const [text, setText] = useState('') const [file, setFile] = useState(null) const [isRecording, setIsRecording] = useState(false) const [isTranscribing, setIsTranscribing] = useState(false) const textareaRef = useRef(null) const fileInputRef = useRef(null) const mediaRecRef = useRef(null) const audioChunksRef = useRef([]) /* ── Auto-resize textarea ──────────────────────────────── */ useEffect(() => { const ta = textareaRef.current if (!ta) return ta.style.height = 'auto' ta.style.height = Math.min(ta.scrollHeight, 180) + 'px' }, [text]) /* ── Send logic ────────────────────────────────────────── */ const handleSend = useCallback(async () => { if (isLoading || isTranscribing) return if (file) { await onFileSelect(file) setFile(null) setText('') return } const msg = text.trim() if (!msg) return setText('') if (textareaRef.current) { textareaRef.current.style.height = 'auto' } await onSendText(msg) }, [isLoading, isTranscribing, file, text, onFileSelect, onSendText]) /* ── Keyboard: Enter to send, Shift+Enter for newline ───── */ const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSend() } } /* ── File picker ────────────────────────────────────────── */ const handleFileChange = (e) => { const f = e.target.files?.[0] if (f) { setFile(f) setText('') } e.target.value = '' } const removeFile = () => setFile(null) /* ── Voice recording ────────────────────────────────────── */ const startRecording = useCallback(async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) audioChunksRef.current = [] const mimeType = MediaRecorder.isTypeSupported('audio/webm;codecs=opus') ? 'audio/webm;codecs=opus' : 'audio/webm' const recorder = new MediaRecorder(stream, { mimeType }) mediaRecRef.current = recorder recorder.ondataavailable = (e) => { if (e.data.size > 0) audioChunksRef.current.push(e.data) } recorder.onstop = async () => { stream.getTracks().forEach(t => t.stop()) const blob = new Blob(audioChunksRef.current, { type: mimeType }) if (blob.size < 500) return // too short / silence setIsTranscribing(true) try { const transcript = await onTranscript(blob) if (transcript) { setText(prev => prev ? prev + ' ' + transcript : transcript) textareaRef.current?.focus() } } finally { setIsTranscribing(false) } } recorder.start(200) // collect chunks every 200ms setIsRecording(true) } catch (err) { alert('মাইক্রোফোন অ্যাক্সেস করতে পারা যাচ্ছে না। অনুগ্রহ করে ব্রাউজার অনুমতি দিন।') } }, [onTranscript]) const stopRecording = useCallback(() => { if (mediaRecRef.current && isRecording) { mediaRecRef.current.stop() setIsRecording(false) } }, [isRecording]) const toggleRecording = () => { if (isRecording) stopRecording() else startRecording() } /* ── Can send? ──────────────────────────────────────────── */ const canSend = !isLoading && !isTranscribing && (file || text.trim().length > 0) /* ── Placeholder ────────────────────────────────────────── */ const placeholder = isTranscribing ? 'কণ্ঠস্বর লেখায় রূপান্তরিত হচ্ছে...' : isRecording ? '🔴 রেকর্ড হচ্ছে... থামাতে মাইক্রোফোনে ক্লিক করুন' : hasDocument ? 'নথি সম্পর্কে প্রশ্ন করুন...' : 'বাংলায় টাইপ করুন বা PDF/কণ্ঠস্বর দিন...' return (
{hasDocument ? 'নথির প্রসঙ্গে উত্তর দেওয়া হবে' : 'PDF আপলোড করুন অথবা সরাসরি বাংলায় প্রশ্ন করুন'}