// --- State --- let sessionId = null; let models = []; let currentModel = null; const REQUEST_LIMIT = 3; const REQUEST_WINDOW_MS = 30 * 1000; // 1 minute let requestTimestamps = []; // --- DOM Elements --- const modal = document.getElementById('modal'); const apiKeyInput = document.getElementById('apiKeyInput'); const loginBtn = document.getElementById('loginBtn'); const loginError = document.getElementById('loginError'); const app = document.getElementById('app'); const chatContainer = document.getElementById('chatContainer'); const toolToggle = document.getElementById('toolToggle'); const inputForm = document.getElementById('inputForm'); const userInput = document.getElementById('userInput'); const modelSelect = document.getElementById('modelSelect'); const logoutBtn = document.getElementById('logoutBtn'); const typingIndicator = document.getElementById('typingIndicator'); let toolUse = true; // --- Helpers --- function canSendRequest() { const now = Date.now(); // Remove timestamps older than 1 minute requestTimestamps = requestTimestamps.filter(ts => now - ts < REQUEST_WINDOW_MS); return requestTimestamps.length < REQUEST_LIMIT; } function recordRequest() { requestTimestamps.push(Date.now()); } function scrollToBottom() { chatContainer.scrollTop = chatContainer.scrollHeight; } function addMessage(text, sender) { const msgDiv = document.createElement('div'); msgDiv.className = 'message ' + sender; const bubble = document.createElement('div'); bubble.className = 'bubble'; bubble.textContent = text; msgDiv.appendChild(bubble); chatContainer.appendChild(msgDiv); scrollToBottom(); } function setTyping(isTyping) { typingIndicator.classList.toggle('hidden', !isTyping); scrollToBottom(); } function showError(msg) { loginError.textContent = msg || ''; } toolToggle.addEventListener('change', function() { toolUse = toolToggle.checked; }); // --- Auth & Model Selection --- loginBtn.onclick = async function () { const apiKey = apiKeyInput.value.trim(); if (!apiKey) { showError('API key required.'); return; } loginBtn.disabled = true; showError(''); try { const res = await fetch('/init', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({api_key: apiKey}) }); const data = await res.json(); if (!data.success) { showError(data.error || 'Login failed.'); loginBtn.disabled = false; return; } sessionId = data.session_id; models = data.models || []; // Populate model dropdown modelSelect.innerHTML = ''; models.forEach(m => { const opt = document.createElement('option'); opt.value = m; opt.textContent = m; modelSelect.appendChild(opt); }); currentModel = models[0]; modelSelect.value = currentModel; // Show app, hide modal modal.classList.add('hidden'); app.classList.remove('hidden'); chatContainer.innerHTML = ''; userInput.focus(); } catch (e) { showError('Network error.'); loginBtn.disabled = false; } }; modelSelect.onchange = function () { currentModel = modelSelect.value; }; // --- Chat Logic --- inputForm.onsubmit = async function (e) { e.preventDefault(); const text = userInput.value.trim(); if (!text) return; if (!canSendRequest()) { addMessage('[Rate limit] Please wait: only 4 requests per minute allowed.', 'bot'); return; } recordRequest(); addMessage(text, 'user'); userInput.value = ''; setTyping(true); try { const res = await fetch('/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ session_id: sessionId, query: text, tool_use: toolUse, // <-- Use the toggle value here model: currentModel }) }); const data = await res.json(); setTyping(false); if (data.error) { addMessage('[Error] ' + data.error, 'bot'); return; } addMessage(data.output, 'bot'); } catch (e) { setTyping(false); addMessage('[Network error]', 'bot'); } }; // --- Logout Logic --- logoutBtn.onclick = async function () { await logoutAndReset(); }; async function logoutAndReset() { if (sessionId) { try { await fetch('/logout', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({session_id: sessionId}) }); } catch {} sessionId = null; models = []; currentModel = null; } app.classList.add('hidden'); modal.classList.remove('hidden'); apiKeyInput.value = ''; chatContainer.innerHTML = ''; showError(''); loginBtn.disabled = false; } // --- Auto logout on exit --- window.addEventListener('beforeunload', logoutAndReset); // --- UX: Enter key in modal input triggers login --- apiKeyInput.addEventListener('keyup', function(e) { if (e.key === 'Enter') loginBtn.click(); });