executable-stories-react 0.11.0 → 0.12.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.
package/dist/index.cjs CHANGED
@@ -84,6 +84,7 @@ __export(src_exports, {
84
84
  parseStoryReport: () => parseStoryReport,
85
85
  prUrl: () => prUrl,
86
86
  reportLastRunMs: () => reportLastRunMs,
87
+ scenarioLastRunMs: () => scenarioLastRunMs,
87
88
  storyReportSchema: () => storyReportSchema,
88
89
  useBuiltinRenderers: () => useBuiltinRenderers,
89
90
  useCustomRenderers: () => useCustomRenderers,
@@ -380,23 +381,45 @@ function useReport() {
380
381
  // src/components/ReportScenario.tsx
381
382
  var import_duration3 = require("executable-stories-core/utils/duration");
382
383
 
384
+ // src/lib/step-params.tsx
385
+ var import_jsx_runtime3 = require("react/jsx-runtime");
386
+ var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
387
+ function highlightStepParams(text) {
388
+ const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
389
+ if (matches.length === 0) return text;
390
+ const nodes = [];
391
+ let lastIndex = 0;
392
+ matches.forEach((match, i) => {
393
+ const start = match.index;
394
+ const end = start + match[0].length;
395
+ if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
396
+ nodes.push(
397
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
398
+ );
399
+ lastIndex = end;
400
+ });
401
+ if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
402
+ return nodes;
403
+ }
404
+
383
405
  // src/components/ReportSteps.tsx
406
+ var import_assertive_steps = require("executable-stories-core/utils/assertive-steps");
384
407
  var import_duration = require("executable-stories-core/utils/duration");
385
408
 
386
409
  // src/components/doc/DocNote.tsx
387
- var import_jsx_runtime3 = require("react/jsx-runtime");
410
+ var import_jsx_runtime4 = require("react/jsx-runtime");
388
411
  function DocNote({ entry }) {
389
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
412
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
390
413
  }
391
414
 
392
415
  // src/components/doc/DocTag.tsx
393
- var import_jsx_runtime4 = require("react/jsx-runtime");
416
+ var import_jsx_runtime5 = require("react/jsx-runtime");
394
417
  function DocTag({ entry }) {
395
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
418
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
396
419
  }
397
420
 
398
421
  // src/components/doc/DocKv.tsx
399
- var import_jsx_runtime5 = require("react/jsx-runtime");
422
+ var import_jsx_runtime6 = require("react/jsx-runtime");
400
423
  function formatValue(value) {
401
424
  if (value === null) return "null";
402
425
  if (typeof value === "string") return value;
@@ -408,9 +431,9 @@ function formatValue(value) {
408
431
  }
409
432
  }
410
433
  function DocKv({ entry }) {
411
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
412
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
413
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
434
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
435
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
436
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
414
437
  ] });
415
438
  }
416
439
 
@@ -428,37 +451,37 @@ function useBuiltinRenderers() {
428
451
  }
429
452
 
430
453
  // src/components/doc/DocCode.tsx
431
- var import_jsx_runtime6 = require("react/jsx-runtime");
454
+ var import_jsx_runtime7 = require("react/jsx-runtime");
432
455
  function CodeFigure({ label, children }) {
433
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("figure", { className: "my-3", children: [
434
- label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
435
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
456
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("figure", { className: "my-3", children: [
457
+ label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
458
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
436
459
  ] });
437
460
  }
438
461
  function DocCode({ entry }) {
439
462
  const renderers = useBuiltinRenderers();
440
463
  if (renderers.code) {
441
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderers.code(entry) });
464
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: renderers.code(entry) });
442
465
  }
443
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
466
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
444
467
  }
445
468
 
446
469
  // src/components/doc/DocTable.tsx
447
- var import_jsx_runtime7 = require("react/jsx-runtime");
470
+ var import_jsx_runtime8 = require("react/jsx-runtime");
448
471
  function DocTable({ entry }) {
449
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("figure", { className: "my-3", children: [
450
- entry.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
451
- /* @__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: [
452
- /* @__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)) }) }),
453
- /* @__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)) })
472
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("figure", { className: "my-3", children: [
473
+ entry.label ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
474
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
475
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
454
477
  ] }) })
455
478
  ] });
456
479
  }
457
480
 
458
481
  // src/components/doc/DocLink.tsx
459
- var import_jsx_runtime8 = require("react/jsx-runtime");
482
+ var import_jsx_runtime9 = require("react/jsx-runtime");
460
483
  function DocLink({ entry }) {
461
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
484
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
462
485
  "a",
463
486
  {
464
487
  className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
@@ -492,16 +515,16 @@ function neutralizeUrl(value) {
492
515
  }
493
516
 
494
517
  // src/components/doc/DocSection.tsx
495
- var import_jsx_runtime9 = require("react/jsx-runtime");
518
+ var import_jsx_runtime10 = require("react/jsx-runtime");
496
519
  function DocSection({ entry }) {
497
520
  const renderers = useBuiltinRenderers();
498
521
  if (renderers.section) {
499
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderers.section(entry) });
522
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: renderers.section(entry) });
500
523
  }
501
524
  const html = safeMarkdownHtml(entry.markdown);
502
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
503
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
504
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
525
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
526
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
527
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
505
528
  "div",
506
529
  {
507
530
  className: "es-doc-prose prose prose-sm max-w-none",
@@ -512,11 +535,11 @@ function DocSection({ entry }) {
512
535
  }
513
536
 
514
537
  // src/components/doc/DocMermaid.tsx
515
- var import_jsx_runtime10 = require("react/jsx-runtime");
538
+ var import_jsx_runtime11 = require("react/jsx-runtime");
516
539
  function MermaidSource({ entry, ref }) {
517
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
518
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
519
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
540
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
541
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
542
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
520
543
  "pre",
521
544
  {
522
545
  "data-mermaid": true,
@@ -530,24 +553,24 @@ function MermaidSource({ entry, ref }) {
530
553
  function DocMermaid({ entry }) {
531
554
  const renderers = useBuiltinRenderers();
532
555
  if (renderers.mermaid) {
533
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: renderers.mermaid(entry) });
556
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: renderers.mermaid(entry) });
534
557
  }
535
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MermaidSource, { entry });
558
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MermaidSource, { entry });
536
559
  }
537
560
 
538
561
  // src/components/doc/DocScreenshot.tsx
539
- var import_jsx_runtime11 = require("react/jsx-runtime");
562
+ var import_jsx_runtime12 = require("react/jsx-runtime");
540
563
  function DocScreenshot({ entry }) {
541
564
  const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
542
565
  if (!src) {
543
- 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: [
544
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
545
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
546
- entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
566
+ 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: [
567
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
568
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
569
+ entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
547
570
  ] });
548
571
  }
549
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
550
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
572
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
573
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
551
574
  "img",
552
575
  {
553
576
  src,
@@ -556,24 +579,24 @@ function DocScreenshot({ entry }) {
556
579
  className: "max-w-full rounded-md border border-border"
557
580
  }
558
581
  ),
559
- entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
582
+ entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
560
583
  ] });
561
584
  }
562
585
 
563
586
  // src/components/doc/DocVideo.tsx
564
- var import_jsx_runtime12 = require("react/jsx-runtime");
587
+ var import_jsx_runtime13 = require("react/jsx-runtime");
565
588
  function DocVideo({ entry }) {
566
589
  if ((0, import_url.isLocalFsPath)(entry.path)) {
567
- 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: [
568
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
569
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
570
- entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
590
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
591
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
592
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
593
+ entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
571
594
  ] });
572
595
  }
573
596
  const src = (0, import_url.safeUrl)(entry.path);
574
597
  const poster = (0, import_url.safeUrl)(entry.poster);
575
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
576
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
598
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3", children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
577
600
  "video",
578
601
  {
579
602
  controls: true,
@@ -583,21 +606,21 @@ function DocVideo({ entry }) {
583
606
  className: "max-w-full rounded-md border border-border"
584
607
  }
585
608
  ),
586
- entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
609
+ entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
587
610
  ] });
588
611
  }
589
612
 
590
613
  // src/components/doc/DocHtml.tsx
591
- var import_jsx_runtime13 = require("react/jsx-runtime");
614
+ var import_jsx_runtime14 = require("react/jsx-runtime");
592
615
  function DocHtml({ entry }) {
593
616
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
594
617
  const title = entry.title ?? "Embedded HTML";
595
618
  const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
596
619
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
597
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
598
- /* @__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: [
599
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: entry.title ?? "HTML" }),
600
- safeHref ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
620
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
621
+ /* @__PURE__ */ (0, import_jsx_runtime14.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: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: entry.title ?? "HTML" }),
623
+ safeHref ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
601
624
  "a",
602
625
  {
603
626
  href: safeHref,
@@ -610,7 +633,7 @@ function DocHtml({ entry }) {
610
633
  }
611
634
  ) : null
612
635
  ] }),
