executable-stories-react 0.8.0 → 0.10.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,7 +47,7 @@ __export(interactive_exports, {
47
47
  module.exports = __toCommonJS(interactive_exports);
48
48
 
49
49
  // src/interactive/ReportInteractive.tsx
50
- var import_react18 = require("react");
50
+ var import_react19 = require("react");
51
51
 
52
52
  // src/result.ts
53
53
  var ok = (data) => ({ ok: true, data });
@@ -65,63 +65,6 @@ var import_react2 = require("react");
65
65
  var import_react = require("react");
66
66
  var ReportContext = (0, import_react.createContext)(null);
67
67
 
68
- // src/context/ReportRoot.tsx
69
- var import_jsx_runtime = require("react/jsx-runtime");
70
- var EMPTY_CUSTOM = {};
71
- var EMPTY_RENDERERS = {};
72
- function ReportRoot({
73
- report,
74
- customRenderers,
75
- renderers,
76
- children
77
- }) {
78
- const value = (0, import_react2.useMemo)(
79
- () => ({
80
- report,
81
- customRenderers: customRenderers ?? EMPTY_CUSTOM,
82
- renderers: renderers ?? EMPTY_RENDERERS
83
- }),
84
- [report, customRenderers, renderers]
85
- );
86
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReportContext.Provider, { value, children });
87
- }
88
-
89
- // src/hooks/useReport.ts
90
- var import_react3 = require("react");
91
- function useReport() {
92
- const ctx = (0, import_react3.useContext)(ReportContext);
93
- if (!ctx) {
94
- throw new Error(
95
- "useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
96
- );
97
- }
98
- return ctx.report;
99
- }
100
-
101
- // ../executable-stories-core/dist/utils/duration.js
102
- function formatDuration(ms) {
103
- if (ms < 1e3) {
104
- return `${Math.round(ms)} ms`;
105
- }
106
- if (ms < 6e4) {
107
- return `${(ms / 1e3).toFixed(2)} s`;
108
- }
109
- const minutes = Math.floor(ms / 6e4);
110
- const seconds = Math.floor(ms % 6e4 / 1e3);
111
- if (minutes < 60) {
112
- return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
113
- }
114
- const hours = Math.floor(minutes / 60);
115
- const remainingMinutes = minutes % 60;
116
- return `${hours}h ${remainingMinutes}m`;
117
- }
118
-
119
- // src/components/doc/DocNote.tsx
120
- var import_jsx_runtime2 = require("react/jsx-runtime");
121
- function DocNote({ entry }) {
122
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
123
- }
124
-
125
68
  // src/components/ui/badge.tsx
126
69
  var React = require("react");
127
70
  var import_class_variance_authority = require("class-variance-authority");
@@ -180,14 +123,249 @@ function Badge({
180
123
  });
181
124
  }
182
125
 
183
- // src/components/doc/DocTag.tsx
126
+ // src/components/doc/NarrativeBlocks.tsx
127
+ var import_jsx_runtime = require("react/jsx-runtime");
128
+ var FILE_TREE_TYPE = "file-tree";
129
+ var DATA_MODEL_TYPE = "data-model";
130
+ function isChangeKind(value) {
131
+ return value === "added" || value === "modified" || value === "removed" || value === "renamed";
132
+ }
133
+ function isRecord(value) {
134
+ return typeof value === "object" && value !== null && !Array.isArray(value);
135
+ }
136
+ function str(value) {
137
+ return typeof value === "string" && value !== "" ? value : void 0;
138
+ }
139
+ function AuthoredMarker({ authored }) {
140
+ if (authored !== "agent") return null;
141
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
142
+ }
143
+ function BlockFrame({
144
+ title,
145
+ authored,
146
+ children
147
+ }) {
148
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("figure", { className: "my-3", children: [
149
+ title || authored === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
150
+ title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthoredMarker, { authored })
152
+ ] }) : null,
153
+ children
154
+ ] });
155
+ }
156
+ function ChangeBadge({ change }) {
157
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { variant: "tag", className: "text-[10px]", children: change });
158
+ }
159
+ function parseFileTree(data) {
160
+ if (!isRecord(data) || !Array.isArray(data.files)) return void 0;
161
+ const files = [];
162
+ for (const raw of data.files) {
163
+ if (typeof raw === "string") {
164
+ if (raw) files.push({ path: raw });
165
+ continue;
166
+ }
167
+ if (!isRecord(raw)) continue;
168
+ const path = str(raw.path);
169
+ if (!path) continue;
170
+ files.push({
171
+ path,
172
+ change: isChangeKind(raw.change) ? raw.change : void 0,
173
+ note: str(raw.note)
174
+ });
175
+ }
176
+ if (files.length === 0) return void 0;
177
+ return { title: str(data.title), authored: str(data.authored), files };
178
+ }
179
+ function buildTree(files) {
180
+ const root = { name: "", children: /* @__PURE__ */ new Map() };
181
+ for (const file of files) {
182
+ const segments = file.path.split("/").filter(Boolean);
183
+ let node = root;
184
+ segments.forEach((segment, index) => {
185
+ let child = node.children.get(segment);
186
+ if (!child) {
187
+ child = { name: segment, children: /* @__PURE__ */ new Map() };
188
+ node.children.set(segment, child);
189
+ }
190
+ if (index === segments.length - 1) child.file = file;
191
+ node = child;
192
+ });
193
+ }
194
+ return root;
195
+ }
196
+ function sortedChildren(node) {
197
+ return [...node.children.values()].sort((a, b) => {
198
+ const aDir = a.children.size > 0;
199
+ const bDir = b.children.size > 0;
200
+ if (aDir !== bDir) return aDir ? -1 : 1;
201
+ return a.name.localeCompare(b.name);
202
+ });
203
+ }
204
+ function TreeBranch({ node, depth }) {
205
+ const children = sortedChildren(node);
206
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
207
+ const isDir = child.children.size > 0;
208
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-baseline gap-2", children: [
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
211
+ "span",
212
+ {
213
+ className: cn(
214
+ "font-mono text-xs",
215
+ isDir ? "text-muted-foreground" : "text-foreground"
216
+ ),
217
+ children: isDir ? `${child.name}/` : child.name
218
+ }
219
+ ),
220
+ child.file?.change ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeBadge, { change: child.file.change }) : null,
221
+ child.file?.note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
222
+ ] }),
223
+ isDir ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeBranch, { node: child, depth: depth + 1 }) : null
224
+ ] }, child.name);
225
+ }) });
226
+ }
227
+ function FileTreeBlock({ entry }) {
228
+ const data = parseFileTree(entry.data);
229
+ if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnreadableBlock, { entry });
230
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
231
+ }
232
+ function parseDataModel(data) {
233
+ if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
234
+ const fields = [];
235
+ for (const raw of data.fields) {
236
+ if (!isRecord(raw)) continue;
237
+ const name = str(raw.name);
238
+ if (!name) continue;
239
+ fields.push({
240
+ name,
241
+ type: str(raw.type),
242
+ note: str(raw.note),
243
+ change: isChangeKind(raw.change) ? raw.change : void 0
244
+ });
245
+ }
246
+ if (fields.length === 0) return void 0;
247
+ return {
248
+ title: str(data.title),
249
+ authored: str(data.authored),
250
+ name: str(data.name),
251
+ fields
252
+ };
253
+ }
254
+ function DataModelBlock({ entry }) {
255
+ const data = parseDataModel(entry.data);
256
+ if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnreadableBlock, { entry });
257
+ const hasNotes = data.fields.some((f) => f.note);
258
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
259
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: "bg-muted/50", children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
261
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
262
+ hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
263
+ ] }) }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: data.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex flex-wrap items-center gap-2", children: [
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono text-xs text-foreground", children: field.name }),
267
+ field.change ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeBadge, { change: field.change }) : null
268
+ ] }) }),
269
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
270
+ hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
271
+ ] }, field.name)) })
272
+ ] }) }) });
273
+ }
274
+ function UnreadableBlock({ entry }) {
275
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
276
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
277
+ entry.type,
278
+ " (unrecognised shape)"
279
+ ] }),
280
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
281
+ "pre",
282
+ {
283
+ tabIndex: 0,
284
+ className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
285
+ children: safeStringify(entry.data)
286
+ }
287
+ )
288
+ ] });
289
+ }
290
+ function safeStringify(value) {
291
+ try {
292
+ return JSON.stringify(value, null, 2);
293
+ } catch {
294
+ return String(value);
295
+ }
296
+ }
297
+ var narrativeBlockRenderers = {
298
+ [FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileTreeBlock, { entry }),
299
+ [DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataModelBlock, { entry })
300
+ };
301
+
302
+ // src/context/ReportRoot.tsx
303
+ var import_jsx_runtime2 = require("react/jsx-runtime");
304
+ var EMPTY_RENDERERS = {};
305
+ function ReportRoot({
306
+ report,
307
+ customRenderers,
308
+ renderers,
309
+ children
310
+ }) {
311
+ const value = (0, import_react2.useMemo)(
312
+ () => ({
313
+ report,
314
+ // Narrative blocks render everywhere the report does (static SSR, the
315
+ // island, Astro) without every host wiring them up. A host renderer for
316
+ // the same type still wins.
317
+ customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
318
+ renderers: renderers ?? EMPTY_RENDERERS
319
+ }),
320
+ [report, customRenderers, renderers]
321
+ );
322
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ReportContext.Provider, { value, children });
323
+ }
324
+
325
+ // src/hooks/useReport.ts
326
+ var import_react3 = require("react");
327
+ function useReport() {
328
+ const ctx = (0, import_react3.useContext)(ReportContext);
329
+ if (!ctx) {
330
+ throw new Error(
331
+ "useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
332
+ );
333
+ }
334
+ return ctx.report;
335
+ }
336
+
337
+ // ../executable-stories-core/dist/utils/duration.js
338
+ function formatDuration(ms) {
339
+ if (ms < 1e3) {
340
+ return `${Math.round(ms)} ms`;
341
+ }
342
+ if (ms < 6e4) {
343
+ return `${(ms / 1e3).toFixed(2)} s`;
344
+ }
345
+ const minutes = Math.floor(ms / 6e4);
346
+ const seconds = Math.floor(ms % 6e4 / 1e3);
347
+ if (minutes < 60) {
348
+ return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
349
+ }
350
+ const hours = Math.floor(minutes / 60);
351
+ const remainingMinutes = minutes % 60;
352
+ return `${hours}h ${remainingMinutes}m`;
353
+ }
354
+
355
+ // src/components/doc/DocNote.tsx
184
356
  var import_jsx_runtime3 = require("react/jsx-runtime");
357
+ function DocNote({ entry }) {
358
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
359
+ }
360
+
361
+ // src/components/doc/DocTag.tsx
362
+ var import_jsx_runtime4 = require("react/jsx-runtime");
185
363
  function DocTag({ entry }) {
186
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
364
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
187
365
  }
188
366
 
189
367
  // src/components/doc/DocKv.tsx
190
- var import_jsx_runtime4 = require("react/jsx-runtime");
368
+ var import_jsx_runtime5 = require("react/jsx-runtime");
191
369
  function formatValue(value) {
192
370
  if (value === null) return "null";
193
371
  if (typeof value === "string") return value;
@@ -199,19 +377,19 @@ function formatValue(value) {
199
377
  }
200
378
  }
