executable-stories-react 0.15.0 → 0.17.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.
@@ -48,8 +48,8 @@ module.exports = __toCommonJS(interactive_exports);
48
48
 
49
49
  // src/interactive/ReportInteractive.tsx
50
50
  var import_lucide_react7 = require("lucide-react");
51
- var import_react23 = require("webmcpable/react");
52
- var import_react24 = require("react");
51
+ var import_react25 = require("webmcpable/react");
52
+ var import_react26 = require("react");
53
53
 
54
54
  // src/components/ui/empty.tsx
55
55
  var import_class_variance_authority = require("class-variance-authority");
@@ -420,12 +420,29 @@ function DocVideo({ entry }) {
420
420
  }
421
421
 
422
422
  // src/components/doc/DocHtml.tsx
423
+ var import_react4 = require("react");
423
424
  var import_jsx_runtime14 = require("react/jsx-runtime");
425
+ var MEASURE_SCRIPT = "<script>(function(){if(parent===window)return;var last=0,queued=false;var measure=function(){var kids=document.body.children,h=0;for(var i=0;i<kids.length;i++)h=Math.max(h,kids[i].getBoundingClientRect().bottom);return Math.ceil(h||document.body.getBoundingClientRect().bottom)};var post=function(){queued=false;var h=measure();if(h===last)return;last=h;parent.postMessage({__esHtmlHeight:h},'*')};var schedule=function(){if(queued)return;queued=true;requestAnimationFrame(post)};var start=function(){new ResizeObserver(schedule).observe(document.body);new MutationObserver(schedule).observe(document.body,{subtree:true,childList:true,attributes:true,characterData:true});addEventListener('load',schedule);post()};if(document.body)start();else addEventListener('DOMContentLoaded',start)})();</script>";
426
+ var MAX_MEASURED_HEIGHT = 5e3;
424
427
  function DocHtml({ entry }) {
425
- const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
428
+ const declaredHeight = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
426
429
  const title = entry.title ?? "Embedded HTML";
427
430
  const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
428
- const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
431
+ const frameRef = (0, import_react4.useRef)(null);
432
+ const [measuredHeight, setMeasuredHeight] = (0, import_react4.useState)();
433
+ (0, import_react4.useEffect)(() => {
434
+ if (entry.content === void 0) return;
435
+ const onMessage = (event) => {
436
+ if (event.source !== frameRef.current?.contentWindow) return;
437
+ const reported = event.data?.__esHtmlHeight;
438
+ if (typeof reported !== "number" || !Number.isFinite(reported) || reported <= 0) return;
439
+ setMeasuredHeight(Math.min(reported, MAX_MEASURED_HEIGHT));
440
+ };
441
+ window.addEventListener("message", onMessage);
442
+ return () => window.removeEventListener("message", onMessage);
443
+ }, [entry.content]);
444
+ const height = measuredHeight === void 0 ? declaredHeight : `${measuredHeight}px`;
445
+ const frameProps = entry.content !== void 0 ? { srcDoc: entry.content + MEASURE_SCRIPT } : { src: safeHref ?? "" };
429
446
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
430
447
  /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
431
448
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: entry.title ?? "HTML" }),
@@ -445,6 +462,7 @@ function DocHtml({ entry }) {
445
462
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
446
463
  "iframe",
447
464
  {
465
+ ref: frameRef,
448
466
  ...frameProps,
449
467
  sandbox: "allow-scripts",
450
468
  loading: "lazy",
@@ -898,11 +916,11 @@ function ReportTrace({ spans }) {
898
916
  }
899
917
 
900
918
  // src/interactive/scenario-history-context.ts
901
- var import_react4 = require("react");
902
- var ScenarioHistoryContext = (0, import_react4.createContext)(null);
919
+ var import_react5 = require("react");
920
+ var ScenarioHistoryContext = (0, import_react5.createContext)(null);
903
921
  var ScenarioHistoryProvider = ScenarioHistoryContext.Provider;
904
922
  function useScenarioHistory() {
905
- return (0, import_react4.useContext)(ScenarioHistoryContext);
923
+ return (0, import_react5.useContext)(ScenarioHistoryContext);
906
924
  }
907
925
 
908
926
  // src/lib/run-history.ts
@@ -1011,7 +1029,7 @@ function ScenarioRunHistory({ scenarioId }) {
1011
1029
  }
1012
1030
 
1013
1031
  // src/components/ScenarioStaleness.tsx
1014
- var import_react6 = require("react");
1032
+ var import_react7 = require("react");
1015
1033
 
1016
1034
  // src/lib/provenance.ts
1017
1035
  function reportLastRunMs(report) {
@@ -1063,11 +1081,11 @@ function prUrl(ci) {
1063
1081
  }
1064
1082
 
1065
1083
  // src/interactive/scenario-freshness-context.tsx
1066
- var import_react5 = require("react");
1067
- var ScenarioFreshnessContext = (0, import_react5.createContext)(null);
1084
+ var import_react6 = require("react");
1085
+ var ScenarioFreshnessContext = (0, import_react6.createContext)(null);
1068
1086
  var ScenarioFreshnessProvider = ScenarioFreshnessContext.Provider;
1069
1087
  function useScenarioFreshness() {
1070
- return (0, import_react5.useContext)(ScenarioFreshnessContext);
1088
+ return (0, import_react6.useContext)(ScenarioFreshnessContext);
1071
1089
  }
1072
1090
 
1073
1091
  // src/components/ScenarioStaleness.tsx
@@ -1075,8 +1093,8 @@ var import_jsx_runtime24 = require("react/jsx-runtime");
1075
1093
  function ScenarioStaleness({ scenario }) {
1076
1094
  const freshness = useScenarioFreshness();
1077
1095
  const fixedNow = freshness?.nowMs;
1078
- const [now, setNow] = (0, import_react6.useState)(fixedNow);
1079
- (0, import_react6.useEffect)(() => {
1096
+ const [now, setNow] = (0, import_react7.useState)(fixedNow);
1097
+ (0, import_react7.useEffect)(() => {
1080
1098
  if (fixedNow === void 0) setNow(Date.now());
1081
1099
  }, [fixedNow]);
1082
1100
  if (!freshness || now === void 0) return null;
@@ -1090,11 +1108,11 @@ function ScenarioStaleness({ scenario }) {
1090
1108
  }
1091
1109
 
1092
1110
  // src/interactive/collapse-context.ts
1093
- var import_react7 = require("react");
1094
- var CollapseContext = (0, import_react7.createContext)(null);
1111
+ var import_react8 = require("react");
1112
+ var CollapseContext = (0, import_react8.createContext)(null);
1095
1113
  var CollapseProvider = CollapseContext.Provider;
1096
1114
  function useCollapse() {
1097
- return (0, import_react7.useContext)(CollapseContext);
1115
+ return (0, import_react8.useContext)(CollapseContext);
1098
1116
  }
1099
1117
  var STORAGE_KEY = "es-collapsed-ids";
1100
1118
  function readPersisted() {
@@ -1114,11 +1132,11 @@ function persist(ids) {
1114
1132
  }
1115
1133
  }
1116
1134
  function useCollapseState(computeDefault) {
1117
- const [collapsed, setCollapsed] = (0, import_react7.useState)(
1135
+ const [collapsed, setCollapsed] = (0, import_react8.useState)(
1118
1136
  () => readPersisted() ?? computeDefault?.() ?? /* @__PURE__ */ new Set()
1119
1137
  );
1120
- const isCollapsed = (0, import_react7.useCallback)((id) => collapsed.has(id), [collapsed]);
1121
- const toggle = (0, import_react7.useCallback)((id) => {
1138
+ const isCollapsed = (0, import_react8.useCallback)((id) => collapsed.has(id), [collapsed]);
1139
+ const toggle = (0, import_react8.useCallback)((id) => {
1122
1140
  setCollapsed((prev) => {
1123
1141
  const next = new Set(prev);
1124
1142
  if (next.has(id)) next.delete(id);
@@ -1127,14 +1145,14 @@ function useCollapseState(computeDefault) {
1127
1145
  return next;
1128
1146
  });
1129
1147
  }, []);
1130
- const collapseAll = (0, import_react7.useCallback)((ids) => {
1148
+ const collapseAll = (0, import_react8.useCallback)((ids) => {
1131
1149
  setCollapsed(() => {
1132
1150
  const next = new Set(ids);
1133
1151
  persist(next);
1134
1152
  return next;
1135
1153
  });
1136
1154
  }, []);
1137
- const expandAll = (0, import_react7.useCallback)(() => {
1155
+ const expandAll = (0, import_react8.useCallback)(() => {
1138
1156
  setCollapsed(() => {
1139
1157
  const next = /* @__PURE__ */ new Set();
1140
1158
  persist(next);
@@ -1145,12 +1163,12 @@ function useCollapseState(computeDefault) {
1145
1163
  }
1146
1164
 
1147
1165
  // src/interactive/scenario-actions.tsx
1148
- var import_react8 = require("react");
1166
+ var import_react9 = require("react");
1149
1167
  var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
1150
- var ScenarioActionsContext = (0, import_react8.createContext)(null);
1168
+ var ScenarioActionsContext = (0, import_react9.createContext)(null);
1151
1169
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1152
1170
  function useScenarioActions() {
1153
- return (0, import_react8.useContext)(ScenarioActionsContext);
1171
+ return (0, import_react9.useContext)(ScenarioActionsContext);
1154
1172
  }
1155
1173
  function scenarioToMarkdown(scenario) {
1156
1174
  return (0, import_scenario_markdown.scenarioToMarkdown)(scenario, { variant: "compact" });
@@ -1688,15 +1706,75 @@ function ReportErrorShell({
1688
1706
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportSchemaError, { error }) });
1689
1707
  }
1690
1708
 
1709
+ // src/components/ReportSpanGraph.tsx
1710
+ var import_span_graph = require("executable-stories-core/span-graph");
1711
+ var import_react10 = require("react");
1712
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1713
+ var LANE_LABEL = {
1714
+ edge: "Edge",
1715
+ service: "Service",
1716
+ queue: "Queue",
1717
+ datastore: "Data"
1718
+ };
1719
+ var MARK_LABEL = {
1720
+ failing: "Failing",
1721
+ added: "New",
1722
+ changed: "Changed"
1723
+ };
1724
+ var MARK_CLASS = {
1725
+ failing: "text-fail",
1726
+ added: "text-pass",
1727
+ changed: "text-pend"
1728
+ };
1729
+ function MarkBadge({ node }) {
1730
+ const mark = (0, import_span_graph.spanGraphNodeMark)(node);
1731
+ if (!mark) return null;
1732
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: cn("ml-2 text-xs font-medium", MARK_CLASS[mark]), children: MARK_LABEL[mark] });
1733
+ }
1734
+ function ReportSpanGraph() {
1735
+ const report = useReport();
1736
+ const { code, graph, titleById } = (0, import_react10.useMemo)(() => {
1737
+ const scenarios = report.features.flatMap((feature) => feature.scenarios);
1738
+ const derived = (0, import_span_graph.deriveSpanGraph)(scenarios);
1739
+ return {
1740
+ graph: derived,
1741
+ code: (0, import_span_graph.spanGraphToMermaid)(derived),
1742
+ titleById: new Map(scenarios.map((scenario) => [scenario.id, scenario.title]))
1743
+ };
1744
+ }, [report]);
1745
+ if (code === "") return null;
1746
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("section", { className: "es-span-graph mt-6", "aria-labelledby": "es-span-graph-heading", children: [
1747
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h2", { id: "es-span-graph-heading", className: "text-base font-semibold text-foreground", children: "Architecture, as it ran" }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "mt-1 text-sm text-muted-foreground", children: "Derived from this run\u2019s traces. Every component was named by a span while a scenario ran, so nothing here is inferred from the code. Instrumented, exercised paths only: a component no scenario reaches does not appear." }),
1749
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(DocMermaid, { entry: { kind: "mermaid", phase: "static", code } }),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("table", { className: "mt-2 w-full border-collapse text-left text-sm", children: [
1751
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("caption", { className: "sr-only", children: "Each component and the scenarios that exercised it" }),
1752
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "text-xs text-muted-foreground", children: [
1753
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Component" }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Lane" }),
1755
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("th", { scope: "col", className: "py-1 font-medium", children: "Covered by" })
1756
+ ] }) }),
1757
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("tbody", { children: graph.nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("tr", { className: "border-t border-border align-top", children: [
1758
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("th", { scope: "row", className: "py-1.5 pr-4 font-mono text-xs font-normal", children: [
1759
+ node.id,
1760
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(MarkBadge, { node })
1761
+ ] }),
1762
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-1.5 pr-4 text-xs text-muted-foreground", children: LANE_LABEL[node.kind] }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("td", { className: "py-1.5", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { className: "flex flex-col gap-0.5", children: node.scenarioIds.map((id) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("a", { href: `#${id}`, className: "text-xs text-link underline underline-offset-2 hover:text-foreground", children: titleById.get(id) ?? id }) }, id)) }) })
1764
+ ] }, node.id)) })
1765
+ ] })
1766
+ ] });
1767
+ }
1768
+
1691
1769
  // src/components/ui/switch.tsx
1692
1770
  var React4 = require("react");
1693
1771
  var import_switch = require("@base-ui/react/switch");
1694
- var import_jsx_runtime36 = require("react/jsx-runtime");
1772
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1695
1773
  function Switch({
1696
1774
  className,
1697
1775
  ...props
1698
1776
  }) {
1699
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1777
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1700
1778
  import_switch.Switch.Root,
1701
1779
  {
1702
1780
  "data-slot": "switch",
@@ -1705,7 +1783,7 @@ function Switch({
1705
1783
  className
1706
1784
  ),
1707
1785
  ...props,
1708
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1786
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1709
1787
  import_switch.Switch.Thumb,
1710
1788
  {
1711
1789
  "data-slot": "switch-thumb",
@@ -1721,7 +1799,7 @@ var React5 = __toESM(require("react"), 1);
1721
1799
  var import_toggle_group = require("@base-ui/react/toggle-group");
1722
1800
  var import_toggle = require("@base-ui/react/toggle");
1723
1801
  var import_class_variance_authority3 = require("class-variance-authority");
1724
- var import_jsx_runtime37 = require("react/jsx-runtime");
1802
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1725
1803
  var toggleItemVariants = (0, import_class_variance_authority3.cva)(
1726
1804
  "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",
1727
1805
  {
@@ -1743,13 +1821,13 @@ function ToggleGroup({
1743
1821
  children,
1744
1822
  ...props
1745
1823
  }) {
1746
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1824
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1747
1825
  import_toggle_group.ToggleGroup,
1748
1826
  {
1749
1827
  "data-slot": "toggle-group",
1750
1828
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1751
1829
  ...props,
1752
- children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1830
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1753
1831
  }
1754
1832
  );
1755
1833
  }
@@ -1760,7 +1838,7 @@ function ToggleGroupItem({
1760
1838
  ...props
1761
1839
  }) {
1762
1840
  const context = React5.useContext(ToggleGroupContext);
1763
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1841
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1764
1842
  import_toggle.Toggle,
1765
1843
  {
1766
1844
  "data-slot": "toggle-group-item",
@@ -1772,10 +1850,10 @@ function ToggleGroupItem({
1772
1850
  }
1773
1851
 
1774
1852
  // src/context/ReportRoot.tsx
1775
- var import_react9 = require("react");
1853
+ var import_react11 = require("react");
1776
1854
 
1777
1855
  // src/components/doc/NarrativeBlocks.tsx
1778
- var import_jsx_runtime38 = require("react/jsx-runtime");
1856
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1779
1857
  var FILE_TREE_TYPE = "file-tree";
1780
1858
  var DATA_MODEL_TYPE = "data-model";
1781
1859
  function isChangeKind(value) {
@@ -1789,23 +1867,23 @@ function str(value) {
1789
1867
  }
1790
1868
  function AuthoredMarker({ authored }) {
1791
1869
  if (authored !== "agent") return null;
1792
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
1870
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
1793
1871
  }
1794
1872
  function BlockFrame({
1795
1873
  title,
1796
1874
  authored,
1797
1875
  children
1798
1876
  }) {
1799
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("figure", { className: "my-3", children: [
1800
- title || authored === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
1801
- title ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
1802
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(AuthoredMarker, { authored })
1877
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("figure", { className: "my-3", children: [
1878
+ title || authored === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
1879
+ title ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
1880
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(AuthoredMarker, { authored })
1803
1881
  ] }) : null,
1804
1882
  children
1805
1883
  ] });
1806
1884
  }
1807
1885
  function ChangeBadge({ change }) {
1808
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Badge, { variant: "tag", className: "text-[10px]", children: change });
1886
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Badge, { variant: "tag", className: "text-[10px]", children: change });
1809
1887
  }
1810
1888
  function parseFileTree(data) {
1811
1889
  if (!isRecord(data) || !Array.isArray(data.files)) return void 0;
@@ -1854,11 +1932,11 @@ function sortedChildren(node) {
1854
1932
  }
1855
1933
  function TreeBranch({ node, depth }) {
1856
1934
  const children = sortedChildren(node);
1857
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
1935
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
1858
1936
  const isDir = child.children.size > 0;
1859
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { children: [
1860
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-wrap items-baseline gap-2", children: [
1861
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1937
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("li", { children: [
1938
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-wrap items-baseline gap-2", children: [
1939
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1862
1940
  "span",
1863
1941
  {
1864
1942
  className: cn(
@@ -1868,17 +1946,17 @@ function TreeBranch({ node, depth }) {
1868
1946
  children: isDir ? `${child.name}/` : child.name
1869
1947
  }
1870
1948
  ),
1871
- child.file?.change ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ChangeBadge, { change: child.file.change }) : null,
1872
- child.file?.note ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
1949
+ child.file?.change ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ChangeBadge, { change: child.file.change }) : null,
1950
+ child.file?.note ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
1873
1951
  ] }),
1874
- isDir ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TreeBranch, { node: child, depth: depth + 1 }) : null
1952
+ isDir ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(TreeBranch, { node: child, depth: depth + 1 }) : null
1875
1953
  ] }, child.name);
1876
1954
  }) });
1877
1955
  }
1878
1956
  function FileTreeBlock({ entry }) {
1879
1957
  const data = parseFileTree(entry.data);
1880
- if (!data) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(UnreadableBlock, { entry });
1881
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
1958
+ if (!data) return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(UnreadableBlock, { entry });
1959
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
1882
1960
  }
1883
1961
  function parseDataModel(data) {
1884
1962
  if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
@@ -1904,31 +1982,31 @@ function parseDataModel(data) {
1904
1982
  }
1905
1983
  function DataModelBlock({ entry }) {
1906
1984
  const data = parseDataModel(entry.data);
1907
- if (!data) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(UnreadableBlock, { entry });
1985
+ if (!data) return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(UnreadableBlock, { entry });
1908
1986
  const hasNotes = data.fields.some((f) => f.note);
1909
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
1910
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("tr", { className: "bg-muted/50", children: [
1911
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
1912
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
1913
- hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
1987
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("tr", { className: "bg-muted/50", children: [
1989
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
1990
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
1991
+ hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
1914
1992
  ] }) }),
1915
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("tbody", { children: data.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("tr", { children: [
1916
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "flex flex-wrap items-center gap-2", children: [
1917
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "font-mono text-xs text-foreground", children: field.name }),
1918
- field.change ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ChangeBadge, { change: field.change }) : null
1993
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("tbody", { children: data.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("tr", { children: [
1994
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "flex flex-wrap items-center gap-2", children: [
1995
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-mono text-xs text-foreground", children: field.name }),
1996
+ field.change ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ChangeBadge, { change: field.change }) : null
1919
1997
  ] }) }),
1920
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
1921
- hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
1998
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
1999
+ hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
1922
2000
  ] }, field.name)) })
1923
2001
  ] }) }) });
1924
2002
  }
1925
2003
  function UnreadableBlock({ entry }) {
1926
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
1927
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
2004
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
2005
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
1928
2006
  entry.type,
1929
2007
  " (unrecognised shape)"
1930
2008
  ] }),