613
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
636
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
614
637
  "iframe",
615
638
  {
616
639
  ...frameProps,
@@ -625,7 +648,7 @@ function DocHtml({ entry }) {
625
648
  }
626
649
 
627
650
  // src/components/doc/DocState.tsx
628
- var import_jsx_runtime14 = require("react/jsx-runtime");
651
+ var import_jsx_runtime15 = require("react/jsx-runtime");
629
652
  function DocState({ entry }) {
630
653
  let json;
631
654
  try {
@@ -633,20 +656,20 @@ function DocState({ entry }) {
633
656
  } catch {
634
657
  json = String(entry.value);
635
658
  }
636
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
659
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
637
660
  }
638
661
 
639
662
  // src/components/doc/DocCustom.tsx
640
- var import_jsx_runtime15 = require("react/jsx-runtime");
663
+ var import_jsx_runtime16 = require("react/jsx-runtime");
641
664
  function DocCustom({ entry }) {
642
665
  const renderers = useCustomRenderers();
643
666
  const renderer = renderers[entry.type];
644
667
  if (renderer) {
645
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: renderer(entry) });
668
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: renderer(entry) });
646
669
  }
647
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
648
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
649
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
670
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
650
673
  ] });
651
674
  }
652
675
  function safeStringify2(value) {
@@ -658,64 +681,43 @@ function safeStringify2(value) {
658
681
  }
659
682
 
660
683
  // src/components/doc/DocEntry.tsx
661
- var import_jsx_runtime16 = require("react/jsx-runtime");
684
+ var import_jsx_runtime17 = require("react/jsx-runtime");
662
685
  function DocEntry({ entry }) {
663
686
  switch (entry.kind) {
664
687
  case "note":
665
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocNote, { entry });
688
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocNote, { entry });
666
689
  case "tag":
667
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocTag, { entry });
690
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocTag, { entry });
668
691
  case "kv":
669
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocKv, { entry });
692
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocKv, { entry });
670
693
  case "code":
671
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocCode, { entry });
694
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocCode, { entry });
672
695
  case "table":
673
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocTable, { entry });
696
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocTable, { entry });
674
697
  case "link":
675
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocLink, { entry });
698
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocLink, { entry });
676
699
  case "section":
677
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocSection, { entry });
700
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocSection, { entry });
678
701
  case "mermaid":
679
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocMermaid, { entry });
702
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocMermaid, { entry });
680
703
  case "screenshot":
681
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocScreenshot, { entry });
704
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocScreenshot, { entry });
682
705
  case "video":
683
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocVideo, { entry });
706
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocVideo, { entry });
684
707
  case "html":
685
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocHtml, { entry });
708
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocHtml, { entry });
686
709
  case "state":
687
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocState, { entry });
710
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocState, { entry });
688
711
  case "custom":
689
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocCustom, { entry });
712
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocCustom, { entry });
690
713
  }
691
714
  }
692
715
 
693
716
  // src/components/ReportDocEntries.tsx
694
- var import_jsx_runtime17 = require("react/jsx-runtime");
717
+ var import_jsx_runtime18 = require("react/jsx-runtime");
695
718
  function ReportDocEntries({ entries }) {
696
719
  if (entries.length === 0) return null;
697
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocEntry, { entry }, i)) });
698
- }
699
-
700
- // src/lib/step-params.tsx
701
- var import_jsx_runtime18 = require("react/jsx-runtime");
702
- var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
703
- function highlightStepParams(text) {
704
- const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
705
- if (matches.length === 0) return text;
706
- const nodes = [];
707
- let lastIndex = 0;
708
- matches.forEach((match, i) => {
709
- const start = match.index;
710
- const end = start + match[0].length;
711
- if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
712
- nodes.push(
713
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
714
- );
715
- lastIndex = end;
716
- });
717
- if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
718
- return nodes;
720
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_jsx_runtime18.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DocEntry, { entry }, i)) });
719
721
  }
720
722
 
721
723
  // src/components/ReportSteps.tsx
@@ -734,9 +736,22 @@ var STEP_GLYPH_COLOR = {
734
736
  };
735
737
  function ReportSteps({ scenario }) {
736
738
  if (scenario.steps.length === 0) return null;
737
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ReportStepItem, { step }, step.id)) });
739
+ const unasserted = new Set(
740
+ (0, import_assertive_steps.assertionState)(scenario.steps) === "unasserted" ? (0, import_assertive_steps.assertiveSteps)(scenario.steps).map((step) => step.id) : []
741
+ );
742
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
743
+ ReportStepItem,
744
+ {
745
+ step,
746
+ unasserted: unasserted.has(step.id)
747
+ },
748
+ step.id
749
+ )) });
738
750
  }
739
- function ReportStepItem({ step }) {
751
+ function ReportStepItem({
752
+ step,
753
+ unasserted = false
754
+ }) {
740
755
  const kw = step.keyword.toLowerCase();
741
756
  const continuation = kw === "and" || kw === "but";
742
757
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
@@ -744,10 +759,23 @@ function ReportStepItem({ step }) {
744
759
  {
745
760
  id: step.id,
746
761
  "data-status": step.status,
747
- className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
762
+ className: cn(
763
+ "py-1.5 text-[0.8125rem] leading-normal",
764
+ continuation && "pl-5"
765
+ ),
748
766
  children: [
749
767
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-baseline gap-2", children: [
750
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
769
+ "span",
770
+ {
771
+ "aria-hidden": true,
772
+ className: cn(
773
+ "w-4 shrink-0 text-center text-xs",
774
+ STEP_GLYPH_COLOR[step.status]
775
+ ),
776
+ children: STEP_GLYPH[step.status] ?? "\u25CB"
777
+ }
778
+ ),
751
779
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
752
780
  "span",
753
781
  {
@@ -759,6 +787,7 @@ function ReportStepItem({ step }) {
759
787
  }
760
788
  ),
761
789
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
790
+ unasserted ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 rounded-full border border-pend-border bg-pend-bg px-2 py-0.5 text-[0.6875rem] font-medium text-pend", children: "No assertion" }) : null,
762
791
  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
763
792
  ] }),
764
793
  step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
@@ -1154,28 +1183,111 @@ function ScenarioRunHistory({ scenarioId }) {
1154
1183
  ] });
1155
1184
  }
1156
1185
 
