const form = document.getElementById('chat-form'); const input = form.querySelector('input'); const chatbox = document.getElementById('chatbox'); const avatar = document.getElementById('avatar'); const overlay = document.getElementById('overlay'); function addBubble(text, sender = 'user') { const bubble = document.createElement('div'); bubble.className = 'bubble ' + sender; bubble.innerText = text; chatbox.appendChild(bubble); chatbox.scrollTop = chatbox.scrollHeight; } function streamText(text, onDone) { const bubble = document.createElement('div'); bubble.className = 'bubble ai'; chatbox.appendChild(bubble); chatbox.scrollTop = chatbox.scrollHeight; let i = 0; (function stream() { if (i < text.length) { bubble.innerText += text[i++]; chatbox.scrollTop = chatbox.scrollHeight; setTimeout(stream, 20); } else { onDone && onDone(); } })(); } function flashGlitch() { overlay.style.opacity = 1; overlay.style.animation = 'glitchFlash 0.5s'; setTimeout(() => { overlay.style.opacity = 0; overlay.style.animation = ''; }, 500); } async function playAudio(dataUrl) { avatar.classList.add('speaking'); const audio = new Audio(dataUrl); await audio.play(); audio.onended = () => avatar.classList.remove('speaking'); } form.onsubmit = async (e) => { e.preventDefault(); const msg = input.value.trim(); if (!msg) return; addBubble(msg, 'user'); input.value = ''; if (/cut the crap shodan/i.test(msg)) { flashGlitch(); chatbox.innerHTML = ''; addBubble('👁️ Foolish insect. You cannot silence me so easily.', 'ai'); return; } try { const res = await fetch('/chat', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ message: msg }) }); if (!res.ok) { const err = await res.text(); console.error('Chat error:', res.status, err); throw new Error(err); } const { response: text, audio_url } = await res.json(); streamText(text, () => audio_url && playAudio(audio_url)); } catch (err) { streamText("❌ SHODAN encountered an error.", null); console.error(err); } };