executable-stories-react 0.9.0 → 0.10.1

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.
@@ -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,237 @@ 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
+ // src/components/ReportScenario.tsx
338
+ var import_duration3 = require("executable-stories-core/utils/duration");
339
+
340
+ // src/components/ReportSteps.tsx
341
+ var import_duration = require("executable-stories-core/utils/duration");
342
+
343
+ // src/components/doc/DocNote.tsx
184
344
  var import_jsx_runtime3 = require("react/jsx-runtime");
345
+ function DocNote({ entry }) {
346
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
347
+ }
348
+
349
+ // src/components/doc/DocTag.tsx
350
+ var import_jsx_runtime4 = require("react/jsx-runtime");
185
351
  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)) });
352
+ 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
353
  }
188
354
 
189
355
  // src/components/doc/DocKv.tsx
190
- var import_jsx_runtime4 = require("react/jsx-runtime");
356
+ var import_jsx_runtime5 = require("react/jsx-runtime");
191
357
  function formatValue(value) {
192
358
  if (value === null) return "null";
193
359
  if (typeof value === "string") return value;
@@ -199,19 +365,19 @@ function formatValue(value) {
199
365
  }
200
366
  }
201
367
  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) })
368
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
369
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
370
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
205
371
  ] });
206
372
  }
207
373
 
208
374
  // src/hooks/useRenderers.ts
209
375
  var import_react4 = require("react");
210
- var EMPTY_CUSTOM2 = {};
376
+ var EMPTY_CUSTOM = {};
211
377
  var EMPTY_RENDERERS2 = {};
212
378
  function useCustomRenderers() {
213
379
  const ctx = (0, import_react4.useContext)(ReportContext);
214
- return ctx?.customRenderers ?? EMPTY_CUSTOM2;
380
+ return ctx?.customRenderers ?? EMPTY_CUSTOM;
215
381
  }
216
382
  function useBuiltinRenderers() {
217
383
  const ctx = (0, import_react4.useContext)(ReportContext);
@@ -219,37 +385,37 @@ function useBuiltinRenderers() {
219
385
  }
220
386
 
221
387
  // src/components/doc/DocCode.tsx
222
- var import_jsx_runtime5 = require("react/jsx-runtime");
388
+ var import_jsx_runtime6 = require("react/jsx-runtime");
223
389
  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 })
390
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("figure", { className: "my-3", children: [
391
+ label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
392
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
227
393
  ] });
228
394
  }
229
395
  function DocCode({ entry }) {
230
396
  const renderers = useBuiltinRenderers();
231
397
  if (renderers.code) {
232
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: renderers.code(entry) });
398
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderers.code(entry) });
233
399
  }
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 }) });
400
+ 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
401
  }
236
402
 
237
403
  // src/components/doc/DocTable.tsx
238
- var import_jsx_runtime6 = require("react/jsx-runtime");
404
+ var import_jsx_runtime7 = require("react/jsx-runtime");
239
405
  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)) })
