andrewwda commited on
Commit
d278ac9
·
verified ·
1 Parent(s): a9e421a

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +310 -413
templates/index.html CHANGED
@@ -7,7 +7,7 @@
7
  <meta name="mobile-web-app-capable" content="yes">
8
  <meta name="apple-mobile-web-app-capable" content="yes">
9
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
10
- <title>ChibiCat AI - Neo Mobile</title>
11
  <style>
12
  * {
13
  margin: 0;
@@ -19,6 +19,7 @@
19
  body {
20
  background: #0a0c15;
21
  overflow: hidden;
 
22
  -webkit-tap-highlight-color: transparent;
23
  touch-action: pan-x pan-y;
24
  }
@@ -36,15 +37,15 @@
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;
@@ -60,34 +61,34 @@
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
 
68
  .sidebar-logo {
69
- width: 50px;
70
- height: 50px;
71
- border-radius: 14px;
72
  background: linear-gradient(135deg, #00d4ff, #0099ff);
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;
@@ -97,7 +98,7 @@
97
 
98
  .sidebar p {
99
  color: #6c7a9e;
100
- font-size: 11px;
101
  }
102
 
103
  .new-chat {
@@ -105,43 +106,42 @@
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;
114
  box-shadow: 0 4px 12px rgba(0, 212, 255, 0.3);
115
- width: 100%;
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
  /* =========================
@@ -155,7 +155,6 @@
155
  backdrop-filter: blur(3px);
156
  z-index: 998;
157
  display: none;
158
- transition: 0.2s;
159
  }
160
 
161
  .overlay.active {
@@ -173,98 +172,97 @@
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
  }
218
 
219
  .logo-img {
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 {
241
- font-size: 10px;
242
  color: #00d4ff;
243
  display: flex;
244
  align-items: center;
245
- gap: 4px;
246
  }
247
 
248
  .title p::before {
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
  }
265
 
266
  .chat-area::-webkit-scrollbar {
267
- width: 4px;
268
  }
269
  .chat-area::-webkit-scrollbar-track {
270
  background: #1a1f2e;
@@ -276,20 +274,20 @@
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,7 +301,7 @@
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,29 +310,28 @@
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;
@@ -342,7 +339,7 @@
342
 
343
  input:focus {
344
  border-color: #00d4ff;
345
- box-shadow: 0 0 8px rgba(0, 212, 255, 0.3);
346
  }
347
 
348
  input::placeholder {
@@ -354,145 +351,125 @@
354
  background: linear-gradient(135deg, #00d4ff, #0099ff);
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>
@@ -501,10 +478,12 @@
501
  <div class="overlay" id="overlay" onclick="closeSidebar()"></div>
502
 
503
  <div class="app">
 
504
  <!-- SIDEBAR -->
505
  <div class="sidebar" id="sidebar">
506
  <div class="sidebar-header">
507
  <div class="sidebar-logo" id="sidebarLogo">
 
508
  🐱‍👤
509
  </div>
510
  <div>
@@ -512,15 +491,17 @@
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">
@@ -529,6 +510,7 @@
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>
@@ -536,298 +518,213 @@
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>
 
7
  <meta name="mobile-web-app-capable" content="yes">
8
  <meta name="apple-mobile-web-app-capable" content="yes">
9
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
10
+ <title>ChibiCat AI - Neo</title>
11
  <style>
12
  * {
13
  margin: 0;
 
19
  body {
20
  background: #0a0c15;
21
  overflow: hidden;
22
+ /* TAMBAHAN UNTUK ANDROID */
23
  -webkit-tap-highlight-color: transparent;
24
  touch-action: pan-x pan-y;
25
  }
 
37
  SIDEBAR - DARK NEO
38
  ========================= */
39
  .sidebar {
40
+ width: 300px;
41
  background: linear-gradient(180deg, #0f111a 0%, #080a10 100%);
42
  backdrop-filter: blur(10px);
43
  color: #e8edff;
44
  display: flex;
45
  flex-direction: column;
46
+ padding: 24px 20px;
47
  position: fixed;
48
+ left: -320px;
49
  top: 0;
50
  height: 100vh;
51
  z-index: 999;
 
61
  .sidebar-header {
62
  display: flex;
63
  align-items: center;
64
+ margin-bottom: 35px;
65
+ padding-bottom: 20px;
66
  border-bottom: 1px solid rgba(0, 212, 255, 0.2);
67
  }
68
 
69
  .sidebar-logo {
70
+ width: 55px;
71
+ height: 55px;
72
+ border-radius: 16px;
73
  background: linear-gradient(135deg, #00d4ff, #0099ff);
74
  display: flex;
75
  align-items: center;
76
  justify-content: center;
77
+ font-size: 32px;
78
+ margin-right: 15px;
79
  box-shadow: 0 0 15px rgba(0, 212, 255, 0.4);
 
80
  }
81
 
82
+ /* GAMBAR LOGO UNTUK SIDEBAR */
83
  .sidebar-logo-img {
84
+ width: 55px;
85
+ height: 55px;
86
+ border-radius: 16px;
87
  object-fit: cover;
88
  }
89
 
90
  .sidebar h2 {
91
+ font-size: 26px;
92
  letter-spacing: -0.5px;
93
  background: linear-gradient(135deg, #00d4ff, #4d9eff);
94
  -webkit-background-clip: text;
 
98
 
99
  .sidebar p {
100
  color: #6c7a9e;
101
+ font-size: 13px;
102
  }
103
 
104
  .new-chat {
 
106
  border: none;
107
  color: #0a0c15;
108
  font-weight: bold;
109
+ padding: 14px;
110
  border-radius: 14px;
111
+ font-size: 16px;
112
  cursor: pointer;
113
+ margin-bottom: 30px;
114
  transition: all 0.2s;
115
  box-shadow: 0 4px 12px rgba(0, 212, 255, 0.3);
 
116
  }
117
 
118
+ .new-chat:hover {
119
+ transform: translateY(-2px);
120
+ box-shadow: 0 6px 20px rgba(0, 212, 255, 0.5);
121
  }
122
 
123
  .chat-history {
124
  flex: 1;
125
  overflow-y: auto;
 
126
  }
127
 
128
  .history-item {
129
  background: rgba(18, 22, 40, 0.7);
130
  backdrop-filter: blur(5px);
131
+ padding: 14px 18px;
132
+ border-radius: 14px;
133
+ margin-bottom: 12px;
134
  cursor: pointer;
135
+ transition: 0.2s;
136
  border: 1px solid rgba(0, 212, 255, 0.1);
137
  font-weight: 500;
138
+ font-size: 14px;
 
139
  }
140
 
141
+ .history-item:hover {
142
+ background: rgba(0, 212, 255, 0.15);
143
+ border-color: rgba(0, 212, 255, 0.4);
144
+ transform: translateX(5px);
145
  }
146
 
147
  /* =========================
 
155
  backdrop-filter: blur(3px);
156
  z-index: 998;
157
  display: none;
 
158
  }
159
 
160
  .overlay.active {
 
172
  }
173
 
174
  /* =========================
175
+ TOPBAR - NEO MASCULINE
176
  ========================= */
177
  .topbar {
178
+ height: 80px;
179
  background: rgba(10, 12, 21, 0.95);
180
  backdrop-filter: blur(10px);
181
  display: flex;
182
  align-items: center;
183
+ padding: 0 30px;
184
  color: #e8edff;
185
  border-bottom: 1px solid rgba(0, 212, 255, 0.3);
186
  box-shadow: 0 2px 20px rgba(0, 0, 0, 0.3);
 
187
  }
188
 
189
  .menu-btn {
190
+ font-size: 28px;
191
+ margin-right: 20px;
192
  cursor: pointer;
193
+ transition: 0.2s;
194
  color: #00d4ff;
 
 
195
  }
196
 
197
+ .menu-btn:hover {
198
+ transform: scale(1.1);
199
+ text-shadow: 0 0 8px #00d4ff;
200
  }
201
 
202
+ /* LOGO AREA - DENGAN GAMBAR */
203
  .logo {
204
+ width: 50px;
205
+ height: 50px;
206
+ border-radius: 14px;
207
  background: linear-gradient(135deg, #00d4ff, #0099ff);
208
  display: flex;
209
  align-items: center;
210
  justify-content: center;
211
+ margin-right: 15px;
212
+ box-shadow: 0 0 12px rgba(0, 212, 255, 0.5);
213
  overflow: hidden;
 
214
  }
215
 
216
  .logo-img {
217
  width: 100%;
218
  height: 100%;
219
  object-fit: cover;
220
+ border-radius: 14px;
221
  }
222
 
223
+ /* Fallback jika gambar tidak ada - pake icon keren */
224
  .logo-icon {
225
+ font-size: 28px;
226
  }
227
 
228
  .title h1 {
229
+ font-size: 24px;
230
+ letter-spacing: -0.5px;
231
  background: linear-gradient(135deg, #e8edff, #00d4ff);
232
  -webkit-background-clip: text;
233
  background-clip: text;
234
  color: transparent;
 
235
  }
236
 
237
  .title p {
238
+ font-size: 13px;
239
  color: #00d4ff;
240
  display: flex;
241
  align-items: center;
242
+ gap: 6px;
243
  }
244
 
245
  .title p::before {
246
  content: "●";
247
+ font-size: 10px;
248
  color: #00ff88;
249
  text-shadow: 0 0 5px #00ff88;
250
  }
251
 
252
  /* =========================
253
+ CHAT AREA - GLASS
254
  ========================= */
255
  .chat-area {
256
  flex: 1;
257
  overflow-y: auto;
258
+ padding: 30px;
259
  background: linear-gradient(180deg, #0a0c15 0%, #0f111a 100%);
260
+ /* TAMBAHAN UNTUK ANDROID */
261
  -webkit-overflow-scrolling: touch;
262
  }
263
 
264
  .chat-area::-webkit-scrollbar {
265
+ width: 6px;
266
  }
267
  .chat-area::-webkit-scrollbar-track {
268
  background: #1a1f2e;
 
274
  }
275
 
276
  .message {
277
+ max-width: 75%;
278
+ padding: 14px 20px;
279
  border-radius: 20px;
280
+ margin-bottom: 20px;
281
+ font-size: 16px;
282
+ line-height: 1.5;
283
  word-wrap: break-word;
284
+ animation: fadeInUp 0.3s ease;
285
  }
286
 
287
  @keyframes fadeInUp {
288
  from {
289
  opacity: 0;
290
+ transform: translateY(10px);
291
  }
292
  to {
293
  opacity: 1;
 
301
  border: 1px solid rgba(0, 212, 255, 0.2);
302
  color: #d0d9ff;
303
  border-bottom-left-radius: 6px;
304
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
305
  }
306
 
307
  .user {
 
310
  font-weight: 600;
311
  margin-left: auto;
312
  border-bottom-right-radius: 6px;
313
+ box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3);
314
  }
315
 
316
  /* =========================
317
+ INPUT - CYBER
318
  ========================= */
319
  .input-area {
320
  display: flex;
321
+ padding: 20px 30px;
322
+ gap: 15px;
323
  background: rgba(10, 12, 21, 0.95);
324
  backdrop-filter: blur(10px);
325
  border-top: 1px solid rgba(0, 212, 255, 0.2);
 
326
  }
327
 
328
  input {
329
  flex: 1;
330
  border: 1px solid rgba(0, 212, 255, 0.3);
331
  background: rgba(18, 22, 40, 0.8);
332
+ border-radius: 18px;
333
+ padding: 16px 22px;
334
+ font-size: 16px;
335
  outline: none;
336
  color: #e8edff;
337
  transition: all 0.2s;
 
339
 
340
  input:focus {
341
  border-color: #00d4ff;
342
+ box-shadow: 0 0 12px rgba(0, 212, 255, 0.3);
343
  }
344
 
345
  input::placeholder {
 
351
  background: linear-gradient(135deg, #00d4ff, #0099ff);
352
  color: #0a0c15;
353
  font-weight: bold;
354
+ padding: 0 32px;
355
+ border-radius: 18px;
356
+ font-size: 16px;
357
  cursor: pointer;
358
+ transition: 0.2s;
359
  display: flex;
360
  align-items: center;
361
+ gap: 8px;
 
362
  }
363
 
364
+ button.send:hover {
365
+ transform: scale(1.02);
366
+ box-shadow: 0 0 15px rgba(0, 212, 255, 0.5);
367
  }
368
 
369
  /* =========================
370
+ RESPONSIVE UNTUK ANDROID
371
  ========================= */
372
+ @media (max-width: 768px) {
373
  .sidebar {
374
  width: 85%;
 
375
  }
376
  .message {
377
+ max-width: 90%;
378
+ font-size: 15px;
 
 
 
 
379
  }
380
  .topbar {
381
+ padding: 0 20px;
382
  }
383
+ .input-area {
384
+ padding: 15px 20px;
385
  }
386
+ button.send {
387
+ padding: 0 24px;
 
388
  }
389
  }
390
 
391
+ /* TAMBAHAN KHUSUS UNTUK ANDROID */
392
  @media (max-width: 480px) {
393
  .topbar {
394
+ height: 65px;
395
+ padding: 0 15px;
396
  }
397
  .menu-btn {
398
  font-size: 24px;
399
+ margin-right: 12px;
400
  }
401
  .logo {
402
+ width: 40px;
403
+ height: 40px;
 
404
  }
405
  .title h1 {
406
+ font-size: 18px;
407
  }
408
  .title p {
409
+ font-size: 10px;
410
  }
411
  .chat-area {
412
+ padding: 15px;
413
  }
414
  .message {
415
+ padding: 10px 14px;
416
+ font-size: 14px;
417
  max-width: 95%;
 
418
  }
419
  .input-area {
420
+ padding: 10px 15px;
421
+ gap: 10px;
422
  }
423
  input {
424
+ padding: 12px 16px;
425
  font-size: 14px;
426
  }
427
  button.send {
428
+ padding: 0 18px;
429
+ font-size: 14px;
430
  }
431
  .sidebar {
432
+ width: 80%;
433
+ padding: 20px 15px;
434
  }
435
  .sidebar-logo {
436
+ width: 45px;
437
+ height: 45px;
438
  font-size: 24px;
439
  }
440
  .sidebar h2 {
441
+ font-size: 22px;
442
  }
443
  .new-chat {
444
+ padding: 12px;
445
+ font-size: 14px;
446
  }
447
  .history-item {
448
+ padding: 12px 15px;
449
+ font-size: 13px;
450
  }
451
  }
452
 
453
+ /* TAMBAHAN UNTUK LANDSCAPE MODE ANDROID */
454
  @media (max-width: 900px) and (orientation: landscape) {
455
  .topbar {
456
+ height: 55px;
457
  }
458
  .chat-area {
459
+ padding: 10px 20px;
460
  }
461
  .message {
462
+ margin-bottom: 10px;
 
463
  }
464
  .input-area {
465
+ padding: 8px 15px;
466
  }
467
  input {
468
+ padding: 10px 16px;
469
  }
470
  button.send {
471
+ padding: 0 20px;
472
  }
 
 
 
 
 
 
 
 
 
 
 
 
473
  }
474
  </style>
475
  </head>
 
478
  <div class="overlay" id="overlay" onclick="closeSidebar()"></div>
479
 
480
  <div class="app">
481
+
482
  <!-- SIDEBAR -->
483
  <div class="sidebar" id="sidebar">
484
  <div class="sidebar-header">
485
  <div class="sidebar-logo" id="sidebarLogo">
486
+ <!-- Logo akan diisi JS -->
487
  🐱‍👤
488
  </div>
489
  <div>
 
491
  <p>Neo Intelligence</p>
492
  </div>
493
  </div>
494
+ <button class="new-chat" onclick="createNewChat()">+ New Chat</button>
495
  <div class="chat-history" id="chatHistory"></div>
496
  </div>
497
 
498
+ <!-- MAIN -->
499
  <div class="main">
500
+ <!-- TOPBAR -->
501
  <div class="topbar">
502
+ <div class="menu-btn" onclick="openSidebar()">☰</div>
503
  <div class="logo" id="mainLogo">
504
+ <!-- Logo akan diisi JS -->
505
  🐱‍👤
506
  </div>
507
  <div class="title">
 
510
  </div>
511
  </div>
512
 
513
+ <!-- CHAT AREA -->
514
  <div class="chat-area" id="chatArea">
515
  <div class="message bot">
516
  <span style="font-weight: bold;">⚡ ChibiCat Neo</span><br>
 
518
  </div>
519
  </div>
520
 
521
+ <!-- INPUT -->
522
  <div class="input-area">
523
+ <input type="text" id="messageInput" placeholder="Ketik pesan bro..." />
524
+ <button class="send" onclick="sendMessage()">
525
+ <span></span> Kirim
526
  </button>
527
  </div>
528
  </div>
529
  </div>
530
 
531
  <script>
532
+ let currentChatId = null
533
+
534
+ // =========================================
535
+ // KONFIGURASI LOGO
536
+ // =========================================
537
+ // Pilih salah satu mode logo:
538
+ // mode: "emoji" = pake emoji keren
539
+ // mode: "image" = pake gambar dari URL
540
+ // mode: "svg" = pake karakter ASCII keren
541
 
542
+ const LOGO_CONFIG = {
543
+ mode: "image", // "emoji", "image", atau "svg"
544
+ emoji: "🐱‍🚀", // emoji untuk mode emoji
545
+ imageUrl: "/static/logo.png", // cyber cat image
546
+ imageUrl2: "https://cdn-icons-png.flaticon.com/512/1998/1998592.png", // alternative robot cat
547
+ svgIcon: "⚡🐱⚡" // untuk mode svg/ascii
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  }
549
 
550
+ function initLogos() {
551
+ const sidebarLogo = document.getElementById("sidebarLogo")
552
+ const mainLogo = document.getElementById("mainLogo")
553
+
554
+ if (LOGO_CONFIG.mode === "emoji") {
555
+ // Mode Emoji - simple tapi keren
556
+ sidebarLogo.innerHTML = LOGO_CONFIG.emoji
557
+ sidebarLogo.style.fontSize = "32px"
558
+ sidebarLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)"
559
+
560
+ mainLogo.innerHTML = LOGO_CONFIG.emoji
561
+ mainLogo.style.fontSize = "28px"
562
+ mainLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)"
563
+
564
+ } else if (LOGO_CONFIG.mode === "image") {
565
+ // Mode Gambar - lebih profesional
566
+ sidebarLogo.innerHTML = `<img src="${LOGO_CONFIG.imageUrl}" class="sidebar-logo-img" style="width:100%;height:100%;border-radius:16px;object-fit:cover;" onerror="this.src='${LOGO_CONFIG.imageUrl2}'">`
567
+ sidebarLogo.style.background = "transparent"
568
+ sidebarLogo.style.padding = "0"
569
+ sidebarLogo.style.boxShadow = "0 0 15px rgba(0,212,255,0.4)"
570
+
571
+ mainLogo.innerHTML = `<img src="${LOGO_CONFIG.imageUrl}" class="logo-img" style="width:100%;height:100%;border-radius:14px;object-fit:cover;" onerror="this.src='${LOGO_CONFIG.imageUrl2}'">`
572
+ mainLogo.style.background = "transparent"
573
+ mainLogo.style.padding = "0"
574
+
575
+ } else {
576
+ // Mode SVG/ASCII - modern tech look
577
+ sidebarLogo.innerHTML = LOGO_CONFIG.svgIcon
578
+ sidebarLogo.style.fontSize = "28px"
579
+ sidebarLogo.style.fontWeight = "bold"
580
+ sidebarLogo.style.letterSpacing = "2px"
581
+
582
+ mainLogo.innerHTML = LOGO_CONFIG.svgIcon
583
+ mainLogo.style.fontSize = "24px"
584
+ mainLogo.style.fontWeight = "bold"
585
+ }
586
  }
587
+
588
+ function openSidebar() {
589
+ document.getElementById("sidebar").classList.add("active")
590
+ document.getElementById("overlay").classList.add("active")
591
  }
592
+
593
+ function closeSidebar() {
594
+ document.getElementById("sidebar").classList.remove("active")
595
+ document.getElementById("overlay").classList.remove("active")
 
 
 
596
  }
597
+
598
+ async function createNewChat() {
599
+ const response = await fetch("/new_chat", { method: "POST" })
600
+ const data = await response.json()
601
+ currentChatId = data.chat_id
602
+ document.getElementById("chatArea").innerHTML = `
603
+ <div class="message bot">
604
+ <span style="font-weight: bold;"> ChibiCat Neo</span><br>
605
+ Halo bro! Ada yang bisa gue bantu? 🔥😎
606
+ </div>
607
+ `
608
+ loadChats()
609
+ closeSidebar()
610
  }
611
+
612
+ async function loadChats() {
613
+ const response = await fetch("/get_chats")
614
+ const chats = await response.json()
615
+ const history = document.getElementById("chatHistory")
616
+ history.innerHTML = ""
617
+ chats.forEach(chat => {
618
+ history.innerHTML += `
619
+ <div class="history-item" onclick="loadChat('${chat.chat_id}')">
620
+ 💬 ${chat.title}
621
  </div>
622
+ `
623
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
  }
625
+
626
+ async function loadChat(chatId) {
627
+ currentChatId = chatId
628
+ const response = await fetch(`/load_chat/${chatId}`)
629
+ const messages = await response.json()
630
+ const chatArea = document.getElementById("chatArea")
631
+ chatArea.innerHTML = ""
632
+ messages.forEach(msg => {
633
+ const role = msg.role === "user" ? "user" : "bot"
634
+ chatArea.innerHTML += `<div class="message ${role}">${msg.content}</div>`
635
+ })
636
+ closeSidebar()
 
 
 
 
637
  }
638
+
639
  async function sendMessage() {
640
+ const input = document.getElementById("messageInput")
641
+ const message = input.value.trim()
642
+ if (message === "") return
643
+
644
+ const chatArea = document.getElementById("chatArea")
645
+
646
+ chatArea.innerHTML += `<div class="message user">${escapeHtml(message)}</div>`
647
+ input.value = ""
648
+ chatArea.scrollTop = chatArea.scrollHeight
649
+
650
+ try {
651
+ const response = await fetch("/chat", {
652
+ method: "POST",
653
+ headers: { "Content-Type": "application/json" },
654
+ body: JSON.stringify({ message: message })
655
+ })
656
+ const data = await response.json()
657
+ chatArea.innerHTML += `<div class="message bot">${escapeHtml(data.reply)}</div>`
658
+ chatArea.scrollTop = chatArea.scrollHeight
659
+ loadChats()
660
+ } catch (error) {
661
+ chatArea.innerHTML += `<div class="message bot">⚠️ Server error, coba lagi bro 😭</div>`
662
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
663
  }
664
+
665
+ // TAMBAHAN: Fungsi untuk menghindari XSS dan support emoji di Android
666
  function escapeHtml(text) {
667
+ const div = document.createElement('div')
668
+ div.textContent = text
669
+ return div.innerHTML
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  }
671
+
672
+ // TAMBAHAN: Fix untuk keyboard Android
673
+ function fixAndroidKeyboard() {
674
+ const input = document.getElementById("messageInput")
675
+ if (input) {
676
+ input.addEventListener("focus", function() {
677
+ setTimeout(() => {
678
+ document.querySelector('.chat-area').scrollTop = document.querySelector('.chat-area').scrollHeight
679
+ }, 300)
680
+ })
681
  }
682
  }
683
+
684
+ // TAMBAHAN: Deteksi Android dan tambah kelas khusus
685
+ function detectAndroid() {
686
+ const userAgent = navigator.userAgent.toLowerCase()
687
+ const isAndroid = userAgent.indexOf("android") > -1
 
 
 
 
 
688
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
  if (isAndroid) {
690
+ document.body.classList.add("android-device")
691
+ console.log("✅ Android device detected - optimized layout")
 
 
 
 
 
 
 
 
 
 
 
692
  }
693
  }
694
+
695
+ document.getElementById("messageInput").addEventListener("keypress", function(e) {
696
+ if (e.key === "Enter") sendMessage()
697
+ })
698
+
699
+ // TAMBAHAN: Swipe gesture untuk Android (geser kanan buka sidebar)
700
+ let touchStartX = 0
701
+ let touchEndX = 0
702
 
703
+ document.addEventListener('touchstart', function(e) {
704
+ touchStartX = e.changedTouches[0].screenX
705
+ }, false)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706
 
707
+ document.addEventListener('touchend', function(e) {
708
+ touchEndX = e.changedTouches[0].screenX
709
+ const swipeDistance = touchEndX - touchStartX
710
+
711
+ // Geser kanan lebih dari 50px untuk buka sidebar
712
+ if (swipeDistance > 50 && touchStartX < 50) {
713
+ openSidebar()
714
+ }
715
+
716
+ // Geser kiri lebih dari 50px untuk tutup sidebar
717
+ if (swipeDistance < -50) {
718
+ closeSidebar()
719
+ }
720
+ }, false)
721
+
722
+ // Inisialisasi logo saat halaman load
723
+ initLogos()
724
+ loadChats()
725
+ detectAndroid()
726
+ fixAndroidKeyboard()
727
  </script>
728
+
729
  </body>
730
  </html>