about / index.html
rishhh's picture
Upload 13 files
77a43a4 verified
Raw History Blame Contribute Delete
26.8 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rishabh Patil: models, data and research</title>
<meta name="description" content="AI engineer and researcher in London. Co-founder and CEO of Valuren. Claim verification, safe text-to-SQL, LLM evaluation and world models, with live download numbers from the Hugging Face Hub.">
<style>
:root {
color-scheme: light;
--bg: #ffffff; --surface: #f6f8fa; --panel: #ffffff; --border: #d0d7de;
--ink: #1f2328; --ink2: #59636e; --muted: #6e7781; --grid: #d8dee4; --track: #cde2fb;
--s1: #2a78d6; --s2: #eb6834; --s3: #1baf7a; --other: #8c959f;
--goodtext: #006300; --good: #0ca30c; --link: #0969da; --focus: #2a78d6;
--shadow: 0 8px 24px rgba(31, 35, 40, 0.12);
}
@media (prefers-color-scheme: dark) {
:root:where(:not([data-theme="light"])) {
color-scheme: dark;
--bg: #0d1117; --surface: #161b22; --panel: #0d1117; --border: #30363d;
--ink: #f0f6fc; --ink2: #9198a1; --muted: #848d97; --grid: #30363d; --track: #104281;
--s1: #3987e5; --s2: #d95926; --s3: #199e70; --other: #6e7781;
--goodtext: #0ca30c; --good: #0ca30c; --link: #4493f8; --focus: #3987e5;
--shadow: 0 8px 24px rgba(1, 4, 9, 0.6);
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--bg: #0d1117; --surface: #161b22; --panel: #0d1117; --border: #30363d;
--ink: #f0f6fc; --ink2: #9198a1; --muted: #848d97; --grid: #30363d; --track: #104281;
--s1: #3987e5; --s2: #d95926; --s3: #199e70; --other: #6e7781;
--goodtext: #0ca30c; --good: #0ca30c; --link: #4493f8; --focus: #3987e5;
--shadow: 0 8px 24px rgba(1, 4, 9, 0.6);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0; background: var(--bg); color: var(--ink);
font: 16px/1.6 system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
main { max-width: 880px; margin: 0 auto; padding: 24px 16px 56px; }
a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible, [tabindex]:focus-visible, summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 6px; }
h2 { font-size: 1.3rem; line-height: 1.3; margin: 44px 0 6px; }
h3 { font-size: 1.05rem; margin: 0 0 4px; }
p { margin: 0 0 12px; }
.art { display: block; width: 100%; height: auto; }
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 18px; padding: 0; list-style: none; }
.chips a {
display: inline-block; padding: 5px 12px; border: 1px solid var(--border); border-radius: 999px;
color: var(--ink); background: var(--surface); font-size: 0.9rem;
}
.chips a:hover { text-decoration: none; border-color: var(--ink2); }
.lead { color: var(--ink2); font-size: 1.04rem; }
.card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 22px 22px 18px; }
.cardhead { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 4px 16px; margin-bottom: 12px; }
.cardhead h3 { margin: 0; font-size: 1rem; }
.status { color: var(--muted); font-size: 0.82rem; }
.status .dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; background: var(--muted); vertical-align: 1px; }
.status.live .dot { background: var(--good); }
.kpis { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 16px; align-items: end; }
.hero .value { font-size: 3rem; line-height: 1.05; font-weight: 600; letter-spacing: -0.5px; }
.tile .value { font-size: 1.75rem; line-height: 1.1; font-weight: 600; }
.label { color: var(--ink2); font-size: 0.92rem; }
.delta { font-size: 0.9rem; color: var(--ink2); margin-top: 2px; }
.delta strong { color: var(--goodtext); }
.delta svg { vertical-align: -1px; margin-right: 4px; }
.rule { border: 0; border-top: 1px solid var(--grid); margin: 20px 0 16px; }
.legend { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 0 0 10px; padding: 0; list-style: none; font-size: 0.85rem; color: var(--ink2); }
.legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; vertical-align: -0.5px; }
.chart { position: relative; }
.row { padding: 4px 0 6px; border-radius: 6px; cursor: default; }
.row:hover .bar, .row:focus-visible .bar { filter: brightness(1.12); }
.row .name { font-size: 0.84rem; color: var(--ink2); overflow-wrap: anywhere; line-height: 1.35; }
.row .name .kind { color: var(--muted); }
.barline { display: flex; align-items: center; gap: 8px; height: 16px; }
.bar { height: 12px; border-radius: 0 4px 4px 0; flex: none; min-width: 3px; }
.val { font-size: 0.84rem; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.c-VeriSci { background: var(--s1); } .c-SchemaSage { background: var(--s2); } .c-Other { background: var(--other); }
.tooltip {
position: absolute; z-index: 5; pointer-events: none; min-width: 180px; max-width: 280px;
background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow);
padding: 10px 12px; font-size: 0.84rem; opacity: 0; transition: opacity 0.12s;
}
.tooltip.on { opacity: 1; }
.tooltip .tv { font-size: 1.05rem; font-weight: 600; color: var(--ink); }
.tooltip .tl { color: var(--ink2); }
.tooltip .key { display: inline-block; width: 12px; height: 2px; margin-right: 6px; vertical-align: 3px; }
details { margin-top: 12px; }
summary { cursor: pointer; color: var(--ink2); font-size: 0.9rem; }
.table-wrap { overflow-x: auto; margin-top: 10px; border: 1px solid var(--border); border-radius: 10px; }
table { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--grid); vertical-align: top; }
th { background: var(--panel); font-weight: 600; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
tr:last-child td { border-bottom: 0; }
.links { margin: 10px 0 0; padding-left: 20px; color: var(--ink2); }
.links li { margin: 2px 0; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 12px; }
.project { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.project .fig { width: 100%; height: auto; aspect-ratio: 16 / 10; object-fit: contain; border-radius: 8px; border: 1px solid var(--border); background: #ffffff; }
.project p { color: var(--ink2); font-size: 0.92rem; margin: 0; }
.badges { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.badges img { height: 20px; }
.compact { list-style: none; padding: 0; margin: 16px 0 0; display: grid; gap: 10px; }
.compact li { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 12px 16px; }
.compact .meta { color: var(--ink2); font-size: 0.92rem; }
footer { margin-top: 48px; padding-top: 16px; border-top: 1px solid var(--grid); color: var(--muted); font-size: 0.85rem; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (max-width: 720px) {
.kpis { grid-template-columns: 1fr 1fr 1fr; }
.hero { grid-column: 1 / -1; }
.hero .value { font-size: 2.5rem; }
.grid { grid-template-columns: 1fr; }
.card { padding: 18px 16px 14px; }
}
@media (prefers-reduced-motion: reduce) { .tooltip { transition: none; } }
</style>
</head>
<body>
<main>
<header>
<img class="art" data-art="banner" src="assets/banner-auto.svg" width="1000" height="250"
alt="Rishabh Patil. AI engineer and researcher, co-founder and CEO of Valuren.">
<ul class="chips">
<li><a href="https://huggingface.co/rishhh" target="_blank" rel="noopener noreferrer">Hugging Face</a></li>
<li><a href="https://github.com/MrRobotop" target="_blank" rel="noopener noreferrer">GitHub</a></li>
<li><a href="https://www.linkedin.com/in/rishabh-ashok-patil/" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
<li><a href="https://orcid.org/0009-0007-0868-9673" target="_blank" rel="noopener noreferrer">ORCID</a></li>
<li><a href="https://www.rishabhpatil.com" target="_blank" rel="noopener noreferrer">Website</a></li>
<li><a href="mailto:rishabh.a.patil@outlook.com">Email</a></li>
</ul>
<p class="lead">AI engineer and researcher in London. Co-founder and CEO of <a href="https://www.linkedin.com/company/valuren/" target="_blank" rel="noopener noreferrer">Valuren</a>, which gives fashion and luxury brands digital product passports. I work on verification for AI: checking claims against evidence, evaluating LLM systems, and planning safely in learned latent spaces.</p>
</header>
<section aria-labelledby="hub-h">
<h2 id="hub-h">On the Hub</h2>
<div class="card">
<div class="cardhead">
<h3>Downloads</h3>
<span class="status" id="status"><span class="dot"></span><span id="status-text">Loading live numbers</span></span>
</div>
<div class="kpis">
<div class="hero">
<div class="value" id="k-total">2,788</div>
<div class="label">downloads, all time</div>
<div class="delta" id="k-delta"><svg width="12" height="10" viewBox="0 0 12 10" aria-hidden="true"><path d="M6 0l6 10H0z" fill="var(--good)"/></svg><strong id="k-d30">+308</strong> in the last 30 days</div>
</div>
<div class="tile"><div class="value" id="k-models">28</div><div class="label">models</div></div>
<div class="tile"><div class="value" id="k-datasets">1</div><div class="label" id="k-datasets-l">dataset</div></div>
<div class="tile"><div class="value" id="k-spaces">1</div><div class="label" id="k-spaces-l">Space</div></div>
</div>
<hr class="rule">
<div class="cardhead"><h3>Most downloaded repos</h3><span class="status">Hover or tab through the bars for 30-day numbers</span></div>
<ul class="legend" id="legend"></ul>
<div class="chart" id="chart" role="list" aria-label="Most downloaded repos, all time"></div>
<details>
<summary>Table view of every repo</summary>
<div class="table-wrap"><table>
<thead><tr><th>Repo</th><th>Type</th><th class="num">All time</th><th class="num">Last 30 days</th></tr></thead>
<tbody id="table"></tbody>
</table></div>
</details>
</div>
</section>
<section aria-labelledby="v-h">
<h2 id="v-h">VeriSci: scientific claim verification</h2>
<p>Paste a scientific claim and get SUPPORTS, REFUTES or NOT ENOUGH INFO, with the sentences that justify the label.</p>
<img class="art" data-art="verisci" src="assets/verisci-auto.svg" width="840" height="372"
alt="VeriSci pipeline: claim, retrieve with BM25 and a fine-tuned e5 retriever, select evidence sentences, verify with a calibrated DeBERTa model, return a label with evidence. Retriever Recall@5 0.867. Verifier accuracy 0.905, macro-F1 0.874, calibration error 0.024. End to end on SciFact validation: 0.691 accuracy, 0.653 macro-F1.">
<ul class="links">
<li><a href="https://huggingface.co/spaces/rishhh/verisci-claim-space" target="_blank" rel="noopener noreferrer">Live demo</a> (may take a minute to wake if idle)</li>
<li><a href="https://huggingface.co/rishhh/verisci-claim-verifier-retrieval-adapted-seed123" target="_blank" rel="noopener noreferrer">Release verifier</a> and <a href="https://huggingface.co/rishhh/verisci-scifact-e5-retriever" target="_blank" rel="noopener noreferrer">retriever</a></li>
<li>Seeded ablations of evidence selection, each with its own model card</li>
</ul>
</section>
<section aria-labelledby="s-h">
<h2 id="s-h">SchemaSage-SQL: safe text-to-SQL</h2>
<p>QLoRA adapters on Qwen3-4B that write read-only SQL grounded in the schema you give them, with a safety layer that refuses destructive requests.</p>
<img class="art" data-art="schemasage" src="assets/schemasage-auto.svg" width="840" height="246"
alt="SchemaSage-SQL release baseline on 64 cleaned held-out examples: 100% SQL parse validity, 98.3% schema adherence, 0% unsafe queries, 100% correct refusals. QLoRA on Qwen3-4B-Instruct-2507, trained on 111,444 cleaned examples, 10,862 of them refusals.">
<ul class="links">
<li><a href="https://huggingface.co/rishhh/schemasage-sql-qwen3-4b-clean-balanced-200" target="_blank" rel="noopener noreferrer">Release baseline</a>; the <a href="https://huggingface.co/rishhh/schemasage-sql-qwen3-4b-clean-balanced-8k-600-v2" target="_blank" rel="noopener noreferrer">larger candidate</a> scored higher on exact match and execution accuracy but missed one blocked refusal, so it was not shipped</li>
<li><a href="https://huggingface.co/datasets/rishhh/schemasage-sql-clean-text2sql" target="_blank" rel="noopener noreferrer">Dataset</a> and <a href="https://github.com/MrRobotop/schemasage-sql" target="_blank" rel="noopener noreferrer">code</a></li>
</ul>
</section>
<section aria-labelledby="r-h">
<h2 id="r-h">Research code</h2>
<p>Each library pairs installable code with a first-author preprint.</p>
<div class="grid">
<article class="project">
<img class="fig" src="assets/toploss-sasp.png" width="1600" height="589" loading="lazy"
alt="SASP results: test cross-entropy over 120 epochs against the baseline, and Hessian trace at convergence, about 171 for the baseline against about 100 with SASP.">
<h3><a href="https://github.com/MrRobotop/toploss" target="_blank" rel="noopener noreferrer">toploss</a></h3>
<p>Five optimiser-free PyTorch regularisers that rebuild SAM, cautious weight decay and AdEMAMix as loss penalties. SASP cut the Hessian trace 41% at +15% wall-clock, against +107% for SAM (three seeds, CPU).</p>
<div class="badges">
<img alt="PyPI version" src="https://img.shields.io/pypi/v/toploss?style=flat-square&amp;label=PyPI&amp;color=2a78d6">
<img alt="CI, live status" src="https://img.shields.io/github/actions/workflow/status/MrRobotop/toploss/ci.yaml?style=flat-square&amp;label=CI">
<a href="https://doi.org/10.5281/zenodo.20497841" target="_blank" rel="noopener noreferrer">Preprint</a>
</div>
</article>
<article class="project">
<img class="fig" src="assets/clap-planning.png" width="1280" height="877" loading="lazy"
alt="CLAP planning in a two-dimensional latent field: the trajectory reaches the safe target region and dwells there, avoiding a reward trap and an unsafe zone.">
<h3><a href="https://github.com/MrRobotop/clap-family" target="_blank" rel="noopener noreferrer">clap-family</a></h3>
<p>Conservative Lapse-Action Planning: reach the best safe region of a latent space, then stay there. Seven planner variants, 72 tests.</p>
<div class="badges">
<img alt="PyPI version" src="https://img.shields.io/pypi/v/clap-family?style=flat-square&amp;label=PyPI&amp;color=2a78d6">
<img alt="Checks, live status" src="https://img.shields.io/github/check-runs/MrRobotop/clap-family/HEAD?style=flat-square&amp;label=checks">
<a href="https://doi.org/10.5281/zenodo.20467271" target="_blank" rel="noopener noreferrer">Preprint</a>
</div>
</article>
</div>
<ul class="compact">
<li><a href="https://github.com/MrRobotop/micro-world-model" target="_blank" rel="noopener noreferrer"><strong>micro-world-model</strong></a> · <a href="https://doi.org/10.5281/zenodo.20480620" target="_blank" rel="noopener noreferrer">preprint</a><br><span class="meta">Hierarchical JEPA world model that plans in a 256-dimensional latent space with no reconstruction, no reward and no EMA target.</span></li>
<li><a href="https://github.com/MrRobotop/ariadne-eval" target="_blank" rel="noopener noreferrer"><strong>ariadne-eval</strong></a><br><span class="meta">Trajectory-level tracing and scoring for multi-step, tool-using agents. 285 tests, mypy-strict.</span></li>
<li><a href="https://github.com/MrRobotop/evalforge" target="_blank" rel="noopener noreferrer"><strong>evalforge</strong></a><br><span class="meta">LLM evaluation with bootstrapped confidence intervals, calibrated LLM-as-judge and a CI regression gate. 538 tests.</span></li>
</ul>
</section>
<section aria-labelledby="b-h">
<h2 id="b-h">Background</h2>
<p>MSc Artificial Intelligence, University of St Andrews (dissertation on dependent types for machine learning, supervised by Dr Edwin Brady). BSc Artificial Intelligence, VU Amsterdam. Before Valuren I co-founded RelAIable, an AI consultancy in Amsterdam, as Chief AI Officer.</p>
</section>
<footer>
<p id="foot">Download numbers come live from the Hugging Face Hub API. If it can't be reached, the page shows a snapshot from 8 Oct 2026.</p>
</footer>
</main>
<div class="tooltip" id="tip" role="status" aria-live="polite"></div>
<script>
(() => {
"use strict";
const USER = "rishhh";
const SNAPSHOT = {"date":"2026-10-08","dateLabel":"8 Oct 2026","spaces":1,"repos":[{"id":"rishhh/schemasage-sql-qwen3-4b-smoke","type":"model","all":3,"d30":0},{"id":"rishhh/schemasage-sql-qwen3-4b-longer-200","type":"model","all":57,"d30":4},{"id":"rishhh/schemasage-sql-qwen3-4b-clean-200","type":"model","all":43,"d30":5},{"id":"rishhh/schemasage-sql-qwen3-4b-clean-balanced-200","type":"model","all":132,"d30":6},{"id":"rishhh/schemasage-sql-qwen3-4b-clean-balanced-8k-600","type":"model","all":33,"d30":4},{"id":"rishhh/schemasage-sql-qwen3-4b-clean-balanced-8k-600-v2","type":"model","all":135,"d30":4},{"id":"rishhh/verisci-claim-verifier","type":"model","all":55,"d30":0},{"id":"rishhh/verisci-claim-verifier-sentence-nli","type":"model","all":63,"d30":27},{"id":"rishhh/verisci-claim-verifier-sentence-nli-refute","type":"model","all":44,"d30":14},{"id":"rishhh/verisci-claim-verifier-sentence-nli-seed123","type":"model","all":419,"d30":25},{"id":"rishhh/verisci-scifact-e5-retriever","type":"model","all":320,"d30":61},{"id":"rishhh/verisci-claim-verifier-retrieval-aware-seed123","type":"model","all":83,"d30":53},{"id":"rishhh/verisci-claim-verifier-retrieval-adapted-seed123","type":"model","all":941,"d30":13},{"id":"rishhh/verisci-claim-verifier-dense-adapted-seed123","type":"model","all":99,"d30":14},{"id":"rishhh/verisci-claim-verifier-claim-aggregator-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-claim-aggregator-holdout-safe-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-joint-evidence-seed123","type":"model","all":56,"d30":17},{"id":"rishhh/verisci-claim-verifier-joint-evidence-selector-seed123","type":"model","all":46,"d30":15},{"id":"rishhh/verisci-claim-verifier-learned-selector-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-refutes-hard-selector-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-constrained-selector-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-hard-negative-selector-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-contradiction-adapted-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-scifact-sentence-reranker-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-evidence-gated-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-selective-evidence-gated-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-learned-selector-base-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/verisci-claim-verifier-learned-selector-nli-warm-joint-seed123","type":"model","all":0,"d30":0},{"id":"rishhh/schemasage-sql-clean-text2sql","type":"dataset","all":259,"d30":46}]};
const PROJECTS = [["VeriSci", "verisci"], ["SchemaSage", "schemasage"]];
const root = document.documentElement;
// Hugging Face passes ?__theme=light|dark to Space iframes; follow it when present.
const asked = new URLSearchParams(location.search).get("__theme");
if (asked === "light" || asked === "dark") root.dataset.theme = asked;
const dark = matchMedia("(prefers-color-scheme: dark)");
const theme = () => (root.dataset.theme === "light" || root.dataset.theme === "dark")
? root.dataset.theme : (dark.matches ? "dark" : "light");
const swapArt = () => document.querySelectorAll("img[data-art]").forEach((img) => {
img.src = `assets/${img.dataset.art}-${theme()}.svg`;
});
swapArt();
dark.addEventListener("change", swapArt);
const fmt = (n) => Number(n || 0).toLocaleString("en-GB");
const projectOf = (id) => {
const name = id.split("/").pop().toLowerCase();
const hit = PROJECTS.find(([, prefix]) => name.startsWith(prefix));
return hit ? hit[0] : "Other";
};
const el = (tag, cls, text) => {
const node = document.createElement(tag);
if (cls) node.className = cls;
if (text !== undefined) node.textContent = text;
return node;
};
const plural = (n, word) => (n === 1 ? word : word + "s");
async function getJSON(url) {
const res = await fetch(url, { headers: { Accept: "application/json" } });
if (!res.ok) throw new Error(`${res.status} ${url}`);
return res.json();
}
async function load() {
const q = "&limit=1000&expand%5B%5D=downloadsAllTime&expand%5B%5D=downloads";
try {
const [models, datasets, spaces] = await Promise.all([
getJSON(`https://huggingface.co/api/models?author=${USER}${q}`),
getJSON(`https://huggingface.co/api/datasets?author=${USER}${q}`),
getJSON(`https://huggingface.co/api/spaces?author=${USER}&limit=1000`),
]);
const repos = [
...models.map((m) => ({ id: m.id, type: "model", all: m.downloadsAllTime || 0, d30: m.downloads || 0 })),
...datasets.map((d) => ({ id: d.id, type: "dataset", all: d.downloadsAllTime || 0, d30: d.downloads || 0 })),
];
render({ repos, spaces: spaces.length, live: true });
} catch (err) {
render({ repos: SNAPSHOT.repos, spaces: SNAPSHOT.spaces, live: false });
}
}
function render({ repos, spaces, live }) {
const models = repos.filter((r) => r.type === "model").length;
const datasets = repos.filter((r) => r.type === "dataset").length;
const total = repos.reduce((s, r) => s + r.all, 0);
const d30 = repos.reduce((s, r) => s + r.d30, 0);
document.getElementById("k-total").textContent = fmt(total);
document.getElementById("k-d30").textContent = "+" + fmt(d30);
document.getElementById("k-models").textContent = fmt(models);
document.getElementById("k-datasets").textContent = fmt(datasets);
document.getElementById("k-datasets-l").textContent = plural(datasets, "dataset");
document.getElementById("k-spaces").textContent = fmt(spaces);
document.getElementById("k-spaces-l").textContent = plural(spaces, "Space");
const status = document.getElementById("status");
status.classList.toggle("live", live);
document.getElementById("status-text").textContent = live
? "Live from the Hugging Face Hub"
: `Snapshot from ${SNAPSHOT.dateLabel}`;
const sorted = [...repos].sort((a, b) => b.all - a.all || a.id.localeCompare(b.id));
const top = sorted.filter((r) => r.all > 0).slice(0, 10);
const max = Math.max(1, ...top.map((r) => r.all));
const used = [...new Set(top.map((r) => projectOf(r.id)))];
const order = ["VeriSci", "SchemaSage", "Other"].filter((p) => used.includes(p));
const legend = document.getElementById("legend");
legend.replaceChildren(...order.map((p) => {
const li = el("li");
li.append(el("i", `c-${p}`), document.createTextNode(p));
return li;
}));
const chart = document.getElementById("chart");
chart.replaceChildren(...top.map((r) => {
const project = projectOf(r.id);
const name = r.id.split("/").pop();
const row = el("div", "row");
row.setAttribute("role", "listitem");
row.tabIndex = 0;
row.setAttribute("aria-label", `${name}, ${r.type}, ${fmt(r.all)} downloads all time, ${fmt(r.d30)} in the last 30 days`);
const label = el("div", "name");
label.append(document.createTextNode(name));
if (r.type === "dataset") label.append(el("span", "kind", " (dataset)"));
const line = el("div", "barline");
const bar = el("div", `bar c-${project}`);
bar.style.width = `calc((100% - 64px) * ${(r.all / max).toFixed(4)})`;
line.append(bar, el("span", "val", fmt(r.all)));
row.append(label, line);
const show = (x, y) => showTip(r, project, x, y);
row.addEventListener("pointermove", (e) => show(e.clientX, e.clientY));
row.addEventListener("pointerleave", hideTip);
row.addEventListener("focus", () => { const b = row.getBoundingClientRect(); show(b.left + 24, b.bottom); });
row.addEventListener("blur", hideTip);
return row;
}));
const tbody = document.getElementById("table");
tbody.replaceChildren(...sorted.map((r) => {
const tr = el("tr");
const a = el("a", "", r.id.split("/").pop());
a.href = `https://huggingface.co/${r.type === "dataset" ? "datasets/" : ""}${r.id}`;
a.target = "_blank"; a.rel = "noopener noreferrer";
const name = el("td"); name.append(a);
tr.append(name, el("td", "", r.type), el("td", "num", fmt(r.all)), el("td", "num", fmt(r.d30)));
return tr;
}));
}
const tip = document.getElementById("tip");
function showTip(r, project, x, y) {
const v = el("div", "tv", `${fmt(r.all)} downloads`);
const l1 = el("div", "tl", `${fmt(r.d30)} in the last 30 days`);
const l2 = el("div", "tl");
const key = el("span", `key c-${project}`);
l2.append(key, document.createTextNode(`${project} ${r.type}`));
tip.replaceChildren(v, l1, l2);
tip.classList.add("on");
const w = tip.offsetWidth, h = tip.offsetHeight;
const left = Math.min(window.innerWidth - w - 8, Math.max(8, x + 14)) + window.scrollX;
const top = (y + h + 20 > window.innerHeight ? y - h - 12 : y + 16) + window.scrollY;
tip.style.left = `${left}px`;
tip.style.top = `${top}px`;
}
function hideTip() { tip.classList.remove("on"); }
render({ repos: SNAPSHOT.repos, spaces: SNAPSHOT.spaces, live: false });
document.getElementById("status-text").textContent = "Loading live numbers";
load();
})();
</script>
</body>
</html>