Spaces:
Running
Running
Bootstrap automatic contribution interface
Browse files- README.md +8 -3
- app.js +87 -0
- catalog.json +1 -0
- index.html +55 -17
- style.css +51 -26
README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
| 1 |
---
|
| 2 |
title: Gene Embeddings Staging
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: blue
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
|
|
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Gene Embeddings Staging
|
| 3 |
+
emoji: 🧬
|
| 4 |
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: static
|
| 7 |
+
app_file: index.html
|
| 8 |
pinned: false
|
| 9 |
+
datasets:
|
| 10 |
+
- embeddingsSSRF/gene_embeddings_staging
|
| 11 |
---
|
| 12 |
|
| 13 |
+
# Gene Embeddings Staging Catalog
|
| 14 |
+
|
| 15 |
+
A static catalog of approved gene embeddings. Community additions are automatically validated against the published technical policy before appearing here.
|
app.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(() => {
|
| 2 |
+
"use strict";
|
| 3 |
+
const GITHUB_CONTRIBUTING = "https://github.com/DanielTabaresLopez/Gene-Embeddings-Contributions/blob/main/CONTRIBUTING.md";
|
| 4 |
+
const DATASET_BASE = "https://huggingface.co/datasets/embeddingsSSRF/gene_embeddings_staging/tree/main/";
|
| 5 |
+
|
| 6 |
+
const grid = document.getElementById("grid");
|
| 7 |
+
const search = document.getElementById("search");
|
| 8 |
+
const category = document.getElementById("category");
|
| 9 |
+
const dialog = document.getElementById("detail-dialog");
|
| 10 |
+
const detail = document.getElementById("detail-content");
|
| 11 |
+
let catalog = [];
|
| 12 |
+
|
| 13 |
+
document.getElementById("contribute-link").href = GITHUB_CONTRIBUTING;
|
| 14 |
+
document.getElementById("contribute-link-bottom").href = GITHUB_CONTRIBUTING;
|
| 15 |
+
document.getElementById("close-detail").addEventListener("click", () => dialog.close());
|
| 16 |
+
|
| 17 |
+
const escapeHtml = (value) => {
|
| 18 |
+
const div = document.createElement("div");
|
| 19 |
+
div.textContent = value ?? "";
|
| 20 |
+
return div.innerHTML;
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
function render() {
|
| 24 |
+
const term = search.value.trim().toLowerCase();
|
| 25 |
+
const selected = category.value;
|
| 26 |
+
const items = catalog.filter((item) => {
|
| 27 |
+
if (selected !== "All" && item.category !== selected) return false;
|
| 28 |
+
const haystack = `${item.name} ${item.category} ${item.modality} ${item.algorithm} ${item.description}`.toLowerCase();
|
| 29 |
+
return !term || haystack.includes(term);
|
| 30 |
+
});
|
| 31 |
+
document.getElementById("result-count").textContent = `${items.length} of ${catalog.length} embedding sets`;
|
| 32 |
+
grid.innerHTML = "";
|
| 33 |
+
items.forEach((item) => {
|
| 34 |
+
const card = document.createElement("button");
|
| 35 |
+
card.className = "card";
|
| 36 |
+
card.innerHTML = `
|
| 37 |
+
<span class="badge">${escapeHtml(item.category)}</span>
|
| 38 |
+
<h3>${escapeHtml(item.name)}</h3>
|
| 39 |
+
<p>${escapeHtml(item.description || "No description provided.")}</p>
|
| 40 |
+
<div class="metrics">
|
| 41 |
+
<span><strong>${Number(item.n_genes).toLocaleString()}</strong> genes</span>
|
| 42 |
+
<span><strong>${Number(item.n_dims).toLocaleString()}</strong> dims</span>
|
| 43 |
+
<span><strong>${item.coverage_pct}%</strong> coverage</span>
|
| 44 |
+
</div>`;
|
| 45 |
+
card.addEventListener("click", () => openDetail(item));
|
| 46 |
+
grid.appendChild(card);
|
| 47 |
+
});
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
function openDetail(item) {
|
| 51 |
+
const path = item.dataset_path || `data/embeddings/${item.id}`;
|
| 52 |
+
detail.innerHTML = `
|
| 53 |
+
<p class="badge">${escapeHtml(item.category)}</p>
|
| 54 |
+
<h2>${escapeHtml(item.name)}</h2>
|
| 55 |
+
<p>${escapeHtml(item.description || "")}</p>
|
| 56 |
+
<div class="detail-grid">
|
| 57 |
+
<div><strong>${Number(item.n_genes).toLocaleString()}</strong>genes</div>
|
| 58 |
+
<div><strong>${Number(item.n_dims).toLocaleString()}</strong>dimensions</div>
|
| 59 |
+
<div><strong>${item.coverage_pct}%</strong>coverage</div>
|
| 60 |
+
</div>
|
| 61 |
+
<p><b>Modality:</b> ${escapeHtml(item.modality || "—")}</p>
|
| 62 |
+
<p><b>Method:</b> ${escapeHtml(item.algorithm || "—")}</p>
|
| 63 |
+
<p><b>QC:</b> ${escapeHtml(item.qc_status || "legacy package")}</p>
|
| 64 |
+
<p><b>License:</b> ${escapeHtml(item.license || "see package metadata")}</p>
|
| 65 |
+
<a class="button primary" href="${DATASET_BASE}${encodeURI(path)}" target="_blank" rel="noopener">Open package files</a>`;
|
| 66 |
+
dialog.showModal();
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
async function init() {
|
| 70 |
+
const response = await fetch("catalog.json", { cache: "no-store" });
|
| 71 |
+
if (!response.ok) throw new Error(`Could not load catalog.json (${response.status})`);
|
| 72 |
+
catalog = await response.json();
|
| 73 |
+
const categories = [...new Set(catalog.map((item) => item.category))].sort();
|
| 74 |
+
categories.forEach((value) => category.add(new Option(value, value)));
|
| 75 |
+
document.getElementById("stat-count").textContent = catalog.length.toLocaleString();
|
| 76 |
+
document.getElementById("stat-genes").textContent = Math.max(...catalog.map((item) => Number(item.n_genes) || 0)).toLocaleString();
|
| 77 |
+
const dims = catalog.map((item) => Number(item.n_dims) || 0);
|
| 78 |
+
document.getElementById("stat-dims").textContent = `${Math.min(...dims).toLocaleString()}–${Math.max(...dims).toLocaleString()}`;
|
| 79 |
+
render();
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
search.addEventListener("input", render);
|
| 83 |
+
category.addEventListener("change", render);
|
| 84 |
+
init().catch((error) => {
|
| 85 |
+
grid.innerHTML = `<p>Catalog failed to load: ${escapeHtml(error.message)}</p>`;
|
| 86 |
+
});
|
| 87 |
+
})();
|
catalog.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
[]
|
index.html
CHANGED
|
@@ -1,19 +1,57 @@
|
|
| 1 |
<!doctype html>
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Gene Embeddings</title>
|
| 7 |
+
<link rel="stylesheet" href="style.css" />
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<header class="hero">
|
| 11 |
+
<div class="hero-inner">
|
| 12 |
+
<p class="eyebrow">Unified human gene representations</p>
|
| 13 |
+
<h1>Gene Embeddings</h1>
|
| 14 |
+
<p class="lead">Browse standardized gene-level representations across sequence, expression, literature, pathway, network, regulatory, knowledge-graph, and single-cell modalities.</p>
|
| 15 |
+
<div class="actions">
|
| 16 |
+
<a class="button primary" id="contribute-link" href="#">Contribute an embedding</a>
|
| 17 |
+
<a class="button" href="https://huggingface.co/datasets/embeddingsSSRF/gene_embeddings_staging" target="_blank" rel="noopener">Open Dataset</a>
|
| 18 |
+
</div>
|
| 19 |
+
<p class="notice">Community contributions are automatically checked for compliance with the technical policy. Inclusion is not independent scientific or legal endorsement.</p>
|
| 20 |
+
</div>
|
| 21 |
+
</header>
|
| 22 |
+
|
| 23 |
+
<main>
|
| 24 |
+
<section class="stats" aria-label="Catalog summary">
|
| 25 |
+
<article><strong id="stat-count">—</strong><span>embedding sets</span></article>
|
| 26 |
+
<article><strong id="stat-genes">—</strong><span>maximum genes</span></article>
|
| 27 |
+
<article><strong id="stat-dims">—</strong><span>dimension range</span></article>
|
| 28 |
+
</section>
|
| 29 |
+
|
| 30 |
+
<section class="controls">
|
| 31 |
+
<input id="search" type="search" placeholder="Search names, modalities, or methods" aria-label="Search catalog" />
|
| 32 |
+
<select id="category" aria-label="Filter by category"><option value="All">All categories</option></select>
|
| 33 |
+
</section>
|
| 34 |
+
|
| 35 |
+
<p id="result-count" class="result-count"></p>
|
| 36 |
+
<section id="grid" class="grid" aria-live="polite"></section>
|
| 37 |
+
|
| 38 |
+
<section class="contribute" id="contribute">
|
| 39 |
+
<h2>Automatic contribution workflow</h2>
|
| 40 |
+
<ol>
|
| 41 |
+
<li>Put <code>embeddings.npz</code>, <code>genes.tsv</code>, <code>metadata.json</code>, and <code>README.md</code> in a public Hugging Face Dataset.</li>
|
| 42 |
+
<li>Freeze the candidate at a full commit SHA.</li>
|
| 43 |
+
<li>Add one submission YAML through a GitHub Pull Request.</li>
|
| 44 |
+
<li>GitHub validates the frozen package. A strict PASS is merged and published automatically.</li>
|
| 45 |
+
</ol>
|
| 46 |
+
<a class="button primary" id="contribute-link-bottom" href="#">Open contribution instructions</a>
|
| 47 |
+
</section>
|
| 48 |
+
</main>
|
| 49 |
+
|
| 50 |
+
<dialog id="detail-dialog">
|
| 51 |
+
<button id="close-detail" class="close" aria-label="Close">×</button>
|
| 52 |
+
<div id="detail-content"></div>
|
| 53 |
+
</dialog>
|
| 54 |
+
|
| 55 |
+
<script src="app.js"></script>
|
| 56 |
+
</body>
|
| 57 |
</html>
|
style.css
CHANGED
|
@@ -1,28 +1,53 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
}
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
.
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
| 1 |
+
:root {
|
| 2 |
+
--ink: #172033;
|
| 3 |
+
--muted: #647089;
|
| 4 |
+
--line: #dbe2ee;
|
| 5 |
+
--paper: #ffffff;
|
| 6 |
+
--wash: #f4f7fb;
|
| 7 |
+
--brand: #3157d5;
|
| 8 |
+
--brand-dark: #213c9d;
|
| 9 |
+
--success: #167c5a;
|
| 10 |
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 11 |
}
|
| 12 |
+
* { box-sizing: border-box; }
|
| 13 |
+
body { margin: 0; color: var(--ink); background: var(--wash); }
|
| 14 |
+
a { color: inherit; }
|
| 15 |
+
.hero { background: linear-gradient(135deg, #182858, #3157d5 62%, #5a80ef); color: white; }
|
| 16 |
+
.hero-inner { max-width: 1160px; margin: auto; padding: 72px 24px 58px; }
|
| 17 |
+
.eyebrow { text-transform: uppercase; letter-spacing: .14em; font-weight: 700; font-size: .78rem; opacity: .8; }
|
| 18 |
+
h1 { font-size: clamp(2.5rem, 7vw, 5.2rem); line-height: .98; margin: 12px 0 20px; }
|
| 19 |
+
.lead { max-width: 800px; font-size: 1.15rem; line-height: 1.65; color: #e9efff; }
|
| 20 |
+
.actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }
|
| 21 |
+
.button { display: inline-flex; align-items: center; justify-content: center; padding: 12px 17px; border-radius: 10px; border: 1px solid currentColor; text-decoration: none; font-weight: 700; }
|
| 22 |
+
.hero .button { color: white; }
|
| 23 |
+
.hero .button.primary { background: white; color: var(--brand-dark); border-color: white; }
|
| 24 |
+
.notice { max-width: 850px; font-size: .88rem; color: #d6e0ff; margin-top: 20px; }
|
| 25 |
+
main { max-width: 1160px; margin: auto; padding: 0 24px 70px; }
|
| 26 |
+
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: -28px; }
|
| 27 |
+
.stats article { background: var(--paper); border: 1px solid var(--line); border-radius: 15px; padding: 22px; box-shadow: 0 8px 28px rgba(31, 47, 88, .08); }
|
| 28 |
+
.stats strong { display: block; font-size: 2rem; }
|
| 29 |
+
.stats span { color: var(--muted); }
|
| 30 |
+
.controls { display: grid; grid-template-columns: 1fr 260px; gap: 12px; margin: 38px 0 12px; }
|
| 31 |
+
input, select { width: 100%; padding: 13px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--paper); font: inherit; }
|
| 32 |
+
.result-count { color: var(--muted); }
|
| 33 |
+
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
|
| 34 |
+
.card { text-align: left; border: 1px solid var(--line); background: var(--paper); border-radius: 15px; padding: 19px; cursor: pointer; transition: transform .15s ease, box-shadow .15s ease; }
|
| 35 |
+
.card:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(31,47,88,.10); }
|
| 36 |
+
.card h3 { margin: 0 0 9px; font-size: 1.05rem; }
|
| 37 |
+
.card p { color: var(--muted); line-height: 1.5; min-height: 4.5em; }
|
| 38 |
+
.badge { display: inline-block; border-radius: 999px; background: #e8edff; color: var(--brand-dark); padding: 5px 9px; font-size: .75rem; font-weight: 700; }
|
| 39 |
+
.metrics { display: flex; gap: 12px; flex-wrap: wrap; border-top: 1px solid var(--line); padding-top: 13px; font-size: .83rem; color: var(--muted); }
|
| 40 |
+
.metrics strong { color: var(--ink); }
|
| 41 |
+
.contribute { margin-top: 54px; padding: 30px; background: var(--paper); border: 1px solid var(--line); border-radius: 16px; }
|
| 42 |
+
.contribute li { margin: 10px 0; line-height: 1.55; }
|
| 43 |
+
.contribute .button.primary { background: var(--brand); color: white; border-color: var(--brand); }
|
| 44 |
+
dialog { width: min(720px, calc(100vw - 32px)); border: 0; border-radius: 16px; padding: 28px; box-shadow: 0 30px 100px rgba(0,0,0,.3); }
|
| 45 |
+
dialog::backdrop { background: rgba(12,20,40,.55); }
|
| 46 |
+
.close { float: right; border: 0; background: none; font-size: 2rem; cursor: pointer; }
|
| 47 |
+
.detail-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 18px 0; }
|
| 48 |
+
.detail-grid div { background: var(--wash); border-radius: 10px; padding: 13px; }
|
| 49 |
+
.detail-grid strong { display: block; font-size: 1.2rem; }
|
| 50 |
+
@media (max-width: 720px) {
|
| 51 |
+
.stats, .controls { grid-template-columns: 1fr; }
|
| 52 |
+
.hero-inner { padding-top: 48px; }
|
| 53 |
}
|