201
379
  function DocKv({ entry }) {
202
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
203
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
204
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
380
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
382
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
205
383
  ] });
206
384
  }
207
385
 
208
386
  // src/hooks/useRenderers.ts
209
387
  var import_react4 = require("react");
210
- var EMPTY_CUSTOM2 = {};
388
+ var EMPTY_CUSTOM = {};
211
389
  var EMPTY_RENDERERS2 = {};
212
390
  function useCustomRenderers() {
213
391
  const ctx = (0, import_react4.useContext)(ReportContext);
214
- return ctx?.customRenderers ?? EMPTY_CUSTOM2;
392
+ return ctx?.customRenderers ?? EMPTY_CUSTOM;
215
393
  }
216
394
  function useBuiltinRenderers() {
217
395
  const ctx = (0, import_react4.useContext)(ReportContext);
@@ -219,37 +397,37 @@ function useBuiltinRenderers() {
219
397
  }
220
398
 
221
399
  // src/components/doc/DocCode.tsx
222
- var import_jsx_runtime5 = require("react/jsx-runtime");
400
+ var import_jsx_runtime6 = require("react/jsx-runtime");
223
401
  function CodeFigure({ label, children }) {
224
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("figure", { className: "my-3", children: [
225
- label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
226
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
402
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("figure", { className: "my-3", children: [
403
+ label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
404
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
227
405
  ] });
228
406
  }
229
407
  function DocCode({ entry }) {
230
408
  const renderers = useBuiltinRenderers();
231
409
  if (renderers.code) {
232
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: renderers.code(entry) });
410
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderers.code(entry) });
233
411
  }
234
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
412
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
235
413
  }
236
414
 
237
415
  // src/components/doc/DocTable.tsx
238
- var import_jsx_runtime6 = require("react/jsx-runtime");
416
+ var import_jsx_runtime7 = require("react/jsx-runtime");
239
417
  function DocTable({ entry }) {
240
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("figure", { className: "my-3", children: [
241
- entry.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
242
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
243
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
244
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("tr", { children: row.map((cell2, j) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell2 }, j)) }, i)) })
418
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("figure", { className: "my-3", children: [
419
+ entry.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
420
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
421
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
422
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: row.map((cell2, j) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell2 }, j)) }, i)) })
245
423
  ] }) })
246
424
  ] });
247
425
  }
248
426
 
249
427
  // src/components/doc/DocLink.tsx
250
- var import_jsx_runtime7 = require("react/jsx-runtime");
428
+ var import_jsx_runtime8 = require("react/jsx-runtime");
251
429
  function DocLink({ entry }) {
252
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
430
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
253
431
  "a",
254
432
  {
255
433
  className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
@@ -283,7 +461,7 @@ function isLocalFsPath(value) {
283
461
  }
284
462
 
285
463
  // src/components/doc/DocSection.tsx
286
- var import_jsx_runtime8 = require("react/jsx-runtime");
464
+ var import_jsx_runtime9 = require("react/jsx-runtime");
287
465
  function neutralizeUrl(value) {
288
466
  return safeUrl(value) ?? "#";
289
467
  }
@@ -294,12 +472,12 @@ function safeMarkdownHtml(markdown) {
294
472
  function DocSection({ entry }) {
295
473
  const renderers = useBuiltinRenderers();
296
474
  if (renderers.section) {
297
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: renderers.section(entry) });
475
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderers.section(entry) });
298
476
  }
299
477
  const html = safeMarkdownHtml(entry.markdown);
300
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
301
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
302
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
478
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
479
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
480
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
303
481
  "div",
304
482
  {
305
483
  className: "es-doc-prose prose prose-sm max-w-none",
@@ -310,11 +488,11 @@ function DocSection({ entry }) {
310
488
  }
311
489
 
312
490
  // src/components/doc/DocMermaid.tsx
313
- var import_jsx_runtime9 = require("react/jsx-runtime");
491
+ var import_jsx_runtime10 = require("react/jsx-runtime");
314
492
  function MermaidSource({ entry, ref }) {
315
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
316
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
317
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
493
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
494
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
495
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
318
496
  "pre",
319
497
  {
320
498
  "data-mermaid": true,
@@ -328,24 +506,24 @@ function MermaidSource({ entry, ref }) {
328
506
  function DocMermaid({ entry }) {
329
507
  const renderers = useBuiltinRenderers();
330
508
  if (renderers.mermaid) {
331
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderers.mermaid(entry) });
509
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: renderers.mermaid(entry) });
332
510
  }
333
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MermaidSource, { entry });
511
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MermaidSource, { entry });
334
512
  }
335
513
 
336
514
  // src/components/doc/DocScreenshot.tsx
337
- var import_jsx_runtime10 = require("react/jsx-runtime");
515
+ var import_jsx_runtime11 = require("react/jsx-runtime");
338
516
  function DocScreenshot({ entry }) {
339
517
  const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
340
518
  if (!src) {
341
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
342
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
343
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
344
- entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
519
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
520
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
521
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
522
+ entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
345
523
  ] });
346
524
  }
347
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3", children: [
348
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
525
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
526
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
349
527
  "img",
350
528
  {
351
529
  src,
@@ -354,24 +532,24 @@ function DocScreenshot({ entry }) {
354
532
  className: "max-w-full rounded-md border border-border"
355
533
  }
356
534
  ),
357
- entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
535
+ entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
358
536
  ] });
359
537
  }
360
538
 
361
539
  // src/components/doc/DocVideo.tsx
362
- var import_jsx_runtime11 = require("react/jsx-runtime");
540
+ var import_jsx_runtime12 = require("react/jsx-runtime");
363
541
  function DocVideo({ entry }) {
364
542
  if (isLocalFsPath(entry.path)) {
365
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
366
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
367
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
368
- entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
543
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
544
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
545
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
546
+ entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
369
547
  ] });
370
548
  }
371
549
  const src = safeUrl(entry.path);
372
550
  const poster = safeUrl(entry.poster);
373
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
374
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
551
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
552
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
375
553
  "video",
376
554
  {
377
555
  controls: true,
@@ -381,21 +559,21 @@ function DocVideo({ entry }) {
381
559
  className: "max-w-full rounded-md border border-border"
382
560
  }
383
561
  ),
384
- entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
562
+ entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
385
563
  ] });
386
564
  }
387
565
 
388
566
  // src/components/doc/DocHtml.tsx
389
- var import_jsx_runtime12 = require("react/jsx-runtime");
567
+ var import_jsx_runtime13 = require("react/jsx-runtime");
390
568
  function DocHtml({ entry }) {
391
569
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
392
570
  const title = entry.title ?? "Embedded HTML";
393
571
  const safeHref = safeUrl(entry.url ?? entry.path);
394
572
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
395
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
396
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
397
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: entry.title ?? "HTML" }),
398
- safeHref ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
573
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
574
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
575
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: entry.title ?? "HTML" }),
576
+ safeHref ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
399
577
  "a",
400
578
  {
401
579
  href: safeHref,
@@ -408,7 +586,7 @@ function DocHtml({ entry }) {
408
586
  }
409
587
  ) : null
410
588
  ] }),
411
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
589
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
412
590
  "iframe",
413
591
  {
414
592
  ...frameProps,
@@ -423,7 +601,7 @@ function DocHtml({ entry }) {
423
601
  }
424
602
 
425
603
  // src/components/doc/DocState.tsx
426
- var import_jsx_runtime13 = require("react/jsx-runtime");
604
+ var import_jsx_runtime14 = require("react/jsx-runtime");
427
605
  function DocState({ entry }) {
428
606
  let json;
429
607
  try {
@@ -431,23 +609,23 @@ function DocState({ entry }) {
431
609
  } catch {
432
610
  json = String(entry.value);
433
611
  }
434
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
612
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
435
613
  }
436
614
 
437
615
  // src/components/doc/DocCustom.tsx
438
- var import_jsx_runtime14 = require("react/jsx-runtime");
616
+ var import_jsx_runtime15 = require("react/jsx-runtime");
439
617
  function DocCustom({ entry }) {
440
618
  const renderers = useCustomRenderers();
441
619
  const renderer = renderers[entry.type];
442
620
  if (renderer) {
443
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: renderer(entry) });
621
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: renderer(entry) });
444
622
  }
445
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
446
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
447
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
623
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
624
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
625
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
448
626
  ] });
449
627
  }
450
- function safeStringify(value) {
628
+ function safeStringify2(value) {
451
629
  try {
452
630
  return JSON.stringify(value, null, 2);
453
631
  } catch {
@@ -456,47 +634,47 @@ function safeStringify(value) {
456
634
  }
457
635
 
458
636
  // src/components/doc/DocEntry.tsx
459
- var import_jsx_runtime15 = require("react/jsx-runtime");
637
+ var import_jsx_runtime16 = require("react/jsx-runtime");
460
638
  function DocEntry({ entry }) {
461
639
  switch (entry.kind) {
462
640
  case "note":
463
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocNote, { entry });
641
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocNote, { entry });
464
642
  case "tag":
465
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTag, { entry });
643
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocTag, { entry });
466
644
  case "kv":
467
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocKv, { entry });
645
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocKv, { entry });
468
646
  case "code":
469
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCode, { entry });
647
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocCode, { entry });
470
648
  case "table":
471
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTable, { entry });
649
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocTable, { entry });
472
650
  case "link":
473
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocLink, { entry });
651
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocLink, { entry });
474
652
  case "section":
475
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocSection, { entry });
653
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocSection, { entry });
476
654
  case "mermaid":
477
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocMermaid, { entry });
655
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocMermaid, { entry });
478
656
  case "screenshot":
479
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocScreenshot, { entry });
657
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocScreenshot, { entry });
480
658
  case "video":
481
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocVideo, { entry });
659
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocVideo, { entry });
482
660
  case "html":
483
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocHtml, { entry });
661
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocHtml, { entry });
484
662
  case "state":
485
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocState, { entry });
663
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocState, { entry });
486
664
  case "custom":
487
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCustom, { entry });
665
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocCustom, { entry });
488
666
  }
489
667
  }
490
668
 
491
669
  // src/components/ReportDocEntries.tsx
492
- var import_jsx_runtime16 = require("react/jsx-runtime");
670
+ var import_jsx_runtime17 = require("react/jsx-runtime");
493
671
  function ReportDocEntries({ entries }) {
494
672
  if (entries.length === 0) return null;
495
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocEntry, { entry }, i)) });
673
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_jsx_runtime17.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocEntry, { entry }, i)) });
496
674
  }
497
675
 
498
676
  // src/lib/step-params.tsx
499
- var import_jsx_runtime17 = require("react/jsx-runtime");
677
+ var import_jsx_runtime18 = require("react/jsx-runtime");
500
678
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
501
679
  function highlightStepParams(text) {
502
680
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -508,7 +686,7 @@ function highlightStepParams(text) {
508
686
  const end = start + match[0].length;
509
687
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
510
688
  nodes.push(
511
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
689
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
512
690
  );
513
691
  lastIndex = end;
514
692
  });
@@ -517,7 +695,7 @@ function highlightStepParams(text) {
517
695
  }
518
696
 
519
697
  // src/components/ReportSteps.tsx
