/* ===================================================
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);
})();