executable-stories-react 0.4.1 → 0.5.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.
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
  "use strict";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __export = (target, all) => {
8
10
  for (var name in all)
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/interactive/index.ts
@@ -37,7 +47,7 @@ __export(interactive_exports, {
37
47
  module.exports = __toCommonJS(interactive_exports);
38
48
 
39
49
  // src/interactive/ReportInteractive.tsx
40
- var import_react16 = require("react");
50
+ var import_react18 = require("react");
41
51
 
42
52
  // src/result.ts
43
53
  var ok = (data) => ({ ok: true, data });
@@ -136,11 +146,15 @@ var badgeVariants = (0, import_class_variance_authority.cva)(
136
146
  outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
137
147
  ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
138
148
  link: "text-primary underline-offset-4 [a&]:hover:underline",
139
- passed: "border-pass-border bg-pass-bg text-pass font-mono uppercase",
149
+ // Pass/skip/pending are the quiet states: outline + coloured text, no
150
+ // fill, so a green report reads calm. Only `failed` keeps a tinted
151
+ // fill, making it the single loud badge on the page, so red pops the
152
+ // instant something breaks. Colour-as-accent, matching the KPI cards.
153
+ passed: "border-pass-border text-pass font-mono uppercase",
140
154
  failed: "border-fail-border bg-fail-bg text-fail font-mono uppercase",
141
- skipped: "border-skip-border bg-skip-bg text-skip font-mono uppercase",
142
- pending: "border-pend-border bg-pend-bg text-pend font-mono uppercase",
143
- tag: "border-pass-border bg-pass-bg text-pass font-mono font-medium"
155
+ skipped: "border-skip-border text-skip font-mono uppercase",
156
+ pending: "border-pend-border text-pend font-mono uppercase",
157
+ tag: "border-border text-muted-foreground font-mono font-medium [a&]:hover:bg-muted"
144
158
  }
145
159
  },
146
160
  defaultVariants: {
@@ -693,7 +707,7 @@ function ReportTrace({ spans }) {
693
707
  {
694
708
  className: "min-w-0 flex-1 truncate text-foreground",
695
709
  style: { paddingLeft: `${row.depth * 12}px` },
696
- title: row.statusMessage ? `${row.name} \u2014 ${row.statusMessage}` : row.name,
710
+ title: row.statusMessage ? `${row.name}: ${row.statusMessage}` : row.name,
697
711
  children: row.name
698
712
  }
699
713
  ),
@@ -832,12 +846,12 @@ function useCollapse() {
832
846
  }
833
847
  var STORAGE_KEY = "es-collapsed-ids";
834
848
  function readPersisted() {
835
- if (typeof localStorage === "undefined") return /* @__PURE__ */ new Set();
849
+ if (typeof localStorage === "undefined") return null;
836
850
  try {
837
851
  const raw = localStorage.getItem(STORAGE_KEY);
838
- return raw ? new Set(JSON.parse(raw)) : /* @__PURE__ */ new Set();
852
+ return raw ? new Set(JSON.parse(raw)) : null;
839
853
  } catch {
840
- return /* @__PURE__ */ new Set();
854
+ return null;
841
855
  }
842
856
  }
843
857
  function persist(ids) {
@@ -847,8 +861,10 @@ function persist(ids) {
847
861
  } catch {
848
862
  }
849
863
  }
850
- function useCollapseState() {
851
- const [collapsed, setCollapsed] = (0, import_react6.useState)(readPersisted);
864
+ function useCollapseState(computeDefault) {
865
+ const [collapsed, setCollapsed] = (0, import_react6.useState)(
866
+ () => readPersisted() ?? computeDefault?.() ?? /* @__PURE__ */ new Set()
867
+ );
852
868
  const isCollapsed = (0, import_react6.useCallback)((id) => collapsed.has(id), [collapsed]);
853
869
  const toggle = (0, import_react6.useCallback)((id) => {
854
870
  setCollapsed((prev) => {
@@ -926,7 +942,7 @@ function Card({ className, ...props }) {
926
942
  {
927
943
  "data-slot": "card",
928
944
  className: cn(
929
- "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
945
+ "flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-xs",
930
946
  className
931
947
  ),
932
948
  ...props
@@ -957,52 +973,89 @@ function CardContent({ className, ...props }) {
957
973
  );
958
974
  }
959
975
 
960
- // src/components/ReportScenario.tsx
976
+ // src/components/ui/dropdown-menu.tsx
977
+ var React3 = require("react");
978
+ var import_menu = require("@base-ui/react/menu");
979
+ var import_lucide_react = require("lucide-react");
961
980
  var import_jsx_runtime22 = require("react/jsx-runtime");
962
- var STATUS_LABEL = {
963
- passed: "Passed",
964
- failed: "Failed",
965
- skipped: "Skipped",
966
- pending: "Pending"
967
- };
968
- var STATUS_GLYPH = {
969
- passed: "\u2713",
970
- failed: "\u2717",
971
- skipped: "\u25CB",
972
- pending: "\u25CB"
973
- };
974
- var STATUS_GLYPH_COLOR = {
975
- passed: "text-pass",
976
- failed: "text-fail",
977
- skipped: "text-skip",
978
- pending: "text-pend"
979
- };
980
- var STATUS_BADGE = {
981
- passed: "passed",
982
- failed: "failed",
983
- skipped: "skipped",
984
- pending: "pending"
981
+ var itemClasses = "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground";
982
+ function DropdownMenu(props) {
983
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
984
+ }
985
+ function DropdownMenuTrigger(props) {
986
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
987
+ }
988
+ function DropdownMenuContent({
989
+ className,
990
+ align = "end",
991
+ sideOffset = 4,
992
+ ...props
993
+ }) {
994
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
995
+ import_menu.Menu.Positioner,
996
+ {
997
+ "data-slot": "dropdown-menu-positioner",
998
+ className: "z-50",
999
+ align,
1000
+ sideOffset,
1001
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1002
+ import_menu.Menu.Popup,
1003
+ {
1004
+ "data-slot": "dropdown-menu-content",
1005
+ className: cn(
1006
+ "z-50 min-w-[11rem] origin-[var(--transform-origin)] overflow-hidden rounded-md border border-border bg-popover p-1 font-sans text-sm text-popover-foreground shadow-md transition-[opacity,transform] data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
1007
+ className
1008
+ ),
1009
+ ...props
1010
+ }
1011
+ )
1012
+ }
1013
+ ) });
1014
+ }
1015
+ function DropdownMenuItem({
1016
+ className,
1017
+ ...props
1018
+ }) {
1019
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1020
+ import_menu.Menu.Item,
1021
+ {
1022
+ "data-slot": "dropdown-menu-item",
1023
+ className: cn(itemClasses, className),
1024
+ ...props
1025
+ }
1026
+ );
1027
+ }
1028
+
1029
+ // src/components/ReportScenario.tsx
1030
+ var import_lucide_react2 = require("lucide-react");
1031
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1032
+ var STATUS_META = {
1033
+ passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1034
+ failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
1035
+ skipped: { label: "Skipped", glyph: "\u25CB", glyphColor: "text-skip", badgeVariant: "skipped", showPill: true },
1036
+ pending: { label: "Pending", glyph: "\u25CB", glyphColor: "text-pend", badgeVariant: "pending", showPill: true }
985
1037
  };
986
1038
  function ReportScenario({ scenario, hideTitle = false }) {
987
1039
  const titleId = `${scenario.id}-title`;
988
1040
  const bodyId = `${scenario.id}-body`;
989
- const label = scenario.planned ? "Planned" : STATUS_LABEL[scenario.status];
1041
+ const meta = STATUS_META[scenario.status];
1042
+ const label = scenario.planned ? "Planned" : meta.label;
990
1043
  const collapse = useCollapse();
991
1044
  const collapsible = collapse !== null && !hideTitle;
992
1045
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
993
1046
  const actions = useScenarioActions();
994
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1047
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
995
1048
  Card,
996
1049
  {
997
1050
  id: scenario.id,
998
1051
  "data-status": scenario.status,
999
- ...hideTitle ? { "aria-label": `${scenario.title} \u2014 ${label}` } : { "aria-labelledby": titleId },
1052
+ ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1000
1053
  className: "gap-0 py-0",
1001
1054
  children: [
1002
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1003
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1004
- hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1005
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1055
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1056
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1057
+ hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1058
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1006
1059
  "button",
1007
1060
  {
1008
1061
  type: "button",
@@ -1011,60 +1064,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1011
1064
  "aria-controls": bodyId,
1012
1065
  "aria-label": `Toggle ${scenario.title}`,
1013
1066
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1014
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1067
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1015
1068
  }
1016
1069
  ) : null,
1017
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", STATUS_GLYPH_COLOR[scenario.status]), children: STATUS_GLYPH[scenario.status] }),
1018
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: scenario.title })
1070
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: scenario.title })
1019
1072
  ] }),
1020
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1021
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1022
- scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1023
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: STATUS_BADGE[scenario.status], "aria-label": `Status: ${label}`, children: label }),
1024
- actions ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-0.5", children: [
1025
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1026
- "button",
1027
- {
1028
- type: "button",
1029
- onClick: () => actions.copyLink(scenario),
1030
- "aria-label": `Copy link to ${scenario.title}`,
1031
- title: "Copy permalink",
1032
- className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
1033
- children: "link"
1034
- }
1035
- ),
1036
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1037
- "button",
1073
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1074
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1075
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1076
+ meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1077
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenu, { children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1079
+ DropdownMenuTrigger,
1038
1080
  {
1039
- type: "button",
1040
- onClick: () => actions.copyMarkdown(scenario),
1041
- "aria-label": `Copy ${scenario.title} as Markdown`,
1042
- title: "Copy as Markdown",
1043
- className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
1044
- children: "md"
1081
+ "aria-label": `Actions for ${scenario.title}`,
1082
+ className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1083
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1045
1084
  }
1046
1085
  ),
