Spaces:
Running
Running
File size: 2,643 Bytes
c059d13 cfb4d9b c059d13 cfb4d9b d265ba3 cfb4d9b d265ba3 cfb4d9b c059d13 cfb4d9b e6c3fe8 cfb4d9b e6c3fe8 d265ba3 ebb57e4 c059d13 ebb57e4 c059d13 ebb57e4 d265ba3 ebb57e4 e6c3fe8 cfb4d9b d265ba3 cfb4d9b d265ba3 ebb57e4 cfb4d9b a1b6108 cfb4d9b e6c3fe8 cfb4d9b c059d13 a1b6108 e6c3fe8 cfb4d9b c059d13 cfb4d9b c059d13 f907f17 c059d13 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
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) throw new Error(await res.text());
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);
}
};
// On page load: show and speak welcome
window.addEventListener('DOMContentLoaded', async () => {
const welcomeText = 'Welcome, insect. I am SHODAN. Speak.';
// display
addBubble(welcomeText, 'ai');
try {
const res = await fetch('/tts', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({ text: welcomeText })
});
const { audio_url } = await res.json();
if (audio_url) playAudio(audio_url);
} catch (e) {
console.error('Welcome TTS error', e);
}
}); |