CharlieBoyer HF Staff commited on
Commit
f9c7472
·
verified ·
1 Parent(s): bef99c3

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +929 -19
index.html CHANGED
@@ -1,19 +1,929 @@
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>Qwen Chat - AI Assistant</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
+ <script>
11
+ tailwind.config = {
12
+ theme: {
13
+ extend: {
14
+ fontFamily: {
15
+ sans: ['Inter', 'sans-serif'],
16
+ },
17
+ colors: {
18
+ primary: {
19
+ 50: '#f0f9ff',
20
+ 100: '#e0f2fe',
21
+ 200: '#bae6fd',
22
+ 300: '#7dd3fc',
23
+ 400: '#38bdf8',
24
+ 500: '#0ea5e9',
25
+ 600: '#0284c7',
26
+ 700: '#0369a1',
27
+ 800: '#075985',
28
+ 900: '#0c4a6e',
29
+ },
30
+ dark: {
31
+ 800: '#1e293b',
32
+ 900: '#0f172a',
33
+ 950: '#020617',
34
+ }
35
+ },
36
+ animation: {
37
+ 'fade-in': 'fadeIn 0.5s ease-out',
38
+ 'slide-up': 'slideUp 0.3s ease-out',
39
+ 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
40
+ 'typing': 'typing 1.5s infinite',
41
+ },
42
+ keyframes: {
43
+ fadeIn: {
44
+ '0%': { opacity: '0' },
45
+ '100%': { opacity: '1' },
46
+ },
47
+ slideUp: {
48
+ '0%': { transform: 'translateY(20px)', opacity: '0' },
49
+ '100%': { transform: 'translateY(0)', opacity: '1' },
50
+ },
51
+ typing: {
52
+ '0%, 100%': { opacity: '1' },
53
+ '50%': { opacity: '0.3' },
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ </script>
60
+ <style>
61
+ /* Custom Scrollbar */
62
+ ::-webkit-scrollbar {
63
+ width: 6px;
64
+ height: 6px;
65
+ }
66
+ ::-webkit-scrollbar-track {
67
+ background: transparent;
68
+ }
69
+ ::-webkit-scrollbar-thumb {
70
+ background: #475569;
71
+ border-radius: 3px;
72
+ }
73
+ ::-webkit-scrollbar-thumb:hover {
74
+ background: #64748b;
75
+ }
76
+
77
+ /* Glassmorphism */
78
+ .glass {
79
+ background: rgba(30, 41, 59, 0.7);
80
+ backdrop-filter: blur(12px);
81
+ -webkit-backdrop-filter: blur(12px);
82
+ border: 1px solid rgba(255, 255, 255, 0.05);
83
+ }
84
+
85
+ .glass-light {
86
+ background: rgba(255, 255, 255, 0.03);
87
+ backdrop-filter: blur(8px);
88
+ -webkit-backdrop-filter: blur(8px);
89
+ }
90
+
91
+ /* Code Block Styling */
92
+ .code-block {
93
+ background: #0d1117;
94
+ border-radius: 8px;
95
+ overflow: hidden;
96
+ margin: 12px 0;
97
+ }
98
+ .code-header {
99
+ background: #161b22;
100
+ padding: 8px 16px;
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ border-bottom: 1px solid #30363d;
105
+ }
106
+ .code-content {
107
+ padding: 16px;
108
+ overflow-x: auto;
109
+ font-family: 'Fira Code', 'Consolas', monospace;
110
+ font-size: 13px;
111
+ line-height: 1.6;
112
+ color: #e6edf3;
113
+ }
114
+ .code-content .keyword { color: #ff7b72; }
115
+ .code-content .string { color: #a5d6ff; }
116
+ .code-content .comment { color: #8b949e; font-style: italic; }
117
+ .code-content .function { color: #d2a8ff; }
118
+ .code-content .number { color: #79c0ff; }
119
+
120
+ /* Typing Indicator */
121
+ .typing-dot {
122
+ width: 6px;
123
+ height: 6px;
124
+ background: #38bdf8;
125
+ border-radius: 50%;
126
+ display: inline-block;
127
+ animation: typing 1.4s infinite ease-in-out both;
128
+ }
129
+ .typing-dot:nth-child(1) { animation-delay: -0.32s; }
130
+ .typing-dot:nth-child(2) { animation-delay: -0.16s; }
131
+
132
+ /* Message Animations */
133
+ .message-appear {
134
+ animation: slideUp 0.3s ease-out forwards;
135
+ }
136
+
137
+ /* Gradient Text */
138
+ .gradient-text {
139
+ background: linear-gradient(135deg, #38bdf8 0%, #818cf8 50%, #c084fc 100%);
140
+ -webkit-background-clip: text;
141
+ -webkit-text-fill-color: transparent;
142
+ background-clip: text;
143
+ }
144
+
145
+ /* Input Glow */
146
+ .input-glow:focus-within {
147
+ box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.2), 0 0 20px rgba(56, 189, 248, 0.1);
148
+ }
149
+
150
+ /* Background Pattern */
151
+ .bg-pattern {
152
+ background-image:
153
+ radial-gradient(circle at 20% 50%, rgba(56, 189, 248, 0.08) 0%, transparent 50%),
154
+ radial-gradient(circle at 80% 80%, rgba(192, 132, 252, 0.06) 0%, transparent 50%),
155
+ radial-gradient(circle at 40% 20%, rgba(129, 140, 248, 0.05) 0%, transparent 50%);
156
+ }
157
+
158
+ /* Markdown Styles */
159
+ .markdown-body h1 { font-size: 1.5em; font-weight: 700; margin: 16px 0 8px; }
160
+ .markdown-body h2 { font-size: 1.25em; font-weight: 600; margin: 14px 0 8px; }
161
+ .markdown-body h3 { font-size: 1.1em; font-weight: 600; margin: 12px 0 6px; }
162
+ .markdown-body p { margin: 8px 0; line-height: 1.7; }
163
+ .markdown-body ul, .markdown-body ol { margin: 8px 0; padding-left: 20px; }
164
+ .markdown-body li { margin: 4px 0; }
165
+ .markdown-body strong { font-weight: 600; color: #f1f5f9; }
166
+ .markdown-body em { font-style: italic; }
167
+ .markdown-body blockquote {
168
+ border-left: 3px solid #38bdf8;
169
+ padding-left: 12px;
170
+ margin: 12px 0;
171
+ color: #94a3b8;
172
+ font-style: italic;
173
+ }
174
+ .markdown-body pre { margin: 12px 0; }
175
+ .markdown-body code {
176
+ background: rgba(56, 189, 248, 0.1);
177
+ padding: 2px 6px;
178
+ border-radius: 4px;
179
+ font-size: 0.9em;
180
+ color: #7dd3fc;
181
+ font-family: 'Fira Code', monospace;
182
+ }
183
+ .markdown-body pre code {
184
+ background: none;
185
+ padding: 0;
186
+ color: inherit;
187
+ }
188
+ .markdown-body table {
189
+ width: 100%;
190
+ border-collapse: collapse;
191
+ margin: 12px 0;
192
+ }
193
+ .markdown-body th, .markdown-body td {
194
+ border: 1px solid #334155;
195
+ padding: 8px 12px;
196
+ text-align: left;
197
+ }
198
+ .markdown-body th {
199
+ background: rgba(56, 189, 248, 0.1);
200
+ font-weight: 600;
201
+ }
202
+ .markdown-body a {
203
+ color: #38bdf8;
204
+ text-decoration: underline;
205
+ }
206
+ .markdown-body hr {
207
+ border: none;
208
+ border-top: 1px solid #334155;
209
+ margin: 16px 0;
210
+ }
211
+
212
+ /* Tooltip */
213
+ .tooltip {
214
+ position: relative;
215
+ }
216
+ .tooltip::after {
217
+ content: attr(data-tooltip);
218
+ position: absolute;
219
+ bottom: 100%;
220
+ left: 50%;
221
+ transform: translateX(-50%);
222
+ padding: 4px 8px;
223
+ background: #1e293b;
224
+ color: #f8fafc;
225
+ font-size: 12px;
226
+ border-radius: 4px;
227
+ white-space: nowrap;
228
+ opacity: 0;
229
+ pointer-events: none;
230
+ transition: opacity 0.2s;
231
+ margin-bottom: 4px;
232
+ border: 1px solid #334155;
233
+ }
234
+ .tooltip:hover::after {
235
+ opacity: 1;
236
+ }
237
+
238
+ /* Selection */
239
+ ::selection {
240
+ background: rgba(56, 189, 248, 0.3);
241
+ color: #f8fafc;
242
+ }
243
+ </style>
244
+ </head>
245
+ <body class="bg-dark-950 text-slate-200 font-sans h-screen overflow-hidden bg-pattern">
246
+
247
+ <!-- App Container -->
248
+ <div class="flex h-full">
249
+
250
+ <!-- Sidebar -->
251
+ <aside id="sidebar" class="w-72 bg-dark-900 border-r border-slate-800/50 flex flex-col transition-all duration-300 ease-in-out flex-shrink-0">
252
+ <!-- Sidebar Header -->
253
+ <div class="p-4 border-b border-slate-800/50">
254
+ <div class="flex items-center gap-3 mb-4">
255
+ <div class="w-10 h-10 rounded-xl bg-gradient-to-br from-sky-500 to-violet-500 flex items-center justify-center shadow-lg shadow-sky-500/20">
256
+ <i class="fas fa-robot text-white text-lg"></i>
257
+ </div>
258
+ <div>
259
+ <h1 class="font-bold text-lg text-slate-100">Qwen Chat</h1>
260
+ <p class="text-xs text-slate-400">AI Assistant</p>
261
+ </div>
262
+ </div>
263
+ <button onclick="newChat()" class="w-full py-2.5 px-4 rounded-lg bg-slate-800 hover:bg-slate-700 border border-slate-700/50 text-slate-200 text-sm font-medium transition-all duration-200 flex items-center justify-center gap-2 hover:border-slate-600 group">
264
+ <i class="fas fa-plus text-sky-400 group-hover:scale-110 transition-transform"></i>
265
+ New Chat
266
+ </button>
267
+ </div>
268
+
269
+ <!-- Chat History -->
270
+ <div class="flex-1 overflow-y-auto p-3 space-y-1" id="chatHistory">
271
+ <!-- History items will be populated by JS -->
272
+ </div>
273
+
274
+ <!-- Sidebar Footer -->
275
+ <div class="p-4 border-t border-slate-800/50 space-y-3">
276
+ <!-- API Key Input -->
277
+ <div class="relative">
278
+ <input
279
+ type="password"
280
+ id="apiKeyInput"
281
+ placeholder="Enter HuggingFace Token"
282
+ class="w-full bg-slate-800/50 border border-slate-700/50 rounded-lg px-3 py-2 text-xs text-slate-300 placeholder-slate-500 focus:outline-none focus:border-sky-500/50 focus:ring-1 focus:ring-sky-500/20 transition-all"
283
+ >
284
+ <button onclick="saveApiKey()" class="absolute right-2 top-1/2 -translate-y-1/2 text-sky-400 hover:text-sky-300 text-xs">
285
+ <i class="fas fa-save"></i>
286
+ </button>
287
+ </div>
288
+ <div class="flex items-center justify-between text-xs text-slate-500">
289
+ <span class="flex items-center gap-1">
290
+ <i class="fas fa-shield-alt text-emerald-500"></i>
291
+ Secure
292
+ </span>
293
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="hover:text-sky-400 transition-colors flex items-center gap-1">
294
+ Built with <span class="text-sky-400 font-medium">anycoder</span>
295
+ <i class="fas fa-external-link-alt text-[10px]"></i>
296
+ </a>
297
+ </div>
298
+ </div>
299
+ </aside>
300
+
301
+ <!-- Main Content -->
302
+ <main class="flex-1 flex flex-col relative min-w-0">
303
+ <!-- Mobile Header -->
304
+ <div class="lg:hidden flex items-center justify-between p-4 border-b border-slate-800/50 glass">
305
+ <button onclick="toggleSidebar()" class="text-slate-400 hover:text-slate-200">
306
+ <i class="fas fa-bars text-xl"></i>
307
+ </button>
308
+ <div class="flex items-center gap-2">
309
+ <div class="w-8 h-8 rounded-lg bg-gradient-to-br from-sky-500 to-violet-500 flex items-center justify-center">
310
+ <i class="fas fa-robot text-white text-sm"></i>
311
+ </div>
312
+ <span class="font-semibold text-slate-200">Qwen Chat</span>
313
+ </div>
314
+ <div class="w-8"></div>
315
+ </div>
316
+
317
+ <!-- Chat Area -->
318
+ <div id="chatContainer" class="flex-1 overflow-y-auto p-4 lg:p-8 space-y-6 scroll-smooth">
319
+ <!-- Welcome Screen -->
320
+ <div id="welcomeScreen" class="flex flex-col items-center justify-center h-full text-center px-4 max-w-2xl mx-auto">
321
+ <div class="w-20 h-20 rounded-2xl bg-gradient-to-br from-sky-500 to-violet-500 flex items-center justify-center mb-6 shadow-2xl shadow-sky-500/20 animate-pulse-slow">
322
+ <i class="fas fa-sparkles text-white text-3xl"></i>
323
+ </div>
324
+ <h2 class="text-3xl lg:text-4xl font-bold mb-3 gradient-text">How can I help you today?</h2>
325
+ <p class="text-slate-400 mb-8 max-w-md">Powered by Qwen 2.5 - Alibaba's latest large language model. Ask me anything about coding, writing, analysis, or creative tasks.</p>
326
+
327
+ <!-- Quick Suggestions -->
328
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 w-full max-w-lg">
329
+ <button onclick="sendQuickMessage('Explain quantum computing in simple terms')" class="p-4 rounded-xl bg-slate-800/50 border border-slate-700/50 hover:border-sky-500/30 hover:bg-slate-800 transition-all text-left group">
330
+ <div class="w-8 h-8 rounded-lg bg-violet-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform">
331
+ <i class="fas fa-atom text-violet-400"></i>
332
+ </div>
333
+ <p class="text-sm text-slate-300 font-medium">Explain quantum computing</p>
334
+ <p class="text-xs text-slate-500 mt-1">in simple terms</p>
335
+ </button>
336
+ <button onclick="sendQuickMessage('Write a Python function to sort a list of dictionaries by multiple keys')" class="p-4 rounded-xl bg-slate-800/50 border border-slate-700/50 hover:border-sky-500/30 hover:bg-slate-800 transition-all text-left group">
337
+ <div class="w-8 h-8 rounded-lg bg-sky-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform">
338
+ <i class="fas fa-code text-sky-400"></i>
339
+ </div>
340
+ <p class="text-sm text-slate-300 font-medium">Write Python code</p>
341
+ <p class="text-xs text-slate-500 mt-1">sort dictionaries by keys</p>
342
+ </button>
343
+ <button onclick="sendQuickMessage('Create a marketing strategy for a new eco-friendly product')" class="p-4 rounded-xl bg-slate-800/50 border border-slate-700/50 hover:border-sky-500/30 hover:bg-slate-800 transition-all text-left group">
344
+ <div class="w-8 h-8 rounded-lg bg-emerald-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform">
345
+ <i class="fas fa-leaf text-emerald-400"></i>
346
+ </div>
347
+ <p class="text-sm text-slate-300 font-medium">Marketing strategy</p>
348
+ <p class="text-xs text-slate-500 mt-1">for eco-friendly product</p>
349
+ </button>
350
+ <button onclick="sendQuickMessage('Analyze the pros and cons of remote work for tech companies')" class="p-4 rounded-xl bg-slate-800/50 border border-slate-700/50 hover:border-sky-500/30 hover:bg-slate-800 transition-all text-left group">
351
+ <div class="w-8 h-8 rounded-lg bg-amber-500/20 flex items-center justify-center mb-3 group-hover:scale-110 transition-transform">
352
+ <i class="fas fa-chart-line text-amber-400"></i>
353
+ </div>
354
+ <p class="text-sm text-slate-300 font-medium">Analyze remote work</p>
355
+ <p class="text-xs text-slate-500 mt-1">pros and cons for tech</p>
356
+ </button>
357
+ </div>
358
+ </div>
359
+
360
+ <!-- Messages will appear here -->
361
+ <div id="messagesArea" class="hidden max-w-3xl mx-auto w-full space-y-6"></div>
362
+ </div>
363
+
364
+ <!-- Input Area -->
365
+ <div class="p-4 lg:p-6 border-t border-slate-800/50 glass">
366
+ <div class="max-w-3xl mx-auto">
367
+ <!-- Model Selector -->
368
+ <div class="flex items-center gap-2 mb-3 overflow-x-auto pb-1">
369
+ <button onclick="setModel('qwen2.5-72b-instruct')" class="model-btn active px-3 py-1.5 rounded-full text-xs font-medium bg-sky-500/20 text-sky-300 border border-sky-500/30 transition-all whitespace-nowrap" data-model="qwen2.5-72b-instruct">
370
+ <i class="fas fa-bolt mr-1"></i>Qwen 2.5 72B
371
+ </button>
372
+ <button onclick="setModel('qwen2.5-14b-instruct')" class="model-btn px-3 py-1.5 rounded-full text-xs font-medium bg-slate-800 text-slate-400 border border-slate-700 hover:border-slate-600 transition-all whitespace-nowrap" data-model="qwen2.5-14b-instruct">
373
+ Qwen 2.5 14B
374
+ </button>
375
+ <button onclick="setModel('qwen2.5-7b-instruct')" class="model-btn px-3 py-1.5 rounded-full text-xs font-medium bg-slate-800 text-slate-400 border border-slate-700 hover:border-slate-600 transition-all whitespace-nowrap" data-model="qwen2.5-7b-instruct">
376
+ Qwen 2.5 7B
377
+ </button>
378
+ <button onclick="setModel('qwen2.5-coder-32b-instruct')" class="model-btn px-3 py-1.5 rounded-full text-xs font-medium bg-slate-800 text-slate-400 border border-slate-700 hover:border-slate-600 transition-all whitespace-nowrap" data-model="qwen2.5-coder-32b-instruct">
379
+ <i class="fas fa-code mr-1"></i>Coder 32B
380
+ </button>
381
+ </div>
382
+
383
+ <!-- Input Box -->
384
+ <div class="relative input-glow rounded-2xl bg-slate-800/80 border border-slate-700/50 transition-all duration-300">
385
+ <textarea
386
+ id="messageInput"
387
+ rows="1"
388
+ placeholder="Message Qwen..."
389
+ class="w-full bg-transparent text-slate-200 placeholder-slate-500 px-5 py-4 pr-14 resize-none focus:outline-none max-h-48 text-sm leading-relaxed"
390
+ onkeydown="handleKeyDown(event)"
391
+ oninput="autoResize(this)"
392
+ ></textarea>
393
+ <div class="absolute right-3 bottom-3 flex items-center gap-2">
394
+ <button onclick="clearInput()" class="p-2 text-slate-500 hover:text-slate-300 transition-colors rounded-lg hover:bg-slate-700/50">
395
+ <i class="fas fa-times"></i>
396
+ </button>
397
+ <button onclick="sendMessage()" id="sendBtn" class="p-2.5 bg-sky-500 hover:bg-sky-400 text-white rounded-xl transition-all duration-200 shadow-lg shadow-sky-500/20 hover:shadow-sky-500/30 disabled:opacity-50 disabled:cursor-not-allowed">
398
+ <i class="fas fa-paper-plane text-sm"></i>
399
+ </button>
400
+ </div>
401
+ </div>
402
+ <p class="text-center text-xs text-slate-600 mt-2">
403
+ Qwen can make mistakes. Consider checking important information.
404
+ </p>
405
+ </div>
406
+ </div>
407
+ </main>
408
+ </div>
409
+
410
+ <!-- Toast Notification -->
411
+ <div id="toast" class="fixed top-4 right-4 px-4 py-3 rounded-xl glass border border-slate-700/50 text-sm text-slate-200 transform translate-x-full transition-transform duration-300 z-50 flex items-center gap-2">
412
+ <i class="fas fa-check-circle text-emerald-400"></i>
413
+ <span id="toastMessage">Notification</span>
414
+ </div>
415
+
416
+ <script>
417
+ // State Management
418
+ let currentModel = 'qwen2.5-72b-instruct';
419
+ let chatHistory = [];
420
+ let currentChatId = null;
421
+ let isGenerating = false;
422
+ let abortController = null;
423
+
424
+ // Initialize
425
+ document.addEventListener('DOMContentLoaded', () => {
426
+ loadApiKey();
427
+ loadChatHistory();
428
+ createNewChat();
429
+ autoResize(document.getElementById('messageInput'));
430
+ });
431
+
432
+ // API Key Management
433
+ function saveApiKey() {
434
+ const key = document.getElementById('apiKeyInput').value.trim();
435
+ if (key) {
436
+ localStorage.setItem('hf_token', key);
437
+ showToast('API Key saved successfully');
438
+ }
439
+ }
440
+
441
+ function loadApiKey() {
442
+ const key = localStorage.getItem('hf_token');
443
+ if (key) {
444
+ document.getElementById('apiKeyInput').value = key;
445
+ }
446
+ }
447
+
448
+ function getApiKey() {
449
+ return localStorage.getItem('hf_token');
450
+ }
451
+
452
+ // Chat History Management
453
+ function loadChatHistory() {
454
+ const saved = localStorage.getItem('chatHistory');
455
+ if (saved) {
456
+ chatHistory = JSON.parse(saved);
457
+ renderChatHistory();
458
+ }
459
+ }
460
+
461
+ function saveChatHistory() {
462
+ localStorage.setItem('chatHistory', JSON.stringify(chatHistory));
463
+ }
464
+
465
+ function renderChatHistory() {
466
+ const container = document.getElementById('chatHistory');
467
+ container.innerHTML = chatHistory.map(chat => `
468
+ <div onclick="loadChat('${chat.id}')" class="group flex items-center gap-3 p-3 rounded-xl cursor-pointer transition-all duration-200 ${chat.id === currentChatId ? 'bg-sky-500/10 border border-sky-500/20' : 'hover:bg-slate-800/50 border border-transparent'}">
469
+ <div class="w-8 h-8 rounded-lg ${chat.id === currentChatId ? 'bg-sky-500/20' : 'bg-slate-800'} flex items-center justify-center flex-shrink-0">
470
+ <i class="fas fa-message ${chat.id === currentChatId ? 'text-sky-400' : 'text-slate-500'} text-xs"></i>
471
+ </div>
472
+ <div class="flex-1 min-w-0">
473
+ <p class="text-sm text-slate-300 truncate font-medium">${chat.title}</p>
474
+ <p class="text-xs text-slate-500 truncate">${new Date(chat.timestamp).toLocaleDateString()}</p>
475
+ </div>
476
+ <button onclick="event.stopPropagation(); deleteChat('${chat.id}')" class="opacity-0 group-hover:opacity-100 p-1.5 text-slate-500 hover:text-red-400 transition-all rounded-lg hover:bg-red-500/10">
477
+ <i class="fas fa-trash-alt text-xs"></i>
478
+ </button>
479
+ </div>
480
+ `).join('');
481
+ }
482
+
483
+ function createNewChat() {
484
+ currentChatId = Date.now().toString();
485
+ const newChat = {
486
+ id: currentChatId,
487
+ title: 'New Conversation',
488
+ messages: [],
489
+ timestamp: Date.now(),
490
+ model: currentModel
491
+ };
492
+ chatHistory.unshift(newChat);
493
+ saveChatHistory();
494
+ renderChatHistory();
495
+ renderMessages();
496
+
497
+ document.getElementById('welcomeScreen').classList.remove('hidden');
498
+ document.getElementById('messagesArea').classList.add('hidden');
499
+ }
500
+
501
+ function newChat() {
502
+ createNewChat();
503
+ document.getElementById('messageInput').focus();
504
+ }
505
+
506
+ function loadChat(id) {
507
+ currentChatId = id;
508
+ const chat = chatHistory.find(c => c.id === id);
509
+ if (chat) {
510
+ currentModel = chat.model || currentModel;
511
+ updateModelButtons();
512
+ renderMessages();
513
+ renderChatHistory();
514
+ }
515
+ }
516
+
517
+ function deleteChat(id) {
518
+ chatHistory = chatHistory.filter(c => c.id !== id);
519
+ saveChatHistory();
520
+ renderChatHistory();
521
+ if (currentChatId === id) {
522
+ createNewChat();
523
+ }
524
+ }
525
+
526
+ // Message Rendering
527
+ function renderMessages() {
528
+ const chat = chatHistory.find(c => c.id === currentChatId);
529
+ const messagesArea = document.getElementById('messagesArea');
530
+
531
+ if (!chat || chat.messages.length === 0) {
532
+ document.getElementById('welcomeScreen').classList.remove('hidden');
533
+ messagesArea.classList.add('hidden');
534
+ return;
535
+ }
536
+
537
+ document.getElementById('welcomeScreen').classList.add('hidden');
538
+ messagesArea.classList.remove('hidden');
539
+
540
+ messagesArea.innerHTML = chat.messages.map((msg, index) => `
541
+ <div class="message-appear flex gap-4 ${msg.role === 'user' ? 'flex-row-reverse' : ''}" style="animation-delay: ${index * 0.05}s">
542
+ <!-- Avatar -->
543
+ <div class="flex-shrink-0">
544
+ ${msg.role === 'user'
545
+ ? `<div class="w-8 h-8 rounded-full bg-gradient-to-br from-sky-500 to-violet-500 flex items-center justify-center">
546
+ <i class="fas fa-user text-white text-xs"></i>
547
+ </div>`
548
+ : `<div class="w-8 h-8 rounded-full bg-gradient-to-br from-emerald-500 to-teal-500 flex items-center justify-center">
549
+ <i class="fas fa-robot text-white text-xs"></i>
550
+ </div>`
551
+ }
552
+ </div>
553
+
554
+ <!-- Message Content -->
555
+ <div class="flex-1 ${msg.role === 'user' ? 'text-right' : ''} max-w-[85%] lg:max-w-[80%]">
556
+ <div class="inline-block text-left">
557
+ <div class="flex items-center gap-2 mb-1 ${msg.role === 'user' ? 'justify-end' : ''}">
558
+ <span class="text-xs font-medium ${msg.role === 'user' ? 'text-sky-400' : 'text-emerald-400'}">
559
+ ${msg.role === 'user' ? 'You' : 'Qwen'}
560
+ </span>
561
+ <span class="text-xs text-slate-600">${formatTime(msg.timestamp)}</span>
562
+ </div>
563
+
564
+ <div class="${msg.role === 'user'
565
+ ? 'bg-sky-500/10 border border-sky-500/20 text-slate-200'
566
+ : 'bg-slate-800/50 border border-slate-700/50 text-slate-300'}
567
+ rounded-2xl px-5 py-3.5 text-sm leading-relaxed markdown-body">
568
+ ${msg.role === 'assistant' ? renderMarkdown(msg.content) : escapeHtml(msg.content)}
569
+ </div>
570
+
571
+ ${msg.role === 'assistant' ? `
572
+ <div class="flex items-center gap-2 mt-2">
573
+ <button onclick="copyMessage('${escapeJs(msg.content)}')" class="tooltip p-1.5 text-slate-500 hover:text-sky-400 transition-colors rounded-lg hover:bg-slate-800" data-tooltip="Copy">
574
+ <i class="fas fa-copy text-xs"></i>
575
+ </button>
576
+ <button onclick="regenerateMessage(${index})" class="tooltip p-1.5 text-slate-500 hover:text-sky-400 transition-colors rounded-lg hover:bg-slate-800" data-tooltip="Regenerate">
577
+ <i class="fas fa-rotate-right text-xs"></i>
578
+ </button>
579
+ <button onclick="likeMessage(${index})" class="tooltip p-1.5 text-slate-500 hover:text-emerald-400 transition-colors rounded-lg hover:bg-slate-800" data-tooltip="Like">
580
+ <i class="fas fa-thumbs-up text-xs"></i>
581
+ </button>
582
+ </div>
583
+ ` : ''}
584
+ </div>
585
+ </div>
586
+ </div>
587
+ `).join('');
588
+
589
+ // Scroll to bottom
590
+ const container = document.getElementById('chatContainer');
591
+ container.scrollTop = container.scrollHeight;
592
+ }
593
+
594
+ // Markdown Renderer
595
+ function renderMarkdown(text) {
596
+ // Escape HTML first
597
+ let html = escapeHtml(text);
598
+
599
+ // Code blocks with language
600
+ html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
601
+ const language = lang || 'text';
602
+ const highlightedCode = syntaxHighlight(code.trim());
603
+ return `
604
+ <div class="code-block">
605
+ <div class="code-header">
606
+ <span class="text-xs text-slate-400 font-mono">${language}</span>
607
+ <button onclick="copyCode(this)" class="text-xs text-slate-500 hover:text-sky-400 transition-colors flex items-center gap-1">
608
+ <i class="fas fa-copy"></i> Copy
609
+ </button>
610
+ </div>
611
+ <div class="code-content">${highlightedCode}</div>
612
+ </div>
613
+ `;
614
+ });
615
+
616
+ // Inline code
617
+ html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
618
+
619
+ // Headers
620
+ html = html.replace(/^### (.*$)/gm, '<h3>$1</h3>');
621
+ html = html.replace(/^## (.*$)/gm, '<h2>$1</h2>');
622
+ html = html.replace(/^# (.*$)/gm, '<h1>$1</h1>');
623
+
624
+ // Bold and italic
625
+ html = html.replace(/\*\*\*(.*?)\*\*\*/g, '<strong><em>$1</em></strong>');
626
+ html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
627
+ html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
628
+
629
+ // Lists
630
+ html = html.replace(/^- (.*$)/gm, '<li>$1</li>');
631
+ html = html.replace(/(<li>.*<<\/li>\n)+/g, '<ul>$&</ul>');
632
+
633
+ // Numbered lists
634
+ html = html.replace(/^\d+\. (.*$)/gm, '<li>$1</li>');
635
+ html = html.replace(/(<li>.*<<\/li>\n)+/g, '<ol>$&</ol>');
636
+
637
+ // Links
638
+ html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
639
+
640
+ // Blockquotes
641
+ html = html.replace(/^> (.*$)/gm, '<blockquote>$1</blockquote>');
642
+
643
+ // Horizontal rules
644
+ html = html.replace(/^---$/gm, '<hr>');
645
+
646
+ // Paragraphs (split by double newline)
647
+ html = html.split('\n\n').map(p => {
648
+ if (p.trim() && !p.startsWith('<')) {
649
+ return `<p>${p.replace(/\n/g, '<br>')}</p>`;
650
+ }
651
+ return p;
652
+ }).join('');
653
+
654
+ return html;
655
+ }
656
+
657
+ // Syntax Highlighting
658
+ function syntaxHighlight(code) {
659
+ // Simple syntax highlighting
660
+ return code
661
+ .replace(/\b(def|class|return|if|else|elif|for|while|import|from|as|try|except|with|lambda|yield|async|await|const|let|var|function|return|if|else|for|while|class|import|export|try|catch)\b/g, '<span class="keyword">$1</span>')
662
+ .replace(/(".*?"|'.*?'|`.*?`)/g, '<span class="string">$1</span>')
663
+ .replace(/\b(\d+)\b/g, '<span class="number">$1</span>')
664
+ .replace(/(\w+)\(/g, '<span class="function">$1</span>(')
665
+ .replace(/(#.*$|\/\/.*$)/gm, '<span class="comment">$1</span>');
666
+ }
667
+
668
+ // Utility Functions
669
+ function escapeHtml(text) {
670
+ const div = document.createElement('div');
671
+ div.textContent = text;
672
+ return div.innerHTML;
673
+ }
674
+
675
+ function escapeJs(text) {
676
+ return text.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, '\\n');
677
+ }
678
+
679
+ function formatTime(timestamp) {
680
+ return new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
681
+ }
682
+
683
+ // UI Interactions
684
+ function setModel(model) {
685
+ currentModel = model;
686
+ updateModelButtons();
687
+ const chat = chatHistory.find(c => c.id === currentChatId);
688
+ if (chat) {
689
+ chat.model = model;
690
+ saveChatHistory();
691
+ }
692
+ }
693
+
694
+ function updateModelButtons() {
695
+ document.querySelectorAll('.model-btn').forEach(btn => {
696
+ if (btn.dataset.model === currentModel) {
697
+ btn.classList.add('bg-sky-500/20', 'text-sky-300', 'border-sky-500/30');
698
+ btn.classList.remove('bg-slate-800', 'text-slate-400', 'border-slate-700');
699
+ } else {
700
+ btn.classList.remove('bg-sky-500/20', 'text-sky-300', 'border-sky-500/30');
701
+ btn.classList.add('bg-slate-800', 'text-slate-400', 'border-slate-700');
702
+ }
703
+ });
704
+ }
705
+
706
+ function autoResize(textarea) {
707
+ textarea.style.height = 'auto';
708
+ textarea.style.height = Math.min(textarea.scrollHeight, 192) + 'px';
709
+ }
710
+
711
+ function handleKeyDown(e) {
712
+ if (e.key === 'Enter' && !e.shiftKey) {
713
+ e.preventDefault();
714
+ sendMessage();
715
+ }
716
+ }
717
+
718
+ function clearInput() {
719
+ const input = document.getElementById('messageInput');
720
+ input.value = '';
721
+ input.style.height = 'auto';
722
+ input.focus();
723
+ }
724
+
725
+ function sendQuickMessage(text) {
726
+ document.getElementById('messageInput').value = text;
727
+ autoResize(document.getElementById('messageInput'));
728
+ sendMessage();
729
+ }
730
+
731
+ // Message Actions
732
+ function copyMessage(content) {
733
+ navigator.clipboard.writeText(content).then(() => {
734
+ showToast('Copied to clipboard');
735
+ });
736
+ }
737
+
738
+ function copyCode(btn) {
739
+ const code = btn.closest('.code-block').querySelector('.code-content').textContent;
740
+ navigator.clipboard.writeText(code).then(() => {
741
+ const original = btn.innerHTML;
742
+ btn.innerHTML = '<i class="fas fa-check"></i> Copied';
743
+ setTimeout(() => btn.innerHTML = original, 2000);
744
+ });
745
+ }
746
+
747
+ function likeMessage(index) {
748
+ showToast('Thanks for your feedback!');
749
+ }
750
+
751
+ function regenerateMessage(index) {
752
+ const chat = chatHistory.find(c => c.id === currentChatId);
753
+ if (chat && index > 0) {
754
+ const userMessage = chat.messages[index - 1];
755
+ if (userMessage.role === 'user') {
756
+ chat.messages.splice(index - 1, 2);
757
+ saveChatHistory();
758
+ renderMessages();
759
+ sendMessageToAPI(userMessage.content);
760
+ }
761
+ }
762
+ }
763
+
764
+ // Send Message
765
+ async function sendMessage() {
766
+ const input = document.getElementById('messageInput');
767
+ const content = input.value.trim();
768
+
769
+ if (!content || isGenerating) return;
770
+
771
+ // Check API key
772
+ const apiKey = getApiKey();
773
+ if (!apiKey) {
774
+ showToast('Please enter your HuggingFace token first', 'error');
775
+ document.getElementById('apiKeyInput').focus();
776
+ return;
777
+ }
778
+
779
+ // Add user message
780
+ const chat = chatHistory.find(c => c.id === currentChatId);
781
+ const userMessage = {
782
+ role: 'user',
783
+ content: content,
784
+ timestamp: Date.now()
785
+ };
786
+ chat.messages.push(userMessage);
787
+
788
+ // Update title if first message
789
+ if (chat.messages.length === 1) {
790
+ chat.title = content.substring(0, 50) + (content.length > 50 ? '...' : '');
791
+ }
792
+
793
+ saveChatHistory();
794
+ renderChatHistory();
795
+ renderMessages();
796
+
797
+ // Clear input
798
+ input.value = '';
799
+ input.style.height = 'auto';
800
+
801
+ // Send to API
802
+ await sendMessageToAPI(content);
803
+ }
804
+
805
+ // API Communication
806
+ async function sendMessageToAPI(content) {
807
+ isGenerating = true;
808
+ const sendBtn = document.getElementById('sendBtn');
809
+ sendBtn.disabled = true;
810
+
811
+ // Add typing indicator
812
+ const chat = chatHistory.find(c => c.id === currentChatId);
813
+ const assistantMessage = {
814
+ role: 'assistant',
815
+ content: '',
816
+ timestamp: Date.now(),
817
+ isTyping: true
818
+ };
819
+ chat.messages.push(assistantMessage);
820
+ renderMessages();
821
+
822
+ // Show typing animation
823
+ const messagesArea = document.getElementById('messagesArea');
824
+ const lastMessage = messagesArea.lastElementChild;
825
+ const contentDiv = lastMessage.querySelector('.markdown-body');
826
+ contentDiv.innerHTML = `
827
+ <div class="flex items-center gap-2 py-2">
828
+ <span class="typing-dot"></span>
829
+ <span class="typing-dot" style="animation-delay: -0.16s"></span>
830
+ <span class="typing-dot" style="animation-delay: -0.32s"></span>
831
+ </div>
832
+ `;
833
+
834
+ try {
835
+ abortController = new AbortController();
836
+
837
+ const response = await fetch('https://api-inference.huggingface.co/models/Qwen/' + currentModel, {
838
+ method: 'POST',
839
+ headers: {
840
+ 'Authorization': `Bearer ${getApiKey()}`,
841
+ 'Content-Type': 'application/json'
842
+ },
843
+ body: JSON.stringify({
844
+ inputs: formatConversation(chat.messages.slice(0, -1)),
845
+ parameters: {
846
+ max_new_tokens: 2048,
847
+ temperature: 0.7,
848
+ top_p: 0.9,
849
+ return_full_text: false
850
+ }
851
+ }),
852
+ signal: abortController.signal
853
+ });
854
+
855
+ if (!response.ok) {
856
+ throw new Error(`HTTP error! status: ${response.status}`);
857
+ }
858
+
859
+ const data = await response.json();
860
+
861
+ // Remove typing indicator and add actual response
862
+ assistantMessage.isTyping = false;
863
+
864
+ if (Array.isArray(data) && data[0]?.generated_text) {
865
+ assistantMessage.content = data[0].generated_text;
866
+ } else if (data.generated_text) {
867
+ assistantMessage.content = data.generated_text;
868
+ } else {
869
+ throw new Error('Unexpected response format');
870
+ }
871
+
872
+ } catch (error) {
873
+ if (error.name === 'AbortError') {
874
+ assistantMessage.content = 'Generation stopped.';
875
+ } else {
876
+ assistantMessage.content = `Error: ${error.message}. Please check your API token and try again.`;
877
+ }
878
+ assistantMessage.isTyping = false;
879
+ } finally {
880
+ isGenerating = false;
881
+ sendBtn.disabled = false;
882
+ abortController = null;
883
+ saveChatHistory();
884
+ renderMessages();
885
+ }
886
+ }
887
+
888
+ function formatConversation(messages) {
889
+ let prompt = '';
890
+ for (const msg of messages) {
891
+ if (msg.role === 'user') {
892
+ prompt += `|<|im_start|>user\n${msg.content}|<|im_end|>\n`;
893
+ } else if (msg.role === 'assistant') {
894
+ prompt += `|<|im_start|>assistant\n${msg.content}|<|im_end|>\n`;
895
+ }
896
+ }
897
+ prompt += '|<|im_start|>assistant\n';
898
+ return prompt;
899
+ }
900
+
901
+ // Toast Notification
902
+ function showToast(message, type = 'success') {
903
+ const toast = document.getElementById('toast');
904
+ const toastMessage = document.getElementById('toastMessage');
905
+
906
+ toastMessage.textContent = message;
907
+ toast.querySelector('i').className = type === 'error' ? 'fas fa-exclamation-circle text-red-400' : 'fas fa-check-circle text-emerald-400';
908
+
909
+ toast.classList.remove('translate-x-full');
910
+ setTimeout(() => {
911
+ toast.classList.add('translate-x-full');
912
+ }, 3000);
913
+ }
914
+
915
+ // Sidebar Toggle (Mobile)
916
+ function toggleSidebar() {
917
+ const sidebar = document.getElementById('sidebar');
918
+ sidebar.classList.toggle('-translate-x-full');
919
+ }
920
+
921
+ // Stop Generation
922
+ function stopGeneration() {
923
+ if (abortController) {
924
+ abortController.abort();
925
+ }
926
+ }
927
+ </script>
928
+ </body>
929
+ </html>