/** * JARVIS — Chat Input Bar * Multi-mode input: text / image / voice / tools * Shows pending image preview, sends on Enter. */ import React, { useState, useRef, useCallback } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Send, Image, Mic, Wrench, X, Activity, MessageSquare, Paperclip, } from 'lucide-react' import clsx from 'clsx' import useAppStore from '../../stores/appStore' import toast from 'react-hot-toast' export default function ChatInput({ onToggleTrace, showTrace }) { const [text, setText] = useState('') const textareaRef = useRef(null) const { sendMessage, isLoading, inputMode, setInputMode, pendingImage, clearPendingImage } = useAppStore() const handleSend = useCallback(async () => { if (!text.trim() || isLoading) return const msg = text.trim() setText('') textareaRef.current?.focus() await sendMessage(msg) }, [text, isLoading, sendMessage]) const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSend() } } // Auto-resize textarea const handleInput = (e) => { setText(e.target.value) const ta = e.target ta.style.height = 'auto' ta.style.height = Math.min(ta.scrollHeight, 160) + 'px' } const modes = [ { id: 'text', icon: MessageSquare, label: 'Chat' }, { id: 'image', icon: Image, label: 'Vision' }, { id: 'voice', icon: Mic, label: 'Voice' }, { id: 'tools', icon: Wrench, label: 'Tools' }, ] return (
{pendingImage.file?.name || 'Image attached'}
{pendingImage.caption && ({pendingImage.caption}
)}JARVIS · Multi-Modal AI · Powered by Mistral + BLIP + Whisper