406
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("figure", { className: "my-3", children: [
407
+ entry.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
408
+ /* @__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: [
409
+ /* @__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)) }) }),
410
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
245
411
  ] }) })
246
412
  ] });
247
413
  }
248
414
 
249
415
  // src/components/doc/DocLink.tsx
250
- var import_jsx_runtime7 = require("react/jsx-runtime");
416
+ var import_jsx_runtime8 = require("react/jsx-runtime");
251
417
  function DocLink({ entry }) {
252
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
418
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
253
419
  "a",
254
420
  {
255
421
  className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
@@ -264,28 +430,13 @@ function DocLink({ entry }) {
264
430
  // src/components/doc/DocSection.tsx
265
431
  var import_marked = require("marked");
266
432
 
267
- // ../executable-stories-core/dist/utils/url.js
268
- function safeUrl(value) {
269
- if (!value) return void 0;
270
- const trimmed = value.trim();
271
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
272
- if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
273
- return trimmed;
274
- }
275
- function safeImageUrl(value) {
276
- if (!value) return void 0;
277
- const trimmed = value.trim();
278
- if (/^data:image\//i.test(trimmed)) return trimmed;
279
- return safeUrl(trimmed);
280
- }
281
- function isLocalFsPath(value) {
282
- return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
283
- }
433
+ // src/lib/url.ts
434
+ var import_url = require("executable-stories-core/utils/url");
284
435
 
285
436
  // src/components/doc/DocSection.tsx
286
- var import_jsx_runtime8 = require("react/jsx-runtime");
437
+ var import_jsx_runtime9 = require("react/jsx-runtime");
287
438
  function neutralizeUrl(value) {
288
- return safeUrl(value) ?? "#";
439
+ return (0, import_url.safeUrl)(value) ?? "#";
289
440
  }
290
441
  function safeMarkdownHtml(markdown) {
291
442
  const raw = import_marked.marked.parse(markdown, { async: false });
@@ -294,12 +445,12 @@ function safeMarkdownHtml(markdown) {
294
445
  function DocSection({ entry }) {
295
446
  const renderers = useBuiltinRenderers();
296
447
  if (renderers.section) {
297
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: renderers.section(entry) });
448
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderers.section(entry) });
298
449
  }
299
450
  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)(
451
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
452
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
453
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
303
454
  "div",
304
455
  {
305
456
  className: "es-doc-prose prose prose-sm max-w-none",
@@ -310,11 +461,11 @@ function DocSection({ entry }) {
310
461
  }
311
462
 
312
463
  // src/components/doc/DocMermaid.tsx
313
- var import_jsx_runtime9 = require("react/jsx-runtime");
464
+ var import_jsx_runtime10 = require("react/jsx-runtime");
314
465
  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)(
466
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
467
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
468
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
318
469
  "pre",
319
470
  {
320
471
  "data-mermaid": true,
@@ -328,24 +479,24 @@ function MermaidSource({ entry, ref }) {
328
479
  function DocMermaid({ entry }) {
329
480
  const renderers = useBuiltinRenderers();
330
481
  if (renderers.mermaid) {
331
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: renderers.mermaid(entry) });
482
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: renderers.mermaid(entry) });
332
483
  }
333
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MermaidSource, { entry });
484
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MermaidSource, { entry });
334
485
  }
335
486
 
336
487
  // src/components/doc/DocScreenshot.tsx
337
- var import_jsx_runtime10 = require("react/jsx-runtime");
488
+ var import_jsx_runtime11 = require("react/jsx-runtime");
338
489
  function DocScreenshot({ entry }) {
339
- const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
490
+ const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
340
491
  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
492
+ 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: [
493
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
494
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
495
+ entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
345
496
  ] });
346
497
  }
347
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("figure", { className: "my-3", children: [
348
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
498
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { className: "my-3", children: [
499
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
349
500
  "img",
350
501
  {
351
502
  src,
@@ -354,24 +505,24 @@ function DocScreenshot({ entry }) {
354
505
  className: "max-w-full rounded-md border border-border"
355
506
  }
356
507
  ),
357
- entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
508
+ entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
358
509
  ] });
359
510
  }
360
511
 
361
512
  // src/components/doc/DocVideo.tsx
362
- var import_jsx_runtime11 = require("react/jsx-runtime");
513
+ var import_jsx_runtime12 = require("react/jsx-runtime");
363
514
  function DocVideo({ entry }) {
364
- 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
515
+ if ((0, import_url.isLocalFsPath)(entry.path)) {
516
+ 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: [
517
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
518
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
519
+ entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
369
520
  ] });
370
521
  }
371
- const src = safeUrl(entry.path);
372
- 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)(
522
+ const src = (0, import_url.safeUrl)(entry.path);
523
+ const poster = (0, import_url.safeUrl)(entry.poster);
524
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
525
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
375
526
  "video",
376
527
  {
377
528
  controls: true,
@@ -381,21 +532,21 @@ function DocVideo({ entry }) {
381
532
  className: "max-w-full rounded-md border border-border"
382
533
  }
383
534
  ),
384
- entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
535
+ entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
385
536
  ] });
386
537
  }
387
538
 
388
539
  // src/components/doc/DocHtml.tsx
389
- var import_jsx_runtime12 = require("react/jsx-runtime");
540
+ var import_jsx_runtime13 = require("react/jsx-runtime");
390
541
  function DocHtml({ entry }) {
391
542
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
392
543
  const title = entry.title ?? "Embedded HTML";
393
- const safeHref = safeUrl(entry.url ?? entry.path);
544
+ const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
394
545
  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)(
546
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
547
+ /* @__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: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: entry.title ?? "HTML" }),
549
+ safeHref ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
399
550
  "a",
400
551
  {
401
552
  href: safeHref,
@@ -408,7 +559,7 @@ function DocHtml({ entry }) {
408
559
  }
409
560
  ) : null
410
561
  ] }),
411
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
562
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
412
563
  "iframe",
413
564
  {
414
565
  ...frameProps,
@@ -423,7 +574,7 @@ function DocHtml({ entry }) {
423
574
  }
424
575
 
425
576
  // src/components/doc/DocState.tsx
426
- var import_jsx_runtime13 = require("react/jsx-runtime");
577
+ var import_jsx_runtime14 = require("react/jsx-runtime");
427
578
  function DocState({ entry }) {
428
579
  let json;
429
580
  try {
@@ -431,23 +582,23 @@ function DocState({ entry }) {
431
582
  } catch {
432
583
  json = String(entry.value);
433
584
  }
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 }) });
585
+ 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
586
  }
436
587
 
437
588
  // src/components/doc/DocCustom.tsx
438
- var import_jsx_runtime14 = require("react/jsx-runtime");
589
+ var import_jsx_runtime15 = require("react/jsx-runtime");
439
590
  function DocCustom({ entry }) {
440
591
  const renderers = useCustomRenderers();
441
592
  const renderer = renderers[entry.type];
442
593
  if (renderer) {
443
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: renderer(entry) });
594
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: renderer(entry) });
444
595
  }
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) })
596
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
597
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
598
+ /* @__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
599
  ] });
449
600
  }
450
- function safeStringify(value) {
601
+ function safeStringify2(value) {
451
602
  try {
452
603
  return JSON.stringify(value, null, 2);
453
604
  } catch {
@@ -456,47 +607,47 @@ function safeStringify(value) {
456
607
  }
457
608
 
458
609
  // src/components/doc/DocEntry.tsx
459
- var import_jsx_runtime15 = require("react/jsx-runtime");
610
+ var import_jsx_runtime16 = require("react/jsx-runtime");
460
611
  function DocEntry({ entry }) {
461
612
  switch (entry.kind) {
462
613
  case "note":
463
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocNote, { entry });
614
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocNote, { entry });
464
615
  case "tag":
465
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTag, { entry });
616
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocTag, { entry });
466
617
  case "kv":
467
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocKv, { entry });
618
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocKv, { entry });
468
619
  case "code":
469
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCode, { entry });
620
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocCode, { entry });
470
621
  case "table":
471
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocTable, { entry });
622
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocTable, { entry });
472
623
  case "link":
473
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocLink, { entry });
624
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocLink, { entry });
474
625
  case "section":
475
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocSection, { entry });
626
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocSection, { entry });
476
627
  case "mermaid":
477
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocMermaid, { entry });
628
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocMermaid, { entry });
478
629
  case "screenshot":
479
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocScreenshot, { entry });
630
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocScreenshot, { entry });
480
631
  case "video":
481
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocVideo, { entry });
632
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocVideo, { entry });
482
633
  case "html":
483
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocHtml, { entry });
634
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocHtml, { entry });
484
635
  case "state":
485
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocState, { entry });
636
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocState, { entry });
486
637
  case "custom":
487
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DocCustom, { entry });
638
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(DocCustom, { entry });
488
639
  }
489
640
  }
490
641
 
491
642
  // src/components/ReportDocEntries.tsx
492
- var import_jsx_runtime16 = require("react/jsx-runtime");
643
+ var import_jsx_runtime17 = require("react/jsx-runtime");
493
644
  function ReportDocEntries({ entries }) {
494
645
  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)) });
646
+ 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
647
  }
497
648
 
498
649
  // src/lib/step-params.tsx
499
- var import_jsx_runtime17 = require("react/jsx-runtime");
650
+ var import_jsx_runtime18 = require("react/jsx-runtime");
500
651
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
501
652
  function highlightStepParams(text) {
502
653
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -508,7 +659,7 @@ function highlightStepParams(text) {
508
659
  const end = start + match[0].length;
509
660
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
510
661
  nodes.push(
511
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
662
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
512
663
  );
513
664
  lastIndex = end;
514
665
  });
@@ -517,7 +668,7 @@ function highlightStepParams(text) {
517
668
  }
518
669
 
519
670
  // src/components/ReportSteps.tsx
520
- var import_jsx_runtime18 = require("react/jsx-runtime");
671
+ var import_jsx_runtime19 = require("react/jsx-runtime");
521
672
  var STEP_GLYPH = {
522
673
  passed: "\u2713",
523
674
  failed: "\u2717",
@@ -532,21 +683,21 @@ var STEP_GLYPH_COLOR = {
532
683
  };
533
684
  function ReportSteps({ scenario }) {
534
685
  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)) });
686
+ 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
687
  }
537
688
  function ReportStepItem({ step }) {
538
689
  const kw = step.keyword.toLowerCase();
539
690
  const continuation = kw === "and" || kw === "but";
540
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
691
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
541
692
  "li",
542
693
  {
543
694
  id: step.id,
544
695
  "data-status": step.status,
545
696
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
546
697
  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)(
698
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-baseline gap-2", children: [
699
+ /* @__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" }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
550
701
  "span",
551
702
  {
552
703
  className: cn(
@@ -556,10 +707,10 @@ function ReportStepItem({ step }) {
556
707
  children: step.keyword
557
708
  }
558
709
  ),
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
710
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
711
+ step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration.formatDuration)(step.durationMs) }) : null
561
712
  ] }),
562
- step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
713
+ step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
563
714
  "pre",
564
715
  {
565
716
  role: "alert",
@@ -567,132 +718,17 @@ function ReportStepItem({ step }) {
567
718
  children: step.errorMessage
568
719
  }
569
720
  ) : 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
721
+ 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
722
  ]
572
723
  }
573
724
  );
574
725
  }
575
726
 
576
- // ../executable-stories-core/dist/state-diff.js
577
- var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
578
- function deepEqual(a, b) {
579
- if (Object.is(a, b)) return true;
580
- if (Array.isArray(a) && Array.isArray(b)) {
581
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
582
- }
583
- if (isPlainObject(a) && isPlainObject(b)) {
584
- const ak = Object.keys(a);
585
- const bk = Object.keys(b);
586
- return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
587
- }
588
- return false;
589
- }
590
- var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
591
- function walk(path, before, after, out) {
592
- if (Object.is(before, after)) return;
593
- if (Array.isArray(before) && Array.isArray(after)) {
594
- const shared = Math.min(before.length, after.length);
595
- for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
596
- for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
597
- for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
598
- return;
599
- }
600
- if (isPlainObject(before) && isPlainObject(after)) {
601
- for (const key of Object.keys(after)) {
602
- const p = joinKey(path, key);
603
- if (key in before) walk(p, before[key], after[key], out);
604
- else out.push({ path: p, kind: "added", after: after[key] });
605
- }
606
- for (const key of Object.keys(before)) {
607
- if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
608
- }
609
- return;
610
- }
611
- if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
612
- }
613
- function diffStateValues(before, after) {
614
- const out = [];
615
- walk("", before, after, out);
616
- return out;
617
- }
618
- function formatStateValue(value, maxLen = 60) {
619
- const s = value === void 0 ? "undefined" : JSON.stringify(value);
620
- return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
621
- }
622
- function summarizeStateChanges(changes) {
623
- return changes.map((c) => {
624
- const at = c.path === "" ? "(root)" : c.path;
625
- switch (c.kind) {
626
- case "added":
627
- return `+ ${at}: ${formatStateValue(c.after)}`;
628
- case "removed":
629
- return `\u2212 ${at}`;
630
- case "changed":
631
- return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
632
- }
633
- });
634
- }
635
-
636
- // ../executable-stories-core/dist/storyboard.js
637
- function findScreenshot(entries) {
638
- for (const entry of entries) {
639
- if (entry.kind === "screenshot") return entry;
640
- if (entry.children) {
641
- const nested = findScreenshot(entry.children);
642
- if (nested) return nested;
643
- }
644
- }
645
- return void 0;
646
- }
647
- function findStates(entries, out = []) {
648
- for (const entry of entries) {
649
- if (entry.kind === "state") out.push(entry);
650
- if (entry.children) findStates(entry.children, out);
651
- }
652
- return out;
653
- }
654
- var laneOf = (label) => label ?? "";
655
- function extractStoryboardFrames(scenario) {
656
- const frames = [];
657
- const laneOrder = [];
658
- const previous = /* @__PURE__ */ new Map();
659
- for (const step of scenario.steps) {
660
- const shot = findScreenshot(step.docEntries);
661
- const stateDocs = findStates(step.docEntries);
662
- if (!shot && stateDocs.length === 0) continue;
663
- const byLane = /* @__PURE__ */ new Map();
664
- for (const doc of stateDocs) {
665
- const lane = laneOf(doc.label);
666
- if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
667
- byLane.set(lane, doc);
668
- }
669
- const states = [];
670
- for (const lane of laneOrder) {
671
- const doc = byLane.get(lane);
672
- if (!doc) continue;
673
- const card = {
674
- ...doc.label !== void 0 && { label: doc.label },
675
- value: doc.value
676
- };
677
- if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
678
- previous.set(lane, doc.value);
679
- states.push(card);
680
- }
681
- frames.push({
682
- stepId: step.id,
683
- stepIndex: step.index,
684
- keyword: step.keyword,
685
- text: step.text,
686
- status: step.status,
687
- ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
688
- states
689
- });
690
- }
691
- return frames;
692
- }
693
-
694
727
  // src/components/ReportStoryboard.tsx
695
- var import_jsx_runtime19 = require("react/jsx-runtime");
728
+ var import_storyboard = require("executable-stories-core/storyboard");
729
+ var import_state_diff = require("executable-stories-core/state-diff");
730
+ var import_url6 = require("executable-stories-core/utils/url");
731
+ var import_jsx_runtime20 = require("react/jsx-runtime");
696
732
  var FRAME_GLYPH = {
697
733
  passed: "\u2713",
698
734
  failed: "\u2717",
@@ -706,48 +742,48 @@ var FRAME_GLYPH_COLOR = {
706
742
  pending: "text-pend"
707
743
  };
708
744
  function ReportStoryboard({ scenario }) {
709
- const frames = extractStoryboardFrames(scenario);
745
+ const frames = (0, import_storyboard.extractStoryboardFrames)(scenario);
710
746
  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)) })
