executable-stories-react 0.7.0 → 0.8.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.
@@ -422,17 +422,29 @@ function DocHtml({ entry }) {
422
422
  ] });
423
423
  }
424
424
 
425
- // src/components/doc/DocCustom.tsx
425
+ // src/components/doc/DocState.tsx
426
426
  var import_jsx_runtime13 = require("react/jsx-runtime");
427
+ function DocState({ entry }) {
428
+ let json;
429
+ try {
430
+ json = JSON.stringify(entry.value, null, 2) ?? String(entry.value);
431
+ } catch {
432
+ json = String(entry.value);
433
+ }
434
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
435
+ }
436
+
437
+ // src/components/doc/DocCustom.tsx
438
+ var import_jsx_runtime14 = require("react/jsx-runtime");
427
439
  function DocCustom({ entry }) {
428
440
  const renderers = useCustomRenderers();
429
441
  const renderer = renderers[entry.type];
430
442
  if (renderer) {
431
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_jsx_runtime13.Fragment, { children: renderer(entry) });
443
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: renderer(entry) });
432
444
  }
433
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
434
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
435
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
445
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
446
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
447
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
436
448
  ] });
437
449
  }
438
450
  function safeStringify(value) {
@@ -444,45 +456,47 @@ function safeStringify(value) {
444
456
  }
445
457
 
446
458
  // src/components/doc/DocEntry.tsx
447
- var import_jsx_runtime14 = require("react/jsx-runtime");
459
+ var import_jsx_runtime15 = require("react/jsx-runtime");
448
460
  function DocEntry({ entry }) {
449
461
  switch (entry.kind) {
450
462
  case "note":
451
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocNote, { entry });
463
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocNote, { entry });
452
464
  case "tag":
453
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocTag, { entry });
465
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTag, { entry });
454
466
  case "kv":
455
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocKv, { entry });
467
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocKv, { entry });
456
468
  case "code":
457
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocCode, { entry });
469
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCode, { entry });
458
470
  case "table":
459
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocTable, { entry });
471
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTable, { entry });
460
472
  case "link":
461
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocLink, { entry });
473
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocLink, { entry });
462
474
  case "section":
463
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocSection, { entry });
475
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocSection, { entry });
464
476
  case "mermaid":
465
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocMermaid, { entry });
477
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocMermaid, { entry });
466
478
  case "screenshot":
467
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocScreenshot, { entry });
479
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocScreenshot, { entry });
468
480
  case "video":
469
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocVideo, { entry });
481
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocVideo, { entry });
470
482
  case "html":
471
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocHtml, { entry });
483
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocHtml, { entry });
484
+ case "state":
485
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocState, { entry });
472
486
  case "custom":
473
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DocCustom, { entry });
487
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCustom, { entry });
474
488
  }
475
489
  }
476
490
 
477
491
  // src/components/ReportDocEntries.tsx
478
- var import_jsx_runtime15 = require("react/jsx-runtime");
492
+ var import_jsx_runtime16 = require("react/jsx-runtime");
479
493
  function ReportDocEntries({ entries }) {
480
494
  if (entries.length === 0) return null;
481
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocEntry, { entry }, i)) });
495
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocEntry, { entry }, i)) });
482
496
  }
483
497
 
484
498
  // src/lib/step-params.tsx
485
- var import_jsx_runtime16 = require("react/jsx-runtime");
499
+ var import_jsx_runtime17 = require("react/jsx-runtime");
486
500
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
487
501
  function highlightStepParams(text) {
488
502
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -494,7 +508,7 @@ function highlightStepParams(text) {
494
508
  const end = start + match[0].length;
495
509
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
496
510
  nodes.push(
497
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
511
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
498
512
  );
499
513
  lastIndex = end;
500
514
  });
@@ -503,7 +517,7 @@ function highlightStepParams(text) {
503
517
  }
504
518
 
505
519
  // src/components/ReportSteps.tsx
506
- var import_jsx_runtime17 = require("react/jsx-runtime");
520
+ var import_jsx_runtime18 = require("react/jsx-runtime");
507
521
  var STEP_GLYPH = {
508
522
  passed: "\u2713",
509
523
  failed: "\u2717",
@@ -518,21 +532,21 @@ var STEP_GLYPH_COLOR = {
518
532
  };
519
533
  function ReportSteps({ scenario }) {
520
534
  if (scenario.steps.length === 0) return null;
521
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReportStepItem, { step }, step.id)) });
535
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportStepItem, { step }, step.id)) });
522
536
  }
523
537
  function ReportStepItem({ step }) {
524
538
  const kw = step.keyword.toLowerCase();
525
539
  const continuation = kw === "and" || kw === "but";
526
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
540
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
527
541
  "li",
528
542
  {
529
543
  id: step.id,
530
544
  "data-status": step.status,
531
545
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
532
546
  children: [
533
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-baseline gap-2", children: [
534
- /* @__PURE__ */ (0, import_jsx_runtime17.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" }),
535
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
547
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-baseline gap-2", children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime18.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" }),
549
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
536
550
  "span",
537
551
  {
538
552
  className: cn(
@@ -542,10 +556,10 @@ function ReportStepItem({ step }) {
542
556
  children: step.keyword
543
557
  }
544
558
  ),
545
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
546
- step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
559
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
560
+ step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
547
561
  ] }),
548
- step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
562
+ step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
549
563
  "pre",
550
564
  {
551
565
  role: "alert",
@@ -553,12 +567,72 @@ function ReportStepItem({ step }) {
553
567
  children: step.errorMessage
554
568
  }
555
569
  ) : null,
556
- step.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ReportDocEntries, { entries: step.docEntries }) }) : null
570
+ step.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportDocEntries, { entries: step.docEntries }) }) : null
557
571
  ]
558
572
  }
559
573
  );
560
574
  }
561
575
 
576
+ // ../executable-stories-core/dist/state-diff.js
577
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
578
+ function deepEqual(a, b) {
579
+ if (Object.is(a, b)) return true;
580
+ if (Array.isArray(a) && Array.isArray(b)) {
581
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
582
+ }
583
+ if (isPlainObject(a) && isPlainObject(b)) {
584
+ const ak = Object.keys(a);
585
+ const bk = Object.keys(b);
586
+ return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
587
+ }
588
+ return false;
589
+ }
590
+ var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
591
+ function walk(path, before, after, out) {
592
+ if (Object.is(before, after)) return;
593
+ if (Array.isArray(before) && Array.isArray(after)) {
594
+ const shared = Math.min(before.length, after.length);
595
+ for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
596
+ for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
597
+ for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
598
+ return;
599
+ }
600
+ if (isPlainObject(before) && isPlainObject(after)) {
601
+ for (const key of Object.keys(after)) {
602
+ const p = joinKey(path, key);
603
+ if (key in before) walk(p, before[key], after[key], out);
604
+ else out.push({ path: p, kind: "added", after: after[key] });
605
+ }
606
+ for (const key of Object.keys(before)) {
607
+ if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
608
+ }
609
+ return;
610
+ }
611
+ if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
612
+ }
613
+ function diffStateValues(before, after) {
614
+ const out = [];
615
+ walk("", before, after, out);
616
+ return out;
617
+ }
618
+ function formatStateValue(value, maxLen = 60) {
619
+ const s = value === void 0 ? "undefined" : JSON.stringify(value);
620
+ return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
621
+ }
622
+ function summarizeStateChanges(changes) {
623
+ return changes.map((c) => {
624
+ const at = c.path === "" ? "(root)" : c.path;
625
+ switch (c.kind) {
626
+ case "added":
627
+ return `+ ${at}: ${formatStateValue(c.after)}`;
628
+ case "removed":
629
+ return `\u2212 ${at}`;
630
+ case "changed":
631
+ return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
632
+ }
633
+ });
634
+ }
635
+
562
636
  // ../executable-stories-core/dist/storyboard.js
