const API_BASE = ""; let sessions = []; let activeSessionId = null; let isLoading = false; let sidebarCollapsed = localStorage.getItem("sidebarCollapsed") === "true"; let textarea, sendBtn, messagesList; // Wait for DOM to be ready function initializeApp() { console.log("=== App Initialization ==="); textarea = document.getElementById("query-input"); sendBtn = document.getElementById("send-btn"); messagesList = document.getElementById("messages-list"); console.log("Elements found - textarea:", !!textarea, "sendBtn:", !!sendBtn, "messagesList:", !!messagesList); if (!textarea || !sendBtn || !messagesList) { console.error("CRITICAL: Required elements not found in DOM!"); console.error("textarea:", textarea); console.error("sendBtn:", sendBtn); console.error("messagesList:", messagesList); return; } if (sidebarCollapsed) { document.getElementById("sidebar").classList.add("collapsed"); } textarea.addEventListener("input", () => { textarea.style.height = "auto"; textarea.style.height = Math.min(textarea.scrollHeight, 120) + "px"; }); textarea.addEventListener("keydown", e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); submitQuery(); } }); console.log("App initialization complete"); } // Initialize when DOM is ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initializeApp); } else { // DOM already loaded (script might be async or deferred) initializeApp(); } function showScreen(name) { console.log("showScreen called with name:", name); document.querySelectorAll(".screen").forEach(s => { s.classList.remove("active"); s.classList.add("hidden"); }); const screenEl = document.getElementById("screen-" + name); if (!screenEl) { console.error("Screen element not found:", "screen-" + name); return; } screenEl.classList.remove("hidden"); screenEl.classList.add("active"); console.log("Screen switched to:", name); } function toggleSidebar() { const sidebar = document.getElementById("sidebar"); sidebarCollapsed = !sidebarCollapsed; sidebar.classList.toggle("collapsed"); localStorage.setItem("sidebarCollapsed", sidebarCollapsed); } function createSession(firstQuery) { const id = Date.now(); const title = firstQuery.length > 40 ? firstQuery.substring(0, 40) + "…" : firstQuery; const session = { id, title, messages: [] }; sessions.unshift(session); activeSessionId = id; renderSessionsList(); return session; } function getActiveSession() { return sessions.find(s => s.id === activeSessionId); } function switchSession(id) { activeSessionId = id; renderSessionsList(); renderMessages(); showScreen("chat"); const session = getActiveSession(); document.getElementById("topbar-title").textContent = session.title; } function newChat() { activeSessionId = null; messagesList.innerHTML = ""; showScreen("welcome"); document.getElementById("topbar-title").textContent = "Research Chamber"; renderSessionsList(); textarea.focus(); } function renderSessionsList() { const list = document.getElementById("sessions-list"); if (sessions.length === 0) { list.innerHTML = '
No active cases
'; return; } list.innerHTML = sessions.map(s => `

${escHtml(s.title)}

Click to open

`).join(""); } function renderMessages() { const session = getActiveSession(); if (!session) return; messagesList.innerHTML = ""; session.messages.forEach(msg => { if (msg.role === "user") appendUserBubble(msg.text, false); else if (msg.role === "ai") appendAIBubble(msg.data, false); else if (msg.role === "error") appendErrorBubble(msg.text, false); }); scrollBottom(); } async function submitQuery() { const query = textarea.value.trim(); if (!query || isLoading) return; if (query.length < 10) { alert("Query too short (minimum 10 characters)"); return; } if (query.length > 1000) { alert("Query too long"); return; } setLoading(true); console.log("=== Submitting Query ==="); console.log("Query:", query); // Create session if needed and switch to chat view if (!activeSessionId) { createSession(query); console.log("Created new session:", activeSessionId); } // Ensure chat screen is visible showScreen("chat"); console.log("Switched to chat screen"); const session = getActiveSession(); // Add user message to session and display session.messages.push({ role: "user", text: query }); appendUserBubble(query); console.log("Added user message to session"); // Clear input textarea.value = ""; textarea.style.height = "auto"; // Show loading state const loaderId = appendLoader(); console.log("Showing loader:", loaderId); try { const sessionId = activeSessionId ? String(activeSessionId) : "default"; console.log("Posting to /query with session_id:", sessionId); const res = await fetch(`${API_BASE}/query`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, session_id: sessionId }) }); console.log("Response status:", res.status); console.log("Response ok:", res.ok); const data = await res.json(); console.log("Response data:", data); removeLoader(loaderId); console.log("Removed loader"); if (!res.ok) { const msg = data.detail || data.error || JSON.stringify(data) || "Error: please try again"; console.error("API Error:", msg); session.messages.push({ role: "error", text: msg }); appendErrorBubble(msg); } else { console.log("API Success - Adding AI message"); session.messages.push({ role: "ai", data }); appendAIBubble(data); console.log("AI message added"); } } catch (err) { removeLoader(loaderId); console.error("Fetch error:", err); const msg = `Connection error: ${err.message}`; session.messages.push({ role: "error", text: msg }); appendErrorBubble(msg); } setLoading(false); scrollBottom(); console.log("=== Query Complete ==="); } function usesuggestion(queryText) { textarea.value = queryText; textarea.style.height = "auto"; textarea.style.height = Math.min(textarea.scrollHeight, 120) + "px"; submitQuery(); } function appendUserBubble(text, scroll = true) { console.log("appendUserBubble called with text:", text); const div = document.createElement("div"); div.className = "flex items-start gap-4 ml-12 flex-row-reverse"; div.innerHTML = `
person