520
- var import_jsx_runtime18 = require("react/jsx-runtime");
698
+ var import_jsx_runtime19 = require("react/jsx-runtime");
521
699
  var STEP_GLYPH = {
522
700
  passed: "\u2713",
523
701
  failed: "\u2717",
@@ -532,21 +710,21 @@ var STEP_GLYPH_COLOR = {
532
710
  };
533
711
  function ReportSteps({ scenario }) {
534
712
  if (scenario.steps.length === 0) return null;
535
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportStepItem, { step }, step.id)) });
713
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ReportStepItem, { step }, step.id)) });
536
714
  }
537
715
  function ReportStepItem({ step }) {
538
716
  const kw = step.keyword.toLowerCase();
539
717
  const continuation = kw === "and" || kw === "but";
540
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
718
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
541
719
  "li",
542
720
  {
543
721
  id: step.id,
544
722
  "data-status": step.status,
545
723
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
546
724
  children: [
547
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-baseline gap-2", children: [
548
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
549
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
725
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-baseline gap-2", children: [
726
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
727
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
550
728
  "span",
551
729
  {
552
730
  className: cn(
@@ -556,10 +734,10 @@ function ReportStepItem({ step }) {
556
734
  children: step.keyword
557
735
  }
558
736
  ),
559
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
560
- step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
737
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
738
+ step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
561
739
  ] }),
562
- step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
740
+ step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
563
741
  "pre",
564
742
  {
565
743
  role: "alert",
@@ -567,7 +745,7 @@ function ReportStepItem({ step }) {
567
745
  children: step.errorMessage
568
746
  }
569
747
  ) : null,
570
- step.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ReportDocEntries, { entries: step.docEntries }) }) : null
748
+ step.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ReportDocEntries, { entries: step.docEntries }) }) : null
571
749
  ]
572
750
  }
573
751
  );
@@ -692,7 +870,7 @@ function extractStoryboardFrames(scenario) {
692
870
  }
693
871
 
694
872
  // src/components/ReportStoryboard.tsx
695
- var import_jsx_runtime19 = require("react/jsx-runtime");
873
+ var import_jsx_runtime20 = require("react/jsx-runtime");
696
874
  var FRAME_GLYPH = {
697
875
  passed: "\u2713",
698
876
  failed: "\u2717",
@@ -708,32 +886,32 @@ var FRAME_GLYPH_COLOR = {
708
886
  function ReportStoryboard({ scenario }) {
709
887
  const frames = extractStoryboardFrames(scenario);
710
888
  if (frames.length < 2) return null;
711
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
712
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
713
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StoryboardFrameItem, { frame }, frame.stepId)) })
889
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StoryboardFrameItem, { frame }, frame.stepId)) })
714
892
  ] });
715
893
  }
716
894
  function StoryboardFrameItem({ frame }) {
717
895
  const label = `${frame.keyword} ${frame.text}`;
718
896
  const hasStates = frame.states.length > 0;
719
- const caption = /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
720
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
721
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "min-w-0", children: [
722
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
897
+ const caption = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
898
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
899
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "min-w-0", children: [
900
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
723
901
  " ",
724
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
902
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
725
903
  ] })
726
904
  ] });
727
905
  return (
728
906
  // State cards need room for diff rows; screenshot-only frames keep the
729
907
  // original narrow film-cell width (and DOM) unchanged.
730
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
731
- frame.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(FrameScreenshot, { frame, label }) : null,
732
- hasStates ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(StateCardView, { card }, card.label ?? i)) }) : null,
908
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
909
+ frame.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(FrameScreenshot, { frame, label }) : null,
910
+ hasStates ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StateCardView, { card }, card.label ?? i)) }) : null,
733
911
  frame.screenshot ? caption : (
734
912
  // No screenshot means no image anchor, so the caption carries the
735
913
  // jump-to-step link instead.
736
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
914
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
737
915
  )
738
916
  ] })
739
917
  );
@@ -741,13 +919,13 @@ function StoryboardFrameItem({ frame }) {
741
919
  function FrameScreenshot({ frame, label }) {
742
920
  const path = frame.screenshot?.path ?? "";
743
921
  const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
744
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
922
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
745
923
  "a",
746
924
  {
747
925
  href: `#${frame.stepId}`,
748
926
  "aria-label": `Jump to step: ${label}`,
749
927
  className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
750
- children: src ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
928
+ children: src ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
751
929
  "img",
752
930
  {
753
931
  src,
@@ -755,7 +933,7 @@ function FrameScreenshot({ frame, label }) {
755
933
  loading: "lazy",
756
934
  className: "h-24 w-full bg-muted object-cover object-top"
757
935
  }
758
- ) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
936
+ ) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex h-24 w-full items-center justify-center border-b border-dashed border-border bg-muted px-2 text-center text-[0.6875rem] text-muted-foreground", children: "Screenshot unavailable" })
759
937
  }
760
938
  );
761
939
  }
@@ -774,12 +952,12 @@ function prettyJson(value) {
774
952
  function StateCardView({ card }) {
775
953
  const json = prettyJson(card.value);
776
954
  const firstCapture = card.changes === void 0;
777
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
778
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
779
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "inline-flex rounded-full border border-border px-1.5 font-mono text-[0.625rem] font-medium text-muted-foreground", children: card.label ?? "State" }),
780
- firstCapture ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
955
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
956
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex rounded-full border border-border px-1.5 font-mono text-[0.625rem] font-medium text-muted-foreground", children: card.label ?? "State" }),
958
+ firstCapture ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
781
959
  ] }),
782
- !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
960
+ !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
783
961
  "li",
784
962
  {
785
963
  className: cn(
@@ -789,15 +967,15 @@ function StateCardView({ card }) {
789
967
  children: summarizeStateChanges([change])[0]
790
968
  },
791
969
  i
792
- )) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
793
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
970
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
971
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
794
972
  "details",
795
973
  {
796
974
  open: firstCapture && json.length <= 240,
797
975
  className: cn(firstCapture ? "mt-1.5" : "mt-1"),
798
976
  children: [
799
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
800
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { tabIndex: 0, className: "mt-1 max-h-48 overflow-auto rounded-sm bg-muted p-1.5 font-mono text-[0.625rem] leading-snug", children: json })
977
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
978
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("pre", { tabIndex: 0, className: "mt-1 max-h-48 overflow-auto rounded-sm bg-muted p-1.5 font-mono text-[0.625rem] leading-snug", children: json })
801
979
  ]
802
980
  }
803
981
  )
@@ -805,17 +983,17 @@ function StateCardView({ card }) {
805
983
  }
806
984
 
807
985
  // src/components/ReportAttachments.tsx
808
- var import_jsx_runtime20 = require("react/jsx-runtime");
986
+ var import_jsx_runtime21 = require("react/jsx-runtime");
809
987
  function ReportAttachments({ attachments }) {
810
988
  if (attachments.length === 0) return null;
811
989
  return (
812
990
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
813
991
  // blocks don't create duplicate "Attachments" region landmarks.
814
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
992
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
815
993
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
816
994
  const isImage = att.mediaType.startsWith("image/");
817
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
818
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
995
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
996
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
819
997
  "img",
820
998
  {
821
999
  src: dataUri,
@@ -824,8 +1002,8 @@ function ReportAttachments({ attachments }) {
824
1002
  className: "max-w-full rounded-md border border-border"
825
1003
  }
826
1004
  ),
827
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
828
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1005
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
1006
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
829
1007
  "a",
830
1008
  {
831
1009
  href: dataUri,
@@ -834,7 +1012,7 @@ function ReportAttachments({ attachments }) {
834
1012
  children: [
835
1013
  att.name,
836
1014
  " ",
837
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-muted-foreground", children: [
1015
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-muted-foreground", children: [
838
1016
  "(",
839
1017
  att.mediaType,
840
1018
  ")"
@@ -922,7 +1100,7 @@ function traceWindow(rows) {
922
1100
  }
923
1101
 
924
1102
  // src/components/ReportTrace.tsx
925
- var import_jsx_runtime21 = require("react/jsx-runtime");
1103
+ var import_jsx_runtime22 = require("react/jsx-runtime");
926
1104
  var BAR_COLOR = {
927
1105
  ok: "bg-pass",
928
1106
  error: "bg-fail",
@@ -933,8 +1111,8 @@ function ReportTrace({ spans }) {
933
1111
  const rows = toTraceRows(spans);
934
1112
  if (rows.length === 0) return null;
935
1113
  const { minStartMs, totalMs } = traceWindow(rows);
936
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
937
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
1114
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
938
1116
  "Trace (",
939
1117
  rows.length,
940
1118
  " span",
@@ -943,11 +1121,11 @@ function ReportTrace({ spans }) {
943
1121
  formatDuration(totalMs),
944
1122
  ")"
945
1123
  ] }),
946
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
1124
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
947
1125
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
948
1126
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
949
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-center gap-2", children: [
950
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1127
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("li", { className: "flex items-center gap-2", children: [
1128
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
951
1129
  "span",
952
1130
  {
953
1131
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -956,14 +1134,14 @@ function ReportTrace({ spans }) {
956
1134
  children: row.name
957
1135
  }
958
1136
  ),
959
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1137
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
960
1138
  "span",
961
1139
  {
962
1140
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
963
1141
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
964
1142
  }
965
1143
  ) }),
966
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
1144
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
967
1145
  ] }, `${row.spanId}-${i}`);
968
1146
  }) })
969
1147
  ] });
@@ -1012,11 +1190,11 @@ function flakinessOf(entries) {
1012
1190
  if (transitionScore > 0.2 || failureRate > 0.3) return "unstable";
1013
1191
  return "stable";
1014
1192
  }
