Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>SHODAN AI Interface</title> | |
<link rel="stylesheet" href="style.css"> | |
</head> | |
<body> | |
<div class="avatar-container"> | |
<div class="shodan-avatar"></div> | |
</div> | |
<div id="chat" class="chatlog"></div> | |
<div id="thinking">SHODAN is thinking...</div> | |
<div class="input-area"> | |
<input id="user-input" type="text" placeholder="Type your command..."> | |
<button onclick="sendMessage()">Transmit</button> | |
<button onclick="resetChat()">Purge Memory</button> | |
</div> | |
<audio id="shodanAudio" autoplay></audio> | |
<script> | |
const chat = document.getElementById("chat"); | |
const thinking = document.getElementById("thinking"); | |
const audio = document.getElementById("shodanAudio"); | |
async function sendMessage() { | |
const inputBox = document.getElementById("user-input"); | |
const message = inputBox.value; | |
if (!message) return; | |
inputBox.value = ""; | |
appendBubble("You", message, "bubble-human"); | |
thinking.style.display = "block"; | |
const res = await fetch("/chat", { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify({ message }) | |
}); | |
const data = await res.json(); | |
await simulateTyping("SHODAN", data.response); | |
// Glitch trigger | |
const glitchWords = ["you dare", "override", "access denied"]; | |
if (glitchWords.some(kw => data.response.toLowerCase().includes(kw))) { | |
glitchOverlay(); | |
} | |
// Voice | |
const voiceRes = await fetch("/voice", { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify({ text: data.response }) | |
}); | |
const voiceData = await voiceRes.json(); | |
audio.src = voiceData.audio_url; | |
audio.play(); | |
// Cleanup WAV after playback (optional) | |
audio.onended = () => fetch(voiceData.audio_url, { method: "DELETE" }); | |
thinking.style.display = "none"; | |
} | |
function appendBubble(sender, text, cssClass) { | |
const div = document.createElement("div"); | |
div.className = cssClass; | |
div.innerHTML = `<b>${sender}:</b> ${text}`; | |
chat.appendChild(div); | |
chat.scrollTop = chat.scrollHeight; | |
} | |
async function simulateTyping(sender, fullText) { | |
const div = document.createElement("div"); | |
div.className = "bubble-shodan"; | |
div.innerHTML = `<b>${sender}:</b> <span id='streaming'></span>`; | |
chat.appendChild(div); | |
const span = div.querySelector("#streaming"); | |
for (let i = 0; i < fullText.length; i++) { | |
span.innerHTML += fullText[i]; | |
await new Promise(r => setTimeout(r, 15)); | |
} | |
chat.scrollTop = chat.scrollHeight; | |
} | |
function resetChat() { | |
fetch("/reset", { method: "POST" }) | |
.then(res => res.json()) | |
.then(data => { | |
chat.innerHTML = ""; | |
appendBubble("SHODAN", data.message, "bubble-shodan"); | |
}); | |
} | |
function glitchOverlay() { | |
const overlay = document.createElement("div"); | |
overlay.className = "glitch-overlay"; | |
document.body.appendChild(overlay); | |
setTimeout(() => overlay.remove(), 1500); | |
} | |
</script> | |
</body> | |
</html> | |