563
637
  function findScreenshot(entries) {
564
638
  for (const entry of entries) {
@@ -570,26 +644,55 @@ function findScreenshot(entries) {
570
644
  }
571
645
  return void 0;
572
646
  }
647
+ function findStates(entries, out = []) {
648
+ for (const entry of entries) {
649
+ if (entry.kind === "state") out.push(entry);
650
+ if (entry.children) findStates(entry.children, out);
651
+ }
652
+ return out;
653
+ }
654
+ var laneOf = (label) => label ?? "";
573
655
  function extractStoryboardFrames(scenario) {
574
656
  const frames = [];
657
+ const laneOrder = [];
658
+ const previous = /* @__PURE__ */ new Map();
575
659
  for (const step of scenario.steps) {
576
660
  const shot = findScreenshot(step.docEntries);
577
- if (!shot) continue;
661
+ const stateDocs = findStates(step.docEntries);
662
+ if (!shot && stateDocs.length === 0) continue;
663
+ const byLane = /* @__PURE__ */ new Map();
664
+ for (const doc of stateDocs) {
665
+ const lane = laneOf(doc.label);
666
+ if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
667
+ byLane.set(lane, doc);
668
+ }
669
+ const states = [];
670
+ for (const lane of laneOrder) {
671
+ const doc = byLane.get(lane);
672
+ if (!doc) continue;
673
+ const card = {
674
+ ...doc.label !== void 0 && { label: doc.label },
675
+ value: doc.value
676
+ };
677
+ if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
678
+ previous.set(lane, doc.value);
679
+ states.push(card);
680
+ }
578
681
  frames.push({
579
682
  stepId: step.id,
580
683
  stepIndex: step.index,
581
684
  keyword: step.keyword,
582
685
  text: step.text,
583
686
  status: step.status,
584
- path: shot.path,
585
- ...shot.alt !== void 0 && { alt: shot.alt }
687
+ ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
688
+ states
586
689
  });
587
690
  }
588
691
  return frames;
589
692
  }
590
693
 
591
694
  // src/components/ReportStoryboard.tsx
592
- var import_jsx_runtime18 = require("react/jsx-runtime");
695
+ var import_jsx_runtime19 = require("react/jsx-runtime");
593
696
  var FRAME_GLYPH = {
594
697
  passed: "\u2713",
595
698
  failed: "\u2717",
@@ -605,55 +708,114 @@ var FRAME_GLYPH_COLOR = {
605
708
  function ReportStoryboard({ scenario }) {
606
709
  const frames = extractStoryboardFrames(scenario);
607
710
  if (frames.length < 2) return null;
608
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
609
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
610
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ol", { className: "flex gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StoryboardFrameItem, { frame }, frame.stepId)) })
711
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
712
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
713
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StoryboardFrameItem, { frame }, frame.stepId)) })
611
714
  ] });
612
715
  }
613
716
  function StoryboardFrameItem({ frame }) {
614
- const src = isLocalFsPath(frame.path) ? void 0 : safeImageUrl(frame.path);
615
717
  const label = `${frame.keyword} ${frame.text}`;
616
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("li", { className: "w-40 shrink-0", children: [
617
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
618
- "a",
718
+ const hasStates = frame.states.length > 0;
719
+ const caption = /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
720
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
721
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "min-w-0", children: [
722
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
723
+ " ",
724
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
725
+ ] })
726
+ ] });
727
+ return (
728
+ // State cards need room for diff rows; screenshot-only frames keep the
729
+ // original narrow film-cell width (and DOM) unchanged.
730
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
731
+ frame.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FrameScreenshot, { frame, label }) : null,
732
+ hasStates ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StateCardView, { card }, card.label ?? i)) }) : null,
733
+ frame.screenshot ? caption : (
734
+ // No screenshot means no image anchor, so the caption carries the
735
+ // jump-to-step link instead.
736
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
737
+ )
738
+ ] })
739
+ );
740
+ }
741
+ function FrameScreenshot({ frame, label }) {
742
+ const path = frame.screenshot?.path ?? "";
743
+ const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
744
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
745
+ "a",
746
+ {
747
+ href: `#${frame.stepId}`,
748
+ "aria-label": `Jump to step: ${label}`,
749
+ className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
750
+ children: src ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
751
+ "img",
752
+ {
753
+ src,
754
+ alt: frame.screenshot?.alt ?? label,
755
+ loading: "lazy",
756
+ className: "h-24 w-full bg-muted object-cover object-top"
757
+ }
758
+ ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
759
+ }
760
+ );
761
+ }
762
+ var CHANGE_TONE = {
763
+ added: "bg-pass-bg text-pass",
764
+ removed: "bg-fail-bg text-fail",
765
+ changed: "bg-skip-bg text-skip"
766
+ };
767
+ function prettyJson(value) {
768
+ try {
769
+ return JSON.stringify(value, null, 2) ?? String(value);
770
+ } catch {
771
+ return String(value);
772
+ }
773
+ }
774
+ function StateCardView({ card }) {
775
+ const json = prettyJson(card.value);
776
+ const firstCapture = card.changes === void 0;
777
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "inline-flex rounded-full border border-border px-1.5 font-mono text-[0.625rem] font-medium text-muted-foreground", children: card.label ?? "State" }),
780
+ firstCapture ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
781
+ ] }),
782
+ !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
783
+ "li",
619
784
  {
620
- href: `#${frame.stepId}`,
621
- "aria-label": `Jump to step: ${label}`,
622
- className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
623
- children: src ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
624
- "img",
625
- {
626
- src,
627
- alt: frame.alt ?? label,
628
- loading: "lazy",
629
- className: "h-24 w-full bg-muted object-cover object-top"
630
- }
631
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
785
+ className: cn(
786
+ "rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
787
+ CHANGE_TONE[change.kind]
788
+ ),
789
+ children: summarizeStateChanges([change])[0]
790
+ },
791
+ i
792
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
793
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
794
+ "details",
795
+ {
796
+ open: firstCapture && json.length <= 240,
797
+ className: cn(firstCapture ? "mt-1.5" : "mt-1"),
798
+ children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
800
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { tabIndex: 0, className: "mt-1 max-h-48 overflow-auto rounded-sm bg-muted p-1.5 font-mono text-[0.625rem] leading-snug", children: json })
801
+ ]
632
802
  }
633
- ),
634
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
635
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
636
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "min-w-0", children: [
637
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
638
- " ",
639
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-muted-foreground", children: frame.alt ?? frame.text })
640
- ] })
641
- ] })
803
+ )
642
804
  ] });
643
805
  }
644
806
 
645
807
  // src/components/ReportAttachments.tsx