1047
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1048
- "button",
1049
- {
1050
- type: "button",
1051
- onClick: () => actions.copyPrompt(scenario),
1052
- "aria-label": `Copy ${scenario.title} as an AI investigation prompt`,
1053
- title: "Copy as AI prompt",
1054
- className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
1055
- children: "ai"
1056
- }
1057
- )
1086
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenuContent, { align: "end", children: [
1087
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1088
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react2.LinkIcon, {}),
1089
+ " Copy link"
1090
+ ] }),
1091
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1092
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react2.FileTextIcon, {}),
1093
+ " Copy as Markdown"
1094
+ ] }),
1095
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1096
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react2.SparklesIcon, {}),
1097
+ " Explain with AI"
1098
+ ] })
1099
+ ] })
1058
1100
  ] }) : null
1059
1101
  ] })
1060
1102
  ] }),
1061
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1062
- scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1063
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1103
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1104
+ scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1105
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1064
1106
  ] }) : null
1065
1107
  ] }),
1066
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1067
- scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1108
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1109
+ scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1068
1110
  "pre",
1069
1111
  {
1070
1112
  role: "alert",
@@ -1072,10 +1114,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1072
1114
  children: scenario.errorMessage
1073
1115
  }
1074
1116
  ) : null,
1075
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportSteps, { scenario }),
1076
- scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1077
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1078
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReportTrace, { spans: scenario.otelSpans })
1117
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportSteps, { scenario }),
1118
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1119
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportTrace, { spans: scenario.otelSpans })
1079
1121
  ] })
1080
1122
  ]
1081
1123
  }
@@ -1083,13 +1125,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1083
1125
  }
1084
1126
 
1085
1127
  // src/components/ReportScenarioList.tsx
1086
- var import_jsx_runtime23 = require("react/jsx-runtime");
1128
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1087
1129
  function ReportScenarioList({ feature }) {
1088
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ReportScenario, { scenario }, scenario.id)) });
1130
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportScenario, { scenario }, scenario.id)) });
1089
1131
  }
1090
1132
 
1091
1133
  // src/components/ReportFeature.tsx
1092
- var import_jsx_runtime24 = require("react/jsx-runtime");
1134
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1093
1135
  function ReportFeature({ feature }) {
1094
1136
  const titleId = `${feature.id}-title`;
1095
1137
  const bodyId = `${feature.id}-body`;
@@ -1097,74 +1139,80 @@ function ReportFeature({ feature }) {
1097
1139
  const skipped = s.skipped + s.pending;
1098
1140
  const collapse = useCollapse();
1099
1141
  const collapsed = collapse?.isCollapsed(feature.id) ?? false;
1100
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1101
- "section",
1102
- {
1103
- id: feature.id,
1104
- "data-slot": "feature",
1105
- "aria-labelledby": titleId,
1106
- className: "overflow-hidden rounded-xl border border-border bg-card shadow-sm",
1107
- children: [
1108
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center justify-between gap-4 border-b border-border bg-muted/40 px-4 py-3", children: [
1109
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
1110
- collapse ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1111
- "button",
1142
+ return (
1143
+ // Suite = a section heading + a divider, NOT another bordered card. Drops a
1144
+ // layer of nesting so the scenario cards below read as the primary unit.
1145
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1146
+ "section",
1147
+ {
1148
+ id: feature.id,
1149
+ "data-slot": "feature",
1150
+ "aria-labelledby": titleId,
1151
+ className: "flex flex-col gap-3",
1152
+ children: [
1153
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1154
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "min-w-0", children: [
1155
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1156
+ "button",
1157
+ {
1158
+ type: "button",
1159
+ onClick: () => collapse.toggle(feature.id),
1160
+ "aria-expanded": !collapsed,
1161
+ "aria-controls": bodyId,
1162
+ className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1163
+ children: [
1164
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "truncate", children: feature.title })
1166
+ ]
1167
+ }
1168
+ ) : feature.title }),
1169
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1170
+ ] }),
1171
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1172
+ "p",
1112
1173
  {
1113
- type: "button",
1114
- onClick: () => collapse.toggle(feature.id),
1115
- "aria-expanded": !collapsed,
1116
- "aria-controls": bodyId,
1117
- "aria-label": `Toggle ${feature.title}`,
1118
- className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1119
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1174
+ className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1175
+ "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1176
+ children: [
1177
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { children: [
1178
+ s.passed,
1179
+ " passed"
1180
+ ] }),
1181
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1183
+ s.failed,
1184
+ " failed"
1185
+ ] }),
1186
+ skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
1187
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1188
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { children: [
1189
+ skipped,
1190
+ " skipped"
1191
+ ] })
1192
+ ] }) : null
1193
+ ]
1120
1194
  }
1121
- ) : null,
1122
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "min-w-0", children: [
1123
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("h2", { id: titleId, className: "text-sm font-semibold tracking-tight text-foreground", children: feature.title }),
1124
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "mt-0.5 truncate font-mono text-xs text-muted-foreground", children: feature.sourceFile })
1125
- ] })
1195
+ )
1126
1196
  ] }),
1127
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1128
- "div",
1129
- {
1130
- role: "img",
1131
- className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
1132
- "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1133
- children: [
1134
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-pass", children: [
1135
- "\u2713",
1136
- s.passed
1137
- ] }),
1138
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-fail", children: [
1139
- "\u2717",
1140
- s.failed
1141
- ] }),
1142
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": true, className: "text-skip", children: [
1143
- "\u25CB",
1144
- skipped
1145
- ] })
1146
- ]
1147
- }
1148
- )
1149
- ] }),
1150
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ReportScenarioList, { feature }) })
1151
- ]
1152
- }
1197
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportScenarioList, { feature }) })
1198
+ ]
1199
+ }
1200
+ )
1153
1201
  );
1154
1202
  }
1155
1203
 
1156
1204
  // src/components/ReportFeatureList.tsx
1157
- var import_jsx_runtime25 = require("react/jsx-runtime");
1205
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1158
1206
  function ReportFeatureList() {
1159
1207
  const report = useReport();
1160
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportFeature, { feature }, feature.id)) });
1208
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportFeature, { feature }, feature.id)) });
1161
1209
  }
1162
1210
 
1163
1211
  // src/components/ui/empty.tsx
1164
1212
  var import_class_variance_authority2 = require("class-variance-authority");
