executable-stories-react 0.6.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.
package/dist/ssr.js CHANGED
@@ -217,7 +217,7 @@ function DocLink({ entry }) {
217
217
  // src/components/doc/DocSection.tsx
218
218
  import { marked } from "marked";
219
219
 
220
- // src/lib/url.ts
220
+ // ../executable-stories-core/dist/utils/url.js
221
221
  function safeUrl(value) {
222
222
  if (!value) return void 0;
223
223
  const trimmed = value.trim();
@@ -375,17 +375,29 @@ function DocHtml({ entry }) {
375
375
  ] });
376
376
  }
377
377
 
378
+ // src/components/doc/DocState.tsx
379
+ import { jsx as jsx13 } from "react/jsx-runtime";
380
+ function DocState({ entry }) {
381
+ let json;
382
+ try {
383
+ json = JSON.stringify(entry.value, null, 2) ?? String(entry.value);
384
+ } catch {
385
+ json = String(entry.value);
386
+ }
387
+ return /* @__PURE__ */ jsx13(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx13("code", { className: "font-mono text-xs language-json", children: json }) });
388
+ }
389
+
378
390
  // src/components/doc/DocCustom.tsx
379
- import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
391
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
380
392
  function DocCustom({ entry }) {
381
393
  const renderers = useCustomRenderers();
382
394
  const renderer = renderers[entry.type];
383
395
  if (renderer) {
384
- return /* @__PURE__ */ jsx13(Fragment4, { children: renderer(entry) });
396
+ return /* @__PURE__ */ jsx14(Fragment4, { children: renderer(entry) });
385
397
  }
386
398
  return /* @__PURE__ */ jsxs9("div", { className: "my-2", "data-type": entry.type, children: [
387
- /* @__PURE__ */ jsx13("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
388
- /* @__PURE__ */ jsx13("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
399
+ /* @__PURE__ */ jsx14("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
400
+ /* @__PURE__ */ jsx14("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
389
401
  ] });
390
402
  }
391
403
  function safeStringify(value) {
@@ -397,45 +409,47 @@ function safeStringify(value) {
397
409
  }
398
410
 
399
411
  // src/components/doc/DocEntry.tsx
400
- import { jsx as jsx14 } from "react/jsx-runtime";
412
+ import { jsx as jsx15 } from "react/jsx-runtime";
401
413
  function DocEntry({ entry }) {
402
414
  switch (entry.kind) {
403
415
  case "note":
404
- return /* @__PURE__ */ jsx14(DocNote, { entry });
416
+ return /* @__PURE__ */ jsx15(DocNote, { entry });
405
417
  case "tag":
406
- return /* @__PURE__ */ jsx14(DocTag, { entry });
418
+ return /* @__PURE__ */ jsx15(DocTag, { entry });
407
419
  case "kv":
408
- return /* @__PURE__ */ jsx14(DocKv, { entry });
420
+ return /* @__PURE__ */ jsx15(DocKv, { entry });
409
421
  case "code":
410
- return /* @__PURE__ */ jsx14(DocCode, { entry });
422
+ return /* @__PURE__ */ jsx15(DocCode, { entry });
411
423
  case "table":
412
- return /* @__PURE__ */ jsx14(DocTable, { entry });
424
+ return /* @__PURE__ */ jsx15(DocTable, { entry });
413
425
  case "link":
414
- return /* @__PURE__ */ jsx14(DocLink, { entry });
426
+ return /* @__PURE__ */ jsx15(DocLink, { entry });
415
427
  case "section":
416
- return /* @__PURE__ */ jsx14(DocSection, { entry });
428
+ return /* @__PURE__ */ jsx15(DocSection, { entry });
417
429
  case "mermaid":
418
- return /* @__PURE__ */ jsx14(DocMermaid, { entry });
430
+ return /* @__PURE__ */ jsx15(DocMermaid, { entry });
419
431
  case "screenshot":
420
- return /* @__PURE__ */ jsx14(DocScreenshot, { entry });
432
+ return /* @__PURE__ */ jsx15(DocScreenshot, { entry });
421
433
  case "video":
422
- return /* @__PURE__ */ jsx14(DocVideo, { entry });
434
+ return /* @__PURE__ */ jsx15(DocVideo, { entry });
423
435
  case "html":
424
- return /* @__PURE__ */ jsx14(DocHtml, { entry });
436
+ return /* @__PURE__ */ jsx15(DocHtml, { entry });
437
+ case "state":
438
+ return /* @__PURE__ */ jsx15(DocState, { entry });
425
439
  case "custom":
426
- return /* @__PURE__ */ jsx14(DocCustom, { entry });
440
+ return /* @__PURE__ */ jsx15(DocCustom, { entry });
427
441
  }
428
442
  }
429
443
 
430
444
  // src/components/ReportDocEntries.tsx
431
- import { Fragment as Fragment5, jsx as jsx15 } from "react/jsx-runtime";
445
+ import { Fragment as Fragment5, jsx as jsx16 } from "react/jsx-runtime";
432
446
  function ReportDocEntries({ entries }) {
433
447
  if (entries.length === 0) return null;
434
- return /* @__PURE__ */ jsx15(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx15(DocEntry, { entry }, i)) });
448
+ return /* @__PURE__ */ jsx16(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx16(DocEntry, { entry }, i)) });
435
449
  }
436
450
 
437
451
  // src/lib/step-params.tsx
438
- import { jsx as jsx16 } from "react/jsx-runtime";
452
+ import { jsx as jsx17 } from "react/jsx-runtime";
439
453
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
440
454
  function highlightStepParams(text) {
441
455
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -447,7 +461,7 @@ function highlightStepParams(text) {
447
461
  const end = start + match[0].length;
448
462
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
449
463
  nodes.push(
450
- /* @__PURE__ */ jsx16("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
464
+ /* @__PURE__ */ jsx17("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
451
465
  );
452
466
  lastIndex = end;
453
467
  });
@@ -456,7 +470,7 @@ function highlightStepParams(text) {
456
470
  }
457
471
 
458
472
  // src/components/ReportSteps.tsx
459
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
473
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
460
474
  var STEP_GLYPH = {
461
475
  passed: "\u2713",
462
476
  failed: "\u2717",
@@ -471,7 +485,7 @@ var STEP_GLYPH_COLOR = {
471
485
  };
472
486
  function ReportSteps({ scenario }) {
473
487
  if (scenario.steps.length === 0) return null;
474
- return /* @__PURE__ */ jsx17("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx17(ReportStepItem, { step }, step.id)) });
488
+ return /* @__PURE__ */ jsx18("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx18(ReportStepItem, { step }, step.id)) });
475
489
  }
476
490
  function ReportStepItem({ step }) {
477
491
  const kw = step.keyword.toLowerCase();
@@ -484,8 +498,8 @@ function ReportStepItem({ step }) {
484
498
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
485
499
  children: [
486
500
  /* @__PURE__ */ jsxs10("div", { className: "flex items-baseline gap-2", children: [
487
- /* @__PURE__ */ jsx17("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" }),
488
- /* @__PURE__ */ jsx17(
501
+ /* @__PURE__ */ jsx18("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" }),
502
+ /* @__PURE__ */ jsx18(
489
503
  "span",
490
504
  {
491
505
  className: cn(
@@ -495,10 +509,10 @@ function ReportStepItem({ step }) {
495
509
  children: step.keyword
496
510
  }
497
511
  ),
498
- /* @__PURE__ */ jsx17("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
499
- step.durationMs > 0 ? /* @__PURE__ */ jsx17("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
512
+ /* @__PURE__ */ jsx18("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
513
+ step.durationMs > 0 ? /* @__PURE__ */ jsx18("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
500
514
  ] }),
501
- step.errorMessage ? /* @__PURE__ */ jsx17(
515
+ step.errorMessage ? /* @__PURE__ */ jsx18(
502
516
  "pre",
503
517
  {
504
518
  role: "alert",
@@ -506,24 +520,255 @@ function ReportStepItem({ step }) {
506
520
  children: step.errorMessage
507
521
  }
508
522
  ) : null,
509
- step.docEntries.length > 0 ? /* @__PURE__ */ jsx17("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx17(ReportDocEntries, { entries: step.docEntries }) }) : null
523
+ step.docEntries.length > 0 ? /* @__PURE__ */ jsx18("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx18(ReportDocEntries, { entries: step.docEntries }) }) : null
510
524
  ]
511
525
  }
512
526
  );
513
527
  }
514
528
 
529
+ // ../executable-stories-core/dist/state-diff.js
530
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
531
+ function deepEqual(a, b) {
532
+ if (Object.is(a, b)) return true;
533
+ if (Array.isArray(a) && Array.isArray(b)) {
534
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
535
+ }
536
+ if (isPlainObject(a) && isPlainObject(b)) {
537
+ const ak = Object.keys(a);
538
+ const bk = Object.keys(b);
539
+ return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
540
+ }
541
+ return false;
542
+ }
543
+ var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
544
+ function walk(path, before, after, out) {
545
+ if (Object.is(before, after)) return;
546
+ if (Array.isArray(before) && Array.isArray(after)) {
547
+ const shared = Math.min(before.length, after.length);
548
+ for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
549
+ for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
550
+ for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
551
+ return;
552
+ }
553
+ if (isPlainObject(before) && isPlainObject(after)) {
554
+ for (const key of Object.keys(after)) {
555
+ const p = joinKey(path, key);
556
+ if (key in before) walk(p, before[key], after[key], out);
557
+ else out.push({ path: p, kind: "added", after: after[key] });
558
+ }
559
+ for (const key of Object.keys(before)) {
560
+ if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
561
+ }
562
+ return;
563
+ }
564
+ if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
565
+ }
566
+ function diffStateValues(before, after) {
567
+ const out = [];
568
+ walk("", before, after, out);
569
+ return out;
570
+ }
571
+ function formatStateValue(value, maxLen = 60) {
572
+ const s = value === void 0 ? "undefined" : JSON.stringify(value);
573
+ return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
574
+ }
575
+ function summarizeStateChanges(changes) {
576
+ return changes.map((c) => {
577
+ const at = c.path === "" ? "(root)" : c.path;
578
+ switch (c.kind) {
579
+ case "added":
580
+ return `+ ${at}: ${formatStateValue(c.after)}`;
581
+ case "removed":
582
+ return `\u2212 ${at}`;
583
+ case "changed":
584
+ return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
585
+ }
586
+ });
587
+ }
588
+
589
+ // ../executable-stories-core/dist/storyboard.js
590
+ function findScreenshot(entries) {
591
+ for (const entry of entries) {
592
+ if (entry.kind === "screenshot") return entry;
593
+ if (entry.children) {
594
+ const nested = findScreenshot(entry.children);
595
+ if (nested) return nested;
596
+ }
597
+ }
598
+ return void 0;
599
+ }
600
+ function findStates(entries, out = []) {
601
+ for (const entry of entries) {
602
+ if (entry.kind === "state") out.push(entry);
603
+ if (entry.children) findStates(entry.children, out);
604
+ }
605
+ return out;
606
+ }
607
+ var laneOf = (label) => label ?? "";
608
+ function extractStoryboardFrames(scenario) {
609
+ const frames = [];
610
+ const laneOrder = [];
611
+ const previous = /* @__PURE__ */ new Map();
612
+ for (const step of scenario.steps) {
613
+ const shot = findScreenshot(step.docEntries);
614
+ const stateDocs = findStates(step.docEntries);
615
+ if (!shot && stateDocs.length === 0) continue;
616
+ const byLane = /* @__PURE__ */ new Map();
617
+ for (const doc of stateDocs) {
618
+ const lane = laneOf(doc.label);
619
+ if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
620
+ byLane.set(lane, doc);
621
+ }
622
+ const states = [];
623
+ for (const lane of laneOrder) {
624
+ const doc = byLane.get(lane);
625
+ if (!doc) continue;
626
+ const card = {
627
+ ...doc.label !== void 0 && { label: doc.label },
628
+ value: doc.value
629
+ };
630
+ if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
631
+ previous.set(lane, doc.value);
632
+ states.push(card);
633
+ }
634
+ frames.push({
635
+ stepId: step.id,
636
+ stepIndex: step.index,
637
+ keyword: step.keyword,
638
+ text: step.text,
639
+ status: step.status,
640
+ ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
641
+ states
642
+ });
643
+ }
644
+ return frames;
645
+ }
646
+
647
+ // src/components/ReportStoryboard.tsx
648
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
649
+ var FRAME_GLYPH = {
650
+ passed: "\u2713",
651
+ failed: "\u2717",
652
+ skipped: "\u25CB",
653
+ pending: "\u25CB"
654
+ };
655
+ var FRAME_GLYPH_COLOR = {
656
+ passed: "text-pass",
657
+ failed: "text-fail",
658
+ skipped: "text-skip",
659
+ pending: "text-pend"
660
+ };
661
+ function ReportStoryboard({ scenario }) {
662
+ const frames = extractStoryboardFrames(scenario);
663
+ if (frames.length < 2) return null;
664
+ return /* @__PURE__ */ jsxs11("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
665
+ /* @__PURE__ */ jsx19("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
666
+ /* @__PURE__ */ jsx19("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx19(StoryboardFrameItem, { frame }, frame.stepId)) })
667
+ ] });
668
+ }
669
+ function StoryboardFrameItem({ frame }) {
670
+ const label = `${frame.keyword} ${frame.text}`;
671
+ const hasStates = frame.states.length > 0;
672
+ const caption = /* @__PURE__ */ jsxs11("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
673
+ /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
674
+ /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
675
+ /* @__PURE__ */ jsx19("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
676
+ " ",
677
+ /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
678
+ ] })
679
+ ] });
680
+ return (
681
+ // State cards need room for diff rows; screenshot-only frames keep the
682
+ // original narrow film-cell width (and DOM) unchanged.
683
+ /* @__PURE__ */ jsxs11("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
684
+ frame.screenshot ? /* @__PURE__ */ jsx19(FrameScreenshot, { frame, label }) : null,
685
+ hasStates ? /* @__PURE__ */ jsx19("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ jsx19(StateCardView, { card }, card.label ?? i)) }) : null,
686
+ frame.screenshot ? caption : (
687
+ // No screenshot means no image anchor, so the caption carries the
688
+ // jump-to-step link instead.
689
+ /* @__PURE__ */ jsx19("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
690
+ )
691
+ ] })
692
+ );
693
+ }
694
+ function FrameScreenshot({ frame, label }) {
695
+ const path = frame.screenshot?.path ?? "";
696
+ const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
697
+ return /* @__PURE__ */ jsx19(
698
+ "a",
699
+ {
700
+ href: `#${frame.stepId}`,
701
+ "aria-label": `Jump to step: ${label}`,
702
+ className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
703
+ children: src ? /* @__PURE__ */ jsx19(
704
+ "img",
705
+ {
706
+ src,
707
+ alt: frame.screenshot?.alt ?? label,
708
+ loading: "lazy",
709
+ className: "h-24 w-full bg-muted object-cover object-top"
710
+ }
711
+ ) : /* @__PURE__ */ jsx19("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" })
712
+ }
713
+ );
714
+ }
715
+ var CHANGE_TONE = {
716
+ added: "bg-pass-bg text-pass",
717
+ removed: "bg-fail-bg text-fail",
718
+ changed: "bg-skip-bg text-skip"
719
+ };
720
+ function prettyJson(value) {
721
+ try {
722
+ return JSON.stringify(value, null, 2) ?? String(value);
723
+ } catch {
724
+ return String(value);
725
+ }
726
+ }
727
+ function StateCardView({ card }) {
728
+ const json = prettyJson(card.value);
729
+ const firstCapture = card.changes === void 0;
730
+ return /* @__PURE__ */ jsxs11("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
731
+ /* @__PURE__ */ jsxs11("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
732
+ /* @__PURE__ */ jsx19("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" }),
733
+ firstCapture ? /* @__PURE__ */ jsx19("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
734
+ ] }),
735
+ !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ jsx19("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ jsx19(
736
+ "li",
737
+ {
738
+ className: cn(
739
+ "rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
740
+ CHANGE_TONE[change.kind]
741
+ ),
742
+ children: summarizeStateChanges([change])[0]
743
+ },
744
+ i
745
+ )) }) : /* @__PURE__ */ jsx19("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
746
+ /* @__PURE__ */ jsxs11(
747
+ "details",
748
+ {
749
+ open: firstCapture && json.length <= 240,
750
+ className: cn(firstCapture ? "mt-1.5" : "mt-1"),
751
+ children: [
752
+ /* @__PURE__ */ jsx19("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
753
+ /* @__PURE__ */ jsx19("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 })
754
+ ]
755
+ }
756
+ )
757
+ ] });
758
+ }
759
+
515
760
  // src/components/ReportAttachments.tsx
516
- import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
761
+ import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
517
762
  function ReportAttachments({ attachments }) {
518
763
  if (attachments.length === 0) return null;
519
764
  return (
520
765
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
521
766
  // blocks don't create duplicate "Attachments" region landmarks.
522
- /* @__PURE__ */ jsx18("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
767
+ /* @__PURE__ */ jsx20("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
523
768
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
524
769
  const isImage = att.mediaType.startsWith("image/");
525
- return /* @__PURE__ */ jsx18("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs11(Fragment6, { children: [
526
- /* @__PURE__ */ jsx18(
770
+ return /* @__PURE__ */ jsx20("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs12(Fragment6, { children: [
771
+ /* @__PURE__ */ jsx20(
527
772
  "img",
528
773
  {
529
774
  src: dataUri,
@@ -532,8 +777,8 @@ function ReportAttachments({ attachments }) {
532
777
  className: "max-w-full rounded-md border border-border"
533
778
  }
534
779
  ),
535
- /* @__PURE__ */ jsx18("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
536
- ] }) : /* @__PURE__ */ jsxs11(
780
+ /* @__PURE__ */ jsx20("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
781
+ ] }) : /* @__PURE__ */ jsxs12(
537
782
  "a",
538
783
  {
539
784
  href: dataUri,
@@ -542,7 +787,7 @@ function ReportAttachments({ attachments }) {
542
787
  children: [
543
788
  att.name,
544
789
  " ",
545
- /* @__PURE__ */ jsxs11("span", { className: "text-muted-foreground", children: [
790
+ /* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
546
791
  "(",
547
792
  att.mediaType,
548
793
  ")"
@@ -606,15 +851,15 @@ function toTraceRows(spans) {
606
851
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
607
852
  const rows = [];
608
853
  const seen = /* @__PURE__ */ new Set();
609
- const walk = (node, depth) => {
854
+ const walk2 = (node, depth) => {
610
855
  if (seen.has(node.span.spanId)) return;
611
856
  seen.add(node.span.spanId);
612
857
  rows.push({ ...node.span, depth });
613
- for (const child of node.children) walk(child, depth + 1);
858
+ for (const child of node.children) walk2(child, depth + 1);
614
859
  };
615
- for (const root of roots) walk(root, 0);
860
+ for (const root of roots) walk2(root, 0);
616
861
  for (const node of byId.values()) {
617
- if (!seen.has(node.span.spanId)) walk(node, 0);
862
+ if (!seen.has(node.span.spanId)) walk2(node, 0);
618
863
  }
619
864
  return rows;
620
865
  }
@@ -630,7 +875,7 @@ function traceWindow(rows) {
630
875
  }
631
876
 
632
877
  // src/components/ReportTrace.tsx
633
- import { jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
878
+ import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
634
879
  var BAR_COLOR = {
635
880
  ok: "bg-pass",
636
881
  error: "bg-fail",
@@ -641,8 +886,8 @@ function ReportTrace({ spans }) {
641
886
  const rows = toTraceRows(spans);
642
887
  if (rows.length === 0) return null;
643
888
  const { minStartMs, totalMs } = traceWindow(rows);
644
- return /* @__PURE__ */ jsxs12("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
645
- /* @__PURE__ */ jsxs12("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
889
+ return /* @__PURE__ */ jsxs13("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
890
+ /* @__PURE__ */ jsxs13("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
646
891
  "Trace (",
647
892
  rows.length,
648
893
  " span",
@@ -651,11 +896,11 @@ function ReportTrace({ spans }) {
651
896
  formatDuration(totalMs),
652
897
  ")"
653
898
  ] }),
654
- /* @__PURE__ */ jsx19("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
899
+ /* @__PURE__ */ jsx21("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
655
900
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
656
901
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
657
- return /* @__PURE__ */ jsxs12("li", { className: "flex items-center gap-2", children: [
658
- /* @__PURE__ */ jsx19(
902
+ return /* @__PURE__ */ jsxs13("li", { className: "flex items-center gap-2", children: [
903
+ /* @__PURE__ */ jsx21(
659
904
  "span",
660
905
  {
661
906
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -664,14 +909,14 @@ function ReportTrace({ spans }) {
664
909
  children: row.name
665
910
  }
666
911
  ),
667
- /* @__PURE__ */ jsx19("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx19(
912
+ /* @__PURE__ */ jsx21("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx21(
668
913
  "span",
669
914
  {
670
915
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
671
916
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
672
917
  }
673
918
  ) }),
674
- /* @__PURE__ */ jsx19("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
919
+ /* @__PURE__ */ jsx21("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
675
920
  ] }, `${row.spanId}-${i}`);
676
921
  }) })
677
922
  ] });
@@ -722,7 +967,7 @@ function flakinessOf(entries) {
722
967
  }
723
968
 
724
969
  // src/components/ScenarioRunHistory.tsx
725
- import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
970
+ import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
726
971
  var MAX_DOTS = 10;
727
972
  var DOT_COLOR = {
728
973
  passed: "bg-pass",
@@ -742,16 +987,16 @@ function ScenarioRunHistory({ scenarioId }) {
742
987
  const recent = entries.slice(-MAX_DOTS);
743
988
  const flaky = flakinessOf(recent) === "flaky";
744
989
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
745
- return /* @__PURE__ */ jsxs13(Fragment7, { children: [
746
- flaky ? /* @__PURE__ */ jsx20(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
747
- /* @__PURE__ */ jsx20(
990
+ return /* @__PURE__ */ jsxs14(Fragment7, { children: [
991
+ flaky ? /* @__PURE__ */ jsx22(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
992
+ /* @__PURE__ */ jsx22(
748
993
  "span",
749
994
  {
750
995
  role: "img",
751
996
  "aria-label": `Run history: ${summary}`,
752
997
  title: summary,
753
998
  className: "flex shrink-0 items-center gap-[3px]",
754
- children: recent.map((e, i) => /* @__PURE__ */ jsx20(
999
+ children: recent.map((e, i) => /* @__PURE__ */ jsx22(
755
1000
  "span",
756
1001
  {
757
1002
  "aria-hidden": true,
@@ -790,9 +1035,9 @@ function useScenarioActions() {
790
1035
 
791
1036
  // src/components/ui/card.tsx
792
1037
  import "react";
793
- import { jsx as jsx21 } from "react/jsx-runtime";
1038
+ import { jsx as jsx23 } from "react/jsx-runtime";
794
1039
  function Card({ className, ...props }) {
795
- return /* @__PURE__ */ jsx21(
1040
+ return /* @__PURE__ */ jsx23(
796
1041
  "div",
797
1042
  {
798
1043
  "data-slot": "card",
@@ -805,7 +1050,7 @@ function Card({ className, ...props }) {
805
1050
  );
806
1051
  }
807
1052
  function CardHeader({ className, ...props }) {
808
- return /* @__PURE__ */ jsx21(
1053
+ return /* @__PURE__ */ jsx23(
809
1054
  "div",
810
1055
  {
811
1056
  "data-slot": "card-header",
@@ -818,7 +1063,7 @@ function CardHeader({ className, ...props }) {
818
1063
  );
819
1064
  }
820
1065
  function CardContent({ className, ...props }) {
821
- return /* @__PURE__ */ jsx21(
1066
+ return /* @__PURE__ */ jsx23(
822
1067
  "div",
823
1068
  {
824
1069
  "data-slot": "card-content",
@@ -832,13 +1077,13 @@ function CardContent({ className, ...props }) {
832
1077
  import "react";
833
1078
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
834
1079
  import { CheckIcon } from "lucide-react";
835
- import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1080
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
836
1081
  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";
837
1082
  function DropdownMenu(props) {
838
- return /* @__PURE__ */ jsx22(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1083
+ return /* @__PURE__ */ jsx24(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
839
1084
  }
840
1085
  function DropdownMenuTrigger(props) {
841
- return /* @__PURE__ */ jsx22(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1086
+ return /* @__PURE__ */ jsx24(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
842
1087
  }
843
1088
  function DropdownMenuContent({
844
1089
  className,
@@ -846,14 +1091,14 @@ function DropdownMenuContent({
846
1091
  sideOffset = 4,
847
1092
  ...props
848
1093
  }) {
849
- return /* @__PURE__ */ jsx22(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx22(
1094
+ return /* @__PURE__ */ jsx24(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx24(
850
1095
  MenuPrimitive.Positioner,
851
1096
  {
852
1097
  "data-slot": "dropdown-menu-positioner",
853
1098
  className: "z-50",
854
1099
  align,
855
1100
  sideOffset,
856
- children: /* @__PURE__ */ jsx22(
1101
+ children: /* @__PURE__ */ jsx24(
857
1102
  MenuPrimitive.Popup,
858
1103
  {
859
1104
  "data-slot": "dropdown-menu-content",
@@ -871,7 +1116,7 @@ function DropdownMenuItem({
871
1116
  className,
872
1117
  ...props
873
1118
  }) {
874
- return /* @__PURE__ */ jsx22(
1119
+ return /* @__PURE__ */ jsx24(
875
1120
  MenuPrimitive.Item,
876
1121
  {
877
1122
  "data-slot": "dropdown-menu-item",
@@ -883,7 +1128,7 @@ function DropdownMenuItem({
883
1128
 
884
1129
  // src/components/ReportScenario.tsx
885
1130
  import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
886
- import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
1131
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
887
1132
  var STATUS_META = {
888
1133
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
889
1134
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -899,7 +1144,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
899
1144
  const collapsible = collapse !== null && !hideTitle;
900
1145
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
901
1146
  const actions = useScenarioActions();
902
- return /* @__PURE__ */ jsxs15(
1147
+ return /* @__PURE__ */ jsxs16(
903
1148
  Card,
904
1149
  {
905
1150
  id: scenario.id,
@@ -907,10 +1152,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
907
1152
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
908
1153
  className: "gap-0 py-0",
909
1154
  children: [
910
- /* @__PURE__ */ jsxs15(CardHeader, { className: "px-4 py-3", children: [
911
- /* @__PURE__ */ jsxs15("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
912
- hideTitle ? null : /* @__PURE__ */ jsxs15("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
913
- collapsible ? /* @__PURE__ */ jsx23(
1155
+ /* @__PURE__ */ jsxs16(CardHeader, { className: "px-4 py-3", children: [
1156
+ /* @__PURE__ */ jsxs16("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1157
+ hideTitle ? null : /* @__PURE__ */ jsxs16("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1158
+ collapsible ? /* @__PURE__ */ jsx25(
914
1159
  "button",
915
1160
  {
916
1161
  type: "button",
@@ -919,49 +1164,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
919
1164
  "aria-controls": bodyId,
920
1165
  "aria-label": `Toggle ${scenario.title}`,
921
1166
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
922
- children: /* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1167
+ children: /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
923
1168
  }
924
1169
  ) : null,
925
- /* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
926
- /* @__PURE__ */ jsx23("span", { children: scenario.title })
1170
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1171
+ /* @__PURE__ */ jsx25("span", { children: scenario.title })
927
1172
  ] }),
928
- /* @__PURE__ */ jsxs15("div", { className: "flex shrink-0 items-center gap-2", children: [
929
- /* @__PURE__ */ jsx23(ScenarioRunHistory, { scenarioId: scenario.id }),
930
- scenario.durationMs > 0 ? /* @__PURE__ */ jsx23("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
931
- meta.showPill ? /* @__PURE__ */ jsx23(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx23("span", { className: "sr-only", children: `Status: ${label}` }),
932
- actions ? /* @__PURE__ */ jsxs15(DropdownMenu, { children: [
933
- /* @__PURE__ */ jsx23(
1173
+ /* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-2", children: [
1174
+ /* @__PURE__ */ jsx25(ScenarioRunHistory, { scenarioId: scenario.id }),
1175
+ scenario.durationMs > 0 ? /* @__PURE__ */ jsx25("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1176
+ meta.showPill ? /* @__PURE__ */ jsx25(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx25("span", { className: "sr-only", children: `Status: ${label}` }),
1177
+ actions ? /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1178
+ /* @__PURE__ */ jsx25(
934
1179
  DropdownMenuTrigger,
935
1180
  {
936
1181
  "aria-label": `Actions for ${scenario.title}`,
937
1182
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
938
- children: /* @__PURE__ */ jsx23(MoreHorizontalIcon, { "aria-hidden": true })
1183
+ children: /* @__PURE__ */ jsx25(MoreHorizontalIcon, { "aria-hidden": true })
939
1184
  }
940
1185
  ),
941
- /* @__PURE__ */ jsxs15(DropdownMenuContent, { align: "end", children: [
942
- /* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
943
- /* @__PURE__ */ jsx23(LinkIcon, {}),
1186
+ /* @__PURE__ */ jsxs16(DropdownMenuContent, { align: "end", children: [
1187
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1188
+ /* @__PURE__ */ jsx25(LinkIcon, {}),
944
1189
  " Copy link"
945
1190
  ] }),
946
- /* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
947
- /* @__PURE__ */ jsx23(FileTextIcon, {}),
1191
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1192
+ /* @__PURE__ */ jsx25(FileTextIcon, {}),
948
1193
  " Copy as Markdown"
949
1194
  ] }),
950
- /* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
951
- /* @__PURE__ */ jsx23(SparklesIcon, {}),
1195
+ /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1196
+ /* @__PURE__ */ jsx25(SparklesIcon, {}),
952
1197
  " Explain with AI"
953
1198
  ] })
954
1199
  ] })
955
1200
  ] }) : null
956
1201
  ] })
957
1202
  ] }),
958
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs15("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
959
- scenario.tags.map((t) => /* @__PURE__ */ jsx23("li", { children: /* @__PURE__ */ jsx23(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
960
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx23("li", { children: ticket.url ? /* @__PURE__ */ jsx23("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx23(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx23(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1203
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs16("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1204
+ scenario.tags.map((t) => /* @__PURE__ */ jsx25("li", { children: /* @__PURE__ */ jsx25(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1205
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx25("li", { children: ticket.url ? /* @__PURE__ */ jsx25("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx25(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx25(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
961
1206
  ] }) : null
962
1207
  ] }),
963
- /* @__PURE__ */ jsxs15(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
964
- scenario.errorMessage ? /* @__PURE__ */ jsx23(
1208
+ /* @__PURE__ */ jsxs16(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1209
+ scenario.errorMessage ? /* @__PURE__ */ jsx25(
965
1210
  "pre",
966
1211
  {
967
1212
  role: "alert",
@@ -969,10 +1214,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
969
1214
  children: scenario.errorMessage
970
1215
  }
971
1216
  ) : null,
972
- /* @__PURE__ */ jsx23(ReportSteps, { scenario }),
973
- scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx23("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx23(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
974
- /* @__PURE__ */ jsx23(ReportAttachments, { attachments: scenario.attachments }),
975
- /* @__PURE__ */ jsx23(ReportTrace, { spans: scenario.otelSpans })
1217
+ /* @__PURE__ */ jsx25(ReportStoryboard, { scenario }),
1218
+ /* @__PURE__ */ jsx25(ReportSteps, { scenario }),
1219
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx25("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx25(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1220
+ /* @__PURE__ */ jsx25(ReportAttachments, { attachments: scenario.attachments }),
1221
+ /* @__PURE__ */ jsx25(ReportTrace, { spans: scenario.otelSpans })
976
1222
  ] })
977
1223
  ]
978
1224
  }
@@ -980,13 +1226,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
980
1226
  }
981
1227
 
982
1228
  // src/components/ReportScenarioList.tsx
983
- import { Fragment as Fragment8, jsx as jsx24 } from "react/jsx-runtime";
1229
+ import { Fragment as Fragment8, jsx as jsx26 } from "react/jsx-runtime";
984
1230
  function ReportScenarioList({ feature }) {
985
- return /* @__PURE__ */ jsx24(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx24(ReportScenario, { scenario }, scenario.id)) });
1231
+ return /* @__PURE__ */ jsx26(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx26(ReportScenario, { scenario }, scenario.id)) });
986
1232
  }
987
1233
 
988
1234
  // src/components/ReportFeature.tsx
989
- import { Fragment as Fragment9, jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1235
+ import { Fragment as Fragment9, jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
990
1236
  function ReportFeature({ feature }) {
991
1237
  const titleId = `${feature.id}-title`;
992
1238
  const bodyId = `${feature.id}-body`;
@@ -997,7 +1243,7 @@ function ReportFeature({ feature }) {
997
1243
  return (
998
1244
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
999
1245
  // layer of nesting so the scenario cards below read as the primary unit.
1000
- /* @__PURE__ */ jsxs16(
1246
+ /* @__PURE__ */ jsxs17(
1001
1247
  "section",
1002
1248
  {
1003
1249
  id: feature.id,
@@ -1005,9 +1251,9 @@ function ReportFeature({ feature }) {
1005
1251
  "aria-labelledby": titleId,
1006
1252
  className: "flex flex-col gap-3",
1007
1253
  children: [
1008
- /* @__PURE__ */ jsxs16("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1009
- /* @__PURE__ */ jsxs16("div", { className: "min-w-0", children: [
1010
- /* @__PURE__ */ jsx25("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs16(
1254
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1255
+ /* @__PURE__ */ jsxs17("div", { className: "min-w-0", children: [
1256
+ /* @__PURE__ */ jsx27("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs17(
1011
1257
  "button",
1012
1258
  {
1013
1259
  type: "button",
@@ -1016,31 +1262,31 @@ function ReportFeature({ feature }) {
1016
1262
  "aria-controls": bodyId,
1017
1263
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1018
1264
  children: [
1019
- /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1020
- /* @__PURE__ */ jsx25("span", { className: "truncate", children: feature.title })
1265
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1266
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: feature.title })
1021
1267
  ]
1022
1268
  }
1023
1269
  ) : feature.title }),
1024
- /* @__PURE__ */ jsx25("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1270
+ /* @__PURE__ */ jsx27("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1025
1271
  ] }),
1026
- /* @__PURE__ */ jsxs16(
1272
+ /* @__PURE__ */ jsxs17(
1027
1273
  "p",
1028
1274
  {
1029
1275
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1030
1276
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1031
1277
  children: [
1032
- /* @__PURE__ */ jsxs16("span", { children: [
1278
+ /* @__PURE__ */ jsxs17("span", { children: [
1033
1279
  s.passed,
1034
1280
  " passed"
1035
1281
  ] }),
1036
- /* @__PURE__ */ jsx25("span", { "aria-hidden": true, children: "\xB7" }),
1037
- /* @__PURE__ */ jsxs16("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1282
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1283
+ /* @__PURE__ */ jsxs17("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1038
1284
  s.failed,
1039
1285
  " failed"
1040
1286
  ] }),
1041
- skipped > 0 ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
1042
- /* @__PURE__ */ jsx25("span", { "aria-hidden": true, children: "\xB7" }),
1043
- /* @__PURE__ */ jsxs16("span", { children: [
1287
+ skipped > 0 ? /* @__PURE__ */ jsxs17(Fragment9, { children: [
1288
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1289
+ /* @__PURE__ */ jsxs17("span", { children: [
1044
1290
  skipped,
1045
1291
  " skipped"
1046
1292
  ] })
@@ -1049,7 +1295,7 @@ function ReportFeature({ feature }) {
1049
1295
  }
1050
1296
  )
1051
1297
  ] }),
1052
- /* @__PURE__ */ jsx25("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx25(ReportScenarioList, { feature }) })
1298
+ /* @__PURE__ */ jsx27("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx27(ReportScenarioList, { feature }) })
1053
1299
  ]
1054
1300
  }
1055
1301
  )
@@ -1057,17 +1303,17 @@ function ReportFeature({ feature }) {
1057
1303
  }
1058
1304
 
1059
1305
  // src/components/ReportFeatureList.tsx
1060
- import { jsx as jsx26 } from "react/jsx-runtime";
1306
+ import { jsx as jsx28 } from "react/jsx-runtime";
1061
1307
  function ReportFeatureList() {
1062
1308
  const report = useReport();
1063
- return /* @__PURE__ */ jsx26("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx26(ReportFeature, { feature }, feature.id)) });
1309
+ return /* @__PURE__ */ jsx28("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx28(ReportFeature, { feature }, feature.id)) });
1064
1310
  }
1065
1311
 
1066
1312
  // src/components/ui/empty.tsx
1067
1313
  import { cva as cva2 } from "class-variance-authority";
1068
- import { jsx as jsx27 } from "react/jsx-runtime";
1314
+ import { jsx as jsx29 } from "react/jsx-runtime";
1069
1315
  function Empty({ className, ...props }) {
1070
- return /* @__PURE__ */ jsx27(
1316
+ return /* @__PURE__ */ jsx29(
1071
1317
  "div",
1072
1318
  {
1073
1319
  "data-slot": "empty",
@@ -1080,7 +1326,7 @@ function Empty({ className, ...props }) {
1080
1326
  );
1081
1327
  }
1082
1328
  function EmptyHeader({ className, ...props }) {
1083
- return /* @__PURE__ */ jsx27(
1329
+ return /* @__PURE__ */ jsx29(
1084
1330
  "div",
1085
1331
  {
1086
1332
  "data-slot": "empty-header",
@@ -1107,7 +1353,7 @@ var emptyMediaVariants = cva2(
1107
1353
  }
1108
1354
  );
1109
1355
  function EmptyTitle({ className, ...props }) {
1110
- return /* @__PURE__ */ jsx27(
1356
+ return /* @__PURE__ */ jsx29(
1111
1357
  "div",
1112
1358
  {
1113
1359
  "data-slot": "empty-title",
@@ -1118,16 +1364,16 @@ function EmptyTitle({ className, ...props }) {
1118
1364
  }
1119
1365
 
1120
1366
  // src/components/ReportEmpty.tsx
1121
- import { jsx as jsx28 } from "react/jsx-runtime";
1367
+ import { jsx as jsx30 } from "react/jsx-runtime";
1122
1368
  function ReportEmpty({ message }) {
1123
- return /* @__PURE__ */ jsx28(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx28(EmptyHeader, { children: /* @__PURE__ */ jsx28(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1369
+ return /* @__PURE__ */ jsx30(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx30(EmptyHeader, { children: /* @__PURE__ */ jsx30(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1124
1370
  }
1125
1371
 
1126
1372
  // src/components/ReportSummary.tsx
1127
- import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
1373
+ import { jsx as jsx31, jsxs as jsxs18 } from "react/jsx-runtime";
1128
1374
  function ReportSummary({ className }) {
1129
1375
  const report = useReport();
1130
- return /* @__PURE__ */ jsx29(
1376
+ return /* @__PURE__ */ jsx31(
1131
1377
  ReportSummaryView,
1132
1378
  {
1133
1379
  summary: report.summary,
@@ -1153,7 +1399,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1153
1399
  if (summary.pending > 0) {
1154
1400
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1155
1401
  }
1156
- return /* @__PURE__ */ jsx29(
1402
+ return /* @__PURE__ */ jsx31(
1157
1403
  "dl",
1158
1404
  {
1159
1405
  className: cn(
@@ -1166,17 +1412,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1166
1412
  "aria-label": ariaLabel,
1167
1413
  children: cards.map((c) => {
1168
1414
  const tone = TONE[c.status];
1169
- return /* @__PURE__ */ jsxs17(
1415
+ return /* @__PURE__ */ jsxs18(
1170
1416
  "div",
1171
1417
  {
1172
1418
  "data-status": c.status,
1173
1419
  className: "rounded-lg border bg-card px-4 py-3",
1174
1420
  children: [
1175
- /* @__PURE__ */ jsxs17("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1176
- /* @__PURE__ */ jsx29("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1421
+ /* @__PURE__ */ jsxs18("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1422
+ /* @__PURE__ */ jsx31("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1177
1423
  c.label
1178
1424
  ] }),
1179
- /* @__PURE__ */ jsx29("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1425
+ /* @__PURE__ */ jsx31("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1180
1426
  ]
1181
1427
  },
1182
1428
  c.status
@@ -1217,7 +1463,7 @@ function prUrl(ci) {
1217
1463
  }
1218
1464
 
1219
1465
  // src/components/ReportMeta.tsx
1220
- import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
1466
+ import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
1221
1467
  function ReportMeta() {
1222
1468
  const report = useReport();
1223
1469
  const items = [];
@@ -1240,13 +1486,13 @@ function ReportMeta() {
1240
1486
  const short = sha.slice(0, 8);
1241
1487
  items.push({
1242
1488
  label: "Commit",
1243
- value: url ? /* @__PURE__ */ jsx30("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1489
+ value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1244
1490
  });
1245
1491
  }
1246
1492
  if (report.ci) {
1247
1493
  items.push({
1248
1494
  label: "CI",
1249
- value: report.ci.url ? /* @__PURE__ */ jsxs18("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1495
+ value: report.ci.url ? /* @__PURE__ */ jsxs19("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1250
1496
  ciDisplayName(report.ci.name),
1251
1497
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1252
1498
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1257,7 +1503,7 @@ function ReportMeta() {
1257
1503
  const label = `#${report.ci.prNumber}`;
1258
1504
  items.push({
1259
1505
  label: "PR",
1260
- value: url ? /* @__PURE__ */ jsx30("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1506
+ value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1261
1507
  });
1262
1508
  }
1263
1509
  if (items.length === 0) return null;
@@ -1265,48 +1511,48 @@ function ReportMeta() {
1265
1511
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1266
1512
  // collapsed disclosure so it doesn't push the result down. Native <details>
1267
1513
  // so it works with no JS (the SSR/no-hydration report still toggles).
1268
- /* @__PURE__ */ jsxs18("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1269
- /* @__PURE__ */ jsxs18("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: [
1270
- /* @__PURE__ */ jsx30("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1514
+ /* @__PURE__ */ jsxs19("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1515
+ /* @__PURE__ */ jsxs19("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: [
1516
+ /* @__PURE__ */ jsx32("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1271
1517
  "Run details"
1272
1518
  ] }),
1273
- /* @__PURE__ */ jsx30("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__ */ jsxs18("div", { className: "flex items-baseline gap-1.5", children: [
1274
- /* @__PURE__ */ jsx30("dt", { className: "font-medium", children: item.label }),
1275
- /* @__PURE__ */ jsx30("dd", { className: "font-mono text-foreground", children: item.value })
1519
+ /* @__PURE__ */ jsx32("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__ */ jsxs19("div", { className: "flex items-baseline gap-1.5", children: [
1520
+ /* @__PURE__ */ jsx32("dt", { className: "font-medium", children: item.label }),
1521
+ /* @__PURE__ */ jsx32("dd", { className: "font-mono text-foreground", children: item.value })
1276
1522
  ] }, item.label)) })
1277
1523
  ] })
1278
1524
  );
1279
1525
  }
1280
1526
 
1281
1527
  // src/components/ReportSchemaError.tsx
1282
- import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
1528
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1283
1529
  function ReportSchemaError({ error }) {
1284
- return /* @__PURE__ */ jsxs19("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1285
- /* @__PURE__ */ jsx31("p", { children: /* @__PURE__ */ jsx31("strong", { children: "Report could not be displayed." }) }),
1286
- /* @__PURE__ */ jsx31("p", { children: error.message }),
1287
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs19("p", { children: [
1530
+ return /* @__PURE__ */ jsxs20("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1531
+ /* @__PURE__ */ jsx33("p", { children: /* @__PURE__ */ jsx33("strong", { children: "Report could not be displayed." }) }),
1532
+ /* @__PURE__ */ jsx33("p", { children: error.message }),
1533
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs20("p", { children: [
1288
1534
  "The report bundle is newer than this version of ",
1289
- /* @__PURE__ */ jsx31("code", { children: "executable-stories-react" }),
1535
+ /* @__PURE__ */ jsx33("code", { children: "executable-stories-react" }),
1290
1536
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1291
1537
  ] }) : null,
1292
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs19("details", { children: [
1293
- /* @__PURE__ */ jsxs19("summary", { children: [
1538
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs20("details", { children: [
1539
+ /* @__PURE__ */ jsxs20("summary", { children: [
1294
1540
  error.issues.length,
1295
1541
  " validation issue",
1296
1542
  error.issues.length === 1 ? "" : "s"
1297
1543
  ] }),
1298
- /* @__PURE__ */ jsx31("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1544
+ /* @__PURE__ */ jsx33("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1299
1545
  ] }) : null
1300
1546
  ] });
1301
1547
  }
1302
1548
 
1303
1549
  // src/components/ReportShell.tsx
1304
- import { Fragment as Fragment10, jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
1550
+ import { Fragment as Fragment10, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1305
1551
  function ReportTitleBlock({ title }) {
1306
- return /* @__PURE__ */ jsxs20(Fragment10, { children: [
1307
- /* @__PURE__ */ jsx32("h1", { children: title ?? "Story Report" }),
1308
- /* @__PURE__ */ jsx32(ReportSummary, {}),
1309
- /* @__PURE__ */ jsx32(ReportMeta, {})
1552
+ return /* @__PURE__ */ jsxs21(Fragment10, { children: [
1553
+ /* @__PURE__ */ jsx34("h1", { children: title ?? "Story Report" }),
1554
+ /* @__PURE__ */ jsx34(ReportSummary, {}),
1555
+ /* @__PURE__ */ jsx34(ReportMeta, {})
1310
1556
  ] });
1311
1557
  }
1312
1558
  function ReportErrorShell({
@@ -1315,22 +1561,22 @@ function ReportErrorShell({
1315
1561
  title,
1316
1562
  dataTheme
1317
1563
  }) {
1318
- return /* @__PURE__ */ jsx32("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx32(ReportSchemaError, { error }) });
1564
+ return /* @__PURE__ */ jsx34("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx34(ReportSchemaError, { error }) });
1319
1565
  }
1320
1566
 
1321
1567
  // src/components/Report.tsx
1322
- import { jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
1568
+ import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1323
1569
  function Report(props) {
1324
1570
  const { report, customRenderers, renderers, className, title, dataTheme } = props;
1325
1571
  const result = unwrapReport(report);
1326
1572
  if (!result.ok) {
1327
- return /* @__PURE__ */ jsx33(ReportErrorShell, { error: result.error, className, title, dataTheme });
1573
+ return /* @__PURE__ */ jsx35(ReportErrorShell, { error: result.error, className, title, dataTheme });
1328
1574
  }
1329
1575
  const data = result.data;
1330
1576
  const hasContent = data.features.length > 0;
1331
- return /* @__PURE__ */ jsx33(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs21("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1332
- /* @__PURE__ */ jsx33("header", { className: "es-report-header", children: /* @__PURE__ */ jsx33(ReportTitleBlock, { title }) }),
1333
- hasContent ? /* @__PURE__ */ jsx33(ReportFeatureList, {}) : /* @__PURE__ */ jsx33(ReportEmpty, {})
1577
+ return /* @__PURE__ */ jsx35(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs22("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1578
+ /* @__PURE__ */ jsx35("header", { className: "es-report-header", children: /* @__PURE__ */ jsx35(ReportTitleBlock, { title }) }),
1579
+ hasContent ? /* @__PURE__ */ jsx35(ReportFeatureList, {}) : /* @__PURE__ */ jsx35(ReportEmpty, {})
1334
1580
  ] }) });
1335
1581
  }
1336
1582