InputField.tsx
1 import React, { useState, useEffect, KeyboardEvent } from 'react'; 2 import { useApp } from '../contexts/AppContext'; 3 import { mockAgents } from '../data/mockData'; 4 import './InputField.css'; 5 6 const FlameAnimation: React.FC = () => { 7 const [flames, setFlames] = useState('🔥🔥🔥🔥'); 8 9 useEffect(() => { 10 const interval = setInterval(() => { 11 const count = 2 + Math.floor(Math.random() * 7); 12 setFlames('🔥'.repeat(count)); 13 }, 100); 14 15 return () => clearInterval(interval); 16 }, []); 17 18 return <div className="flame-row">{flames}</div>; 19 }; 20 21 interface AutocompleteProps { 22 query: string; 23 onSelect: (agent: string) => void; 24 } 25 26 const AgentAutocomplete: React.FC<AutocompleteProps> = ({ query, onSelect }) => { 27 const prefix = query.toLowerCase(); 28 const matches = mockAgents.filter(agent => 29 prefix === '' || agent.name.toLowerCase().startsWith(prefix) 30 ).slice(0, 5); 31 32 if (matches.length === 0) return null; 33 34 return ( 35 <div className="autocomplete-dropdown"> 36 {matches.map(agent => ( 37 <div 38 key={agent.id} 39 className="autocomplete-item" 40 onClick={() => onSelect(agent.name)} 41 > 42 <span className="autocomplete-icon">{agent.icon}</span> 43 <span className="autocomplete-name">{agent.name}</span> 44 <span className="autocomplete-level" style={{ color: agent.color }}> 45 [{agent.level}] 46 </span> 47 </div> 48 ))} 49 </div> 50 ); 51 }; 52 53 export const InputField: React.FC = () => { 54 const { addMessage, setLoading, loading } = useApp(); 55 const [input, setInput] = useState(''); 56 const [showAutocomplete, setShowAutocomplete] = useState(false); 57 const [autocompleteQuery, setAutocompleteQuery] = useState(''); 58 59 useEffect(() => { 60 // Check for @ mention 61 const words = input.split(' '); 62 const lastWord = words[words.length - 1]; 63 64 if (lastWord.startsWith('@')) { 65 const query = lastWord.substring(1); 66 setAutocompleteQuery(query); 67 setShowAutocomplete(true); 68 } else { 69 setShowAutocomplete(false); 70 } 71 }, [input]); 72 73 const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => { 74 if (e.key === 'Enter' && !e.shiftKey) { 75 e.preventDefault(); 76 handleSend(); 77 } 78 79 if (e.key === 'Tab' && showAutocomplete) { 80 e.preventDefault(); 81 const words = input.split(' '); 82 const lastWord = words[words.length - 1]; 83 84 if (lastWord.startsWith('@')) { 85 const query = lastWord.substring(1).toLowerCase(); 86 const match = mockAgents.find(agent => 87 query === '' || agent.name.toLowerCase().startsWith(query) 88 ); 89 90 if (match) { 91 words[words.length - 1] = `@${match.name}`; 92 setInput(words.join(' ') + ' '); 93 setShowAutocomplete(false); 94 } 95 } 96 } 97 }; 98 99 const handleSend = () => { 100 if (input.trim() === '' || loading) return; 101 102 // Add user message 103 addMessage({ 104 role: 'user', 105 content: input, 106 timestamp: new Date() 107 }); 108 109 // Simulate AI response 110 setLoading(true); 111 setTimeout(() => { 112 addMessage({ 113 role: 'assistant', 114 content: `I've received your message: "${input}". This is a placeholder response from the Kamaji system. In a full implementation, this would be connected to an actual LLM provider.`, 115 agentName: 'Kamaji', 116 timestamp: new Date() 117 }); 118 setLoading(false); 119 }, 1500); 120 121 setInput(''); 122 setShowAutocomplete(false); 123 }; 124 125 const handleAutocompleteSelect = (agentName: string) => { 126 const words = input.split(' '); 127 words[words.length - 1] = `@${agentName}`; 128 setInput(words.join(' ') + ' '); 129 setShowAutocomplete(false); 130 }; 131 132 return ( 133 <div className="input-field-container"> 134 {showAutocomplete && ( 135 <AgentAutocomplete 136 query={autocompleteQuery} 137 onSelect={handleAutocompleteSelect} 138 /> 139 )} 140 141 <div className="input-field"> 142 <span className="input-flame">🔥</span> 143 <textarea 144 value={input} 145 onChange={(e) => setInput(e.target.value)} 146 onKeyDown={handleKeyDown} 147 placeholder="Speak to the furnace spirit... (type @ for agents)" 148 className="input-textarea" 149 disabled={loading} 150 rows={1} 151 /> 152 </div> 153 154 <FlameAnimation /> 155 </div> 156 ); 157 };