executable-stories-react 0.7.0 → 0.9.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
@@ -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,12 +544,72 @@ 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
+
539
613
  // ../executable-stories-core/dist/storyboard.js
540
614
  function findScreenshot(entries) {
541
615
  for (const entry of entries) {
@@ -547,26 +621,55 @@ function findScreenshot(entries) {
547
621
  }
548
622
  return void 0;
549
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 ?? "";
550
632
  function extractStoryboardFrames(scenario) {
551
633
  const frames = [];
634
+ const laneOrder = [];
635
+ const previous = /* @__PURE__ */ new Map();
552
636
  for (const step of scenario.steps) {
553
637
  const shot = findScreenshot(step.docEntries);
554
- if (!shot) continue;
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
+ }
555
658
  frames.push({
556
659
  stepId: step.id,
557
660
  stepIndex: step.index,
558
661
  keyword: step.keyword,
559
662
  text: step.text,
560
663
  status: step.status,
561
- path: shot.path,
562
- ...shot.alt !== void 0 && { alt: shot.alt }
664
+ ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
665
+ states
563
666
  });
564
667
  }
565
668
  return frames;
566
669
  }
567
670
 
568
671
  // src/components/ReportStoryboard.tsx
569
- var import_jsx_runtime18 = require("react/jsx-runtime");
672
+ var import_jsx_runtime19 = require("react/jsx-runtime");
570
673
  var FRAME_GLYPH = {
571
674
  passed: "\u2713",
572
675
  failed: "\u2717",
@@ -582,55 +685,114 @@ var FRAME_GLYPH_COLOR = {
582
685
  function ReportStoryboard({ scenario }) {
583
686
  const frames = extractStoryboardFrames(scenario);
584
687
  if (frames.length < 2) return null;
585
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
586
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
587
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ol", { className: "flex gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(StoryboardFrameItem, { frame }, frame.stepId)) })
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)) })
588
691
  ] });
589
692
  }
590
693
  function StoryboardFrameItem({ frame }) {
591
- const src = isLocalFsPath(frame.path) ? void 0 : safeImageUrl(frame.path);
592
694
  const label = `${frame.keyword} ${frame.text}`;
593
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("li", { className: "w-40 shrink-0", children: [
594
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
595
- "a",
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",
596
761
  {
597
- href: `#${frame.stepId}`,
598
- "aria-label": `Jump to step: ${label}`,
599
- className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
600
- children: src ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
601
- "img",
602
- {
603
- src,
604
- alt: frame.alt ?? label,
605
- loading: "lazy",
606
- className: "h-24 w-full bg-muted object-cover object-top"
607
- }
608
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
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
+ ]
609
779
  }
610
- ),
611
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
612
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
613
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "min-w-0", children: [
614
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
615
- " ",
616
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-muted-foreground", children: frame.alt ?? frame.text })
617
- ] })
618
- ] })
780
+ )
619
781
  ] });
620
782
  }
621
783
 
622
784
  // src/components/ReportAttachments.tsx
