// Live log streaming via Server-Sent Events + command-input wiring. // EventSource gives us instant push (no 1-second polling lag) and reconnects // automatically if the connection drops. "use strict"; import { api, apiJson, escapeHtml } from "./api.js"; import { state } from "./state.js"; const consoleEl = () => document.getElementById("console"); export function setupConsole() { consoleEl().textContent = "Connecting to server log…"; const es = new EventSource("/api/logs/stream"); let firstEvent = true; es.addEventListener("log", e => { if (firstEvent) { consoleEl().textContent = ""; firstEvent = false; } try { const d = JSON.parse(e.data); const ts = new Date(d.t).toLocaleTimeString(); const div = document.createElement("div"); if (d.e) div.className = "err"; div.textContent = `[${ts}] ${d.m}`; consoleEl().appendChild(div); consoleEl().scrollTop = consoleEl().scrollHeight; // Trim very old lines so the DOM doesn't grow unbounded while (consoleEl().childNodes.length > 5000) { consoleEl().removeChild(consoleEl().firstChild); } // Re-broadcast so other modules (e.g. pregen) can react to log lines // without opening a second SSE connection. document.dispatchEvent(new CustomEvent("serverlog", { detail: d })); } catch {} }); es.onerror = () => { // EventSource will retry automatically. }; // Command input const cmdInput = document.getElementById("cmdInput"); document.getElementById("cmdSend").addEventListener("click", sendCommand); cmdInput.addEventListener("keydown", onCmdKeyDown); } async function sendCommand() { const cmdInput = document.getElementById("cmdInput"); const v = cmdInput.value.trim(); if (!v) return; try { await apiJson("/api/command", { command: v }); state.cmdHistory.push(v); state.cmdHistoryIdx = state.cmdHistory.length; cmdInput.value = ""; cmdInput.dispatchEvent(new Event("input")); // refresh ghost text } catch (e) { alert(e.message); } } function onCmdKeyDown(e) { const cmdInput = document.getElementById("cmdInput"); if (e.key === "Enter") { sendCommand(); } else if (e.key === "ArrowUp") { if (state.cmdHistory.length === 0) return; e.preventDefault(); state.cmdHistoryIdx = Math.max(0, state.cmdHistoryIdx - 1); cmdInput.value = state.cmdHistory[state.cmdHistoryIdx] || ""; cmdInput.dispatchEvent(new Event("input")); } else if (e.key === "ArrowDown") { if (state.cmdHistory.length === 0) return; e.preventDefault(); state.cmdHistoryIdx = Math.min(state.cmdHistory.length, state.cmdHistoryIdx + 1); cmdInput.value = state.cmdHistory[state.cmdHistoryIdx] || ""; cmdInput.dispatchEvent(new Event("input")); } // Note: Tab is handled by the autocomplete module's keydown listener. }