Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions tests/js/dashboard_e2e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,28 @@ try {
check(heat.distinct > 1, `matrix is not a heatmap: only ${heat.distinct} distinct fill colour(s)`);
ok(`heatmap rendered in ${matrixMs}ms: ${heat.filled} filled cells, ${heat.distinct} distinct colours (e.g. ${JSON.stringify(heat.sample)})`);

// the heatmap is self-explanatory: a colour-scale legend with a gradient
// bar, the metric's min/max, a scale label and the SOTA marker.
const legend = await page.evaluate(() => {
const l = document.querySelector(".matrix-legend");
if (!l) return null;
return {
label: (l.querySelector(".legend-label")?.textContent || "").trim(),
min: (l.querySelector(".legend-min")?.textContent || "").trim(),
max: (l.querySelector(".legend-max")?.textContent || "").trim(),
hasBar: !!l.querySelector(".legend-bar"),
sota: (l.querySelector(".legend-sota")?.textContent || "").trim(),
};
});
check(!!legend, "heatmap legend missing");
if (legend) {
check(legend.label.length > 0, "legend has no scale label");
check(legend.hasBar, "legend has no gradient bar");
check(/^\d/.test(legend.min) && /^\d/.test(legend.max), `legend min/max not numeric: ${legend.min}/${legend.max}`);
check(legend.sota.length > 0, "legend has no SOTA marker label");
ok(`heatmap legend: "${legend.label}" ${legend.min}→${legend.max}, bar + SOTA marker present`);
}

// switch the metric and confirm it stays a heatmap (updateMatrixMetric path)
const hasSelect = await page.$(".matrix-metric-select");
if (hasSelect) {
Expand Down
32 changes: 32 additions & 0 deletions web/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,7 @@ header#topBar {
.matrix-controls {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--space-12);
margin-bottom: var(--space-12);
font-size: var(--text-base);
Expand Down Expand Up @@ -1006,6 +1007,37 @@ header#topBar {
.cell-sota { background: var(--green-dim); color: var(--green); font-weight: 700; }
.cell-empty { background: var(--bg-base); color: var(--text-muted); }

/* ── Heatmap colour-scale legend ──────────────────────────────────── */
.matrix-legend {
display: inline-flex;
align-items: center;
gap: var(--space-6);
font-size: var(--text-2xs);
color: var(--text-dim);
}
.matrix-legend .legend-label { font-weight: 600; }
.matrix-legend .legend-min, .matrix-legend .legend-max { font-variant-numeric: tabular-nums; }
.matrix-legend .legend-bar {
width: 72px;
height: 9px;
border-radius: var(--radius-sm);
border: 1px solid var(--border);
background: linear-gradient(to right, var(--heat-lo), var(--heat-hi));
}
.matrix-legend .legend-sota {
display: inline-flex;
align-items: center;
gap: var(--space-4);
margin-left: var(--space-6);
}
.matrix-legend .legend-sota-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--green-dim);
border: 1.5px solid var(--green);
}

/* ── Gaps in Evidence tab ─────────────────────────────────────────── */

.gaps-list { display: flex; flex-direction: column; gap: 8px; }
Expand Down
30 changes: 29 additions & 1 deletion web/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -991,11 +991,28 @@ function buildMatrixHeat(matrix, metric) {
if (v > max) max = v;
}
const span = max - min;
return (value) => {
const map = (value) => {
if (value == null) return '';
const t = span > 0 ? (Number(value) - min) / span : 0.5;
return _lerpRgb(lo, hi, Math.max(0, Math.min(1, t)));
};
// Expose the range so the legend can label the gradient endpoints.
map.min = min;
map.max = max;
return map;
}

// The colour-scale legend that makes the heatmap self-explanatory: a low→high
// gradient bar tagged with the metric's actual min/max, plus the SOTA marker.
function matrixHeatLegendHtml(heat) {
const hasRange = isFinite(heat.min) && isFinite(heat.max);
return `<span class="matrix-legend">
<span class="legend-label">${esc(tr('evidence.heatScale'))}</span>
<span class="legend-min">${hasRange ? heat.min.toFixed(1) : ''}</span>
<span class="legend-bar" aria-hidden="true"></span>
<span class="legend-max">${hasRange ? heat.max.toFixed(1) : ''}</span>
<span class="legend-sota"><span class="legend-sota-dot" aria-hidden="true"></span>${esc(tr('evidence.heatSota'))}</span>
</span>`;
}

function renderMatrix(container, matrix) {
Expand All @@ -1022,6 +1039,7 @@ function renderMatrix(container, matrix) {
}
html += '</select>';
html += `<span class="matrix-info">${esc(tr('common.methodsCount', { count: matrix.methods.length }))} x ${esc(tr('common.datasetsCount', { count: matrix.datasets.length }))}</span>`;
html += matrixHeatLegendHtml(heat);
html += '</div>';

html += '<div class="matrix-scroll"><table class="matrix-table">';
Expand Down Expand Up @@ -1062,6 +1080,16 @@ function updateMatrixMetric(selectEl) {

const metric = selectEl.value;
const heat = buildMatrixHeat(matrix, metric);

// Keep the legend's gradient endpoints in sync with the selected metric.
const minEl = container.querySelector('.matrix-legend .legend-min');
const maxEl = container.querySelector('.matrix-legend .legend-max');
if (minEl && maxEl) {
const hasRange = isFinite(heat.min) && isFinite(heat.max);
minEl.textContent = hasRange ? heat.min.toFixed(1) : '';
maxEl.textContent = hasRange ? heat.max.toFixed(1) : '';
}

const rows = container.querySelectorAll('tbody tr');

rows.forEach((row, mi) => {
Expand Down
4 changes: 4 additions & 0 deletions web/static/js/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@
"evidence.select": "Select research area:",
"evidence.option": "-- Select a leaf research area --",
"evidence.hint": "Select a leaf research area to view the benchmark matrix.",
"evidence.heatScale": "Cell colour = value (per metric)",
"evidence.heatSota": "best (SOTA)",
"evidence.gaps": "Matrix Gaps",
"papers.title": "Source Paper",
"papers.filter": "Filter source papers...",
Expand Down Expand Up @@ -524,6 +526,8 @@
"evidence.select": "选择研究领域:",
"evidence.option": "-- 选择叶子研究领域 --",
"evidence.hint": "选择叶子研究领域以查看基准矩阵。",
"evidence.heatScale": "格子颜色 = 数值高低(按指标)",
"evidence.heatSota": "最佳(SOTA)",
"evidence.gaps": "矩阵空白",
"papers.title": "文献",
"papers.filter": "筛选文献...",
Expand Down
Loading