executable-stories-react 0.10.0 → 0.11.0

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.
@@ -334,23 +334,11 @@ function useReport() {
334
334
  return ctx.report;
335
335
  }
336
336
 
337
- // ../executable-stories-core/dist/utils/duration.js
338
- function formatDuration(ms) {
339
- if (ms < 1e3) {
340
- return `${Math.round(ms)} ms`;
341
- }
342
- if (ms < 6e4) {
343
- return `${(ms / 1e3).toFixed(2)} s`;
344
- }
345
- const minutes = Math.floor(ms / 6e4);
346
- const seconds = Math.floor(ms % 6e4 / 1e3);
347
- if (minutes < 60) {
348
- return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
349
- }
350
- const hours = Math.floor(minutes / 60);
351
- const remainingMinutes = minutes % 60;
352
- return `${hours}h ${remainingMinutes}m`;
353
- }
337
+ // src/components/ReportScenario.tsx
338
+ var import_duration3 = require("executable-stories-core/utils/duration");
339
+
340
+ // src/components/ReportSteps.tsx
341
+ var import_duration = require("executable-stories-core/utils/duration");
354
342
 
355
343
  // src/components/doc/DocNote.tsx
356
344
  var import_jsx_runtime3 = require("react/jsx-runtime");
@@ -419,7 +407,7 @@ function DocTable({ entry }) {
419
407
  entry.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
420
408
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
421
409
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
422
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: row.map((cell2, j) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell2 }, j)) }, i)) })
410
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
423
411
  ] }) })
424
412
  ] });
425
413
  }
@@ -439,36 +427,29 @@ function DocLink({ entry }) {
439
427
  );
440
428
  }
441
429
 
442
- // src/components/doc/DocSection.tsx
430
+ // src/lib/markdown.ts
443
431
  var import_marked = require("marked");
444
432
 
445
- // ../executable-stories-core/dist/utils/url.js
446
- function safeUrl(value) {
447
- if (!value) return void 0;
448
- const trimmed = value.trim();
449
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
450
- if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
451
- return trimmed;
433
+ // src/lib/url.ts
434
+ var import_url = require("executable-stories-core/utils/url");
435
+
436
+ // src/lib/markdown.ts
437
+ function safeMarkdownHtml(markdown) {
438
+ const raw = import_marked.marked.parse(markdown, { async: false });
439
+ return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<\/?(?:iframe|object|embed|form|base)\b[^>]*>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/(href|src)\s*=\s*"([^"]*)"/gi, (_m, attr, val) => `${attr}="${neutralizeUrl(val)}"`).replace(/(href|src)\s*=\s*'([^']*)'/gi, (_m, attr, val) => `${attr}='${neutralizeUrl(val)}'`);
452
440
  }
453
- function safeImageUrl(value) {
454
- if (!value) return void 0;
455
- const trimmed = value.trim();
456
- if (/^data:image\//i.test(trimmed)) return trimmed;
457
- return safeUrl(trimmed);
441
+ function dedent(text) {
442
+ const lines = text.replace(/\t/g, " ").split("\n");
443
+ const indents = lines.filter((line) => line.trim().length > 0).map((line) => line.length - line.trimStart().length);
444
+ const shortest = indents.length > 0 ? Math.min(...indents) : 0;
445
+ return lines.map((line) => line.slice(shortest)).join("\n").trim();
458
446
  }
459
- function isLocalFsPath(value) {
460
- return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
447
+ function neutralizeUrl(value) {
448
+ return (0, import_url.safeUrl)(value) ?? "#";
461
449
  }
462
450
 
463
451
  // src/components/doc/DocSection.tsx
464
452
  var import_jsx_runtime9 = require("react/jsx-runtime");
465
- function neutralizeUrl(value) {
466
- return safeUrl(value) ?? "#";
467
- }
468
- function safeMarkdownHtml(markdown) {
469
- const raw = import_marked.marked.parse(markdown, { async: false });
470
- return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<\/?(?:iframe|object|embed|form|base)\b[^>]*>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/(href|src)\s*=\s*"([^"]*)"/gi, (_m, attr, val) => `${attr}="${neutralizeUrl(val)}"`).replace(/(href|src)\s*=\s*'([^']*)'/gi, (_m, attr, val) => `${attr}='${neutralizeUrl(val)}'`);
471
- }
472
453
  function DocSection({ entry }) {
473
454
  const renderers = useBuiltinRenderers();
474
455
  if (renderers.section) {
@@ -514,7 +495,7 @@ function DocMermaid({ entry }) {
514
495
  // src/components/doc/DocScreenshot.tsx
515
496
  var import_jsx_runtime11 = require("react/jsx-runtime");
516
497
  function DocScreenshot({ entry }) {
517
- const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
498
+ const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
518
499
  if (!src) {
519
500
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
520
501
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
@@ -539,15 +520,15 @@ function DocScreenshot({ entry }) {
539
520
  // src/components/doc/DocVideo.tsx
540
521
  var import_jsx_runtime12 = require("react/jsx-runtime");
541
522
  function DocVideo({ entry }) {
542
- if (isLocalFsPath(entry.path)) {
523
+ if ((0, import_url.isLocalFsPath)(entry.path)) {
543
524
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
544
525
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
545
526
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
546
527
  entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
547
528
  ] });
548
529
  }
549
- const src = safeUrl(entry.path);
550
- const poster = safeUrl(entry.poster);
530
+ const src = (0, import_url.safeUrl)(entry.path);
531
+ const poster = (0, import_url.safeUrl)(entry.poster);
551
532
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
552
533
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
553
534
  "video",
@@ -568,7 +549,7 @@ var import_jsx_runtime13 = require("react/jsx-runtime");
568
549
  function DocHtml({ entry }) {
569
550
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
570
551
  const title = entry.title ?? "Embedded HTML";
571
- const safeHref = safeUrl(entry.url ?? entry.path);
552
+ const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
572
553
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
573
554
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
574
555
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
@@ -735,7 +716,7 @@ function ReportStepItem({ step }) {
735
716
  }
736
717
  ),
737
718
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
738
- step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
719
+ step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration.formatDuration)(step.durationMs) }) : null
739
720
  ] }),
740
721
  step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
741
722
  "pre",
@@ -751,125 +732,10 @@ function ReportStepItem({ step }) {
751
732
  );
752
733
  }
753
734
 
