/* Brain of Reese — Sources page (knowledge base index view). * * Wires the real `GET /api/docs` endpoint (import phase): stat cards + * full-width document table, or the designed empty state when nothing is * indexed yet. Cells are built with DOM APIs (textContent) — never * innerHTML with document-derived data (XSS-safe by construction). */ const tbody = document.querySelector("#docs-tbody"); const emptyEl = document.querySelector("#sources-empty"); const tableWrap = document.querySelector(".table-wrap"); const statDocs = document.querySelector("#stat-docs"); const statChunks = document.querySelector("#stat-chunks"); const statLast = document.querySelector("#stat-last"); function fmtDate(iso) { try { return new Date(iso).toLocaleString(); } catch { return iso; } } async function loadDocs() { let r; try { r = await fetch("/api/docs"); } catch { showEmpty(); return; } if (!r.ok) { showEmpty(); return; } const { documents } = await r.json(); if (!documents.length) { showEmpty(); return; } tbody.replaceChildren(); let totalChunks = 0; let last = ""; for (const d of documents) { totalChunks += d.chunks; if (d.indexed_at > last) last = d.indexed_at; tbody.appendChild(makeRow(d)); } statDocs.textContent = String(documents.length); statChunks.textContent = String(totalChunks); statLast.textContent = last ? fmtDate(last) : "–"; emptyEl.hidden = true; tableWrap.hidden = false; } function makeRow(d) { const tr = document.createElement("tr"); const cells = [d.source, d.path, d.title, String(d.chunks), fmtDate(d.indexed_at)]; for (const value of cells) { const td = document.createElement("td"); td.textContent = value; // document-derived text — never innerHTML tr.appendChild(td); } tr.children[1].title = d.path; // full path on hover (column is ellipsized) return tr; } function showEmpty() { statDocs.textContent = "0"; statChunks.textContent = "0"; statLast.textContent = "–"; emptyEl.hidden = false; if (tableWrap) tableWrap.hidden = true; } loadDocs();