executable-stories-react 0.12.0 → 0.13.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.
@@ -47,8 +47,9 @@ __export(interactive_exports, {
47
47
  module.exports = __toCommonJS(interactive_exports);
48
48
 
49
49
  // src/interactive/ReportInteractive.tsx
50
- var import_lucide_react5 = require("lucide-react");
51
- var import_react21 = require("react");
50
+ var import_lucide_react6 = require("lucide-react");
51
+ var import_react21 = require("webmcpable/react");
52
+ var import_react22 = require("react");
52
53
 
53
54
  // src/components/ui/empty.tsx
54
55
  var import_class_variance_authority = require("class-variance-authority");
@@ -2104,9 +2105,69 @@ function listFailures(report) {
2104
2105
  return out;
2105
2106
  }
2106
2107
 
2108
+ // src/interactive/ReportAgentFilterNotice.tsx
2109
+ var import_lucide_react3 = require("lucide-react");
2110
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2111
+ function ReportAgentFilterNotice({
2112
+ applied,
2113
+ onReset,
2114
+ onDismiss
2115
+ }) {
2116
+ const terms = [];
2117
+ if (applied.search) terms.push(`matching \u201C${applied.search}\u201D`);
2118
+ if (applied.status !== "all") terms.push(applied.status);
2119
+ if (applied.tags.length > 0) terms.push(applied.tags.join(", "));
2120
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2121
+ "div",
2122
+ {
2123
+ role: "status",
2124
+ "aria-live": "polite",
2125
+ className: "flex flex-wrap items-center gap-x-3 gap-y-1 rounded-md border border-primary/40 bg-primary/5 px-3 py-2 text-sm",
2126
+ children: [
2127
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react3.Bot, { "aria-hidden": true, className: "size-4 shrink-0 text-primary" }),
2128
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "min-w-0 flex-1", children: [
2129
+ "An agent filtered this report",
2130
+ terms.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2131
+ " to ",
2132
+ terms.join(" \xB7 ")
2133
+ ] }) : null,
2134
+ " \u2014",
2135
+ " ",
2136
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "tabular-nums", children: [
2137
+ applied.matched,
2138
+ " of ",
2139
+ applied.total,
2140
+ " scenarios"
2141
+ ] }),
2142
+ "."
2143
+ ] }),
2144
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2145
+ "button",
2146
+ {
2147
+ type: "button",
2148
+ onClick: onReset,
2149
+ className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs hover:bg-muted",
2150
+ children: "Show all"
2151
+ }
2152
+ ),
2153
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2154
+ "button",
2155
+ {
2156
+ type: "button",
2157
+ onClick: onDismiss,
2158
+ "aria-label": "Dismiss",
2159
+ className: "cursor-pointer rounded-md p-1 text-muted-foreground hover:text-foreground",
2160
+ children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react3.X, { "aria-hidden": true, className: "size-4" })
2161
+ }
2162
+ )
2163
+ ]
2164
+ }
2165
+ );
2166
+ }
2167
+
2107
2168
  // src/interactive/ReportFailureBanner.tsx
2108
2169
  var import_react10 = require("react");
2109
- var import_jsx_runtime40 = require("react/jsx-runtime");
2170
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2110
2171
  function ReportFailureBanner({ failures }) {
2111
2172
  const first = failures[0];
2112
2173
  const jumpToFirst = (0, import_react10.useCallback)(() => {
@@ -2114,7 +2175,7 @@ function ReportFailureBanner({ failures }) {
2114
2175
  scrollToScenarioId(first.scenarioId);
2115
2176
  }, [first]);
2116
2177
  if (failures.length === 0) return null;
2117
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2178
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2118
2179
  "div",
2119
2180
  {
2120
2181
  className: "es-failure-banner",
@@ -2122,13 +2183,13 @@ function ReportFailureBanner({ failures }) {
2122
2183
  "aria-live": "polite",
2123
2184
  "aria-label": "Failure summary",
2124
2185
  children: [
2125
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "es-failure-banner-text", children: [
2126
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: failures.length }),
2186
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "es-failure-banner-text", children: [
2187
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: failures.length }),
2127
2188
  " ",
2128
2189
  "failure",
2129
2190
  failures.length === 1 ? "" : "s"
2130
2191
  ] }),
2131
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2192
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2132
2193
  "button",