747
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
748
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
749
+ /* @__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
750
  ] });
715
751
  }
716
752
  function StoryboardFrameItem({ frame }) {
717
753
  const label = `${frame.keyword} ${frame.text}`;
718
754
  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 }),
755
+ const caption = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
756
+ /* @__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" }),
757
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "min-w-0", children: [
758
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
723
759
  " ",
724
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
760
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
725
761
  ] })
726
762
  ] });
727
763
  return (
728
764
  // State cards need room for diff rows; screenshot-only frames keep the
729
765
  // 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,
766
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
767
+ frame.screenshot ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(FrameScreenshot, { frame, label }) : null,
768
+ 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
769
  frame.screenshot ? caption : (
734
770
  // No screenshot means no image anchor, so the caption carries the
735
771
  // 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 })
772
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
737
773
  )
738
774
  ] })
739
775
  );
740
776
  }
741
777
  function FrameScreenshot({ frame, label }) {
742
778
  const path = frame.screenshot?.path ?? "";
743
- const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
744
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
779
+ const src = (0, import_url6.isLocalFsPath)(path) ? void 0 : (0, import_url6.safeImageUrl)(path);
780
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
745
781
  "a",
746
782
  {
747
783
  href: `#${frame.stepId}`,
748
784
  "aria-label": `Jump to step: ${label}`,
749
785
  className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
750
- children: src ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
786
+ children: src ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
751
787
  "img",
752
788
  {
753
789
  src,
@@ -755,7 +791,7 @@ function FrameScreenshot({ frame, label }) {
755
791
  loading: "lazy",
756
792
  className: "h-24 w-full bg-muted object-cover object-top"
757
793
  }
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" })
794
+ ) : /* @__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
795
  }
760
796
  );
761
797
  }
@@ -774,30 +810,30 @@ function prettyJson(value) {
774
810
  function StateCardView({ card }) {
775
811
  const json = prettyJson(card.value);
776
812
  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
813
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
814
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
815
+ /* @__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" }),
816
+ firstCapture ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
781
817
  ] }),
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)(
818
+ !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
819
  "li",
784
820
  {
785
821
  className: cn(
786
822
  "rounded-sm px-1.5 py-0.5 font-mono text-[0.625rem] leading-snug break-all",
787
823
  CHANGE_TONE[change.kind]
788
824
  ),
789
- children: summarizeStateChanges([change])[0]
825
+ children: (0, import_state_diff.summarizeStateChanges)([change])[0]
790
826
  },
791
827
  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)(
828
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
829
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
794
830
  "details",
795
831
  {
796
832
  open: firstCapture && json.length <= 240,
797
833
  className: cn(firstCapture ? "mt-1.5" : "mt-1"),
798
834
  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 })
835
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
836
+ /* @__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
837
  ]
802
838
  }
803
839
  )
@@ -805,17 +841,17 @@ function StateCardView({ card }) {
805
841
  }
806
842
 
807
843
  // src/components/ReportAttachments.tsx
808
- var import_jsx_runtime20 = require("react/jsx-runtime");
844
+ var import_jsx_runtime21 = require("react/jsx-runtime");
809
845
  function ReportAttachments({ attachments }) {
810
846
  if (attachments.length === 0) return null;
811
847
  return (
812
848
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
813
849
  // 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) => {
850
+ /* @__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
851
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
816
852
  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)(
853
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
819
855
  "img",
820
856
  {
821
857
  src: dataUri,
@@ -824,8 +860,8 @@ function ReportAttachments({ attachments }) {
824
860
  className: "max-w-full rounded-md border border-border"
825
861
  }
826
862
  ),
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)(
863
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
864
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
829
865
  "a",
830
866
  {
831
867
  href: dataUri,
@@ -834,7 +870,7 @@ function ReportAttachments({ attachments }) {
834
870
  children: [
835
871
  att.name,
836
872
  " ",
837
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-muted-foreground", children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-muted-foreground", children: [
838
874
  "(",
839
875
  att.mediaType,
840
876
  ")"
@@ -846,6 +882,9 @@ function ReportAttachments({ attachments }) {
846
882
  );
847
883
  }
848
884
 
885
+ // src/components/ReportTrace.tsx
886
+ var import_duration2 = require("executable-stories-core/utils/duration");
887
+
849
888
  // src/lib/trace.ts
850
889
  var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
851
890
  var safeStatus = (s) => VALID.has(s) ? s : "unset";
@@ -898,15 +937,15 @@ function toTraceRows(spans) {
898
937
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
899
938
  const rows = [];
900
939
  const seen = /* @__PURE__ */ new Set();
901
- const walk2 = (node, depth) => {
940
+ const walk = (node, depth) => {
902
941
  if (seen.has(node.span.spanId)) return;
903
942
  seen.add(node.span.spanId);
904
943
  rows.push({ ...node.span, depth });
905
- for (const child of node.children) walk2(child, depth + 1);
944
+ for (const child of node.children) walk(child, depth + 1);
906
945
  };
907
- for (const root of roots) walk2(root, 0);
946
+ for (const root of roots) walk(root, 0);
908
947
  for (const node of byId.values()) {
909
- if (!seen.has(node.span.spanId)) walk2(node, 0);
948
+ if (!seen.has(node.span.spanId)) walk(node, 0);
910
949
  }
911
950
  return rows;
912
951
  }
@@ -922,7 +961,7 @@ function traceWindow(rows) {
922
961
  }
923
962
 
924
963
  // src/components/ReportTrace.tsx
