Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
94 changes: 80 additions & 14 deletions evalscope/report/template/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Design tokens ───────────────────────────────────────────── */
:root {
:root,
[data-theme="dark"] {
color-scheme: dark;
--bg: #0c0c1a;
--bg-card: #12122b;
--bg-card2: #16163a;
Expand All @@ -23,6 +25,23 @@
--green: #0F9C7E;
--green-dim: rgba(15, 156, 126, 0.12);
--yellow: #fbbf24;
--surface-subtle: rgba(255,255,255,0.015);
--surface-muted: rgba(255,255,255,0.025);
--surface-raised: rgba(255,255,255,0.03);
--surface-hover: rgba(255,255,255,0.06);
--header-bg: linear-gradient(140deg, #0e0e28 0%, #13163a 55%, #0b1024 100%);
--title-start: #e2e8f0;
--brand-wordmark: #D8D8E3;
--inner-active-text: #fff;
--control-active-text: #0d1117;
--scrollbar-thumb: rgba(129,109,248,0.2);
--scrollbar-thumb-hover: rgba(129,109,248,0.38);
--plotly-paper: rgba(0,0,0,0);
--plotly-text: #e2e8f0;
--plotly-grid: rgba(99,179,237,0.08);
--plotly-zero: rgba(99,179,237,0.12);
--plotly-hover-bg: #16163a;
--plotly-hover-border: rgba(129,109,248,0.28);
--radius-xs: 4px;
--radius-sm: 8px;
--radius: 12px;
Expand All @@ -34,6 +53,50 @@
--transition: 0.18s ease;
}

[data-theme="light"] {
color-scheme: light;
--bg: #faf9f5;
--bg-card: #ffffff;
--bg-card2: #f5f0e7;
--bg-deep: #f0ebe1;
--bg-hover: #ebe5da;
--border: #e6dfd8;
--border-md: #d6cdbe;
--border-strong: #c1b6a3;
--text: #141413;
--text-muted: #6c6a64;
--text-dim: #8e8b82;
--accent: #6c57e8;
--accent-dim: rgba(108, 87, 232, 0.14);
--accent-glow: rgba(108, 87, 232, 0.22);
--accent-dark: #4f3ec8;
--purple: #7c6be0;
--purple-dim: rgba(124, 107, 224, 0.12);
--green: #0a8a6e;
--green-dim: rgba(10, 138, 110, 0.12);
--yellow: #d97706;
--surface-subtle: rgba(20,20,19,0.015);
--surface-muted: rgba(20,20,19,0.025);
--surface-raised: rgba(20,20,19,0.03);
--surface-hover: rgba(20,20,19,0.06);
--header-bg: linear-gradient(140deg, #ffffff 0%, #f5f0e7 55%, #f0ebe1 100%);
--title-start: #141413;
--brand-wordmark: #141413;
--inner-active-text: #4f3ec8;
--control-active-text: #ffffff;
--scrollbar-thumb: rgba(108,87,232,0.22);
--scrollbar-thumb-hover: rgba(108,87,232,0.42);
--plotly-paper: rgba(0,0,0,0);
--plotly-text: #141413;
--plotly-grid: rgba(108,87,232,0.12);
--plotly-zero: rgba(108,87,232,0.20);
--plotly-hover-bg: #ffffff;
--plotly-hover-border: #d6cdbe;
--shadow-sm: 0 1px 2px rgba(20,20,19,0.04), 0 4px 12px rgba(20,20,19,0.06);
--shadow: 0 4px 16px rgba(20,20,19,0.07), 0 12px 32px rgba(20,20,19,0.05);
--shadow-lg: 0 12px 24px rgba(20,20,19,0.09), 0 24px 48px rgba(20,20,19,0.07);
}

html { scroll-behavior: smooth; }

body {
Expand All @@ -48,9 +111,9 @@ body {
/* thin global scrollbar */
::-webkit-scrollbar { width: 5px; height: 5px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(129,109,248,0.2); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: rgba(129,109,248,0.38); }
* { scrollbar-width: thin; scrollbar-color: rgba(129,109,248,0.2) transparent; }
::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
* { scrollbar-width: thin; scrollbar-color: var(--scrollbar-thumb) transparent; }

/* ── Layout ──────────────────────────────────────────────────── */
.container {
Expand Down Expand Up @@ -204,10 +267,11 @@ body {
height: 20px;
width: auto;
}
.brand-logo svg path[fill="#D8D8E3"] { fill: var(--brand-wordmark); }

/* ── Header card ─────────────────────────────────────────────── */
.report-header {
background: linear-gradient(140deg, #0e0e28 0%, #13163a 55%, #0b1024 100%);
background: var(--header-bg);
border: 1px solid var(--border-md);
border-radius: var(--radius-xl);
padding: 2.75rem 2.5rem 2.25rem;
Expand Down Expand Up @@ -258,7 +322,7 @@ body {
.report-title {
font-size: 2.4rem;
font-weight: 800;
background: linear-gradient(130deg, #e2e8f0 30%, var(--accent) 100%);
background: linear-gradient(130deg, var(--title-start) 30%, var(--accent) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
Expand All @@ -276,7 +340,7 @@ body {
display: flex;
flex-direction: column;
gap: 0.1rem;
background: rgba(255,255,255,0.03);
background: var(--surface-raised);
border: 1px solid var(--border-md);
border-radius: var(--radius-sm);
padding: 0.5rem 0.9rem;
Expand Down Expand Up @@ -347,7 +411,7 @@ body {
gap: 0.75rem;
padding: 0.8rem 1.25rem;
border-bottom: 1px solid var(--border);
background: rgba(255,255,255,0.015);
background: var(--surface-subtle);
}
.card-title {
font-size: 0.78rem;
Expand Down Expand Up @@ -387,6 +451,8 @@ body {
/* ── Chart wrap ──────────────────────────────────────────────── */
.chart-wrap { padding: 0.75rem; min-height: 350px; }
.chart-wrap > div { width: 100% !important; }
.modebar-btn path { fill: var(--plotly-text) !important; }
.modebar-btn:hover path { fill: var(--accent) !important; }

/* ── Tables ──────────────────────────────────────────────────── */
.table-wrap {
Expand All @@ -401,7 +467,7 @@ table {
background: transparent;
}

thead tr { background: rgba(255,255,255,0.025); }
thead tr { background: var(--surface-muted); }

th {
padding: 0.7rem 1.1rem;
Expand Down Expand Up @@ -475,7 +541,7 @@ tbody tr:hover td { background: rgba(129,109,248,0.04); }
justify-content: space-between;
gap: 0.75rem;
user-select: none;
background: rgba(255,255,255,0.02);
background: var(--surface-muted);
transition: background var(--transition);
}
.ds-summary::-webkit-details-marker { display: none; }
Expand Down Expand Up @@ -543,13 +609,13 @@ tbody tr:hover td { background: rgba(129,109,248,0.04); }
}
.inner-toggle > summary::-webkit-details-marker { display: none; }
.inner-toggle > summary:hover {
color: #fff;
color: var(--inner-active-text);
background: rgba(129,109,248,0.18);
border-color: rgba(129,109,248,0.5);
box-shadow: 0 0 8px rgba(129,109,248,0.2);
}
.inner-toggle[open] > summary {
color: #fff;
color: var(--inner-active-text);
background: rgba(129,109,248,0.15);
border-color: rgba(129,109,248,0.45);
}
Expand Down Expand Up @@ -685,8 +751,8 @@ tbody tr:hover td { background: rgba(129,109,248,0.04); }
letter-spacing: 0.04em;
line-height: 1.4;
}
.lang-btn:hover { background: rgba(255,255,255,0.06); color: var(--text); }
.lang-btn.active { background: var(--accent); color: #0d1117; }
.lang-btn:hover { background: var(--surface-hover); color: var(--text); }
.lang-btn.active { background: var(--accent); color: var(--control-active-text); }

/* ── Responsive ──────────────────────────────────────────────── */
@media (max-width: 900px) {
Expand Down
2 changes: 1 addition & 1 deletion evalscope/report/template/css/perf_extra.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
padding: 0 0.75rem;
overflow-x: auto;
scrollbar-width: none;
background: rgba(255,255,255,0.015);
background: var(--surface-subtle);
}
.tab-nav::-webkit-scrollbar { display: none; }

Expand Down
60 changes: 60 additions & 0 deletions evalscope/report/template/js/shared.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
/* ── Theme synchronisation ────────────────────────────────────── */

function applyPlotlyTheme() {
if (!window.Plotly) return;

var styles = getComputedStyle(document.documentElement);
var paper = styles.getPropertyValue('--plotly-paper').trim();
var text = styles.getPropertyValue('--plotly-text').trim();
var grid = styles.getPropertyValue('--plotly-grid').trim();
var zero = styles.getPropertyValue('--plotly-zero').trim();
var hoverBg = styles.getPropertyValue('--plotly-hover-bg').trim();
var hoverBorder = styles.getPropertyValue('--plotly-hover-border').trim();

document.querySelectorAll('.plotly-graph-div').forEach(function (plot) {
var update = {
paper_bgcolor: paper,
plot_bgcolor: paper,
'font.color': text,
'legend.bgcolor': paper,
'legend.font.color': text,
'hoverlabel.bgcolor': hoverBg,
'hoverlabel.bordercolor': hoverBorder,
'hoverlabel.font.color': text
};
var layout = plot._fullLayout || {};

Object.keys(layout).forEach(function (key) {
if (/^[xy]axis\d*$/.test(key)) {
update[key + '.color'] = text;
update[key + '.tickfont.color'] = text;
update[key + '.title.font.color'] = text;
update[key + '.gridcolor'] = grid;
update[key + '.zerolinecolor'] = zero;
} else if (/^polar\d*$/.test(key)) {
update[key + '.bgcolor'] = paper;
['angularaxis', 'radialaxis'].forEach(function (axis) {
update[key + '.' + axis + '.color'] = text;
update[key + '.' + axis + '.tickfont.color'] = text;
update[key + '.' + axis + '.gridcolor'] = grid;
update[key + '.' + axis + '.linecolor'] = zero;
});
}
});

try {
window.Plotly.relayout(plot, update);
} catch (e) {
/* Keep the report usable if one chart cannot be restyled. */
}
});
}

function applyTheme(theme) {
if (theme !== 'light' && theme !== 'dark') return;
document.documentElement.setAttribute('data-theme', theme);
applyPlotlyTheme();
}

applyTheme(document.documentElement.getAttribute('data-theme') || 'dark');

/* ── Language switcher ─────────────────────────────────────────── */

/**
Expand Down
19 changes: 19 additions & 0 deletions evalscope/report/template/js/theme_init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* Apply the shared console theme before first paint. */
(function () {
'use strict';

var theme = 'dark';
try {
var saved = localStorage.getItem('evalscope-theme');
if (saved === 'light' || saved === 'dark') {
theme = saved;
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
theme = 'light';
}
} catch (e) {
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
theme = 'light';
}
}
document.documentElement.setAttribute('data-theme', theme);
}());
3 changes: 3 additions & 0 deletions evalscope/report/template/perf_report.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Performance Report — {{ model }}</title>
<script>
{% include 'js/theme_init.js' %}
</script>
<style>
{% include 'css/base.css' %}
{% include 'css/perf_extra.css' %}
Expand Down
3 changes: 3 additions & 0 deletions evalscope/report/template/report.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Evaluation Report — {{ models | join(", ") if models else "N/A" }}</title>
<script>
{% include 'js/theme_init.js' %}
</script>
<style>
{% include 'css/base.css' %}
</style>
Expand Down
68 changes: 68 additions & 0 deletions tests/report/test_static_report_theme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from pathlib import Path

import pytest
from jinja2 import Environment, FileSystemLoader

_TEMPLATE_DIR = Path(__file__).parents[2] / 'evalscope' / 'report' / 'template'


def _render_template(name: str) -> str:
env = Environment(loader=FileSystemLoader(_TEMPLATE_DIR), autoescape=False)
common = {
'generated_at': '',
'default_lang': 'en',
'plotly_cdn_url': '',
}
if name == 'report.html.j2':
return env.get_template(name).render(
**common,
models=[],
datasets=[],
summary_rows=[],
overview_chart_div='',
sunburst_chart_div='',
dataset_sections=[],
)
return env.get_template(name).render(
**common,
model='',
api_type='',
dataset='',
evalscope_version='',
basic_info={},
summary_columns=[],
summary_rows=[],
best_config={},
recommendations=[],
latency_tabs=[],
throughput_tabs=[],
run_sections=[],
)


@pytest.mark.parametrize('template_name', ['report.html.j2', 'perf_report.html.j2'])
def test_static_report_inherits_console_theme_before_first_paint(template_name: str) -> None:
html = _render_template(template_name)

assert html.index("localStorage.getItem('evalscope-theme')") < html.index('<style>')
assert html.index("document.documentElement.setAttribute('data-theme', theme)") < html.index('<body>')
assert '[data-theme="light"]' in html
assert '--bg: #faf9f5;' in html


@pytest.mark.parametrize('template_name', ['report.html.j2', 'perf_report.html.j2'])
def test_static_report_applies_plotly_theme(template_name: str) -> None:
html = _render_template(template_name)

assert 'function applyPlotlyTheme()' in html
assert "document.querySelectorAll('.plotly-graph-div')" in html
assert 'window.Plotly.relayout(plot, update)' in html
assert "styles.getPropertyValue('--plotly-text')" in html
assert "addEventListener('storage'" not in html


def test_perf_report_uses_theme_aware_shared_surface() -> None:
html = _render_template('perf_report.html.j2')

assert '.tab-nav {' in html
assert 'background: var(--surface-subtle);' in html
Loading