// Chat UI Module - Threat-aware chat with GPT (function () { const { state } = APP.core; const { $ } = APP.core.utils; const { log } = APP.ui.logging; let chatHistory = []; /** * Initialize the chat module. */ function init() { const chatInput = $("#chatInput"); const chatSend = $("#chatSend"); if (chatSend) { chatSend.addEventListener("click", sendMessage); } if (chatInput) { chatInput.addEventListener("keydown", (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); } // Toggle chat panel const chatToggle = $("#chatToggle"); const chatPanel = $("#chatPanel"); if (chatToggle && chatPanel) { chatToggle.addEventListener("click", () => { chatPanel.classList.toggle("collapsed"); chatToggle.textContent = chatPanel.classList.contains("collapsed") ? "▼ Chat" : "▲ Close Chat"; }); } } /** * Send a chat message about current threats. */ async function sendMessage() { const chatInput = $("#chatInput"); const chatMessages = $("#chatMessages"); if (!chatInput || !chatMessages) return; const question = chatInput.value.trim(); if (!question) { log("Please enter a question.", "w"); return; } // Check if we have detections if (!state.detections || state.detections.length === 0) { appendMessage("system", "No detections yet. Run Detect first to analyze the scene."); return; } // Add user message to UI appendMessage("user", question); chatInput.value = ""; chatInput.disabled = true; // Show loading indicator const loadingId = appendMessage("assistant", "Analyzing scene...", true); try { const response = await APP.api.client.chatAboutThreats(question, state.detections); // Remove loading message removeMessage(loadingId); if (response.response) { appendMessage("assistant", response.response); chatHistory.push({ role: "user", content: question }); chatHistory.push({ role: "assistant", content: response.response }); } else if (response.error || response.detail) { appendMessage("system", `Error: ${response.error || response.detail}`); } } catch (err) { removeMessage(loadingId); appendMessage("system", `Failed to get response: ${err.message}`); log(`Chat error: ${err.message}`, "e"); } finally { chatInput.disabled = false; chatInput.focus(); } } /** * Append a message to the chat display. */ function appendMessage(role, content, isLoading = false) { const chatMessages = $("#chatMessages"); if (!chatMessages) return null; const msgId = `msg-${Date.now()}`; const msgDiv = document.createElement("div"); msgDiv.className = `chat-message chat-${role}`; msgDiv.id = msgId; if (isLoading) { msgDiv.classList.add("loading"); } // Format content with line breaks const formatted = content.replace(/\n/g, "
"); const icon = role === "user" ? "YOU" : role === "assistant" ? "TAC" : "SYS"; msgDiv.innerHTML = `${icon}${formatted}`; chatMessages.appendChild(msgDiv); chatMessages.scrollTop = chatMessages.scrollHeight; return msgId; } /** * Remove a message by ID. */ function removeMessage(msgId) { const msg = document.getElementById(msgId); if (msg) msg.remove(); } /** * Clear chat history. */ function clearChat() { const chatMessages = $("#chatMessages"); if (chatMessages) { chatMessages.innerHTML = ""; } chatHistory = []; } // Export APP.ui = APP.ui || {}; APP.ui.chat = { init, sendMessage, clearChat }; // Auto-init on DOMContentLoaded if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })();