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.
@@ -26,63 +26,6 @@ import { useMemo } from "react";
26
26
  import { createContext } from "react";
27
27
  var ReportContext = createContext(null);
28
28
 
29
- // src/context/ReportRoot.tsx
30
- import { jsx } from "react/jsx-runtime";
31
- var EMPTY_CUSTOM = {};
32
- var EMPTY_RENDERERS = {};
33
- function ReportRoot({
34
- report,
35
- customRenderers,
36
- renderers,
37
- children
38
- }) {
39
- const value = useMemo(
40
- () => ({
41
- report,
42
- customRenderers: customRenderers ?? EMPTY_CUSTOM,
43
- renderers: renderers ?? EMPTY_RENDERERS
44
- }),
45
- [report, customRenderers, renderers]
46
- );
47
- return /* @__PURE__ */ jsx(ReportContext.Provider, { value, children });
48
- }
49
-
50
- // src/hooks/useReport.ts
51
- import { useContext } from "react";
52
- function useReport() {
53
- const ctx = useContext(ReportContext);
54
- if (!ctx) {
55
- throw new Error(
56
- "useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
57
- );
58
- }
59
- return ctx.report;
60
- }
61
-
62
- // ../executable-stories-core/dist/utils/duration.js
63
- function formatDuration(ms) {
64
- if (ms < 1e3) {
65
- return `${Math.round(ms)} ms`;
66
- }
67
- if (ms < 6e4) {
68
- return `${(ms / 1e3).toFixed(2)} s`;
69
- }
70
- const minutes = Math.floor(ms / 6e4);
71
- const seconds = Math.floor(ms % 6e4 / 1e3);
72
- if (minutes < 60) {
73
- return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
74
- }
75
- const hours = Math.floor(minutes / 60);
76
- const remainingMinutes = minutes % 60;
77
- return `${hours}h ${remainingMinutes}m`;
78
- }
79
-
80
- // src/components/doc/DocNote.tsx
81
- import { jsx as jsx2 } from "react/jsx-runtime";
82
- function DocNote({ entry }) {
83
- return /* @__PURE__ */ jsx2("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
84
- }
85
-
86
29
  // src/components/ui/badge.tsx
87
30
  import "react";
88
31
  import { cva } from "class-variance-authority";
@@ -141,14 +84,237 @@ function Badge({
141
84
  });
142
85
  }
143
86
 
