protae5544 commited on
Commit
432be07
·
verified ·
1 Parent(s): e250b4d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +527 -656
index.html CHANGED
@@ -1,44 +1,46 @@
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 Chatbot with HuggingFace API - Enhanced</title>
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
8
- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
9
  <style>
10
  :root {
11
- --bg-primary: #0a0a0a;
12
- --bg-secondary: #1a1a1a;
13
- --bg-tertiary: #2a2a2a;
14
- --text-primary: #ffffff;
15
- --text-secondary: #b0b0b0;
16
- --accent-primary: #00d4ff;
17
- --accent-secondary: #ff6b35;
18
- --accent-tertiary: #7c4dff;
 
 
19
  --error: #ff4757;
20
  --success: #2ed573;
21
  --warning: #ffa502;
22
- --glass-bg: rgba(255, 255, 255, 0.05);
23
- --glass-border: rgba(255, 255, 255, 0.1);
24
  --shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.6);
25
  --shadow-xl: 0 25px 50px rgba(0, 0, 0, 0.8);
26
- --gradient-primary: linear-gradient(135deg, var(--accent-primary), var(--accent-tertiary));
27
- --gradient-secondary: linear-gradient(135deg, var(--accent-secondary), var(--accent-primary));
28
- --gradient-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
29
  }
30
 
31
  * {
32
  box-sizing: border-box;
 
 
33
  }
34
 
35
  body {
36
- margin: 0;
37
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Inter', sans-serif;
38
  background: var(--bg-primary);
39
- background-image:
40
- radial-gradient(ellipse at top left, rgba(0, 212, 255, 0.05) 0%, transparent 50%),
41
- radial-gradient(ellipse at bottom right, rgba(124, 77, 255, 0.05) 0%, transparent 50%);
42
  color: var(--text-primary);
43
  display: flex;
44
  justify-content: center;
@@ -46,29 +48,27 @@
46
  min-height: 100vh;
47
  overflow: hidden;
48
  -webkit-tap-highlight-color: transparent;
49
- backdrop-filter: blur(10px);
50
  }
51
 
52
  .app-container {
53
  width: 95%;
54
  max-width: 1400px;
55
  background: var(--glass-bg);
56
- backdrop-filter: blur(20px);
 
57
  border: 1px solid var(--glass-border);
58
- border-radius: 20px;
59
  box-shadow: var(--shadow-xl);
60
  display: flex;
61
  overflow: hidden;
62
  height: 90vh;
63
  position: relative;
64
- animation: slideIn 0.8s cubic-bezier(0.23, 1, 0.32, 1);
 
 
65
  }
66
 
67
  @keyframes slideIn {
68
- from {
69
- opacity: 0;
70
- transform: translateY(50px) scale(0.95);
71
- }
72
  to {
73
  opacity: 1;
74
  transform: translateY(0) scale(1);
@@ -76,34 +76,30 @@
76
  }
77
 
78
  .settings-panel {
79
- width: 320px;
80
- padding: 24px;
81
- background: var(--bg-secondary);
82
  backdrop-filter: blur(15px);
 
83
  border-right: 1px solid var(--glass-border);
84
- transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
 
 
85
  overflow-y: auto;
86
  scrollbar-width: thin;
87
  scrollbar-color: var(--accent-primary) transparent;
88
  }
89
 
90
- .settings-panel::-webkit-scrollbar {
91
- width: 6px;
92
- }
93
-
94
- .settings-panel::-webkit-scrollbar-track {
95
- background: transparent;
96
- }
97
-
98
- .settings-panel::-webkit-scrollbar-thumb {
99
- background: var(--accent-primary);
100
- border-radius: 3px;
101
- }
102
 
103
  .settings-panel.collapsed {
104
  width: 0;
105
  padding: 0;
106
  overflow: hidden;
 
 
107
  }
108
 
109
  .main-content {
@@ -111,58 +107,55 @@
111
  display: flex;
112
  flex-direction: column;
113
  position: relative;
114
- background: var(--bg-primary);
115
  }
116
 
117
  .form-field {
118
- margin-bottom: 20px;
119
- animation: fadeInUp 0.6s cubic-bezier(0.23, 1, 0.32, 1);
120
  }
121
 
122
  .form-field label {
123
  display: block;
124
- margin-bottom: 8px;
125
- font-size: 1rem;
126
  font-weight: 600;
127
  color: var(--text-secondary);
128
  text-transform: uppercase;
129
- letter-spacing: 0.5px;
130
  }
131
 
132
  .form-field select, .form-field textarea {
133
  width: 100%;
134
- padding: 12px 16px;
135
- border: 2px solid transparent;
136
- border-radius: 12px;
137
  background: var(--bg-tertiary);
138
  color: var(--text-primary);
139
  font-size: 1rem;
140
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
141
- backdrop-filter: blur(10px);
142
  }
143
 
144
  .form-field select:focus, .form-field textarea:focus {
145
  outline: none;
146
  border-color: var(--accent-primary);
147
- box-shadow: 0 0 20px rgba(0, 212, 255, 0.3);
148
  transform: translateY(-2px);
149
  }
150
 
151
- .form-field textarea {
152
- resize: vertical;
153
- min-height: 100px;
154
- }
155
 
156
  button {
157
- padding: 12px 24px;
158
  border: none;
159
- border-radius: 12px;
160
  background: var(--gradient-primary);
161
- color: var(--text-primary);
162
  cursor: pointer;
163
  font-size: 1rem;
164
- font-weight: 600;
165
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
166
  touch-action: manipulation;
167
  position: relative;
168
  overflow: hidden;
@@ -175,117 +168,110 @@
175
  left: -100%;
176
  width: 100%;
177
  height: 100%;
178
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
179
- transition: left 0.5s;
180
- }
181
-
182
- button:hover::before {
183
- left: 100%;
184
- }
185
-
186
- button:hover {
187
- transform: translateY(-3px);
188
- box-shadow: 0 10px 25px rgba(0, 212, 255, 0.4);
189
  }
190
 
191
- button:active {
192
- transform: translateY(-1px);
193
- }
194
 
195
- .material-icons {
196
- font-family: 'Material Icons';
197
- font-size: 28px;
198
- vertical-align: middle;
199
- }
200
 
201
  .chat-container {
202
  flex: 1;
203
- padding: 24px;
204
  overflow-y: auto;
205
  display: flex;
206
  flex-direction: column;
207
- gap: 16px;
208
  scrollbar-width: thin;
209
- scrollbar-color: var(--accent-primary) transparent;
 
 
210
  }
211
 
212
- .chat-container::-webkit-scrollbar {
213
- width: 8px;
214
- }
215
-
216
- .chat-container::-webkit-scrollbar-track {
217
- background: transparent;
218
- }
219
-
220
- .chat-container::-webkit-scrollbar-thumb {
221
- background: var(--accent-primary);
222
- border-radius: 4px;
223
- }
224
 
225
  .message {
226
- max-width: 80%;
227
- padding: 16px 20px;
228
- border-radius: 20px;
229
  font-size: 1rem;
230
- line-height: 1.6;
231
  position: relative;
232
- animation: messageSlide 0.5s cubic-bezier(0.23, 1, 0.32, 1);
233
- backdrop-filter: blur(10px);
 
234
  }
235
 
236
  @keyframes messageSlide {
237
- from {
238
- opacity: 0;
239
- transform: translateY(20px);
240
- }
241
- to {
242
- opacity: 1;
243
- transform: translateY(0);
244
- }
245
  }
246
 
247
  .message.user {
248
- background: var(--gradient-primary);
249
  align-self: flex-end;
250
- box-shadow: 0 8px 20px rgba(0, 212, 255, 0.3);
 
251
  }
252
 
253
  .message.ai {
254
- background: var(--glass-bg);
255
- border: 1px solid var(--glass-border);
256
  align-self: flex-start;
257
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  }
259
 
260
  .input-container {
261
  display: flex;
262
- gap: 12px;
263
- padding: 20px;
264
- background: var(--glass-bg);
265
- backdrop-filter: blur(20px);
266
  border-top: 1px solid var(--glass-border);
267
  align-items: center;
 
268
  }
269
 
270
  #userInput {
271
  flex: 1;
272
- padding: 16px 20px;
273
- border: 2px solid transparent;
274
- border-radius: 20px;
275
- background: var(--bg-secondary);
276
  color: var(--text-primary);
277
  resize: none;
278
  min-height: 60px;
279
- max-height: 150px;
280
  font-size: 1rem;
281
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
282
- backdrop-filter: blur(10px);
283
  }
284
 
285
  #userInput:focus {
286
  outline: none;
287
  border-color: var(--accent-primary);
288
- box-shadow: 0 0 25px rgba(0, 212, 255, 0.3);
289
  transform: translateY(-2px);
290
  }
291
 
@@ -297,12 +283,14 @@
297
  height: 60px;
298
  border-radius: 50%;
299
  background: var(--gradient-primary);
300
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
 
 
301
  }
302
 
303
  #sendButton:hover, #attachButton:hover {
304
- transform: translateY(-3px) scale(1.05);
305
- box-shadow: 0 12px 30px rgba(0, 212, 255, 0.5);
306
  }
307
 
308
  #sendButton:disabled {
@@ -310,152 +298,121 @@
310
  cursor: not-allowed;
311
  transform: none;
312
  box-shadow: none;
 
313
  }
314
 
315
  .spinner {
316
- width: 28px;
317
- height: 28px;
318
  border: 3px solid rgba(255, 255, 255, 0.3);
319
  border-top: 3px solid var(--accent-primary);
320
  border-radius: 50%;
321
  animation: spin 1s linear infinite;
322
  }
323
 
324
- @keyframes spin {
325
- to { transform: rotate(360deg); }
326
- }
327
 