You

${escHtml(text)}

`; messagesList.appendChild(div); console.log("User message appended to DOM"); if (scroll) scrollBottom(); } function appendAIBubble(data, scroll = true) { console.log("appendAIBubble called with data:", data); // Handle different response structures const answer = data.answer || data.response || data.text || JSON.stringify(data); const verified = data.verification_status === true || data.verification_status === "verified"; const sources = data.sources || data.source_judgments || []; const sourceCount = sources.length; const sourcesBtn = sourceCount > 0 ? ` ` : ""; const div = document.createElement("div"); div.className = "flex items-start gap-4 mr-12"; div.innerHTML = `
smart_toy

Registry Assistant

${formatAnswer(answer)}
${sourcesBtn}
`; messagesList.appendChild(div); // Attach click handler to citations button if it exists if (sourceCount > 0) { div.querySelector('button').onclick = () => showCitationsModal(sources); } console.log("AI message appended to DOM"); if (scroll) scrollBottom(); } function appendErrorBubble(text, scroll = true) { console.error("appendErrorBubble called with text:", text); const div = document.createElement("div"); div.className = "flex items-start gap-4 mr-12"; div.innerHTML = `
error

Error

${escHtml(text)}

`; messagesList.appendChild(div); console.log("Error message appended to DOM"); if (scroll) scrollBottom(); } function appendLoader() { const id = "loader-" + Date.now(); const div = document.createElement("div"); div.id = id; div.className = "flex items-start gap-4 mr-12"; div.innerHTML = `
smart_toy

Searching legal archives...

`; if (!messagesList) { console.error("messagesList element not found!"); return id; } messagesList.appendChild(div); console.log("Loader appended with id:", id, "messagesList children:", messagesList.children.length); scrollBottom(); return id; } function removeLoader(id) { const el = document.getElementById(id); if (el) el.remove(); } function setLoading(loading) { isLoading = loading; sendBtn.disabled = loading; const statusText = document.getElementById("status-text"); if (loading) { statusText.textContent = "SEARCHING..."; } else { statusText.textContent = "READY"; } } function scrollBottom() { setTimeout(() => { // Find the scrollable content container (the flex container with overflow-y-auto) const container = document.querySelector("main > div:nth-child(2)"); if (container) { container.scrollTop = container.scrollHeight; } }, 100); // Small delay to ensure DOM has updated } function formatAnswer(text) { if (!text) { console.warn("formatAnswer received empty text"); return "No response received"; } if (typeof text !== "string") { console.log("formatAnswer received non-string:", typeof text); return "Invalid response format"; } return text.replace(/\n/g, "
"); } function escHtml(text) { const map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }; return text.replace(/[&<>"']/g, m => map[m]); } function showAnalytics() { showScreen("analytics"); document.getElementById("topbar-title").textContent = "System Analytics"; loadAnalytics(); } function loadAnalytics() { fetch(`${API_BASE}/analytics`) .then(r => r.json()) .then(data => { document.getElementById("stat-total").textContent = data.total_queries || "1,284"; document.getElementById("stat-verified").textContent = (data.verified_rate || 99.2).toFixed(1) + "%"; document.getElementById("stat-latency").textContent = (data.avg_latency_ms || 0.8).toFixed(1) + "s"; }) .catch(err => console.error("Analytics load failed:", err)); } function showCitationsModal(sources) { console.log("showCitationsModal called with sources:", sources); if (!sources || sources.length === 0) { alert("No citations available"); return; } // Build citations list let citationsHTML = ""; sources.forEach((src, idx) => { const title = src.title || src.name || "Unknown Case"; const year = src.year || src.date || ""; const citation = src.citation || src.ref || ""; const court = src.court || ""; citationsHTML += `

${escHtml(title)}

${escHtml(citation)} ${year ? `(${year})` : ""}

${court ? `

${escHtml(court)}

` : ""}
`; }); // Create modal const modal = document.createElement("div"); modal.className = "fixed inset-0 bg-black/30 backdrop-blur-sm flex items-center justify-center z-50"; modal.onclick = () => modal.remove(); modal.innerHTML = `

description Citations

${citationsHTML}
`; document.body.appendChild(modal); }