1015
- function diffSinceLastRun(history2, scenarios) {
1193
+ function diffSinceLastRun(history, scenarios) {
1016
1194
  let hasPreviousRun = false;
1017
1195
  const delta = { newlyFailing: [], newlyPassing: [], added: [] };
1018
1196
  for (const scenario of scenarios) {
1019
- const entries = history2[scenario.id];
1197
+ const entries = history[scenario.id];
1020
1198
  if (!entries || entries.length === 0) continue;
1021
1199
  const last = entries[entries.length - 1];
1022
1200
  const prev = entries[entries.length - 2];
@@ -1032,7 +1210,7 @@ function diffSinceLastRun(history2, scenarios) {
1032
1210
  }
1033
1211
 
1034
1212
  // src/components/ScenarioRunHistory.tsx
1035
- var import_jsx_runtime22 = require("react/jsx-runtime");
1213
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1036
1214
  var MAX_DOTS = 10;
1037
1215
  var DOT_COLOR = {
1038
1216
  passed: "bg-pass",
@@ -1046,22 +1224,22 @@ function dotTitle(e) {
1046
1224
  return parts.join(" \xB7 ");
1047
1225
  }
1048
1226
  function ScenarioRunHistory({ scenarioId }) {
1049
- const history2 = useScenarioHistory();
1050
- const entries = history2?.[scenarioId];
1227
+ const history = useScenarioHistory();
1228
+ const entries = history?.[scenarioId];
1051
1229
  if (!entries || entries.length < 2) return null;
1052
1230
  const recent = entries.slice(-MAX_DOTS);
1053
1231
  const flaky = flakinessOf(recent) === "flaky";
1054
1232
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
1055
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1056
- flaky ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1057
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1233
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
1234
+ flaky ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1235
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1058
1236
  "span",
1059
1237
  {
1060
1238
  role: "img",
1061
1239
  "aria-label": `Run history: ${summary}`,
1062
1240
  title: summary,
1063
1241
  className: "flex shrink-0 items-center gap-[3px]",
1064
- children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1242
+ children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1065
1243
  "span",
1066
1244
  {
1067
1245
  "aria-hidden": true,
@@ -1290,9 +1468,9 @@ function scenarioPermalink(scenario) {
1290
1468
 
1291
1469
  // src/components/ui/card.tsx
1292
1470
  var React2 = require("react");
1293
- var import_jsx_runtime23 = require("react/jsx-runtime");
1471
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1294
1472
  function Card({ className, ...props }) {
1295
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1473
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1296
1474
  "div",
1297
1475
  {
1298
1476
  "data-slot": "card",
@@ -1305,7 +1483,7 @@ function Card({ className, ...props }) {
1305
1483
  );
1306
1484
  }
1307
1485
  function CardHeader({ className, ...props }) {
1308
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1486
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1309
1487
  "div",
1310
1488
  {
1311
1489
  "data-slot": "card-header",
@@ -1318,7 +1496,7 @@ function CardHeader({ className, ...props }) {
1318
1496
  );
1319
1497
  }
1320
1498
  function CardContent({ className, ...props }) {
1321
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1499
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1322
1500
  "div",
1323
1501
  {
1324
1502
  "data-slot": "card-content",
@@ -1332,13 +1510,13 @@ function CardContent({ className, ...props }) {
1332
1510
  var React3 = require("react");
1333
1511
  var import_menu = require("@base-ui/react/menu");
1334
1512
  var import_lucide_react = require("lucide-react");
1335
- var import_jsx_runtime24 = require("react/jsx-runtime");
1513
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1336
1514
  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";
1337
1515
  function DropdownMenu(props) {
1338
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1516
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1339
1517
  }
1340
1518
  function DropdownMenuTrigger(props) {
1341
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1519
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1342
1520
  }
1343
1521
  function DropdownMenuContent({
1344
1522
  className,
@@ -1346,14 +1524,14 @@ function DropdownMenuContent({
1346
1524
  sideOffset = 4,
1347
1525
  ...props
1348
1526
  }) {
1349
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1527
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1350
1528
  import_menu.Menu.Positioner,
1351
1529
  {
1352
1530
  "data-slot": "dropdown-menu-positioner",
1353
1531
  className: "z-50",
1354
1532
  align,
1355
1533
  sideOffset,
1356
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1534
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1357
1535
  import_menu.Menu.Popup,
1358
1536
  {
1359
1537
  "data-slot": "dropdown-menu-content",
@@ -1371,7 +1549,7 @@ function DropdownMenuItem({
1371
1549
  className,
1372
1550
  ...props
1373
1551
  }) {
1374
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1552
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1375
1553
  import_menu.Menu.Item,
1376
1554
  {
1377
1555
  "data-slot": "dropdown-menu-item",
@@ -1383,7 +1561,7 @@ function DropdownMenuItem({
1383
1561
 
1384
1562
  // src/components/ReportScenario.tsx
1385
1563
  var import_lucide_react2 = require("lucide-react");
1386
- var import_jsx_runtime25 = require("react/jsx-runtime");
1564
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1387
1565
  var STATUS_META = {
1388
1566
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1389
1567
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1399,7 +1577,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1399
1577
  const collapsible = collapse !== null && !hideTitle;
1400
1578
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1401
1579
  const actions = useScenarioActions();
1402
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1580
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1403
1581
  Card,
1404
1582
  {
1405
1583
  id: scenario.id,
@@ -1407,10 +1585,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1407
1585
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1408
1586
  className: "gap-0 py-0",
1409
1587
  children: [
1410
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1411
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1412
- hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1413
- collapsible ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1588
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1590
+ hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1591
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1414
1592
  "button",
1415
1593
  {
1416
1594
  type: "button",
@@ -1419,49 +1597,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1419
1597
  "aria-controls": bodyId,
1420
1598
  "aria-label": `Toggle ${scenario.title}`,
1421
1599
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1422
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1600
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1423
1601
  }
1424
1602
  ) : null,
1425
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1426
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: scenario.title })
1603
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1604
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: scenario.title })
1427
1605
  ] }),
1428
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1429
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1430
- scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1431
- meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1432
- actions ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenu, { children: [
1433
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1606
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1607
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1608
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1609
+ meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
1610
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenu, { children: [
1611
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1434
1612
  DropdownMenuTrigger,
1435
1613
  {
1436
1614
  "aria-label": `Actions for ${scenario.title}`,
1437
1615
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1438
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1616
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1439
1617
  }
1440
1618
  ),
1441
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuContent, { align: "end", children: [
1442
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1443
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.LinkIcon, {}),
1619
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuContent, { align: "end", children: [
1620
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1621
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.LinkIcon, {}),
1444
1622
  " Copy link"
1445
1623
  ] }),
1446
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1447
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.FileTextIcon, {}),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1625
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.FileTextIcon, {}),
1448
1626
  " Copy as Markdown"
1449
1627
  ] }),
1450
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1451
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.SparklesIcon, {}),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.SparklesIcon, {}),
1452
1630
  " Explain with AI"
1453
1631
  ] })
1454
1632
  ] })
1455
1633
  ] }) : null
1456
1634
  ] })
1457
1635
  ] }),
1458
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1459
- scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1460
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1636
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1637
+ scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1638
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1461
1639
  ] }) : null
1462
1640
  ] }),
1463
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1464
- scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1641
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1642
+ scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1465
1643
  "pre",
1466
1644
  {
1467
1645
  role: "alert",
@@ -1469,11 +1647,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1469
1647
  children: scenario.errorMessage
1470
1648
  }
1471
1649
  ) : null,
1472
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportStoryboard, { scenario }),
1473
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportSteps, { scenario }),
1474
- scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1475
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1476
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ReportTrace, { spans: scenario.otelSpans })
1650
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportStoryboard, { scenario }),
1651
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportSteps, { scenario }),
1652
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1653
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1654
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportTrace, { spans: scenario.otelSpans })
1477
1655
  ] })
1478
1656
  ]
1479
1657
  }
@@ -1481,13 +1659,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1481
1659
  }
1482
1660
 
1483
1661
  // src/components/ReportScenarioList.tsx
1484
- var import_jsx_runtime26 = require("react/jsx-runtime");
1662
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1485
1663
  function ReportScenarioList({ feature }) {
1486
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportScenario, { scenario }, scenario.id)) });
1664
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportScenario, { scenario }, scenario.id)) });
1487
1665
  }
1488
1666
 
1489
1667
  // src/components/ReportFeature.tsx
1490
- var import_jsx_runtime27 = require("react/jsx-runtime");
1668
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1491
1669
  function ReportFeature({ feature }) {
1492
1670
  const titleId = `${feature.id}-title`;
1493
1671
  const bodyId = `${feature.id}-body`;
@@ -1498,7 +1676,7 @@ function ReportFeature({ feature }) {
1498
1676
  return (
1499
1677
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1500
1678
  // layer of nesting so the scenario cards below read as the primary unit.
1501
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1679
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1502
1680
  "section",
1503
1681
  {
1504
1682
  id: feature.id,
@@ -1506,9 +1684,9 @@ function ReportFeature({ feature }) {
1506
1684
  "aria-labelledby": titleId,
1507
1685
  className: "flex flex-col gap-3",
1508
1686
  children: [
1509
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1510
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "min-w-0", children: [
1511
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1687
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1688
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "min-w-0", children: [
1689
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1512
1690
  "button",
1513
1691
  {
1514
1692
  type: "button",
@@ -1517,31 +1695,31 @@ function ReportFeature({ feature }) {
1517
1695
  "aria-controls": bodyId,
1518
1696
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1519
1697
  children: [
1520
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1521
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "truncate", children: feature.title })
1698
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1699
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate", children: feature.title })
1522
1700
  ]
1523
1701
  }
1524
1702
  ) : feature.title }),
1525
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1703
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1526
1704
  ] }),
1527
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1705
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1528
1706
  "p",
1529
1707
  {
1530
1708
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1531
1709
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1532
1710
  children: [
1533
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
1711
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1534
1712
  s.passed,
1535
1713
  " passed"
1536
1714
  ] }),
1537
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1538
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1715
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1716
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1539
1717
  s.failed,
1540
1718
  " failed"
1541
1719
  ] }),
1542
- skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
1543
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1544
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
1720
+ skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1721
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1722
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1545
1723
  skipped,
1546
1724
  " skipped"
1547
1725
  ] })
@@ -1550,7 +1728,7 @@ function ReportFeature({ feature }) {
1550
1728
  }
1551
1729
  )
1552
1730
  ] }),
1553
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportScenarioList, { feature }) })
1731
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportScenarioList, { feature }) })
1554
1732
  ]
1555
1733
  }
1556
1734
  )
@@ -1558,17 +1736,17 @@ function ReportFeature({ feature }) {
1558
1736
  }
1559
1737
 
1560
1738
  // src/components/ReportFeatureList.tsx
1561
- var import_jsx_runtime28 = require("react/jsx-runtime");
1739
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1562
1740
  function ReportFeatureList() {
1563
1741
  const report = useReport();
1564
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportFeature, { feature }, feature.id)) });
1742
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ReportFeature, { feature }, feature.id)) });
1565
1743
  }
1566
1744
 
1567
1745
  // src/components/ui/empty.tsx
1568
1746
  var import_class_variance_authority2 = require("class-variance-authority");
1569
- var import_jsx_runtime29 = require("react/jsx-runtime");
1747
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1570
1748
  function Empty({ className, ...props }) {
1571
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1749
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1572
1750
  "div",
1573
1751
  {
1574
1752
  "data-slot": "empty",
@@ -1581,7 +1759,7 @@ function Empty({ className, ...props }) {
1581
1759
  );
1582
1760
  }
1583
1761
  function EmptyHeader({ className, ...props }) {
1584
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1762
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1585
1763
  "div",
1586
1764
  {
1587
1765
  "data-slot": "empty-header",
@@ -1608,7 +1786,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1608
1786
  }
1609
1787
  );
1610
1788
  function EmptyTitle({ className, ...props }) {
1611
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1789
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1612
1790
  "div",
1613
1791
  {
1614
1792
  "data-slot": "empty-title",
@@ -1619,16 +1797,16 @@ function EmptyTitle({ className, ...props }) {
1619
1797
  }
1620
1798
 
1621
1799
  // src/components/ReportEmpty.tsx
1622
- var import_jsx_runtime30 = require("react/jsx-runtime");
1800
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1623
1801
  function ReportEmpty({ message }) {
1624
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1802
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1625
1803
  }
1626
1804
 
1627
1805
  // src/components/ReportSummary.tsx
1628
- var import_jsx_runtime31 = require("react/jsx-runtime");
1806
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1629
1807
  function ReportSummary({ className }) {
1630
1808
  const report = useReport();
1631
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1809
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1632
1810
  ReportSummaryView,
1633
1811
  {
1634
1812
  summary: report.summary,
@@ -1654,7 +1832,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1654
1832
  if (summary.pending > 0) {
1655
1833
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1656
1834
  }
1657
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1835
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1658
1836
  "dl",
1659
1837
  {
1660
1838
  className: cn(
@@ -1667,17 +1845,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1667
1845
  "aria-label": ariaLabel,
1668
1846
  children: cards.map((c) => {
1669
1847
  const tone = TONE[c.status];
1670
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1848
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1671
1849
  "div",
1672
1850
  {
1673
1851
  "data-status": c.status,
1674
1852
  className: "rounded-lg border bg-card px-4 py-3",
1675
1853
  children: [
1676
- /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1677
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1854
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1855
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1678
1856
  c.label
1679
1857
  ] }),
1680
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1858
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1681
1859
  ]
1682
1860
  },
1683
1861
  c.status
@@ -1737,7 +1915,7 @@ function prUrl(ci) {
1737
1915
  }
1738
1916
 
1739
1917
  // src/components/ReportMeta.tsx
1740
- var import_jsx_runtime32 = require("react/jsx-runtime");
1918
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1741
1919
  function ReportMeta() {
1742
1920
  const report = useReport();
1743
1921
  const items = [];
@@ -1760,13 +1938,13 @@ function ReportMeta() {
1760
1938
  const short = sha.slice(0, 8);
1761
1939
  items.push({
1762
1940
  label: "Commit",
1763
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1941
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1764
1942
  });
1765
1943
  }
1766
1944
  if (report.ci) {
1767
1945
  items.push({
1768
1946
  label: "CI",
1769
- value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1947
+ value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1770
1948
  ciDisplayName(report.ci.name),
1771
1949
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1772
1950
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1777,7 +1955,7 @@ function ReportMeta() {
1777
1955
  const label = `#${report.ci.prNumber}`;
1778
1956
  items.push({
1779
1957
  label: "PR",
1780
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1958
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1781
1959
  });
1782
1960
  }
1783
1961
  if (items.length === 0) return null;
@@ -1785,50 +1963,50 @@ function ReportMeta() {
1785
1963
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1786
1964
  // collapsed disclosure so it doesn't push the result down. Native <details>
1787
1965
  // so it works with no JS (the SSR/no-hydration report still toggles).
1788
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1789
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
1790
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1966
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1967
+ /* @__PURE__ */ (0, import_jsx_runtime33.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: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1791
1969
  "Run details"
1792
1970
  ] }),
1793
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1794
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dt", { className: "font-medium", children: item.label }),
1795
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1971
+ /* @__PURE__ */ (0, import_jsx_runtime33.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_runtime33.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
1972
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dt", { className: "font-medium", children: item.label }),
1973
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1796
1974
  ] }, item.label)) })
1797
1975
  ] })
1798
1976
  );
1799
1977
  }
1800
1978
 
1801
1979
  // src/components/ReportSchemaError.tsx
1802
- var import_jsx_runtime33 = require("react/jsx-runtime");
1980
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1803
1981
  function ReportSchemaError({ error }) {
1804
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1805
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("strong", { children: "Report could not be displayed." }) }),
1806
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: error.message }),
1807
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { children: [
1982
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1983
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("strong", { children: "Report could not be displayed." }) }),
1984
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: error.message }),
1985
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { children: [
1808
1986
  "The report bundle is newer than this version of ",
1809
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "executable-stories-react" }),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("code", { children: "executable-stories-react" }),
1810
1988
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1811
1989
  ] }) : null,
1812
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { children: [
1813
- /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("summary", { children: [
1990
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { children: [
1991
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { children: [
1814
1992
  error.issues.length,
1815
1993
  " validation issue",
1816
1994
  error.issues.length === 1 ? "" : "s"
1817
1995
  ] }),
1818
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1996
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1819
1997
  ] }) : null
1820
1998
  ] });
1821
1999
  }
1822
2000
 
1823
2001
  // src/components/ReportShell.tsx
1824
- var import_jsx_runtime34 = require("react/jsx-runtime");
2002
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1825
2003
  function ReportErrorShell({
1826
2004
  error,
1827
2005
  className,
1828
2006
  title,
1829
2007
  dataTheme
1830
2008
  }) {
1831
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReportSchemaError, { error }) });
2009
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportSchemaError, { error }) });
1832
2010
  }
1833
2011
 
1834
2012
  // src/interactive/ReportInteractive.tsx
@@ -1837,12 +2015,12 @@ var import_lucide_react5 = require("lucide-react");
1837
2015
  // src/components/ui/switch.tsx
1838
2016
  var React4 = require("react");
1839
2017
  var import_switch = require("@base-ui/react/switch");
1840
- var import_jsx_runtime35 = require("react/jsx-runtime");
2018
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1841
2019
  function Switch({
1842
2020
  className,
1843
2021
  ...props
1844
2022
  }) {
1845
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2023
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1846
2024
  import_switch.Switch.Root,
1847
2025
  {
1848
2026
  "data-slot": "switch",
@@ -1851,7 +2029,7 @@ function Switch({
1851
2029
  className
1852
2030
  ),
1853
2031
  ...props,
1854
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2032
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1855
2033
  import_switch.Switch.Thumb,
1856
2034
  {
1857
2035
  "data-slot": "switch-thumb",
@@ -1867,7 +2045,7 @@ var React5 = __toESM(require("react"), 1);
1867
2045
  var import_toggle_group = require("@base-ui/react/toggle-group");
1868
2046
  var import_toggle = require("@base-ui/react/toggle");
1869
2047
  var import_class_variance_authority3 = require("class-variance-authority");
1870
- var import_jsx_runtime36 = require("react/jsx-runtime");
2048
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1871
2049
  var toggleItemVariants = (0, import_class_variance_authority3.cva)(
1872
2050
  "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",
1873
2051
  {
@@ -1889,13 +2067,13 @@ function ToggleGroup({
1889
2067
  children,
1890
2068
  ...props
1891
2069
  }) {
1892
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2070
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1893
2071
  import_toggle_group.ToggleGroup,
1894
2072
  {
1895
2073
  "data-slot": "toggle-group",
1896
2074
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1897
2075
  ...props,
1898
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
2076
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1899
2077
  }
1900
2078
  );
1901
2079
  }
@@ -1906,7 +2084,7 @@ function ToggleGroupItem({
1906
2084
  ...props
1907
2085
  }) {
1908
2086
  const context = React5.useContext(ToggleGroupContext);
1909
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2087
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1910
2088
  import_toggle.Toggle,
1911
2089
  {
1912
2090
  "data-slot": "toggle-group-item",
@@ -1923,9 +2101,9 @@ var import_lucide_react3 = require("lucide-react");
1923
2101
 
1924
2102
  // src/components/ui/input.tsx
1925
2103
  var React6 = require("react");
1926
- var import_jsx_runtime37 = require("react/jsx-runtime");
2104
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1927
2105
  function Input({ className, type, ...props }) {
1928
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2106
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1929
2107
  "input",
1930
2108
  {
1931
2109
  type,
@@ -1942,7 +2120,7 @@ function Input({ className, type, ...props }) {
1942
2120
  }
1943
2121
 
1944
2122
  // src/interactive/ReportSearch.tsx
1945
- var import_jsx_runtime38 = require("react/jsx-runtime");
2123
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1946
2124
  var ReportSearch = (0, import_react8.forwardRef)(
1947
2125
  function ReportSearch2(props, ref) {
1948
2126
  const {
@@ -1964,17 +2142,17 @@ var ReportSearch = (0, import_react8.forwardRef)(
1964
2142
  e.preventDefault();
1965
2143
  }
1966
2144
  }
1967
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1968
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1969
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "relative w-full sm:w-72", children: [
1970
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2145
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
2146
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
2147
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "relative w-full sm:w-72", children: [
2148
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1971
2149
  import_lucide_react3.Search,
1972
2150
  {
1973
2151
  "aria-hidden": true,
1974
2152
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
1975
2153
  }
1976
2154
  ),
1977
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2155
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1978
2156
  Input,
1979
2157
  {
1980
2158
  ref,
@@ -1991,7 +2169,7 @@ var ReportSearch = (0, import_react8.forwardRef)(
1991
2169
  }
1992
2170
  )
1993
2171
  ] }),
1994
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2172
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
1995
2173
  "span",
1996
2174
  {
1997
2175
  className: "text-xs tabular-nums text-muted-foreground",
@@ -2011,6 +2189,54 @@ var ReportSearch = (0, import_react8.forwardRef)(
2011
2189
  // src/interactive/ReportFailureBanner.tsx
2012
2190
  var import_react9 = require("react");
2013
2191
 
2192
+ // src/lib/hash-state.ts
2193
+ var DEFAULT_URL_STATE = {
2194
+ query: "",
2195
+ status: "all",
2196
+ tags: [],
2197
+ detail: "full"
2198
+ };
2199
+ var STATUSES = ["all", "passed", "failed", "skipped", "pending"];
2200
+ function splitHash(hash) {
2201
+ const raw = hash.replace(/^#/, "");
2202
+ const split = raw.indexOf("?");
2203
+ if (split === -1) return { anchor: raw, params: new URLSearchParams() };
2204
+ return { anchor: raw.slice(0, split), params: new URLSearchParams(raw.slice(split + 1)) };
2205
+ }
2206
+ function decodeUrlState(params) {
2207
+ const status = params.get("status");
2208
+ const tags = params.get("tags");
2209
+ return {
2210
+ query: params.get("q") ?? DEFAULT_URL_STATE.query,
2211
+ status: STATUSES.includes(status) ? status : DEFAULT_URL_STATE.status,
2212
+ tags: tags ? tags.split(",").filter(Boolean) : [],
2213
+ detail: params.get("docs") === "0" ? "minimal" : "full"
2214
+ };
2215
+ }
2216
+ function encodeUrlState(state) {
2217
+ const params = new URLSearchParams();
2218
+ if (state.query) params.set("q", state.query);
2219
+ if (state.status !== "all") params.set("status", state.status);
2220
+ if (state.tags.length > 0) params.set("tags", state.tags.join(","));
2221
+ if (state.detail === "minimal") params.set("docs", "0");
2222
+ return params.toString();
2223
+ }
2224
+ function formatHash(anchor, params) {
2225
+ if (params) return `#${anchor}?${params}`;
2226
+ return anchor ? `#${anchor}` : "";
2227
+ }
2228
+ function writeHash(anchor, params) {
2229
+ if (typeof window === "undefined") return;
2230
+ const next = formatHash(anchor, params);
2231
+ if (next === window.location.hash) return;
2232
+ const url = next || window.location.href.split("#")[0];
2233
+ try {
2234
+ window.history.replaceState(window.history.state, "", url);
2235
+ } catch {
2236
+ window.location.hash = next.slice(1);
2237
+ }
2238
+ }
2239
+
2014
2240
  // src/lib/scroll.ts
2015
2241
  function prefersReducedMotion() {
2016
2242
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
@@ -2026,13 +2252,13 @@ function scrollToScenarioId(scenarioId, options = {}) {
2026
2252
  behavior: prefersReducedMotion() ? "auto" : "smooth",
2027
2253
  block: "start"
2028
2254
  });
2029
- if (options.updateHash !== false && typeof history !== "undefined") {
2030
- history.replaceState(null, "", `#${scenarioId}`);
2255
+ if (options.updateHash !== false) {
2256
+ writeHash(scenarioId, splitHash(window.location.hash).params.toString());
2031
2257
  }
2032
2258
  }
2033
2259
 
2034
2260
  // src/interactive/ReportFailureBanner.tsx
2035
- var import_jsx_runtime39 = require("react/jsx-runtime");
2261
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2036
2262
  function ReportFailureBanner({ failures }) {
2037
2263
  const first = failures[0];
2038
2264
  const jumpToFirst = (0, import_react9.useCallback)(() => {
@@ -2040,7 +2266,7 @@ function ReportFailureBanner({ failures }) {
2040
2266
  scrollToScenarioId(first.scenarioId);
2041
2267
  }, [first]);
2042
2268
  if (failures.length === 0) return null;
2043
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2269
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2044
2270
  "div",
2045
2271
  {
2046
2272
  className: "es-failure-banner",
@@ -2048,13 +2274,13 @@ function ReportFailureBanner({ failures }) {
2048
2274
  "aria-live": "polite",
2049
2275
  "aria-label": "Failure summary",
2050
2276
  children: [
2051
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "es-failure-banner-text", children: [
2052
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("strong", { children: failures.length }),
2277
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "es-failure-banner-text", children: [
2278
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: failures.length }),
2053
2279
  " ",
2054
2280
  "failure",
2055
2281
  failures.length === 1 ? "" : "s"
2056
2282
  ] }),
2057
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2283
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2058
2284
  "button",
2059
2285
  {
2060
2286
  type: "button",
@@ -2071,7 +2297,7 @@ function ReportFailureBanner({ failures }) {
2071
2297
 
2072
2298
  // src/interactive/ReportFreshness.tsx
2073
2299
  var import_react10 = require("react");
2074
- var import_jsx_runtime40 = require("react/jsx-runtime");
2300
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2075
2301
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2076
2302
  const [now, setNow] = (0, import_react10.useState)(nowMs);
2077
2303
  (0, import_react10.useEffect)(() => {
@@ -2079,7 +2305,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2079
2305
  }, [nowMs]);
2080
2306
  if (lastRunMs === void 0 || now === void 0) return null;
2081
2307
  const age = formatRelativeAge(lastRunMs, now);
2082
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2308
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2083
2309
  "a",
2084
2310
  {
2085
2311
  href: ciUrl,
@@ -2090,9 +2316,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2090
2316
  }
2091
2317
  ) : null;
2092
2318
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
2093
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2094
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
2095
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("strong", { children: [
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2320
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2321
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("strong", { children: [
2096
2322
  "Last verified ",
2097
2323
  age,
2098
2324
  "."
@@ -2102,10 +2328,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2102
2328
  runLink
2103
2329
  ] });
2104
2330
  }
2105
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2331
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2106
2332
  "Verified ",
2107
2333
  age,
2108
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2334
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2109
2335
  " \xB7 ",
2110
2336
  runLink
2111
2337
  ] }) : null
@@ -2113,15 +2339,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2113
2339
  }
2114
2340
 
2115
2341
  // src/interactive/ReportLastRunDelta.tsx
2116
- var import_jsx_runtime41 = require("react/jsx-runtime");
2342
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2117
2343
  var MAX_LINKS = 5;
2118
2344
  function ScenarioLinks({ scenarios, tone }) {
2119
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2120
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2345
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2346
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2121
2347
  i > 0 ? ", " : "",
2122
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2348
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2123
2349
  ] }, s.id)),
2124
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2350
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2125
2351
  " and ",
2126
2352
  scenarios.length - MAX_LINKS,
2127
2353
  " more"
@@ -2129,32 +2355,32 @@ function ScenarioLinks({ scenarios, tone }) {
2129
2355
  ] });
2130
2356
  }
2131
2357
  function ReportLastRunDelta({
2132
- history: history2,
2358
+ history,
2133
2359
  report
2134
2360
  }) {
2135
- if (!history2) return null;
2361
+ if (!history) return null;
2136
2362
  const scenarios = report.features.flatMap((f) => f.scenarios.map((s) => ({ id: s.id, title: s.title })));
2137
- const delta = diffSinceLastRun(history2, scenarios);
2363
+ const delta = diffSinceLastRun(history, scenarios);
2138
2364
  if (!delta) return null;
2139
2365
  const { newlyFailing, newlyPassing, added } = delta;
2140
2366
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
2141
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2367
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2142
2368
  }
2143
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2144
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2145
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-fail", children: [
2369
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2370
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2371
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2 text-fail", children: [
2146
2372
  newlyFailing.length,
2147
2373
  " newly failing (",
2148
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2374
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2149
2375
  ")"
2150
2376
  ] }) : null,
2151
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-pass", children: [
2377
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2 text-pass", children: [
2152
2378
  newlyPassing.length,
2153
2379
  " fixed (",
2154
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2380
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2155
2381
  ")"
2156
2382
  ] }) : null,
2157
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2", children: [
2383
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2", children: [
2158
2384
  added.length,
2159
2385
  " new"
2160
2386
  ] }) : null