2133
2194
  {
2134
2195
  type: "button",
@@ -2145,7 +2206,7 @@ function ReportFailureBanner({ failures }) {
2145
2206
 
2146
2207
  // src/interactive/ReportFilters.tsx
2147
2208
  var import_react11 = require("react");
2148
- var import_jsx_runtime41 = require("react/jsx-runtime");
2209
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2149
2210
  var TAG_LIMIT = 12;
2150
2211
  function ReportFilters({
2151
2212
  statuses,
@@ -2159,10 +2220,10 @@ function ReportFilters({
2159
2220
  if (statuses.length === 0 && tags.length === 0) return null;
2160
2221
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2161
2222
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2162
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-col gap-2", children: [
2163
- 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) => {
2223
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-col gap-2", children: [
2224
+ statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2164
2225
  const active = status === opt.key;
2165
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2226
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2166
2227
  "button",
2167
2228
  {
2168
2229
  type: "button",
@@ -2174,23 +2235,23 @@ function ReportFilters({
2174
2235
  ),
2175
2236
  children: [
2176
2237
  opt.label,
2177
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2238
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2178
2239
  ]
2179
2240
  },
2180
2241
  opt.key
2181
2242
  );
2182
2243
  }) }) : null,
2183
- 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: [
2244
+ tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2184
2245
  visibleTags.map((tag) => {
2185
2246
  const active = activeTags.includes(tag);
2186
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2247
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2187
2248
  "button",
2188
2249
  {
2189
2250
  type: "button",
2190
2251
  "aria-pressed": active,
2191
2252
  onClick: () => onToggleTag(tag),
2192
2253
  className: "cursor-pointer",
2193
- children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2254
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2194
2255
  Badge,
2195
2256
  {
2196
2257
  variant: "tag",
@@ -2203,7 +2264,7 @@ function ReportFilters({
2203
2264
  }
2204
2265
  ) }, tag);
2205
2266
  }),
2206
- tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2267
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2207
2268
  "button",
2208
2269
  {
2209
2270
  type: "button",
@@ -2219,7 +2280,7 @@ function ReportFilters({
2219
2280
 
2220
2281
  // src/interactive/ReportFreshness.tsx
2221
2282
  var import_react12 = require("react");
2222
- var import_jsx_runtime42 = require("react/jsx-runtime");
2283
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2223
2284
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2224
2285
  const [now, setNow] = (0, import_react12.useState)(nowMs);
2225
2286
  (0, import_react12.useEffect)(() => {
@@ -2227,7 +2288,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2227
2288
  }, [nowMs]);
2228
2289
  if (lastRunMs === void 0 || now === void 0) return null;
2229
2290
  const age = formatRelativeAge(lastRunMs, now);
2230
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2291
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2231
2292
  "a",
2232
2293
  {
2233
2294
  href: ciUrl,
@@ -2238,9 +2299,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2238
2299
  }
2239
2300
  ) : null;
2240
2301
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
2241
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2242
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2243
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("strong", { children: [
2302
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2303
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
2304
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("strong", { children: [
2244
2305
  "Last verified ",
2245
2306
  age,
2246
2307
  "."
@@ -2250,10 +2311,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2250
2311
  runLink
2251
2312
  ] });
2252
2313
  }
2253
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2314
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2254
2315
  "Verified ",
2255
2316
  age,
2256
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2317
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
2257
2318
  " \xB7 ",
2258
2319
  runLink
2259
2320
  ] }) : null
@@ -2261,15 +2322,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2261
2322
  }
2262
2323
 
2263
2324
  // src/interactive/ReportLastRunDelta.tsx
2264
- var import_jsx_runtime43 = require("react/jsx-runtime");
2325
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2265
2326
  var MAX_LINKS = 5;
2266
2327
  function ScenarioLinks({ scenarios, tone }) {
2267
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
2268
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
2328
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
2329
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { children: [
2269
2330
  i > 0 ? ", " : "",
2270
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2331
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2271
2332
  ] }, s.id)),
2272
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
2333
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { children: [
2273
2334
  " and ",
2274
2335
  scenarios.length - MAX_LINKS,
2275
2336
  " more"
@@ -2286,23 +2347,23 @@ function ReportLastRunDelta({
2286
2347
  if (!delta) return null;
2287
2348
  const { newlyFailing, newlyPassing, added } = delta;
2288
2349
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
2289
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2350
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2290
2351
  }
2291
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2292
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2293
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2 text-fail", children: [
2352
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2353
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2354
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-2 text-fail", children: [
2294
2355
  newlyFailing.length,
2295
2356
  " newly failing (",
2296
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2357
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2297
2358
  ")"
2298
2359
  ] }) : null,
2299
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2 text-pass", children: [
2360
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-2 text-pass", children: [
2300
2361
  newlyPassing.length,
2301
2362
  " fixed (",
2302
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2363
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2303
2364
  ")"
2304
2365
  ] }) : null,
2305
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2", children: [
2366
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "ml-2", children: [
2306
2367
  added.length,
2307
2368
  " new"
2308
2369
  ] }) : null
@@ -2311,13 +2372,13 @@ function ReportLastRunDelta({
2311
2372
 
2312
2373
  // src/interactive/ReportSearch.tsx
2313
2374
  var import_react13 = require("react");
2314
- var import_lucide_react3 = require("lucide-react");
2375
+ var import_lucide_react4 = require("lucide-react");
2315
2376
 
2316
2377
  // src/components/ui/input.tsx
2317
2378
  var React6 = require("react");
2318
- var import_jsx_runtime44 = require("react/jsx-runtime");
2379
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2319
2380
  function Input({ className, type, ...props }) {
2320
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2381
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2321
2382
  "input",
2322
2383
  {
2323
2384
  type,
@@ -2334,7 +2395,7 @@ function Input({ className, type, ...props }) {
2334
2395
  }
2335
2396
 
2336
2397
  // src/interactive/ReportSearch.tsx
2337
- var import_jsx_runtime45 = require("react/jsx-runtime");
2398
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2338
2399
  var ReportSearch = (0, import_react13.forwardRef)(
2339
2400
  function ReportSearch2(props, ref) {
2340
2401
  const {
@@ -2356,17 +2417,17 @@ var ReportSearch = (0, import_react13.forwardRef)(
2356
2417
  e.preventDefault();
2357
2418
  }
2358
2419
  }
2359
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
2360
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
2361
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "relative w-full sm:w-72", children: [
2362
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2363
- import_lucide_react3.Search,
2420
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
2421
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
2422
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "relative w-full sm:w-72", children: [
2423
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2424
+ import_lucide_react4.Search,
2364
2425
  {
2365
2426
  "aria-hidden": true,
2366
2427
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
2367
2428
  }
2368
2429
  ),
2369
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2430
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2370
2431
  Input,
2371
2432
  {
2372
2433
  ref,
@@ -2383,7 +2444,7 @@ var ReportSearch = (0, import_react13.forwardRef)(
2383
2444
  }
2384
2445
  )
2385
2446
  ] }),
2386
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2447
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2387
2448
  "span",
2388
2449
  {
2389
2450
  className: "text-xs tabular-nums text-muted-foreground",
@@ -2457,7 +2518,7 @@ function Button({
2457
2518
  }
2458
2519
 
2459
2520
  // src/interactive/ReportShortcutsHelp.tsx
2460
- var import_jsx_runtime46 = require("react/jsx-runtime");
2521
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2461
2522
  var SHORTCUTS = [
2462
2523
  { keys: "/", description: "Focus search" },
2463
2524
  { keys: "f", description: "Jump to next failure" },
@@ -2469,9 +2530,9 @@ var SHORTCUTS = [
2469
2530
  ];
2470
2531
  function Keys({ keys }) {
2471
2532
  const parts = keys.split(" ");
2472
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2473
- i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2474
- /* @__PURE__ */ (0, import_jsx_runtime46.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 })
2533
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2534
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2535
+ /* @__PURE__ */ (0, import_jsx_runtime47.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 })
2475
2536
  ] }, k)) });
