Update src/App.js
Browse files- src/App.js +24 -23
src/App.js
CHANGED
|
@@ -3,6 +3,9 @@ import { modelCompanies, allModels, findModelById } from './models/modelConfig';
|
|
| 3 |
import { HuggingFaceService } from './services/huggingfaceService';
|
| 4 |
import './App.css';
|
| 5 |
|
|
|
|
|
|
|
|
|
|
| 6 |
function App() {
|
| 7 |
const [messages, setMessages] = useState([]);
|
| 8 |
const [inputValue, setInputValue] = useState('');
|
|
@@ -21,14 +24,23 @@ function App() {
|
|
| 21 |
|
| 22 |
// Initialize Lucide icons
|
| 23 |
useEffect(() => {
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
}
|
| 30 |
-
};
|
| 31 |
-
initIcons();
|
| 32 |
}, []);
|
| 33 |
|
| 34 |
// Check for stored token
|
|
@@ -59,7 +71,7 @@ function App() {
|
|
| 59 |
// Auto-resize textarea
|
| 60 |
useEffect(() => {
|
| 61 |
if (textareaRef.current) {
|
| 62 |
-
textareaRef.current.style.height = '
|
| 63 |
textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 200) + 'px';
|
| 64 |
}
|
| 65 |
}, [inputValue]);
|
|
@@ -137,35 +149,31 @@ function App() {
|
|
| 137 |
content: msg.content
|
| 138 |
}));
|
| 139 |
|
| 140 |
-
await hfService.
|
| 141 |
chatMessages,
|
| 142 |
currentModelConfig,
|
| 143 |
(chunk) => {
|
| 144 |
-
// Update message content with streaming chunks
|
| 145 |
setMessages(prev => prev.map(msg =>
|
| 146 |
msg.id === assistantMessageId
|
| 147 |
-
? { ...msg, content: msg.content + chunk }
|
| 148 |
: msg
|
| 149 |
));
|
| 150 |
},
|
| 151 |
() => {
|
| 152 |
-
// Streaming completed
|
| 153 |
setIsLoading(false);
|
| 154 |
currentMessageRef.current = null;
|
| 155 |
},
|
| 156 |
(errorMsg) => {
|
| 157 |
-
// Error handling
|
| 158 |
setError(`Model error: ${errorMsg}`);
|
| 159 |
setIsLoading(false);
|
| 160 |
currentMessageRef.current = null;
|
| 161 |
-
|
| 162 |
-
// Remove empty assistant message on error
|
| 163 |
setMessages(prev => prev.filter(msg => msg.id !== assistantMessageId));
|
| 164 |
}
|
| 165 |
);
|
| 166 |
|
| 167 |
} catch (err) {
|
| 168 |
-
|
|
|
|
| 169 |
setIsLoading(false);
|
| 170 |
currentMessageRef.current = null;
|
| 171 |
setMessages(prev => prev.filter(msg => msg.id !== assistantMessageId));
|
|
@@ -202,14 +210,12 @@ function App() {
|
|
| 202 |
<p className="auth-description">
|
| 203 |
Enter your Hugging Face token to start chatting with AI models
|
| 204 |
</p>
|
| 205 |
-
|
| 206 |
{error && (
|
| 207 |
<div className="error-message">
|
| 208 |
<i data-lucide="alert-circle"></i>
|
| 209 |
{error}
|
| 210 |
</div>
|
| 211 |
)}
|
| 212 |
-
|
| 213 |
<div className="auth-input">
|
| 214 |
<input
|
| 215 |
type="password"
|
|
@@ -220,14 +226,12 @@ function App() {
|
|
| 220 |
onKeyPress={(e) => e.key === 'Enter' && handleTokenSubmit()}
|
| 221 |
/>
|
| 222 |
</div>
|
| 223 |
-
|
| 224 |
<div className="auth-actions">
|
| 225 |
<button className="btn primary" onClick={handleTokenSubmit}>
|
| 226 |
<i data-lucide="key"></i>
|
| 227 |
Start Chatting
|
| 228 |
</button>
|
| 229 |
</div>
|
| 230 |
-
|
| 231 |
<div className="token-info">
|
| 232 |
<h4>How to get your Hugging Face token:</h4>
|
| 233 |
<ol>
|
|
@@ -288,7 +292,6 @@ function App() {
|
|
| 288 |
</div>
|
| 289 |
</div>
|
| 290 |
)}
|
| 291 |
-
|
| 292 |
{messages.map((message) => (
|
| 293 |
<div key={message.id} className={`message ${message.role}`}>
|
| 294 |
<div className="message-content">
|
|
@@ -296,7 +299,6 @@ function App() {
|
|
| 296 |
</div>
|
| 297 |
</div>
|
| 298 |
))}
|
| 299 |
-
|
| 300 |
{isLoading && !currentMessageRef.current && (
|
| 301 |
<div className="message assistant">
|
| 302 |
<div className="typing-indicator">
|
|
@@ -331,7 +333,6 @@ function App() {
|
|
| 331 |
<span style={{ flex: 1, textAlign: 'left' }}>{currentModel?.name}</span>
|
| 332 |
<i data-lucide="chevron-down"></i>
|
| 333 |
</button>
|
| 334 |
-
|
| 335 |
{showModelDropdown && (
|
| 336 |
<div className="dropdown-content">
|
| 337 |
{groupedModels.map((company) => (
|
|
|
|
| 3 |
import { HuggingFaceService } from './services/huggingfaceService';
|
| 4 |
import './App.css';
|
| 5 |
|
| 6 |
+
// Import Lucide icons
|
| 7 |
+
import { createIcons, Brain, Key, Sun, Moon, X, ChevronDown, Cpu, BrainCircuit, Atom, Check, Send, AlertCircle, Sparkles } from 'lucide-react';
|
| 8 |
+
|
| 9 |
function App() {
|
| 10 |
const [messages, setMessages] = useState([]);
|
| 11 |
const [inputValue, setInputValue] = useState('');
|
|
|
|
| 24 |
|
| 25 |
// Initialize Lucide icons
|
| 26 |
useEffect(() => {
|
| 27 |
+
createIcons({
|
| 28 |
+
icons: {
|
| 29 |
+
Brain,
|
| 30 |
+
Key,
|
| 31 |
+
Sun,
|
| 32 |
+
Moon,
|
| 33 |
+
X,
|
| 34 |
+
ChevronDown,
|
| 35 |
+
Cpu,
|
| 36 |
+
BrainCircuit,
|
| 37 |
+
Atom,
|
| 38 |
+
Check,
|
| 39 |
+
Send,
|
| 40 |
+
AlertCircle,
|
| 41 |
+
Sparkles
|
| 42 |
}
|
| 43 |
+
});
|
|
|
|
| 44 |
}, []);
|
| 45 |
|
| 46 |
// Check for stored token
|
|
|
|
| 71 |
// Auto-resize textarea
|
| 72 |
useEffect(() => {
|
| 73 |
if (textareaRef.current) {
|
| 74 |
+
textareaRef.current.style.height = 'auto';
|
| 75 |
textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 200) + 'px';
|
| 76 |
}
|
| 77 |
}, [inputValue]);
|
|
|
|
| 149 |
content: msg.content
|
| 150 |
}));
|
| 151 |
|
| 152 |
+
await hfService.streamChatCompletion(
|
| 153 |
chatMessages,
|
| 154 |
currentModelConfig,
|
| 155 |
(chunk) => {
|
|
|
|
| 156 |
setMessages(prev => prev.map(msg =>
|
| 157 |
msg.id === assistantMessageId
|
| 158 |
+
? { ...msg, content: msg.content + (chunk || '') }
|
| 159 |
: msg
|
| 160 |
));
|
| 161 |
},
|
| 162 |
() => {
|
|
|
|
| 163 |
setIsLoading(false);
|
| 164 |
currentMessageRef.current = null;
|
| 165 |
},
|
| 166 |
(errorMsg) => {
|
|
|
|
| 167 |
setError(`Model error: ${errorMsg}`);
|
| 168 |
setIsLoading(false);
|
| 169 |
currentMessageRef.current = null;
|
|
|
|
|
|
|
| 170 |
setMessages(prev => prev.filter(msg => msg.id !== assistantMessageId));
|
| 171 |
}
|
| 172 |
);
|
| 173 |
|
| 174 |
} catch (err) {
|
| 175 |
+
console.error('Chat error:', err);
|
| 176 |
+
setError(`Failed to connect to AI model: ${err.message}`);
|
| 177 |
setIsLoading(false);
|
| 178 |
currentMessageRef.current = null;
|
| 179 |
setMessages(prev => prev.filter(msg => msg.id !== assistantMessageId));
|
|
|
|
| 210 |
<p className="auth-description">
|
| 211 |
Enter your Hugging Face token to start chatting with AI models
|
| 212 |
</p>
|
|
|
|
| 213 |
{error && (
|
| 214 |
<div className="error-message">
|
| 215 |
<i data-lucide="alert-circle"></i>
|
| 216 |
{error}
|
| 217 |
</div>
|
| 218 |
)}
|
|
|
|
| 219 |
<div className="auth-input">
|
| 220 |
<input
|
| 221 |
type="password"
|
|
|
|
| 226 |
onKeyPress={(e) => e.key === 'Enter' && handleTokenSubmit()}
|
| 227 |
/>
|
| 228 |
</div>
|
|
|
|
| 229 |
<div className="auth-actions">
|
| 230 |
<button className="btn primary" onClick={handleTokenSubmit}>
|
| 231 |
<i data-lucide="key"></i>
|
| 232 |
Start Chatting
|
| 233 |
</button>
|
| 234 |
</div>
|
|
|
|
| 235 |
<div className="token-info">
|
| 236 |
<h4>How to get your Hugging Face token:</h4>
|
| 237 |
<ol>
|
|
|
|
| 292 |
</div>
|
| 293 |
</div>
|
| 294 |
)}
|
|
|
|
| 295 |
{messages.map((message) => (
|
| 296 |
<div key={message.id} className={`message ${message.role}`}>
|
| 297 |
<div className="message-content">
|
|
|
|
| 299 |
</div>
|
| 300 |
</div>
|
| 301 |
))}
|
|
|
|
| 302 |
{isLoading && !currentMessageRef.current && (
|
| 303 |
<div className="message assistant">
|
| 304 |
<div className="typing-indicator">
|
|
|
|
| 333 |
<span style={{ flex: 1, textAlign: 'left' }}>{currentModel?.name}</span>
|
| 334 |
<i data-lucide="chevron-down"></i>
|
| 335 |
</button>
|
|
|
|
| 336 |
{showModelDropdown && (
|
| 337 |
<div className="dropdown-content">
|
| 338 |
{groupedModels.map((company) => (
|