/* ======================================================================= File: static/js/log_view.js Purpose: High-performance rolling log viewer - efficient delta polling - append-only mode (no DOM reset) - rolling limit (prevents memory freeze) - supports both global logs and per-book logs ======================================================================= */ console.log(">>> log_view.js LOADING…"); /* --------------------------------------------------------- Global log viewer state --------------------------------------------------------- */ let LOG_FILTER = "ALL"; let LAST_LOG_INDEX = -1; // delta offset const MAX_LOG_LINES = 600; // safe rolling window /* --------------------------------------------------------- Apply filter on existing log lines --------------------------------------------------------- */ function applyLogFilter() { const lines = $$(".log-line"); lines.forEach((line) => { const text = line.innerText; const show = LOG_FILTER === "ALL" || (text && text.includes(LOG_FILTER)); line.style.display = show ? "block" : "none"; }); } /* --------------------------------------------------------- DOM Ready — bind clear/filter --------------------------------------------------------- */ document.addEventListener("DOMContentLoaded", () => { console.log(">>> log_view.js DOMContentLoaded"); const filterSel = $("#log-filter"); const clearBtn = $("#log-clear"); const output = $("#log-output"); if (!output) { console.log( ">>> log_view.js: No #log-output on this page → viewer disabled" ); return; } console.log(">>> log_view.js: log viewer detected."); // Filter dropdown (currently disabled in your UI) // if (filterSel) { // filterSel.addEventListener("change", () => { // LOG_FILTER = filterSel.value; // applyLogFilter(); // }); // } if (clearBtn) { clearBtn.addEventListener("click", () => { console.log(">>> log_view.js: Clear log viewer"); output.innerHTML = ""; LAST_LOG_INDEX = -1; // reset delta polling }); } }); /* --------------------------------------------------------- Append ONE line (smart class assignment) --------------------------------------------------------- */ function rollingAppend(lineText) { const output = $("#log-output"); if (!output) return; const div = document.createElement("div"); div.classList.add("log-line"); // Type detection if (lineText.includes("[DL]") || lineText.includes("[DOWNLOAD]")) div.classList.add("dl"); else if (lineText.includes("[PARSE]")) div.classList.add("parse"); else if (lineText.includes("[SAVE]")) div.classList.add("save"); else if (lineText.includes("[AUDIO]")) div.classList.add("audio"); else if (lineText.includes("[CTRL]")) div.classList.add("ctrl"); else if (lineText.includes("[ERROR]")) div.classList.add("error"); else div.classList.add("default"); div.textContent = lineText; output.appendChild(div); // Rolling limit while (output.childNodes.length > MAX_LOG_LINES) { output.removeChild(output.firstChild); } } /* --------------------------------------------------------- Primary API entry: updateLogs() Used by dashboard.js AND delta polling Accepts: { logs: [...], last_index: N } OR legacy: { lines: [...], total: N } --------------------------------------------------------- */ function updateLogs(packet) { const output = $("#log-output"); if (!output) return; if (!packet) return; // Normalized log arrays let lines = packet.logs || packet.lines || []; if (!Array.isArray(lines)) return; // Append only new lines lines.forEach((line) => rollingAppend(line)); // Update delta index if (packet.last_index !== undefined) { LAST_LOG_INDEX = packet.last_index; } else if (packet.total !== undefined) { LAST_LOG_INDEX = packet.total - 1; } applyLogFilter(); autoScroll(output); } /* --------------------------------------------------------- Delta polling: ONLY global logs use this Dashboard overrides logs per book. --------------------------------------------------------- */ function pollLogs() { fetch(`/logs?last_index=${LAST_LOG_INDEX}`) .then((r) => r.json()) .then((data) => { const lines = data.lines || []; if (lines.length > 0) { lines.forEach((line) => logAppend(line)); LAST_LOG_INDEX = data.last; // <-- DE JUISTE INDEX! } }) .catch((err) => { console.warn(">>> log_view.js pollLogs() error:", err); }); } setInterval(pollLogs, 2800); console.log(">>> log_view.js LOADED");