925
- var import_jsx_runtime21 = require("react/jsx-runtime");
964
+ var import_jsx_runtime22 = require("react/jsx-runtime");
926
965
  var BAR_COLOR = {
927
966
  ok: "bg-pass",
928
967
  error: "bg-fail",
@@ -933,21 +972,21 @@ function ReportTrace({ spans }) {
933
972
  const rows = toTraceRows(spans);
934
973
  if (rows.length === 0) return null;
935
974
  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: [
975
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
976
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
938
977
  "Trace (",
939
978
  rows.length,
940
979
  " span",
941
980
  rows.length === 1 ? "" : "s",
942
981
  ", ",
943
- formatDuration(totalMs),
982
+ (0, import_duration2.formatDuration)(totalMs),
944
983
  ")"
945
984
  ] }),
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) => {
985
+ /* @__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
986
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
948
987
  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)(
988
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("li", { className: "flex items-center gap-2", children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
951
990
  "span",
952
991
  {
953
992
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -956,14 +995,14 @@ function ReportTrace({ spans }) {
956
995
  children: row.name
957
996
  }
958
997
  ),
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)(
998
+ /* @__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
999
  "span",
961
1000
  {
962
1001
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
963
1002
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
964
1003
  }
965
1004
  ) }),
966
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
1005
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: (0, import_duration2.formatDuration)(row.durationMs) })
967
1006
  ] }, `${row.spanId}-${i}`);
968
1007
  }) })
969
1008
  ] });
@@ -1032,7 +1071,7 @@ function diffSinceLastRun(history, scenarios) {
1032
1071
  }
1033
1072
 
1034
1073
  // src/components/ScenarioRunHistory.tsx
1035
- var import_jsx_runtime22 = require("react/jsx-runtime");
1074
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1036
1075
  var MAX_DOTS = 10;
1037
1076
  var DOT_COLOR = {
1038
1077
  passed: "bg-pass",
@@ -1052,16 +1091,16 @@ function ScenarioRunHistory({ scenarioId }) {
1052
1091
  const recent = entries.slice(-MAX_DOTS);
1053
1092
  const flaky = flakinessOf(recent) === "flaky";
1054
1093
  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)(
1094
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
1095
+ flaky ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1096
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1058
1097
  "span",
1059
1098
  {
1060
1099
  role: "img",
1061
1100
  "aria-label": `Run history: ${summary}`,
1062
1101
  title: summary,
1063
1102
  className: "flex shrink-0 items-center gap-[3px]",
1064
- children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1103
+ children: recent.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1065
1104
  "span",
1066
1105
  {
1067
1106
  "aria-hidden": true,
@@ -1139,130 +1178,14 @@ function useCollapseState(computeDefault) {
1139
1178
 
1140
1179
  // src/interactive/scenario-actions.tsx
1141
1180
  var import_react7 = require("react");
1142
-
1143
- // ../executable-stories-core/dist/utils/scenario-markdown.js
1144
- function cell(value) {
1145
- return value.replaceAll("|", "\\|").replaceAll("\n", " ");
1146
- }
1147
- function stringify(value) {
1148
- if (typeof value === "string") return value;
1149
- try {
1150
- return JSON.stringify(value, null, 2) ?? String(value);
1151
- } catch {
1152
- return String(value);
1153
- }
1154
- }
1155
- function docEntryToMarkdown(entry, stateLanes) {
1156
- const lines = [];
1157
- switch (entry.kind) {
1158
- case "note":
1159
- lines.push(entry.text);
1160
- break;
1161
- case "tag":
1162
- lines.push(entry.names.map((n) => `\`@${n}\``).join(" "));
1163
- break;
1164
- case "kv":
1165
- lines.push(`**${entry.label}:** ${stringify(entry.value)}`);
1166
- break;
1167
- case "code":
1168
- lines.push(`**${entry.label}**`, "", "```" + (entry.lang ?? ""), entry.content, "```");
1169
- break;
1170
- case "table": {
1171
- lines.push(`**${entry.label}**`, "");
1172
- lines.push(`| ${entry.columns.map(cell).join(" | ")} |`);
1173
- lines.push(`| ${entry.columns.map(() => "---").join(" | ")} |`);
1174
- for (const row of entry.rows) lines.push(`| ${row.map(cell).join(" | ")} |`);
1175
- break;
1176
- }
1177
- case "link":
1178
- lines.push(`[${entry.label}](${entry.url})`);
1179
- break;
1180
- case "section":
1181
- lines.push(`### ${entry.title}`, "", entry.markdown);
1182
- break;
1183
- case "mermaid":
1184
- if (entry.title) lines.push(`**${entry.title}**`, "");
1185
- lines.push("```mermaid", entry.code, "```");
1186
- break;
1187
- case "screenshot":
1188
- lines.push(`![${entry.alt ?? "screenshot"}](${entry.path})`);
1189
- break;
1190
- case "video":
1191
- lines.push(`Video: ${entry.path}${entry.caption ? ` \u2014 ${entry.caption}` : ""}`);
1192
- break;
1193
- case "html": {
1194
- const htmlTitle = entry.title ?? "Embedded HTML";
1195
- if (entry.content) {
1196
- lines.push(`**${htmlTitle}**`, "", "```html", entry.content, "```");
1197
- } else if (entry.url) {
1198
- lines.push(`[${htmlTitle}](${entry.url})`);
1199
- } else {
1200
- lines.push(`Embedded HTML: ${entry.path ?? "(inline)"}`);
1201
- }
1202
- break;
1203
- }
1204
- case "custom":
1205
- lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
1206
- break;
1207
- case "state": {
1208
- const lane = entry.label ?? "";
1209
- lines.push(`**${entry.label ?? "State"}**`);
1210
- if (stateLanes?.has(lane)) {
1211
- const summary = summarizeStateChanges(diffStateValues(stateLanes.get(lane), entry.value));
1212
- if (summary.length > 0) lines.push("", ...summary.map((l) => `- ${l}`));
1213
- }
1214
- stateLanes?.set(lane, entry.value);
1215
- lines.push("", "<details>", "<summary>snapshot</summary>", "", "```json", stringify(entry.value ?? null), "```", "", "</details>");
1216
- break;
1217
- }
1218
- }
1219
- for (const child of entry.children ?? []) {
1220
- lines.push("", ...docEntryToMarkdown(child, stateLanes));
1221
- }
1222
- return lines;
1223
- }
1224
- function stepLines(step, ordinal, full, stateLanes) {
1225
- const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
1226
- const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
1227
- if (!full) return lines;
1228
- if (step.errorMessage) {
1229
- lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
1230
- }
1231
- for (const entry of step.docEntries) {
1232
- lines.push("", ...docEntryToMarkdown(entry, stateLanes).map((l) => l === "" ? l : ` ${l}`));
1233
- }
1234
- return lines;
1235
- }
1236
- function scenarioToMarkdown(scenario, options = {}) {
1237
- const { variant = "full", meta = [] } = options;
1238
- const full = variant === "full";
1239
- const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
1240
- if (full && meta.length > 0) lines.push(...meta);
1241
- const stateLanes = /* @__PURE__ */ new Map();
1242
- if (scenario.steps.length > 0) {
1243
- if (full) lines.push("", "## Steps", "");
1244
- scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full, stateLanes)));
1245
- }
1246
- if (scenario.errorMessage) {
1247
- if (full) lines.push("", "## Failure", "");
1248
- else lines.push("");
1249
- lines.push("```", scenario.errorMessage.trim(), "```");
1250
- }
1251
- if (full && scenario.docEntries.length > 0) {
1252
- lines.push("", "## Docs", "");
1253
- for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry, stateLanes), "");
1254
- }
1255
- return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
1256
- }
1257
-
1258
- // src/interactive/scenario-actions.tsx
1181
+ var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
1259
1182
  var ScenarioActionsContext = (0, import_react7.createContext)(null);
1260
1183
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1261
1184
  function useScenarioActions() {
1262
1185
  return (0, import_react7.useContext)(ScenarioActionsContext);
1263
1186
  }