754
- // ../executable-stories-core/dist/state-diff.js
755
- var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
756
- function deepEqual(a, b) {
757
- if (Object.is(a, b)) return true;
758
- if (Array.isArray(a) && Array.isArray(b)) {
759
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
760
- }
761
- if (isPlainObject(a) && isPlainObject(b)) {
762
- const ak = Object.keys(a);
763
- const bk = Object.keys(b);
764
- return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
765
- }
766
- return false;
767
- }
768
- var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
769
- function walk(path, before, after, out) {
770
- if (Object.is(before, after)) return;
771
- if (Array.isArray(before) && Array.isArray(after)) {
772
- const shared = Math.min(before.length, after.length);
773
- for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
774
- for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
775
- for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
776
- return;
777
- }
778
- if (isPlainObject(before) && isPlainObject(after)) {
779
- for (const key of Object.keys(after)) {
780
- const p = joinKey(path, key);
781
- if (key in before) walk(p, before[key], after[key], out);
782
- else out.push({ path: p, kind: "added", after: after[key] });
783
- }
784
- for (const key of Object.keys(before)) {
785
- if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
786
- }
787
- return;
788
- }
789
- if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
790
- }
791
- function diffStateValues(before, after) {
792
- const out = [];
793
- walk("", before, after, out);
794
- return out;
795
- }
796
- function formatStateValue(value, maxLen = 60) {
797
- const s = value === void 0 ? "undefined" : JSON.stringify(value);
798
- return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
799
- }
800
- function summarizeStateChanges(changes) {
801
- return changes.map((c) => {
802
- const at = c.path === "" ? "(root)" : c.path;
803
- switch (c.kind) {
804
- case "added":
805
- return `+ ${at}: ${formatStateValue(c.after)}`;
806
- case "removed":
807
- return `\u2212 ${at}`;
808
- case "changed":
809
- return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
810
- }
811
- });
812
- }
813
-
814
- // ../executable-stories-core/dist/storyboard.js
815
- function findScreenshot(entries) {
816
- for (const entry of entries) {
817
- if (entry.kind === "screenshot") return entry;
818
- if (entry.children) {
819
- const nested = findScreenshot(entry.children);
820
- if (nested) return nested;
821
- }
822
- }
823
- return void 0;
824
- }
825
- function findStates(entries, out = []) {
826
- for (const entry of entries) {
827
- if (entry.kind === "state") out.push(entry);
828
- if (entry.children) findStates(entry.children, out);
829
- }
830
- return out;
831
- }
832
- var laneOf = (label) => label ?? "";
833
- function extractStoryboardFrames(scenario) {
834
- const frames = [];
835
- const laneOrder = [];
836
- const previous = /* @__PURE__ */ new Map();
837
- for (const step of scenario.steps) {
838
- const shot = findScreenshot(step.docEntries);
839
- const stateDocs = findStates(step.docEntries);
840
- if (!shot && stateDocs.length === 0) continue;
841
- const byLane = /* @__PURE__ */ new Map();
842
- for (const doc of stateDocs) {
843
- const lane = laneOf(doc.label);
844
- if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
845
- byLane.set(lane, doc);
846
- }
847
- const states = [];
848
- for (const lane of laneOrder) {
849
- const doc = byLane.get(lane);
850
- if (!doc) continue;
851
- const card = {
852
- ...doc.label !== void 0 && { label: doc.label },
853
- value: doc.value
854
- };
855
- if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
856
- previous.set(lane, doc.value);
857
- states.push(card);
858
- }
859
- frames.push({
860
- stepId: step.id,
861
- stepIndex: step.index,
862
- keyword: step.keyword,
863
- text: step.text,
864
- status: step.status,
865
- ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
866
- states
867
- });
868
- }
869
- return frames;
870
- }
871
-
872
735
  // src/components/ReportStoryboard.tsx
736
+ var import_storyboard = require("executable-stories-core/storyboard");
737
+ var import_state_diff = require("executable-stories-core/state-diff");
738
+ var import_url6 = require("executable-stories-core/utils/url");
873
739
  var import_jsx_runtime20 = require("react/jsx-runtime");
874
740
  var FRAME_GLYPH = {
875
741
  passed: "\u2713",
@@ -884,7 +750,7 @@ var FRAME_GLYPH_COLOR = {
884
750
  pending: "text-pend"
885
751
  };
886
752
  function ReportStoryboard({ scenario }) {
887
- const frames = extractStoryboardFrames(scenario);
753
+ const frames = (0, import_storyboard.extractStoryboardFrames)(scenario);
888
754
  if (frames.length < 2) return null;
889
755
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
890
756
  /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
@@ -918,7 +784,7 @@ function StoryboardFrameItem({ frame }) {
918
784
  }
919
785
  function FrameScreenshot({ frame, label }) {
920
786
  const path = frame.screenshot?.path ?? "";
921
- const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
787
+ const src = (0, import_url6.isLocalFsPath)(path) ? void 0 : (0, import_url6.safeImageUrl)(path);
922
788
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
923
789
  "a",
924
790
  {
@@ -964,7 +830,7 @@ function StateCardView({ card }) {
964
830
  "rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
965
831
  CHANGE_TONE[change.kind]
966
832
  ),
967
- children: summarizeStateChanges([change])[0]
833
+ children: (0, import_state_diff.summarizeStateChanges)([change])[0]
968
834
  },
969
835
  i
970
836
  )) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
@@ -1024,6 +890,9 @@ function ReportAttachments({ attachments }) {
1024
890
  );
1025
891
  }
1026
892
 
893
+ // src/components/ReportTrace.tsx
894
+ var import_duration2 = require("executable-stories-core/utils/duration");
895
+
1027
896
  // src/lib/trace.ts
1028
897
  var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
1029
898
  var safeStatus = (s) => VALID.has(s) ? s : "unset";
@@ -1076,15 +945,15 @@ function toTraceRows(spans) {
1076
945
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
1077
946
  const rows = [];
1078
947
  const seen = /* @__PURE__ */ new Set();
1079
- const walk2 = (node, depth) => {
948
+ const walk = (node, depth) => {
1080
949
  if (seen.has(node.span.spanId)) return;
1081
950
  seen.add(node.span.spanId);
1082
951
  rows.push({ ...node.span, depth });
1083
- for (const child of node.children) walk2(child, depth + 1);
952
+ for (const child of node.children) walk(child, depth + 1);
1084
953
  };
1085
- for (const root of roots) walk2(root, 0);
954
+ for (const root of roots) walk(root, 0);
1086
955
  for (const node of byId.values()) {
1087
- if (!seen.has(node.span.spanId)) walk2(node, 0);
956
+ if (!seen.has(node.span.spanId)) walk(node, 0);
1088
957
  }
1089
958
  return rows;
1090
959
  }
@@ -1118,7 +987,7 @@ function ReportTrace({ spans }) {
1118
987
  " span",
1119
988
  rows.length === 1 ? "" : "s",
1120
989
  ", ",
1121
- formatDuration(totalMs),
990
+ (0, import_duration2.formatDuration)(totalMs),
1122
991
  ")"
1123
992
  ] }),
1124
993
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
@@ -1141,7 +1010,7 @@ function ReportTrace({ spans }) {
1141
1010
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
1142
1011
  }
1143
1012
  ) }),
1144
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
1013
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: (0, import_duration2.formatDuration)(row.durationMs) })
1145
1014
  ] }, `${row.spanId}-${i}`);
1146
1015
  }) })
1147
1016
  ] });