1157
- // src/interactive/collapse-context.ts
1186
+ // src/components/ScenarioStaleness.tsx
1187
+ var import_react7 = require("react");
1188
+
1189
+ // src/lib/provenance.ts
1190
+ function reportLastRunMs(report) {
1191
+ if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
1192
+ if (report.startedAtMs && report.startedAtMs > 0) return report.startedAtMs;
1193
+ return void 0;
1194
+ }
1195
+ function scenarioLastRunMs(scenario, report) {
1196
+ if (scenario.lastRunAtMs && scenario.lastRunAtMs > 0) return scenario.lastRunAtMs;
1197
+ return reportLastRunMs(report);
1198
+ }
1199
+ function formatRelativeAge(thenMs, nowMs) {
1200
+ const deltaMs = Math.max(0, nowMs - thenMs);
1201
+ const minutes = Math.floor(deltaMs / 6e4);
1202
+ if (minutes < 1) return "just now";
1203
+ if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
1204
+ const hours = Math.floor(minutes / 60);
1205
+ if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
1206
+ const days = Math.floor(hours / 24);
1207
+ return `${days} day${days === 1 ? "" : "s"} ago`;
1208
+ }
1209
+ function isReportStale(lastRunMs, staleAfterDays, nowMs) {
1210
+ if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
1211
+ return nowMs - lastRunMs >= staleAfterDays * 864e5;
1212
+ }
1213
+ var CI_DISPLAY_NAMES = {
1214
+ github: "GitHub Actions",
1215
+ gitlab: "GitLab CI",
1216
+ circleci: "CircleCI",
1217
+ jenkins: "Jenkins",
1218
+ azure: "Azure DevOps",
1219
+ buildkite: "Buildkite",
1220
+ travis: "Travis CI",
1221
+ ci: "CI"
1222
+ };
1223
+ function ciDisplayName(name) {
1224
+ return CI_DISPLAY_NAMES[name] ?? name;
1225
+ }
1226
+ function commitUrl(ci, sha) {
1227
+ const commitSha = ci?.commitSha ?? sha;
1228
+ if (!ci?.url || !commitSha) return void 0;
1229
+ const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1230
+ if (github) return `${github[1]}/commit/${commitSha}`;
1231
+ const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
1232
+ if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
1233
+ return void 0;
1234
+ }
1235
+ function prUrl(ci) {
1236
+ if (!ci?.url || !ci.prNumber) return void 0;
1237
+ const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1238
+ if (github) return `${github[1]}/pull/${ci.prNumber}`;
1239
+ return void 0;
1240
+ }
1241
+
1242
+ // src/interactive/scenario-freshness-context.tsx
1158
1243
  var import_react6 = require("react");
1159
- var CollapseContext = (0, import_react6.createContext)(null);
1244
+ var ScenarioFreshnessContext = (0, import_react6.createContext)(null);
1245
+ var ScenarioFreshnessProvider = ScenarioFreshnessContext.Provider;
1246
+ function useScenarioFreshness() {
1247
+ return (0, import_react6.useContext)(ScenarioFreshnessContext);
1248
+ }
1249
+
1250
+ // src/components/ScenarioStaleness.tsx
1251
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1252
+ function ScenarioStaleness({ scenario }) {
1253
+ const freshness = useScenarioFreshness();
1254
+ const fixedNow = freshness?.nowMs;
1255
+ const [now, setNow] = (0, import_react7.useState)(fixedNow);
1256
+ (0, import_react7.useEffect)(() => {
1257
+ if (fixedNow === void 0) setNow(Date.now());
1258
+ }, [fixedNow]);
1259
+ if (!freshness || now === void 0) return null;
1260
+ const lastRunAtMs = scenario.lastRunAtMs;
1261
+ if (!lastRunAtMs || lastRunAtMs <= 0) return null;
1262
+ if (!isReportStale(lastRunAtMs, freshness.staleAfterDays, now)) return null;
1263
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Badge, { variant: "outline", "aria-label": "Scenario freshness", children: [
1264
+ "Last ran ",
1265
+ formatRelativeAge(lastRunAtMs, now)
1266
+ ] });
1267
+ }
1268
+
1269
+ // src/interactive/collapse-context.ts
1270
+ var import_react8 = require("react");
1271
+ var CollapseContext = (0, import_react8.createContext)(null);
1160
1272
  var CollapseProvider = CollapseContext.Provider;
1161
1273
  function useCollapse() {
1162
- return (0, import_react6.useContext)(CollapseContext);
1274
+ return (0, import_react8.useContext)(CollapseContext);
1163
1275
  }
1164
1276
 
1165
1277
  // src/interactive/scenario-actions.tsx
1166
- var import_react7 = require("react");
1278
+ var import_react9 = require("react");
1167
1279
  var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
1168
- var ScenarioActionsContext = (0, import_react7.createContext)(null);
1280
+ var ScenarioActionsContext = (0, import_react9.createContext)(null);
1169
1281
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1170
1282
  function useScenarioActions() {
1171
- return (0, import_react7.useContext)(ScenarioActionsContext);
1283
+ return (0, import_react9.useContext)(ScenarioActionsContext);
1172
1284
  }
1173
1285
 
1174
1286
  // src/components/ui/card.tsx
1175
1287
  var React2 = require("react");