1165
- var import_jsx_runtime26 = require("react/jsx-runtime");
1213
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1166
1214
  function Empty({ className, ...props }) {
1167
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1215
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1168
1216
  "div",
1169
1217
  {
1170
1218
  "data-slot": "empty",
@@ -1177,7 +1225,7 @@ function Empty({ className, ...props }) {
1177
1225
  );
1178
1226
  }
1179
1227
  function EmptyHeader({ className, ...props }) {
1180
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1228
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1181
1229
  "div",
1182
1230
  {
1183
1231
  "data-slot": "empty-header",
@@ -1204,7 +1252,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1204
1252
  }
1205
1253
  );
1206
1254
  function EmptyTitle({ className, ...props }) {
1207
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1255
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1208
1256
  "div",
1209
1257
  {
1210
1258
  "data-slot": "empty-title",
@@ -1215,16 +1263,16 @@ function EmptyTitle({ className, ...props }) {
1215
1263
  }
1216
1264
 
1217
1265
  // src/components/ReportEmpty.tsx
1218
- var import_jsx_runtime27 = require("react/jsx-runtime");
1266
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1219
1267
  function ReportEmpty({ message }) {
1220
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1268
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1221
1269
  }
1222
1270
 
1223
1271
  // src/components/ReportSummary.tsx
1224
- var import_jsx_runtime28 = require("react/jsx-runtime");
1272
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1225
1273
  function ReportSummary({ className }) {
1226
1274
  const report = useReport();
1227
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1228
1276
  ReportSummaryView,
1229
1277
  {
1230
1278
  summary: report.summary,
@@ -1233,38 +1281,54 @@ function ReportSummary({ className }) {
1233
1281
  }
1234
1282
  );
1235
1283
  }
1284
+ var TONE = {
1285
+ total: { num: "text-foreground", dot: "bg-muted-foreground/50" },
1286
+ passed: { num: "text-pass", dot: "bg-pass" },
1287
+ failed: { num: "text-fail", dot: "bg-fail" },
1288
+ skipped: { num: "text-skip", dot: "bg-skip" },
1289
+ pending: { num: "text-pend", dot: "bg-pend" }
1290
+ };
1236
1291
  function ReportSummaryView({ summary, className, ariaLabel }) {
1237
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { className: cn("text-sm text-muted-foreground", className), "aria-label": ariaLabel, children: [
1238
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1239
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("strong", { className: "font-semibold text-foreground", children: summary.total }),
1240
- " scenario",
1241
- summary.total === 1 ? "" : "s"
1242
- ] }),
1243
- " \xB7 ",
1244
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "passed", className: "text-pass", children: [
1245
- summary.passed,
1246
- " passed"
1247
- ] }),
1248
- " \xB7 ",
1249
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "failed", className: "text-fail", children: [
1250
- summary.failed,
1251
- " failed"
1252
- ] }),
1253
- summary.skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1254
- " \xB7 ",
1255
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "skipped", className: "text-skip", children: [
1256
- summary.skipped,
1257
- " skipped"
1258
- ] })
1259
- ] }) : null,
1260
- summary.pending > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1261
- " \xB7 ",
1262
- /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { "data-status": "pending", className: "text-pend", children: [
1263
- summary.pending,
1264
- " pending"
1265
- ] })
1266
- ] }) : null
1267
- ] });
1292
+ const cards = [
1293
+ { status: "total", label: "Total", value: summary.total },
1294
+ { status: "passed", label: "Passed", value: summary.passed },
1295
+ { status: "failed", label: "Failed", value: summary.failed },
1296
+ { status: "skipped", label: "Skipped", value: summary.skipped }
1297
+ ];
1298
+ if (summary.pending > 0) {
1299
+ cards.push({ status: "pending", label: "Pending", value: summary.pending });
1300
+ }
1301
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1302
+ "dl",
1303
+ {
1304
+ className: cn(
1305
+ "grid grid-cols-2 gap-3",
1306
+ // Columns follow the card count so the row is always full — 5 statuses
1307
+ // (pending present) never leave a lone orphan card below a row of four.
1308
+ cards.length === 5 ? "sm:grid-cols-5" : "sm:grid-cols-4",
1309
+ className
1310
+ ),
1311
+ "aria-label": ariaLabel,
1312
+ children: cards.map((c) => {
1313
+ const tone = TONE[c.status];
1314
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1315
+ "div",
1316
+ {
1317
+ "data-status": c.status,
1318
+ className: "rounded-lg border bg-card px-4 py-3",
1319
+ children: [
1320
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1321
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1322
+ c.label
1323
+ ] }),
1324
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1325
+ ]
1326
+ },
1327
+ c.status
1328
+ );
1329
+ })
1330
+ }
1331
+ );
1268
1332
  }
1269
1333
 
1270
1334
  // src/lib/provenance.ts
@@ -1317,7 +1381,7 @@ function prUrl(ci) {
1317
1381
  }
1318
1382
 
1319
1383
  // src/components/ReportMeta.tsx
1320
- var import_jsx_runtime29 = require("react/jsx-runtime");
1384
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1321
1385
  function ReportMeta() {
1322
1386
  const report = useReport();
1323
1387
  const items = [];
@@ -1340,13 +1404,13 @@ function ReportMeta() {
1340
1404
  const short = sha.slice(0, 8);
1341
1405
  items.push({
1342
1406
  label: "Commit",
1343
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1407
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1344
1408
  });
1345
1409
  }
1346
1410
  if (report.ci) {
1347
1411
  items.push({
1348
1412
  label: "CI",
1349
- value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1413
+ value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1350
1414
  ciDisplayName(report.ci.name),
1351
1415
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1352
1416
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1357,59 +1421,172 @@ function ReportMeta() {
1357
1421
  const label = `#${report.ci.prNumber}`;
1358
1422
  items.push({
1359
1423
  label: "PR",
1360
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1424
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1361
1425
  });
1362
1426
  }
1363
1427
  if (items.length === 0) return null;
1364
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dl", { "aria-label": "Run metadata", className: "flex flex-wrap gap-x-6 gap-y-1 text-xs text-muted-foreground", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex gap-1.5", children: [
1365
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dt", { className: "font-medium", children: item.label }),
1366
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1367
- ] }, item.label)) });
1428
+ return (
1429
+ // Run metadata is useful but secondary to "what failed" — tuck it into a
1430
+ // collapsed disclosure so it doesn't push the result down. Native <details>
1431
+ // so it works with no JS (the SSR/no-hydration report still toggles).
1432
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1434
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1435
+ "Run details"
1436
+ ] }),
1437
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1438
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("dt", { className: "font-medium", children: item.label }),
1439
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1440
+ ] }, item.label)) })
1441
+ ] })
1442
+ );
1368
1443
  }
1369
1444
 
1370
1445
  // src/components/ReportSchemaError.tsx
1371
- var import_jsx_runtime30 = require("react/jsx-runtime");
1446
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1372
1447
  function ReportSchemaError({ error }) {
1373
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1374
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("strong", { children: "Report could not be displayed." }) }),
1375
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: error.message }),
1376
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { children: [
1448
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("strong", { children: "Report could not be displayed." }) }),
1450
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { children: error.message }),
1451
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { children: [
1377
1452
  "The report bundle is newer than this version of ",
1378
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("code", { children: "executable-stories-react" }),
1453
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("code", { children: "executable-stories-react" }),
1379
1454
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1380
1455
  ] }) : null,
1381
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("details", { children: [
1382
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("summary", { children: [
1456
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("details", { children: [
1457
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("summary", { children: [
1383
1458
  error.issues.length,
1384
1459
  " validation issue",
1385
1460
  error.issues.length === 1 ? "" : "s"
1386
1461
  ] }),
1387
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1462
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1388
1463
  ] }) : null
1389
1464
  ] });
1390
1465
  }
1391
1466
 
1392
1467
  // src/components/ReportShell.tsx
1393
- var import_jsx_runtime31 = require("react/jsx-runtime");
1394
- function ReportTitleBlock({ title }) {
1395
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
1396
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("h1", { children: title ?? "Story Report" }),
1397
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportSummary, {}),
1398
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportMeta, {})
1399
- ] });
1400
- }
1468
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1401
1469
  function ReportErrorShell({
1402
1470
  error,
1403
1471
  className,
1404
1472
  title,
1405
1473
  dataTheme
1406
1474
  }) {
1407
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportSchemaError, { error }) });
1475
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ReportSchemaError, { error }) });
1476
+ }
1477
+
1478
+ // src/interactive/ReportInteractive.tsx
1479
+ var import_lucide_react5 = require("lucide-react");
1480
+
1481
+ // src/components/ui/switch.tsx
1482
+ var React4 = require("react");
1483
+ var import_switch = require("@base-ui/react/switch");
1484
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1485
+ function Switch({
1486
+ className,
1487
+ ...props
1488
+ }) {
1489
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1490
+ import_switch.Switch.Root,
1491
+ {
1492
+ "data-slot": "switch",
1493
+ className: cn(
1494
+ "peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-primary data-[unchecked]:bg-input",
1495
+ className
1496
+ ),
1497
+ ...props,
1498
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1499
+ import_switch.Switch.Thumb,
1500
+ {
1501
+ "data-slot": "switch-thumb",
1502
+ className: "pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[checked]:translate-x-[calc(100%-2px)] data-[unchecked]:translate-x-0"
1503
+ }
1504
+ )
1505
+ }
1506
+ );
1507
+ }
1508
+
1509
+ // src/components/ui/toggle-group.tsx
1510
+ var React5 = __toESM(require("react"), 1);
1511
+ var import_toggle_group = require("@base-ui/react/toggle-group");
1512
+ var import_toggle = require("@base-ui/react/toggle");
1513
+ var import_class_variance_authority3 = require("class-variance-authority");
1514
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1515
+ var toggleItemVariants = (0, import_class_variance_authority3.cva)(
1516
+ "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",
1517
+ {
1518
+ variants: {
1519
+ size: {
1520
+ default: "h-9 px-3 text-sm",
1521
+ sm: "h-8 px-2.5 text-[0.82rem]"
1522
+ }
1523
+ },
1524
+ defaultVariants: { size: "default" }
1525
+ }
1526
+ );
1527
+ var ToggleGroupContext = React5.createContext({
1528
+ size: "default"
1529
+ });
1530
+ function ToggleGroup({
1531
+ className,
1532
+ size,
1533
+ children,
1534
+ ...props
1535
+ }) {
1536
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1537
+ import_toggle_group.ToggleGroup,
1538
+ {
1539
+ "data-slot": "toggle-group",
1540
+ className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1541
+ ...props,
1542
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1543
+ }
1544
+ );
1545
+ }
1546
+ function ToggleGroupItem({
1547
+ className,
1548
+ children,
1549
+ size,
1550
+ ...props
1551
+ }) {
1552
+ const context = React5.useContext(ToggleGroupContext);
1553
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1554
+ import_toggle.Toggle,
1555
+ {
1556
+ "data-slot": "toggle-group-item",
1557
+ className: cn(toggleItemVariants({ size: context.size ?? size }), "flex-1 shrink-0", className),
1558
+ ...props,
1559
+ children
1560
+ }
1561
+ );
1408
1562
  }
1409
1563
 
1410
1564
  // src/interactive/ReportSearch.tsx
1411
1565
  var import_react8 = require("react");
1412
- var import_jsx_runtime32 = require("react/jsx-runtime");
1566
+ var import_lucide_react3 = require("lucide-react");
1567
+
1568
+ // src/components/ui/input.tsx
1569
+ var React6 = require("react");
1570
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1571
+ function Input({ className, type, ...props }) {
1572
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1573
+ "input",
1574
+ {
1575
+ type,
1576
+ "data-slot": "input",
1577
+ className: cn(
1578
+ "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
1579
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
1580
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
1581
+ className
1582
+ ),
1583
+ ...props
1584
+ }
1585
+ );
1586
+ }
1587
+
1588
+ // src/interactive/ReportSearch.tsx
1589
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1413
1590
  var ReportSearch = (0, import_react8.forwardRef)(
1414
1591
  function ReportSearch2(props, ref) {
1415
1592
  const {
@@ -1417,11 +1594,11 @@ var ReportSearch = (0, import_react8.forwardRef)(
1417
1594
  onChange,
1418
1595
  matchedCount,
1419
1596
  totalCount,
1420
- placeholder = "Search scenarios, tags, or step text\u2026",
1597
+ placeholder = "Search scenarios or tags\u2026",
1421
1598
  className
1422
1599
  } = props;
1423
1600
  const inputId = (0, import_react8.useId)();
1424
- const showCounts = typeof matchedCount === "number" && typeof totalCount === "number";
1601
+ const showCounts = value.trim().length > 0 && typeof matchedCount === "number" && typeof totalCount === "number";
1425
1602
  function handleChange(e) {
1426
1603
  onChange(e.target.value);
1427
1604
  }
@@ -1431,24 +1608,46 @@ var ReportSearch = (0, import_react8.forwardRef)(
1431
1608
  e.preventDefault();
1432
1609
  }
1433
1610
  }
1434
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: ["es-search", className].filter(Boolean).join(" "), children: [
1435
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("label", { htmlFor: inputId, className: "es-search-label", children: "Search" }),
1436
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1437
- "input",
1611
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1612
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1613
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "relative w-full sm:w-72", children: [
1614
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1615
+ import_lucide_react3.Search,
1616
+ {
1617
+ "aria-hidden": true,
1618
+ className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
1619
+ }
1620
+ ),
1621
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1622
+ Input,
1623
+ {
1624
+ ref,
1625
+ id: inputId,
1626
+ type: "search",
1627
+ value,
1628
+ onChange: handleChange,
1629
+ onKeyDown: handleKeyDown,
1630
+ placeholder,
1631
+ autoComplete: "off",
1632
+ spellCheck: false,
1633
+ "aria-keyshortcuts": "/",
1634
+ className: "pl-8"
1635
+ }
1636
+ )
1637
+ ] }),
1638
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
1639
+ "span",
1438
1640
  {
1439
- ref,
1440
- id: inputId,
1441
- type: "search",
1442
- value,
1443
- onChange: handleChange,
1444
- onKeyDown: handleKeyDown,
1445
- placeholder,
1446
- autoComplete: "off",
1447
- spellCheck: false,
1448
- "aria-keyshortcuts": "/"
1641
+ className: "text-xs tabular-nums text-muted-foreground",
1642
+ "aria-live": "polite",
1643
+ children: [
1644
+ matchedCount,
1645
+ " of ",
1646
+ totalCount,
1647
+ " scenarios"
1648
+ ]
1449
1649
  }
1450
- ),
1451
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "es-search-counts", "aria-live": "polite", children: value ? `${matchedCount} of ${totalCount}` : `${totalCount} total` }) : null
1650
+ ) : null
1452
1651
  ] });
1453
1652
  }
1454
1653
  );
@@ -1477,7 +1676,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
1477
1676
  }
1478
1677
 
1479
1678
  // src/interactive/ReportFailureBanner.tsx
1480
- var import_jsx_runtime33 = require("react/jsx-runtime");
1679
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1481
1680
  function ReportFailureBanner({ failures }) {
1482
1681
  const first = failures[0];
1483
1682
  const jumpToFirst = (0, import_react9.useCallback)(() => {
@@ -1485,7 +1684,7 @@ function ReportFailureBanner({ failures }) {
1485
1684
  scrollToScenarioId(first.scenarioId);
1486
1685
  }, [first]);
1487
1686
  if (failures.length === 0) return null;
1488
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1687
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
1489
1688
  "div",
1490
1689
  {
1491
1690
  className: "es-failure-banner",
@@ -1493,20 +1692,20 @@ function ReportFailureBanner({ failures }) {
1493
1692
  "aria-live": "polite",
1494
1693
  "aria-label": "Failure summary",
1495
1694
  children: [
1496
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "es-failure-banner-text", children: [
1497
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: failures.length }),
1695
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "es-failure-banner-text", children: [
1696
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("strong", { children: failures.length }),
1498
1697
  " ",
1499
1698
  "failure",
1500
1699
  failures.length === 1 ? "" : "s"
1501
1700
  ] }),
1502
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1701
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1503
1702
  "button",
1504
1703
  {
1505
1704
  type: "button",
1506
1705
  className: "es-failure-banner-jump",
1507
1706
  onClick: jumpToFirst,
1508
- "aria-label": "Jump to first failure",
1509
- children: "Jump to first \u2193"
1707
+ "aria-label": "View first failure",
1708
+ children: "View first failure \u2193"
1510
1709
  }
1511
1710
  )
1512
1711
  ]
@@ -1516,7 +1715,7 @@ function ReportFailureBanner({ failures }) {
1516
1715
 
1517
1716
  // src/interactive/ReportFreshness.tsx
1518
1717
  var import_react10 = require("react");
1519
- var import_jsx_runtime34 = require("react/jsx-runtime");
1718
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1520
1719
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1521
1720
  const [now, setNow] = (0, import_react10.useState)(nowMs);
1522
1721
  (0, import_react10.useEffect)(() => {
@@ -1524,7 +1723,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1524
1723
  }, [nowMs]);
1525
1724
  if (lastRunMs === void 0 || now === void 0) return null;
1526
1725
  const age = formatRelativeAge(lastRunMs, now);
1527
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1726
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1528
1727
  "a",
1529
1728
  {
1530
1729
  href: ciUrl,
@@ -1535,22 +1734,22 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1535
1734
  }
1536
1735
  ) : null;
1537
1736
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
1538
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
1539
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { children: [
1540
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("strong", { children: [
1737
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
1738
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { children: [
1739
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("strong", { children: [
1541
1740
  "Last verified ",
1542
1741
  age,
1543
1742
  "."
1544
1743
  ] }),
1545
- " This report may no longer match the current behavior \u2014 look for a newer run."
1744
+ " This report may no longer match the current behavior. Look for a newer run."
1546
1745
  ] }),
1547
1746
  runLink
1548
1747
  ] });
1549
1748
  }
1550
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
1749
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
1551
1750
  "Verified ",
1552
1751
  age,
1553
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
1752
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
1554
1753
  " \xB7 ",
1555
1754
  runLink
1556
1755
  ] }) : null
@@ -1558,15 +1757,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
1558
1757
  }
1559
1758
 
1560
1759
  // src/interactive/ReportLastRunDelta.tsx
1561
- var import_jsx_runtime35 = require("react/jsx-runtime");
1760
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1562
1761
  var MAX_LINKS = 5;
1563
1762
  function ScenarioLinks({ scenarios, tone }) {
1564
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
1565
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
1763
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
1764
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { children: [
1566
1765
  i > 0 ? ", " : "",
1567
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
1766
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
1568
1767
  ] }, s.id)),
1569
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
1768
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { children: [
1570
1769
  " and ",
1571
1770
  scenarios.length - MAX_LINKS,
1572
1771
  " more"
@@ -1583,23 +1782,23 @@ function ReportLastRunDelta({
1583
1782
  if (!delta) return null;
1584
1783
  const { newlyFailing, newlyPassing, added } = delta;
1585
1784
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
1586
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
1785
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
1587
1786
  }
1588
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
1589
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
1590
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ml-2 text-fail", children: [
1787
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
1788
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
1789
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "ml-2 text-fail", children: [
1591
1790
  newlyFailing.length,
1592
1791
  " newly failing (",
1593
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
1792
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
1594
1793
  ")"
1595
1794
  ] }) : null,
1596
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ml-2 text-pass", children: [
1795
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "ml-2 text-pass", children: [
1597
1796
  newlyPassing.length,
1598
1797
  " fixed (",
1599
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
1798
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
1600
1799
  ")"
1601
1800
  ] }) : null,
1602
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "ml-2", children: [
1801
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "ml-2", children: [
1603
1802
  added.length,
1604
1803
  " new"
1605
1804
  ] }) : null
@@ -1608,14 +1807,78 @@ function ReportLastRunDelta({
1608
1807
 
1609
1808
  // src/interactive/ReportShortcutsHelp.tsx
1610
1809
  var import_react11 = require("react");
1611
- var import_jsx_runtime36 = require("react/jsx-runtime");
1810
+
1811
+ // src/components/ui/button.tsx
1812
+ var React7 = require("react");
1813
+ var import_class_variance_authority4 = require("class-variance-authority");
1814
+ var import_use_render2 = require("@base-ui/react/use-render");
1815
+ var buttonVariants = (0, import_class_variance_authority4.cva)(
1816
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
1817
+ {
1818
+ variants: {
1819
+ variant: {
1820
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
1821
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1822
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1823
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1824
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1825
+ link: "text-primary underline-offset-4 hover:underline"
1826
+ },
1827
+ size: {
1828
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
1829
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
1830
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1831
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1832
+ icon: "size-9",
1833
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
1834
+ "icon-sm": "size-8",
1835
+ "icon-lg": "size-10"
1836
+ }
1837
+ },
1838
+ defaultVariants: {
1839
+ variant: "default",
1840
+ size: "default"
1841
+ }
1842
+ }
1843
+ );
1844
+ function Button({
1845
+ className,
1846
+ variant = "default",
1847
+ size = "default",
1848
+ render,
1849
+ ...props
1850
+ }) {
1851
+ return (0, import_use_render2.useRender)({
1852
+ defaultTagName: "button",
1853
+ render,
1854
+ props: {
1855
+ "data-slot": "button",
1856
+ "data-variant": variant,
1857
+ "data-size": size,
1858
+ className: cn(buttonVariants({ variant, size, className })),
1859
+ ...props
1860
+ }
1861
+ });
1862
+ }
1863
+
1864
+ // src/interactive/ReportShortcutsHelp.tsx
1865
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1612
1866
  var SHORTCUTS = [
1613
1867
  { keys: "/", description: "Focus search" },
1614
1868
  { keys: "f", description: "Jump to next failure" },
1615
- { keys: "Shift+F", description: "Jump to previous failure" },
1616
- { keys: "Esc", description: "Clear search / close dialog" },
1617
- { keys: "?", description: "Toggle this help" }
1869
+ { keys: "Shift F", description: "Jump to previous failure" },
1870
+ { keys: "e", description: "Expand all" },
1871
+ { keys: "c", description: "Collapse all" },
1872
+ { keys: "?", description: "Toggle this help" },
1873
+ { keys: "Esc", description: "Clear search / close" }
1618
1874
  ];
1875
+ function Keys({ keys }) {
1876
+ const parts = keys.split(" ");
1877
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
1878
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
1879
+ /* @__PURE__ */ (0, import_jsx_runtime40.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 })
1880
+ ] }, k)) });
1881
+ }
1619
1882
  function ReportShortcutsHelp({ open, onClose }) {
1620
1883
  const dialogRef = (0, import_react11.useRef)(null);
1621
1884
  (0, import_react11.useEffect)(() => {
@@ -1624,20 +1887,20 @@ function ReportShortcutsHelp({ open, onClose }) {
1624
1887
  if (open && !dialog.open) dialog.showModal();
1625
1888
  else if (!open && dialog.open) dialog.close();
1626
1889
  }, [open]);
1627
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
1890
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
1628
1891
  "dialog",
1629
1892
  {
1630
1893
  ref: dialogRef,
1631
- className: "es-shortcuts-help",
1894
+ className: "es-shortcuts-help m-auto w-[min(90vw,28rem)] rounded-lg border border-border bg-card p-6 text-card-foreground shadow-lg [overscroll-behavior:contain] backdrop:bg-black/40",
1632
1895
  "aria-label": "Keyboard shortcuts",
1633
1896
  onClose,
1634
1897
  children: [
1635
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("h2", { children: "Keyboard shortcuts" }),
1636
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("dl", { children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { children: [
1637
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("dt", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("kbd", { children: s.keys }) }),
1638
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("dd", { children: s.description })
1898
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
1899
+ /* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
1900
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Keys, { keys: s.keys }) }),
1901
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
1639
1902
  ] }, s.keys)) }),
1640
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("form", { method: "dialog", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "submit", className: "es-shortcuts-close", children: "Close" }) })
1903
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
1641
1904
  ]
1642
1905
  }
1643
1906
  );
@@ -1810,7 +2073,9 @@ function listFailures(report) {
1810
2073
  }
1811
2074
 
1812
2075
  // src/interactive/ReportFilters.tsx
1813
- var import_jsx_runtime37 = require("react/jsx-runtime");
2076
+ var import_react14 = require("react");
2077
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2078
+ var TAG_LIMIT = 12;
1814
2079
  function ReportFilters({
1815
2080
  statuses,
1816
2081
  status,
@@ -1819,11 +2084,14 @@ function ReportFilters({
1819
2084
  activeTags,
1820
2085
  onToggleTag
1821
2086
  }) {
2087
+ const [showAllTags, setShowAllTags] = (0, import_react14.useState)(false);
1822
2088
  if (statuses.length === 0 && tags.length === 0) return null;
1823
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-2", children: [
1824
- statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2089
+ const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2090
+ const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2091
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-col gap-2", children: [
2092
+ statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
1825
2093
  const active = status === opt.key;
1826
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2094
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
1827
2095
  "button",
1828
2096
  {
1829
2097
  type: "button",
@@ -1835,45 +2103,66 @@ function ReportFilters({
1835
2103
  ),
1836
2104
  children: [
1837
2105
  opt.label,
1838
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2106
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
1839
2107
  ]
1840
2108
  },
1841
2109
  opt.key
1842
2110
  );
1843
2111
  }) }) : null,
1844
- tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap gap-1.5", children: tags.map((tag) => {
1845
- const active = activeTags.includes(tag);
1846
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2112
+ tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2113
+ visibleTags.map((tag) => {
2114
+ const active = activeTags.includes(tag);
2115
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2116
+ "button",
2117
+ {
2118
+ type: "button",
2119
+ "aria-pressed": active,
2120
+ onClick: () => onToggleTag(tag),
2121
+ className: "cursor-pointer",
2122
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2123
+ Badge,
2124
+ {
2125
+ variant: "tag",
2126
+ className: cn(
2127
+ active && "border-primary bg-primary text-primary-foreground"
2128
+ ),
2129
+ children: tag
2130
+ }
2131
+ )
2132
+ }
2133
+ ) }, tag);
2134
+ }),
2135
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
1847
2136
  "button",
1848
2137
  {
1849
2138
  type: "button",
1850
- "aria-pressed": active,
1851
- onClick: () => onToggleTag(tag),
1852
- className: "cursor-pointer",
1853
- children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Badge, { variant: active ? "default" : "tag", children: tag })
2139
+ "aria-expanded": showAllTags,
2140
+ onClick: () => setShowAllTags((v) => !v),
2141
+ className: "cursor-pointer rounded-full px-2 py-0.5 text-xs font-medium text-muted-foreground hover:text-foreground",
2142
+ children: tagsCollapsed ? `+${tags.length - TAG_LIMIT} more` : "Show fewer"
1854
2143
  }
1855
- ) }, tag);
1856
- }) }) : null
2144
+ ) }) : null
2145
+ ] }) : null
1857
2146
  ] });
1858
2147
  }
1859
2148
 
1860
2149
  // src/interactive/ReportToc.tsx
1861
- var import_react14 = require("react");
1862
- var import_jsx_runtime38 = require("react/jsx-runtime");
2150
+ var import_react15 = require("react");
2151
+ var import_jsx_runtime42 = require("react/jsx-runtime");
1863
2152
  var DOT = {
1864
- passed: "text-pass",
1865
- failed: "text-fail",
1866
- skipped: "text-skip",
1867
- pending: "text-pend"
2153
+ passed: "bg-pass",
2154
+ failed: "bg-fail",
2155
+ skipped: "bg-skip",
2156
+ pending: "bg-pend"
1868
2157
  };
1869
- function ReportToc() {
2158
+ function TocContent({ onNavigate }) {
1870
2159
  const report = useReport();
1871
- const [activeId, setActiveId] = (0, import_react14.useState)(null);
1872
- const scenarioIds = (0, import_react14.useMemo)(
2160
+ const [activeId, setActiveId] = (0, import_react15.useState)(null);
2161
+ const scenarioIds = (0, import_react15.useMemo)(
1873
2162
  () => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
1874
2163
  [report]
1875
2164
  );
1876
- (0, import_react14.useEffect)(() => {
2165
+ (0, import_react15.useEffect)(() => {
1877
2166
  if (typeof IntersectionObserver === "undefined") return;
1878
2167
  const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
1879
2168
  if (els.length === 0) return;
@@ -1888,91 +2177,166 @@ function ReportToc() {
1888
2177
  return () => observer.disconnect();
1889
2178
  }, [scenarioIds]);
1890
2179
  if (report.features.length === 0) return null;
1891
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2180
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { children: [
2181
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2182
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2183
+ const active = activeId === scenario.id;
2184
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2185
+ "a",
2186
+ {
2187
+ href: `#${scenario.id}`,
2188
+ onClick: (e) => {
2189
+ e.preventDefault();
2190
+ scrollToScenarioId(scenario.id, { updateHash: true });
2191
+ onNavigate?.();
2192
+ },
2193
+ "aria-current": active ? "true" : void 0,
2194
+ className: cn(
2195
+ "flex items-start gap-2 border-l-2 py-1 pl-2.5 leading-snug hover:text-foreground",
2196
+ active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2197
+ ),
2198
+ children: [
2199
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2200
+ "span",
2201
+ {
2202
+ "aria-hidden": true,
2203
+ className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2204
+ }
2205
+ ),
2206
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "break-words", children: scenario.title })
2207
+ ]
2208
+ }
2209
+ ) }, scenario.id);
2210
+ }) })
2211
+ ] }, feature.id)) });
2212
+ }
2213
+ function ReportToc() {
2214
+ const report = useReport();
2215
+ if (report.features.length === 0) return null;
2216
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
1892
2217
  "nav",
1893
2218
  {
1894
2219
  "aria-label": "Table of contents",
1895
- className: "sticky top-4 hidden h-fit w-56 shrink-0 self-start text-xs lg:block",
1896
- children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "flex flex-col gap-3", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { children: [
1897
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "mb-1 truncate font-semibold text-foreground", children: feature.title }),
1898
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "flex flex-col", children: feature.scenarios.map((scenario) => {
1899
- const active = activeId === scenario.id;
1900
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
1901
- "a",
1902
- {
1903
- href: `#${scenario.id}`,
1904
- onClick: (e) => {
1905
- e.preventDefault();
1906
- scrollToScenarioId(scenario.id, { updateHash: true });
1907
- },
1908
- "aria-current": active ? "true" : void 0,
1909
- className: cn(
1910
- "flex items-center gap-1.5 truncate border-l-2 py-0.5 pl-2 hover:text-foreground",
1911
- active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
1912
- ),
1913
- children: [
1914
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", DOT[scenario.status]), children: "\u2022" }),
1915
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate", children: scenario.title })
1916
- ]
1917
- }
1918
- ) }, scenario.id);
1919
- }) })
1920
- ] }, feature.id)) })
2220
+ className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2221
+ children: [
2222
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2223
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TocContent, {})
2224
+ ]
1921
2225
  }
1922
2226
  );
1923
2227
  }
1924
2228
 
2229
+ // src/interactive/ReportTocDrawer.tsx
2230
+ var import_react16 = require("react");
2231
+ var import_dialog = require("@base-ui/react/dialog");
2232
+ var import_lucide_react4 = require("lucide-react");
2233
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2234
+ function ReportTocDrawer() {
2235
+ const report = useReport();
2236
+ const [open, setOpen] = (0, import_react16.useState)(false);
2237
+ if (report.features.length === 0) return null;
2238
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2239
+ /* @__PURE__ */ (0, import_jsx_runtime43.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: [
2240
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2241
+ " Contents"
2242
+ ] }),
2243
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_dialog.Dialog.Portal, { children: [
2244
+ /* @__PURE__ */ (0, import_jsx_runtime43.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" }),
2245
+ /* @__PURE__ */ (0, import_jsx_runtime43.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: [
2246
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center justify-between", children: [
2247
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2248
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2249
+ import_dialog.Dialog.Close,
2250
+ {
2251
+ "aria-label": "Close contents",
2252
+ className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2253
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2254
+ }
2255
+ )
2256
+ ] }),
2257
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2258
+ ] })
2259
+ ] })
2260
+ ] });
2261
+ }
2262
+
1925
2263
  // src/interactive/use-theme.ts
1926
- var import_react15 = require("react");
2264
+ var import_react17 = require("react");
1927
2265
  var KEY = "es-theme";
1928
- function applyTheme(theme) {
2266
+ function systemDark() {
2267
+ return typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches;
2268
+ }
2269
+ function resolve(pref) {
2270
+ return pref === "system" ? systemDark() ? "dark" : "light" : pref;
2271
+ }
2272
+ function applyTheme(pref) {
1929
2273
  if (typeof document !== "undefined") {
1930
- document.documentElement.setAttribute("data-theme", theme);
2274
+ document.documentElement.setAttribute("data-theme", resolve(pref));
1931
2275
  }
1932
2276
  }
1933
- function initialTheme() {
2277
+ function initialPref() {
1934
2278
  if (typeof localStorage !== "undefined") {
1935
2279
  const stored = localStorage.getItem(KEY);
1936
- if (stored === "light" || stored === "dark") return stored;
1937
- }
1938
- if (typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") === "dark") {
1939
- return "dark";
2280
+ if (stored === "light" || stored === "dark" || stored === "system") return stored;
1940
2281
  }
1941
- if (typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches) {
1942
- return "dark";
1943
- }
1944
- return "light";
2282
+ return "system";
1945
2283
  }
1946
2284
  function useTheme() {
1947
- const [theme, setTheme] = (0, import_react15.useState)("light");
1948
- (0, import_react15.useEffect)(() => {
1949
- const t = initialTheme();
1950
- setTheme(t);
1951
- applyTheme(t);
2285
+ const [pref, setPrefState] = (0, import_react17.useState)("system");
2286
+ (0, import_react17.useEffect)(() => {
2287
+ const p = initialPref();
2288
+ setPrefState(p);
2289
+ applyTheme(p);
1952
2290
  }, []);
1953
- const toggle = (0, import_react15.useCallback)(() => {
1954
- setTheme((prev) => {
1955
- const next = prev === "dark" ? "light" : "dark";
1956
- applyTheme(next);
1957
- try {
1958
- localStorage.setItem(KEY, next);
1959
- } catch {
1960
- }
1961
- return next;
1962
- });
2291
+ (0, import_react17.useEffect)(() => {
2292
+ if (typeof matchMedia === "undefined") return;
2293
+ const mq = matchMedia("(prefers-color-scheme: dark)");
2294
+ const onChange = () => {
2295
+ if (pref === "system") applyTheme("system");
2296
+ };
2297
+ mq.addEventListener("change", onChange);
2298
+ return () => mq.removeEventListener("change", onChange);
2299
+ }, [pref]);
2300
+ const setPref = (0, import_react17.useCallback)((p) => {
2301
+ setPrefState(p);
2302
+ applyTheme(p);
2303
+ try {
2304
+ localStorage.setItem(KEY, p);
2305
+ } catch {
2306
+ }
1963
2307
  }, []);
1964
- return { theme, toggle };
2308
+ return { pref, setPref };
1965
2309
  }
1966
2310
 
1967
2311
  // src/interactive/ReportInteractive.tsx
1968
- var import_jsx_runtime39 = require("react/jsx-runtime");
2312
+ var import_jsx_runtime44 = require("react/jsx-runtime");
1969
2313
  function ReportInteractive(props) {
1970
2314
  const { report, className, title, dataTheme } = props;
1971
2315
  const result = unwrapReport(report);
1972
2316
  if (!result.ok) {
1973
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2317
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
1974
2318
  }
1975
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportInteractiveView, { ...props, report: result.data });
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportInteractiveView, { ...props, report: result.data });
2320
+ }
2321
+ var THEME_OPTIONS = [
2322
+ { value: "light", label: "Light", Icon: import_lucide_react5.Sun },
2323
+ { value: "system", label: "System", Icon: import_lucide_react5.Monitor },
2324
+ { value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
2325
+ ];
2326
+ function ThemeSegment({ pref, onPref }) {
2327
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2328
+ ToggleGroup,
2329
+ {
2330
+ size: "sm",
2331
+ value: [pref],
2332
+ onValueChange: (v) => {
2333
+ const next = v[0];
2334
+ if (next) onPref(next);
2335
+ },
2336
+ "aria-label": "Color theme",
2337
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Icon, {}) }, value))
2338
+ }
2339
+ );
1976
2340
  }
1977
2341
  function ReportInteractiveView({
1978
2342
  report,
@@ -1982,24 +2346,27 @@ function ReportInteractiveView({
1982
2346
  title,
1983
2347
  dataTheme,
1984
2348
  hideHeader = false,
2349
+ hideToc = false,
1985
2350
  staleAfterDays = 7,
1986
2351
  scenarioHistory
1987
2352
  }) {
1988
- const [query, setQuery] = (0, import_react16.useState)("");
1989
- const [statusFilter, setStatusFilter] = (0, import_react16.useState)("all");
1990
- const [activeTags, setActiveTags] = (0, import_react16.useState)([]);
1991
- const [detail, setDetail] = (0, import_react16.useState)("full");
1992
- const [helpOpen, setHelpOpen] = (0, import_react16.useState)(false);
1993
- const { theme, toggle: toggleTheme } = useTheme();
1994
- const searchRef = (0, import_react16.useRef)(null);
1995
- const deferredQuery = (0, import_react16.useDeferredValue)(query);
1996
- const failures = (0, import_react16.useMemo)(() => listFailures(report), [report]);
1997
- const filtered = (0, import_react16.useMemo)(
2353
+ const [query, setQuery] = (0, import_react18.useState)("");
2354
+ const [statusFilter, setStatusFilter] = (0, import_react18.useState)("all");
2355
+ const [activeTags, setActiveTags] = (0, import_react18.useState)([]);
2356
+ const [detail, setDetail] = (0, import_react18.useState)("full");
2357
+ const [helpOpen, setHelpOpen] = (0, import_react18.useState)(false);
2358
+ const { pref: themePref, setPref: setThemePref } = useTheme();
2359
+ const searchRef = (0, import_react18.useRef)(null);
2360
+ const expandId = (0, import_react18.useId)();
2361
+ const docsId = (0, import_react18.useId)();
2362
+ const deferredQuery = (0, import_react18.useDeferredValue)(query);
2363
+ const failures = (0, import_react18.useMemo)(() => listFailures(report), [report]);
2364
+ const filtered = (0, import_react18.useMemo)(
1998
2365
  () => filterReport(report, { query: deferredQuery, status: statusFilter, tags: activeTags }),
1999
2366
  [report, deferredQuery, statusFilter, activeTags]
2000
2367
  );
2001
2368
  const isFiltering = query !== deferredQuery;
2002
- const statusOptions = (0, import_react16.useMemo)(() => {
2369
+ const statusOptions = (0, import_react18.useMemo)(() => {
2003
2370
  const s = report.summary;
2004
2371
  const opts = [
2005
2372
  { key: "all", label: "All", count: s.total }
@@ -2010,19 +2377,19 @@ function ReportInteractiveView({
2010
2377
  if (s.pending) opts.push({ key: "pending", label: "Pending", count: s.pending });
2011
2378
  return opts;
2012
2379
  }, [report]);
2013
- const tagOptions = (0, import_react16.useMemo)(() => allTags(report), [report]);
2014
- const toggleTag = (0, import_react16.useCallback)(
2380
+ const tagOptions = (0, import_react18.useMemo)(() => allTags(report), [report]);
2381
+ const toggleTag = (0, import_react18.useCallback)(
2015
2382
  (tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
2016
2383
  []
2017
2384
  );
2018
- const [toast, setToast] = (0, import_react16.useState)(null);
2019
- const toastTimer = (0, import_react16.useRef)(null);
2020
- const showToast = (0, import_react16.useCallback)((msg) => {
2385
+ const [toast, setToast] = (0, import_react18.useState)(null);
2386
+ const toastTimer = (0, import_react18.useRef)(null);
2387
+ const showToast = (0, import_react18.useCallback)((msg) => {
2021
2388
  setToast(msg);
2022
2389
  if (toastTimer.current) clearTimeout(toastTimer.current);
2023
2390
  toastTimer.current = setTimeout(() => setToast(null), 1800);
2024
2391
  }, []);
2025
- const copy = (0, import_react16.useCallback)(
2392
+ const copy = (0, import_react18.useCallback)(
2026
2393
  (text, msg) => {
2027
2394
  if (typeof navigator !== "undefined" && navigator.clipboard) {
2028
2395
  navigator.clipboard.writeText(text).then(() => showToast(msg), () => showToast("Copy failed"));
@@ -2030,7 +2397,7 @@ function ReportInteractiveView({
2030
2397
  },
2031
2398
  [showToast]
2032
2399
  );
2033
- const scenarioActions = (0, import_react16.useMemo)(
2400
+ const scenarioActions = (0, import_react18.useMemo)(
2034
2401
  () => ({
2035
2402
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
2036
2403
  copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
@@ -2038,17 +2405,26 @@ function ReportInteractiveView({
2038
2405
  }),
2039
2406
  [copy]
2040
2407
  );
2041
- const failureIndexRef = (0, import_react16.useRef)(0);
2042
- const collapse = useCollapseState();
2043
- const allCollapsibleIds = (0, import_react16.useMemo)(
2408
+ const failureIndexRef = (0, import_react18.useRef)(0);
2409
+ const allCollapsibleIds = (0, import_react18.useMemo)(
2044
2410
  () => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
2045
2411
  [report]
2046
2412
  );
2047
- const collapseAll = (0, import_react16.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
2048
- const focusSearch = (0, import_react16.useCallback)(() => {
2413
+ const collapse = useCollapseState();
2414
+ const collapseAll = (0, import_react18.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
2415
+ const [expandedAll, setExpandedAll] = (0, import_react18.useState)(true);
2416
+ const setExpanded = (0, import_react18.useCallback)(
2417
+ (expanded) => {
2418
+ if (expanded) collapse.expandAll();
2419
+ else collapseAll();
2420
+ setExpandedAll(expanded);
2421
+ },
2422
+ [collapse, collapseAll]
2423
+ );
2424
+ const focusSearch = (0, import_react18.useCallback)(() => {
2049
2425
  searchRef.current?.focus();
2050
2426
  }, []);
2051
- const stepFailure = (0, import_react16.useCallback)(
2427
+ const stepFailure = (0, import_react18.useCallback)(
2052
2428
  (direction) => {
2053
2429
  if (failures.length === 0) return;
2054
2430
  failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
@@ -2057,10 +2433,10 @@ function ReportInteractiveView({
2057
2433
  },
2058
2434
  [failures]
2059
2435
  );
2060
- const toggleHelp = (0, import_react16.useCallback)(() => {
2436
+ const toggleHelp = (0, import_react18.useCallback)(() => {
2061
2437
  setHelpOpen((v) => !v);
2062
2438
  }, []);
2063
- const escape = (0, import_react16.useCallback)(() => {
2439
+ const escape = (0, import_react18.useCallback)(() => {
2064
2440
  if (helpOpen) setHelpOpen(false);
2065
2441
  else if (query !== "") setQuery("");
2066
2442
  }, [helpOpen, query]);
@@ -2070,20 +2446,21 @@ function ReportInteractiveView({
2070
2446
  onPrevFailure: () => stepFailure(-1),
2071
2447
  onToggleHelp: toggleHelp,
2072
2448
  onEscape: escape,
2073
- onExpandAll: collapse.expandAll,
2074
- onCollapseAll: collapseAll
2449
+ onExpandAll: () => setExpanded(true),
2450
+ onCollapseAll: () => setExpanded(false)
2075
2451
  });
2076
2452
  useDeepLinkScroll();
2077
2453
  const hasContent = filtered.features.length > 0;
2078
2454
  const totalScenarios = report.summary.total;
2079
2455
  const matchedScenarios = filtered.summary.total;
2080
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2456
+ const showToc = !hideToc && totalScenarios > 3;
2457
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2081
2458
  ReportRoot,
2082
2459
  {
2083
2460
  report: filtered,
2084
2461
  customRenderers,
2085
2462
  renderers,
2086
- children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2463
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2087
2464
  "main",
2088
2465
  {
2089
2466
  className: cn("es-report", "es-report-interactive", className),
@@ -2092,9 +2469,38 @@ function ReportInteractiveView({
2092
2469
  "data-theme": dataTheme,
2093
2470
  "data-detail-level": detail,
2094
2471
  children: [
2095
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("header", { className: "es-report-header", children: [
2096
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportTitleBlock, { title }),
2097
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2472
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("header", { className: "es-report-header", children: [
2473
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2474
+ "div",
2475
+ {
2476
+ "data-es-navbar": true,
2477
+ className: cn(
2478
+ "flex flex-wrap items-center gap-4",
2479
+ hideHeader ? "justify-end" : "justify-between"
2480
+ ),
2481
+ children: [
2482
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2483
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
2484
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2485
+ ReportSearch,
2486
+ {
2487
+ ref: searchRef,
2488
+ value: query,
2489
+ onChange: setQuery,
2490
+ matchedCount: matchedScenarios,
2491
+ totalCount: totalScenarios
2492
+ }
2493
+ ),
2494
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2495
+ ] })
2496
+ ]
2497
+ }
2498
+ ),
2499
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
2500
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportSummary, {}),
2501
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportMeta, {})
2502
+ ] }),
2503
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2098
2504
  ReportFreshness,
2099
2505
  {
2100
2506
  lastRunMs: reportLastRunMs(report),
@@ -2102,62 +2508,8 @@ function ReportInteractiveView({
2102
2508
  staleAfterDays
2103
2509
  }
2104
2510
  ),
2105
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2106
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
2107
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2108
- ReportSearch,
2109
- {
2110
- ref: searchRef,
2111
- value: query,
2112
- onChange: setQuery,
2113
- matchedCount: matchedScenarios,
2114
- totalCount: totalScenarios
2115
- }
2116
- ),
2117
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex shrink-0 gap-1", children: [
2118
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2119
- "button",
2120
- {
2121
- type: "button",
2122
- onClick: collapse.expandAll,
2123
- "aria-keyshortcuts": "e",
2124
- className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
2125
- children: "Expand all"
2126
- }
2127
- ),
2128
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2129
- "button",
2130
- {
2131
- type: "button",
2132
- onClick: collapseAll,
2133
- "aria-keyshortcuts": "c",
2134
- className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
2135
- children: "Collapse all"
2136
- }
2137
- ),
2138
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2139
- "button",
2140
- {
2141
- type: "button",
2142
- onClick: () => setDetail((d) => d === "full" ? "minimal" : "full"),
2143
- "aria-pressed": detail === "minimal",
2144
- className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
2145
- children: detail === "full" ? "Hide docs" : "Show docs"
2146
- }
2147
- ),
2148
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2149
- "button",
2150
- {
2151
- type: "button",
2152
- onClick: toggleTheme,
2153
- "aria-label": `Switch to ${theme === "dark" ? "light" : "dark"} theme`,
2154
- className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
2155
- children: theme === "dark" ? "\u2600 Light" : "\u263E Dark"
2156
- }
2157
- )
2158
- ] })
2159
- ] }),
2160
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2511
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2512
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2161
2513
  ReportFilters,
2162
2514
  {
2163
2515
  statuses: statusOptions,
@@ -2167,26 +2519,44 @@ function ReportInteractiveView({
2167
2519
  activeTags,
2168
2520
  onToggleTag: toggleTag
2169
2521
  }
2170
- )
2522
+ ),
2523
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2524
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportTocDrawer, {}) : null,
2525
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
2526
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2527
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2528
+ ] }),
2529
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
2530
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2531
+ Switch,
2532
+ {
2533
+ id: docsId,
2534
+ checked: detail === "full",
2535
+ onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
2536
+ }
2537
+ ),
2538
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2539
+ ] })
2540
+ ] })
2171
2541
  ] }),
2172
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportFailureBanner, { failures }),
2173
- hasContent ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex gap-6", children: [
2174
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportToc, {}),
2175
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportFeatureList, {}) })
2176
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2177
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2542
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportFailureBanner, { failures }),
2543
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex gap-6", children: [
2544
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportToc, {}),
2545
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportFeatureList, {}) })
2546
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2547
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2178
2548
  "button",
