Felguk commited on
Commit
4eee945
·
verified ·
1 Parent(s): c6231dc

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +681 -19
index.html CHANGED
@@ -1,19 +1,681 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Open-Ministral Chat - OpenRouter</title>
7
+ <!-- Import Phosphor Icons -->
8
+ <script src="https://unpkg.com/@phosphor-icons/web"></script>
9
+ <!-- Import Marked for Markdown Parsing -->
10
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
11
+ <!-- Import Highlight.js for Code Syntax Highlighting -->
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
14
+
15
+ <style>
16
+ :root {
17
+ --bg-color: #0f172a;
18
+ --chat-bg: #1e293b;
19
+ --primary: #6366f1;
20
+ --primary-hover: #4f46e5;
21
+ --text-color: #f8fafc;
22
+ --text-muted: #94a3b8;
23
+ --user-msg-bg: #4f46e5;
24
+ --ai-msg-bg: #334155;
25
+ --border-color: #334155;
26
+ --glass-bg: rgba(30, 41, 59, 0.7);
27
+ --glass-border: rgba(255, 255, 255, 0.1);
28
+ --blur: 12px;
29
+ --font-family: 'Inter', system-ui, -apple-system, sans-serif;
30
+ }
31
+
32
+ * {
33
+ box-sizing: border-box;
34
+ margin: 0;
35
+ padding: 0;
36
+ }
37
+
38
+ body {
39
+ font-family: var(--font-family);
40
+ background-color: var(--bg-color);
41
+ background-image:
42
+ radial-gradient(at 0% 0%, rgba(99, 102, 241, 0.15) 0px, transparent 50%),
43
+ radial-gradient(at 100% 100%, rgba(168, 85, 247, 0.15) 0px, transparent 50%);
44
+ color: var(--text-color);
45
+ height: 100vh;
46
+ display: flex;
47
+ flex-direction: column;
48
+ overflow: hidden;
49
+ }
50
+
51
+ /* Header */
52
+ header {
53
+ padding: 1rem 1.5rem;
54
+ display: flex;
55
+ justify-content: space-between;
56
+ align-items: center;
57
+ background: var(--glass-bg);
58
+ backdrop-filter: blur(var(--blur));
59
+ border-bottom: 1px solid var(--glass-border);
60
+ z-index: 10;
61
+ }
62
+
63
+ .brand {
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 0.75rem;
67
+ font-weight: 700;
68
+ font-size: 1.25rem;
69
+ letter-spacing: -0.025em;
70
+ }
71
+
72
+ .brand i {
73
+ color: var(--primary);
74
+ font-size: 1.5rem;
75
+ }
76
+
77
+ .header-links a {
78
+ color: var(--text-muted);
79
+ text-decoration: none;
80
+ font-size: 0.875rem;
81
+ transition: color 0.2s;
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 0.5rem;
85
+ }
86
+
87
+ .header-links a:hover {
88
+ color: var(--primary);
89
+ }
90
+
91
+ /* Main Layout */
92
+ main {
93
+ flex: 1;
94
+ display: flex;
95
+ flex-direction: column;
96
+ max-width: 900px;
97
+ width: 100%;
98
+ margin: 0 auto;
99
+ position: relative;
100
+ }
101
+
102
+ /* API Key Modal */
103
+ #api-modal {
104
+ position: fixed;
105
+ top: 0;
106
+ left: 0;
107
+ width: 100%;
108
+ height: 100%;
109
+ background: rgba(15, 23, 42, 0.8);
110
+ backdrop-filter: blur(4px);
111
+ display: flex;
112
+ justify-content: center;
113
+ align-items: center;
114
+ z-index: 100;
115
+ opacity: 1;
116
+ transition: opacity 0.3s ease;
117
+ }
118
+
119
+ #api-modal.hidden {
120
+ opacity: 0;
121
+ pointer-events: none;
122
+ }
123
+
124
+ .modal-content {
125
+ background: var(--chat-bg);
126
+ padding: 2rem;
127
+ border-radius: 1rem;
128
+ border: 1px solid var(--border-color);
129
+ width: 90%;
130
+ max-width: 400px;
131
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
132
+ text-align: center;
133
+ }
134
+
135
+ .modal-content h2 {
136
+ margin-bottom: 1rem;
137
+ font-size: 1.5rem;
138
+ }
139
+
140
+ .modal-content p {
141
+ color: var(--text-muted);
142
+ margin-bottom: 1.5rem;
143
+ font-size: 0.9rem;
144
+ line-height: 1.5;
145
+ }
146
+
147
+ .input-group {
148
+ position: relative;
149
+ margin-bottom: 1.5rem;
150
+ }
151
+
152
+ .input-group input {
153
+ width: 100%;
154
+ padding: 0.75rem 1rem;
155
+ background: var(--bg-color);
156
+ border: 1px solid var(--border-color);
157
+ border-radius: 0.5rem;
158
+ color: var(--text-color);
159
+ font-size: 1rem;
160
+ transition: border-color 0.2s;
161
+ }
162
+
163
+ .input-group input:focus {
164
+ outline: none;
165
+ border-color: var(--primary);
166
+ }
167
+
168
+ .btn {
169
+ background: var(--primary);
170
+ color: white;
171
+ border: none;
172
+ padding: 0.75rem 1.5rem;
173
+ border-radius: 0.5rem;
174
+ font-weight: 600;
175
+ cursor: pointer;
176
+ transition: background 0.2s;
177
+ width: 100%;
178
+ }
179
+
180
+ .btn:hover {
181
+ background: var(--primary-hover);
182
+ }
183
+
184
+ /* Chat Container */
185
+ #chat-container {
186
+ flex: 1;
187
+ overflow-y: auto;
188
+ padding: 2rem 1rem;
189
+ display: flex;
190
+ flex-direction: column;
191
+ gap: 1.5rem;
192
+ scroll-behavior: smooth;
193
+ }
194
+
195
+ /* Scrollbar Styling */
196
+ #chat-container::-webkit-scrollbar {
197
+ width: 8px;
198
+ }
199
+ #chat-container::-webkit-scrollbar-track {
200
+ background: transparent;
201
+ }
202
+ #chat-container::-webkit-scrollbar-thumb {
203
+ background: var(--border-color);
204
+ border-radius: 4px;
205
+ }
206
+
207
+ /* Messages */
208
+ .message {
209
+ display: flex;
210
+ gap: 1rem;
211
+ max-width: 85%;
212
+ animation: fadeIn 0.3s ease-out;
213
+ }
214
+
215
+ .message.user {
216
+ align-self: flex-end;
217
+ flex-direction: row-reverse;
218
+ }
219
+
220
+ .message.ai {
221
+ align-self: flex-start;
222
+ }
223
+
224
+ .avatar {
225
+ width: 2.5rem;
226
+ height: 2.5rem;
227
+ border-radius: 50%;
228
+ display: flex;
229
+ align-items: center;
230
+ justify-content: center;
231
+ flex-shrink: 0;
232
+ font-size: 1.25rem;
233
+ }
234
+
235
+ .user .avatar {
236
+ background: linear-gradient(135deg, #6366f1, #8b5cf6);
237
+ color: white;
238
+ }
239
+
240
+ .ai .avatar {
241
+ background: linear-gradient(135deg, #10b981, #059669);
242
+ color: white;
243
+ }
244
+
245
+ .msg-content {
246
+ padding: 1rem 1.25rem;
247
+ border-radius: 1rem;
248
+ line-height: 1.6;
249
+ font-size: 0.95rem;
250
+ position: relative;
251
+ word-wrap: break-word;
252
+ overflow-wrap: break-word;
253
+ }
254
+
255
+ .user .msg-content {
256
+ background: var(--user-msg-bg);
257
+ color: white;
258
+ border-bottom-right-radius: 0.25rem;
259
+ }
260
+
261
+ .ai .msg-content {
262
+ background: var(--ai-msg-bg);
263
+ color: var(--text-color);
264
+ border-bottom-left-radius: 0.25rem;
265
+ border: 1px solid var(--border-color);
266
+ }
267
+
268
+ /* Markdown Styles inside messages */
269
+ .msg-content p { margin-bottom: 0.75rem; }
270
+ .msg-content p:last-child { margin-bottom: 0; }
271
+ .msg-content pre {
272
+ background: #0d1117;
273
+ padding: 1rem;
274
+ border-radius: 0.5rem;
275
+ overflow-x: auto;
276
+ margin: 0.75rem 0;
277
+ border: 1px solid rgba(255,255,255,0.1);
278
+ }
279
+ .msg-content code {
280
+ font-family: 'Fira Code', monospace;
281
+ font-size: 0.85em;
282
+ }
283
+ .msg-content :not(pre) > code {
284
+ background: rgba(255,255,255,0.1);
285
+ padding: 0.2em 0.4em;
286
+ border-radius: 0.3em;
287
+ }
288
+ .msg-content ul, .msg-content ol {
289
+ margin-left: 1.5rem;
290
+ margin-bottom: 0.75rem;
291
+ }
292
+ .msg-content a { color: #60a5fa; }
293
+
294
+ /* Input Area */
295
+ .input-area {
296
+ padding: 1.5rem;
297
+ background: var(--glass-bg);
298
+ backdrop-filter: blur(var(--blur));
299
+ border-top: 1px solid var(--glass-border);
300
+ display: flex;
301
+ gap: 1rem;
302
+ align-items: flex-end;
303
+ }
304
+
305
+ textarea {
306
+ flex: 1;
307
+ background: var(--chat-bg);
308
+ border: 1px solid var(--border-color);
309
+ border-radius: 0.75rem;
310
+ padding: 1rem;
311
+ color: var(--text-color);
312
+ font-family: var(--font-family);
313
+ font-size: 1rem;
314
+ resize: none;
315
+ height: 60px;
316
+ max-height: 150px;
317
+ transition: border-color 0.2s, background 0.2s;
318
+ }
319
+
320
+ textarea:focus {
321
+ outline: none;
322
+ border-color: var(--primary);
323
+ background: rgba(30, 41, 59, 1);
324
+ }
325
+
326
+ .send-btn {
327
+ width: 3.5rem;
328
+ height: 3.5rem; /* Matches default textarea height approx */
329
+ border-radius: 0.75rem;
330
+ background: var(--primary);
331
+ color: white;
332
+ border: none;
333
+ cursor: pointer;
334
+ display: flex;
335
+ align-items: center;
336
+ justify-content: center;
337
+ font-size: 1.5rem;
338
+ transition: all 0.2s;
339
+ flex-shrink: 0;
340
+ }
341
+
342
+ .send-btn:hover {
343
+ background: var(--primary-hover);
344
+ transform: translateY(-2px);
345
+ }
346
+
347
+ .send-btn:disabled {
348
+ background: var(--border-color);
349
+ cursor: not-allowed;
350
+ transform: none;
351
+ opacity: 0.7;
352
+ }
353
+
354
+ /* Typing Indicator */
355
+ .typing {
356
+ display: flex;
357
+ gap: 4px;
358
+ padding: 4px 0;
359
+ }
360
+ .dot {
361
+ width: 6px;
362
+ height: 6px;
363
+ background: var(--text-muted);
364
+ border-radius: 50%;
365
+ animation: bounce 1.4s infinite ease-in-out both;
366
+ }
367
+ .dot:nth-child(1) { animation-delay: -0.32s; }
368
+ .dot:nth-child(2) { animation-delay: -0.16s; }
369
+
370
+ @keyframes bounce {
371
+ 0%, 80%, 100% { transform: scale(0); }
372
+ 40% { transform: scale(1); }
373
+ }
374
+
375
+ @keyframes fadeIn {
376
+ from { opacity: 0; transform: translateY(10px); }
377
+ to { opacity: 1; transform: translateY(0); }
378
+ }
379
+
380
+ /* Settings Button */
381
+ .settings-btn {
382
+ background: transparent;
383
+ border: none;
384
+ color: var(--text-muted);
385
+ cursor: pointer;
386
+ font-size: 1.5rem;
387
+ transition: color 0.2s;
388
+ }
389
+ .settings-btn:hover { color: var(--text-color); }
390
+
391
+ /* Empty State */
392
+ .empty-state {
393
+ display: flex;
394
+ flex-direction: column;
395
+ align-items: center;
396
+ justify-content: center;
397
+ height: 100%;
398
+ text-align: center;
399
+ color: var(--text-muted);
400
+ padding: 2rem;
401
+ }
402
+ .empty-state i {
403
+ font-size: 4rem;
404
+ margin-bottom: 1rem;
405
+ color: var(--border-color);
406
+ }
407
+ .empty-state h3 {
408
+ color: var(--text-color);
409
+ margin-bottom: 0.5rem;
410
+ }
411
+ .suggestion-chips {
412
+ display: flex;
413
+ flex-wrap: wrap;
414
+ justify-content: center;
415
+ gap: 0.75rem;
416
+ margin-top: 2rem;
417
+ }
418
+ .chip {
419
+ background: var(--chat-bg);
420
+ border: 1px solid var(--border-color);
421
+ padding: 0.5rem 1rem;
422
+ border-radius: 2rem;
423
+ font-size: 0.85rem;
424
+ cursor: pointer;
425
+ transition: all 0.2s;
426
+ color: var(--text-muted);
427
+ }
428
+ .chip:hover {
429
+ border-color: var(--primary);
430
+ color: var(--primary);
431
+ background: rgba(99, 102, 241, 0.1);
432
+ }
433
+
434
+ @media (max-width: 600px) {
435
+ .msg-content {
436
+ max-width: 100%;
437
+ font-size: 0.9rem;
438
+ }
439
+ .message {
440
+ max-width: 95%;
441
+ }
442
+ header {
443
+ padding: 0.75rem 1rem;
444
+ }
445
+ }
446
+ </style>
447
+ </head>
448
+ <body>
449
+
450
+ <!-- Header -->
451
+ <header>
452
+ <div class="brand">
453
+ <i class="ph ph-robot"></i>
454
+ <span>Ministral Chat</span>
455
+ </div>
456
+ <div class="header-links">
457
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener noreferrer">
458
+ Built with anycoder
459
+ <i class="ph ph-arrow-square-out"></i>
460
+ </a>
461
+ <button class="settings-btn" id="settings-trigger" title="Settings">
462
+ <i class="ph ph-gear"></i>
463
+ </button>
464
+ </div>
465
+ </header>
466
+
467
+ <!-- Main Chat Area -->
468
+ <main>
469
+ <div id="chat-container">
470
+ <!-- Empty State -->
471
+ <div id="empty-state" class="empty-state">
472
+ <i class="ph ph-chat-teardrop-text"></i>
473
+ <h3>Start a conversation with Ministral</h3>
474
+ <p>Powered by OpenRouter & Mistral AI (14B)</p>
475
+
476
+ <div class="suggestion-chips">
477
+ <div class="chip" onclick="setInput('Explain quantum computing simply')">Explain quantum computing</div>
478
+ <div class="chip" onclick="setInput('Write a Python function to sort a list')">Python sort function</div>
479
+ <div class="chip" onclick="setInput('Give me a creative story about a robot')">Robot story</div>
480
+ <div class="chip" onclick="setInput('What are the benefits of meditation?')">Meditation benefits</div>
481
+ </div>
482
+ </div>
483
+ <!-- Messages will be injected here -->
484
+ </div>
485
+
486
+ <div class="input-area">
487
+ <textarea id="user-input" placeholder="Type your message here..." rows="1" oninput="autoResize(this)"></textarea>
488
+ <button id="send-btn" class="send-btn" disabled>
489
+ <i class="ph ph-paper-plane-right"></i>
490
+ </button>
491
+ </div>
492
+ </main>
493
+
494
+ <!-- API Key Modal -->
495
+ <div id="api-modal">
496
+ <div class="modal-content">
497
+ <h2>OpenRouter API Key</h2>
498
+ <p>To use this application, you need an OpenRouter API key. It is stored locally in your browser and never sent anywhere else.</p>
499
+ <div class="input-group">
500
+ <input type="password" id="api-key-input" placeholder="sk-or-v1-...">
501
+ </div>
502
+ <button class="btn" id="save-key-btn">Start Chatting</button>
503
+ <p style="margin-top: 1rem; font-size: 0.8rem;">
504
+ <a href="https://openrouter.ai/keys" target="_blank" style="color: var(--primary);">Get a key here</a>
505
+ </p>
506
+ </div>
507
+ </div>
508
+
509
+ <script>
510
+ // --- Configuration ---
511
+ const MODEL_ID = "mistralai/ministral-14b-2512";
512
+ const SYSTEM_PROMPT = "You are Ministral, a helpful, clever, and efficient AI assistant created by Mistral AI. You answer concisely but comprehensively. Use Markdown for formatting.";
513
+
514
+ // --- State ---
515
+ let apiKey = localStorage.getItem('openrouter_api_key');
516
+ let chatHistory = [];
517
+ let isGenerating = false;
518
+
519
+ // --- DOM Elements ---
520
+ const modal = document.getElementById('api-modal');
521
+ const apiKeyInput = document.getElementById('api-key-input');
522
+ const saveKeyBtn = document.getElementById('save-key-btn');
523
+ const chatContainer = document.getElementById('chat-container');
524
+ const userInput = document.getElementById('user-input');
525
+ const sendBtn = document.getElementById('send-btn');
526
+ const emptyState = document.getElementById('empty-state');
527
+ const settingsBtn = document.getElementById('settings-trigger');
528
+
529
+ // --- Initialization ---
530
+ if (!apiKey) {
531
+ modal.classList.remove('hidden');
532
+ } else {
533
+ modal.classList.add('hidden');
534
+ }
535
+
536
+ // --- Event Listeners ---
537
+ saveKeyBtn.addEventListener('click', () => {
538
+ const key = apiKeyInput.value.trim();
539
+ if (key.startsWith('sk-or-')) {
540
+ localStorage.setItem('openrouter_api_key', key);
541
+ apiKey = key;
542
+ modal.classList.add('hidden');
543
+ } else {
544
+ alert('Please enter a valid OpenRouter API key (starts with sk-or-).');
545
+ }
546
+ });
547
+
548
+ settingsBtn.addEventListener('click', () => {
549
+ apiKeyInput.value = apiKey || '';
550
+ modal.classList.remove('hidden');
551
+ });
552
+
553
+ userInput.addEventListener('input', () => {
554
+ sendBtn.disabled = userInput.value.trim() === '';
555
+ autoResize(userInput);
556
+ });
557
+
558
+ userInput.addEventListener('keydown', (e) => {
559
+ if (e.key === 'Enter' && !e.shiftKey) {
560
+ e.preventDefault();
561
+ sendMessage();
562
+ }
563
+ });
564
+
565
+ sendBtn.addEventListener('click', sendMessage);
566
+
567
+ // --- Helper Functions ---
568
+
569
+ function setInput(text) {
570
+ userInput.value = text;
571
+ userInput.focus();
572
+ autoResize(userInput);
573
+ sendBtn.disabled = false;
574
+ }
575
+
576
+ function autoResize(el) {
577
+ el.style.height = 'auto';
578
+ el.style.height = Math.min(el.scrollHeight, 150) + 'px';
579
+ }
580
+
581
+ function appendMessage(role, content, isThinking = false) {
582
+ if (emptyState) emptyState.style.display = 'none';
583
+
584
+ const msgDiv = document.createElement('div');
585
+ msgDiv.className = `message ${role}`;
586
+
587
+ const iconClass = role === 'user' ? 'ph-user' : 'ph-robot';
588
+
589
+ let innerContent = '';
590
+ if (isThinking) {
591
+ innerContent = `
592
+ <div class="typing">
593
+ <div class="dot"></div>
594
+ <div class="dot"></div>
595
+ <div class="dot"></div>
596
+ </div>`;
597
+ } else {
598
+ // Parse markdown and sanitize (basic)
599
+ innerContent = marked.parse(content);
600
+ }
601
+
602
+ msgDiv.innerHTML = `
603
+ <div class="avatar"><i class="ph ${iconClass}"></i></div>
604
+ <div class="msg-content">${innerContent}</div>
605
+ `;
606
+
607
+ chatContainer.appendChild(msgDiv);
608
+
609
+ // Highlight code blocks
610
+ if (!isThinking) {
611
+ msgDiv.querySelectorAll('pre code').forEach((block) => {
612
+ hljs.highlightElement(block);
613
+ });
614
+ }
615
+
616
+ chatContainer.scrollTop = chatContainer.scrollHeight;
617
+ return msgDiv;
618
+ }
619
+
620
+ async function sendMessage() {
621
+ const text = userInput.value.trim();
622
+ if (!text || isGenerating) return;
623
+
624
+ // UI Updates
625
+ userInput.value = '';
626
+ userInput.style.height = '60px'; // Reset height
627
+ sendBtn.disabled = true;
628
+ isGenerating = true;
629
+
630
+ // Add User Message
631
+ appendMessage('user', text);
632
+ chatHistory.push({ role: "user", content: text });
633
+
634
+ // Add Thinking Bubble
635
+ const thinkingMsg = appendMessage('ai', '', true);
636
+
637
+ try {
638
+ const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
639
+ method: "POST",
640
+ headers: {
641
+ "Authorization": `Bearer ${apiKey}`,
642
+ "Content-Type": "application/json",
643
+ "HTTP-Referer": window.location.href, // Required by OpenRouter
644
+ "X-Title": "Ministral HTML Chat"
645
+ },
646
+ body: JSON.stringify({
647
+ model: MODEL_ID,
648
+ messages: [
649
+ { role: "system", content: SYSTEM_PROMPT },
650
+ ...chatHistory
651
+ ]
652
+ })
653
+ });
654
+
655
+ if (!response.ok) {
656
+ const errorData = await response.json();
657
+ throw new Error(errorData.error?.message || 'API Request Failed');
658
+ }
659
+
660
+ const data = await response.json();
661
+ const aiResponse = data.choices[0].message.content;
662
+
663
+ // Remove thinking bubble
664
+ thinkingMsg.remove();
665
+
666
+ // Add AI Message
667
+ appendMessage('ai', aiResponse);
668
+ chatHistory.push({ role: "assistant", content: aiResponse });
669
+
670
+ } catch (error) {
671
+ thinkingMsg.remove();
672
+ appendMessage('ai', `**Error:** ${error.message}. Please check your API key.`);
673
+ } finally {
674
+ isGenerating = false;
675
+ // Focus back on input only if not on mobile (to prevent keyboard jumping)
676
+ if (window.innerWidth > 600) userInput.focus();
677
+ }
678
+ }
679
+ </script>
680
+ </body>
681
+ </html>