| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- <!doctype html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>sm121 — tested recipes</title>
- <style>
- :root { color-scheme: light dark; }
- body { font: 13px/1.45 system-ui, sans-serif; margin: 16px; }
- h1 { font-size: 18px; margin: 0 0 10px; }
- #bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
- #q { flex: 1 1 220px; max-width: 380px; padding: 4px 8px; }
- select, button, #cols summary { padding: 3px 8px; cursor: pointer; }
- #cols { position: relative; display: inline-block; }
- #colbox { position: absolute; z-index: 5; background: canvas; border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
- padding: 6px 10px; max-height: 60vh; overflow: auto; white-space: nowrap; }
- #colbox label { display: block; }
- table { border-collapse: collapse; width: 100%; }
- th, td { border: 1px solid color-mix(in srgb, currentColor 20%, transparent); padding: 3px 8px; text-align: left; vertical-align: top; }
- th { cursor: pointer; user-select: none; white-space: nowrap; position: sticky; top: 0; background: canvas; }
- tr.g1 td { font-weight: 700; background: color-mix(in srgb, currentColor 8%, canvas); cursor: pointer; }
- tr.g2 td { font-style: italic; background: color-mix(in srgb, currentColor 4%, canvas); cursor: pointer; }
- tr.hist td { font-family: ui-monospace, monospace; font-size: 12px; background: color-mix(in srgb, currentColor 3%, canvas); }
- a.open { color: inherit; text-decoration: none; border: 1px solid color-mix(in srgb, currentColor 30%, transparent); border-radius: 3px; padding: 0 4px; }
- a.open:hover { background: color-mix(in srgb, currentColor 12%, canvas); }
- tr.row { cursor: pointer; }
- td.num { text-align: right; white-space: nowrap; }
- #count { color: gray; }
- .dim { color: gray; }
- </style>
- </head>
- <body>
- <h1>sm121 — tested recipes & benchmark history</h1>
- <div id="bar">
- <input id="q" type="search" placeholder="Search recipes, models, images, labels…">
- <label>Group:
- <select id="group">
- <option value="mq" selected>Model → Quant</option>
- <option value="model">Model</option>
- <option value="quant">Quant</option>
- <option value="engine">Engine</option>
- <option value="">None (flat)</option>
- </select>
- </label>
- <details id="cols"><summary>Columns ▾</summary><div id="colbox"></div></details>
- <label>Rows/page:
- <select id="size"><option>25</option><option selected>50</option><option>100</option><option value="0">All</option></select>
- </label>
- <button id="prev">‹</button><span id="count"></span><button id="next">›</button>
- </div>
- <table id="tbl"><thead></thead><tbody></tbody></table>
- <script>
- "use strict";
- const DATA = {{DATA}};
- const COLS = [
- ["name", "Recipe"], ["title", "Title"], ["model", "Model"], ["quant", "Quant"],
- ["engine", "Engine"], ["image", "Image"], ["nodes", "Nodes"], ["tp", "TP"],
- ["deploy", "Deploy"], ["port", "Port"], ["n", "Reports"], ["last", "Last bench"],
- ["points", "Points"], ["pct", "Score /100"],
- ];
- const NUM = new Set(["nodes", "tp", "port", "n", "pct"]);
- // latest report matching a predicate, for the points / score columns
- function latest(r, f) { for (let i = r.reports.length - 1; i >= 0; i--) if (f(r.reports[i])) return r.reports[i]; return null; }
- function cell(r, k) {
- if (k === "n") return r.reports.length;
- if (k === "last") return r.reports.length ? r.reports[r.reports.length - 1].date : "";
- if (k === "points") { const e = latest(r, e => e.points); return e ? e.points : ""; }
- if (k === "pct") { const e = latest(r, e => e.pct != null); return e ? e.pct : ""; }
- return r[k];
- }
- // deep-link state: the full view (filter, grouping, sort, columns, paging,
- // collapsed groups) lives in the URL — survives any reload and is shareable
- const P = new URLSearchParams(location.search);
- const size0 = +(P.get("size") ?? 50);
- const S = {
- q: P.get("q") ?? "",
- group: P.get("group") ?? "mq",
- sort: P.get("sort") ?? "name",
- dir: P.get("dir") ? +P.get("dir") : 1,
- size: [25, 50, 100, 0].includes(size0) ? size0 : 50,
- page: Math.max(0, +(P.get("page") ?? 0)),
- hidden: new Set((P.get("hidden") ?? "title,image,port").split(",").filter(Boolean)),
- closed: new Set((P.get("closed") ?? "").split("|").map(decodeURIComponent).filter(Boolean)),
- openRow: P.get("open") ?? null,
- };
- document.getElementById("q").value = S.q;
- document.getElementById("group").value = S.group;
- document.getElementById("size").value = String(S.size);
- function syncUrl() {
- const p = new URLSearchParams();
- if (S.q) p.set("q", S.q);
- if (S.group !== "mq") p.set("group", S.group);
- if (S.sort !== "name") p.set("sort", S.sort);
- if (S.dir !== 1) p.set("dir", String(S.dir));
- if (S.size !== 50) p.set("size", String(S.size));
- if (S.page) p.set("page", String(S.page));
- const hidden = [...S.hidden].sort();
- if (hidden.join(",") !== ["title", "image", "port"].sort().join(",")) p.set("hidden", hidden.join(","));
- const closed = [...S.closed].map(encodeURIComponent);
- if (closed.length) p.set("closed", closed.join("|"));
- if (S.openRow) p.set("open", S.openRow);
- const qs = p.toString();
- history.replaceState(null, "", qs ? "?" + qs : location.pathname);
- }
- const esc = s => String(s).replace(/[&<>"]/g, c => ({"&":"&","<":"<",">":">",'"':"""}[c]));
- function filtered() {
- const q = S.q.toLowerCase();
- return DATA.filter(r => !q || (r.name + " " + r.title + " " + r.model + " " + r.quant + " " +
- r.engine + " " + r.image + " " + r.deploy + " " +
- r.reports.map(e => e.date + " " + e.label).join(" ")).toLowerCase().includes(q));
- }
- function sorted(rows) {
- const gk = { mq: r => r.model + "\0" + r.quant, model: r => r.model,
- quant: r => r.quant, engine: r => r.engine }[S.group];
- const k = S.sort, d = S.dir;
- const val = r => {
- if (k === "points") { const e = latest(r, e => e.ratio != null); return e ? e.ratio : -1; }
- const v = cell(r, k); return NUM.has(k) || k === "last" ? v : String(v).toLowerCase();
- };
- return rows.slice().sort((a, b) => {
- if (gk) { const ga = gk(a), gb = gk(b); if (ga !== gb) return ga.localeCompare(gb); }
- let x = val(a), y = val(b);
- if (x === y) return a.name.localeCompare(b.name);
- if (typeof x === "number" && typeof y === "number") return (x - y) * d;
- return String(x).localeCompare(String(y)) * d;
- });
- }
- function gkey(r, level) {
- if (S.group === "mq") return level === 1 ? r.model : r.model + "\u0001" + r.quant;
- const f = { model: "model", quant: "quant", engine: "engine" }[S.group];
- return level === 1 ? r[f] : null;
- }
- function render() {
- syncUrl();
- // visible columns
- const vis = COLS.filter(c => !S.hidden.has(c[0]));
- document.querySelector("thead").innerHTML = "<tr>" + vis.map(c => {
- const arrow = S.sort === c[0] ? (S.dir > 0 ? " ▲" : " ▼") : "";
- return `<th data-k="${c[0]}">${esc(c[1])}${arrow}</th>`;
- }).join("") + "</tr>";
- const all = sorted(filtered());
- const size = +document.getElementById("size").value;
- const pages = size ? Math.max(1, Math.ceil(all.length / size)) : 1;
- if (S.page >= pages) S.page = pages - 1;
- const rows = size ? all.slice(S.page * size, S.page * size + size) : all;
- document.getElementById("count").textContent = size
- ? `${all.length ? S.page * size + 1 : 0}–${Math.min(all.length, S.page * size + size)} / ${all.length}` : `${all.length}`;
- // group tree over the current page slice
- const out = [];
- let g1 = null, g2 = null;
- for (const r of rows) {
- if (S.group) {
- const k1 = gkey(r, 1);
- if (k1 !== g1) {
- g1 = k1; g2 = null;
- const closed = S.closed.has("1\0" + k1);
- out.push(`<tr class="g1" data-g="${encodeURIComponent("1\0" + k1)}"><td colspan="${vis.length}">${closed ? "▶" : "▼"} ${esc(k1)} <span class="dim">(${all.filter(x => gkey(x, 1) === k1).length})</span></td></tr>`);
- }
- if (S.closed.has("1\0" + k1)) continue;
- const k2 = gkey(r, 2);
- if (k2 !== null) {
- if (k2 !== g2) {
- g2 = k2;
- const closed = S.closed.has("2\0" + k2);
- out.push(`<tr class="g2" data-g="${encodeURIComponent("2\0" + k2)}"><td colspan="${vis.length}">${closed ? "▶" : "▼"} ${esc(r.quant)} <span class="dim">(${all.filter(x => gkey(x, 2) === k2).length})</span></td></tr>`);
- }
- if (S.closed.has("2\0" + k2)) continue;
- }
- }
- out.push("<tr class=\"row\" data-n=\"" + esc(r.name) + "\">" + vis.map(c => {
- const v = cell(r, c[0]);
- const lr = r.reports.length ? r.reports[r.reports.length - 1] : null;
- const tip = c[0] === "last" && lr
- ? ` title="${esc(lr.date + (lr.time ? " " + lr.time : ""))}"` : "";
- return `<td${NUM.has(c[0]) ? " class=\"num\"" : ""}${tip}>${v === "" || v == null ? '<span class="dim">—</span>' : esc(v)}</td>`;
- }).join("") + "</tr>");
- if (S.openRow === r.name)
- out.push(`<tr class="hist"><td colspan="${vis.length}">${r.reports.length ? r.reports.map(e => {
- const fl = e.files && e.files.length ? e.files : [e.file];
- return `<span>${fl.map(f =>
- `<a class="open" href="/r/${encodeURIComponent(r.name)}/${encodeURIComponent(f)}" target="_blank" title="open ${esc(f)}">${esc(f.split('.').pop())}</a>`).join(" ")}</span> ` +
- `${esc(e.date)}${e.time ? "·" + esc(e.time) : ""} ${esc(e.label)} — ${e.pct != null ? esc(e.pct) + " /100" : '<span class="dim">no score</span>'}${e.points ? " (" + esc(e.points) + ")" : ""}`;
- }).join("<br>") : "no reports"}</td></tr>`);
- }
- document.querySelector("tbody").innerHTML = out.join("");
- }
- document.querySelector("thead").addEventListener("click", e => {
- const th = e.target.closest("th"); if (!th) return;
- const k = th.dataset.k;
- if (S.sort === k) S.dir = -S.dir; else { S.sort = k; S.dir = 1; }
- render();
- });
- document.querySelector("tbody").addEventListener("click", e => {
- const g = e.target.closest("tr.g1,tr.g2");
- if (g) { const k = decodeURIComponent(g.dataset.g); S.closed.has(k) ? S.closed.delete(k) : S.closed.add(k); render(); return; }
- const row = e.target.closest("tr.row");
- if (row) { S.openRow = S.openRow === row.dataset.n ? null : row.dataset.n; render(); }
- });
- document.getElementById("q").addEventListener("input", e => { S.q = e.target.value; S.page = 0; render(); });
- document.getElementById("group").addEventListener("change", e => { S.group = e.target.value; S.closed.clear(); S.page = 0; render(); });
- document.getElementById("size").addEventListener("change", () => { S.page = 0; render(); });
- document.getElementById("prev").addEventListener("click", () => { if (S.page > 0) { S.page--; render(); } });
- document.getElementById("next").addEventListener("click", () => { S.page++; render(); });
- // column checkboxes
- document.getElementById("colbox").innerHTML = COLS.map(c =>
- `<label><input type="checkbox" data-k="${c[0]}" ${S.hidden.has(c[0]) ? "" : "checked"}> ${esc(c[1])}</label>`).join("");
- document.getElementById("colbox").addEventListener("change", e => {
- const k = e.target.dataset.k; if (!k) return;
- e.target.checked ? S.hidden.delete(k) : S.hidden.add(k);
- render();
- });
- render();
- </script>
- </body>
- </html>
|