1176
- var import_jsx_runtime24 = require("react/jsx-runtime");
1288
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1177
1289
  function Card({ className, ...props }) {
1178
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1290
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1179
1291
  "div",
1180
1292
  {
1181
1293
  "data-slot": "card",
@@ -1188,7 +1300,7 @@ function Card({ className, ...props }) {
1188
1300
  );
1189
1301
  }
1190
1302
  function CardHeader({ className, ...props }) {
1191
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1303
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1192
1304
  "div",
1193
1305
  {
1194
1306
  "data-slot": "card-header",
@@ -1201,7 +1313,7 @@ function CardHeader({ className, ...props }) {
1201
1313
  );
1202
1314
  }
1203
1315
  function CardContent({ className, ...props }) {
1204
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1316
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1205
1317
  "div",
1206
1318
  {
1207
1319
  "data-slot": "card-content",
@@ -1215,13 +1327,13 @@ function CardContent({ className, ...props }) {
1215
1327
  var React3 = require("react");
1216
1328
  var import_menu = require("@base-ui/react/menu");
1217
1329
  var import_lucide_react = require("lucide-react");
1218
- var import_jsx_runtime25 = require("react/jsx-runtime");
1330
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1219
1331
  var itemClasses = "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground";
1220
1332
  function DropdownMenu(props) {
1221
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1333
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1222
1334
  }
1223
1335
  function DropdownMenuTrigger(props) {
1224
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1336
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1225
1337
  }
1226
1338
  function DropdownMenuContent({
1227
1339
  className,
@@ -1229,14 +1341,14 @@ function DropdownMenuContent({
1229
1341
  sideOffset = 4,
1230
1342
  ...props
1231
1343
  }) {
1232
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1344
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1233
1345
  import_menu.Menu.Positioner,
1234
1346
  {
1235
1347
  "data-slot": "dropdown-menu-positioner",
1236
1348
  className: "z-50",
1237
1349
  align,
1238
1350
  sideOffset,
1239
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1351
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1240
1352
  import_menu.Menu.Popup,
1241
1353
  {
1242
1354
  "data-slot": "dropdown-menu-content",
@@ -1254,7 +1366,7 @@ function DropdownMenuItem({
1254
1366
  className,
1255
1367
  ...props
1256
1368
  }) {
1257
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1369
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1258
1370
  import_menu.Menu.Item,
1259
1371
  {
1260
1372
  "data-slot": "dropdown-menu-item",
@@ -1266,7 +1378,7 @@ function DropdownMenuItem({
1266
1378
 
1267
1379
  // src/components/ReportScenario.tsx
1268
1380
  var import_lucide_react2 = require("lucide-react");
1269
- var import_jsx_runtime26 = require("react/jsx-runtime");
1381
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1270
1382
  var STATUS_META = {
1271
1383
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1272
1384
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1282,7 +1394,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1282
1394
  const collapsible = collapse !== null && !hideTitle;
1283
1395
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1284
1396
  const actions = useScenarioActions();
1285
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1397
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1286
1398
  Card,
1287
1399
  {
1288
1400
  id: scenario.id,
@@ -1290,10 +1402,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1290
1402
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1291
1403
  className: "gap-0 py-0",
1292
1404
  children: [
1293
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1294
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1295
- hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1296
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1405
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1406
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1407
+ hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1408
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1297
1409
  "button",
1298
1410
  {
1299
1411
  type: "button",
@@ -1302,49 +1414,50 @@ function ReportScenario({ scenario, hideTitle = false }) {
1302
1414
  "aria-controls": bodyId,
1303
1415
  "aria-label": `Toggle ${scenario.title}`,
1304
1416
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1305
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1417
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1306
1418
  }
1307
1419
  ) : null,
1308
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1309
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: scenario.title })
1420
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: scenario.title })
1310
1422
  ] }),
1311
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1312
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1313
- 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,
1314
- 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}` }),
1315
- actions ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenu, { children: [
1316
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1423
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1424
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScenarioStaleness, { scenario }),
1426
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
1427
+ meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1428
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenu, { children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1317
1430
  DropdownMenuTrigger,
1318
1431
  {
1319
1432
  "aria-label": `Actions for ${scenario.title}`,
1320
1433
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1321
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1434
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1322
1435
  }
1323
1436
  ),
1324
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuContent, { align: "end", children: [
1325
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1326
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.LinkIcon, {}),
1437
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuContent, { align: "end", children: [
1438
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1439
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.LinkIcon, {}),
1327
1440
  " Copy link"
1328
1441
  ] }),
1329
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1330
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.FileTextIcon, {}),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.FileTextIcon, {}),
1331
1444
  " Copy as Markdown"
1332
1445
  ] }),
1333
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1334
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.SparklesIcon, {}),
1446
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1447
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.SparklesIcon, {}),
1335
1448
  " Explain with AI"
1336
1449
  ] })
1337
1450
  ] })
1338
1451
  ] }) : null
1339
1452
  ] })
1340
1453
  ] }),
1341
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1342
- scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1343
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1454
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1455
+ scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1456
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1344
1457
  ] }) : null
1345
1458
  ] }),
1346
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1347
- scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1459
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1460
+ scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1348
1461
  "pre",
1349
1462
  {
1350
1463
  role: "alert",
@@ -1352,11 +1465,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1352
1465
  children: scenario.errorMessage
1353
1466
  }
1354
1467
  ) : null,
1355
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportStoryboard, { scenario }),
1356
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportSteps, { scenario }),
1357
- scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1358
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1359
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportTrace, { spans: scenario.otelSpans })
1468
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportStoryboard, { scenario }),
1469
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportSteps, { scenario }),
1470
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1471
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1472
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportTrace, { spans: scenario.otelSpans })
1360
1473
  ] })
1361
1474
  ]
1362
1475
  }
@@ -1364,28 +1477,28 @@ function ReportScenario({ scenario, hideTitle = false }) {
1364
1477
  }
1365
1478
 
1366
1479
  // src/components/ReportScenarioList.tsx
1367
- var import_jsx_runtime27 = require("react/jsx-runtime");
1480
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1368
1481
  function ReportScenarioList({ feature }) {
1369
- 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)) });
1482
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportScenario, { scenario }, scenario.id)) });
1370
1483
  }
1371
1484
 
1372
1485
  // src/components/ReportFeatureIntro.tsx
1373
- var import_jsx_runtime28 = require("react/jsx-runtime");
1486
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1374
1487
  function ReportFeatureIntro({ feature }) {
1375
1488
  const hasNarrative = Boolean(feature.narrative?.trim());
1376
1489
  const hasGlossary = Boolean(feature.glossary?.length);
1377
1490
  if (!hasNarrative && !hasGlossary) return null;
1378
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
1379
- hasNarrative ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1491
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
1492
+ hasNarrative ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1380
1493
  "div",
1381
1494
  {
1382
1495
  className: "es-doc-prose prose prose-sm max-w-none text-muted-foreground",
1383
1496
  dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
1384
1497
  }
1385
1498
  ) : null,
1386
- 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: [
1387
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dt", { className: "font-medium text-foreground", children: entry.term }),
1388
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
1499
+ hasGlossary ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dt", { className: "font-medium text-foreground", children: entry.term }),
1501
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
1389
1502
  ] }, entry.term)) }) : null
1390
1503
  ] });
1391
1504
  }
@@ -1397,7 +1510,7 @@ function featureKindLabel(kind) {
1397
1510
  }
1398
1511
 
1399
1512
  // src/components/ReportFeature.tsx
1400
- var import_jsx_runtime29 = require("react/jsx-runtime");
1513
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1401
1514
  function ReportFeature({ feature }) {
1402
1515
  const titleId = `${feature.id}-title`;
1403
1516
  const bodyId = `${feature.id}-body`;
@@ -1409,7 +1522,7 @@ function ReportFeature({ feature }) {
1409
1522
  return (
1410
1523
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1411
1524
  // layer of nesting so the scenario cards below read as the primary unit.
1412
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1525
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1413
1526
  "section",
1414
1527
  {
1415
1528
  id: feature.id,
@@ -1417,9 +1530,9 @@ function ReportFeature({ feature }) {
1417
1530
  "aria-labelledby": titleId,
1418
1531
  className: "flex flex-col gap-3",
1419
1532
  children: [
1420
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1421
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "min-w-0", children: [
1422
- /* @__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)(
1533
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1534
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "min-w-0", children: [
1535
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1423
1536
  "button",
1424
1537
  {
1425
1538
  type: "button",
@@ -1428,34 +1541,34 @@ function ReportFeature({ feature }) {
1428
1541
  "aria-controls": bodyId,
1429
1542
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1430
1543
  children: [
1431
- /* @__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" }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate", children: feature.title })
1544
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1545
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate", children: feature.title })
1433
1546
  ]
1434
1547
  }
1435
1548
  ) : feature.title }),
1436
- /* @__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: [
1437
- 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,
1438
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "truncate font-mono", children: feature.sourceFile })
1549
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
1550
+ kindLabel ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-medium text-foreground", children: kindLabel }) : null,
1551
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate font-mono", children: feature.sourceFile })
1439
1552
  ] })
1440
1553
  ] }),
1441
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1554
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1442
1555
  "p",
1443
1556
  {
1444
1557
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1445
1558
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1446
1559
  children: [
1447
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
1448
1561
  s.passed,
1449
1562
  " passed"
1450
1563
  ] }),
1451
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1452
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1564
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1565
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1453
1566
  s.failed,
1454
1567
  " failed"
1455
1568
  ] }),
1456
- skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1457
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1458
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { children: [
1569
+ skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1570
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1571
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
1459
1572
  skipped,
1460
1573
  " skipped"
1461
1574
  ] })
@@ -1464,9 +1577,9 @@ function ReportFeature({ feature }) {
1464
1577
  }
1465
1578
  )
1466
1579
  ] }),
1467
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
1468
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportFeatureIntro, { feature }),
1469
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportScenarioList, { feature })
1580
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
1581
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportFeatureIntro, { feature }),
1582
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportScenarioList, { feature })
1470
1583
  ] })
1471
1584
  ]
1472
1585
  }
@@ -1475,17 +1588,17 @@ function ReportFeature({ feature }) {
1475
1588
  }
1476
1589
 
1477
1590
  // src/components/ReportFeatureList.tsx
1478
- var import_jsx_runtime30 = require("react/jsx-runtime");
1591
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1479
1592
  function ReportFeatureList() {
1480
1593
  const report = useReport();
1481
- 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)) });
1594
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportFeature, { feature }, feature.id)) });
1482
1595
  }
1483
1596
 
1484
1597
  // src/components/ui/empty.tsx
1485
1598
  var import_class_variance_authority2 = require("class-variance-authority");
1486
- var import_jsx_runtime31 = require("react/jsx-runtime");
1599
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1487
1600
  function Empty({ className, ...props }) {
1488
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1601
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1489
1602
  "div",
1490
1603
  {
1491
1604
  "data-slot": "empty",
@@ -1498,7 +1611,7 @@ function Empty({ className, ...props }) {
1498
1611
  );
1499
1612
  }
1500
1613
  function EmptyHeader({ className, ...props }) {
1501
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1614
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1502
1615
  "div",
1503
1616
  {
1504
1617
  "data-slot": "empty-header",
@@ -1525,7 +1638,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1525
1638
  }
1526
1639
  );
1527
1640
  function EmptyTitle({ className, ...props }) {
1528
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1641
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1529
1642
  "div",
1530
1643
  {
1531
1644
  "data-slot": "empty-title",
@@ -1536,16 +1649,16 @@ function EmptyTitle({ className, ...props }) {
1536
1649
  }
1537
1650
 
1538
1651
  // src/components/ReportEmpty.tsx
1539
- var import_jsx_runtime32 = require("react/jsx-runtime");
1652
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1540
1653
  function ReportEmpty({ message }) {
1541
- 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." }) }) });
1654
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1542
1655
  }
1543
1656
 
1544
1657
  // src/components/ReportSummary.tsx
1545
- var import_jsx_runtime33 = require("react/jsx-runtime");
1658
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1546
1659
  function ReportSummary({ className }) {
1547
1660
  const report = useReport();
1548
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1661
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1549
1662
  ReportSummaryView,
1550
1663
  {
1551
1664
  summary: report.summary,
@@ -1571,7 +1684,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1571
1684
  if (summary.pending > 0) {
1572
1685
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1573
1686
  }
1574
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1687
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1575
1688
  "dl",
1576
1689
  {
1577
1690
  className: cn(
@@ -1584,17 +1697,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1584
1697
  "aria-label": ariaLabel,
1585
1698
  children: cards.map((c) => {
1586
1699
  const tone = TONE[c.status];
1587
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1700
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1588
1701
  "div",
1589
1702
  {
1590
1703
  "data-status": c.status,
1591
1704
  className: "rounded-lg border bg-card px-4 py-3",
1592
1705
  children: [
1593
- /* @__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: [
1594
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1707
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1595
1708
  c.label
1596
1709
  ] }),
1597
- /* @__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 })
1710
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1598
1711
  ]
1599
1712
  },
1600
1713
  c.status
@@ -1606,58 +1719,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1606
1719
 
1607
1720
  // src/components/ReportMeta.tsx
1608
1721
  var import_duration4 = require("executable-stories-core/utils/duration");
1609
-
1610
- // src/lib/provenance.ts
1611
- function reportLastRunMs(report) {
1612
- if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
1613
- if (report.startedAtMs && report.startedAtMs > 0) return report.startedAtMs;
1614
- return void 0;
1615
- }
1616
- function formatRelativeAge(thenMs, nowMs) {
1617
- const deltaMs = Math.max(0, nowMs - thenMs);
1618
- const minutes = Math.floor(deltaMs / 6e4);
1619
- if (minutes < 1) return "just now";
1620
- if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
1621
- const hours = Math.floor(minutes / 60);
1622
- if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
1623
- const days = Math.floor(hours / 24);
1624
- return `${days} day${days === 1 ? "" : "s"} ago`;
1625
- }
1626
- function isReportStale(lastRunMs, staleAfterDays, nowMs) {
1627
- if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
1628
- return nowMs - lastRunMs >= staleAfterDays * 864e5;
1629
- }
1630
- var CI_DISPLAY_NAMES = {
1631
- github: "GitHub Actions",
1632
- gitlab: "GitLab CI",
1633
- circleci: "CircleCI",
1634
- jenkins: "Jenkins",
1635
- azure: "Azure DevOps",
1636
- buildkite: "Buildkite",
1637
- travis: "Travis CI",
1638
- ci: "CI"
1639
- };
1640
- function ciDisplayName(name) {
1641
- return CI_DISPLAY_NAMES[name] ?? name;
1642
- }
1643
- function commitUrl(ci, sha) {
1644
- const commitSha = ci?.commitSha ?? sha;
1645
- if (!ci?.url || !commitSha) return void 0;
1646
- const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1647
- if (github) return `${github[1]}/commit/${commitSha}`;
1648
- const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
1649
- if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
1650
- return void 0;
1651
- }
1652
- function prUrl(ci) {
1653
- if (!ci?.url || !ci.prNumber) return void 0;
1654
- const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
1655
- if (github) return `${github[1]}/pull/${ci.prNumber}`;
1656
- return void 0;
1657
- }
1658
-
1659
- // src/components/ReportMeta.tsx
1660
- var import_jsx_runtime34 = require("react/jsx-runtime");
1722
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1661
1723
  function ReportMeta() {
1662
1724
  const report = useReport();
1663
1725
  const items = [];
@@ -1680,13 +1742,13 @@ function ReportMeta() {
1680
1742
  const short = sha.slice(0, 8);
1681
1743
  items.push({
1682
1744
  label: "Commit",
1683
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1745
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1684
1746
  });
1685
1747
  }
1686
1748
  if (report.ci) {
1687
1749
  items.push({
1688
1750
  label: "CI",
1689
- value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1751
+ value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1690
1752
  ciDisplayName(report.ci.name),
1691
1753
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1692
1754
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1697,7 +1759,7 @@ function ReportMeta() {
1697
1759
  const label = `#${report.ci.prNumber}`;
1698
1760
  items.push({
1699
1761
  label: "PR",
1700
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1762
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1701
1763
  });
1702
1764
  }
1703
1765
  if (items.length === 0) return null;
@@ -1705,48 +1767,48 @@ function ReportMeta() {
1705
1767
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1706
1768
  // collapsed disclosure so it doesn't push the result down. Native <details>
1707
1769
  // so it works with no JS (the SSR/no-hydration report still toggles).
1708
- /* @__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: [
1709
- /* @__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: [
1710
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1770
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1771
+ /* @__PURE__ */ (0, import_jsx_runtime35.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: [
1772
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1711
1773
  "Run details"
1712
1774
  ] }),
1713
- /* @__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: [
1714
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dt", { className: "font-medium", children: item.label }),
1715
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1775
+ /* @__PURE__ */ (0, import_jsx_runtime35.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_runtime35.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1776
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("dt", { className: "font-medium", children: item.label }),
1777
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1716
1778
  ] }, item.label)) })
1717
1779
  ] })