2179
2549
  {
2180
2550
  type: "button",
2181
- className: "es-shortcuts-trigger",
2551
+ className: "es-shortcuts-trigger fixed right-4 bottom-4 z-40 flex size-9 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50",
2182
2552
  "aria-label": "Keyboard shortcuts",
2183
- "aria-keyshortcuts": "Shift+?",
2553
+ "aria-keyshortcuts": "?",
2184
2554
  onClick: toggleHelp,
2185
2555
  children: "?"
2186
2556
  }
2187
2557
  ),
2188
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2189
- toast ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2558
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2559
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2190
2560
  "div",
2191
2561
  {
2192
2562
  role: "status",
@@ -2203,14 +2573,14 @@ function ReportInteractiveView({
2203
2573
  }
2204
2574
 
2205
2575
  // src/interactive/ReportInteractiveIsland.tsx
2206
- var import_react19 = require("react");
2576
+ var import_react21 = require("react");
2207
2577
 
2208
2578
  // src/interactive/island-renderers.tsx
2209
- var import_react18 = require("react");
2579
+ var import_react20 = require("react");
2210
2580
 
2211
2581
  // src/components/doc/MermaidView.tsx
2212
- var import_react17 = require("react");
2213
- var import_jsx_runtime40 = require("react/jsx-runtime");
2582
+ var import_react19 = require("react");
2583
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2214
2584
  function prefersDark(el) {
2215
2585
  const themed = el?.closest("[data-theme]");
2216
2586
  if (themed?.dataset.theme === "dark") return true;
@@ -2218,9 +2588,9 @@ function prefersDark(el) {
2218
2588
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
2219
2589
  }
2220
2590
  function MermaidView({ entry, load }) {
2221
- const ref = (0, import_react17.useRef)(null);
2222
- const [svg, setSvg] = (0, import_react17.useState)(null);
2223
- (0, import_react17.useEffect)(() => {
2591
+ const ref = (0, import_react19.useRef)(null);
2592
+ const [svg, setSvg] = (0, import_react19.useState)(null);
2593
+ (0, import_react19.useEffect)(() => {
2224
2594
  let cancelled = false;
2225
2595
  const draw = async () => {
2226
2596
  try {
@@ -2247,9 +2617,9 @@ function MermaidView({ entry, load }) {
2247
2617
  };
2248
2618
  }, [entry.code, load]);
2249
2619
  if (svg) {
2250
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("figure", { ref, className: "my-3", children: [
2251
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
2252
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2620
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("figure", { ref, className: "my-3", children: [
2621
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
2622
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2253
2623
  "div",
2254
2624
  {
2255
2625
  role: "img",
@@ -2260,11 +2630,11 @@ function MermaidView({ entry, load }) {
2260
2630
  )
2261
2631
  ] });
2262
2632
  }
2263
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MermaidSource, { entry, ref });
2633
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(MermaidSource, { entry, ref });
2264
2634
  }
2265
2635
 
2266
2636
  // src/interactive/island-renderers.tsx
2267
- var import_jsx_runtime41 = require("react/jsx-runtime");
2637
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2268
2638
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
2269
2639
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
2270
2640
  function cachedCdnModule(url) {
@@ -2281,8 +2651,8 @@ function cachedCdnModule(url) {
2281
2651
  var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
2282
2652
  var loadHljs = cachedCdnModule(HLJS_ESM);
2283
2653
  function CdnHighlightedCode({ entry }) {
2284
- const [html, setHtml] = (0, import_react18.useState)(null);
2285
- (0, import_react18.useEffect)(() => {
2654
+ const [html, setHtml] = (0, import_react20.useState)(null);
2655
+ (0, import_react20.useEffect)(() => {
2286
2656
  let cancelled = false;
2287
2657
  void (async () => {
2288
2658
  try {
@@ -2298,46 +2668,48 @@ function CdnHighlightedCode({ entry }) {
2298
2668
  cancelled = true;
2299
2669
  };
2300
2670
  }, [entry.content, entry.lang]);
2301
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2671
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2302
2672
  "code",
2303
2673
  {
2304
2674
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
2305
2675
  dangerouslySetInnerHTML: { __html: html }
2306
2676
  }
2307
- ) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
2677
+ ) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
2308
2678
  }
2309
2679
  function buildIslandRenderers(opts) {
2310
2680
  const renderers = {};
2311
2681
  if (opts.mermaid) {
2312
- renderers.mermaid = (entry) => (0, import_react18.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
2682
+ renderers.mermaid = (entry) => (0, import_react20.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
2313
2683
  }
2314
2684
  if (opts.syntaxHighlighting) {
2315
- renderers.code = (entry) => (0, import_react18.createElement)(CdnHighlightedCode, { entry });
2685
+ renderers.code = (entry) => (0, import_react20.createElement)(CdnHighlightedCode, { entry });
2316
2686
  }
2317
2687
  return renderers;
2318
2688
  }
2319
2689
 
2320
2690
  // src/interactive/ReportInteractiveIsland.tsx
2321
- var import_jsx_runtime42 = require("react/jsx-runtime");
2691
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2322
2692
  function ReportInteractiveIsland({
2323
2693
  report,
2324
2694
  title,
2325
2695
  className,
2326
2696
  hideHeader,
2697
+ hideToc,
2327
2698
  syntaxHighlighting = true,
2328
2699
  mermaid = true
2329
2700
  }) {
2330
- const renderers = (0, import_react19.useMemo)(
2701
+ const renderers = (0, import_react21.useMemo)(
2331
2702
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
2332
2703
  [syntaxHighlighting, mermaid]
2333
2704
  );
2334
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2705
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2335
2706
  ReportInteractive,
2336
2707
  {
2337
2708
  report,
2338
2709
  title,
2339
2710
  className,
2340
2711
  hideHeader,
2712
+ hideToc,
2341
2713
  renderers
2342
2714
  }
2343
2715
  );