328
  .notification {
329
- position: fixed;
330
- top: 24px;
331
- right: 24px;
332
- padding: 16px 24px;
333
- border-radius: 12px;
334
- color: #fff;
335
- font-size: 1rem;
336
- font-weight: 600;
337
- z-index: 1000;
338
- display: none;
339
- backdrop-filter: blur(15px);
340
- animation: notificationSlide 0.5s cubic-bezier(0.23, 1, 0.32, 1);
341
- }
342
-
343
- @keyframes notificationSlide {
344
- from {
345
  opacity: 0;
346
  transform: translateX(100px);
347
- }
348
- to {
349
- opacity: 1;
350
- transform: translateX(0);
351
- }
352
- }
353
-
354
- .error-message {
355
- background: linear-gradient(135deg, var(--error), #ff6b6b);
356
- box-shadow: 0 10px 25px rgba(255, 71, 87, 0.4);
357
  }
358
 
359
- .success-message {
360
- background: linear-gradient(135deg, var(--success), #5af78e);
361
- box-shadow: 0 10px 25px rgba(46, 213, 115, 0.4);
362
- }
363
- .carousel {
364
- touch-action: pan-x pan-y;
365
- transition: transform 0.6s ease-in-out;
366
  }
367
 
368
- .carousel-controls button {
369
- touch-action: manipulation;
370
- user-select: none; }
371
-
372
- .carousel.dragging {
373
- touch-action: none;
374
- }
375
 
376
  .carousel-container {
377
- perspective: 1200px;
378
- height: 100%;
379
- display: flex;
380
- flex-direction: column;
381
- align-items: center;
382
- justify-content: center;
383
- position: relative;
384
- user-select: none;
385
- padding: 24px;
386
  }
387
 
388
  .carousel {
389
- position: relative;
390
- width: 90%;
391
- max-width: 500px;
392
- height: 300px;
393
- transform-style: preserve-3d;
394
- transition: transform 0.6s cubic-bezier(0.23, 1, 0.32, 1);
395
- cursor: grab;
396
- }
397
-
398
- .carousel.dragging {
399
- cursor: grabbing;
400
  }
401
 
402
  .carousel-card {
403
- position: absolute;
404
- width: 100%;
405
- height: 100%;
406
- background: var(--glass-bg);
407
- backdrop-filter: blur(20px);
408
- border: 1px solid var(--glass-border);
409
- border-radius: 16px;
410
- padding: 24px;
411
- box-shadow: var(--shadow-lg);
412
- display: flex;
413
- flex-direction: column;
414
- gap: 12px;
415
- transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
 
416
  }
417
 
418
  .carousel-controls, .carousel-indicator {
419
  display: flex;
420
- gap: 16px;
421
- margin-top: 24px;
422
  }
423
 
424
  .carousel-dot {
425
- width: 14px;
426
- height: 14px;
427
  background: var(--bg-tertiary);
428
  border-radius: 50%;
429
  cursor: pointer;
430
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
431
  border: 2px solid transparent;
432
  }
433
 
434
  .carousel-dot.active {
435
  background: var(--accent-primary);
436
- box-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
437
- transform: scale(1.2);
438
  }
439
 
440
- .carousel-dot:hover {
441
- transform: scale(1.1);
442
- border-color: var(--accent-primary);
443
- }
444
 
445
  .mode-toggle {
446
  display: flex;
447
  background: var(--bg-secondary);
448
- backdrop-filter: blur(15px);
449
- border-radius: 20px;
450
- padding: 4px;
451
  position: relative;
452
- margin-bottom: 20px;
453
  border: 1px solid var(--glass-border);
454
  }
455
 
456
  .mode-toggle button {
457
  flex: 1;
458
- padding: 12px 20px;
459
  background: none;
460
  border: none;
461
  color: var(--text-secondary);
@@ -463,60 +420,49 @@
463
  z-index: 1;
464
  font-size: 1rem;
465
  font-weight: 600;
466
- border-radius: 16px;
467
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
 
468
  }
469
 
470
- .mode-toggle button.active {
471
- color: var(--text-primary);
472
- }
473
 
474
  .mode-toggle-slider {
475
  position: absolute;
476
- top: 4px;
477
- left: 4px;
478
- height: calc(100% - 8px);
 
479
  background: var(--gradient-primary);
480
- border-radius: 16px;
481
- transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
482
- box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3);
483
  }
484
 
485
  .dropzone {
486
- position: absolute;
487
- top: 0;
488
- left: 0;
489
- right: 0;
490
- bottom: 0;
491
- background: rgba(0, 212, 255, 0.1);
492
- backdrop-filter: blur(20px);
493
- border: 3px dashed var(--accent-primary);
494
- display: none;
495
- align-items: center;
496
- justify-content: center;
497
- color: var(--text-primary);
498
- font-size: 1.4rem;
499
- font-weight: 600;
500
- z-index: 10;
501
- border-radius: 20px;
502
- }
503
-
504
- .dropzone.active {
505
- display: flex;
506
- animation: pulse 2s infinite;
507
- }
508
-
509
- @keyframes pulse {
510
- 0%, 100% { opacity: 0.7; }
511
- 50% { opacity: 1; }
512
- }
513
 
514
  .file-name {
515
- font-size: 1rem;
516
  color: var(--accent-primary);
517
- margin-left: 12px;
518
  align-self: center;
519
- max-width: 200px;
520
  white-space: nowrap;
521
  overflow: hidden;
522
  text-overflow: ellipsis;
@@ -525,217 +471,149 @@
525
 
526
  .chip-container {
527
  display: flex;
528
- gap: 12px;
529
- margin-bottom: 20px;
530
  flex-wrap: wrap;
531
- padding: 0 24px;
532
  }
533
 
534
  .chip {
535
- padding: 10px 18px;
536
- background: var(--glass-bg);
537
- backdrop-filter: blur(15px);
538
  border: 1px solid var(--glass-border);
539
- border-radius: 20px;
540
  font-size: 0.9rem;
541
  font-weight: 600;
542
  cursor: pointer;
543
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
544
  color: var(--text-secondary);
545
  }
546
 
547
  .chip:hover {
548
- background: var(--accent-primary);
549
- color: var(--text-primary);
550
- transform: translateY(-2px);
551
- box-shadow: 0 6px 20px rgba(0, 212, 255, 0.4);
 
552
  }
553
 
554
  .floating-buttons {
555
  position: absolute;
556
- top: 20px;
557
- right: 20px;
558
  display: flex;
559
- gap: 12px;
560
- z-index: 100;
561
  }
562
 
563
  .floating-btn {
564
- width: 48px;
565
- height: 48px;
566
  border-radius: 50%;
567
- background: var(--glass-bg);
568
- backdrop-filter: blur(15px);
569
  border: 1px solid var(--glass-border);
570
  display: flex;
571
  align-items: center;
572
  justify-content: center;
573
- transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
574
  padding: 0;
 
 
575
  }
576
 
577
  .floating-btn:hover {
578
  background: var(--accent-primary);
579
- transform: translateY(-3px) scale(1.05);
580
- box-shadow: 0 8px 25px rgba(0, 212, 255, 0.4);
 
581
  }
582
 
583
  .code-tools {
584
  position: absolute;
585
- top: 12px;
586
- right: 12px;
587
  display: flex;
588
- gap: 8px;
 
 
589
  }
 
590
 
591
  .code-tools button {
592
- padding: 6px 12px;
593
- font-size: 0.85rem;
594
- background: var(--glass-bg);
595
- backdrop-filter: blur(10px);
596
- border: 1px solid var(--glass-border);
597
- }
598
-
599
- pre {
600
- position: relative;
601
- background: var(--bg-secondary);
602
  border: 1px solid var(--glass-border);
603
- padding: 20px;
604
- border-radius: 12px;
605
- overflow-x: auto;
606
- backdrop-filter: blur(10px);
607
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
608
  }
609
-
610
- code {
611
- font-family: 'Fira Code', 'Consolas', monospace;
612
- font-size: 0.9rem;
 
613
  }
614
 
615
  .image-preview {
616
  max-width: 100%;
617
- max-height: 300px;
618
- border-radius: 12px;
619
- margin-top: 12px;
620
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
 
621
  }
622
 
623
  @keyframes fadeInUp {
624
- from {
625
- opacity: 0;
626
- transform: translateY(30px);
627
- }
628
- to {
629
- opacity: 1;
630
- transform: translateY(0);
631
- }
632
  }
633
 
634
  @media (max-width: 768px) {
635
- .app-container {
636
- flex-direction: column;
637
- height: auto;
638
- min-height: 100vh;
639
- border-radius: 0;
640
- width: 100%;
641
- }
642
-
643
- .settings-panel {
644
- width: 100%;
645
- max-height: 40vh;
646
- border-right: none;
647
- border-bottom: 1px solid var(--glass-border);
648
- }
649
-
650
- .settings-panel.collapsed {
651
- max-height: 0;
652
- }
653
-
654
- .chat-container {
655
- max-height: 50vh;
656
- padding: 16px;
657
- }
658
-
659
- .input-container {
660
- flex-wrap: wrap;
661
- gap: 12px;
662
- padding: 16px;
663
- }
664
-
665
- #userInput {
666
- width: 100%;
667
- margin-bottom: 12px;
668
- min-height: 50px;
669
- }
670
-
671
- #sendButton, #attachButton {
672
- width: 56px;
673
- height: 56px;
674
- }
675
-
676
- .carousel {
677
- width: 95%;
678
- height: 250px;
679
- }
680
-
681
- .floating-buttons {
682
- top: 12px;
683
- right: 12px;
684
- }
685
-
686
- .floating-btn {
687
- width: 44px;
688
- height: 44px;
689
- }
690
-
691
- .material-icons {
692
- font-size: 24px;
693
- }
694
  }
695
-
696
  @media (max-width: 480px) {
697
- .chip-container {
698
- padding: 0 16px;
699
- }
700
-
701
- .chip {
702
- font-size: 0.8rem;
703
- padding: 8px 14px;
704
- }
705
-
706
- .form-field select, .form-field textarea, #userInput {
707
- font-size: 16px;
708
- }
709
  }
710
  </style>
711
  </head>
712
  <body>
713
  <div class="app-container" id="appContainer">
714
  <div class="settings-panel" id="settingsPanel">
715
- <h2 style="font-size: 1.4rem; margin-bottom: 24px; background: var(--gradient-primary); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">⚙️ Settings</h2>
716
  <div class="form-field">
717
- <label>🤖 Main Model:</label>
718
  <select id="modelSelect">
719
- <option value="Qwen/Qwen2.5-Coder-32B-Instruct">Qwen2.5-Coder 32B (Code/Text)</option>
720
- <option value="Qwen/Qwen2-VL-72B-Instruct">Qwen2-VL 72B (Text/Image)</option>
721
- <option value="microsoft/Florence-2-large">Florence-2 Large (Text/Image)</option>
722
- <option value="mistralai/Mixtral-8x7B-Instruct-v0.1">Mixtral 8x7B (Text)</option>
723
- <option value="meta-llama/Llama-2-70b-chat-hf">Llama-2 70B (Chat)</option>
724
- </select>
725
- <p style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 8px;">
726
- 💡 Choose a model. Use VL or Florence for image processing.
727
  </p>
728
  </div>
729
  <div class="form-field">
730
- <label>👁️ OCR Model (for images):</label>
731
  <select id="ocrModelSelect">
732
- <option value="none">None</option>
733
- <option value="scb10x/typhoon-v1.5x-72b-instruct">Typhoon 1.5x 72B</option>
734
- <option value="microsoft/trocr-base-printed">TrOCR Base</option>
735
- </select>
736
  </div>
737
- <button id="saveSettingsBtn">💾 Save Settings</button>
738
  </div>
 
739
  <div class="main-content">
740
  <div class="floating-buttons">
741
  <button id="settingsToggle" class="floating-btn">
@@ -750,25 +628,25 @@
750
  </div>
751
 
752
  <div class="mode-toggle">
753
- <button id="carouselModeBtn" class="active">🎛️ Prompt Editor</button>
754
- <button id="chatModeBtn">💬 Chat</button>
755
  <div class="mode-toggle-slider" id="modeToggleSlider"></div>
756
  </div>
757
 
758
  <div id="carouselMode">
759
  <div class="carousel-container">
760
  <div class="carousel" id="promptCarousel">
761
- <div class="carousel-card" style="transform: rotateY(0deg) translateZ(500px);">
762
  <label>🎯 Primary System Prompt:</label>
763
- <textarea id="primarySystemPrompt" placeholder="Enter primary system prompt for the AI assistant...">You are a powerful AI assistant that excels at understanding code, images, and technical content. Provide clear, accurate, and helpful responses. Focus on practical solutions and detailed explanations.</textarea>
764
  </div>