2476
2537
  }
2477
2538
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2482,7 +2543,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2482
2543
  if (open && !dialog.open) dialog.showModal();
2483
2544
  else if (!open && dialog.open) dialog.close();
2484
2545
  }, [open]);
2485
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2546
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2486
2547
  "dialog",
2487
2548
  {
2488
2549
  ref: dialogRef,
@@ -2490,12 +2551,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2490
2551
  "aria-label": "Keyboard shortcuts",
2491
2552
  onClose,
2492
2553
  children: [
2493
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2494
- /* @__PURE__ */ (0, import_jsx_runtime46.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_runtime46.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2495
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Keys, { keys: s.keys }) }),
2496
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2554
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime47.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_runtime47.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2556
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Keys, { keys: s.keys }) }),
2557
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2497
2558
  ] }, s.keys)) }),
2498
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2559
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2499
2560
  ]
2500
2561
  }
2501
2562
  );
@@ -2503,7 +2564,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2503
2564
 
2504
2565
  // src/interactive/ReportToc.tsx
2505
2566
  var import_react15 = require("react");
2506
- var import_jsx_runtime47 = require("react/jsx-runtime");
2567
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2507
2568
  var DOT = {
2508
2569
  passed: "bg-pass",
2509
2570
  failed: "bg-fail",
@@ -2532,11 +2593,11 @@ function TocContent({ onNavigate }) {
2532
2593
  return () => observer.disconnect();
2533
2594
  }, [scenarioIds]);
2534
2595
  if (report.features.length === 0) return null;
2535
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("li", { children: [
2536
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2537
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2596
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("li", { children: [
2597
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2598
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2538
2599
  const active = activeId === scenario.id;
2539
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2600
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
2540
2601
  "a",
2541
2602
  {
2542
2603
  href: `#${scenario.id}`,
@@ -2551,14 +2612,14 @@ function TocContent({ onNavigate }) {
2551
2612
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2552
2613
  ),
2553
2614
  children: [
2554
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2615
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2555
2616
  "span",
2556
2617
  {
2557
2618
  "aria-hidden": true,
2558
2619
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2559
2620
  }
2560
2621
  ),
2561
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "break-words", children: scenario.title })
2622
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "break-words", children: scenario.title })
2562
2623
  ]
2563
2624
  }
2564
2625
  ) }, scenario.id);
@@ -2568,14 +2629,14 @@ function TocContent({ onNavigate }) {
2568
2629
  function ReportToc() {
2569
2630
  const report = useReport();
2570
2631
  if (report.features.length === 0) return null;
2571
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2632
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
2572
2633
  "nav",
2573
2634
  {
2574
2635
  "aria-label": "Table of contents",
2575
2636
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2576
2637
  children: [
2577
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2578
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TocContent, {})
2638
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2639
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(TocContent, {})
2579
2640
  ]
2580
2641
  }
2581
2642
  );
@@ -2584,32 +2645,32 @@ function ReportToc() {
2584
2645
  // src/interactive/ReportTocDrawer.tsx
2585
2646
  var import_react16 = require("react");
2586
2647
  var import_dialog = require("@base-ui/react/dialog");
2587
- var import_lucide_react4 = require("lucide-react");
2588
- var import_jsx_runtime48 = require("react/jsx-runtime");
2648
+ var import_lucide_react5 = require("lucide-react");
2649
+ var import_jsx_runtime49 = require("react/jsx-runtime");
2589
2650
  function ReportTocDrawer() {
2590
2651
  const report = useReport();
2591
2652
  const [open, setOpen] = (0, import_react16.useState)(false);
2592
2653
  if (report.features.length === 0) return null;
2593
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2594
- /* @__PURE__ */ (0, import_jsx_runtime48.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: [
2595
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2654
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2655
+ /* @__PURE__ */ (0, import_jsx_runtime49.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: [
2656
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react5.ListIcon, { "aria-hidden": true }),
2596
2657
  " Contents"
2597
2658
  ] }),
2598
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_dialog.Dialog.Portal, { children: [
2599
- /* @__PURE__ */ (0, import_jsx_runtime48.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" }),
2600
- /* @__PURE__ */ (0, import_jsx_runtime48.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: [
2601
- /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center justify-between", children: [
2602
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2603
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2659
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_dialog.Dialog.Portal, { children: [
2660
+ /* @__PURE__ */ (0, import_jsx_runtime49.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" }),
2661
+ /* @__PURE__ */ (0, import_jsx_runtime49.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: [
2662
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center justify-between", children: [
2663
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2664
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2604
2665
  import_dialog.Dialog.Close,
2605
2666
  {
2606
2667
  "aria-label": "Close contents",
2607
2668
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2608
- children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2669
+ children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react5.XIcon, { "aria-hidden": true })
2609
2670
  }
2610
2671
  )
2611
2672
  ] }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2673
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2613
2674
  ] })
2614
2675
  ] })
2615
2676
  ] });
@@ -2792,13 +2853,227 @@ function useUrlState() {
2792
2853
  return [state, update];
2793
2854
  }
2794
2855
 
