andrewwda commited on
Commit
a9e421a
Β·
verified Β·
1 Parent(s): 72d1f16

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +429 -362
templates/index.html CHANGED
@@ -23,30 +23,33 @@
23
  touch-action: pan-x pan-y;
24
  }
25
 
26
- /* APP LAYOUT */
 
 
27
  .app {
28
  display: flex;
29
  height: 100vh;
30
  width: 100%;
31
- position: relative;
32
  }
33
 
34
- /* SIDEBAR MODERN */
 
 
35
  .sidebar {
36
- position: fixed;
37
- left: -100%;
38
- top: 0;
39
- width: 85%;
40
- max-width: 320px;
41
- height: 100vh;
42
  background: linear-gradient(180deg, #0f111a 0%, #080a10 100%);
43
- backdrop-filter: blur(20px);
44
- z-index: 1000;
45
- transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
46
- padding: 20px 16px;
47
  display: flex;
48
  flex-direction: column;
49
- border-right: 1px solid rgba(0, 255, 255, 0.2);
 
 
 
 
 
 
 
50
  box-shadow: 4px 0 30px rgba(0, 0, 0, 0.5);
51
  }
52
 
@@ -57,9 +60,8 @@
57
  .sidebar-header {
58
  display: flex;
59
  align-items: center;
60
- gap: 12px;
61
- margin-bottom: 30px;
62
- padding-bottom: 20px;
63
  border-bottom: 1px solid rgba(0, 212, 255, 0.2);
64
  }
65
 
@@ -71,28 +73,31 @@
71
  display: flex;
72
  align-items: center;
73
  justify-content: center;
74
- overflow: hidden;
 
 
75
  flex-shrink: 0;
76
  }
77
 
78
  .sidebar-logo-img {
79
- width: 100%;
80
- height: 100%;
 
81
  object-fit: cover;
82
  }
83
 
