(() => { "use strict"; const DATASET_BASE = "https://huggingface.co/datasets/" + "BoevaLab/Gene-Embedding-Hub-dataset/tree/main/"; const grid = document.getElementById("grid"); const search = document.getElementById("search"); const category = document.getElementById("category"); const dialog = document.getElementById("detail-dialog"); const detail = document.getElementById("detail-content"); let catalog = []; document .getElementById("close-detail") .addEventListener("click", () => dialog.close()); const escapeHtml = (value) => { const div = document.createElement("div"); const safeValue = value === null || value === undefined ? "" : String(value); div.textContent = safeValue; return div.innerHTML; }; const externalValue = (label, url) => { const safeLabel = escapeHtml(label || "—"); const rawUrl = String(url || "").trim(); if (!/^https?:\/\//i.test(rawUrl)) { return safeLabel; } return ` ${safeLabel}`; }; const communityBadge = (item) => { if (item.community_contribution !== true) { return ""; } return ` Community contribution `; }; const contributorDetail = (item) => { if (item.community_contribution !== true) { return ""; } const name = String( item.contributor_name || "Not recorded" ).trim(); const affiliation = String( item.contributor_affiliation || "" ).trim(); const affiliationText = affiliation ? ` — ${escapeHtml(affiliation)}` : ""; return `

Contributor: ${escapeHtml(name)}${affiliationText}

`; }; const categoryClass = (value) => { const slug = String(value || "Other") .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); return `category-${slug || "other"}`; }; function render() { const term = search.value.trim().toLowerCase(); const selected = category.value; const items = catalog.filter((item) => { if ( selected !== "All" && item.category !== selected ) { return false; } const haystack = [ item.name, item.category, item.modality, item.algorithm, item.description, item.contributor_name, item.contributor_affiliation, ] .map((value) => String(value || "")) .join(" ") .toLowerCase(); return !term || haystack.includes(term); }); document.getElementById("result-count").textContent = `${items.length} of ${catalog.length} embedding sets`; grid.innerHTML = ""; items.forEach((item) => { const card = document.createElement("button"); card.className = `card ${categoryClass(item.category)}`; card.innerHTML = `
${escapeHtml(item.category || "Uncategorized")} ${communityBadge(item)}

${escapeHtml(item.name)}

${escapeHtml( item.description || "No description provided." )}

${Number(item.n_genes).toLocaleString()} genes ${Number(item.n_dims).toLocaleString()} dims ${item.coverage_pct}% coverage
`; card.addEventListener( "click", () => openDetail(item) ); grid.appendChild(card); }); } function openDetail(item) { const path = item.dataset_path || `data/embeddings/${item.id}`; const packageUrl = `${DATASET_BASE}${encodeURI(path)}`; detail.innerHTML = `
${escapeHtml(item.category || "Uncategorized")} ${communityBadge(item)}

${escapeHtml(item.name)}

${escapeHtml(item.description || "")}

${Number(item.n_genes).toLocaleString()} genes
${Number(item.n_dims).toLocaleString()} dimensions
${item.coverage_pct}% coverage
${contributorDetail(item)} Open package files `; dialog.showModal(); } async function init() { const response = await fetch( "catalog.json", { cache: "no-store" } ); if (!response.ok) { throw new Error( `Could not load catalog.json (${response.status})` ); } catalog = await response.json(); const categories = [ ...new Set( catalog.map((item) => String(item.category || "Uncategorized") ) ), ].sort(); categories.forEach((value) => { category.add(new Option(value, value)); }); document.getElementById("stat-count").textContent = catalog.length.toLocaleString(); document.getElementById("stat-genes").textContent = Math.max( ...catalog.map( (item) => Number(item.n_genes) || 0 ) ).toLocaleString(); const dims = catalog.map( (item) => Number(item.n_dims) || 0 ); document.getElementById("stat-dims").textContent = `${Math.min(...dims).toLocaleString()}–` + `${Math.max(...dims).toLocaleString()}`; render(); } search.addEventListener("input", render); category.addEventListener("change", render); init().catch((error) => { grid.innerHTML = `

Catalog failed to load: ${escapeHtml(error.message)}

`; }); })();