@@ -2218,7 +2444,7 @@ function Button({
2218
2444
  }
2219
2445
 
2220
2446
  // src/interactive/ReportShortcutsHelp.tsx
2221
- var import_jsx_runtime42 = require("react/jsx-runtime");
2447
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2222
2448
  var SHORTCUTS = [
2223
2449
  { keys: "/", description: "Focus search" },
2224
2450
  { keys: "f", description: "Jump to next failure" },
@@ -2230,9 +2456,9 @@ var SHORTCUTS = [
2230
2456
  ];
2231
2457
  function Keys({ keys }) {
2232
2458
  const parts = keys.split(" ");
2233
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2234
- i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2235
- /* @__PURE__ */ (0, import_jsx_runtime42.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 })
2459
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
2460
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2461
+ /* @__PURE__ */ (0, import_jsx_runtime43.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 })
2236
2462
  ] }, k)) });
2237
2463
  }
2238
2464
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2243,7 +2469,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2243
2469
  if (open && !dialog.open) dialog.showModal();
2244
2470
  else if (!open && dialog.open) dialog.close();
2245
2471
  }, [open]);
2246
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2472
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2247
2473
  "dialog",
2248
2474
  {
2249
2475
  ref: dialogRef,
@@ -2251,12 +2477,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2251
2477
  "aria-label": "Keyboard shortcuts",
2252
2478
  onClose,
2253
2479
  children: [
2254
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2255
- /* @__PURE__ */ (0, import_jsx_runtime42.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_runtime42.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2256
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Keys, { keys: s.keys }) }),
2257
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2480
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2481
+ /* @__PURE__ */ (0, import_jsx_runtime43.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_runtime43.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2482
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Keys, { keys: s.keys }) }),
2483
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2258
2484
  ] }, s.keys)) }),
2259
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2485
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2260
2486
  ]
2261
2487
  }
2262
2488
  );
@@ -2320,12 +2546,14 @@ function useKeyboardShortcuts(handlers) {
2320
2546
  // src/interactive/use-deep-link-scroll.ts
2321
2547
  var import_react13 = require("react");
2322
2548
  function useDeepLinkScroll() {
2549
+ const lastAnchor = (0, import_react13.useRef)(null);
2323
2550
  (0, import_react13.useEffect)(() => {
2324
2551
  function scrollToHash() {
2325
- const hash = window.location.hash.replace(/^#/, "");
2326
- if (!hash) return;
2552
+ const { anchor } = splitHash(window.location.hash);
2553
+ if (!anchor || anchor === lastAnchor.current) return;
2554
+ lastAnchor.current = anchor;
2327
2555
  requestAnimationFrame(() => {
2328
- scrollToScenarioId(hash, { updateHash: false });
2556
+ scrollToScenarioId(anchor, { updateHash: false });
2329
2557
  });
2330
2558
  }
2331
2559
  scrollToHash();
@@ -2457,7 +2685,7 @@ function failuresFirst(report) {
2457
2685
 
2458
2686
  // src/interactive/ReportFilters.tsx
2459
2687
  var import_react14 = require("react");
2460
- var import_jsx_runtime43 = require("react/jsx-runtime");
2688
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2461
2689
  var TAG_LIMIT = 12;
2462
2690
  function ReportFilters({
2463
2691
  statuses,
@@ -2471,10 +2699,10 @@ function ReportFilters({
2471
2699
  if (statuses.length === 0 && tags.length === 0) return null;
2472
2700
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2473
2701
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2474
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col gap-2", children: [
2475
- statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2702
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-col gap-2", children: [
2703
+ statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2476
2704
  const active = status === opt.key;
2477
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2705
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2478
2706
  "button",
2479
2707
  {
2480
2708
  type: "button",
@@ -2486,23 +2714,23 @@ function ReportFilters({
2486
2714
  ),
2487
2715
  children: [
2488
2716
  opt.label,
2489
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2717
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2490
2718
  ]
2491
2719
  },
2492
2720
  opt.key
2493
2721
  );
2494
2722
  }) }) : null,
2495
- tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2723
+ tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2496
2724
  visibleTags.map((tag) => {
2497
2725
  const active = activeTags.includes(tag);
2498
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2726
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2499
2727
  "button",
2500
2728
  {
2501
2729
  type: "button",
2502
2730
  "aria-pressed": active,
2503
2731
  onClick: () => onToggleTag(tag),
2504
2732
  className: "cursor-pointer",
2505
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2733
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2506
2734
  Badge,
2507
2735
  {
2508
2736
  variant: "tag",
@@ -2515,7 +2743,7 @@ function ReportFilters({
2515
2743
  }
2516
2744
  ) }, tag);
2517
2745
  }),
2518
- tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2746
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2519
2747
  "button",
2520
2748
  {
2521
2749
  type: "button",
@@ -2531,7 +2759,7 @@ function ReportFilters({
2531
2759
 
2532
2760
  // src/interactive/ReportToc.tsx
2533
2761
  var import_react15 = require("react");
2534
- var import_jsx_runtime44 = require("react/jsx-runtime");
2762
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2535
2763
  var DOT = {
2536
2764
  passed: "bg-pass",
2537
2765
  failed: "bg-fail",
@@ -2560,11 +2788,11 @@ function TocContent({ onNavigate }) {
2560
2788
  return () => observer.disconnect();
2561
2789
  }, [scenarioIds]);
2562
2790
  if (report.features.length === 0) return null;
2563
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { children: [
2564
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2565
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2791
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("li", { children: [
2792
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2793
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2566
2794
  const active = activeId === scenario.id;
2567
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2795
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2568
2796
  "a",
2569
2797
  {
2570
2798
  href: `#${scenario.id}`,
@@ -2579,14 +2807,14 @@ function TocContent({ onNavigate }) {
2579
2807
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2580
2808
  ),
2581
2809
  children: [
2582
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2810
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2583
2811
  "span",
2584
2812
  {
2585
2813
  "aria-hidden": true,
2586
2814
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2587
2815
  }
2588
2816
  ),
2589
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "break-words", children: scenario.title })
2817
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "break-words", children: scenario.title })
2590
2818
  ]
2591
2819
  }
2592
2820
  ) }, scenario.id);
@@ -2596,14 +2824,14 @@ function TocContent({ onNavigate }) {
2596
2824
  function ReportToc() {
2597
2825
  const report = useReport();
2598
2826
  if (report.features.length === 0) return null;
2599
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2827
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2600
2828
  "nav",
2601
2829
  {
2602
2830
  "aria-label": "Table of contents",
2603
2831
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2604
2832
  children: [
2605
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2606
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(TocContent, {})
2833
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2834
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, {})
2607
2835
  ]
2608
2836
  }
2609
2837
  );
@@ -2613,31 +2841,31 @@ function ReportToc() {
2613
2841
  var import_react16 = require("react");
2614
2842
  var import_dialog = require("@base-ui/react/dialog");
2615
2843
  var import_lucide_react4 = require("lucide-react");
2616
- var import_jsx_runtime45 = require("react/jsx-runtime");
2844
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2617
2845
  function ReportTocDrawer() {
2618
2846
  const report = useReport();
2619
2847
  const [open, setOpen] = (0, import_react16.useState)(false);
2620
2848
  if (report.features.length === 0) return null;
2621
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2622
- /* @__PURE__ */ (0, import_jsx_runtime45.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: [
2623
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2849
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2850
+ /* @__PURE__ */ (0, import_jsx_runtime46.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: [
2851
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2624
2852
  " Contents"
2625
2853
  ] }),
2626
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Portal, { children: [
2627
- /* @__PURE__ */ (0, import_jsx_runtime45.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" }),
2628
- /* @__PURE__ */ (0, import_jsx_runtime45.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: [
2629
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between", children: [
2630
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2631
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2854
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Portal, { children: [
2855
+ /* @__PURE__ */ (0, import_jsx_runtime46.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" }),
2856
+ /* @__PURE__ */ (0, import_jsx_runtime46.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: [
2857
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center justify-between", children: [
2858
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2859
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2632
2860
  import_dialog.Dialog.Close,
2633
2861
  {
2634
2862
  "aria-label": "Close contents",
2635
2863
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2636
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2864
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2637
2865
  }
2638
2866
  )
2639
2867
  ] }),
2640
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2868
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2641
2869
  ] })
2642
2870
  ] })
2643
2871
  ] });
@@ -2691,15 +2919,43 @@ function useTheme() {
2691
2919
  return { pref, setPref };
2692
2920
  }
2693
2921
 
2922
+ // src/interactive/use-url-state.ts
2923
+ var import_react18 = require("react");
2924
+ var WRITE_DELAY_MS = 250;
2925
+ function useUrlState() {
2926
+ const [state, setState] = (0, import_react18.useState)(DEFAULT_URL_STATE);
2927
+ const ready = (0, import_react18.useRef)(false);
2928
+ (0, import_react18.useEffect)(() => {
2929
+ const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
2930
+ read();
2931
+ ready.current = true;
2932
+ window.addEventListener("hashchange", read);
2933
+ return () => window.removeEventListener("hashchange", read);
2934
+ }, []);
2935
+ (0, import_react18.useEffect)(() => {
2936
+ if (!ready.current) return;
2937
+ const timer = setTimeout(
2938
+ () => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
2939
+ WRITE_DELAY_MS
2940
+ );
2941
+ return () => clearTimeout(timer);
2942
+ }, [state]);
2943
+ const update = (0, import_react18.useCallback)(
2944
+ (patch) => setState((prev) => ({ ...prev, ...patch })),
2945
+ []
2946
+ );
2947
+ return [state, update];
2948
+ }
2949
+
2694
2950
  // src/interactive/ReportInteractive.tsx
2695
- var import_jsx_runtime46 = require("react/jsx-runtime");
2951
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2696
2952
  function ReportInteractive(props) {
2697
2953
  const { report, className, title, dataTheme } = props;
2698
2954
  const result = unwrapReport(report);
2699
2955
  if (!result.ok) {
2700
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2956
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2701
2957
  }
2702
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportInteractiveView, { ...props, report: result.data });
2958
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportInteractiveView, { ...props, report: result.data });
2703
2959
  }
2704
2960
  var THEME_OPTIONS = [
2705
2961
  { value: "light", label: "Light", Icon: import_lucide_react5.Sun },
@@ -2707,7 +2963,7 @@ var THEME_OPTIONS = [
2707
2963
  { value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
2708
2964
  ];
2709
2965
  function ThemeSegment({ pref, onPref }) {
2710
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2966
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2711
2967
  ToggleGroup,
2712
2968
  {
2713
2969
  size: "sm",
@@ -2717,7 +2973,7 @@ function ThemeSegment({ pref, onPref }) {
2717
2973
  if (next) onPref(next);
2718
2974
  },
2719
2975
  "aria-label": "Color theme",
2720
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Icon, {}) }, value))
2976
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Icon, {}) }, value))
2721
2977
  }