765
- <div class="carousel-card" style="transform: rotateY(60deg) translateZ(500px); opacity: 0.8;">
766
  <label>👁️ OCR System Prompt:</label>
767
- <textarea id="ocrSystemPrompt" placeholder="Enter OCR system prompt for image text extraction...">Extract all text from the provided image clearly and accurately. Preserve formatting, structure, and layout when possible. If text is unclear, indicate uncertain parts.</textarea>
768
  </div>
769
- <div class="carousel-card" style="transform: rotateY(120deg) translateZ(500px); opacity: 0.8;">
770
  <label>💻 Code Template:</label>
771
- <textarea id="codeTemplate" placeholder="Enter code formatting template...">```javascript
772
  // Enhanced code implementation
773
  function solution() {
774
  // Your optimized code here
@@ -776,17 +654,17 @@ function solution() {
776
  }
777
  ```</textarea>
778
  </div>
779
- <div class="carousel-card" style="transform: rotateY(180deg) translateZ(500px); opacity: 0.8;">
780
  <label>📋 Additional Instructions:</label>
781
- <textarea id="additionalInstructions" placeholder="Enter additional instructions for the AI...">Always provide working, tested code examples. Include error handling and optimization suggestions. Explain complex concepts step by step. Use modern best practices.</textarea>
782
  </div>
783
- <div class="carousel-card" style="transform: rotateY(240deg) translateZ(500px); opacity: 0.8;">
784
  <label>🚀 Prompt Prefix:</label>
785
- <textarea id="promptPrefix" placeholder="Enter text to prepend to all prompts...">Please analyze the following request carefully and provide a comprehensive solution:</textarea>
786
  </div>
787
- <div class="carousel-card" style="transform: rotateY(300deg) translateZ(500px); opacity: 0.8;">
788
  <label>✨ Prompt Suffix:</label>
789
- <textarea id="promptSuffix" placeholder="Enter text to append to all prompts...">Ensure your response is complete, accurate, and includes practical examples where applicable.</textarea>
790
  </div>
791
  </div>
792
  <div class="carousel-controls">
