ptywright 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -843,6 +843,186 @@ function createAgentTemplateSpec(flavor) {
843
843
  };
844
844
  }
845
845
  //#endregion
846
+ //#region src/agent/report_theme_css.ts
847
+ const TOKENS_LIGHT = `
848
+ --canvas: #f5f7fa;
849
+ --panel: #ffffff;
850
+ --raised: #fbfcfe;
851
+ --line: #e4e8ee;
852
+ --line-strong: #ccd3dd;
853
+ --ink: #131822;
854
+ --muted: #5a6473;
855
+ --faint: #8b95a3;
856
+ --accent: #0a7fd4;
857
+ --accent-ink: #ffffff;
858
+ --accent-soft: rgba(10, 127, 212, 0.12);
859
+ --pass: #18794e;
860
+ --pass-soft: rgba(24, 121, 78, 0.12);
861
+ --fail: #d12d4f;
862
+ --fail-soft: rgba(209, 45, 79, 0.1);
863
+ --changed: #ad6800;
864
+ --changed-soft: rgba(173, 104, 0, 0.12);
865
+ --shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 8px 24px -16px rgba(15, 23, 42, 0.24);`;
866
+ const TOKENS_DARK = `
867
+ --canvas: #0a0e16;
868
+ --panel: #111824;
869
+ --raised: #18212f;
870
+ --line: rgba(148, 163, 184, 0.16);
871
+ --line-strong: rgba(148, 163, 184, 0.3);
872
+ --ink: #e7edf6;
873
+ --muted: #9aa7b8;
874
+ --faint: #687486;
875
+ --accent: #58b6ff;
876
+ --accent-ink: #04101d;
877
+ --accent-soft: rgba(88, 182, 255, 0.16);
878
+ --pass: #46cd7c;
879
+ --pass-soft: rgba(70, 205, 124, 0.16);
880
+ --fail: #ff6b81;
881
+ --fail-soft: rgba(255, 107, 129, 0.16);
882
+ --changed: #f5b440;
883
+ --changed-soft: rgba(245, 180, 64, 0.16);
884
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 18px 48px -22px rgba(0, 0, 0, 0.7);`;
885
+ /** Design tokens only (no component rules); reusable when a page needs just the palette. */
886
+ function renderReportThemeTokens() {
887
+ return ` :root {
888
+ color-scheme: light dark;${TOKENS_LIGHT}
889
+ --radius: 14px;
890
+ --radius-sm: 9px;
891
+ --radius-xs: 6px;
892
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
893
+ --font-mono: ui-monospace, "SFMono-Regular", "JetBrains Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;
894
+ }
895
+ @media (prefers-color-scheme: dark) {
896
+ :root {${TOKENS_DARK}
897
+ }
898
+ }
899
+ :root[data-theme="light"] {${TOKENS_LIGHT}
900
+ }
901
+ :root[data-theme="dark"] {${TOKENS_DARK}
902
+ }`;
903
+ }
904
+ /** Full shared sheet: tokens + reset + primitive components. */
905
+ function renderReportThemeCss() {
906
+ return `${renderReportThemeTokens()}
907
+
908
+ * { box-sizing: border-box; }
909
+ body {
910
+ margin: 0;
911
+ background: var(--canvas);
912
+ color: var(--ink);
913
+ font-family: var(--font-sans);
914
+ font-size: 14px;
915
+ line-height: 1.5;
916
+ -webkit-font-smoothing: antialiased;
917
+ text-rendering: optimizeLegibility;
918
+ }
919
+
920
+ /* Top status rail — a thin build-status bar coloured by the overall result. */
921
+ .rail {
922
+ position: sticky;
923
+ top: 0;
924
+ z-index: 30;
925
+ height: 3px;
926
+ background: var(--line-strong);
927
+ }
928
+ .rail.pass { background: linear-gradient(90deg, var(--pass), color-mix(in oklab, var(--pass) 55%, var(--accent))); }
929
+ .rail.fail { background: linear-gradient(90deg, var(--fail), color-mix(in oklab, var(--fail) 60%, var(--changed))); }
930
+
931
+ .shell {
932
+ width: min(1140px, 100% - 40px);
933
+ margin-inline: auto;
934
+ padding: 30px 0 64px;
935
+ }
936
+ @media (max-width: 640px) {
937
+ .shell { width: min(100% - 24px, 1140px); padding-top: 18px; }
938
+ }
939
+
940
+ h1 { margin: 0; font-size: 25px; line-height: 1.15; letter-spacing: -0.02em; font-weight: 680; }
941
+ h2 { margin: 0; font-size: 14px; font-weight: 640; letter-spacing: 0.01em; color: var(--ink); }
942
+ a { color: var(--accent); text-decoration: none; }
943
+ a:hover { text-decoration: underline; }
944
+ code, pre, .mono { font-family: var(--font-mono); }
945
+
946
+ /* Status line: a coloured dot + bold label (replaces fat pills). */
947
+ .statusline { display: inline-flex; align-items: center; gap: 8px; font-weight: 660; font-size: 13px; letter-spacing: 0.04em; }
948
+ .statusline .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); box-shadow: 0 0 0 4px color-mix(in oklab, var(--muted) 22%, transparent); }
949
+ .statusline.pass { color: var(--pass); }
950
+ .statusline.pass .dot { background: var(--pass); box-shadow: 0 0 0 4px var(--pass-soft); }
951
+ .statusline.fail { color: var(--fail); }
952
+ .statusline.fail .dot { background: var(--fail); box-shadow: 0 0 0 4px var(--fail-soft); }
953
+
954
+ /* Tiny inline status dot for dense rows. */
955
+ .dot-sm { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
956
+ .dot-sm.pass { background: var(--pass); }
957
+ .dot-sm.fail { background: var(--fail); }
958
+
959
+ /* Chips: low-key metadata tags. */
960
+ .chip {
961
+ display: inline-flex; align-items: center; gap: 6px; min-height: 26px;
962
+ padding: 0 10px; border: 1px solid var(--line); border-radius: 999px;
963
+ background: var(--panel); color: var(--muted); font-size: 12px; white-space: nowrap;
964
+ }
965
+ .chip.mono { font-family: var(--font-mono); }
966
+ .chip-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
967
+
968
+ /* Stat strip: compact metrics with tabular numerals + hairline dividers. */
969
+ .statstrip {
970
+ display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
971
+ border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden; background: var(--panel);
972
+ }
973
+ .statstrip .stat { padding: 12px 16px; border-left: 1px solid var(--line); }
974
+ .statstrip .stat:first-child { border-left: 0; }
975
+ .stat .num { display: block; font-size: 21px; font-weight: 680; line-height: 1.1; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
976
+ .stat .lbl { display: block; margin-top: 3px; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
977
+ .stat.pass .num { color: var(--pass); }
978
+ .stat.fail .num { color: var(--fail); }
979
+ @media (max-width: 560px) {
980
+ .statstrip { grid-auto-flow: row; grid-auto-columns: auto; }
981
+ .statstrip .stat { border-left: 0; border-top: 1px solid var(--line); }
982
+ .statstrip .stat:first-child { border-top: 0; }
983
+ }
984
+
985
+ /* Pass-rate meter: a segmented progress bar. */
986
+ .meter { height: 8px; border-radius: 999px; background: var(--line); overflow: hidden; }
987
+ .meter > i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--pass), color-mix(in oklab, var(--pass) 60%, var(--accent))); }
988
+ .meter.has-fail > i { background: linear-gradient(90deg, var(--fail), var(--changed)); }
989
+
990
+ /* Panel: the standard surface card. */
991
+ .panel { border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel); padding: 18px; box-shadow: var(--shadow); }
992
+ .panel + .panel { margin-top: 16px; }
993
+ .panel > h2 { margin-bottom: 14px; }
994
+
995
+ /* Path display: muted parent dir (middle-ellipsis) + bright basename. */
996
+ .path { display: inline-flex; max-width: 100%; min-width: 0; align-items: baseline; font-family: var(--font-mono); font-size: 12.5px; }
997
+ .path .dir { min-width: 0; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: rtl; }
998
+ .path .base { color: var(--muted); font-weight: 600; flex: none; }
999
+
1000
+ /* Code block with one-click copy. */
1001
+ .codeblock { position: relative; border: 1px solid var(--line); border-radius: var(--radius-sm); background: color-mix(in oklab, var(--canvas) 55%, var(--panel)); }
1002
+ .codeblock > .lbl { display: block; padding: 8px 12px 0; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 640; }
1003
+ .codeblock > pre { margin: 0; padding: 8px 44px 12px 12px; overflow-x: auto; font-size: 12.5px; line-height: 1.55; color: var(--ink); white-space: pre-wrap; word-break: break-word; }
1004
+ .copybtn {
1005
+ position: absolute; top: 8px; right: 8px; width: 28px; height: 28px;
1006
+ display: inline-flex; align-items: center; justify-content: center;
1007
+ border: 1px solid var(--line); border-radius: var(--radius-xs); background: var(--panel);
1008
+ color: var(--muted); cursor: pointer; font-size: 13px; line-height: 1; transition: all 0.12s ease;
1009
+ }
1010
+ .copybtn:hover { color: var(--ink); border-color: var(--line-strong); }
1011
+ .copybtn.copied { color: var(--pass); border-color: color-mix(in oklab, var(--pass) 45%, var(--line)); }
1012
+
1013
+ /* Action links / buttons. */
1014
+ .btn {
1015
+ display: inline-flex; align-items: center; gap: 5px; min-height: 28px; padding: 0 11px;
1016
+ border: 1px solid var(--line); border-radius: var(--radius-xs); background: var(--panel);
1017
+ color: var(--ink); font-size: 12.5px; font-weight: 560; cursor: pointer; transition: all 0.12s ease;
1018
+ }
1019
+ .btn:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
1020
+ .btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
1021
+ .btn.primary:hover { color: var(--accent-ink); filter: brightness(1.05); }
1022
+
1023
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }`;
1024
+ }
1025
+ //#endregion
846
1026
  //#region src/agent/browser.ts