2722
2978
  );
2723
2979
  }
@@ -2733,25 +2989,32 @@ function ReportInteractiveView({
2733
2989
  staleAfterDays = 7,
2734
2990
  scenarioHistory
2735
2991
  }) {
2736
- const [query, setQuery] = (0, import_react18.useState)("");
2737
- const [statusFilter, setStatusFilter] = (0, import_react18.useState)("all");
2738
- const [activeTags, setActiveTags] = (0, import_react18.useState)([]);
2739
- const [detail, setDetail] = (0, import_react18.useState)("full");
2740
- const [helpOpen, setHelpOpen] = (0, import_react18.useState)(false);
2992
+ const [urlState, setUrlState] = useUrlState();
2993
+ const { query, status: statusFilter, tags: activeTags, detail } = urlState;
2994
+ const setQuery = (0, import_react19.useCallback)((next) => setUrlState({ query: next }), [setUrlState]);
2995
+ const setStatusFilter = (0, import_react19.useCallback)(
2996
+ (next) => setUrlState({ status: next }),
2997
+ [setUrlState]
2998
+ );
2999
+ const setDetail = (0, import_react19.useCallback)(
3000
+ (next) => setUrlState({ detail: next }),
3001
+ [setUrlState]
3002
+ );
3003
+ const [helpOpen, setHelpOpen] = (0, import_react19.useState)(false);
2741
3004
  const { pref: themePref, setPref: setThemePref } = useTheme();
2742
- const searchRef = (0, import_react18.useRef)(null);
2743
- const expandId = (0, import_react18.useId)();
2744
- const docsId = (0, import_react18.useId)();
2745
- const deferredQuery = (0, import_react18.useDeferredValue)(query);
2746
- const failures = (0, import_react18.useMemo)(() => listFailures(report), [report]);
2747
- const filtered = (0, import_react18.useMemo)(
3005
+ const searchRef = (0, import_react19.useRef)(null);
3006
+ const expandId = (0, import_react19.useId)();
3007
+ const docsId = (0, import_react19.useId)();
3008
+ const deferredQuery = (0, import_react19.useDeferredValue)(query);
3009
+ const failures = (0, import_react19.useMemo)(() => listFailures(report), [report]);
3010
+ const filtered = (0, import_react19.useMemo)(
2748
3011
  // Features carrying failures float to the top so the first thing on screen
2749
3012
  // is the thing that needs attention; an all-green run keeps source order.
2750
3013
  () => failuresFirst(filterReport(report, { query: deferredQuery, status: statusFilter, tags: activeTags })),
2751
3014
  [report, deferredQuery, statusFilter, activeTags]
2752
3015
  );
2753
3016
  const isFiltering = query !== deferredQuery;
2754
- const statusOptions = (0, import_react18.useMemo)(() => {
3017
+ const statusOptions = (0, import_react19.useMemo)(() => {
2755
3018
  const s = report.summary;
2756
3019
  const opts = [
2757
3020
  { key: "all", label: "All", count: s.total }
@@ -2762,19 +3025,21 @@ function ReportInteractiveView({
2762
3025
  if (s.pending) opts.push({ key: "pending", label: "Pending", count: s.pending });
2763
3026
  return opts;
2764
3027
  }, [report]);
2765
- const tagOptions = (0, import_react18.useMemo)(() => allTags(report), [report]);
2766
- const toggleTag = (0, import_react18.useCallback)(
2767
- (tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
2768
- []
3028
+ const tagOptions = (0, import_react19.useMemo)(() => allTags(report), [report]);
3029
+ const toggleTag = (0, import_react19.useCallback)(
3030
+ (tag) => setUrlState({
3031
+ tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
3032
+ }),
3033
+ [activeTags, setUrlState]
2769
3034
  );
2770
- const [toast, setToast] = (0, import_react18.useState)(null);
2771
- const toastTimer = (0, import_react18.useRef)(null);
2772
- const showToast = (0, import_react18.useCallback)((msg) => {
3035
+ const [toast, setToast] = (0, import_react19.useState)(null);
3036
+ const toastTimer = (0, import_react19.useRef)(null);
3037
+ const showToast = (0, import_react19.useCallback)((msg) => {
2773
3038
  setToast(msg);
2774
3039
  if (toastTimer.current) clearTimeout(toastTimer.current);
2775
3040
  toastTimer.current = setTimeout(() => setToast(null), 1800);
2776
3041
  }, []);
2777
- const copy = (0, import_react18.useCallback)(
3042
+ const copy = (0, import_react19.useCallback)(
2778
3043
  (text, msg) => {
2779
3044
  if (typeof navigator !== "undefined" && navigator.clipboard) {
2780
3045
  navigator.clipboard.writeText(text).then(() => showToast(msg), () => showToast("Copy failed"));
@@ -2782,7 +3047,7 @@ function ReportInteractiveView({
2782
3047
  },
2783
3048
  [showToast]
2784
3049
  );
2785
- const scenarioActions = (0, import_react18.useMemo)(
3050
+ const scenarioActions = (0, import_react19.useMemo)(
2786
3051
  () => ({
2787
3052
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
2788
3053
  copyMarkdown: (s) => copy(scenarioToMarkdown2(s), "Markdown copied"),
@@ -2790,15 +3055,15 @@ function ReportInteractiveView({
2790
3055
  }),
2791
3056
  [copy]
2792
3057
  );
2793
- const failureIndexRef = (0, import_react18.useRef)(0);
2794
- const allCollapsibleIds = (0, import_react18.useMemo)(
3058
+ const failureIndexRef = (0, import_react19.useRef)(0);
3059
+ const allCollapsibleIds = (0, import_react19.useMemo)(
2795
3060
  () => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
2796
3061
  [report]
2797
3062
  );
2798
- const collapse = useCollapseState((0, import_react18.useCallback)(() => defaultCollapsedIds(report), [report]));
2799
- const collapseAll = (0, import_react18.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
2800
- const [expandedAll, setExpandedAll] = (0, import_react18.useState)(() => report.summary.failed === 0);
2801
- const setExpanded = (0, import_react18.useCallback)(
3063
+ const collapse = useCollapseState((0, import_react19.useCallback)(() => defaultCollapsedIds(report), [report]));
3064
+ const collapseAll = (0, import_react19.useCallback)(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
3065
+ const [expandedAll, setExpandedAll] = (0, import_react19.useState)(() => report.summary.failed === 0);
3066
+ const setExpanded = (0, import_react19.useCallback)(
2802
3067
  (expanded) => {
2803
3068
  if (expanded) collapse.expandAll();
2804
3069
  else collapseAll();
@@ -2806,10 +3071,10 @@ function ReportInteractiveView({
2806
3071
  },
2807
3072
  [collapse, collapseAll]
2808
3073
  );
2809
- const focusSearch = (0, import_react18.useCallback)(() => {
3074
+ const focusSearch = (0, import_react19.useCallback)(() => {
2810
3075
  searchRef.current?.focus();
2811
3076
  }, []);
2812
- const stepFailure = (0, import_react18.useCallback)(
3077
+ const stepFailure = (0, import_react19.useCallback)(
2813
3078
  (direction) => {
2814
3079
  if (failures.length === 0) return;
2815
3080
  failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
@@ -2818,10 +3083,10 @@ function ReportInteractiveView({
2818
3083
  },
2819
3084
  [failures]
2820
3085
  );
2821
- const toggleHelp = (0, import_react18.useCallback)(() => {
3086
+ const toggleHelp = (0, import_react19.useCallback)(() => {
2822
3087
  setHelpOpen((v) => !v);
2823
3088
  }, []);
2824
- const escape = (0, import_react18.useCallback)(() => {
3089
+ const escape = (0, import_react19.useCallback)(() => {
2825
3090
  if (helpOpen) setHelpOpen(false);
2826
3091
  else if (query !== "") setQuery("");
2827
3092
  }, [helpOpen, query]);
@@ -2839,13 +3104,13 @@ function ReportInteractiveView({
2839
3104
  const totalScenarios = report.summary.total;
2840
3105
  const matchedScenarios = filtered.summary.total;
2841
3106
  const showToc = !hideToc && totalScenarios > 3;
2842
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3107
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2843
3108
  ReportRoot,
2844
3109
  {
2845
3110
  report: filtered,
2846
3111
  customRenderers,
2847
3112
  renderers,
2848
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3113
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2849
3114
  "main",
2850
3115
  {
2851
3116
  className: cn("es-report", "es-report-interactive", className),
@@ -2854,8 +3119,8 @@ function ReportInteractiveView({
2854
3119
  "data-theme": dataTheme,
2855
3120
  "data-detail-level": detail,
2856
3121
  children: [
2857
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "es-report-header", children: [
2858
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3122
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("header", { className: "es-report-header", children: [
3123
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2859
3124
  "div",
2860
3125
  {
2861
3126
  "data-es-navbar": true,
@@ -2864,9 +3129,9 @@ function ReportInteractiveView({
2864
3129
  hideHeader ? "justify-end" : "justify-between"
2865
3130
  ),
2866
3131
  children: [
2867
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2868
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2869
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3132
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
3133
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
3134
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2870
3135
  ReportSearch,
2871
3136
  {
2872
3137
  ref: searchRef,
@@ -2876,16 +3141,16 @@ function ReportInteractiveView({
2876
3141
  totalCount: totalScenarios
2877
3142
  }
2878
3143
  ),
2879
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
3144
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2880
3145
  ] })
2881
3146
  ]
2882
3147
  }
2883
3148
  ),
2884
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2885
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportSummary, {}),
2886
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportMeta, {})
3149
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
3150
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportSummary, {}),
3151
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportMeta, {})
2887
3152
  ] }),
2888
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3153
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2889
3154
  ReportFreshness,
2890
3155
  {
2891
3156
  lastRunMs: reportLastRunMs(report),
@@ -2893,8 +3158,8 @@ function ReportInteractiveView({
2893
3158
  staleAfterDays
2894
3159
  }
2895
3160
  ),
2896
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2897
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3161
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
3162
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2898
3163
  ReportFilters,
2899
3164
  {
2900
3165
  statuses: statusOptions,
@@ -2905,14 +3170,14 @@ function ReportInteractiveView({
2905
3170
  onToggleTag: toggleTag
2906
3171
  }
2907
3172
  ),
2908
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2909
- showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportTocDrawer, {}) : null,
2910
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2911
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2912
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
3173
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
3174
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportTocDrawer, {}) : null,
3175
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
3176
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
3177
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2913
3178
  ] }),
2914
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2915
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3179
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
3180
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2916
3181
  Switch,
2917
3182
  {
2918
3183
  id: docsId,
@@ -2920,16 +3185,16 @@ function ReportInteractiveView({
2920
3185
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
2921
3186
  }
2922
3187
  ),
2923
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
3188
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2924
3189
  ] })
2925
3190
  ] })
2926
3191
  ] }),
2927
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFailureBanner, { failures }),
2928
- hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex gap-6", children: [
2929
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportToc, {}),
2930
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFeatureList, {}) })
2931
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2932
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3192
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFailureBanner, { failures }),
3193
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex gap-6", children: [
3194
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportToc, {}),
3195
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFeatureList, {}) })
3196
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
3197
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2933
3198
  "button",
2934
3199
  {
2935
3200
  type: "button",
@@ -2940,8 +3205,8 @@ function ReportInteractiveView({
2940
3205
  children: "?"
2941
3206
  }
2942
3207
  ),
2943
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2944
- toast ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3208
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
3209
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2945
3210
  "div",
2946
3211
  {
2947
3212
  role: "status",
@@ -2958,14 +3223,14 @@ function ReportInteractiveView({
2958
3223
  }
2959
3224
 
2960
3225
  // src/interactive/ReportInteractiveIsland.tsx
2961
- var import_react21 = require("react");
3226
+ var import_react22 = require("react");
2962
3227
 
2963
3228
  // src/interactive/island-renderers.tsx
2964
- var import_react20 = require("react");
3229
+ var import_react21 = require("react");
2965
3230
 
2966
3231
  // src/components/doc/MermaidView.tsx
2967
- var import_react19 = require("react");
2968
- var import_jsx_runtime47 = require("react/jsx-runtime");
3232
+ var import_react20 = require("react");
3233
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2969
3234
  function prefersDark(el) {
2970
3235
  const themed = el?.closest("[data-theme]");
2971
3236
  if (themed?.dataset.theme === "dark") return true;
@@ -2973,23 +3238,40 @@ function prefersDark(el) {
2973
3238
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
2974
3239
  }
2975
3240
  function MermaidView({ entry, load }) {
2976
- const ref = (0, import_react19.useRef)(null);
2977
- const [svg, setSvg] = (0, import_react19.useState)(null);
2978
- (0, import_react19.useEffect)(() => {
3241
+ const ref = (0, import_react20.useRef)(null);
3242
+ const [svg, setSvg] = (0, import_react20.useState)(null);
3243
+ const [error, setError] = (0, import_react20.useState)(null);
3244
+ (0, import_react20.useEffect)(() => {
2979
3245
  let cancelled = false;
2980
3246
  const draw = async () => {
3247
+ let mermaid;
3248
+ try {
3249
+ mermaid = await load();
3250
+ } catch {
3251
+ if (!cancelled) {
3252
+ setSvg(null);
3253
+ setError(null);
3254
+ }
3255
+ return;
3256
+ }
2981
3257
  try {
2982
- const mermaid = await load();
2983
3258
  mermaid.initialize({
2984
3259
  startOnLoad: false,
2985
3260
  securityLevel: "strict",
2986
3261
  theme: prefersDark(ref.current) ? "dark" : "default"
2987
3262
  });
3263
+ await mermaid.parse(entry.code);
2988
3264
  const id = `es-mermaid-${Math.random().toString(36).slice(2)}`;
2989
3265
  const { svg: rendered } = await mermaid.render(id, entry.code);
2990
- if (!cancelled) setSvg(rendered);
2991
- } catch {
2992
- if (!cancelled) setSvg(null);
3266
+ if (!cancelled) {
3267
+ setSvg(rendered);
3268
+ setError(null);
3269
+ }
3270
+ } catch (err) {
3271
+ if (!cancelled) {
3272
+ setSvg(null);
3273
+ setError(err instanceof Error ? err.message : String(err));
3274
+ }
2993
3275
  }
2994
3276
  };
2995
3277
  void draw();
@@ -3002,9 +3284,9 @@ function MermaidView({ entry, load }) {
3002
3284
  };
3003
3285
  }, [entry.code, load]);
3004
3286
  if (svg) {
3005
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("figure", { ref, className: "my-3", children: [
3006
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3007
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3287
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("figure", { ref, className: "my-3", children: [
3288
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3289
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3008
3290
  "div",
3009
3291
  {
3010
3292
  role: "img",
@@ -3015,11 +3297,25 @@ function MermaidView({ entry, load }) {
3015
3297
  )
3016
3298
  ] });
3017
3299
  }
3018
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(MermaidSource, { entry, ref });
3300
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
3301
+ error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3302
+ "p",
3303
+ {
3304
+ role: "alert",
3305
+ "data-es-mermaid-error": true,
3306
+ className: "mt-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive",
3307
+ children: [
3308
+ "Diagram failed to render: ",
3309
+ error
3310
+ ]
3311
+ }
3312
+ ) : null,
3313
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(MermaidSource, { entry, ref })
3314
+ ] });
3019
3315
  }
3020
3316
 
3021
3317
  // src/interactive/island-renderers.tsx
3022
- var import_jsx_runtime48 = require("react/jsx-runtime");
3318
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3023
3319
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3024
3320
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3025
3321
  function cachedCdnModule(url) {
@@ -3036,8 +3332,8 @@ function cachedCdnModule(url) {
3036
3332
  var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
3037
3333
  var loadHljs = cachedCdnModule(HLJS_ESM);
3038
3334
  function CdnHighlightedCode({ entry }) {
3039
- const [html, setHtml] = (0, import_react20.useState)(null);
3040
- (0, import_react20.useEffect)(() => {
3335
+ const [html, setHtml] = (0, import_react21.useState)(null);
3336
+ (0, import_react21.useEffect)(() => {
3041
3337
  let cancelled = false;
3042
3338
  void (async () => {
3043
3339
  try {
@@ -3053,27 +3349,27 @@ function CdnHighlightedCode({ entry }) {
3053
3349
  cancelled = true;
3054
3350
  };
3055
3351
  }, [entry.content, entry.lang]);
3056
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3352
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3057
3353
  "code",
3058
3354
  {
3059
3355
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
3060
3356
  dangerouslySetInnerHTML: { __html: html }
3061
3357
  }
3062
- ) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3358
+ ) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3063
3359
  }
3064
3360
  function buildIslandRenderers(opts) {
3065
3361
  const renderers = {};
3066
3362
  if (opts.mermaid) {
3067
- renderers.mermaid = (entry) => (0, import_react20.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
3363
+ renderers.mermaid = (entry) => (0, import_react21.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
3068
3364
  }
3069
3365
  if (opts.syntaxHighlighting) {
3070
- renderers.code = (entry) => (0, import_react20.createElement)(CdnHighlightedCode, { entry });
3366
+ renderers.code = (entry) => (0, import_react21.createElement)(CdnHighlightedCode, { entry });
3071
3367
  }
3072
3368
  return renderers;
3073
3369
  }
3074
3370
 
3075
3371
  // src/interactive/ReportInteractiveIsland.tsx
3076
- var import_jsx_runtime49 = require("react/jsx-runtime");
3372
+ var import_jsx_runtime50 = require("react/jsx-runtime");
3077
3373
  function ReportInteractiveIsland({
3078
3374
  report,
3079
3375
  title,
@@ -3083,11 +3379,11 @@ function ReportInteractiveIsland({
3083
3379
  syntaxHighlighting = true,
3084
3380
  mermaid = true
3085
3381
  }) {
3086
- const renderers = (0, import_react21.useMemo)(
3382
+ const renderers = (0, import_react22.useMemo)(
3087
3383
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
3088
3384
  [syntaxHighlighting, mermaid]
3089
3385
  );
3090
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3386
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3091
3387
  ReportInteractive,
3092
3388
  {
3093
3389
  report,