// Unified [tag] log scrollback — consumes /ws/logs (backlog once, then new // lines as they're appended to lidar.log/camera.log/recording.log). const MAX_RENDERED_LINES = 2000; export function initLogPanel() { const body = document.getElementById("log-body"); const autoscrollChk = document.getElementById("log-autoscroll"); const clearBtn = document.getElementById("btn-log-clear"); function appendLine({ tag, line }) { const row = document.createElement("div"); row.className = `log-line tag-${tag}`; const tagSpan = document.createElement("span"); tagSpan.className = "log-tag"; tagSpan.textContent = `[${tag}]`; const textSpan = document.createElement("span"); textSpan.className = "log-text"; textSpan.textContent = line; row.appendChild(tagSpan); row.appendChild(textSpan); body.appendChild(row); while (body.childElementCount > MAX_RENDERED_LINES) { body.removeChild(body.firstChild); } if (autoscrollChk.checked) { body.scrollTop = body.scrollHeight; } } clearBtn.onclick = () => { body.innerHTML = ""; }; const proto = location.protocol === "https:" ? "wss:" : "ws:"; function connect() { const ws = new WebSocket(`${proto}//${location.host}/ws/logs`); ws.onmessage = (ev) => { const msg = JSON.parse(ev.data); if (msg.backlog) msg.backlog.forEach(appendLine); if (msg.lines) msg.lines.forEach(appendLine); }; ws.onclose = () => setTimeout(connect, 1500); ws.onerror = () => ws.close(); } connect(); }