84
- .sidebar-header h2 {
85
  font-size: 22px;
 
86
  background: linear-gradient(135deg, #00d4ff, #4d9eff);
87
  -webkit-background-clip: text;
88
  background-clip: text;
89
  color: transparent;
90
  }
91
 
92
- .sidebar-header p {
93
  color: #6c7a9e;
94
  font-size: 11px;
95
- margin-top: 2px;
96
  }
97
 
98
  .new-chat {
@@ -100,9 +105,9 @@
100
  border: none;
101
  color: #0a0c15;
102
  font-weight: bold;
103
- padding: 14px;
104
  border-radius: 14px;
105
- font-size: 15px;
106
  cursor: pointer;
107
  margin-bottom: 25px;
108
  transition: all 0.2s;
@@ -111,105 +116,102 @@
111
  }
112
 
113
  .new-chat:active {
114
- transform: scale(0.98);
115
  }
116
 
117
  .chat-history {
118
  flex: 1;
119
  overflow-y: auto;
120
- display: flex;
121
- flex-direction: column;
122
- gap: 10px;
123
  }
124
 
125
  .history-item {
126
  background: rgba(18, 22, 40, 0.7);
127
  backdrop-filter: blur(5px);
128
- padding: 12px 16px;
129
  border-radius: 12px;
 
130
  cursor: pointer;
131
- transition: all 0.2s;
132
  border: 1px solid rgba(0, 212, 255, 0.1);
133
- font-size: 13px;
134
  font-weight: 500;
135
- color: #e8edff;
 
136
  }
137
 
138
  .history-item:active {
139
- background: rgba(0, 212, 255, 0.2);
140
- transform: translateX(5px);
141
  }
142
 
143
- /* OVERLAY */
 
 
144
  .overlay {
145
  position: fixed;
146
- top: 0;
147
- left: 0;
148
  width: 100%;
149
- height: 100%;
150
- background: rgba(0, 0, 0, 0.7);
151
- backdrop-filter: blur(4px);
152
- z-index: 999;
153
  display: none;
154
- transition: all 0.3s;
155
  }
156
 
157
  .overlay.active {
158
  display: block;
159
  }
160
 
161
- /* MAIN AREA */
 
 
162
  .main {
163
- flex: 1;
164
  display: flex;
165
  flex-direction: column;
166
  width: 100%;
167
  background: #0a0c15;
168
  }
169
 
170
- /* TOPBAR */
 
 
171
  .topbar {
172
- height: 65px;
173
  background: rgba(10, 12, 21, 0.95);
174
  backdrop-filter: blur(10px);
175
  display: flex;
176
  align-items: center;
177
  padding: 0 16px;
178
- gap: 12px;
179
  border-bottom: 1px solid rgba(0, 212, 255, 0.3);
180
- position: sticky;
181
- top: 0;
182
- z-index: 10;
183
  }
184
 
185
  .menu-btn {
186
  font-size: 26px;
 
187
  cursor: pointer;
 
188
  color: #00d4ff;
189
- transition: 0.2s;
190
- padding: 8px;
191
- -webkit-tap-highlight-color: transparent;
192
  }
193
 
194
  .menu-btn:active {
195
- transform: scale(0.9);
196
- }
197
-
198
- .logo-area {
199
- display: flex;
200
- align-items: center;
201
- gap: 10px;
202
- flex: 1;
203
  }
204
 
205
  .logo {
206
- width: 42px;
207
- height: 42px;
208
  border-radius: 12px;
209
  background: linear-gradient(135deg, #00d4ff, #0099ff);
210
  display: flex;
211
  align-items: center;
212
  justify-content: center;
 
 
213
  overflow: hidden;
214
  flex-shrink: 0;
215
  }
@@ -218,14 +220,21 @@
218
  width: 100%;
219
  height: 100%;
220
  object-fit: cover;
 
 
 
 
 
221
  }
222
 
223
  .title h1 {
224
  font-size: 18px;
 
225
  background: linear-gradient(135deg, #e8edff, #00d4ff);
226
  -webkit-background-clip: text;
227
  background-clip: text;
228
  color: transparent;
 
229
  }
230
 
231
  .title p {
@@ -240,13 +249,16 @@
240
  content: "●";
241
  font-size: 8px;
242
  color: #00ff88;
 
243
  }
244
 
245
- /* CHAT AREA */
 
 
246
  .chat-area {
247
  flex: 1;
248
  overflow-y: auto;
249
- padding: 20px 16px;
250
  background: linear-gradient(180deg, #0a0c15 0%, #0f111a 100%);
251
  -webkit-overflow-scrolling: touch;
252
  }
@@ -254,31 +266,30 @@
254
  .chat-area::-webkit-scrollbar {
255
  width: 4px;
256
  }
257
-
258
  .chat-area::-webkit-scrollbar-track {
259
  background: #1a1f2e;
 
260
  }
261
-
262
  .chat-area::-webkit-scrollbar-thumb {
263
  background: #00d4ff;
264
  border-radius: 10px;
265
  }
266
 
267
  .message {
268
- max-width: 90%;
269
  padding: 12px 16px;
270
  border-radius: 20px;
271
- margin-bottom: 16px;
272
- font-size: 14px;
273
- line-height: 1.5;
274
  word-wrap: break-word;
275
- animation: fadeInUp 0.3s ease;
276
  }
277
 
278
  @keyframes fadeInUp {
279
  from {
280
  opacity: 0;
281
- transform: translateY(10px);
282
  }
283
  to {
284
  opacity: 1;
@@ -292,7 +303,7 @@
292
  border: 1px solid rgba(0, 212, 255, 0.2);
293
  color: #d0d9ff;
294
  border-bottom-left-radius: 6px;
295
- align-self: flex-start;
296
  }
297
 
298
  .user {
@@ -301,28 +312,29 @@
301
  font-weight: 600;
302
  margin-left: auto;
303
  border-bottom-right-radius: 6px;
304
- align-self: flex-end;
305
  }
306
 
307
- /* INPUT AREA */
 
 
308
  .input-area {
309
  display: flex;
310
- gap: 10px;
311
  padding: 12px 16px;
 
312
  background: rgba(10, 12, 21, 0.95);
313
  backdrop-filter: blur(10px);
314
  border-top: 1px solid rgba(0, 212, 255, 0.2);
315
- position: sticky;
316
- bottom: 0;
317
  }
318
 
319
  input {
320
  flex: 1;
321
  border: 1px solid rgba(0, 212, 255, 0.3);
322
  background: rgba(18, 22, 40, 0.8);
323
- border-radius: 25px;
324
  padding: 12px 18px;
325
- font-size: 14px;
326
  outline: none;
327
  color: #e8edff;
328
  transition: all 0.2s;
@@ -343,139 +355,144 @@
343
  color: #0a0c15;
344
  font-weight: bold;
345
  padding: 0 20px;
346
- border-radius: 25px;
347
  font-size: 14px;
348
  cursor: pointer;
349
- transition: all 0.2s;
350
- white-space: nowrap;
 
 
 
351
  }
352
 
353
  button.send:active {
354
  transform: scale(0.96);
355
  }
356
 
357
- /* RESPONSIVE BREAKPOINTS */
358
- @media (min-width: 768px) {
 
 
359
  .sidebar {
360
- width: 300px;
361
- left: -320px;
362
  }
363
-
364
  .message {
365
- max-width: 75%;
366
- font-size: 15px;
 
 
 
 
367
  }
368
-
369
  .topbar {
370
- padding: 0 24px;
371
  }
372
-
373
- .chat-area {
374
- padding: 24px;
 
 
 
375
  }
376
  }
377
 
378
  @media (max-width: 480px) {
379
- .message {
380
- max-width: 95%;
381
- padding: 10px 14px;
382
- font-size: 13px;
383
- }
384
-
385
  .topbar {
386
- height: 60px;
387
  padding: 0 12px;
388
  }
389
-
390
  .menu-btn {
391
  font-size: 24px;
 
392
  }
393
-
394
  .logo {
395
- width: 38px;
396
- height: 38px;
 
397
  }
398
-
399
  .title h1 {
400
- font-size: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
401
  }
402
-
403
  .input-area {
404
  padding: 10px 12px;
405
  gap: 8px;
406
  }
407
-
408
  input {
409
  padding: 10px 14px;
410
- font-size: 13px;
411
  }
412
-
413
  button.send {
414
  padding: 0 16px;
415
  font-size: 13px;
416
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  }
418
 
419
- /* Landscape mode */
420
  @media (max-width: 900px) and (orientation: landscape) {
421
  .topbar {
422
- height: 50px;
423
  }
424
-
425
  .chat-area {
426
- padding: 12px;
427
  }
428
-
429
  .message {
430
- margin-bottom: 10px;
 
431
  }
432
-
433
  .input-area {
434
  padding: 8px 12px;
435
  }
436
-
437
  input {
438
- padding: 8px 12px;
439
  }
440
- }
441
-
442
- /* Loading animation */
443
- .typing-indicator {
444
- display: flex;
445
- gap: 4px;
446
- padding: 12px 16px;
447
- background: rgba(20, 24, 40, 0.9);
448
- border-radius: 20px;
449
- border-bottom-left-radius: 6px;
450
- width: fit-content;
451
- margin-bottom: 16px;
452
- }
453
-
454
- .typing-indicator span {
455
- width: 8px;
456
- height: 8px;
457
- background: #00d4ff;
458
- border-radius: 50%;
459
- animation: typing 1.4s infinite;
460
- }
461
-
462
- .typing-indicator span:nth-child(2) {
463
- animation-delay: 0.2s;
464
- }
465
-
466
- .typing-indicator span:nth-child(3) {
467
- animation-delay: 0.4s;
468
- }
469
-
470
- @keyframes typing {
471
- 0%, 60%, 100% {
472
- transform: translateY(0);
473
- opacity: 0.4;
474
  }
475
- 30% {
476
- transform: translateY(-10px);
477
- opacity: 1;
478
  }
 
 
 
 
 
 
 
 
 
479
  }
480
  </style>
481
  </head>
@@ -495,272 +512,322 @@
495
  <p>Neo Intelligence</p>
496
  </div>
497
  </div>
498
- <button class="new-chat" onclick="createNewChat()">✨ New Chat</button>
499
- <div class="chat-history" id="chatHistory">
500
- <div style="text-align: center; color: #6c7a9e; padding: 20px;">
501
- Belum ada chat
502
- </div>
503
- </div>
504
  </div>
505
 
506
  <!-- MAIN CONTENT -->
507
  <div class="main">
508
  <div class="topbar">
509
- <div class="menu-btn" onclick="openSidebar()">☰</div>
510
- <div class="logo-area">
511
- <div class="logo" id="mainLogo">
512
- πŸ±β€πŸ‘€
513
- </div>
514
- <div class="title">
515
- <h1>ChibiCat AI</h1>
516
- <p>Online | Neo Mode</p>
517
- </div>
518
  </div>
519
  </div>
520
 
521
  <div class="chat-area" id="chatArea">
522
  <div class="message bot">
523
- <strong>⚑ ChibiCat Neo</strong><br>
524
  Halo bro! Ada yang bisa gue bantu? πŸ”₯😎
525
  </div>
526
  </div>
527
 
528
  <div class="input-area">
529
- <input type="text" id="messageInput" placeholder="Ketik pesan..." />
530
- <button class="send" onclick="sendMessage()">Kirim</button>
 
 
531
  </div>
532
  </div>
533
  </div>
534
 
535
  <script>
536
- let currentChatId = null;
537
- let isTyping = false;
538
-
539
- // Konfigurasi logo
540
  const LOGO_CONFIG = {
541
- mode: "emoji",
542
  emoji: "πŸ±β€πŸš€",
543
  imageUrl: "https://cdn-icons-png.flaticon.com/512/1998/1998592.png",
544
- svgIcon: "⚑🐱⚑"
545
  };
546
 
547
- function initLogos() {
548
- const sidebarLogo = document.getElementById("sidebarLogo");
549
- const mainLogo = document.getElementById("mainLogo");
550
-
551
- if (LOGO_CONFIG.mode === "emoji") {
552
- sidebarLogo.innerHTML = LOGO_CONFIG.emoji;
553
- sidebarLogo.style.fontSize = "28px";
554
- sidebarLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)";
555
- mainLogo.innerHTML = LOGO_CONFIG.emoji;
556
- mainLogo.style.fontSize = "24px";
557
- mainLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)";
558
- } else if (LOGO_CONFIG.mode === "image") {
559
- sidebarLogo.innerHTML = `<img src="${LOGO_CONFIG.imageUrl}" class="sidebar-logo-img" onerror="this.src='https://cdn-icons-png.flaticon.com/512/1998/1998592.png'">`;
560
- sidebarLogo.style.background = "transparent";
561
- mainLogo.innerHTML = `<img src="${LOGO_CONFIG.imageUrl}" class="logo-img" onerror="this.src='https://cdn-icons-png.flaticon.com/512/1998/1998592.png'">`;
562
- mainLogo.style.background = "transparent";
563
- } else {
564
- sidebarLogo.innerHTML = LOGO_CONFIG.svgIcon;
565
- sidebarLogo.style.fontSize = "24px";
566
- mainLogo.innerHTML = LOGO_CONFIG.svgIcon;
567
- mainLogo.style.fontSize = "20px";
 
 
 
 
 
 
 
 
568
  }
569
  }
570
-
571
- function openSidebar() {
572
- document.getElementById("sidebar").classList.add("active");
573
- document.getElementById("overlay").classList.add("active");
574
- document.body.style.overflow = "hidden";
 
575
  }
576
-
577
- function closeSidebar() {
578
- document.getElementById("sidebar").classList.remove("active");
579
- document.getElementById("overlay").classList.remove("active");
580
- document.body.style.overflow = "";
581
  }
582
-
583
- function showTypingIndicator() {
 
 
 
 
 
 
 
 
 
584
  const chatArea = document.getElementById("chatArea");
585
- const typingDiv = document.createElement("div");
586
- typingDiv.className = "typing-indicator";
587
- typingDiv.id = "typingIndicator";
588
- typingDiv.innerHTML = "<span></span><span></span><span></span>";
589
- chatArea.appendChild(typingDiv);
 
 
590
  chatArea.scrollTop = chatArea.scrollHeight;
591
- isTyping = true;
592
- }
593
-
594
- function hideTypingIndicator() {
595
- const indicator = document.getElementById("typingIndicator");
596
- if (indicator) indicator.remove();
597
- isTyping = false;
598
  }
599
-
600
- async function createNewChat() {
601
- try {
602
- const response = await fetch("/new_chat", { method: "POST" });
603
- const data = await response.json();
604
- currentChatId = data.chat_id;
605
- document.getElementById("chatArea").innerHTML = `
606
- <div class="message bot">
607
- <strong>⚑ ChibiCat Neo</strong><br>
608
- Halo bro! Ada yang bisa gue bantu? πŸ”₯😎
609
  </div>
610
  `;
611
- await loadChats();
612
- closeSidebar();
613
- } catch (error) {
614
- console.error("Error creating new chat:", error);
615
- }
 
 
 
 
616
  }
617
-
618
- async function loadChats() {
619
- try {
620
- const response = await fetch("/get_chats");
621
- const chats = await response.json();
622
- const history = document.getElementById("chatHistory");
623
-
624
- if (!chats || chats.length === 0) {
625
- history.innerHTML = '<div style="text-align: center; color: #6c7a9e; padding: 20px;">Belum ada chat</div>';
626
- return;
627
- }
628
-
629
- history.innerHTML = chats.map(chat => `
630
- <div class="history-item" onclick="loadChat('${chat.chat_id}')">
631
- πŸ’¬ ${escapeHtml(chat.title)}
632
- </div>
633
- `).join("");
634
- } catch (error) {
635
- console.error("Error loading chats:", error);
636
  }
637
  }
638
-
639
- async function loadChat(chatId) {
640
- try {
641
- currentChatId = chatId;
642
- const response = await fetch(`/load_chat/${chatId}`);
643
- const messages = await response.json();
644
- const chatArea = document.getElementById("chatArea");
645
- chatArea.innerHTML = messages.map(msg =>
646
- `<div class="message ${msg.role === "user" ? "user" : "bot"}">${escapeHtml(msg.content)}</div>`
647
- ).join("");
648
- closeSidebar();
649
- chatArea.scrollTop = chatArea.scrollHeight;
650
- } catch (error) {
651
- console.error("Error loading chat:", error);
652
- }
 
653
  }
654
-
655
  async function sendMessage() {
656
  const input = document.getElementById("messageInput");
657
  const message = input.value.trim();
658
  if (message === "") return;
659
-
660
- const chatArea = document.getElementById("chatArea");
661
- chatArea.innerHTML += `<div class="message user">${escapeHtml(message)}</div>`;
 
 
 
 
 
662
  input.value = "";
663
- chatArea.scrollTop = chatArea.scrollHeight;
664
-
665
- showTypingIndicator();
666
-
667
- try {
668
- const response = await fetch("/chat", {
669
- method: "POST",
670
- headers: { "Content-Type": "application/json" },
671
- body: JSON.stringify({ message: message, chat_id: currentChatId })
672
- });
673
-
674
- const data = await response.json();
675
- hideTypingIndicator();
676
- chatArea.innerHTML += `<div class="message bot">${escapeHtml(data.reply)}</div>`;
677
- chatArea.scrollTop = chatArea.scrollHeight;
678
- await loadChats();
679
- } catch (error) {
680
- hideTypingIndicator();
681
- chatArea.innerHTML += `<div class="message bot">⚠️ Server error, coba lagi bro 😭</div>`;
682
- chatArea.scrollTop = chatArea.scrollHeight;
683
- console.error("Error sending message:", error);
684
  }
 
 
 
 
 
 
685
  }
686
-
 
687
  function escapeHtml(text) {
688
  const div = document.createElement('div');
689
  div.textContent = text;
690
  return div.innerHTML;
691
  }
692
-
693
- // Keyboard handling untuk Android
694
- function handleAndroidKeyboard() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
695
  const input = document.getElementById("messageInput");
696
- const chatArea = document.querySelector('.chat-area');
697
-
698
  input.addEventListener("focus", () => {
699
  setTimeout(() => {
 
700
  chatArea.scrollTop = chatArea.scrollHeight;
701
- }, 300);
702
  });
703
 
704
- input.addEventListener("keypress", (e) => {
705
- if (e.key === "Enter" && !e.shiftKey) {
706
- e.preventDefault();
707
- sendMessage();
708
- }
 
 
 
709
  });
710
  }
711
-
712
- // Swipe gesture untuk mobile
713
- let touchStartX = 0;
714
- let touchEndX = 0;
715
 
716
- document.addEventListener('touchstart', (e) => {
717
- touchStartX = e.changedTouches[0].screenX;
718
- }, false);
719
-
720
- document.addEventListener('touchend', (e) => {
721
- touchEndX = e.changedTouches[0].screenX;
722
- const swipeDistance = touchEndX - touchStartX;
723
-
724
- if (swipeDistance > 50 && touchStartX < 50) {
725
- openSidebar();
726
- }
727
-
728
- if (swipeDistance < -50) {
729
- closeSidebar();
730
- }
731
- }, false);
732
-
733
- // Detect dan optimize untuk Android
734
- function optimizeForAndroid() {
735
- const ua = navigator.userAgent.toLowerCase();
736
- const isAndroid = ua.includes("android");
737
-
738
  if (isAndroid) {
739
- document.body.style.touchAction = "pan-y";
740
- const viewport = document.querySelector('meta[name="viewport"]');
741
- if (viewport) {
742
- viewport.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes, viewport-fit=cover');
743
- }
 
 
 
 
 
 
 
 
744
  }
745
  }
746
-
747
- // Inisialisasi
748
- initLogos();
749
- loadChats();
750
- handleAndroidKeyboard();
751
- optimizeForAndroid();
752
 
753
- // Auto scroll saat chat area berubah
754
- const observer = new MutationObserver(() => {
755
- const chatArea = document.getElementById("chatArea");
756
- if (chatArea) chatArea.scrollTop = chatArea.scrollHeight;
757
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
 
759
- observer.observe(document.getElementById("chatArea"), {
760
- childList: true,
761
- subtree: true
762
- });
763
  </script>
764
-
765
  </body>
766
  </html>
 
23
  touch-action: pan-x pan-y;
24
  }
25
 
26
+ /* =========================
27
+ APP
28
+ ========================= */
29
  .app {
30
  display: flex;
31
  height: 100vh;
32
  width: 100%;
 
33
  }
34
 
35
+ /* =========================
36
+ SIDEBAR - DARK NEO
37
+ ========================= */
38
  .sidebar {
39
+ width: 280px;
 
 
 
 
 
40
  background: linear-gradient(180deg, #0f111a 0%, #080a10 100%);
41
+ backdrop-filter: blur(10px);
42
+ color: #e8edff;
 
 
43
  display: flex;
44
  flex-direction: column;
45
+ padding: 20px 16px;
46
+ position: fixed;
47
+ left: -300px;
48
+ top: 0;
49
+ height: 100vh;
50
+ z-index: 999;
51
+ transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
52
+ border-right: 1px solid rgba(0, 255, 255, 0.15);
53
  box-shadow: 4px 0 30px rgba(0, 0, 0, 0.5);
54
  }
55
 
 
60
  .sidebar-header {
61
  display: flex;
62
  align-items: center;
63
+ margin-bottom: 25px;
64
+ padding-bottom: 16px;
 
65
  border-bottom: 1px solid rgba(0, 212, 255, 0.2);
66
  }
67
 
 
73
  display: flex;
74
  align-items: center;
75
  justify-content: center;
76
+ font-size: 28px;
77
+ margin-right: 12px;
78
+ box-shadow: 0 0 15px rgba(0, 212, 255, 0.4);
79
  flex-shrink: 0;
80
  }
81
 
82
  .sidebar-logo-img {
83
+ width: 50px;
84
+ height: 50px;
85
+ border-radius: 14px;
86
  object-fit: cover;
87
  }
88
 
89
+ .sidebar h2 {
90
  font-size: 22px;
91
+ letter-spacing: -0.5px;
92
  background: linear-gradient(135deg, #00d4ff, #4d9eff);
93
  -webkit-background-clip: text;
94
  background-clip: text;
95
  color: transparent;
96
  }
97
 
98
+ .sidebar p {
99
  color: #6c7a9e;
100
  font-size: 11px;
 
101
  }
102
 
103
  .new-chat {
 
105
  border: none;
106
  color: #0a0c15;
107
  font-weight: bold;
108
+ padding: 12px;
109
  border-radius: 14px;
110
+ font-size: 14px;
111
  cursor: pointer;
112
  margin-bottom: 25px;
113
  transition: all 0.2s;
 
116
  }
117
 
118
  .new-chat:active {
119
+ transform: scale(0.97);
120
  }
121
 
122
  .chat-history {
123
  flex: 1;
124
  overflow-y: auto;
125
+ -webkit-overflow-scrolling: touch;
 
 
126
  }
127
 
128
  .history-item {
129
  background: rgba(18, 22, 40, 0.7);
130
  backdrop-filter: blur(5px);
131
+ padding: 12px 14px;
132
  border-radius: 12px;
133
+ margin-bottom: 10px;
134
  cursor: pointer;
135
+ transition: 0.15s;
136
  border: 1px solid rgba(0, 212, 255, 0.1);
 
137
  font-weight: 500;
138
+ font-size: 13px;
139
+ word-break: break-word;
140
  }
141
 
142
  .history-item:active {
143
+ background: rgba(0, 212, 255, 0.25);
144
+ transform: translateX(4px);
145
  }
146
 
147
+ /* =========================
148
+ OVERLAY
149
+ ========================= */
150
  .overlay {
151
  position: fixed;
 
 
152
  width: 100%;
153
+ height: 100vh;
154
+ background: rgba(0, 0, 0, 0.6);
155
+ backdrop-filter: blur(3px);
156
+ z-index: 998;
157
  display: none;
158
+ transition: 0.2s;
159
  }
160
 
161
  .overlay.active {
162
  display: block;
163
  }
164
 
165
+ /* =========================
166
+ MAIN - CYBER DARK
167
+ ========================= */
168
  .main {
 
169
  display: flex;
170
  flex-direction: column;
171
  width: 100%;
172
  background: #0a0c15;
173
  }
174
 
175
+ /* =========================
176
+ TOPBAR - MOBILE FRIENDLY
177
+ ========================= */
178
  .topbar {
179
+ height: 64px;
180
  background: rgba(10, 12, 21, 0.95);
181
  backdrop-filter: blur(10px);
182
  display: flex;
183
  align-items: center;
184
  padding: 0 16px;
185
+ color: #e8edff;
186
  border-bottom: 1px solid rgba(0, 212, 255, 0.3);
187
+ box-shadow: 0 2px 20px rgba(0, 0, 0, 0.3);
188
+ flex-shrink: 0;
 
189
  }
190
 
191
  .menu-btn {
192
  font-size: 26px;
193
+ margin-right: 12px;
194
  cursor: pointer;
195
+ transition: 0.15s;
196
  color: #00d4ff;
197
+ padding: 8px 4px;
198
+ touch-action: manipulation;
 
199
  }
200
 
201
  .menu-btn:active {
202
+ transform: scale(0.92);
 
 
 
 
 
 
 
203
  }
204
 
205
  .logo {
206
+ width: 44px;
207
+ height: 44px;
208
  border-radius: 12px;
209
  background: linear-gradient(135deg, #00d4ff, #0099ff);
210
  display: flex;
211
  align-items: center;
212
  justify-content: center;
213
+ margin-right: 12px;
214
+ box-shadow: 0 0 10px rgba(0, 212, 255, 0.5);
215
  overflow: hidden;
216
  flex-shrink: 0;
217
  }
 
220
  width: 100%;
221
  height: 100%;
222
  object-fit: cover;
223
+ border-radius: 12px;
224
+ }
225
+
226
+ .logo-icon {
227
+ font-size: 24px;
228
  }
229
 
230
  .title h1 {
231
  font-size: 18px;
232
+ letter-spacing: -0.3px;
233
  background: linear-gradient(135deg, #e8edff, #00d4ff);
234
  -webkit-background-clip: text;
235
  background-clip: text;
236
  color: transparent;
237
+ white-space: nowrap;
238
  }
239
 
240
  .title p {
 
249
  content: "●";
250
  font-size: 8px;
251
  color: #00ff88;
252
+ text-shadow: 0 0 5px #00ff88;
253
  }
254
 
255
+ /* =========================
256
+ CHAT AREA - OPTIMIZED SCROLL
257
+ ========================= */
258
  .chat-area {
259
  flex: 1;
260
  overflow-y: auto;
261
+ padding: 16px;
262
  background: linear-gradient(180deg, #0a0c15 0%, #0f111a 100%);
263
  -webkit-overflow-scrolling: touch;
264
  }
 
266
  .chat-area::-webkit-scrollbar {
267
  width: 4px;
268
  }
 
269
  .chat-area::-webkit-scrollbar-track {
270
  background: #1a1f2e;
271
+ border-radius: 10px;
272
  }
 
273
  .chat-area::-webkit-scrollbar-thumb {
274
  background: #00d4ff;
275
  border-radius: 10px;
276
  }
277
 
278
  .message {
279
+ max-width: 85%;
280
  padding: 12px 16px;
281
  border-radius: 20px;
282
+ margin-bottom: 14px;
283
+ font-size: 15px;
284
+ line-height: 1.45;
285
  word-wrap: break-word;
286
+ animation: fadeInUp 0.25s ease;
287
  }
288
 
289
  @keyframes fadeInUp {
290
  from {
291
  opacity: 0;
292
+ transform: translateY(8px);
293
  }
294
  to {
295
  opacity: 1;
 
303
  border: 1px solid rgba(0, 212, 255, 0.2);
304
  color: #d0d9ff;
305
  border-bottom-left-radius: 6px;
306
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
307
  }
308
 
309
  .user {
 
312
  font-weight: 600;
313
  margin-left: auto;
314
  border-bottom-right-radius: 6px;
315
+ box-shadow: 0 4px 12px rgba(0, 212, 255, 0.25);
316
  }
317
 
318
+ /* =========================
319
+ INPUT AREA - LANDING THUMB
320
+ ========================= */
321
  .input-area {
322
  display: flex;
 
323
  padding: 12px 16px;
324
+ gap: 10px;
325
  background: rgba(10, 12, 21, 0.95);
326
  backdrop-filter: blur(10px);
327
  border-top: 1px solid rgba(0, 212, 255, 0.2);
328
+ flex-shrink: 0;
 
329
  }
330
 
331
  input {
332
  flex: 1;
333
  border: 1px solid rgba(0, 212, 255, 0.3);
334
  background: rgba(18, 22, 40, 0.8);
335
+ border-radius: 30px;
336
  padding: 12px 18px;
337
+ font-size: 15px;
338
  outline: none;
339
  color: #e8edff;
340
  transition: all 0.2s;
 
355
  color: #0a0c15;
356
  font-weight: bold;
357
  padding: 0 20px;
358
+ border-radius: 30px;
359
  font-size: 14px;
360
  cursor: pointer;
361
+ transition: 0.15s;
362
+ display: flex;
363
+ align-items: center;
364
+ gap: 6px;
365
+ touch-action: manipulation;
366
  }
367
 
368
  button.send:active {
369
  transform: scale(0.96);
370
  }
371
 
372
+ /* =========================
373
+ RESPONSIVE BREAKPOINTS - ANDROID FIRST
374
+ ========================= */
375
+ @media (max-width: 600px) {
376
  .sidebar {
377
+ width: 85%;
378
+ max-width: 280px;
379
  }
 
380
  .message {
381
+ max-width: 92%;
382
+ font-size: 14px;
383
+ padding: 10px 14px;
384
+ }
385
+ .chat-area {
386
+ padding: 12px;
387
  }
 
388
  .topbar {
389
+ height: 58px;
390
  }
391
+ .title h1 {
392
+ font-size: 16px;
393
+ }
394
+ .logo {
395
+ width: 38px;
396
+ height: 38px;
397
  }
398
  }
399
 
400
  @media (max-width: 480px) {
 
 
 
 
 
 
401
  .topbar {
402
+ height: 56px;
403
  padding: 0 12px;
404
  }
 
405
  .menu-btn {
406
  font-size: 24px;
407
+ margin-right: 8px;
408
  }
 
409
  .logo {
410
+ width: 36px;
411
+ height: 36px;
412
+ margin-right: 8px;
413
  }
 
414
  .title h1 {
415
+ font-size: 15px;
416
+ }
417
+ .title p {
418
+ font-size: 9px;
419
+ }
420
+ .chat-area {
421
+ padding: 10px;
422
+ }
423
+ .message {
424
+ padding: 9px 12px;
425
+ font-size: 13.5px;
426
+ max-width: 95%;
427
+ margin-bottom: 12px;
428
  }
 
429
  .input-area {
430
  padding: 10px 12px;
431
  gap: 8px;
432
  }
 
433
  input {
434
  padding: 10px 14px;
435
+ font-size: 14px;
436
  }
 
437
  button.send {
438
  padding: 0 16px;
439
  font-size: 13px;
440
  }
441
+ .sidebar {
442
+ width: 85%;
443
+ padding: 16px 12px;
444
+ }
445
+ .sidebar-logo {
446
+ width: 44px;
447
+ height: 44px;
448
+ font-size: 24px;
449
+ }
450
+ .sidebar h2 {
451
+ font-size: 20px;
452
+ }
453
+ .new-chat {
454
+ padding: 10px;
455
+ font-size: 13px;
456
+ }
457
+ .history-item {
458
+ padding: 10px 12px;
459
+ font-size: 12px;
460
+ }
461
  }
462
 
463
+ /* Landscape mode Android */
464
  @media (max-width: 900px) and (orientation: landscape) {
465
  .topbar {
466
+ height: 52px;
467
  }
 
468
  .chat-area {
469
+ padding: 8px 12px;
470
  }
 
471
  .message {
472
+ margin-bottom: 8px;
473
+ padding: 8px 12px;
474
  }
 
475
  .input-area {
476
  padding: 8px 12px;
477
  }
 
478
  input {
479
+ padding: 8px 14px;
480
  }
481
+ button.send {
482
+ padding: 0 18px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  }
484
+ .sidebar {
485
+ padding: 12px;
 
486
  }
487
+ .chat-history {
488
+ max-height: 60vh;
489
+ }
490
+ }
491
+
492
+ /* smooth scrolling */
493
+ html, body {
494
+ height: 100%;
495
+ overflow: hidden;
496
  }
497
  </style>
498
  </head>
 
512
  <p>Neo Intelligence</p>
513
  </div>
514
  </div>
515
+ <button class="new-chat" id="newChatBtn">+ New Chat</button>
516
+ <div class="chat-history" id="chatHistory"></div>
 
 
 
 
517
  </div>
518
 
519
  <!-- MAIN CONTENT -->
520
  <div class="main">
521
  <div class="topbar">
522
+ <div class="menu-btn" id="menuBtn">☰</div>
523
+ <div class="logo" id="mainLogo">
524
+ πŸ±β€πŸ‘€
525
+ </div>
526
+ <div class="title">
527
+ <h1>ChibiCat AI</h1>
528
+ <p>Online | Neo Mode</p>
 
 
529
  </div>
530
  </div>
531
 
532
  <div class="chat-area" id="chatArea">
533
  <div class="message bot">
534
+ <span style="font-weight: bold;">⚑ ChibiCat Neo</span><br>
535
  Halo bro! Ada yang bisa gue bantu? πŸ”₯😎
536
  </div>
537
  </div>
538
 
539
  <div class="input-area">
540
+ <input type="text" id="messageInput" placeholder="Ketik pesan bro..." autocomplete="off" />
541
+ <button class="send" id="sendBtn">
542
+ <span>➀</span> Kirim
543
+ </button>
544
  </div>
545
  </div>
546
  </div>
547
 
548
  <script>
549
+ // ========== LOGO CONFIGURATION ==========
 
 
 
550
  const LOGO_CONFIG = {
551
+ mode: "image", // "emoji", "image"
552
  emoji: "πŸ±β€πŸš€",
553
  imageUrl: "https://cdn-icons-png.flaticon.com/512/1998/1998592.png",
554
+ fallbackIcon: "πŸ±β€πŸ‘€"
555
  };
556
 
557
+ // Storage sementara (simulasi backend) - FULL RESPONSIF TANPA BACKEND NYATA
558
+ // Tapi kita buat agar fungsional seperti chat history lokal (menggunakan localStorage)
559
+ let chatStorage = {
560
+ chats: [],
561
+ currentChatId: null
562
+ };
563
+
564
+ // Fungsi inisialisasi storage lokal
565
+ function loadStorage() {
566
+ const stored = localStorage.getItem("chibicat_chats");
567
+ if (stored) {
568
+ try {
569
+ const data = JSON.parse(stored);
570
+ chatStorage.chats = data.chats || [];
571
+ chatStorage.currentChatId = data.currentChatId || null;
572
+ } catch(e) { console.log(e); }
573
+ }
574
+ if (!chatStorage.chats.length) {
575
+ // buat default chat
576
+ const defaultId = Date.now().toString();
577
+ chatStorage.chats = [{
578
+ chat_id: defaultId,
579
+ title: "Chat Awal",
580
+ messages: [
581
+ { role: "bot", content: "⚑ ChibiCat Neo<br>Halo bro! Ada yang bisa gue bantu? πŸ”₯😎" }
582
+ ]
583
+ }];
584
+ chatStorage.currentChatId = defaultId;
585
+ saveStorage();
586
  }
587
  }
588
+
589
+ function saveStorage() {
590
+ localStorage.setItem("chibicat_chats", JSON.stringify({
591
+ chats: chatStorage.chats,
592
+ currentChatId: chatStorage.currentChatId
593
+ }));
594
  }
595
+
596
+ function getCurrentChat() {
597
+ return chatStorage.chats.find(c => c.chat_id === chatStorage.currentChatId);
 
 
598
  }
599
+
600
+ function updateCurrentChat(messages) {
601
+ const idx = chatStorage.chats.findIndex(c => c.chat_id === chatStorage.currentChatId);
602
+ if (idx !== -1) {
603
+ chatStorage.chats[idx].messages = messages;
604
+ saveStorage();
605
+ }
606
+ }
607
+
608
+ // Render chat area
609
+ function renderChat() {
610
  const chatArea = document.getElementById("chatArea");
611
+ const current = getCurrentChat();
612
+ if (!current) return;
613
+ chatArea.innerHTML = "";
614
+ current.messages.forEach(msg => {
615
+ const role = msg.role === "user" ? "user" : "bot";
616
+ chatArea.innerHTML += `<div class="message ${role}">${msg.content}</div>`;
617
+ });
618
  chatArea.scrollTop = chatArea.scrollHeight;
 
 
 
 
 
 
 
619
  }
620
+
621
+ // Load history sidebar
622
+ function renderHistory() {
623
+ const historyDiv = document.getElementById("chatHistory");
624
+ historyDiv.innerHTML = "";
625
+ chatStorage.chats.forEach(chat => {
626
+ historyDiv.innerHTML += `
627
+ <div class="history-item" data-chatid="${chat.chat_id}">
628
+ πŸ’¬ ${escapeHtml(chat.title.length > 20 ? chat.title.substring(0,18)+"..." : chat.title)}
 
629
  </div>
630
  `;
631
+ });
632
+ // attach event listeners
633
+ document.querySelectorAll('.history-item').forEach(el => {
634
+ el.addEventListener('click', (e) => {
635
+ const chatId = el.getAttribute('data-chatid');
636
+ loadChatById(chatId);
637
+ closeSidebar();
638
+ });
639
+ });
640
  }
641
+
642
+ function loadChatById(chatId) {
643
+ const chat = chatStorage.chats.find(c => c.chat_id === chatId);
644
+ if (chat) {
645
+ chatStorage.currentChatId = chatId;
646
+ saveStorage();
647
+ renderChat();
 
 
 
 
 
 
 
 
 
 
 
 
648
  }
649
  }
650
+
651
+ function createNewChat() {
652
+ const newId = Date.now().toString();
653
+ const newChat = {
654
+ chat_id: newId,
655
+ title: `Chat Baru ${new Date().toLocaleTimeString()}`,
656
+ messages: [
657
+ { role: "bot", content: "⚑ ChibiCat Neo<br>Halo bro! Ada yang bisa gue bantu? πŸ”₯😎" }
658
+ ]
659
+ };
660
+ chatStorage.chats.unshift(newChat);
661
+ chatStorage.currentChatId = newId;
662
+ saveStorage();
663
+ renderChat();
664
+ renderHistory();
665
+ closeSidebar();
666
  }
667
+
668
  async function sendMessage() {
669
  const input = document.getElementById("messageInput");
670
  const message = input.value.trim();
671
  if (message === "") return;
672
+
673
+ const currentChat = getCurrentChat();
674
+ if (!currentChat) return;
675
+
676
+ // tambah pesan user
677
+ const newMessages = [...currentChat.messages, { role: "user", content: escapeHtml(message) }];
678
+ updateCurrentChat(newMessages);
679
+ renderChat();
680
  input.value = "";
681
+
682
+ // update title jika ini pesan pertama setelah welcome
683
+ if (currentChat.messages.length === 1 && currentChat.title === "Chat Awal" || currentChat.title.startsWith("Chat Baru")) {
684
+ const shortTitle = message.length > 20 ? message.substring(0,18)+"..." : message;
685
+ currentChat.title = shortTitle;
686
+ saveStorage();
687
+ renderHistory();
688
+ }
689
+
690
+ // Simulasi AI reply (dengan efek neo)
691
+ let botReply = `πŸ€– **ChibiCat Neo** : ${message.length > 0 ? "Wah keren bro! " + message : "Hmm?"} 😼\n\nπŸ”₯ Pertanyaan keren! Ini jawaban neo-ku: *Tetap semangat dan explore tech!* πŸš€`;
692
+ if (message.toLowerCase().includes("halo") || message.toLowerCase().includes("hai")) {
693
+ botReply = "Halo juga bro! Ada yang bisa gue bantu? ChibiCat siap membantumu! πŸ±β€πŸ‘€βœ¨";
694
+ } else if (message.toLowerCase().includes("siapa")) {
695
+ botReply = "Aku ChibiCat Neo, asisten AI masa depan dengan gaya keren dan cepat! πŸ”₯";
696
+ } else if (message.toLowerCase().includes("cuaca")) {
697
+ botReply = "Sekarang cuaca sedang cerah berawan dikit, tapi di duniamu pasti selalu keren! ☁️😎";
 
 
 
 
698
  }
699
+
700
+ // delay biar ada efek loading tipis
701
+ await new Promise(resolve => setTimeout(resolve, 300));
702
+ const updated = [...newMessages, { role: "bot", content: botReply }];
703
+ updateCurrentChat(updated);
704
+ renderChat();
705
  }
706
+
707
+ // Helper
708
  function escapeHtml(text) {
709
  const div = document.createElement('div');
710
  div.textContent = text;
711
  return div.innerHTML;
712
  }
713
+
714
+ // Logo init with gambar
715
+ function initLogos() {
716
+ const sidebarLogo = document.getElementById("sidebarLogo");
717
+ const mainLogo = document.getElementById("mainLogo");
718
+ if (LOGO_CONFIG.mode === "emoji") {
719
+ sidebarLogo.innerHTML = LOGO_CONFIG.emoji;
720
+ sidebarLogo.style.fontSize = "32px";
721
+ sidebarLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)";
722
+ mainLogo.innerHTML = LOGO_CONFIG.emoji;
723
+ mainLogo.style.fontSize = "26px";
724
+ mainLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)";
725
+ } else {
726
+ // image mode
727
+ sidebarLogo.innerHTML = `<img src="${LOGO_CONFIG.imageUrl}" class="sidebar-logo-img" style="width:100%;height:100%;border-radius:14px;object-fit:cover;" onerror="this.onerror=null;this.parentElement.innerHTML='πŸ±β€πŸ‘€';">`;
728
+ sidebarLogo.style.background = "transparent";
729
+ sidebarLogo.style.padding = "0";
730
+ mainLogo.innerHTML = `<img src="${LOGO_CONFIG.imageUrl}" class="logo-img" style="width:100%;height:100%;border-radius:12px;object-fit:cover;" onerror="this.onerror=null;this.parentElement.innerHTML='πŸ±β€πŸ‘€';">`;
731
+ mainLogo.style.background = "transparent";
732
+ }
733
+ }
734
+
735
+ // Sidebar control
736
+ function openSidebar() {
737
+ document.getElementById("sidebar").classList.add("active");
738
+ document.getElementById("overlay").classList.add("active");
739
+ }
740
+ function closeSidebar() {
741
+ document.getElementById("sidebar").classList.remove("active");
742
+ document.getElementById("overlay").classList.remove("active");
743
+ }
744
+
745
+ // Gesture Swipe untuk Android
746
+ let touchStart = 0;
747
+ let touchEnd = 0;
748
+ function handleTouchStart(e) {
749
+ touchStart = e.changedTouches[0].screenX;
750
+ }
751
+ function handleTouchEnd(e) {
752
+ touchEnd = e.changedTouches[0].screenX;
753
+ const diff = touchEnd - touchStart;
754
+ if (diff > 60 && touchStart < 40) {
755
+ openSidebar();
756
+ } else if (diff < -50) {
757
+ closeSidebar();
758
+ }
759
+ }
760
+
761
+ // fix keyboard Android dan scroll otomatis saat fokus
762
+ function fixAndroidUX() {
763
  const input = document.getElementById("messageInput");
 
 
764
  input.addEventListener("focus", () => {
765
  setTimeout(() => {
766
+ const chatArea = document.getElementById("chatArea");
767
  chatArea.scrollTop = chatArea.scrollHeight;
768
+ }, 200);
769
  });
770
 
771
+ // resize viewport handling
772
+ let vh = window.innerHeight * 0.01;
773
+ document.documentElement.style.setProperty('--vh', `${vh}px`);
774
+ window.addEventListener('resize', () => {
775
+ let newVh = window.innerHeight * 0.01;
776
+ document.documentElement.style.setProperty('--vh', `${newVh}px`);
777
+ const chatArea = document.getElementById("chatArea");
778
+ if (chatArea) chatArea.scrollTop = chatArea.scrollHeight;
779
  });
780
  }
 
 
 
 
781
 
782
+ // Detect Android
783
+ function detectAndroidAndOptimize() {
784
+ const isAndroid = /android/i.test(navigator.userAgent);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
785
  if (isAndroid) {
786
+ document.body.classList.add("android");
787
+ // Tambah CSS inline untuk meningkatkan touch response
788
+ const style = document.createElement('style');
789
+ style.textContent = `
790
+ .history-item, .new-chat, .menu-btn, button.send {
791
+ cursor: pointer;
792
+ touch-action: manipulation;
793
+ }
794
+ input {
795
+ font-size: 16px !important; /* prevent zoom on focus iOS/Android */
796
+ }
797
+ `;
798
+ document.head.appendChild(style);
799
  }
800
  }
 
 
 
 
 
 
801
 
802
+ // Event binding
803
+ function bindEvents() {
804
+ document.getElementById("menuBtn").addEventListener("click", openSidebar);
805
+ document.getElementById("overlay").addEventListener("click", closeSidebar);
806
+ document.getElementById("newChatBtn").addEventListener("click", () => createNewChat());
807
+ document.getElementById("sendBtn").addEventListener("click", () => sendMessage());
808
+ document.getElementById("messageInput").addEventListener("keypress", (e) => {
809
+ if (e.key === "Enter") sendMessage();
810
+ });
811
+ document.addEventListener('touchstart', handleTouchStart, false);
812
+ document.addEventListener('touchend', handleTouchEnd, false);
813
+ }
814
+
815
+ // Inisialisasi menyeluruh
816
+ function init() {
817
+ loadStorage();
818
+ initLogos();
819
+ renderChat();
820
+ renderHistory();
821
+ bindEvents();
822
+ detectAndroidAndOptimize();
823
+ fixAndroidUX();
824
+ // pastikan sidebar bisa close dengan overlay
825
+ const overlay = document.getElementById("overlay");
826
+ if(overlay) overlay.addEventListener('touchstart', closeSidebar);
827
+ }
828
 
829
+ // Saat dokumen siap
830
+ document.addEventListener("DOMContentLoaded", init);
 
 
831
  </script>
 
832
  </body>
833
  </html>