646
- var import_jsx_runtime19 = require("react/jsx-runtime");
808
+ var import_jsx_runtime20 = require("react/jsx-runtime");
647
809
  function ReportAttachments({ attachments }) {
648
810
  if (attachments.length === 0) return null;
649
811
  return (
650
812
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
651
813
  // blocks don't create duplicate "Attachments" region landmarks.
652
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
814
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
653
815
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
654
816
  const isImage = att.mediaType.startsWith("image/");
655
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
656
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
817
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
818
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
657
819
  "img",
658
820
  {
659
821
  src: dataUri,
@@ -662,8 +824,8 @@ function ReportAttachments({ attachments }) {
662
824
  className: "max-w-full rounded-md border border-border"
663
825
  }
664
826
  ),
665
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
666
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
827
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
828
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
667
829
  "a",
668
830
  {
669
831
  href: dataUri,
@@ -672,7 +834,7 @@ function ReportAttachments({ attachments }) {
672
834
  children: [
673
835
  att.name,
674
836
  " ",
675
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-muted-foreground", children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-muted-foreground", children: [
676
838
  "(",
677
839
  att.mediaType,
678
840
  ")"
@@ -736,15 +898,15 @@ function toTraceRows(spans) {
736
898
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
737
899
  const rows = [];
738
900
  const seen = /* @__PURE__ */ new Set();
739
- const walk = (node, depth) => {
901
+ const walk2 = (node, depth) => {
740
902
  if (seen.has(node.span.spanId)) return;
741
903
  seen.add(node.span.spanId);
742
904
  rows.push({ ...node.span, depth });
743
- for (const child of node.children) walk(child, depth + 1);
905
+ for (const child of node.children) walk2(child, depth + 1);
744
906
  };
745
- for (const root of roots) walk(root, 0);
907
+ for (const root of roots) walk2(root, 0);
746
908
  for (const node of byId.values()) {
747
- if (!seen.has(node.span.spanId)) walk(node, 0);
909
+ if (!seen.has(node.span.spanId)) walk2(node, 0);
748
910
  }
749
911
  return rows;
750
912
  }
@@ -760,7 +922,7 @@ function traceWindow(rows) {
760
922
  }
761
923
 
762
924
  // src/components/ReportTrace.tsx
763
- var import_jsx_runtime20 = require("react/jsx-runtime");
925
+ var import_jsx_runtime21 = require("react/jsx-runtime");
764
926
  var BAR_COLOR = {
765
927
  ok: "bg-pass",
766
928
  error: "bg-fail",
@@ -771,8 +933,8 @@ function ReportTrace({ spans }) {
771
933
  const rows = toTraceRows(spans);
772
934
  if (rows.length === 0) return null;
773
935
  const { minStartMs, totalMs } = traceWindow(rows);
774
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
775
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
936
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
937
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
776
938
  "Trace (",
777
939
  rows.length,
778
940
  " span",
@@ -781,11 +943,11 @@ function ReportTrace({ spans }) {
781
943
  formatDuration(totalMs),
782
944
  ")"
783
945
  ] }),
784
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
946
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
785
947
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
786
948
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
787
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("li", { className: "flex items-center gap-2", children: [
788
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
949
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2", children: [
950
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
789
951
  "span",
790
952
  {
791
953
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -794,14 +956,14 @@ function ReportTrace({ spans }) {
794
956
  children: row.name
795
957
  }
796
958
  ),
797
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
959
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
798
960
  "span",
799
961
  {
800
962
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
801
963
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
802
964
  }
803
965
  ) }),
804
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
966
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
805
967
  ] }, `${row.spanId}-${i}`);
806
968
  }) })
807
969
  ] });
@@ -870,7 +1032,7 @@ function diffSinceLastRun(history2, scenarios) {
870
1032
  }
871
1033
 
872
1034
  // src/components/ScenarioRunHistory.tsx
873
- var import_jsx_runtime21 = require("react/jsx-runtime");
1035
+ var import_jsx_runtime22 = require("react/jsx-runtime");
874
1036
  var MAX_DOTS = 10;
875
1037
  var DOT_COLOR = {
876
1038
  passed: "bg-pass",
@@ -890,16 +1052,16 @@ function ScenarioRunHistory({ scenarioId }) {
890
1052
  const recent = entries.slice(-MAX_DOTS);
891
1053
  const flaky = flakinessOf(recent) === "flaky";
892
1054
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
893
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
894
- flaky ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
895
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1055
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1056
+ flaky ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1057
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
896
1058
  "span",
897
1059
  {
898
1060
  role: "img",
899
1061
  "aria-label": `Run history: ${summary}`,
900
1062
  title: summary,
901
1063
  className: "flex shrink-0 items-center gap-[3px]",
902
- children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1064
+ children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
903
1065
  "span",
904
1066
  {
905
1067
  "aria-hidden": true,
@@ -990,7 +1152,7 @@ function stringify(value) {
990
1152
  return String(value);
991
1153
  }
992
1154
  }
993
- function docEntryToMarkdown(entry) {
1155
+ function docEntryToMarkdown(entry, stateLanes) {
994
1156
  const lines = [];
995
1157
  switch (entry.kind) {
996
1158
  case "note":
@@ -1042,13 +1204,24 @@ function docEntryToMarkdown(entry) {
1042
1204
  case "custom":
1043
1205
  lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
1044
1206
  break;
1207
+ case "state": {
1208
+ const lane = entry.label ?? "";
1209
+ lines.push(`**${entry.label ?? "State"}**`);
1210
+ if (stateLanes?.has(lane)) {
1211
+ const summary = summarizeStateChanges(diffStateValues(stateLanes.get(lane), entry.value));
1212
+ if (summary.length > 0) lines.push("", ...summary.map((l) => `- ${l}`));
1213
+ }
1214
+ stateLanes?.set(lane, entry.value);
1215
+ lines.push("", "<details>", "<summary>snapshot</summary>", "", "```json", stringify(entry.value ?? null), "```", "", "</details>");
1216
+ break;
1217
+ }
1045
1218
  }
1046
1219
  for (const child of entry.children ?? []) {
1047
- lines.push("", ...docEntryToMarkdown(child));
1220
+ lines.push("", ...docEntryToMarkdown(child, stateLanes));
1048
1221
  }
1049
1222
  return lines;
1050
1223
  }
1051
- function stepLines(step, ordinal, full) {
1224
+ function stepLines(step, ordinal, full, stateLanes) {
1052
1225
  const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
1053
1226
  const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
1054
1227
  if (!full) return lines;
@@ -1056,7 +1229,7 @@ function stepLines(step, ordinal, full) {
1056
1229
  lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
1057
1230
  }
1058
1231
  for (const entry of step.docEntries) {
1059
- lines.push("", ...docEntryToMarkdown(entry).map((l) => l === "" ? l : ` ${l}`));
1232
+ lines.push("", ...docEntryToMarkdown(entry, stateLanes).map((l) => l === "" ? l : ` ${l}`));
1060
1233
  }
1061
1234
  return lines;
1062
1235
  }
@@ -1065,9 +1238,10 @@ function scenarioToMarkdown(scenario, options = {}) {
1065
1238
  const full = variant === "full";
1066
1239
  const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
1067
1240
  if (full && meta.length > 0) lines.push(...meta);
1241
+ const stateLanes = /* @__PURE__ */ new Map();
1068
1242
  if (scenario.steps.length > 0) {
1069
1243
  if (full) lines.push("", "## Steps", "");
1070
- scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full)));
1244
+ scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full, stateLanes)));
1071
1245
  }
1072
1246
  if (scenario.errorMessage) {
1073
1247
  if (full) lines.push("", "## Failure", "");
@@ -1076,7 +1250,7 @@ function scenarioToMarkdown(scenario, options = {}) {
1076
1250
  }
1077
1251
  if (full && scenario.docEntries.length > 0) {
1078
1252
  lines.push("", "## Docs", "");
1079
- for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry), "");
1253
+ for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry, stateLanes), "");
1080
1254
  }
1081
1255
  return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
1082
1256
  }