1931
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2009
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1932
2010
  "pre",
1933
2011
  {
1934
2012
  tabIndex: 0,
@@ -1946,12 +2024,12 @@ function safeStringify2(value) {
1946
2024
  }
1947
2025
  }
1948
2026
  var narrativeBlockRenderers = {
1949
- [FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FileTreeBlock, { entry }),
1950
- [DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(DataModelBlock, { entry })
2027
+ [FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(FileTreeBlock, { entry }),
2028
+ [DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(DataModelBlock, { entry })
1951
2029
  };
1952
2030
 
1953
2031
  // src/context/ReportRoot.tsx
1954
- var import_jsx_runtime39 = require("react/jsx-runtime");
2032
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1955
2033
  var EMPTY_RENDERERS2 = {};
1956
2034
  function ReportRoot({
1957
2035
  report,
@@ -1959,7 +2037,7 @@ function ReportRoot({
1959
2037
  renderers,
1960
2038
  children
1961
2039
  }) {
1962
- const value = (0, import_react9.useMemo)(
2040
+ const value = (0, import_react11.useMemo)(
1963
2041
  () => ({
1964
2042
  report,
1965
2043
  // Narrative blocks render everywhere the report does (static SSR, the
@@ -1970,7 +2048,7 @@ function ReportRoot({
1970
2048
  }),
1971
2049
  [report, customRenderers, renderers]
1972
2050
  );
1973
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportContext.Provider, { value, children });
2051
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ReportContext.Provider, { value, children });
1974
2052
  }
1975
2053
 
1976
2054
  // src/lib/hash-state.ts
@@ -2156,7 +2234,7 @@ function listFailures(report) {
2156
2234
 
2157
2235
  // src/interactive/ReportAgentFilterNotice.tsx
2158
2236
  var import_lucide_react3 = require("lucide-react");
2159
- var import_jsx_runtime40 = require("react/jsx-runtime");
2237
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2160
2238
  function ReportAgentFilterNotice({
2161
2239
  applied,
2162
2240
  onReset,
@@ -2166,23 +2244,23 @@ function ReportAgentFilterNotice({
2166
2244
  if (applied.search) terms.push(`matching \u201C${applied.search}\u201D`);
2167
2245
  if (applied.status !== "all") terms.push(applied.status);
2168
2246
  if (applied.tags.length > 0) terms.push(applied.tags.join(", "));
2169
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2247
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2170
2248
  "div",
2171
2249
  {
2172
2250
  role: "status",
2173
2251
  "aria-live": "polite",
2174
2252
  className: "flex flex-wrap items-center gap-x-3 gap-y-1 rounded-md border border-primary/40 bg-primary/5 px-3 py-2 text-sm",
2175
2253
  children: [
2176
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react3.Bot, { "aria-hidden": true, className: "size-4 shrink-0 text-primary" }),
2177
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "min-w-0 flex-1", children: [
2254
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react3.Bot, { "aria-hidden": true, className: "size-4 shrink-0 text-primary" }),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "min-w-0 flex-1", children: [
2178
2256
  "An agent filtered this report",
2179
- terms.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2257
+ terms.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2180
2258
  " to ",
2181
2259
  terms.join(" \xB7 ")
2182
2260
  ] }) : null,
2183
2261
  " \u2014",
2184
2262
  " ",
2185
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "tabular-nums", children: [
2263
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "tabular-nums", children: [
2186
2264
  applied.matched,
2187
2265
  " of ",
2188
2266
  applied.total,
@@ -2190,7 +2268,7 @@ function ReportAgentFilterNotice({
2190
2268
  ] }),
2191
2269
  "."
2192
2270
  ] }),
2193
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2271
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2194
2272
  "button",
2195
2273
  {
2196
2274
  type: "button",
@@ -2199,14 +2277,14 @@ function ReportAgentFilterNotice({
2199
2277
  children: "Show all"
2200
2278
  }
2201
2279
  ),
2202
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2280
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2203
2281
  "button",
2204
2282
  {
2205
2283
  type: "button",
2206
2284
  onClick: onDismiss,
2207
2285
  "aria-label": "Dismiss",
2208
2286
  className: "cursor-pointer rounded-md p-1 text-muted-foreground hover:text-foreground",
2209
- children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react3.X, { "aria-hidden": true, className: "size-4" })
2287
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react3.X, { "aria-hidden": true, className: "size-4" })
2210
2288
  }
2211
2289
  )
2212
2290
  ]
@@ -2215,16 +2293,16 @@ function ReportAgentFilterNotice({
2215
2293
  }
2216
2294
 
2217
2295
  // src/interactive/ReportFailureBanner.tsx
2218
- var import_react10 = require("react");
2219
- var import_jsx_runtime41 = require("react/jsx-runtime");
2296
+ var import_react12 = require("react");
2297
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2220
2298
  function ReportFailureBanner({ failures, onCopyAll }) {
2221
2299
  const first = failures[0];
2222
- const jumpToFirst = (0, import_react10.useCallback)(() => {
2300
+ const jumpToFirst = (0, import_react12.useCallback)(() => {
2223
2301
  if (!first) return;
2224
2302
  scrollToScenarioId(first.scenarioId);
2225
2303
  }, [first]);
2226
2304
  if (failures.length === 0) return null;
2227
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2305
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2228
2306
  "div",
2229
2307
  {
2230
2308
  className: "es-failure-banner",
@@ -2232,13 +2310,13 @@ function ReportFailureBanner({ failures, onCopyAll }) {
2232
2310
  "aria-live": "polite",
2233
2311
  "aria-label": "Failure summary",
2234
2312
  children: [
2235
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "es-failure-banner-text", children: [
2236
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: failures.length }),
2313
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "es-failure-banner-text", children: [
2314
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: failures.length }),
2237
2315
  " ",
2238
2316
  "failure",
2239
2317
  failures.length === 1 ? "" : "s"
2240
2318
  ] }),
2241
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2319
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2242
2320
  "button",
2243
2321
  {
2244
2322
  type: "button",
@@ -2248,7 +2326,7 @@ function ReportFailureBanner({ failures, onCopyAll }) {
2248
2326
  children: "View first failure \u2193"
2249
2327
  }
2250
2328
  ),
2251
- onCopyAll ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2329
+ onCopyAll ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2252
2330
  "button",
2253
2331
  {
2254
2332
  type: "button",
@@ -2270,8 +2348,8 @@ function ReportFailureBanner({ failures, onCopyAll }) {
2270
2348
  }
2271
2349
 
2272
2350
  // src/interactive/ReportFilters.tsx
2273
- var import_react11 = require("react");
2274
- var import_jsx_runtime42 = require("react/jsx-runtime");
2351
+ var import_react13 = require("react");
2352
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2275
2353
  var TAG_LIMIT = 12;
2276
2354
  function ReportFilters({
2277
2355
  statuses,
@@ -2281,14 +2359,14 @@ function ReportFilters({
2281
2359
  activeTags,
2282
2360
  onToggleTag
2283
2361
  }) {
2284
- const [showAllTags, setShowAllTags] = (0, import_react11.useState)(false);
2362
+ const [showAllTags, setShowAllTags] = (0, import_react13.useState)(false);
2285
2363
  if (statuses.length === 0 && tags.length === 0) return null;
2286
2364
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2287
2365
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2288
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-2", children: [
2289
- statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2366
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col gap-2", children: [
2367
+ statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2290
2368
  const active = status === opt.key;
2291
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2369
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2292
2370
  "button",
2293
2371
  {
2294
2372
  type: "button",
@@ -2300,23 +2378,23 @@ function ReportFilters({
2300
2378
  ),
2301
2379
  children: [
2302
2380
  opt.label,
2303
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2381
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2304
2382
  ]
2305
2383
  },
2306
2384
  opt.key
2307
2385
  );
2308
2386
  }) }) : null,
2309
- tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2387
+ tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2310
2388
  visibleTags.map((tag) => {
2311
2389
  const active = activeTags.includes(tag);
2312
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2390
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2313
2391
  "button",
2314
2392
  {
2315
2393
  type: "button",
2316
2394
  "aria-pressed": active,
2317
2395
  onClick: () => onToggleTag(tag),
2318
2396
  className: "cursor-pointer",
2319
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2397
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2320
2398
  Badge,
2321
2399
  {
2322
2400
  variant: "tag",
@@ -2329,7 +2407,7 @@ function ReportFilters({
2329
2407
  }
2330
2408
  ) }, tag);
2331
2409
  }),
2332
- tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2410
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2333
2411
  "button",
2334
2412
  {
2335
2413
  type: "button",
@@ -2344,16 +2422,16 @@ function ReportFilters({
2344
2422
  }
2345
2423
 
2346
2424
  // src/interactive/ReportFreshness.tsx
2347
- var import_react12 = require("react");
2348
- var import_jsx_runtime43 = require("react/jsx-runtime");
2425
+ var import_react14 = require("react");
2426
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2349
2427
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2350
- const [now, setNow] = (0, import_react12.useState)(nowMs);
2351
- (0, import_react12.useEffect)(() => {
2428
+ const [now, setNow] = (0, import_react14.useState)(nowMs);
2429
+ (0, import_react14.useEffect)(() => {
2352
2430
  if (nowMs === void 0) setNow(Date.now());
2353
2431
  }, [nowMs]);
2354
2432
  if (lastRunMs === void 0 || now === void 0) return null;
2355
2433
  const age = formatRelativeAge(lastRunMs, now);
2356
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2434
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2357
2435
  "a",
2358
2436
  {
2359
2437
  href: ciUrl,
@@ -2364,9 +2442,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2364
2442
  }
2365
2443
  ) : null;
2366
2444
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
2367
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2368
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
2369
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("strong", { children: [
2445
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2446
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { children: [
2447
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("strong", { children: [
2370
2448
  "Last verified ",
2371
2449
  age,
2372
2450
  "."
@@ -2376,10 +2454,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2376
2454
  runLink
2377
2455
  ] });
2378
2456
  }
2379
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2457
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2380
2458
  "Verified ",
2381
2459
  age,
2382
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
2460
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
2383
2461
  " \xB7 ",
2384
2462
  runLink
2385
2463
  ] }) : null
@@ -2387,15 +2465,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2387
2465
  }
2388
2466
 
2389
2467
  // src/interactive/ReportLastRunDelta.tsx
2390
- var import_jsx_runtime44 = require("react/jsx-runtime");
2468
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2391
2469
  var MAX_LINKS = 5;
2392
2470
  function ScenarioLinks({ scenarios, tone }) {
2393
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
2394
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { children: [
2471
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
2472
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
2395
2473
  i > 0 ? ", " : "",
2396
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2474
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2397
2475
  ] }, s.id)),
2398
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { children: [
2476
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { children: [
2399
2477
  " and ",
2400
2478
  scenarios.length - MAX_LINKS,
2401
2479
  " more"
@@ -2412,23 +2490,23 @@ function ReportLastRunDelta({
2412
2490
  if (!delta) return null;
2413
2491
  const { newlyFailing, newlyPassing, added } = delta;
2414
2492
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
2415
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2493
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2416
2494
  }
2417
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2418
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2419
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-2 text-fail", children: [
2495
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2496
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2497
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "ml-2 text-fail", children: [
2420
2498
  newlyFailing.length,
2421
2499
  " newly failing (",
2422
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2500
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2423
2501
  ")"
2424
2502
  ] }) : null,
2425
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-2 text-pass", children: [
2503
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "ml-2 text-pass", children: [
2426
2504
  newlyPassing.length,
2427
2505
  " fixed (",
2428
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2506
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2429
2507
  ")"
2430
2508
  ] }) : null,
2431
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-2", children: [
2509
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "ml-2", children: [
2432
2510
  added.length,
2433
2511
  " new"
2434
2512
  ] }) : null
@@ -2436,14 +2514,14 @@ function ReportLastRunDelta({
2436
2514
  }
2437
2515
 
2438
2516
  // src/interactive/ReportSearch.tsx
2439
- var import_react13 = require("react");
2517
+ var import_react15 = require("react");
2440
2518
  var import_lucide_react4 = require("lucide-react");
2441
2519
 
2442
2520
  // src/components/ui/input.tsx
2443
2521
  var React6 = require("react");
2444
- var import_jsx_runtime45 = require("react/jsx-runtime");
2522
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2445
2523
  function Input({ className, type, ...props }) {
2446
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2524
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2447
2525
  "input",
2448
2526
  {
2449
2527
  type,
@@ -2460,8 +2538,8 @@ function Input({ className, type, ...props }) {
2460
2538
  }
2461
2539
 
2462
2540
  // src/interactive/ReportSearch.tsx
2463
- var import_jsx_runtime46 = require("react/jsx-runtime");
2464
- var ReportSearch = (0, import_react13.forwardRef)(
2541
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2542
+ var ReportSearch = (0, import_react15.forwardRef)(
2465
2543
  function ReportSearch2(props, ref) {
2466
2544
  const {
2467
2545
  value,
@@ -2471,7 +2549,7 @@ var ReportSearch = (0, import_react13.forwardRef)(
2471
2549
  placeholder = "Search scenarios, tags, tickets, errors\u2026",
2472
2550
  className
2473
2551
  } = props;
2474
- const inputId = (0, import_react13.useId)();
2552
+ const inputId = (0, import_react15.useId)();
2475
2553
  const showCounts = value.trim().length > 0 && typeof matchedCount === "number" && typeof totalCount === "number";
2476
2554
  function handleChange(e) {
2477
2555
  onChange(e.target.value);
@@ -2482,17 +2560,17 @@ var ReportSearch = (0, import_react13.forwardRef)(
2482
2560
  e.preventDefault();
2483
2561
  }
2484
2562
  }
2485
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
2486
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
2487
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "relative w-full sm:w-72", children: [
2488
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2563
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
2565
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "relative w-full sm:w-72", children: [
2566
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2489
2567
  import_lucide_react4.Search,
2490
2568
  {
2491
2569
  "aria-hidden": true,
2492
2570
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
2493
2571
  }
2494
2572
  ),
2495
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2573
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2496
2574
  Input,
2497
2575
  {
2498
2576
  ref,
@@ -2509,7 +2587,7 @@ var ReportSearch = (0, import_react13.forwardRef)(
2509
2587
  }
2510
2588
  )
2511
2589
  ] }),
2512
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2590
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2513
2591
  "span",
2514
2592
  {
2515
2593
  className: "text-xs tabular-nums text-muted-foreground",
@@ -2528,7 +2606,7 @@ var ReportSearch = (0, import_react13.forwardRef)(
2528
2606
 
2529
2607
  // src/interactive/ReportShare.tsx
2530
2608
  var import_lucide_react5 = require("lucide-react");
2531
- var import_react14 = require("react");
2609
+ var import_react16 = require("react");
2532
2610
 
2533
2611
  // src/components/ui/button.tsx
2534
2612
  var React7 = require("react");
@@ -2584,22 +2662,22 @@ function Button({
2584
2662
  }
2585
2663
 
2586
2664
  // src/interactive/ReportShare.tsx
2587
- var import_jsx_runtime47 = require("react/jsx-runtime");
2665
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2588
2666
  var DEFAULT_SHARE_COMMAND = "npx executable-stories share reports/";
2589
2667
  function ReportShare({
2590
2668
  command = DEFAULT_SHARE_COMMAND,
2591
2669
  onCopy
2592
2670
  }) {
2593
- const [open, setOpen] = (0, import_react14.useState)(false);
2594
- const dialogRef = (0, import_react14.useRef)(null);
2595
- (0, import_react14.useEffect)(() => {
2671
+ const [open, setOpen] = (0, import_react16.useState)(false);
2672
+ const dialogRef = (0, import_react16.useRef)(null);
2673
+ (0, import_react16.useEffect)(() => {
2596
2674
  const dialog = dialogRef.current;
2597
2675
  if (!dialog) return;
2598
2676
  if (open && !dialog.open) dialog.showModal();
2599
2677
  else if (!open && dialog.open) dialog.close();
2600
2678
  }, [open]);
2601
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2602
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2679
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
2680
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
2603
2681
  Button,
2604
2682
  {
2605
2683
  type: "button",
@@ -2609,12 +2687,12 @@ function ReportShare({
2609
2687
  title: "Share this report",
2610
2688
  onClick: () => setOpen(true),
2611
2689
  children: [
2612
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "size-4" }),
2613
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sr-only sm:not-sr-only", children: "Share" })
2690
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "size-4" }),
2691
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "sr-only sm:not-sr-only", children: "Share" })
2614
2692
  ]
2615
2693
  }
2616
2694
  ),
2617
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2695
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
2618
2696
  "dialog",
2619
2697
  {
2620
2698
  ref: dialogRef,
@@ -2622,11 +2700,11 @@ function ReportShare({
2622
2700
  "aria-label": "Share this report",
2623
2701
  onClose: () => setOpen(false),
2624
2702
  children: [
2625
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h2", { className: "text-base font-semibold", children: "Share this report" }),
2626
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: "Publish it to Executable Stories Cloud and send a link. Screenshots and videos go with it. Run this from your project:" }),
2627
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "mt-4 flex items-center gap-2", children: [
2628
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("code", { className: "min-w-0 flex-1 truncate rounded border border-border bg-muted px-3 py-2 font-mono text-xs", children: command }),
2629
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2703
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("h2", { className: "text-base font-semibold", children: "Share this report" }),
2704
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: "Publish it to Executable Stories Cloud and send a link. Screenshots and videos go with it. Run this from your project:" }),
2705
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "mt-4 flex items-center gap-2", children: [
2706
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("code", { className: "min-w-0 flex-1 truncate rounded border border-border bg-muted px-3 py-2 font-mono text-xs", children: command }),
2707
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2630
2708
  Button,
2631
2709
  {
2632
2710
  type: "button",
@@ -2636,9 +2714,9 @@ function ReportShare({
2636
2714
  }
2637
2715
  )
2638
2716
  ] }),
2639
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mt-3 text-xs text-muted-foreground", children: "The file you are reading cannot upload itself, so the command does it: only the CLI can read the assets this report links to." }),
2640
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "mt-6 flex items-center justify-between gap-4", children: [
2641
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2717
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "mt-3 text-xs text-muted-foreground", children: "The file you are reading cannot upload itself, so the command does it: only the CLI can read the assets this report links to." }),
2718
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "mt-6 flex items-center justify-between gap-4", children: [
2719
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2642
2720
  "a",
2643
2721
  {
2644
2722
  className: "text-xs underline underline-offset-2",
@@ -2648,7 +2726,7 @@ function ReportShare({
2648
2726
  children: "How sharing works"
2649
2727
  }
2650
2728
  ),
2651
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setOpen(false), children: "Close" })
2729
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setOpen(false), children: "Close" })
2652
2730
  ] })
2653
2731
  ]
2654
2732
  }
@@ -2657,7 +2735,7 @@ function ReportShare({
2657
2735
  }
2658
2736
 
2659
2737
  // src/interactive/ReportSinceLastVisit.tsx
2660
- var import_react15 = require("react");
2738
+ var import_react17 = require("react");
2661
2739
 
2662
2740
  // src/interactive/last-visit.ts
2663
2741
  function snapshotOf(report, atMs) {
@@ -2693,7 +2771,7 @@ function diffSinceVisit(previous, report) {
2693
2771
  }
2694
2772
 
2695
2773
  // src/interactive/ReportSinceLastVisit.tsx
2696
- var import_jsx_runtime48 = require("react/jsx-runtime");
2774
+ var import_jsx_runtime49 = require("react/jsx-runtime");
2697
2775
  var KEY = "es-last-visit";
2698
2776
  var MAX_LINKS2 = 5;
2699
2777
  function readVisit() {
@@ -2718,12 +2796,12 @@ function writeVisit(snapshot) {
2718
2796
  }
2719
2797
  }
2720
2798
  function ScenarioLinks2({ scenarios, tone }) {
2721
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
2722
- scenarios.slice(0, MAX_LINKS2).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
2799
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
2800
+ scenarios.slice(0, MAX_LINKS2).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { children: [
2723
2801
  i > 0 ? ", " : "",
2724
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2802
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2725
2803
  ] }, s.id)),
2726
- scenarios.length > MAX_LINKS2 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
2804
+ scenarios.length > MAX_LINKS2 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { children: [
2727
2805
  " and ",
2728
2806
  scenarios.length - MAX_LINKS2,
2729
2807
  " more"
@@ -2731,9 +2809,9 @@ function ScenarioLinks2({ scenarios, tone }) {
2731
2809
  ] });
2732
2810
  }
2733
2811
  function ReportSinceLastVisit({ report }) {
2734
- const [delta, setDelta] = (0, import_react15.useState)(null);
2735
- const [nowMs, setNowMs] = (0, import_react15.useState)(0);
2736
- (0, import_react15.useEffect)(() => {
2812
+ const [delta, setDelta] = (0, import_react17.useState)(null);
2813
+ const [nowMs, setNowMs] = (0, import_react17.useState)(0);
2814
+ (0, import_react17.useEffect)(() => {
2737
2815
  const now = Date.now();
2738
2816
  setDelta(diffSinceVisit(readVisit(), report));
2739
2817
  setNowMs(now);
@@ -2741,25 +2819,25 @@ function ReportSinceLastVisit({ report }) {
2741
2819
  }, [report]);
2742
2820
  if (!delta) return null;
2743
2821
  const { newlyFailing, newlyPassing, added } = delta;
2744
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-since-last-visit", children: [
2745
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "font-medium text-foreground", children: [
2822
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-since-last-visit", children: [
2823
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "font-medium text-foreground", children: [
2746
2824
  "Since your last visit (",
2747
2825
  formatRelativeAge(delta.sinceMs, nowMs),
2748
2826
  "):"
2749
2827
  ] }),
2750
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "ml-2 text-fail", children: [
2828
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "ml-2 text-fail", children: [
2751
2829
  newlyFailing.length,
2752
2830
  " started failing (",
2753
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ScenarioLinks2, { scenarios: newlyFailing, tone: "text-fail" }),
2831
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioLinks2, { scenarios: newlyFailing, tone: "text-fail" }),
2754
2832
  ")"
2755
2833
  ] }) : null,
2756
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "ml-2 text-pass", children: [
2834
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "ml-2 text-pass", children: [
2757
2835
  newlyPassing.length,
2758
2836
  " fixed (",
2759
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ScenarioLinks2, { scenarios: newlyPassing, tone: "text-pass" }),
2837
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioLinks2, { scenarios: newlyPassing, tone: "text-pass" }),
2760
2838
  ")"
2761
2839
  ] }) : null,
2762
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "ml-2", children: [
2840
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "ml-2", children: [
2763
2841
  added.length,
2764
2842
  " new"
2765
2843
  ] }) : null
@@ -2767,8 +2845,8 @@ function ReportSinceLastVisit({ report }) {
2767
2845
  }
2768
2846
 
2769
2847
  // src/interactive/ReportShortcutsHelp.tsx
2770
- var import_react16 = require("react");
2771
- var import_jsx_runtime49 = require("react/jsx-runtime");
2848
+ var import_react18 = require("react");
2849
+ var import_jsx_runtime50 = require("react/jsx-runtime");
2772
2850
  var SHORTCUTS = [
2773
2851
  { keys: "/", description: "Focus search" },
2774
2852
  { keys: "f", description: "Jump to next failure" },
@@ -2780,20 +2858,20 @@ var SHORTCUTS = [
2780
2858
  ];
2781
2859
  function Keys({ keys }) {
2782
2860
  const parts = keys.split(" ");
2783
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2784
- i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2785
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2861
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2862
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2863
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
2786
2864
  ] }, k)) });