1718
1780
  );
1719
1781
  }
1720
1782
 
1721
1783
  // src/components/ReportSchemaError.tsx
1722
- var import_jsx_runtime35 = require("react/jsx-runtime");
1784
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1723
1785
  function ReportSchemaError({ error }) {
1724
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1725
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("strong", { children: "Report could not be displayed." }) }),
1726
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: error.message }),
1727
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { children: [
1786
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1787
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("strong", { children: "Report could not be displayed." }) }),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { children: error.message }),
1789
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { children: [
1728
1790
  "The report bundle is newer than this version of ",
1729
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: "executable-stories-react" }),
1791
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("code", { children: "executable-stories-react" }),
1730
1792
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1731
1793
  ] }) : null,
1732
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("details", { children: [
1733
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("summary", { children: [
1794
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("details", { children: [
1795
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("summary", { children: [
1734
1796
  error.issues.length,
1735
1797
  " validation issue",
1736
1798
  error.issues.length === 1 ? "" : "s"
1737
1799
  ] }),
1738
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1800
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1739
1801
  ] }) : null
1740
1802
  ] });
1741
1803
  }
1742
1804
 
1743
1805
  // src/components/ReportShell.tsx
1744
- var import_jsx_runtime36 = require("react/jsx-runtime");
1806
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1745
1807
  function ReportTitleBlock({ title }) {
1746
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
1747
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h1", { children: title ?? "Story Report" }),
1748
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportSummary, {}),
1749
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportMeta, {})
1808
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
1809
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("h1", { children: title ?? "Story Report" }),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportSummary, {}),
1811
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportMeta, {})
1750
1812
  ] });
1751
1813
  }
1752
1814
  function ReportErrorShell({
@@ -1755,28 +1817,28 @@ function ReportErrorShell({
1755
1817
  title,
1756
1818
  dataTheme
1757
1819
  }) {
1758
- 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 }) });
1820
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportSchemaError, { error }) });
1759
1821
  }
1760
1822
 
1761
1823
  // src/components/Report.tsx
1762
- var import_jsx_runtime37 = require("react/jsx-runtime");
1824
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1763
1825
  function Report(props) {
1764
1826
  const { report, customRenderers, renderers, className, title, dataTheme } = props;
1765
1827
  const result = unwrapReport(report);
1766
1828
  if (!result.ok) {
1767
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
1829
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
1768
1830
  }
1769
1831
  const data = result.data;
1770
1832
  const hasContent = data.features.length > 0;
1771
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1772
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportTitleBlock, { title }) }),
1773
- hasContent ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReportEmpty, {})
1833
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1834
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReportTitleBlock, { title }) }),
1835
+ hasContent ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ReportEmpty, {})
1774
1836
  ] }) });
1775
1837
  }
1776
1838
 
1777
1839
  // src/components/doc/MermaidView.tsx
1778
- var import_react8 = require("react");
1779
- var import_jsx_runtime38 = require("react/jsx-runtime");
1840
+ var import_react10 = require("react");
1841
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1780
1842
  function prefersDark(el) {
1781
1843
  const themed = el?.closest("[data-theme]");
1782
1844
  if (themed?.dataset.theme === "dark") return true;
@@ -1784,10 +1846,10 @@ function prefersDark(el) {
1784
1846
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
1785
1847
  }
1786
1848
  function MermaidView({ entry, load }) {
1787
- const ref = (0, import_react8.useRef)(null);
1788
- const [svg, setSvg] = (0, import_react8.useState)(null);
1789
- const [error, setError] = (0, import_react8.useState)(null);
1790
- (0, import_react8.useEffect)(() => {
1849
+ const ref = (0, import_react10.useRef)(null);
1850
+ const [svg, setSvg] = (0, import_react10.useState)(null);
1851
+ const [error, setError] = (0, import_react10.useState)(null);
1852
+ (0, import_react10.useEffect)(() => {
1791
1853
  let cancelled = false;
1792
1854
  const draw = async () => {
1793
1855
  let mermaid;
@@ -1830,9 +1892,9 @@ function MermaidView({ entry, load }) {
1830
1892
  };
1831
1893
  }, [entry.code, load]);
1832
1894
  if (svg) {
1833
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("figure", { ref, className: "my-3", children: [
1834
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
1835
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1895
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("figure", { ref, className: "my-3", children: [
1896
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
1897
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1836
1898
  "div",
1837
1899
  {
1838
1900
  role: "img",
@@ -1843,8 +1905,8 @@ function MermaidView({ entry, load }) {
1843
1905
  )
1844
1906
  ] });
1845
1907
  }
1846
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
1847
- error ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
1908
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
1909
+ error ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
1848
1910
  "p",
1849
1911
  {
1850
1912
  role: "alert",
@@ -1856,34 +1918,34 @@ function MermaidView({ entry, load }) {
1856
1918
  ]
1857
1919
  }
1858
1920
  ) : null,
1859
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MermaidSource, { entry, ref })
1921
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MermaidSource, { entry, ref })
1860
1922
  ] });
1861
1923
  }
1862
1924
 
1863
1925
  // src/components/doc/MermaidDiagram.tsx
1864
- var import_jsx_runtime39 = require("react/jsx-runtime");
1926
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1865
1927
  var mermaidLoader;
1866
1928
  function loadMermaid() {
1867
1929
  mermaidLoader ??= import("mermaid").then((m) => m.default);
1868
1930
  return mermaidLoader;
1869
1931
  }
1870
1932
  function MermaidDiagram({ entry, load = loadMermaid }) {
1871
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MermaidView, { entry, load });
1933
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MermaidView, { entry, load });
1872
1934
  }
1873
1935
 
1874
1936
  // src/components/ScenarioExplorer.tsx
1875
- var import_react9 = require("react");
1937
+ var import_react11 = require("react");
1876
1938
 
1877
1939
  // src/components/ui/command.tsx
1878
1940
  var React4 = require("react");
1879
1941
  var import_cmdk = require("cmdk");
1880
1942
  var import_lucide_react3 = require("lucide-react");
