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.
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/interactive/ReportInteractive.tsx
4
4
  import {
5
- useCallback as useCallback4,
5
+ useCallback as useCallback5,
6
6
  useDeferredValue,
7
7
  useId as useId2,
8
8
  useMemo as useMemo3,
9
- useRef as useRef3,
10
- useState as useState7
9
+ useRef as useRef5,
10
+ useState as useState8
11
11
  } from "react";
12
12
 
13
13
  // src/result.ts
@@ -383,17 +383,29 @@ function DocHtml({ entry }) {
383
383
  ] });
384
384
  }
385
385
 
386
+ // src/components/doc/DocState.tsx
387
+ import { jsx as jsx13 } from "react/jsx-runtime";
388
+ function DocState({ entry }) {
389
+ let json;
390
+ try {
391
+ json = JSON.stringify(entry.value, null, 2) ?? String(entry.value);
392
+ } catch {
393
+ json = String(entry.value);
394
+ }
395
+ return /* @__PURE__ */ jsx13(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx13("code", { className: "font-mono text-xs language-json", children: json }) });
396
+ }
397
+
386
398
  // src/components/doc/DocCustom.tsx
387
- import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
399
+ import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
388
400
  function DocCustom({ entry }) {
389
401
  const renderers = useCustomRenderers();
390
402
  const renderer = renderers[entry.type];
391
403
  if (renderer) {
392
- return /* @__PURE__ */ jsx13(Fragment4, { children: renderer(entry) });
404
+ return /* @__PURE__ */ jsx14(Fragment4, { children: renderer(entry) });
393
405
  }
394
406
  return /* @__PURE__ */ jsxs9("div", { className: "my-2", "data-type": entry.type, children: [
395
- /* @__PURE__ */ jsx13("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
396
- /* @__PURE__ */ jsx13("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
407
+ /* @__PURE__ */ jsx14("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
408
+ /* @__PURE__ */ jsx14("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
397
409
  ] });
398
410
  }
399
411
  function safeStringify(value) {
@@ -405,45 +417,47 @@ function safeStringify(value) {
405
417
  }
406
418
 
407
419
  // src/components/doc/DocEntry.tsx
408
- import { jsx as jsx14 } from "react/jsx-runtime";
420
+ import { jsx as jsx15 } from "react/jsx-runtime";
409
421
  function DocEntry({ entry }) {
410
422
  switch (entry.kind) {
411
423
  case "note":
412
- return /* @__PURE__ */ jsx14(DocNote, { entry });
424
+ return /* @__PURE__ */ jsx15(DocNote, { entry });
413
425
  case "tag":
414
- return /* @__PURE__ */ jsx14(DocTag, { entry });
426
+ return /* @__PURE__ */ jsx15(DocTag, { entry });
415
427
  case "kv":
416
- return /* @__PURE__ */ jsx14(DocKv, { entry });
428
+ return /* @__PURE__ */ jsx15(DocKv, { entry });
417
429
  case "code":
418
- return /* @__PURE__ */ jsx14(DocCode, { entry });
430
+ return /* @__PURE__ */ jsx15(DocCode, { entry });
419
431
  case "table":
420
- return /* @__PURE__ */ jsx14(DocTable, { entry });
432
+ return /* @__PURE__ */ jsx15(DocTable, { entry });
421
433
  case "link":
422
- return /* @__PURE__ */ jsx14(DocLink, { entry });
434
+ return /* @__PURE__ */ jsx15(DocLink, { entry });
423
435
  case "section":
424
- return /* @__PURE__ */ jsx14(DocSection, { entry });
436
+ return /* @__PURE__ */ jsx15(DocSection, { entry });
425
437
  case "mermaid":
426
- return /* @__PURE__ */ jsx14(DocMermaid, { entry });
438
+ return /* @__PURE__ */ jsx15(DocMermaid, { entry });
427
439
  case "screenshot":
428
- return /* @__PURE__ */ jsx14(DocScreenshot, { entry });
440
+ return /* @__PURE__ */ jsx15(DocScreenshot, { entry });
429
441
  case "video":
430
- return /* @__PURE__ */ jsx14(DocVideo, { entry });
442
+ return /* @__PURE__ */ jsx15(DocVideo, { entry });
431
443
  case "html":
432
- return /* @__PURE__ */ jsx14(DocHtml, { entry });
444
+ return /* @__PURE__ */ jsx15(DocHtml, { entry });
445
+ case "state":
446
+ return /* @__PURE__ */ jsx15(DocState, { entry });
433
447
  case "custom":
434
- return /* @__PURE__ */ jsx14(DocCustom, { entry });
448
+ return /* @__PURE__ */ jsx15(DocCustom, { entry });
435
449
  }
436
450
  }
437
451
 
438
452
  // src/components/ReportDocEntries.tsx
439
- import { Fragment as Fragment5, jsx as jsx15 } from "react/jsx-runtime";
453
+ import { Fragment as Fragment5, jsx as jsx16 } from "react/jsx-runtime";
440
454
  function ReportDocEntries({ entries }) {
441
455
  if (entries.length === 0) return null;
442
- return /* @__PURE__ */ jsx15(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx15(DocEntry, { entry }, i)) });
456
+ return /* @__PURE__ */ jsx16(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx16(DocEntry, { entry }, i)) });
443
457
  }
444
458
 
445
459
  // src/lib/step-params.tsx
446
- import { jsx as jsx16 } from "react/jsx-runtime";
460
+ import { jsx as jsx17 } from "react/jsx-runtime";
447
461
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
448
462
  function highlightStepParams(text) {
449
463
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -455,7 +469,7 @@ function highlightStepParams(text) {
455
469
  const end = start + match[0].length;
456
470
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
457
471
  nodes.push(
458
- /* @__PURE__ */ jsx16("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
472
+ /* @__PURE__ */ jsx17("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
459
473
  );
460
474
  lastIndex = end;
461
475
  });
@@ -464,7 +478,7 @@ function highlightStepParams(text) {
464
478
  }
465
479
 
466
480
  // src/components/ReportSteps.tsx
467
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
481
+ import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
468
482
  var STEP_GLYPH = {
469
483
  passed: "\u2713",
470
484
  failed: "\u2717",
@@ -479,7 +493,7 @@ var STEP_GLYPH_COLOR = {
479
493
  };
480
494
  function ReportSteps({ scenario }) {
481
495
  if (scenario.steps.length === 0) return null;
482
- return /* @__PURE__ */ jsx17("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx17(ReportStepItem, { step }, step.id)) });
496
+ return /* @__PURE__ */ jsx18("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx18(ReportStepItem, { step }, step.id)) });
483
497
  }
484
498
  function ReportStepItem({ step }) {
485
499
  const kw = step.keyword.toLowerCase();
@@ -492,8 +506,8 @@ function ReportStepItem({ step }) {
492
506
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
493
507
  children: [
494
508
  /* @__PURE__ */ jsxs10("div", { className: "flex items-baseline gap-2", children: [
495
- /* @__PURE__ */ jsx17("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
496
- /* @__PURE__ */ jsx17(
509
+ /* @__PURE__ */ jsx18("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
510
+ /* @__PURE__ */ jsx18(
497
511
  "span",
498
512
  {
499
513
  className: cn(
@@ -503,10 +517,10 @@ function ReportStepItem({ step }) {
503
517
  children: step.keyword
504
518
  }
505
519
  ),
506
- /* @__PURE__ */ jsx17("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
507
- step.durationMs > 0 ? /* @__PURE__ */ jsx17("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
520
+ /* @__PURE__ */ jsx18("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
521
+ step.durationMs > 0 ? /* @__PURE__ */ jsx18("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
508
522
  ] }),
509
- step.errorMessage ? /* @__PURE__ */ jsx17(
523
+ step.errorMessage ? /* @__PURE__ */ jsx18(
510
524
  "pre",
511
525
  {
512
526
  role: "alert",
@@ -514,12 +528,72 @@ function ReportStepItem({ step }) {
514
528
  children: step.errorMessage
515
529
  }
516
530
  ) : null,
517
- step.docEntries.length > 0 ? /* @__PURE__ */ jsx17("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx17(ReportDocEntries, { entries: step.docEntries }) }) : null
531
+ step.docEntries.length > 0 ? /* @__PURE__ */ jsx18("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx18(ReportDocEntries, { entries: step.docEntries }) }) : null
518
532
  ]
519
533
  }
520
534
  );
521
535
  }
522
536
 
537
+ // ../executable-stories-core/dist/state-diff.js
538
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
539
+ function deepEqual(a, b) {
540
+ if (Object.is(a, b)) return true;
541
+ if (Array.isArray(a) && Array.isArray(b)) {
542
+ return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
543
+ }
544
+ if (isPlainObject(a) && isPlainObject(b)) {
545
+ const ak = Object.keys(a);
546
+ const bk = Object.keys(b);
547
+ return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
548
+ }
549
+ return false;
550
+ }
551
+ var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
552
+ function walk(path, before, after, out) {
553
+ if (Object.is(before, after)) return;
554
+ if (Array.isArray(before) && Array.isArray(after)) {
555
+ const shared = Math.min(before.length, after.length);
556
+ for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
557
+ for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
558
+ for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
559
+ return;
560
+ }
561
+ if (isPlainObject(before) && isPlainObject(after)) {
562
+ for (const key of Object.keys(after)) {
563
+ const p = joinKey(path, key);
564
+ if (key in before) walk(p, before[key], after[key], out);
565
+ else out.push({ path: p, kind: "added", after: after[key] });
566
+ }
567
+ for (const key of Object.keys(before)) {
568
+ if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
569
+ }
570
+ return;
571
+ }
572
+ if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
573
+ }
574
+ function diffStateValues(before, after) {
575
+ const out = [];
576
+ walk("", before, after, out);
577
+ return out;
578
+ }
579
+ function formatStateValue(value, maxLen = 60) {
580
+ const s = value === void 0 ? "undefined" : JSON.stringify(value);
581
+ return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
582
+ }
583
+ function summarizeStateChanges(changes) {
584
+ return changes.map((c) => {
585
+ const at = c.path === "" ? "(root)" : c.path;
586
+ switch (c.kind) {
587
+ case "added":
588
+ return `+ ${at}: ${formatStateValue(c.after)}`;
589
+ case "removed":
590
+ return `\u2212 ${at}`;
591
+ case "changed":
592
+ return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
593
+ }
594
+ });
595
+ }
596
+
523
597
  // ../executable-stories-core/dist/storyboard.js
524
598
  function findScreenshot(entries) {
525
599
  for (const entry of entries) {
@@ -531,26 +605,55 @@ function findScreenshot(entries) {
531
605
  }
532
606
  return void 0;
533
607
  }
608
+ function findStates(entries, out = []) {
609
+ for (const entry of entries) {
610
+ if (entry.kind === "state") out.push(entry);
611
+ if (entry.children) findStates(entry.children, out);
612
+ }
613
+ return out;
614
+ }
615
+ var laneOf = (label) => label ?? "";
534
616
  function extractStoryboardFrames(scenario) {
535
617
  const frames = [];
618
+ const laneOrder = [];
619
+ const previous = /* @__PURE__ */ new Map();
536
620
  for (const step of scenario.steps) {
537
621
  const shot = findScreenshot(step.docEntries);
538
- if (!shot) continue;
622
+ const stateDocs = findStates(step.docEntries);
623
+ if (!shot && stateDocs.length === 0) continue;
624
+ const byLane = /* @__PURE__ */ new Map();
625
+ for (const doc of stateDocs) {
626
+ const lane = laneOf(doc.label);
627
+ if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
628
+ byLane.set(lane, doc);
629
+ }
630
+ const states = [];
631
+ for (const lane of laneOrder) {
632
+ const doc = byLane.get(lane);
633
+ if (!doc) continue;
634
+ const card = {
635
+ ...doc.label !== void 0 && { label: doc.label },
636
+ value: doc.value
637
+ };
638
+ if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
639
+ previous.set(lane, doc.value);
640
+ states.push(card);
641
+ }
539
642
  frames.push({
540
643
  stepId: step.id,
541
644
  stepIndex: step.index,
542
645
  keyword: step.keyword,
543
646
  text: step.text,
544
647
  status: step.status,
545
- path: shot.path,
546
- ...shot.alt !== void 0 && { alt: shot.alt }
648
+ ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
649
+ states
547
650
  });
548
651
  }
549
652
  return frames;
550
653
  }
551
654
 
552
655
  // src/components/ReportStoryboard.tsx
553
- import { jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
656
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
554
657
  var FRAME_GLYPH = {
555
658
  passed: "\u2713",
556
659
  failed: "\u2717",
@@ -567,54 +670,113 @@ function ReportStoryboard({ scenario }) {
567
670
  const frames = extractStoryboardFrames(scenario);
568
671
  if (frames.length < 2) return null;
569
672
  return /* @__PURE__ */ jsxs11("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
570
- /* @__PURE__ */ jsx18("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
571
- /* @__PURE__ */ jsx18("ol", { className: "flex gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx18(StoryboardFrameItem, { frame }, frame.stepId)) })
673
+ /* @__PURE__ */ jsx19("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
674
+ /* @__PURE__ */ jsx19("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx19(StoryboardFrameItem, { frame }, frame.stepId)) })
572
675
  ] });
573
676
  }
574
677
  function StoryboardFrameItem({ frame }) {
575
- const src = isLocalFsPath(frame.path) ? void 0 : safeImageUrl(frame.path);
576
678
  const label = `${frame.keyword} ${frame.text}`;
577
- return /* @__PURE__ */ jsxs11("li", { className: "w-40 shrink-0", children: [
578
- /* @__PURE__ */ jsx18(
579
- "a",
679
+ const hasStates = frame.states.length > 0;
680
+ const caption = /* @__PURE__ */ jsxs11("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
681
+ /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
682
+ /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
683
+ /* @__PURE__ */ jsx19("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
684
+ " ",
685
+ /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
686
+ ] })
687
+ ] });
688
+ return (
689
+ // State cards need room for diff rows; screenshot-only frames keep the
690
+ // original narrow film-cell width (and DOM) unchanged.
691
+ /* @__PURE__ */ jsxs11("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
692
+ frame.screenshot ? /* @__PURE__ */ jsx19(FrameScreenshot, { frame, label }) : null,
693
+ hasStates ? /* @__PURE__ */ jsx19("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ jsx19(StateCardView, { card }, card.label ?? i)) }) : null,
694
+ frame.screenshot ? caption : (
695
+ // No screenshot means no image anchor, so the caption carries the
696
+ // jump-to-step link instead.
697
+ /* @__PURE__ */ jsx19("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
698
+ )
699
+ ] })
700
+ );
701
+ }
702
+ function FrameScreenshot({ frame, label }) {
703
+ const path = frame.screenshot?.path ?? "";
704
+ const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
705
+ return /* @__PURE__ */ jsx19(
706
+ "a",
707
+ {
708
+ href: `#${frame.stepId}`,
709
+ "aria-label": `Jump to step: ${label}`,
710
+ className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
711
+ children: src ? /* @__PURE__ */ jsx19(
712
+ "img",
713
+ {
714
+ src,
715
+ alt: frame.screenshot?.alt ?? label,
716
+ loading: "lazy",
717
+ className: "h-24 w-full bg-muted object-cover object-top"
718
+ }
719
+ ) : /* @__PURE__ */ jsx19("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
720
+ }
721
+ );
722
+ }
723
+ var CHANGE_TONE = {
724
+ added: "bg-pass-bg text-pass",
725
+ removed: "bg-fail-bg text-fail",
726
+ changed: "bg-skip-bg text-skip"
727
+ };
728
+ function prettyJson(value) {
729
+ try {
730
+ return JSON.stringify(value, null, 2) ?? String(value);
731
+ } catch {
732
+ return String(value);
733
+ }
734
+ }
735
+ function StateCardView({ card }) {
736
+ const json = prettyJson(card.value);
737
+ const firstCapture = card.changes === void 0;
738
+ return /* @__PURE__ */ jsxs11("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
739
+ /* @__PURE__ */ jsxs11("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
740
+ /* @__PURE__ */ jsx19("span", { className: "inline-flex rounded-full border border-border px-1.5 font-mono text-[0.625rem] font-medium text-muted-foreground", children: card.label ?? "State" }),
741
+ firstCapture ? /* @__PURE__ */ jsx19("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
742
+ ] }),
743
+ !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ jsx19("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ jsx19(
744
+ "li",
580
745
  {
581
- href: `#${frame.stepId}`,
582
- "aria-label": `Jump to step: ${label}`,
583
- className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
584
- children: src ? /* @__PURE__ */ jsx18(
585
- "img",
586
- {
587
- src,
588
- alt: frame.alt ?? label,
589
- loading: "lazy",
590
- className: "h-24 w-full bg-muted object-cover object-top"
591
- }
592
- ) : /* @__PURE__ */ jsx18("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" })
746
+ className: cn(
747
+ "rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
748
+ CHANGE_TONE[change.kind]
749
+ ),
750
+ children: summarizeStateChanges([change])[0]
751
+ },
752
+ i
753
+ )) }) : /* @__PURE__ */ jsx19("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
754
+ /* @__PURE__ */ jsxs11(
755
+ "details",
756
+ {
757
+ open: firstCapture && json.length <= 240,
758
+ className: cn(firstCapture ? "mt-1.5" : "mt-1"),
759
+ children: [
760
+ /* @__PURE__ */ jsx19("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
761
+ /* @__PURE__ */ jsx19("pre", { tabIndex: 0, className: "mt-1 max-h-48 overflow-auto rounded-sm bg-muted p-1.5 font-mono text-[0.625rem] leading-snug", children: json })
762
+ ]
593
763
  }
594
- ),
595
- /* @__PURE__ */ jsxs11("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
596
- /* @__PURE__ */ jsx18("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
597
- /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
598
- /* @__PURE__ */ jsx18("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
599
- " ",
600
- /* @__PURE__ */ jsx18("span", { className: "text-muted-foreground", children: frame.alt ?? frame.text })
601
- ] })
602
- ] })
764
+ )
603
765
  ] });
604
766
  }
605
767
 
606
768
  // src/components/ReportAttachments.tsx
607
- import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
769
+ import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
608
770
  function ReportAttachments({ attachments }) {
609
771
  if (attachments.length === 0) return null;
610
772
  return (
611
773
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
612
774
  // blocks don't create duplicate "Attachments" region landmarks.
613
- /* @__PURE__ */ jsx19("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
775
+ /* @__PURE__ */ jsx20("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
614
776
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
615
777
  const isImage = att.mediaType.startsWith("image/");
616
- return /* @__PURE__ */ jsx19("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs12(Fragment6, { children: [
617
- /* @__PURE__ */ jsx19(
778
+ return /* @__PURE__ */ jsx20("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs12(Fragment6, { children: [
779
+ /* @__PURE__ */ jsx20(
618
780
  "img",
619
781
  {
620
782
  src: dataUri,
@@ -623,7 +785,7 @@ function ReportAttachments({ attachments }) {
623
785
  className: "max-w-full rounded-md border border-border"
624
786
  }
625
787
  ),
626
- /* @__PURE__ */ jsx19("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
788
+ /* @__PURE__ */ jsx20("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
627
789
  ] }) : /* @__PURE__ */ jsxs12(
628
790
  "a",
629
791
  {
@@ -697,15 +859,15 @@ function toTraceRows(spans) {
697
859
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
698
860
  const rows = [];
699
861
  const seen = /* @__PURE__ */ new Set();
700
- const walk = (node, depth) => {
862
+ const walk2 = (node, depth) => {
701
863
  if (seen.has(node.span.spanId)) return;
702
864
  seen.add(node.span.spanId);
703
865
  rows.push({ ...node.span, depth });
704
- for (const child of node.children) walk(child, depth + 1);
866
+ for (const child of node.children) walk2(child, depth + 1);
705
867
  };
706
- for (const root of roots) walk(root, 0);
868
+ for (const root of roots) walk2(root, 0);
707
869
  for (const node of byId.values()) {
708
- if (!seen.has(node.span.spanId)) walk(node, 0);
870
+ if (!seen.has(node.span.spanId)) walk2(node, 0);
709
871
  }
710
872
  return rows;
711
873
  }
@@ -721,7 +883,7 @@ function traceWindow(rows) {
721
883
  }
722
884
 
723
885
  // src/components/ReportTrace.tsx
724
- import { jsx as jsx20, jsxs as jsxs13 } from "react/jsx-runtime";
886
+ import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
725
887
  var BAR_COLOR = {
726
888
  ok: "bg-pass",
727
889
  error: "bg-fail",
@@ -742,11 +904,11 @@ function ReportTrace({ spans }) {
742
904
  formatDuration(totalMs),
743
905
  ")"
744
906
  ] }),
745
- /* @__PURE__ */ jsx20("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
907
+ /* @__PURE__ */ jsx21("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
746
908
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
747
909
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
748
910
  return /* @__PURE__ */ jsxs13("li", { className: "flex items-center gap-2", children: [
749
- /* @__PURE__ */ jsx20(
911
+ /* @__PURE__ */ jsx21(
750
912
  "span",
751
913
  {
752
914
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -755,14 +917,14 @@ function ReportTrace({ spans }) {
755
917
  children: row.name
756
918
  }
757
919
  ),
758
- /* @__PURE__ */ jsx20("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx20(
920
+ /* @__PURE__ */ jsx21("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx21(
759
921
  "span",
760
922
  {
761
923
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
762
924
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
763
925
  }
764
926
  ) }),
765
- /* @__PURE__ */ jsx20("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
927
+ /* @__PURE__ */ jsx21("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
766
928
  ] }, `${row.spanId}-${i}`);
767
929
  }) })
768
930
  ] });
@@ -811,11 +973,11 @@ function flakinessOf(entries) {
811
973
  if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
812
974
  return "stable";
813
975
  }
814
- function diffSinceLastRun(history2, scenarios) {
976
+ function diffSinceLastRun(history, scenarios) {
815
977
  let hasPreviousRun = false;
816
978
  const delta = { newlyFailing: [], newlyPassing: [], added: [] };
817
979
  for (const scenario of scenarios) {
818
- const entries = history2[scenario.id];
980
+ const entries = history[scenario.id];
819
981
  if (!entries || entries.length === 0) continue;
820
982
  const last = entries[entries.length - 1];
821
983
  const prev = entries[entries.length - 2];
@@ -831,7 +993,7 @@ function diffSinceLastRun(history2, scenarios) {
831
993
  }
832
994
 
833
995
  // src/components/ScenarioRunHistory.tsx
834
- import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
996
+ import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
835
997
  var MAX_DOTS = 10;
836
998
  var DOT_COLOR = {
837
999
  passed: "bg-pass",
@@ -845,22 +1007,22 @@ function dotTitle(e) {
845
1007
  return parts.join(" \xB7 ");
846
1008
  }
847
1009
  function ScenarioRunHistory({ scenarioId }) {
848
- const history2 = useScenarioHistory();
849
- const entries = history2?.[scenarioId];
1010
+ const history = useScenarioHistory();
1011
+ const entries = history?.[scenarioId];
850
1012
  if (!entries || entries.length < 2) return null;
851
1013
  const recent = entries.slice(-MAX_DOTS);
852
1014
  const flaky = flakinessOf(recent) === "flaky";
853
1015
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
854
1016
  return /* @__PURE__ */ jsxs14(Fragment7, { children: [
855
- flaky ? /* @__PURE__ */ jsx21(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
856
- /* @__PURE__ */ jsx21(
1017
+ flaky ? /* @__PURE__ */ jsx22(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1018
+ /* @__PURE__ */ jsx22(
857
1019
  "span",
858
1020
  {
859
1021
  role: "img",
860
1022
  "aria-label": `Run history: ${summary}`,
861
1023
  title: summary,
862
1024
  className: "flex shrink-0 items-center gap-[3px]",
863
- children: recent.map((e, i) => /* @__PURE__ */ jsx21(
1025
+ children: recent.map((e, i) => /* @__PURE__ */ jsx22(
864
1026
  "span",
865
1027
  {
866
1028
  "aria-hidden": true,
@@ -951,7 +1113,7 @@ function stringify(value) {
951
1113
  return String(value);
952
1114
  }
953
1115
  }
954
- function docEntryToMarkdown(entry) {
1116
+ function docEntryToMarkdown(entry, stateLanes) {
955
1117
  const lines = [];
956
1118
  switch (entry.kind) {
957
1119
  case "note":
@@ -1003,13 +1165,24 @@ function docEntryToMarkdown(entry) {
1003
1165
  case "custom":
1004
1166
  lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
1005
1167
  break;
1168
+ case "state": {
1169
+ const lane = entry.label ?? "";
1170
+ lines.push(`**${entry.label ?? "State"}**`);
1171
+ if (stateLanes?.has(lane)) {
1172
+ const summary = summarizeStateChanges(diffStateValues(stateLanes.get(lane), entry.value));
1173
+ if (summary.length > 0) lines.push("", ...summary.map((l) => `- ${l}`));
1174
+ }
1175
+ stateLanes?.set(lane, entry.value);
1176
+ lines.push("", "<details>", "<summary>snapshot</summary>", "", "```json", stringify(entry.value ?? null), "```", "", "</details>");
1177
+ break;
1178
+ }
1006
1179
  }
1007
1180
  for (const child of entry.children ?? []) {
1008
- lines.push("", ...docEntryToMarkdown(child));
1181
+ lines.push("", ...docEntryToMarkdown(child, stateLanes));
1009
1182
  }
1010
1183
  return lines;
1011
1184
  }
1012
- function stepLines(step, ordinal, full) {
1185
+ function stepLines(step, ordinal, full, stateLanes) {
1013
1186
  const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
1014
1187
  const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
1015
1188
  if (!full) return lines;
@@ -1017,7 +1190,7 @@ function stepLines(step, ordinal, full) {
1017
1190
  lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
1018
1191
  }
1019
1192
  for (const entry of step.docEntries) {
1020
- lines.push("", ...docEntryToMarkdown(entry).map((l) => l === "" ? l : ` ${l}`));
1193
+ lines.push("", ...docEntryToMarkdown(entry, stateLanes).map((l) => l === "" ? l : ` ${l}`));
1021
1194
  }
1022
1195
  return lines;
1023
1196
  }
@@ -1026,9 +1199,10 @@ function scenarioToMarkdown(scenario, options = {}) {
1026
1199
  const full = variant === "full";
1027
1200
  const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
1028
1201
  if (full && meta.length > 0) lines.push(...meta);
1202
+ const stateLanes = /* @__PURE__ */ new Map();
1029
1203
  if (scenario.steps.length > 0) {
1030
1204
  if (full) lines.push("", "## Steps", "");
1031
- scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full)));
1205
+ scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full, stateLanes)));
1032
1206
  }
1033
1207
  if (scenario.errorMessage) {
1034
1208
  if (full) lines.push("", "## Failure", "");
@@ -1037,7 +1211,7 @@ function scenarioToMarkdown(scenario, options = {}) {
1037
1211
  }
1038
1212
  if (full && scenario.docEntries.length > 0) {
1039
1213
  lines.push("", "## Docs", "");
1040
- for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry), "");
1214
+ for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry, stateLanes), "");
1041
1215
  }
1042
1216
  return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
1043
1217
  }
@@ -1077,9 +1251,9 @@ function scenarioPermalink(scenario) {
1077
1251
 
1078
1252
  // src/components/ui/card.tsx
1079
1253
  import "react";
1080
- import { jsx as jsx22 } from "react/jsx-runtime";
1254
+ import { jsx as jsx23 } from "react/jsx-runtime";
1081
1255
  function Card({ className, ...props }) {
1082
- return /* @__PURE__ */ jsx22(
1256
+ return /* @__PURE__ */ jsx23(
1083
1257
  "div",
1084
1258
  {
1085
1259
  "data-slot": "card",
@@ -1092,7 +1266,7 @@ function Card({ className, ...props }) {
1092
1266
  );
1093
1267
  }
1094
1268
  function CardHeader({ className, ...props }) {
1095
- return /* @__PURE__ */ jsx22(
1269
+ return /* @__PURE__ */ jsx23(
1096
1270
  "div",
1097
1271
  {
1098
1272
  "data-slot": "card-header",
@@ -1105,7 +1279,7 @@ function CardHeader({ className, ...props }) {
1105
1279
  );
1106
1280
  }
1107
1281
  function CardContent({ className, ...props }) {
1108
- return /* @__PURE__ */ jsx22(
1282
+ return /* @__PURE__ */ jsx23(
1109
1283
  "div",
1110
1284
  {
1111
1285
  "data-slot": "card-content",
@@ -1119,13 +1293,13 @@ function CardContent({ className, ...props }) {
1119
1293
  import "react";
1120
1294
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
1121
1295
  import { CheckIcon } from "lucide-react";
1122
- import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
1296
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
1123
1297
  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";
1124
1298
  function DropdownMenu(props) {
1125
- return /* @__PURE__ */ jsx23(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1299
+ return /* @__PURE__ */ jsx24(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1126
1300
  }
1127
1301
  function DropdownMenuTrigger(props) {
1128
- return /* @__PURE__ */ jsx23(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1302
+ return /* @__PURE__ */ jsx24(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1129
1303
  }
1130
1304
  function DropdownMenuContent({
1131
1305
  className,
@@ -1133,14 +1307,14 @@ function DropdownMenuContent({
1133
1307
  sideOffset = 4,
1134
1308
  ...props
1135
1309
  }) {
1136
- return /* @__PURE__ */ jsx23(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx23(
1310
+ return /* @__PURE__ */ jsx24(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx24(
1137
1311
  MenuPrimitive.Positioner,
1138
1312
  {
1139
1313
  "data-slot": "dropdown-menu-positioner",
1140
1314
  className: "z-50",
1141
1315
  align,
1142
1316
  sideOffset,
1143
- children: /* @__PURE__ */ jsx23(
1317
+ children: /* @__PURE__ */ jsx24(
1144
1318
  MenuPrimitive.Popup,
1145
1319
  {
1146
1320
  "data-slot": "dropdown-menu-content",
@@ -1158,7 +1332,7 @@ function DropdownMenuItem({
1158
1332
  className,
1159
1333
  ...props
1160
1334
  }) {
1161
- return /* @__PURE__ */ jsx23(
1335
+ return /* @__PURE__ */ jsx24(
1162
1336
  MenuPrimitive.Item,
1163
1337
  {
1164
1338
  "data-slot": "dropdown-menu-item",
@@ -1170,7 +1344,7 @@ function DropdownMenuItem({
1170
1344
 
1171
1345
  // src/components/ReportScenario.tsx
1172
1346
  import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
1173
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
1347
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1174
1348
  var STATUS_META = {
1175
1349
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1176
1350
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1197,7 +1371,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1197
1371
  /* @__PURE__ */ jsxs16(CardHeader, { className: "px-4 py-3", children: [
1198
1372
  /* @__PURE__ */ jsxs16("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1199
1373
  hideTitle ? null : /* @__PURE__ */ jsxs16("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1200
- collapsible ? /* @__PURE__ */ jsx24(
1374
+ collapsible ? /* @__PURE__ */ jsx25(
1201
1375
  "button",
1202
1376
  {
1203
1377
  type: "button",
@@ -1206,36 +1380,36 @@ function ReportScenario({ scenario, hideTitle = false }) {
1206
1380
  "aria-controls": bodyId,
1207
1381
  "aria-label": `Toggle ${scenario.title}`,
1208
1382
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1209
- children: /* @__PURE__ */ jsx24("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1383
+ children: /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1210
1384
  }
1211
1385
  ) : null,
1212
- /* @__PURE__ */ jsx24("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1213
- /* @__PURE__ */ jsx24("span", { children: scenario.title })
1386
+ /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1387
+ /* @__PURE__ */ jsx25("span", { children: scenario.title })
1214
1388
  ] }),
1215
1389
  /* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-2", children: [
1216
- /* @__PURE__ */ jsx24(ScenarioRunHistory, { scenarioId: scenario.id }),
1217
- scenario.durationMs > 0 ? /* @__PURE__ */ jsx24("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1218
- meta.showPill ? /* @__PURE__ */ jsx24(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx24("span", { className: "sr-only", children: `Status: ${label}` }),
1390
+ /* @__PURE__ */ jsx25(ScenarioRunHistory, { scenarioId: scenario.id }),
1391
+ scenario.durationMs > 0 ? /* @__PURE__ */ jsx25("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1392
+ meta.showPill ? /* @__PURE__ */ jsx25(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx25("span", { className: "sr-only", children: `Status: ${label}` }),
1219
1393
  actions ? /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1220
- /* @__PURE__ */ jsx24(
1394
+ /* @__PURE__ */ jsx25(
1221
1395
  DropdownMenuTrigger,
1222
1396
  {
1223
1397
  "aria-label": `Actions for ${scenario.title}`,
1224
1398
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1225
- children: /* @__PURE__ */ jsx24(MoreHorizontalIcon, { "aria-hidden": true })
1399
+ children: /* @__PURE__ */ jsx25(MoreHorizontalIcon, { "aria-hidden": true })
1226
1400
  }
1227
1401
  ),
1228
1402
  /* @__PURE__ */ jsxs16(DropdownMenuContent, { align: "end", children: [
1229
1403
  /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1230
- /* @__PURE__ */ jsx24(LinkIcon, {}),
1404
+ /* @__PURE__ */ jsx25(LinkIcon, {}),
1231
1405
  " Copy link"
1232
1406
  ] }),
1233
1407
  /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1234
- /* @__PURE__ */ jsx24(FileTextIcon, {}),
1408
+ /* @__PURE__ */ jsx25(FileTextIcon, {}),
1235
1409
  " Copy as Markdown"
1236
1410
  ] }),
1237
1411
  /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1238
- /* @__PURE__ */ jsx24(SparklesIcon, {}),
1412
+ /* @__PURE__ */ jsx25(SparklesIcon, {}),
1239
1413
  " Explain with AI"
1240
1414
  ] })
1241
1415
  ] })
@@ -1243,12 +1417,12 @@ function ReportScenario({ scenario, hideTitle = false }) {
1243
1417
  ] })
1244
1418
  ] }),
1245
1419
  scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs16("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1246
- scenario.tags.map((t) => /* @__PURE__ */ jsx24("li", { children: /* @__PURE__ */ jsx24(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1247
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx24("li", { children: ticket.url ? /* @__PURE__ */ jsx24("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx24(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx24(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1420
+ scenario.tags.map((t) => /* @__PURE__ */ jsx25("li", { children: /* @__PURE__ */ jsx25(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1421
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx25("li", { children: ticket.url ? /* @__PURE__ */ jsx25("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx25(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx25(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1248
1422
  ] }) : null
1249
1423
  ] }),
1250
1424
  /* @__PURE__ */ jsxs16(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1251
- scenario.errorMessage ? /* @__PURE__ */ jsx24(
1425
+ scenario.errorMessage ? /* @__PURE__ */ jsx25(
1252
1426
  "pre",
1253
1427
  {
1254
1428
  role: "alert",
@@ -1256,11 +1430,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1256
1430
  children: scenario.errorMessage
1257
1431
  }
1258
1432
  ) : null,
1259
- /* @__PURE__ */ jsx24(ReportStoryboard, { scenario }),
1260
- /* @__PURE__ */ jsx24(ReportSteps, { scenario }),
1261
- scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx24("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx24(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1262
- /* @__PURE__ */ jsx24(ReportAttachments, { attachments: scenario.attachments }),
1263
- /* @__PURE__ */ jsx24(ReportTrace, { spans: scenario.otelSpans })
1433
+ /* @__PURE__ */ jsx25(ReportStoryboard, { scenario }),
1434
+ /* @__PURE__ */ jsx25(ReportSteps, { scenario }),
1435
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx25("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx25(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1436
+ /* @__PURE__ */ jsx25(ReportAttachments, { attachments: scenario.attachments }),
1437
+ /* @__PURE__ */ jsx25(ReportTrace, { spans: scenario.otelSpans })
1264
1438
  ] })
1265
1439
  ]
1266
1440
  }
@@ -1268,13 +1442,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1268
1442
  }
1269
1443
 
1270
1444
  // src/components/ReportScenarioList.tsx
1271
- import { Fragment as Fragment8, jsx as jsx25 } from "react/jsx-runtime";
1445
+ import { Fragment as Fragment8, jsx as jsx26 } from "react/jsx-runtime";
1272
1446
  function ReportScenarioList({ feature }) {
1273
- return /* @__PURE__ */ jsx25(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx25(ReportScenario, { scenario }, scenario.id)) });
1447
+ return /* @__PURE__ */ jsx26(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx26(ReportScenario, { scenario }, scenario.id)) });
1274
1448
  }
1275
1449
 
1276
1450
  // src/components/ReportFeature.tsx
1277
- import { Fragment as Fragment9, jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1451
+ import { Fragment as Fragment9, jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
1278
1452
  function ReportFeature({ feature }) {
1279
1453
  const titleId = `${feature.id}-title`;
1280
1454
  const bodyId = `${feature.id}-body`;
@@ -1295,7 +1469,7 @@ function ReportFeature({ feature }) {
1295
1469
  children: [
1296
1470
  /* @__PURE__ */ jsxs17("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1297
1471
  /* @__PURE__ */ jsxs17("div", { className: "min-w-0", children: [
1298
- /* @__PURE__ */ jsx26("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs17(
1472
+ /* @__PURE__ */ jsx27("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs17(
1299
1473
  "button",
1300
1474
  {
1301
1475
  type: "button",
@@ -1304,12 +1478,12 @@ function ReportFeature({ feature }) {
1304
1478
  "aria-controls": bodyId,
1305
1479
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1306
1480
  children: [
1307
- /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1308
- /* @__PURE__ */ jsx26("span", { className: "truncate", children: feature.title })
1481
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1482
+ /* @__PURE__ */ jsx27("span", { className: "truncate", children: feature.title })
1309
1483
  ]
1310
1484
  }
1311
1485
  ) : feature.title }),
1312
- /* @__PURE__ */ jsx26("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1486
+ /* @__PURE__ */ jsx27("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1313
1487
  ] }),
1314
1488
  /* @__PURE__ */ jsxs17(
1315
1489
  "p",
@@ -1321,13 +1495,13 @@ function ReportFeature({ feature }) {
1321
1495
  s.passed,
1322
1496
  " passed"
1323
1497
  ] }),
1324
- /* @__PURE__ */ jsx26("span", { "aria-hidden": true, children: "\xB7" }),
1498
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1325
1499
  /* @__PURE__ */ jsxs17("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1326
1500
  s.failed,
1327
1501
  " failed"
1328
1502
  ] }),
1329
1503
  skipped > 0 ? /* @__PURE__ */ jsxs17(Fragment9, { children: [
1330
- /* @__PURE__ */ jsx26("span", { "aria-hidden": true, children: "\xB7" }),
1504
+ /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1331
1505
  /* @__PURE__ */ jsxs17("span", { children: [
1332
1506
  skipped,
1333
1507
  " skipped"
@@ -1337,7 +1511,7 @@ function ReportFeature({ feature }) {
1337
1511
  }
1338
1512
  )
1339
1513
  ] }),
1340
- /* @__PURE__ */ jsx26("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx26(ReportScenarioList, { feature }) })
1514
+ /* @__PURE__ */ jsx27("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx27(ReportScenarioList, { feature }) })
1341
1515
  ]
1342
1516
  }
1343
1517
  )
@@ -1345,17 +1519,17 @@ function ReportFeature({ feature }) {
1345
1519
  }
1346
1520
 
1347
1521
  // src/components/ReportFeatureList.tsx
1348
- import { jsx as jsx27 } from "react/jsx-runtime";
1522
+ import { jsx as jsx28 } from "react/jsx-runtime";
1349
1523
  function ReportFeatureList() {
1350
1524
  const report = useReport();
1351
- return /* @__PURE__ */ jsx27("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx27(ReportFeature, { feature }, feature.id)) });
1525
+ return /* @__PURE__ */ jsx28("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx28(ReportFeature, { feature }, feature.id)) });
1352
1526
  }
1353
1527
 
1354
1528
  // src/components/ui/empty.tsx
1355
1529
  import { cva as cva2 } from "class-variance-authority";
1356
- import { jsx as jsx28 } from "react/jsx-runtime";
1530
+ import { jsx as jsx29 } from "react/jsx-runtime";
1357
1531
  function Empty({ className, ...props }) {
1358
- return /* @__PURE__ */ jsx28(
1532
+ return /* @__PURE__ */ jsx29(
1359
1533
  "div",
1360
1534
  {
1361
1535
  "data-slot": "empty",
@@ -1368,7 +1542,7 @@ function Empty({ className, ...props }) {
1368
1542
  );
1369
1543
  }
1370
1544
  function EmptyHeader({ className, ...props }) {
1371
- return /* @__PURE__ */ jsx28(
1545
+ return /* @__PURE__ */ jsx29(
1372
1546
  "div",
1373
1547
  {
1374
1548
  "data-slot": "empty-header",
@@ -1395,7 +1569,7 @@ var emptyMediaVariants = cva2(
1395
1569
  }
1396
1570
  );
1397
1571
  function EmptyTitle({ className, ...props }) {
1398
- return /* @__PURE__ */ jsx28(
1572
+ return /* @__PURE__ */ jsx29(
1399
1573
  "div",
1400
1574
  {
1401
1575
  "data-slot": "empty-title",
@@ -1406,16 +1580,16 @@ function EmptyTitle({ className, ...props }) {
1406
1580
  }
1407
1581
 
1408
1582
  // src/components/ReportEmpty.tsx
1409
- import { jsx as jsx29 } from "react/jsx-runtime";
1583
+ import { jsx as jsx30 } from "react/jsx-runtime";
1410
1584
  function ReportEmpty({ message }) {
1411
- return /* @__PURE__ */ jsx29(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx29(EmptyHeader, { children: /* @__PURE__ */ jsx29(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1585
+ return /* @__PURE__ */ jsx30(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx30(EmptyHeader, { children: /* @__PURE__ */ jsx30(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1412
1586
  }
1413
1587
 
1414
1588
  // src/components/ReportSummary.tsx
1415
- import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
1589
+ import { jsx as jsx31, jsxs as jsxs18 } from "react/jsx-runtime";
1416
1590
  function ReportSummary({ className }) {
1417
1591
  const report = useReport();
1418
- return /* @__PURE__ */ jsx30(
1592
+ return /* @__PURE__ */ jsx31(
1419
1593
  ReportSummaryView,
1420
1594
  {
1421
1595
  summary: report.summary,
@@ -1441,7 +1615,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1441
1615
  if (summary.pending > 0) {
1442
1616
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1443
1617
  }
1444
- return /* @__PURE__ */ jsx30(
1618
+ return /* @__PURE__ */ jsx31(
1445
1619
  "dl",
1446
1620
  {
1447
1621
  className: cn(
@@ -1461,10 +1635,10 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1461
1635
  className: "rounded-lg border bg-card px-4 py-3",
1462
1636
  children: [
1463
1637
  /* @__PURE__ */ jsxs18("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1464
- /* @__PURE__ */ jsx30("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1638
+ /* @__PURE__ */ jsx31("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1465
1639
  c.label
1466
1640
  ] }),
1467
- /* @__PURE__ */ jsx30("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1641
+ /* @__PURE__ */ jsx31("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1468
1642
  ]
1469
1643
  },
1470
1644
  c.status
@@ -1524,7 +1698,7 @@ function prUrl(ci) {
1524
1698
  }
1525
1699
 
1526
1700
  // src/components/ReportMeta.tsx
1527
- import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
1701
+ import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
1528
1702
  function ReportMeta() {
1529
1703
  const report = useReport();
1530
1704
  const items = [];
@@ -1547,7 +1721,7 @@ function ReportMeta() {
1547
1721
  const short = sha.slice(0, 8);
1548
1722
  items.push({
1549
1723
  label: "Commit",
1550
- value: url ? /* @__PURE__ */ jsx31("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1724
+ value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1551
1725
  });
1552
1726
  }
1553
1727
  if (report.ci) {
@@ -1564,7 +1738,7 @@ function ReportMeta() {
1564
1738
  const label = `#${report.ci.prNumber}`;
1565
1739
  items.push({
1566
1740
  label: "PR",
1567
- value: url ? /* @__PURE__ */ jsx31("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1741
+ value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1568
1742
  });
1569
1743
  }
1570
1744
  if (items.length === 0) return null;
@@ -1574,26 +1748,26 @@ function ReportMeta() {
1574
1748
  // so it works with no JS (the SSR/no-hydration report still toggles).
1575
1749
  /* @__PURE__ */ jsxs19("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1576
1750
  /* @__PURE__ */ jsxs19("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1577
- /* @__PURE__ */ jsx31("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1751
+ /* @__PURE__ */ jsx32("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1578
1752
  "Run details"
1579
1753
  ] }),
1580
- /* @__PURE__ */ jsx31("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ jsxs19("div", { className: "flex items-baseline gap-1.5", children: [
1581
- /* @__PURE__ */ jsx31("dt", { className: "font-medium", children: item.label }),
1582
- /* @__PURE__ */ jsx31("dd", { className: "font-mono text-foreground", children: item.value })
1754
+ /* @__PURE__ */ jsx32("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ jsxs19("div", { className: "flex items-baseline gap-1.5", children: [
1755
+ /* @__PURE__ */ jsx32("dt", { className: "font-medium", children: item.label }),
1756
+ /* @__PURE__ */ jsx32("dd", { className: "font-mono text-foreground", children: item.value })
1583
1757
  ] }, item.label)) })
1584
1758
  ] })
1585
1759
  );
1586
1760
  }
1587
1761
 
1588
1762
  // src/components/ReportSchemaError.tsx
1589
- import { jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
1763
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1590
1764
  function ReportSchemaError({ error }) {
1591
1765
  return /* @__PURE__ */ jsxs20("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1592
- /* @__PURE__ */ jsx32("p", { children: /* @__PURE__ */ jsx32("strong", { children: "Report could not be displayed." }) }),
1593
- /* @__PURE__ */ jsx32("p", { children: error.message }),
1766
+ /* @__PURE__ */ jsx33("p", { children: /* @__PURE__ */ jsx33("strong", { children: "Report could not be displayed." }) }),
1767
+ /* @__PURE__ */ jsx33("p", { children: error.message }),
1594
1768
  error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs20("p", { children: [
1595
1769
  "The report bundle is newer than this version of ",
1596
- /* @__PURE__ */ jsx32("code", { children: "executable-stories-react" }),
1770
+ /* @__PURE__ */ jsx33("code", { children: "executable-stories-react" }),
1597
1771
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1598
1772
  ] }) : null,
1599
1773
  error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs20("details", { children: [
@@ -1602,20 +1776,20 @@ function ReportSchemaError({ error }) {
1602
1776
  " validation issue",
1603
1777
  error.issues.length === 1 ? "" : "s"
1604
1778
  ] }),
1605
- /* @__PURE__ */ jsx32("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1779
+ /* @__PURE__ */ jsx33("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1606
1780
  ] }) : null
1607
1781
  ] });
1608
1782
  }
1609
1783
 
1610
1784
  // src/components/ReportShell.tsx
1611
- import { Fragment as Fragment10, jsx as jsx33, jsxs as jsxs21 } from "react/jsx-runtime";
1785
+ import { Fragment as Fragment10, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1612
1786
  function ReportErrorShell({
1613
1787
  error,
1614
1788
  className,
1615
1789
  title,
1616
1790
  dataTheme
1617
1791
  }) {
1618
- return /* @__PURE__ */ jsx33("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx33(ReportSchemaError, { error }) });
1792
+ return /* @__PURE__ */ jsx34("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx34(ReportSchemaError, { error }) });
1619
1793
  }
1620
1794
 
1621
1795
  // src/interactive/ReportInteractive.tsx
@@ -1624,12 +1798,12 @@ import { Monitor, Moon, Sun } from "lucide-react";
1624
1798
  // src/components/ui/switch.tsx
1625
1799
  import "react";
1626
1800
  import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
1627
- import { jsx as jsx34 } from "react/jsx-runtime";
1801
+ import { jsx as jsx35 } from "react/jsx-runtime";
1628
1802
  function Switch({
1629
1803
  className,
1630
1804
  ...props
1631
1805
  }) {
1632
- return /* @__PURE__ */ jsx34(
1806
+ return /* @__PURE__ */ jsx35(
1633
1807
  SwitchPrimitive.Root,
1634
1808
  {
1635
1809
  "data-slot": "switch",
@@ -1638,7 +1812,7 @@ function Switch({
1638
1812
  className
1639
1813
  ),
1640
1814
  ...props,
1641
- children: /* @__PURE__ */ jsx34(
1815
+ children: /* @__PURE__ */ jsx35(
1642
1816
  SwitchPrimitive.Thumb,
1643
1817
  {
1644
1818
  "data-slot": "switch-thumb",
@@ -1654,7 +1828,7 @@ import * as React5 from "react";
1654
1828
  import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
1655
1829
  import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
1656
1830
  import { cva as cva3 } from "class-variance-authority";
1657
- import { jsx as jsx35 } from "react/jsx-runtime";
1831
+ import { jsx as jsx36 } from "react/jsx-runtime";
1658
1832
  var toggleItemVariants = cva3(
1659
1833
  "inline-flex items-center justify-center gap-1.5 rounded-md font-medium whitespace-nowrap text-muted-foreground transition-[color,background-color,box-shadow] outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-background data-[pressed]:text-foreground data-[pressed]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1660
1834
  {
@@ -1676,13 +1850,13 @@ function ToggleGroup({
1676
1850
  children,
1677
1851
  ...props
1678
1852
  }) {
1679
- return /* @__PURE__ */ jsx35(
1853
+ return /* @__PURE__ */ jsx36(
1680
1854
  ToggleGroupPrimitive,
1681
1855
  {
1682
1856
  "data-slot": "toggle-group",
1683
1857
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1684
1858
  ...props,
1685
- children: /* @__PURE__ */ jsx35(ToggleGroupContext.Provider, { value: { size }, children })
1859
+ children: /* @__PURE__ */ jsx36(ToggleGroupContext.Provider, { value: { size }, children })
1686
1860
  }
1687
1861
  );
1688
1862
  }
@@ -1693,7 +1867,7 @@ function ToggleGroupItem({
1693
1867
  ...props
1694
1868
  }) {
1695
1869
  const context = React5.useContext(ToggleGroupContext);
1696
- return /* @__PURE__ */ jsx35(
1870
+ return /* @__PURE__ */ jsx36(
1697
1871
  TogglePrimitive,
1698
1872
  {
1699
1873
  "data-slot": "toggle-group-item",
@@ -1713,9 +1887,9 @@ import { Search } from "lucide-react";
1713
1887
 
1714
1888
  // src/components/ui/input.tsx
1715
1889
  import "react";
1716
- import { jsx as jsx36 } from "react/jsx-runtime";
1890
+ import { jsx as jsx37 } from "react/jsx-runtime";
1717
1891
  function Input({ className, type, ...props }) {
1718
- return /* @__PURE__ */ jsx36(
1892
+ return /* @__PURE__ */ jsx37(
1719
1893
  "input",
1720
1894
  {
1721
1895
  type,
@@ -1732,7 +1906,7 @@ function Input({ className, type, ...props }) {
1732
1906
  }
1733
1907
 
1734
1908
  // src/interactive/ReportSearch.tsx
1735
- import { jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
1909
+ import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
1736
1910
  var ReportSearch = forwardRef(
1737
1911
  function ReportSearch2(props, ref) {
1738
1912
  const {
@@ -1755,16 +1929,16 @@ var ReportSearch = forwardRef(
1755
1929
  }
1756
1930
  }
1757
1931
  return /* @__PURE__ */ jsxs22("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1758
- /* @__PURE__ */ jsx37("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1932
+ /* @__PURE__ */ jsx38("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1759
1933
  /* @__PURE__ */ jsxs22("div", { className: "relative w-full sm:w-72", children: [
1760
- /* @__PURE__ */ jsx37(
1934
+ /* @__PURE__ */ jsx38(
1761
1935
  Search,
1762
1936
  {
1763
1937
  "aria-hidden": true,
1764
1938
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
1765
1939
  }
1766
1940
  ),
1767
- /* @__PURE__ */ jsx37(
1941
+ /* @__PURE__ */ jsx38(
1768
1942
  Input,
1769
1943
  {
1770
1944
  ref,
@@ -1801,6 +1975,54 @@ var ReportSearch = forwardRef(
1801
1975
  // src/interactive/ReportFailureBanner.tsx
1802
1976
  import { useCallback as useCallback2 } from "react";
1803
1977
 
1978
+ // src/lib/hash-state.ts
1979
+ var DEFAULT_URL_STATE = {
1980
+ query: "",
1981
+ status: "all",
1982
+ tags: [],
1983
+ detail: "full"
1984
+ };
1985
+ var STATUSES = ["all", "passed", "failed", "skipped", "pending"];
1986
+ function splitHash(hash) {
1987
+ const raw = hash.replace(/^#/, "");
1988
+ const split = raw.indexOf("?");
1989
+ if (split === -1) return { anchor: raw, params: new URLSearchParams() };
1990
+ return { anchor: raw.slice(0, split), params: new URLSearchParams(raw.slice(split + 1)) };
1991
+ }
1992
+ function decodeUrlState(params) {
1993
+ const status = params.get("status");
1994
+ const tags = params.get("tags");
1995
+ return {
1996
+ query: params.get("q") ?? DEFAULT_URL_STATE.query,
1997
+ status: STATUSES.includes(status) ? status : DEFAULT_URL_STATE.status,
1998
+ tags: tags ? tags.split(",").filter(Boolean) : [],
1999
+ detail: params.get("docs") === "0" ? "minimal" : "full"
2000
+ };
2001
+ }
2002
+ function encodeUrlState(state) {
2003
+ const params = new URLSearchParams();
2004
+ if (state.query) params.set("q", state.query);
2005
+ if (state.status !== "all") params.set("status", state.status);
2006
+ if (state.tags.length > 0) params.set("tags", state.tags.join(","));
2007
+ if (state.detail === "minimal") params.set("docs", "0");
2008
+ return params.toString();
2009
+ }
2010
+ function formatHash(anchor, params) {
2011
+ if (params) return `#${anchor}?${params}`;
2012
+ return anchor ? `#${anchor}` : "";
2013
+ }
2014
+ function writeHash(anchor, params) {
2015
+ if (typeof window === "undefined") return;
2016
+ const next = formatHash(anchor, params);
2017
+ if (next === window.location.hash) return;
2018
+ const url = next || window.location.href.split("#")[0];
2019
+ try {
2020
+ window.history.replaceState(window.history.state, "", url);
2021
+ } catch {
2022
+ window.location.hash = next.slice(1);
2023
+ }
2024
+ }
2025
+
1804
2026
  // src/lib/scroll.ts
1805
2027
  function prefersReducedMotion() {
1806
2028
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
@@ -1816,13 +2038,13 @@ function scrollToScenarioId(scenarioId, options = {}) {
1816
2038
  behavior: prefersReducedMotion() ? "auto" : "smooth",
1817
2039
  block: "start"
1818
2040
  });
1819
- if (options.updateHash !== false && typeof history !== "undefined") {
1820
- history.replaceState(null, "", `#${scenarioId}`);
2041
+ if (options.updateHash !== false) {
2042
+ writeHash(scenarioId, splitHash(window.location.hash).params.toString());
1821
2043
  }
1822
2044
  }
1823
2045
 
1824
2046
  // src/interactive/ReportFailureBanner.tsx
1825
- import { jsx as jsx38, jsxs as jsxs23 } from "react/jsx-runtime";
2047
+ import { jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
1826
2048
  function ReportFailureBanner({ failures }) {
1827
2049
  const first = failures[0];
1828
2050
  const jumpToFirst = useCallback2(() => {
@@ -1839,12 +2061,12 @@ function ReportFailureBanner({ failures }) {
1839
2061
  "aria-label": "Failure summary",
1840
2062
  children: [
1841
2063
  /* @__PURE__ */ jsxs23("span", { className: "es-failure-banner-text", children: [
1842
- /* @__PURE__ */ jsx38("strong", { children: failures.length }),
2064
+ /* @__PURE__ */ jsx39("strong", { children: failures.length }),
1843
2065
  " ",
1844
2066
  "failure",
1845
2067
  failures.length === 1 ? "" : "s"
1846
2068
  ] }),
1847
- /* @__PURE__ */ jsx38(
2069
+ /* @__PURE__ */ jsx39(
1848
2070
  "button",
1849
2071
  {
1850
2072
  type: "button",
@@ -1861,7 +2083,7 @@ function ReportFailureBanner({ failures }) {
1861
2083
 
1862
2084
  // src/interactive/ReportFreshness.tsx
1863
2085
  import { useEffect, useState as useState2 } from "react";
1864
- import { Fragment as Fragment11, jsx as jsx39, jsxs as jsxs24 } from "react/jsx-runtime";
2086
+ import { Fragment as Fragment11, jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
1865
2087
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1866
2088
  const [now, setNow] = useState2(nowMs);
1867
2089
  useEffect(() => {
@@ -1869,7 +2091,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1869
2091
  }, [nowMs]);
1870
2092
  if (lastRunMs === void 0 || now === void 0) return null;
1871
2093
  const age = formatRelativeAge(lastRunMs, now);
1872
- const runLink = ciUrl ? /* @__PURE__ */ jsx39(
2094
+ const runLink = ciUrl ? /* @__PURE__ */ jsx40(
1873
2095
  "a",
1874
2096
  {
1875
2097
  href: ciUrl,
@@ -1903,13 +2125,13 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1903
2125
  }
1904
2126
 
1905
2127
  // src/interactive/ReportLastRunDelta.tsx
1906
- import { Fragment as Fragment12, jsx as jsx40, jsxs as jsxs25 } from "react/jsx-runtime";
2128
+ import { Fragment as Fragment12, jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
1907
2129
  var MAX_LINKS = 5;
1908
2130
  function ScenarioLinks({ scenarios, tone }) {
1909
2131
  return /* @__PURE__ */ jsxs25(Fragment12, { children: [
1910
2132
  scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ jsxs25("span", { children: [
1911
2133
  i > 0 ? ", " : "",
1912
- /* @__PURE__ */ jsx40("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2134
+ /* @__PURE__ */ jsx41("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
1913
2135
  ] }, s.id)),
1914
2136
  scenarios.length > MAX_LINKS ? /* @__PURE__ */ jsxs25("span", { children: [
1915
2137
  " and ",
@@ -1919,29 +2141,29 @@ function ScenarioLinks({ scenarios, tone }) {
1919
2141
  ] });
1920
2142
  }
1921
2143
  function ReportLastRunDelta({
1922
- history: history2,
2144
+ history,
1923
2145
  report
1924
2146
  }) {
1925
- if (!history2) return null;
2147
+ if (!history) return null;
1926
2148
  const scenarios = report.features.flatMap((f) => f.scenarios.map((s) => ({ id: s.id, title: s.title })));
1927
- const delta = diffSinceLastRun(history2, scenarios);
2149
+ const delta = diffSinceLastRun(history, scenarios);
1928
2150
  if (!delta) return null;
1929
2151
  const { newlyFailing, newlyPassing, added } = delta;
1930
2152
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
1931
- return /* @__PURE__ */ jsx40("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2153
+ return /* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
1932
2154
  }
1933
2155
  return /* @__PURE__ */ jsxs25("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
1934
- /* @__PURE__ */ jsx40("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2156
+ /* @__PURE__ */ jsx41("span", { className: "font-medium text-foreground", children: "Since last run:" }),
1935
2157
  newlyFailing.length > 0 ? /* @__PURE__ */ jsxs25("span", { className: "ml-2 text-fail", children: [
1936
2158
  newlyFailing.length,
1937
2159
  " newly failing (",
1938
- /* @__PURE__ */ jsx40(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2160
+ /* @__PURE__ */ jsx41(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
1939
2161
  ")"
1940
2162
  ] }) : null,
1941
2163
  newlyPassing.length > 0 ? /* @__PURE__ */ jsxs25("span", { className: "ml-2 text-pass", children: [
1942
2164
  newlyPassing.length,
1943
2165
  " fixed (",
1944
- /* @__PURE__ */ jsx40(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2166
+ /* @__PURE__ */ jsx41(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
1945
2167
  ")"
1946
2168
  ] }) : null,
1947
2169
  added.length > 0 ? /* @__PURE__ */ jsxs25("span", { className: "ml-2", children: [
@@ -2008,7 +2230,7 @@ function Button({
2008
2230
  }
2009
2231
 
2010
2232
  // src/interactive/ReportShortcutsHelp.tsx
2011
- import { jsx as jsx41, jsxs as jsxs26 } from "react/jsx-runtime";
2233
+ import { jsx as jsx42, jsxs as jsxs26 } from "react/jsx-runtime";
2012
2234
  var SHORTCUTS = [
2013
2235
  { keys: "/", description: "Focus search" },
2014
2236
  { keys: "f", description: "Jump to next failure" },
@@ -2020,9 +2242,9 @@ var SHORTCUTS = [
2020
2242
  ];
2021
2243
  function Keys({ keys }) {
2022
2244
  const parts = keys.split(" ");
2023
- return /* @__PURE__ */ jsx41("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center gap-1", children: [
2024
- i > 0 ? /* @__PURE__ */ jsx41("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2025
- /* @__PURE__ */ jsx41("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2245
+ return /* @__PURE__ */ jsx42("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center gap-1", children: [
2246
+ i > 0 ? /* @__PURE__ */ jsx42("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2247
+ /* @__PURE__ */ jsx42("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2026
2248
  ] }, k)) });
2027
2249
  }
2028
2250
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2041,12 +2263,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2041
2263
  "aria-label": "Keyboard shortcuts",
2042
2264
  onClose,
2043
2265
  children: [
2044
- /* @__PURE__ */ jsx41("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2045
- /* @__PURE__ */ jsx41("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs26("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2046
- /* @__PURE__ */ jsx41("dt", { className: "justify-self-start", children: /* @__PURE__ */ jsx41(Keys, { keys: s.keys }) }),
2047
- /* @__PURE__ */ jsx41("dd", { className: "text-sm text-muted-foreground", children: s.description })
2266
+ /* @__PURE__ */ jsx42("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2267
+ /* @__PURE__ */ jsx42("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs26("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2268
+ /* @__PURE__ */ jsx42("dt", { className: "justify-self-start", children: /* @__PURE__ */ jsx42(Keys, { keys: s.keys }) }),
2269
+ /* @__PURE__ */ jsx42("dd", { className: "text-sm text-muted-foreground", children: s.description })
2048
2270
  ] }, s.keys)) }),
2049
- /* @__PURE__ */ jsx41("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ jsx41(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2271
+ /* @__PURE__ */ jsx42("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ jsx42(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2050
2272
  ]
2051
2273
  }
2052
2274
  );
@@ -2108,14 +2330,16 @@ function useKeyboardShortcuts(handlers) {
2108
2330
  }
2109
2331
 
2110
2332
  // src/interactive/use-deep-link-scroll.ts
2111
- import { useEffect as useEffect4 } from "react";
2333
+ import { useEffect as useEffect4, useRef as useRef3 } from "react";
2112
2334
  function useDeepLinkScroll() {
2335
+ const lastAnchor = useRef3(null);
2113
2336
  useEffect4(() => {
2114
2337
  function scrollToHash() {
2115
- const hash = window.location.hash.replace(/^#/, "");
2116
- if (!hash) return;
2338
+ const { anchor } = splitHash(window.location.hash);
2339
+ if (!anchor || anchor === lastAnchor.current) return;
2340
+ lastAnchor.current = anchor;
2117
2341
  requestAnimationFrame(() => {
2118
- scrollToScenarioId(hash, { updateHash: false });
2342
+ scrollToScenarioId(anchor, { updateHash: false });
2119
2343
  });
2120
2344
  }
2121
2345
  scrollToHash();
@@ -2247,7 +2471,7 @@ function failuresFirst(report) {
2247
2471
 
2248
2472
  // src/interactive/ReportFilters.tsx
2249
2473
  import { useState as useState3 } from "react";
2250
- import { jsx as jsx42, jsxs as jsxs27 } from "react/jsx-runtime";
2474
+ import { jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
2251
2475
  var TAG_LIMIT = 12;
2252
2476
  function ReportFilters({
2253
2477
  statuses,
@@ -2262,7 +2486,7 @@ function ReportFilters({
2262
2486
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2263
2487
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2264
2488
  return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-2", children: [
2265
- statuses.length > 0 ? /* @__PURE__ */ jsx42("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2489
+ statuses.length > 0 ? /* @__PURE__ */ jsx43("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2266
2490
  const active = status === opt.key;
2267
2491
  return /* @__PURE__ */ jsxs27(
2268
2492
  "button",
@@ -2276,7 +2500,7 @@ function ReportFilters({
2276
2500
  ),
2277
2501
  children: [
2278
2502
  opt.label,
2279
- /* @__PURE__ */ jsx42("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2503
+ /* @__PURE__ */ jsx43("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2280
2504
  ]
2281
2505
  },
2282
2506
  opt.key
@@ -2285,14 +2509,14 @@ function ReportFilters({
2285
2509
  tags.length > 0 ? /* @__PURE__ */ jsxs27("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2286
2510
  visibleTags.map((tag) => {
2287
2511
  const active = activeTags.includes(tag);
2288
- return /* @__PURE__ */ jsx42("li", { children: /* @__PURE__ */ jsx42(
2512
+ return /* @__PURE__ */ jsx43("li", { children: /* @__PURE__ */ jsx43(
2289
2513
  "button",
2290
2514
  {
2291
2515
  type: "button",
2292
2516
  "aria-pressed": active,
2293
2517
  onClick: () => onToggleTag(tag),
2294
2518
  className: "cursor-pointer",
2295
- children: /* @__PURE__ */ jsx42(
2519
+ children: /* @__PURE__ */ jsx43(
2296
2520
  Badge,
2297
2521
  {
2298
2522
  variant: "tag",
@@ -2305,7 +2529,7 @@ function ReportFilters({
2305
2529
  }
2306
2530
  ) }, tag);
2307
2531
  }),
2308
- tags.length > TAG_LIMIT ? /* @__PURE__ */ jsx42("li", { children: /* @__PURE__ */ jsx42(
2532
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ jsx43("li", { children: /* @__PURE__ */ jsx43(
2309
2533
  "button",
2310
2534
  {
2311
2535
  type: "button",
@@ -2321,7 +2545,7 @@ function ReportFilters({
2321
2545
 
2322
2546
  // src/interactive/ReportToc.tsx
2323
2547
  import { useEffect as useEffect5, useMemo as useMemo2, useState as useState4 } from "react";
2324
- import { jsx as jsx43, jsxs as jsxs28 } from "react/jsx-runtime";
2548
+ import { jsx as jsx44, jsxs as jsxs28 } from "react/jsx-runtime";
2325
2549
  var DOT = {
2326
2550
  passed: "bg-pass",
2327
2551
  failed: "bg-fail",
@@ -2350,11 +2574,11 @@ function TocContent({ onNavigate }) {
2350
2574
  return () => observer.disconnect();
2351
2575
  }, [scenarioIds]);
2352
2576
  if (report.features.length === 0) return null;
2353
- return /* @__PURE__ */ jsx43("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ jsxs28("li", { children: [
2354
- /* @__PURE__ */ jsx43("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2355
- /* @__PURE__ */ jsx43("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2577
+ return /* @__PURE__ */ jsx44("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ jsxs28("li", { children: [
2578
+ /* @__PURE__ */ jsx44("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2579
+ /* @__PURE__ */ jsx44("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2356
2580
  const active = activeId === scenario.id;
2357
- return /* @__PURE__ */ jsx43("li", { children: /* @__PURE__ */ jsxs28(
2581
+ return /* @__PURE__ */ jsx44("li", { children: /* @__PURE__ */ jsxs28(
2358
2582
  "a",
2359
2583
  {
2360
2584
  href: `#${scenario.id}`,
@@ -2369,14 +2593,14 @@ function TocContent({ onNavigate }) {
2369
2593
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2370
2594
  ),
2371
2595
  children: [
2372
- /* @__PURE__ */ jsx43(
2596
+ /* @__PURE__ */ jsx44(
2373
2597
  "span",
2374
2598
  {
2375
2599
  "aria-hidden": true,
2376
2600
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2377
2601
  }
2378
2602
  ),
2379
- /* @__PURE__ */ jsx43("span", { className: "break-words", children: scenario.title })
2603
+ /* @__PURE__ */ jsx44("span", { className: "break-words", children: scenario.title })
2380
2604
  ]
2381
2605
  }
2382
2606
  ) }, scenario.id);
@@ -2392,8 +2616,8 @@ function ReportToc() {
2392
2616
  "aria-label": "Table of contents",
2393
2617
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2394
2618
  children: [
2395
- /* @__PURE__ */ jsx43("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2396
- /* @__PURE__ */ jsx43(TocContent, {})
2619
+ /* @__PURE__ */ jsx44("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2620
+ /* @__PURE__ */ jsx44(TocContent, {})
2397
2621
  ]
2398
2622
  }
2399
2623
  );
@@ -2403,31 +2627,31 @@ function ReportToc() {
2403
2627
  import { useState as useState5 } from "react";
2404
2628
  import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
2405
2629
  import { ListIcon, XIcon } from "lucide-react";
2406
- import { jsx as jsx44, jsxs as jsxs29 } from "react/jsx-runtime";
2630
+ import { jsx as jsx45, jsxs as jsxs29 } from "react/jsx-runtime";
2407
2631
  function ReportTocDrawer() {
2408
2632
  const report = useReport();
2409
2633
  const [open, setOpen] = useState5(false);
2410
2634
  if (report.features.length === 0) return null;
2411
2635
  return /* @__PURE__ */ jsxs29(DialogPrimitive.Root, { open, onOpenChange: setOpen, children: [
2412
2636
  /* @__PURE__ */ jsxs29(DialogPrimitive.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
2413
- /* @__PURE__ */ jsx44(ListIcon, { "aria-hidden": true }),
2637
+ /* @__PURE__ */ jsx45(ListIcon, { "aria-hidden": true }),
2414
2638
  " Contents"
2415
2639
  ] }),
2416
2640
  /* @__PURE__ */ jsxs29(DialogPrimitive.Portal, { children: [
2417
- /* @__PURE__ */ jsx44(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2641
+ /* @__PURE__ */ jsx45(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2418
2642
  /* @__PURE__ */ jsxs29(DialogPrimitive.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
2419
2643
  /* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between", children: [
2420
- /* @__PURE__ */ jsx44(DialogPrimitive.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2421
- /* @__PURE__ */ jsx44(
2644
+ /* @__PURE__ */ jsx45(DialogPrimitive.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2645
+ /* @__PURE__ */ jsx45(
2422
2646
  DialogPrimitive.Close,
2423
2647
  {
2424
2648
  "aria-label": "Close contents",
2425
2649
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2426
- children: /* @__PURE__ */ jsx44(XIcon, { "aria-hidden": true })
2650
+ children: /* @__PURE__ */ jsx45(XIcon, { "aria-hidden": true })
2427
2651
  }
2428
2652
  )
2429
2653
  ] }),
2430
- /* @__PURE__ */ jsx44(TocContent, { onNavigate: () => setOpen(false) })
2654
+ /* @__PURE__ */ jsx45(TocContent, { onNavigate: () => setOpen(false) })
2431
2655
  ] })
2432
2656
  ] })
2433
2657
  ] });
@@ -2481,15 +2705,43 @@ function useTheme() {
2481
2705
  return { pref, setPref };
2482
2706
  }
2483
2707
 
2708
+ // src/interactive/use-url-state.ts
2709
+ import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef4, useState as useState7 } from "react";
2710
+ var WRITE_DELAY_MS = 250;
2711
+ function useUrlState() {
2712
+ const [state, setState] = useState7(DEFAULT_URL_STATE);
2713
+ const ready = useRef4(false);
2714
+ useEffect7(() => {
2715
+ const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
2716
+ read();
2717
+ ready.current = true;
2718
+ window.addEventListener("hashchange", read);
2719
+ return () => window.removeEventListener("hashchange", read);
2720
+ }, []);
2721
+ useEffect7(() => {
2722
+ if (!ready.current) return;
2723
+ const timer = setTimeout(
2724
+ () => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
2725
+ WRITE_DELAY_MS
2726
+ );
2727
+ return () => clearTimeout(timer);
2728
+ }, [state]);
2729
+ const update = useCallback4(
2730
+ (patch) => setState((prev) => ({ ...prev, ...patch })),
2731
+ []
2732
+ );
2733
+ return [state, update];
2734
+ }
2735
+
2484
2736
  // src/interactive/ReportInteractive.tsx
2485
- import { Fragment as Fragment13, jsx as jsx45, jsxs as jsxs30 } from "react/jsx-runtime";
2737
+ import { Fragment as Fragment13, jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
2486
2738
  function ReportInteractive(props) {
2487
2739
  const { report, className, title, dataTheme } = props;
2488
2740
  const result = unwrapReport(report);
2489
2741
  if (!result.ok) {
2490
- return /* @__PURE__ */ jsx45(ReportErrorShell, { error: result.error, className, title, dataTheme });
2742
+ return /* @__PURE__ */ jsx46(ReportErrorShell, { error: result.error, className, title, dataTheme });
2491
2743
  }
2492
- return /* @__PURE__ */ jsx45(ReportInteractiveView, { ...props, report: result.data });
2744
+ return /* @__PURE__ */ jsx46(ReportInteractiveView, { ...props, report: result.data });
2493
2745
  }
2494
2746
  var THEME_OPTIONS = [
2495
2747
  { value: "light", label: "Light", Icon: Sun },
@@ -2497,7 +2749,7 @@ var THEME_OPTIONS = [
2497
2749
  { value: "dark", label: "Dark", Icon: Moon }
2498
2750
  ];
2499
2751
  function ThemeSegment({ pref, onPref }) {
2500
- return /* @__PURE__ */ jsx45(
2752
+ return /* @__PURE__ */ jsx46(
2501
2753
  ToggleGroup,
2502
2754
  {
2503
2755
  size: "sm",
@@ -2507,7 +2759,7 @@ function ThemeSegment({ pref, onPref }) {
2507
2759
  if (next) onPref(next);
2508
2760
  },
2509
2761
  "aria-label": "Color theme",
2510
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx45(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ jsx45(Icon, {}) }, value))
2762
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx46(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ jsx46(Icon, {}) }, value))
2511
2763
  }
2512
2764
  );
2513
2765
  }
@@ -2523,13 +2775,20 @@ function ReportInteractiveView({
2523
2775
  staleAfterDays = 7,
2524
2776
  scenarioHistory
2525
2777
  }) {
2526
- const [query, setQuery] = useState7("");
2527
- const [statusFilter, setStatusFilter] = useState7("all");
2528
- const [activeTags, setActiveTags] = useState7([]);
2529
- const [detail, setDetail] = useState7("full");
2530
- const [helpOpen, setHelpOpen] = useState7(false);
2778
+ const [urlState, setUrlState] = useUrlState();
2779
+ const { query, status: statusFilter, tags: activeTags, detail } = urlState;
2780
+ const setQuery = useCallback5((next) => setUrlState({ query: next }), [setUrlState]);
2781
+ const setStatusFilter = useCallback5(
2782
+ (next) => setUrlState({ status: next }),
2783
+ [setUrlState]
2784
+ );
2785
+ const setDetail = useCallback5(
2786
+ (next) => setUrlState({ detail: next }),
2787
+ [setUrlState]
2788
+ );
2789
+ const [helpOpen, setHelpOpen] = useState8(false);
2531
2790
  const { pref: themePref, setPref: setThemePref } = useTheme();
2532
- const searchRef = useRef3(null);
2791
+ const searchRef = useRef5(null);
2533
2792
  const expandId = useId2();
2534
2793
  const docsId = useId2();
2535
2794
  const deferredQuery = useDeferredValue(query);
@@ -2553,18 +2812,20 @@ function ReportInteractiveView({
2553
2812
  return opts;
2554
2813
  }, [report]);
2555
2814
  const tagOptions = useMemo3(() => allTags(report), [report]);
2556
- const toggleTag = useCallback4(
2557
- (tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
2558
- []
2815
+ const toggleTag = useCallback5(
2816
+ (tag) => setUrlState({
2817
+ tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
2818
+ }),
2819
+ [activeTags, setUrlState]
2559
2820
  );
2560
- const [toast, setToast] = useState7(null);
2561
- const toastTimer = useRef3(null);
2562
- const showToast = useCallback4((msg) => {
2821
+ const [toast, setToast] = useState8(null);
2822
+ const toastTimer = useRef5(null);
2823
+ const showToast = useCallback5((msg) => {
2563
2824
  setToast(msg);
2564
2825
  if (toastTimer.current) clearTimeout(toastTimer.current);
2565
2826
  toastTimer.current = setTimeout(() => setToast(null), 1800);
2566
2827
  }, []);
2567
- const copy = useCallback4(
2828
+ const copy = useCallback5(
2568
2829
  (text, msg) => {
2569
2830
  if (typeof navigator !== "undefined" && navigator.clipboard) {
2570
2831
  navigator.clipboard.writeText(text).then(() => showToast(msg), () => showToast("Copy failed"));
@@ -2580,15 +2841,15 @@ function ReportInteractiveView({
2580
2841
  }),
2581
2842
  [copy]
2582
2843
  );
2583
- const failureIndexRef = useRef3(0);
2844
+ const failureIndexRef = useRef5(0);
2584
2845
  const allCollapsibleIds = useMemo3(
2585
2846
  () => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
2586
2847
  [report]
2587
2848
  );
2588
- const collapse = useCollapseState(useCallback4(() => defaultCollapsedIds(report), [report]));
2589
- const collapseAll = useCallback4(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
2590
- const [expandedAll, setExpandedAll] = useState7(() => report.summary.failed === 0);
2591
- const setExpanded = useCallback4(
2849
+ const collapse = useCollapseState(useCallback5(() => defaultCollapsedIds(report), [report]));
2850
+ const collapseAll = useCallback5(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
2851
+ const [expandedAll, setExpandedAll] = useState8(() => report.summary.failed === 0);
2852
+ const setExpanded = useCallback5(
2592
2853
  (expanded) => {
2593
2854
  if (expanded) collapse.expandAll();
2594
2855
  else collapseAll();
@@ -2596,10 +2857,10 @@ function ReportInteractiveView({
2596
2857
  },
2597
2858
  [collapse, collapseAll]
2598
2859
  );
2599
- const focusSearch = useCallback4(() => {
2860
+ const focusSearch = useCallback5(() => {
2600
2861
  searchRef.current?.focus();
2601
2862
  }, []);
2602
- const stepFailure = useCallback4(
2863
+ const stepFailure = useCallback5(
2603
2864
  (direction) => {
2604
2865
  if (failures.length === 0) return;
2605
2866
  failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
@@ -2608,10 +2869,10 @@ function ReportInteractiveView({
2608
2869
  },
2609
2870
  [failures]
2610
2871
  );
2611
- const toggleHelp = useCallback4(() => {
2872
+ const toggleHelp = useCallback5(() => {
2612
2873
  setHelpOpen((v) => !v);
2613
2874
  }, []);
2614
- const escape = useCallback4(() => {
2875
+ const escape = useCallback5(() => {
2615
2876
  if (helpOpen) setHelpOpen(false);
2616
2877
  else if (query !== "") setQuery("");
2617
2878
  }, [helpOpen, query]);
@@ -2629,7 +2890,7 @@ function ReportInteractiveView({
2629
2890
  const totalScenarios = report.summary.total;
2630
2891
  const matchedScenarios = filtered.summary.total;
2631
2892
  const showToc = !hideToc && totalScenarios > 3;
2632
- return /* @__PURE__ */ jsx45(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ jsx45(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ jsx45(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ jsx45(
2893
+ return /* @__PURE__ */ jsx46(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ jsx46(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ jsx46(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ jsx46(
2633
2894
  ReportRoot,
2634
2895
  {
2635
2896
  report: filtered,
@@ -2654,9 +2915,9 @@ function ReportInteractiveView({
2654
2915
  hideHeader ? "justify-end" : "justify-between"
2655
2916
  ),
2656
2917
  children: [
2657
- hideHeader ? null : /* @__PURE__ */ jsx45("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2918
+ hideHeader ? null : /* @__PURE__ */ jsx46("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2658
2919
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
2659
- /* @__PURE__ */ jsx45(
2920
+ /* @__PURE__ */ jsx46(
2660
2921
  ReportSearch,
2661
2922
  {
2662
2923
  ref: searchRef,
@@ -2666,16 +2927,16 @@ function ReportInteractiveView({
2666
2927
  totalCount: totalScenarios
2667
2928
  }
2668
2929
  ),
2669
- /* @__PURE__ */ jsx45(ThemeSegment, { pref: themePref, onPref: setThemePref })
2930
+ /* @__PURE__ */ jsx46(ThemeSegment, { pref: themePref, onPref: setThemePref })
2670
2931
  ] })
2671
2932
  ]
2672
2933
  }
2673
2934
  ),
2674
2935
  hideHeader ? null : /* @__PURE__ */ jsxs30(Fragment13, { children: [
2675
- /* @__PURE__ */ jsx45(ReportSummary, {}),
2676
- /* @__PURE__ */ jsx45(ReportMeta, {})
2936
+ /* @__PURE__ */ jsx46(ReportSummary, {}),
2937
+ /* @__PURE__ */ jsx46(ReportMeta, {})
2677
2938
  ] }),
2678
- /* @__PURE__ */ jsx45(
2939
+ /* @__PURE__ */ jsx46(
2679
2940
  ReportFreshness,
2680
2941
  {
2681
2942
  lastRunMs: reportLastRunMs(report),
@@ -2683,8 +2944,8 @@ function ReportInteractiveView({
2683
2944
  staleAfterDays
2684
2945
  }
2685
2946
  ),
2686
- /* @__PURE__ */ jsx45(ReportLastRunDelta, { history: scenarioHistory, report }),
2687
- /* @__PURE__ */ jsx45(
2947
+ /* @__PURE__ */ jsx46(ReportLastRunDelta, { history: scenarioHistory, report }),
2948
+ /* @__PURE__ */ jsx46(
2688
2949
  ReportFilters,
2689
2950
  {
2690
2951
  statuses: statusOptions,
@@ -2696,13 +2957,13 @@ function ReportInteractiveView({
2696
2957
  }
2697
2958
  ),
2698
2959
  /* @__PURE__ */ jsxs30("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2699
- showToc ? /* @__PURE__ */ jsx45(ReportTocDrawer, {}) : null,
2960
+ showToc ? /* @__PURE__ */ jsx46(ReportTocDrawer, {}) : null,
2700
2961
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
2701
- /* @__PURE__ */ jsx45(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2702
- /* @__PURE__ */ jsx45("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2962
+ /* @__PURE__ */ jsx46(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2963
+ /* @__PURE__ */ jsx46("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2703
2964
  ] }),
2704
2965
  /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
2705
- /* @__PURE__ */ jsx45(
2966
+ /* @__PURE__ */ jsx46(
2706
2967
  Switch,
2707
2968
  {
2708
2969
  id: docsId,
@@ -2710,16 +2971,16 @@ function ReportInteractiveView({
2710
2971
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
2711
2972
  }
2712
2973
  ),
2713
- /* @__PURE__ */ jsx45("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2974
+ /* @__PURE__ */ jsx46("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2714
2975
  ] })
2715
2976
  ] })
2716
2977
  ] }),
2717
- /* @__PURE__ */ jsx45(ReportFailureBanner, { failures }),
2978
+ /* @__PURE__ */ jsx46(ReportFailureBanner, { failures }),
2718
2979
  hasContent ? showToc ? /* @__PURE__ */ jsxs30("div", { className: "flex gap-6", children: [
2719
- /* @__PURE__ */ jsx45(ReportToc, {}),
2720
- /* @__PURE__ */ jsx45("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx45(ReportFeatureList, {}) })
2721
- ] }) : /* @__PURE__ */ jsx45("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsx45(ReportFeatureList, {}) }) : /* @__PURE__ */ jsx45(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2722
- /* @__PURE__ */ jsx45(
2980
+ /* @__PURE__ */ jsx46(ReportToc, {}),
2981
+ /* @__PURE__ */ jsx46("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx46(ReportFeatureList, {}) })
2982
+ ] }) : /* @__PURE__ */ jsx46("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsx46(ReportFeatureList, {}) }) : /* @__PURE__ */ jsx46(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2983
+ /* @__PURE__ */ jsx46(
2723
2984
  "button",
2724
2985
  {
2725
2986
  type: "button",
@@ -2730,8 +2991,8 @@ function ReportInteractiveView({
2730
2991
  children: "?"
2731
2992
  }
2732
2993
  ),
2733
- /* @__PURE__ */ jsx45(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2734
- toast ? /* @__PURE__ */ jsx45(
2994
+ /* @__PURE__ */ jsx46(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2995
+ toast ? /* @__PURE__ */ jsx46(
2735
2996
  "div",
2736
2997
  {
2737
2998
  role: "status",
@@ -2751,11 +3012,11 @@ function ReportInteractiveView({
2751
3012
  import { useMemo as useMemo4 } from "react";
2752
3013
 
2753
3014
  // src/interactive/island-renderers.tsx
2754
- import { createElement, useEffect as useEffect8, useState as useState9 } from "react";
3015
+ import { createElement, useEffect as useEffect9, useState as useState10 } from "react";
2755
3016
 
2756
3017
  // src/components/doc/MermaidView.tsx
2757
- import { useEffect as useEffect7, useRef as useRef4, useState as useState8 } from "react";
2758
- import { jsx as jsx46, jsxs as jsxs31 } from "react/jsx-runtime";
3018
+ import { useEffect as useEffect8, useRef as useRef6, useState as useState9 } from "react";
3019
+ import { Fragment as Fragment14, jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
2759
3020
  function prefersDark(el) {
2760
3021
  const themed = el?.closest("[data-theme]");
2761
3022
  if (themed?.dataset.theme === "dark") return true;
@@ -2763,23 +3024,40 @@ function prefersDark(el) {
2763
3024
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
2764
3025
  }
2765
3026
  function MermaidView({ entry, load }) {
2766
- const ref = useRef4(null);
2767
- const [svg, setSvg] = useState8(null);
2768
- useEffect7(() => {
3027
+ const ref = useRef6(null);
3028
+ const [svg, setSvg] = useState9(null);
3029
+ const [error, setError] = useState9(null);
3030
+ useEffect8(() => {
2769
3031
  let cancelled = false;
2770
3032
  const draw = async () => {
3033
+ let mermaid;
3034
+ try {
3035
+ mermaid = await load();
3036
+ } catch {
3037
+ if (!cancelled) {
3038
+ setSvg(null);
3039
+ setError(null);
3040
+ }
3041
+ return;
3042
+ }
2771
3043
  try {
2772
- const mermaid = await load();
2773
3044
  mermaid.initialize({
2774
3045
  startOnLoad: false,
2775
3046
  securityLevel: "strict",
2776
3047
  theme: prefersDark(ref.current) ? "dark" : "default"
2777
3048
  });
3049
+ await mermaid.parse(entry.code);
2778
3050
  const id = `es-mermaid-${Math.random().toString(36).slice(2)}`;
2779
3051
  const { svg: rendered } = await mermaid.render(id, entry.code);
2780
- if (!cancelled) setSvg(rendered);
2781
- } catch {
2782
- if (!cancelled) setSvg(null);
3052
+ if (!cancelled) {
3053
+ setSvg(rendered);
3054
+ setError(null);
3055
+ }
3056
+ } catch (err) {
3057
+ if (!cancelled) {
3058
+ setSvg(null);
3059
+ setError(err instanceof Error ? err.message : String(err));
3060
+ }
2783
3061
  }
2784
3062
  };
2785
3063
  void draw();
@@ -2793,8 +3071,8 @@ function MermaidView({ entry, load }) {
2793
3071
  }, [entry.code, load]);
2794
3072
  if (svg) {
2795
3073
  return /* @__PURE__ */ jsxs31("figure", { ref, className: "my-3", children: [
2796
- entry.title ? /* @__PURE__ */ jsx46("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
2797
- /* @__PURE__ */ jsx46(
3074
+ entry.title ? /* @__PURE__ */ jsx47("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3075
+ /* @__PURE__ */ jsx47(
2798
3076
  "div",
2799
3077
  {
2800
3078
  role: "img",
@@ -2805,11 +3083,25 @@ function MermaidView({ entry, load }) {
2805
3083
  )
2806
3084
  ] });
2807
3085
  }
2808
- return /* @__PURE__ */ jsx46(MermaidSource, { entry, ref });
3086
+ return /* @__PURE__ */ jsxs31(Fragment14, { children: [
3087
+ error ? /* @__PURE__ */ jsxs31(
3088
+ "p",
3089
+ {
3090
+ role: "alert",
3091
+ "data-es-mermaid-error": true,
3092
+ className: "mt-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
3093
+ children: [
3094
+ "Diagram failed to render: ",
3095
+ error
3096
+ ]
3097
+ }
3098
+ ) : null,
3099
+ /* @__PURE__ */ jsx47(MermaidSource, { entry, ref })
3100
+ ] });
2809
3101
  }
2810
3102
 
2811
3103
  // src/interactive/island-renderers.tsx
2812
- import { jsx as jsx47 } from "react/jsx-runtime";
3104
+ import { jsx as jsx48 } from "react/jsx-runtime";
2813
3105
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
2814
3106
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
2815
3107
  function cachedCdnModule(url) {
@@ -2826,8 +3118,8 @@ function cachedCdnModule(url) {
2826
3118
  var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
2827
3119
  var loadHljs = cachedCdnModule(HLJS_ESM);
2828
3120
  function CdnHighlightedCode({ entry }) {
2829
- const [html, setHtml] = useState9(null);
2830
- useEffect8(() => {
3121
+ const [html, setHtml] = useState10(null);
3122
+ useEffect9(() => {
2831
3123
  let cancelled = false;
2832
3124
  void (async () => {
2833
3125
  try {
@@ -2843,13 +3135,13 @@ function CdnHighlightedCode({ entry }) {
2843
3135
  cancelled = true;
2844
3136
  };
2845
3137
  }, [entry.content, entry.lang]);
2846
- return /* @__PURE__ */ jsx47(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx47(
3138
+ return /* @__PURE__ */ jsx48(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx48(
2847
3139
  "code",
2848
3140
  {
2849
3141
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
2850
3142
  dangerouslySetInnerHTML: { __html: html }
2851
3143
  }
2852
- ) : /* @__PURE__ */ jsx47("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3144
+ ) : /* @__PURE__ */ jsx48("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
2853
3145
  }
2854
3146
  function buildIslandRenderers(opts) {
2855
3147
  const renderers = {};
@@ -2863,7 +3155,7 @@ function buildIslandRenderers(opts) {
2863
3155
  }
2864
3156
 
2865
3157
  // src/interactive/ReportInteractiveIsland.tsx
2866
- import { jsx as jsx48 } from "react/jsx-runtime";
3158
+ import { jsx as jsx49 } from "react/jsx-runtime";
2867
3159
  function ReportInteractiveIsland({
2868
3160
  report,
2869
3161
  title,
@@ -2877,7 +3169,7 @@ function ReportInteractiveIsland({
2877
3169
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
2878
3170
  [syntaxHighlighting, mermaid]
2879
3171
  );
2880
- return /* @__PURE__ */ jsx48(
3172
+ return /* @__PURE__ */ jsx49(
2881
3173
  ReportInteractive,
2882
3174
  {
2883
3175
  report,