2787
2865
  }
2788
2866
  function ReportShortcutsHelp({ open, onClose }) {
2789
- const dialogRef = (0, import_react16.useRef)(null);
2790
- (0, import_react16.useEffect)(() => {
2867
+ const dialogRef = (0, import_react18.useRef)(null);
2868
+ (0, import_react18.useEffect)(() => {
2791
2869
  const dialog = dialogRef.current;
2792
2870
  if (!dialog) return;
2793
2871
  if (open && !dialog.open) dialog.showModal();
2794
2872
  else if (!open && dialog.open) dialog.close();
2795
2873
  }, [open]);
2796
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
2874
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
2797
2875
  "dialog",
2798
2876
  {
2799
2877
  ref: dialogRef,
@@ -2801,20 +2879,20 @@ function ReportShortcutsHelp({ open, onClose }) {
2801
2879
  "aria-label": "Keyboard shortcuts",
2802
2880
  onClose,
2803
2881
  children: [
2804
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2805
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2806
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Keys, { keys: s.keys }) }),
2807
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2882
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2883
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2884
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Keys, { keys: s.keys }) }),
2885
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2808
2886
  ] }, s.keys)) }),
2809
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2887
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2810
2888
  ]
2811
2889
  }
2812
2890
  );
2813
2891
  }
2814
2892
 
2815
2893
  // src/interactive/ReportToc.tsx
2816
- var import_react17 = require("react");
2817
- var import_jsx_runtime50 = require("react/jsx-runtime");
2894
+ var import_react19 = require("react");
2895
+ var import_jsx_runtime51 = require("react/jsx-runtime");
2818
2896
  var DOT = {
2819
2897
  passed: "bg-pass",
2820
2898
  failed: "bg-fail",
@@ -2823,31 +2901,55 @@ var DOT = {
2823
2901
  };
2824
2902
  function TocContent({ onNavigate }) {
2825
2903
  const report = useReport();
2826
- const [activeId, setActiveId] = (0, import_react17.useState)(null);
2827
- const scenarioIds = (0, import_react17.useMemo)(
2904
+ const [activeId, setActiveId] = (0, import_react19.useState)(null);
2905
+ const scenarioIds = (0, import_react19.useMemo)(
2828
2906
  () => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
2829
2907
  [report]
2830
2908
  );
2831
- (0, import_react17.useEffect)(() => {
2832
- if (typeof IntersectionObserver === "undefined") return;
2909
+ (0, import_react19.useEffect)(() => {
2910
+ if (typeof window === "undefined") return;
2833
2911
  const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
2834
2912
  if (els.length === 0) return;
2835
- const observer = new IntersectionObserver(
2836
- (entries) => {
2837
- const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
2838
- if (visible[0]) setActiveId(visible[0].target.id);
2839
- },
2840
- { rootMargin: "-8% 0px -80% 0px" }
2841
- );
2842
- els.forEach((el) => observer.observe(el));
2843
- return () => observer.disconnect();
2913
+ let frame = 0;
2914
+ const update = () => {
2915
+ frame = 0;
2916
+ const atBottom = window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2;
2917
+ const line = atBottom ? window.innerHeight : window.innerHeight * 0.2;
2918
+ let active = els[0];
2919
+ let best = -Infinity;
2920
+ let firstId = null;
2921
+ let firstTop = Infinity;
2922
+ for (const el of els) {
2923
+ const top = el.getBoundingClientRect().top;
2924
+ if (top <= line && top > best) {
2925
+ best = top;
2926
+ active = el;
2927
+ }
2928
+ if (top < firstTop) {
2929
+ firstTop = top;
2930
+ firstId = el.id;
2931
+ }
2932
+ }
2933
+ setActiveId(best === -Infinity ? firstId : active.id);
2934
+ };
2935
+ const onScroll = () => {
2936
+ if (frame === 0) frame = requestAnimationFrame(update);
2937
+ };
2938
+ update();
2939
+ window.addEventListener("scroll", onScroll, { passive: true });
2940
+ window.addEventListener("resize", onScroll);
2941
+ return () => {
2942
+ window.removeEventListener("scroll", onScroll);
2943
+ window.removeEventListener("resize", onScroll);
2944
+ if (frame !== 0) cancelAnimationFrame(frame);
2945
+ };
2844
2946
  }, [scenarioIds]);
2845
2947
  if (report.features.length === 0) return null;
2846
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("li", { children: [
2847
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2848
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2948
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("li", { children: [
2949
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2950
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2849
2951
  const active = activeId === scenario.id;
2850
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
2952
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
2851
2953
  "a",
2852
2954
  {
2853
2955
  href: `#${scenario.id}`,
@@ -2862,14 +2964,14 @@ function TocContent({ onNavigate }) {
2862
2964
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2863
2965
  ),
2864
2966
  children: [
2865
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
2967
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
2866
2968
  "span",
2867
2969
  {
2868
2970
  "aria-hidden": true,
2869
2971
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2870
2972
  }
2871
2973
  ),
2872
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "break-words", children: scenario.title })
2974
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "break-words", children: scenario.title })
2873
2975
  ]
2874
2976
  }
2875
2977
  ) }, scenario.id);
@@ -2879,48 +2981,48 @@ function TocContent({ onNavigate }) {
2879
2981
  function ReportToc() {
2880
2982
  const report = useReport();
2881
2983
  if (report.features.length === 0) return null;
2882
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
2984
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
2883
2985
  "nav",
2884
2986
  {
2885
2987
  "aria-label": "Table of contents",
2886
2988
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2887
2989
  children: [
2888
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2889
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(TocContent, {})
2990
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2991
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(TocContent, {})
2890
2992
  ]
2891
2993
  }
2892
2994
  );
2893
2995
  }
2894
2996
 
2895
2997
  // src/interactive/ReportTocDrawer.tsx
2896
- var import_react18 = require("react");
2998
+ var import_react20 = require("react");
2897
2999
  var import_dialog = require("@base-ui/react/dialog");
2898
3000
  var import_lucide_react6 = require("lucide-react");
2899
- var import_jsx_runtime51 = require("react/jsx-runtime");
3001
+ var import_jsx_runtime52 = require("react/jsx-runtime");
2900
3002
  function ReportTocDrawer() {
2901
3003
  const report = useReport();
2902
- const [open, setOpen] = (0, import_react18.useState)(false);
3004
+ const [open, setOpen] = (0, import_react20.useState)(false);
2903
3005
  if (report.features.length === 0) return null;
2904
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2905
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
2906
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react6.ListIcon, { "aria-hidden": true }),
3006
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
3007
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
3008
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react6.ListIcon, { "aria-hidden": true }),
2907
3009
  " Contents"
2908
3010
  ] }),
2909
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_dialog.Dialog.Portal, { children: [
2910
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2911
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
2912
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center justify-between", children: [
2913
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2914
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3011
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_dialog.Dialog.Portal, { children: [
3012
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
3013
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
3014
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center justify-between", children: [
3015
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
3016
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
2915
3017
  import_dialog.Dialog.Close,
2916
3018
  {
2917
3019
  "aria-label": "Close contents",
2918
3020
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2919
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react6.XIcon, { "aria-hidden": true })
3021
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react6.XIcon, { "aria-hidden": true })
2920
3022
  }
2921
3023
  )
2922
3024
  ] }),
2923
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(TocContent, { onNavigate: () => setOpen(false) })
3025
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2924
3026
  ] })
2925
3027
  ] })
2926
3028
  ] });
@@ -2954,10 +3056,10 @@ function failuresFirst(report) {
2954
3056
  }
2955
3057
 
2956
3058
  // src/interactive/use-deep-link-scroll.ts
2957
- var import_react19 = require("react");
3059
+ var import_react21 = require("react");
2958
3060
  function useDeepLinkScroll() {
2959
- const lastAnchor = (0, import_react19.useRef)(null);
2960
- (0, import_react19.useEffect)(() => {
3061
+ const lastAnchor = (0, import_react21.useRef)(null);
3062
+ (0, import_react21.useEffect)(() => {
2961
3063
  function scrollToHash() {
2962
3064
  const { anchor } = splitHash(window.location.hash);
2963
3065
  if (!anchor || anchor === lastAnchor.current) return;
@@ -2973,7 +3075,7 @@ function useDeepLinkScroll() {
2973
3075
  }
2974
3076
 
2975
3077
  // src/interactive/use-keyboard-shortcuts.ts
2976
- var import_react20 = require("react");
3078
+ var import_react22 = require("react");
2977
3079
  function isEditableTarget(target) {
2978
3080
  if (!(target instanceof HTMLElement)) return false;
2979
3081
  const tag = target.tagName;
@@ -2982,9 +3084,9 @@ function isEditableTarget(target) {
2982
3084
  return false;
2983
3085
  }
2984
3086
  function useKeyboardShortcuts(handlers) {
2985
- const ref = (0, import_react20.useRef)(handlers);
3087
+ const ref = (0, import_react22.useRef)(handlers);
2986
3088
  ref.current = handlers;
2987
- (0, import_react20.useEffect)(() => {
3089
+ (0, import_react22.useEffect)(() => {
2988
3090
  function onKeyDown(e) {
2989
3091
  const h = ref.current;
2990
3092
  if (e.key === "Escape") {
@@ -3028,7 +3130,7 @@ function useKeyboardShortcuts(handlers) {
3028
3130
  }
3029
3131
 
3030
3132
  // src/interactive/use-theme.ts
3031
- var import_react21 = require("react");
3133
+ var import_react23 = require("react");
3032
3134
  var KEY2 = "es-theme";
3033
3135
  function systemDark() {
3034
3136
  return typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches;
@@ -3049,13 +3151,13 @@ function initialPref() {
3049
3151
  return "system";
3050
3152
  }
3051
3153
  function useTheme() {
3052
- const [pref, setPrefState] = (0, import_react21.useState)("system");
3053
- (0, import_react21.useEffect)(() => {
3154
+ const [pref, setPrefState] = (0, import_react23.useState)("system");
3155
+ (0, import_react23.useEffect)(() => {
3054
3156
  const p = initialPref();
3055
3157
  setPrefState(p);
3056
3158
  applyTheme(p);
3057
3159
  }, []);
3058
- (0, import_react21.useEffect)(() => {
3160
+ (0, import_react23.useEffect)(() => {
3059
3161
  if (typeof matchMedia === "undefined") return;
3060
3162
  const mq = matchMedia("(prefers-color-scheme: dark)");
3061
3163
  const onChange = () => {
@@ -3064,7 +3166,7 @@ function useTheme() {
3064
3166
  mq.addEventListener("change", onChange);
3065
3167
  return () => mq.removeEventListener("change", onChange);
3066
3168
  }, [pref]);
3067
- const setPref = (0, import_react21.useCallback)((p) => {
3169
+ const setPref = (0, import_react23.useCallback)((p) => {
3068
3170
  setPrefState(p);
3069
3171
  applyTheme(p);
3070
3172
  try {
@@ -3076,19 +3178,19 @@ function useTheme() {
3076
3178
  }
3077
3179
 
3078
3180
  // src/interactive/use-url-state.ts
3079
- var import_react22 = require("react");
3181
+ var import_react24 = require("react");
3080
3182
  var WRITE_DELAY_MS = 250;
3081
3183
  function useUrlState() {
3082
- const [state, setState] = (0, import_react22.useState)(DEFAULT_URL_STATE);
3083
- const ready = (0, import_react22.useRef)(false);
3084
- (0, import_react22.useEffect)(() => {
3184
+ const [state, setState] = (0, import_react24.useState)(DEFAULT_URL_STATE);
3185
+ const ready = (0, import_react24.useRef)(false);
3186
+ (0, import_react24.useEffect)(() => {
3085
3187
  const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
3086
3188
  read();
3087
3189
  ready.current = true;
3088
3190
  window.addEventListener("hashchange", read);
3089
3191
  return () => window.removeEventListener("hashchange", read);
3090
3192
  }, []);
3091
- (0, import_react22.useEffect)(() => {
3193
+ (0, import_react24.useEffect)(() => {
3092
3194
  if (!ready.current) return;
3093
3195
  const timer = setTimeout(
3094
3196
  () => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
@@ -3096,7 +3198,7 @@ function useUrlState() {
3096
3198
  );
3097
3199
  return () => clearTimeout(timer);
3098
3200
  }, [state]);
3099
- const update = (0, import_react22.useCallback)(
3201
+ const update = (0, import_react24.useCallback)(
3100
3202
  (patch) => setState((prev) => ({ ...prev, ...patch })),
3101
3203
  []
3102
3204
  );
@@ -3318,12 +3420,12 @@ function reportTools(deps) {
3318
3420
  }
3319
3421
 
3320
3422
  // src/interactive/ReportInteractive.tsx
3321
- var import_jsx_runtime52 = require("react/jsx-runtime");
3423
+ var import_jsx_runtime53 = require("react/jsx-runtime");
3322
3424
  function ReportInteractive(props) {
3323
3425
  const { report, className, title, dataTheme } = props;
3324
3426
  const result = unwrapReport(report);
3325
3427
  if (!result.ok) {
3326
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3428
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3327
3429
  ReportErrorShell,
3328
3430
  {
3329
3431
  error: result.error,
@@ -3333,7 +3435,7 @@ function ReportInteractive(props) {
3333
3435
  }
3334
3436
  );
3335
3437
  }
3336
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportInteractiveView, { ...props, report: result.data });
3438
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportInteractiveView, { ...props, report: result.data });
3337
3439
  }
3338
3440
  var THEME_OPTIONS = [
3339
3441
  { value: "light", label: "Light", Icon: import_lucide_react7.Sun },
@@ -3344,7 +3446,7 @@ function ThemeSegment({
3344
3446
  pref,
3345
3447
  onPref
3346
3448
  }) {
3347
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3449
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3348
3450
  ToggleGroup,
3349
3451
  {
3350
3452
  size: "sm",
@@ -3354,14 +3456,14 @@ function ThemeSegment({
3354
3456
  if (next) onPref(next);
3355
3457
  },
3356
3458
  "aria-label": "Color theme",
3357
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3459
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3358
3460
  ToggleGroupItem,
3359
3461
  {
3360
3462
  value,
3361
3463
  "aria-label": label,
3362
3464
  title: label,
3363
3465
  className: "px-2",
3364
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Icon, {})
3466
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Icon, {})
3365
3467
  },
3366
3468
  value
3367
3469
  ))
@@ -3380,30 +3482,31 @@ function ReportInteractiveView({
3380
3482
  staleAfterDays = 7,
3381
3483
  scenarioHistory,
3382
3484
  share = false,
3485
+ architecture = false,
3383
3486
  shareCommand
3384
3487
  }) {
3385
3488
  const [urlState, setUrlState] = useUrlState();
3386
3489
  const { query, status: statusFilter, tags: activeTags, detail } = urlState;
3387
- const setQuery = (0, import_react24.useCallback)(
3490
+ const setQuery = (0, import_react26.useCallback)(
3388
3491
  (next) => setUrlState({ query: next }),
3389
3492
  [setUrlState]
3390
3493
  );
3391
- const setStatusFilter = (0, import_react24.useCallback)(
3494
+ const setStatusFilter = (0, import_react26.useCallback)(
3392
3495
  (next) => setUrlState({ status: next }),
3393
3496
  [setUrlState]
3394
3497
  );
3395
- const setDetail = (0, import_react24.useCallback)(
3498
+ const setDetail = (0, import_react26.useCallback)(
3396
3499
  (next) => setUrlState({ detail: next }),
3397
3500
  [setUrlState]
3398
3501
  );
3399
- const [helpOpen, setHelpOpen] = (0, import_react24.useState)(false);
3502
+ const [helpOpen, setHelpOpen] = (0, import_react26.useState)(false);
3400
3503
  const { pref: themePref, setPref: setThemePref } = useTheme();
3401
- const searchRef = (0, import_react24.useRef)(null);
3402
- const expandId = (0, import_react24.useId)();
3403
- const docsId = (0, import_react24.useId)();
3404
- const deferredQuery = (0, import_react24.useDeferredValue)(query);
3405
- const failures = (0, import_react24.useMemo)(() => listFailures(report), [report]);
3406
- const filtered = (0, import_react24.useMemo)(
3504
+ const searchRef = (0, import_react26.useRef)(null);
3505
+ const expandId = (0, import_react26.useId)();
3506
+ const docsId = (0, import_react26.useId)();
3507
+ const deferredQuery = (0, import_react26.useDeferredValue)(query);
3508
+ const failures = (0, import_react26.useMemo)(() => listFailures(report), [report]);
3509
+ const filtered = (0, import_react26.useMemo)(
3407
3510
  // Features carrying failures float to the top so the first thing on screen
3408
3511
  // is the thing that needs attention; an all-green run keeps source order.
3409
3512
  () => failuresFirst(
@@ -3416,7 +3519,7 @@ function ReportInteractiveView({
3416
3519
  [report, deferredQuery, statusFilter, activeTags]
3417
3520
  );
3418
3521
  const isFiltering = query !== deferredQuery;
3419
- const statusOptions = (0, import_react24.useMemo)(() => {
3522
+ const statusOptions = (0, import_react26.useMemo)(() => {
3420
3523
  const s = report.summary;
3421
3524
  const opts = [
3422
3525
  { key: "all", label: "All", count: s.total }
@@ -3431,21 +3534,21 @@ function ReportInteractiveView({
3431
3534
  opts.push({ key: "pending", label: "Pending", count: s.pending });
3432
3535
  return opts;
3433
3536
  }, [report]);
3434
- const tagOptions = (0, import_react24.useMemo)(() => allTags(report), [report]);
3435
- const toggleTag = (0, import_react24.useCallback)(
3537
+ const tagOptions = (0, import_react26.useMemo)(() => allTags(report), [report]);
3538
+ const toggleTag = (0, import_react26.useCallback)(
3436
3539
  (tag) => setUrlState({
3437
3540
  tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
3438
3541
  }),
3439
3542
  [activeTags, setUrlState]
3440
3543
  );
3441
- const [toast, setToast] = (0, import_react24.useState)(null);
3442
- const toastTimer = (0, import_react24.useRef)(null);
3443
- const showToast = (0, import_react24.useCallback)((msg) => {
3544
+ const [toast, setToast] = (0, import_react26.useState)(null);
3545
+ const toastTimer = (0, import_react26.useRef)(null);
3546
+ const showToast = (0, import_react26.useCallback)((msg) => {
3444
3547
  setToast(msg);
3445
3548
  if (toastTimer.current) clearTimeout(toastTimer.current);
3446
3549
  toastTimer.current = setTimeout(() => setToast(null), 1800);
3447
3550
  }, []);
3448
- const copy = (0, import_react24.useCallback)(
3551
+ const copy = (0, import_react26.useCallback)(
3449
3552
  (text, msg) => {
3450
3553
  if (typeof navigator !== "undefined" && navigator.clipboard) {
3451
3554
  navigator.clipboard.writeText(text).then(
@@ -3456,7 +3559,7 @@ function ReportInteractiveView({
3456
3559
  },
3457
3560
  [showToast]
3458
3561
  );
3459
- const scenarioActions = (0, import_react24.useMemo)(
3562
+ const scenarioActions = (0, import_react26.useMemo)(
3460
3563
  () => ({
3461
3564
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
3462
3565
  copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
@@ -3464,31 +3567,31 @@ function ReportInteractiveView({
3464
3567
  }),
3465
3568
  [copy]
3466
3569
  );
3467
- const copyAllFailures = (0, import_react24.useCallback)(
3570
+ const copyAllFailures = (0, import_react26.useCallback)(
3468
3571
  () => copy(failuresToPrompt(failedScenarios(filtered)), "Failures copied"),
3469
3572
  [copy, filtered]
3470
3573
  );
3471
- const failureIndexRef = (0, import_react24.useRef)(0);
3472
- const allCollapsibleIds = (0, import_react24.useMemo)(
3574
+ const failureIndexRef = (0, import_react26.useRef)(0);
3575
+ const allCollapsibleIds = (0, import_react26.useMemo)(
3473
3576
  () => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
3474
3577
  [report]
3475
3578
  );
3476
3579
  const collapse = useCollapseState(
3477
- (0, import_react24.useCallback)(() => defaultCollapsedIds(report), [report])
3580
+ (0, import_react26.useCallback)(() => defaultCollapsedIds(report), [report])
3478
3581
  );
3479
3582
  const revealFilteredResults = deferredQuery.trim() !== "" || statusFilter !== "all" || activeTags.length > 0;
3480
- const visibleCollapse = (0, import_react24.useMemo)(
3583
+ const visibleCollapse = (0, import_react26.useMemo)(
3481
3584
  () => revealFilteredResults ? { ...collapse.api, isCollapsed: () => false } : collapse.api,
3482
3585
  [collapse.api, revealFilteredResults]
3483
3586
  );
3484
- const collapseAll = (0, import_react24.useCallback)(
3587
+ const collapseAll = (0, import_react26.useCallback)(
3485
3588
  () => collapse.collapseAll(allCollapsibleIds),
3486
3589
  [collapse, allCollapsibleIds]
3487
3590
  );
3488
- const [expandedAll, setExpandedAll] = (0, import_react24.useState)(
3591
+ const [expandedAll, setExpandedAll] = (0, import_react26.useState)(
3489
3592
  () => report.summary.failed === 0
3490
3593
  );
3491
- const setExpanded = (0, import_react24.useCallback)(
3594
+ const setExpanded = (0, import_react26.useCallback)(
3492
3595
  (expanded) => {
3493
3596
  if (expanded) collapse.expandAll();
3494
3597
  else collapseAll();
@@ -3496,8 +3599,8 @@ function ReportInteractiveView({
3496
3599
  },
3497
3600
  [collapse, collapseAll]
3498
3601
  );
3499
- const [agentFilter, setAgentFilter] = (0, import_react24.useState)(null);
3500
- (0, import_react23.useTools)(
3602
+ const [agentFilter, setAgentFilter] = (0, import_react26.useState)(null);
3603
+ (0, import_react25.useTools)(
3501
3604
  reportTools({
3502
3605
  report,
3503
3606
  view: urlState,
@@ -3506,14 +3609,14 @@ function ReportInteractiveView({
3506
3609
  })
3507
3610
  );
3508
3611
  const showAgentFilter = agentFilter !== null && agentFilter.search === query && agentFilter.status === statusFilter && agentFilter.tags.length === activeTags.length && agentFilter.tags.every((tag) => activeTags.includes(tag));
3509
- const clearFilters = (0, import_react24.useCallback)(
3612
+ const clearFilters = (0, import_react26.useCallback)(
3510
3613
  () => setUrlState({ query: "", status: "all", tags: [] }),
3511
3614
  [setUrlState]
3512
3615
  );
3513
- const focusSearch = (0, import_react24.useCallback)(() => {
3616
+ const focusSearch = (0, import_react26.useCallback)(() => {
3514
3617
  searchRef.current?.focus();
3515
3618
  }, []);
3516
- const stepFailure = (0, import_react24.useCallback)(
3619
+ const stepFailure = (0, import_react26.useCallback)(
3517
3620
  (direction) => {
3518
3621
  if (failures.length === 0) return;
3519
3622
  failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
@@ -3522,10 +3625,10 @@ function ReportInteractiveView({
3522
3625
  },
3523
3626
  [failures]
3524
3627
  );
3525
- const toggleHelp = (0, import_react24.useCallback)(() => {
3628
+ const toggleHelp = (0, import_react26.useCallback)(() => {
3526
3629
  setHelpOpen((v) => !v);
3527
3630
  }, []);
3528
- const escape = (0, import_react24.useCallback)(() => {
3631
+ const escape = (0, import_react26.useCallback)(() => {
3529
3632
  if (helpOpen) setHelpOpen(false);
3530
3633
  else if (query !== "") setQuery("");
3531
3634
  }, [helpOpen, query, setQuery]);
@@ -3543,13 +3646,13 @@ function ReportInteractiveView({
3543
3646
  const totalScenarios = report.summary.total;
3544
3647
  const matchedScenarios = filtered.summary.total;
3545
3648
  const showToc = !hideToc && totalScenarios > 3;
3546
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(CollapseProvider, { value: visibleCollapse, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ScenarioFreshnessProvider, { value: { staleAfterDays, report }, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3649
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(CollapseProvider, { value: visibleCollapse, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ScenarioFreshnessProvider, { value: { staleAfterDays, report }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3547
3650
  ReportRoot,
3548
3651
  {
3549
3652
  report: filtered,
3550
3653
  customRenderers,
3551
3654
  renderers,
3552
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3655
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3553
3656
  "main",
3554
3657
  {
3555
3658
  className: cn("es-report", "es-report-interactive", className),
@@ -3558,8 +3661,8 @@ function ReportInteractiveView({
3558
3661
  "data-theme": dataTheme,
3559
3662
  "data-detail-level": detail,
3560
3663
  children: [
3561
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("header", { className: "es-report-header", children: [
3562
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3664
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("header", { className: "es-report-header", children: [
3665
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3563
3666
  "div",
3564
3667
  {
3565
3668
  "data-es-navbar": true,
@@ -3568,9 +3671,9 @@ function ReportInteractiveView({
3568
3671
  hideHeader ? "justify-end" : "justify-between"
3569
3672
  ),
3570
3673
  children: [
3571
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
3572
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
3573
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3674
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
3675
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center gap-2", children: [
3676
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3574
3677
  ReportSearch,
3575
3678
  {
3576
3679
  ref: searchRef,
@@ -3580,17 +3683,18 @@ function ReportInteractiveView({
3580
3683
  totalCount: totalScenarios
3581
3684
  }
3582
3685
  ),
3583
- share ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportShare, { command: shareCommand, onCopy: copy }) : null,
3584
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
3686
+ share ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportShare, { command: shareCommand, onCopy: copy }) : null,
3687
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
3585
3688
  ] })
3586
3689
  ]
3587
3690
  }
3588
3691
  ),
3589
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_jsx_runtime52.Fragment, { children: [
3590
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportSummary, {}),
3591
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportMeta, {})
3692
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
3693
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportSummary, {}),
3694
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportMeta, {})
3592
3695
  ] }),
3593
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3696
+ architecture ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportSpanGraph, {}) : null,
3697
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3594
3698
  ReportFreshness,
3595
3699
  {
3596
3700
  lastRunMs: reportLastRunMs(report),
@@ -3598,15 +3702,15 @@ function ReportInteractiveView({
3598
3702
  staleAfterDays
3599
3703
  }
3600
3704
  ),
3601
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3705
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3602
3706
  ReportLastRunDelta,
3603
3707
  {
3604
3708
  history: scenarioHistory,
3605
3709
  report
3606
3710
  }
3607
3711
  ),
3608
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportSinceLastVisit, { report }),
3609
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3712
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportSinceLastVisit, { report }),
3713
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3610
3714
  ReportFilters,
3611
3715
  {
3612
3716
  statuses: statusOptions,
@@ -3617,10 +3721,10 @@ function ReportInteractiveView({
3617
3721
  onToggleTag: toggleTag
3618
3722
  }
3619
3723
  ),
3620
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
3621
- showToc ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportTocDrawer, {}) : null,
3622
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
3623
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3724
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
3725
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportTocDrawer, {}) : null,
3726
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center gap-2", children: [
3727
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3624
3728
  Switch,
3625
3729
  {
3626
3730
  id: expandId,
@@ -3628,7 +3732,7 @@ function ReportInteractiveView({
3628
3732
  onCheckedChange: setExpanded
3629
3733
  }
3630
3734
  ),
3631
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3735
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3632
3736
  "label",
3633
3737
  {
3634
3738
  htmlFor: expandId,
@@ -3637,8 +3741,8 @@ function ReportInteractiveView({
3637
3741
  }
3638
3742
  )
3639
3743
  ] }),
3640
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
3641
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3744
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex items-center gap-2", children: [
3745
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3642
3746
  Switch,
3643
3747
  {
3644
3748
  id: docsId,
@@ -3646,7 +3750,7 @@ function ReportInteractiveView({
3646
3750
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
3647
3751
  }
3648
3752
  ),
3649
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3753
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3650
3754
  "label",
3651
3755
  {
3652
3756
  htmlFor: docsId,
@@ -3657,7 +3761,7 @@ function ReportInteractiveView({
3657
3761
  ] })
3658
3762
  ] })
3659
3763
  ] }),
3660
- showAgentFilter ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3764
+ showAgentFilter ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3661
3765
  ReportAgentFilterNotice,
3662
3766
  {
3663
3767
  applied: agentFilter,
@@ -3665,17 +3769,17 @@ function ReportInteractiveView({
3665
3769
  onDismiss: () => setAgentFilter(null)
3666
3770
  }
3667
3771
  ) : null,
3668
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportFailureBanner, { failures, onCopyAll: copyAllFailures }),
3669
- hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex gap-6", children: [
3670
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportToc, {}),
3671
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportFeatureList, {}) })
3672
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3772
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportFailureBanner, { failures, onCopyAll: copyAllFailures }),
3773
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex gap-6", children: [
3774
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportToc, {}),
3775
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportFeatureList, {}) })
3776
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3673
3777
  ReportEmpty,
3674
3778
  {
3675
3779
  message: query ? "No scenarios match the search." : void 0
3676
3780
  }
3677
3781
  ),
3678
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3782
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3679
3783
  "button",
3680
3784
  {
3681
3785
  type: "button",
@@ -3686,14 +3790,14 @@ function ReportInteractiveView({
3686
3790
  children: "?"
3687
3791
  }
3688
3792
  ),
3689
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3793
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3690
3794
  ReportShortcutsHelp,
3691
3795
  {
3692
3796
  open: helpOpen,
3693
3797
  onClose: () => setHelpOpen(false)
3694
3798
  }
3695
3799
  ),
3696
- toast ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3800
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3697
3801
  "div",
3698
3802
  {
3699
3803
  role: "status",
@@ -3710,14 +3814,14 @@ function ReportInteractiveView({
3710
3814
  }
3711
3815
 
3712
3816
  // src/interactive/ReportInteractiveIsland.tsx
3713
- var import_react27 = require("react");
3817
+ var import_react29 = require("react");
3714
3818
 
3715
3819
  // src/interactive/island-renderers.tsx
3716
- var import_react26 = require("react");
3820
+ var import_react28 = require("react");
3717
3821
 
3718
3822
  // src/components/doc/MermaidView.tsx
3719
- var import_react25 = require("react");
3720
- var import_jsx_runtime53 = require("react/jsx-runtime");
3823
+ var import_react27 = require("react");
3824
+ var import_jsx_runtime54 = require("react/jsx-runtime");
3721
3825
  function prefersDark(el) {
3722
3826
  const themed = el?.closest("[data-theme]");
3723
3827
  if (themed?.dataset.theme === "dark") return true;
@@ -3725,10 +3829,10 @@ function prefersDark(el) {
3725
3829
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
3726
3830
  }
3727
3831
  function MermaidView({ entry, load }) {
3728
- const ref = (0, import_react25.useRef)(null);
3729
- const [svg, setSvg] = (0, import_react25.useState)(null);
3730
- const [error, setError] = (0, import_react25.useState)(null);
3731
- (0, import_react25.useEffect)(() => {
3832
+ const ref = (0, import_react27.useRef)(null);
3833
+ const [svg, setSvg] = (0, import_react27.useState)(null);
3834
+ const [error, setError] = (0, import_react27.useState)(null);
3835
+ (0, import_react27.useEffect)(() => {
3732
3836
  let cancelled = false;
3733
3837
  const draw = async () => {
3734
3838
  let mermaid;
@@ -3745,7 +3849,10 @@ function MermaidView({ entry, load }) {
3745
3849
  mermaid.initialize({
3746
3850
  startOnLoad: false,
3747
3851
  securityLevel: "strict",
3748
- theme: prefersDark(ref.current) ? "dark" : "default"
3852
+ // "neutral" in light: the same theme the static, no-JS render uses
3853
+ // in ssr-entry, so a diagram keeps its colours when the island takes
3854
+ // over.
3855
+ theme: prefersDark(ref.current) ? "dark" : "neutral"
3749
3856
  });
3750
3857
  await mermaid.parse(entry.code);
3751
3858
  const id = `es-mermaid-${Math.random().toString(36).slice(2)}`;
@@ -3771,9 +3878,9 @@ function MermaidView({ entry, load }) {
3771
3878
  };
3772
3879
  }, [entry.code, load]);
3773
3880
  if (svg) {
3774
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("figure", { ref, className: "my-3", children: [
3775
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3776
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3881
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("figure", { ref, className: "my-3", children: [
3882
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3883
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3777
3884
  "div",
3778
3885
  {
3779
3886
  role: "img",
@@ -3784,8 +3891,8 @@ function MermaidView({ entry, load }) {
3784
3891
  )
3785
3892
  ] });
3786
3893
  }
3787
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
3788
- error ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3894
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_jsx_runtime54.Fragment, { children: [
3895
+ error ? /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3789
3896
  "p",
3790
3897
  {
3791
3898
  role: "alert",
@@ -3797,14 +3904,14 @@ function MermaidView({ entry, load }) {
3797
3904
  ]
3798
3905
  }
3799
3906
  ) : null,
3800
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(MermaidSource, { entry, ref })
3907
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(MermaidSource, { entry, ref })
3801
3908
  ] });
3802
3909
  }
3803
3910
 
3804
3911
  // src/interactive/island-renderers.tsx
3805
- var import_jsx_runtime54 = require("react/jsx-runtime");
3912
+ var import_jsx_runtime55 = require("react/jsx-runtime");
3806
3913
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3807
- var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3914
+ var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
3808
3915
  function cachedCdnModule(url) {
3809
3916
  let loader;
3810
3917
  return () => {
@@ -3819,8 +3926,8 @@ function cachedCdnModule(url) {
3819
3926
  var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
3820
3927
  var loadHljs = cachedCdnModule(HLJS_ESM);
3821
3928
  function CdnHighlightedCode({ entry }) {
3822
- const [html, setHtml] = (0, import_react26.useState)(null);
3823
- (0, import_react26.useEffect)(() => {
3929
+ const [html, setHtml] = (0, import_react28.useState)(null);
3930
+ (0, import_react28.useEffect)(() => {
3824
3931
  let cancelled = false;
3825
3932
  void (async () => {
3826
3933
  try {
@@ -3836,27 +3943,27 @@ function CdnHighlightedCode({ entry }) {
3836
3943
  cancelled = true;
3837
3944
  };
3838
3945
  }, [entry.content, entry.lang]);
3839
- return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3946
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
3840
3947
  "code",
3841
3948
  {
3842
3949
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
3843
3950
  dangerouslySetInnerHTML: { __html: html }
3844
3951
  }
3845
- ) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3952
+ ) : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3846
3953
  }
3847
3954
  function buildIslandRenderers(opts) {
3848
3955
  const renderers = {};
3849
3956
  if (opts.mermaid) {
3850
- renderers.mermaid = (entry) => (0, import_react26.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
3957
+ renderers.mermaid = (entry) => (0, import_react28.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
3851
3958
  }
3852
3959
  if (opts.syntaxHighlighting) {
3853
- renderers.code = (entry) => (0, import_react26.createElement)(CdnHighlightedCode, { entry });
3960
+ renderers.code = (entry) => (0, import_react28.createElement)(CdnHighlightedCode, { entry });
3854
3961
  }
3855
3962
  return renderers;
3856
3963
  }
3857
3964
 
3858
3965
  // src/interactive/ReportInteractiveIsland.tsx
3859
- var import_jsx_runtime55 = require("react/jsx-runtime");
3966
+ var import_jsx_runtime56 = require("react/jsx-runtime");
3860
3967
  function ReportInteractiveIsland({
3861
3968
  report,
3862
3969
  title,
@@ -3866,11 +3973,11 @@ function ReportInteractiveIsland({
3866
3973
  syntaxHighlighting = true,
3867
3974
  mermaid = true
3868
3975
  }) {
3869
- const renderers = (0, import_react27.useMemo)(
3976
+ const renderers = (0, import_react29.useMemo)(
3870
3977
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
3871
3978
  [syntaxHighlighting, mermaid]
3872
3979
  );
3873
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
3980
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
3874
3981
  ReportInteractive,
3875
3982
  {
3876
3983
  report,