1881
- var import_jsx_runtime40 = require("react/jsx-runtime");
1943
+ var import_jsx_runtime41 = require("react/jsx-runtime");
1882
1944
  function Command({
1883
1945
  className,
1884
1946
  ...props
1885
1947
  }) {
1886
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
1948
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1887
1949
  import_cmdk.Command,
1888
1950
  {
1889
1951
  "data-slot": "command",
@@ -1899,14 +1961,14 @@ function CommandInput({
1899
1961
  className,
1900
1962
  ...props
1901
1963
  }) {
1902
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
1964
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
1903
1965
  "div",
1904
1966
  {
1905
1967
  "data-slot": "command-input-wrapper",
1906
1968
  className: "flex h-9 items-center gap-2 border-b px-3",
1907
1969
  children: [
1908
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react3.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
1909
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
1970
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react3.SearchIcon, { className: "size-4 shrink-0 opacity-50" }),
1971
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1910
1972
  import_cmdk.Command.Input,
1911
1973
  {
1912
1974
  "data-slot": "command-input",
@@ -1925,7 +1987,7 @@ function CommandList({
1925
1987
  className,
1926
1988
  ...props
1927
1989
  }) {
1928
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
1990
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1929
1991
  import_cmdk.Command.List,
1930
1992
  {
1931
1993
  "data-slot": "command-list",
@@ -1940,7 +2002,7 @@ function CommandList({
1940
2002
  function CommandEmpty({
1941
2003
  ...props
1942
2004
  }) {
1943
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2005
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1944
2006
  import_cmdk.Command.Empty,
1945
2007
  {
1946
2008
  "data-slot": "command-empty",
@@ -1953,7 +2015,7 @@ function CommandGroup({
1953
2015
  className,
1954
2016
  ...props
1955
2017
  }) {
1956
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2018
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1957
2019
  import_cmdk.Command.Group,
1958
2020
  {
1959
2021
  "data-slot": "command-group",
@@ -1969,7 +2031,7 @@ function CommandItem({
1969
2031
  className,
1970
2032
  ...props
1971
2033
  }) {
1972
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2034
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1973
2035
  import_cmdk.Command.Item,
1974
2036
  {
1975
2037
  "data-slot": "command-item",
@@ -1983,7 +2045,7 @@ function CommandItem({
1983
2045
  }
1984
2046
 
1985
2047
  // src/components/ScenarioExplorer.tsx
1986
- var import_jsx_runtime41 = require("react/jsx-runtime");
2048
+ var import_jsx_runtime42 = require("react/jsx-runtime");
1987
2049
  var STATUS_FILTERS = [
1988
2050
  { value: "all", label: "All" },
1989
2051
  { value: "passed", label: "Passed" },
@@ -1992,24 +2054,24 @@ var STATUS_FILTERS = [
1992
2054
  { value: "pending", label: "Pending" }
1993
2055
  ];
1994
2056
  function ScenarioExplorer({ scenarios, summary }) {
1995
- const [status, setStatus] = (0, import_react9.useState)("all");
2057
+ const [status, setStatus] = (0, import_react11.useState)("all");
1996
2058
  const visible = status === "all" ? scenarios : scenarios.filter((s) => s.status === status);
1997
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "font-sans text-foreground", children: [
1998
- summary ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "mb-3 text-sm text-muted-foreground", children: [
2059
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "font-sans text-foreground", children: [
2060
+ summary ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "mb-3 text-sm text-muted-foreground", children: [
1999
2061
  summary.total,
2000
2062
  " scenarios \xB7 ",
2001
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-pass", children: [
2063
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-pass", children: [
2002
2064
  summary.passed,
2003
2065
  " passed"
2004
2066
  ] }),
2005
2067
  " \xB7",
2006
2068
  " ",
2007
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-fail", children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-fail", children: [
2008
2070
  summary.failed,
2009
2071
  " failed"
2010
2072
  ] })
2011
2073
  ] }) : null,
2012
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2074
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "mb-2 flex flex-wrap gap-1.5", role: "group", "aria-label": "Filter by status", children: STATUS_FILTERS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2013
2075
  "button",
2014
2076
  {
2015
2077
  type: "button",
@@ -2024,26 +2086,26 @@ function ScenarioExplorer({ scenarios, summary }) {
2024
2086
  },
2025
2087
  f.value
2026
2088
  )) }),
2027
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2089
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2028
2090
  Command,
2029
2091
  {
2030
2092
  className: "rounded-lg border border-border bg-card",
2031
2093
  filter: (value, search) => value.toLowerCase().includes(search.toLowerCase()) ? 1 : 0,
2032
2094
  children: [
2033
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CommandInput, { placeholder: "Search scenarios\u2026" }),
2034
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(CommandList, { className: "max-h-none", children: [
2035
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CommandEmpty, { children: "No scenarios match your filters." }),
2036
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2095
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommandInput, { placeholder: "Search scenarios\u2026" }),
2096
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(CommandList, { className: "max-h-none", children: [
2097
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommandEmpty, { children: "No scenarios match your filters." }),
2098
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommandGroup, { children: visible.map((s) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2037
2099
  CommandItem,
2038
2100
  {
2039
2101
  value: `${s.title} ${s.feature} ${s.tags.join(" ")}`,
2040
2102
  asChild: true,
2041
2103
  className: "cursor-pointer",
2042
- children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("a", { href: s.href, className: "flex items-center gap-3", children: [
2043
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Badge, { variant: s.status, children: s.status }),
2044
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
2045
- s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
2046
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
2104
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("a", { href: s.href, className: "flex items-center gap-3", children: [
2105
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Badge, { variant: s.status, children: s.status }),
2106
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "min-w-0 flex-1 truncate font-medium", children: s.title }),
2107
+ s.tags.slice(0, 3).map((t) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Badge, { variant: "tag", className: "hidden sm:inline-flex", children: t }, t)),
2108
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("code", { className: "hidden font-mono text-xs text-muted-foreground md:inline", children: s.feature })
2047
2109
  ] })
2048
2110
  },
2049
2111
  s.id
@@ -2209,11 +2271,7 @@ var story_report_v1_default = {
2209
2271
  },
2210
2272
  kind: {
2211
2273
  type: "string",
2212
- enum: [
2213
- "feature",
2214
- "ability",
2215
- "business-need"
2216
- ],
2274
+ enum: ["feature", "ability", "business-need"],
2217
2275
  description: "How the feature was declared. Absent when the title was derived rather than declared."
2218
2276
  },
2219
2277
  narrative: {
@@ -2228,13 +2286,7 @@ var story_report_v1_default = {
2228
2286
  description: "Terms this feature defines, linked wherever they appear in scenario text."
2229
2287
  }
2230
2288
  },
2231
- required: [
2232
- "id",
2233
- "title",
2234
- "sourceFile",
2235
- "summary",
2236
- "scenarios"
2237
- ],
2289
+ required: ["id", "title", "sourceFile", "summary", "scenarios"],
2238
2290
  additionalProperties: false
2239
2291
  },
2240
2292
  Scenario: {
@@ -2364,6 +2416,11 @@ var story_report_v1_default = {
2364
2416
  type: "number",
2365
2417
  minimum: 0
2366
2418
  },
2419
+ assertions: {
2420
+ type: "integer",
2421
+ minimum: 0,
2422
+ description: "Assertions observed or declared for this step. Omit when the adapter cannot observe assertions; zero means observed none."
2423
+ },
2367
2424
  errorMessage: {
2368
2425
  type: "string"
2369
2426
  },
@@ -2391,33 +2448,15 @@ var story_report_v1_default = {
2391
2448
  },
2392
2449
  StepKeyword: {
2393
2450
  type: "string",
2394
- enum: [
2395
- "Given",
2396
- "When",
2397
- "Then",
2398
- "And",
2399
- "But"
2400
- ]
2451
+ enum: ["Given", "When", "Then", "And", "But"]
2401
2452
  },
2402
2453
  StepMode: {
2403
2454
  type: "string",
2404
- enum: [
2405
- "normal",
2406
- "skip",
2407
- "only",
2408
- "todo",
2409
- "fails",
2410
- "concurrent"
2411
- ]
2455
+ enum: ["normal", "skip", "only", "todo", "fails", "concurrent"]
2412
2456
  },
2413
2457
  TestStatus: {
2414
2458
  type: "string",
2415
- enum: [
2416
- "passed",
2417
- "failed",
2418
- "skipped",
2419
- "pending"
2420
- ]
2459
+ enum: ["passed", "failed", "skipped", "pending"]
2421
2460
  },
2422
2461
  Ticket: {
2423
2462
  type: "object",
@@ -2430,9 +2469,7 @@ var story_report_v1_default = {
2430
2469
  type: "string"
2431
2470
  }
2432
2471
  },
2433
- required: [
2434
- "id"
2435
- ],
2472
+ required: ["id"],
2436
2473
  additionalProperties: false
2437
2474
  },
2438
2475
  Attachment: {
@@ -2450,18 +2487,10 @@ var story_report_v1_default = {
2450
2487
  },
2451
2488
  contentEncoding: {
2452
2489
  type: "string",
2453
- enum: [
2454
- "BASE64",
2455
- "IDENTITY"
2456
- ]
2490
+ enum: ["BASE64", "IDENTITY"]
2457
2491
  }
2458
2492
  },
2459
- required: [
2460
- "name",
2461
- "mediaType",
2462
- "body",
2463
- "contentEncoding"
2464
- ],
2493
+ required: ["name", "mediaType", "body", "contentEncoding"],
2465
2494
  additionalProperties: false
2466
2495
  },
2467
2496
  CIInfo: {
@@ -2486,9 +2515,7 @@ var story_report_v1_default = {
2486
2515
  type: "string"
2487
2516
  }
2488
2517
  },
2489
- required: [
2490
- "name"
2491
- ],
2518
+ required: ["name"],
2492
2519
  additionalProperties: false
2493
2520
  },
2494
2521
  CoverageSummary: {
@@ -2519,10 +2546,7 @@ var story_report_v1_default = {
2519
2546
  },
2520
2547
  DocPhase: {
2521
2548
  type: "string",
2522
- enum: [
2523
- "static",
2524
- "runtime"
2525
- ]
2549
+ enum: ["static", "runtime"]
2526
2550
  },
2527
2551
  DocEntry: {
2528
2552
  oneOf: [
@@ -2586,11 +2610,7 @@ var story_report_v1_default = {
2586
2610
  }
2587
2611
  }
2588
2612
  },
2589
- required: [
2590
- "kind",
2591
- "text",
2592
- "phase"
2593
- ],
2613
+ required: ["kind", "text", "phase"],
2594
2614
  additionalProperties: false
2595
2615
  },
2596
2616
  DocTag: {
@@ -2615,11 +2635,7 @@ var story_report_v1_default = {
2615
2635
  }
2616
2636
  }
2617
2637
  },
2618
- required: [
2619
- "kind",
2620
- "names",
2621
- "phase"
2622
- ],
2638
+ required: ["kind", "names", "phase"],
2623
2639
  additionalProperties: false
2624
2640
  },
2625
2641
  DocKv: {
@@ -2642,12 +2658,7 @@ var story_report_v1_default = {
2642
2658
  }
2643
2659
  }
2644
2660
  },
2645
- required: [
2646
- "kind",
2647
- "label",
2648
- "value",
2649
- "phase"
2650
- ],
2661
+ required: ["kind", "label", "value", "phase"],
2651
2662
  additionalProperties: false
2652
2663
  },
2653
2664
  DocCode: {
@@ -2675,12 +2686,7 @@ var story_report_v1_default = {
2675
2686
  }
2676
2687
  }
2677
2688
  },
2678
- required: [
2679
- "kind",
2680
- "label",
2681
- "content",
2682
- "phase"
2683
- ],
2689
+ required: ["kind", "label", "content", "phase"],
2684
2690
  additionalProperties: false
2685
2691
  },
2686
2692
  DocTable: {
@@ -2717,13 +2723,7 @@ var story_report_v1_default = {
2717
2723
  }
2718
2724
  }
2719
2725
  },
2720
- required: [
2721
- "kind",
2722
- "label",
2723
- "columns",
2724
- "rows",
2725
- "phase"
2726
- ],
2726
+ required: ["kind", "label", "columns", "rows", "phase"],
2727
2727
  additionalProperties: false
2728
2728
  },
2729
2729
  DocLink: {
@@ -2748,12 +2748,7 @@ var story_report_v1_default = {
2748
2748
  }
2749
2749
  }
2750
2750
  },
2751
- required: [
2752
- "kind",
2753
- "label",
2754
- "url",
2755
- "phase"
2756
- ],
2751
+ required: ["kind", "label", "url", "phase"],
2757
2752
  additionalProperties: false
2758
2753
  },
2759
2754
  DocSection: {
@@ -2778,12 +2773,7 @@ var story_report_v1_default = {
2778
2773
  }
2779
2774
  }
2780
2775
  },
2781
- required: [
2782
- "kind",
2783
- "title",
2784
- "markdown",
2785
- "phase"
2786
- ],
2776
+ required: ["kind", "title", "markdown", "phase"],
2787
2777
  additionalProperties: false
2788
2778
  },
