import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Send, Paperclip, FileText, X, Trash2, Download, Mic, MicOff, MessageCircle, ClipboardList, Loader2, Columns3, FileOutput } from 'lucide-react'; import FileUpload from './FileUpload'; const EnhancedChatInput = ({ onSendMessage, onFileUploaded, uploadedDocuments = [], isLoading, currentChatSessionId, authToken, placeholder = "Ask your advisors anything...", showProfileButtons = false, onOpenOnboarding, onOpenProfileForm, synthesizedMode = false, onToggleSynthesized, ensureSessionId, }) => { const [inputMessage, setInputMessage] = useState(''); const [showUpload, setShowUpload] = useState(false); const [showDocuments, setShowDocuments] = useState(false); const [isUploading, setIsUploading] = useState(false); const [isRecording, setIsRecording] = useState(false); const [isTranscribing, setIsTranscribing] = useState(false); const mediaRecorderRef = useRef(null); const audioChunksRef = useRef([]); const textareaRef = useRef(null); const uploadRef = useRef(null); const uploadBtnRef = useRef(null); const sendForTranscription = useCallback(async (blob) => { if (!blob || blob.size < 100) { console.warn('STT: blob too small, skipping', blob?.size); return; } setIsTranscribing(true); try { const form = new FormData(); form.append('audio', blob, 'recording.webm'); const token = authToken || localStorage.getItem('authToken'); const resp = await fetch(`${process.env.REACT_APP_API_URL}/voice/transcribe`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: form, }); if (resp.ok) { const data = await resp.json(); const text = data?.text?.trim(); if (text) { setInputMessage(prev => prev ? `${prev} ${text}` : text); } } else { console.error('STT response not ok:', resp.status, await resp.text().catch(() => '')); } } catch (err) { console.error('Transcription failed:', err); } finally { setIsTranscribing(false); } }, [authToken]); const toggleRecording = useCallback(async () => { if (isRecording) { mediaRecorderRef.current?.stop(); setIsRecording(false); return; } try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); // Pick a supported mimeType const mimeType = ['audio/webm;codecs=opus', 'audio/webm', 'audio/ogg;codecs=opus', ''] .find(mt => mt === '' || MediaRecorder.isTypeSupported(mt)); const options = mimeType ? { mimeType } : undefined; const mediaRecorder = new MediaRecorder(stream, options); audioChunksRef.current = []; mediaRecorder.ondataavailable = (e) => { if (e.data && e.data.size > 0) audioChunksRef.current.push(e.data); }; mediaRecorder.onstop = () => { stream.getTracks().forEach(t => t.stop()); const blobType = mediaRecorder.mimeType || 'audio/webm'; const blob = new Blob(audioChunksRef.current, { type: blobType }); sendForTranscription(blob); }; mediaRecorderRef.current = mediaRecorder; // Request data every 500ms so chunks are available when stop() fires mediaRecorder.start(500); setIsRecording(true); } catch (err) { console.error('Microphone access error:', err); } }, [isRecording, sendForTranscription]); const handleSend = () => { if (!inputMessage.trim() || isLoading || isUploading) return; onSendMessage(inputMessage); setInputMessage(''); if (textareaRef.current) { textareaRef.current.style.height = 'auto'; } }; const handleKeyPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } }; const handleFileUploaded = (file, response) => { setIsUploading(false); setShowUpload(false); if (onFileUploaded) { onFileUploaded(file, response); } }; const handleUploadStart = () => { setIsUploading(true); }; const toggleUpload = () => { if (!isUploading) { setShowUpload(!showUpload); setShowDocuments(false); // Close documents panel when opening upload } }; const toggleDocuments = () => { setShowDocuments(!showDocuments); setShowUpload(false); // Close upload panel when opening documents }; // Auto-resize textarea useEffect(() => { if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px'; } }, [inputMessage]); // Close upload panel when clicking outside useEffect(() => { if (!showUpload) return; const handleClickOutside = (e) => { if ( uploadRef.current && !uploadRef.current.contains(e.target) && uploadBtnRef.current && !uploadBtnRef.current.contains(e.target) ) { setShowUpload(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [showUpload]); const formatFileSize = (bytes) => { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }; const getFileIcon = (type) => { if (type.includes('pdf')) return '📄'; if (type.includes('word') || type.includes('document')) return '📝'; if (type.includes('text')) return '📃'; return '📄'; }; const formatUploadTime = (date) => { return new Date(date).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; const isDisabled = isLoading || isUploading; const canSend = inputMessage.trim() && !isDisabled; return (
No documents uploaded yet
Upload documents to reference them in your conversations