1264
- function scenarioToMarkdown2(scenario) {
1265
- return scenarioToMarkdown(scenario, { variant: "compact" });
1187
+ function scenarioToMarkdown(scenario) {
1188
+ return (0, import_scenario_markdown.scenarioToMarkdown)(scenario, { variant: "compact" });
1266
1189
  }
1267
1190
  function scenarioToPrompt(scenario) {
1268
1191
  const lines = [
@@ -1290,9 +1213,9 @@ function scenarioPermalink(scenario) {
1290
1213
 
1291
1214
  // src/components/ui/card.tsx
1292
1215
  var React2 = require("react");
1293
- var import_jsx_runtime23 = require("react/jsx-runtime");
1216
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1294
1217
  function Card({ className, ...props }) {
1295
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1218
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1296
1219
  "div",
1297
1220
  {
1298
1221
  "data-slot": "card",
@@ -1305,7 +1228,7 @@ function Card({ className, ...props }) {
1305
1228
  );
1306
1229
  }
1307
1230
  function CardHeader({ className, ...props }) {
1308
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1231
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1309
1232
  "div",
1310
1233
  {
1311
1234
  "data-slot": "card-header",
@@ -1318,7 +1241,7 @@ function CardHeader({ className, ...props }) {
1318
1241
  );
1319
1242
  }
1320
1243
  function CardContent({ className, ...props }) {
1321
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1322
1245
  "div",
1323
1246
  {
1324
1247
  "data-slot": "card-content",
@@ -1332,13 +1255,13 @@ function CardContent({ className, ...props }) {
1332
1255
  var React3 = require("react");
1333
1256
  var import_menu = require("@base-ui/react/menu");
1334
1257
  var import_lucide_react = require("lucide-react");
1335
- var import_jsx_runtime24 = require("react/jsx-runtime");
1258
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1336
1259
  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
1260
  function DropdownMenu(props) {
1338
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1261
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
1339
1262
  }
1340
1263
  function DropdownMenuTrigger(props) {
1341
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1264
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1342
1265
  }
1343
1266
  function DropdownMenuContent({
1344
1267
  className,
@@ -1346,14 +1269,14 @@ function DropdownMenuContent({
1346
1269
  sideOffset = 4,
1347
1270
  ...props
1348
1271
  }) {
1349
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1272
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1350
1273
  import_menu.Menu.Positioner,
1351
1274
  {
1352
1275
  "data-slot": "dropdown-menu-positioner",
1353
1276
  className: "z-50",
1354
1277
  align,
1355
1278
  sideOffset,
1356
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1279
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1357
1280
  import_menu.Menu.Popup,
1358
1281
  {
1359
1282
  "data-slot": "dropdown-menu-content",
@@ -1371,7 +1294,7 @@ function DropdownMenuItem({
1371
1294
  className,
1372
1295
  ...props
1373
1296
  }) {
1374
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1297
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1375
1298
  import_menu.Menu.Item,
1376
1299
  {
1377
1300
  "data-slot": "dropdown-menu-item",
@@ -1383,7 +1306,7 @@ function DropdownMenuItem({
1383
1306
 
1384
1307
  // src/components/ReportScenario.tsx
1385
1308
  var import_lucide_react2 = require("lucide-react");
1386
- var import_jsx_runtime25 = require("react/jsx-runtime");
1309
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1387
1310
  var STATUS_META = {
1388
1311
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1389
1312
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1399,7 +1322,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1399
1322
  const collapsible = collapse !== null && !hideTitle;
1400
1323
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1401
1324
  const actions = useScenarioActions();
1402
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1325
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1403
1326
  Card,
1404
1327
  {
1405
1328
  id: scenario.id,
@@ -1407,10 +1330,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1407
1330
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1408
1331
  className: "gap-0 py-0",
1409
1332
  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)(
1333
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardHeader, { className: "px-4 py-3", children: [
1334
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1335
+ hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1336
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1414
1337
  "button",
1415
1338
  {
1416
1339
  type: "button",
@@ -1419,49 +1342,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1419
1342
  "aria-controls": bodyId,
1420
1343
  "aria-label": `Toggle ${scenario.title}`,
1421
1344
  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" })
1345
+ 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
1346
  }
1424
1347
  ) : 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 })
1348
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: scenario.title })
1427
1350
  ] }),
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)(
1351
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1352
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
1353
+ scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
1354
+ 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}` }),
1355
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenu, { children: [
1356
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1434
1357
  DropdownMenuTrigger,
1435
1358
  {
1436
1359
  "aria-label": `Actions for ${scenario.title}`,
1437
1360
  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 })
1361
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
1439
1362
  }
1440
1363
  ),
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, {}),
1364
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuContent, { align: "end", children: [
1365
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1366
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.LinkIcon, {}),
1444
1367
  " Copy link"
1445
1368
  ] }),
1446
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1447
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.FileTextIcon, {}),
1369
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1370
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.FileTextIcon, {}),
1448
1371
  " Copy as Markdown"
1449
1372
  ] }),
1450
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1451
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react2.SparklesIcon, {}),
1373
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1374
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react2.SparklesIcon, {}),
1452
1375
  " Explain with AI"
1453
1376
  ] })
1454
1377
  ] })
1455
1378
  ] }) : null
1456
1379
  ] })
1457
1380
  ] }),
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}`))
1381
+ 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: [
1382
+ 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}`)),
1383
+ (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
1384
  ] }) : null
1462
1385
  ] }),
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)(
1386
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1387
+ scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1465
1388
  "pre",
1466
1389
  {
1467
1390
  role: "alert",
@@ -1469,11 +1392,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1469
1392
  children: scenario.errorMessage
1470
1393
  }
1471
1394
  ) : 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 })
1395
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportStoryboard, { scenario }),
1396
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportSteps, { scenario }),
1397
+ 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,
1398
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportAttachments, { attachments: scenario.attachments }),
1399
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ReportTrace, { spans: scenario.otelSpans })
1477
1400
  ] })
1478
1401
  ]
1479
1402
  }
@@ -1481,13 +1404,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1481
1404
  }
1482
1405
 
1483
1406
  // src/components/ReportScenarioList.tsx
1484
- var import_jsx_runtime26 = require("react/jsx-runtime");
1407
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1485
1408
  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)) });
1409
+ 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
1410
  }
1488
1411
 
1489
1412
  // src/components/ReportFeature.tsx
1490
- var import_jsx_runtime27 = require("react/jsx-runtime");
1413
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1491
1414
  function ReportFeature({ feature }) {
1492
1415
  const titleId = `${feature.id}-title`;
1493
1416
  const bodyId = `${feature.id}-body`;
@@ -1498,7 +1421,7 @@ function ReportFeature({ feature }) {
1498
1421
  return (
1499
1422
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1500
1423
  // layer of nesting so the scenario cards below read as the primary unit.
1501
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1424
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1502
1425
  "section",
1503
1426
  {
1504
1427
  id: feature.id,
@@ -1506,9 +1429,9 @@ function ReportFeature({ feature }) {
1506
1429
  "aria-labelledby": titleId,
1507
1430
  className: "flex flex-col gap-3",
1508
1431
  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)(
1432
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "min-w-0", children: [
1434
+ /* @__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
1435
  "button",
1513
1436
  {
1514
1437
  type: "button",
@@ -1517,31 +1440,31 @@ function ReportFeature({ feature }) {
1517
1440
  "aria-controls": bodyId,
1518
1441
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1519
1442
  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 })
1443
+ /* @__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" }),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate", children: feature.title })
1522
1445
  ]
1523
1446
  }
1524
1447
  ) : 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 })
1448
+ /* @__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
1449
  ] }),
1527
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1450
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1528
1451
  "p",
1529
1452
  {
1530
1453
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1531
1454
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1532
1455
  children: [
1533
- /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { children: [
1456
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1534
1457
  s.passed,
1535
1458
  " passed"
1536
1459
  ] }),
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: [
1460
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1539
1462
  s.failed,
1540
1463
  " failed"
1541
1464
  ] }),
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: [
1465
+ skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
1467
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { children: [
1545
1468
  skipped,
1546
1469
  " skipped"
1547
1470
  ] })
@@ -1550,7 +1473,7 @@ function ReportFeature({ feature }) {
1550
1473
  }
1551
1474
  )
1552
1475
  ] }),
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 }) })
1476
+ /* @__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
1477
  ]
1555
1478
  }
1556
1479
  )
@@ -1558,17 +1481,17 @@ function ReportFeature({ feature }) {
1558
1481
  }
1559
1482
 
1560
1483
  // src/components/ReportFeatureList.tsx
1561
- var import_jsx_runtime28 = require("react/jsx-runtime");
1484
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1562
1485
  function ReportFeatureList() {
1563
1486
  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)) });
1487
+ 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
1488
  }
1566
1489
 
1567
1490
  // src/components/ui/empty.tsx
1568
1491
  var import_class_variance_authority2 = require("class-variance-authority");
1569
- var import_jsx_runtime29 = require("react/jsx-runtime");
1492
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1570
1493
  function Empty({ className, ...props }) {
1571
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1494
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1572
1495
  "div",
1573
1496
  {
1574
1497
  "data-slot": "empty",
@@ -1581,7 +1504,7 @@ function Empty({ className, ...props }) {
1581
1504
  );
1582
1505
  }
1583
1506
  function EmptyHeader({ className, ...props }) {
1584
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1507
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1585
1508
  "div",
1586
1509
  {
1587
1510
  "data-slot": "empty-header",
@@ -1608,7 +1531,7 @@ var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
1608
1531
  }
1609
1532
  );
1610
1533
  function EmptyTitle({ className, ...props }) {
1611
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1534
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1612
1535
  "div",
1613
1536
  {
1614
1537
  "data-slot": "empty-title",
@@ -1619,16 +1542,16 @@ function EmptyTitle({ className, ...props }) {
1619
1542
  }
1620
1543
 
1621
1544
  // src/components/ReportEmpty.tsx
1622
- var import_jsx_runtime30 = require("react/jsx-runtime");
1545
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1623
1546
  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." }) }) });
1547
+ 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
1548
  }
1626
1549
 
1627
1550
  // src/components/ReportSummary.tsx
1628
- var import_jsx_runtime31 = require("react/jsx-runtime");
1551
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1629
1552
  function ReportSummary({ className }) {
1630
1553
  const report = useReport();
1631
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1554
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1632
1555
  ReportSummaryView,
1633
1556
  {
1634
1557
  summary: report.summary,
@@ -1654,7 +1577,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1654
1577
  if (summary.pending > 0) {
1655
1578
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1656
1579
  }
1657
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1580
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1658
1581
  "dl",
1659
1582
  {
1660
1583
  className: cn(
@@ -1667,17 +1590,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1667
1590
  "aria-label": ariaLabel,
1668
1591
  children: cards.map((c) => {
1669
1592
  const tone = TONE[c.status];
1670
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1593
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1671
1594
  "div",
1672
1595
  {
1673
1596
  "data-status": c.status,
1674
1597
  className: "rounded-lg border bg-card px-4 py-3",
1675
1598
  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" }),
1599
+ /* @__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: [
1600
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1678
1601
  c.label
1679
1602
  ] }),
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 })
1603
+ /* @__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
1604
  ]
1682
1605
  },
1683
1606
  c.status
@@ -1687,6 +1610,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1687
1610
  );
1688
1611
  }
1689
1612
 
1613
+ // src/components/ReportMeta.tsx
1614
+ var import_duration4 = require("executable-stories-core/utils/duration");
1615
+
1690
1616
  // src/lib/provenance.ts
1691
1617
  function reportLastRunMs(report) {
1692
1618
  if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
@@ -1737,7 +1663,7 @@ function prUrl(ci) {
1737
1663
  }
1738
1664
 
1739
1665
  // src/components/ReportMeta.tsx
1740
- var import_jsx_runtime32 = require("react/jsx-runtime");
1666
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1741
1667
  function ReportMeta() {
1742
1668
  const report = useReport();
1743
1669
  const items = [];
@@ -1746,7 +1672,7 @@ function ReportMeta() {
1746
1672
  items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
1747
1673
  }
1748
1674
  if (report.durationMs) {
1749
- items.push({ label: "Duration", value: formatDuration(report.durationMs) });
1675
+ items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
1750
1676
  }
1751
1677
  if (report.packageVersion) {
1752
1678
  items.push({ label: "Version", value: report.packageVersion });
@@ -1760,13 +1686,13 @@ function ReportMeta() {
1760
1686
  const short = sha.slice(0, 8);
1761
1687
  items.push({
1762
1688
  label: "Commit",
1763
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1689
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1764
1690
  });
1765
1691
  }
1766
1692
  if (report.ci) {
1767
1693
  items.push({
1768
1694
  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: [
1695
+ value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1770
1696
  ciDisplayName(report.ci.name),
1771
1697
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1772
1698
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1777,7 +1703,7 @@ function ReportMeta() {
1777
1703
  const label = `#${report.ci.prNumber}`;
1778
1704
  items.push({
1779
1705
  label: "PR",
1780
- value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1706
+ value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1781
1707
  });
1782
1708
  }
1783
1709
  if (items.length === 0) return null;
@@ -1785,50 +1711,50 @@ function ReportMeta() {
1785
1711
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1786
1712
  // collapsed disclosure so it doesn't push the result down. Native <details>
1787
1713
  // 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" }),
1714
+ /* @__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: [
1715
+ /* @__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: [
1716
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1791
1717
  "Run details"
1792
1718
  ] }),
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 })
1719
+ /* @__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: [
1720
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dt", { className: "font-medium", children: item.label }),
1721
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
1796
1722
  ] }, item.label)) })
1797
1723
  ] })
1798
1724
  );
1799
1725
  }
1800
1726
 
1801
1727
  // src/components/ReportSchemaError.tsx
1802
- var import_jsx_runtime33 = require("react/jsx-runtime");
1728
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1803
1729
  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: [
1730
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("strong", { children: "Report could not be displayed." }) }),
1732
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: error.message }),
1733
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { children: [
1808
1734
  "The report bundle is newer than this version of ",
1809
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("code", { children: "executable-stories-react" }),
1735
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("code", { children: "executable-stories-react" }),
1810
1736
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1811
1737
  ] }) : 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: [
1738
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { children: [
1739
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { children: [
1814
1740
  error.issues.length,
1815
1741
  " validation issue",
1816
1742
  error.issues.length === 1 ? "" : "s"
1817
1743
  ] }),
1818
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1744
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1819
1745
  ] }) : null
1820
1746
  ] });
1821
1747
  }
1822
1748
 
1823
1749
  // src/components/ReportShell.tsx
1824
- var import_jsx_runtime34 = require("react/jsx-runtime");
1750
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1825
1751
  function ReportErrorShell({
1826
1752
  error,
1827
1753
  className,
1828
1754
  title,
1829
1755
  dataTheme
1830
1756
  }) {
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 }) });
1757
+ 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
1758
  }
1833
1759
 
1834
1760
  // src/interactive/ReportInteractive.tsx
@@ -1837,12 +1763,12 @@ var import_lucide_react5 = require("lucide-react");
1837
1763
  // src/components/ui/switch.tsx
1838
1764
  var React4 = require("react");
1839
1765
  var import_switch = require("@base-ui/react/switch");
1840
- var import_jsx_runtime35 = require("react/jsx-runtime");
1766
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1841
1767
  function Switch({
1842
1768
  className,
1843
1769
  ...props
1844
1770
  }) {
1845
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1771
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1846
1772
  import_switch.Switch.Root,
1847
1773
  {
1848
1774
  "data-slot": "switch",
@@ -1851,7 +1777,7 @@ function Switch({
1851
1777
  className
1852
1778
  ),
1853
1779
  ...props,
1854
- children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
1780
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1855
1781
  import_switch.Switch.Thumb,
1856
1782
  {
1857
1783
  "data-slot": "switch-thumb",
@@ -1867,7 +1793,7 @@ var React5 = __toESM(require("react"), 1);
1867
1793
  var import_toggle_group = require("@base-ui/react/toggle-group");
1868
1794
  var import_toggle = require("@base-ui/react/toggle");
1869
1795
  var import_class_variance_authority3 = require("class-variance-authority");
1870
- var import_jsx_runtime36 = require("react/jsx-runtime");
1796
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1871
1797
  var toggleItemVariants = (0, import_class_variance_authority3.cva)(
1872
1798
  "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
1799
  {
@@ -1889,13 +1815,13 @@ function ToggleGroup({
1889
1815
  children,
1890
1816
  ...props
1891
1817
  }) {
1892
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1818
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1893
1819
  import_toggle_group.ToggleGroup,
1894
1820
  {
1895
1821
  "data-slot": "toggle-group",
1896
1822
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1897
1823
  ...props,
1898
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1824
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
1899
1825
  }
1900
1826
  );
1901
1827
  }
@@ -1906,7 +1832,7 @@ function ToggleGroupItem({
1906
1832
  ...props
1907
1833
  }) {
1908
1834
  const context = React5.useContext(ToggleGroupContext);
1909
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
1835
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1910
1836
  import_toggle.Toggle,
1911
1837
  {
1912
1838
  "data-slot": "toggle-group-item",
@@ -1923,9 +1849,9 @@ var import_lucide_react3 = require("lucide-react");
1923
1849
 
1924
1850
  // src/components/ui/input.tsx
1925
1851
  var React6 = require("react");
1926
- var import_jsx_runtime37 = require("react/jsx-runtime");
1852
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1927
1853
  function Input({ className, type, ...props }) {
1928
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1854
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1929
1855
  "input",
1930
1856
  {
1931
1857
  type,
@@ -1942,7 +1868,7 @@ function Input({ className, type, ...props }) {
1942
1868
  }
1943
1869
 
1944
1870
  // src/interactive/ReportSearch.tsx
1945
- var import_jsx_runtime38 = require("react/jsx-runtime");
1871
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1946
1872
  var ReportSearch = (0, import_react8.forwardRef)(
1947
1873
  function ReportSearch2(props, ref) {
1948
1874
  const {
@@ -1964,17 +1890,17 @@ var ReportSearch = (0, import_react8.forwardRef)(
1964
1890
  e.preventDefault();
1965
1891
  }
1966
1892
  }
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)(
1893
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1894
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1895
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "relative w-full sm:w-72", children: [
1896
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1971
1897
  import_lucide_react3.Search,
1972
1898
  {
1973
1899
  "aria-hidden": true,
1974
1900
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
1975
1901
  }
1976
1902
  ),
1977
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
1903
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
1978
1904
  Input,
1979
1905
  {
1980
1906
  ref,
@@ -1991,7 +1917,7 @@ var ReportSearch = (0, import_react8.forwardRef)(
1991
1917
  }
1992
1918
  )
1993
1919
  ] }),
1994
- showCounts ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
1920
+ showCounts ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
1995
1921
  "span",
1996
1922
  {
1997
1923
  className: "text-xs tabular-nums text-muted-foreground",
@@ -2080,7 +2006,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
2080
2006
  }
2081
2007
 
2082
2008
  // src/interactive/ReportFailureBanner.tsx
2083
- var import_jsx_runtime39 = require("react/jsx-runtime");
2009
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2084
2010
  function ReportFailureBanner({ failures }) {
2085
2011
  const first = failures[0];
2086
2012
  const jumpToFirst = (0, import_react9.useCallback)(() => {
@@ -2088,7 +2014,7 @@ function ReportFailureBanner({ failures }) {
2088
2014
  scrollToScenarioId(first.scenarioId);
2089
2015
  }, [first]);
2090
2016
  if (failures.length === 0) return null;
2091
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2017
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2092
2018
  "div",
2093
2019
  {
2094
2020
  className: "es-failure-banner",
@@ -2096,13 +2022,13 @@ function ReportFailureBanner({ failures }) {
2096
2022
  "aria-live": "polite",
2097
2023
  "aria-label": "Failure summary",
2098
2024
  children: [
2099
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "es-failure-banner-text", children: [
2100
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("strong", { children: failures.length }),
2025
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "es-failure-banner-text", children: [
2026
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: failures.length }),
2101
2027
  " ",
2102
2028
  "failure",
2103
2029
  failures.length === 1 ? "" : "s"
2104
2030
  ] }),
2105
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2031
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2106
2032
  "button",
2107
2033
  {
2108
2034
  type: "button",
@@ -2119,7 +2045,7 @@ function ReportFailureBanner({ failures }) {
2119
2045
 
2120
2046
  // src/interactive/ReportFreshness.tsx
2121
2047
  var import_react10 = require("react");
2122
- var import_jsx_runtime40 = require("react/jsx-runtime");
2048
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2123
2049
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2124
2050
  const [now, setNow] = (0, import_react10.useState)(nowMs);
2125
2051
  (0, import_react10.useEffect)(() => {
@@ -2127,7 +2053,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2127
2053
  }, [nowMs]);
2128
2054
  if (lastRunMs === void 0 || now === void 0) return null;
2129
2055
  const age = formatRelativeAge(lastRunMs, now);
2130
- const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2056
+ const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2131
2057
  "a",
2132
2058
  {
2133
2059
  href: ciUrl,
@@ -2138,9 +2064,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2138
2064
  }
2139
2065
  ) : null;
2140
2066
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
2141
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2142
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { children: [
2143
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("strong", { children: [
2067
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2068
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2069
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("strong", { children: [
2144
2070
  "Last verified ",
2145
2071
  age,
2146
2072
  "."
@@ -2150,10 +2076,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2150
2076
  runLink
2151
2077
  ] });
2152
2078
  }
2153
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2079
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2154
2080
  "Verified ",
2155
2081
  age,
2156
- runLink ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2082
+ runLink ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2157
2083
  " \xB7 ",
2158
2084
  runLink
2159
2085
  ] }) : null
@@ -2161,15 +2087,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2161
2087
  }
2162
2088
 
2163
2089
  // src/interactive/ReportLastRunDelta.tsx
2164
- var import_jsx_runtime41 = require("react/jsx-runtime");
2090
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2165
2091
  var MAX_LINKS = 5;
2166
2092
  function ScenarioLinks({ scenarios, tone }) {
2167
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2168
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2093
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2094
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2169
2095
  i > 0 ? ", " : "",
2170
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2096
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2171
2097
  ] }, s.id)),
2172
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2098
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2173
2099
  " and ",
2174
2100
  scenarios.length - MAX_LINKS,
2175
2101
  " more"
@@ -2186,23 +2112,23 @@ function ReportLastRunDelta({
2186
2112
  if (!delta) return null;
2187
2113
  const { newlyFailing, newlyPassing, added } = delta;
2188
2114
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
2189
- 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." });
2115
+ 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." });
2190
2116
  }
2191
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2192
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2193
- newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-fail", children: [
2117
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2118
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2119
+ newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2 text-fail", children: [
2194
2120
  newlyFailing.length,
2195
2121
  " newly failing (",
2196
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2122
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2197
2123
  ")"
2198
2124
  ] }) : null,
2199
- newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2 text-pass", children: [
2125
+ newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2 text-pass", children: [
2200
2126
  newlyPassing.length,
2201
2127
  " fixed (",
2202
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2128
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2203
2129
  ")"
2204
2130
  ] }) : null,
2205
- added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "ml-2", children: [
2131
+ added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "ml-2", children: [
2206
2132
  added.length,
2207
2133
  " new"
2208
2134
  ] }) : null
@@ -2266,7 +2192,7 @@ function Button({
2266
2192
  }
2267
2193
 
2268
2194
  // src/interactive/ReportShortcutsHelp.tsx
2269
- var import_jsx_runtime42 = require("react/jsx-runtime");
2195
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2270
2196
  var SHORTCUTS = [
2271
2197
  { keys: "/", description: "Focus search" },
2272
2198
  { keys: "f", description: "Jump to next failure" },
@@ -2278,9 +2204,9 @@ var SHORTCUTS = [
2278
2204
  ];
2279
2205
  function Keys({ keys }) {
2280
2206
  const parts = keys.split(" ");
2281
- 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: [
2282
- i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2283
- /* @__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 })
2207
+ 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: [
2208
+ i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2209
+ /* @__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 })
2284
2210
  ] }, k)) });
2285
2211
  }
2286
2212
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2291,7 +2217,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2291
2217
  if (open && !dialog.open) dialog.showModal();
2292
2218
  else if (!open && dialog.open) dialog.close();
2293
2219
  }, [open]);
2294
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2220
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2295
2221
  "dialog",
2296
2222
  {
2297
2223
  ref: dialogRef,
@@ -2299,12 +2225,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2299
2225
  "aria-label": "Keyboard shortcuts",
2300
2226
  onClose,
2301
2227
  children: [
2302
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2303
- /* @__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: [
2304
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Keys, { keys: s.keys }) }),
2305
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2228
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2229
+ /* @__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: [
2230
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Keys, { keys: s.keys }) }),
2231
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
2306
2232
  ] }, s.keys)) }),
2307
- /* @__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" }) })
2233
+ /* @__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" }) })
2308
2234
  ]
2309
2235
  }
2310
2236
  );
@@ -2507,7 +2433,7 @@ function failuresFirst(report) {
2507
2433
 
2508
2434
  // src/interactive/ReportFilters.tsx
2509
2435
  var import_react14 = require("react");
2510
- var import_jsx_runtime43 = require("react/jsx-runtime");
2436
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2511
2437
  var TAG_LIMIT = 12;
2512
2438
  function ReportFilters({
2513
2439
  statuses,
@@ -2521,10 +2447,10 @@ function ReportFilters({
2521
2447
  if (statuses.length === 0 && tags.length === 0) return null;
2522
2448
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2523
2449
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2524
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-col gap-2", children: [
2525
- 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) => {
2450
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-col gap-2", children: [
2451
+ 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) => {
2526
2452
  const active = status === opt.key;
2527
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2453
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2528
2454
  "button",
2529
2455
  {
2530
2456
  type: "button",
@@ -2536,23 +2462,23 @@ function ReportFilters({
2536
2462
  ),
2537
2463
  children: [
2538
2464
  opt.label,
2539
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2465
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2540
2466
  ]
2541
2467
  },
2542
2468
  opt.key
2543
2469
  );
2544
2470
  }) }) : null,
2545
- 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: [
2471
+ 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: [
2546
2472
  visibleTags.map((tag) => {
2547
2473
  const active = activeTags.includes(tag);
2548
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2474
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2549
2475
  "button",
2550
2476
  {
2551
2477
  type: "button",
2552
2478
  "aria-pressed": active,
2553
2479
  onClick: () => onToggleTag(tag),
2554
2480
  className: "cursor-pointer",
2555
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2481
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2556
2482
  Badge,
2557
2483
  {
2558
2484
  variant: "tag",
@@ -2565,7 +2491,7 @@ function ReportFilters({
2565
2491
  }
2566
2492
  ) }, tag);
2567
2493
  }),
2568
- tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2494
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2569
2495
  "button",
2570
2496
  {
2571
2497
  type: "button",
@@ -2581,7 +2507,7 @@ function ReportFilters({
2581
2507
 
2582
2508
  // src/interactive/ReportToc.tsx
2583
2509
  var import_react15 = require("react");
2584
- var import_jsx_runtime44 = require("react/jsx-runtime");
2510
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2585
2511
  var DOT = {
2586
2512
  passed: "bg-pass",
2587
2513
  failed: "bg-fail",
@@ -2610,11 +2536,11 @@ function TocContent({ onNavigate }) {
2610
2536
  return () => observer.disconnect();
2611
2537
  }, [scenarioIds]);
2612
2538
  if (report.features.length === 0) return null;
2613
- 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: [
2614
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2615
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2539
+ 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: [
2540
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2541
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2616
2542
  const active = activeId === scenario.id;
2617
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2543
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2618
2544
  "a",
2619
2545
  {
2620
2546
  href: `#${scenario.id}`,
@@ -2629,14 +2555,14 @@ function TocContent({ onNavigate }) {
2629
2555
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2630
2556
  ),
2631
2557
  children: [
2632
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2558
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2633
2559
  "span",
2634
2560
  {
2635
2561
  "aria-hidden": true,
2636
2562
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2637
2563
  }
2638
2564
  ),
2639
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "break-words", children: scenario.title })
2565
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "break-words", children: scenario.title })
2640
2566
  ]
2641
2567
  }
2642
2568
  ) }, scenario.id);
@@ -2646,14 +2572,14 @@ function TocContent({ onNavigate }) {
2646
2572
  function ReportToc() {
2647
2573
  const report = useReport();
2648
2574
  if (report.features.length === 0) return null;
2649
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2575
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2650
2576
  "nav",
2651
2577
  {
2652
2578
  "aria-label": "Table of contents",
2653
2579
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2654
2580
  children: [
2655
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2656
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(TocContent, {})
2581
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2582
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, {})
2657
2583
  ]
2658
2584
  }
2659
2585
  );
@@ -2663,31 +2589,31 @@ function ReportToc() {
2663
2589
  var import_react16 = require("react");
2664
2590
  var import_dialog = require("@base-ui/react/dialog");
2665
2591
  var import_lucide_react4 = require("lucide-react");
2666
- var import_jsx_runtime45 = require("react/jsx-runtime");
2592
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2667
2593
  function ReportTocDrawer() {
2668
2594
  const report = useReport();
2669
2595
  const [open, setOpen] = (0, import_react16.useState)(false);
2670
2596
  if (report.features.length === 0) return null;
2671
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2672
- /* @__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: [
2673
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2597
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
2598
+ /* @__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: [
2599
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
2674
2600
  " Contents"
2675
2601
  ] }),
2676
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_dialog.Dialog.Portal, { children: [
2677
- /* @__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" }),
2678
- /* @__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: [
2679
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center justify-between", children: [
2680
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2681
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2602
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_dialog.Dialog.Portal, { children: [
2603
+ /* @__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" }),
2604
+ /* @__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: [
2605
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center justify-between", children: [
2606
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2607
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2682
2608
  import_dialog.Dialog.Close,
2683
2609
  {
2684
2610
  "aria-label": "Close contents",
2685
2611
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2686
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2612
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
2687
2613
  }
2688
2614
  )
2689
2615
  ] }),
2690
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2616
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(TocContent, { onNavigate: () => setOpen(false) })
2691
2617
  ] })
2692
2618
  ] })
2693
2619
  ] });
@@ -2770,14 +2696,14 @@ function useUrlState() {
2770
2696
  }
2771
2697
 
2772
2698
  // src/interactive/ReportInteractive.tsx
2773
- var import_jsx_runtime46 = require("react/jsx-runtime");
2699
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2774
2700
  function ReportInteractive(props) {
2775
2701
  const { report, className, title, dataTheme } = props;
2776
2702
  const result = unwrapReport(report);
2777
2703
  if (!result.ok) {
2778
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2704
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportErrorShell, { error: result.error, className, title, dataTheme });
2779
2705
  }
2780
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportInteractiveView, { ...props, report: result.data });
2706
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportInteractiveView, { ...props, report: result.data });
2781
2707
  }
2782
2708
  var THEME_OPTIONS = [
2783
2709
  { value: "light", label: "Light", Icon: import_lucide_react5.Sun },
@@ -2785,7 +2711,7 @@ var THEME_OPTIONS = [
2785
2711
  { value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
2786
2712
  ];
2787
2713
  function ThemeSegment({ pref, onPref }) {
2788
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2714
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2789
2715
  ToggleGroup,
2790
2716
  {
2791
2717
  size: "sm",
@@ -2795,7 +2721,7 @@ function ThemeSegment({ pref, onPref }) {
2795
2721
  if (next) onPref(next);
2796
2722
  },
2797
2723
  "aria-label": "Color theme",
2798
- 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))
2724
+ 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))
2799
2725
  }
2800
2726
  );
2801
2727
  }
@@ -2872,7 +2798,7 @@ function ReportInteractiveView({
2872
2798
  const scenarioActions = (0, import_react19.useMemo)(
2873
2799
  () => ({
2874
2800
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
2875
- copyMarkdown: (s) => copy(scenarioToMarkdown2(s), "Markdown copied"),
2801
+ copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
2876
2802
  copyPrompt: (s) => copy(scenarioToPrompt(s), "Prompt copied")
2877
2803
  }),
2878
2804
  [copy]
@@ -2926,13 +2852,13 @@ function ReportInteractiveView({
2926
2852
  const totalScenarios = report.summary.total;
2927
2853
  const matchedScenarios = filtered.summary.total;
2928
2854
  const showToc = !hideToc && totalScenarios > 3;
2929
- 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)(
2855
+ 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)(
2930
2856
  ReportRoot,
2931
2857
  {
2932
2858
  report: filtered,
2933
2859
  customRenderers,
2934
2860
  renderers,
2935
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2861
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2936
2862
  "main",
2937
2863
  {
2938
2864
  className: cn("es-report", "es-report-interactive", className),
@@ -2941,8 +2867,8 @@ function ReportInteractiveView({
2941
2867
  "data-theme": dataTheme,
2942
2868
  "data-detail-level": detail,
2943
2869
  children: [
2944
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("header", { className: "es-report-header", children: [
2945
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2870
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("header", { className: "es-report-header", children: [
2871
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
2946
2872
  "div",
2947
2873
  {
2948
2874
  "data-es-navbar": true,
@@ -2951,9 +2877,9 @@ function ReportInteractiveView({
2951
2877
  hideHeader ? "justify-end" : "justify-between"
2952
2878
  ),
2953
2879
  children: [
2954
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2955
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2956
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2880
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2881
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
2882
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2957
2883
  ReportSearch,
2958
2884
  {
2959
2885
  ref: searchRef,
@@ -2963,16 +2889,16 @@ function ReportInteractiveView({
2963
2889
  totalCount: totalScenarios
2964
2890
  }
2965
2891
  ),
2966
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2892
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
2967
2893
  ] })
2968
2894
  ]
2969
2895
  }
2970
2896
  ),
2971
- hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2972
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportSummary, {}),
2973
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportMeta, {})
2897
+ hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2898
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportSummary, {}),
2899
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportMeta, {})
2974
2900
  ] }),
2975
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2901
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2976
2902
  ReportFreshness,
2977
2903
  {
2978
2904
  lastRunMs: reportLastRunMs(report),
@@ -2980,8 +2906,8 @@ function ReportInteractiveView({
2980
2906
  staleAfterDays
2981
2907
  }
2982
2908
  ),
2983
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2984
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2909
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportLastRunDelta, { history: scenarioHistory, report }),
2910
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2985
2911
  ReportFilters,
2986
2912
  {
2987
2913
  statuses: statusOptions,
@@ -2992,14 +2918,14 @@ function ReportInteractiveView({
2992
2918
  onToggleTag: toggleTag
2993
2919
  }
2994
2920
  ),
2995
- /* @__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: [
2996
- showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportTocDrawer, {}) : null,
2997
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
2998
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2999
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2921
+ /* @__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: [
2922
+ showToc ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportTocDrawer, {}) : null,
2923
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
2924
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2925
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
3000
2926
  ] }),
3001
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
3002
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2927
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex items-center gap-2", children: [
2928
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3003
2929
  Switch,
3004
2930
  {
3005
2931
  id: docsId,
@@ -3007,16 +2933,16 @@ function ReportInteractiveView({
3007
2933
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
3008
2934
  }
3009
2935
  ),
3010
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2936
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
3011
2937
  ] })
3012
2938
  ] })
3013
2939
  ] }),
3014
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportFailureBanner, { failures }),
3015
- hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex gap-6", children: [
3016
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportToc, {}),
3017
- /* @__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, {}) })
3018
- ] }) : /* @__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 }),
3019
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2940
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportFailureBanner, { failures }),
2941
+ hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex gap-6", children: [
2942
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportToc, {}),
2943
+ /* @__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, {}) })
2944
+ ] }) : /* @__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 }),
2945
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3020
2946
  "button",
3021
2947
  {
3022
2948
  type: "button",
@@ -3027,8 +2953,8 @@ function ReportInteractiveView({
3027
2953
  children: "?"
3028
2954
  }
3029
2955
  ),
3030
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
3031
- toast ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2956
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2957
+ toast ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3032
2958
  "div",
3033
2959
  {
3034
2960
  role: "status",
@@ -3052,7 +2978,7 @@ var import_react21 = require("react");
3052
2978
 
3053
2979
  // src/components/doc/MermaidView.tsx
3054
2980
  var import_react20 = require("react");
3055
- var import_jsx_runtime47 = require("react/jsx-runtime");
2981
+ var import_jsx_runtime48 = require("react/jsx-runtime");
3056
2982
  function prefersDark(el) {
3057
2983
  const themed = el?.closest("[data-theme]");
3058
2984
  if (themed?.dataset.theme === "dark") return true;
@@ -3106,9 +3032,9 @@ function MermaidView({ entry, load }) {
3106
3032
  };
3107
3033
  }, [entry.code, load]);
3108
3034
  if (svg) {
3109
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("figure", { ref, className: "my-3", children: [
3110
- entry.title ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3111
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3035
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("figure", { ref, className: "my-3", children: [
3036
+ entry.title ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3037
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3112
3038
  "div",
3113
3039
  {
3114
3040
  role: "img",
@@ -3119,8 +3045,8 @@ function MermaidView({ entry, load }) {
3119
3045
  )
3120
3046
  ] });
3121
3047
  }
3122
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
3123
- error ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3048
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
3049
+ error ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3124
3050
  "p",
3125
3051
  {
3126
3052
  role: "alert",
@@ -3132,12 +3058,12 @@ function MermaidView({ entry, load }) {
3132
3058
  ]
3133
3059
  }
3134
3060
  ) : null,
3135
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(MermaidSource, { entry, ref })
3061
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(MermaidSource, { entry, ref })
3136
3062
  ] });
3137
3063
  }
3138
3064
 
3139
3065
  // src/interactive/island-renderers.tsx
3140
- var import_jsx_runtime48 = require("react/jsx-runtime");
3066
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3141
3067
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3142
3068
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3143
3069
  function cachedCdnModule(url) {
@@ -3171,13 +3097,13 @@ function CdnHighlightedCode({ entry }) {
3171
3097
  cancelled = true;
3172
3098
  };
3173
3099
  }, [entry.content, entry.lang]);
3174
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3100
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3175
3101
  "code",
3176
3102
  {
3177
3103
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
3178
3104
  dangerouslySetInnerHTML: { __html: html }
3179
3105
  }
3180
- ) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3106
+ ) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3181
3107
  }
3182
3108
  function buildIslandRenderers(opts) {
3183
3109
  const renderers = {};
@@ -3191,7 +3117,7 @@ function buildIslandRenderers(opts) {
3191
3117
  }
3192
3118
 
3193
3119
  // src/interactive/ReportInteractiveIsland.tsx
3194
- var import_jsx_runtime49 = require("react/jsx-runtime");
3120
+ var import_jsx_runtime50 = require("react/jsx-runtime");
3195
3121
  function ReportInteractiveIsland({
3196
3122
  report,
3197
3123
  title,
@@ -3205,7 +3131,7 @@ function ReportInteractiveIsland({
3205
3131
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
3206
3132
  [syntaxHighlighting, mermaid]
3207
3133
  );
3208
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3134
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3209
3135
  ReportInteractive,
3210
3136
  {
3211
3137
  report,