Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ZNX Coder v1 - Dashboard</title> | |
| <style> | |
| body { | |
| font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif; | |
| background: #0f172a; | |
| color: #e2e8f0; | |
| margin: 0; | |
| padding: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| height: 100vh; | |
| box-sizing: border-box; | |
| } | |
| .config-box { | |
| background: #1e293b; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin-bottom: 15px; | |
| display: flex; | |
| gap: 10px; | |
| align-items: center; | |
| border: 1px solid #334155; | |
| } | |
| input, button { | |
| padding: 12px; | |
| border-radius: 6px; | |
| border: 1px solid #475569; | |
| background: #334155; | |
| color: white; | |
| font-size: 14px; | |
| } | |
| input { flex: 1; } | |
| button { background: #10b981; cursor: pointer; border: none; font-weight: bold; transition: 0.2s; } | |
| button:hover { background: #059669; } | |
| .chat-container { | |
| flex: 1; | |
| background: #1e293b; | |
| border-radius: 8px; | |
| padding: 20px; | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| border: 1px solid #334155; | |
| } | |
| .msg { | |
| padding: 12px 16px; | |
| border-radius: 8px; | |
| max-width: 80%; | |
| line-height: 1.6; | |
| word-wrap: break-word; | |
| white-space: pre-wrap; | |
| font-size: 15px; | |
| } | |
| .user { background: #2563eb; align-self: flex-end; color: white; } | |
| .bot { background: #1e293b; align-self: flex-start; border-left: 4px solid #10b981; color: #f1f5f9; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } | |
| .input-box { display: flex; gap: 10px; margin-top: 15px; } | |
| .input-box input { background: #1e293b; } | |
| #status { font-weight: bold; margin-left: auto; padding: 4px 8px; border-radius: 4px; font-size: 12px; } | |
| .status-disconnected { background: #7f1d1d; color: #f87171; } | |
| .status-connected { background: #064e3b; color: #34d399; } | |
| </style> | |
| </head> | |
| <body> | |
| <h2 style="margin: 0 0 15px 0; color: #10b981; display: flex; align-items: center; gap: 10px;"> | |
| ⚡ ZNX Coder v1 <span style="font-size: 12px; background: #334155; color: #94a3b8; padding: 2px 8px; border-radius: 20px;">v1.0.1</span> | |
| </h2> | |
| <div class="config-box"> | |
| <label style="font-weight: 500;">Ngrok Endpoint:</label> | |
| <input type="text" id="endpoint" value="https://massive-imaging-junkie.ngrok-free.dev" placeholder="Masukkan URL Ngrok lengkap"> | |
| <span id="status" class="status-disconnected">DISCONNECTED</span> | |
| </div> | |
| <div class="chat-container" id="chat"> | |
| <div class="msg bot"><strong>System:</strong> Masukkan link Ngrok lu, klik kirim buat tes model <strong>znx-coder-v1</strong>.</div> | |
| </div> | |
| <div class="input-box"> | |
| <input type="text" id="userInput" placeholder="Tulis perintah koding atau pertanyaan disini..." onkeypress="if(event.key === 'Enter') sendRequest()"> | |
| <button onclick="sendRequest()">Kirim</button> | |
| </div> | |
| <script> | |
| async function sendRequest() { | |
| const endpointInput = document.getElementById('endpoint').value.trim(); | |
| const userInput = document.getElementById('userInput'); | |
| const chatContainer = document.getElementById('chat'); | |
| const statusSpan = document.getElementById('status'); | |
| if (!endpointInput) { alert('Isi URL Ngrok dulu jing!'); return; } | |
| if (!userInput.value.trim()) return; | |
| const text = userInput.value; | |
| userInput.value = ''; | |
| chatContainer.innerHTML += `<div class="msg user">${text}</div>`; | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| const botMessageId = 'bot-' + Date.now(); | |
| chatContainer.innerHTML += `<div class="msg bot" id="${botMessageId}"><em>ZNX sedang memproses instruksi...</em></div>`; | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| const botMessageDiv = document.getElementById(botMessageId); | |
| // Bersihkan format URL | |
| let baseUrl = endpointInput.replace(/\/$/, ""); | |
| try { | |
| statusSpan.innerText = "CONNECTING..."; | |
| statusSpan.style.background = "#78350f"; | |
| statusSpan.style.color = "#fbbf24"; | |
| const response = await fetch(`${baseUrl}/v1/chat/completions`, { | |
| method: "POST", | |
| headers: { | |
| "Content-Type": "application/json", | |
| "Authorization": "Bearer sk-zenith", | |
| "ngrok-skip-browser-warning": "true" // Lewati halaman warning bawaan Ngrok gratisan | |
| }, | |
| body: JSON.stringify({ | |
| messages: [{ role: "user", content: text }], | |
| temperature: 0.5, | |
| stream: false | |
| }) | |
| }); | |
| if (!response.ok) throw new Error(`HTTP Error! Status: ${response.status}`); | |
| const data = await response.json(); | |
| const reply = data.choices[0].message.content; // FIX: Perbaikan array selector OpenAI format | |
| botMessageDiv.innerHTML = `<strong>ZNX Coder:</strong><br>${reply}`; | |
| statusSpan.innerText = "CONNECTED"; | |
| statusSpan.className = "status-connected"; | |
| statusSpan.style.background = "#064e3b"; | |
| statusSpan.style.color = "#34d399"; | |
| } catch (err) { | |
| botMessageDiv.innerHTML = `<strong>Error:</strong> Gagal terhubung ke API backend.<br><small style="color: #f87171;">Periksa link atau pastikan server log Colab tidak crash.</small>`; | |
| statusSpan.innerText = "ERROR"; | |
| statusSpan.className = "status-disconnected"; | |
| statusSpan.style.background = "#7f1d1d"; | |
| statusSpan.style.color = "#f87171"; | |
| console.error(err); | |
| } | |
| chatContainer.scrollTop = chatContainer.scrollHeight; | |
| } | |
| </script> | |
| </body> | |
| </html> | |