AlibiAI commited on
Commit
68d260a
·
verified ·
1 Parent(s): 1cc374c

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +618 -19
index.html CHANGED
@@ -1,19 +1,618 @@
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>Chat</title>
7
+ <link rel="manifest" href="/manifest.json">
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
+ min-height: 100vh;
19
+ display: flex;
20
+ justify-content: center;
21
+ align-items: center;
22
+ padding: 20px;
23
+ }
24
+
25
+ .container {
26
+ background: rgba(255, 255, 255, 0.95);
27
+ backdrop-filter: blur(10px);
28
+ border-radius: 20px;
29
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
30
+ width: 100%;
31
+ max-width: 800px;
32
+ height: 600px;
33
+ display: flex;
34
+ flex-direction: column;
35
+ overflow: hidden;
36
+ animation: slideUp 0.5s ease-out;
37
+ }
38
+
39
+ @keyframes slideUp {
40
+ from {
41
+ opacity: 0;
42
+ transform: translateY(30px);
43
+ }
44
+ to {
45
+ opacity: 1;
46
+ transform: translateY(0);
47
+ }
48
+ }
49
+
50
+ .header {
51
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
52
+ color: white;
53
+ padding: 15px 20px;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: space-between;
57
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
58
+ }
59
+
60
+ .header .avatar {
61
+ width: 35px;
62
+ height: 35px;
63
+ border-radius: 50%;
64
+ background: #ffffff33;
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+ font-weight: bold;
69
+ margin-right: 10px;
70
+ }
71
+
72
+ .header h1 {
73
+ font-size: 20px;
74
+ font-weight: 500;
75
+ flex: 1;
76
+ }
77
+
78
+ .message-counter {
79
+ background: #ef4444;
80
+ color: white;
81
+ font-size: 12px;
82
+ padding: 4px 8px;
83
+ border-radius: 12px;
84
+ line-height: 1;
85
+ }
86
+
87
+ .status {
88
+ padding: 10px 20px;
89
+ background: #f7f9fc;
90
+ border-bottom: 1px solid #e1e8ed;
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 10px;
94
+ }
95
+
96
+ .status-indicator {
97
+ width: 10px;
98
+ height: 10px;
99
+ border-radius: 50%;
100
+ background: #fbbf24;
101
+ animation: pulse 2s infinite;
102
+ }
103
+
104
+ .status-indicator.ready {
105
+ background: #10b981;
106
+ animation: none;
107
+ }
108
+
109
+ .status-indicator.error {
110
+ background: #ef4444;
111
+ animation: none;
112
+ }
113
+
114
+ @keyframes pulse {
115
+ 0%, 100% {
116
+ opacity: 1;
117
+ }
118
+ 50% {
119
+ opacity: 0.5;
120
+ }
121
+ }
122
+
123
+ .chat-container {
124
+ flex: 1;
125
+ overflow-y: auto;
126
+ padding: 20px;
127
+ display: flex;
128
+ flex-direction: column;
129
+ gap: 15px;
130
+ }
131
+
132
+ .message {
133
+ display: flex;
134
+ gap: 10px;
135
+ animation: fadeIn 0.3s ease-in;
136
+ }
137
+
138
+ @keyframes fadeIn {
139
+ from {
140
+ opacity: 0;
141
+ transform: translateY(10px);
142
+ }
143
+ to {
144
+ opacity: 1;
145
+ transform: translateY(0);
146
+ }
147
+ }
148
+
149
+ .message.user {
150
+ flex-direction: row-reverse;
151
+ }
152
+
153
+ .message-bubble {
154
+ max-width: 70%;
155
+ padding: 12px 16px;
156
+ border-radius: 18px;
157
+ word-wrap: break-word;
158
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
159
+ }
160
+
161
+ .message.user .message-bubble {
162
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
163
+ color: white;
164
+ border-bottom-right-radius: 4px;
165
+ }
166
+
167
+ .message.ai .message-bubble {
168
+ background: #f1f3f5;
169
+ color: #1a1a1a;
170
+ border-bottom-left-radius: 4px;
171
+ }
172
+
173
+ .avatar {
174
+ width: 35px;
175
+ height: 35px;
176
+ border-radius: 50%;
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
180
+ font-weight: bold;
181
+ color: white;
182
+ flex-shrink: 0;
183
+ }
184
+
185
+ .message.user .avatar {
186
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
187
+ }
188
+
189
+ .message.ai .avatar {
190
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
191
+ }
192
+
193
+ .typing-indicator {
194
+ display: none;
195
+ padding: 12px 16px;
196
+ background: #f1f3f5;
197
+ border-radius: 18px;
198
+ border-bottom-left-radius: 4px;
199
+ width: fit-content;
200
+ }
201
+
202
+ .typing-indicator.active {
203
+ display: block;
204
+ }
205
+
206
+ .typing-indicator span {
207
+ display: inline-block;
208
+ width: 8px;
209
+ height: 8px;
210
+ border-radius: 50%;
211
+ background: #667eea;
212
+ margin: 0 2px;
213
+ animation: typing 1.4s infinite;
214
+ }
215
+
216
+ .typing-indicator span:nth-child(2) {
217
+ animation-delay: 0.2s;
218
+ }
219
+
220
+ .typing-indicator span:nth-child(3) {
221
+ animation-delay: 0.4s;
222
+ }
223
+
224
+ @keyframes typing {
225
+ 0%, 60%, 100% {
226
+ transform: translateY(0);
227
+ }
228
+ 30% {
229
+ transform: translateY(-10px);
230
+ }
231
+ }
232
+
233
+ .input-container {
234
+ padding: 20px;
235
+ background: #f7f9fc;
236
+ border-top: 1px solid #e1e8ed;
237
+ }
238
+
239
+ .input-wrapper {
240
+ display: flex;
241
+ gap: 10px;
242
+ }
243
+
244
+ .message-input {
245
+ flex: 1;
246
+ padding: 12px 16px;
247
+ border: 2px solid #e1e8ed;
248
+ border-radius: 25px;
249
+ font-size: 14px;
250
+ outline: none;
251
+ transition: all 0.3s ease;
252
+ }
253
+
254
+ .message-input:focus {
255
+ border-color: #667eea;
256
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
257
+ }
258
+
259
+ .send-button {
260
+ padding: 12px 24px;
261
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
262
+ color: white;
263
+ border: none;
264
+ border-radius: 25px;
265
+ font-size: 14px;
266
+ font-weight: 600;
267
+ cursor: pointer;
268
+ transition: all 0.3s ease;
269
+ display: flex;
270
+ align-items: center;
271
+ gap: 5px;
272
+ }
273
+
274
+ .send-button:hover:not(:disabled) {
275
+ transform: translateY(-2px);
276
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
277
+ }
278
+
279
+ .send-button:disabled {
280
+ opacity: 0.5;
281
+ cursor: not-allowed;
282
+ }
283
+
284
+ .error-message {
285
+ background: #fee;
286
+ color: #c00;
287
+ padding: 10px;
288
+ border-radius: 8px;
289
+ margin: 10px 20px;
290
+ display: none;
291
+ }
292
+
293
+ .error-message.show {
294
+ display: block;
295
+ }
296
+
297
+ .footer {
298
+ text-align: center;
299
+ padding: 10px;
300
+ font-size: 12px;
301
+ color: #666;
302
+ }
303
+
304
+ .footer a {
305
+ color: #667eea;
306
+ text-decoration: none;
307
+ }
308
+
309
+ @media (max-width: 600px) {
310
+ .container {
311
+ height: 100vh;
312
+ max-width: 100%;
313
+ border-radius: 0;
314
+ }
315
+
316
+ .message-bubble {
317
+ max-width: 85%;
318
+ }
319
+ }
320
+ </style>
321
+ </head>
322
+ <body>
323
+ <div class="container">
324
+ <div class="header">
325
+ <div class="message-counter">3</div>
326
+ <div class="avatar" id="friendAvatar"></div>
327
+ <h1 id="friendName">Loading...</h1>
328
+ </div>
329
+
330
+ <div class="status">
331
+ <div class="status-indicator" id="statusIndicator"></div>
332
+ <span id="statusText">Connecting...</span>
333
+ </div>
334
+
335
+ <div class="error-message" id="errorMessage"></div>
336
+
337
+ <div class="chat-container" id="chatContainer">
338
+ <div class="message ai">
339
+ <div class="avatar" id="aiAvatar">AI</div>
340
+ <div class="message-bubble">
341
+ Hey! What's up?
342
+ </div>
343
+ </div>
344
+ </div>
345
+
346
+ <div class="input-container">
347
+ <div class="input-wrapper">
348
+ <input
349
+ type="text"
350
+ class="message-input"
351
+ id="messageInput"
352
+ placeholder="Message..."
353
+ disabled
354
+ >
355
+ <button class="send-button" id="sendButton" disabled>
356
+ <span>Send</span>
357
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
358
+ <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>
359
+ </svg>
360
+ </button>
361
+ </div>
362
+ </div>
363
+
364
+ <div class="footer">
365
+ Powered by <a href="https://huggingface.co/HuggingFaceTB/SmolLM-135M-Instruct" target="_blank">SmolLM</a> from Hugging Face (Apache 2.0).
366
+ </div>
367
+ </div>
368
+
369
+ <script type="module">
370
+ // Register service worker for PWA
371
+ if ('serviceWorker' in navigator) {
372
+ window.addEventListener('load', () => {
373
+ navigator.serviceWorker.register('/sw.js')
374
+ .then(reg => console.log('Service Worker registered'))
375
+ .catch(err => console.error('Service Worker registration failed:', err));
376
+ });
377
+ }
378
+
379
+ import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@huggingface/[email protected]';
380
+
381
+ // Configure environment for browser usage
382
+ env.allowLocalModels = false;
383
+ env.useBrowserCache = true;
384
+
385
+ let generator = null;
386
+ let isProcessing = false;
387
+ let conversationHistory = [];
388
+ let modelId = /mobile|android|iphone|ipad/.test(navigator.userAgent.toLowerCase())
389
+ ? 'HuggingFaceTB/SmolLM-135M-Instruct'
390
+ : 'HuggingFaceTB/SmolLM2-360M-Instruct';
391
+
392
+ const chatContainer = document.getElementById('chatContainer');
393
+ const messageInput = document.getElementById('messageInput');
394
+ const sendButton = document.getElementById('sendButton');
395
+ const statusIndicator = document.getElementById('statusIndicator');
396
+ const statusText = document.getElementById('statusText');
397
+ const errorMessage = document.getElementById('errorMessage');
398
+ const friendNameElement = document.getElementById('friendName');
399
+ const friendAvatar = document.getElementById('friendAvatar');
400
+ const aiAvatar = document.getElementById('aiAvatar');
401
+
402
+ // Prompt for friend's name
403
+ let friendName = prompt('Who are you chatting with?', 'Alex');
404
+ friendName = friendName ? friendName.trim() : 'Alex';
405
+ friendNameElement.textContent = friendName;
406
+ friendAvatar.textContent = friendName[0].toUpperCase();
407
+ aiAvatar.textContent = friendName[0].toUpperCase();
408
+
409
+ // Check browser compatibility
410
+ function checkBrowserCompatibility() {
411
+ const ua = navigator.userAgent.toLowerCase();
412
+ const isMobile = /mobile|android|iphone|ipad/.test(ua);
413
+ const isChrome = ua.includes('chrome') && !ua.includes('edge');
414
+ const isEdge = ua.includes('edg/');
415
+ const isSafari = ua.includes('safari') && !ua.includes('chrome');
416
+ return { isMobile, isChrome, isEdge, isSafari };
417
+ }
418
+
419
+ // Check WebGPU support
420
+ async function checkWebGPU() {
421
+ if (!navigator.gpu) return false;
422
+ try {
423
+ const adapter = await navigator.gpu.requestAdapter();
424
+ return !!adapter;
425
+ } catch (e) {
426
+ console.error('WebGPU check failed:', e);
427
+ return false;
428
+ }
429
+ }
430
+
431
+ // Initialize the model with fallback
432
+ async function initializeModel(attemptSmallerModel = false) {
433
+ try {
434
+ statusText.textContent = `Connecting...`;
435
+
436
+ generator = await pipeline(
437
+ 'text-generation',
438
+ attemptSmallerModel ? 'HuggingFaceTB/SmolLM-135M-Instruct' : modelId
439
+ );
440
+
441
+ statusIndicator.classList.add('ready');
442
+ statusText.textContent = 'Connected';
443
+ messageInput.disabled = false;
444
+ sendButton.disabled = false;
445
+ messageInput.focus();
446
+
447
+ } catch (error) {
448
+ console.error('Error loading model:', error, error.stack);
449
+ if (!attemptSmallerModel && error.message.includes('memory')) {
450
+ console.warn('Memory error detected, trying smaller model...');
451
+ modelId = 'HuggingFaceTB/SmolLM-135M-Instruct';
452
+ initializeModel(true);
453
+ } else {
454
+ statusIndicator.classList.add('error');
455
+ statusText.textContent = 'Offline';
456
+ showError(`Oops, can't connect right now. Try refreshing?`);
457
+ }
458
+ }
459
+ }
460
+
461
+ // Start compatibility and model initialization
462
+ const browser = checkBrowserCompatibility();
463
+ if (browser.isMobile && (browser.isSafari || (!browser.isChrome && !browser.isEdge))) {
464
+ statusText.textContent = 'Connecting...';
465
+ showError('Hey, this works best on Chrome or Edge. Give one of those a try?');
466
+ initializeModel();
467
+ } else {
468
+ checkWebGPU().then(supported => {
469
+ if (!supported) {
470
+ statusText.textContent = 'Connecting...';
471
+ showError('Running a bit slow, but we’re good! Try a short message.');
472
+ initializeModel();
473
+ } else {
474
+ initializeModel();
475
+ }
476
+ });
477
+ }
478
+
479
+ function showError(message) {
480
+ errorMessage.textContent = message;
481
+ errorMessage.classList.add('show');
482
+ setTimeout(() => {
483
+ errorMessage.classList.remove('show');
484
+ }, 5000);
485
+ }
486
+
487
+ function addMessage(content, isUser = false) {
488
+ const messageDiv = document.createElement('div');
489
+ messageDiv.className = `message ${isUser ? 'user' : 'ai'}`;
490
+
491
+ const avatar = document.createElement('div');
492
+ avatar.className = 'avatar';
493
+ avatar.textContent = isUser ? 'You' : friendName[0].toUpperCase();
494
+
495
+ const bubble = document.createElement('div');
496
+ bubble.className = 'message-bubble';
497
+ bubble.textContent = content;
498
+
499
+ messageDiv.appendChild(avatar);
500
+ messageDiv.appendChild(bubble);
501
+
502
+ chatContainer.appendChild(messageDiv);
503
+ chatContainer.scrollTop = chatContainer.scrollHeight;
504
+
505
+ // Store in conversation history (limit to 2 for mobile)
506
+ if (isUser) {
507
+ conversationHistory.push(`user: ${content}`);
508
+ } else {
509
+ conversationHistory.push(`assistant: ${content}`);
510
+ }
511
+ if (conversationHistory.length > 2) {
512
+ conversationHistory = conversationHistory.slice(-2);
513
+ }
514
+ }
515
+
516
+ function showTypingIndicator() {
517
+ const typingDiv = document.createElement('div');
518
+ typingDiv.className = 'message ai';
519
+ typingDiv.id = 'typingIndicator';
520
+
521
+ const avatar = document.createElement('div');
522
+ avatar.className = 'avatar';
523
+ avatar.textContent = friendName[0].toUpperCase();
524
+
525
+ const indicator = document.createElement('div');
526
+ indicator.className = 'typing-indicator active';
527
+ indicator.innerHTML = '<span></span><span></span><span></span>';
528
+
529
+ typingDiv.appendChild(avatar);
530
+ typingDiv.appendChild(indicator);
531
+
532
+ chatContainer.appendChild(typingDiv);
533
+ chatContainer.scrollTop = chatContainer.scrollHeight;
534
+ }
535
+
536
+ function removeTypingIndicator() {
537
+ const indicator = document.getElementById('typingIndicator');
538
+ if (indicator) {
539
+ indicator.remove();
540
+ }
541
+ }
542
+
543
+ async function generateResponse(userMessage) {
544
+ if (!generator || isProcessing) return;
545
+
546
+ isProcessing = true;
547
+ sendButton.disabled = true;
548
+ messageInput.disabled = true;
549
+
550
+ showTypingIndicator();
551
+
552
+ try {
553
+ const history = conversationHistory.length > 0
554
+ ? conversationHistory.join('\n') + '\n'
555
+ : '';
556
+ const prompt = `<|im_start|>user\n${history}${userMessage}\n<|im_end|>\n<|im_start|>assistant\n`;
557
+
558
+ const output = await generator(prompt, {
559
+ max_new_tokens: 100,
560
+ temperature: 0.8,
561
+ top_p: 0.9,
562
+ return_full_text: false
563
+ });
564
+
565
+ removeTypingIndicator();
566
+
567
+ let response = output[0].generated_text.trim();
568
+ response = response.replace(/<\|im_end\|>|<\|im_start\|>.*$/g, '').trim();
569
+
570
+ if (response) {
571
+ addMessage(response);
572
+ } else {
573
+ addMessage("Not sure what to say... Wanna try that again?");
574
+ console.warn('Empty or invalid response received from model');
575
+ showError('Hmm, I got nothing. Try a different question?');
576
+ }
577
+
578
+ } catch (error) {
579
+ console.error('Error generating response:', error, error.stack);
580
+ removeTypingIndicator();
581
+ let errorMsg = error.message || 'Unknown error';
582
+ if (error.message.includes('memory')) {
583
+ errorMsg = 'Phone’s a bit overloaded. Close some apps?';
584
+ } else if (error.message.includes('WebGPU')) {
585
+ errorMsg = 'Need a better connection or browser. Try Chrome?';
586
+ }
587
+ addMessage("Oops, something’s up! Try again?");
588
+ showError(`Can’t reply right now: ${errorMsg}`);
589
+ } finally {
590
+ isProcessing = false;
591
+ sendButton.disabled = false;
592
+ messageInput.disabled = false;
593
+ messageInput.focus();
594
+ }
595
+ }
596
+
597
+ async function handleSend() {
598
+ const inputMessage = messageInput.value.trim();
599
+ if (!inputMessage || !generator || isProcessing) return;
600
+
601
+ addMessage(inputMessage, true);
602
+ messageInput.value = '';
603
+
604
+ await generateResponse(inputMessage);
605
+ }
606
+
607
+ // Event listeners
608
+ sendButton.addEventListener('click', handleSend);
609
+
610
+ messageInput.addEventListener('keypress', (e) => {
611
+ if (e.key === 'Enter' && !e.shiftKey) {
612
+ e.preventDefault();
613
+ handleSend();
614
+ }
615
+ });
616
+ </script>
617
+ </body>
618
+ </html>