@@ -1116,9 +1290,9 @@ function scenarioPermalink(scenario) {
1116
1290
 
1117
1291
  // src/components/ui/card.tsx
1118
1292
  var React2 = require("react");
1119
- var import_jsx_runtime22 = require("react/jsx-runtime");
1293
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1120
1294
  function Card({ className, ...props }) {
1121
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1295
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1122
1296
  "div",
1123
1297
  {
1124
1298
  "data-slot": "card",
@@ -1131,7 +1305,7 @@ function Card({ className, ...props }) {
1131
1305
  );
1132
1306
  }
1133
1307
  function CardHeader({ className, ...props }) {
1134
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1308
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1135
1309
  "div",
1136
1310
  {
1137
1311
  "data-slot": "card-header",
@@ -1144,7 +1318,7 @@ function CardHeader({ className, ...props }) {
1144
1318
  );
1145
1319
  }
1146
1320
  function CardContent({ className, ...props }) {
1147
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1321
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1148
1322
  "div",
1149
1323
  {
1150
1324
  "data-slot": "card-content",
@@ -1158,13 +1332,13 @@ function CardContent({ className, ...props }) {
1158
1332
  var React3 = require("react");
1159
1333
  var import_menu = require("@base-ui/react/menu");
1160
1334
  var import_lucide_react = require("lucide-react");
1161
- var import_jsx_runtime23 = require("react/jsx-runtime");
1335
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1162
1336
  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";
1163
1337
  function DropdownMenu(props) {
1164
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1338
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1165
1339
  }
1166
1340
  function DropdownMenuTrigger(props) {
1167
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1341
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1168
1342
  }
1169
1343
  function DropdownMenuContent({
1170
1344
  className,
@@ -1172,14 +1346,14 @@ function DropdownMenuContent({
1172
1346
  sideOffset = 4,
1173
1347
  ...props
1174
1348
  }) {
1175
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1349
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1176
1350
  import_menu.Menu.Positioner,
1177
1351
  {
1178
1352
  "data-slot": "dropdown-menu-positioner",
1179
1353
  className: "z-50",
1180
1354
  align,
1181
1355
  sideOffset,
1182
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1356
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1183
1357
  import_menu.Menu.Popup,
1184
1358
  {
1185
1359
  "data-slot": "dropdown-menu-content",
@@ -1197,7 +1371,7 @@ function DropdownMenuItem({
1197
1371
  className,
1198
1372
  ...props
1199
1373
  }) {
1200
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1374
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1201
1375
  import_menu.Menu.Item,
1202
1376
  {
1203
1377
  "data-slot": "dropdown-menu-item",
@@ -1209,7 +1383,7 @@ function DropdownMenuItem({
1209
1383
 
1210
1384
  // src/components/ReportScenario.tsx
1211
1385
  var import_lucide_react2 = require("lucide-react");
1212
- var import_jsx_runtime24 = require("react/jsx-runtime");
1386
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1213
1387
  var STATUS_META = {
1214
1388
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1215
1389
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1225,7 +1399,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1225
1399
  const collapsible = collapse !== null && !hideTitle;
1226
1400
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1227
1401
  const actions = useScenarioActions();
1228
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1402
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1229
1403
  Card,
1230
1404
  {
1231
1405
  id: scenario.id,
@@ -1233,10 +1407,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1233
1407
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1234
1408
  className: "gap-0 py-0",
1235
1409
  children: [
1236
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1237
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1238
- hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1239
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1410
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1412
+ hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1413
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1240
1414
  "button",
1241
1415
  {
1242
1416
  type: "button",
@@ -1245,49 +1419,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1245
1419
  "aria-controls": bodyId,
1246
1420
  "aria-label": `Toggle ${scenario.title}`,
1247
1421
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1248
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1422
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1249
1423
  }
1250
1424
  ) : null,
1251
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1252
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: scenario.title })
1425
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1426
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: scenario.title })
1253
1427
  ] }),
1254
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1255
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1256
- scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1257
- meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1258
- actions ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenu, { children: [
1259
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1428
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1430
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1431
+ meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1432
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenu, { children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1260
1434
  DropdownMenuTrigger,
1261
1435
  {
1262
1436
  "aria-label": `Actions for ${scenario.title}`,
1263
1437
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1264
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1438
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1265
1439
  }
1266
1440
  ),
1267
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuContent, { align: "end", children: [
1268
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1269
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react2.LinkIcon, {}),
1441
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuContent, { align: "end", children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.LinkIcon, {}),
1270
1444
  " Copy link"
1271
1445
  ] }),
1272
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1273
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react2.FileTextIcon, {}),
1446
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1447
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.FileTextIcon, {}),
1274
1448
  " Copy as Markdown"
1275
1449
  ] }),
1276
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1277
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react2.SparklesIcon, {}),
1450
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1451
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.SparklesIcon, {}),
1278
1452
  " Explain with AI"
1279
1453
  ] })
1280
1454
  ] })
1281
1455
  ] }) : null
1282
1456
  ] })
1283
1457
  ] }),
1284
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1285
- scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1286
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1458
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1459
+ scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1460
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1287
1461
  ] }) : null
1288
1462
  ] }),
1289
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1290
- scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1463
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1464
+ scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1291
1465
  "pre",
1292
1466
  {
1293
1467
  role: "alert",
@@ -1295,11 +1469,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1295
1469
  children: scenario.errorMessage
1296
1470
  }
1297
1471
  ) : null,
1298
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportStoryboard, { scenario }),
1299
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportSteps, { scenario }),
1300
- scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1301
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1302
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportTrace, { spans: scenario.otelSpans })
1472
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportStoryboard, { scenario }),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportSteps, { scenario }),
1474
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1475
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1476
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportTrace, { spans: scenario.otelSpans })
1303
1477
  ] })
1304
1478
  ]
1305
1479
  }
@@ -1307,13 +1481,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1307
1481
  }
1308
1482
 
1309
1483
  // src/components/ReportScenarioList.tsx
1310
- var import_jsx_runtime25 = require("react/jsx-runtime");
1484
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1311
1485
  function ReportScenarioList({ feature }) {
1312
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportScenario, { scenario }, scenario.id)) });
1486
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportScenario, { scenario }, scenario.id)) });
1313
1487
  }
1314
1488
 
1315
1489
  // src/components/ReportFeature.tsx
1316
- var import_jsx_runtime26 = require("react/jsx-runtime");
1490
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1317
1491
  function ReportFeature({ feature }) {
1318
1492
  const titleId = `${feature.id}-title`;
1319
1493
  const bodyId = `${feature.id}-body`;
@@ -1324,7 +1498,7 @@ function ReportFeature({ feature }) {
1324
1498
  return (
1325
1499
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1326
1500
  // layer of nesting so the scenario cards below read as the primary unit.
1327
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1501
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1328
1502
  "section",
1329
1503
  {
1330
1504
  id: feature.id,
@@ -1332,9 +1506,9 @@ function ReportFeature({ feature }) {
1332
1506
  "aria-labelledby": titleId,
1333
1507
  className: "flex flex-col gap-3",
1334
1508
  children: [
1335
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1336
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "min-w-0", children: [
1337
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1509
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1510
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "min-w-0", children: [
1511
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1338
1512
  "button",
1339
1513
  {
1340
1514
  type: "button",
@@ -1343,31 +1517,31 @@ function ReportFeature({ feature }) {
1343
1517
  "aria-controls": bodyId,
1344
1518
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1345
1519
  children: [
1346
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1347
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "truncate", children: feature.title })
1520
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1521
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate", children: feature.title })
1348
1522
  ]
1349
1523
  }
1350
1524
  ) : feature.title }),
1351
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1525
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1352
1526
  ] }),
1353
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1527
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1354
1528
  "p",
1355
1529
  {
1356
1530
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1357
1531
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1358
1532
  children: [
1359
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { children: [
1533
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
1360
1534
  s.passed,
1361
1535
  " passed"
1362
1536
  ] }),
1363
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1364
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1537
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1538
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1365
1539
  s.failed,
1366
1540
  " failed"
1367
1541
  ] }),