@@ -1317,130 +1186,14 @@ function useCollapseState(computeDefault) {
1317
1186
 
1318
1187
  // src/interactive/scenario-actions.tsx
1319
1188
  var import_react7 = require("react");
1320
-
1321
- // ../executable-stories-core/dist/utils/scenario-markdown.js
1322
- function cell(value) {
1323
- return value.replaceAll("|", "\\|").replaceAll("\n", " ");
1324
- }
1325
- function stringify(value) {
1326
- if (typeof value === "string") return value;
1327
- try {
1328
- return JSON.stringify(value, null, 2) ?? String(value);
1329
- } catch {
1330
- return String(value);
1331
- }
1332
- }
1333
- function docEntryToMarkdown(entry, stateLanes) {
1334
- const lines = [];
1335
- switch (entry.kind) {
1336
- case "note":
1337
- lines.push(entry.text);
1338
- break;
1339
- case "tag":
1340
- lines.push(entry.names.map((n) => `\`@${n}\``).join(" "));
1341
- break;
1342
- case "kv":
1343
- lines.push(`**${entry.label}:** ${stringify(entry.value)}`);
1344
- break;
1345
- case "code":
1346
- lines.push(`**${entry.label}**`, "", "```" + (entry.lang ?? ""), entry.content, "```");
1347
- break;
1348
- case "table": {
1349
- lines.push(`**${entry.label}**`, "");
1350
- lines.push(`| ${entry.columns.map(cell).join(" | ")} |`);
1351
- lines.push(`| ${entry.columns.map(() => "---").join(" | ")} |`);
1352
- for (const row of entry.rows) lines.push(`| ${row.map(cell).join(" | ")} |`);
1353
- break;
1354
- }
1355
- case "link":
1356
- lines.push(`[${entry.label}](${entry.url})`);
1357
- break;
1358
- case "section":
1359
- lines.push(`### ${entry.title}`, "", entry.markdown);
1360
- break;
1361
- case "mermaid":
1362
- if (entry.title) lines.push(`**${entry.title}**`, "");
1363
- lines.push("```mermaid", entry.code, "```");
1364
- break;
1365
- case "screenshot":
1366
- lines.push(`![${entry.alt ?? "screenshot"}](${entry.path})`);
1367
- break;
1368
- case "video":
1369
- lines.push(`Video: ${entry.path}${entry.caption ? ` \u2014 ${entry.caption}` : ""}`);
1370
- break;
1371
- case "html": {
1372
- const htmlTitle = entry.title ?? "Embedded HTML";
1373
- if (entry.content) {
1374
- lines.push(`**${htmlTitle}**`, "", "```html", entry.content, "```");
1375
- } else if (entry.url) {
1376
- lines.push(`[${htmlTitle}](${entry.url})`);
1377
- } else {
1378
- lines.push(`Embedded HTML: ${entry.path ?? "(inline)"}`);
1379
- }
1380
- break;
1381
- }
1382
- case "custom":
1383
- lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
1384
- break;
1385
- case "state": {
1386
- const lane = entry.label ?? "";
1387
- lines.push(`**${entry.label ?? "State"}**`);
1388
- if (stateLanes?.has(lane)) {
1389
- const summary = summarizeStateChanges(diffStateValues(stateLanes.get(lane), entry.value));
1390
- if (summary.length > 0) lines.push("", ...summary.map((l) => `- ${l}`));
1391
- }
1392
- stateLanes?.set(lane, entry.value);
1393
- lines.push("", "<details>", "<summary>snapshot</summary>", "", "```json", stringify(entry.value ?? null), "```", "", "</details>");
1394
- break;
1395
- }
1396
- }
1397
- for (const child of entry.children ?? []) {
1398
- lines.push("", ...docEntryToMarkdown(child, stateLanes));
1399
- }
1400
- return lines;
1401
- }
1402
- function stepLines(step, ordinal, full, stateLanes) {
1403
- const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
1404
- const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
1405
- if (!full) return lines;
1406
- if (step.errorMessage) {
1407
- lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
1408
- }
1409
- for (const entry of step.docEntries) {
1410
- lines.push("", ...docEntryToMarkdown(entry, stateLanes).map((l) => l === "" ? l : ` ${l}`));
1411
- }
1412
- return lines;
1413
- }
1414
- function scenarioToMarkdown(scenario, options = {}) {
1415
- const { variant = "full", meta = [] } = options;
1416
- const full = variant === "full";
1417
- const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
1418
- if (full && meta.length > 0) lines.push(...meta);
1419
- const stateLanes = /* @__PURE__ */ new Map();
1420
- if (scenario.steps.length > 0) {
1421
- if (full) lines.push("", "## Steps", "");
1422
- scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full, stateLanes)));
1423
- }
1424
- if (scenario.errorMessage) {
1425
- if (full) lines.push("", "## Failure", "");
1426
- else lines.push("");
1427
- lines.push("```", scenario.errorMessage.trim(), "```");
1428
- }
1429
- if (full && scenario.docEntries.length > 0) {
1430
- lines.push("", "## Docs", "");
1431
- for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry, stateLanes), "");
1432
- }
1433
- return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
1434
- }
1435
-
1436
- // src/interactive/scenario-actions.tsx
1189
+ var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
1437
1190
  var ScenarioActionsContext = (0, import_react7.createContext)(null);
1438
1191
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1439
1192
  function useScenarioActions() {
1440
1193
  return (0, import_react7.useContext)(ScenarioActionsContext);
1441
1194
  }
1442
- function scenarioToMarkdown2(scenario) {
1443
- return scenarioToMarkdown(scenario, { variant: "compact" });
1195
+ function scenarioToMarkdown(scenario) {
1196
+ return (0, import_scenario_markdown.scenarioToMarkdown)(scenario, { variant: "compact" });
1444
1197
  }
1445
1198
  function scenarioToPrompt(scenario) {
1446
1199
  const lines = [
@@ -1605,7 +1358,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1605
1358
  ] }),
1606
1359
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1607
1360
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1608
- scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1361
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
1609
1362
  meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1610
1363
  actions ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenu, { children: [
1611
1364
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
@@ -1664,8 +1417,35 @@ function ReportScenarioList({ feature }) {
1664
1417
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportScenario, { scenario }, scenario.id)) });
1665
1418
  }
1666
1419
 
1667
- // src/components/ReportFeature.tsx
1420
+ // src/components/ReportFeatureIntro.tsx
1668
1421
  var import_jsx_runtime28 = require("react/jsx-runtime");