2789
2779
  DocMermaid: {
@@ -2808,11 +2798,7 @@ var story_report_v1_default = {
2808
2798
  }
2809
2799
  }
2810
2800
  },
2811
- required: [
2812
- "kind",
2813
- "code",
2814
- "phase"
2815
- ],
2801
+ required: ["kind", "code", "phase"],
2816
2802
  additionalProperties: false
2817
2803
  },
2818
2804
  DocScreenshot: {
@@ -2837,11 +2823,7 @@ var story_report_v1_default = {
2837
2823
  }
2838
2824
  }
2839
2825
  },
2840
- required: [
2841
- "kind",
2842
- "path",
2843
- "phase"
2844
- ],
2826
+ required: ["kind", "path", "phase"],
2845
2827
  additionalProperties: false
2846
2828
  },
2847
2829
  DocVideo: {
@@ -2869,11 +2851,7 @@ var story_report_v1_default = {
2869
2851
  }
2870
2852
  }
2871
2853
  },
2872
- required: [
2873
- "kind",
2874
- "path",
2875
- "phase"
2876
- ],
2854
+ required: ["kind", "path", "phase"],
2877
2855
  additionalProperties: false
2878
2856
  },
2879
2857
  DocHtml: {
@@ -2914,26 +2892,17 @@ var story_report_v1_default = {
2914
2892
  }
2915
2893
  }
2916
2894
  },
2917
- required: [
2918
- "kind",
2919
- "phase"
2920
- ],
2895
+ required: ["kind", "phase"],
2921
2896
  additionalProperties: false,
2922
2897
  oneOf: [
2923
2898
  {
2924
- required: [
2925
- "path"
2926
- ]
2899
+ required: ["path"]
2927
2900
  },
2928
2901
  {
2929
- required: [
2930
- "url"
2931
- ]
2902
+ required: ["url"]
2932
2903
  },
2933
2904
  {
2934
- required: [
2935
- "content"
2936
- ]
2905
+ required: ["content"]
2937
2906
  }
2938
2907
  ]
2939
2908
  },
@@ -2957,11 +2926,7 @@ var story_report_v1_default = {
2957
2926
  }
2958
2927
  }
2959
2928
  },
2960
- required: [
2961
- "kind",
2962
- "value",
2963
- "phase"
2964
- ],
2929
+ required: ["kind", "value", "phase"],
2965
2930
  additionalProperties: false
2966
2931
  },
2967
2932
  DocCustom: {
@@ -2985,21 +2950,12 @@ var story_report_v1_default = {
2985
2950
  }
2986
2951
  }
2987
2952
  },
2988
- required: [
2989
- "kind",
2990
- "type",
2991
- "data",
2992
- "phase"
2993
- ],
2953
+ required: ["kind", "type", "data", "phase"],
2994
2954
  additionalProperties: false
2995
2955
  },
2996
2956
  OtelSpan: {
2997
2957
  type: "object",
2998
- required: [
2999
- "spanId",
3000
- "name",
3001
- "status"
3002
- ],
2958
+ required: ["spanId", "name", "status"],
3003
2959
  properties: {
3004
2960
  spanId: {
3005
2961
  type: "string"
@@ -3023,11 +2979,7 @@ var story_report_v1_default = {
3023
2979
  type: "number"
3024
2980
  },
3025
2981
  status: {
3026
- enum: [
3027
- "ok",
3028
- "error",
3029
- "unset"
3030
- ]
2982
+ enum: ["ok", "error", "unset"]
3031
2983
  },
3032
2984
  statusMessage: {
3033
2985
  type: "string"
@@ -3051,10 +3003,7 @@ var story_report_v1_default = {
3051
3003
  minLength: 1
3052
3004
  }
3053
3005
  },
3054
- required: [
3055
- "term",
3056
- "definition"
3057
- ],
3006
+ required: ["term", "definition"],
3058
3007
  additionalProperties: false
3059
3008
  }
3060
3009
  }
@@ -3158,6 +3107,7 @@ function parseStoryReport(input) {
3158
3107
  parseStoryReport,
3159
3108
  prUrl,
3160
3109
  reportLastRunMs,
3110
+ scenarioLastRunMs,
3161
3111
  storyReportSchema,
3162
3112
  useBuiltinRenderers,
3163
3113
  useCustomRenderers,