1368
- skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
1369
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1370
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { children: [
1542
+ skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
1371
1545
  skipped,
1372
1546
  " skipped"
1373
1547
  ] })
@@ -1376,7 +1550,7 @@ function ReportFeature({ feature }) {
1376
1550
  }
1377
1551
  )
1378
1552
  ] }),
1379
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportScenarioList, { feature }) })
1553
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportScenarioList, { feature }) })
1380
1554
  ]
1381
1555
  }
1382
1556
  )
@@ -1384,17 +1558,17 @@ function ReportFeature({ feature }) {
1384
1558
  }
1385
1559
 
1386
1560
  // src/components/ReportFeatureList.tsx
1387
- var import_jsx_runtime27 = require("react/jsx-runtime");
1561
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1388
1562
  function ReportFeatureList() {
1389
1563
  const report = useReport();
1390
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportFeature, { feature }, feature.id)) });
1564
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportFeature, { feature }, feature.id)) });
1391
1565
  }
1392
1566
 
1393
1567
  // src/components/ui/empty.tsx
1394
1568
  var import_class_variance_authority2 = require("class-variance-authority");
1395
- var import_jsx_runtime28 = require("react/jsx-runtime");
1569
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1396
1570
  function Empty({ className, ...props }) {
1397
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1571
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1398
1572
  "div",
1399
1573
  {
1400
1574
  "data-slot": "empty",
@@ -1407,7 +1581,7 @@ function Empty({ className, ...props }) {
1407
1581
  );
1408
1582
  }
1409
1583
  function EmptyHeader({ className, ...props }) {
1410
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1584
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1411
1585
  "div",
1412
1586
  {
1413
1587
  "data-slot": "empty-header",
@@ -1434,7 +1608,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1434
1608
  }
1435
1609
  );
1436
1610
  function EmptyTitle({ className, ...props }) {
1437
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1611
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1438
1612
  "div",
1439
1613
  {
1440
1614
  "data-slot": "empty-title",
@@ -1445,16 +1619,16 @@ function EmptyTitle({ className, ...props }) {
1445
1619
  }
1446
1620
 
1447
1621
  // src/components/ReportEmpty.tsx
1448
- var import_jsx_runtime29 = require("react/jsx-runtime");
1622
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1449
1623
  function ReportEmpty({ message }) {
1450
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1624
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1451
1625
  }
1452
1626
 
1453
1627
  // src/components/ReportSummary.tsx
1454
- var import_jsx_runtime30 = require("react/jsx-runtime");
1628
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1455
1629
  function ReportSummary({ className }) {
1456
1630
  const report = useReport();
1457
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1631
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1458
1632
  ReportSummaryView,
1459
1633
  {
1460
1634
  summary: report.summary,
@@ -1480,7 +1654,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1480
1654
  if (summary.pending > 0) {
1481
1655
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1482
1656
  }
1483
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1657
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1484
1658
  "dl",
1485
1659
  {
1486
1660
  className: cn(
@@ -1493,17 +1667,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1493
1667
  "aria-label": ariaLabel,
1494
1668
  children: cards.map((c) => {
1495
1669
  const tone = TONE[c.status];
1496
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1670
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1497
1671
  "div",
1498
1672
  {
1499
1673
  "data-status": c.status,
1500
1674
  className: "rounded-lg border bg-card px-4 py-3",
1501
1675
  children: [
1502
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1503
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1676
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1677
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1504
1678
  c.label
1505
1679
  ] }),
1506
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1680
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1507
1681
  ]
1508
1682
  },
1509
1683
  c.status
@@ -1563,7 +1737,7 @@ function prUrl(ci) {
1563
1737
  }
1564
1738
 
1565
1739
  // src/components/ReportMeta.tsx
1566
- var import_jsx_runtime31 = require("react/jsx-runtime");
1740
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1567
1741
  function ReportMeta() {
1568
1742
  const report = useReport();
1569
1743
  const items = [];
@@ -1586,13 +1760,13 @@ function ReportMeta() {
1586
1760
  const short = sha.slice(0, 8);
1587
1761
  items.push({
1588
1762
  label: "Commit",
1589
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1763
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1590
1764
  });
1591
1765
  }
1592
1766
  if (report.ci) {
1593
1767
  items.push({
1594
1768
  label: "CI",
1595
- value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1769
+ value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1596
1770
  ciDisplayName(report.ci.name),
1597
1771
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1598
1772
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1603,7 +1777,7 @@ function ReportMeta() {
1603
1777
  const label = `#${report.ci.prNumber}`;
1604
1778
  items.push({
1605
1779
  label: "PR",
1606
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1780
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1607
1781
  });
1608
1782
  }
1609
1783
  if (items.length === 0) return null;
@@ -1611,50 +1785,50 @@ function ReportMeta() {
1611
1785
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1612
1786
  // collapsed disclosure so it doesn't push the result down. Native <details>
1613
1787
  // so it works with no JS (the SSR/no-hydration report still toggles).
1614
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1615
- /* @__PURE__ */ (0, import_jsx_runtime31.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: [
1616
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1789
+ /* @__PURE__ */ (0, import_jsx_runtime32.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: [
1790
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1617
1791
  "Run details"
1618
1792
  ] }),
1619
- /* @__PURE__ */ (0, import_jsx_runtime31.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_runtime31.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1620
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dt", { className: "font-medium", children: item.label }),
1621
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1793
+ /* @__PURE__ */ (0, import_jsx_runtime32.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_runtime32.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1794
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dt", { className: "font-medium", children: item.label }),
1795
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1622
1796
  ] }, item.label)) })
1623
1797
  ] })
1624
1798
  );
1625
1799
  }
1626
1800
 
1627
1801
  // src/components/ReportSchemaError.tsx
1628
- var import_jsx_runtime32 = require("react/jsx-runtime");
1802
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1629
1803
  function ReportSchemaError({ error }) {
1630
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1631
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("strong", { children: "Report could not be displayed." }) }),
1632
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { children: error.message }),
1633
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { children: [
1804
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1805
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "Report could not be displayed." }) }),
1806
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: error.message }),
1807
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { children: [
1634
1808
  "The report bundle is newer than this version of ",
1635
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("code", { children: "executable-stories-react" }),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "executable-stories-react" }),
1636
1810
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1637
1811
  ] }) : null,
1638
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("details", { children: [
1639
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("summary", { children: [
1812
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { children: [
1813
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("summary", { children: [
1640
1814
  error.issues.length,
1641
1815
  " validation issue",
1642
1816
  error.issues.length === 1 ? "" : "s"
1643
1817
  ] }),
1644
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1818
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1645
1819
  ] }) : null
1646
1820
  ] });
1647
1821
  }
1648
1822
 
1649
1823
  // src/components/ReportShell.tsx
1650
- var import_jsx_runtime33 = require("react/jsx-runtime");
1824
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1651
1825
  function ReportErrorShell({
1652
1826
  error,
1653
1827
  className,
1654
1828
  title,
1655
1829
  dataTheme
1656
1830
  }) {
1657
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ReportSchemaError, { error }) });
1831
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportSchemaError, { error }) });
1658
1832
  }
1659
1833
 
1660
1834
  // src/interactive/ReportInteractive.tsx
@@ -1663,12 +1837,12 @@ var import_lucide_react5 = require("lucide-react");
1663
1837
  // src/components/ui/switch.tsx
1664
1838
  var React4 = require("react");
1665
1839
  var import_switch = require("@base-ui/react/switch");