847
1027
  async function launchAgentBrowser(args) {
848
1028
  let lastError;
@@ -1236,20 +1416,15 @@ function renderRawArtifactViewerCss() {
1236
1416
  width: max-content;
1237
1417
  min-height: 100%;
1238
1418
  margin: 0;
1239
- background:
1240
- linear-gradient(180deg, color-mix(in srgb, #162033 92%, black), #0c111d);
1241
- color: #e6edf7;
1242
- font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
1419
+ background: var(--panel);
1420
+ color: var(--ink);
1421
+ font-family: var(--font-mono);
1243
1422
  font-size: 13px;
1244
1423
  line-height: 1.45;
1245
1424
  overflow: visible;
1246
1425
  padding: 12px;
1247
1426
  white-space: pre;
1248
1427
  }
1249
- .viewer-page[data-theme="light"] .raw-artifact-text {
1250
- background: #ffffff;
1251
- color: #4c4f69;
1252
- }
1253
1428
  ${renderReportViewportPanCss()}`;
1254
1429
  }
1255
1430
  function renderRawArtifactViewerScript() {
@@ -1290,32 +1465,15 @@ function renderArtifactViewerFragment(args) {
1290
1465
  //#endregion
1291
1466
  //#region src/agent/report_artifact_viewer_shell_css.ts
1292
1467
  function renderArtifactViewerShellCss() {
1293
- return ` :root {
1294
- color-scheme: dark;
1295
- --bg: #080d16;
1296
- --panel: #0c111d;
1297
- --line: rgba(148, 163, 184, 0.26);
1298
- --ink: #e6edf7;
1299
- --muted: #91a0b8;
1300
- --focus: #79c0ff;
1301
- font-family:
1302
- ui-sans-serif,
1303
- system-ui,
1304
- -apple-system,
1305
- BlinkMacSystemFont,
1306
- "Segoe UI",
1307
- sans-serif;
1308
- }
1309
- * {
1310
- box-sizing: border-box;
1311
- }
1468
+ return `${renderReportThemeCss()}
1469
+
1312
1470
  html,
1313
1471
  body {
1314
1472
  width: 100%;
1315
1473
  height: 100%;
1316
1474
  margin: 0;
1317
1475
  overflow: hidden;
1318
- background: var(--bg);
1476
+ background: var(--canvas);
1319
1477
  color: var(--ink);
1320
1478
  }
1321
1479
  .viewer-page {
@@ -1333,7 +1491,7 @@ function renderArtifactViewerShellCss() {
1333
1491
  align-items: center;
1334
1492
  min-width: 0;
1335
1493
  border-bottom: 1px solid var(--line);
1336
- background: color-mix(in srgb, var(--panel) 88%, black);
1494
+ background: var(--raised);
1337
1495
  padding: 10px 12px;
1338
1496
  }
1339
1497
  .viewer-title {
@@ -1341,7 +1499,8 @@ function renderArtifactViewerShellCss() {
1341
1499
  margin-right: auto;
1342
1500
  overflow-wrap: anywhere;
1343
1501
  font-size: 14px;
1344
- font-weight: 700;
1502
+ font-weight: 640;
1503
+ color: var(--ink);
1345
1504
  }
1346
1505
  .viewer-link,
1347
1506
  .viewer-pill {
@@ -1354,10 +1513,15 @@ function renderArtifactViewerShellCss() {
1354
1513
  color: var(--muted);
1355
1514
  font-size: 12px;
1356
1515
  text-decoration: none;
1516
+ transition: all 0.12s ease;
1357
1517
  }
1358
1518
  .viewer-link {
1359
- color: var(--focus);
1360
- font-weight: 700;
1519
+ color: var(--accent);
1520
+ font-weight: 640;
1521
+ }
1522
+ .viewer-link:hover {
1523
+ border-color: var(--accent);
1524
+ background: var(--accent-soft);
1361
1525
  }
1362
1526
  .viewer-stage {
1363
1527
  display: grid;
@@ -1368,9 +1532,7 @@ function renderArtifactViewerShellCss() {
1368
1532
  justify-items: center;
1369
1533
  align-content: start;
1370
1534
  overflow: hidden;
1371
- background:
1372
- radial-gradient(circle at top left, rgba(121, 192, 255, 0.1), transparent 32%),
1373
- #060a13;
1535
+ background: var(--canvas);
1374
1536
  padding: 14px;
1375
1537
  }
1376
1538
  .viewer-viewport {
@@ -1381,10 +1543,10 @@ function renderArtifactViewerShellCss() {
1381
1543
  overflow: auto;
1382
1544
  overscroll-behavior: contain;
1383
1545
  border: 0;
1384
- border-radius: 8px;
1385
- background: #0c111d;
1546
+ border-radius: var(--radius-sm);
1547
+ background: var(--panel);
1386
1548
  outline: 1px solid var(--line);
1387
- box-shadow: 0 18px 52px rgba(0, 0, 0, 0.34);
1549
+ box-shadow: var(--shadow);
1388
1550
  }
1389
1551
  .viewer-viewport[data-mobile="true"] {
1390
1552
  width: min(var(--config-viewport-width), 100%);
@@ -1397,22 +1559,7 @@ function renderArtifactViewerShellCss() {
1397
1559
  width: 100%;
1398
1560
  height: 100%;
1399
1561
  border: 0;
1400
- background: #0c111d;
1401
- }
1402
- .viewer-page[data-theme="light"] {
1403
- --bg: #f8fafc;
1404
- --panel: #ffffff;
1405
- --line: rgba(15, 23, 42, 0.14);
1406
- --ink: #0f172a;
1407
- --muted: #64748b;
1408
- --focus: #1e66f5;
1409
- }
1410
- .viewer-page[data-theme="light"] .viewer-stage {
1411
- background: #f8fafc;
1412
- }
1413
- .viewer-page[data-theme="light"] .viewer-viewport,
1414
- .viewer-page[data-theme="light"] .dom-viewer-frame {
1415
- background: #ffffff;
1562
+ background: var(--panel);
1416
1563
  }
1417
1564
  @media (max-width: 720px) {
1418
1565
  .viewer-toolbar {
@@ -2171,36 +2318,114 @@ function readArtifactText(path) {
2171
2318
  //#endregion
2172
2319
  //#region src/agent/report_index_artifacts.ts
2173
2320
  function renderAgentReportArtifacts(args) {
2174
- return args.artifacts.map((artifact) => renderArtifactRow({
2175
- artifact,
2321
+ if (args.artifacts.length === 0) return "<p>No artifacts were captured.</p>";
2322
+ const byViewport = /* @__PURE__ */ new Map();
2323
+ for (const artifact of args.artifacts) {
2324
+ const list = byViewport.get(artifact.viewport) ?? [];
2325
+ list.push(artifact);
2326
+ byViewport.set(artifact.viewport, list);
2327
+ }
2328
+ return Array.from(byViewport.entries()).map(([viewport, artifacts]) => renderViewportGroup({
2329
+ viewport,
2330
+ artifacts,
2176
2331
  artifactsDir: args.artifactsDir,
2177
2332
  reportPath: args.reportPath
2178
- })).join("\n") || "<p>No artifacts were captured.</p>";
2333
+ })).join("\n");
2334
+ }
2335
+ function renderViewportGroup(args) {
2336
+ const { viewport, artifacts, artifactsDir, reportPath } = args;
2337
+ const byName = /* @__PURE__ */ new Map();
2338
+ for (const artifact of artifacts) {
2339
+ const list = byName.get(artifact.name) ?? [];
2340
+ list.push(artifact);
2341
+ byName.set(artifact.name, list);
2342
+ }
2343
+ const artifactRows = Array.from(byName.entries()).map(([name, artifacts]) => renderArtifactGroup({
2344
+ name,
2345
+ artifacts,
2346
+ artifactsDir,
2347
+ reportPath
2348
+ })).join("\n");
2349
+ return `<div class="viewport-group">
2350
+ <h3 class="viewport-title">${escapeHtml(viewport)}</h3>
2351
+ <div class="viewport-artifacts">
2352
+ ${artifactRows}
2353
+ </div>
2354
+ </div>`;
2179
2355
  }
2180
- function renderArtifactRow(args) {
2356
+ function renderArtifactGroup(args) {
2357
+ const { name, artifacts, artifactsDir, reportPath } = args;
2358
+ const chips = artifacts.map((artifact) => {
2359
+ const state = artifact.ok ? "pass" : "fail";
2360
+ const viewerPath = artifactViewerPath(artifact);
2361
+ return `<a href="${escapeAttribute(viewerPath ? relativeHref(reportPath, viewerPath, artifactsDir) : relativeHref(reportPath, artifact.path, artifactsDir))}" class="artifact-chip ${state}" title="${escapeHtml(artifact.kind)}">
2362
+ ${escapeHtml(artifact.kind)}
2363
+ </a>`;
2364
+ }).join("");
2365
+ const failedArtifact = artifacts.find((a) => !a.ok);
2366
+ const detailView = failedArtifact ? renderFailedArtifactDetail({
2367
+ artifact: failedArtifact,
2368
+ artifactsDir,
2369
+ reportPath
2370
+ }) : "";
2371
+ return `<div class="artifact-group">
2372
+ <div class="artifact-group-header">
2373
+ <span class="artifact-name">${escapeHtml(name)}</span>
2374
+ <div class="artifact-chips">
2375
+ ${chips}
2376
+ </div>
2377
+ </div>
2378
+ ${detailView}
2379
+ </div>`;
2380
+ }
2381
+ function renderFailedArtifactDetail(args) {
2181
2382
  const { artifact, artifactsDir, reportPath } = args;
2182
- const state = artifact.ok ? "pass" : "fail";
2183
- const href = relativeHref(reportPath, artifact.path, artifactsDir);
2383
+ const currentHref = relativeHref(reportPath, artifact.path, artifactsDir);
2184
2384
  const baselineHref = artifact.baselinePath ? relativeHref(reportPath, artifact.baselinePath, artifactsDir) : "";
2185
2385
  const diffHref = artifact.diffPath ? relativeHref(reportPath, artifact.diffPath, artifactsDir) : "";
2186
- const viewerPath = artifactViewerPath(artifact);
2187
- const viewerHref = viewerPath ? relativeHref(reportPath, viewerPath, artifactsDir) : "";
2188
- return `<article class="artifact">
2189
- <div class="artifact-summary">
2190
- <span class="badge ${state}">${state}</span>
2191
- <div class="artifact-meta">
2192
- <div class="artifact-links">
2193
- <a href="${escapeAttribute(viewerHref || href)}">${escapeHtml(artifact.kind)}</a>
2194
- ${viewerHref ? `<a href="${escapeAttribute(href)}">raw</a>` : ""}
2195
- ${baselineHref ? `<a href="${escapeAttribute(baselineHref)}">baseline</a>` : ""}
2196
- ${diffHref ? `<a href="${escapeAttribute(diffHref)}">diff</a>` : ""}
2386
+ const isDom = artifact.kind === "dom";
2387
+ const isScreenshot = artifact.kind === "screenshot";
2388
+ if (isDom || isScreenshot) return `<div class="artifact-detail">
2389
+ <div class="artifact-comparison">
2390
+ ${baselineHref ? `<div class="artifact-preview">
2391
+ <div class="artifact-preview-label">Baseline</div>
2392
+ <div class="device-frame">
2393
+ <iframe src="${escapeAttribute(baselineHref)}" loading="lazy" sandbox="allow-same-origin"></iframe>
2394
+ </div>
2395
+ </div>` : ""}
2396
+ <div class="artifact-preview">
2397
+ <div class="artifact-preview-label">Current</div>
2398
+ <div class="device-frame ${artifact.ok ? "" : "failed"}">
2399
+ <iframe src="${escapeAttribute(currentHref)}" loading="lazy" sandbox="allow-same-origin"></iframe>
2400
+ </div>
2197
2401
  </div>
2198
- <div><code>${escapeHtml(artifact.viewport)} / ${escapeHtml(artifact.name)}</code></div>
2199
- ${artifact.error ? `<div><code>${escapeHtml(artifact.error)}</code></div>` : ""}
2402
+ ${diffHref ? `<div class="artifact-preview">
2403
+ <div class="artifact-preview-label">Diff</div>
2404
+ <div class="device-frame diff">
2405
+ <iframe src="${escapeAttribute(diffHref)}" loading="lazy" sandbox="allow-same-origin"></iframe>
2406
+ </div>
2407
+ </div>` : ""}
2408
+ </div>
2409
+ ${artifact.error ? `<div class="artifact-error">
2410
+ <strong>Error:</strong> ${escapeHtml(artifact.error)}
2411
+ </div>` : ""}
2412
+ <div class="artifact-meta-row">
2413
+ <code class="artifact-hash">${escapeHtml(artifact.hash ?? "")}</code>
2200
2414
  </div>
2415
+ </div>`;
2416
+ return `<div class="artifact-detail">
2417
+ <div class="artifact-links">
2418
+ <a href="${escapeAttribute(currentHref)}" class="btn">View Current</a>
2419
+ ${baselineHref ? `<a href="${escapeAttribute(baselineHref)}" class="btn">View Baseline</a>` : ""}
2420
+ ${diffHref ? `<a href="${escapeAttribute(diffHref)}" class="btn">View Diff</a>` : ""}
2421
+ </div>
2422
+ ${artifact.error ? `<div class="artifact-error">
2423
+ <strong>Error:</strong> ${escapeHtml(artifact.error)}
2424
+ </div>` : ""}
2425
+ <div class="artifact-meta-row">
2201
2426
  <code class="artifact-hash">${escapeHtml(artifact.hash ?? "")}</code>
2202
2427
  </div>
2203
- </article>`;
2428
+ </div>`;
2204
2429
  }
2205
2430
  //#endregion
2206
2431
  //#region src/agent/report_index_commands.ts
@@ -2213,107 +2438,223 @@ function renderAgentReportCommandBlock(label, argv) {
2213
2438
  //#endregion
2214
2439
  //#region src/agent/report_index_artifacts_css.ts
2215
2440
  function renderAgentReportArtifactsCss() {
2216
- return ` .artifacts {
2217
- display: grid;
2218
- gap: 14px;
2441
+ return ` /* Viewport grouping */
2442
+ .viewport-group {
2443
+ margin-bottom: 32px;
2444
+ min-width: 0;
2219
2445
  }
2220
- .artifact {
2446
+ .viewport-group:last-child {
2447
+ margin-bottom: 0;
2448
+ }
2449
+ .viewport-title {
2450
+ margin: 0 0 16px;
2451
+ font-size: 18px;
2452
+ font-weight: 660;
2453
+ color: var(--ink);
2454
+ padding-bottom: 10px;
2455
+ border-bottom: 2px solid var(--line);
2456
+ overflow-wrap: break-word;
2457
+ word-break: break-word;
2458
+ }
2459
+ .viewport-artifacts {
2221
2460
  display: grid;
2222
- gap: 12px;
2461
+ gap: 16px;
2462
+ min-width: 0;
2463
+ }
2464
+
2465
+ /* Artifact group (by name: status, ready, etc.) */
2466
+ .artifact-group {
2223
2467
  border: 1px solid var(--line);
2224
- border-radius: 8px;
2225
- padding: 12px;
2468
+ border-radius: var(--radius);
2226
2469
  background: var(--panel);
2470
+ overflow: hidden;
2471
+ box-shadow: var(--shadow);
2472
+ min-width: 0;
2227
2473
  }
2228
- .artifact-summary {
2229
- display: grid;
2230
- grid-template-columns: auto minmax(0, 1fr) minmax(96px, auto);
2231
- gap: 14px;
2232
- align-items: start;
2474
+ .artifact-group-header {
2475
+ display: flex;
2476
+ align-items: center;
2477
+ justify-content: space-between;
2478
+ gap: 12px;
2479
+ padding: 14px 16px;
2480
+ background: var(--raised);
2481
+ border-bottom: 1px solid var(--line);
2482
+ min-width: 0;
2233
2483
  }
2234
- .artifact-meta {
2235
- display: grid;
2236
- gap: 4px;
2484
+ .artifact-name {
2485
+ font-weight: 640;
2486
+ font-size: 14px;
2487
+ color: var(--ink);
2488
+ font-family: var(--font-mono);
2489
+ overflow-wrap: break-word;
2490
+ word-break: break-word;
2237
2491
  min-width: 0;
2238
2492
  }
2239
- .artifact-links {
2493
+ .artifact-chips {
2240
2494
  display: flex;
2241
2495
  flex-wrap: wrap;
2242
- gap: 8px 12px;
2496
+ gap: 6px;
2497
+ flex-shrink: 0;
2243
2498
  }
2244
- .artifact a {
2245
- color: var(--focus);
2246
- font-weight: 700;
2499
+ .artifact-chip {
2500
+ display: inline-flex;
2501
+ align-items: center;
2502
+ padding: 4px 10px;
2503
+ border-radius: var(--radius-xs);
2504
+ font-size: 11px;
2505
+ font-weight: 640;
2506
+ text-transform: uppercase;
2507
+ letter-spacing: 0.03em;
2247
2508
  text-decoration: none;
2248
- }
2249
- .artifact code {
2509
+ transition: all 0.12s ease;
2510
+ border: 1px solid var(--line);
2511
+ background: var(--panel);
2250
2512
  color: var(--muted);
2251
- overflow-wrap: anywhere;
2513
+ white-space: nowrap;
2252
2514
  }
2253
- .artifact-hash {
2254
- justify-self: end;
2255
- text-align: right;
2515
+ .artifact-chip.pass {
2516
+ border-color: var(--pass);
2517
+ background: var(--pass-soft);
2518
+ color: var(--pass);
2256
2519
  }
2257
- .badge {
2258
- justify-self: start;
2259
- border-radius: 999px;
2260
- padding: 5px 9px;
2261
- background: color-mix(in oklch, var(--line) 52%, transparent);
2262
- color: var(--muted);
2520
+ .artifact-chip.fail {
2521
+ border-color: var(--fail);
2522
+ background: var(--fail-soft);
2523
+ color: var(--fail);
2524
+ }
2525
+ .artifact-chip:hover {
2526
+ transform: translateY(-1px);
2527
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
2528
+ }
2529
+
2530
+ /* Artifact detail (for failed artifacts) */
2531
+ .artifact-detail {
2532
+ padding: 16px;
2533
+ min-width: 0;
2534
+ }
2535
+ .artifact-comparison {
2536
+ display: grid;
2537
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
2538
+ gap: 20px;
2539
+ margin-bottom: 16px;
2540
+ min-width: 0;
2541
+ }
2542
+ .artifact-preview {
2543
+ display: flex;
2544
+ flex-direction: column;
2545
+ gap: 10px;
2546
+ min-width: 0;
2547
+ }
2548
+ .artifact-preview-label {
2263
2549
  font-size: 12px;
2264
- font-weight: 700;
2550
+ font-weight: 640;
2551
+ color: var(--muted);
2552
+ text-transform: uppercase;
2553
+ letter-spacing: 0.05em;
2554
+ overflow-wrap: break-word;
2555
+ }
2556
+
2557
+ /* Device frame for DOM/screenshot previews */
2558
+ .device-frame {
2559
+ position: relative;
2560
+ border: 1px solid var(--line);
2561
+ border-radius: var(--radius-sm);
2562
+ background: var(--canvas);
2563
+ overflow: hidden;
2564
+ box-shadow: var(--shadow);
2565
+ aspect-ratio: 3 / 4;
2566
+ max-height: 600px;
2567
+ min-width: 0;
2265
2568
  }
2266
- .badge.fail {
2267
- background: color-mix(in oklch, var(--bad) 12%, var(--panel));
2268
- color: var(--bad);
2569
+ .device-frame.failed {
2570
+ border-color: var(--fail);
2571
+ box-shadow: 0 0 0 2px var(--fail-soft);
2269
2572
  }
2270
- .badge.pass {
2271
- background: color-mix(in oklch, var(--good) 12%, var(--panel));
2272
- color: var(--good);
2573
+ .device-frame.diff {
2574
+ border-color: var(--changed);
2575
+ box-shadow: 0 0 0 2px var(--changed-soft);
2576
+ }
2577
+ .device-frame iframe {
2578
+ width: 100%;
2579
+ height: 100%;
2580
+ border: 0;
2581
+ display: block;
2582
+ }
2583
+
2584
+ /* Artifact error message */
2585
+ .artifact-error {
2586
+ padding: 12px;
2587
+ background: var(--fail-soft);
2588
+ border: 1px solid color-mix(in oklab, var(--fail) 25%, var(--line));
2589
+ border-radius: var(--radius-xs);
2590
+ color: var(--fail);
2591
+ font-size: 13px;
2592
+ margin-bottom: 12px;
2593
+ overflow-wrap: break-word;
2594
+ word-break: break-word;
2595
+ min-width: 0;
2596
+ }
2597
+ .artifact-error strong {
2598
+ font-weight: 660;
2599
+ }
2600
+
2601
+ /* Artifact metadata row */
2602
+ .artifact-meta-row {
2603
+ display: flex;
2604
+ justify-content: flex-end;
2605
+ padding-top: 12px;
2606
+ border-top: 1px solid var(--line);
2607
+ min-width: 0;
2608
+ }
2609
+ .artifact-hash {
2610
+ font-family: var(--font-mono);
2611
+ font-size: 11px;
2612
+ color: var(--faint);
2613
+ overflow: hidden;
2614
+ text-overflow: ellipsis;
2615
+ white-space: nowrap;
2616
+ max-width: 100%;
2617
+ min-width: 0;
2618
+ }
2619
+
2620
+ /* Artifact links (for non-visual artifacts) */
2621
+ .artifact-links {
2622
+ display: flex;
2623
+ flex-wrap: wrap;
2624
+ gap: 8px;
2625
+ margin-bottom: 12px;
2626
+ min-width: 0;
2273
2627
  }
2628
+
2629
+ /* Responsive adjustments */
2274
2630
  @media (max-width: 720px) {
2275
- .artifact-summary {
2631
+ .artifact-comparison {
2276
2632
  grid-template-columns: 1fr;
2277
2633
  }
2278
- .artifact-hash {
2279
- justify-self: start;
2280
- text-align: left;
2634
+ .artifact-group-header {
2635
+ flex-direction: column;
2636
+ align-items: flex-start;
2637
+ }
2638
+ .device-frame {
2639
+ max-height: 500px;
2281
2640
  }
2282
2641
  }`;
2283
2642
  }
2284
2643
  //#endregion
2285
2644
  //#region src/agent/report_index_base_css.ts
2286
2645
  function renderAgentReportBaseCss() {
2287
- return ` :root {
2288
- color-scheme: light;
2289
- --bg: oklch(97.5% 0.008 210);
2290
- --ink: oklch(19% 0.018 230);
2291
- --muted: oklch(48% 0.02 230);
2292
- --line: oklch(86% 0.018 230);
2293
- --panel: oklch(99% 0.006 210);
2294
- --good: oklch(55% 0.15 155);
2295
- --bad: oklch(58% 0.19 25);
2296
- --focus: oklch(55% 0.14 235);
2297
- font-family:
2298
- ui-sans-serif,
2299
- system-ui,
2300
- -apple-system,
2301
- BlinkMacSystemFont,
2302
- "Segoe UI",
2303
- sans-serif;
2304
- }
2305
- * { box-sizing: border-box; }
2306
- body {
2307
- margin: 0;
2308
- background: var(--bg);
2309
- color: var(--ink);
2310
- }
2646
+ return `${renderReportThemeCss()}
2647
+
2311
2648
  main {
2312
2649
  display: grid;
2313
2650
  gap: 24px;
2314
2651
  width: min(1180px, calc(100vw - 32px));
2315
2652
  margin: 0 auto;
2316
2653
  padding: 32px 0 48px;
2654
+ min-width: 0;
2655
+ }
2656
+ main > * {
2657
+ min-width: 0;
2317
2658
  }
2318
2659
  header {
2319
2660
  display: grid;
@@ -2322,27 +2663,43 @@ function renderAgentReportBaseCss() {
2322
2663
  align-items: start;
2323
2664
  border-bottom: 1px solid var(--line);
2324
2665
  padding-bottom: 24px;
2666
+ min-width: 0;
2667
+ }
2668
+ header > * {
2669
+ min-width: 0;
2325
2670
  }
2326
2671
  h1 {
2327
2672
  margin: 0;
2328
2673
  font-size: 28px;
2329
2674
  line-height: 1.15;
2330
2675
  letter-spacing: 0;
2676
+ max-width: 100%;
2677
+ overflow-wrap: anywhere;
2331
2678
  }
2332
2679
  h2 {
2333
2680
  margin: 0;
2334
2681
  font-size: 18px;
2335
2682
  line-height: 1.25;
2683
+ overflow-wrap: anywhere;
2684
+ }
2685
+ h3 {
2686
+ margin: 0;
2687
+ font-size: 16px;
2688
+ line-height: 1.3;
2689
+ overflow-wrap: anywhere;
2336
2690
  }
2337
2691
  .meta {
2338
2692
  display: flex;
2339
2693
  flex-wrap: wrap;
2340
2694
  gap: 8px;
2341
2695
  margin-top: 12px;
2696
+ min-width: 0;
2342
2697
  }
2343
2698
  .pill,
2344
2699
  .status {
2345
2700
  display: inline-flex;
2701
+ max-width: 100%;
2702
+ min-width: 0;
2346
2703
  min-height: 32px;
2347
2704
  align-items: center;
2348
2705
  border: 1px solid var(--line);
@@ -2350,25 +2707,44 @@ function renderAgentReportBaseCss() {
2350
2707
  padding: 0 12px;
2351
2708
  color: var(--muted);
2352
2709
  font-size: 13px;
2710
+ overflow-wrap: anywhere;
2353
2711
  }
2354
2712
  .status {
2355
2713
  font-weight: 700;
2356
2714
  }
2357
2715
  .status.pass {
2358
- border-color: color-mix(in oklch, var(--good) 42%, var(--line));
2359
- color: var(--good);
2716
+ border-color: var(--pass);
2717
+ background: var(--pass-soft);
2718
+ color: var(--pass);
2360
2719
  }
2361
2720
  .status.fail {
2362
- border-color: color-mix(in oklch, var(--bad) 44%, var(--line));
2363
- color: var(--bad);
2721
+ border-color: var(--fail);
2722
+ background: var(--fail-soft);
2723
+ color: var(--fail);
2364
2724
  }
2365
2725
  .panel {
2366
2726
  display: grid;
2367
2727
  gap: 16px;
2368
2728
  border: 1px solid var(--line);
2369
- border-radius: 8px;
2729
+ border-radius: var(--radius);
2370
2730
  background: var(--panel);
2371
2731
  padding: 18px;
2732
+ min-width: 0;
2733
+ box-shadow: var(--shadow);
2734
+ }
2735
+ .panel > * {
2736
+ min-width: 0;
2737
+ }
2738
+ .panel p {
2739
+ margin: 0;
2740
+ max-width: 100%;
2741
+ overflow-wrap: break-word;
2742
+ word-break: break-word;
2743
+ }
2744
+ code {
2745
+ font-family: var(--font-mono);
2746
+ overflow-wrap: break-word;
2747
+ word-break: break-all;
2372
2748
  }
2373
2749
  @media (max-width: 720px) {
2374
2750
  main {
@@ -2389,28 +2765,38 @@ function renderAgentReportCommandsCss() {
2389
2765
  return ` .commands {
2390
2766
  display: grid;
2391
2767
  gap: 10px;
2768
+ min-width: 0;
2392
2769
  }
2393
2770
  .command {
2394
2771
  display: grid;
2395
2772
  gap: 5px;
2773
+ min-width: 0;
2396
2774
  }
2397
2775
  .command span {
2398
2776
  color: var(--muted);
2399
2777
  font-size: 13px;
2400
2778
  font-weight: 700;
2779
+ overflow-wrap: anywhere;
2401
2780
  }
2402
2781
  .artifact code,
2403
2782
  pre {
2404
2783
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
2405
2784
  }
2406
2785
  pre {
2407
- overflow: auto;
2786
+ overflow-x: auto;
2787
+ overflow-y: hidden;
2788
+ max-width: 100%;
2789
+ min-width: 0;
2790
+ width: 100%;
2408
2791
  margin: 0;
2409
2792
  border-radius: 8px;
2410
2793
  background: oklch(20% 0.015 230);
2411
2794
  color: oklch(92% 0.012 230);
2412
2795
  padding: 14px;
2413
2796
  line-height: 1.5;
2797
+ white-space: pre;
2798
+ word-wrap: normal;
2799
+ word-break: normal;
2414
2800
  }`;
2415
2801
  }
2416
2802
  //#endregion
@@ -2418,25 +2804,29 @@ function renderAgentReportCommandsCss() {
2418
2804
  function renderAgentReportSummaryCss() {
2419
2805
  return ` .summary {
2420
2806
  display: grid;
2421
- grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
2807
+ grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
2422
2808
  gap: 12px;
2809
+ min-width: 0;
2423
2810
  }
2424
2811
  .metric {
2425
2812
  border: 1px solid var(--line);
2426
2813
  border-radius: 8px;
2427
2814
  padding: 14px;
2428
2815
  background: color-mix(in oklch, var(--panel) 82%, var(--bg));
2816
+ min-width: 0;
2429
2817
  }
2430
2818
  .metric strong {
2431
2819
  display: block;
2432
2820
  font-size: 24px;
2433
2821
  line-height: 1.1;
2822
+ overflow-wrap: anywhere;
2434
2823
  }
2435
2824
  .metric span {
2436
2825
  display: block;
2437
2826
  margin-top: 4px;
2438
2827
  color: var(--muted);
2439
2828
  font-size: 13px;
2829
+ overflow-wrap: anywhere;
2440
2830
  }`;
2441
2831
  }
2442
2832
  //#endregion
@@ -3409,4 +3799,4 @@ function defaultSpecNameForPath(path) {
3409
3799
  return sanitizeArtifactName(basename(path, extname(path)));
3410
3800
  }
3411
3801
  //#endregion
3412
- export { writeAgentManifestPath as C, escapeHtml as D, escapeAttribute as E, normalizeAgentFlowSpec as O, validateAgentManifestFiles as S, readAgentCassettePath as T, formatArgv as _, formatAgentLaunchPlan as a, isAgentManifestLike as b, launchAgentBrowser as c, AGENT_RUN_RECORD_SCHEMA_URL as d, agentRunModeSchema as f, writeAgentRunRecordPath as g, readAgentRunRecordPath as h, runAgentSpecPath as i, sanitizeArtifactName as k, createAgentTemplateSpec as l, isAgentRunRecordLike as m, replayAgentRecordPath as n, resolveAgentLaunchTarget as o, formatAgentArgv as p, runAgentSpec as r, normalizeAgentFlowSpecWithConfig as s, defaultSpecNameForPath as t, loadAgentSpec as u, AGENT_MANIFEST_FILE_NAME as v, isAgentCassetteLike as w, readAgentManifestPath as x, agentManifestPath as y };
3802
+ export { sanitizeArtifactName as A, validateAgentManifestFiles as C, escapeAttribute as D, readAgentCassettePath as E, escapeHtml as O, readAgentManifestPath as S, isAgentCassetteLike as T, writeAgentRunRecordPath as _, formatAgentLaunchPlan as a, agentManifestPath as b, launchAgentBrowser as c, loadAgentSpec as d, AGENT_RUN_RECORD_SCHEMA_URL as f, readAgentRunRecordPath as g, isAgentRunRecordLike as h, runAgentSpecPath as i, normalizeAgentFlowSpec as k, renderReportThemeCss as l, formatAgentArgv as m, replayAgentRecordPath as n, resolveAgentLaunchTarget as o, agentRunModeSchema as p, runAgentSpec as r, normalizeAgentFlowSpecWithConfig as s, defaultSpecNameForPath as t, createAgentTemplateSpec as u, formatArgv as v, writeAgentManifestPath as w, isAgentManifestLike as x, AGENT_MANIFEST_FILE_NAME as y };