2856
+ // src/interactive/webmcp-tools.ts
2857
+ var import_report_queries = require("executable-stories-core/report-queries");
2858
+ var STATUSES2 = [
2859
+ "passed",
2860
+ "failed",
2861
+ "skipped",
2862
+ "pending"
2863
+ ];
2864
+ var VIEW_STATUSES = ["all", ...STATUSES2];
2865
+ var ToolArgumentError = class extends Error {
2866
+ };
2867
+ function asRecord(input) {
2868
+ return typeof input === "object" && input !== null ? input : {};
2869
+ }
2870
+ function readString(input, key) {
2871
+ const value = input[key];
2872
+ if (value === void 0 || value === null) return void 0;
2873
+ if (typeof value !== "string") {
2874
+ throw new ToolArgumentError(`"${key}" must be a string, received ${typeof value}.`);
2875
+ }
2876
+ return value;
2877
+ }
2878
+ function readStringArray(input, key) {
2879
+ const value = input[key];
2880
+ if (value === void 0 || value === null) return void 0;
2881
+ if (typeof value === "string") return [value];
2882
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
2883
+ throw new ToolArgumentError(`"${key}" must be an array of strings.`);
2884
+ }
2885
+ return value;
2886
+ }
2887
+ function readEnum(input, key, allowed) {
2888
+ const value = readString(input, key);
2889
+ if (value === void 0) return void 0;
2890
+ if (!allowed.includes(value)) {
2891
+ throw new ToolArgumentError(
2892
+ `"${key}" must be one of: ${allowed.join(", ")}. Received "${value}".`
2893
+ );
2894
+ }
2895
+ return value;
2896
+ }
2897
+ function readStatuses(input) {
2898
+ const values = readStringArray(input, "statuses");
2899
+ if (values === void 0) return void 0;
2900
+ const invalid = values.filter((v) => !STATUSES2.includes(v));
2901
+ if (invalid.length > 0) {
2902
+ throw new ToolArgumentError(
2903
+ `"statuses" must contain only: ${STATUSES2.join(", ")}. Received "${invalid.join('", "')}".`
2904
+ );
2905
+ }
2906
+ return values;
2907
+ }
2908
+ function guarded(run) {
2909
+ try {
2910
+ return run();
2911
+ } catch (error) {
2912
+ if (error instanceof ToolArgumentError) return `Invalid input \u2014 ${error.message}`;
2913
+ throw error;
2914
+ }
2915
+ }
2916
+ var scenarioListSchema = {
2917
+ type: "object",
2918
+ properties: {
2919
+ statuses: {
2920
+ type: "array",
2921
+ items: { type: "string", enum: [...STATUSES2] },
2922
+ description: "Keep only scenarios with any of these statuses."
2923
+ },
2924
+ tags: {
2925
+ type: "array",
2926
+ items: { type: "string" },
2927
+ description: "Keep only scenarios carrying any of these tags."
2928
+ },
2929
+ sourceFiles: {
2930
+ type: "array",
2931
+ items: { type: "string" },
2932
+ description: "Keep only scenarios whose source file path contains any of these."
2933
+ }
2934
+ }
2935
+ };
2936
+ var READ_ONLY = { readOnlyHint: true, untrustedContentHint: true };
2937
+ function reportTools(deps) {
2938
+ const { report, setView, onAgentFilter } = deps;
2939
+ const now = deps.now ?? Date.now;
2940
+ const provenance = () => ({ run: (0, import_report_queries.runProvenance)(report, now()) });
2941
+ const list = (filters) => {
2942
+ const scenarios = (0, import_report_queries.listScenarioSummaries)(report, filters);
2943
+ return { ...provenance(), total: scenarios.length, scenarios };
2944
+ };
2945
+ return {
2946
+ list_scenarios: {
2947
+ title: "List scenarios",
2948
+ description: "List the behaviour scenarios in this report, optionally filtered by status, tag, or source file. Returns each scenario with its steps, tags, timing and failure message.",
2949
+ annotations: READ_ONLY,
2950
+ input: scenarioListSchema,
2951
+ execute: (input) => guarded(() => {
2952
+ const args = asRecord(input);
2953
+ const filters = {};
2954
+ const statuses = readStatuses(args);
2955
+ const tags = readStringArray(args, "tags");
2956
+ const sourceFiles = readStringArray(args, "sourceFiles");
2957
+ if (statuses) filters.statuses = statuses;
2958
+ if (tags) filters.tags = tags;
2959
+ if (sourceFiles) filters.sourceFiles = sourceFiles;
2960
+ return list(filters);
2961
+ })
2962
+ },
2963
+ get_failing_scenarios: {
2964
+ title: "Get failing scenarios",
2965
+ description: "List only the scenarios that failed in this run, each with the step that failed and its error message. Use this first when asked what is broken.",
2966
+ annotations: READ_ONLY,
2967
+ execute: () => list({ statuses: ["failed"] })
2968
+ },
2969
+ get_feature_summary: {
2970
+ title: "Get feature summary",
2971
+ description: 'Summarise each feature in the report with its scenario counts by status. Use this for "how are we doing" questions rather than listing every scenario.',
2972
+ annotations: READ_ONLY,
2973
+ execute: () => ({
2974
+ ...provenance(),
2975
+ summary: report.summary,
2976
+ features: (0, import_report_queries.getFeatureSummary)(report)
2977
+ })
2978
+ },
2979
+ get_scenario: {
2980
+ title: "Get scenario",
2981
+ description: "Get one scenario by its id or its exact title, with every step and any failure detail. Attachment and document bodies are omitted \u2014 `docKinds` names what the page shows.",
2982
+ annotations: READ_ONLY,
2983
+ input: {
2984
+ type: "object",
2985
+ properties: {
2986
+ idOrTitle: {
2987
+ type: "string",
2988
+ description: "The scenario id, or its exact title as shown in the report."
2989
+ }
2990
+ },
2991
+ required: ["idOrTitle"]
2992
+ },
2993
+ execute: (input) => guarded(() => {
2994
+ const idOrTitle = readString(asRecord(input), "idOrTitle") ?? "";
2995
+ const found = (0, import_report_queries.getScenario)(report, idOrTitle);
2996
+ if (!found) {
2997
+ return {
2998
+ ...provenance(),
2999
+ error: `No scenario found with id or title "${idOrTitle}". Call list_scenarios to see what this report contains.`
3000
+ };
3001
+ }
3002
+ return {
3003
+ ...provenance(),
3004
+ feature: {
3005
+ id: found.feature.id,
3006
+ title: found.feature.title,
3007
+ sourceFile: found.feature.sourceFile
3008
+ },
3009
+ // The summary projection, not the raw scenario: doc entry bodies and
3010
+ // base64 attachments are unbounded, and this payload goes straight
3011
+ // into a model's context window.
3012
+ scenario: (0, import_report_queries.toScenarioSummary)(found.feature, found.scenario)
3013
+ };
3014
+ })
3015
+ },
3016
+ filter_scenarios: {
3017
+ title: "Filter the report",
3018
+ description: `Change what the reader is looking at: set the report's search text, status filter and tag filter. Omitted fields are left as they are; pass an empty string, "all", or an empty array to clear one.`,
3019
+ input: {
3020
+ type: "object",
3021
+ properties: {
3022
+ search: {
3023
+ type: "string",
3024
+ description: "Free text matched against scenario titles, tags and step text. Empty string clears it."
3025
+ },
3026
+ status: {
3027
+ type: "string",
3028
+ enum: [...VIEW_STATUSES],
3029
+ description: 'Show only scenarios with this status. "all" clears the filter.'
3030
+ },
3031
+ tags: {
3032
+ type: "array",
3033
+ items: { type: "string" },
3034
+ description: "Show only scenarios carrying any of these tags. Empty array clears the filter."
3035
+ }
3036
+ }
3037
+ },
3038
+ execute: (input) => guarded(() => {
3039
+ const args = asRecord(input);
3040
+ const search = readString(args, "search");
3041
+ const status = readEnum(args, "status", VIEW_STATUSES);
3042
+ const tags = readStringArray(args, "tags");
3043
+ const applied = {
3044
+ search: search ?? deps.view.query,
3045
+ status: status ?? deps.view.status,
3046
+ tags: tags ?? deps.view.tags
3047
+ };
3048
+ const patch = {};
3049
+ if (search !== void 0) patch.query = search;
3050
+ if (status !== void 0) patch.status = status;
3051
+ if (tags !== void 0) patch.tags = tags;
3052
+ setView(patch);
3053
+ const matched = filterReport(report, {
3054
+ query: applied.search,
3055
+ status: applied.status,
3056
+ tags: applied.tags
3057
+ }).summary.total;
3058
+ const result = {
3059
+ ...applied,
3060
+ matched,
3061
+ total: report.summary.total
3062
+ };
3063
+ onAgentFilter?.(result);
3064
+ return { ...provenance(), applied: result };
3065
+ })
3066
+ }
3067
+ };
3068
+ }
3069
+
2795
3070
  // src/interactive/ReportInteractive.tsx