1666
- var import_jsx_runtime34 = require("react/jsx-runtime");
1840
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1667
1841
  function Switch({
1668
1842
  className,
1669
1843
  ...props
1670
1844
  }) {
1671
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1845
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1672
1846
  import_switch.Switch.Root,
1673
1847
  {
1674
1848
  "data-slot": "switch",
@@ -1677,7 +1851,7 @@ function Switch({
1677
1851
  className
1678
1852
  ),
1679
1853
  ...props,
1680
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1854
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1681
1855
  import_switch.Switch.Thumb,
1682
1856
  {
1683
1857
  "data-slot": "switch-thumb",
@@ -1693,7 +1867,7 @@ var React5 = __toESM(require("react"), 1);
1693
1867
  var import_toggle_group = require("@base-ui/react/toggle-group");
1694
1868
  var import_toggle = require("@base-ui/react/toggle");
1695
1869
  var import_class_variance_authority3 = require("class-variance-authority");
1696
- var import_jsx_runtime35 = require("react/jsx-runtime");
1870
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1697
1871
  var toggleItemVariants = (0, import_class_variance_authority3.cva)(
1698
1872
  "inline-flex items-center justify-center gap-1.5 rounded-md font-medium whitespace-nowrap text-muted-foreground transition-[color,background-color,box-shadow] outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-background data-[pressed]:text-foreground data-[pressed]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1699
1873
  {
@@ -1715,13 +1889,13 @@ function ToggleGroup({
1715
1889
  children,
1716
1890
  ...props
1717
1891
  }) {
1718
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1892
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1719
1893
  import_toggle_group.ToggleGroup,
1720
1894
  {
1721
1895
  "data-slot": "toggle-group",
1722
1896
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1723
1897
  ...props,
1724
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1898
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1725
1899
  }
1726
1900
  );
1727
1901
  }
@@ -1732,7 +1906,7 @@ function ToggleGroupItem({
1732
1906
  ...props
1733
1907
  }) {
1734
1908
  const context = React5.useContext(ToggleGroupContext);
1735
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1909
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1736
1910
  import_toggle.Toggle,
1737
1911
  {
1738
1912
  "data-slot": "toggle-group-item",
@@ -1749,9 +1923,9 @@ var import_lucide_react3 = require("lucide-react");
1749
1923
 
1750
1924
  // src/components/ui/input.tsx
1751
1925
  var React6 = require("react");
1752
- var import_jsx_runtime36 = require("react/jsx-runtime");
1926
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1753
1927
  function Input({ className, type, ...props }) {
1754
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1928
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1755
1929
  "input",
1756
1930
  {
1757
1931
  type,
@@ -1768,7 +1942,7 @@ function Input({ className, type, ...props }) {
1768
1942
  }
1769
1943
 
1770
1944
  // src/interactive/ReportSearch.tsx
1771
- var import_jsx_runtime37 = require("react/jsx-runtime");
1945
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1772
1946
  var ReportSearch = (0, import_react8.forwardRef)(
1773
1947
  function ReportSearch2(props, ref) {
1774
1948
  const {
@@ -1790,17 +1964,17 @@ var ReportSearch = (0, import_react8.forwardRef)(
1790
1964
  e.preventDefault();
1791
1965
  }
1792
1966
  }
1793
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1794
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1795
- /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "relative w-full sm:w-72", children: [
1796
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1967
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "relative w-full sm:w-72", children: [
1970
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1797
1971
  import_lucide_react3.Search,
1798
1972
  {
1799
1973
  "aria-hidden": true,
1800
1974
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
1801
1975
  }
1802
1976
  ),
1803
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1977
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1804
1978
  Input,
1805
1979
  {
1806
1980
  ref,
@@ -1817,7 +1991,7 @@ var ReportSearch = (0, import_react8.forwardRef)(
1817
1991
  }
1818
1992
  )
1819
1993
  ] }),
1820
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
1994
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
1821
1995
  "span",
1822
1996
  {
1823
1997
  className: "text-xs tabular-nums text-muted-foreground",
@@ -1858,7 +2032,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
1858
2032
  }
1859
2033
 
1860
2034
  // src/interactive/ReportFailureBanner.tsx
1861
- var import_jsx_runtime38 = require("react/jsx-runtime");
2035
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1862
2036
  function ReportFailureBanner({ failures }) {
1863
2037
  const first = failures[0];
1864
2038
  const jumpToFirst = (0, import_react9.useCallback)(() => {
@@ -1866,7 +2040,7 @@ function ReportFailureBanner({ failures }) {
1866
2040
  scrollToScenarioId(first.scenarioId);
1867
2041
  }, [first]);
1868
2042
  if (failures.length === 0) return null;
1869
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2043
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
1870
2044
  "div",
1871
2045
  {
1872
2046
  className: "es-failure-banner",
@@ -1874,13 +2048,13 @@ function ReportFailureBanner({ failures }) {
1874
2048
  "aria-live": "polite",
1875
2049
  "aria-label": "Failure summary",
1876
2050
  children: [
1877
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "es-failure-banner-text", children: [
1878
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("strong", { children: failures.length }),
2051
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "es-failure-banner-text", children: [
2052
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("strong", { children: failures.length }),
1879
2053
  " ",
1880
2054
  "failure",
1881
2055
  failures.length === 1 ? "" : "s"
1882
2056
  ] }),
1883
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2057
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1884
2058
  "button",
1885
2059
  {
1886
2060
  type: "button",
@@ -1897,7 +2071,7 @@ function ReportFailureBanner({ failures }) {
1897
2071
 
1898
2072
  // src/interactive/ReportFreshness.tsx
1899
2073
  var import_react10 = require("react");
1900
- var import_jsx_runtime39 = require("react/jsx-runtime");
2074
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1901
2075
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1902
2076
  const [now, setNow] = (0, import_react10.useState)(nowMs);
1903
2077
  (0, import_react10.useEffect)(() => {
@@ -1905,7 +2079,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1905
2079
  }, [nowMs]);
1906
2080
  if (lastRunMs === void 0 || now === void 0) return null;
1907
2081
  const age = formatRelativeAge(lastRunMs, now);
1908
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2082
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
1909
2083
  "a",
1910
2084
  {
1911
2085
  href: ciUrl,
@@ -1916,9 +2090,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1916
2090
  }
1917
2091
  ) : null;
1918
2092
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
1919
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
1920
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { children: [
1921
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("strong", { children: [
2093
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2094
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
2095
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("strong", { children: [
1922
2096
  "Last verified ",
1923
2097
  age,
1924
2098
  "."
@@ -1928,10 +2102,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1928
2102
  runLink
1929
2103
  ] });
1930
2104
  }
1931
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2105
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
1932
2106
  "Verified ",
1933
2107
  age,
1934
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
2108
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
1935
2109
  " \xB7 ",
1936
2110
  runLink
1937
2111
  ] }) : null
@@ -1939,15 +2113,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1939
2113
  }
1940
2114
 
1941
2115
  // src/interactive/ReportLastRunDelta.tsx
1942
- var import_jsx_runtime40 = require("react/jsx-runtime");
2116
+ var import_jsx_runtime41 = require("react/jsx-runtime");
1943
2117
  var MAX_LINKS = 5;
1944
2118
  function ScenarioLinks({ scenarios, tone }) {
1945
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
1946
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
2119
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2120
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
1947
2121
  i > 0 ? ", " : "",
1948
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2122
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
1949
2123
  ] }, s.id)),
1950
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
2124
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
1951
2125
  " and ",
1952
2126
  scenarios.length - MAX_LINKS,
1953
2127
  " more"
@@ -1964,23 +2138,23 @@ function ReportLastRunDelta({
1964
2138
  if (!delta) return null;
1965
2139
  const { newlyFailing, newlyPassing, added } = delta;
1966
2140
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
1967
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2141
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
1968
2142
  }
1969
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
1970
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
1971
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "ml-2 text-fail", children: [
2143
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2144
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2145
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-fail", children: [
1972
2146
  newlyFailing.length,
1973
2147
  " newly failing (",
1974
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2148
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
1975
2149
  ")"
1976
2150
  ] }) : null,
1977
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "ml-2 text-pass", children: [
2151
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-pass", children: [
1978
2152
  newlyPassing.length,
1979
2153
  " fixed (",
1980
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2154
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
1981
2155
  ")"
1982
2156
  ] }) : null,
1983
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "ml-2", children: [
2157
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2", children: [
1984
2158
  added.length,
1985
2159
  " new"
1986
2160
  ] }) : null
@@ -2044,7 +2218,7 @@ function Button({
2044
2218
  }
2045
2219
 
2046
2220
  // src/interactive/ReportShortcutsHelp.tsx
2047
- var import_jsx_runtime41 = require("react/jsx-runtime");
2221
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2048
2222
  var SHORTCUTS = [
2049
2223
  { keys: "/", description: "Focus search" },
2050
2224
  { keys: "f", description: "Jump to next failure" },
@@ -2056,9 +2230,9 @@ var SHORTCUTS = [
2056
2230
  ];
2057
2231
  function Keys({ keys }) {
2058
2232
  const parts = keys.split(" ");
2059
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2060
- i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2061
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2233
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2234
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2235
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2062
2236
  ] }, k)) });
2063
2237
  }
2064
2238
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2069,7 +2243,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2069
2243
  if (open && !dialog.open) dialog.showModal();
2070
2244
  else if (!open && dialog.open) dialog.close();
2071
2245
  }, [open]);
