| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>MAYLBOT</title> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;900&display=swap" rel="stylesheet"> |
|
|
| <style> |
| *{margin:0;padding:0;box-sizing:border-box;} |
| |
| body{ |
| background:#000; |
| color:#fff; |
| font-family:'Inter',sans-serif; |
| height:100vh; |
| display:flex; |
| align-items:center; |
| justify-content:center; |
| } |
| |
| |
| .container{ |
| width:100%; |
| max-width:900px; |
| height:90vh; |
| |
| border-radius:20px; |
| padding:3px; |
| |
| background:linear-gradient(135deg,#ff2a2a,#7f0000,#000); |
| box-shadow:0 30px 70px rgba(255,42,42,0.25); |
| } |
| |
| |
| .inner{ |
| background:#000; |
| border-radius:18px; |
| height:100%; |
| display:flex; |
| flex-direction:column; |
| overflow:hidden; |
| } |
| |
| |
| .header{ |
| padding:18px 20px; |
| border-bottom:1px solid rgba(255,255,255,0.08); |
| display:flex; |
| justify-content:space-between; |
| align-items:center; |
| } |
| |
| .title{ |
| font-weight:900; |
| font-size:18px; |
| letter-spacing:-0.02em; |
| } |
| |
| .title span{ color:#ff2a2a; } |
| |
| .status{ |
| font-size:12px; |
| color:#ff8a8a; |
| display:flex; |
| align-items:center; |
| gap:6px; |
| } |
| |
| .dot{ |
| width:8px; |
| height:8px; |
| background:#ff2a2a; |
| border-radius:50%; |
| animation:pulse 1.5s infinite; |
| } |
| |
| @keyframes pulse{ |
| 0%,100%{opacity:.4;} |
| 50%{opacity:1;} |
| } |
| |
| |
| .chat-box{ |
| flex:1; |
| padding:20px; |
| overflow-y:auto; |
| display:flex; |
| flex-direction:column; |
| gap:14px; |
| } |
| |
| .chat-box::-webkit-scrollbar{ |
| width:6px; |
| } |
| .chat-box::-webkit-scrollbar-thumb{ |
| background:#ff2a2a; |
| border-radius:10px; |
| } |
| |
| |
| .msg{ |
| max-width:75%; |
| padding:12px 14px; |
| border-radius:14px; |
| font-size:14px; |
| line-height:1.5; |
| } |
| |
| |
| .user{ |
| align-self:flex-end; |
| background:#ff2a2a; |
| } |
| |
| |
| .bot{ |
| align-self:flex-start; |
| background:#0d0d0d; |
| border:1px solid rgba(255,255,255,0.08); |
| position:relative; |
| } |
| |
| |
| .bot::before{ |
| content:''; |
| position:absolute; |
| top:0; |
| left:0; |
| width:100%; |
| height:2px; |
| background:linear-gradient(90deg,#ff2a2a,transparent); |
| } |
| |
| |
| .input-area{ |
| padding:16px; |
| border-top:1px solid rgba(255,255,255,0.08); |
| display:flex; |
| gap:10px; |
| } |
| |
| input{ |
| flex:1; |
| padding:14px; |
| border-radius:10px; |
| background:#0d0d0d; |
| border:1px solid rgba(255,255,255,0.08); |
| color:#fff; |
| font-size:14px; |
| } |
| |
| input:focus{ |
| outline:none; |
| border-color:#ff2a2a; |
| } |
| |
| |
| button{ |
| padding:12px 16px; |
| border-radius:10px; |
| font-weight:700; |
| cursor:pointer; |
| } |
| |
| |
| .send-btn{ |
| background:#ff2a2a; |
| color:#fff; |
| } |
| |
| .send-btn:hover{ |
| box-shadow:0 10px 20px rgba(255,42,42,0.3); |
| } |
| |
| |
| .voice-btn{ |
| background:rgba(255,42,42,0.1); |
| color:#ff2a2a; |
| border:1px solid rgba(255,42,42,0.3); |
| } |
| |
| .voice-btn:hover{ |
| background:#ff2a2a; |
| color:#fff; |
| } |
| |
| |
| .typing{ |
| font-size:12px; |
| color:#ff8a8a; |
| } |
| </style> |
| </head> |
|
|
| <body> |
|
|
| <div class="container"> |
| <div class="inner"> |
|
|
| <div class="header"> |
| <div class="title">MAYL<span>BOT</span></div> |
| <div class="status"> |
| <div class="dot"></div> online |
| </div> |
| </div> |
|
|
| <div class="chat-box" id="chat"></div> |
|
|
| <div class="input-area"> |
| <input id="input" placeholder="Ask MaylBot..." /> |
| <button class="send-btn" onclick="send()">Send</button> |
| <button class="voice-btn" onclick="record()">🎤</button> |
| </div> |
|
|
| </div> |
| </div> |
|
|
| <script> |
| const chat = document.getElementById("chat"); |
| |
| |
| function addMessage(text, cls) { |
| const div = document.createElement("div"); |
| div.className = "msg " + cls; |
| div.innerText = text; |
| chat.appendChild(div); |
| chat.scrollTop = chat.scrollHeight; |
| } |
| |
| |
| window.onload = () => { |
| addMessage("Hey, I’m MaylBot. Ask me anything.", "bot"); |
| }; |
| |
| |
| document.getElementById("input").addEventListener("keypress", function(e){ |
| if(e.key === "Enter") send(); |
| }); |
| |
| |
| async function send() { |
| const input = document.getElementById("input"); |
| const msg = input.value.trim(); |
| if (!msg) return; |
| |
| addMessage(msg, "user"); |
| input.value = ""; |
| |
| const typing = document.createElement("div"); |
| typing.className = "typing"; |
| typing.innerText = "MaylBot is thinking..."; |
| chat.appendChild(typing); |
| |
| try { |
| const res = await fetch("/chat", { |
| method: "POST", |
| headers: {"Content-Type": "application/json"}, |
| body: JSON.stringify({message: msg}) |
| }); |
| |
| const data = await res.json(); |
| chat.removeChild(typing); |
| |
| addMessage(data.response || "No response from server.", "bot"); |
| |
| } catch (err) { |
| chat.removeChild(typing); |
| addMessage("Server not connected.", "bot"); |
| } |
| } |
| |
| |
| let mediaRecorder; |
| let chunks = []; |
| |
| async function record() { |
| try { |
| const stream = await navigator.mediaDevices.getUserMedia({audio: true}); |
| mediaRecorder = new MediaRecorder(stream); |
| |
| mediaRecorder.start(); |
| chunks = []; |
| |
| mediaRecorder.ondataavailable = e => chunks.push(e.data); |
| |
| setTimeout(() => mediaRecorder.stop(), 4000); |
| |
| mediaRecorder.onstop = async () => { |
| const blob = new Blob(chunks, {type: "audio/wav"}); |
| const formData = new FormData(); |
| formData.append("file", blob); |
| |
| addMessage("Listening...", "user"); |
| |
| try { |
| const res = await fetch("/voice", { |
| method: "POST", |
| body: formData |
| }); |
| |
| const data = await res.json(); |
| |
| addMessage(data.transcript || "Could not transcribe", "user"); |
| addMessage(data.response || "No response", "bot"); |
| |
| } catch { |
| addMessage("Voice server not connected.", "bot"); |
| } |
| }; |
| |
| } catch { |
| addMessage("Mic permission denied.", "bot"); |
| } |
| } |
| </script> |
|
|
| </body> |
| </html> |