dzezzefezfz commited on
Commit
24f5035
·
verified ·
1 Parent(s): 02bfd4b

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +493 -0
index.html CHANGED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>AI Chat Assistant</title>
7
+
8
+ <!-- Tailwind CSS -->
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+
11
+ <!-- Markdown Parser -->
12
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
13
+
14
+ <!-- Code Highlighting -->
15
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
16
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
17
+
18
+ <!-- Icons -->
19
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
20
+
21
+ <script>
22
+ tailwind.config = {
23
+ darkMode: 'class',
24
+ theme: {
25
+ extend: {
26
+ colors: {
27
+ gray: {
28
+ 750: '#2d2d35',
29
+ 850: '#1a1a20',
30
+ 950: '#0d0d10',
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ </script>
37
+
38
+ <style>
39
+ /* Custom Scrollbar */
40
+ ::-webkit-scrollbar {
41
+ width: 8px;
42
+ height: 8px;
43
+ }
44
+ ::-webkit-scrollbar-track {
45
+ background: transparent;
46
+ }
47
+ ::-webkit-scrollbar-thumb {
48
+ background: #565869;
49
+ border-radius: 4px;
50
+ }
51
+ ::-webkit-scrollbar-thumb:hover {
52
+ background: #acacbe;
53
+ }
54
+
55
+ /* Typing Animation */
56
+ .typing-dot {
57
+ animation: typing 1.4s infinite ease-in-out both;
58
+ }
59
+ .typing-dot:nth-child(1) { animation-delay: -0.32s; }
60
+ .typing-dot:nth-child(2) { animation-delay: -0.16s; }
61
+
62
+ @keyframes typing {
63
+ 0%, 80%, 100% { transform: scale(0); }
64
+ 40% { transform: scale(1); }
65
+ }
66
+
67
+ /* Markdown Styles */
68
+ .prose pre {
69
+ background-color: #0d0d10;
70
+ border-radius: 0.5rem;
71
+ padding: 1rem;
72
+ margin: 1rem 0;
73
+ overflow-x: auto;
74
+ }
75
+ .prose code {
76
+ background-color: rgba(255,255,255,0.1);
77
+ padding: 0.2rem 0.4rem;
78
+ border-radius: 0.25rem;
79
+ font-family: monospace;
80
+ font-size: 0.9em;
81
+ }
82
+ .prose pre code {
83
+ background-color: transparent;
84
+ padding: 0;
85
+ border-radius: 0;
86
+ color: inherit;
87
+ }
88
+ .prose p {
89
+ margin-bottom: 1rem;
90
+ }
91
+ .prose p:last-child {
92
+ margin-bottom: 0;
93
+ }
94
+ .prose ul {
95
+ list-style-type: disc;
96
+ padding-left: 1.5rem;
97
+ margin-bottom: 1rem;
98
+ }
99
+ .prose ol {
100
+ list-style-type: decimal;
101
+ padding-left: 1.5rem;
102
+ margin-bottom: 1rem;
103
+ }
104
+ </style>
105
+ </head>
106
+ <body class="bg-gray-800 text-gray-100 h-screen flex overflow-hidden font-sans antialiased">
107
+
108
+ <!-- Sidebar -->
109
+ <div id="sidebar" class="bg-gray-900 w-[260px] flex-shrink-0 flex flex-col transition-all duration-300 -ml-[260px] md:ml-0 border-r border-white/10 z-20 absolute md:relative h-full">
110
+ <!-- New Chat Button -->
111
+ <div class="p-3 flex-shrink-0">
112
+ <button onclick="startNewChat()" class="flex items-center gap-3 px-3 py-3 w-full text-sm text-white border border-white/20 rounded-md hover:bg-gray-800 transition-colors text-left">
113
+ <i class="fa-solid fa-plus"></i>
114
+ New chat
115
+ </button>
116
+ </div>
117
+
118
+ <!-- History List -->
119
+ <div class="flex-1 overflow-y-auto px-3 py-2 space-y-2" id="history-list">
120
+ <!-- History items will be injected here -->
121
+ <div class="text-xs font-medium text-gray-500 px-3 py-2">Today</div>
122
+ </div>
123
+
124
+ <!-- User / Settings -->
125
+ <div class="p-3 border-t border-white/10">
126
+ <button onclick="toggleSettings()" class="flex items-center gap-3 px-3 py-3 w-full text-sm text-white hover:bg-gray-800 rounded-md transition-colors text-left">
127
+ <i class="fa-solid fa-gear"></i>
128
+ Settings
129
+ </button>
130
+ </div>
131
+ </div>
132
+
133
+ <!-- Main Content -->
134
+ <div class="flex-1 flex flex-col h-full relative bg-gray-800">
135
+
136
+ <!-- Mobile Header -->
137
+ <div class="md:hidden flex items-center p-4 border-b border-white/10 bg-gray-800 text-gray-100 z-10">
138
+ <button onclick="toggleSidebar()" class="text-gray-300 hover:text-white">
139
+ <i class="fa-solid fa-bars text-xl"></i>
140
+ </button>
141
+ <span class="ml-4 font-medium">New chat</span>
142
+ </div>
143
+
144
+ <!-- Chat Area -->
145
+ <div class="flex-1 overflow-y-auto scroll-smooth" id="chat-container">
146
+ <!-- Welcome Screen -->
147
+ <div id="welcome-screen" class="h-full flex flex-col items-center justify-center p-8 text-center">
148
+ <div class="bg-white/10 p-4 rounded-full mb-6">
149
+ <i class="fa-solid fa-robot text-4xl text-white"></i>
150
+ </div>
151
+ <h2 class="text-2xl font-semibold mb-2">How can I help you today?</h2>
152
+ </div>
153
+
154
+ <!-- Messages will be injected here -->
155
+ <div id="messages-list" class="flex flex-col pb-32 pt-4"></div>
156
+ </div>
157
+
158
+ <!-- Input Area -->
159
+ <div class="absolute bottom-0 left-0 w-full bg-gradient-to-t from-gray-800 via-gray-800 to-transparent pt-10 pb-6 px-4">
160
+ <div class="max-w-3xl mx-auto">
161
+ <div class="relative flex items-end gap-2 bg-[#40414f] border border-black/10 dark:border-gray-900/50 rounded-xl shadow-xs overflow-hidden ring-offset-2 focus-within:ring-2 ring-blue-500/50">
162
+ <textarea
163
+ id="user-input"
164
+ rows="1"
165
+ class="w-full max-h-[200px] py-3 pl-4 pr-12 bg-transparent border-0 text-white placeholder-gray-400 focus:ring-0 resize-none overflow-y-auto"
166
+ placeholder="Message ChatGPT..."
167
+ oninput="this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px'"
168
+ onkeydown="handleEnter(event)"
169
+ ></textarea>
170
+ <button
171
+ onclick="sendMessage()"
172
+ id="send-btn"
173
+ class="absolute right-2 bottom-2 p-2 rounded-md text-gray-400 hover:bg-black/50 hover:text-white disabled:opacity-40 disabled:hover:bg-transparent transition-colors"
174
+ disabled
175
+ >
176
+ <i class="fa-solid fa-paper-plane"></i>
177
+ </button>
178
+ </div>
179
+ <div class="text-center mt-2">
180
+ <p class="text-xs text-gray-500">AI can make mistakes. Consider checking important information.</p>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </div>
185
+
186
+ <!-- Settings Modal -->
187
+ <div id="settings-modal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4">
188
+ <div class="bg-gray-900 rounded-xl w-full max-w-md border border-white/10 shadow-2xl transform transition-all scale-95 opacity-0" id="settings-content">
189
+ <div class="flex justify-between items-center p-4 border-b border-white/10">
190
+ <h3 class="text-lg font-medium">Settings</h3>
191
+ <button onclick="toggleSettings()" class="text-gray-400 hover:text-white">
192
+ <i class="fa-solid fa-xmark"></i>
193
+ </button>
194
+ </div>
195
+ <div class="p-6 space-y-4">
196
+ <div>
197
+ <label class="block text-sm font-medium text-gray-300 mb-1">Hugging Face API Key</label>
198
+ <input type="password" id="api-key-input" class="w-full bg-gray-800 border border-white/10 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="hf_...">
199
+ <p class="text-xs text-gray-500 mt-1">Required for the chatbot to function.</p>
200
+ </div>
201
+ <div>
202
+ <label class="block text-sm font-medium text-gray-300 mb-1">Model ID</label>
203
+ <input type="text" id="model-input" class="w-full bg-gray-800 border border-white/10 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-blue-500" value="mistralai/Mistral-7B-Instruct-v0.2">
204
+ <p class="text-xs text-gray-500 mt-1">e.g., mistralai/Mistral-7B-Instruct-v0.2, HuggingFaceH4/zephyr-7b-beta</p>
205
+ </div>
206
+ <div>
207
+ <label class="block text-sm font-medium text-gray-300 mb-1">System Prompt</label>
208
+ <textarea id="system-prompt-input" rows="3" class="w-full bg-gray-800 border border-white/10 rounded-md px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-blue-500">You are a helpful AI assistant.</textarea>
209
+ </div>
210
+ </div>
211
+ <div class="p-4 border-t border-white/10 flex justify-end">
212
+ <button onclick="saveSettings()" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md transition-colors">Save</button>
213
+ </div>
214
+ </div>
215
+ </div>
216
+
217
+ <script>
218
+ // --- Configuration ---
219
+ const DEFAULT_MODEL = "mistralai/Mistral-7B-Instruct-v0.2";
220
+ let CONFIG = {
221
+ apiKey: localStorage.getItem('hf_api_key') || '',
222
+ model: localStorage.getItem('hf_model') || DEFAULT_MODEL,
223
+ systemPrompt: localStorage.getItem('hf_system_prompt') || "You are a helpful AI assistant."
224
+ };
225
+
226
+ // --- State ---
227
+ let messages = [];
228
+ let isGenerating = false;
229
+
230
+ // --- Initialization ---
231
+ document.addEventListener('DOMContentLoaded', () => {
232
+ // Load settings into inputs
233
+ document.getElementById('api-key-input').value = CONFIG.apiKey;
234
+ document.getElementById('model-input').value = CONFIG.model;
235
+ document.getElementById('system-prompt-input').value = CONFIG.systemPrompt;
236
+
237
+ // Check if API key is missing
238
+ if (!CONFIG.apiKey) {
239
+ toggleSettings();
240
+ }
241
+
242
+ // Input listener
243
+ const input = document.getElementById('user-input');
244
+ input.addEventListener('input', () => {
245
+ const btn = document.getElementById('send-btn');
246
+ btn.disabled = input.value.trim() === '';
247
+ if (input.value.trim() !== '') {
248
+ btn.classList.add('bg-green-600', 'text-white');
249
+ btn.classList.remove('text-gray-400');
250
+ } else {
251
+ btn.classList.remove('bg-green-600', 'text-white');
252
+ btn.classList.add('text-gray-400');
253
+ }
254
+ });
255
+ });
256
+
257
+ // --- UI Functions ---
258
+ function toggleSidebar() {
259
+ const sidebar = document.getElementById('sidebar');
260
+ sidebar.classList.toggle('-ml-[260px]');
261
+ }
262
+
263
+ function toggleSettings() {
264
+ const modal = document.getElementById('settings-modal');
265
+ const content = document.getElementById('settings-content');
266
+
267
+ if (modal.classList.contains('hidden')) {
268
+ modal.classList.remove('hidden');
269
+ // Small delay for animation
270
+ setTimeout(() => {
271
+ content.classList.remove('scale-95', 'opacity-0');
272
+ content.classList.add('scale-100', 'opacity-100');
273
+ }, 10);
274
+ } else {
275
+ content.classList.remove('scale-100', 'opacity-100');
276
+ content.classList.add('scale-95', 'opacity-0');
277
+ setTimeout(() => {
278
+ modal.classList.add('hidden');
279
+ }, 300);
280
+ }
281
+ }
282
+
283
+ function saveSettings() {
284
+ const newKey = document.getElementById('api-key-input').value.trim();
285
+ const newModel = document.getElementById('model-input').value.trim();
286
+ const newPrompt = document.getElementById('system-prompt-input').value.trim();
287
+
288
+ if (newKey) {
289
+ localStorage.setItem('hf_api_key', newKey);
290
+ CONFIG.apiKey = newKey;
291
+ }
292
+ if (newModel) {
293
+ localStorage.setItem('hf_model', newModel);
294
+ CONFIG.model = newModel;
295
+ }
296
+ if (newPrompt) {
297
+ localStorage.setItem('hf_system_prompt', newPrompt);
298
+ CONFIG.systemPrompt = newPrompt;
299
+ }
300
+
301
+ toggleSettings();
302
+ alert('Settings saved!');
303
+ }
304
+
305
+ function handleEnter(e) {
306
+ if (e.key === 'Enter' && !e.shiftKey) {
307
+ e.preventDefault();
308
+ sendMessage();
309
+ }
310
+ }
311
+
312
+ function startNewChat() {
313
+ messages = [];
314
+ document.getElementById('messages-list').innerHTML = '';
315
+ document.getElementById('welcome-screen').classList.remove('hidden');
316
+ document.getElementById('user-input').value = '';
317
+ document.getElementById('user-input').style.height = 'auto';
318
+ }
319
+
320
+ function appendMessage(role, content) {
321
+ const list = document.getElementById('messages-list');
322
+ const welcome = document.getElementById('welcome-screen');
323
+
324
+ if (!welcome.classList.contains('hidden')) {
325
+ welcome.classList.add('hidden');
326
+ }
327
+
328
+ const isUser = role === 'user';
329
+ const avatar = isUser
330
+ ? '<div class="w-8 h-8 bg-gray-600 rounded-sm flex items-center justify-center"><i class="fa-solid fa-user text-white"></i></div>'
331
+ : '<div class="w-8 h-8 bg-green-500 rounded-sm flex items-center justify-center"><i class="fa-solid fa-robot text-white"></i></div>';
332
+
333
+ const bgClass = isUser ? '' : 'bg-[#444654]';
334
+
335
+ const msgDiv = document.createElement('div');
336
+ msgDiv.className = `w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group ${bgClass}`;
337
+
338
+ // Render Markdown
339
+ const renderedContent = marked.parse(content);
340
+
341
+ msgDiv.innerHTML = `
342
+ <div class="text-base gap-4 md:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem] p-4 md:py-6 flex lg:px-0 m-auto">
343
+ <div class="flex-shrink-0 flex flex-col relative items-end">
344
+ ${avatar}
345
+ </div>
346
+ <div class="relative flex-1 prose prose-invert max-w-none break-words">
347
+ ${renderedContent}
348
+ </div>
349
+ </div>
350
+ `;
351
+
352
+ list.appendChild(msgDiv);
353
+
354
+ // Highlight code blocks
355
+ msgDiv.querySelectorAll('pre code').forEach((block) => {
356
+ hljs.highlightElement(block);
357
+ });
358
+
359
+ // Scroll to bottom
360
+ const container = document.getElementById('chat-container');
361
+ container.scrollTop = container.scrollHeight;
362
+
363
+ return msgDiv;
364
+ }
365
+
366
+ function showTypingIndicator() {
367
+ const list = document.getElementById('messages-list');
368
+ const div = document.createElement('div');
369
+ div.id = 'typing-indicator';
370
+ div.className = 'w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 bg-[#444654]';
371
+ div.innerHTML = `
372
+ <div class="text-base gap-4 md:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem] p-4 md:py-6 flex lg:px-0 m-auto">
373
+ <div class="flex-shrink-0 flex flex-col relative items-end">
374
+ <div class="w-8 h-8 bg-green-500 rounded-sm flex items-center justify-center"><i class="fa-solid fa-robot text-white"></i></div>
375
+ </div>
376
+ <div class="relative flex-1 flex items-center gap-1">
377
+ <div class="w-2 h-2 bg-gray-400 rounded-full typing-dot"></div>
378
+ <div class="w-2 h-2 bg-gray-400 rounded-full typing-dot"></div>
379
+ <div class="w-2 h-2 bg-gray-400 rounded-full typing-dot"></div>
380
+ </div>
381
+ </div>
382
+ `;
383
+ list.appendChild(div);
384
+ const container = document.getElementById('chat-container');
385
+ container.scrollTop = container.scrollHeight;
386
+ }
387
+
388
+ function removeTypingIndicator() {
389
+ const indicator = document.getElementById('typing-indicator');
390
+ if (indicator) indicator.remove();
391
+ }
392
+
393
+ // --- API Interaction ---
394
+ async function sendMessage() {
395
+ if (isGenerating) return;
396
+
397
+ const input = document.getElementById('user-input');
398
+ const text = input.value.trim();
399
+ if (!text) return;
400
+
401
+ if (!CONFIG.apiKey) {
402
+ alert('Please set your Hugging Face API Key in settings first.');
403
+ toggleSettings();
404
+ return;
405
+ }
406
+
407
+ // UI Updates
408
+ input.value = '';
409
+ input.style.height = 'auto';
410
+ document.getElementById('send-btn').disabled = true;
411
+ document.getElementById('send-btn').classList.remove('bg-green-600', 'text-white');
412
+
413
+ // Add User Message
414
+ messages.push({ role: 'user', content: text });
415
+ appendMessage('user', text);
416
+
417
+ // Show Loading
418
+ isGenerating = true;
419
+ showTypingIndicator();
420
+
421
+ try {
422
+ // Prepare prompt
423
+ let prompt = "<s>";
424
+ for (let i = 0; i < messages.length; i++) {
425
+ const msg = messages[i];
426
+ if (msg.role === 'user') {
427
+ if (i === 0) {
428
+ prompt += `[INST] ${CONFIG.systemPrompt}\n\n${msg.content} [/INST]`;
429
+ } else {
430
+ prompt += `[INST] ${msg.content} [/INST]`;
431
+ }
432
+ } else {
433
+ prompt += ` ${msg.content} </s>`;
434
+ }
435
+ }
436
+
437
+ const response = await fetch(`https://api-inference.huggingface.co/models/${CONFIG.model}`, {
438
+ method: 'POST',
439
+ headers: {
440
+ 'Authorization': `Bearer ${CONFIG.apiKey}`,
441
+ 'Content-Type': 'application/json'
442
+ },
443
+ body: JSON.stringify({
444
+ inputs: prompt,
445
+ parameters: {
446
+ max_new_tokens: 1024,
447
+ temperature: 0.7,
448
+ top_p: 0.95,
449
+ return_full_text: false,
450
+ stream: false
451
+ }
452
+ })
453
+ });
454
+
455
+ if (!response.ok) {
456
+ let errorMsg = `API Error: ${response.status} ${response.statusText}`;
457
+ try {
458
+ const errorData = await response.json();
459
+ if (errorData.error) {
460
+ errorMsg += `\nDetails: ${errorData.error}`;
461
+ }
462
+ } catch (e) {
463
+ // Ignore JSON parse error
464
+ }
465
+ throw new Error(errorMsg);
466
+ }
467
+
468
+ const data = await response.json();
469
+
470
+ let botText = '';
471
+ if (Array.isArray(data) && data.length > 0) {
472
+ botText = data[0].generated_text;
473
+ } else if (data.generated_text) {
474
+ botText = data.generated_text;
475
+ } else {
476
+ botText = "Error: No response from model.";
477
+ }
478
+
479
+ removeTypingIndicator();
480
+ messages.push({ role: 'assistant', content: botText });
481
+ appendMessage('assistant', botText);
482
+
483
+ } catch (error) {
484
+ console.error(error);
485
+ removeTypingIndicator();
486
+ appendMessage('assistant', `**Error:** ${error.message}\n\n**Troubleshooting:**\n1. Check your API Key in Settings.\n2. Ensure your token has 'Read' permissions.\n3. Try a different model ID (e.g., 'HuggingFaceH4/zephyr-7b-beta').`);
487
+ } finally {
488
+ isGenerating = false;
489
+ }
490
+ }
491
+ </script>
492
+ </body>
493
+ </html>