mirror of
https://github.com/status-im/status-app-benchmarks.git
synced 2026-08-27 11:11:13 +00:00
desktop: richer PR listing cards and channel badges
This commit is contained in:
@@ -62,6 +62,51 @@
|
||||
main { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
|
||||
h1 { margin: 0 0 0.25rem; font-size: 1.5rem; }
|
||||
.subtitle { color: var(--muted); margin: 0; }
|
||||
.page-heading {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.65rem 0.85rem;
|
||||
margin: 0 0 0.25rem;
|
||||
}
|
||||
.page-heading h1 { margin: 0; }
|
||||
.channel-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.22rem 0.6rem;
|
||||
border-radius: 999px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.channel-badge-nightly {
|
||||
background: #ddf4ff;
|
||||
color: #0969da;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #dafbe1;
|
||||
color: #1a7f37;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #fff1e5;
|
||||
color: #bc4c00;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.channel-badge-nightly {
|
||||
background: #12263a;
|
||||
color: #58a6ff;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #12261e;
|
||||
color: #3fb950;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #2d1f14;
|
||||
color: #f0883e;
|
||||
}
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
@@ -80,6 +125,50 @@
|
||||
.card:hover { border-color: var(--link); }
|
||||
.card h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
|
||||
.card p { margin: 0; color: var(--muted); font-size: 0.9rem; }
|
||||
.listing-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.listing-card:hover { border-color: var(--link); }
|
||||
.listing-card .listing-main {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
flex: 1;
|
||||
}
|
||||
.listing-card .listing-main h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
|
||||
.listing-card .listing-meta {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.listing-card .listing-meta + .listing-meta { margin-top: 0.2rem; }
|
||||
.listing-card .listing-extra {
|
||||
font-size: 0.85rem;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
.listing-card .listing-extra:hover { text-decoration: underline; }
|
||||
.listing-history {
|
||||
list-style: none;
|
||||
margin: 0.65rem 0 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.listing-history li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
.listing-history .commit {
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
.profile-fact-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Nightly benchmarks — Status App Benchmarks</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
--bg: #f6f8fa;
|
||||
--card: #ffffff;
|
||||
--text: #1f2328;
|
||||
--muted: #656d76;
|
||||
--border: #d0d7de;
|
||||
--link: #0969da;
|
||||
--header-bg: #24292f;
|
||||
--header-text: #ffffff;
|
||||
--accent-data: #57606a;
|
||||
--accent-data-bg: #eaeef2;
|
||||
--accent-wallet: #6e40c9;
|
||||
--accent-wallet-bg: #f3f0ff;
|
||||
--accent-messenger: #1a7f37;
|
||||
--accent-messenger-bg: #dafbe1;
|
||||
--accent-communities: #bc4c00;
|
||||
--accent-communities-bg: #fff1e5;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--bg: #0d1117;
|
||||
--card: #161b22;
|
||||
--text: #e6edf3;
|
||||
--muted: #8b949e;
|
||||
--border: #30363d;
|
||||
--link: #58a6ff;
|
||||
--header-bg: #010409;
|
||||
--header-text: #e6edf3;
|
||||
--accent-data: #8b949e;
|
||||
--accent-data-bg: #21262d;
|
||||
--accent-wallet: #a371f7;
|
||||
--accent-wallet-bg: #2b1f47;
|
||||
--accent-messenger: #3fb950;
|
||||
--accent-messenger-bg: #12261e;
|
||||
--accent-communities: #f0883e;
|
||||
--accent-communities-bg: #2d1f14;
|
||||
}
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
line-height: 1.5;
|
||||
}
|
||||
header {
|
||||
background: var(--header-bg);
|
||||
color: var(--header-text);
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
header a { color: var(--header-text); text-decoration: none; }
|
||||
header a:hover { text-decoration: underline; }
|
||||
main { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
|
||||
h1 { margin: 0 0 0.25rem; font-size: 1.5rem; }
|
||||
.subtitle { color: var(--muted); margin: 0; }
|
||||
.page-heading {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.65rem 0.85rem;
|
||||
margin: 0 0 0.25rem;
|
||||
}
|
||||
.page-heading h1 { margin: 0; }
|
||||
.channel-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.22rem 0.6rem;
|
||||
border-radius: 999px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.channel-badge-nightly {
|
||||
background: #ddf4ff;
|
||||
color: #0969da;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #dafbe1;
|
||||
color: #1a7f37;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #fff1e5;
|
||||
color: #bc4c00;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.channel-badge-nightly {
|
||||
background: #12263a;
|
||||
color: #58a6ff;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #12261e;
|
||||
color: #3fb950;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #2d1f14;
|
||||
color: #f0883e;
|
||||
}
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
.card:hover { border-color: var(--link); }
|
||||
.card h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
|
||||
.card p { margin: 0; color: var(--muted); font-size: 0.9rem; }
|
||||
.listing-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.listing-card:hover { border-color: var(--link); }
|
||||
.listing-card .listing-main {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
flex: 1;
|
||||
}
|
||||
.listing-card .listing-main h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
|
||||
.listing-card .listing-meta {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.listing-card .listing-meta + .listing-meta { margin-top: 0.2rem; }
|
||||
.listing-card .listing-extra {
|
||||
font-size: 0.85rem;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
.listing-card .listing-extra:hover { text-decoration: underline; }
|
||||
.profile-fact-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
.stat-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
align-items: center;
|
||||
}
|
||||
.stat-group-label {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
min-width: 5.5rem;
|
||||
}
|
||||
.stat-chip {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.2rem;
|
||||
border-radius: 999px;
|
||||
padding: 0.15rem 0.55rem;
|
||||
font-size: 0.76rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.stat-chip strong { font-weight: 700; }
|
||||
.stat-chip.stat-zero { opacity: 0.55; }
|
||||
.stat-data {
|
||||
align-self: flex-start;
|
||||
background: var(--accent-data-bg);
|
||||
color: var(--accent-data);
|
||||
font-weight: 600;
|
||||
}
|
||||
.stat-wallet .stat-group-label { color: var(--accent-wallet); }
|
||||
.stat-wallet .stat-chip {
|
||||
background: var(--accent-wallet-bg);
|
||||
color: var(--accent-wallet);
|
||||
}
|
||||
.stat-messenger .stat-group-label { color: var(--accent-messenger); }
|
||||
.stat-messenger .stat-chip {
|
||||
background: var(--accent-messenger-bg);
|
||||
color: var(--accent-messenger);
|
||||
}
|
||||
.stat-communities .stat-group-label { color: var(--accent-communities); }
|
||||
.stat-communities .stat-chip {
|
||||
background: var(--accent-communities-bg);
|
||||
color: var(--accent-communities);
|
||||
}
|
||||
.profile-details {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
margin: 1rem 0 1.5rem;
|
||||
}
|
||||
.profile-details h2 { margin: 0 0 0.75rem; font-size: 1.05rem; }
|
||||
.profile-details-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.profile-detail-section {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 0.85rem;
|
||||
}
|
||||
.profile-detail-section h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.profile-detail-section.stat-wallet {
|
||||
border-color: color-mix(in srgb, var(--accent-wallet) 35%, var(--border));
|
||||
}
|
||||
.profile-detail-section.stat-wallet h3 { color: var(--accent-wallet); }
|
||||
.profile-detail-section.stat-messenger {
|
||||
border-color: color-mix(in srgb, var(--accent-messenger) 35%, var(--border));
|
||||
}
|
||||
.profile-detail-section.stat-messenger h3 { color: var(--accent-messenger); }
|
||||
.profile-detail-section.stat-communities {
|
||||
border-color: color-mix(in srgb, var(--accent-communities) 35%, var(--border));
|
||||
}
|
||||
.profile-detail-section.stat-communities h3 { color: var(--accent-communities); }
|
||||
.profile-detail-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.area-group { margin-top: 2rem; }
|
||||
.area-group > h2 { margin-bottom: 0.75rem; }
|
||||
.scenario-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
details.scenario-charts {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
details.scenario-charts[open] { border-color: var(--link); }
|
||||
details.scenario-charts > summary {
|
||||
cursor: pointer;
|
||||
padding: 0.9rem 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
details.scenario-charts > summary:hover {
|
||||
background: var(--accent-data-bg);
|
||||
}
|
||||
details.scenario-charts > summary:focus-visible {
|
||||
outline: 2px solid var(--link);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.scenario-summary-content {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
width: calc(100% - 1.5rem);
|
||||
margin-left: 0.25rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.scenario-chart-count {
|
||||
color: var(--muted);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.scenario-charts-body {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 1rem;
|
||||
}
|
||||
.scenario-charts-body section.chart:last-child { margin-bottom: 0; }
|
||||
.area-empty {
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.summary-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.summary-link {
|
||||
display: inline-block;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.summary-link:hover { text-decoration: underline; }
|
||||
.summary-badge {
|
||||
display: inline-block;
|
||||
margin-left: 0.35rem;
|
||||
padding: 0.1rem 0.45rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
background: #cf222e;
|
||||
color: #fff;
|
||||
}
|
||||
.summary-profile { margin: 2rem 0; }
|
||||
.summary-profile h2 { margin-bottom: 0.25rem; }
|
||||
.summary-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
margin-top: 0.75rem;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
.summary-table th,
|
||||
.summary-table td {
|
||||
padding: 0.65rem 0.75rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.summary-table th { color: var(--muted); font-size: 0.78rem; }
|
||||
.summary-table .load-time-column {
|
||||
min-width: 145px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.summary-table .reference-column {
|
||||
min-width: 105px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.summary-table .measured-column {
|
||||
min-width: 6.5rem;
|
||||
}
|
||||
.measured-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.measured-build {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
.measured-date {
|
||||
color: var(--muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
.summary-table tr:last-child td { border-bottom: 0; }
|
||||
.load-time-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.metric-value { white-space: nowrap; font-weight: 600; }
|
||||
.status {
|
||||
display: inline-block;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 0.1rem 0.45rem;
|
||||
margin: 0 0.2rem 0.2rem 0;
|
||||
white-space: nowrap;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status-fast {
|
||||
color: #1a7f37;
|
||||
border-color: #1a7f37;
|
||||
background: #dafbe1;
|
||||
}
|
||||
.status-ok {
|
||||
color: var(--link);
|
||||
border-color: var(--link);
|
||||
background: #ddf4ff;
|
||||
}
|
||||
.status-ok-warn {
|
||||
color: #9a6700;
|
||||
border-color: #9a6700;
|
||||
background: #fff8c5;
|
||||
}
|
||||
.status-slow {
|
||||
color: #cf222e;
|
||||
border-color: #cf222e;
|
||||
background: #ffebe9;
|
||||
}
|
||||
.status-backlog {
|
||||
color: var(--accent-wallet);
|
||||
border-color: var(--accent-wallet);
|
||||
background: var(--accent-wallet-bg);
|
||||
}
|
||||
.status-neutral,
|
||||
.status-no-data,
|
||||
.status-not-tested {
|
||||
color: var(--muted);
|
||||
background: var(--accent-data-bg);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.status-fast { background: var(--accent-messenger-bg); }
|
||||
.status-ok { background: #0c2d6b; color: #58a6ff; border-color: #58a6ff; }
|
||||
.status-ok-warn { background: #3d2e00; color: #d4a72c; border-color: #d4a72c; }
|
||||
.status-slow { background: #3d1418; }
|
||||
.status-backlog { background: var(--accent-wallet-bg); }
|
||||
}
|
||||
.speed-legend {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.25rem 0.75rem;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
.speed-fast { color: #1a7f37; font-weight: 600; }
|
||||
.speed-ok { color: var(--link); font-weight: 600; }
|
||||
.speed-ok-warn { color: #9a6700; font-weight: 600; }
|
||||
.speed-slow { color: #cf222e; font-weight: 600; }
|
||||
.regression-legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.regression-legend-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.section-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.section-heading .summary-badge { margin-left: 0; }
|
||||
.reference-value { font-weight: 600; white-space: nowrap; }
|
||||
.reference-parity,
|
||||
.reference-improvement { color: #1a7f37; }
|
||||
.reference-regression { color: #cf222e; }
|
||||
.reference-neutral { color: var(--muted); }
|
||||
section.chart {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem 1.25rem;
|
||||
margin-bottom: 1.5rem;
|
||||
scroll-margin-top: 1rem;
|
||||
}
|
||||
.chart-permalink {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.chart-permalink a {
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
.chart-permalink a:hover { text-decoration: underline; }
|
||||
section.chart iframe {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
display: block;
|
||||
}
|
||||
.chart-footnote {
|
||||
color: var(--muted);
|
||||
font-size: 0.85rem;
|
||||
margin: 0.5rem 0 0;
|
||||
}
|
||||
section.chart-placeholder {
|
||||
border-style: dashed;
|
||||
background: transparent;
|
||||
}
|
||||
.placeholder-note {
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
.note { color: var(--muted); font-size: 0.9rem; margin: 1rem 0 0; }
|
||||
nav.back { margin-bottom: 1rem; }
|
||||
nav.back a { color: var(--link); text-decoration: none; }
|
||||
nav.back a:hover { text-decoration: underline; }
|
||||
section.machine-info {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem 1.25rem;
|
||||
margin: 1rem 0 1.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
section.machine-info h2 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
section.machine-info dl {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 0.5rem 1.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
section.machine-info dt {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
section.machine-info dd {
|
||||
margin: 0.1rem 0 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
main { padding: 1rem; }
|
||||
.summary-table,
|
||||
.summary-table tbody,
|
||||
.summary-table tr,
|
||||
.summary-table td { display: block; width: 100%; }
|
||||
.summary-table thead { display: none; }
|
||||
.summary-table tr { padding: 0.45rem 0; border-bottom: 1px solid var(--border); }
|
||||
.summary-table tr:last-child { border-bottom: 0; }
|
||||
.summary-table td { border: 0; padding: 0.25rem 0.75rem; }
|
||||
.summary-table td::before {
|
||||
content: attr(data-label) ": ";
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header><a href="index.html">Status App Benchmarks</a></header>
|
||||
<main><nav class="back"><a href="../">← All Windows benchmarks</a></nav><div class="page-heading"><span class="channel-badge channel-badge-nightly">Nightly</span><h1>Windows Nightly Benchmark Dashboard</h1></div><p class="subtitle">Nightly charts have not been published to this path yet.</p><p class="note">After the next successful nightly publish, this page is replaced with the full rolling dashboard. Older charts may still appear under <a href="../">docs/desktop/</a> until that cleanup.</p></main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -62,6 +62,51 @@
|
||||
main { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
|
||||
h1 { margin: 0 0 0.25rem; font-size: 1.5rem; }
|
||||
.subtitle { color: var(--muted); margin: 0; }
|
||||
.page-heading {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.65rem 0.85rem;
|
||||
margin: 0 0 0.25rem;
|
||||
}
|
||||
.page-heading h1 { margin: 0; }
|
||||
.channel-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.22rem 0.6rem;
|
||||
border-radius: 999px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.channel-badge-nightly {
|
||||
background: #ddf4ff;
|
||||
color: #0969da;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #dafbe1;
|
||||
color: #1a7f37;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #fff1e5;
|
||||
color: #bc4c00;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.channel-badge-nightly {
|
||||
background: #12263a;
|
||||
color: #58a6ff;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #12261e;
|
||||
color: #3fb950;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #2d1f14;
|
||||
color: #f0883e;
|
||||
}
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
@@ -80,6 +125,50 @@
|
||||
.card:hover { border-color: var(--link); }
|
||||
.card h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
|
||||
.card p { margin: 0; color: var(--muted); font-size: 0.9rem; }
|
||||
.listing-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.listing-card:hover { border-color: var(--link); }
|
||||
.listing-card .listing-main {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
flex: 1;
|
||||
}
|
||||
.listing-card .listing-main h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
|
||||
.listing-card .listing-meta {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.listing-card .listing-meta + .listing-meta { margin-top: 0.2rem; }
|
||||
.listing-card .listing-extra {
|
||||
font-size: 0.85rem;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
.listing-card .listing-extra:hover { text-decoration: underline; }
|
||||
.listing-history {
|
||||
list-style: none;
|
||||
margin: 0.65rem 0 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.listing-history li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
.listing-history .commit {
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
.profile-fact-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -475,6 +564,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<header><a href="index.html">Status App Benchmarks</a></header>
|
||||
<main><nav class="back"><a href="../">← All Windows benchmarks</a></nav><h1>Pull request benchmarks</h1><p class="subtitle">Persistent benchmark history for explicitly tested pull requests.</p><ul><li><a href="21980/">21980</a></li></ul></main>
|
||||
<main><nav class="back"><a href="../">← All Windows benchmarks</a></nav><div class="page-heading"><span class="channel-badge channel-badge-pr">Pull request</span><h1>Pull request benchmarks</h1></div><p class="subtitle">Persistent benchmark history for explicitly tested pull requests.</p><div class="grid"><article class="card listing-card"><a class="listing-main" href="21980/"><h2>PR #21980</h2><p class="listing-meta">2 runs · 1 commit · last 2026-08-14 17:44 UTC</p><ul class="listing-history"><li><span class="commit">d99550</span><span>2026-08-14 17:44 UTC</span></li><li><span class="commit">d99550</span><span>2026-08-14 16:23 UTC</span></li></ul></a><a class="listing-extra" href="https://github.com/status-im/status-app/pull/21980" target="_blank" rel="noopener">View on GitHub →</a></article></div></main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -62,6 +62,51 @@
|
||||
main { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
|
||||
h1 { margin: 0 0 0.25rem; font-size: 1.5rem; }
|
||||
.subtitle { color: var(--muted); margin: 0; }
|
||||
.page-heading {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.65rem 0.85rem;
|
||||
margin: 0 0 0.25rem;
|
||||
}
|
||||
.page-heading h1 { margin: 0; }
|
||||
.channel-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.22rem 0.6rem;
|
||||
border-radius: 999px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.channel-badge-nightly {
|
||||
background: #ddf4ff;
|
||||
color: #0969da;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #dafbe1;
|
||||
color: #1a7f37;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #fff1e5;
|
||||
color: #bc4c00;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.channel-badge-nightly {
|
||||
background: #12263a;
|
||||
color: #58a6ff;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #12261e;
|
||||
color: #3fb950;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #2d1f14;
|
||||
color: #f0883e;
|
||||
}
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
@@ -80,6 +125,50 @@
|
||||
.card:hover { border-color: var(--link); }
|
||||
.card h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
|
||||
.card p { margin: 0; color: var(--muted); font-size: 0.9rem; }
|
||||
.listing-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.listing-card:hover { border-color: var(--link); }
|
||||
.listing-card .listing-main {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
flex: 1;
|
||||
}
|
||||
.listing-card .listing-main h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
|
||||
.listing-card .listing-meta {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.listing-card .listing-meta + .listing-meta { margin-top: 0.2rem; }
|
||||
.listing-card .listing-extra {
|
||||
font-size: 0.85rem;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
.listing-card .listing-extra:hover { text-decoration: underline; }
|
||||
.listing-history {
|
||||
list-style: none;
|
||||
margin: 0.65rem 0 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.listing-history li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
.listing-history .commit {
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
.profile-fact-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -475,6 +564,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<header><a href="index.html">Status App Benchmarks</a></header>
|
||||
<main><nav class="back"><a href="../">← All Windows benchmarks</a></nav><h1>Release benchmarks</h1><p class="subtitle">RC-to-final performance history, isolated by release series.</p><p class="note">No published runs yet.</p></main>
|
||||
<main><nav class="back"><a href="../">← All Windows benchmarks</a></nav><div class="page-heading"><span class="channel-badge channel-badge-release">Release</span><h1>Release benchmarks</h1></div><p class="subtitle">RC-to-final performance history, isolated by release series.</p><p class="note">No published runs yet.</p></main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+351
-24
@@ -20,6 +20,12 @@ from regression_report import ScenarioSummary, Violation
|
||||
|
||||
CHARTS_DIR = 'charts'
|
||||
SITE_TITLE = 'Status App Benchmarks'
|
||||
STATUS_APP_REPO = 'https://github.com/status-im/status-app'
|
||||
CHANNEL_BADGE = {
|
||||
'nightly': ('Nightly', 'channel-badge-nightly'),
|
||||
'pr': ('Pull request', 'channel-badge-pr'),
|
||||
'release': ('Release', 'channel-badge-release'),
|
||||
}
|
||||
MACHINE_FIELD_LABELS = {
|
||||
'hostname': 'Host',
|
||||
'windows_version': 'Windows',
|
||||
@@ -113,6 +119,51 @@ def _page_styles() -> str:
|
||||
main { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }
|
||||
h1 { margin: 0 0 0.25rem; font-size: 1.5rem; }
|
||||
.subtitle { color: var(--muted); margin: 0; }
|
||||
.page-heading {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.65rem 0.85rem;
|
||||
margin: 0 0 0.25rem;
|
||||
}
|
||||
.page-heading h1 { margin: 0; }
|
||||
.channel-badge {
|
||||
display: inline-block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
padding: 0.22rem 0.6rem;
|
||||
border-radius: 999px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.channel-badge-nightly {
|
||||
background: #ddf4ff;
|
||||
color: #0969da;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #dafbe1;
|
||||
color: #1a7f37;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #fff1e5;
|
||||
color: #bc4c00;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.channel-badge-nightly {
|
||||
background: #12263a;
|
||||
color: #58a6ff;
|
||||
}
|
||||
.channel-badge-pr {
|
||||
background: #12261e;
|
||||
color: #3fb950;
|
||||
}
|
||||
.channel-badge-release {
|
||||
background: #2d1f14;
|
||||
color: #f0883e;
|
||||
}
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
@@ -131,6 +182,50 @@ def _page_styles() -> str:
|
||||
.card:hover { border-color: var(--link); }
|
||||
.card h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
|
||||
.card p { margin: 0; color: var(--muted); font-size: 0.9rem; }
|
||||
.listing-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.listing-card:hover { border-color: var(--link); }
|
||||
.listing-card .listing-main {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
flex: 1;
|
||||
}
|
||||
.listing-card .listing-main h2 { margin: 0 0 0.4rem; font-size: 1.1rem; }
|
||||
.listing-card .listing-meta {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.listing-card .listing-meta + .listing-meta { margin-top: 0.2rem; }
|
||||
.listing-card .listing-extra {
|
||||
font-size: 0.85rem;
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
}
|
||||
.listing-card .listing-extra:hover { text-decoration: underline; }
|
||||
.listing-history {
|
||||
list-style: none;
|
||||
margin: 0.65rem 0 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.listing-history li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem 0.75rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
.listing-history .commit {
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
.profile-fact-groups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -542,6 +637,18 @@ def _layout(title: str, body: str) -> str:
|
||||
"""
|
||||
|
||||
|
||||
def _channel_badge_html(channel: str) -> str:
|
||||
label, css_class = CHANNEL_BADGE.get(channel, (channel.title(), 'channel-badge-nightly'))
|
||||
return f'<span class="channel-badge {css_class}">{escape(label)}</span>'
|
||||
|
||||
|
||||
def _heading_with_badge(heading: str, channel: str) -> str:
|
||||
return (
|
||||
f'<div class="page-heading">{_channel_badge_html(channel)}'
|
||||
f'<h1>{escape(heading)}</h1></div>'
|
||||
)
|
||||
|
||||
|
||||
def _chart_iframe(chart_path: str, title: str) -> str:
|
||||
return (
|
||||
f'<iframe src="{escape(chart_path)}" '
|
||||
@@ -995,6 +1102,8 @@ def _summary_page(
|
||||
pages: tuple[BenchmarkPage, ...],
|
||||
charts_by_id: dict[str, ChartTest],
|
||||
summaries: dict[str, ScenarioSummary],
|
||||
*,
|
||||
channel: str = 'nightly',
|
||||
) -> str:
|
||||
sections = []
|
||||
for page in pages:
|
||||
@@ -1034,7 +1143,7 @@ def _summary_page(
|
||||
)
|
||||
return (
|
||||
'<nav class="back"><a href="index.html">← Dashboard</a></nav>'
|
||||
'<h1>Scenario summary</h1>'
|
||||
f'{_heading_with_badge("Scenario summary", channel)}'
|
||||
'<p class="subtitle">Latest result for every tested scenario. '
|
||||
'Speed categories:<br>'
|
||||
'<span class="speed-legend">'
|
||||
@@ -1149,6 +1258,8 @@ def _regression_page(
|
||||
violations: list[Violation],
|
||||
flag_tickets: dict[str, FlagTicket] | None = None,
|
||||
page_slugs_by_test_id: dict[str, str] | None = None,
|
||||
*,
|
||||
channel: str = 'nightly',
|
||||
) -> str:
|
||||
tickets = flag_tickets or {}
|
||||
page_slugs = page_slugs_by_test_id or {}
|
||||
@@ -1163,7 +1274,7 @@ def _regression_page(
|
||||
)
|
||||
return (
|
||||
'<nav class="back"><a href="index.html">← Dashboard</a></nav>'
|
||||
'<h1>Flags</h1>'
|
||||
f'{_heading_with_badge("Flags", channel)}'
|
||||
'<p class="subtitle">Automated flags from nightly performance data.</p>'
|
||||
'<p class="subtitle regression-legend">'
|
||||
'<span class="regression-legend-item">'
|
||||
@@ -1242,7 +1353,7 @@ def write_site(
|
||||
)
|
||||
index_body = (
|
||||
f'<nav class="back"><a href="{_channel_root_href(channel)}">← All Windows benchmarks</a></nav>'
|
||||
f'<h1>{escape(heading)}</h1>'
|
||||
f'{_heading_with_badge(heading, channel)}'
|
||||
f'<p class="subtitle">{escape(subtitle)} '
|
||||
'Load-time charts plot the average of samples per run.</p>'
|
||||
f'{machine_panel}'
|
||||
@@ -1257,7 +1368,10 @@ def write_site(
|
||||
print('Generated index.html')
|
||||
|
||||
(output_dir / 'summary.html').write_text(
|
||||
_layout('Scenario summary', _summary_page(pages, charts_by_id, scenario_summaries)),
|
||||
_layout(
|
||||
'Scenario summary',
|
||||
_summary_page(pages, charts_by_id, scenario_summaries, channel=channel),
|
||||
),
|
||||
encoding='utf-8',
|
||||
)
|
||||
print('Generated summary.html')
|
||||
@@ -1265,7 +1379,12 @@ def write_site(
|
||||
(output_dir / 'regression_report.html').write_text(
|
||||
_layout(
|
||||
'Flags',
|
||||
_regression_page(regression_violations, tickets, page_slugs_by_test_id),
|
||||
_regression_page(
|
||||
regression_violations,
|
||||
tickets,
|
||||
page_slugs_by_test_id,
|
||||
channel=channel,
|
||||
),
|
||||
),
|
||||
encoding='utf-8',
|
||||
)
|
||||
@@ -1289,7 +1408,7 @@ def write_site(
|
||||
)
|
||||
page_body = (
|
||||
'<nav class="back"><a href="index.html">← Dashboard</a></nav>'
|
||||
f'<h1>{escape(page.title)}</h1>'
|
||||
f'{_heading_with_badge(page.title, channel)}'
|
||||
f'<p class="subtitle">{escape(page.description)}</p>'
|
||||
f'{_profile_details(page)}'
|
||||
f'{"".join(area_sections)}'
|
||||
@@ -1345,35 +1464,243 @@ def channel_listing_sort_key(name: str) -> tuple:
|
||||
return (2, name)
|
||||
|
||||
|
||||
MANIFEST_CSV_NAME = 'runs.csv'
|
||||
|
||||
|
||||
def _desktop_data_dir(desktop_dir: Path) -> Path:
|
||||
"""Map docs/desktop -> <repo>/data/desktop."""
|
||||
return desktop_dir.parent.parent / 'data' / 'desktop'
|
||||
|
||||
|
||||
def _format_run_date(value: object) -> str:
|
||||
text = _field_text(value)
|
||||
if not text:
|
||||
return ''
|
||||
try:
|
||||
parsed = pd.to_datetime(text, utc=True)
|
||||
except (TypeError, ValueError):
|
||||
return text
|
||||
if pd.isna(parsed):
|
||||
return text
|
||||
return parsed.strftime('%Y-%m-%d %H:%M UTC')
|
||||
|
||||
|
||||
LISTING_HISTORY_LIMIT = 8
|
||||
|
||||
|
||||
def _load_runs_frame(runs_csv: Path) -> pd.DataFrame | None:
|
||||
if not runs_csv.exists():
|
||||
return None
|
||||
try:
|
||||
frame = pd.read_csv(runs_csv)
|
||||
except (OSError, pd.errors.EmptyDataError, pd.errors.ParserError):
|
||||
return None
|
||||
if frame.empty:
|
||||
return None
|
||||
ordered = frame.copy()
|
||||
ordered['_sort_date'] = pd.to_datetime(
|
||||
ordered['date'] if 'date' in ordered.columns else pd.NaT,
|
||||
errors='coerce',
|
||||
utc=True,
|
||||
)
|
||||
return ordered.sort_values('_sort_date', ascending=False, na_position='last')
|
||||
|
||||
|
||||
def _run_history_entries(frame: pd.DataFrame) -> list[dict[str, object]]:
|
||||
"""Newest-first run rows for listing cards (commit + date per publish)."""
|
||||
entries: list[dict[str, object]] = []
|
||||
for _, row in frame.head(LISTING_HISTORY_LIMIT).iterrows():
|
||||
entries.append({
|
||||
'commit': _field_text(row.get('commit_hash')) or 'unknown',
|
||||
'date': _format_run_date(row.get('date')),
|
||||
'version': _field_text(row.get('release_version')),
|
||||
})
|
||||
return entries
|
||||
|
||||
|
||||
def _unique_commit_count(frame: pd.DataFrame) -> int:
|
||||
if 'commit_hash' not in frame.columns:
|
||||
return 0
|
||||
commits = {_field_text(value) for value in frame['commit_hash'] if _field_text(value)}
|
||||
return len(commits)
|
||||
|
||||
|
||||
def _history_list_html(entries: list[dict[str, object]]) -> str:
|
||||
if not entries:
|
||||
return ''
|
||||
items = []
|
||||
for entry in entries:
|
||||
commit = str(entry.get('commit') or '')
|
||||
date_text = str(entry.get('date') or '')
|
||||
version = str(entry.get('version') or '')
|
||||
left = escape(commit)
|
||||
if version:
|
||||
left = f'{escape(version)} · {escape(commit)}'
|
||||
items.append(
|
||||
'<li>'
|
||||
f'<span class="commit">{left}</span>'
|
||||
f'<span>{escape(date_text)}</span>'
|
||||
'</li>'
|
||||
)
|
||||
return f'<ul class="listing-history">{"".join(items)}</ul>'
|
||||
|
||||
|
||||
def _listing_card(
|
||||
*,
|
||||
href: str,
|
||||
title: str,
|
||||
meta_lines: list[str],
|
||||
history_html: str = '',
|
||||
extra_href: str = '',
|
||||
extra_label: str = '',
|
||||
) -> str:
|
||||
meta_html = ''.join(
|
||||
f'<p class="listing-meta">{escape(line)}</p>' for line in meta_lines if line
|
||||
)
|
||||
extra = ''
|
||||
if extra_href and extra_label:
|
||||
extra = (
|
||||
f'<a class="listing-extra" href="{escape(extra_href, quote=True)}" '
|
||||
f'target="_blank" rel="noopener">{escape(extra_label)}</a>'
|
||||
)
|
||||
return (
|
||||
f'<article class="card listing-card">'
|
||||
f'<a class="listing-main" href="{escape(href, quote=True)}">'
|
||||
f'<h2>{escape(title)}</h2>{meta_html}{history_html}</a>{extra}</article>'
|
||||
)
|
||||
|
||||
|
||||
def _pr_listing_card(path: Path, data_root: Path) -> str:
|
||||
runs_csv = data_root / 'pr' / path.name / MANIFEST_CSV_NAME
|
||||
frame = _load_runs_frame(runs_csv)
|
||||
title = f'PR #{path.name}'
|
||||
meta_lines: list[str] = []
|
||||
history_html = ''
|
||||
if frame is None or frame.empty:
|
||||
meta_lines.append('No run metadata yet.')
|
||||
else:
|
||||
run_count = len(frame)
|
||||
commit_count = _unique_commit_count(frame)
|
||||
latest_date = _format_run_date(frame.iloc[0].get('date'))
|
||||
parts = []
|
||||
if run_count:
|
||||
parts.append(f'{run_count} run{"s" if run_count != 1 else ""}')
|
||||
if commit_count:
|
||||
parts.append(f'{commit_count} commit{"s" if commit_count != 1 else ""}')
|
||||
if latest_date:
|
||||
parts.append(f'last {latest_date}')
|
||||
if parts:
|
||||
meta_lines.append(' · '.join(parts))
|
||||
history_html = _history_list_html(_run_history_entries(frame))
|
||||
remaining = run_count - min(run_count, LISTING_HISTORY_LIMIT)
|
||||
if remaining > 0:
|
||||
history_html += (
|
||||
f'<p class="listing-meta">+{remaining} earlier run'
|
||||
f'{"s" if remaining != 1 else ""}</p>'
|
||||
)
|
||||
return _listing_card(
|
||||
href=f'{path.name}/',
|
||||
title=title,
|
||||
meta_lines=meta_lines,
|
||||
history_html=history_html,
|
||||
extra_href=f'{STATUS_APP_REPO}/pull/{path.name}',
|
||||
extra_label='View on GitHub →',
|
||||
)
|
||||
|
||||
|
||||
def _release_listing_card(path: Path, data_root: Path) -> str:
|
||||
runs_csv = data_root / 'releases' / path.name / MANIFEST_CSV_NAME
|
||||
frame = _load_runs_frame(runs_csv)
|
||||
title = f'Release {path.name}'
|
||||
meta_lines: list[str] = []
|
||||
history_html = ''
|
||||
if frame is None or frame.empty:
|
||||
meta_lines.append('No run metadata yet.')
|
||||
else:
|
||||
run_count = len(frame)
|
||||
latest_date = _format_run_date(frame.iloc[0].get('date'))
|
||||
parts = []
|
||||
if run_count:
|
||||
parts.append(f'{run_count} run{"s" if run_count != 1 else ""}')
|
||||
if latest_date:
|
||||
parts.append(f'last {latest_date}')
|
||||
if parts:
|
||||
meta_lines.append(' · '.join(parts))
|
||||
history_html = _history_list_html(_run_history_entries(frame))
|
||||
remaining = run_count - min(run_count, LISTING_HISTORY_LIMIT)
|
||||
if remaining > 0:
|
||||
history_html += (
|
||||
f'<p class="listing-meta">+{remaining} earlier run'
|
||||
f'{"s" if remaining != 1 else ""}</p>'
|
||||
)
|
||||
return _listing_card(
|
||||
href=f'{path.name}/',
|
||||
title=title,
|
||||
meta_lines=meta_lines,
|
||||
history_html=history_html,
|
||||
)
|
||||
|
||||
|
||||
def _channel_directory_cards(
|
||||
parent: Path,
|
||||
data_root: Path,
|
||||
*,
|
||||
kind: str,
|
||||
) -> str:
|
||||
if not parent.exists():
|
||||
return '<p class="note">No published runs yet.</p>'
|
||||
entries = [
|
||||
path for path in parent.iterdir()
|
||||
if path.is_dir() and (path / 'index.html').exists()
|
||||
]
|
||||
entries.sort(key=lambda item: channel_listing_sort_key(item.name), reverse=True)
|
||||
if not entries:
|
||||
return '<p class="note">No published runs yet.</p>'
|
||||
if kind == 'pr':
|
||||
cards = ''.join(_pr_listing_card(path, data_root) for path in entries)
|
||||
else:
|
||||
cards = ''.join(_release_listing_card(path, data_root) for path in entries)
|
||||
return f'<div class="grid">{cards}</div>'
|
||||
|
||||
|
||||
def _write_nightly_stub(nightly_dir: Path) -> None:
|
||||
"""Placeholder until the first nightly graphs publish lands in docs/desktop/nightly/."""
|
||||
if (nightly_dir / 'index.html').exists():
|
||||
return
|
||||
nightly_dir.mkdir(parents=True, exist_ok=True)
|
||||
body = (
|
||||
'<nav class="back"><a href="../">← All Windows benchmarks</a></nav>'
|
||||
f'{_heading_with_badge("Windows Nightly Benchmark Dashboard", "nightly")}'
|
||||
'<p class="subtitle">Nightly charts have not been published to this path yet.</p>'
|
||||
'<p class="note">After the next successful nightly publish, this page is replaced '
|
||||
'with the full rolling dashboard. Older charts may still appear under '
|
||||
'<a href="../">docs/desktop/</a> until that cleanup.</p>'
|
||||
)
|
||||
(nightly_dir / 'index.html').write_text(
|
||||
_layout('Nightly benchmarks', body), encoding='utf-8',
|
||||
)
|
||||
print(f'Generated nightly stub {nightly_dir / "index.html"}')
|
||||
|
||||
|
||||
def write_desktop_landing(desktop_dir: Path) -> None:
|
||||
"""Write the channel picker and discovered PR/release-series links."""
|
||||
desktop_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def directory_links(parent: Path, prefix: str = '') -> str:
|
||||
if not parent.exists():
|
||||
return '<p class="note">No published runs yet.</p>'
|
||||
entries = [
|
||||
path for path in parent.iterdir()
|
||||
if path.is_dir() and (path / 'index.html').exists()
|
||||
]
|
||||
entries.sort(key=lambda item: channel_listing_sort_key(item.name), reverse=True)
|
||||
links = [
|
||||
f'<li><a href="{prefix}{escape(path.name)}/">{escape(path.name)}</a></li>'
|
||||
for path in entries
|
||||
]
|
||||
return f'<ul>{"".join(links)}</ul>' if links else '<p class="note">No published runs yet.</p>'
|
||||
data_root = _desktop_data_dir(desktop_dir)
|
||||
|
||||
releases_dir = desktop_dir / 'releases'
|
||||
prs_dir = desktop_dir / 'pr'
|
||||
nightly_dir = desktop_dir / 'nightly'
|
||||
releases_dir.mkdir(parents=True, exist_ok=True)
|
||||
prs_dir.mkdir(parents=True, exist_ok=True)
|
||||
_write_nightly_stub(nightly_dir)
|
||||
|
||||
(releases_dir / 'index.html').write_text(
|
||||
_layout(
|
||||
'Release benchmarks',
|
||||
'<nav class="back"><a href="../">← All Windows benchmarks</a></nav>'
|
||||
'<h1>Release benchmarks</h1>'
|
||||
f'{_heading_with_badge("Release benchmarks", "release")}'
|
||||
'<p class="subtitle">RC-to-final performance history, isolated by release series.</p>'
|
||||
f'{directory_links(releases_dir)}',
|
||||
f'{_channel_directory_cards(releases_dir, data_root, kind="release")}',
|
||||
),
|
||||
encoding='utf-8',
|
||||
)
|
||||
@@ -1381,9 +1708,9 @@ def write_desktop_landing(desktop_dir: Path) -> None:
|
||||
_layout(
|
||||
'Pull request benchmarks',
|
||||
'<nav class="back"><a href="../">← All Windows benchmarks</a></nav>'
|
||||
'<h1>Pull request benchmarks</h1>'
|
||||
f'{_heading_with_badge("Pull request benchmarks", "pr")}'
|
||||
'<p class="subtitle">Persistent benchmark history for explicitly tested pull requests.</p>'
|
||||
f'{directory_links(prs_dir)}',
|
||||
f'{_channel_directory_cards(prs_dir, data_root, kind="pr")}',
|
||||
),
|
||||
encoding='utf-8',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user