Spaces:
Running
Running
| (function () { | |
| "use strict"; | |
| function esc(s) { | |
| return String(s == null ? "" : s) | |
| .replace(/&/g, "&") | |
| .replace(/</g, "<") | |
| .replace(/>/g, ">"); | |
| } | |
| function subdomain(id) { | |
| return id.toLowerCase().replace(/[^a-z0-9-]/g, "-"); | |
| } | |
| function countExperiments(node) { | |
| let n = 0; | |
| (node.children || []).forEach((c) => { | |
| n += 1 + countExperiments(c); | |
| }); | |
| return n; | |
| } | |
| function timeAgo(iso) { | |
| if (!iso) return ""; | |
| const d = (Date.now() - new Date(iso).getTime()) / 86400000; | |
| if (d < 1) return "today"; | |
| if (d < 2) return "yesterday"; | |
| if (d < 30) return `${Math.floor(d)}d ago`; | |
| if (d < 365) return `${Math.floor(d / 30)}mo ago`; | |
| return `${Math.floor(d / 365)}y ago`; | |
| } | |
| function card(space) { | |
| const id = space.id; | |
| const owner = id.split("/")[0]; | |
| const name = id.split("/")[1] || id; | |
| const sub = subdomain(id); | |
| const title = (space.cardData && space.cardData.title) || name; | |
| const a = document.createElement("a"); | |
| a.className = "card"; | |
| a.href = `https://huggingface.co/spaces/${id}`; | |
| a.target = "_blank"; | |
| a.rel = "noopener"; | |
| a.innerHTML = | |
| `<div class="preview"><div class="ph">preview…</div>` + | |
| `<iframe loading="lazy" title="${esc(title)}" ` + | |
| `src="https://${sub}.static.hf.space/"></iframe></div>` + | |
| `<div class="card-body">` + | |
| `<div class="card-title">${esc(title)}</div>` + | |
| `<div class="card-owner">${esc(owner)}</div>` + | |
| `<div class="card-meta">` + | |
| (space.likes ? `<span class="chip">♥ ${space.likes}</span>` : "") + | |
| (space.lastModified | |
| ? `<span class="chip">${esc(timeAgo(space.lastModified))}</span>` | |
| : "") + | |
| `<span class="chip exp" data-exp></span>` + | |
| `</div></div>`; | |
| return a; | |
| } | |
| async function enrich(space, el) { | |
| const sub = subdomain(space.id); | |
| const chip = el.querySelector("[data-exp]"); | |
| try { | |
| const r = await fetch(`https://${sub}.static.hf.space/logbook.json`); | |
| if (!r.ok) throw new Error(); | |
| const manifest = await r.json(); | |
| const n = countExperiments(manifest.root || {}); | |
| chip.textContent = n === 1 ? "1 experiment" : `${n} experiments`; | |
| if (manifest.title) el.querySelector(".card-title").textContent = manifest.title; | |
| } catch (e) { | |
| chip.remove(); | |
| } | |
| } | |
| async function main() { | |
| const grid = document.getElementById("grid"); | |
| let spaces = []; | |
| try { | |
| const r = await fetch( | |
| "https://huggingface.co/api/spaces?filter=trackio-logbook&full=true&limit=300" | |
| ); | |
| spaces = await r.json(); | |
| } catch (e) { | |
| grid.innerHTML = '<p class="empty">Could not load logbooks right now.</p>'; | |
| return; | |
| } | |
| spaces = spaces.filter((s) => !s.sdk || s.sdk === "static"); | |
| spaces.sort( | |
| (a, b) => new Date(b.lastModified || 0) - new Date(a.lastModified || 0) | |
| ); | |
| if (!spaces.length) { | |
| grid.innerHTML = | |
| '<p class="empty">No logbooks published yet. Be the first!</p>'; | |
| document.getElementById("count").textContent = ""; | |
| return; | |
| } | |
| document.getElementById("count").textContent = | |
| `${spaces.length} logbook${spaces.length === 1 ? "" : "s"}`; | |
| grid.innerHTML = ""; | |
| spaces.forEach((s) => { | |
| const el = card(s); | |
| grid.appendChild(el); | |
| enrich(s, el); | |
| }); | |
| } | |
| main(); | |
| })(); | |