2796
- var import_jsx_runtime49 = require("react/jsx-runtime");
3071
+ var import_jsx_runtime50 = require("react/jsx-runtime");
2797
3072
  function ReportInteractive(props) {
2798
3073
  const { report, className, title, dataTheme } = props;
2799
3074
  const result = unwrapReport(report);
2800
3075
  if (!result.ok) {
2801
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3076
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
2802
3077
  ReportErrorShell,
2803
3078
  {
2804
3079
  error: result.error,
@@ -2808,18 +3083,18 @@ function ReportInteractive(props) {
2808
3083
  }
2809
3084
  );
2810
3085
  }
2811
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportInteractiveView, { ...props, report: result.data });
3086
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportInteractiveView, { ...props, report: result.data });
2812
3087
  }
2813
3088
  var THEME_OPTIONS = [
2814
- { value: "light", label: "Light", Icon: import_lucide_react5.Sun },
2815
- { value: "system", label: "System", Icon: import_lucide_react5.Monitor },
2816
- { value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
3089
+ { value: "light", label: "Light", Icon: import_lucide_react6.Sun },
3090
+ { value: "system", label: "System", Icon: import_lucide_react6.Monitor },
3091
+ { value: "dark", label: "Dark", Icon: import_lucide_react6.Moon }
2817
3092
  ];
2818
3093
  function ThemeSegment({
2819
3094
  pref,
2820
3095
  onPref
2821
3096
  }) {
2822
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3097
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
2823
3098
  ToggleGroup,
2824
3099
  {
2825
3100
  size: "sm",
@@ -2829,14 +3104,14 @@ function ThemeSegment({
2829
3104
  if (next) onPref(next);
2830
3105
  },
2831
3106
  "aria-label": "Color theme",
2832
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3107
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
2833
3108
  ToggleGroupItem,
2834
3109
  {
2835
3110
  value,
2836
3111
  "aria-label": label,
2837
3112
  title: label,
2838
3113
  className: "px-2",
2839
- children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Icon, {})
3114
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Icon, {})
2840
3115
  },
2841
3116
  value
2842
3117
  ))
