import { STALL_AFTER_H } from "./tracker.js"; const LABEL = { extracting: "Extracting", deduping: "Deduplicating", stalled: "Stalled", done: "Done", queued: "Manifest ready", not_started: "Not started", }; const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]); const pct = (f) => `${(f * 100).toFixed(1)}%`; const chunks = (c) => c.nChunks == null ? "—" : `${c.chunksDone} / ${c.nChunks}`; const rowsCell = (c) => c.rows == null ? "—" : c.rows.toLocaleString("en-US"); function ago(d, now) { if (!d) return "—"; const s = Math.floor((now - d) / 1000); for (const [u, n] of [["d", 86400], ["h", 3600], ["m", 60]]) if (s >= n) return `${Math.floor(s / n)}${u} ago`; return "just now"; } function dur(s) { if (s == null) return "—"; const d = s / 86400; return `~${d.toFixed(1)} ${d.toFixed(1) === "1.0" ? "day" : "days"}`; } const bar = (f, st) => `
`; const badge = (st) => `${LABEL[st]}`; export function render(data) { const now = data.fetchedAt; const live = data.crawls.filter((c) => ["extracting", "deduping", "stalled"].includes(c.status)); const stats = `Nothing is running.
`; const rows = data.crawls.map((c) => `| Crawl | Status | Progress | Chunks | Manifest rows | Last activity |
|---|
As of ${now.toLocaleString()} · overall progress is the average of all ${data.crawls.length} target crawls' own % complete, so crawls not started yet count as 0% and pull it down too · "Stalled" means no chunk upload for ${STALL_AFTER_H}h+