+ return `
${html(profile.label)} ${html(profile.generation)} ${html(profile.provider)} · ${html(profile.model)}
${columns}`;
@@ -530,7 +561,7 @@ function renderSuiteMatrix(input: {
Test suite
${html(suite.label)} ${html(suite.description)}
${summaryHtml}
Configuration matrix ${profiles.length} profiles · ${environments.length} environments · ${selected} selected
-
Agent profile ${environmentHeaders}${rows}
+
${environments.map(() => ' ').join("")}Agent profile ${environmentHeaders}${rows}
`;
}
@@ -578,6 +609,22 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
const publicSummaryImageHref = safePublicAssetHref(
input.publicSummaryImageHref,
);
+ const filterProfiles = [
+ ...new Map(
+ input.catalog.map((execution) => [
+ execution.profile.id,
+ execution.profile,
+ ]),
+ ).values(),
+ ];
+ const filterEnvironments = [
+ ...new Map(
+ input.catalog.map((execution) => [
+ execution.environment.id,
+ execution.environment,
+ ]),
+ ).values(),
+ ];
return `
@@ -617,6 +664,7 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
--idle-text: #52585d;
--idle-border: #a8aeb2;
--radius: 8px;
+ --navigation-sticky-offset: 58px;
--font-sans: "Paperclip Inter", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
@@ -672,23 +720,32 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.gallery-launch, .gallery-control, .gallery-close { border: 1px solid var(--border-strong); border-radius: calc(var(--radius) * .8); background: var(--background); color: var(--foreground); cursor: pointer; font-weight: 600; transition: background 150ms ease, color 150ms ease, border-color 150ms ease; }
.gallery-launch { min-width: 138px; padding: 10px 15px; }
.gallery-launch:hover, .gallery-control:hover, .gallery-close:hover { border-color: var(--foreground); background: var(--primary); color: var(--primary-foreground); }
- .gallery-launch:focus-visible, .gallery-control:focus-visible, .gallery-close:focus-visible, .screenshot-trigger:focus-visible, summary:focus-visible, a:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }
+ .gallery-launch:focus-visible, .gallery-control:focus-visible, .gallery-close:focus-visible, .screenshot-trigger:focus-visible, .report-filters input:focus-visible, .report-filters select:focus-visible, .filter-reset:focus-visible, summary:focus-visible, a:focus-visible { outline: 2px solid #2563eb; outline-offset: 3px; }
+ .report-filters { display: grid; grid-template-columns: minmax(240px, 1.6fr) repeat(4, minmax(130px, .7fr)) auto; align-items: end; gap: 10px; margin: 0 0 40px; padding: 12px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); }
+ .report-filters label { display: grid; gap: 5px; min-width: 0; }
+ .report-filters label > span { color: var(--muted-foreground); font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }
+ .report-filters input, .report-filters select, .filter-reset { width: 100%; min-height: 38px; padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: calc(var(--radius) * .8); background: var(--background); color: var(--foreground); }
+ .filter-reset { width: auto; cursor: pointer; font-weight: 600; }
+ .filter-reset:hover { border-color: var(--foreground); }
+ .filter-result { grid-column: 1 / -1; margin: 0; color: var(--muted-foreground); font: 10px/1.4 var(--font-mono); }
.table-kicker { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px 0; border-top: 1px solid var(--border); color: var(--muted-foreground); }
.table-kicker strong { color: var(--foreground); font-weight: 600; }
.table-kicker span { font: 11px/1.4 var(--font-mono); font-variant-numeric: tabular-nums; }
.table-wrap { overflow: visible; }
- .matrix { width: 100%; min-width: 1120px; border-collapse: separate; border-spacing: 0; }
+ .matrix { width: 100%; min-width: 1120px; table-layout: fixed; border-collapse: separate; border-spacing: 0; }
+ .matrix .profile-column { width: 260px; }
+ .matrix .environment-column { width: auto; }
.matrix th, .matrix td { padding: 16px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); vertical-align: top; text-align: left; }
.matrix tr:last-child th, .matrix tr:last-child td { border-bottom: 0; }
.matrix th:last-child, .matrix td:last-child { border-right: 0; }
- .matrix thead th { position: sticky; top: 0; z-index: 4; background: var(--raised); }
+ .matrix thead th { position: sticky; top: var(--navigation-sticky-offset); z-index: 4; background: var(--raised); }
.matrix thead th:first-child { left: 0; z-index: 6; width: 260px; }
.matrix thead small, .matrix tbody th small { display: block; margin-top: 4px; color: var(--muted-foreground); font-weight: 400; }
.environment-label { font-size: 15px; font-weight: 650; }
.mobile-environment-header { display: none; }
.profile-cell { position: sticky; left: 0; z-index: 3; width: 260px; background: var(--card); }
.profile-cell { display: table-cell; }
- .profile-sticky { position: sticky; top: 76px; display: flex; align-items: flex-start; min-width: 0; }
+ .profile-sticky { position: sticky; top: calc(var(--navigation-sticky-offset) + 76px); display: flex; align-items: flex-start; min-width: 0; }
.profile-sticky > .agent-capsule { flex: none; margin: 2px 12px 18px 0; }
.profile-copy { display: block; min-width: 0; }
.profile-copy strong { font-size: 14px; }
@@ -706,7 +763,7 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.agent-10 { background: linear-gradient(to bottom, #f2d95f, #4fbcba); }
.generation { display: inline-block; margin-left: 7px; padding: 2px 7px; border: 1px solid var(--border); border-radius: 999px; color: var(--muted-foreground); font: 500 10px/1.4 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; }
.case-stack { display: grid; gap: 12px; }
- .case { min-width: 0; padding: 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); }
+ .case { min-width: 0; max-width: 100%; padding: 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); overflow: hidden; }
.case-heading { display: flex; align-items: start; justify-content: space-between; gap: 12px; }
.case-heading strong { font-size: 14px; font-weight: 650; }
.status, .matcher-state { display: inline-block; flex: none; padding: 3px 8px; border: 1px solid; border-radius: 999px; font: 600 10px/1.4 var(--font-sans); letter-spacing: .05em; text-transform: uppercase; }
@@ -732,13 +789,18 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.screenshot-caption { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 9px; }
.screenshot-caption span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted-foreground); font-size: 10px; }
.screenshot-caption span:last-child { font-size: 10px; font-weight: 650; }
- .matcher-wrap { overflow-x: auto; margin: 12px 0; border: 1px solid var(--border); border-radius: calc(var(--radius) * .8); }
- .matchers { width: 100%; min-width: 680px; border-collapse: collapse; font-size: 11px; }
+ .case-context { display: block; max-width: 100%; }
+ .matcher-wrap { width: 100%; max-width: 100%; overflow-x: auto; margin: 12px 0; border: 1px solid var(--border); border-radius: calc(var(--radius) * .8); }
+ .matchers { width: 100%; min-width: 680px; table-layout: fixed; border-collapse: collapse; font-size: 11px; }
+ .matchers th:nth-child(1) { width: 72px; }
+ .matchers th:nth-child(2) { width: 144px; }
+ .matchers th:nth-child(4) { width: 110px; }
.matchers th, .matchers td { padding: 8px; border-right: 1px solid var(--border); border-bottom: 1px solid var(--border); vertical-align: top; }
.matchers tr:last-child td { border-bottom: 0; }
.matchers th:last-child, .matchers td:last-child { border-right: 0; }
.matchers th { background: var(--raised); color: var(--muted-foreground); font-weight: 600; }
.matchers code { display: inline; color: var(--foreground); }
+ .matchers td { overflow-wrap: anywhere; }
.usage { margin: 12px 0; color: var(--muted-foreground); }
.billing-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 12px 0 10px; border: 1px solid var(--border); border-radius: calc(var(--radius) * .8); background: var(--raised); }
.billing-strip > div { min-width: 0; padding: 9px 10px; border-right: 1px solid var(--border); }
@@ -753,10 +815,10 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.billing-metric strong { display: block; overflow-wrap: anywhere; font: 550 17px/1.25 var(--font-mono); font-variant-numeric: tabular-nums; }
.billing-metric span { display: block; margin-top: 4px; color: var(--muted-foreground); font-size: 10px; letter-spacing: .06em; text-transform: uppercase; }
.billing-note { grid-column: 1 / -1; margin: 0; padding: 11px 16px; border-top: 1px solid var(--border); color: var(--muted-foreground); font-size: 11px; }
- .suite-nav { position: sticky; top: 0; z-index: 12; display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 40px; padding: 10px 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); background: var(--background); }
+ .suite-nav { position: sticky; top: 0; z-index: 12; display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 24px; padding: 10px 0; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); background: var(--background); }
.suite-nav a { padding: 7px 10px; border-radius: calc(var(--radius) * .8); color: var(--muted-foreground); font-size: 12px; font-weight: 600; text-decoration: none; }
.suite-nav a:hover { background: var(--raised); color: var(--foreground); }
- .suite-section, .history-section { scroll-margin-top: 72px; margin-top: 64px; }
+ .suite-section, .history-section { scroll-margin-top: calc(var(--navigation-sticky-offset) + 16px); margin-top: 64px; }
.section-heading { display: grid; grid-template-columns: minmax(0, 1fr) minmax(260px, 520px); align-items: end; gap: 32px; margin-bottom: 24px; }
.section-heading h2 { margin: 0; font-size: clamp(25px, 3vw, 38px); line-height: 1.05; letter-spacing: -.035em; }
.section-heading > p { margin: 0; color: var(--muted-foreground); }
@@ -816,6 +878,13 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.gallery-context span { display: inline-flex; align-items: baseline; gap: 6px; min-width: 0; }
.gallery-context strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.gallery-context em { color: #b8b8b5; font: 10px/1.4 var(--font-mono); font-style: normal; white-space: nowrap; }
+ .gallery-facts { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin-top: 8px; }
+ .gallery-facts > span { display: inline-flex; align-items: baseline; gap: 6px; min-width: 0; }
+ .gallery-facts em { color: #b8b8b5; font: 10px/1.4 var(--font-mono); font-style: normal; text-transform: uppercase; }
+ .gallery-facts strong { font: 600 11px/1.4 var(--font-mono); }
+ .gallery-status { padding: 3px 8px; border: 1px solid rgb(255 255 255 / 36%); border-radius: 999px; font: 650 10px/1.4 var(--font-sans); letter-spacing: .05em; text-transform: uppercase; }
+ .gallery-status[data-status="passed"] { border-color: var(--pass-border); background: var(--pass-bg); color: #6ee79a; }
+ .gallery-status[data-status="failed"] { border-color: var(--fail-border); background: var(--fail-bg); color: #ff8181; }
.gallery-execution { margin-top: 3px; color: #b8b8b5; font: 10px/1.4 var(--font-mono); }
.gallery-close, .gallery-control { min-height: 38px; padding: 8px 12px; border-color: rgb(255 255 255 / 32%); background: transparent; color: #fafafa; }
.gallery-stage { position: relative; display: grid; place-items: center; min-height: 0; padding: 24px 88px; overflow: hidden; touch-action: pan-y; }
@@ -853,6 +922,10 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.section-heading { grid-template-columns: 1fr; gap: 12px; }
.trend-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.history-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+ .report-filters { grid-template-columns: repeat(2, minmax(0, 1fr)); }
+ .report-search, .filter-result { grid-column: 1 / -1; }
+ .suite-nav { top: 0; }
+ .suite-section, .history-section { scroll-margin-top: 72px; }
}
@media (max-width: 640px) {
.brand-bar { min-height: 56px; padding: 0 16px; }
@@ -875,6 +948,8 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
.suite-nav { top: 0; overflow-x: auto; flex-wrap: nowrap; }
.suite-nav a { white-space: nowrap; }
.history-pointers, .trend-grid, .history-filters { grid-template-columns: 1fr; }
+ .report-filters { grid-template-columns: 1fr; }
+ .report-search, .filter-result { grid-column: auto; }
.suite-summary { grid-template-columns: 1fr; }
.suite-summary > div, .suite-summary > div:nth-child(even) { border-right: 0; border-bottom: 1px solid var(--border); }
.suite-summary > div:last-child { border-bottom: 0; }
@@ -906,7 +981,7 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
Full-stack acceptance campaign
${html(input.title)}
-
A browser-verified matrix of runner profiles, execution environments, and deterministic task contracts. Provider-produced visual evidence remains in the access-controlled workflow artifact; public history contains inert structured evidence and a trusted synthetic campaign summary.
+
A browser-verified matrix of runner profiles, execution environments, and deterministic task contracts. Visual evidence is retained in the access-controlled workflow artifact; public history contains inert structured evidence and a trusted synthetic campaign summary.
@@ -934,6 +1009,15 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
${suites.map((suite) => `
${html(suite.label)} `).join("")}
History
+
${suiteSections}
${historySection}
Generated ${html(input.generatedAt)} ${input.catalog.length} catalog executions · Public history excludes provider-produced visual evidence
@@ -948,6 +1032,12 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
+
+
+ Duration
+ Tokens
+ Tests
+
Close
@@ -976,13 +1066,24 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
const environmentDetail = dialog.querySelector("[data-gallery-environment-detail]");
const caseLabel = dialog.querySelector("[data-gallery-case]");
const runtime = dialog.querySelector("[data-gallery-runtime]");
+ const status = dialog.querySelector("[data-gallery-status]");
+ const duration = dialog.querySelector("[data-gallery-duration]");
+ const tokens = dialog.querySelector("[data-gallery-tokens]");
+ const matchers = dialog.querySelector("[data-gallery-matchers]");
const position = dialog.querySelector("[data-gallery-position]");
const stage = dialog.querySelector("[data-gallery-stage]");
- let activeIndex = 0;
+ let activeItem = items[0];
let pointerStartX = null;
const render = () => {
- const item = items[activeIndex];
+ const visibleItems = items.filter((item) => !item.closest("[data-report-case]").hidden);
+ if (visibleItems.length === 0) return;
+ let activeIndex = visibleItems.indexOf(activeItem);
+ if (activeIndex < 0) {
+ activeIndex = 0;
+ activeItem = visibleItems[0];
+ }
+ const item = activeItem;
image.src = item.dataset.galleryHref;
image.alt = item.dataset.galleryLabel + " for " + item.dataset.galleryExecution;
title.textContent = item.dataset.galleryLabel;
@@ -993,14 +1094,23 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
environmentDetail.textContent = item.dataset.galleryEnvironmentProvider + " · " + item.dataset.galleryExecutionTarget;
caseLabel.textContent = item.dataset.galleryCase;
runtime.textContent = item.dataset.galleryRuntime + " runtime";
- position.textContent = "Image " + (activeIndex + 1) + " of " + items.length;
+ status.textContent = item.dataset.galleryStatus;
+ status.dataset.status = item.dataset.galleryStatus;
+ duration.textContent = item.dataset.galleryDuration;
+ tokens.textContent = item.dataset.galleryTokens;
+ matchers.textContent = item.dataset.galleryMatchers;
+ position.textContent = "Image " + (activeIndex + 1) + " of " + visibleItems.length;
};
const move = (amount) => {
- activeIndex = (activeIndex + amount + items.length) % items.length;
+ const visibleItems = items.filter((item) => !item.closest("[data-report-case]").hidden);
+ if (visibleItems.length === 0) return;
+ const activeIndex = Math.max(0, visibleItems.indexOf(activeItem));
+ activeItem = visibleItems[(activeIndex + amount + visibleItems.length) % visibleItems.length];
render();
};
- const open = (index) => {
- activeIndex = index;
+ const open = (item) => {
+ if (!item) return;
+ activeItem = item;
render();
if (typeof dialog.showModal === "function") dialog.showModal();
else dialog.setAttribute("open", "");
@@ -1010,8 +1120,8 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
else dialog.removeAttribute("open");
};
- items.forEach((item, index) => item.addEventListener("click", () => open(index)));
- document.querySelectorAll("[data-gallery-open]").forEach((button) => button.addEventListener("click", () => open(0)));
+ items.forEach((item) => item.addEventListener("click", () => open(item)));
+ document.querySelectorAll("[data-gallery-open]").forEach((button) => button.addEventListener("click", () => open(items.find((item) => !item.closest("[data-report-case]").hidden))));
dialog.querySelector("[data-gallery-previous]").addEventListener("click", () => move(-1));
dialog.querySelector("[data-gallery-next]").addEventListener("click", () => move(1));
dialog.querySelector("[data-gallery-close]").addEventListener("click", close);
@@ -1031,6 +1141,69 @@ export function renderRunnerE2EDashboard(input: RunnerDashboardInput) {
stage.addEventListener("pointercancel", () => { pointerStartX = null; });
})();
+