623
- var import_jsx_runtime19 = require("react/jsx-runtime");
785
+ var import_jsx_runtime20 = require("react/jsx-runtime");
624
786
  function ReportAttachments({ attachments }) {
625
787
  if (attachments.length === 0) return null;
626
788
  return (
627
789
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
628
790
  // blocks don't create duplicate "Attachments" region landmarks.
629
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
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) => {
630
792
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
631
793
  const isImage = att.mediaType.startsWith("image/");
632
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
633
- /* @__PURE__ */ (0, import_jsx_runtime19.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)(
634
796
  "img",
635
797
  {
636
798
  src: dataUri,
@@ -639,8 +801,8 @@ function ReportAttachments({ attachments }) {
639
801
  className: "max-w-full rounded-md border border-border"
640
802
  }
641
803
  ),
642
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
643
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.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)(
644
806
  "a",
645
807
  {
646
808
  href: dataUri,
@@ -649,7 +811,7 @@ function ReportAttachments({ attachments }) {
649
811
  children: [
650
812
  att.name,
651
813
  " ",
652
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "text-muted-foreground", children: [
814
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-muted-foreground", children: [
653
815
  "(",
654
816
  att.mediaType,
655
817
  ")"
@@ -713,15 +875,15 @@ function toTraceRows(spans) {
713
875
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
714
876
  const rows = [];
715
877
  const seen = /* @__PURE__ */ new Set();
716
- const walk = (node, depth) => {
878
+ const walk2 = (node, depth) => {
717
879
  if (seen.has(node.span.spanId)) return;
718
880
  seen.add(node.span.spanId);
719
881
  rows.push({ ...node.span, depth });
720
- for (const child of node.children) walk(child, depth + 1);
882
+ for (const child of node.children) walk2(child, depth + 1);
721
883
  };
722
- for (const root of roots) walk(root, 0);
884
+ for (const root of roots) walk2(root, 0);
723
885
  for (const node of byId.values()) {
724
- if (!seen.has(node.span.spanId)) walk(node, 0);
886
+ if (!seen.has(node.span.spanId)) walk2(node, 0);
725
887
  }
726
888
  return rows;
727
889
  }
@@ -737,7 +899,7 @@ function traceWindow(rows) {
737
899
  }
738
900
 
739
901
  // src/components/ReportTrace.tsx
740
- var import_jsx_runtime20 = require("react/jsx-runtime");
902
+ var import_jsx_runtime21 = require("react/jsx-runtime");
741
903
  var BAR_COLOR = {
742
904
  ok: "bg-pass",
743
905
  error: "bg-fail",
@@ -748,8 +910,8 @@ function ReportTrace({ spans }) {
748
910
  const rows = toTraceRows(spans);
749
911
  if (rows.length === 0) return null;
750
912
  const { minStartMs, totalMs } = traceWindow(rows);
751
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
752
- /* @__PURE__ */ (0, import_jsx_runtime20.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: [
753
915
  "Trace (",
754
916
  rows.length,
755
917
  " span",
@@ -758,11 +920,11 @@ function ReportTrace({ spans }) {
758
920
  formatDuration(totalMs),
759
921
  ")"
760
922
  ] }),
761
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
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) => {
762
924
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
763
925
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
764
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("li", { className: "flex items-center gap-2", children: [
765
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
926
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2", children: [
927
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
766
928
  "span",
767
929
  {
768
930
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -771,14 +933,14 @@ function ReportTrace({ spans }) {
771
933
  children: row.name
772
934
  }
773
935
  ),
774
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
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)(
775
937
  "span",
776
938
  {
777
939
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
778
940
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
779
941
  }
780
942
  ) }),
781
- /* @__PURE__ */ (0, import_jsx_runtime20.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) })
782
944
  ] }, `${row.spanId}-${i}`);
783
945
  }) })
784
946
  ] });
@@ -829,7 +991,7 @@ function flakinessOf(entries) {
829
991
  }
830
992
 
831
993
  // src/components/ScenarioRunHistory.tsx
832
- var import_jsx_runtime21 = require("react/jsx-runtime");
994
+ var import_jsx_runtime22 = require("react/jsx-runtime");
833
995
  var MAX_DOTS = 10;
