/* Vesuvius Radar page: asks the API for one window and view, draws the charts, lists the items, follows the collector, and keeps the viewer's own stars, hides and last visit in this browser. No libraries. */ (function () { "use strict"; const SRC = ["arxiv", "github", "hf", "pypi", "zenodo", "journals", "blog"]; const SRC_LABEL = { arxiv: "arXiv", github: "GitHub", hf: "Hugging Face", pypi: "PyPI", zenodo: "Zenodo", journals: "Journals", blog: "News and blogs" }; const SHORT = { arxiv: "arXiv", github: "GitHub", hf: "HF", pypi: "PyPI", zenodo: "Zenodo", journals: "Journals", blog: "News" }; const BANDS = ["high", "medium", "low"]; const BAND_LABEL = { high: "High", medium: "Medium", low: "Low" }; // An issue's or pull request's author is who opened it; a later comment or review has its own author. const OPENED = { issue: "opened by", pr: "opened by", thread: "started by" }; const clip = (s, n) => (s.length > n ? s.slice(0, n - 2) + "..." : s); // who with its first name replaced by the person's name (who_name), co-authors and other posters kept const byline = (i) => (i.who_name ? [i.who_name].concat(i.who.split(",").slice(1).map((s) => s.trim())).join(", ") : i.who); const KIND = { paper: "paper", repo: "repository", pr: "pull request", issue: "issue", release: "release", model: "model", dataset: "dataset", space: "Space", "daily-paper": "daily paper", package: "package version", post: "blog post", news: "news story" }; const LOOKBACKS = [["1m", "1 min"], ["5m", "5 min"], ["15m", "15 min"], ["30m", "30 min"], ["1h", "1 hour"], ["3h", "3 hours"], ["6h", "6 hours"], ["12h", "12 hours"], ["1d", "1 day"], ["2d", "2 days"], ["3d", "3 days"], ["7d", "7 days"], ["14d", "14 days"], ["30d", "30 days"]]; const VIEW_ICON = { all: '', research: '', team: '', builders: '', }; const VIEW_SHORT = { all: "Everything, from every source", research: "Papers and methods", team: "Community activity about the scrolls", builders: "Tools, models, data, releases" }; const PER = [10, 20, 50, 100]; // items per page: the list is paged, so the page never grows without end const $ = (s, r = document) => r.querySelector(s); const $$ = (s, r = document) => Array.from(r.querySelectorAll(s)); const esc = (s) => String(s == null ? "" : s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); // GitHub reports a license file it cannot identify as NOASSERTION, and a Hub card may say "other" or "unknown": show // those in words, with the raw value kept in the tooltip (and in the API). const LICENSE_WORDS = { noassertion: ["Other license", "GitHub found a license file it could not identify"], other: ["Other license", "The card names a license outside the Hub's list"], unknown: ["License unknown", "The card does not say which license"] }; const licenseTag = (l) => { const w = LICENSE_WORDS[String(l).toLowerCase()]; return w ? `${esc(w[0])}` : `${esc(l)}`; }; const safe = (u) => (/^https?:\/\//i.test(String(u || "")) ? String(u) : "#"); const fmtN = (n) => (n == null ? "" : Number(n).toLocaleString("en-US")); const store = { get(k, d) { try { const v = localStorage.getItem("vradar." + k); return v == null ? d : JSON.parse(v); } catch (e) { return d; } }, set(k, v) { try { localStorage.setItem("vradar." + k, JSON.stringify(v)); } catch (e) { /* private window */ } }, }; /* ------------------------------------------------------------ state */ const defaults = () => ({ view: "all", since: "1d", by: "date", q: "", meaning: false, sort: "date", min: 20, sources: [], bands: [], topics: [], code: false, starred: false, showHidden: false, newOnly: false }); // Saved filters from before version 2 kept "Most relevant" as the sort; the default is now Newest, so drop the old sort. const saved = store.get("filters", {}); if (saved.v !== 2) delete saved.sort; delete saved.v; const perSaved = +store.get("per", 10); const S = Object.assign({ f: Object.assign(defaults(), saved), per: PER.includes(perSaved) ? perSaved : 10, page: 0, items: [], matched: 0, stars: new Set(store.get("stars", [])), hide: new Set(store.get("hidden", [])), ov: null, base: null, counts: null, cfg: null, open: new Set(), like: new Set() }); // The last visit: items first seen after it get a "new" mark. Updated a little after the page opens. S.lastVisit = store.get("lastVisit", null); setTimeout(() => store.set("lastVisit", new Date().toISOString().slice(0, 19) + "Z"), 15000); readUrl(); function readUrl() { const p = new URLSearchParams(location.search); // Hugging Face opens the Space with parameters of its own (__theme, and __sign on a private Space). Only a link that // names a filter replaces the saved filters, and a link without "min" keeps the saved minimum score. if (!["view", "since", "by", "q", "meaning", "sort", "min", "sources", "bands", "topics", "code", "per"].some((k) => p.has(k))) return; const f = Object.assign(defaults(), { min: S.f.min }); if (p.get("view")) f.view = p.get("view"); if (p.get("since")) f.since = p.get("since"); if (p.get("by")) f.by = p.get("by"); if (p.get("q")) f.q = p.get("q"); if (p.get("meaning") === "1") f.meaning = true; if (p.get("sort")) f.sort = p.get("sort"); if (p.get("min")) f.min = Math.max(0, Math.min(90, +p.get("min") || 0)); ["sources", "bands", "topics"].forEach((k) => { if (p.get(k)) f[k] = p.get(k).split(",").filter(Boolean); }); if (p.get("code") === "1") f.code = true; if (PER.includes(+p.get("per"))) S.per = +p.get("per"); S.f = f; } function viewParams(extra) { const f = S.f, p = new URLSearchParams(); p.set("since", f.since); p.set("view", f.view); if (f.by !== "date") p.set("by", f.by); if (f.q.trim()) { p.set("q", f.q.trim()); if (f.meaning) p.set("by_meaning", "true"); } if (f.min) p.set("min_score", f.min); if (f.sources.length) p.set("sources", f.sources.join(",")); if (f.bands.length) p.set("bands", f.bands.join(",")); if (f.topics.length) p.set("topics", f.topics.join(",")); if (f.code) p.set("has_code", "true"); if (f.starred) p.set("ids", Array.from(S.stars).join(",") || "none"); for (const k in extra || {}) { if (extra[k] == null) p.delete(k); else p.set(k, extra[k]); } return p; } function pageUrl() { const f = S.f, p = new URLSearchParams(); const d = defaults(); ["view", "since", "by", "q", "sort"].forEach((k) => { if (f[k] && f[k] !== d[k]) p.set(k, f[k]); }); if (f.meaning) p.set("meaning", "1"); p.set("min", f.min); // always, so a shared link shows its own minimum rather than the reader's saved one ["sources", "bands", "topics"].forEach((k) => { if (f[k].length) p.set(k, f[k].join(",")); }); if (f.code) p.set("code", "1"); if (S.per !== 10) p.set("per", S.per); const qs = p.toString(); return location.origin + location.pathname + (qs ? "?" + qs : ""); } /* ------------------------------------------------------------ time */ const MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; const D = (iso) => new Date(iso); const pad = (n) => String(n).padStart(2, "0"); const hm = (d) => pad(d.getUTCHours()) + ":" + pad(d.getUTCMinutes()); const dm = (d) => d.getUTCDate() + " " + MON[d.getUTCMonth()]; const stamp = (iso) => { const d = D(iso); return dm(d) + " " + hm(d) + " UTC"; }; function ago(iso) { const s = (Date.now() - D(iso).getTime()) / 1000; if (s < 0) return "in " + until(iso); if (s < 50) return "just now"; if (s < 3600) return Math.max(1, Math.round(s / 60)) + " min ago"; if (s < 86400 * 1.5) return Math.round(s / 3600) + " h ago"; return Math.round(s / 86400) + " days ago"; } function until(iso) { const s = (D(iso).getTime() - Date.now()) / 1000; if (s < 60) return "under a minute"; if (s < 3600) return Math.round(s / 60) + " min"; return Math.round(s / 3600) + " h"; } const lbLabel = (v) => (LOOKBACKS.find((x) => x[0] === v) || [v, v])[1]; /* ------------------------------------------------------------ svg helpers */ const NS = "http://www.w3.org/2000/svg"; function el(tag, attrs, text) { const e = document.createElementNS(NS, tag); for (const k in attrs) if (attrs[k] != null) e.setAttribute(k, attrs[k]); if (text != null) e.textContent = text; return e; } // A bar with only its data end rounded, anchored square on the baseline. function barPath(x, y, w, h, r, horizontal) { if (w <= 0 || h <= 0) return ""; r = Math.max(0, Math.min(r, horizontal ? h / 2 : w / 2, horizontal ? w : h)); if (horizontal) return `M${x},${y}H${x + w - r}Q${x + w},${y} ${x + w},${y + r}V${y + h - r}Q${x + w},${y + h} ${x + w - r},${y + h}H${x}Z`; return `M${x},${y + h}V${y + r}Q${x},${y} ${x + r},${y}H${x + w - r}Q${x + w},${y} ${x + w},${y + r}V${y + h}Z`; } const niceMax = (v) => { if (v <= 4) return 4; const p = Math.pow(10, Math.floor(Math.log10(v))); for (const m of [1, 2, 2.5, 5, 10]) if (m * p >= v) return m * p; return 10 * p; }; function ticks(max, n) { const step = niceMax(max / n); const out = []; for (let v = 0; v <= max + 1e-9; v += step) out.push(v); return out; } const width = (host, min, max) => Math.max(min, Math.min(max, Math.round(host.clientWidth || max))); /* ------------------------------------------------------------ tooltip */ const tip = $("#tip"); function showTip(html, x, y) { tip.innerHTML = html; tip.hidden = false; const r = tip.getBoundingClientRect(); let left = x + 14, top = y + 14; if (left + r.width > innerWidth - 8) left = x - r.width - 14; if (top + r.height > innerHeight - 8) top = y - r.height - 14; tip.style.left = Math.max(8, left) + "px"; tip.style.top = Math.max(8, top) + "px"; } document.addEventListener("pointermove", (e) => { const t = e.target.closest && e.target.closest("[data-tip]"); if (t) showTip(t.getAttribute("data-tip"), e.clientX, e.clientY); else tip.hidden = true; }); document.addEventListener("scroll", () => { tip.hidden = true; }, true); const row = (color, label, val) => `
${color ? `` : ""}${esc(label)}${esc(val)}
`; const hint = (t) => `
${esc(t)}
`; /* ------------------------------------------------------------ data */ async function getJSON(url, opt) { const r = await fetch(url, Object.assign({ cache: "no-store" }, opt || {})); const body = await r.json().catch(() => ({})); if (!r.ok && r.status !== 409 && r.status !== 429) throw new Error(body.detail || ("HTTP " + r.status)); return { status: r.status, body }; } let seq = 0; async function load() { const my = ++seq; store.set("filters", Object.assign({}, S.f, { starred: false, showHidden: false, newOnly: false, v: 2 })); store.set("per", S.per); try { history.replaceState(null, "", pageUrl().replace(location.origin, "") + location.hash); } catch (e) { /* sandboxed */ } const base = viewParams({ sources: null, bands: null, topics: null }); const sort = S.f.meaning && S.f.q.trim() ? "score" : S.f.sort; const [ov, bs, items, counts] = await Promise.all([ getJSON("api/overview?" + viewParams()), getJSON("api/overview?" + base), getJSON("api/items?" + viewParams({ sort, limit: S.per, offset: 0 })), getJSON("api/counts?" + viewParams({ since: "30d", by_meaning: null })), ]); if (my !== seq) return; S.ov = ov.body; S.base = bs.body; S.counts = counts.body.lookbacks; S.matched = items.body.matched; S.items = items.body.items; S.page = 0; S.byMeaning = items.body.by_meaning; renderAll(); if (!$("[data-panel=briefing]").hidden) loadBriefing(); renderLinks(); } async function goPage(n) { const pages = Math.max(1, Math.ceil(S.matched / S.per)); n = Math.max(0, Math.min(pages - 1, n)); const sort = S.f.meaning && S.f.q.trim() ? "score" : S.f.sort; const { body } = await getJSON("api/items?" + viewParams({ sort, limit: S.per, offset: n * S.per })); S.items = body.items; S.page = n; S.matched = body.matched; renderList(); const top = $("#listTop"); if (top.getBoundingClientRect().top < 0) top.scrollIntoView({ block: "start" }); } function pagerHTML() { const pages = Math.ceil(S.matched / S.per), p = S.page; if (pages <= 1) return ""; const nums = [...new Set([0, p - 2, p - 1, p, p + 1, p + 2, pages - 1].filter((k) => k >= 0 && k < pages))].sort((a, b) => a - b); let out = "", prev = -1; nums.forEach((k) => { if (k > prev + 1) out += ''; out += ``; prev = k; }); const from = p * S.per + 1, to = Math.min(S.matched, (p + 1) * S.per); return `${fmtN(from)} to ${fmtN(to)} of ${fmtN(S.matched)} ${out} `; } let loadTimer; const reload = (ms) => { clearTimeout(loadTimer); loadTimer = setTimeout(() => load().catch(fail), ms || 0); }; function fail(e) { notice("Could not load the radar: " + esc(e.message) + ". It may be waking up; try again in a minute.", "bad"); } /* ------------------------------------------------------------ controls */ function renderViews() { const V = S.views || {}; $("#views").innerHTML = ["all", "research", "team", "builders"].map((k) => ``).join(""); $("#viewAbout").textContent = (V[S.f.view] && V[S.f.view].about) || ""; } $("#views").addEventListener("click", (e) => { const b = e.target.closest("[data-view]"); if (!b) return; S.f.view = b.dataset.view; renderViews(); reload(); }); function renderLookback() { const opts = LOOKBACKS.some(([v]) => v === S.f.since) ? LOOKBACKS : LOOKBACKS.concat([[S.f.since, S.f.since]]); const n = (v) => (S.counts && S.counts[v] != null ? ` (${fmtN(S.counts[v])} ${S.counts[v] === 1 ? "item" : "items"})` : ""); $("#lookback").innerHTML = opts.map(([v, l]) => ``).join(""); $$("#byPick button").forEach((b) => b.setAttribute("aria-pressed", String(b.dataset.by === S.f.by))); } $("#lookback").addEventListener("change", (e) => { S.f.since = e.target.value; renderLookback(); reload(); }); $("#byPick").addEventListener("click", (e) => { const b = e.target.closest("[data-by]"); if (!b) return; S.f.by = b.dataset.by; renderLookback(); reload(); }); /* ------------------------------------------------------------ collector */ const notice = (msg, kind) => { const n = $("#notice"); n.className = "notice" + (kind ? " " + kind : ""); n.innerHTML = msg; n.hidden = !msg; }; let colTimer, wasRunning = false, lastSaved = null; async function pollSources() { clearTimeout(colTimer); let running = false; try { const { body } = await getJSON("api/sources"); S.src = body; running = !!body.check_requested || Object.values(body.sources).some((s) => s.state === "running"); renderCollector(body, running); if (!HERO.done) { HERO.done = true; heroStats(); } if (!$("[data-panel=sources]").hidden) renderSources(); if (body.store.saved && lastSaved && body.store.saved !== lastSaved && !running) reload(300); lastSaved = body.store.saved; if (wasRunning && !running) { const n = Object.values(body.sources).reduce((a, s) => a + (s.new || 0), 0); notice(`Check finished: ${fmtN(n)} items new to the radar. Showing the updated window.`, ""); reload(); heroStats(); } wasRunning = running; } catch (e) { $("#colText").textContent = "The Space is waking up or busy; retrying..."; } colTimer = setTimeout(pollSources, running ? 2000 : 20000); } function renderCollector(b, running) { const src = b.sources, list = Object.values(src); const done = list.filter((s) => s.last_check); const newest = done.map((s) => s.last_check).sort().pop(); const next = list.map((s) => s.next_check).sort()[0]; const bad = list.filter((s) => s.state === "error" || s.state === "partial"); const p = $("#pulse"); p.className = "pulse" + (running ? " busy" : bad.length ? " warn" : ""); $("#colText").innerHTML = running ? `Checking sources now. ${list.filter((s) => s.state !== "running").length} of ${list.length} done.` : (newest ? `Collecting around the clock. Last check ${esc(ago(newest))}; next in ${esc(until(next))}. ` : "Waiting for the first check. ") + `${list.length - bad.length} of ${list.length} sources healthy · ${fmtN(b.store.items)} items from the last 30 days`; const btn = $("#checkBtn"); btn.disabled = running; btn.classList.toggle("busy", running); btn.querySelector("span").textContent = running ? "Checking..." : "Check now"; document.body.classList.toggle("scanning", running); $("#progress").hidden = !running; if (running) { $("#progRows").innerHTML = SRC.filter((k) => src[k]).map((k) => { const v = src[k], fresh = b.check_requested && v.last_check && v.last_check >= b.check_requested; const st = v.state === "running" ? "running" : fresh ? v.state : "queued"; const icon = st === "running" ? '' : st === "queued" ? "○" : st === "ok" ? '✓' : st === "partial" ? '!' : '✕'; return `
${icon}${esc(v.label)}${fresh ? fmtN(v.new) + " new" : ""}${esc(v.step || (fresh ? (v.notes[0] || v.state) : st))}
`; }).join(""); } $("#build").textContent = `Store: ${fmtN(b.store.items)} items, saved ${b.store.saved ? ago(b.store.saved) : "not yet"} · model ${b.semantic.model} ${b.semantic.ready ? "ready" : "not loaded"} · up since ${stamp(b.store.booted)}`; } $("#checkBtn").addEventListener("click", async () => { notice(""); try { const { status, body } = await getJSON("api/check", { method: "POST" }); if (status === 429) { notice(esc(body.reason) + ". The collector also runs on its own schedule (Sources tab).", "warn"); return; } if (status === 409) notice("A check is already running; following it."); wasRunning = true; pollSources(); } catch (err) { notice("Could not start a check: " + esc(err.message), "bad"); } }); /* ------------------------------------------------------------ KPIs */ function renderKpis() { const o = S.ov, items = o.items; const spark = SRC.filter((k) => o.by_source[k]).map((k) => ``).join(""); const bspark = BANDS.filter((b) => o.by_band[b]).map((b) => ``).join(""); const nNew = S.lastVisit ? S.items.filter((i) => (i.first_seen || "") > S.lastVisit).length : null; const win = `in the last ${lbLabel(S.f.since)}`; $("#kpis").innerHTML = [ `
Items${fmtN(items)}${esc(win)}
${spark}
`, `
High relevance${fmtN(o.high)}score 60 or more; ${fmtN(o.medium)} medium
${bspark}
`, `
With code or data${fmtN(o.with_code + o.new_repos)}${fmtN(o.with_code)} link code, ${fmtN(o.new_repos)} new repositories
`, o.learned && o.learned.ready ? `
Learned scores${fmtN(o.learned.items)}scored by a model fitted to blind ratings of ${fmtN(o.learned.trained_on)} items
` : `
Found by meaning${o.semantic.ready ? fmtN(o.lifted_by_meaning) : "off"}${o.semantic.ready ? "scored higher by the model than by keywords" : "the model is loading"}
`, `
New since your visit${nNew == null ? "first" : fmtN(nNew)}${S.lastVisit ? "among the top items, since " + esc(stamp(S.lastVisit)) : "visit; marks start next time"}
`, ].join(""); } /* ------------------------------------------------------------ charts */ function binLabel(iso, step) { const d = D(iso); if (step < 3600) return hm(d); if (step < 86400) return d.getUTCHours() === 0 || step >= 6 * 3600 ? dm(d) + " " + hm(d) : hm(d); return dm(d); } function chartTime() { const host = $("#chTime"); host.innerHTML = ""; const tl = S.ov.timeline, bins = tl.bins, step = tl.bin_seconds; const unit = step < 3600 ? (step / 60) + "-minute" : step < 86400 ? (step / 3600) + "-hour" : "day"; $("#tlCaption").textContent = `Items per ${unit} bin (UTC), by ${S.f.by === "found" ? "when the radar found them" : "publish or update time"}, stacked by source.`; const W = width(host, 320, 1140), H = W < 600 ? 210 : 230, L = 38, R = 10, T = 16, B = 28; const tot = (b) => SRC.reduce((a, k) => a + (b.n[k] || 0), 0); const max = niceMax(Math.max(1, ...bins.map(tot))); const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": "Items per time bin, stacked by source" }); const y = (v) => T + (H - T - B) * (1 - v / max); ticks(max, 4).forEach((v) => { svg.appendChild(el("line", { x1: L, x2: W - R, y1: y(v), y2: y(v), class: v === 0 ? "base" : "gl" })); svg.appendChild(el("text", { x: L - 7, y: y(v) + 3.5, "text-anchor": "end", class: "tick" }, fmtN(v))); }); const bw = (W - L - R) / bins.length, w = Math.max(1.5, Math.min(40, bw * 0.7)); const every = Math.ceil(bins.length / Math.max(3, Math.floor((W - L) / 84))); bins.forEach((b, n) => { const cx = L + bw * n + bw / 2, t = tot(b); const present = SRC.filter((k) => b.n[k]); let acc = 0; present.forEach((k, j) => { const y0 = y(acc), y1 = y(acc + b.n[k]); acc += b.n[k]; const top = j === present.length - 1, gap = top || w < 4 ? 0 : 1.5; const h = Math.max(0, y0 - y1 - gap); svg.appendChild(el("path", { d: top ? barPath(cx - w / 2, y1, w, h, Math.min(3, w / 2)) : `M${cx - w / 2},${y1 + gap}h${w}v${h}h${-w}Z`, style: `fill:var(--s-${k})`, class: "mark" })); }); if (t && bins.length <= 40) svg.appendChild(el("text", { x: cx, y: y(t) - 5, "text-anchor": "middle", class: "tick", style: "fill:var(--fg2)" }, fmtN(t))); if (n % every === 0) svg.appendChild(el("text", { x: cx, y: H - 9, "text-anchor": "middle", class: "tick" }, binLabel(b.t, step))); const end = new Date(D(b.t).getTime() + step * 1000).toISOString(); svg.appendChild(el("rect", { x: L + bw * n, y: T, width: bw, height: H - T - B, class: "hit", "data-tip": `${esc(stamp(b.t))} to ${esc(hm(D(end)))}` + (t ? SRC.filter((k) => b.n[k]).map((k) => row(`var(--s-${k})`, SRC_LABEL[k], b.n[k])).join("") : hint("Nothing in this bin")) + (t ? row("", "Total", t) : "") })); }); if (!S.ov.items) svg.appendChild(el("text", { x: W / 2, y: H / 2, "text-anchor": "middle", class: "empty" }, "Nothing in this window yet")); host.appendChild(svg); $("#legTime").innerHTML = SRC.filter((k) => S.ov.by_source[k]).map((k) => `${SRC_LABEL[k]}`).join(""); } function chartTopics() { const host = $("#chTopics"); host.innerHTML = ""; const rows = Object.entries(S.ov.by_topic).slice(0, 10); const W = width(host, 300, 520), rowH = 30, L = 156, R = 40, H = Math.max(70, rows.length * rowH + 8); const max = Math.max(1, ...rows.map((r) => r[1])); const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": "Items per topic" }); host.classList.toggle("dim", S.f.topics.length > 0); rows.forEach(([t, n], k) => { const y = 4 + k * rowH, w = (W - L - R) * n / max, on = S.f.topics.includes(t); svg.appendChild(el("text", { x: L - 10, y: y + rowH / 2 + 4, "text-anchor": "end", "font-size": 12.5, style: on ? "fill:var(--fg);font-weight:650" : "" }, t)); svg.appendChild(el("path", { d: barPath(L, y + 7, Math.max(3, w), rowH - 14, 4, true), class: "mark" + (on ? " on" : ""), style: "fill:var(--accent)" })); svg.appendChild(el("text", { x: L + w + 7, y: y + rowH / 2 + 4, class: "tick", style: "fill:var(--fg2)" }, fmtN(n))); const hit = el("rect", { x: 0, y, width: W, height: rowH, class: "hit", "data-tip": `${esc(t)}` + row("", "Items", fmtN(n)) + hint(on ? "Click to remove this filter" : "Click to filter by this topic") }); hit.addEventListener("click", () => toggle("topics", t)); svg.appendChild(hit); }); if (!rows.length) svg.appendChild(el("text", { x: W / 2, y: 38, "text-anchor": "middle", class: "empty" }, "No topics in view")); host.appendChild(svg); } function chartScores() { const host = $("#chScores"); host.innerHTML = ""; const bins = S.base.score_hist; const W = width(host, 300, 520), H = 240, L = 34, R = 8, T = 14, B = 28; const max = niceMax(Math.max(1, ...bins)); const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": "Histogram of relevance scores" }); const y = (v) => T + (H - T - B) * (1 - v / max), x = (s) => L + (W - L - R) * s / 100; ticks(max, 3).forEach((v) => { svg.appendChild(el("line", { x1: L, x2: W - R, y1: y(v), y2: y(v), class: v === 0 ? "base" : "gl" })); svg.appendChild(el("text", { x: L - 6, y: y(v) + 3.5, "text-anchor": "end", class: "tick" }, fmtN(v))); }); const band = (a) => (a >= 60 ? "high" : a >= 35 ? "medium" : "low"); bins.forEach((n, k) => { const a = k * 5, x0 = x(a) + 1, w = x(a + 5) - x(a) - 2; if (n) svg.appendChild(el("path", { d: barPath(x0, y(n), w, y(0) - y(n), 3), style: `fill:var(--b-${band(a)})`, opacity: a + 5 <= S.f.min ? 0.3 : 1 })); const hit = el("rect", { x: x0, y: T, width: w, height: H - T - B, class: "hit", "data-tip": `Score ${a} to ${a + 4}` + row(`var(--b-${band(a)})`, BAND_LABEL[band(a)] + " band", fmtN(n) + " items") + hint("Click to set the minimum score here") }); hit.addEventListener("click", () => { S.f.min = Math.min(90, a); reload(); }); svg.appendChild(hit); }); [0, 20, 35, 60, 80, 100].forEach((v) => svg.appendChild(el("text", { x: x(v), y: H - 9, "text-anchor": "middle", class: "tick" }, v))); const mx = x(S.f.min); svg.appendChild(el("line", { x1: mx, x2: mx, y1: T - 4, y2: y(0), style: "stroke:var(--fg);stroke-width:1.5", "stroke-dasharray": "4 3" })); svg.appendChild(el("text", { x: mx + 5, y: T + 7, "font-size": 11, style: "fill:var(--fg)" }, "min " + S.f.min)); host.appendChild(svg); } function chartSources() { const host = $("#chSources"); host.innerHTML = ""; const by = S.base.by_source, rows = SRC.map((k) => [k, by[k] || 0]); const W = width(host, 300, 520), rowH = 30, L = 120, R = 44, H = rows.length * rowH + 6; const max = Math.max(1, ...rows.map((r) => r[1])); const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": "Items per source" }); host.classList.toggle("dim", S.f.sources.length > 0); rows.forEach(([k, n], j) => { const y = 3 + j * rowH, w = (W - L - R) * n / max, on = S.f.sources.includes(k); svg.appendChild(el("circle", { cx: 8, cy: y + rowH / 2, r: 5, style: `fill:var(--s-${k})` })); svg.appendChild(el("text", { x: 20, y: y + rowH / 2 + 4, "font-size": 13, style: on ? "fill:var(--fg);font-weight:650" : "fill:var(--fg)" }, SRC_LABEL[k])); svg.appendChild(el("rect", { x: L, y: y + 8, width: W - L - R, height: rowH - 16, rx: 4, style: "fill:var(--chip)" })); if (n) svg.appendChild(el("path", { d: barPath(L, y + 8, Math.max(3, w), rowH - 16, 4, true), class: "mark" + (on ? " on" : ""), style: `fill:var(--s-${k})` })); svg.appendChild(el("text", { x: W - R + 8, y: y + rowH / 2 + 4, class: "tick", style: "fill:var(--fg2)" }, fmtN(n))); const hit = el("rect", { x: 0, y, width: W, height: rowH, class: "hit", "data-tip": `${SRC_LABEL[k]}` + row(`var(--s-${k})`, "Items in view", fmtN(n)) + hint(on ? "Click to remove this filter" : "Click to show only this source") }); hit.addEventListener("click", () => toggle("sources", k)); svg.appendChild(hit); }); host.appendChild(svg); } function mix(t) { // a share of the heat ramp, as a CSS color-mix return `color-mix(in oklab, var(--heat-1) ${Math.round(12 + 88 * t)}%, var(--heat-0))`; } function chartMatrix() { const host = $("#chMatrix"); host.innerHTML = ""; const m = S.ov.topic_source, topics = Object.keys(S.ov.by_topic).slice(0, 9), srcs = SRC.filter((k) => S.ov.by_source[k]); if (!topics.length) { host.innerHTML = '

No items in view.

'; return; } const W = width(host, 320, 560), L = 150, T = 26, cw = (W - L - 4) / Math.max(1, srcs.length), ch = 26, H = T + topics.length * ch + 4; const max = Math.max(1, ...topics.flatMap((t) => srcs.map((k) => (m[t] || {})[k] || 0))); const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": "Items per topic and source" }); srcs.forEach((k, j) => svg.appendChild(el("text", { x: L + cw * j + cw / 2, y: 14, "text-anchor": "middle", "font-size": 11.5 }, (cw < 60 ? SHORT : SRC_LABEL)[k]))); topics.forEach((t, i) => { const y = T + i * ch; svg.appendChild(el("text", { x: L - 10, y: y + ch / 2 + 4, "text-anchor": "end", "font-size": 12.5 }, t)); srcs.forEach((k, j) => { const n = (m[t] || {})[k] || 0, x = L + cw * j; svg.appendChild(el("rect", { x: x + 1, y: y + 1, width: cw - 2, height: ch - 2, rx: 4, style: `fill:${n ? mix(Math.sqrt(n / max)) : "var(--sunk)"}`, "data-tip": `${esc(t)}` + row(`var(--s-${k})`, SRC_LABEL[k], fmtN(n) + " items") })); if (n && cw > 30) svg.appendChild(el("text", { x: x + cw / 2, y: y + ch / 2 + 4, "text-anchor": "middle", class: "tick", style: n / max > 0.45 ? "fill:var(--surface);pointer-events:none" : "fill:var(--fg);pointer-events:none" }, fmtN(n))); }); }); host.appendChild(svg); } function chartPeople() { const host = $("#chPeople"); host.innerHTML = ""; const rows = (S.ov.people || Object.entries(S.ov.top_people).map(([name, value]) => ({ name, value, works: null, items: null }))).slice(0, 8); if (!rows.length) { host.innerHTML = '

No authors in view.

'; return; } const W = width(host, 320, 560), rowH = 30, L = Math.min(210, W * 0.42), R = 96, H = rows.length * rowH + 6; const max = Math.max(1, ...rows.map((r) => r.value)); const svg = el("svg", { viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": "Top contributors by pieces of work" }); rows.forEach((r, k) => { const y = 3 + k * rowH, w = (W - L - R) * r.value / max, p = r.name; const name = p.length > 28 ? p.slice(0, 26) + "..." : p; const works = r.works == null ? "" : `${r.works} ${r.works === 1 ? "piece" : "pieces"}`; svg.appendChild(el("text", { x: L - 10, y: y + rowH / 2 + 4, "text-anchor": "end", "font-size": 12.5 }, name)); svg.appendChild(el("path", { d: barPath(L, y + 8, Math.max(3, w), rowH - 16, 4, true), style: "fill:var(--b-medium)" })); svg.appendChild(el("text", { x: L + w + 7, y: y + rowH / 2 + 4, class: "tick", style: "fill:var(--fg2)" }, works || fmtN(r.value))); const tipHtml = `${esc(p)}` + (r.works != null ? row("", "Pieces of work", fmtN(r.works)) + row("", "Items", fmtN(r.items)) : "") + row("", "Weight (best score per piece, summed)", fmtN(r.value)) + hint("Click to search for this name"); const hit = el("rect", { x: 0, y, width: W, height: rowH, class: "hit", "data-tip": tipHtml }); hit.addEventListener("click", () => { S.f.q = p; S.f.meaning = false; reload(); }); svg.appendChild(hit); }); host.appendChild(svg); } /* ------------------------------------------------------------ filters */ // Sources, topics, relevance and the browser-only toggles sit in four small dropdowns; what is switched on shows as // removable pills under them, so the panel stays one row however many filters exist. function opt(group, value, label, n, extra, title) { const on = group === "toggle" ? S.f[value] : S.f[group].includes(value); return ``; } const TOGGLES = { code: "Links code", starred: "Starred", showHidden: "Show hidden", newOnly: "New since your visit" }; function summary(sel, all, label) { if (!sel.length) return ""; return sel.length === 1 ? label(sel[0]) : `${sel.length} of ${all}`; } function renderFilters() { const b = S.base; // Only what this window holds, plus anything already picked, so the lists carry no rows of zeros. const srcs = SRC.filter((k) => b.by_source[k] || S.f.sources.includes(k)); $("#fSources").innerHTML = srcs.map((k) => opt("sources", k, SRC_LABEL[k], b.by_source[k] || 0, ``)).join("") || '

Nothing in this window.

'; const topics = Object.keys(b.by_topic); S.f.topics.forEach((t) => { if (!topics.includes(t)) topics.push(t); }); $("#fTopics").innerHTML = topics.map((t) => opt("topics", t, t, b.by_topic[t] || 0)).join("") || '

Nothing in this window.

'; $("#fBands").innerHTML = BANDS.map((x) => opt("bands", x, BAND_LABEL[x] + (x === "high" ? " (60+)" : x === "medium" ? " (35 to 59)" : " (under 35)"), b.by_band[x] || 0, ``)).join(""); $("#fToggles").innerHTML = opt("toggle", "code", TOGGLES.code, null, "", "Only items that link code, models or data") + opt("toggle", "starred", TOGGLES.starred, S.stars.size, "", "Stars live in this browser") + opt("toggle", "showHidden", TOGGLES.showHidden, S.hide.size, "", "Hidden items live in this browser") + (S.lastVisit ? opt("toggle", "newOnly", TOGGLES.newOnly, null, "", "First seen by the radar after " + stamp(S.lastVisit)) : ""); $("#vSources").textContent = summary(S.f.sources, srcs.length, (k) => SRC_LABEL[k]); $("#vTopics").textContent = summary(S.f.topics, topics.length, (t) => t); $("#vRel").textContent = [S.f.bands.length ? S.f.bands.map((x) => BAND_LABEL[x]).join(", ") : "", S.f.min ? S.f.min + "+" : ""].filter(Boolean).join(" · "); const tg = Object.keys(TOGGLES).filter((k) => S.f[k]); $("#vMore").textContent = summary(tg, Object.keys(TOGGLES).length, (k) => TOGGLES[k]); $$("#fbar .dd").forEach((d) => d.classList.toggle("active", !!d.querySelector(".ddv") && !!d.querySelector(".ddv").textContent)); $("#q").value = S.f.q; $("#meaning").checked = S.f.meaning; $("#meaning").disabled = !S.f.q.trim(); $("#sort").value = S.f.sort; $("#min").value = S.f.min; $("#minOut").textContent = S.f.min; $("#per").value = String(S.per); $("#sort").disabled = S.f.meaning && !!S.f.q.trim(); renderPills(); } function renderPills() { const d = defaults(), pills = []; const pill = (g, v, label, dot) => pills.push(``); if (S.f.q.trim()) pill("q", "", (S.f.meaning ? "Meaning: " : "Search: ") + S.f.q.trim()); S.f.sources.forEach((k) => pill("sources", k, SRC_LABEL[k], ``)); S.f.topics.forEach((t) => pill("topics", t, t)); S.f.bands.forEach((x) => pill("bands", x, BAND_LABEL[x], ``)); if (S.f.min !== d.min) pill("min", "", "Score " + S.f.min + "+"); Object.keys(TOGGLES).forEach((k) => { if (S.f[k]) pill("toggle", k, TOGGLES[k]); }); $("#pills").innerHTML = pills.join("") + (pills.length > 1 ? '' : ""); $("#pills").hidden = !pills.length; $("#reset").hidden = !pills.length; } $("#pills").addEventListener("click", (e) => { const b = e.target.closest("[data-pg]"); if (!b) return; const g = b.dataset.pg, v = b.dataset.pv; if (g === "all") return resetFilters(); if (g === "q") { S.f.q = ""; S.f.meaning = false; } else if (g === "min") S.f.min = defaults().min; else if (g === "toggle") S.f[v] = false; else S.f[g] = S.f[g].filter((x) => x !== v); if (g === "toggle" && v !== "code" && v !== "starred") { renderFilters(); renderList(); } else reload(); }); function toggle(group, v) { const a = S.f[group], k = a.indexOf(v); if (k >= 0) a.splice(k, 1); else a.push(v); reload(); } document.addEventListener("click", (e) => { const b = e.target.closest(".fchip"); if (!b) return; const g = b.dataset.g, v = b.dataset.v; if (g === "toggle") { S.f[v] = !S.f[v]; if (v === "starred" || v === "code") reload(); else { renderFilters(); renderList(); } } else toggle(g, v); }); // Dropdowns: one open at a time, closed by a click elsewhere or Escape, and kept inside the screen on a phone. $$(".filters .dd").forEach((d) => d.addEventListener("toggle", () => { if (!d.open) return; $$(".filters .dd[open]").forEach((o) => { if (o !== d) o.open = false; }); const pnl = d.querySelector(".ddpanel"); pnl.style.transform = ""; const r = pnl.getBoundingClientRect(), edge = document.documentElement.clientWidth - 12; if (r.right > edge) pnl.style.transform = `translateX(${Math.round(edge - r.right)}px)`; else if (r.left < 12) pnl.style.transform = `translateX(${Math.round(12 - r.left)}px)`; })); document.addEventListener("click", (e) => { $$(".filters .dd[open]").forEach((d) => { if (!d.contains(e.target)) d.open = false; }); }); document.addEventListener("keydown", (e) => { if (e.key !== "Escape") return; const d = $(".filters .dd[open]"); if (d) { d.open = false; d.querySelector("summary").focus(); } }); $("#q").addEventListener("input", (e) => { S.f.q = e.target.value; $("#meaning").disabled = !S.f.q.trim(); reload(S.f.meaning ? 450 : 220); egg.typed(S.f.q); }); $("#meaning").addEventListener("change", (e) => { S.f.meaning = e.target.checked; reload(); }); $("#sort").addEventListener("change", (e) => { S.f.sort = e.target.value; reload(); }); $("#per").addEventListener("change", (e) => { S.per = PER.includes(+e.target.value) ? +e.target.value : 10; reload(); }); $("#min").addEventListener("input", (e) => { S.f.min = +e.target.value; $("#minOut").textContent = S.f.min; reload(250); }); function resetFilters() { const keep = { view: S.f.view, since: S.f.since, sort: S.f.sort }; S.f = Object.assign(defaults(), keep); renderViews(); renderLookback(); reload(); } $("#reset").addEventListener("click", resetFilters); $("#shareBtn").addEventListener("click", (e) => copy(pageUrl(), e.currentTarget, "Copy link to this view")); function copy(text, btn, label) { const done = () => { btn.textContent = "Copied"; setTimeout(() => (btn.textContent = label), 1300); }; if (navigator.clipboard) navigator.clipboard.writeText(text).then(done).catch(() => prompt("Copy this link", text)); else prompt("Copy this link", text); } /* ------------------------------------------------------------ list */ const ICON = { star: '', starOn: '', hide: '', show: '', copy: '', ok: '', }; function ring(i) { const r = 20, c = 2 * Math.PI * r, f = c * i.score / 100; const t = `Score ${i.score}` + row(`var(--b-${i.band})`, BAND_LABEL[i.band] + " band", "") + (i.learned ? row("", "Learned model", i.score) : "") + row("", "Keywords", i.keyword) + (i.semantic != null ? row("", "Meaning", i.semantic) : "") + (i.match != null ? row("", "Match to your search", i.match) : ""); return `
${i.score}
`; } function itemHTML(i) { const isNew = S.lastVisit && (i.first_seen || "") > S.lastVisit; const meta = [ `${esc(SRC_LABEL[i.source] || i.source)}`, `${esc(KIND[i.kind] || i.kind)}${i.state ? " · " + esc(i.state) : ""}${i.category ? " · " + esc(i.category) : ""}`, `${esc(dm(D(i.date)))} (${esc(ago(i.date))})`, i.who ? `${OPENED[i.kind] ? OPENED[i.kind] + " " : ""}${esc(clip(byline(i), 80))}` : "", i.stars != null ? `${fmtN(i.stars)} stars` : "", i.likes ? `${fmtN(i.likes)} likes` : "", i.downloads ? `${fmtN(i.downloads)} downloads` : "", i.upvotes ? `${fmtN(i.upvotes)} upvotes` : "", i.comments ? `${fmtN(i.comments)} comments` : "", i.language ? `${esc(i.language)}` : "", i.license ? licenseTag(i.license) : "", isNew ? `new` : "", i.code ? `code` : "", i.new_repo ? `created in window` : "", i.updated ? `updated` : "", i.prerelease ? `pre-release` : "", !i.learned && i.semantic != null && i.score > i.keyword ? `by meaning` : "", ].filter(Boolean).join(""); const byMeaning = new Set(i.topics_by_meaning || []); const links = [ `Open`, i.pdf ? `PDF` : "", i.hf_url ? `Hugging Face papers` : "", i.doi_url ? `DOI` : "", i.discussion_url ? `Discussion` : "", ...(i.code || []).map((u) => `Code: ${esc(u.replace(/^https?:\/\/(www\.)?/, "").slice(0, 48))}`), ].filter(Boolean).join(""); const starred = S.stars.has(i.id), hidden = S.hide.has(i.id), open = S.open.has(i.id), like = S.like.has(i.id); return `
${ring(i)}
${meta}
${updateHTML(i)} ${i.summary ? `

${i.activity && i.activity.who ? `${esc(cap(OPENED[i.kind] || "posted by"))} ${esc(i.who_name || i.who || "")}: ` : ""}${esc(i.summary)}

` : ""}
${i.topics.map((t) => ``).join("")}
${open ? whyHTML(i) : ""}
`; } const cap = (t) => t.charAt(0).toUpperCase() + t.slice(1); const SEE = { comment: "the comment", review: "the review", commit: "the commit" }; function updateHTML(i) { const a = i.activity; if (!a || !a.who) return ""; const what = cap(a.what); const kind = /comment/.test(a.what) ? "comment" : /review|approved|changes requested/.test(a.what) ? "review" : a.what === "new commits" ? "commit" : ""; const also = (a.also || []).length ? ` · also: ${esc(a.also.join(", "))}` : ""; const link = a.url ? ` · See ${SEE[kind] || "it"}` : ""; return `
Latest update${esc(what)} by ${esc(a.who_name || a.who)} · ${esc(ago(a.date))}${also}${link}` + (a.text ? `

${esc(a.text)}

` : "") + `
`; } function whyHTML(i) { const parts = i.parts || []; const max = Math.max(20, ...parts.map((p) => Math.abs(p[1]))); const rows = parts.map(([l, p]) => `
${esc(l)}${p > 0 ? "+" : ""}${p}
`).join(""); const mean = i.semantic != null ? `Meaning ${i.semantic} (cosine ${i.sim} to "${esc(i.near || "")}")` : `Meaning: not scored${i.source === "pypi" ? " (package versions are scored by the package)" : ""}`; return `

Why this score

${rows || '

No term matched; the score comes from meaning alone.

'}
Keywords ${i.keyword}${mean}Final ${i.score} (${esc(BAND_LABEL[i.band])})
${i.learned ? `

The final score comes from the learned model, fitted to blind ratings of what items are worth to scroll work; it reads the ${i.learned === "full" ? "text's meaning, " : ""}keyword score, source, kind and popularity. The keyword points above explain the match; they no longer set the score.

` : ""}${i.capped ? `

Capped at ${i.score}: ${esc(i.capped)}.

` : ""}`; } function visible() { let items = S.items.filter((i) => S.f.showHidden || !S.hide.has(i.id)); if (S.f.newOnly && S.lastVisit) items = items.filter((i) => (i.first_seen || "") > S.lastVisit); return items; } function renderList() { const items = visible(); const hiddenHere = S.items.length - S.items.filter((i) => !S.hide.has(i.id)).length; $("#count").innerHTML = `${fmtN(S.matched)} items match` + (S.byMeaning ? ", closest in meaning to your words first" : "") + (hiddenHere && !S.f.showHidden ? ` · ${fmtN(hiddenHere)} hidden` : ""); if (!items.length) { const short = /^(1|5|15|30)m$/.test(S.f.since); $("#list").innerHTML = `
Nothing here yet.${short ? "Short windows are often empty: papers arrive once a day and the collector checks GitHub and Hugging Face every 20 minutes. Try " : "Lower the minimum score, widen the lookback or "}
`; $("#pager").innerHTML = ""; return; } $("#list").innerHTML = items.map(itemHTML).join(""); $("#pager").innerHTML = pagerHTML(); $$("[data-similar]:not([hidden])").forEach((d) => loadSims(d.closest(".item").dataset.id, d)); } $("#pager").addEventListener("click", (e) => { const b = e.target.closest("[data-page]"); if (b && !b.disabled) goPage(+b.dataset.page).catch(fail); }); $("#list").addEventListener("click", (e) => { const q = e.target.closest("[data-quick]"); if (q) { if (q.dataset.quick === "1d") { S.f.since = "1d"; renderLookback(); } else if (q.dataset.quick === "found") { S.f.by = "found"; renderLookback(); } else { const keep = { view: S.f.view, since: S.f.since }; S.f = Object.assign(defaults(), keep); } reload(); return; } const card = e.target.closest(".item"); if (!card) return; const id = card.dataset.id, item = S.items.find((x) => x.id === id); const t = e.target.closest("[data-topic]"); if (t) { if (!S.f.topics.includes(t.dataset.topic)) toggle("topics", t.dataset.topic); return; } if (e.target.closest(".summary, .usaid")) { e.target.closest(".summary, .usaid").classList.toggle("open"); return; } const a = e.target.closest("[data-act]"); if (!a || !item) return; const act = a.dataset.act; if (act === "star") { S.stars.has(id) ? S.stars.delete(id) : S.stars.add(id); store.set("stars", Array.from(S.stars)); renderFilters(); renderList(); } if (act === "hide") { S.hide.has(id) ? S.hide.delete(id) : S.hide.add(id); store.set("hidden", Array.from(S.hide)); renderFilters(); renderList(); } if (act === "copy") { (navigator.clipboard ? navigator.clipboard.writeText(item.url) : Promise.reject()).then(() => { a.innerHTML = ICON.ok; setTimeout(() => (a.innerHTML = ICON.copy), 1200); }).catch(() => {}); } // Each button opens and closes its own panel: the score's breakdown, or the nearest items in meaning. if (act === "why") { const d = card.querySelector("[data-detail]"); if (d.hidden) { S.open.add(id); d.innerHTML = whyHTML(item); d.hidden = false; } else { S.open.delete(id); d.hidden = true; } a.setAttribute("aria-expanded", String(!d.hidden)); } if (act === "like") { const d = card.querySelector("[data-similar]"); if (d.hidden) { S.like.add(id); d.hidden = false; loadSims(id, d, true); } else { S.like.delete(id); d.hidden = true; } a.setAttribute("aria-expanded", String(!d.hidden)); } }); const simCache = {}; async function loadSims(id, box, scroll) { box.innerHTML = '

More like this

Finding the closest items in meaning...

'; try { const it = simCache[id] || (simCache[id] = (await getJSON("api/item?id=" + encodeURIComponent(id))).body); const sims = it.similar || []; box.innerHTML = `

More like this

` + (sims.length ? `` : '

The meaning model is not loaded yet, so there are no neighbors to show.

'); if (scroll) box.scrollIntoView({ block: "nearest", behavior: "smooth" }); } catch (e) { box.innerHTML = '

Could not load similar items: ' + esc(e.message) + "

"; } } let rz; addEventListener("resize", () => { clearTimeout(rz); rz = setTimeout(() => { if (S.ov) { chartTime(); chartTopics(); chartScores(); chartSources(); chartMatrix(); chartPeople(); } }, 150); }); function renderAll() { renderViews(); renderLookback(); renderKpis(); renderFilters(); chartTime(); chartTopics(); chartScores(); chartSources(); chartMatrix(); chartPeople(); renderList(); } /* ------------------------------------------------------------ briefing */ async function loadBriefing() { const p = viewParams({ min_score: Math.max(35, S.f.min), per_topic: 3 }); const { body: b } = await getJSON("api/briefing?" + p); $("#briefWhen").textContent = `Last ${lbLabel(S.f.since)} · ${stamp(b.since)} to now`; $("#briefTitle").textContent = `Briefing for ${b.label}`; $("#briefAbout").textContent = b.about; const ov = S.ov || { items: 0, high: 0, with_code: 0, new_repos: 0 }; $("#briefStats").innerHTML = [[b.items, "items score 35+"], [ov.high, "high relevance"], [ov.with_code + ov.new_repos, "with code or new repos"]].map(([n, l]) => `
${fmtN(n)}${l}
`).join(""); const topics = Object.entries(b.topics); $("#briefGrid").innerHTML = topics.length ? topics.map(([t, items]) => `

${esc(t)}${fmtN((S.ov && S.ov.by_topic[t]) || items.length)} in view

${items.map((i) => `
${i.score}${esc(i.title)} ${esc(SRC_LABEL[i.source] || i.source)} · ${esc(KIND[i.kind] || i.kind)} · ${esc(ago(i.date))}${i.activity && i.activity.who ? " · " + esc(i.activity.what) + " by " + esc(i.activity.who_name || i.activity.who) : i.who ? " · " + esc(i.who_name || i.who.split(",")[0]) : ""}${i.code ? ' · code' : ""}
`).join("")}
`).join("") : `
Nothing scores 35 or more in this window.Widen the lookback above, or switch to Everyone.
`; } /* ------------------------------------------------------------ sources tab */ const SRC_ABOUT = { arxiv: (c) => [`Today's announcement for ${c.rss.join(", ")}, with abstracts`, `Past-week lists of ${c.pastweek.join(", ")} once a day, to catch up after sleep`, "A search of every category for scroll terms"], github: (c) => [`Repository searches: ${c.queries.join("; ")}`, `Pull requests, issues and releases of ${c.watch_repos.join(", ")}`, "Logged out; unchanged listings answered from cache"], hf: (c) => [`Models, datasets and Spaces matching ${c.search.join(", ")}`, `Everything by ${c.authors.join(", ")}`, "Daily papers, merged with their arXiv rows"], pypi: (c) => [`New versions of ${Object.keys(c.packages).length} packages: ${Object.keys(c.packages).join(", ")}`, "Pre-releases score 10 lower"], zenodo: (c) => [`Records matching ${c.queries.join("; ")}`, "Datasets, software, preprints and articles"], journals: (c) => [`Crossref works created in the window matching ${c.queries.join("; ")}`, "Journal articles, chapters, proceedings and posted content"], blog: (c) => [`The feeds of ${[...new Set(c.feeds.map((f) => f.name || f[0]))].join(", ")}`, "News searches go through the scorer like any listing, and a story found twice is kept once"], }; function renderSources() { const b = S.src; if (!b) return; $("#srcTable tbody").innerHTML = SRC.map((k) => { const v = b.sources[k]; return `${esc(v.label)}${v.every_min < 60 ? v.every_min + " min" : v.every_min / 60 + " h"} ${v.last_check ? esc(ago(v.last_check)) : "not yet"}${v.state === "running" ? "now" : esc(until(v.next_check))} ${esc(v.state)}${fmtN(v.read)}${fmtN(v.kept)} ${fmtN(v.new)}${fmtN(v.items_stored)}${v.seconds} s`; }).join(""); $("#runTable tbody").innerHTML = (b.runs || []).slice(0, 40).map((r) => `${esc(stamp(r.finished))}${esc(SRC_LABEL[r.source] || r.source)} ${esc(r.state)}${fmtN(r.read)}${fmtN(r.kept)}${fmtN(r.new)} ${(r.ms / 1000).toFixed(1)} s${esc((r.notes || []).join("; "))}`).join("") || `No checks yet since the Space started.`; if (S.cfg && !$("#srcCards").children.length) { $("#srcCards").innerHTML = SRC.map((k) => `

${esc(SRC_LABEL[k])}

`).join(""); } } /* ------------------------------------------------------------ connect + how */ function renderLinks() { const base = location.origin + location.pathname.replace(/[^/]*$/, ""); $("#lnkJson").textContent = base + "api/items?" + viewParams({ ids: null, limit: 100 }); $("#lnkCsv").textContent = base + "api/export.csv?" + viewParams({ ids: null }); $("#lnkRss").textContent = base + "feed.xml?" + viewParams({ ids: null, by_meaning: null, q: null, min_score: Math.max(S.f.min, 35) }); $("#lnkPage").textContent = pageUrl(); $("#dlCsv").href = "api/export.csv?" + viewParams(); $("#dlJson").href = "api/items?" + viewParams({ limit: 100 }); $("#dlRss").href = "feed.xml?" + viewParams({ ids: null, by_meaning: null, q: null, min_score: Math.max(S.f.min, 35) }); } document.addEventListener("click", (e) => { const b = e.target.closest("[data-copy]"); if (b) copy($("#" + b.dataset.copy).textContent, b, "Copy"); }); async function loadConfig() { const [{ body: c }, { body: v }] = await Promise.all([getJSON("api/config"), getJSON("api/views")]); S.cfg = c; S.views = v; renderViews(); $("#howModel").textContent = c.semantic.model; $("#howTopics").innerHTML = `

Scroll terms+${c.core.weight}

${c.core.terms.map(esc).join(", ")}

` + Object.entries(c.topics).map(([n, t]) => `

${esc(n)}+${t.weight} strong, +${Math.floor(t.weight / 2)} weak

strong: ${t.strong.map(esc).join(", ")}
weak: ${t.weak.map(esc).join(", ")}

`).join("") + `

Context terms+${c.context_bonus}

${c.context.map(esc).join(", ")}

` + `

Off-topic terms-15 each, up to 2

${c.negative.map(esc).join(", ")}

` + (c.learned && c.learned.ready ? `

Learned modelrank agreement ${c.learned.evaluation.full.spearman} on held-out items

Ridge regression on the embedding plus ${esc(c.learned.meta_features.join(", "))}, source and kind; fitted ${esc(c.learned.trained)} to blind ratings of ${fmtN(c.learned.items)} items, ${esc(c.learned.calibration)} calibration to 0 to 100. The keyword and meaning scores alone agreed ${c.learned.evaluation.rule_score} with the same ratings.

` : "") + `

Meaning anchorscosine ${c.semantic.sim_floor} to ${c.semantic.sim_ceiling} maps to 0 to 100

${Object.values(c.semantic.anchors).flat().map(esc).join("
")}

`; $("#howSources").innerHTML = SRC.map((k) => `
  • ${esc(SRC_LABEL[k])} (every ${c.sources[k].every_min < 60 ? c.sources[k].every_min + " minutes" : c.sources[k].every_min / 60 + " hours"}): ${esc(SRC_ABOUT[k](c.sources[k]).join("; "))}.
  • `).join(""); renderSources(); } /* ------------------------------------------------------------ tabs, theme */ function selectTab(name) { $$(".tabs button").forEach((b) => b.setAttribute("aria-selected", String(b.dataset.tab === name))); $$(".panel").forEach((p) => (p.hidden = p.dataset.panel !== name)); if (name === "briefing") loadBriefing().catch(fail); if (name === "sources") renderSources(); if (name === "connect") renderLinks(); try { history.replaceState(null, "", location.pathname + location.search + "#" + name); } catch (e) { /* sandboxed */ } } $$(".tabs button").forEach((b) => b.addEventListener("click", () => selectTab(b.dataset.tab))); /* The theme is kept in this browser; "auto" follows Hugging Face's own theme (?__theme=, read in index.html before first paint), else the system. */ function applyTheme(choice) { const d = document.documentElement, show = choice === "auto" ? d.dataset.hfTheme : choice; if (show) d.dataset.theme = show; else delete d.dataset.theme; d.dataset.themeChoice = choice; $$("[data-theme-set]").forEach((b) => b.setAttribute("aria-pressed", String(b.dataset.themeSet === choice))); } function setTheme(t) { applyTheme(t); try { localStorage.setItem("vradar.theme", t === "auto" ? "" : t); } catch (e) { /* ignore */ } drawScene(); } $$("[data-theme-set]").forEach((b) => b.addEventListener("click", () => setTheme(b.dataset.themeSet))); applyTheme(document.documentElement.dataset.themeChoice || "auto"); /* ------------------------------------------------------------ hero: Vesuvius over the bay, with a radar sweep */ // A drawn scene, not a photo: the Bay of Naples, Vesuvius (Monte Somma and the Gran Cono), Pliny's pine-shaped // plume, Herculaneum's colonnades and stone pines on the shore, and radar rings from the crater. Each blip is a // source that brought items in the last day, in that source's color. Deterministic, so it never jumps on reload. const HERO = { blips: [] }; const sceneTheme = () => { const t = document.documentElement.dataset.theme; return t === "light" || t === "dark" || t === "vesuvius" ? t : (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); }; const PAL = { light: { sky: ["#cfe0f3", "#eef0f0", "#f8e3c8"], sun: "#fff3d6", sunO: .95, ring: "#2a78d6", ringO: .22, sweep: "#2a78d6", sweepO: .16, far: "#b9c6d6", shade: "#8d8f9e", lit: "#b4b3b9", gully: "#6d6e7d", smoke: "#9b9aa6", smokeO: .34, crater: null, sea: ["#9cc0de", "#7ea9cf"], shim: "#ffffff", land: "#7f9a6a", land2: "#5f7d4f", wall: "#efe4d1", roof: "#c9714a", col: "#d8cbb3", pine: "#3f5a35", trunk: "#5b4636" }, dark: { sky: ["#0a1220", "#152036", "#2a2b3d"], sun: "#c9d6ff", sunO: .35, ring: "#86b6ef", ringO: .22, sweep: "#3987e5", sweepO: .22, far: "#1e2738", shade: "#141a26", lit: "#232b3b", gully: "#0d121b", smoke: "#6b7385", smokeO: .22, crater: "#ff8a4c", sea: ["#13233a", "#0d1a2c"], shim: "#9fc3ff", land: "#1a2419", land2: "#121a12", wall: "#3a3f4c", roof: "#5a3a33", col: "#4a4f5c", pine: "#0f1a12", trunk: "#1b1712" }, vesuvius: { sky: ["#120b0a", "#2a120b", "#6a250d"], sun: "#ff9a4a", sunO: .45, ring: "#ffa070", ringO: .2, sweep: "#ff6a2b", sweepO: .28, far: "#2c1712", shade: "#1c110e", lit: "#2f1d17", gully: "#120a08", smoke: "#4a3a34", smokeO: .55, crater: "#ff6a2b", sea: ["#2a1410", "#1a0d0a"], shim: "#ff9a5a", land: "#20140f", land2: "#160d0a", wall: "#3a2a22", roof: "#6e2e14", col: "#4b3529", pine: "#120a07", trunk: "#1a100b" }, }; function drawScene() { const svg = $("#scene"); if (!svg) return; const P = PAL[sceneTheme()], W = 1600, cx = 1162, cy = 292; let seed = 7; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647; const f = (v) => v.toFixed(1); const o = []; o.push(` `); o.push(``); o.push(``); o.push(''); // radar rings and cross hairs from the crater, behind the mountain const R = [110, 210, 310, 410, 510]; R.forEach((r, i) => o.push(``)); o.push(``); // the sweep: thin wedges that fade behind the leading edge let sw = ""; for (let k = 0; k < 14; k++) { const a0 = (-90 - k * 4) * Math.PI / 180, a1 = (-90 - (k + 1) * 4) * Math.PI / 180, r = 520; sw += ``; } sw += ``; o.push(`${sw}`); // blips: one per source with items in the last day (two for the busiest), placed in the sky inside the rings HERO.blips.forEach((b, i) => { let x, y, tries = 0; do { const a = (-165 + rnd() * 150) * Math.PI / 180, r = 130 + rnd() * 300; x = cx + r * Math.cos(a); y = cy + r * Math.sin(a); tries++; } while ((y > 250 || y < 20 || x < 830 || Math.abs(x - cx - 30) < 90 && y < cy - 120) && tries < 60); const d = f((i * 0.63) % 4.5); o.push(``); o.push(``); }); // Pliny's plume: a column that spreads like a stone pine let pl = ``; for (let k = 0; k < 16; k++) { const u = k / 15, px = cx + 22 + (u - .5) * 280 + (rnd() - .5) * 30 + u * 40, py = cy - 170 - Math.sin(u * Math.PI) * 40 + (rnd() - .5) * 16; pl += ``; } o.push(`${pl}`); // the far Apennines o.push(``); // Vesuvius: Monte Somma's broken rim on the left, the Gran Cono in the middle, one light from the right const mtn = `M560 700 C700 650 800 560 880 480 C920 440 950 420 985 404 L1004 416 L1026 392 L1046 404 L1068 372 L1090 362 L1104 338 L1124 304 L1140 294 L1186 290 L1204 300 C1240 340 1300 400 1380 460 C1460 520 1540 560 1600 584 L1600 760 L560 760Z`; o.push(``); o.push(``); // gullies down the cone for (let k = 0; k < 16; k++) { const t = rnd(), sy = 320 + rnd() * 110, half = (sy - 290) * (1.05 + t * .35), sx = cx + 10 + (t - .5) * 2 * half; const len = 60 + rnd() * 150, dx = (sx - cx - 10) * len / Math.max(60, sy - 280) * .9; o.push(``); } // haze across the lower slopes gives the mountain distance o.push(``); if (P.crater) { o.push(``); if (sceneTheme() === "vesuvius") for (let k = 0; k < 3; k++) { const sx = cx - 8 + k * 14; o.push(``); } } // eruption layer (filled on a click in the Vesuvius theme) and the click target over the mountain o.push(`Vesuvius`); // the bay o.push(``); for (let k = 0; k < 26; k++) { const x = 640 + rnd() * 900, y = 664 + rnd() * 84, w = 16 + rnd() * 44; o.push(``); } // the shore at Herculaneum: a terrace, houses with tiled roofs, a colonnade o.push(``); const houses = [[880, 618, 54, 26], [940, 610, 38, 34], [986, 622, 60, 22], [1300, 614, 46, 30], [1352, 620, 58, 24]]; houses.forEach(([x, y, w, h]) => o.push(``)); o.push(``); for (let k = 0; k < 12; k++) o.push(``); o.push(``); // stone pines in front const pine = (x, y, s) => { let g = ``; for (let k = 0; k < 7; k++) g += ``; return g; }; o.push(``); o.push(pine(1470, 700, 1.15) + pine(1560, 690, .95) + pine(845, 668, .62) + pine(1215, 652, .5)); o.push(""); svg.innerHTML = o.join(""); } async function heroStats() { try { const [d1, d7] = await Promise.all([getJSON("api/overview?since=1d"), getJSON("api/overview?since=7d")]); const by = d1.body.by_source || {}; const ranked = SRC.filter((k) => by[k]).sort((a, b) => by[b] - by[a]); HERO.blips = ranked.concat(ranked.slice(0, 3)).concat(ranked.slice(0, 1)); drawScene(); const c = d7.body.community || {}; const stats = [[fmtN(d1.body.items), "new in the last day"], [fmtN(d7.body.high), "high scores this week"], [fmtN(c.new_scroll_repos || 0), "new scroll repositories this week"], [fmtN(c.villa_merged || 0), "villa pull requests merged this week"]]; $("#heroStats").innerHTML = stats.map(([b, l]) => `
    ${b}${l}
    `).join(""); } catch (e) { /* the hero keeps its drawing; the stats wait for the next try */ } } matchMedia("(prefers-color-scheme: dark)").addEventListener("change", drawScene); $$("[data-go]").forEach((a) => a.addEventListener("click", (e) => { e.preventDefault(); const tab = a.dataset.go; if (tab === "radar" && S.f.since !== "1d") { S.f.since = "1d"; renderLookback(); reload(0); } selectTab(tab); $(".tabs").scrollIntoView({ behavior: matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth", block: "start" }); })); /* ------------------------------------------------------------ easter eggs */ // 1. In the Vesuvius theme, click the volcano: it erupts (lava fountain, ash column, flows, volcanic lightning). // 2. The Konami code (up up down down left right left right B A): every source's color goes up as fireworks. // 3. Type "pliny" in the search box: Pliny the Younger's line on the pine-shaped cloud, and the plume swells. const reduce = () => matchMedia("(prefers-reduced-motion: reduce)").matches; let toastTimer; function toast(html, ms) { const t = $("#toast"); t.innerHTML = html; t.hidden = false; t.classList.remove("out"); clearTimeout(toastTimer); toastTimer = setTimeout(() => { t.classList.add("out"); setTimeout(() => { t.hidden = true; }, 600); }, ms || 6500); } const egg = { busy: false, erupt() { if (egg.busy || sceneTheme() !== "vesuvius") return; const g = $("#erupt"); if (!g) return; egg.busy = true; const cx = 1162, cy = 292, o = []; let seed = 79; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647; const LAVA = ["#ffd27a", "#ffb347", "#ff8a3d", "#ff6a2b", "#ff3b2f", "#ffe9a8"]; o.push(``); // the ash column: puffs that rise, spread and drift with the wind for (let k = 0; k < 26; k++) { const r = 30 + rnd() * 40, d = (k * 0.16).toFixed(2), dx = Math.round(-60 + rnd() * 160 + k * 4), dy = Math.round(-200 - rnd() * 260); o.push(``); } // volcanic lightning inside the column [[cx - 40, cy - 250, "#c9b6ff"], [cx + 70, cy - 320, "#9fd0ff"], [cx + 10, cy - 200, "#ffd1f0"]].forEach(([x, y, col], k) => { let d = `M${x} ${y}`; for (let j = 0; j < 5; j++) d += ` l${Math.round(-18 + rnd() * 36)} ${Math.round(14 + rnd() * 16)}`; o.push(``); }); // the lava fountain: glowing bombs thrown in arcs for (let k = 0; k < 46; k++) { const ang = (-90 + (rnd() - 0.5) * 70) * Math.PI / 180, v = 120 + rnd() * 230; const dx = Math.round(Math.cos(ang) * v * 1.4), up = Math.round(Math.sin(ang) * v), down = Math.round(160 + rnd() * 120); o.push(``); } // lava flows down the cone [[-10, 260, 0.6], [18, 300, 1.1], [44, 240, 1.7], [-34, 220, 2.3]].forEach(([off, len, d]) => { o.push(``); }); g.innerHTML = o.join(""); $(".hero").classList.add("erupting"); toast("Vesuvius erupts. Herculaneum's library lay under the ash for about 1,700 years; the radar is here to help read it.", 7000); setTimeout(() => { g.innerHTML = ""; $(".hero").classList.remove("erupting"); egg.busy = false; }, reduce() ? 2500 : 11000); }, fireworks() { const svg = $("#scene"); if (!svg) return; const box = svg.viewBox.baseVal, o = []; let seed = 4; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647; SRC.concat(SRC).forEach((k, n) => { const x = box.width * (0.45 + rnd() * 0.5), y = 60 + rnd() * 260, d = (n * 0.45).toFixed(2); for (let j = 0; j < 12; j++) { const a = j / 12 * 2 * Math.PI, r = 50 + rnd() * 30; o.push(``); } }); const g = el("g", { class: "e-fire" }); g.innerHTML = o.join(""); svg.appendChild(g); document.body.classList.add("party"); toast("ΧΑΙΡΕ! (Greek for hello.) Seven sources, seven colors.", 6500); setTimeout(() => { g.remove(); document.body.classList.remove("party"); }, reduce() ? 2000 : 9000); }, typed(q) { if (q.trim().toLowerCase() !== "pliny") return; $(".hero").classList.add("plinian"); setTimeout(() => $(".hero").classList.remove("plinian"), 9000); toast('"Nubes... oriebatur, cuius similitudinem et formam non alia magis arbor quam pinus expresserit." No tree shows the shape of the cloud over Vesuvius better than a pine: Pliny the Younger, Letters 6.16.', 9000); }, }; $("#scene").addEventListener("click", (e) => { if (e.target.closest(".vhit")) egg.erupt(); }); const KONAMI = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; let kpos = 0; document.addEventListener("keydown", (e) => { const k = e.key.length === 1 ? e.key.toLowerCase() : e.key; kpos = k === KONAMI[kpos] ? kpos + 1 : (k === KONAMI[0] ? 1 : 0); if (kpos === KONAMI.length) { kpos = 0; egg.fireworks(); } }); /* ------------------------------------------------------------ start */ const startTab = (location.hash || "#radar").slice(1); renderViews(); renderLookback(); selectTab(["radar", "briefing", "sources", "connect", "how"].includes(startTab) ? startTab : "radar"); loadConfig().catch(() => { /* the page still works without it */ }); drawScene(); load().catch(fail); pollSources(); // A public helper for people driving the page from the console or a test. window.vesuviusRadar = { state: () => JSON.parse(JSON.stringify(S.f)), set: (f) => { Object.assign(S.f, f); renderViews(); renderLookback(); return load(); }, erupt: () => egg.erupt(), fireworks: () => egg.fireworks() }; })();