1422
+ function ReportFeatureIntro({ feature }) {
1423
+ const hasNarrative = Boolean(feature.narrative?.trim());
1424
+ const hasGlossary = Boolean(feature.glossary?.length);
1425
+ if (!hasNarrative && !hasGlossary) return null;
1426
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
1427
+ hasNarrative ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1428
+ "div",
1429
+ {
1430
+ className: "es-doc-prose prose prose-sm max-w-none text-muted-foreground",
1431
+ dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
1432
+ }
1433
+ ) : null,
1434
+ hasGlossary ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dt", { className: "font-medium text-foreground", children: entry.term }),
1436
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
1437
+ ] }, entry.term)) }) : null
1438
+ ] });
1439
+ }
1440
+ function featureKindLabel(kind) {
1441
+ if (kind === "ability") return "Ability";
1442
+ if (kind === "business-need") return "Business need";
1443
+ if (kind === "feature") return "Feature";
1444
+ return null;
1445
+ }
1446
+
1447
+ // src/components/ReportFeature.tsx
1448
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1669
1449
  function ReportFeature({ feature }) {
1670
1450
  const titleId = `${feature.id}-title`;
1671
1451
  const bodyId = `${feature.id}-body`;
@@ -1673,10 +1453,11 @@ function ReportFeature({ feature }) {
1673
1453
  const skipped = s.skipped + s.pending;
1674
1454
  const collapse = useCollapse();
1675
1455
  const collapsed = collapse?.isCollapsed(feature.id) ?? false;
1456
+ const kindLabel = featureKindLabel(feature.kind);
1676
1457
  return (
1677
1458
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1678
1459
  // layer of nesting so the scenario cards below read as the primary unit.
1679
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1460
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1680
1461
  "section",
1681
1462
  {
1682
1463
  id: feature.id,
@@ -1684,9 +1465,9 @@ function ReportFeature({ feature }) {
1684
1465
  "aria-labelledby": titleId,
1685
1466
  className: "flex flex-col gap-3",
1686
1467
  children: [
1687
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1688
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "min-w-0", children: [
1689
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1468
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1469
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "min-w-0", children: [
1470
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1690
1471
  "button",
1691
1472
  {
1692
1473
  type: "button",
@@ -1695,31 +1476,34 @@ function ReportFeature({ feature }) {
1695
1476
  "aria-controls": bodyId,
1696
1477
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1697
1478
  children: [
1698
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1699
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate", children: feature.title })
1479
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1480
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate", children: feature.title })
1700
1481
  ]
1701
1482
  }
1702
1483
  ) : feature.title }),
1703
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1484
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
1485
+ kindLabel ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-medium text-foreground", children: kindLabel }) : null,
1486
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate font-mono", children: feature.sourceFile })
1487
+ ] })
1704
1488
  ] }),
1705
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1489
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1706
1490
  "p",
1707
1491
  {
1708
1492
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1709
1493
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1710
1494
  children: [
1711
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { children: [
1712
1496
  s.passed,
1713
1497
  " passed"
1714
1498
  ] }),
1715
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1716
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1499
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1500
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1717
1501
  s.failed,
1718
1502
  " failed"
1719
1503
  ] }),
1720
- skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1721
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1722
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1504
+ skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1505
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1506
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { children: [
1723
1507
  skipped,
1724
1508
  " skipped"
1725
1509
  ] })
@@ -1728,7 +1512,10 @@ function ReportFeature({ feature }) {
1728
1512
  }
1729
1513
  )
1730
1514
  ] }),
1731
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportScenarioList, { feature }) })
1515
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
1516
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportFeatureIntro, { feature }),
1517
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportScenarioList, { feature })
1518
+ ] })
1732
1519
  ]
1733
1520
  }
1734
1521
  )
@@ -1736,17 +1523,17 @@ function ReportFeature({ feature }) {
1736
1523
  }
1737
1524
 
1738
1525
  // src/components/ReportFeatureList.tsx
1739
- var import_jsx_runtime29 = require("react/jsx-runtime");
1526
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1740
1527
  function ReportFeatureList() {
1741
1528
  const report = useReport();
1742
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportFeature, { feature }, feature.id)) });
1529
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportFeature, { feature }, feature.id)) });
1743
1530
  }
1744
1531
 
1745
1532
  // src/components/ui/empty.tsx
1746
1533
  var import_class_variance_authority2 = require("class-variance-authority");
1747
- var import_jsx_runtime30 = require("react/jsx-runtime");
1534
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1748
1535
  function Empty({ className, ...props }) {
1749
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1536
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1750
1537
  "div",
1751
1538
  {
1752
1539
  "data-slot": "empty",
@@ -1759,7 +1546,7 @@ function Empty({ className, ...props }) {
1759
1546
  );
1760
1547
  }
1761
1548
  function EmptyHeader({ className, ...props }) {
1762
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1549
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1763
1550
  "div",
1764
1551
  {
1765
1552
  "data-slot": "empty-header",
@@ -1786,7 +1573,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1786
1573
  }
1787
1574
  );
1788
1575
  function EmptyTitle({ className, ...props }) {
1789
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1576
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1790
1577
  "div",
1791
1578
  {
1792
1579
  "data-slot": "empty-title",
@@ -1797,16 +1584,16 @@ function EmptyTitle({ className, ...props }) {
1797
1584
  }
1798
1585
 
1799
1586
  // src/components/ReportEmpty.tsx
1800
- var import_jsx_runtime31 = require("react/jsx-runtime");
1587
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1801
1588
  function ReportEmpty({ message }) {
1802
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1589
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1803
1590
  }
1804
1591
 
1805
1592
  // src/components/ReportSummary.tsx
1806
- var import_jsx_runtime32 = require("react/jsx-runtime");
1593
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1807
1594
  function ReportSummary({ className }) {
1808
1595
  const report = useReport();
1809
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1596
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1810
1597
  ReportSummaryView,
1811
1598
  {
1812
1599
  summary: report.summary,
@@ -1832,7 +1619,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1832
1619
  if (summary.pending > 0) {
1833
1620
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1834
1621
  }
1835
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1622
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1836
1623
  "dl",
1837
1624
  {
1838
1625
  className: cn(
@@ -1845,17 +1632,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1845
1632
  "aria-label": ariaLabel,
1846
1633
  children: cards.map((c) => {
1847
1634
  const tone = TONE[c.status];
1848
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1635
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1849
1636
  "div",
1850
1637
  {
1851
1638
  "data-status": c.status,
1852
1639
  className: "rounded-lg border bg-card px-4 py-3",
1853
1640
  children: [
1854
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1855
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1642
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1856
1643
  c.label
1857
1644
  ] }),
1858
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1645
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1859
1646
  ]
1860
1647
  },
1861
1648
  c.status
@@ -1865,6 +1652,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1865
1652
  );
1866
1653
  }
1867
1654
 
1655
+ // src/components/ReportMeta.tsx
1656
+ var import_duration4 = require("executable-stories-core/utils/duration");
1657
+
1868
1658
  // src/lib/provenance.ts
1869
1659
  function reportLastRunMs(report) {
1870
1660
  if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
@@ -1915,7 +1705,7 @@ function prUrl(ci) {
1915
1705
  }
1916
1706
 
1917
1707
  // src/components/ReportMeta.tsx
1918
- var import_jsx_runtime33 = require("react/jsx-runtime");
1708
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1919
1709
  function ReportMeta() {
1920
1710
  const report = useReport();
1921
1711
  const items = [];
@@ -1924,7 +1714,7 @@ function ReportMeta() {
1924
1714
  items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
1925
1715
  }
1926
1716
  if (report.durationMs) {
1927
- items.push({ label: "Duration", value: formatDuration(report.durationMs) });
1717
+ items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
1928
1718
  }
1929
1719
  if (report.packageVersion) {
1930
1720
  items.push({ label: "Version", value: report.packageVersion });
@@ -1938,13 +1728,13 @@ function ReportMeta() {
1938
1728
  const short = sha.slice(0, 8);
1939
1729
  items.push({
1940
1730
  label: "Commit",
1941
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1731
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1942
1732
  });
1943
1733
  }
1944
1734
  if (report.ci) {
1945
1735
  items.push({
1946
1736
  label: "CI",
1947
- value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1737
+ value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1948
1738
  ciDisplayName(report.ci.name),
1949
1739
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1950
1740
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1955,7 +1745,7 @@ function ReportMeta() {
1955
1745
  const label = `#${report.ci.prNumber}`;
1956
1746
  items.push({
1957
1747
  label: "PR",
1958
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1748
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1959
1749
  });
1960
1750
  }
1961
1751
  if (items.length === 0) return null;
@@ -1963,50 +1753,50 @@ function ReportMeta() {
1963
1753
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1964
1754
  // collapsed disclosure so it doesn't push the result down. Native <details>
1965
1755
  // so it works with no JS (the SSR/no-hydration report still toggles).
1966
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1967
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1968
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1756
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1757
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1758
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1969
1759
  "Run details"
1970
1760
  ] }),
1971
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1972
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dt", { className: "font-medium", children: item.label }),
1973
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1761
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dt", { className: "font-medium", children: item.label }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1974
1764
  ] }, item.label)) })
1975
1765
  ] })