834
996
  var DOT_COLOR = {
835
997
  passed: "bg-pass",
@@ -849,16 +1011,16 @@ function ScenarioRunHistory({ scenarioId }) {
849
1011
  const recent = entries.slice(-MAX_DOTS);
850
1012
  const flaky = flakinessOf(recent) === "flaky";
851
1013
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
852
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
853
- flaky ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
854
- /* @__PURE__ */ (0, import_jsx_runtime21.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)(
855
1017
  "span",
856
1018
  {
857
1019
  role: "img",
858
1020
  "aria-label": `Run history: ${summary}`,
859
1021
  title: summary,
860
1022
  className: "flex shrink-0 items-center gap-[3px]",
861
- children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1023
+ children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
862
1024
  "span",
863
1025
  {
864
1026
  "aria-hidden": true,
@@ -897,9 +1059,9 @@ function useScenarioActions() {
897
1059
 
898
1060
  // src/components/ui/card.tsx
899
1061
  var React2 = require("react");
900
- var import_jsx_runtime22 = require("react/jsx-runtime");
1062
+ var import_jsx_runtime23 = require("react/jsx-runtime");
901
1063
  function Card({ className, ...props }) {
902
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1064
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
903
1065
  "div",
904
1066
  {
905
1067
  "data-slot": "card",
@@ -912,7 +1074,7 @@ function Card({ className, ...props }) {
912
1074
  );
913
1075
  }
914
1076
  function CardHeader({ className, ...props }) {
915
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1077
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
916
1078
  "div",
917
1079
  {
918
1080
  "data-slot": "card-header",
@@ -925,7 +1087,7 @@ function CardHeader({ className, ...props }) {
925
1087
  );
926
1088
  }
927
1089
  function CardContent({ className, ...props }) {
928
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1090
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
929
1091
  "div",
930
1092
  {
931
1093
  "data-slot": "card-content",
@@ -939,13 +1101,13 @@ function CardContent({ className, ...props }) {
939
1101
  var React3 = require("react");
940
1102
  var import_menu = require("@base-ui/react/menu");
941
1103
  var import_lucide_react = require("lucide-react");
942
- var import_jsx_runtime23 = require("react/jsx-runtime");
1104
+ var import_jsx_runtime24 = require("react/jsx-runtime");
943
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";
944
1106
  function DropdownMenu(props) {
945
- return /* @__PURE__ */ (0, import_jsx_runtime23.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 });
946
1108
  }
947
1109
  function DropdownMenuTrigger(props) {
948
- return /* @__PURE__ */ (0, import_jsx_runtime23.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 });
949
1111
  }
950
1112
  function DropdownMenuContent({
951
1113
  className,
@@ -953,14 +1115,14 @@ function DropdownMenuContent({
953
1115
  sideOffset = 4,
954
1116
  ...props
955
1117
  }) {
956
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1118
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
957
1119
  import_menu.Menu.Positioner,
958
1120
  {
959
1121
  "data-slot": "dropdown-menu-positioner",
960
1122
  className: "z-50",
961
1123
  align,
962
1124
  sideOffset,
963
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1125
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
964
1126
  import_menu.Menu.Popup,
965
1127
  {
966
1128
  "data-slot": "dropdown-menu-content",
@@ -978,7 +1140,7 @@ function DropdownMenuItem({
978
1140
  className,
979
1141
  ...props
980
1142
  }) {
981
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1143
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
982
1144
  import_menu.Menu.Item,
983
1145
  {
984
1146
  "data-slot": "dropdown-menu-item",
@@ -990,7 +1152,7 @@ function DropdownMenuItem({
990
1152
 
991
1153
  // src/components/ReportScenario.tsx
992
1154
  var import_lucide_react2 = require("lucide-react");
993
- var import_jsx_runtime24 = require("react/jsx-runtime");
1155
+ var import_jsx_runtime25 = require("react/jsx-runtime");
994
1156
  var STATUS_META = {
995
1157
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
996
1158
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1006,7 +1168,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1006
1168
  const collapsible = collapse !== null && !hideTitle;
1007
1169
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1008
1170
  const actions = useScenarioActions();
1009
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1171
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1010
1172
  Card,
1011
1173
  {
1012
1174
  id: scenario.id,
@@ -1014,10 +1176,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1014
1176
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1015
1177
  className: "gap-0 py-0",
1016
1178
  children: [
1017
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1018
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1019
- hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1020
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime24.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)(
1021
1183
  "button",
1022
1184
  {
1023
1185
  type: "button",
@@ -1026,49 +1188,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1026
1188
  "aria-controls": bodyId,
1027
1189
  "aria-label": `Toggle ${scenario.title}`,
1028
1190
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1029
- children: /* @__PURE__ */ (0, import_jsx_runtime24.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" })
1030
1192
  }
1031
1193
  ) : null,
1032
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1033
- /* @__PURE__ */ (0, import_jsx_runtime24.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 })
1034
1196
  ] }),
1035
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1036
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1037
- scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1038
- meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1039
- actions ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenu, { children: [
1040
- /* @__PURE__ */ (0, import_jsx_runtime24.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)(
1041
1203
  DropdownMenuTrigger,
1042
1204
  {
1043
1205
  "aria-label": `Actions for ${scenario.title}`,
1044
1206
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1045
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1207
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1046
1208
  }
1047
1209
  ),
1048
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuContent, { align: "end", children: [
1049
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1050
- /* @__PURE__ */ (0, import_jsx_runtime24.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, {}),
1051
1213
  " Copy link"
1052
1214
  ] }),
1053
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1054
- /* @__PURE__ */ (0, import_jsx_runtime24.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, {}),
1055
1217
  " Copy as Markdown"
1056
1218
  ] }),
1057
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime24.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, {}),
1059
1221
  " Explain with AI"
1060
1222
  ] })
1061
1223
  ] })
1062
1224
  ] }) : null
1063
1225
  ] })
1064
1226
  ] }),