@@ -801,28 +679,29 @@ function solution() {
801
  <div class="carousel-dot" data-index="4"></div>
802
  <div class="carousel-dot" data-index="5"></div>
803
  </div>
804
- <button id="applyPromptBtn" style="margin-top: 24px;">✅ Apply Prompts & Switch to Chat</button>
805
  </div>
806
  </div>
807
 
808
- <div id="chatMode" style="display: none;">
809
- <div class="chip-container" id="quickPrompts">
810
- <span class="chip" data-prompt="Explain this code in detail">🔍 Explain Code</span>
811
- <span class="chip" data-prompt="Generate a complete function">⚡ Generate Function</span>
812
- <span class="chip" data-prompt="Debug and fix this issue">🐛 Debug Code</span>
813
- <span class="chip" data-prompt="Optimize this for performance">🚀 Optimize</span>
814
- <span class="chip" data-prompt="Add error handling">🛡️ Error Handling</span>
815
- <span class="chip" data-prompt="Convert to modern syntax">✨ Modernize</span>
816
  </div>
817
- <div class="chat-container" id="chatContainer"></div>
 
818
  <div class="input-container">
819
  <button id="attachButton"><span class="material-icons">attach_file</span></button>
820
  <input type="file" id="fileInput" multiple accept="image/*" style="display: none;">
821
  <span class="file-name" id="fileName"></span>
822
- <textarea id="userInput" placeholder="Type your message here... (Shift+Enter for new line)"></textarea>
823
  <button id="sendButton"><span class="material-icons">send</span></button>
824
  </div>
825
- <div class="dropzone" id="dropzone">📁 Drop files here to attach them</div>
826
  </div>
827
 
828
  <div class="notification error-message" id="errorMessage">
@@ -834,14 +713,14 @@ function solution() {
834
  </div>
835
  </div>
836
 
837
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
838
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
839
  <script>
840
  // Global variables
841
  let attachedFiles = [];
842
  let currentCarouselIndex = 0;
843
  let isLoading = false;
844
- let isDragging = false;
845
 
846
  // Initialize the application
847
  document.addEventListener('DOMContentLoaded', function() {
@@ -849,7 +728,8 @@ function solution() {
849
  setupEventListeners();
850
  setupCarousel();
851
  setupDropzone();
852
- hljs.highlightAll();
 
853
  });
854
 
855
  // Settings management
@@ -884,76 +764,29 @@ function solution() {
884
  localStorage.setItem('additionalInstructions', document.getElementById('additionalInstructions').value);
885
  localStorage.setItem('promptPrefix', document.getElementById('promptPrefix').value);
886
  localStorage.setItem('promptSuffix', document.getElementById('promptSuffix').value);
887
- showNotification('Settings saved successfully!', 'success');
888
  }
889
 
890
  // Event listeners setup
891
  function setupEventListeners() {
892
- let startX = 0;
893
- let startY = 0;
894
- let lastTranslateX = 0;
895
- let lastTranslateY = 0;
896
- let isDragging = false;
897
-
898
- document.getElementById('carousel').addEventListener('touchstart', (e) => {
899
- isDragging = true;
900
- startX = e.touches[0].clientX;
901
- startY = e.touches[0].clientY;
902
- });
903
-
904
- document.getElementById('carousel').addEventListener('touchmove', (e) => {
905
- if (!isDragging) return;
906
-
907
- const dx = e.touches[0].clientX - startX;
908
- const dy = e.touches[0].clientY - startY;
909
-
910
- const newX = lastTranslateX + dx;
911
- const newY = lastTranslateY + dy;
912
-
913
- document.getElementById('carousel').style.transform = `translate3D(${newX}px, ${newY}px, 0)`;
914
-
915
- startX = e.touches[0].clientX;
916
- startY = e.touches[0].clientY;
917
- }, { passive: false });
918
-
919
- document.getElementById('carousel').addEventListener('touchend', () => {
920
- isDragging = false;
921
- lastTranslateX = parseFloat(document.getElementById('carousel').style.transform.match(/-?\d+(\.\d+)?px/g)[0]);
922
- lastTranslateY = parseFloat(document.getElementById('carousel').style.transform.match(/-?\d+(\.\d+)?px/g)[1]);
923
- });
924
  // Mode toggle
925
  document.getElementById('carouselModeBtn').addEventListener('click', () => switchMode('carousel'));
926
  document.getElementById('chatModeBtn').addEventListener('click', () => switchMode('chat'));
927
-
928
  // Settings
929
  document.getElementById('saveSettingsBtn').addEventListener('click', saveSettings);
930
  document.getElementById('settingsToggle').addEventListener('click', toggleSettings);
931
-
932
- // Carousel controls
933
- document.getElementById('prevCard').addEventListener('touchstart', () => rotateCarousel(-1, 0));
934
- document.getElementById('nextCard').addEventListener('touchstart', () => rotateCarousel(1, 0));
935
-
936
- document.getElementById('prevCard').addEventListener('touchmove', (e) => {
937
- const dx = e.touches[0].clientX - startX;
938
- if (dx > 50) rotateCarousel(-1, 0);
939
- });
940
-
941
- document.getElementById('nextCard').addEventListener('touchmove', (e) => {
942
- const dx = e.touches[0].clientX - startX;
943
- if (dx < -50) rotateCarousel(1, 0);
944
- });
945
  document.getElementById('applyPromptBtn').addEventListener('click', applyPromptsAndSwitchToChat);
946
-
947
  // Chat controls
948
  document.getElementById('sendButton').addEventListener('click', sendMessage);
949
  document.getElementById('attachButton').addEventListener('click', () => document.getElementById('fileInput').click());
950
  document.getElementById('fileInput').addEventListener('change', handleFileSelect);
951
  document.getElementById('userInput').addEventListener('keydown', handleKeyDown);
952
-
953
  // Other controls
954
  document.getElementById('fullscreenToggle').addEventListener('click', toggleFullscreen);
955
  document.getElementById('refreshBtn').addEventListener('click', refreshApp);
956
-
957
  // Quick prompts
958
  document.querySelectorAll('.chip').forEach(chip => {
959
  chip.addEventListener('click', (e) => {
@@ -962,7 +795,6 @@ function solution() {
962
  document.getElementById('userInput').focus();
963
  });
964
  });
965
-
966
  // Carousel dots
967
  document.querySelectorAll('.carousel-dot').forEach(dot => {
968
  dot.addEventListener('click', (e) => {
@@ -981,87 +813,90 @@ function solution() {
981
  const slider = document.getElementById('modeToggleSlider');
982
 
983
  if (mode === 'carousel') {
984
- carouselMode.style.display = 'block';
985
  chatMode.style.display = 'none';
986
  carouselBtn.classList.add('active');
987
  chatBtn.classList.remove('active');
988
- slider.style.width = '50%';
989
- slider.style.left = '4px';
990
  } else {
991
  carouselMode.style.display = 'none';
992
- chatMode.style.display = 'block';
993
  carouselBtn.classList.remove('active');
994
  chatBtn.classList.add('active');
995
- slider.style.width = '50%';
996
- slider.style.left = '50%';
997
  }
998
  }
999
 
1000
-
1001
- // Carousel functionality
1002
  function setupCarousel() {
1003
- updateCarouselPosition();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
  }
1005
 
 
1006
  function rotateCarousel(direction) {
1007
  const totalCards = 6;
1008
  currentCarouselIndex = (currentCarouselIndex + direction + totalCards) % totalCards;
1009
  updateCarouselPosition();
1010
  }
1011
- function rotateCarousel(dx, dy) {
1012
- const totalCards = 6;
1013
- const cardWidth = 100 / totalCards;
1014
- const cardHeight = 100 / totalCards;
1015
-
1016
- let newIndex = currentCarouselIndex;
1017
-
1018
- if (dx > 0) newIndex--;
1019
- if (dx < 0) newIndex++;
1020
- if (dy > 0) newIndex++;
1021
- if (dy < 0) newIndex--;
1022
-
1023
- newIndex = (newIndex + totalCards) % totalCards;
1024
-
1025
- currentCarouselIndex = newIndex;
1026
- updateCarouselPosition();
1027
- }
1028
  function goToCarouselIndex(index) {
1029
  currentCarouselIndex = index;
1030
  updateCarouselPosition();
1031
  }
1032
 
1033
  function updateCarouselPosition() {
1034
- const carousel = document.getElementById('promptCarousel');
1035
- const cards = carousel.querySelectorAll('.carousel-card');
1036
- const dots = document.querySelectorAll('.carousel-dot');
1037
-
1038
- cards.forEach((card, index) => {
1039
- const angle = (index - currentCarouselIndex) * 60;
1040
- const isActive = index === currentCarouselIndex;
1041
-
1042
- card.style.transform = `rotateY(${angle}deg) translateZ(500px)`;
1043
- card.style.opacity = isActive ? '1' : '0.6';
1044
- card.style.zIndex = isActive ? '10' : '1';
1045
- });
 
 
 
 
1046
 
1047
- dots.forEach((dot, index) => {
1048
- dot.classList.toggle('active', index === currentCarouselIndex);
1049
- });
1050
  }
1051
 
 
1052
  function applyPromptsAndSwitchToChat() {
1053
  saveSettings();
1054
  switchMode('chat');
1055
- showNotification('Prompts applied successfully!', 'success');
1056
  }
1057
 
1058
  // File handling
1059
  function setupDropzone() {
1060
  const dropzone = document.getElementById('dropzone');
1061
- const chatMode = document.getElementById('chatMode');
1062
 
1063
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
1064
- chatMode.addEventListener(eventName, preventDefaults, false);
1065
  });
1066
 
1067
  function preventDefaults(e) {
@@ -1070,17 +905,22 @@ function solution() {
1070
  }
1071
 
1072
  ['dragenter', 'dragover'].forEach(eventName => {
1073
- chatMode.addEventListener(eventName, () => dropzone.classList.add('active'), false);
 
 
 
 
1074
  });
1075
 
1076
  ['dragleave', 'drop'].forEach(eventName => {
1077
- chatMode.addEventListener(eventName, () => dropzone.classList.remove('active'), false);
1078
  });
1079
 
1080
- chatMode.addEventListener('drop', handleDrop, false);
1081
  }
1082
 
1083
  function handleDrop(e) {
 
1084
  const dt = e.dataTransfer;
1085
  const files = dt.files;
1086
  handleFiles(files);
@@ -1092,8 +932,11 @@ function solution() {
1092
  }
1093
 
1094
  function handleFiles(files) {
1095
- attachedFiles = Array.from(files);
1096
  updateFileDisplay();
 
 
 
1097
  }
1098
 
1099
  function updateFileDisplay() {
@@ -1103,7 +946,7 @@ function solution() {
1103
  } else if (attachedFiles.length === 1) {
1104
  fileName.textContent = attachedFiles[0].name;
1105
  } else {
1106
- fileName.textContent = `${attachedFiles.length} files selected`;
1107
  }
1108
  }
1109
 
@@ -1118,151 +961,169 @@ function solution() {
1118
  async function sendMessage() {
1119
  const userInput = document.getElementById('userInput');
1120
  const message = userInput.value.trim();
1121
-
1122
  if (!message && attachedFiles.length === 0) return;
1123
  if (isLoading) return;
1124
 
 
1125
  const chatContainer = document.getElementById('chatContainer');
1126
-
 
1127
  // Add user message
1128
- if (message) {
1129
- addMessage(message, 'user');
1130
  }
1131
 
1132
- // Add file previews
1133
- if (attachedFiles.length > 0) {
1134
- for (const file of attachedFiles) {
1135
- if (file.type.startsWith('image/')) {
1136
- const imageUrl = URL.createObjectURL(file);
1137
- addImageMessage(imageUrl, 'user');
1138
- }
1139
- }
 
1140
  }
1141
 
1142
- userInput.value = '';
1143
- setLoading(true);
1144
 
1145
  try {
1146
- const response = await callHuggingFaceAPI(message, attachedFiles);
1147
  addMessage(response, 'ai');
1148
  } catch (error) {
1149
  console.error('Error:', error);
1150
- addMessage('Sorry, there was an error processing your request. Please try again.', 'ai');
1151
  showNotification('Error: ' + error.message, 'error');
1152
  } finally {
1153
  setLoading(false);
1154
- attachedFiles = [];
1155
- updateFileDisplay();
1156
  }
1157
-
1158
- chatContainer.scrollTop = chatContainer.scrollHeight;
1159
  }
1160
 
1161
  async function callHuggingFaceAPI(message, files) {
1162
- const model = document.getElementById('modelSelect').value;
1163
- const primaryPrompt = document.getElementById('primarySystemPrompt').value;
1164
- const prefix = document.getElementById('promptPrefix').value;
1165
- const suffix = document.getElementById('promptSuffix').value;
1166
-
1167
- let fullPrompt = `${primaryPrompt}\n\n${prefix}\n\n${message}\n\n${suffix}`;
1168
-
1169
- // Handle OCR for images if needed
1170
- if (files.length > 0) {
1171
  const ocrModel = document.getElementById('ocrModelSelect').value;
1172
- if (ocrModel !== 'none') {
1173
- for (const file of files) {
1174
- if (file.type.startsWith('image/')) {
1175
- try {
1176
- const ocrText = await performOCR(file, ocrModel);
1177
- fullPrompt += `\n\nExtracted text from image: ${ocrText}`;
1178
- } catch (error) {
1179
- console.warn('OCR failed:', error);
1180
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1181
  }
1182
- }
1183
  }
1184
- }
1185
 
1186
- const response = await fetch(`https://api-inference.huggingface.co/models/${model}`, {
1187
- method: 'POST',
1188
- headers: {
1189
- 'Authorization': 'Bearer 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', // Replace with actual token
1190
- 'Content-Type': 'application/json',
1191
- },
1192
- body: JSON.stringify({
1193
- inputs: fullPrompt,
1194
- parameters: {
1195
- max_new_tokens: 2048,
1196
- temperature: 0.7,
1197
- top_p: 0.9,
1198
- do_sample: true
1199
- }
1200
- })
1201
- });
1202
 
1203
- if (!response.ok) {
1204
- throw new Error(`HTTP error! status: ${response.status}`);
1205
- }
 
1206
 
1207
- const data = await response.json();
1208
-
1209
- if (data.error) {
1210
- throw new Error(data.error);
1211
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
1212
 
1213
- return data[0]?.generated_text || data.choices?.[0]?.message?.content || 'No response generated';
 
 
 
 
 
 
 
 
 
 
 
1214
  }
1215
 
1216
  async function performOCR(file, ocrModel) {
1217
- const formData = new FormData();
1218
- formData.append('file', file);
1219
-
1220
- const response = await fetch(`https://api-inference.huggingface.co/models/${ocrModel}`, {
1221
- method: 'POST',
1222
- headers: {
1223
- 'Authorization': 'Bearer hf_your_token_here', // Replace with actual token
1224
- },
1225
- body: formData
1226
- });
1227
 
1228
- if (!response.ok) {
1229
- throw new Error(`OCR failed: ${response.status}`);
1230
- }
 
1231
 
1232
- const data = await response.json();
1233
- return data.generated_text || data.text || 'Could not extract text';
 
1234
  }
1235
 
 
1236
  function addMessage(content, sender) {
1237
  const chatContainer = document.getElementById('chatContainer');
1238
  const messageDiv = document.createElement('div');
1239
  messageDiv.className = `message ${sender}`;
1240
-
1241
  if (sender === 'ai') {
1242
  messageDiv.innerHTML = marked.parse(content);
1243
- // Re-highlight code blocks
1244
- messageDiv.querySelectorAll('pre code').forEach(block => {
1245
- hljs.highlightElement(block);
1246
- addCodeTools(block.parentElement);
1247
  });
1248
  } else {
1249
- messageDiv.textContent = content;
1250
  }
1251
-
1252
  chatContainer.appendChild(messageDiv);
1253
  chatContainer.scrollTop = chatContainer.scrollHeight;
1254
  }
1255
 
 
1256
  function addImageMessage(imageUrl, sender) {
1257
  const chatContainer = document.getElementById('chatContainer');
1258
  const messageDiv = document.createElement('div');
1259
  messageDiv.className = `message ${sender}`;
1260
-
1261
  const img = document.createElement('img');
1262
  img.src = imageUrl;
1263
  img.className = 'image-preview';
1264
- img.alt = 'Uploaded image';
1265
-
 
1266
  messageDiv.appendChild(img);
1267
  chatContainer.appendChild(messageDiv);
1268
  chatContainer.scrollTop = chatContainer.scrollHeight;
@@ -1271,32 +1132,27 @@ function solution() {
1271
  function addCodeTools(preElement) {
1272
  const toolsDiv = document.createElement('div');
1273
  toolsDiv.className = 'code-tools';
1274
-
1275
  const copyBtn = document.createElement('button');
1276
- copyBtn.textContent = 'Copy';
1277
  copyBtn.onclick = () => {
1278
- navigator.clipboard.writeText(preElement.textContent);
1279
- showNotification('Code copied to clipboard!', 'success');
 
 
1280
  };
1281
-
1282
  toolsDiv.appendChild(copyBtn);
1283
- preElement.style.position = 'relative';
1284
- preElement.appendChild(toolsDiv);
1285
  }
1286
 
1287
  function setLoading(loading) {
1288
  isLoading = loading;
1289
  const sendButton = document.getElementById('sendButton');
1290
- const spinner = sendButton.querySelector('.spinner');
1291
  const icon = sendButton.querySelector('.material-icons');
1292
-
1293
  if (loading) {
1294
- if (!spinner) {
1295
- const spinnerDiv = document.createElement('div');
1296
- spinnerDiv.className = 'spinner';
1297
- sendButton.innerHTML = '';
1298
- sendButton.appendChild(spinnerDiv);
1299
- }
1300
  sendButton.disabled = true;
1301
  } else {
1302
  sendButton.innerHTML = '<span class="material-icons">send</span>';
@@ -1306,15 +1162,18 @@ function solution() {
1306
 
1307
  // Utility functions
1308
  function toggleSettings() {
1309
- const settingsPanel = document.getElementById('settingsPanel');
1310
- settingsPanel.classList.toggle('collapsed');
1311
  }
1312
 
1313
  function toggleFullscreen() {
 
 
1314
  if (!document.fullscreenElement) {
1315
- document.documentElement.requestFullscreen();
 
1316
  } else {
1317
  document.exitFullscreen();
 
1318
  }
1319
  }
1320
 
@@ -1325,12 +1184,24 @@ function solution() {
1325
  function showNotification(message, type) {
1326
  const notification = document.getElementById(type === 'error' ? 'errorMessage' : 'successMessage');
1327
  const textElement = document.getElementById(type === 'error' ? 'errorText' : 'successText');
1328
-
1329
  textElement.textContent = message;
1330
  notification.style.display = 'block';
1331
-
 
 
 
 
 
 
 
 
 
 
1332
  setTimeout(() => {
1333
- notification.style.display = 'none';
 
 
1334
  }, 3000);
1335
  }
1336
  </script>
 
1
  <!DOCTYPE html>
2
+ <html lang="th">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Chatbot - ปรับปรุงแล้ว</title>
7
+ <link rel="stylesheet" href="[https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css](https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css)">
8
+ <link href="[https://fonts.googleapis.com/icon?family=Material+Icons](https://fonts.googleapis.com/icon?family=Material+Icons)" rel="stylesheet">
9
  <style>
10
  :root {
11
+ --bg-primary: #1a1a1a; /* สีน้ำตาลเข้ม/ดำ */
12
+ --bg-secondary: #2c2c2c; /* สีน้ำตาลเข้ม/ดำ (สว่างขึ้น) */
13
+ --bg-tertiary: #3d3d3d; /* สีน้ำตาลเข้ม/ดำ (สว่างสุด) */
14
+ --text-primary: #f0e6d2; /* สีครีม/สว่าง */
15
+ --text-secondary: #a0937d; /* สีครีม/น้ำตาล (หม่น) */
16
+ --accent-primary: #33ff99; /* สีเขียว */
17
+ --accent-secondary: #ff9933; /* สีส้ม */
18
+ --accent-tertiary: #8b4513; /* สีน้ำตาล */
19
+ --chat-bg: #f5f5dc; /* สีครีม */
20
+ --chat-text: #333333; /* สีข้อความเข้มสำหรับพื้นหลังครีม */
21
  --error: #ff4757;
22
  --success: #2ed573;
23
  --warning: #ffa502;
24
+ --glass-bg: rgba(61, 61, 61, 0.1);
25
+ --glass-border: rgba(240, 230, 210, 0.15);
26
  --shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.6);
27
  --shadow-xl: 0 25px 50px rgba(0, 0, 0, 0.8);
28
+ --gradient-primary: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
29
+ --gradient-secondary: linear-gradient(135deg, var(--accent-secondary), var(--accent-tertiary));
 
30
  }
31
 
32
  * {
33
  box-sizing: border-box;
34
+ margin: 0;
35
+ padding: 0;
36
  }
37
 
38
  body {
 
39
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Inter', sans-serif;
40
  background: var(--bg-primary);
41
+ background-image:
42
+ radial-gradient(ellipse at top left, rgba(51, 255, 153, 0.08) 0%, transparent 60%),
43
+ radial-gradient(ellipse at bottom right, rgba(255, 153, 51, 0.08) 0%, transparent 60%);
44
  color: var(--text-primary);
45
  display: flex;
46
  justify-content: center;
 
48
  min-height: 100vh;
49
  overflow: hidden;
50
  -webkit-tap-highlight-color: transparent;
 
51
  }
52
 
53
  .app-container {
54
  width: 95%;
55
  max-width: 1400px;
56
  background: var(--glass-bg);
57
+ backdrop-filter: blur(25px);
58
+ -webkit-backdrop-filter: blur(25px);
59
  border: 1px solid var(--glass-border);
60
+ border-radius: 25px;
61
  box-shadow: var(--shadow-xl);
62
  display: flex;
63
  overflow: hidden;
64
  height: 90vh;
65
  position: relative;
66
+ animation: slideIn 0.9s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
67
+ opacity: 0;
68
+ transform: translateY(40px) scale(0.98);
69
  }
70
 
71
  @keyframes slideIn {
 
 
 
 
72
  to {
73
  opacity: 1;
74
  transform: translateY(0) scale(1);
 
76
  }
77
 
78
  .settings-panel {
79
+ width: 300px;
80
+ padding: 30px;
81
+ background: rgba(44, 44, 44, 0.7);
82
  backdrop-filter: blur(15px);
83
+ -webkit-backdrop-filter: blur(15px);
84
  border-right: 1px solid var(--glass-border);
85
+ transition: width 0.5s cubic-bezier(0.165, 0.84, 0.44, 1),
86
+ padding 0.5s cubic-bezier(0.165, 0.84, 0.44, 1),
87
+ opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1);
88
  overflow-y: auto;
89
  scrollbar-width: thin;
90
  scrollbar-color: var(--accent-primary) transparent;
91
  }
92
 
93
+ .settings-panel::-webkit-scrollbar { width: 6px; }
94
+ .settings-panel::-webkit-scrollbar-track { background: transparent; }
95
+ .settings-panel::-webkit-scrollbar-thumb { background: var(--accent-primary); border-radius: 3px; }
 
 
 
 
 
 
 
 
 
96
 
97
  .settings-panel.collapsed {
98
  width: 0;
99
  padding: 0;
100
  overflow: hidden;
101
+ border-right: none;
102
+ opacity: 0;
103
  }
104
 
105
  .main-content {
 
107
  display: flex;
108
  flex-direction: column;
109
  position: relative;
110
+ background: transparent; /* Changed */
111
  }
112
 
113
  .form-field {
114
+ margin-bottom: 25px;
115
+ animation: fadeInUp 0.7s cubic-bezier(0.165, 0.84, 0.44, 1) both;
116
  }
117
 
118
  .form-field label {
119
  display: block;
120
+ margin-bottom: 10px;
121
+ font-size: 0.9rem;
122
  font-weight: 600;
123
  color: var(--text-secondary);
124
  text-transform: uppercase;
125
+ letter-spacing: 0.8px;
126
  }
127
 
128
  .form-field select, .form-field textarea {
129
  width: 100%;
130
+ padding: 14px 18px;
131
+ border: 1px solid var(--glass-border);
132
+ border-radius: 10px;
133
  background: var(--bg-tertiary);
134
  color: var(--text-primary);
135
  font-size: 1rem;
136
+ transition: all 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
137
+ backdrop-filter: blur(5px);
138
  }
139
 
140
  .form-field select:focus, .form-field textarea:focus {
141
  outline: none;
142
  border-color: var(--accent-primary);
143
+ box-shadow: 0 0 25px rgba(51, 255, 153, 0.3);
144
  transform: translateY(-2px);
145
  }
146
 
147
+ .form-field textarea { resize: vertical; min-height: 110px; }
 
 
 
148
 
149
  button {
150
+ padding: 14px 28px;
151
  border: none;
152
+ border-radius: 10px;
153
  background: var(--gradient-primary);
154
+ color: #000; /* Changed for better contrast on gradient */
155
  cursor: pointer;
156
  font-size: 1rem;
157
+ font-weight: 700;
158
+ transition: all 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
159
  touch-action: manipulation;
160
  position: relative;
161
  overflow: hidden;
 
168
  left: -100%;
169
  width: 100%;
170
  height: 100%;
171
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
172
+ transition: left 0.6s;
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
+ button:hover::before { left: 100%; }
176
+ button:hover { transform: translateY(-4px); box-shadow: 0 12px 30px rgba(51, 255, 153, 0.35); }
177
+ button:active { transform: translateY(-1px); }
178
 
179
+ .material-icons { font-family: 'Material Icons'; font-size: 26px; vertical-align: middle; }
 
 
 
 
180
 
181
  .chat-container {
182
  flex: 1;
183
+ padding: 30px;
184
  overflow-y: auto;
185
  display: flex;
186
  flex-direction: column;
187
+ gap: 20px;
188
  scrollbar-width: thin;
189
+ scrollbar-color: var(--accent-secondary) transparent;
190
+ background: var(--chat-bg); /* Cream Background */
191
+ border-radius: 0 0 0 20px; /* Rounded corners */
192
  }
193
 
194
+ .chat-container::-webkit-scrollbar { width: 8px; }
195
+ .chat-container::-webkit-scrollbar-track { background: transparent; }
196
+ .chat-container::-webkit-scrollbar-thumb { background: var(--accent-secondary); border-radius: 4px; }
 
 
 
 
 
 
 
 
 
197
 
198
  .message {
199
+ max-width: 85%;
200
+ padding: 18px 24px;
201
+ border-radius: 22px;
202
  font-size: 1rem;
203
+ line-height: 1.65;
204
  position: relative;
205
+ animation: messageSlide 0.6s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
206
+ opacity: 0;
207
+ transform: translateY(25px);
208
  }
209
 
210
  @keyframes messageSlide {
211
+ to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
212
  }
213
 
214
  .message.user {
215
+ background: var(--gradient-secondary);
216
  align-self: flex-end;
217
+ box-shadow: 0 10px 25px rgba(255, 153, 51, 0.3);
218
+ color: #fff; /* White text for user */
219
  }
220
 
221
  .message.ai {
222
+ background: #ffffff; /* White AI messages */
223
+ border: 1px solid #e0e0e0;
224
  align-self: flex-start;
225
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
226
+ color: var(--chat-text); /* Dark text for AI */
227
+ }
228
+
229
+ .message.ai pre {
230
+ background: #0d1117; /* Dark background for code */
231
+ color: #c9d1d9; /* Light text for code */
232
+ padding: 20px;
233
+ border-radius: 10px;
234
+ overflow-x: auto;
235
+ border: 1px solid #30363d;
236
+ margin-top: 15px;
237
+ position: relative;
238
+ }
239
+ .message.ai pre code {
240
+ font-family: 'Fira Code', 'Consolas', monospace;
241
+ font-size: 0.9rem;
242
+ background: none !important; /* Ensure no inner background */
243
+ color: inherit !important; /* Ensure text color is inherited */
244
  }
245
 
246
  .input-container {
247
  display: flex;
248
+ gap: 15px;
249
+ padding: 25px;
250
+ background: var(--bg-secondary);
251
+ backdrop-filter: blur(10px);
252
  border-top: 1px solid var(--glass-border);
253
  align-items: center;
254
+ border-radius: 0 0 20px 0; /* Match app container */
255
  }
256
 
257
  #userInput {
258
  flex: 1;
259
+ padding: 18px 22px;
260
+ border: 1px solid var(--glass-border);
261
+ border-radius: 15px;
262
+ background: var(--bg-tertiary);
263
  color: var(--text-primary);
264
  resize: none;
265
  min-height: 60px;
266
+ max-height: 160px;
267
  font-size: 1rem;
268
+ transition: all 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
 
269
  }
270
 
271
  #userInput:focus {
272
  outline: none;
273
  border-color: var(--accent-primary);
274
+ box-shadow: 0 0 30px rgba(51, 255, 153, 0.35);
275
  transform: translateY(-2px);
276
  }
277
 
 
283
  height: 60px;
284
  border-radius: 50%;
285
  background: var(--gradient-primary);
286
+ transition: all 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
287
+ padding: 0; /* Reset padding */
288
+ border: none; /* Reset border */
289
  }
290
 
291
  #sendButton:hover, #attachButton:hover {
292
+ transform: translateY(-4px) scale(1.08);
293
+ box-shadow: 0 15px 35px rgba(51, 255, 153, 0.45);
294
  }
295
 
296
  #sendButton:disabled {
 
298
  cursor: not-allowed;
299
  transform: none;
300
  box-shadow: none;
301
+ opacity: 0.6;
302
  }
303
 
304
  .spinner {
305
+ width: 26px;
306
+ height: 26px;
307
  border: 3px solid rgba(255, 255, 255, 0.3);
308
  border-top: 3px solid var(--accent-primary);
309
  border-radius: 50%;
310
  animation: spin 1s linear infinite;
311
  }
312
 
313
+ @keyframes spin { to { transform: rotate(360deg); } }
 
 
314
 
315
  .notification {
316
+ position: fixed;
317
+ top: 30px;
318
+ right: 30px;
319
+ padding: 18px 26px;
320
+ border-radius: 15px;
321
+ color: #fff;
322
+ font-size: 1rem;
323
+ font-weight: 600;
324
+ z-index: 1000;
325
+ display: none;
326
+ backdrop-filter: blur(18px);
327
+ -webkit-backdrop-filter: blur(18px);
328
+ animation: notificationSlide 0.6s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
 
 
 
329
  opacity: 0;
330
  transform: translateX(100px);
 
 
 
 
 
 
 
 
 
 
331
  }
332
 
333
+ @keyframes notificationSlide {
334
+ to { opacity: 1; transform: translateX(0); }
 
 
 
 
 
335
  }
336
 
337
+ .error-message { background: linear-gradient(135deg, var(--error), #ff6b6b); box-shadow: 0 10px 25px rgba(255, 71, 87, 0.4); }
338
+ .success-message { background: linear-gradient(135deg, var(--success), #5af78e); box-shadow: 0 10px 25px rgba(46, 213, 115, 0.4); }
 
 
 
 
 
339
 
340
  .carousel-container {
341
+ perspective: 1500px;
342
+ height: 100%;
343
+ display: flex;
344
+ flex-direction: column;
345
+ align-items: center;
346
+ justify-content: center;
347
+ position: relative;
348
+ user-select: none;
349
+ padding: 30px;
350
  }
351
 
352
  .carousel {
353
+ position: relative;
354
+ width: 90%;
355
+ max-width: 550px; /* Increased size */
356
+ height: 350px; /* Increased size */
357
+ transform-style: preserve-3d;
358
+ transition: transform 0.8s cubic-bezier(0.19, 1, 0.22, 1); /* Smoother transition */
359
+ cursor: grab;
 
 
 
 
360
  }
361
 
362
  .carousel-card {
363
+ position: absolute;
364
+ width: 100%;
365
+ height: 100%;
366
+ background: var(--bg-secondary); /* Darker cards */
367
+ backdrop-filter: blur(15px);
368
+ border: 1px solid var(--glass-border);
369
+ border-radius: 20px;
370
+ padding: 30px;
371
+ box-shadow: var(--shadow-lg);
372
+ display: flex;
373
+ flex-direction: column;
374
+ gap: 15px;
375
+ transition: all 0.8s cubic-bezier(0.19, 1, 0.22, 1);
376
+ backface-visibility: hidden; /* Hide back for smoother rotation */
377
  }
378
 
379
  .carousel-controls, .carousel-indicator {
380
  display: flex;
381
+ gap: 20px;
382
+ margin-top: 30px;
383
  }
384
 
385
  .carousel-dot {
386
+ width: 16px;
387
+ height: 16px;
388
  background: var(--bg-tertiary);
389
  border-radius: 50%;
390
  cursor: pointer;
391
+ transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
392
  border: 2px solid transparent;
393
  }
394
 
395
  .carousel-dot.active {
396
  background: var(--accent-primary);
397
+ box-shadow: 0 0 25px rgba(51, 255, 153, 0.5);
398
+ transform: scale(1.3);
399
  }
400
 
401
+ .carousel-dot:hover { transform: scale(1.15); border-color: var(--accent-primary); }
 
 
 
402
 
403
  .mode-toggle {
404
  display: flex;
405
  background: var(--bg-secondary);
406
+ border-radius: 15px;
407
+ padding: 5px;
 
408
  position: relative;
409
+ margin: 20px; /* Added margin */
410
  border: 1px solid var(--glass-border);
411
  }
412
 
413
  .mode-toggle button {
414
  flex: 1;
415
+ padding: 14px 22px;
416
  background: none;
417
  border: none;
418
  color: var(--text-secondary);
 
420
  z-index: 1;
421
  font-size: 1rem;
422
  font-weight: 600;
423
+ border-radius: 12px;
424
+ transition: color 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
425
+ box-shadow: none; /* Remove button shadow */
426
  }
427
 
428
+ .mode-toggle button.active { color: #fff; } /* White active text */
 
 
429
 
430
  .mode-toggle-slider {
431
  position: absolute;
432
+ top: 5px;
433
+ left: 5px;
434
+ width: calc(50% - 5px); /* Adjusted width */
435
+ height: calc(100% - 10px);
436
  background: var(--gradient-primary);
437
+ border-radius: 12px;
438
+ transition: all 0.5s cubic-bezier(0.19, 1, 0.22, 1);
439
+ box-shadow: 0 5px 20px rgba(51, 255, 153, 0.3);
440
  }
441
 
442
  .dropzone {
443
+ position: absolute;
444
+ top: 0; left: 0; right: 0; bottom: 0;
445
+ background: rgba(51, 255, 153, 0.1);
446
+ backdrop-filter: blur(15px);
447
+ border: 3px dashed var(--accent-primary);
448
+ display: none;
449
+ align-items: center;
450
+ justify-content: center;
451
+ color: var(--text-primary);
452
+ font-size: 1.5rem;
453
+ font-weight: 600;
454
+ z-index: 100; /* Ensure it's on top */
455
+ border-radius: 20px;
456
+ }
457
+ .dropzone.active { display: flex; animation: pulse 1.8s infinite; }
458
+ @keyframes pulse { 0%, 100% { opacity: 0.7; transform: scale(1); } 50% { opacity: 1; transform: scale(1.02); } }
 
 
 
 
 
 
 
 
 
 
 
459
 
460
  .file-name {
461
+ font-size: 0.9rem; /* Smaller */
462
  color: var(--accent-primary);
463
+ margin-left: 15px;
464
  align-self: center;
465
+ max-width: 180px;
466
  white-space: nowrap;
467
  overflow: hidden;
468
  text-overflow: ellipsis;
 
471
 
472
  .chip-container {
473
  display: flex;
474
+ gap: 15px;
475
+ margin-bottom: 25px;
476
  flex-wrap: wrap;
477
+ padding: 0 30px;
478
  }
479
 
480
  .chip {
481
+ padding: 12px 20px;
482
+ background: var(--bg-tertiary);
 
483
  border: 1px solid var(--glass-border);
484
+ border-radius: 25px;
485
  font-size: 0.9rem;
486
  font-weight: 600;
487
  cursor: pointer;
488
+ transition: all 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
489
  color: var(--text-secondary);
490
  }
491
 
492
  .chip:hover {
493
+ background: var(--accent-secondary);
494
+ color: #fff;
495
+ transform: translateY(-3px);
496
+ box-shadow: 0 8px 22px rgba(255, 153, 51, 0.4);
497
+ border-color: transparent;
498
  }
499
 
500
  .floating-buttons {
501
  position: absolute;
502
+ top: 25px;
503
+ right: 25px;
504
  display: flex;
505
+ gap: 15px;
506
+ z-index: 200; /* Ensure above chat */
507
  }
508
 
509
  .floating-btn {
510
+ width: 50px;
511
+ height: 50px;
512
  border-radius: 50%;
513
+ background: var(--bg-secondary);
514
+ backdrop-filter: blur(10px);
515
  border: 1px solid var(--glass-border);
516
  display: flex;
517
  align-items: center;
518
  justify-content: center;
519
+ transition: all 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
520
  padding: 0;
521
+ color: var(--text-primary);
522
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
523
  }
524
 
525
  .floating-btn:hover {
526
  background: var(--accent-primary);
527
+ color: #000;
528
+ transform: translateY(-3px) scale(1.1);
529
+ box-shadow: 0 10px 30px rgba(51, 255, 153, 0.4);
530
  }
531
 
532
  .code-tools {
533
  position: absolute;
534
+ top: 15px;
535
+ right: 15px;
536
  display: flex;
537
+ gap: 10px;
538
+ opacity: 0; /* Hidden by default */
539
+ transition: opacity 0.3s ease;
540
  }
541
+ pre:hover .code-tools { opacity: 1; } /* Show on hover */
542
 
543
  .code-tools button {
544
+ padding: 8px 14px;
545
+ font-size: 0.8rem;
546
+ background: var(--bg-tertiary);
547
+ backdrop-filter: blur(5px);
 
 
 
 
 
 
548
  border: 1px solid var(--glass-border);
549
+ color: var(--text-primary);
550
+ border-radius: 6px;
551
+ box-shadow: none;
 
 
552
  }
553
+ .code-tools button:hover {
554
+ background: var(--accent-primary);
555
+ color: #000;
556
+ transform: translateY(-2px);
557
+ box-shadow: 0 4px 10px rgba(51, 255, 153, 0.3);
558
  }
559
 
560
  .image-preview {
561
  max-width: 100%;
562
+ max-height: 350px;
563
+ border-radius: 15px;
564
+ margin-top: 15px;
565
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
566
+ border: 1px solid #ddd;
567
  }
568
 
569
  @keyframes fadeInUp {
570
+ from { opacity: 0; transform: translateY(35px); }
571
+ to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
572
  }
573
 
574
  @media (max-width: 768px) {
575
+ .app-container { flex-direction: column; height: 100vh; border-radius: 0; width: 100%; }
576
+ .settings-panel { width: 100%; max-height: 35vh; border-right: none; border-bottom: 1px solid var(--glass-border); }
577
+ .settings-panel.collapsed { max-height: 0; }
578
+ .chat-container { max-height: 55vh; padding: 20px; border-radius: 0; }
579
+ .input-container { padding: 20px; border-radius: 0; }
580
+ #userInput { margin-bottom: 0; }
581
+ .floating-buttons { top: 15px; right: 15px; }
582
+ .floating-btn { width: 45px; height: 45px; }
583
+ .material-icons { font-size: 24px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
  }
 
585
  @media (max-width: 480px) {
586
+ .chip { font-size: 0.85rem; padding: 10px 16px; }
587
+ .form-field select, .form-field textarea, #userInput { font-size: 1rem; }
588
+ .message { padding: 15px 20px; }
 
 
 
 
 
 
 
 
 
589
  }
590
  </style>
591
  </head>
592
  <body>
593
  <div class="app-container" id="appContainer">
594
  <div class="settings-panel" id="settingsPanel">
595
+ <h2 style="font-size: 1.4rem; margin-bottom: 30px; background: var(--gradient-primary); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">⚙️ ตั้งค่า</h2>
596
  <div class="form-field">
597
+ <label>🤖 โมเดลหลัก:</label>
598
  <select id="modelSelect">
599
+ <option value="Qwen/Qwen2.5-Coder-32B-Instruct">Qwen2.5-Coder 32B (โค้ด/ข้อความ)</option>
600
+ <option value="Qwen/Qwen2-VL-72B-Instruct">Qwen2-VL 72B (ข้อความ/รูปภาพ)</option>
601
+ <option value="microsoft/Florence-2-large">Florence-2 Large (ข้อความ/รูปภาพ)</option>
602
+ </select>
603
+ <p style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 10px;">
604
+ 💡 เลือกโมเดลที่ต้องการ ใช้ VL หรือ Florence สำหรับรูปภาพ
 
 
605
  </p>
606
  </div>
607
  <div class="form-field">
608
+ <label>👁️ โมเดล OCR (สำหรับรูปภาพ):</label>
609
  <select id="ocrModelSelect">
610
+ <option value="none">ไม่ใช้</option>
611
+ <option value="microsoft/trocr-base-printed">TrOCR Base (แนะนำ)</option>
612
+ </select>
 
613
  </div>
614
+ <button id="saveSettingsBtn">💾 บันทึกการตั้งค่า</button>
615
  </div>
616
+
617
  <div class="main-content">
618
  <div class="floating-buttons">
619
  <button id="settingsToggle" class="floating-btn">
 
628
  </div>
629
 
630
  <div class="mode-toggle">
631
+ <button id="carouselModeBtn" class="active">🎛️ แก้ไข Prompt</button>
632
+ <button id="chatModeBtn">💬 แชท</button>
633
  <div class="mode-toggle-slider" id="modeToggleSlider"></div>
634
  </div>
635
 
636
  <div id="carouselMode">
637
  <div class="carousel-container">
638
  <div class="carousel" id="promptCarousel">
639
+ <div class="carousel-card" style="transform: rotateY(0deg) translateZ(350px);">
640
  <label>🎯 Primary System Prompt:</label>
641
+ <textarea id="primarySystemPrompt" placeholder="ป้อน Prompt หลักสำหรับ AI...">You are a powerful AI assistant that excels at understanding code, images, and technical content. Provide clear, accurate, and helpful responses. Focus on practical solutions and detailed explanations.</textarea>
642
  </div>
643
+ <div class="carousel-card" style="transform: rotateY(60deg) translateZ(350px); opacity: 0.7;">
644
  <label>👁️ OCR System Prompt:</label>
645
+ <textarea id="ocrSystemPrompt" placeholder="ป้อน Prompt สำหรับ OCR...">Extract all text from the provided image clearly and accurately. Preserve formatting, structure, and layout when possible. If text is unclear, indicate uncertain parts.</textarea>
646
  </div>
647
+ <div class="carousel-card" style="transform: rotateY(120deg) translateZ(350px); opacity: 0.7;">
648
  <label>💻 Code Template:</label>
649
+ <textarea id="codeTemplate" placeholder="ป้อน Template สำหรับโค้ด...">```javascript
650
  // Enhanced code implementation
651
  function solution() {
652
  // Your optimized code here
 
654
  }
655
  ```</textarea>
656
  </div>
657
+ <div class="carousel-card" style="transform: rotateY(180deg) translateZ(350px); opacity: 0.7;">
658
  <label>📋 Additional Instructions:</label>
659
+ <textarea id="additionalInstructions" placeholder="ป้อนคำแนะนำเพิ่มเติม...">Always provide working, tested code examples. Include error handling and optimization suggestions. Explain complex concepts step by step. Use modern best practices.</textarea>
660
  </div>
661
+ <div class="carousel-card" style="transform: rotateY(240deg) translateZ(350px); opacity: 0.7;">
662
  <label>🚀 Prompt Prefix:</label>
663
+ <textarea id="promptPrefix" placeholder="ข้อความนำหน้า Prompt...">Please analyze the following request carefully and provide a comprehensive solution:</textarea>
664
  </div>
665
+ <div class="carousel-card" style="transform: rotateY(300deg) translateZ(350px); opacity: 0.7;">
666
  <label>✨ Prompt Suffix:</label>
667
+ <textarea id="promptSuffix" placeholder="ข้อความต่อท้าย Prompt...">Ensure your response is complete, accurate, and includes practical examples where applicable.</textarea>
668
  </div>
669
  </div>
670
  <div class="carousel-controls">
 
679
  <div class="carousel-dot" data-index="4"></div>
680
  <div class="carousel-dot" data-index="5"></div>
681
  </div>
682
+ <button id="applyPromptBtn" style="margin-top: 30px;">✅ ใช้ Prompt & ไปที่แชท</button>
683
  </div>
684
  </div>
685
 
686
+ <div id="chatMode" style="display: none; flex: 1; display: flex; flex-direction: column;">
687
+ <div class="chip-container" id="quickPrompts">
688
+ <span class="chip" data-prompt="อธิบายโค้ดนี้">🔍 อธิบายโค้ด</span>
689
+ <span class="chip" data-prompt="สร้างฟังก์ชัน">⚡ สร้างฟังก์ชัน</span>
690
+ <span class="chip" data-prompt="แก้บั๊กโค้ดนี้">🐛 แก้บั๊ก</span>
691
+ <span class="chip" data-prompt="ปรับปรุงประสิทธิภาพ">🚀 ปรับปรุง</span>
692
+ <span class="chip" data-prompt="เพิ่ม Error Handling">🛡️ จัดการข้อผิดพลาด</span>
693
+ <span class="chip" data-prompt="แปลงเป็น Syntax ใหม่">✨ ทำให้ทันสมัย</span>
694
  </div>
695
+ <div class="chat-container" id="chatContainer">
696
+ </div>
697
  <div class="input-container">
698
  <button id="attachButton"><span class="material-icons">attach_file</span></button>
699
  <input type="file" id="fileInput" multiple accept="image/*" style="display: none;">
700
  <span class="file-name" id="fileName"></span>
701
+ <textarea id="userInput" placeholder="พิมพ์ข้อความที่นี่... (Shift+Enter เพื่อขึ้นบรรทัดใหม่)"></textarea>
702
  <button id="sendButton"><span class="material-icons">send</span></button>
703
  </div>
704
+ <div class="dropzone" id="dropzone">📁 วางไฟล์ที่นี่เพื่อแนบ</div>
705
  </div>
706
 
707
  <div class="notification error-message" id="errorMessage">
 
713
  </div>
714
  </div>
715
 
716
+ <script src="[https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js](https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js)"></script>
717
+ <script src="[https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js](https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js)"></script>
718
  <script>
719
  // Global variables
720
  let attachedFiles = [];
721
  let currentCarouselIndex = 0;
722
  let isLoading = false;
723
+ const HUGGINGFACE_TOKEN = "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"; // <<< !!! ใส่ Hugging Face Token ของคุณที่นี่ !!!
724
 
725
  // Initialize the application
726
  document.addEventListener('DOMContentLoaded', function() {
 
728
  setupEventListeners();
729
  setupCarousel();
730
  setupDropzone();
731
+ switchMode('carousel'); // Start in carousel mode
732
+ updateCarouselPosition(); // Ensure initial position is set
733
  });
734
 
735
  // Settings management
 
764
  localStorage.setItem('additionalInstructions', document.getElementById('additionalInstructions').value);
765
  localStorage.setItem('promptPrefix', document.getElementById('promptPrefix').value);
766
  localStorage.setItem('promptSuffix', document.getElementById('promptSuffix').value);
767
+ showNotification('บันทึกการตั้งค่าแล้ว!', 'success');
768
  }
769
 
770
  // Event listeners setup
771
  function setupEventListeners() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  // Mode toggle
773
  document.getElementById('carouselModeBtn').addEventListener('click', () => switchMode('carousel'));
774
  document.getElementById('chatModeBtn').addEventListener('click', () => switchMode('chat'));
 
775
  // Settings
776
  document.getElementById('saveSettingsBtn').addEventListener('click', saveSettings);
777
  document.getElementById('settingsToggle').addEventListener('click', toggleSettings);
778
+ // Carousel controls - Using 'click' for better cross-device compatibility
779
+ document.getElementById('prevCard').addEventListener('click', () => rotateCarousel(-1));
780
+ document.getElementById('nextCard').addEventListener('click', () => rotateCarousel(1));
 
 
 
 
 
 
 
 
 
 
 
781
  document.getElementById('applyPromptBtn').addEventListener('click', applyPromptsAndSwitchToChat);
 
782
  // Chat controls
783
  document.getElementById('sendButton').addEventListener('click', sendMessage);
784
  document.getElementById('attachButton').addEventListener('click', () => document.getElementById('fileInput').click());
785
  document.getElementById('fileInput').addEventListener('change', handleFileSelect);
786
  document.getElementById('userInput').addEventListener('keydown', handleKeyDown);
 
787
  // Other controls
788
  document.getElementById('fullscreenToggle').addEventListener('click', toggleFullscreen);
789
  document.getElementById('refreshBtn').addEventListener('click', refreshApp);
 
790
  // Quick prompts
791
  document.querySelectorAll('.chip').forEach(chip => {
792
  chip.addEventListener('click', (e) => {
 
795
  document.getElementById('userInput').focus();
796
  });
797
  });
 
798
  // Carousel dots
799
  document.querySelectorAll('.carousel-dot').forEach(dot => {
800
  dot.addEventListener('click', (e) => {
 
813
  const slider = document.getElementById('modeToggleSlider');
814
 
815
  if (mode === 'carousel') {
816
+ carouselMode.style.display = 'flex'; // Use flex for centering
817
  chatMode.style.display = 'none';
818
  carouselBtn.classList.add('active');
819
  chatBtn.classList.remove('active');
820
+ slider.style.left = '5px';
821
+ slider.style.width = 'calc(50% - 5px)';
822
  } else {
823
  carouselMode.style.display = 'none';
824
+ chatMode.style.display = 'flex'; // Use flex for layout
825
  carouselBtn.classList.remove('active');
826
  chatBtn.classList.add('active');
827
+ slider.style.left = 'calc(50% + 0px)'; // Adjusted position
828
+ slider.style.width = 'calc(50% - 5px)';
829
  }
830
  }
831
 
832
+ // Carousel functionality
 
833
  function setupCarousel() {
834
+ const carousel = document.getElementById('promptCarousel');
835
+ const cards = carousel.querySelectorAll('.carousel-card');
836
+ const cardWidth = cards[0].offsetWidth; // Get card width
837
+ const totalCards = cards.length;
838
+ const angle = 360 / totalCards;
839
+ const translateZ = (cardWidth / 2) / Math.tan( (angle / 2) * (Math.PI / 180) ); // Calculate translateZ dynamically
840
+
841
+ cards.forEach((card, index) => {
842
+ const cardAngle = index * angle;
843
+ card.style.transform = `rotateY(${cardAngle}deg) translateZ(${translateZ}px)`;
844
+ });
845
+
846
+ // Store translateZ for rotation
847
+ carousel.dataset.translateZ = translateZ;
848
+ updateCarouselPosition();
849
  }
850
 
851
+
852
  function rotateCarousel(direction) {
853
  const totalCards = 6;
854
  currentCarouselIndex = (currentCarouselIndex + direction + totalCards) % totalCards;
855
  updateCarouselPosition();
856
  }
857
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
858
  function goToCarouselIndex(index) {
859
  currentCarouselIndex = index;
860
  updateCarouselPosition();
861
  }
862
 
863
  function updateCarouselPosition() {
864
+ const carousel = document.getElementById('promptCarousel');
865
+ const cards = carousel.querySelectorAll('.carousel-card');
866
+ const dots = document.querySelectorAll('.carousel-dot');
867
+ const totalCards = cards.length;
868
+ const anglePerCard = 360 / totalCards;
869
+ const rotationAngle = -currentCarouselIndex * anglePerCard;
870
+ const translateZ = carousel.dataset.translateZ || 350; // Use stored or default
871
+
872
+ carousel.style.transform = `translateZ(-${translateZ}px) rotateY(${rotationAngle}deg)`;
873
+
874
+ cards.forEach((card, index) => {
875
+ const isActive = index === currentCarouselIndex;
876
+ card.style.opacity = isActive ? '1' : '0.6';
877
+ card.style.cursor = isActive ? 'default' : 'pointer'; // Make non-active cards clickable? Maybe not.
878
+ // No need to change individual card transforms here, only opacity/etc.
879
+ });
880
 
881
+ dots.forEach((dot, index) => {
882
+ dot.classList.toggle('active', index === currentCarouselIndex);
883
+ });
884
  }
885
 
886
+
887
  function applyPromptsAndSwitchToChat() {
888
  saveSettings();
889
  switchMode('chat');
890
+ showNotification('ใช้ Prompt แล้ว, เริ่มแชทได้เลย!', 'success');
891
  }
892
 
893
  // File handling
894
  function setupDropzone() {
895
  const dropzone = document.getElementById('dropzone');
896
+ const appContainer = document.getElementById('appContainer'); // Listen on a higher level
897
 
898
  ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
899
+ appContainer.addEventListener(eventName, preventDefaults, false);
900
  });
901
 
902
  function preventDefaults(e) {
 
905
  }
906
 
907
  ['dragenter', 'dragover'].forEach(eventName => {
908
+ appContainer.addEventListener(eventName, () => {
909
+ if (document.getElementById('chatMode').style.display !== 'none') {
910
+ dropzone.classList.add('active');
911
+ }
912
+ }, false);
913
  });
914
 
915
  ['dragleave', 'drop'].forEach(eventName => {
916
+ appContainer.addEventListener(eventName, () => dropzone.classList.remove('active'), false);
917
  });
918
 
919
+ appContainer.addEventListener('drop', handleDrop, false);
920
  }
921
 
922
  function handleDrop(e) {
923
+ if (document.getElementById('chatMode').style.display === 'none') return;
924
  const dt = e.dataTransfer;
925
  const files = dt.files;
926
  handleFiles(files);
 
932
  }
933
 
934
  function handleFiles(files) {
935
+ attachedFiles.push(...Array.from(files).filter(f => f.type.startsWith('image/'))); // Only add images
936
  updateFileDisplay();
937
+ if (attachedFiles.length > 0) {
938
+ showNotification(`${attachedFiles.length} รูปถูกแนบแล้ว`, 'success');
939
+ }
940
  }
941
 
942
  function updateFileDisplay() {
 
946
  } else if (attachedFiles.length === 1) {
947
  fileName.textContent = attachedFiles[0].name;
948
  } else {
949
+ fileName.textContent = `${attachedFiles.length} ไฟล์`;
950
  }
951
  }
952
 
 
961
  async function sendMessage() {
962
  const userInput = document.getElementById('userInput');
963
  const message = userInput.value.trim();
964
+
965
  if (!message && attachedFiles.length === 0) return;
966
  if (isLoading) return;
967
 
968
+ setLoading(true);
969
  const chatContainer = document.getElementById('chatContainer');
970
+ const userMessage = message; // Keep a copy
971
+
972
  // Add user message
973
+ if (userMessage) {
974
+ addMessage(userMessage, 'user');
975
  }
976
 
977
+ // Add file previews and clear input/files
978
+ const currentFiles = [...attachedFiles];
979
+ attachedFiles = [];
980
+ userInput.value = '';
981
+ updateFileDisplay();
982
+
983
+ for (const file of currentFiles) {
984
+ const imageUrl = URL.createObjectURL(file);
985
+ addImageMessage(imageUrl, 'user');
986
  }
987
 
988
+ chatContainer.scrollTop = chatContainer.scrollHeight;
 
989
 
990
  try {
991
+ const response = await callHuggingFaceAPI(userMessage, currentFiles);
992
  addMessage(response, 'ai');
993
  } catch (error) {
994
  console.error('Error:', error);
995
+ addMessage('ขออภัย, เกิดข้อผิดพลาดในการประมวลผล โปรดลองอีกครั้ง', 'ai');
996
  showNotification('Error: ' + error.message, 'error');
997
  } finally {
998
  setLoading(false);
999
+ chatContainer.scrollTop = chatContainer.scrollHeight;
 
1000
  }
 
 
1001
  }
1002
 
1003
  async function callHuggingFaceAPI(message, files) {
1004
+ const model = document.getElementById('modelSelect').value;
1005
+ const primaryPrompt = document.getElementById('primarySystemPrompt').value;
1006
+ const prefix = document.getElementById('promptPrefix').value;
1007
+ const suffix = document.getElementById('promptSuffix').value;
 
 
 
 
 
1008
  const ocrModel = document.getElementById('ocrModelSelect').value;
1009
+
1010
+ let fullPrompt = `${primaryPrompt}\n\n${prefix}\n\nUser: ${message}`;
1011
+
1012
+ // Handle OCR and image data based on model
1013
+ let imageInput = null;
1014
+ if (files.length > 0) {
1015
+ const file = files[0]; // Assuming one image for simplicity now
1016
+ const reader = new FileReader();
1017
+ imageInput = await new Promise((resolve) => {
1018
+ reader.onloadend = () => resolve(reader.result.split(',')[1]); // Base64
1019
+ reader.readAsDataURL(file);
1020
+ });
1021
+
1022
+ if (ocrModel !== 'none') {
1023
+ try {
1024
+ const ocrText = await performOCR(file, ocrModel);
1025
+ fullPrompt += `\n\n[Image Content: ${ocrText}]`;
1026
+ } catch (error) {
1027
+ console.warn('OCR failed:', error);
1028
+ fullPrompt += `\n\n[Image attached, OCR failed or skipped]`;
1029
+ }
1030
+ } else {
1031
+ fullPrompt += `\n\n[Image attached]`;
1032
  }
 
1033
  }
 
1034
 
1035
+ fullPrompt += `\n\n${suffix}\n\nAI:`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1036
 
1037
+ const payload = {
1038
+ inputs: fullPrompt,
1039
+ parameters: { max_new_tokens: 2048, temperature: 0.6, top_p: 0.9, do_sample: true }
1040
+ };
1041
 
1042
+ // Adjust payload for Vision models if needed (this part needs refinement based on specific model docs)
1043
+ // For many HF models, you might pass the image as part of the prompt or a separate field.
1044
+ // This example focuses on text+OCR, a more complex vision integration might require different API calls or libraries.
1045
+
1046
+ const response = await fetch(`https://api-inference.huggingface.co/models/${model}`, {
1047
+ method: 'POST',
1048
+ headers: {
1049
+ 'Authorization': `Bearer ${HUGGINGFACE_TOKEN}`,
1050
+ 'Content-Type': 'application/json',
1051
+ },
1052
+ body: JSON.stringify(payload)
1053
+ });
1054
+
1055
+ if (!response.ok) {
1056
+ const errorBody = await response.text();
1057
+ console.error("API Error Body:", errorBody);
1058
+ throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
1059
+ }
1060
 
1061
+ const data = await response.json();
1062
+
1063
+ if (data.error) throw new Error(data.error);
1064
+
1065
+ let generatedText = data[0]?.generated_text || data.choices?.[0]?.message?.content || 'ไม่มีการตอบกลับ';
1066
+
1067
+ // Clean up response - remove the prompt part if it's included
1068
+ if(generatedText.startsWith(fullPrompt)) {
1069
+ generatedText = generatedText.substring(fullPrompt.length).trim();
1070
+ }
1071
+
1072
+ return generatedText;
1073
  }
1074
 
1075
  async function performOCR(file, ocrModel) {
1076
+ // OCR often needs raw file data, not base64
1077
+ const response = await fetch(`https://api-inference.huggingface.co/models/${ocrModel}`, {
1078
+ method: 'POST',
1079
+ headers: {
1080
+ 'Authorization': `Bearer ${HUGGINGFACE_TOKEN}`,
1081
+ },
1082
+ body: file // Send file directly
1083
+ });
 
 
1084
 
1085
+ if (!response.ok) {
1086
+ const errorBody = await response.text();
1087
+ throw new Error(`OCR failed: ${response.status} - ${errorBody}`);
1088
+ }
1089
 
1090
+ const data = await response.json();
1091
+ // OCR models have varying outputs, check common ones
1092
+ return data[0]?.generated_text || data.generated_text || data.text || 'ไม่สามารถอ่านข้อความได้';
1093
  }
1094
 
1095
+
1096
  function addMessage(content, sender) {
1097
  const chatContainer = document.getElementById('chatContainer');
1098
  const messageDiv = document.createElement('div');
1099
  messageDiv.className = `message ${sender}`;
1100
+
1101
  if (sender === 'ai') {
1102
  messageDiv.innerHTML = marked.parse(content);
1103
+ messageDiv.querySelectorAll('pre').forEach(pre => {
1104
+ hljs.highlightElement(pre.querySelector('code'));
1105
+ addCodeTools(pre); // Add tools to 'pre' element
 
1106
  });
1107
  } else {
1108
+ messageDiv.textContent = content; // Keep user message as plain text
1109
  }
1110
+
1111
  chatContainer.appendChild(messageDiv);
1112
  chatContainer.scrollTop = chatContainer.scrollHeight;
1113
  }
1114
 
1115
+
1116
  function addImageMessage(imageUrl, sender) {
1117
  const chatContainer = document.getElementById('chatContainer');
1118
  const messageDiv = document.createElement('div');
1119
  messageDiv.className = `message ${sender}`;
1120
+
1121
  const img = document.createElement('img');
1122
  img.src = imageUrl;
1123
  img.className = 'image-preview';
1124
+ img.alt = 'รูปภาพที่อัปโหลด';
1125
+ img.onload = () => chatContainer.scrollTop = chatContainer.scrollHeight; // Scroll after image loads
1126
+
1127
  messageDiv.appendChild(img);
1128
  chatContainer.appendChild(messageDiv);
1129
  chatContainer.scrollTop = chatContainer.scrollHeight;
 
1132
  function addCodeTools(preElement) {
1133
  const toolsDiv = document.createElement('div');
1134
  toolsDiv.className = 'code-tools';
1135
+
1136
  const copyBtn = document.createElement('button');
1137
+ copyBtn.innerHTML = '<span class="material-icons" style="font-size: 16px;">content_copy</span> Copy';
1138
  copyBtn.onclick = () => {
1139
+ const code = preElement.querySelector('code').innerText;
1140
+ navigator.clipboard.writeText(code)
1141
+ .then(() => showNotification('คัดลอกโค้ดแล้ว!', 'success'))
1142
+ .catch(err => showNotification('คัดลอกไม่สำเร็จ!', 'error'));
1143
  };
1144
+
1145
  toolsDiv.appendChild(copyBtn);
1146
+ preElement.insertBefore(toolsDiv, preElement.firstChild); // Insert before code
 
1147
  }
1148
 
1149
  function setLoading(loading) {
1150
  isLoading = loading;
1151
  const sendButton = document.getElementById('sendButton');
 
1152
  const icon = sendButton.querySelector('.material-icons');
1153
+
1154
  if (loading) {
1155
+ sendButton.innerHTML = '<div class="spinner"></div>';
 
 
 
 
 
1156
  sendButton.disabled = true;
1157
  } else {
1158
  sendButton.innerHTML = '<span class="material-icons">send</span>';
 
1162
 
1163
  // Utility functions
1164
  function toggleSettings() {
1165
+ document.getElementById('settingsPanel').classList.toggle('collapsed');
 
1166
  }
1167
 
1168
  function toggleFullscreen() {
1169
+ const elem = document.documentElement;
1170
+ const icon = document.querySelector('#fullscreenToggle .material-icons');
1171
  if (!document.fullscreenElement) {
1172
+ elem.requestFullscreen().catch(err => console.error(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`));
1173
+ icon.textContent = 'fullscreen_exit';
1174
  } else {
1175
  document.exitFullscreen();
1176
+ icon.textContent = 'fullscreen';
1177
  }
1178
  }
1179
 
 
1184
  function showNotification(message, type) {
1185
  const notification = document.getElementById(type === 'error' ? 'errorMessage' : 'successMessage');
1186
  const textElement = document.getElementById(type === 'error' ? 'errorText' : 'successText');
1187
+
1188
  textElement.textContent = message;
1189
  notification.style.display = 'block';
1190
+ notification.style.opacity = '0'; // Start faded out
1191
+ notification.style.transform = 'translateX(100px)'; // Start off-screen
1192
+
1193
+ // Force reflow before adding class
1194
+ void notification.offsetWidth;
1195
+
1196
+ requestAnimationFrame(() => {
1197
+ notification.style.opacity = '1';
1198
+ notification.style.transform = 'translateX(0)';
1199
+ });
1200
+
1201
  setTimeout(() => {
1202
+ notification.style.opacity = '0';
1203
+ notification.style.transform = 'translateX(100px)';
1204
+ setTimeout(() => { notification.style.display = 'none'; }, 600); // Wait for transition
1205
  }, 3000);
1206
  }
1207
  </script>