1976
1766
  );
1977
1767
  }
1978
1768
 
1979
1769
  // src/components/ReportSchemaError.tsx
1980
- var import_jsx_runtime34 = require("react/jsx-runtime");
1770
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1981
1771
  function ReportSchemaError({ error }) {
1982
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1983
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("strong", { children: "Report could not be displayed." }) }),
1984
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: error.message }),
1985
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { children: [
1772
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1773
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("strong", { children: "Report could not be displayed." }) }),
1774
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: error.message }),
1775
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { children: [
1986
1776
  "The report bundle is newer than this version of ",
1987
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("code", { children: "executable-stories-react" }),
1777
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: "executable-stories-react" }),
1988
1778
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1989
1779
  ] }) : null,
1990
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { children: [
1991
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { children: [
1780
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("details", { children: [
1781
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("summary", { children: [
1992
1782
  error.issues.length,
1993
1783
  " validation issue",
1994
1784
  error.issues.length === 1 ? "" : "s"
1995
1785
  ] }),
1996
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1786
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1997
1787
  ] }) : null
1998
1788
  ] });
1999
1789
  }
2000
1790
 
2001
1791
  // src/components/ReportShell.tsx
2002
- var import_jsx_runtime35 = require("react/jsx-runtime");
1792
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2003
1793
  function ReportErrorShell({
2004
1794
  error,
2005
1795
  className,
2006
1796
  title,
2007
1797
  dataTheme
2008
1798
  }) {
2009
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportSchemaError, { error }) });
1799
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportSchemaError, { error }) });
2010
1800
  }
2011
1801
 
2012
1802
  // src/interactive/ReportInteractive.tsx
@@ -2015,12 +1805,12 @@ var import_lucide_react5 = require("lucide-react");
2015
1805
  // src/components/ui/switch.tsx
2016
1806
  var React4 = require("react");
2017
1807
  var import_switch = require("@base-ui/react/switch");
