Spaces:
Sleeping
Sleeping
| <html lang="id"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover"> | |
| <meta name="theme-color" content="#0a0c15"> | |
| <meta name="mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-capable" content="yes"> | |
| <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | |
| <title>ChibiCat AI - Neo</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-family: 'Segoe UI', 'Poppins', 'Inter', system-ui, -apple-system, sans-serif; | |
| } | |
| body { | |
| background: #0a0c15; | |
| overflow: hidden; | |
| /* TAMBAHAN UNTUK ANDROID */ | |
| -webkit-tap-highlight-color: transparent; | |
| touch-action: pan-x pan-y; | |
| } | |
| /* ========================= | |
| APP | |
| ========================= */ | |
| .app { | |
| display: flex; | |
| height: 100vh; | |
| width: 100%; | |
| } | |
| /* ========================= | |
| SIDEBAR - DARK NEO | |
| ========================= */ | |
| .sidebar { | |
| width: 300px; | |
| background: linear-gradient(180deg, #0f111a 0%, #080a10 100%); | |
| backdrop-filter: blur(10px); | |
| color: #e8edff; | |
| display: flex; | |
| flex-direction: column; | |
| padding: 24px 20px; | |
| position: fixed; | |
| left: -320px; | |
| top: 0; | |
| height: 100vh; | |
| z-index: 999; | |
| transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| border-right: 1px solid rgba(0, 255, 255, 0.15); | |
| box-shadow: 4px 0 30px rgba(0, 0, 0, 0.5); | |
| } | |
| .sidebar.active { | |
| left: 0; | |
| } | |
| .sidebar-header { | |
| display: flex; | |
| align-items: center; | |
| margin-bottom: 35px; | |
| padding-bottom: 20px; | |
| border-bottom: 1px solid rgba(0, 212, 255, 0.2); | |
| } | |
| .sidebar-logo { | |
| width: 55px; | |
| height: 55px; | |
| border-radius: 16px; | |
| background: linear-gradient(135deg, #00d4ff, #0099ff); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 32px; | |
| margin-right: 15px; | |
| box-shadow: 0 0 15px rgba(0, 212, 255, 0.4); | |
| } | |
| /* GAMBAR LOGO UNTUK SIDEBAR */ | |
| .sidebar-logo-img { | |
| width: 55px; | |
| height: 55px; | |
| border-radius: 16px; | |
| object-fit: cover; | |
| } | |
| .sidebar h2 { | |
| font-size: 26px; | |
| letter-spacing: -0.5px; | |
| background: linear-gradient(135deg, #00d4ff, #4d9eff); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .sidebar p { | |
| color: #6c7a9e; | |
| font-size: 13px; | |
| } | |
| .new-chat { | |
| background: linear-gradient(135deg, #00d4ff, #0099ff); | |
| border: none; | |
| color: #0a0c15; | |
| font-weight: bold; | |
| padding: 14px; | |
| border-radius: 14px; | |
| font-size: 16px; | |
| cursor: pointer; | |
| margin-bottom: 30px; | |
| transition: all 0.2s; | |
| box-shadow: 0 4px 12px rgba(0, 212, 255, 0.3); | |
| } | |
| .new-chat:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(0, 212, 255, 0.5); | |
| } | |
| .chat-history { | |
| flex: 1; | |
| overflow-y: auto; | |
| } | |
| .history-item { | |
| background: rgba(18, 22, 40, 0.7); | |
| backdrop-filter: blur(5px); | |
| padding: 14px 18px; | |
| border-radius: 14px; | |
| margin-bottom: 12px; | |
| cursor: pointer; | |
| transition: 0.2s; | |
| border: 1px solid rgba(0, 212, 255, 0.1); | |
| font-weight: 500; | |
| font-size: 14px; | |
| } | |
| .history-item:hover { | |
| background: rgba(0, 212, 255, 0.15); | |
| border-color: rgba(0, 212, 255, 0.4); | |
| transform: translateX(5px); | |
| } | |
| /* ========================= | |
| OVERLAY | |
| ========================= */ | |
| .overlay { | |
| position: fixed; | |
| width: 100%; | |
| height: 100vh; | |
| background: rgba(0, 0, 0, 0.6); | |
| backdrop-filter: blur(3px); | |
| z-index: 998; | |
| display: none; | |
| } | |
| .overlay.active { | |
| display: block; | |
| } | |
| /* ========================= | |
| MAIN - CYBER DARK | |
| ========================= */ | |
| .main { | |
| display: flex; | |
| flex-direction: column; | |
| width: 100%; | |
| background: #0a0c15; | |
| } | |
| /* ========================= | |
| TOPBAR - NEO MASCULINE | |
| ========================= */ | |
| .topbar { | |
| height: 80px; | |
| background: rgba(10, 12, 21, 0.95); | |
| backdrop-filter: blur(10px); | |
| display: flex; | |
| align-items: center; | |
| padding: 0 30px; | |
| color: #e8edff; | |
| border-bottom: 1px solid rgba(0, 212, 255, 0.3); | |
| box-shadow: 0 2px 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .menu-btn { | |
| font-size: 28px; | |
| margin-right: 20px; | |
| cursor: pointer; | |
| transition: 0.2s; | |
| color: #00d4ff; | |
| } | |
| .menu-btn:hover { | |
| transform: scale(1.1); | |
| text-shadow: 0 0 8px #00d4ff; | |
| } | |
| /* LOGO AREA - DENGAN GAMBAR */ | |
| .logo { | |
| width: 50px; | |
| height: 50px; | |
| border-radius: 14px; | |
| background: linear-gradient(135deg, #00d4ff, #0099ff); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| margin-right: 15px; | |
| box-shadow: 0 0 12px rgba(0, 212, 255, 0.5); | |
| overflow: hidden; | |
| } | |
| .logo-img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| border-radius: 14px; | |
| } | |
| /* Fallback jika gambar tidak ada - pake icon keren */ | |
| .logo-icon { | |
| font-size: 28px; | |
| } | |
| .title h1 { | |
| font-size: 24px; | |
| letter-spacing: -0.5px; | |
| background: linear-gradient(135deg, #e8edff, #00d4ff); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .title p { | |
| font-size: 13px; | |
| color: #00d4ff; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .title p::before { | |
| content: "●"; | |
| font-size: 10px; | |
| color: #00ff88; | |
| text-shadow: 0 0 5px #00ff88; | |
| } | |
| /* ========================= | |
| CHAT AREA - GLASS | |
| ========================= */ | |
| .chat-area { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 30px; | |
| background: linear-gradient(180deg, #0a0c15 0%, #0f111a 100%); | |
| /* TAMBAHAN UNTUK ANDROID */ | |
| -webkit-overflow-scrolling: touch; | |
| } | |
| .chat-area::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .chat-area::-webkit-scrollbar-track { | |
| background: #1a1f2e; | |
| border-radius: 10px; | |
| } | |
| .chat-area::-webkit-scrollbar-thumb { | |
| background: #00d4ff; | |
| border-radius: 10px; | |
| } | |
| .message { | |
| max-width: 75%; | |
| padding: 14px 20px; | |
| border-radius: 20px; | |
| margin-bottom: 20px; | |
| font-size: 16px; | |
| line-height: 1.5; | |
| word-wrap: break-word; | |
| animation: fadeInUp 0.3s ease; | |
| } | |
| @keyframes fadeInUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .bot { | |
| background: rgba(20, 24, 40, 0.9); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(0, 212, 255, 0.2); | |
| color: #d0d9ff; | |
| border-bottom-left-radius: 6px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); | |
| } | |
| .user { | |
| background: linear-gradient(135deg, #00d4ff, #0099ff); | |
| color: #0a0c15; | |
| font-weight: 600; | |
| margin-left: auto; | |
| border-bottom-right-radius: 6px; | |
| box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3); | |
| } | |
| /* ========================= | |
| INPUT - CYBER | |
| ========================= */ | |
| .input-area { | |
| display: flex; | |
| padding: 20px 30px; | |
| gap: 15px; | |
| background: rgba(10, 12, 21, 0.95); | |
| backdrop-filter: blur(10px); | |
| border-top: 1px solid rgba(0, 212, 255, 0.2); | |
| } | |
| input { | |
| flex: 1; | |
| border: 1px solid rgba(0, 212, 255, 0.3); | |
| background: rgba(18, 22, 40, 0.8); | |
| border-radius: 18px; | |
| padding: 16px 22px; | |
| font-size: 16px; | |
| outline: none; | |
| color: #e8edff; | |
| transition: all 0.2s; | |
| } | |
| input:focus { | |
| border-color: #00d4ff; | |
| box-shadow: 0 0 12px rgba(0, 212, 255, 0.3); | |
| } | |
| input::placeholder { | |
| color: #4a5578; | |
| } | |
| button.send { | |
| border: none; | |
| background: linear-gradient(135deg, #00d4ff, #0099ff); | |
| color: #0a0c15; | |
| font-weight: bold; | |
| padding: 0 32px; | |
| border-radius: 18px; | |
| font-size: 16px; | |
| cursor: pointer; | |
| transition: 0.2s; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| button.send:hover { | |
| transform: scale(1.02); | |
| box-shadow: 0 0 15px rgba(0, 212, 255, 0.5); | |
| } | |
| /* ========================= | |
| RESPONSIVE UNTUK ANDROID | |
| ========================= */ | |
| @media (max-width: 768px) { | |
| .sidebar { | |
| width: 85%; | |
| } | |
| .message { | |
| max-width: 90%; | |
| font-size: 15px; | |
| } | |
| .topbar { | |
| padding: 0 20px; | |
| } | |
| .input-area { | |
| padding: 15px 20px; | |
| } | |
| button.send { | |
| padding: 0 24px; | |
| } | |
| } | |
| /* TAMBAHAN KHUSUS UNTUK ANDROID */ | |
| @media (max-width: 480px) { | |
| .topbar { | |
| height: 65px; | |
| padding: 0 15px; | |
| } | |
| .menu-btn { | |
| font-size: 24px; | |
| margin-right: 12px; | |
| } | |
| .logo { | |
| width: 40px; | |
| height: 40px; | |
| } | |
| .title h1 { | |
| font-size: 18px; | |
| } | |
| .title p { | |
| font-size: 10px; | |
| } | |
| .chat-area { | |
| padding: 15px; | |
| } | |
| .message { | |
| padding: 10px 14px; | |
| font-size: 14px; | |
| max-width: 95%; | |
| } | |
| .input-area { | |
| padding: 10px 15px; | |
| gap: 10px; | |
| } | |
| input { | |
| padding: 12px 16px; | |
| font-size: 14px; | |
| } | |
| button.send { | |
| padding: 0 18px; | |
| font-size: 14px; | |
| } | |
| .sidebar { | |
| width: 80%; | |
| padding: 20px 15px; | |
| } | |
| .sidebar-logo { | |
| width: 45px; | |
| height: 45px; | |
| font-size: 24px; | |
| } | |
| .sidebar h2 { | |
| font-size: 22px; | |
| } | |
| .new-chat { | |
| padding: 12px; | |
| font-size: 14px; | |
| } | |
| .history-item { | |
| padding: 12px 15px; | |
| font-size: 13px; | |
| } | |
| } | |
| /* TAMBAHAN UNTUK LANDSCAPE MODE ANDROID */ | |
| @media (max-width: 900px) and (orientation: landscape) { | |
| .topbar { | |
| height: 55px; | |
| } | |
| .chat-area { | |
| padding: 10px 20px; | |
| } | |
| .message { | |
| margin-bottom: 10px; | |
| } | |
| .input-area { | |
| padding: 8px 15px; | |
| } | |
| input { | |
| padding: 10px 16px; | |
| } | |
| button.send { | |
| padding: 0 20px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="overlay" id="overlay" onclick="closeSidebar()"></div> | |
| <div class="app"> | |
| <!-- SIDEBAR --> | |
| <div class="sidebar" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <div class="sidebar-logo" id="sidebarLogo"> | |
| <!-- Logo akan diisi JS --> | |
| 🐱👤 | |
| </div> | |
| <div> | |
| <h2>ChibiCat</h2> | |
| <p>Neo Intelligence</p> | |
| </div> | |
| </div> | |
| <button class="new-chat" onclick="createNewChat()">+ New Chat</button> | |
| <div class="chat-history" id="chatHistory"></div> | |
| </div> | |
| <!-- MAIN --> | |
| <div class="main"> | |
| <!-- TOPBAR --> | |
| <div class="topbar"> | |
| <div class="menu-btn" onclick="openSidebar()">☰</div> | |
| <div class="logo" id="mainLogo"> | |
| <!-- Logo akan diisi JS --> | |
| 🐱👤 | |
| </div> | |
| <div class="title"> | |
| <h1>ChibiCat AI</h1> | |
| <p>Online | Neo Mode</p> | |
| </div> | |
| </div> | |
| <!-- CHAT AREA --> | |
| <div class="chat-area" id="chatArea"> | |
| <div class="message bot"> | |
| <span style="font-weight: bold;">⚡ ChibiCat Neo</span><br> | |
| Halo bro! Ada yang bisa gue bantu? 🔥😎 | |
| </div> | |
| </div> | |
| <!-- INPUT --> | |
| <div class="input-area"> | |
| <input type="text" id="messageInput" placeholder="Ketik pesan bro..." /> | |
| <button class="send" onclick="sendMessage()"> | |
| <span></span> Kirim | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentChatId = null | |
| // ========================================= | |
| // KONFIGURASI LOGO | |
| // ========================================= | |
| // Pilih salah satu mode logo: | |
| // mode: "emoji" = pake emoji keren | |
| // mode: "image" = pake gambar dari URL | |
| // mode: "svg" = pake karakter ASCII keren | |
| const LOGO_CONFIG = { | |
| mode: "image", // "emoji", "image", atau "svg" | |
| emoji: "🐱🚀", // emoji untuk mode emoji | |
| imageUrl: "/static/logo.png", // cyber cat image | |
| imageUrl2: "https://cdn-icons-png.flaticon.com/512/1998/1998592.png", // alternative robot cat | |
| svgIcon: "⚡🐱⚡" // untuk mode svg/ascii | |
| } | |
| function initLogos() { | |
| const sidebarLogo = document.getElementById("sidebarLogo") | |
| const mainLogo = document.getElementById("mainLogo") | |
| if (LOGO_CONFIG.mode === "emoji") { | |
| // Mode Emoji - simple tapi keren | |
| sidebarLogo.innerHTML = LOGO_CONFIG.emoji | |
| sidebarLogo.style.fontSize = "32px" | |
| sidebarLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)" | |
| mainLogo.innerHTML = LOGO_CONFIG.emoji | |
| mainLogo.style.fontSize = "28px" | |
| mainLogo.style.background = "linear-gradient(135deg, #00d4ff, #0099ff)" | |
| } else if (LOGO_CONFIG.mode === "image") { | |
| // Mode Gambar - lebih profesional | |
| 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}'">` | |
| sidebarLogo.style.background = "transparent" | |
| sidebarLogo.style.padding = "0" | |
| sidebarLogo.style.boxShadow = "0 0 15px rgba(0,212,255,0.4)" | |
| 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}'">` | |
| mainLogo.style.background = "transparent" | |
| mainLogo.style.padding = "0" | |
| } else { | |
| // Mode SVG/ASCII - modern tech look | |
| sidebarLogo.innerHTML = LOGO_CONFIG.svgIcon | |
| sidebarLogo.style.fontSize = "28px" | |
| sidebarLogo.style.fontWeight = "bold" | |
| sidebarLogo.style.letterSpacing = "2px" | |
| mainLogo.innerHTML = LOGO_CONFIG.svgIcon | |
| mainLogo.style.fontSize = "24px" | |
| mainLogo.style.fontWeight = "bold" | |
| } | |
| } | |
| function openSidebar() { | |
| document.getElementById("sidebar").classList.add("active") | |
| document.getElementById("overlay").classList.add("active") | |
| } | |
| function closeSidebar() { | |
| document.getElementById("sidebar").classList.remove("active") | |
| document.getElementById("overlay").classList.remove("active") | |
| } | |
| async function createNewChat() { | |
| const response = await fetch("/new_chat", { method: "POST" }) | |
| const data = await response.json() | |
| currentChatId = data.chat_id | |
| document.getElementById("chatArea").innerHTML = ` | |
| <div class="message bot"> | |
| <span style="font-weight: bold;">⚡ ChibiCat Neo</span><br> | |
| Halo bro! Ada yang bisa gue bantu? 🔥😎 | |
| </div> | |
| ` | |
| loadChats() | |
| closeSidebar() | |
| } | |
| async function loadChats() { | |
| const response = await fetch("/get_chats") | |
| const chats = await response.json() | |
| const history = document.getElementById("chatHistory") | |
| history.innerHTML = "" | |
| chats.forEach(chat => { | |
| history.innerHTML += ` | |
| <div class="history-item" onclick="loadChat('${chat.chat_id}')"> | |
| 💬 ${chat.title} | |
| </div> | |
| ` | |
| }) | |
| } | |
| async function loadChat(chatId) { | |
| currentChatId = chatId | |
| const response = await fetch(`/load_chat/${chatId}`) | |
| const messages = await response.json() | |
| const chatArea = document.getElementById("chatArea") | |
| chatArea.innerHTML = "" | |
| messages.forEach(msg => { | |
| const role = msg.role === "user" ? "user" : "bot" | |
| chatArea.innerHTML += `<div class="message ${role}">${msg.content}</div>` | |
| }) | |
| closeSidebar() | |
| } | |
| async function sendMessage() { | |
| const input = document.getElementById("messageInput") | |
| const message = input.value.trim() | |
| if (message === "") return | |
| const chatArea = document.getElementById("chatArea") | |
| chatArea.innerHTML += `<div class="message user">${escapeHtml(message)}</div>` | |
| input.value = "" | |
| chatArea.scrollTop = chatArea.scrollHeight | |
| try { | |
| const response = await fetch("/chat", { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ message: message }) | |
| }) | |
| const data = await response.json() | |
| chatArea.innerHTML += `<div class="message bot">${escapeHtml(data.reply)}</div>` | |
| chatArea.scrollTop = chatArea.scrollHeight | |
| loadChats() | |
| } catch (error) { | |
| chatArea.innerHTML += `<div class="message bot">⚠️ Server error, coba lagi bro 😭</div>` | |
| } | |
| } | |
| // TAMBAHAN: Fungsi untuk menghindari XSS dan support emoji di Android | |
| function escapeHtml(text) { | |
| const div = document.createElement('div') | |
| div.textContent = text | |
| return div.innerHTML | |
| } | |
| // TAMBAHAN: Fix untuk keyboard Android | |
| function fixAndroidKeyboard() { | |
| const input = document.getElementById("messageInput") | |
| if (input) { | |
| input.addEventListener("focus", function() { | |
| setTimeout(() => { | |
| document.querySelector('.chat-area').scrollTop = document.querySelector('.chat-area').scrollHeight | |
| }, 300) | |
| }) | |
| } | |
| } | |
| // TAMBAHAN: Deteksi Android dan tambah kelas khusus | |
| function detectAndroid() { | |
| const userAgent = navigator.userAgent.toLowerCase() | |
| const isAndroid = userAgent.indexOf("android") > -1 | |
| if (isAndroid) { | |
| document.body.classList.add("android-device") | |
| console.log("✅ Android device detected - optimized layout") | |
| } | |
| } | |
| document.getElementById("messageInput").addEventListener("keypress", function(e) { | |
| if (e.key === "Enter") sendMessage() | |
| }) | |
| // TAMBAHAN: Swipe gesture untuk Android (geser kanan buka sidebar) | |
| let touchStartX = 0 | |
| let touchEndX = 0 | |
| document.addEventListener('touchstart', function(e) { | |
| touchStartX = e.changedTouches[0].screenX | |
| }, false) | |
| document.addEventListener('touchend', function(e) { | |
| touchEndX = e.changedTouches[0].screenX | |
| const swipeDistance = touchEndX - touchStartX | |
| // Geser kanan lebih dari 50px untuk buka sidebar | |
| if (swipeDistance > 50 && touchStartX < 50) { | |
| openSidebar() | |
| } | |
| // Geser kiri lebih dari 50px untuk tutup sidebar | |
| if (swipeDistance < -50) { | |
| closeSidebar() | |
| } | |
| }, false) | |
| // Inisialisasi logo saat halaman load | |
| initLogos() | |
| loadChats() | |
| detectAndroid() | |
| fixAndroidKeyboard() | |
| </script> | |
| </body> | |
| </html> |