2072
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2246
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2073
2247
  "dialog",
2074
2248
  {
2075
2249
  ref: dialogRef,
@@ -2077,12 +2251,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2077
2251
  "aria-label": "Keyboard shortcuts",
2078
2252
  onClose,
2079
2253
  children: [
2080
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2081
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2082
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Keys, { keys: s.keys }) }),
2083
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2254
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2256
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Keys, { keys: s.keys }) }),
2257
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2084
2258
  ] }, s.keys)) }),
2085
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2259
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2086
2260
  ]
2087
2261
  }
2088
2262
  );
@@ -2283,7 +2457,7 @@ function failuresFirst(report) {
2283
2457
 
2284
2458
  // src/interactive/ReportFilters.tsx
2285
2459
  var import_react14 = require("react");
2286
- var import_jsx_runtime42 = require("react/jsx-runtime");
2460
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2287
2461
  var TAG_LIMIT = 12;
2288
2462
  function ReportFilters({
2289
2463
  statuses,
@@ -2297,10 +2471,10 @@ function ReportFilters({
2297
2471
  if (statuses.length === 0 && tags.length === 0) return null;
2298
2472
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2299
2473
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2300
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-2", children: [
2301
- statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2474
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col gap-2", children: [
2475
+ statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2302
2476
  const active = status === opt.key;
2303
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2477
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2304
2478
  "button",
2305
2479
  {
2306
2480
  type: "button",
@@ -2312,23 +2486,23 @@ function ReportFilters({
2312
2486
  ),
2313
2487
  children: [
2314
2488
  opt.label,
2315
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2489
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2316
2490
  ]
2317
2491
  },
2318
2492
  opt.key
2319
2493
  );
2320
2494
  }) }) : null,
2321
- tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2495
+ tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2322
2496
  visibleTags.map((tag) => {
2323
2497
  const active = activeTags.includes(tag);
2324
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2498
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2325
2499
  "button",
2326
2500
  {
2327
2501
  type: "button",
2328
2502
  "aria-pressed": active,
2329
2503
  onClick: () => onToggleTag(tag),
2330
2504
  className: "cursor-pointer",
2331
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2505
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2332
2506
  Badge,
2333
2507
  {
2334
2508
  variant: "tag",
@@ -2341,7 +2515,7 @@ function ReportFilters({
2341
2515
  }
2342
2516
  ) }, tag);
2343
2517
  }),
2344
- tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2518
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2345
2519
  "button",
2346
2520
  {
2347
2521
  type: "button",
@@ -2357,7 +2531,7 @@ function ReportFilters({
2357
2531
 
2358
2532
  // src/interactive/ReportToc.tsx
2359
2533
  var import_react15 = require("react");
2360
- var import_jsx_runtime43 = require("react/jsx-runtime");
2534
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2361
2535
  var DOT = {
2362
2536
  passed: "bg-pass",
2363
2537
  failed: "bg-fail",
@@ -2386,11 +2560,11 @@ function TocContent({ onNavigate }) {
2386
2560
  return () => observer.disconnect();
2387
2561
  }, [scenarioIds]);
2388
2562
  if (report.features.length === 0) return null;
2389
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { children: [
2390
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2391
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2563
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2565
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2392
2566
  const active = activeId === scenario.id;
2393
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2567
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2394
2568
  "a",
2395
2569
  {
2396
2570
  href: `#${scenario.id}`,
@@ -2405,14 +2579,14 @@ function TocContent({ onNavigate }) {
2405
2579
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2406
2580
  ),
2407
2581
  children: [
2408
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2582
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2409
2583
  "span",
2410
2584
  {
2411
2585
  "aria-hidden": true,
2412
2586
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2413
2587
  }
2414
2588
  ),
2415
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "break-words", children: scenario.title })
2589
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "break-words", children: scenario.title })
2416
2590
  ]
2417
2591
  }
2418
2592
  ) }, scenario.id);
@@ -2422,14 +2596,14 @@ function TocContent({ onNavigate }) {
2422
2596
  function ReportToc() {
2423
2597
  const report = useReport();
2424
2598
  if (report.features.length === 0) return null;
2425
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2599
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2426
2600
  "nav",
2427
2601
  {
2428
2602
  "aria-label": "Table of contents",
2429
2603
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2430
2604
  children: [
2431
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2432
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(TocContent, {})
2605
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2606
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(TocContent, {})
2433
2607
  ]
2434
2608
  }
2435
2609
  );
@@ -2439,31 +2613,31 @@ function ReportToc() {
2439
2613
  var import_react16 = require("react");
2440
2614
  var import_dialog = require("@base-ui/react/dialog");
2441
2615
  var import_lucide_react4 = require("lucide-react");
2442
- var import_jsx_runtime44 = require("react/jsx-runtime");
2616
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2443
2617
  function ReportTocDrawer() {
2444
2618
  const report = useReport();
2445
2619
  const [open, setOpen] = (0, import_react16.useState)(false);
2446
2620
  if (report.features.length === 0) return null;
2447
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2448
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
2449
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2621
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2622
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
2623
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2450
2624
  " Contents"
2451
2625
  ] }),
2452
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_dialog.Dialog.Portal, { children: [
2453
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2454
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
2455
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center justify-between", children: [
2456
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2457
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2626
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Portal, { children: [
2627
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2628
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
2629
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between", children: [
2630
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2631
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2458
2632
  import_dialog.Dialog.Close,
2459
2633
  {
2460
2634
  "aria-label": "Close contents",
2461
2635
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2462
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2636
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2463
2637
  }
2464
2638
  )
2465
2639
  ] }),
2466
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2640
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2467
2641
  ] })
2468
2642
  ] })
2469
2643
  ] });
@@ -2518,14 +2692,14 @@ function useTheme() {
2518
2692
  }
2519
2693
 
2520
2694
  // src/interactive/ReportInteractive.tsx