2018
- var import_jsx_runtime36 = require("react/jsx-runtime");
1808
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2019
1809
  function Switch({
2020
1810
  className,
2021
1811
  ...props
2022
1812
  }) {
2023
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1813
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2024
1814
  import_switch.Switch.Root,
2025
1815
  {
2026
1816
  "data-slot": "switch",
@@ -2029,7 +1819,7 @@ function Switch({
2029
1819
  className
2030
1820
  ),
2031
1821
  ...props,
2032
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1822
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2033
1823
  import_switch.Switch.Thumb,
2034
1824
  {
2035
1825
  "data-slot": "switch-thumb",
@@ -2045,7 +1835,7 @@ var React5 = __toESM(require("react"), 1);
2045
1835
  var import_toggle_group = require("@base-ui/react/toggle-group");
2046
1836
  var import_toggle = require("@base-ui/react/toggle");
2047
1837
  var import_class_variance_authority3 = require("class-variance-authority");
2048
- var import_jsx_runtime37 = require("react/jsx-runtime");
1838
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2049
1839
  var toggleItemVariants = (0, import_class_variance_authority3.cva)(
2050
1840
  "inline-flex items-center justify-center gap-1.5 rounded-md font-medium whitespace-nowrap text-muted-foreground transition-[color,background-color,box-shadow] outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-background data-[pressed]:text-foreground data-[pressed]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
2051
1841
  {
@@ -2067,13 +1857,13 @@ function ToggleGroup({
2067
1857
  children,
2068
1858
  ...props
2069
1859
  }) {
2070
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1860
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2071
1861
  import_toggle_group.ToggleGroup,
2072
1862
  {
2073
1863
  "data-slot": "toggle-group",
2074
1864
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
2075
1865
  ...props,
2076
- children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1866
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
2077
1867
  }
2078
1868
  );
2079
1869
  }
@@ -2084,7 +1874,7 @@ function ToggleGroupItem({
2084
1874
  ...props
2085
1875
  }) {
2086
1876
  const context = React5.useContext(ToggleGroupContext);
2087
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1877
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2088
1878
  import_toggle.Toggle,
2089
1879
  {
2090
1880
  "data-slot": "toggle-group-item",
@@ -2101,9 +1891,9 @@ var import_lucide_react3 = require("lucide-react");
2101
1891
 
2102
1892
  // src/components/ui/input.tsx
2103
1893
  var React6 = require("react");
2104
- var import_jsx_runtime38 = require("react/jsx-runtime");
1894
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2105
1895
  function Input({ className, type, ...props }) {
2106
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1896
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2107
1897
  "input",
2108
1898
  {
2109
1899
  type,
@@ -2120,7 +1910,7 @@ function Input({ className, type, ...props }) {
2120
1910
  }
2121
1911
 
2122
1912
  // src/interactive/ReportSearch.tsx
2123
- var import_jsx_runtime39 = require("react/jsx-runtime");
1913
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2124
1914
  var ReportSearch = (0, import_react8.forwardRef)(
2125
1915
  function ReportSearch2(props, ref) {
2126
1916
  const {
@@ -2142,17 +1932,17 @@ var ReportSearch = (0, import_react8.forwardRef)(
2142
1932
  e.preventDefault();
2143
1933
  }
2144
1934
  }
2145
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
2146
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
2147
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "relative w-full sm:w-72", children: [
2148
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1935
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1936
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1937
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "relative w-full sm:w-72", children: [
1938
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2149
1939
  import_lucide_react3.Search,
2150
1940
  {
2151
1941
  "aria-hidden": true,
2152
1942
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
2153
1943
  }
2154
1944
  ),
2155
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1945
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2156
1946
  Input,
2157
1947
  {
2158
1948
  ref,
@@ -2169,7 +1959,7 @@ var ReportSearch = (0, import_react8.forwardRef)(
2169
1959
  }
2170
1960
  )
2171
1961
  ] }),
2172
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
1962
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2173
1963
  "span",
2174
1964
  {
2175
1965
  className: "text-xs tabular-nums text-muted-foreground",
@@ -2258,7 +2048,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
2258
2048
  }
2259
2049
 
2260
2050
  // src/interactive/ReportFailureBanner.tsx
2261
- var import_jsx_runtime40 = require("react/jsx-runtime");
2051
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2262
2052
  function ReportFailureBanner({ failures }) {
2263
2053
  const first = failures[0];
2264
2054
  const jumpToFirst = (0, import_react9.useCallback)(() => {
@@ -2266,7 +2056,7 @@ function ReportFailureBanner({ failures }) {
2266
2056
  scrollToScenarioId(first.scenarioId);
2267
2057
  }, [first]);
2268
2058
  if (failures.length === 0) return null;
2269
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2059
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2270
2060
  "div",
2271
2061
  {
2272
2062
  className: "es-failure-banner",
@@ -2274,13 +2064,13 @@ function ReportFailureBanner({ failures }) {
2274
2064
  "aria-live": "polite",
2275
2065
  "aria-label": "Failure summary",
2276
2066
  children: [
2277
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "es-failure-banner-text", children: [
2278
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: failures.length }),
2067
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "es-failure-banner-text", children: [
2068
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: failures.length }),
2279
2069
  " ",
2280
2070
  "failure",
2281
2071
  failures.length === 1 ? "" : "s"
2282
2072
  ] }),
2283
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2073
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2284
2074
  "button",
2285
2075
  {
2286
2076
  type: "button",
@@ -2297,7 +2087,7 @@ function ReportFailureBanner({ failures }) {
2297
2087
 
2298
2088
  // src/interactive/ReportFreshness.tsx
2299
2089
  var import_react10 = require("react");
2300
- var import_jsx_runtime41 = require("react/jsx-runtime");
2090
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2301
2091
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2302
2092
  const [now, setNow] = (0, import_react10.useState)(nowMs);
2303
2093
  (0, import_react10.useEffect)(() => {
@@ -2305,7 +2095,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2305
2095
  }, [nowMs]);
2306
2096
  if (lastRunMs === void 0 || now === void 0) return null;
2307
2097
  const age = formatRelativeAge(lastRunMs, now);
2308
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2098
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2309
2099
  "a",
2310
2100
  {
2311
2101
  href: ciUrl,
@@ -2316,9 +2106,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2316
2106
  }
2317
2107
  ) : null;
2318
2108
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
2319
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2320
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2321
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("strong", { children: [
2109
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2110
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2111
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("strong", { children: [
2322
2112
  "Last verified ",
2323
2113
  age,
2324
2114
  "."
@@ -2328,10 +2118,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2328
2118
  runLink
2329
2119
  ] });
2330
2120
  }
2331
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2121
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2332
2122
  "Verified ",
2333
2123
  age,
2334
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2124
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2335
2125
  " \xB7 ",
2336
2126
  runLink
2337
2127
  ] }) : null
@@ -2339,15 +2129,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2339
2129
  }
2340
2130
 
2341
2131
  // src/interactive/ReportLastRunDelta.tsx
2342
- var import_jsx_runtime42 = require("react/jsx-runtime");
2132
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2343
2133
  var MAX_LINKS = 5;
2344
2134
  function ScenarioLinks({ scenarios, tone }) {
2345
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2346
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2135
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
2136
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
2347
2137
  i > 0 ? ", " : "",
2348
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2138
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2349
2139
  ] }, s.id)),
2350
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2140
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
2351
2141
  " and ",
2352
2142
  scenarios.length - MAX_LINKS,
2353
2143
  " more"
@@ -2364,23 +2154,23 @@ function ReportLastRunDelta({
2364
2154
  if (!delta) return null;
2365
2155
  const { newlyFailing, newlyPassing, added } = delta;
2366
2156
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
2367
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2157
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2368
2158
  }
2369
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2370
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2371
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2 text-fail", children: [
2159
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2160
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2161
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2 text-fail", children: [
2372
2162
  newlyFailing.length,
2373
2163
  " newly failing (",
2374
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2164
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2375
2165
  ")"
2376
2166
  ] }) : null,
2377
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2 text-pass", children: [
2167
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2 text-pass", children: [
2378
2168
  newlyPassing.length,
2379
2169
  " fixed (",
2380
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2381
2171
  ")"
2382
2172
  ] }) : null,
2383
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2", children: [
2173
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2", children: [
2384
2174
  added.length,
2385
2175
  " new"
2386
2176
  ] }) : null
@@ -2444,7 +2234,7 @@ function Button({
2444
2234
  }
2445
2235
 
2446
2236
  // src/interactive/ReportShortcutsHelp.tsx
2447
- var import_jsx_runtime43 = require("react/jsx-runtime");
2237
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2448
2238
  var SHORTCUTS = [
2449
2239
  { keys: "/", description: "Focus search" },
2450
2240
  { keys: "f", description: "Jump to next failure" },
@@ -2456,9 +2246,9 @@ var SHORTCUTS = [
2456
2246
  ];
2457
2247
  function Keys({ keys }) {
2458
2248
  const parts = keys.split(" ");
2459
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2460
- i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2461
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2249
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2250
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2251
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2462
2252
  ] }, k)) });
2463
2253
  }
2464
2254
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2469,7 +2259,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2469
2259
  if (open && !dialog.open) dialog.showModal();
2470
2260
  else if (!open && dialog.open) dialog.close();
2471
2261
  }, [open]);
2472
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2262
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2473
2263
  "dialog",
2474
2264
  {
2475
2265
  ref: dialogRef,
@@ -2477,12 +2267,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2477
2267
  "aria-label": "Keyboard shortcuts",
2478
2268
  onClose,
2479
2269
  children: [
2480
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2481
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2482
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Keys, { keys: s.keys }) }),
2483
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2270
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2271
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2272
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Keys, { keys: s.keys }) }),
2273
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2484
2274
  ] }, s.keys)) }),
2485
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2275
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2486
2276
  ]
2487
2277
  }
2488
2278
  );