144
- // src/components/doc/DocTag.tsx
87
+ // src/components/doc/NarrativeBlocks.tsx
88
+ import { jsx, jsxs } from "react/jsx-runtime";
89
+ var FILE_TREE_TYPE = "file-tree";
90
+ var DATA_MODEL_TYPE = "data-model";
91
+ function isChangeKind(value) {
92
+ return value === "added" || value === "modified" || value === "removed" || value === "renamed";
93
+ }
94
+ function isRecord(value) {
95
+ return typeof value === "object" && value !== null && !Array.isArray(value);
96
+ }
97
+ function str(value) {
98
+ return typeof value === "string" && value !== "" ? value : void 0;
99
+ }
100
+ function AuthoredMarker({ authored }) {
101
+ if (authored !== "agent") return null;
102
+ return /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
103
+ }
104
+ function BlockFrame({
105
+ title,
106
+ authored,
107
+ children
108
+ }) {
109
+ return /* @__PURE__ */ jsxs("figure", { className: "my-3", children: [
110
+ title || authored === "agent" ? /* @__PURE__ */ jsxs("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
111
+ title ? /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
112
+ /* @__PURE__ */ jsx(AuthoredMarker, { authored })
113
+ ] }) : null,
114
+ children
115
+ ] });
116
+ }
117
+ function ChangeBadge({ change }) {
118
+ return /* @__PURE__ */ jsx(Badge, { variant: "tag", className: "text-[10px]", children: change });
119
+ }
120
+ function parseFileTree(data) {
121
+ if (!isRecord(data) || !Array.isArray(data.files)) return void 0;
122
+ const files = [];
123
+ for (const raw of data.files) {
124
+ if (typeof raw === "string") {
125
+ if (raw) files.push({ path: raw });
126
+ continue;
127
+ }
128
+ if (!isRecord(raw)) continue;
129
+ const path = str(raw.path);
130
+ if (!path) continue;
131
+ files.push({
132
+ path,
133
+ change: isChangeKind(raw.change) ? raw.change : void 0,
134
+ note: str(raw.note)
135
+ });
136
+ }
137
+ if (files.length === 0) return void 0;
138
+ return { title: str(data.title), authored: str(data.authored), files };
139
+ }
140
+ function buildTree(files) {
141
+ const root = { name: "", children: /* @__PURE__ */ new Map() };
142
+ for (const file of files) {
143
+ const segments = file.path.split("/").filter(Boolean);
144
+ let node = root;
145
+ segments.forEach((segment, index) => {
146
+ let child = node.children.get(segment);
147
+ if (!child) {
148
+ child = { name: segment, children: /* @__PURE__ */ new Map() };
149
+ node.children.set(segment, child);
150
+ }
151
+ if (index === segments.length - 1) child.file = file;
152
+ node = child;
153
+ });
154
+ }
155
+ return root;
156
+ }
157
+ function sortedChildren(node) {
158
+ return [...node.children.values()].sort((a, b) => {
159
+ const aDir = a.children.size > 0;
160
+ const bDir = b.children.size > 0;
161
+ if (aDir !== bDir) return aDir ? -1 : 1;
162
+ return a.name.localeCompare(b.name);
163
+ });
164
+ }
165
+ function TreeBranch({ node, depth }) {
166
+ const children = sortedChildren(node);
167
+ return /* @__PURE__ */ jsx("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
168
+ const isDir = child.children.size > 0;
169
+ return /* @__PURE__ */ jsxs("li", { children: [
170
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-2", children: [
171
+ /* @__PURE__ */ jsx(
172
+ "span",
173
+ {
174
+ className: cn(
175
+ "font-mono text-xs",
176
+ isDir ? "text-muted-foreground" : "text-foreground"
177
+ ),
178
+ children: isDir ? `${child.name}/` : child.name
179
+ }
180
+ ),
181
+ child.file?.change ? /* @__PURE__ */ jsx(ChangeBadge, { change: child.file.change }) : null,
182
+ child.file?.note ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
183
+ ] }),
184
+ isDir ? /* @__PURE__ */ jsx(TreeBranch, { node: child, depth: depth + 1 }) : null
185
+ ] }, child.name);
186
+ }) });
187
+ }
188
+ function FileTreeBlock({ entry }) {
189
+ const data = parseFileTree(entry.data);
190
+ if (!data) return /* @__PURE__ */ jsx(UnreadableBlock, { entry });
191
+ return /* @__PURE__ */ jsx(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ jsx("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ jsx(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
192
+ }
193
+ function parseDataModel(data) {
194
+ if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
195
+ const fields = [];
196
+ for (const raw of data.fields) {
197
+ if (!isRecord(raw)) continue;
198
+ const name = str(raw.name);
199
+ if (!name) continue;
200
+ fields.push({
201
+ name,
202
+ type: str(raw.type),
203
+ note: str(raw.note),
204
+ change: isChangeKind(raw.change) ? raw.change : void 0
205
+ });
206
+ }
207
+ if (fields.length === 0) return void 0;
208
+ return {
209
+ title: str(data.title),
210
+ authored: str(data.authored),
211
+ name: str(data.name),
212
+ fields
213
+ };
214
+ }
215
+ function DataModelBlock({ entry }) {
216
+ const data = parseDataModel(entry.data);
217
+ if (!data) return /* @__PURE__ */ jsx(UnreadableBlock, { entry });
218
+ const hasNotes = data.fields.some((f) => f.note);
219
+ return /* @__PURE__ */ jsx(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ jsx("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
220
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "bg-muted/50", children: [
221
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
222
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
223
+ hasNotes ? /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
224
+ ] }) }),
225
+ /* @__PURE__ */ jsx("tbody", { children: data.fields.map((field) => /* @__PURE__ */ jsxs("tr", { children: [
226
+ /* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs("span", { className: "flex flex-wrap items-center gap-2", children: [
227
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-foreground", children: field.name }),
228
+ field.change ? /* @__PURE__ */ jsx(ChangeBadge, { change: field.change }) : null
229
+ ] }) }),
230
+ /* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
231
+ hasNotes ? /* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
232
+ ] }, field.name)) })
233
+ ] }) }) });
234
+ }
235
+ function UnreadableBlock({ entry }) {
236
+ return /* @__PURE__ */ jsxs("div", { className: "my-2", "data-type": entry.type, children: [
237
+ /* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
238
+ entry.type,
239
+ " (unrecognised shape)"
240
+ ] }),
241
+ /* @__PURE__ */ jsx(
242
+ "pre",
243
+ {
244
+ tabIndex: 0,
245
+ className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
246
+ children: safeStringify(entry.data)
247
+ }
248
+ )
249
+ ] });
250
+ }
251
+ function safeStringify(value) {
252
+ try {
253
+ return JSON.stringify(value, null, 2);
254
+ } catch {
255
+ return String(value);
256
+ }
257
+ }
258
+ var narrativeBlockRenderers = {
259
+ [FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ jsx(FileTreeBlock, { entry }),
260
+ [DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ jsx(DataModelBlock, { entry })
261
+ };
262
+
263
+ // src/context/ReportRoot.tsx
264
+ import { jsx as jsx2 } from "react/jsx-runtime";
265
+ var EMPTY_RENDERERS = {};
266
+ function ReportRoot({
267
+ report,
268
+ customRenderers,
269
+ renderers,
270
+ children
271
+ }) {
272
+ const value = useMemo(
273
+ () => ({
274
+ report,
275
+ // Narrative blocks render everywhere the report does (static SSR, the
276
+ // island, Astro) without every host wiring them up. A host renderer for
277
+ // the same type still wins.
278
+ customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
279
+ renderers: renderers ?? EMPTY_RENDERERS
280
+ }),
281
+ [report, customRenderers, renderers]
282
+ );
283
+ return /* @__PURE__ */ jsx2(ReportContext.Provider, { value, children });
284
+ }
285
+
286
+ // src/hooks/useReport.ts
287
+ import { useContext } from "react";
288
+ function useReport() {
289
+ const ctx = useContext(ReportContext);
290
+ if (!ctx) {
291
+ throw new Error(
292
+ "useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
293
+ );
294
+ }
295
+ return ctx.report;
296
+ }
297
+
298
+ // src/components/ReportScenario.tsx
299
+ import { formatDuration as formatDuration3 } from "executable-stories-core/utils/duration";
300
+
301
+ // src/components/ReportSteps.tsx
302
+ import { formatDuration } from "executable-stories-core/utils/duration";
303
+
304
+ // src/components/doc/DocNote.tsx
145
305
  import { jsx as jsx3 } from "react/jsx-runtime";
306
+ function DocNote({ entry }) {
307
+ return /* @__PURE__ */ jsx3("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
308
+ }
309
+
310
+ // src/components/doc/DocTag.tsx
311
+ import { jsx as jsx4 } from "react/jsx-runtime";
146
312
  function DocTag({ entry }) {
147
- return /* @__PURE__ */ jsx3("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(Badge, { variant: "tag", children: n }) }, n)) });
313
+ return /* @__PURE__ */ jsx4("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(Badge, { variant: "tag", children: n }) }, n)) });
148
314
  }
149
315
 
150
316
  // src/components/doc/DocKv.tsx
151
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
317
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
152
318
  function formatValue(value) {
153
319
  if (value === null) return "null";
154
320
  if (typeof value === "string") return value;
@@ -160,19 +326,19 @@ function formatValue(value) {
160
326
  }
161
327
  }
162
328
  function DocKv({ entry }) {
163
- return /* @__PURE__ */ jsxs("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
164
- /* @__PURE__ */ jsx4("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
165
- /* @__PURE__ */ jsx4("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
329
+ return /* @__PURE__ */ jsxs2("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
330
+ /* @__PURE__ */ jsx5("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
331
+ /* @__PURE__ */ jsx5("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
166
332
  ] });
167
333
  }
168
334
 
169
335
  // src/hooks/useRenderers.ts
170
336
  import { useContext as useContext2 } from "react";
171
- var EMPTY_CUSTOM2 = {};
337
+ var EMPTY_CUSTOM = {};
172
338
  var EMPTY_RENDERERS2 = {};
173
339
  function useCustomRenderers() {
174
340
  const ctx = useContext2(ReportContext);
175
- return ctx?.customRenderers ?? EMPTY_CUSTOM2;
341
+ return ctx?.customRenderers ?? EMPTY_CUSTOM;
176
342
  }
177
343
  function useBuiltinRenderers() {
178
344
  const ctx = useContext2(ReportContext);
@@ -180,37 +346,37 @@ function useBuiltinRenderers() {
180
346
  }
181
347
 
182
348
  // src/components/doc/DocCode.tsx
183
- import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
349
+ import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
184
350
  function CodeFigure({ label, children }) {
185
- return /* @__PURE__ */ jsxs2("figure", { className: "my-3", children: [
186
- label ? /* @__PURE__ */ jsx5("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
187
- /* @__PURE__ */ jsx5("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
351
+ return /* @__PURE__ */ jsxs3("figure", { className: "my-3", children: [
352
+ label ? /* @__PURE__ */ jsx6("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
353
+ /* @__PURE__ */ jsx6("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
188
354
  ] });
189
355
  }
190
356
  function DocCode({ entry }) {
191
357
  const renderers = useBuiltinRenderers();
192
358
  if (renderers.code) {
193
- return /* @__PURE__ */ jsx5(Fragment, { children: renderers.code(entry) });
359
+ return /* @__PURE__ */ jsx6(Fragment, { children: renderers.code(entry) });
194
360
  }
195
- return /* @__PURE__ */ jsx5(CodeFigure, { label: entry.label, children: /* @__PURE__ */ jsx5("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
361
+ return /* @__PURE__ */ jsx6(CodeFigure, { label: entry.label, children: /* @__PURE__ */ jsx6("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
196
362
  }
197
363
 
198
364
  // src/components/doc/DocTable.tsx
199
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
365
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
200
366
  function DocTable({ entry }) {
201
- return /* @__PURE__ */ jsxs3("figure", { className: "my-3", children: [
202
- entry.label ? /* @__PURE__ */ jsx6("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
203
- /* @__PURE__ */ jsx6("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs3("table", { className: "w-full border-collapse text-sm", children: [
204
- /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsx6("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ jsx6("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
205
- /* @__PURE__ */ jsx6("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx6("tr", { children: row.map((cell2, j) => /* @__PURE__ */ jsx6("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell2 }, j)) }, i)) })
367
+ return /* @__PURE__ */ jsxs4("figure", { className: "my-3", children: [
368
+ entry.label ? /* @__PURE__ */ jsx7("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
369
+ /* @__PURE__ */ jsx7("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs4("table", { className: "w-full border-collapse text-sm", children: [
370
+ /* @__PURE__ */ jsx7("thead", { children: /* @__PURE__ */ jsx7("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ jsx7("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
371
+ /* @__PURE__ */ jsx7("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx7("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx7("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
206
372
  ] }) })
207
373
  ] });
208
374
  }
209
375
 
210
376
  // src/components/doc/DocLink.tsx
211
- import { jsx as jsx7 } from "react/jsx-runtime";
377
+ import { jsx as jsx8 } from "react/jsx-runtime";
212
378
  function DocLink({ entry }) {
213
- return /* @__PURE__ */ jsx7(
379
+ return /* @__PURE__ */ jsx8(
214
380
  "a",
215
381
  {
216
382
  className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
@@ -225,26 +391,11 @@ function DocLink({ entry }) {
225
391
  // src/components/doc/DocSection.tsx
226
392
  import { marked } from "marked";
227
393
 
228
- // ../executable-stories-core/dist/utils/url.js
229
- function safeUrl(value) {
230
- if (!value) return void 0;
231
- const trimmed = value.trim();
232
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
233
- if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
234
- return trimmed;
235
- }
236
- function safeImageUrl(value) {
237
- if (!value) return void 0;
238
- const trimmed = value.trim();
239
- if (/^data:image\//i.test(trimmed)) return trimmed;
240
- return safeUrl(trimmed);
241
- }
242
- function isLocalFsPath(value) {
243
- return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
244
- }
394
+ // src/lib/url.ts
395
+ import { safeUrl, safeImageUrl, isLocalFsPath } from "executable-stories-core/utils/url";
245
396
 
246
397
  // src/components/doc/DocSection.tsx
247
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
398
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
248
399
  function neutralizeUrl(value) {
249
400
  return safeUrl(value) ?? "#";
250
401
  }
@@ -255,12 +406,12 @@ function safeMarkdownHtml(markdown) {
255
406
  function DocSection({ entry }) {
256
407
  const renderers = useBuiltinRenderers();
257
408
  if (renderers.section) {
258
- return /* @__PURE__ */ jsx8(Fragment2, { children: renderers.section(entry) });
409
+ return /* @__PURE__ */ jsx9(Fragment2, { children: renderers.section(entry) });
259
410
  }
260
411
  const html = safeMarkdownHtml(entry.markdown);
261
- return /* @__PURE__ */ jsxs4("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
262
- entry.title ? /* @__PURE__ */ jsx8("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
263
- /* @__PURE__ */ jsx8(
412
+ return /* @__PURE__ */ jsxs5("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
413
+ entry.title ? /* @__PURE__ */ jsx9("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
414
+ /* @__PURE__ */ jsx9(
264
415
  "div",
265
416
  {
266
417
  className: "es-doc-prose prose prose-sm max-w-none",
@@ -271,11 +422,11 @@ function DocSection({ entry }) {
271
422
  }
272
423
 
273
424
  // src/components/doc/DocMermaid.tsx
274
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
425
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
275
426
  function MermaidSource({ entry, ref }) {
276
- return /* @__PURE__ */ jsxs5("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
277
- entry.title ? /* @__PURE__ */ jsx9("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
278
- /* @__PURE__ */ jsx9(
427
+ return /* @__PURE__ */ jsxs6("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
428
+ entry.title ? /* @__PURE__ */ jsx10("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
429
+ /* @__PURE__ */ jsx10(
279
430
  "pre",
280
431
  {
281
432
  "data-mermaid": true,
@@ -289,24 +440,24 @@ function MermaidSource({ entry, ref }) {
289
440
  function DocMermaid({ entry }) {
290
441
  const renderers = useBuiltinRenderers();
291
442
  if (renderers.mermaid) {
292
- return /* @__PURE__ */ jsx9(Fragment3, { children: renderers.mermaid(entry) });
443
+ return /* @__PURE__ */ jsx10(Fragment3, { children: renderers.mermaid(entry) });
293
444
  }
294
- return /* @__PURE__ */ jsx9(MermaidSource, { entry });
445
+ return /* @__PURE__ */ jsx10(MermaidSource, { entry });
295
446
  }
296
447
 
297
448
  // src/components/doc/DocScreenshot.tsx
298
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
449
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
299
450
  function DocScreenshot({ entry }) {
300
451
  const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
301
452
  if (!src) {
302
- return /* @__PURE__ */ jsxs6("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
303
- /* @__PURE__ */ jsx10("div", { className: "font-medium", children: "Screenshot unavailable" }),
304
- /* @__PURE__ */ jsx10("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
305
- entry.alt ? /* @__PURE__ */ jsx10("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
453
+ return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
454
+ /* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Screenshot unavailable" }),
455
+ /* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
456
+ entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
306
457
  ] });
307
458
  }
308
- return /* @__PURE__ */ jsxs6("figure", { className: "my-3", children: [
309
- /* @__PURE__ */ jsx10(
459
+ return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
460
+ /* @__PURE__ */ jsx11(
310
461
  "img",
311
462
  {
312
463
  src,
@@ -315,24 +466,24 @@ function DocScreenshot({ entry }) {
315
466
  className: "max-w-full rounded-md border border-border"
316
467
  }
317
468
  ),
318
- entry.alt ? /* @__PURE__ */ jsx10("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
469
+ entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
319
470
  ] });
320
471
  }
321
472
 
322
473
  // src/components/doc/DocVideo.tsx
323
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
474
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
324
475
  function DocVideo({ entry }) {
325
476
  if (isLocalFsPath(entry.path)) {
326
- return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
327
- /* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Video unavailable" }),
328
- /* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
329
- entry.caption ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
477
+ return /* @__PURE__ */ jsxs8("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
478
+ /* @__PURE__ */ jsx12("div", { className: "font-medium", children: "Video unavailable" }),
479
+ /* @__PURE__ */ jsx12("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
480
+ entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
330
481
  ] });
331
482
  }
332
483
  const src = safeUrl(entry.path);
333
484
  const poster = safeUrl(entry.poster);
334
- return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
335
- /* @__PURE__ */ jsx11(
485
+ return /* @__PURE__ */ jsxs8("figure", { className: "my-3", children: [
486
+ /* @__PURE__ */ jsx12(
336
487
  "video",
337
488
  {
338
489
  controls: true,
@@ -342,21 +493,21 @@ function DocVideo({ entry }) {
342
493
  className: "max-w-full rounded-md border border-border"
343
494
  }
344
495
  ),
345
- entry.caption ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
496
+ entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
346
497
  ] });
347
498
  }
348
499
 
349
500
  // src/components/doc/DocHtml.tsx
350
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
501
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
351
502
  function DocHtml({ entry }) {
352
503
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
353
504
  const title = entry.title ?? "Embedded HTML";
354
505
  const safeHref = safeUrl(entry.url ?? entry.path);
355
506
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
356
- return /* @__PURE__ */ jsxs8("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
357
- /* @__PURE__ */ jsxs8("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: [
358
- /* @__PURE__ */ jsx12("span", { children: entry.title ?? "HTML" }),
359
- safeHref ? /* @__PURE__ */ jsx12(
507
+ return /* @__PURE__ */ jsxs9("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
508
+ /* @__PURE__ */ jsxs9("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: [
509
+ /* @__PURE__ */ jsx13("span", { children: entry.title ?? "HTML" }),
510
+ safeHref ? /* @__PURE__ */ jsx13(
360
511
  "a",
361
512
  {
362
513
  href: safeHref,
@@ -369,7 +520,7 @@ function DocHtml({ entry }) {
369
520
  }
370
521
  ) : null
371
522
  ] }),
372
- /* @__PURE__ */ jsx12(
523
+ /* @__PURE__ */ jsx13(
373
524
  "iframe",
374
525
  {
375
526
  ...frameProps,
@@ -384,7 +535,7 @@ function DocHtml({ entry }) {
384
535
  }
385
536
 
386
537
  // src/components/doc/DocState.tsx
387
- import { jsx as jsx13 } from "react/jsx-runtime";
538
+ import { jsx as jsx14 } from "react/jsx-runtime";
388
539
  function DocState({ entry }) {
389
540
  let json;
390
541
  try {
@@ -392,23 +543,23 @@ function DocState({ entry }) {
392
543
  } catch {
393
544
  json = String(entry.value);
394
545
  }
395
- return /* @__PURE__ */ jsx13(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx13("code", { className: "font-mono text-xs language-json", children: json }) });
546
+ return /* @__PURE__ */ jsx14(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx14("code", { className: "font-mono text-xs language-json", children: json }) });
396
547
  }
397
548
 
398
549
  // src/components/doc/DocCustom.tsx
399
- import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
550
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
400
551
  function DocCustom({ entry }) {
401
552
  const renderers = useCustomRenderers();
402
553
  const renderer = renderers[entry.type];
403
554
  if (renderer) {
404
- return /* @__PURE__ */ jsx14(Fragment4, { children: renderer(entry) });
555
+ return /* @__PURE__ */ jsx15(Fragment4, { children: renderer(entry) });
405
556
  }
406
- return /* @__PURE__ */ jsxs9("div", { className: "my-2", "data-type": entry.type, children: [
407
- /* @__PURE__ */ jsx14("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
408
- /* @__PURE__ */ jsx14("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
557
+ return /* @__PURE__ */ jsxs10("div", { className: "my-2", "data-type": entry.type, children: [
558
+ /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
559
+ /* @__PURE__ */ jsx15("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify2(entry.data) })
409
560
  ] });
410
561
  }
411
- function safeStringify(value) {
562
+ function safeStringify2(value) {
412
563
  try {
413
564
  return JSON.stringify(value, null, 2);
414
565
  } catch {
@@ -417,47 +568,47 @@ function safeStringify(value) {
417
568
  }
418
569
 
419
570
  // src/components/doc/DocEntry.tsx
420
- import { jsx as jsx15 } from "react/jsx-runtime";
571
+ import { jsx as jsx16 } from "react/jsx-runtime";
421
572
  function DocEntry({ entry }) {
422
573
  switch (entry.kind) {
423
574
  case "note":
424
- return /* @__PURE__ */ jsx15(DocNote, { entry });
575
+ return /* @__PURE__ */ jsx16(DocNote, { entry });
425
576
  case "tag":
426
- return /* @__PURE__ */ jsx15(DocTag, { entry });
577
+ return /* @__PURE__ */ jsx16(DocTag, { entry });
427
578
  case "kv":
428
- return /* @__PURE__ */ jsx15(DocKv, { entry });
579
+ return /* @__PURE__ */ jsx16(DocKv, { entry });
429
580
  case "code":
430
- return /* @__PURE__ */ jsx15(DocCode, { entry });
581
+ return /* @__PURE__ */ jsx16(DocCode, { entry });
431
582
  case "table":
432
- return /* @__PURE__ */ jsx15(DocTable, { entry });
583
+ return /* @__PURE__ */ jsx16(DocTable, { entry });
433
584
  case "link":
434
- return /* @__PURE__ */ jsx15(DocLink, { entry });
585
+ return /* @__PURE__ */ jsx16(DocLink, { entry });
435
586
  case "section":
436
- return /* @__PURE__ */ jsx15(DocSection, { entry });
587
+ return /* @__PURE__ */ jsx16(DocSection, { entry });
437
588
  case "mermaid":
438
- return /* @__PURE__ */ jsx15(DocMermaid, { entry });
589
+ return /* @__PURE__ */ jsx16(DocMermaid, { entry });
439
590
  case "screenshot":
440
- return /* @__PURE__ */ jsx15(DocScreenshot, { entry });
591
+ return /* @__PURE__ */ jsx16(DocScreenshot, { entry });
441
592
  case "video":
442
- return /* @__PURE__ */ jsx15(DocVideo, { entry });
593
+ return /* @__PURE__ */ jsx16(DocVideo, { entry });
443
594
  case "html":
444
- return /* @__PURE__ */ jsx15(DocHtml, { entry });
595
+ return /* @__PURE__ */ jsx16(DocHtml, { entry });
445
596
  case "state":
446
- return /* @__PURE__ */ jsx15(DocState, { entry });
597
+ return /* @__PURE__ */ jsx16(DocState, { entry });
447
598
  case "custom":
448
- return /* @__PURE__ */ jsx15(DocCustom, { entry });
599
+ return /* @__PURE__ */ jsx16(DocCustom, { entry });
449
600
  }
450
601
  }
451
602
 
452
603
  // src/components/ReportDocEntries.tsx
453
- import { Fragment as Fragment5, jsx as jsx16 } from "react/jsx-runtime";
604
+ import { Fragment as Fragment5, jsx as jsx17 } from "react/jsx-runtime";
454
605
  function ReportDocEntries({ entries }) {
455
606
  if (entries.length === 0) return null;
456
- return /* @__PURE__ */ jsx16(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx16(DocEntry, { entry }, i)) });
607
+ return /* @__PURE__ */ jsx17(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx17(DocEntry, { entry }, i)) });
457
608
  }
458
609
 
459
610
  // src/lib/step-params.tsx
460
- import { jsx as jsx17 } from "react/jsx-runtime";
611
+ import { jsx as jsx18 } from "react/jsx-runtime";
461
612
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
462
613
  function highlightStepParams(text) {
463
614
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -469,7 +620,7 @@ function highlightStepParams(text) {
469
620
  const end = start + match[0].length;
470
621
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
471
622
  nodes.push(
472
- /* @__PURE__ */ jsx17("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
623
+ /* @__PURE__ */ jsx18("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
473
624
  );
474
625
  lastIndex = end;
475
626
  });
@@ -478,7 +629,7 @@ function highlightStepParams(text) {
478
629
  }
479
630
 
480
631
  // src/components/ReportSteps.tsx
481
- import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
632
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
482
633
  var STEP_GLYPH = {
483
634
  passed: "\u2713",
484
635
  failed: "\u2717",
@@ -493,21 +644,21 @@ var STEP_GLYPH_COLOR = {
493
644
  };
494
645
  function ReportSteps({ scenario }) {
495
646
  if (scenario.steps.length === 0) return null;
496
- return /* @__PURE__ */ jsx18("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx18(ReportStepItem, { step }, step.id)) });
647
+ return /* @__PURE__ */ jsx19("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx19(ReportStepItem, { step }, step.id)) });
497
648
  }
498
649
  function ReportStepItem({ step }) {
499
650
  const kw = step.keyword.toLowerCase();
500
651
  const continuation = kw === "and" || kw === "but";
501
- return /* @__PURE__ */ jsxs10(
652
+ return /* @__PURE__ */ jsxs11(
502
653
  "li",
503
654
  {
504
655
  id: step.id,
505
656
  "data-status": step.status,
506
657
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
507
658
  children: [
508
- /* @__PURE__ */ jsxs10("div", { className: "flex items-baseline gap-2", children: [
509
- /* @__PURE__ */ jsx18("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" }),
510
- /* @__PURE__ */ jsx18(
659
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-baseline gap-2", children: [
660
+ /* @__PURE__ */ jsx19("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" }),
661
+ /* @__PURE__ */ jsx19(
511
662
  "span",
512
663
  {
513
664
  className: cn(
@@ -517,10 +668,10 @@ function ReportStepItem({ step }) {
517
668
  children: step.keyword
518
669
  }
519
670
  ),
520
- /* @__PURE__ */ jsx18("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
521
- step.durationMs > 0 ? /* @__PURE__ */ jsx18("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
671
+ /* @__PURE__ */ jsx19("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
672
+ step.durationMs > 0 ? /* @__PURE__ */ jsx19("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
522
673
  ] }),
523
- step.errorMessage ? /* @__PURE__ */ jsx18(
674
+ step.errorMessage ? /* @__PURE__ */ jsx19(
524
675
  "pre",
525
676
  {
526
677
  role: "alert",
@@ -528,132 +679,17 @@ function ReportStepItem({ step }) {
528
679
  children: step.errorMessage
529
680
  }
530
681
  ) : null,
531
- step.docEntries.length > 0 ? /* @__PURE__ */ jsx18("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx18(ReportDocEntries, { entries: step.docEntries }) }) : null
682
+ step.docEntries.length > 0 ? /* @__PURE__ */ jsx19("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx19(ReportDocEntries, { entries: step.docEntries }) }) : null
532
683
  ]
533
684
  }
534
685
  );
535
686
  }
536
687
 
537
- // ../executable-stories-core/dist/state-diff.js
538
- var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
539
- function deepEqual(a, b) {
540
- if (Object.is(a, b)) return true;
541
- if (Array.isArray(a) && Array.isArray(b)) {
542
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
543
- }
544
- if (isPlainObject(a) && isPlainObject(b)) {
545
- const ak = Object.keys(a);
546
- const bk = Object.keys(b);
547
- return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
548
- }
549
- return false;
550
- }
551
- var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
552
- function walk(path, before, after, out) {
553
- if (Object.is(before, after)) return;
554
- if (Array.isArray(before) && Array.isArray(after)) {
555
- const shared = Math.min(before.length, after.length);
556
- for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
557
- for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
558
- for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
559
- return;
560
- }
561
- if (isPlainObject(before) && isPlainObject(after)) {
562
- for (const key of Object.keys(after)) {
563
- const p = joinKey(path, key);
564
- if (key in before) walk(p, before[key], after[key], out);
565
- else out.push({ path: p, kind: "added", after: after[key] });
566
- }
567
- for (const key of Object.keys(before)) {
568
- if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
569
- }
570
- return;
571
- }
572
- if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
573
- }
574
- function diffStateValues(before, after) {
575
- const out = [];
576
- walk("", before, after, out);
577
- return out;
578
- }
579
- function formatStateValue(value, maxLen = 60) {
580
- const s = value === void 0 ? "undefined" : JSON.stringify(value);
581
- return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
582
- }
583
- function summarizeStateChanges(changes) {
584
- return changes.map((c) => {
585
- const at = c.path === "" ? "(root)" : c.path;
586
- switch (c.kind) {
587
- case "added":
588
- return `+ ${at}: ${formatStateValue(c.after)}`;
589
- case "removed":
590
- return `\u2212 ${at}`;
591
- case "changed":
592
- return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
593
- }
594
- });
595
- }
596
-
597
- // ../executable-stories-core/dist/storyboard.js
598
- function findScreenshot(entries) {
599
- for (const entry of entries) {
600
- if (entry.kind === "screenshot") return entry;
601
- if (entry.children) {
602
- const nested = findScreenshot(entry.children);
603
- if (nested) return nested;
604
- }
605
- }
606
- return void 0;
607
- }
608
- function findStates(entries, out = []) {
609
- for (const entry of entries) {
610
- if (entry.kind === "state") out.push(entry);
611
- if (entry.children) findStates(entry.children, out);
612
- }
613
- return out;
614
- }
615
- var laneOf = (label) => label ?? "";
616
- function extractStoryboardFrames(scenario) {
617
- const frames = [];
618
- const laneOrder = [];
619
- const previous = /* @__PURE__ */ new Map();
620
- for (const step of scenario.steps) {
621
- const shot = findScreenshot(step.docEntries);
622
- const stateDocs = findStates(step.docEntries);
623
- if (!shot && stateDocs.length === 0) continue;
624
- const byLane = /* @__PURE__ */ new Map();
625
- for (const doc of stateDocs) {
626
- const lane = laneOf(doc.label);
627
- if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
628
- byLane.set(lane, doc);
629
- }
630
- const states = [];
631
- for (const lane of laneOrder) {
632
- const doc = byLane.get(lane);
633
- if (!doc) continue;
634
- const card = {
635
- ...doc.label !== void 0 && { label: doc.label },
636
- value: doc.value
637
- };
638
- if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
639
- previous.set(lane, doc.value);
640
- states.push(card);
641
- }
642
- frames.push({
643
- stepId: step.id,
644
- stepIndex: step.index,
645
- keyword: step.keyword,
646
- text: step.text,
647
- status: step.status,
648
- ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
649
- states
650
- });
651
- }
652
- return frames;
653
- }
654
-
655
688
  // src/components/ReportStoryboard.tsx
656
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
689
+ import { extractStoryboardFrames } from "executable-stories-core/storyboard";
690
+ import { summarizeStateChanges } from "executable-stories-core/state-diff";
691
+ import { isLocalFsPath as isLocalFsPath2, safeImageUrl as safeImageUrl2 } from "executable-stories-core/utils/url";
692
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
657
693
  var FRAME_GLYPH = {
658
694
  passed: "\u2713",
659
695
  failed: "\u2717",
@@ -669,46 +705,46 @@ var FRAME_GLYPH_COLOR = {
669
705
  function ReportStoryboard({ scenario }) {
670
706
  const frames = extractStoryboardFrames(scenario);
671
707
  if (frames.length < 2) return null;
672
- return /* @__PURE__ */ jsxs11("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
673
- /* @__PURE__ */ jsx19("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
674
- /* @__PURE__ */ jsx19("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx19(StoryboardFrameItem, { frame }, frame.stepId)) })
708
+ return /* @__PURE__ */ jsxs12("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
709
+ /* @__PURE__ */ jsx20("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
710
+ /* @__PURE__ */ jsx20("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx20(StoryboardFrameItem, { frame }, frame.stepId)) })
675
711
  ] });
676
712
  }
677
713
  function StoryboardFrameItem({ frame }) {
678
714
  const label = `${frame.keyword} ${frame.text}`;
679
715
  const hasStates = frame.states.length > 0;
680
- const caption = /* @__PURE__ */ jsxs11("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
681
- /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
682
- /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
683
- /* @__PURE__ */ jsx19("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
716
+ const caption = /* @__PURE__ */ jsxs12("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
717
+ /* @__PURE__ */ jsx20("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
718
+ /* @__PURE__ */ jsxs12("span", { className: "min-w-0", children: [
719
+ /* @__PURE__ */ jsx20("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
684
720
  " ",
685
- /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
721
+ /* @__PURE__ */ jsx20("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
686
722
  ] })
687
723
  ] });
688
724
  return (
689
725
  // State cards need room for diff rows; screenshot-only frames keep the
690
726
  // original narrow film-cell width (and DOM) unchanged.
691
- /* @__PURE__ */ jsxs11("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
692
- frame.screenshot ? /* @__PURE__ */ jsx19(FrameScreenshot, { frame, label }) : null,
693
- hasStates ? /* @__PURE__ */ jsx19("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ jsx19(StateCardView, { card }, card.label ?? i)) }) : null,
727
+ /* @__PURE__ */ jsxs12("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
728
+ frame.screenshot ? /* @__PURE__ */ jsx20(FrameScreenshot, { frame, label }) : null,
729
+ hasStates ? /* @__PURE__ */ jsx20("div", { className: cn("flex flex-col gap-1.5", frame.screenshot && "mt-1.5"), children: frame.states.map((card, i) => /* @__PURE__ */ jsx20(StateCardView, { card }, card.label ?? i)) }) : null,
694
730
  frame.screenshot ? caption : (
695
731
  // No screenshot means no image anchor, so the caption carries the
696
732
  // jump-to-step link instead.
697
- /* @__PURE__ */ jsx19("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
733
+ /* @__PURE__ */ jsx20("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
698
734
  )
699
735
  ] })
700
736
  );
701
737
  }
702
738
  function FrameScreenshot({ frame, label }) {
703
739
  const path = frame.screenshot?.path ?? "";
704
- const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
705
- return /* @__PURE__ */ jsx19(
740
+ const src = isLocalFsPath2(path) ? void 0 : safeImageUrl2(path);
741
+ return /* @__PURE__ */ jsx20(
706
742
  "a",
707
743
  {
708
744
  href: `#${frame.stepId}`,
709
745
  "aria-label": `Jump to step: ${label}`,
710
746
  className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
711
- children: src ? /* @__PURE__ */ jsx19(
747
+ children: src ? /* @__PURE__ */ jsx20(
712
748
  "img",
713
749
  {
714
750
  src,
@@ -716,7 +752,7 @@ function FrameScreenshot({ frame, label }) {
716
752
  loading: "lazy",
717
753
  className: "h-24 w-full bg-muted object-cover object-top"
718
754
  }
719
- ) : /* @__PURE__ */ jsx19("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" })
755
+ ) : /* @__PURE__ */ jsx20("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" })
720
756
  }
721
757
  );
722
758
  }
@@ -735,12 +771,12 @@ function prettyJson(value) {
735
771
  function StateCardView({ card }) {
736
772
  const json = prettyJson(card.value);
737
773
  const firstCapture = card.changes === void 0;
738
- return /* @__PURE__ */ jsxs11("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
739
- /* @__PURE__ */ jsxs11("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
740
- /* @__PURE__ */ jsx19("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" }),
741
- firstCapture ? /* @__PURE__ */ jsx19("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
774
+ return /* @__PURE__ */ jsxs12("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
775
+ /* @__PURE__ */ jsxs12("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
776
+ /* @__PURE__ */ jsx20("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" }),
777
+ firstCapture ? /* @__PURE__ */ jsx20("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
742
778
  ] }),
743
- !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ jsx19("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ jsx19(
779
+ !firstCapture && (card.changes && card.changes.length > 0 ? /* @__PURE__ */ jsx20("ul", { className: "mt-1.5 flex flex-col gap-0.5", children: card.changes.map((change, i) => /* @__PURE__ */ jsx20(
744
780
  "li",
745
781
  {
746
782
  className: cn(
@@ -750,15 +786,15 @@ function StateCardView({ card }) {
750
786
  children: summarizeStateChanges([change])[0]
751
787
  },
752
788
  i
753
- )) }) : /* @__PURE__ */ jsx19("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
754
- /* @__PURE__ */ jsxs11(
789
+ )) }) : /* @__PURE__ */ jsx20("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
790
+ /* @__PURE__ */ jsxs12(
755
791
  "details",
756
792
  {
757
793
  open: firstCapture && json.length <= 240,
758
794
  className: cn(firstCapture ? "mt-1.5" : "mt-1"),
759
795
  children: [
760
- /* @__PURE__ */ jsx19("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
761
- /* @__PURE__ */ jsx19("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 })
796
+ /* @__PURE__ */ jsx20("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
797
+ /* @__PURE__ */ jsx20("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 })
762
798
  ]
763
799
  }
764
800
  )
@@ -766,17 +802,17 @@ function StateCardView({ card }) {
766
802
  }
767
803
 
768
804
  // src/components/ReportAttachments.tsx
769
- import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
805
+ import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
770
806
  function ReportAttachments({ attachments }) {
771
807
  if (attachments.length === 0) return null;
772
808
  return (
773
809
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
774
810
  // blocks don't create duplicate "Attachments" region landmarks.
775
- /* @__PURE__ */ jsx20("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
811
+ /* @__PURE__ */ jsx21("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
776
812
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
777
813
  const isImage = att.mediaType.startsWith("image/");
778
- return /* @__PURE__ */ jsx20("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs12(Fragment6, { children: [
779
- /* @__PURE__ */ jsx20(
814
+ return /* @__PURE__ */ jsx21("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
815
+ /* @__PURE__ */ jsx21(
780
816
  "img",
781
817
  {
782
818
  src: dataUri,
@@ -785,8 +821,8 @@ function ReportAttachments({ attachments }) {
785
821
  className: "max-w-full rounded-md border border-border"
786
822
  }
787
823
  ),
788
- /* @__PURE__ */ jsx20("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
789
- ] }) : /* @__PURE__ */ jsxs12(
824
+ /* @__PURE__ */ jsx21("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
825
+ ] }) : /* @__PURE__ */ jsxs13(
790
826
  "a",
791
827
  {
792
828
  href: dataUri,
@@ -795,7 +831,7 @@ function ReportAttachments({ attachments }) {
795
831
  children: [
796
832
  att.name,
797
833
  " ",
798
- /* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
834
+ /* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground", children: [
799
835
  "(",
800
836
  att.mediaType,
801
837
  ")"
@@ -807,6 +843,9 @@ function ReportAttachments({ attachments }) {
807
843
  );
808
844
  }
809
845
 
846
+ // src/components/ReportTrace.tsx
847
+ import { formatDuration as formatDuration2 } from "executable-stories-core/utils/duration";
848
+
810
849
  // src/lib/trace.ts
811
850
  var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
812
851
  var safeStatus = (s) => VALID.has(s) ? s : "unset";
@@ -859,15 +898,15 @@ function toTraceRows(spans) {
859
898
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
860
899
  const rows = [];
861
900
  const seen = /* @__PURE__ */ new Set();
862
- const walk2 = (node, depth) => {
901
+ const walk = (node, depth) => {
863
902
  if (seen.has(node.span.spanId)) return;
864
903
  seen.add(node.span.spanId);
865
904
  rows.push({ ...node.span, depth });
866
- for (const child of node.children) walk2(child, depth + 1);
905
+ for (const child of node.children) walk(child, depth + 1);
867
906
  };
868
- for (const root of roots) walk2(root, 0);
907
+ for (const root of roots) walk(root, 0);
869
908
  for (const node of byId.values()) {
870
- if (!seen.has(node.span.spanId)) walk2(node, 0);
909
+ if (!seen.has(node.span.spanId)) walk(node, 0);
871
910
  }
872
911
  return rows;
873
912
  }
@@ -883,7 +922,7 @@ function traceWindow(rows) {
883
922
  }
884
923
 
885
924
  // src/components/ReportTrace.tsx
886
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
925
+ import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
887
926
  var BAR_COLOR = {
888
927
  ok: "bg-pass",
889
928
  error: "bg-fail",
@@ -894,21 +933,21 @@ function ReportTrace({ spans }) {
894
933
  const rows = toTraceRows(spans);
895
934
  if (rows.length === 0) return null;
896
935
  const { minStartMs, totalMs } = traceWindow(rows);
897
- return /* @__PURE__ */ jsxs13("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
898
- /* @__PURE__ */ jsxs13("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
936
+ return /* @__PURE__ */ jsxs14("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
937
+ /* @__PURE__ */ jsxs14("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
899
938
  "Trace (",
900
939
  rows.length,
901
940
  " span",
902
941
  rows.length === 1 ? "" : "s",
903
942
  ", ",
904
- formatDuration(totalMs),
943
+ formatDuration2(totalMs),
905
944
  ")"
906
945
  ] }),
907
- /* @__PURE__ */ jsx21("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
946
+ /* @__PURE__ */ jsx22("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
908
947
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
909
948
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
910
- return /* @__PURE__ */ jsxs13("li", { className: "flex items-center gap-2", children: [
911
- /* @__PURE__ */ jsx21(
949
+ return /* @__PURE__ */ jsxs14("li", { className: "flex items-center gap-2", children: [
950
+ /* @__PURE__ */ jsx22(
912
951
  "span",
913
952
  {
914
953
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -917,14 +956,14 @@ function ReportTrace({ spans }) {
917
956
  children: row.name
918
957
  }
919
958
  ),
920
- /* @__PURE__ */ jsx21("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx21(
959
+ /* @__PURE__ */ jsx22("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx22(
921
960
  "span",
922
961
  {
923
962
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
924
963
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
925
964
  }
926
965
  ) }),
927
- /* @__PURE__ */ jsx21("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
966
+ /* @__PURE__ */ jsx22("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration2(row.durationMs) })
928
967
  ] }, `${row.spanId}-${i}`);
929
968
  }) })
930
969
  ] });
@@ -993,7 +1032,7 @@ function diffSinceLastRun(history, scenarios) {
993
1032
  }
994
1033
 
995
1034
  // src/components/ScenarioRunHistory.tsx
996
- import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1035
+ import { Fragment as Fragment7, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
997
1036
  var MAX_DOTS = 10;
998
1037
  var DOT_COLOR = {
999
1038
  passed: "bg-pass",
@@ -1013,16 +1052,16 @@ function ScenarioRunHistory({ scenarioId }) {
1013
1052
  const recent = entries.slice(-MAX_DOTS);
1014
1053
  const flaky = flakinessOf(recent) === "flaky";
1015
1054
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
1016
- return /* @__PURE__ */ jsxs14(Fragment7, { children: [
1017
- flaky ? /* @__PURE__ */ jsx22(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1018
- /* @__PURE__ */ jsx22(
1055
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
1056
+ flaky ? /* @__PURE__ */ jsx23(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1057
+ /* @__PURE__ */ jsx23(
1019
1058
  "span",
1020
1059
  {
1021
1060
  role: "img",
1022
1061
  "aria-label": `Run history: ${summary}`,
1023
1062
  title: summary,
1024
1063
  className: "flex shrink-0 items-center gap-[3px]",
1025
- children: recent.map((e, i) => /* @__PURE__ */ jsx22(
1064
+ children: recent.map((e, i) => /* @__PURE__ */ jsx23(
1026
1065
  "span",
1027
1066
  {
1028
1067
  "aria-hidden": true,
@@ -1100,130 +1139,14 @@ function useCollapseState(computeDefault) {
1100
1139
 
1101
1140
  // src/interactive/scenario-actions.tsx
1102
1141
  import { createContext as createContext4, useContext as useContext5 } from "react";
1103
-
1104
- // ../executable-stories-core/dist/utils/scenario-markdown.js
1105
- function cell(value) {
1106
- return value.replaceAll("|", "\\|").replaceAll("\n", " ");
1107
- }
1108
- function stringify(value) {
1109
- if (typeof value === "string") return value;
1110
- try {
1111
- return JSON.stringify(value, null, 2) ?? String(value);
1112
- } catch {
1113
- return String(value);
1114
- }
1115
- }
1116
- function docEntryToMarkdown(entry, stateLanes) {
1117
- const lines = [];
1118
- switch (entry.kind) {
1119
- case "note":
1120
- lines.push(entry.text);
1121
- break;
1122
- case "tag":
1123
- lines.push(entry.names.map((n) => `\`@${n}\``).join(" "));
1124
- break;
1125
- case "kv":
1126
- lines.push(`**${entry.label}:** ${stringify(entry.value)}`);
1127
- break;
1128
- case "code":
1129
- lines.push(`**${entry.label}**`, "", "```" + (entry.lang ?? ""), entry.content, "```");
1130
- break;
1131
- case "table": {
1132
- lines.push(`**${entry.label}**`, "");
1133
- lines.push(`| ${entry.columns.map(cell).join(" | ")} |`);
1134
- lines.push(`| ${entry.columns.map(() => "---").join(" | ")} |`);
1135
- for (const row of entry.rows) lines.push(`| ${row.map(cell).join(" | ")} |`);
1136
- break;
1137
- }
1138
- case "link":
1139
- lines.push(`[${entry.label}](${entry.url})`);
1140
- break;
1141
- case "section":
1142
- lines.push(`### ${entry.title}`, "", entry.markdown);
1143
- break;
1144
- case "mermaid":
1145
- if (entry.title) lines.push(`**${entry.title}**`, "");
1146
- lines.push("```mermaid", entry.code, "```");
1147
- break;
1148
- case "screenshot":
1149
- lines.push(`![${entry.alt ?? "screenshot"}](${entry.path})`);
1150
- break;
1151
- case "video":
1152
- lines.push(`Video: ${entry.path}${entry.caption ? ` \u2014 ${entry.caption}` : ""}`);
1153
- break;
1154
- case "html": {
1155
- const htmlTitle = entry.title ?? "Embedded HTML";
1156
- if (entry.content) {
1157
- lines.push(`**${htmlTitle}**`, "", "```html", entry.content, "```");
1158
- } else if (entry.url) {
1159
- lines.push(`[${htmlTitle}](${entry.url})`);
1160
- } else {
1161
- lines.push(`Embedded HTML: ${entry.path ?? "(inline)"}`);
1162
- }
1163
- break;
1164
- }
1165
- case "custom":
1166
- lines.push(`**${entry.type}**`, "", "```json", stringify(entry.data), "```");
1167
- break;
1168
- case "state": {
1169
- const lane = entry.label ?? "";
1170
- lines.push(`**${entry.label ?? "State"}**`);
1171
- if (stateLanes?.has(lane)) {
1172
- const summary = summarizeStateChanges(diffStateValues(stateLanes.get(lane), entry.value));
1173
- if (summary.length > 0) lines.push("", ...summary.map((l) => `- ${l}`));
1174
- }
1175
- stateLanes?.set(lane, entry.value);
1176
- lines.push("", "<details>", "<summary>snapshot</summary>", "", "```json", stringify(entry.value ?? null), "```", "", "</details>");
1177
- break;
1178
- }
1179
- }
1180
- for (const child of entry.children ?? []) {
1181
- lines.push("", ...docEntryToMarkdown(child, stateLanes));
1182
- }
1183
- return lines;
1184
- }
1185
- function stepLines(step, ordinal, full, stateLanes) {
1186
- const status = step.status === "passed" ? "" : ` \u2014 **${step.status}**`;
1187
- const lines = [`${ordinal}. **${step.keyword}** ${step.text}${status}`];
1188
- if (!full) return lines;
1189
- if (step.errorMessage) {
1190
- lines.push(...step.errorMessage.split("\n").map((l) => ` > ${l}`));
1191
- }
1192
- for (const entry of step.docEntries) {
1193
- lines.push("", ...docEntryToMarkdown(entry, stateLanes).map((l) => l === "" ? l : ` ${l}`));
1194
- }
1195
- return lines;
1196
- }
1197
- function scenarioToMarkdown(scenario, options = {}) {
1198
- const { variant = "full", meta = [] } = options;
1199
- const full = variant === "full";
1200
- const lines = full ? [`# ${scenario.title}`, ""] : [`## ${scenario.title} _(${scenario.status})_`, ""];
1201
- if (full && meta.length > 0) lines.push(...meta);
1202
- const stateLanes = /* @__PURE__ */ new Map();
1203
- if (scenario.steps.length > 0) {
1204
- if (full) lines.push("", "## Steps", "");
1205
- scenario.steps.forEach((step, i) => lines.push(...stepLines(step, i + 1, full, stateLanes)));
1206
- }
1207
- if (scenario.errorMessage) {
1208
- if (full) lines.push("", "## Failure", "");
1209
- else lines.push("");
1210
- lines.push("```", scenario.errorMessage.trim(), "```");
1211
- }
1212
- if (full && scenario.docEntries.length > 0) {
1213
- lines.push("", "## Docs", "");
1214
- for (const entry of scenario.docEntries) lines.push(...docEntryToMarkdown(entry, stateLanes), "");
1215
- }
1216
- return lines.join("\n").replaceAll(/\n{3,}/g, "\n\n").trimEnd() + (full ? "\n" : "");
1217
- }
1218
-
1219
- // src/interactive/scenario-actions.tsx
1142
+ import { scenarioToMarkdown as coreScenarioToMarkdown } from "executable-stories-core/utils/scenario-markdown";
1220
1143
  var ScenarioActionsContext = createContext4(null);
1221
1144
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1222
1145
  function useScenarioActions() {
1223
1146
  return useContext5(ScenarioActionsContext);
1224
1147
  }
1225
- function scenarioToMarkdown2(scenario) {
1226
- return scenarioToMarkdown(scenario, { variant: "compact" });
1148
+ function scenarioToMarkdown(scenario) {
1149
+ return coreScenarioToMarkdown(scenario, { variant: "compact" });
1227
1150
  }
1228
1151
  function scenarioToPrompt(scenario) {
1229
1152
  const lines = [
@@ -1251,9 +1174,9 @@ function scenarioPermalink(scenario) {
1251
1174
 
1252
1175
  // src/components/ui/card.tsx
1253
1176
  import "react";
1254
- import { jsx as jsx23 } from "react/jsx-runtime";
1177
+ import { jsx as jsx24 } from "react/jsx-runtime";
1255
1178
  function Card({ className, ...props }) {
1256
- return /* @__PURE__ */ jsx23(
1179
+ return /* @__PURE__ */ jsx24(
1257
1180
  "div",
1258
1181
  {
1259
1182
  "data-slot": "card",
@@ -1266,7 +1189,7 @@ function Card({ className, ...props }) {
1266
1189
  );
1267
1190
  }
1268
1191
  function CardHeader({ className, ...props }) {
1269
- return /* @__PURE__ */ jsx23(
1192
+ return /* @__PURE__ */ jsx24(
1270
1193
  "div",
1271
1194
  {
1272
1195
  "data-slot": "card-header",
@@ -1279,7 +1202,7 @@ function CardHeader({ className, ...props }) {
1279
1202
  );
1280
1203
  }
1281
1204
  function CardContent({ className, ...props }) {
1282
- return /* @__PURE__ */ jsx23(
1205
+ return /* @__PURE__ */ jsx24(
1283
1206
  "div",
1284
1207
  {
1285
1208
  "data-slot": "card-content",
@@ -1293,13 +1216,13 @@ function CardContent({ className, ...props }) {
1293
1216
  import "react";
1294
1217
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
1295
1218
  import { CheckIcon } from "lucide-react";
1296
- import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
1219
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1297
1220
  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";
1298
1221
  function DropdownMenu(props) {
1299
- return /* @__PURE__ */ jsx24(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1222
+ return /* @__PURE__ */ jsx25(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1300
1223
  }
1301
1224
  function DropdownMenuTrigger(props) {
1302
- return /* @__PURE__ */ jsx24(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1225
+ return /* @__PURE__ */ jsx25(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1303
1226
  }
1304
1227
  function DropdownMenuContent({
1305
1228
  className,
@@ -1307,14 +1230,14 @@ function DropdownMenuContent({
1307
1230
  sideOffset = 4,
1308
1231
  ...props
1309
1232
  }) {
1310
- return /* @__PURE__ */ jsx24(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx24(
1233
+ return /* @__PURE__ */ jsx25(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx25(
1311
1234
  MenuPrimitive.Positioner,
1312
1235
  {
1313
1236
  "data-slot": "dropdown-menu-positioner",
1314
1237
  className: "z-50",
1315
1238
  align,
1316
1239
  sideOffset,
1317
- children: /* @__PURE__ */ jsx24(
1240
+ children: /* @__PURE__ */ jsx25(
1318
1241
  MenuPrimitive.Popup,
1319
1242
  {
1320
1243
  "data-slot": "dropdown-menu-content",
@@ -1332,7 +1255,7 @@ function DropdownMenuItem({
1332
1255
  className,
1333
1256
  ...props
1334
1257
  }) {
1335
- return /* @__PURE__ */ jsx24(
1258
+ return /* @__PURE__ */ jsx25(
1336
1259
  MenuPrimitive.Item,
1337
1260
  {
1338
1261
  "data-slot": "dropdown-menu-item",
@@ -1344,7 +1267,7 @@ function DropdownMenuItem({
1344
1267
 
1345
1268
  // src/components/ReportScenario.tsx
1346
1269
  import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
1347
- import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1270
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1348
1271
  var STATUS_META = {
1349
1272
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1350
1273
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1360,7 +1283,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1360
1283
  const collapsible = collapse !== null && !hideTitle;
1361
1284
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1362
1285
  const actions = useScenarioActions();
1363
- return /* @__PURE__ */ jsxs16(
1286
+ return /* @__PURE__ */ jsxs17(
1364
1287
  Card,
1365
1288
  {
1366
1289
  id: scenario.id,
@@ -1368,10 +1291,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1368
1291
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1369
1292
  className: "gap-0 py-0",
1370
1293
  children: [
1371
- /* @__PURE__ */ jsxs16(CardHeader, { className: "px-4 py-3", children: [
1372
- /* @__PURE__ */ jsxs16("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1373
- hideTitle ? null : /* @__PURE__ */ jsxs16("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1374
- collapsible ? /* @__PURE__ */ jsx25(
1294
+ /* @__PURE__ */ jsxs17(CardHeader, { className: "px-4 py-3", children: [
1295
+ /* @__PURE__ */ jsxs17("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1296
+ hideTitle ? null : /* @__PURE__ */ jsxs17("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1297
+ collapsible ? /* @__PURE__ */ jsx26(
1375
1298
  "button",
1376
1299
  {
1377
1300
  type: "button",
@@ -1380,49 +1303,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1380
1303
  "aria-controls": bodyId,
1381
1304
  "aria-label": `Toggle ${scenario.title}`,
1382
1305
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1383
- children: /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1306
+ children: /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1384
1307
  }
1385
1308
  ) : null,
1386
- /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1387
- /* @__PURE__ */ jsx25("span", { children: scenario.title })
1309
+ /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1310
+ /* @__PURE__ */ jsx26("span", { children: scenario.title })
1388
1311
  ] }),
1389
- /* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-2", children: [
1390
- /* @__PURE__ */ jsx25(ScenarioRunHistory, { scenarioId: scenario.id }),
1391
- scenario.durationMs > 0 ? /* @__PURE__ */ jsx25("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1392
- meta.showPill ? /* @__PURE__ */ jsx25(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx25("span", { className: "sr-only", children: `Status: ${label}` }),
1393
- actions ? /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1394
- /* @__PURE__ */ jsx25(
1312
+ /* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center gap-2", children: [
1313
+ /* @__PURE__ */ jsx26(ScenarioRunHistory, { scenarioId: scenario.id }),
1314
+ scenario.durationMs > 0 ? /* @__PURE__ */ jsx26("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration3(scenario.durationMs) }) : null,
1315
+ meta.showPill ? /* @__PURE__ */ jsx26(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx26("span", { className: "sr-only", children: `Status: ${label}` }),
1316
+ actions ? /* @__PURE__ */ jsxs17(DropdownMenu, { children: [
1317
+ /* @__PURE__ */ jsx26(
1395
1318
  DropdownMenuTrigger,
1396
1319
  {
1397
1320
  "aria-label": `Actions for ${scenario.title}`,
1398
1321
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1399
- children: /* @__PURE__ */ jsx25(MoreHorizontalIcon, { "aria-hidden": true })
1322
+ children: /* @__PURE__ */ jsx26(MoreHorizontalIcon, { "aria-hidden": true })
1400
1323
  }
1401
1324
  ),
1402
- /* @__PURE__ */ jsxs16(DropdownMenuContent, { align: "end", children: [
1403
- /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1404
- /* @__PURE__ */ jsx25(LinkIcon, {}),
1325
+ /* @__PURE__ */ jsxs17(DropdownMenuContent, { align: "end", children: [
1326
+ /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1327
+ /* @__PURE__ */ jsx26(LinkIcon, {}),
1405
1328
  " Copy link"
1406
1329
  ] }),
1407
- /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1408
- /* @__PURE__ */ jsx25(FileTextIcon, {}),
1330
+ /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1331
+ /* @__PURE__ */ jsx26(FileTextIcon, {}),
1409
1332
  " Copy as Markdown"
1410
1333
  ] }),
1411
- /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1412
- /* @__PURE__ */ jsx25(SparklesIcon, {}),
1334
+ /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1335
+ /* @__PURE__ */ jsx26(SparklesIcon, {}),
1413
1336
  " Explain with AI"
1414
1337
  ] })
1415
1338
  ] })
1416
1339
  ] }) : null
1417
1340
  ] })
1418
1341
  ] }),
1419
- scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs16("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1420
- scenario.tags.map((t) => /* @__PURE__ */ jsx25("li", { children: /* @__PURE__ */ jsx25(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1421
- (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx25("li", { children: ticket.url ? /* @__PURE__ */ jsx25("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx25(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx25(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1342
+ scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs17("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
1343
+ scenario.tags.map((t) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsx26(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1344
+ (scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx26("li", { children: ticket.url ? /* @__PURE__ */ jsx26("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx26(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx26(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
1422
1345
  ] }) : null
1423
1346
  ] }),
1424
- /* @__PURE__ */ jsxs16(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1425
- scenario.errorMessage ? /* @__PURE__ */ jsx25(
1347
+ /* @__PURE__ */ jsxs17(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1348
+ scenario.errorMessage ? /* @__PURE__ */ jsx26(
1426
1349
  "pre",
1427
1350
  {
1428
1351
  role: "alert",
@@ -1430,11 +1353,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1430
1353
  children: scenario.errorMessage
1431
1354
  }
1432
1355
  ) : null,
1433
- /* @__PURE__ */ jsx25(ReportStoryboard, { scenario }),
1434
- /* @__PURE__ */ jsx25(ReportSteps, { scenario }),
1435
- scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx25("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx25(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1436
- /* @__PURE__ */ jsx25(ReportAttachments, { attachments: scenario.attachments }),
1437
- /* @__PURE__ */ jsx25(ReportTrace, { spans: scenario.otelSpans })
1356
+ /* @__PURE__ */ jsx26(ReportStoryboard, { scenario }),
1357
+ /* @__PURE__ */ jsx26(ReportSteps, { scenario }),
1358
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx26("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx26(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1359
+ /* @__PURE__ */ jsx26(ReportAttachments, { attachments: scenario.attachments }),
1360
+ /* @__PURE__ */ jsx26(ReportTrace, { spans: scenario.otelSpans })
1438
1361
  ] })
1439
1362
  ]
1440
1363
  }
@@ -1442,13 +1365,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1442
1365
  }
1443
1366
 
1444
1367
  // src/components/ReportScenarioList.tsx
1445
- import { Fragment as Fragment8, jsx as jsx26 } from "react/jsx-runtime";
1368
+ import { Fragment as Fragment8, jsx as jsx27 } from "react/jsx-runtime";
1446
1369
  function ReportScenarioList({ feature }) {
1447
- return /* @__PURE__ */ jsx26(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx26(ReportScenario, { scenario }, scenario.id)) });
1370
+ return /* @__PURE__ */ jsx27(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx27(ReportScenario, { scenario }, scenario.id)) });
1448
1371
  }
1449
1372
 
1450
1373
  // src/components/ReportFeature.tsx
1451
- import { Fragment as Fragment9, jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
1374
+ import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1452
1375
  function ReportFeature({ feature }) {
1453
1376
  const titleId = `${feature.id}-title`;
1454
1377
  const bodyId = `${feature.id}-body`;
@@ -1459,7 +1382,7 @@ function ReportFeature({ feature }) {
1459
1382
  return (
1460
1383
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1461
1384
  // layer of nesting so the scenario cards below read as the primary unit.
1462
- /* @__PURE__ */ jsxs17(
1385
+ /* @__PURE__ */ jsxs18(
1463
1386
  "section",
1464
1387
  {
1465
1388
  id: feature.id,
@@ -1467,9 +1390,9 @@ function ReportFeature({ feature }) {
1467
1390
  "aria-labelledby": titleId,
1468
1391
  className: "flex flex-col gap-3",
1469
1392
  children: [
1470
- /* @__PURE__ */ jsxs17("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1471
- /* @__PURE__ */ jsxs17("div", { className: "min-w-0", children: [
1472
- /* @__PURE__ */ jsx27("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs17(
1393
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1394
+ /* @__PURE__ */ jsxs18("div", { className: "min-w-0", children: [
1395
+ /* @__PURE__ */ jsx28("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs18(
1473
1396
  "button",
1474
1397
  {
1475
1398
  type: "button",
@@ -1478,31 +1401,31 @@ function ReportFeature({ feature }) {
1478
1401
  "aria-controls": bodyId,
1479
1402
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1480
1403
  children: [
1481
- /* @__PURE__ */ jsx27("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1482
- /* @__PURE__ */ jsx27("span", { className: "truncate", children: feature.title })
1404
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1405
+ /* @__PURE__ */ jsx28("span", { className: "truncate", children: feature.title })
1483
1406
  ]
1484
1407
  }
1485
1408
  ) : feature.title }),
1486
- /* @__PURE__ */ jsx27("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1409
+ /* @__PURE__ */ jsx28("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1487
1410
  ] }),
1488
- /* @__PURE__ */ jsxs17(
1411
+ /* @__PURE__ */ jsxs18(
1489
1412
  "p",
1490
1413
  {
1491
1414
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1492
1415
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1493
1416
  children: [
1494
- /* @__PURE__ */ jsxs17("span", { children: [
1417
+ /* @__PURE__ */ jsxs18("span", { children: [
1495
1418
  s.passed,
1496
1419
  " passed"
1497
1420
  ] }),
1498
- /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1499
- /* @__PURE__ */ jsxs17("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1421
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": true, children: "\xB7" }),
1422
+ /* @__PURE__ */ jsxs18("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1500
1423
  s.failed,
1501
1424
  " failed"
1502
1425
  ] }),
1503
- skipped > 0 ? /* @__PURE__ */ jsxs17(Fragment9, { children: [
1504
- /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1505
- /* @__PURE__ */ jsxs17("span", { children: [
1426
+ skipped > 0 ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
1427
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": true, children: "\xB7" }),
1428
+ /* @__PURE__ */ jsxs18("span", { children: [
1506
1429
  skipped,
1507
1430
  " skipped"
1508
1431
  ] })
@@ -1511,7 +1434,7 @@ function ReportFeature({ feature }) {
1511
1434
  }
1512
1435
  )
1513
1436
  ] }),
1514
- /* @__PURE__ */ jsx27("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx27(ReportScenarioList, { feature }) })
1437
+ /* @__PURE__ */ jsx28("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx28(ReportScenarioList, { feature }) })
1515
1438
  ]
1516
1439
  }
1517
1440
  )
@@ -1519,17 +1442,17 @@ function ReportFeature({ feature }) {
1519
1442
  }
1520
1443
 
1521
1444
  // src/components/ReportFeatureList.tsx
1522
- import { jsx as jsx28 } from "react/jsx-runtime";
1445
+ import { jsx as jsx29 } from "react/jsx-runtime";
1523
1446
  function ReportFeatureList() {
1524
1447
  const report = useReport();
1525
- return /* @__PURE__ */ jsx28("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx28(ReportFeature, { feature }, feature.id)) });
1448
+ return /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx29(ReportFeature, { feature }, feature.id)) });
1526
1449
  }
1527
1450
 
1528
1451
  // src/components/ui/empty.tsx
1529
1452
  import { cva as cva2 } from "class-variance-authority";
1530
- import { jsx as jsx29 } from "react/jsx-runtime";
1453
+ import { jsx as jsx30 } from "react/jsx-runtime";
1531
1454
  function Empty({ className, ...props }) {
1532
- return /* @__PURE__ */ jsx29(
1455
+ return /* @__PURE__ */ jsx30(
1533
1456
  "div",
1534
1457
  {
1535
1458
  "data-slot": "empty",
@@ -1542,7 +1465,7 @@ function Empty({ className, ...props }) {
1542
1465
  );
1543
1466
  }
1544
1467
  function EmptyHeader({ className, ...props }) {
1545
- return /* @__PURE__ */ jsx29(
1468
+ return /* @__PURE__ */ jsx30(
1546
1469
  "div",
1547
1470
  {
1548
1471
  "data-slot": "empty-header",
@@ -1569,7 +1492,7 @@ var emptyMediaVariants = cva2(
1569
1492
  }
1570
1493
  );
1571
1494
  function EmptyTitle({ className, ...props }) {
1572
- return /* @__PURE__ */ jsx29(
1495
+ return /* @__PURE__ */ jsx30(
1573
1496
  "div",
1574
1497
  {
1575
1498
  "data-slot": "empty-title",
@@ -1580,16 +1503,16 @@ function EmptyTitle({ className, ...props }) {
1580
1503
  }
1581
1504
 
1582
1505
  // src/components/ReportEmpty.tsx
1583
- import { jsx as jsx30 } from "react/jsx-runtime";
1506
+ import { jsx as jsx31 } from "react/jsx-runtime";
1584
1507
  function ReportEmpty({ message }) {
1585
- return /* @__PURE__ */ jsx30(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx30(EmptyHeader, { children: /* @__PURE__ */ jsx30(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1508
+ return /* @__PURE__ */ jsx31(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx31(EmptyHeader, { children: /* @__PURE__ */ jsx31(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1586
1509
  }
1587
1510
 
1588
1511
  // src/components/ReportSummary.tsx
1589
- import { jsx as jsx31, jsxs as jsxs18 } from "react/jsx-runtime";
1512
+ import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
1590
1513
  function ReportSummary({ className }) {
1591
1514
  const report = useReport();
1592
- return /* @__PURE__ */ jsx31(
1515
+ return /* @__PURE__ */ jsx32(
1593
1516
  ReportSummaryView,
1594
1517
  {
1595
1518
  summary: report.summary,
@@ -1615,7 +1538,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1615
1538
  if (summary.pending > 0) {
1616
1539
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1617
1540
  }
1618
- return /* @__PURE__ */ jsx31(
1541
+ return /* @__PURE__ */ jsx32(
1619
1542
  "dl",
1620
1543
  {
1621
1544
  className: cn(
@@ -1628,17 +1551,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1628
1551
  "aria-label": ariaLabel,
1629
1552
  children: cards.map((c) => {
1630
1553
  const tone = TONE[c.status];
1631
- return /* @__PURE__ */ jsxs18(
1554
+ return /* @__PURE__ */ jsxs19(
1632
1555
  "div",
1633
1556
  {
1634
1557
  "data-status": c.status,
1635
1558
  className: "rounded-lg border bg-card px-4 py-3",
1636
1559
  children: [
1637
- /* @__PURE__ */ jsxs18("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1638
- /* @__PURE__ */ jsx31("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1560
+ /* @__PURE__ */ jsxs19("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1561
+ /* @__PURE__ */ jsx32("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1639
1562
  c.label
1640
1563
  ] }),
1641
- /* @__PURE__ */ jsx31("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1564
+ /* @__PURE__ */ jsx32("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1642
1565
  ]
1643
1566
  },
1644
1567
  c.status
@@ -1648,6 +1571,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1648
1571
  );
1649
1572
  }
1650
1573
 
1574
+ // src/components/ReportMeta.tsx
1575
+ import { formatDuration as formatDuration4 } from "executable-stories-core/utils/duration";
1576
+
1651
1577
  // src/lib/provenance.ts
1652
1578
  function reportLastRunMs(report) {
1653
1579
  if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
@@ -1698,7 +1624,7 @@ function prUrl(ci) {
1698
1624
  }
1699
1625
 
1700
1626
  // src/components/ReportMeta.tsx
1701
- import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
1627
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1702
1628
  function ReportMeta() {
1703
1629
  const report = useReport();
1704
1630
  const items = [];
@@ -1707,7 +1633,7 @@ function ReportMeta() {
1707
1633
  items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
1708
1634
  }
1709
1635
  if (report.durationMs) {
1710
- items.push({ label: "Duration", value: formatDuration(report.durationMs) });
1636
+ items.push({ label: "Duration", value: formatDuration4(report.durationMs) });
1711
1637
  }
1712
1638
  if (report.packageVersion) {
1713
1639
  items.push({ label: "Version", value: report.packageVersion });
@@ -1721,13 +1647,13 @@ function ReportMeta() {
1721
1647
  const short = sha.slice(0, 8);
1722
1648
  items.push({
1723
1649
  label: "Commit",
1724
- value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1650
+ value: url ? /* @__PURE__ */ jsx33("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1725
1651
  });
1726
1652
  }
1727
1653
  if (report.ci) {
1728
1654
  items.push({
1729
1655
  label: "CI",
1730
- value: report.ci.url ? /* @__PURE__ */ jsxs19("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1656
+ value: report.ci.url ? /* @__PURE__ */ jsxs20("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1731
1657
  ciDisplayName(report.ci.name),
1732
1658
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1733
1659
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1738,7 +1664,7 @@ function ReportMeta() {
1738
1664
  const label = `#${report.ci.prNumber}`;
1739
1665
  items.push({
1740
1666
  label: "PR",
1741
- value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1667
+ value: url ? /* @__PURE__ */ jsx33("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1742
1668
  });
1743
1669
  }
1744
1670
  if (items.length === 0) return null;
@@ -1746,50 +1672,50 @@ function ReportMeta() {
1746
1672
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1747
1673
  // collapsed disclosure so it doesn't push the result down. Native <details>
1748
1674
  // so it works with no JS (the SSR/no-hydration report still toggles).
1749
- /* @__PURE__ */ jsxs19("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1750
- /* @__PURE__ */ jsxs19("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: [
1751
- /* @__PURE__ */ jsx32("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1675
+ /* @__PURE__ */ jsxs20("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1676
+ /* @__PURE__ */ jsxs20("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: [
1677
+ /* @__PURE__ */ jsx33("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1752
1678
  "Run details"
1753
1679
  ] }),
1754
- /* @__PURE__ */ jsx32("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__ */ jsxs19("div", { className: "flex items-baseline gap-1.5", children: [
1755
- /* @__PURE__ */ jsx32("dt", { className: "font-medium", children: item.label }),
1756
- /* @__PURE__ */ jsx32("dd", { className: "font-mono text-foreground", children: item.value })
1680
+ /* @__PURE__ */ jsx33("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__ */ jsxs20("div", { className: "flex items-baseline gap-1.5", children: [
1681
+ /* @__PURE__ */ jsx33("dt", { className: "font-medium", children: item.label }),
1682
+ /* @__PURE__ */ jsx33("dd", { className: "font-mono text-foreground", children: item.value })
1757
1683
  ] }, item.label)) })
1758
1684
  ] })
1759
1685
  );
1760
1686
  }
1761
1687
 
1762
1688
  // src/components/ReportSchemaError.tsx
1763
- import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1689
+ import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1764
1690
  function ReportSchemaError({ error }) {
1765
- return /* @__PURE__ */ jsxs20("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1766
- /* @__PURE__ */ jsx33("p", { children: /* @__PURE__ */ jsx33("strong", { children: "Report could not be displayed." }) }),
1767
- /* @__PURE__ */ jsx33("p", { children: error.message }),
1768
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs20("p", { children: [
1691
+ return /* @__PURE__ */ jsxs21("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1692
+ /* @__PURE__ */ jsx34("p", { children: /* @__PURE__ */ jsx34("strong", { children: "Report could not be displayed." }) }),
1693
+ /* @__PURE__ */ jsx34("p", { children: error.message }),
1694
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs21("p", { children: [
1769
1695
  "The report bundle is newer than this version of ",
1770
- /* @__PURE__ */ jsx33("code", { children: "executable-stories-react" }),
1696
+ /* @__PURE__ */ jsx34("code", { children: "executable-stories-react" }),
1771
1697
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1772
1698
  ] }) : null,
1773
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs20("details", { children: [
1774
- /* @__PURE__ */ jsxs20("summary", { children: [
1699
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs21("details", { children: [
1700
+ /* @__PURE__ */ jsxs21("summary", { children: [
1775
1701
  error.issues.length,
1776
1702
  " validation issue",
1777
1703
  error.issues.length === 1 ? "" : "s"
1778
1704
  ] }),
1779
- /* @__PURE__ */ jsx33("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1705
+ /* @__PURE__ */ jsx34("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1780
1706
  ] }) : null
1781
1707
  ] });
1782
1708
  }
1783
1709
 
1784
1710
  // src/components/ReportShell.tsx
1785
- import { Fragment as Fragment10, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1711
+ import { Fragment as Fragment10, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1786
1712
  function ReportErrorShell({
1787
1713
  error,
1788
1714
  className,
1789
1715
  title,
1790
1716
  dataTheme
1791
1717
  }) {
1792
- return /* @__PURE__ */ jsx34("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx34(ReportSchemaError, { error }) });
1718
+ return /* @__PURE__ */ jsx35("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx35(ReportSchemaError, { error }) });
1793
1719
  }
1794
1720
 
1795
1721
  // src/interactive/ReportInteractive.tsx
@@ -1798,12 +1724,12 @@ import { Monitor, Moon, Sun } from "lucide-react";
1798
1724
  // src/components/ui/switch.tsx
1799
1725
  import "react";
1800
1726
  import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
1801
- import { jsx as jsx35 } from "react/jsx-runtime";
1727
+ import { jsx as jsx36 } from "react/jsx-runtime";
1802
1728
  function Switch({
1803
1729
  className,
1804
1730
  ...props
1805
1731
  }) {
1806
- return /* @__PURE__ */ jsx35(
1732
+ return /* @__PURE__ */ jsx36(
1807
1733
  SwitchPrimitive.Root,
1808
1734
  {
1809
1735
  "data-slot": "switch",
@@ -1812,7 +1738,7 @@ function Switch({
1812
1738
  className
1813
1739
  ),
1814
1740
  ...props,
1815
- children: /* @__PURE__ */ jsx35(
1741
+ children: /* @__PURE__ */ jsx36(
1816
1742
  SwitchPrimitive.Thumb,
1817
1743
  {
1818
1744
  "data-slot": "switch-thumb",
@@ -1828,7 +1754,7 @@ import * as React5 from "react";
1828
1754
  import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
1829
1755
  import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
1830
1756
  import { cva as cva3 } from "class-variance-authority";
1831
- import { jsx as jsx36 } from "react/jsx-runtime";
1757
+ import { jsx as jsx37 } from "react/jsx-runtime";
1832
1758
  var toggleItemVariants = cva3(
1833
1759
  "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",
1834
1760
  {
@@ -1850,13 +1776,13 @@ function ToggleGroup({
1850
1776
  children,
1851
1777
  ...props
1852
1778
  }) {
1853
- return /* @__PURE__ */ jsx36(
1779
+ return /* @__PURE__ */ jsx37(
1854
1780
  ToggleGroupPrimitive,
1855
1781
  {
1856
1782
  "data-slot": "toggle-group",
1857
1783
  className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
1858
1784
  ...props,
1859
- children: /* @__PURE__ */ jsx36(ToggleGroupContext.Provider, { value: { size }, children })
1785
+ children: /* @__PURE__ */ jsx37(ToggleGroupContext.Provider, { value: { size }, children })
1860
1786
  }
1861
1787
  );
1862
1788
  }
@@ -1867,7 +1793,7 @@ function ToggleGroupItem({
1867
1793
  ...props
1868
1794
  }) {
1869
1795
  const context = React5.useContext(ToggleGroupContext);
1870
- return /* @__PURE__ */ jsx36(
1796
+ return /* @__PURE__ */ jsx37(
1871
1797
  TogglePrimitive,
1872
1798
  {
1873
1799
  "data-slot": "toggle-group-item",
@@ -1887,9 +1813,9 @@ import { Search } from "lucide-react";
1887
1813
 
1888
1814
  // src/components/ui/input.tsx
1889
1815
  import "react";
1890
- import { jsx as jsx37 } from "react/jsx-runtime";
1816
+ import { jsx as jsx38 } from "react/jsx-runtime";
1891
1817
  function Input({ className, type, ...props }) {
1892
- return /* @__PURE__ */ jsx37(
1818
+ return /* @__PURE__ */ jsx38(
1893
1819
  "input",
1894
1820
  {
1895
1821
  type,
@@ -1906,7 +1832,7 @@ function Input({ className, type, ...props }) {
1906
1832
  }
1907
1833
 
1908
1834
  // src/interactive/ReportSearch.tsx
1909
- import { jsx as jsx38, jsxs as jsxs22 } from "react/jsx-runtime";
1835
+ import { jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
1910
1836
  var ReportSearch = forwardRef(
1911
1837
  function ReportSearch2(props, ref) {
1912
1838
  const {
@@ -1928,17 +1854,17 @@ var ReportSearch = forwardRef(
1928
1854
  e.preventDefault();
1929
1855
  }
1930
1856
  }
1931
- return /* @__PURE__ */ jsxs22("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1932
- /* @__PURE__ */ jsx38("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1933
- /* @__PURE__ */ jsxs22("div", { className: "relative w-full sm:w-72", children: [
1934
- /* @__PURE__ */ jsx38(
1857
+ return /* @__PURE__ */ jsxs23("div", { className: cn("es-search flex flex-col gap-1", className), children: [
1858
+ /* @__PURE__ */ jsx39("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
1859
+ /* @__PURE__ */ jsxs23("div", { className: "relative w-full sm:w-72", children: [
1860
+ /* @__PURE__ */ jsx39(
1935
1861
  Search,
1936
1862
  {
1937
1863
  "aria-hidden": true,
1938
1864
  className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
1939
1865
  }
1940
1866
  ),
1941
- /* @__PURE__ */ jsx38(
1867
+ /* @__PURE__ */ jsx39(
1942
1868
  Input,
1943
1869
  {
1944
1870
  ref,
@@ -1955,7 +1881,7 @@ var ReportSearch = forwardRef(
1955
1881
  }
1956
1882
  )
1957
1883
  ] }),
1958
- showCounts ? /* @__PURE__ */ jsxs22(
1884
+ showCounts ? /* @__PURE__ */ jsxs23(
1959
1885
  "span",
1960
1886
  {
1961
1887
  className: "text-xs tabular-nums text-muted-foreground",
@@ -2044,7 +1970,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
2044
1970
  }
2045
1971
 
2046
1972
  // src/interactive/ReportFailureBanner.tsx
2047
- import { jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
1973
+ import { jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
2048
1974
  function ReportFailureBanner({ failures }) {
2049
1975
  const first = failures[0];
2050
1976
  const jumpToFirst = useCallback2(() => {
@@ -2052,7 +1978,7 @@ function ReportFailureBanner({ failures }) {
2052
1978
  scrollToScenarioId(first.scenarioId);
2053
1979
  }, [first]);
2054
1980
  if (failures.length === 0) return null;
2055
- return /* @__PURE__ */ jsxs23(
1981
+ return /* @__PURE__ */ jsxs24(
2056
1982
  "div",
2057
1983
  {
2058
1984
  className: "es-failure-banner",
@@ -2060,13 +1986,13 @@ function ReportFailureBanner({ failures }) {
2060
1986
  "aria-live": "polite",
2061
1987
  "aria-label": "Failure summary",
2062
1988
  children: [
2063
- /* @__PURE__ */ jsxs23("span", { className: "es-failure-banner-text", children: [
2064
- /* @__PURE__ */ jsx39("strong", { children: failures.length }),
1989
+ /* @__PURE__ */ jsxs24("span", { className: "es-failure-banner-text", children: [
1990
+ /* @__PURE__ */ jsx40("strong", { children: failures.length }),
2065
1991
  " ",
2066
1992
  "failure",
2067
1993
  failures.length === 1 ? "" : "s"
2068
1994
  ] }),
2069
- /* @__PURE__ */ jsx39(
1995
+ /* @__PURE__ */ jsx40(
2070
1996
  "button",
2071
1997
  {
2072
1998
  type: "button",
@@ -2083,7 +2009,7 @@ function ReportFailureBanner({ failures }) {
2083
2009
 
2084
2010
  // src/interactive/ReportFreshness.tsx
2085
2011
  import { useEffect, useState as useState2 } from "react";
2086
- import { Fragment as Fragment11, jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
2012
+ import { Fragment as Fragment11, jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
2087
2013
  function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2088
2014
  const [now, setNow] = useState2(nowMs);
2089
2015
  useEffect(() => {
@@ -2091,7 +2017,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2091
2017
  }, [nowMs]);
2092
2018
  if (lastRunMs === void 0 || now === void 0) return null;
2093
2019
  const age = formatRelativeAge(lastRunMs, now);
2094
- const runLink = ciUrl ? /* @__PURE__ */ jsx40(
2020
+ const runLink = ciUrl ? /* @__PURE__ */ jsx41(
2095
2021
  "a",
2096
2022
  {
2097
2023
  href: ciUrl,
@@ -2102,9 +2028,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2102
2028
  }
2103
2029
  ) : null;
2104
2030
  if (isReportStale(lastRunMs, staleAfterDays, now)) {
2105
- return /* @__PURE__ */ jsxs24("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2106
- /* @__PURE__ */ jsxs24("span", { children: [
2107
- /* @__PURE__ */ jsxs24("strong", { children: [
2031
+ return /* @__PURE__ */ jsxs25("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
2032
+ /* @__PURE__ */ jsxs25("span", { children: [
2033
+ /* @__PURE__ */ jsxs25("strong", { children: [
2108
2034
  "Last verified ",
2109
2035
  age,
2110
2036
  "."
@@ -2114,10 +2040,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2114
2040
  runLink
2115
2041
  ] });
2116
2042
  }
2117
- return /* @__PURE__ */ jsxs24("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2043
+ return /* @__PURE__ */ jsxs25("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
2118
2044
  "Verified ",
2119
2045
  age,
2120
- runLink ? /* @__PURE__ */ jsxs24(Fragment11, { children: [
2046
+ runLink ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
2121
2047
  " \xB7 ",
2122
2048
  runLink
2123
2049
  ] }) : null
@@ -2125,15 +2051,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
2125
2051
  }
2126
2052
 
2127
2053
  // src/interactive/ReportLastRunDelta.tsx
2128
- import { Fragment as Fragment12, jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
2054
+ import { Fragment as Fragment12, jsx as jsx42, jsxs as jsxs26 } from "react/jsx-runtime";
2129
2055
  var MAX_LINKS = 5;
2130
2056
  function ScenarioLinks({ scenarios, tone }) {
2131
- return /* @__PURE__ */ jsxs25(Fragment12, { children: [
2132
- scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ jsxs25("span", { children: [
2057
+ return /* @__PURE__ */ jsxs26(Fragment12, { children: [
2058
+ scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ jsxs26("span", { children: [
2133
2059
  i > 0 ? ", " : "",
2134
- /* @__PURE__ */ jsx41("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2060
+ /* @__PURE__ */ jsx42("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
2135
2061
  ] }, s.id)),
2136
- scenarios.length > MAX_LINKS ? /* @__PURE__ */ jsxs25("span", { children: [
2062
+ scenarios.length > MAX_LINKS ? /* @__PURE__ */ jsxs26("span", { children: [
2137
2063
  " and ",
2138
2064
  scenarios.length - MAX_LINKS,
2139
2065
  " more"
@@ -2150,23 +2076,23 @@ function ReportLastRunDelta({
2150
2076
  if (!delta) return null;
2151
2077
  const { newlyFailing, newlyPassing, added } = delta;
2152
2078
  if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
2153
- return /* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2079
+ return /* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
2154
2080
  }
2155
- return /* @__PURE__ */ jsxs25("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2156
- /* @__PURE__ */ jsx41("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2157
- newlyFailing.length > 0 ? /* @__PURE__ */ jsxs25("span", { className: "ml-2 text-fail", children: [
2081
+ return /* @__PURE__ */ jsxs26("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
2082
+ /* @__PURE__ */ jsx42("span", { className: "font-medium text-foreground", children: "Since last run:" }),
2083
+ newlyFailing.length > 0 ? /* @__PURE__ */ jsxs26("span", { className: "ml-2 text-fail", children: [
2158
2084
  newlyFailing.length,
2159
2085
  " newly failing (",
2160
- /* @__PURE__ */ jsx41(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2086
+ /* @__PURE__ */ jsx42(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
2161
2087
  ")"
2162
2088
  ] }) : null,
2163
- newlyPassing.length > 0 ? /* @__PURE__ */ jsxs25("span", { className: "ml-2 text-pass", children: [
2089
+ newlyPassing.length > 0 ? /* @__PURE__ */ jsxs26("span", { className: "ml-2 text-pass", children: [
2164
2090
  newlyPassing.length,
2165
2091
  " fixed (",
2166
- /* @__PURE__ */ jsx41(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2092
+ /* @__PURE__ */ jsx42(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
2167
2093
  ")"
2168
2094
  ] }) : null,
2169
- added.length > 0 ? /* @__PURE__ */ jsxs25("span", { className: "ml-2", children: [
2095
+ added.length > 0 ? /* @__PURE__ */ jsxs26("span", { className: "ml-2", children: [
2170
2096
  added.length,
2171
2097
  " new"
2172
2098
  ] }) : null
@@ -2230,7 +2156,7 @@ function Button({
2230
2156
  }
2231
2157
 
2232
2158
  // src/interactive/ReportShortcutsHelp.tsx
2233
- import { jsx as jsx42, jsxs as jsxs26 } from "react/jsx-runtime";
2159
+ import { jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
2234
2160
  var SHORTCUTS = [
2235
2161
  { keys: "/", description: "Focus search" },
2236
2162
  { keys: "f", description: "Jump to next failure" },
@@ -2242,9 +2168,9 @@ var SHORTCUTS = [
2242
2168
  ];
2243
2169
  function Keys({ keys }) {
2244
2170
  const parts = keys.split(" ");
2245
- return /* @__PURE__ */ jsx42("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center gap-1", children: [
2246
- i > 0 ? /* @__PURE__ */ jsx42("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2247
- /* @__PURE__ */ jsx42("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 })
2171
+ return /* @__PURE__ */ jsx43("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ jsxs27("span", { className: "inline-flex items-center gap-1", children: [
2172
+ i > 0 ? /* @__PURE__ */ jsx43("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
2173
+ /* @__PURE__ */ jsx43("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 })
2248
2174
  ] }, k)) });
2249
2175
  }
2250
2176
  function ReportShortcutsHelp({ open, onClose }) {
@@ -2255,7 +2181,7 @@ function ReportShortcutsHelp({ open, onClose }) {
2255
2181
  if (open && !dialog.open) dialog.showModal();
2256
2182
  else if (!open && dialog.open) dialog.close();
2257
2183
  }, [open]);
2258
- return /* @__PURE__ */ jsxs26(
2184
+ return /* @__PURE__ */ jsxs27(
2259
2185
  "dialog",
2260
2186
  {
2261
2187
  ref: dialogRef,
@@ -2263,12 +2189,12 @@ function ReportShortcutsHelp({ open, onClose }) {
2263
2189
  "aria-label": "Keyboard shortcuts",
2264
2190
  onClose,
2265
2191
  children: [
2266
- /* @__PURE__ */ jsx42("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2267
- /* @__PURE__ */ jsx42("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs26("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2268
- /* @__PURE__ */ jsx42("dt", { className: "justify-self-start", children: /* @__PURE__ */ jsx42(Keys, { keys: s.keys }) }),
2269
- /* @__PURE__ */ jsx42("dd", { className: "text-sm text-muted-foreground", children: s.description })
2192
+ /* @__PURE__ */ jsx43("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
2193
+ /* @__PURE__ */ jsx43("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs27("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
2194
+ /* @__PURE__ */ jsx43("dt", { className: "justify-self-start", children: /* @__PURE__ */ jsx43(Keys, { keys: s.keys }) }),
2195
+ /* @__PURE__ */ jsx43("dd", { className: "text-sm text-muted-foreground", children: s.description })
2270
2196
  ] }, s.keys)) }),
2271
- /* @__PURE__ */ jsx42("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ jsx42(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2197
+ /* @__PURE__ */ jsx43("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ jsx43(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
2272
2198
  ]
2273
2199
  }
2274
2200
  );
@@ -2471,7 +2397,7 @@ function failuresFirst(report) {
2471
2397
 
2472
2398
  // src/interactive/ReportFilters.tsx
2473
2399
  import { useState as useState3 } from "react";
2474
- import { jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
2400
+ import { jsx as jsx44, jsxs as jsxs28 } from "react/jsx-runtime";
2475
2401
  var TAG_LIMIT = 12;
2476
2402
  function ReportFilters({
2477
2403
  statuses,
@@ -2485,10 +2411,10 @@ function ReportFilters({
2485
2411
  if (statuses.length === 0 && tags.length === 0) return null;
2486
2412
  const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
2487
2413
  const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
2488
- return /* @__PURE__ */ jsxs27("div", { className: "flex flex-col gap-2", children: [
2489
- statuses.length > 0 ? /* @__PURE__ */ jsx43("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2414
+ return /* @__PURE__ */ jsxs28("div", { className: "flex flex-col gap-2", children: [
2415
+ statuses.length > 0 ? /* @__PURE__ */ jsx44("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
2490
2416
  const active = status === opt.key;
2491
- return /* @__PURE__ */ jsxs27(
2417
+ return /* @__PURE__ */ jsxs28(
2492
2418
  "button",
2493
2419
  {
2494
2420
  type: "button",
@@ -2500,23 +2426,23 @@ function ReportFilters({
2500
2426
  ),
2501
2427
  children: [
2502
2428
  opt.label,
2503
- /* @__PURE__ */ jsx43("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2429
+ /* @__PURE__ */ jsx44("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
2504
2430
  ]
2505
2431
  },
2506
2432
  opt.key
2507
2433
  );
2508
2434
  }) }) : null,
2509
- tags.length > 0 ? /* @__PURE__ */ jsxs27("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2435
+ tags.length > 0 ? /* @__PURE__ */ jsxs28("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
2510
2436
  visibleTags.map((tag) => {
2511
2437
  const active = activeTags.includes(tag);
2512
- return /* @__PURE__ */ jsx43("li", { children: /* @__PURE__ */ jsx43(
2438
+ return /* @__PURE__ */ jsx44("li", { children: /* @__PURE__ */ jsx44(
2513
2439
  "button",
2514
2440
  {
2515
2441
  type: "button",
2516
2442
  "aria-pressed": active,
2517
2443
  onClick: () => onToggleTag(tag),
2518
2444
  className: "cursor-pointer",
2519
- children: /* @__PURE__ */ jsx43(
2445
+ children: /* @__PURE__ */ jsx44(
2520
2446
  Badge,
2521
2447
  {
2522
2448
  variant: "tag",
@@ -2529,7 +2455,7 @@ function ReportFilters({
2529
2455
  }
2530
2456
  ) }, tag);
2531
2457
  }),
2532
- tags.length > TAG_LIMIT ? /* @__PURE__ */ jsx43("li", { children: /* @__PURE__ */ jsx43(
2458
+ tags.length > TAG_LIMIT ? /* @__PURE__ */ jsx44("li", { children: /* @__PURE__ */ jsx44(
2533
2459
  "button",
2534
2460
  {
2535
2461
  type: "button",
@@ -2545,7 +2471,7 @@ function ReportFilters({
2545
2471
 
2546
2472
  // src/interactive/ReportToc.tsx
2547
2473
  import { useEffect as useEffect5, useMemo as useMemo2, useState as useState4 } from "react";
2548
- import { jsx as jsx44, jsxs as jsxs28 } from "react/jsx-runtime";
2474
+ import { jsx as jsx45, jsxs as jsxs29 } from "react/jsx-runtime";
2549
2475
  var DOT = {
2550
2476
  passed: "bg-pass",
2551
2477
  failed: "bg-fail",
@@ -2574,11 +2500,11 @@ function TocContent({ onNavigate }) {
2574
2500
  return () => observer.disconnect();
2575
2501
  }, [scenarioIds]);
2576
2502
  if (report.features.length === 0) return null;
2577
- return /* @__PURE__ */ jsx44("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ jsxs28("li", { children: [
2578
- /* @__PURE__ */ jsx44("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2579
- /* @__PURE__ */ jsx44("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2503
+ return /* @__PURE__ */ jsx45("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ jsxs29("li", { children: [
2504
+ /* @__PURE__ */ jsx45("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
2505
+ /* @__PURE__ */ jsx45("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
2580
2506
  const active = activeId === scenario.id;
2581
- return /* @__PURE__ */ jsx44("li", { children: /* @__PURE__ */ jsxs28(
2507
+ return /* @__PURE__ */ jsx45("li", { children: /* @__PURE__ */ jsxs29(
2582
2508
  "a",
2583
2509
  {
2584
2510
  href: `#${scenario.id}`,
@@ -2593,14 +2519,14 @@ function TocContent({ onNavigate }) {
2593
2519
  active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
2594
2520
  ),
2595
2521
  children: [
2596
- /* @__PURE__ */ jsx44(
2522
+ /* @__PURE__ */ jsx45(
2597
2523
  "span",
2598
2524
  {
2599
2525
  "aria-hidden": true,
2600
2526
  className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
2601
2527
  }
2602
2528
  ),
2603
- /* @__PURE__ */ jsx44("span", { className: "break-words", children: scenario.title })
2529
+ /* @__PURE__ */ jsx45("span", { className: "break-words", children: scenario.title })
2604
2530
  ]
2605
2531
  }
2606
2532
  ) }, scenario.id);
@@ -2610,14 +2536,14 @@ function TocContent({ onNavigate }) {
2610
2536
  function ReportToc() {
2611
2537
  const report = useReport();
2612
2538
  if (report.features.length === 0) return null;
2613
- return /* @__PURE__ */ jsxs28(
2539
+ return /* @__PURE__ */ jsxs29(
2614
2540
  "nav",
2615
2541
  {
2616
2542
  "aria-label": "Table of contents",
2617
2543
  className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
2618
2544
  children: [
2619
- /* @__PURE__ */ jsx44("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2620
- /* @__PURE__ */ jsx44(TocContent, {})
2545
+ /* @__PURE__ */ jsx45("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
2546
+ /* @__PURE__ */ jsx45(TocContent, {})
2621
2547
  ]
2622
2548
  }
2623
2549
  );
@@ -2627,31 +2553,31 @@ function ReportToc() {
2627
2553
  import { useState as useState5 } from "react";
2628
2554
  import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
2629
2555
  import { ListIcon, XIcon } from "lucide-react";
2630
- import { jsx as jsx45, jsxs as jsxs29 } from "react/jsx-runtime";
2556
+ import { jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
2631
2557
  function ReportTocDrawer() {
2632
2558
  const report = useReport();
2633
2559
  const [open, setOpen] = useState5(false);
2634
2560
  if (report.features.length === 0) return null;
2635
- return /* @__PURE__ */ jsxs29(DialogPrimitive.Root, { open, onOpenChange: setOpen, children: [
2636
- /* @__PURE__ */ jsxs29(DialogPrimitive.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: [
2637
- /* @__PURE__ */ jsx45(ListIcon, { "aria-hidden": true }),
2561
+ return /* @__PURE__ */ jsxs30(DialogPrimitive.Root, { open, onOpenChange: setOpen, children: [
2562
+ /* @__PURE__ */ jsxs30(DialogPrimitive.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: [
2563
+ /* @__PURE__ */ jsx46(ListIcon, { "aria-hidden": true }),
2638
2564
  " Contents"
2639
2565
  ] }),
2640
- /* @__PURE__ */ jsxs29(DialogPrimitive.Portal, { children: [
2641
- /* @__PURE__ */ jsx45(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2642
- /* @__PURE__ */ jsxs29(DialogPrimitive.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: [
2643
- /* @__PURE__ */ jsxs29("div", { className: "flex items-center justify-between", children: [
2644
- /* @__PURE__ */ jsx45(DialogPrimitive.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2645
- /* @__PURE__ */ jsx45(
2566
+ /* @__PURE__ */ jsxs30(DialogPrimitive.Portal, { children: [
2567
+ /* @__PURE__ */ jsx46(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
2568
+ /* @__PURE__ */ jsxs30(DialogPrimitive.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: [
2569
+ /* @__PURE__ */ jsxs30("div", { className: "flex items-center justify-between", children: [
2570
+ /* @__PURE__ */ jsx46(DialogPrimitive.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
2571
+ /* @__PURE__ */ jsx46(
2646
2572
  DialogPrimitive.Close,
2647
2573
  {
2648
2574
  "aria-label": "Close contents",
2649
2575
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
2650
- children: /* @__PURE__ */ jsx45(XIcon, { "aria-hidden": true })
2576
+ children: /* @__PURE__ */ jsx46(XIcon, { "aria-hidden": true })
2651
2577
  }
2652
2578
  )
2653
2579
  ] }),
2654
- /* @__PURE__ */ jsx45(TocContent, { onNavigate: () => setOpen(false) })
2580
+ /* @__PURE__ */ jsx46(TocContent, { onNavigate: () => setOpen(false) })
2655
2581
  ] })
2656
2582
  ] })
2657
2583
  ] });
@@ -2734,14 +2660,14 @@ function useUrlState() {
2734
2660
  }
2735
2661
 
2736
2662
  // src/interactive/ReportInteractive.tsx
2737
- import { Fragment as Fragment13, jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
2663
+ import { Fragment as Fragment13, jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
2738
2664
  function ReportInteractive(props) {
2739
2665
  const { report, className, title, dataTheme } = props;
2740
2666
  const result = unwrapReport(report);
2741
2667
  if (!result.ok) {
2742
- return /* @__PURE__ */ jsx46(ReportErrorShell, { error: result.error, className, title, dataTheme });
2668
+ return /* @__PURE__ */ jsx47(ReportErrorShell, { error: result.error, className, title, dataTheme });
2743
2669
  }
2744
- return /* @__PURE__ */ jsx46(ReportInteractiveView, { ...props, report: result.data });
2670
+ return /* @__PURE__ */ jsx47(ReportInteractiveView, { ...props, report: result.data });
2745
2671
  }
2746
2672
  var THEME_OPTIONS = [
2747
2673
  { value: "light", label: "Light", Icon: Sun },
@@ -2749,7 +2675,7 @@ var THEME_OPTIONS = [
2749
2675
  { value: "dark", label: "Dark", Icon: Moon }
2750
2676
  ];
2751
2677
  function ThemeSegment({ pref, onPref }) {
2752
- return /* @__PURE__ */ jsx46(
2678
+ return /* @__PURE__ */ jsx47(
2753
2679
  ToggleGroup,
2754
2680
  {
2755
2681
  size: "sm",
@@ -2759,7 +2685,7 @@ function ThemeSegment({ pref, onPref }) {
2759
2685
  if (next) onPref(next);
2760
2686
  },
2761
2687
  "aria-label": "Color theme",
2762
- children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx46(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ jsx46(Icon, {}) }, value))
2688
+ children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx47(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ jsx47(Icon, {}) }, value))
2763
2689
  }
2764
2690
  );
2765
2691
  }
@@ -2836,7 +2762,7 @@ function ReportInteractiveView({
2836
2762
  const scenarioActions = useMemo3(
2837
2763
  () => ({
2838
2764
  copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
2839
- copyMarkdown: (s) => copy(scenarioToMarkdown2(s), "Markdown copied"),
2765
+ copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
2840
2766
  copyPrompt: (s) => copy(scenarioToPrompt(s), "Prompt copied")
2841
2767
  }),
2842
2768
  [copy]
@@ -2890,13 +2816,13 @@ function ReportInteractiveView({
2890
2816
  const totalScenarios = report.summary.total;
2891
2817
  const matchedScenarios = filtered.summary.total;
2892
2818
  const showToc = !hideToc && totalScenarios > 3;
2893
- return /* @__PURE__ */ jsx46(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ jsx46(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ jsx46(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ jsx46(
2819
+ return /* @__PURE__ */ jsx47(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ jsx47(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ jsx47(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ jsx47(
2894
2820
  ReportRoot,
2895
2821
  {
2896
2822
  report: filtered,
2897
2823
  customRenderers,
2898
2824
  renderers,
2899
- children: /* @__PURE__ */ jsxs30(
2825
+ children: /* @__PURE__ */ jsxs31(
2900
2826
  "main",
2901
2827
  {
2902
2828
  className: cn("es-report", "es-report-interactive", className),
@@ -2905,8 +2831,8 @@ function ReportInteractiveView({
2905
2831
  "data-theme": dataTheme,
2906
2832
  "data-detail-level": detail,
2907
2833
  children: [
2908
- /* @__PURE__ */ jsxs30("header", { className: "es-report-header", children: [
2909
- /* @__PURE__ */ jsxs30(
2834
+ /* @__PURE__ */ jsxs31("header", { className: "es-report-header", children: [
2835
+ /* @__PURE__ */ jsxs31(
2910
2836
  "div",
2911
2837
  {
2912
2838
  "data-es-navbar": true,
@@ -2915,9 +2841,9 @@ function ReportInteractiveView({
2915
2841
  hideHeader ? "justify-end" : "justify-between"
2916
2842
  ),
2917
2843
  children: [
2918
- hideHeader ? null : /* @__PURE__ */ jsx46("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2919
- /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
2920
- /* @__PURE__ */ jsx46(
2844
+ hideHeader ? null : /* @__PURE__ */ jsx47("h1", { className: "min-w-0", children: title ?? "Story Report" }),
2845
+ /* @__PURE__ */ jsxs31("div", { className: "flex items-center gap-2", children: [
2846
+ /* @__PURE__ */ jsx47(
2921
2847
  ReportSearch,
2922
2848
  {
2923
2849
  ref: searchRef,
@@ -2927,16 +2853,16 @@ function ReportInteractiveView({
2927
2853
  totalCount: totalScenarios
2928
2854
  }
2929
2855
  ),
2930
- /* @__PURE__ */ jsx46(ThemeSegment, { pref: themePref, onPref: setThemePref })
2856
+ /* @__PURE__ */ jsx47(ThemeSegment, { pref: themePref, onPref: setThemePref })
2931
2857
  ] })
2932
2858
  ]
2933
2859
  }
2934
2860
  ),
2935
- hideHeader ? null : /* @__PURE__ */ jsxs30(Fragment13, { children: [
2936
- /* @__PURE__ */ jsx46(ReportSummary, {}),
2937
- /* @__PURE__ */ jsx46(ReportMeta, {})
2861
+ hideHeader ? null : /* @__PURE__ */ jsxs31(Fragment13, { children: [
2862
+ /* @__PURE__ */ jsx47(ReportSummary, {}),
2863
+ /* @__PURE__ */ jsx47(ReportMeta, {})
2938
2864
  ] }),
2939
- /* @__PURE__ */ jsx46(
2865
+ /* @__PURE__ */ jsx47(
2940
2866
  ReportFreshness,
2941
2867
  {
2942
2868
  lastRunMs: reportLastRunMs(report),
@@ -2944,8 +2870,8 @@ function ReportInteractiveView({
2944
2870
  staleAfterDays
2945
2871
  }
2946
2872
  ),
2947
- /* @__PURE__ */ jsx46(ReportLastRunDelta, { history: scenarioHistory, report }),
2948
- /* @__PURE__ */ jsx46(
2873
+ /* @__PURE__ */ jsx47(ReportLastRunDelta, { history: scenarioHistory, report }),
2874
+ /* @__PURE__ */ jsx47(
2949
2875
  ReportFilters,
2950
2876
  {
2951
2877
  statuses: statusOptions,
@@ -2956,14 +2882,14 @@ function ReportInteractiveView({
2956
2882
  onToggleTag: toggleTag
2957
2883
  }
2958
2884
  ),
2959
- /* @__PURE__ */ jsxs30("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2960
- showToc ? /* @__PURE__ */ jsx46(ReportTocDrawer, {}) : null,
2961
- /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
2962
- /* @__PURE__ */ jsx46(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2963
- /* @__PURE__ */ jsx46("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2885
+ /* @__PURE__ */ jsxs31("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
2886
+ showToc ? /* @__PURE__ */ jsx47(ReportTocDrawer, {}) : null,
2887
+ /* @__PURE__ */ jsxs31("div", { className: "flex items-center gap-2", children: [
2888
+ /* @__PURE__ */ jsx47(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
2889
+ /* @__PURE__ */ jsx47("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
2964
2890
  ] }),
2965
- /* @__PURE__ */ jsxs30("div", { className: "flex items-center gap-2", children: [
2966
- /* @__PURE__ */ jsx46(
2891
+ /* @__PURE__ */ jsxs31("div", { className: "flex items-center gap-2", children: [
2892
+ /* @__PURE__ */ jsx47(
2967
2893
  Switch,
2968
2894
  {
2969
2895
  id: docsId,
@@ -2971,16 +2897,16 @@ function ReportInteractiveView({
2971
2897
  onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
2972
2898
  }
2973
2899
  ),
2974
- /* @__PURE__ */ jsx46("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2900
+ /* @__PURE__ */ jsx47("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
2975
2901
  ] })
2976
2902
  ] })
2977
2903
  ] }),
2978
- /* @__PURE__ */ jsx46(ReportFailureBanner, { failures }),
2979
- hasContent ? showToc ? /* @__PURE__ */ jsxs30("div", { className: "flex gap-6", children: [
2980
- /* @__PURE__ */ jsx46(ReportToc, {}),
2981
- /* @__PURE__ */ jsx46("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx46(ReportFeatureList, {}) })
2982
- ] }) : /* @__PURE__ */ jsx46("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsx46(ReportFeatureList, {}) }) : /* @__PURE__ */ jsx46(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2983
- /* @__PURE__ */ jsx46(
2904
+ /* @__PURE__ */ jsx47(ReportFailureBanner, { failures }),
2905
+ hasContent ? showToc ? /* @__PURE__ */ jsxs31("div", { className: "flex gap-6", children: [
2906
+ /* @__PURE__ */ jsx47(ReportToc, {}),
2907
+ /* @__PURE__ */ jsx47("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx47(ReportFeatureList, {}) })
2908
+ ] }) : /* @__PURE__ */ jsx47("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsx47(ReportFeatureList, {}) }) : /* @__PURE__ */ jsx47(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
2909
+ /* @__PURE__ */ jsx47(
2984
2910
  "button",
2985
2911
  {
2986
2912
  type: "button",
@@ -2991,8 +2917,8 @@ function ReportInteractiveView({
2991
2917
  children: "?"
2992
2918
  }
2993
2919
  ),
2994
- /* @__PURE__ */ jsx46(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2995
- toast ? /* @__PURE__ */ jsx46(
2920
+ /* @__PURE__ */ jsx47(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
2921
+ toast ? /* @__PURE__ */ jsx47(
2996
2922
  "div",
2997
2923
  {
2998
2924
  role: "status",
@@ -3016,7 +2942,7 @@ import { createElement, useEffect as useEffect9, useState as useState10 } from "
3016
2942
 
3017
2943
  // src/components/doc/MermaidView.tsx
3018
2944
  import { useEffect as useEffect8, useRef as useRef6, useState as useState9 } from "react";
3019
- import { Fragment as Fragment14, jsx as jsx47, jsxs as jsxs31 } from "react/jsx-runtime";
2945
+ import { Fragment as Fragment14, jsx as jsx48, jsxs as jsxs32 } from "react/jsx-runtime";
3020
2946
  function prefersDark(el) {
3021
2947
  const themed = el?.closest("[data-theme]");
3022
2948
  if (themed?.dataset.theme === "dark") return true;
@@ -3070,9 +2996,9 @@ function MermaidView({ entry, load }) {
3070
2996
  };
3071
2997
  }, [entry.code, load]);
3072
2998
  if (svg) {
3073
- return /* @__PURE__ */ jsxs31("figure", { ref, className: "my-3", children: [
3074
- entry.title ? /* @__PURE__ */ jsx47("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3075
- /* @__PURE__ */ jsx47(
2999
+ return /* @__PURE__ */ jsxs32("figure", { ref, className: "my-3", children: [
3000
+ entry.title ? /* @__PURE__ */ jsx48("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
3001
+ /* @__PURE__ */ jsx48(
3076
3002
  "div",
3077
3003
  {
3078
3004
  role: "img",
@@ -3083,8 +3009,8 @@ function MermaidView({ entry, load }) {
3083
3009
  )
3084
3010
  ] });
3085
3011
  }
3086
- return /* @__PURE__ */ jsxs31(Fragment14, { children: [
3087
- error ? /* @__PURE__ */ jsxs31(
3012
+ return /* @__PURE__ */ jsxs32(Fragment14, { children: [
3013
+ error ? /* @__PURE__ */ jsxs32(
3088
3014
  "p",
3089
3015
  {
3090
3016
  role: "alert",
@@ -3096,12 +3022,12 @@ function MermaidView({ entry, load }) {
3096
3022
  ]
3097
3023
  }
3098
3024
  ) : null,
3099
- /* @__PURE__ */ jsx47(MermaidSource, { entry, ref })
3025
+ /* @__PURE__ */ jsx48(MermaidSource, { entry, ref })
3100
3026
  ] });
3101
3027
  }
3102
3028
 
3103
3029
  // src/interactive/island-renderers.tsx
3104
- import { jsx as jsx48 } from "react/jsx-runtime";
3030
+ import { jsx as jsx49 } from "react/jsx-runtime";
3105
3031
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3106
3032
  var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3107
3033
  function cachedCdnModule(url) {
@@ -3135,13 +3061,13 @@ function CdnHighlightedCode({ entry }) {
3135
3061
  cancelled = true;
3136
3062
  };
3137
3063
  }, [entry.content, entry.lang]);
3138
- return /* @__PURE__ */ jsx48(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx48(
3064
+ return /* @__PURE__ */ jsx49(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx49(
3139
3065
  "code",
3140
3066
  {
3141
3067
  className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
3142
3068
  dangerouslySetInnerHTML: { __html: html }
3143
3069
  }
3144
- ) : /* @__PURE__ */ jsx48("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3070
+ ) : /* @__PURE__ */ jsx49("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
3145
3071
  }
3146
3072
  function buildIslandRenderers(opts) {
3147
3073
  const renderers = {};
@@ -3155,7 +3081,7 @@ function buildIslandRenderers(opts) {
3155
3081
  }
3156
3082
 
3157
3083
  // src/interactive/ReportInteractiveIsland.tsx
3158
- import { jsx as jsx49 } from "react/jsx-runtime";
3084
+ import { jsx as jsx50 } from "react/jsx-runtime";
3159
3085
  function ReportInteractiveIsland({
3160
3086
  report,
3161
3087
  title,
@@ -3169,7 +3095,7 @@ function ReportInteractiveIsland({
3169
3095
  () => buildIslandRenderers({ syntaxHighlighting, mermaid }),
3170
3096
  [syntaxHighlighting, mermaid]
3171
3097
  );
3172
- return /* @__PURE__ */ jsx49(
3098
+ return /* @__PURE__ */ jsx50(
3173
3099
  ReportInteractive,
3174
3100
  {
3175
3101
  report,