@@ -2857,26 +3132,26 @@ function ReportInteractiveView({
2857
3132
  }) {
2858
3133
  const [urlState, setUrlState] = useUrlState();
2859
3134
  const { query, status: statusFilter, tags: activeTags, detail } = urlState;
2860
- const setQuery = (0, import_react21.useCallback)(
3135
+ const setQuery = (0, import_react22.useCallback)(
2861
3136
  (next) => setUrlState({ query: next }),
2862
3137
  [setUrlState]
2863
3138
  );
2864
- const setStatusFilter = (0, import_react21.useCallback)(
3139
+ const setStatusFilter = (0, import_react22.useCallback)(
2865
3140
  (next) => setUrlState({ status: next }),
2866
3141
  [setUrlState]
2867
3142
  );
2868
- const setDetail = (0, import_react21.useCallback)(
3143
+ const setDetail = (0, import_react22.useCallback)(
2869
3144
  (next) => setUrlState({ detail: next }),
2870
3145
  [setUrlState]
2871
3146
  );
2872
- const [helpOpen, setHelpOpen] = (0, import_react21.useState)(false);
3147
+ const [helpOpen, setHelpOpen] = (0, import_react22.useState)(false);
2873
3148
  const { pref: themePref, setPref: setThemePref } = useTheme();
2874
- const searchRef = (0, import_react21.useRef)(null);
2875
- const expandId = (0, import_react21.useId)();
2876
- const docsId = (0, import_react21.useId)();
2877
- const deferredQuery = (0, import_react21.useDeferredValue)(query);
2878
- const failures = (0, import_react21.useMemo)(() => listFailures(report), [report]);
2879
- const filtered = (0, import_react21.useMemo)(
3149
+ const searchRef = (0, import_react22.useRef)(null);
3150
+ const expandId = (0, import_react22.useId)();
3151
+ const docsId = (0, import_react22.useId)();
3152
+ const deferredQuery = (0, import_react22.useDeferredValue)(query);
3153
+ const failures = (0, import_react22.useMemo)(() => listFailures(report), [report]);
3154
+ const filtered = (0, import_react22.useMemo)(
2880
3155
  // Features carrying failures float to the top so the first thing on screen
2881
3156
  // is the thing that needs attention; an all-green run keeps source order.
2882
3157
  () => failuresFirst(
@@ -2889,7 +3164,7 @@ function ReportInteractiveView({
2889
3164
  [report, deferredQuery, statusFilter, activeTags]
2890
3165
  );
2891
3166
  const isFiltering = query !== deferredQuery;
2892
- const statusOptions = (0, import_react21.useMemo)(() => {
3167
+ const statusOptions = (0, import_react22.useMemo)(() => {
2893
3168
  const s = report.summary;
2894
3169
  const opts = [
2895
3170
  { key: "all", label: "All", count: s.total }
@@ -2904,21 +3179,21 @@ function ReportInteractiveView({
2904
3179
  opts.push({ key: "pending", label: "Pending", count: s.pending });
2905
3180
  return opts;
2906
3181
  }, [report]);
2907
- const tagOptions = (0, import_react21.useMemo)(() => allTags(report), [report]);
2908
- const toggleTag = (0, import_react21.useCallback)(
3182
+ const tagOptions = (0, import_react22.useMemo)(() => allTags(report), [report]);
3183
+ const toggleTag = (0, import_react22.useCallback)(
2909
3184
  (tag) => setUrlState({
2910
3185
  tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
2911
3186
  }),
2912
3187
  [activeTags, setUrlState]
2913
3188
  );
2914
- const [toast, setToast] = (0, import_react21.useState)(null);
2915
- const toastTimer = (0, import_react21.useRef)(null);
2916
- const showToast = (0, import_react21.useCallback)((msg) => {
3189
+ const [toast, setToast] = (0, import_react22.useState)(null);
3190
+ const toastTimer = (0, import_react22.useRef)(null);
3191
+ const showToast = (0, import_react22.useCallback)((msg) => {
2917
3192
  setToast(msg);
2918
3193
  if (toastTimer.current) clearTimeout(toastTimer.current);
2919
3194
  toastTimer.current = setTimeout(() => setToast(null), 1800);
2920
3195
  }, []);
2921
- const copy = (0, import_react21.useCallback)(
3196
+ const copy = (0, import_react22.useCallback)(
2922
3197
  (text, msg) => {
2923
3198
  if (typeof navigator !== "undefined" && navigator.clipboard) {
2924
3199
  navigator.clipboard.writeText(text).then(
@@ -2929,7 +3204,7 @@ function ReportInteractiveView({
2929
3204
  },
2930
3205
  [showToast]
2931
3206
  );
2932
- const scenarioActions = (0, import_react21.useMemo)(
3207
+ const scenarioActions = (0, import_react22.useMemo)(
2933
3208
  () => ({
2934
3209
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
2935
3210
  copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
@@ -2937,27 +3212,27 @@ function ReportInteractiveView({
2937
3212
  }),
2938
3213
  [copy]
2939
3214
  );
2940
- const failureIndexRef = (0, import_react21.useRef)(0);
2941
- const allCollapsibleIds = (0, import_react21.useMemo)(
3215
+ const failureIndexRef = (0, import_react22.useRef)(0);
3216
+ const allCollapsibleIds = (0, import_react22.useMemo)(
2942
3217
  () => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
2943
3218
  [report]
2944
3219
  );
2945
3220
  const collapse = useCollapseState(
2946
- (0, import_react21.useCallback)(() => defaultCollapsedIds(report), [report])
3221
+ (0, import_react22.useCallback)(() => defaultCollapsedIds(report), [report])
2947
3222
  );
2948
3223
  const revealFilteredResults = deferredQuery.trim() !== "" || statusFilter !== "all" || activeTags.length > 0;
2949
- const visibleCollapse = (0, import_react21.useMemo)(
3224
+ const visibleCollapse = (0, import_react22.useMemo)(
2950
3225
  () => revealFilteredResults ? { ...collapse.api, isCollapsed: () => false } : collapse.api,
2951
3226
  [collapse.api, revealFilteredResults]
2952
3227
  );
2953
- const collapseAll = (0, import_react21.useCallback)(
3228
+ const collapseAll = (0, import_react22.useCallback)(
2954
3229
  () => collapse.collapseAll(allCollapsibleIds),
2955
3230
  [collapse, allCollapsibleIds]
2956
3231
  );
2957
- const [expandedAll, setExpandedAll] = (0, import_react21.useState)(
3232
+ const [expandedAll, setExpandedAll] = (0, import_react22.useState)(
2958
3233
  () => report.summary.failed === 0
2959
3234
  );
2960
- const setExpanded = (0, import_react21.useCallback)(
3235
+ const setExpanded = (0, import_react22.useCallback)(
2961
3236
  (expanded) => {
2962
3237
  if (expanded) collapse.expandAll();
2963
3238
  else collapseAll();
@@ -2965,10 +3240,24 @@ function ReportInteractiveView({
2965
3240
  },
2966
3241
  [collapse, collapseAll]
2967
3242
  );
2968
- const focusSearch = (0, import_react21.useCallback)(() => {
3243
+ const [agentFilter, setAgentFilter] = (0, import_react22.useState)(null);
3244
+ (0, import_react21.useTools)(
3245
+ reportTools({
3246
+ report,
3247
+ view: urlState,
3248
+ setView: setUrlState,
3249
+ onAgentFilter: setAgentFilter
3250
+ })
3251
+ );
3252
+ const showAgentFilter = agentFilter !== null && agentFilter.search === query && agentFilter.status === statusFilter && agentFilter.tags.length === activeTags.length && agentFilter.tags.every((tag) => activeTags.includes(tag));
3253
+ const clearFilters = (0, import_react22.useCallback)(
3254
+ () => setUrlState({ query: "", status: "all", tags: [] }),
3255
+ [setUrlState]
3256
+ );
3257
+ const focusSearch = (0, import_react22.useCallback)(() => {
2969
3258
  searchRef.current?.focus();
2970
3259
  }, []);
2971
- const stepFailure = (0, import_react21.useCallback)(
3260
+ const stepFailure = (0, import_react22.useCallback)(
2972
3261
  (direction) => {
2973
3262
  if (failures.length === 0) return;
2974
3263
  failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
@@ -2977,10 +3266,10 @@ function ReportInteractiveView({
2977
3266
  },
2978
3267
  [failures]
2979
3268
  );
2980
- const toggleHelp = (0, import_react21.useCallback)(() => {
3269
+ const toggleHelp = (0, import_react22.useCallback)(() => {
2981
3270
  setHelpOpen((v) => !v);
2982
3271
  }, []);
2983
- const escape = (0, import_react21.useCallback)(() => {
3272
+ const escape = (0, import_react22.useCallback)(() => {
2984
3273
  if (helpOpen) setHelpOpen(false);
2985
3274
  else if (query !== "") setQuery("");
2986
3275
  }, [helpOpen, query, setQuery]);
@@ -2998,13 +3287,13 @@ function ReportInteractiveView({
2998
3287
  const totalScenarios = report.summary.total;
2999
3288
  const matchedScenarios = filtered.summary.total;
3000
3289
  const showToc = !hideToc && totalScenarios > 3;
3001
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(CollapseProvider, { value: visibleCollapse, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioFreshnessProvider, { value: { staleAfterDays, report }, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3290
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(CollapseProvider, { value: visibleCollapse, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ScenarioFreshnessProvider, { value: { staleAfterDays, report }, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3002
3291
  ReportRoot,
3003
3292
  {
3004
3293
  report: filtered,
3005
3294
  customRenderers,
3006
3295
  renderers,
3007
- children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
3296
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3008
3297
  "main",
3009
3298
  {
3010
3299
  className: cn("es-report", "es-report-interactive", className),
@@ -3013,8 +3302,8 @@ function ReportInteractiveView({
3013
3302
  "data-theme": dataTheme,
3014
3303
  "data-detail-level": detail,
3015
3304
  children: [
3016
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("header", { className: "es-report-header", children: [
3017
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
3305
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("header", { className: "es-report-header", children: [
3306
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3018
3307
  "div",
3019
3308
  {
3020
3309
  "data-es-navbar": true,
@@ -3023,9 +3312,9 @@ function ReportInteractiveView({
3023
3312
  hideHeader ? "justify-end" : "justify-between"
3024
3313
  ),
3025
3314
  children: [
3026
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
3027
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
3028
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3315
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
3316
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3317
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3029
3318
  ReportSearch,
3030
3319
  {
3031
3320
  ref: searchRef,
@@ -3035,16 +3324,16 @@ function ReportInteractiveView({
3035
3324
  totalCount: totalScenarios
3036
3325
  }
3037
3326
  ),
3038
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
3327
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
3039
3328
  ] })
3040
3329
  ]
3041
3330
  }
3042
3331
  ),
3043
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
3044
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportSummary, {}),
3045
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportMeta, {})
3332
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3333
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportSummary, {}),
3334
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportMeta, {})
3046
3335
  ] }),
3047
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3336
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3048
3337
  ReportFreshness,
3049
3338
  {
3050
3339
  lastRunMs: reportLastRunMs(report),
@@ -3052,14 +3341,14 @@ function ReportInteractiveView({
3052
3341
  staleAfterDays
3053
3342
  }
3054
3343
  ),
3055
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3344
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3056
3345
  ReportLastRunDelta,
3057
3346
  {
3058
3347
  history: scenarioHistory,
3059
3348
  report
3060
3349
  }
3061
3350
  ),
3062
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3351
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3063
3352
  ReportFilters,
3064
3353
  {
3065
3354
  statuses: statusOptions,
@@ -3070,10 +3359,10 @@ function ReportInteractiveView({
3070
3359
  onToggleTag: toggleTag
3071
3360
  }
3072
3361
  ),
3073
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
3074
- showToc ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportTocDrawer, {}) : null,
3075
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
3076
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3362
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
3363
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportTocDrawer, {}) : null,
3364
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3365
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3077
3366
  Switch,
3078
3367
  {
3079
3368
  id: expandId,
@@ -3081,7 +3370,7 @@ function ReportInteractiveView({
3081
3370
  onCheckedChange: setExpanded
3082
3371
  }
3083
3372
  ),
3084
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3373
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3085
3374
  "label",
3086
3375
  {
3087
3376
  htmlFor: expandId,
@@ -3090,8 +3379,8 @@ function ReportInteractiveView({
3090
3379
  }
3091
3380
  )
3092
3381
  ] }),
3093
- /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
3094
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3382
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3383
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3095
3384
  Switch,
3096
3385
  {
3097
3386
  id: docsId,
@@ -3099,7 +3388,7 @@ function ReportInteractiveView({
3099
3388
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
3100
3389
  }
3101
3390
  ),
3102
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3391
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3103
3392
  "label",
3104
3393
  {
3105
3394
  htmlFor: docsId,
@@ -3110,17 +3399,25 @@ function ReportInteractiveView({
3110
3399
  ] })
3111
3400
  ] })
3112
3401
  ] }),
3113
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportFailureBanner, { failures }),
3114
- hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex gap-6", children: [
3115
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportToc, {}),
3116
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportFeatureList, {}) })
3117
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3402
+ showAgentFilter ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3403
+ ReportAgentFilterNotice,
3404
+ {
3405
+ applied: agentFilter,
3406
+ onReset: clearFilters,
3407
+ onDismiss: () => setAgentFilter(null)
3408
+ }
3409
+ ) : null,
3410
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportFailureBanner, { failures }),
3411
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex gap-6", children: [
3412
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportToc, {}),
3413
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportFeatureList, {}) })
3414
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3118
3415
  ReportEmpty,
3119
3416
  {
3120
3417
  message: query ? "No scenarios match the search." : void 0
3121
3418
  }
3122
3419
  ),
3123
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3420
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3124
3421
  "button",
3125
3422
  {
3126
3423
  type: "button",
@@ -3131,14 +3428,14 @@ function ReportInteractiveView({
3131
3428
  children: "?"
3132
3429
  }
3133
3430
  ),
3134
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3431
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3135
3432
  ReportShortcutsHelp,
3136
3433
  {
3137
3434
  open: helpOpen,
3138
3435
  onClose: () => setHelpOpen(false)
3139
3436
  }
3140
3437
  ),
3141
- toast ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3438
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3142
3439
  "div",
3143
3440
  {
3144
3441
  role: "status",
@@ -3155,14 +3452,14 @@ function ReportInteractiveView({
3155
3452
  }
3156
3453
 
3157
3454
  // src/interactive/ReportInteractiveIsland.tsx
3158
- var import_react24 = require("react");
3455
+ var import_react25 = require("react");
3159
3456
 
3160
3457
  // src/interactive/island-renderers.tsx
3161
- var import_react23 = require("react");
3458
+ var import_react24 = require("react");
3162
3459
 
3163
3460
  // src/components/doc/MermaidView.tsx
3164
- var import_react22 = require("react");
3165
- var import_jsx_runtime50 = require("react/jsx-runtime");
3461
+ var import_react23 = require("react");
3462
+ var import_jsx_runtime51 = require("react/jsx-runtime");
3166
3463
  function prefersDark(el) {
3167
3464
  const themed = el?.closest("[data-theme]");
3168
3465
  if (themed?.dataset.theme === "dark") return true;
@@ -3170,10 +3467,10 @@ function prefersDark(el) {
3170
3467
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
3171
3468
  }
3172
3469
  function MermaidView({ entry, load }) {
3173
- const ref = (0, import_react22.useRef)(null);
3174
- const [svg, setSvg] = (0, import_react22.useState)(null);
3175
- const [error, setError] = (0, import_react22.useState)(null);
3176
- (0, import_react22.useEffect)(() => {
3470
+ const ref = (0, import_react23.useRef)(null);
3471
+ const [svg, setSvg] = (0, import_react23.useState)(null);
3472
+ const [error, setError] = (0, import_react23.useState)(null);
3473
+ (0, import_react23.useEffect)(() => {
3177
3474
  let cancelled = false;
3178
3475
  const draw = async () => {
3179
3476
  let mermaid;
@@ -3216,9 +3513,9 @@ function MermaidView({ entry, load }) {
3216
3513
  };
3217
3514
  }, [entry.code, load]);
3218
3515
  if (svg) {
3219
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("figure", { ref, className: "my-3", children: [
3220
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3221
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3516
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("figure", { ref, className: "my-3", children: [
3517
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3518
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3222
3519
  "div",
3223
3520
  {
3224
3521
  role: "img",
@@ -3229,8 +3526,8 @@ function MermaidView({ entry, load }) {
3229
3526
  )
3230
3527
  ] });
3231
3528
  }
3232
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3233
- error ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3529
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
3530
+ error ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
3234
3531
  "p",
3235
3532
  {
3236
3533
  role: "alert",
@@ -3242,12 +3539,12 @@ function MermaidView({ entry, load }) {
3242
3539
  ]
3243
3540
  }
3244
3541
  ) : null,
3245
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MermaidSource, { entry, ref })
3542
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(MermaidSource, { entry, ref })
3246
3543
  ] });
3247
3544
  }
3248
3545
 
3249
3546
  // src/interactive/island-renderers.tsx
3250
- var import_jsx_runtime51 = require("react/jsx-runtime");
3547
+ var import_jsx_runtime52 = require("react/jsx-runtime");
3251
3548
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3252
3549
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3253
3550
  function cachedCdnModule(url) {
@@ -3264,8 +3561,8 @@ function cachedCdnModule(url) {
3264
3561
  var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
3265
3562
  var loadHljs = cachedCdnModule(HLJS_ESM);
3266
3563
  function CdnHighlightedCode({ entry }) {
3267
- const [html, setHtml] = (0, import_react23.useState)(null);
3268
- (0, import_react23.useEffect)(() => {
3564
+ const [html, setHtml] = (0, import_react24.useState)(null);
3565
+ (0, import_react24.useEffect)(() => {
3269
3566
  let cancelled = false;
3270
3567
  void (async () => {
3271
3568
  try {
@@ -3281,27 +3578,27 @@ function CdnHighlightedCode({ entry }) {
3281
3578
  cancelled = true;
3282
3579
  };
3283
3580
  }, [entry.content, entry.lang]);
3284
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3581
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3285
3582
  "code",
3286
3583
  {
3287
3584
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
3288
3585
  dangerouslySetInnerHTML: { __html: html }
3289
3586
  }
3290
- ) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3587
+ ) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3291
3588
  }
3292
3589
  function buildIslandRenderers(opts) {
3293
3590
  const renderers = {};
3294
3591
  if (opts.mermaid) {
3295
- renderers.mermaid = (entry) => (0, import_react23.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
3592
+ renderers.mermaid = (entry) => (0, import_react24.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
3296
3593
  }
3297
3594
  if (opts.syntaxHighlighting) {
3298
- renderers.code = (entry) => (0, import_react23.createElement)(CdnHighlightedCode, { entry });
3595
+ renderers.code = (entry) => (0, import_react24.createElement)(CdnHighlightedCode, { entry });
3299
3596
  }
3300
3597
  return renderers;
3301
3598
  }
3302
3599
 
3303
3600
  // src/interactive/ReportInteractiveIsland.tsx
3304
- var import_jsx_runtime52 = require("react/jsx-runtime");
3601
+ var import_jsx_runtime53 = require("react/jsx-runtime");
3305
3602
  function ReportInteractiveIsland({
3306
3603
  report,
3307
3604
  title,
@@ -3311,11 +3608,11 @@ function ReportInteractiveIsland({
3311
3608
  syntaxHighlighting = true,
3312
3609
  mermaid = true
3313
3610
  }) {
3314
- const renderers = (0, import_react24.useMemo)(
3611
+ const renderers = (0, import_react25.useMemo)(
3315
3612
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
3316
3613
  [syntaxHighlighting, mermaid]
3317
3614
  );
3318
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3615
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3319
3616
  ReportInteractive,
3320
3617
  {
3321
3618
  report,