/* =================================================== JAESWIFT.XYZ — JAE-AI Chat Terminal Venice API chat interface =================================================== */ (function () { 'use strict'; const chatMessages = document.getElementById('chatMessages'); const chatInput = document.getElementById('chatInput'); const chatSend = document.getElementById('chatSend'); const chatStatus = document.getElementById('chatStatus'); if (!chatMessages || !chatInput || !chatSend) return; let history = []; let isWaiting = false; // ─── Render a message bubble ─── function addMessage(role, text) { // Remove welcome screen on first message const welcome = chatMessages.querySelector('.chat-welcome'); if (welcome) welcome.remove(); const msg = document.createElement('div'); msg.className = `chat-msg chat-msg-${role}`; const label = document.createElement('span'); label.className = 'chat-msg-label'; label.textContent = role === 'user' ? 'YOU' : 'JAE-AI'; const body = document.createElement('div'); body.className = 'chat-msg-body'; body.textContent = text; msg.appendChild(label); msg.appendChild(body); chatMessages.appendChild(msg); chatMessages.scrollTop = chatMessages.scrollHeight; return body; } // ─── Typing indicator ─── function showTyping() { const indicator = document.createElement('div'); indicator.className = 'chat-msg chat-msg-assistant chat-typing-indicator'; indicator.id = 'chatTyping'; indicator.innerHTML = ` JAE-AI
`; chatMessages.appendChild(indicator); chatMessages.scrollTop = chatMessages.scrollHeight; } function hideTyping() { const el = document.getElementById('chatTyping'); if (el) el.remove(); } // ─── Typewriter effect for AI responses ─── function typewriterEffect(element, text, speed) { speed = speed || 12; let i = 0; element.textContent = ''; return new Promise(function (resolve) { function tick() { if (i < text.length) { element.textContent += text.charAt(i); i++; chatMessages.scrollTop = chatMessages.scrollHeight; setTimeout(tick, speed); } else { resolve(); } } tick(); }); } // ─── Send message to API ─── async function sendMessage() { const text = chatInput.value.trim(); if (!text || isWaiting) return; isWaiting = true; chatInput.value = ''; chatStatus.textContent = '● PROCESSING'; chatStatus.classList.remove('status-green'); chatStatus.classList.add('status-amber'); addMessage('user', text); history.push({ role: 'user', content: text }); showTyping(); try { const resp = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: text, history: history.slice(0, -1) }) }); hideTyping(); if (!resp.ok) { const err = await resp.json().catch(function () { return {}; }); addMessage('assistant', 'ERROR: ' + (err.error || 'Connection failed')); chatStatus.textContent = '● ERROR'; chatStatus.classList.remove('status-amber'); chatStatus.classList.add('status-red'); isWaiting = false; return; } const data = await resp.json(); const reply = data.reply || 'No response received.'; history.push({ role: 'assistant', content: reply }); // Keep history manageable if (history.length > 40) { history = history.slice(-30); } const bodyEl = addMessage('assistant', ''); await typewriterEffect(bodyEl, reply, 10); } catch (e) { hideTyping(); addMessage('assistant', 'ERROR: Network failure — ' + e.message); chatStatus.textContent = '● OFFLINE'; chatStatus.classList.remove('status-amber'); chatStatus.classList.add('status-red'); } chatStatus.textContent = '● ONLINE'; chatStatus.classList.remove('status-amber', 'status-red'); chatStatus.classList.add('status-green'); isWaiting = false; chatInput.focus(); } // ─── Event listeners ─── chatSend.addEventListener('click', sendMessage); chatInput.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); // ─── Auto-greeting after short delay ─── setTimeout(function () { showTyping(); setTimeout(function () { hideTyping(); var greeting = 'Welcome to JAESWIFT.XYZ — I\'m JAE-AI, your onboard guide. Ask me anything about this system, or try saying "what can I explore here?"'; history.push({ role: 'assistant', content: greeting }); var bodyEl = addMessage('assistant', ''); typewriterEffect(bodyEl, greeting, 15); }, 1500); }, 2000); })();