1065
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1066
- scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1067
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
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}`))
1068
1230
  ] }) : null
1069
1231
  ] }),
1070
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1071
- scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime24.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)(
1072
1234
  "pre",
1073
1235
  {
1074
1236
  role: "alert",
@@ -1076,11 +1238,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1076
1238
  children: scenario.errorMessage
1077
1239
  }
1078
1240
  ) : null,
1079
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportStoryboard, { scenario }),
1080
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportSteps, { scenario }),
1081
- scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1082
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1083
- /* @__PURE__ */ (0, import_jsx_runtime24.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 })
1084
1246
  ] })
1085
1247
  ]
1086
1248
  }
@@ -1088,13 +1250,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1088
1250
  }
1089
1251
 
1090
1252
  // src/components/ReportScenarioList.tsx
1091
- var import_jsx_runtime25 = require("react/jsx-runtime");
1253
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1092
1254
  function ReportScenarioList({ feature }) {
1093
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportScenario, { scenario }, scenario.id)) });
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)) });
1094
1256
  }
1095
1257
 
1096
1258
  // src/components/ReportFeature.tsx
1097
- var import_jsx_runtime26 = require("react/jsx-runtime");
1259
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1098
1260
  function ReportFeature({ feature }) {
1099
1261
  const titleId = `${feature.id}-title`;
1100
1262
  const bodyId = `${feature.id}-body`;
@@ -1105,7 +1267,7 @@ function ReportFeature({ feature }) {
1105
1267
  return (
1106
1268
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1107
1269
  // layer of nesting so the scenario cards below read as the primary unit.
1108
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1270
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1109
1271
  "section",
1110
1272
  {
1111
1273
  id: feature.id,
@@ -1113,9 +1275,9 @@ function ReportFeature({ feature }) {
1113
1275
  "aria-labelledby": titleId,
1114
1276
  className: "flex flex-col gap-3",
1115
1277
  children: [
1116
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1117
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "min-w-0", children: [
1118
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
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)(
1119
1281
  "button",
1120
1282
  {
1121
1283
  type: "button",
@@ -1124,31 +1286,31 @@ function ReportFeature({ feature }) {
1124
1286
  "aria-controls": bodyId,
1125
1287
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1126
1288
  children: [
1127
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1128
- /* @__PURE__ */ (0, import_jsx_runtime26.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 })
1129
1291
  ]
1130
1292
  }
1131
1293
  ) : feature.title }),
1132
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
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 })
1133
1295
  ] }),
1134
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1296
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1135
1297
  "p",
1136
1298
  {
1137
1299
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1138
1300
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1139
1301
  children: [
1140
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { children: [
1302
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
1141
1303
  s.passed,
1142
1304
  " passed"
1143
1305
  ] }),
1144
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1145
- /* @__PURE__ */ (0, import_jsx_runtime26.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: [
1146
1308
  s.failed,
1147
1309
  " failed"
1148
1310
  ] }),
1149
- skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
1150
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1151
- /* @__PURE__ */ (0, import_jsx_runtime26.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: [
1152
1314
  skipped,
1153
1315
  " skipped"
1154
1316
  ] })
@@ -1157,7 +1319,7 @@ function ReportFeature({ feature }) {
1157
1319
  }
1158
1320
  )
1159
1321
  ] }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportScenarioList, { feature }) })
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 }) })
1161
1323
  ]
1162
1324
  }
1163
1325
  )
@@ -1165,17 +1327,17 @@ function ReportFeature({ feature }) {
1165
1327
  }
1166
1328
 
1167
1329
  // src/components/ReportFeatureList.tsx
1168
- var import_jsx_runtime27 = require("react/jsx-runtime");
1330
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1169
1331
  function ReportFeatureList() {
1170
1332
  const report = useReport();
1171
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportFeature, { feature }, feature.id)) });
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)) });
1172
1334
  }
1173
1335
 
1174
1336
  // src/components/ui/empty.tsx
1175
1337
  var import_class_variance_authority2 = require("class-variance-authority");
1176
- var import_jsx_runtime28 = require("react/jsx-runtime");
1338
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1177
1339
  function Empty({ className, ...props }) {
1178
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1340
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1179
1341
  "div",
1180
1342
  {
1181
1343
  "data-slot": "empty",
@@ -1188,7 +1350,7 @@ function Empty({ className, ...props }) {
1188
1350
  );
1189
1351
  }
1190
1352
  function EmptyHeader({ className, ...props }) {
1191
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1353
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1192
1354
  "div",
1193
1355
  {
1194
1356
  "data-slot": "empty-header",
@@ -1215,7 +1377,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1215
1377
  }
1216
1378
  );
1217
1379
  function EmptyTitle({ className, ...props }) {
1218
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1380
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1219
1381
  "div",
1220
1382
  {
1221
1383
  "data-slot": "empty-title",
@@ -1226,16 +1388,16 @@ function EmptyTitle({ className, ...props }) {
1226
1388
  }
1227
1389
 
1228
1390
  // src/components/ReportEmpty.tsx
1229
- var import_jsx_runtime29 = require("react/jsx-runtime");
1391
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1230
1392
  function ReportEmpty({ message }) {
1231
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
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." }) }) });
1232
1394
  }
1233
1395
 
1234
1396
  // src/components/ReportSummary.tsx
1235
- var import_jsx_runtime30 = require("react/jsx-runtime");
1397
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1236
1398
  function ReportSummary({ className }) {
1237
1399
  const report = useReport();
1238
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1400
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1239
1401
  ReportSummaryView,
1240
1402
  {
1241
1403
  summary: report.summary,
@@ -1261,7 +1423,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1261
1423
  if (summary.pending > 0) {
1262
1424
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1263
1425
  }
1264
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1426
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1265
1427
  "dl",
1266
1428
  {
1267
1429
  className: cn(
@@ -1274,17 +1436,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1274
1436
  "aria-label": ariaLabel,
1275
1437
  children: cards.map((c) => {
1276
1438
  const tone = TONE[c.status];
1277
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
1439
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1278
1440
  "div",
1279
1441
  {
1280
1442
  "data-status": c.status,
1281
1443
  className: "rounded-lg border bg-card px-4 py-3",
1282
1444
  children: [
1283
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1284
- /* @__PURE__ */ (0, import_jsx_runtime30.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" }),
1285
1447
  c.label
1286
1448
  ] }),
1287
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
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 })
1288
1450
  ]
1289
1451
  },
1290
1452
  c.status
@@ -1325,7 +1487,7 @@ function prUrl(ci) {
1325
1487
  }
1326
1488
 
1327
1489
  // src/components/ReportMeta.tsx
1328
- var import_jsx_runtime31 = require("react/jsx-runtime");
1490
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1329
1491
  function ReportMeta() {
1330
1492
  const report = useReport();
1331
1493
  const items = [];
@@ -1348,13 +1510,13 @@ function ReportMeta() {
1348
1510
  const short = sha.slice(0, 8);
1349
1511
  items.push({
1350
1512
  label: "Commit",
1351
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime31.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
1352
1514
  });
1353
1515
  }
1354
1516
  if (report.ci) {
1355
1517
  items.push({
1356
1518
  label: "CI",
1357
- value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime31.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: [
1358
1520
  ciDisplayName(report.ci.name),
1359
1521
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1360
1522
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1365,7 +1527,7 @@ function ReportMeta() {
1365
1527
  const label = `#${report.ci.prNumber}`;