@@ -2685,7 +2475,7 @@ function failuresFirst(report) {
2685
2475
 
2686
2476
  // src/interactive/ReportFilters.tsx
2687
2477
  var import_react14 = require("react");
2688
- var import_jsx_runtime44 = require("react/jsx-runtime");
2478
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2689
2479
  var TAG_LIMIT = 12;
2690
2480
  function ReportFilters({
2691
2481
  statuses,
@@ -2699,10 +2489,10 @@ function ReportFilters({
2699
2489
  if (statuses.length === 0 && tags.length === 0) return null;
2700
2490
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2701
2491
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2702
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-col gap-2", children: [
2703
- statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2492
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-col gap-2", children: [
2493
+ statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2704
2494
  const active = status === opt.key;
2705
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2495
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2706
2496
  "button",
2707
2497
  {
2708
2498
  type: "button",
@@ -2714,23 +2504,23 @@ function ReportFilters({
2714
2504
  ),
2715
2505
  children: [
2716
2506
  opt.label,
2717
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2507
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2718
2508
  ]
2719
2509
  },
2720
2510
  opt.key
2721
2511
  );
2722
2512
  }) }) : null,
2723
- tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2513
+ tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2724
2514
  visibleTags.map((tag) => {
2725
2515
  const active = activeTags.includes(tag);
2726
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2516
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2727
2517
  "button",
2728
2518
  {
2729
2519
  type: "button",
2730
2520
  "aria-pressed": active,
2731
2521
  onClick: () => onToggleTag(tag),
2732
2522
  className: "cursor-pointer",
2733
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2523
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2734
2524
  Badge,
2735
2525
  {
2736
2526
  variant: "tag",
@@ -2743,7 +2533,7 @@ function ReportFilters({
2743
2533
  }
2744
2534
  ) }, tag);
2745
2535
  }),
2746
- tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2536
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2747
2537
  "button",
2748
2538
  {
2749
2539
  type: "button",
@@ -2759,7 +2549,7 @@ function ReportFilters({
2759
2549
 
2760
2550
  // src/interactive/ReportToc.tsx
2761
2551
  var import_react15 = require("react");
2762
- var import_jsx_runtime45 = require("react/jsx-runtime");
2552
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2763
2553
  var DOT = {
2764
2554
  passed: "bg-pass",
2765
2555
  failed: "bg-fail",
@@ -2788,11 +2578,11 @@ function TocContent({ onNavigate }) {
2788
2578
  return () => observer.disconnect();
2789
2579
  }, [scenarioIds]);
2790
2580
  if (report.features.length === 0) return null;
2791
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { children: [
2792
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2793
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2581
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("li", { children: [
2582
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2583
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2794
2584
  const active = activeId === scenario.id;
2795
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2585
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2796
2586
  "a",
2797
2587
  {
2798
2588
  href: `#${scenario.id}`,
@@ -2807,14 +2597,14 @@ function TocContent({ onNavigate }) {
2807
2597
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2808
2598
  ),
2809
2599
  children: [
2810
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2600
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2811
2601
  "span",
2812
2602
  {
2813
2603
  "aria-hidden": true,
2814
2604
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2815
2605
  }
2816
2606
  ),
2817
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "break-words", children: scenario.title })
2607
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "break-words", children: scenario.title })
2818
2608
  ]
2819
2609
  }
2820
2610
  ) }, scenario.id);
@@ -2824,14 +2614,14 @@ function TocContent({ onNavigate }) {
2824
2614
  function ReportToc() {
2825
2615
  const report = useReport();
2826
2616
  if (report.features.length === 0) return null;
2827
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2617
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2828
2618
  "nav",
2829
2619
  {
2830
2620
  "aria-label": "Table of contents",
2831
2621
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2832
2622
  children: [
2833
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2834
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, {})
2623
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2624
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(TocContent, {})
2835
2625
  ]
2836
2626
  }
2837
2627
  );
@@ -2841,31 +2631,31 @@ function ReportToc() {
2841
2631
  var import_react16 = require("react");
2842
2632
  var import_dialog = require("@base-ui/react/dialog");
2843
2633
  var import_lucide_react4 = require("lucide-react");
2844
- var import_jsx_runtime46 = require("react/jsx-runtime");
2634
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2845
2635
  function ReportTocDrawer() {
2846
2636
  const report = useReport();
2847
2637
  const [open, setOpen] = (0, import_react16.useState)(false);
2848
2638
  if (report.features.length === 0) return null;
2849
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2850
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
2851
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2639
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2640
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
2641
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2852
2642
  " Contents"
2853
2643
  ] }),
2854
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Portal, { children: [
2855
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2856
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
2857
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center justify-between", children: [
2858
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2859
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2644
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_dialog.Dialog.Portal, { children: [
2645
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2646
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
2647
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center justify-between", children: [
2648
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2649
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2860
2650
  import_dialog.Dialog.Close,
2861
2651
  {
2862
2652
  "aria-label": "Close contents",
2863
2653
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2864
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2654
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2865
2655
  }
2866
2656
  )
2867
2657
  ] }),
2868
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2658
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2869
2659
  ] })
2870
2660
  ] })
2871
2661
  ] });
@@ -2948,14 +2738,14 @@ function useUrlState() {
2948
2738
  }
2949
2739
 
2950
2740
  // src/interactive/ReportInteractive.tsx
2951
- var import_jsx_runtime47 = require("react/jsx-runtime");
2741
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2952
2742
  function ReportInteractive(props) {
2953
2743
  const { report, className, title, dataTheme } = props;
2954
2744
  const result = unwrapReport(report);
2955
2745
  if (!result.ok) {
2956
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2746
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2957
2747
  }
2958
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportInteractiveView, { ...props, report: result.data });
2748
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportInteractiveView, { ...props, report: result.data });
2959
2749
  }
2960
2750
  var THEME_OPTIONS = [
2961
2751
  { value: "light", label: "Light", Icon: import_lucide_react5.Sun },
@@ -2963,7 +2753,7 @@ var THEME_OPTIONS = [
2963
2753
  { value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
2964
2754
  ];
2965
2755
  function ThemeSegment({ pref, onPref }) {
2966
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2756
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2967
2757
  ToggleGroup,
2968
2758
  {
2969
2759
  size: "sm",
@@ -2973,7 +2763,7 @@ function ThemeSegment({ pref, onPref }) {
2973
2763
  if (next) onPref(next);
2974
2764
  },
2975
2765
  "aria-label": "Color theme",
2976
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Icon, {}) }, value))
2766
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Icon, {}) }, value))
2977
2767
  }
2978
2768
  );
2979
2769
  }
