// Layer view — feature browser table for a single layer. function LayerView({ layer, landingSummary, onPickFeature, onOpenCaseStudy, layersAvailable }) { const idx = useFetch(() => API.index(layer), [layer]); const [filter, setFilter] = React.useState("all"); const [search, setSearch] = React.useState(""); const [sortKey, setSortKey] = React.useState("m7_score"); const [sortDir, setSortDir] = React.useState("desc"); const all = idx.data || []; const rows = React.useMemo(() => { let xs = all.filter((r) => { const cat = rowCategory(r); if (filter !== "all" && cat !== filter) return false; if (search) { const s = search.toLowerCase(); const fid = String(r.feature_id); if (!fid.includes(s) && !((r.m7_label || "").toLowerCase().includes(s)) && !((r.m1_label || "").toLowerCase().includes(s)) && !((r.m2_label || "").toLowerCase().includes(s)) && !((r.m6_label || "").toLowerCase().includes(s))) return false; } return true; }); xs = [...xs].sort((a, b) => { const av = a[sortKey], bv = b[sortKey]; if (av == null) return 1; if (bv == null) return -1; if (typeof av === "string") return sortDir === "desc" ? bv.localeCompare(av) : av.localeCompare(bv); return sortDir === "desc" ? bv - av : av - bv; }); return xs; }, [all, filter, search, sortKey, sortDir]); // No cap — render all rows. The wrap is overflow:auto so the user // scrolls inside the table; the sticky thead keeps headers in view. const visible = rows; const layerMeta = (landingSummary?.layers || []).find(L => L.layer === layer) || {}; function sortBy(k) { if (sortKey === k) setSortDir(d => d === "desc" ? "asc" : "desc"); else { setSortKey(k); setSortDir("desc"); } } const arrow = (k) => sortKey === k ? (sortDir === "desc" ? " ↓" : " ↑") : ""; if (idx.loading) return
Each row is one SAE feature. Columns show the score under each annotation method; the geometric column is the paper's novel test. Highlighted cells are q < 0.05. Click a row to open the feature.
| Activity | Geometric — Cα backbone | MEME motif | InterPro residue | InterPro protein | Compact | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sortBy("feature_id")} style={{cursor:'pointer'}}>ID{arrow("feature_id")} | Profile | Sig 1·2·3·4·5·6·7 | sortBy("pct_proteins_activated")} style={{cursor:'pointer'}}>% prot{arrow("pct_proteins_activated")} | sortBy("max_activation")} style={{cursor:'pointer'}}>Max act{arrow("max_activation")} | sortBy("m7_score")} style={{cursor:'pointer'}}>PR-AUC{arrow("m7_score")} | Top geom label | sortBy("m6_score")} style={{cursor:'pointer'}}>PR-AUC{arrow("m6_score")} | Top motif | sortBy("m2_score")} style={{cursor:'pointer'}}>F1{arrow("m2_score")} | IPR residue label | sortBy("m1_score")} style={{cursor:'pointer'}}>F1{arrow("m1_score")} | IPR protein label | CATH·R | CATH·P | Pos |
| f/{r.feature_id} |
{radarArr ? (
|
{fmt(r.pct_proteins_activated, 1)} | {fmt(r.max_activation, 2)} | {fmt(r.m7_score, 2)} | {r.m7_label || "—"} | {fmt(r.m6_score, 2)} | {r.m6_label || "—"} | {fmt(r.m2_score, 2)} | {r.m2_label || "—"} | {fmt(r.m1_score, 2)} | {r.m1_label || "—"} | {fmt(r.m4_score, 2)} | {fmt(r.m3_score, 2)} | {fmt(r.m5_score, 2)} | |