1366
1528
  items.push({
1367
1529
  label: "PR",
1368
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime31.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
1369
1531
  });
1370
1532
  }
1371
1533
  if (items.length === 0) return null;
@@ -1373,48 +1535,48 @@ function ReportMeta() {
1373
1535
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1374
1536
  // collapsed disclosure so it doesn't push the result down. Native <details>
1375
1537
  // so it works with no JS (the SSR/no-hydration report still toggles).
1376
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1377
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1378
- /* @__PURE__ */ (0, import_jsx_runtime31.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" }),
1379
1541
  "Run details"
1380
1542
  ] }),
1381
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1382
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dt", { className: "font-medium", children: item.label }),
1383
- /* @__PURE__ */ (0, import_jsx_runtime31.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 })
1384
1546
  ] }, item.label)) })
1385
1547
  ] })
1386
1548
  );
1387
1549
  }
1388
1550
 
1389
1551
  // src/components/ReportSchemaError.tsx
1390
- var import_jsx_runtime32 = require("react/jsx-runtime");
1552
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1391
1553
  function ReportSchemaError({ error }) {
1392
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1393
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("strong", { children: "Report could not be displayed." }) }),
1394
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { children: error.message }),
1395
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime32.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: [
1396
1558
  "The report bundle is newer than this version of ",
1397
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("code", { children: "executable-stories-react" }),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "executable-stories-react" }),
1398
1560
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1399
1561
  ] }) : null,