@@ -3050,7 +2840,7 @@ function ReportInteractiveView({
3050
2840
  const scenarioActions = (0, import_react19.useMemo)(
3051
2841
  () => ({
3052
2842
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
3053
- copyMarkdown: (s) => copy(scenarioToMarkdown2(s), "Markdown copied"),
2843
+ copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
3054
2844
  copyPrompt: (s) => copy(scenarioToPrompt(s), "Prompt copied")
3055
2845
  }),
3056
2846
  [copy]
@@ -3104,13 +2894,13 @@ function ReportInteractiveView({
3104
2894
  const totalScenarios = report.summary.total;
3105
2895
  const matchedScenarios = filtered.summary.total;
3106
2896
  const showToc = !hideToc && totalScenarios > 3;
3107
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2897
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3108
2898
  ReportRoot,
3109
2899
  {
3110
2900
  report: filtered,
3111
2901
  customRenderers,
3112
2902
  renderers,
3113
- children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2903
+ children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3114
2904
  "main",
3115
2905
  {
3116
2906
  className: cn("es-report", "es-report-interactive", className),
@@ -3119,8 +2909,8 @@ function ReportInteractiveView({
3119
2909
  "data-theme": dataTheme,
3120
2910
  "data-detail-level": detail,
3121
2911
  children: [
3122
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("header", { className: "es-report-header", children: [
3123
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2912
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("header", { className: "es-report-header", children: [
2913
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3124
2914
  "div",
3125
2915
  {
3126
2916
  "data-es-navbar": true,
@@ -3129,9 +2919,9 @@ function ReportInteractiveView({
3129
2919
  hideHeader ? "justify-end" : "justify-between"
3130
2920
  ),
3131
2921
  children: [
3132
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
3133
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
3134
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2922
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2923
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
2924
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3135
2925
  ReportSearch,
3136
2926
  {
3137
2927
  ref: searchRef,
@@ -3141,16 +2931,16 @@ function ReportInteractiveView({
3141
2931
  totalCount: totalScenarios
3142
2932
  }
3143
2933
  ),
3144
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2934
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
3145
2935
  ] })
3146
2936
  ]
3147
2937
  }
3148
2938
  ),
3149
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
3150
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportSummary, {}),
3151
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportMeta, {})
2939
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
2940
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportSummary, {}),
2941
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportMeta, {})
3152
2942
  ] }),
3153
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2943
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3154
2944
  ReportFreshness,
3155
2945
  {
3156
2946
  lastRunMs: reportLastRunMs(report),
@@ -3158,8 +2948,8 @@ function ReportInteractiveView({
3158
2948
  staleAfterDays
3159
2949
  }
3160
2950
  ),
3161
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
3162
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2951
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2952
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3163
2953
  ReportFilters,
3164
2954
  {
3165
2955
  statuses: statusOptions,
@@ -3170,14 +2960,14 @@ function ReportInteractiveView({
3170
2960
  onToggleTag: toggleTag
3171
2961
  }
3172
2962
  ),
3173
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
3174
- showToc ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportTocDrawer, {}) : null,
3175
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
3176
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
3177
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2963
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2964
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportTocDrawer, {}) : null,
2965
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
2966
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2967
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
3178
2968
  ] }),
3179
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
3180
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2969
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center gap-2", children: [
2970
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3181
2971
  Switch,
3182
2972
  {
3183
2973
  id: docsId,
@@ -3185,16 +2975,16 @@ function ReportInteractiveView({
3185
2975
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
3186
2976
  }
3187
2977
  ),
3188
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2978
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
3189
2979
  ] })
3190
2980
  ] })
3191
2981
  ] }),
3192
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFailureBanner, { failures }),
3193
- hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex gap-6", children: [
3194
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportToc, {}),
3195
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFeatureList, {}) })
3196
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
3197
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2982
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportFailureBanner, { failures }),
2983
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex gap-6", children: [
2984
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportToc, {}),
2985
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportFeatureList, {}) })
2986
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2987
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3198
2988
  "button",
3199
2989
  {
3200
2990
  type: "button",
@@ -3205,8 +2995,8 @@ function ReportInteractiveView({
3205
2995
  children: "?"
3206
2996
  }
3207
2997
  ),
3208
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
3209
- toast ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2998
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2999
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3210
3000
  "div",
3211
3001
  {
3212
3002
  role: "status",
@@ -3230,7 +3020,7 @@ var import_react21 = require("react");
3230
3020
 
3231
3021
  // src/components/doc/MermaidView.tsx
3232
3022
  var import_react20 = require("react");
3233
- var import_jsx_runtime48 = require("react/jsx-runtime");
3023
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3234
3024
  function prefersDark(el) {
3235
3025
  const themed = el?.closest("[data-theme]");
3236
3026
  if (themed?.dataset.theme === "dark") return true;
@@ -3284,9 +3074,9 @@ function MermaidView({ entry, load }) {
3284
3074
  };
3285
3075
  }, [entry.code, load]);
3286
3076
  if (svg) {
3287
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("figure", { ref, className: "my-3", children: [
3288
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3289
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3077
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("figure", { ref, className: "my-3", children: [
3078
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3079
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3290
3080
  "div",
3291
3081
  {
3292
3082
  role: "img",
@@ -3297,8 +3087,8 @@ function MermaidView({ entry, load }) {
3297
3087
  )
3298
3088
  ] });
3299
3089
  }
3300
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
3301
- error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3090
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
3091
+ error ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
3302
3092
  "p",
3303
3093
  {
3304
3094
  role: "alert",
@@ -3310,12 +3100,12 @@ function MermaidView({ entry, load }) {
3310
3100
  ]
3311
3101
  }
3312
3102
  ) : null,
3313
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(MermaidSource, { entry, ref })
3103
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(MermaidSource, { entry, ref })
3314
3104
  ] });
3315
3105
  }
3316
3106
 
3317
3107
  // src/interactive/island-renderers.tsx
3318
- var import_jsx_runtime49 = require("react/jsx-runtime");
3108
+ var import_jsx_runtime50 = require("react/jsx-runtime");
3319
3109
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3320
3110
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3321
3111
  function cachedCdnModule(url) {
@@ -3349,13 +3139,13 @@ function CdnHighlightedCode({ entry }) {
3349
3139
  cancelled = true;
3350
3140
  };
3351
3141
  }, [entry.content, entry.lang]);
3352
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3142
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3353
3143
  "code",
3354
3144
  {
3355
3145
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
3356
3146
  dangerouslySetInnerHTML: { __html: html }
3357
3147
  }
3358
- ) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3148
+ ) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3359
3149
  }
3360
3150
  function buildIslandRenderers(opts) {
3361
3151
  const renderers = {};
@@ -3369,7 +3159,7 @@ function buildIslandRenderers(opts) {
3369
3159
  }
3370
3160
 
3371
3161
  // src/interactive/ReportInteractiveIsland.tsx
3372
- var import_jsx_runtime50 = require("react/jsx-runtime");
3162
+ var import_jsx_runtime51 = require("react/jsx-runtime");
3373
3163
  function ReportInteractiveIsland({
3374
3164
  report,
3375
3165
  title,
@@ -3383,7 +3173,7 @@ function ReportInteractiveIsland({
3383
3173
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
3384
3174
  [syntaxHighlighting, mermaid]
3385
3175
  );
3386
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3176
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3387
3177
  ReportInteractive,
3388
3178
  {
3389
3179
  report,