2521
- var import_jsx_runtime45 = require("react/jsx-runtime");
2695
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2522
2696
  function ReportInteractive(props) {
2523
2697
  const { report, className, title, dataTheme } = props;
2524
2698
  const result = unwrapReport(report);
2525
2699
  if (!result.ok) {
2526
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2700
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2527
2701
  }
2528
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportInteractiveView, { ...props, report: result.data });
2702
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportInteractiveView, { ...props, report: result.data });
2529
2703
  }
2530
2704
  var THEME_OPTIONS = [
2531
2705
  { value: "light", label: "Light", Icon: import_lucide_react5.Sun },
@@ -2533,7 +2707,7 @@ var THEME_OPTIONS = [
2533
2707
  { value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
2534
2708
  ];
2535
2709
  function ThemeSegment({ pref, onPref }) {
2536
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2710
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2537
2711
  ToggleGroup,
2538
2712
  {
2539
2713
  size: "sm",
@@ -2543,7 +2717,7 @@ function ThemeSegment({ pref, onPref }) {
2543
2717
  if (next) onPref(next);
2544
2718
  },
2545
2719
  "aria-label": "Color theme",
2546
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Icon, {}) }, value))
2720
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Icon, {}) }, value))
2547
2721
  }
2548
2722
  );
2549
2723
  }
@@ -2665,13 +2839,13 @@ function ReportInteractiveView({
2665
2839
  const totalScenarios = report.summary.total;
2666
2840
  const matchedScenarios = filtered.summary.total;
2667
2841
  const showToc = !hideToc && totalScenarios > 3;
2668
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2842
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2669
2843
  ReportRoot,
2670
2844
  {
2671
2845
  report: filtered,
2672
2846
  customRenderers,
2673
2847
  renderers,
2674
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2848
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2675
2849
  "main",
2676
2850
  {
2677
2851
  className: cn("es-report", "es-report-interactive", className),
@@ -2680,8 +2854,8 @@ function ReportInteractiveView({
2680
2854
  "data-theme": dataTheme,
2681
2855
  "data-detail-level": detail,
2682
2856
  children: [
2683
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("header", { className: "es-report-header", children: [
2684
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2857
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "es-report-header", children: [
2858
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2685
2859
  "div",
2686
2860
  {
2687
2861
  "data-es-navbar": true,
@@ -2690,9 +2864,9 @@ function ReportInteractiveView({
2690
2864
  hideHeader ? "justify-end" : "justify-between"
2691
2865
  ),
2692
2866
  children: [
2693
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2694
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
2695
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2867
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2868
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2869
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2696
2870
  ReportSearch,
2697
2871
  {
2698
2872
  ref: searchRef,
@@ -2702,16 +2876,16 @@ function ReportInteractiveView({
2702
2876
  totalCount: totalScenarios
2703
2877
  }
2704
2878
  ),
2705
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2879
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2706
2880
  ] })
2707
2881
  ]
2708
2882
  }
2709
2883
  ),
2710
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
2711
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportSummary, {}),
2712
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportMeta, {})
2884
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2885
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportSummary, {}),
2886
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportMeta, {})
2713
2887
  ] }),
2714
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2888
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2715
2889
  ReportFreshness,
2716
2890
  {
2717
2891
  lastRunMs: reportLastRunMs(report),
@@ -2719,8 +2893,8 @@ function ReportInteractiveView({
2719
2893
  staleAfterDays
2720
2894
  }
2721
2895
  ),
2722
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2723
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2896
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2897
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2724
2898
  ReportFilters,
2725
2899
  {
2726
2900
  statuses: statusOptions,
@@ -2731,14 +2905,14 @@ function ReportInteractiveView({
2731
2905
  onToggleTag: toggleTag
2732
2906
  }
2733
2907
  ),
2734
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2735
- showToc ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportTocDrawer, {}) : null,
2736
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
2737
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2738
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2908
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2909
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportTocDrawer, {}) : null,
2910
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2911
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2912
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2739
2913
  ] }),
2740
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
2741
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2914
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2915
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2742
2916
  Switch,
2743
2917
  {
2744
2918
  id: docsId,
@@ -2746,16 +2920,16 @@ function ReportInteractiveView({
2746
2920
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
2747
2921
  }
2748
2922
  ),
2749
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2923
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2750
2924
  ] })
2751
2925
  ] })
2752
2926
  ] }),
2753
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportFailureBanner, { failures }),
2754
- hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex gap-6", children: [
2755
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportToc, {}),
2756
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportFeatureList, {}) })
2757
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2758
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2927
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFailureBanner, { failures }),
2928
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex gap-6", children: [
2929
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportToc, {}),
2930
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFeatureList, {}) })
2931
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2932
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2759
2933
  "button",
2760
2934
  {
2761
2935
  type: "button",
@@ -2766,8 +2940,8 @@ function ReportInteractiveView({
2766
2940
  children: "?"
2767
2941
  }
2768
2942
  ),
2769
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2770
- toast ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2943
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2944
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2771
2945
  "div",
2772
2946
  {
2773
2947
  role: "status",
@@ -2791,7 +2965,7 @@ var import_react20 = require("react");
2791
2965
 
2792
2966
  // src/components/doc/MermaidView.tsx
2793
2967
  var import_react19 = require("react");
2794
- var import_jsx_runtime46 = require("react/jsx-runtime");
2968
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2795
2969
  function prefersDark(el) {
2796
2970
  const themed = el?.closest("[data-theme]");
2797
2971
  if (themed?.dataset.theme === "dark") return true;
@@ -2828,9 +3002,9 @@ function MermaidView({ entry, load }) {
2828
3002
  };
2829
3003
  }, [entry.code, load]);
2830
3004
  if (svg) {
2831
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("figure", { ref, className: "my-3", children: [
2832
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
2833
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3005
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("figure", { ref, className: "my-3", children: [
3006
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3007
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2834
3008
  "div",
2835
3009
  {
2836
3010
  role: "img",
@@ -2841,11 +3015,11 @@ function MermaidView({ entry, load }) {
2841
3015
  )
2842
3016
  ] });
2843
3017
  }
2844
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(MermaidSource, { entry, ref });
3018
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(MermaidSource, { entry, ref });
2845
3019
  }
2846
3020
 
2847
3021
  // src/interactive/island-renderers.tsx
2848
- var import_jsx_runtime47 = require("react/jsx-runtime");
3022
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2849
3023
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
2850
3024
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
2851
3025
  function cachedCdnModule(url) {
@@ -2879,13 +3053,13 @@ function CdnHighlightedCode({ entry }) {
2879
3053
  cancelled = true;
2880
3054
  };
2881
3055
  }, [entry.content, entry.lang]);
2882
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3056
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2883
3057
  "code",
2884
3058
  {
2885
3059
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
2886
3060
  dangerouslySetInnerHTML: { __html: html }
2887
3061
  }
2888
- ) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3062
+ ) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
2889
3063
  }
2890
3064
  function buildIslandRenderers(opts) {
2891
3065
  const renderers = {};
@@ -2899,7 +3073,7 @@ function buildIslandRenderers(opts) {
2899
3073
  }
2900
3074
 
2901
3075
  // src/interactive/ReportInteractiveIsland.tsx
2902
- var import_jsx_runtime48 = require("react/jsx-runtime");
3076
+ var import_jsx_runtime49 = require("react/jsx-runtime");
2903
3077
  function ReportInteractiveIsland({
2904
3078
  report,
2905
3079
  title,
@@ -2913,7 +3087,7 @@ function ReportInteractiveIsland({
2913
3087
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
2914
3088
  [syntaxHighlighting, mermaid]
2915
3089
  );
2916
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3090
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2917
3091
  ReportInteractive,
2918
3092
  {
2919
3093
  report,