1400
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("details", { children: [
1401
- /* @__PURE__ */ (0, import_jsx_runtime32.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: [
1402
1564
  error.issues.length,
1403
1565
  " validation issue",
1404
1566
  error.issues.length === 1 ? "" : "s"
1405
1567
  ] }),
1406
- /* @__PURE__ */ (0, import_jsx_runtime32.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") })
1407
1569
  ] }) : null
1408
1570
  ] });
1409
1571
  }
1410
1572
 
1411
1573
  // src/components/ReportShell.tsx
1412
- var import_jsx_runtime33 = require("react/jsx-runtime");
1574
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1413
1575
  function ReportTitleBlock({ title }) {
1414
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
1415
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h1", { children: title ?? "Story Report" }),
1416
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ReportSummary, {}),
1417
- /* @__PURE__ */ (0, import_jsx_runtime33.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, {})
1418
1580
  ] });
1419
1581
  }
1420
1582
  function ReportErrorShell({
@@ -1423,22 +1585,22 @@ function ReportErrorShell({
1423
1585
  title,
1424
1586
  dataTheme
1425
1587
  }) {
1426
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ReportSchemaError, { error }) });
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 }) });
1427
1589
  }
1428
1590
 
1429
1591
  // src/components/Report.tsx
1430
- var import_jsx_runtime34 = require("react/jsx-runtime");
1592
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1431
1593
  function Report(props) {
1432
1594
  const { report, customRenderers, renderers, className, title, dataTheme } = props;
1433
1595
  const result = unwrapReport(report);
1434
1596
  if (!result.ok) {
1435
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
1597
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
1436
1598
  }
1437
1599
  const data = result.data;
1438
1600
  const hasContent = data.features.length > 0;
1439
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1440
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("header", { className: "es-report-header", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportTitleBlock, { title }) }),
1441
- hasContent ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportFeatureList, {}) : /* @__PURE__ */ (0, import_jsx_runtime34.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, {})
1442
1604
  ] }) });
1443
1605
  }
1444
1606