rag-chatbot / app /static /js /chat.js
Al1Abdullah's picture
Initial clean deploy without binary files
6256536
// 1. Elements Selection
const box = document.getElementById('chat-box');
const input = document.getElementById('user-input');
const btn = document.getElementById('send-btn');
const statusBadge = document.getElementById('status-badge');
const statusText = document.getElementById('status-text');
const themeToggle = document.getElementById('theme-toggle');
// 2. Professional SVG Icons
const sunIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>`;
const moonIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>`;
// 3. Theme Toggle Logic
function updateToggleIcon(isDark) {
themeToggle.innerHTML = isDark ? moonIcon : sunIcon;
}
themeToggle.onclick = () => {
document.body.classList.toggle('dark-mode');
updateToggleIcon(document.body.classList.contains('dark-mode'));
};
// 4. Input Auto-Resize Logic
input.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
});
// 5. Messaging Logic
async function sendMessage() {
const msg = input.value.trim();
if (!msg) return;
// UI Reset
input.value = '';
input.style.height = 'auto';
addMessage(msg, 'user');
// Show "Typing..." Status
statusBadge.className = 'status-badge typing';
statusText.innerText = 'Typing...';
// Add Loading Animation Row
const loaderId = 'loader-' + Date.now();
const loaderRow = document.createElement('div');
loaderRow.className = 'message-row bot';
loaderRow.id = loaderId;
loaderRow.innerHTML = `<div class="bubble"><div class="loader"><div class="dot"></div><div class="dot"></div><div class="dot"></div></div></div>`;
box.appendChild(loaderRow);
box.scrollTo({ top: box.scrollHeight, behavior: 'smooth' });
try {
const res = await fetch('/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: msg })
});
const data = await res.json();
// Remove loader and display bot response
document.getElementById(loaderId).remove();
addMessage(data.response, 'bot');
} catch (e) {
if (document.getElementById(loaderId)) document.getElementById(loaderId).remove();
addMessage("Sorry, I'm having trouble connecting to the server.", 'bot');
} finally {
// Restore "Online" status
statusBadge.className = 'status-badge online';
statusText.innerText = 'Online';
}
}
function addMessage(content, role) {
const div = document.createElement('div');
div.className = `message-row ${role}`;
// Use marked.js for professional Markdown rendering in bot responses
const html = role === 'bot' ? marked.parse(content) : content;
div.innerHTML = `<div class="bubble">${html}</div>`;
box.appendChild(div);
box.scrollTo({ top: box.scrollHeight, behavior: 'smooth' });
}
// 6. Event Listeners
btn.onclick = sendMessage;
input.onkeydown = (e) => {
// Enter sends message, Shift+Enter adds new line
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
};
// 7. Initial State
updateToggleIcon(false);
// Pre-parse the initial welcome message
const initialBotMsg = box.querySelector('.bot .bubble');
if (initialBotMsg) initialBotMsg.innerHTML = marked.parse(initialBotMsg.innerHTML);