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