Spaces:
Running
Running
Update index.html
Browse files- index.html +66 -46
index.html
CHANGED
|
@@ -66,7 +66,6 @@
|
|
| 66 |
.org { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }
|
| 67 |
.dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
|
| 68 |
.num { font-variant-numeric: tabular-nums; }
|
| 69 |
-
.best { color: var(--sage-ink); font-weight: 600; background: var(--sage-soft); }
|
| 70 |
.muted { color: var(--muted); }
|
| 71 |
.links a { color: var(--clay); text-decoration: none; font-size: 13px; margin-right: 12px; }
|
| 72 |
.links a:hover { text-decoration: underline; }
|
|
@@ -174,17 +173,18 @@
|
|
| 174 |
<tbody id="boardBody"></tbody>
|
| 175 |
</table>
|
| 176 |
</div>
|
| 177 |
-
<div class="legend">EM = exact grid match. fmt = output parses as a grid. Overall = mean across MARC-1/2/3. pass@n > EM means the model has latent rule inference it doesn't always land.
|
| 178 |
</section>
|
| 179 |
|
| 180 |
<!-- TIER TABS (MARC-1, MARC-2, MARC-3) -->
|
| 181 |
<section id="view-tier" hidden>
|
| 182 |
-
|
| 183 |
<div class="info-box">
|
| 184 |
-
<b>Understanding the graphs:</b> Small models often fall back on statistical guessing rather than solving the rule.
|
| 185 |
<ul>
|
| 186 |
<li>The <b style="color:#e05e5e;">Red dashed line</b> is the mathematical random-guessing baseline.</li>
|
| 187 |
<li>The <b style="color:#e0b25c;">Yellow dashed line</b> is <b>2Γ the random baseline</b>. Scoring below this generally means the model is just guessing, or worse, copying the input.</li>
|
|
|
|
| 188 |
</ul>
|
| 189 |
</div>
|
| 190 |
|
|
@@ -410,6 +410,7 @@ const DEMO_DATA = { models: [
|
|
| 410 |
const esc = s => String(s ?? "").replace(/[&<>"']/g, c => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
| 411 |
const fmtPct = v => v == null ? "β" : (v * 100).toFixed(1) + "%";
|
| 412 |
const fmtNum = v => Number.isInteger(v) ? String(v) : (+v).toFixed(3).replace(/\.?0+$/,"");
|
|
|
|
| 413 |
|
| 414 |
function parseParams(s) {
|
| 415 |
if (!s) return NaN;
|
|
@@ -461,6 +462,15 @@ function getFamily(name) {
|
|
| 461 |
return match ? match[1] : base;
|
| 462 |
}
|
| 463 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
// βββ Sort βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 465 |
function buildSortOptions() {
|
| 466 |
const sel = document.getElementById("sortSel");
|
|
@@ -500,7 +510,7 @@ function buildOrgChips(elId, customOrg, onPick) {
|
|
| 500 |
document.getElementById(elId).innerHTML =
|
| 501 |
chip("all","All",models.length) +
|
| 502 |
Object.keys(counts).sort().map(k => chip(k,k,counts[k])).join("");
|
| 503 |
-
|
| 504 |
document.getElementById(elId).onclick = e => {
|
| 505 |
const c = e.target.closest(".chip"); if (!c) return;
|
| 506 |
onPick(c.dataset.org);
|
|
@@ -509,21 +519,11 @@ function buildOrgChips(elId, customOrg, onPick) {
|
|
| 509 |
|
| 510 |
function renderTable(isTierTab, tierId = null) {
|
| 511 |
const key = isTierTab ? "marc"+tierId : sortKeyForTab();
|
| 512 |
-
|
| 513 |
-
|
| 514 |
const list = models
|
| 515 |
-
.filter(m => (
|
| 516 |
.sort(SORTS[key]);
|
| 517 |
|
| 518 |
-
const bestOverall = list.map(overallEm).filter(v => v != null);
|
| 519 |
-
const bestTier = k => list.map(m => tierValue(m,k,"em")).filter(v => v != null);
|
| 520 |
-
const bestFmt = list.map(overallFmt).filter(v => v != null);
|
| 521 |
-
const bO = bestOverall.length ? Math.max(...bestOverall) : null;
|
| 522 |
-
const b1 = bestTier("1").length ? Math.max(...bestTier("1")) : null;
|
| 523 |
-
const b2 = bestTier("2").length ? Math.max(...bestTier("2")) : null;
|
| 524 |
-
const b3 = bestTier("3").length ? Math.max(...bestTier("3")) : null;
|
| 525 |
-
const bF = bestFmt.length ? Math.max(...bestFmt) : null;
|
| 526 |
-
|
| 527 |
const countId = isTierTab ? "tierCount" : "count";
|
| 528 |
const headId = isTierTab ? "tierHead" : "boardHead";
|
| 529 |
const bodyId = isTierTab ? "tierBody" : "boardBody";
|
|
@@ -542,6 +542,9 @@ function renderTable(isTierTab, tierId = null) {
|
|
| 542 |
<th>Links</th>
|
| 543 |
</tr>`;
|
| 544 |
|
|
|
|
|
|
|
|
|
|
| 545 |
document.getElementById(bodyId).innerHTML = list.length ? list.map((m,i) => {
|
| 546 |
const c = orgColor[m.org] || "#888";
|
| 547 |
const ov = overallEm(m), t1 = tierValue(m,"1","em"), t2 = tierValue(m,"2","em"), t3 = tierValue(m,"3","em"), f = overallFmt(m);
|
|
@@ -549,18 +552,17 @@ function renderTable(isTierTab, tierId = null) {
|
|
| 549 |
if (m.links?.card) links.push(`<a href="${esc(m.links.card)}" target="_blank" rel="noopener">Card</a>`);
|
| 550 |
if (m.links?.demo) links.push(`<a href="${esc(m.links.demo)}" target="_blank" rel="noopener">Demo</a>`);
|
| 551 |
const mode = m.chat ? `<span class="mode chat">chat</span>` : `<span class="mode base">base</span>`;
|
| 552 |
-
const cell = (v, best) => `<td class="r num ${v != null && v === best ? "best" : ""}">${v != null ? fmtPct(v) : '<span class="muted">β</span>'}</td>`;
|
| 553 |
return `<tr>
|
| 554 |
<td class="rank">${i+1}</td>
|
| 555 |
<td class="name">${esc(m.name)}</td>
|
| 556 |
<td><span class="org"><span class="dot" style="background:${c}"></span>${esc(m.org)}</span></td>
|
| 557 |
<td class="num">${esc(m.params)}</td>
|
| 558 |
<td>${mode}</td>
|
| 559 |
-
${cell(ov
|
| 560 |
-
${cell(t1
|
| 561 |
-
${cell(t2
|
| 562 |
-
${cell(t3
|
| 563 |
-
|
| 564 |
<td class="links">${links.join("") || '<span class="muted">β</span>'}</td>
|
| 565 |
</tr>`;
|
| 566 |
}).join("") : `<tr><td colspan="11" class="empty">No models match these filters</td></tr>`;
|
|
@@ -576,7 +578,7 @@ const baselinePlugin = {
|
|
| 576 |
if (!options || options.random === undefined) return;
|
| 577 |
const { ctx, chartArea: { top, bottom, left, right }, scales: { y } } = chart;
|
| 578 |
ctx.save();
|
| 579 |
-
|
| 580 |
// Red line: 1x Random
|
| 581 |
if (options.random > 0) {
|
| 582 |
const y1 = y.getPixelForValue(options.random);
|
|
@@ -590,7 +592,7 @@ const baselinePlugin = {
|
|
| 590 |
ctx.stroke();
|
| 591 |
}
|
| 592 |
}
|
| 593 |
-
|
| 594 |
// Yellow line: 2x Random
|
| 595 |
if (options.random > 0) {
|
| 596 |
const y2 = y.getPixelForValue(options.random * 2);
|
|
@@ -604,6 +606,25 @@ const baselinePlugin = {
|
|
| 604 |
ctx.stroke();
|
| 605 |
}
|
| 606 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
ctx.restore();
|
| 608 |
}
|
| 609 |
};
|
|
@@ -612,19 +633,21 @@ Chart.register(baselinePlugin);
|
|
| 612 |
function renderTierCharts(tierId) {
|
| 613 |
tierChartInstances.forEach(c => c.destroy());
|
| 614 |
tierChartInstances = [];
|
| 615 |
-
|
| 616 |
const t = String(tierId);
|
|
|
|
| 617 |
|
| 618 |
FAMILY_KEYS.forEach(fam => {
|
| 619 |
// Group by family
|
| 620 |
const groups = {};
|
| 621 |
models.forEach(m => {
|
|
|
|
| 622 |
const groupName = getFamily(m.name);
|
| 623 |
if (!groups[groupName]) groups[groupName] = [];
|
| 624 |
-
|
| 625 |
const p = parseParams(m.params);
|
| 626 |
const emVal = m.families?.[fam]?.[t]?.em;
|
| 627 |
-
|
| 628 |
if (!isNaN(p) && emVal != null) {
|
| 629 |
groups[groupName].push({ x: p, y: emVal * 100, name: m.name, org: m.org });
|
| 630 |
}
|
|
@@ -649,6 +672,8 @@ function renderTierCharts(tierId) {
|
|
| 649 |
Chart.defaults.color = "#a29686";
|
| 650 |
Chart.defaults.font.family = '"Avenir Next", "Segoe UI", system-ui, sans-serif';
|
| 651 |
|
|
|
|
|
|
|
| 652 |
const inst = new Chart(document.getElementById(`tierChart-${fam}`), {
|
| 653 |
type: "scatter",
|
| 654 |
data: { datasets },
|
|
@@ -657,7 +682,10 @@ function renderTierCharts(tierId) {
|
|
| 657 |
layout: { padding: { top: 10, right: 10 } },
|
| 658 |
plugins: {
|
| 659 |
legend: { display: false },
|
| 660 |
-
baselines: {
|
|
|
|
|
|
|
|
|
|
| 661 |
tooltip: { callbacks: { label: c => `${c.raw.name}: ${c.parsed.y.toFixed(1)}%` } }
|
| 662 |
},
|
| 663 |
scales: {
|
|
@@ -675,20 +703,12 @@ function renderTierCharts(tierId) {
|
|
| 675 |
}
|
| 676 |
}
|
| 677 |
});
|
| 678 |
-
|
| 679 |
tierChartInstances.push(inst);
|
| 680 |
});
|
| 681 |
}
|
| 682 |
|
| 683 |
// βββ Families Heatmap βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 684 |
-
function heatColor(v) {
|
| 685 |
-
if (v == null) return "transparent";
|
| 686 |
-
const t = Math.max(0, Math.min(1, v));
|
| 687 |
-
const lo = [74, 48, 38], hi = [45, 58, 47];
|
| 688 |
-
const mix = lo.map((c, i) => Math.round(c + (hi[i] - c) * t));
|
| 689 |
-
return `rgb(${mix[0]},${mix[1]},${mix[2]})`;
|
| 690 |
-
}
|
| 691 |
-
|
| 692 |
function renderFamilies() {
|
| 693 |
const tierSel = document.getElementById("famTierSel").value;
|
| 694 |
const sorted = [...models].sort((a,b) => (familyAvgEm(b, tierSel).avg ?? -1) - (familyAvgEm(a, tierSel).avg ?? -1));
|
|
@@ -789,12 +809,12 @@ function renderCharts() {
|
|
| 789 |
function setTab(t) {
|
| 790 |
currentTab = t;
|
| 791 |
document.querySelectorAll(".tab").forEach(b => b.classList.toggle("on", b.dataset.tab === t));
|
| 792 |
-
|
| 793 |
let viewId = "view-" + t;
|
| 794 |
if (t === "marc1" || t === "marc2" || t === "marc3") {
|
| 795 |
viewId = "view-tier";
|
| 796 |
}
|
| 797 |
-
|
| 798 |
document.querySelectorAll('[id^="view-"]').forEach(s => s.hidden = s.id !== viewId);
|
| 799 |
|
| 800 |
if (t === "families") {
|
|
@@ -803,7 +823,8 @@ function setTab(t) {
|
|
| 803 |
renderCharts();
|
| 804 |
} else if (t === "marc1" || t === "marc2" || t === "marc3") {
|
| 805 |
const tierId = t.replace("marc", "");
|
| 806 |
-
|
|
|
|
| 807 |
renderTierCharts(tierId);
|
| 808 |
renderTable(true, tierId);
|
| 809 |
} else {
|
|
@@ -813,8 +834,6 @@ function setTab(t) {
|
|
| 813 |
document.querySelectorAll(".tab").forEach(b => b.addEventListener("click", () => setTab(b.dataset.tab)));
|
| 814 |
|
| 815 |
// βββ Filters ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 816 |
-
buildOrgChips("orgChips", org, (newOrg) => { org = newOrg; buildOrgChips("orgChips", org, (o)=>{org=o;renderTable(false);}); renderTable(false); });
|
| 817 |
-
|
| 818 |
document.getElementById("sizeSel").addEventListener("change", e => { size = e.target.value; renderTable(false); });
|
| 819 |
document.getElementById("sortSel").addEventListener("change", e => { sortKey = e.target.value; renderTable(false); });
|
| 820 |
document.getElementById("famTierSel").addEventListener("change", () => renderFamilies());
|
|
@@ -864,10 +883,11 @@ async function init() {
|
|
| 864 |
}
|
| 865 |
const orgs = [...new Set(models.map(m => m.org))].sort();
|
| 866 |
orgs.forEach((o, i) => orgColor[o] = PALETTE[i % PALETTE.length]);
|
| 867 |
-
|
| 868 |
buildSortOptions();
|
| 869 |
-
|
| 870 |
-
|
|
|
|
| 871 |
setTab("overall");
|
| 872 |
}
|
| 873 |
init();
|
|
|
|
| 66 |
.org { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--muted); }
|
| 67 |
.dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
|
| 68 |
.num { font-variant-numeric: tabular-nums; }
|
|
|
|
| 69 |
.muted { color: var(--muted); }
|
| 70 |
.links a { color: var(--clay); text-decoration: none; font-size: 13px; margin-right: 12px; }
|
| 71 |
.links a:hover { text-decoration: underline; }
|
|
|
|
| 173 |
<tbody id="boardBody"></tbody>
|
| 174 |
</table>
|
| 175 |
</div>
|
| 176 |
+
<div class="legend">EM = exact grid match. fmt = output parses as a grid. Overall = mean across MARC-1/2/3. pass@n > EM means the model has latent rule inference it doesn't always land. Cell color scales from clay (low) to sage (high).</div>
|
| 177 |
</section>
|
| 178 |
|
| 179 |
<!-- TIER TABS (MARC-1, MARC-2, MARC-3) -->
|
| 180 |
<section id="view-tier" hidden>
|
| 181 |
+
|
| 182 |
<div class="info-box">
|
| 183 |
+
<b>Understanding the graphs:</b> Small models often fall back on statistical guessing rather than solving the rule.
|
| 184 |
<ul>
|
| 185 |
<li>The <b style="color:#e05e5e;">Red dashed line</b> is the mathematical random-guessing baseline.</li>
|
| 186 |
<li>The <b style="color:#e0b25c;">Yellow dashed line</b> is <b>2Γ the random baseline</b>. Scoring below this generally means the model is just guessing, or worse, copying the input.</li>
|
| 187 |
+
<li>The <b style="color:#8fb395;">Green dashed line</b> is the <b>average human</b> score for that family and tier.</li>
|
| 188 |
</ul>
|
| 189 |
</div>
|
| 190 |
|
|
|
|
| 410 |
const esc = s => String(s ?? "").replace(/[&<>"']/g, c => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
|
| 411 |
const fmtPct = v => v == null ? "β" : (v * 100).toFixed(1) + "%";
|
| 412 |
const fmtNum = v => Number.isInteger(v) ? String(v) : (+v).toFixed(3).replace(/\.?0+$/,"");
|
| 413 |
+
const isHuman = m => /human/i.test(m?.name || "");
|
| 414 |
|
| 415 |
function parseParams(s) {
|
| 416 |
if (!s) return NaN;
|
|
|
|
| 462 |
return match ? match[1] : base;
|
| 463 |
}
|
| 464 |
|
| 465 |
+
// Red (low) -> green (high) heat color, shared by every table
|
| 466 |
+
function heatColor(v) {
|
| 467 |
+
if (v == null) return "transparent";
|
| 468 |
+
const t = Math.max(0, Math.min(1, v));
|
| 469 |
+
const lo = [74, 48, 38], hi = [45, 58, 47];
|
| 470 |
+
const mix = lo.map((c, i) => Math.round(c + (hi[i] - c) * t));
|
| 471 |
+
return `rgb(${mix[0]},${mix[1]},${mix[2]})`;
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
// βββ Sort βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 475 |
function buildSortOptions() {
|
| 476 |
const sel = document.getElementById("sortSel");
|
|
|
|
| 510 |
document.getElementById(elId).innerHTML =
|
| 511 |
chip("all","All",models.length) +
|
| 512 |
Object.keys(counts).sort().map(k => chip(k,k,counts[k])).join("");
|
| 513 |
+
|
| 514 |
document.getElementById(elId).onclick = e => {
|
| 515 |
const c = e.target.closest(".chip"); if (!c) return;
|
| 516 |
onPick(c.dataset.org);
|
|
|
|
| 519 |
|
| 520 |
function renderTable(isTierTab, tierId = null) {
|
| 521 |
const key = isTierTab ? "marc"+tierId : sortKeyForTab();
|
| 522 |
+
|
|
|
|
| 523 |
const list = models
|
| 524 |
+
.filter(m => (org === "all" || m.org === org) && (size === "all" || sizeOf(m.params) === size))
|
| 525 |
.sort(SORTS[key]);
|
| 526 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
const countId = isTierTab ? "tierCount" : "count";
|
| 528 |
const headId = isTierTab ? "tierHead" : "boardHead";
|
| 529 |
const bodyId = isTierTab ? "tierBody" : "boardBody";
|
|
|
|
| 542 |
<th>Links</th>
|
| 543 |
</tr>`;
|
| 544 |
|
| 545 |
+
// Heatmap cell: same red -> green gradient as the Families tab
|
| 546 |
+
const cell = v => `<td class="r num hm-cell" ${v != null ? `style="background:${heatColor(v)};color:var(--ink)"` : ""}>${v != null ? fmtPct(v) : '<span class="muted">β</span>'}</td>`;
|
| 547 |
+
|
| 548 |
document.getElementById(bodyId).innerHTML = list.length ? list.map((m,i) => {
|
| 549 |
const c = orgColor[m.org] || "#888";
|
| 550 |
const ov = overallEm(m), t1 = tierValue(m,"1","em"), t2 = tierValue(m,"2","em"), t3 = tierValue(m,"3","em"), f = overallFmt(m);
|
|
|
|
| 552 |
if (m.links?.card) links.push(`<a href="${esc(m.links.card)}" target="_blank" rel="noopener">Card</a>`);
|
| 553 |
if (m.links?.demo) links.push(`<a href="${esc(m.links.demo)}" target="_blank" rel="noopener">Demo</a>`);
|
| 554 |
const mode = m.chat ? `<span class="mode chat">chat</span>` : `<span class="mode base">base</span>`;
|
|
|
|
| 555 |
return `<tr>
|
| 556 |
<td class="rank">${i+1}</td>
|
| 557 |
<td class="name">${esc(m.name)}</td>
|
| 558 |
<td><span class="org"><span class="dot" style="background:${c}"></span>${esc(m.org)}</span></td>
|
| 559 |
<td class="num">${esc(m.params)}</td>
|
| 560 |
<td>${mode}</td>
|
| 561 |
+
${cell(ov)}
|
| 562 |
+
${cell(t1)}
|
| 563 |
+
${cell(t2)}
|
| 564 |
+
${cell(t3)}
|
| 565 |
+
${cell(f)}
|
| 566 |
<td class="links">${links.join("") || '<span class="muted">β</span>'}</td>
|
| 567 |
</tr>`;
|
| 568 |
}).join("") : `<tr><td colspan="11" class="empty">No models match these filters</td></tr>`;
|
|
|
|
| 578 |
if (!options || options.random === undefined) return;
|
| 579 |
const { ctx, chartArea: { top, bottom, left, right }, scales: { y } } = chart;
|
| 580 |
ctx.save();
|
| 581 |
+
|
| 582 |
// Red line: 1x Random
|
| 583 |
if (options.random > 0) {
|
| 584 |
const y1 = y.getPixelForValue(options.random);
|
|
|
|
| 592 |
ctx.stroke();
|
| 593 |
}
|
| 594 |
}
|
| 595 |
+
|
| 596 |
// Yellow line: 2x Random
|
| 597 |
if (options.random > 0) {
|
| 598 |
const y2 = y.getPixelForValue(options.random * 2);
|
|
|
|
| 606 |
ctx.stroke();
|
| 607 |
}
|
| 608 |
}
|
| 609 |
+
|
| 610 |
+
// Green line: average human
|
| 611 |
+
if (options.human != null) {
|
| 612 |
+
const yh = y.getPixelForValue(options.human);
|
| 613 |
+
if (yh >= top && yh <= bottom) {
|
| 614 |
+
ctx.beginPath();
|
| 615 |
+
ctx.setLineDash([6, 4]);
|
| 616 |
+
ctx.lineWidth = 1.5;
|
| 617 |
+
ctx.strokeStyle = '#8fb395'; // sage green
|
| 618 |
+
ctx.moveTo(left, yh);
|
| 619 |
+
ctx.lineTo(right, yh);
|
| 620 |
+
ctx.stroke();
|
| 621 |
+
ctx.setLineDash([]);
|
| 622 |
+
ctx.fillStyle = '#8fb395';
|
| 623 |
+
ctx.font = '11px sans-serif';
|
| 624 |
+
ctx.textAlign = 'right';
|
| 625 |
+
ctx.fillText('Human ' + options.human.toFixed(0) + '%', right - 4, yh - 4);
|
| 626 |
+
}
|
| 627 |
+
}
|
| 628 |
ctx.restore();
|
| 629 |
}
|
| 630 |
};
|
|
|
|
| 633 |
function renderTierCharts(tierId) {
|
| 634 |
tierChartInstances.forEach(c => c.destroy());
|
| 635 |
tierChartInstances = [];
|
| 636 |
+
|
| 637 |
const t = String(tierId);
|
| 638 |
+
const human = models.find(isHuman);
|
| 639 |
|
| 640 |
FAMILY_KEYS.forEach(fam => {
|
| 641 |
// Group by family
|
| 642 |
const groups = {};
|
| 643 |
models.forEach(m => {
|
| 644 |
+
if (isHuman(m)) return; // human is drawn as a dashed line instead
|
| 645 |
const groupName = getFamily(m.name);
|
| 646 |
if (!groups[groupName]) groups[groupName] = [];
|
| 647 |
+
|
| 648 |
const p = parseParams(m.params);
|
| 649 |
const emVal = m.families?.[fam]?.[t]?.em;
|
| 650 |
+
|
| 651 |
if (!isNaN(p) && emVal != null) {
|
| 652 |
groups[groupName].push({ x: p, y: emVal * 100, name: m.name, org: m.org });
|
| 653 |
}
|
|
|
|
| 672 |
Chart.defaults.color = "#a29686";
|
| 673 |
Chart.defaults.font.family = '"Avenir Next", "Segoe UI", system-ui, sans-serif';
|
| 674 |
|
| 675 |
+
const humanEm = human?.families?.[fam]?.[t]?.em;
|
| 676 |
+
|
| 677 |
const inst = new Chart(document.getElementById(`tierChart-${fam}`), {
|
| 678 |
type: "scatter",
|
| 679 |
data: { datasets },
|
|
|
|
| 682 |
layout: { padding: { top: 10, right: 10 } },
|
| 683 |
plugins: {
|
| 684 |
legend: { display: false },
|
| 685 |
+
baselines: {
|
| 686 |
+
random: RANDOM_BASELINES[t][fam],
|
| 687 |
+
human: humanEm != null ? humanEm * 100 : null
|
| 688 |
+
},
|
| 689 |
tooltip: { callbacks: { label: c => `${c.raw.name}: ${c.parsed.y.toFixed(1)}%` } }
|
| 690 |
},
|
| 691 |
scales: {
|
|
|
|
| 703 |
}
|
| 704 |
}
|
| 705 |
});
|
| 706 |
+
|
| 707 |
tierChartInstances.push(inst);
|
| 708 |
});
|
| 709 |
}
|
| 710 |
|
| 711 |
// βββ Families Heatmap βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 712 |
function renderFamilies() {
|
| 713 |
const tierSel = document.getElementById("famTierSel").value;
|
| 714 |
const sorted = [...models].sort((a,b) => (familyAvgEm(b, tierSel).avg ?? -1) - (familyAvgEm(a, tierSel).avg ?? -1));
|
|
|
|
| 809 |
function setTab(t) {
|
| 810 |
currentTab = t;
|
| 811 |
document.querySelectorAll(".tab").forEach(b => b.classList.toggle("on", b.dataset.tab === t));
|
| 812 |
+
|
| 813 |
let viewId = "view-" + t;
|
| 814 |
if (t === "marc1" || t === "marc2" || t === "marc3") {
|
| 815 |
viewId = "view-tier";
|
| 816 |
}
|
| 817 |
+
|
| 818 |
document.querySelectorAll('[id^="view-"]').forEach(s => s.hidden = s.id !== viewId);
|
| 819 |
|
| 820 |
if (t === "families") {
|
|
|
|
| 823 |
renderCharts();
|
| 824 |
} else if (t === "marc1" || t === "marc2" || t === "marc3") {
|
| 825 |
const tierId = t.replace("marc", "");
|
| 826 |
+
const pick = o => { org = o; buildOrgChips("tierOrgChips", org, pick); renderTable(true, tierId); };
|
| 827 |
+
buildOrgChips("tierOrgChips", org, pick);
|
| 828 |
renderTierCharts(tierId);
|
| 829 |
renderTable(true, tierId);
|
| 830 |
} else {
|
|
|
|
| 834 |
document.querySelectorAll(".tab").forEach(b => b.addEventListener("click", () => setTab(b.dataset.tab)));
|
| 835 |
|
| 836 |
// βββ Filters ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
| 837 |
document.getElementById("sizeSel").addEventListener("change", e => { size = e.target.value; renderTable(false); });
|
| 838 |
document.getElementById("sortSel").addEventListener("change", e => { sortKey = e.target.value; renderTable(false); });
|
| 839 |
document.getElementById("famTierSel").addEventListener("change", () => renderFamilies());
|
|
|
|
| 883 |
}
|
| 884 |
const orgs = [...new Set(models.map(m => m.org))].sort();
|
| 885 |
orgs.forEach((o, i) => orgColor[o] = PALETTE[i % PALETTE.length]);
|
| 886 |
+
|
| 887 |
buildSortOptions();
|
| 888 |
+
const pickOverall = o => { org = o; buildOrgChips("orgChips", org, pickOverall); renderTable(false); };
|
| 889 |
+
buildOrgChips("orgChips", org, pickOverall);
|
| 890 |
+
|
| 891 |
setTab("overall");
|
| 892 |
}
|
| 893 |
init();
|