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.
package/dist/ssr.js CHANGED
@@ -18,63 +18,6 @@ import { useMemo } from "react";
18
18
  import { createContext } from "react";
19
19
  var ReportContext = createContext(null);
20
20
 
21
- // src/context/ReportRoot.tsx
22
- import { jsx } from "react/jsx-runtime";
23
- var EMPTY_CUSTOM = {};
24
- var EMPTY_RENDERERS = {};
25
- function ReportRoot({
26
- report,
27
- customRenderers,
28
- renderers,
29
- children
30
- }) {
31
- const value = useMemo(
32
- () => ({
33
- report,
34
- customRenderers: customRenderers ?? EMPTY_CUSTOM,
35
- renderers: renderers ?? EMPTY_RENDERERS
36
- }),
37
- [report, customRenderers, renderers]
38
- );
39
- return /* @__PURE__ */ jsx(ReportContext.Provider, { value, children });
40
- }
41
-
42
- // src/hooks/useReport.ts
43
- import { useContext } from "react";
44
- function useReport() {
45
- const ctx = useContext(ReportContext);
46
- if (!ctx) {
47
- throw new Error(
48
- "useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
49
- );
50
- }
51
- return ctx.report;
52
- }
53
-
54
- // ../executable-stories-core/dist/utils/duration.js
55
- function formatDuration(ms) {
56
- if (ms < 1e3) {
57
- return `${Math.round(ms)} ms`;
58
- }
59
- if (ms < 6e4) {
60
- return `${(ms / 1e3).toFixed(2)} s`;
61
- }
62
- const minutes = Math.floor(ms / 6e4);
63
- const seconds = Math.floor(ms % 6e4 / 1e3);
64
- if (minutes < 60) {
65
- return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
66
- }
67
- const hours = Math.floor(minutes / 60);
68
- const remainingMinutes = minutes % 60;
69
- return `${hours}h ${remainingMinutes}m`;
70
- }
71
-
72
- // src/components/doc/DocNote.tsx
73
- import { jsx as jsx2 } from "react/jsx-runtime";
74
- function DocNote({ entry }) {
75
- return /* @__PURE__ */ jsx2("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
76
- }
77
-
78
21
  // src/components/ui/badge.tsx
79
22
  import "react";
80
23
  import { cva } from "class-variance-authority";
@@ -133,14 +76,237 @@ function Badge({
133
76
  });
134
77
  }
135
78
 
136
- // src/components/doc/DocTag.tsx
79
+ // src/components/doc/NarrativeBlocks.tsx
80
+ import { jsx, jsxs } from "react/jsx-runtime";
81
+ var FILE_TREE_TYPE = "file-tree";
82
+ var DATA_MODEL_TYPE = "data-model";
83
+ function isChangeKind(value) {
84
+ return value === "added" || value === "modified" || value === "removed" || value === "renamed";
85
+ }
86
+ function isRecord(value) {
87
+ return typeof value === "object" && value !== null && !Array.isArray(value);
88
+ }
89
+ function str(value) {
90
+ return typeof value === "string" && value !== "" ? value : void 0;
91
+ }
92
+ function AuthoredMarker({ authored }) {
93
+ if (authored !== "agent") return null;
94
+ return /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
95
+ }
96
+ function BlockFrame({
97
+ title,
98
+ authored,
99
+ children
100
+ }) {
101
+ return /* @__PURE__ */ jsxs("figure", { className: "my-3", children: [
102
+ title || authored === "agent" ? /* @__PURE__ */ jsxs("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
103
+ title ? /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
104
+ /* @__PURE__ */ jsx(AuthoredMarker, { authored })
105
+ ] }) : null,
106
+ children
107
+ ] });
108
+ }
109
+ function ChangeBadge({ change }) {
110
+ return /* @__PURE__ */ jsx(Badge, { variant: "tag", className: "text-[10px]", children: change });
111
+ }
112
+ function parseFileTree(data) {
113
+ if (!isRecord(data) || !Array.isArray(data.files)) return void 0;
114
+ const files = [];
115
+ for (const raw of data.files) {
116
+ if (typeof raw === "string") {
117
+ if (raw) files.push({ path: raw });
118
+ continue;
119
+ }
120
+ if (!isRecord(raw)) continue;
121
+ const path = str(raw.path);
122
+ if (!path) continue;
123
+ files.push({
124
+ path,
125
+ change: isChangeKind(raw.change) ? raw.change : void 0,
126
+ note: str(raw.note)
127
+ });
128
+ }
129
+ if (files.length === 0) return void 0;
130
+ return { title: str(data.title), authored: str(data.authored), files };
131
+ }
132
+ function buildTree(files) {
133
+ const root = { name: "", children: /* @__PURE__ */ new Map() };
134
+ for (const file of files) {
135
+ const segments = file.path.split("/").filter(Boolean);
136
+ let node = root;
137
+ segments.forEach((segment, index) => {
138
+ let child = node.children.get(segment);
139
+ if (!child) {
140
+ child = { name: segment, children: /* @__PURE__ */ new Map() };
141
+ node.children.set(segment, child);
142
+ }
143
+ if (index === segments.length - 1) child.file = file;
144
+ node = child;
145
+ });
146
+ }
147
+ return root;
148
+ }
149
+ function sortedChildren(node) {
150
+ return [...node.children.values()].sort((a, b) => {
151
+ const aDir = a.children.size > 0;
152
+ const bDir = b.children.size > 0;
153
+ if (aDir !== bDir) return aDir ? -1 : 1;
154
+ return a.name.localeCompare(b.name);
155
+ });
156
+ }
157
+ function TreeBranch({ node, depth }) {
158
+ const children = sortedChildren(node);
159
+ 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) => {
160
+ const isDir = child.children.size > 0;
161
+ return /* @__PURE__ */ jsxs("li", { children: [
162
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-2", children: [
163
+ /* @__PURE__ */ jsx(
164
+ "span",
165
+ {
166
+ className: cn(
167
+ "font-mono text-xs",
168
+ isDir ? "text-muted-foreground" : "text-foreground"
169
+ ),
170
+ children: isDir ? `${child.name}/` : child.name
171
+ }
172
+ ),
173
+ child.file?.change ? /* @__PURE__ */ jsx(ChangeBadge, { change: child.file.change }) : null,
174
+ child.file?.note ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
175
+ ] }),
176
+ isDir ? /* @__PURE__ */ jsx(TreeBranch, { node: child, depth: depth + 1 }) : null
177
+ ] }, child.name);
178
+ }) });
179
+ }
180
+ function FileTreeBlock({ entry }) {
181
+ const data = parseFileTree(entry.data);
182
+ if (!data) return /* @__PURE__ */ jsx(UnreadableBlock, { entry });
183
+ 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 }) }) });
184
+ }
185
+ function parseDataModel(data) {
186
+ if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
187
+ const fields = [];
188
+ for (const raw of data.fields) {
189
+ if (!isRecord(raw)) continue;
190
+ const name = str(raw.name);
191
+ if (!name) continue;
192
+ fields.push({
193
+ name,
194
+ type: str(raw.type),
195
+ note: str(raw.note),
196
+ change: isChangeKind(raw.change) ? raw.change : void 0
197
+ });
198
+ }
199
+ if (fields.length === 0) return void 0;
200
+ return {
201
+ title: str(data.title),
202
+ authored: str(data.authored),
203
+ name: str(data.name),
204
+ fields
205
+ };
206
+ }
207
+ function DataModelBlock({ entry }) {
208
+ const data = parseDataModel(entry.data);
209
+ if (!data) return /* @__PURE__ */ jsx(UnreadableBlock, { entry });
210
+ const hasNotes = data.fields.some((f) => f.note);
211
+ 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: [
212
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "bg-muted/50", children: [
213
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
214
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
215
+ hasNotes ? /* @__PURE__ */ jsx("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
216
+ ] }) }),
217
+ /* @__PURE__ */ jsx("tbody", { children: data.fields.map((field) => /* @__PURE__ */ jsxs("tr", { children: [
218
+ /* @__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: [
219
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-foreground", children: field.name }),
220
+ field.change ? /* @__PURE__ */ jsx(ChangeBadge, { change: field.change }) : null
221
+ ] }) }),
222
+ /* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
223
+ hasNotes ? /* @__PURE__ */ jsx("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
224
+ ] }, field.name)) })
225
+ ] }) }) });
226
+ }
227
+ function UnreadableBlock({ entry }) {
228
+ return /* @__PURE__ */ jsxs("div", { className: "my-2", "data-type": entry.type, children: [
229
+ /* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
230
+ entry.type,
231
+ " (unrecognised shape)"
232
+ ] }),
233
+ /* @__PURE__ */ jsx(
234
+ "pre",
235
+ {
236
+ tabIndex: 0,
237
+ className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
238
+ children: safeStringify(entry.data)
239
+ }
240
+ )
241
+ ] });
242
+ }
243
+ function safeStringify(value) {
244
+ try {
245
+ return JSON.stringify(value, null, 2);
246
+ } catch {
247
+ return String(value);
248
+ }
249
+ }
250
+ var narrativeBlockRenderers = {
251
+ [FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ jsx(FileTreeBlock, { entry }),
252
+ [DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ jsx(DataModelBlock, { entry })
253
+ };
254
+
255
+ // src/context/ReportRoot.tsx
256
+ import { jsx as jsx2 } from "react/jsx-runtime";
257
+ var EMPTY_RENDERERS = {};
258
+ function ReportRoot({
259
+ report,
260
+ customRenderers,
261
+ renderers,
262
+ children
263
+ }) {
264
+ const value = useMemo(
265
+ () => ({
266
+ report,
267
+ // Narrative blocks render everywhere the report does (static SSR, the
268
+ // island, Astro) without every host wiring them up. A host renderer for
269
+ // the same type still wins.
270
+ customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
271
+ renderers: renderers ?? EMPTY_RENDERERS
272
+ }),
273
+ [report, customRenderers, renderers]
274
+ );
275
+ return /* @__PURE__ */ jsx2(ReportContext.Provider, { value, children });
276
+ }
277
+
278
+ // src/hooks/useReport.ts
279
+ import { useContext } from "react";
280
+ function useReport() {
281
+ const ctx = useContext(ReportContext);
282
+ if (!ctx) {
283
+ throw new Error(
284
+ "useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
285
+ );
286
+ }
287
+ return ctx.report;
288
+ }
289
+
290
+ // src/components/ReportScenario.tsx
291
+ import { formatDuration as formatDuration3 } from "executable-stories-core/utils/duration";
292
+
293
+ // src/components/ReportSteps.tsx
294
+ import { formatDuration } from "executable-stories-core/utils/duration";
295
+
296
+ // src/components/doc/DocNote.tsx
137
297
  import { jsx as jsx3 } from "react/jsx-runtime";
298
+ function DocNote({ entry }) {
299
+ return /* @__PURE__ */ jsx3("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
300
+ }
301
+
302
+ // src/components/doc/DocTag.tsx
303
+ import { jsx as jsx4 } from "react/jsx-runtime";
138
304
  function DocTag({ entry }) {
139
- 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)) });
305
+ 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)) });
140
306
  }
141
307
 
142
308
  // src/components/doc/DocKv.tsx
143
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
309
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
144
310
  function formatValue(value) {
145
311
  if (value === null) return "null";
146
312
  if (typeof value === "string") return value;
@@ -152,19 +318,19 @@ function formatValue(value) {
152
318
  }
153
319
  }
154
320
  function DocKv({ entry }) {
155
- return /* @__PURE__ */ jsxs("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
156
- /* @__PURE__ */ jsx4("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
157
- /* @__PURE__ */ jsx4("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
321
+ return /* @__PURE__ */ jsxs2("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
322
+ /* @__PURE__ */ jsx5("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
323
+ /* @__PURE__ */ jsx5("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
158
324
  ] });
159
325
  }
160
326
 
161
327
  // src/hooks/useRenderers.ts
162
328
  import { useContext as useContext2 } from "react";
163
- var EMPTY_CUSTOM2 = {};
329
+ var EMPTY_CUSTOM = {};
164
330
  var EMPTY_RENDERERS2 = {};
165
331
  function useCustomRenderers() {
166
332
  const ctx = useContext2(ReportContext);
167
- return ctx?.customRenderers ?? EMPTY_CUSTOM2;
333
+ return ctx?.customRenderers ?? EMPTY_CUSTOM;
168
334
  }
169
335
  function useBuiltinRenderers() {
170
336
  const ctx = useContext2(ReportContext);
@@ -172,37 +338,37 @@ function useBuiltinRenderers() {
172
338
  }
173
339
 
174
340
  // src/components/doc/DocCode.tsx
175
- import { Fragment, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
341
+ import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
176
342
  function CodeFigure({ label, children }) {
177
- return /* @__PURE__ */ jsxs2("figure", { className: "my-3", children: [
178
- label ? /* @__PURE__ */ jsx5("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
179
- /* @__PURE__ */ jsx5("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
343
+ return /* @__PURE__ */ jsxs3("figure", { className: "my-3", children: [
344
+ label ? /* @__PURE__ */ jsx6("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
345
+ /* @__PURE__ */ jsx6("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
180
346
  ] });
181
347
  }
182
348
  function DocCode({ entry }) {
183
349
  const renderers = useBuiltinRenderers();
184
350
  if (renderers.code) {
185
- return /* @__PURE__ */ jsx5(Fragment, { children: renderers.code(entry) });
351
+ return /* @__PURE__ */ jsx6(Fragment, { children: renderers.code(entry) });
186
352
  }
187
- 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 }) });
353
+ 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 }) });
188
354
  }
189
355
 
190
356
  // src/components/doc/DocTable.tsx
191
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
357
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
192
358
  function DocTable({ entry }) {
193
- return /* @__PURE__ */ jsxs3("figure", { className: "my-3", children: [
194
- entry.label ? /* @__PURE__ */ jsx6("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
195
- /* @__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: [
196
- /* @__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)) }) }),
197
- /* @__PURE__ */ jsx6("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ jsx6("tr", { children: row.map((cell, j) => /* @__PURE__ */ jsx6("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
359
+ return /* @__PURE__ */ jsxs4("figure", { className: "my-3", children: [
360
+ entry.label ? /* @__PURE__ */ jsx7("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
361
+ /* @__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: [
362
+ /* @__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)) }) }),
363
+ /* @__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)) })
198
364
  ] }) })
199
365
  ] });
200
366
  }
201
367
 
202
368
  // src/components/doc/DocLink.tsx
203
- import { jsx as jsx7 } from "react/jsx-runtime";
369
+ import { jsx as jsx8 } from "react/jsx-runtime";
204
370
  function DocLink({ entry }) {
205
- return /* @__PURE__ */ jsx7(
371
+ return /* @__PURE__ */ jsx8(
206
372
  "a",
207
373
  {
208
374
  className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
@@ -217,26 +383,11 @@ function DocLink({ entry }) {
217
383
  // src/components/doc/DocSection.tsx
218
384
  import { marked } from "marked";
219
385
 
220
- // ../executable-stories-core/dist/utils/url.js
221
- function safeUrl(value) {
222
- if (!value) return void 0;
223
- const trimmed = value.trim();
224
- const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(trimmed);
225
- if (scheme && !/^https?$/i.test(scheme[1] ?? "")) return void 0;
226
- return trimmed;
227
- }
228
- function safeImageUrl(value) {
229
- if (!value) return void 0;
230
- const trimmed = value.trim();
231
- if (/^data:image\//i.test(trimmed)) return trimmed;
232
- return safeUrl(trimmed);
233
- }
234
- function isLocalFsPath(value) {
235
- return /^(?:[/\\]|[A-Za-z]:[/\\])/.test(value);
236
- }
386
+ // src/lib/url.ts
387
+ import { safeUrl, safeImageUrl, isLocalFsPath } from "executable-stories-core/utils/url";
237
388
 
238
389
  // src/components/doc/DocSection.tsx
239
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
390
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
240
391
  function neutralizeUrl(value) {
241
392
  return safeUrl(value) ?? "#";
242
393
  }
@@ -247,12 +398,12 @@ function safeMarkdownHtml(markdown) {
247
398
  function DocSection({ entry }) {
248
399
  const renderers = useBuiltinRenderers();
249
400
  if (renderers.section) {
250
- return /* @__PURE__ */ jsx8(Fragment2, { children: renderers.section(entry) });
401
+ return /* @__PURE__ */ jsx9(Fragment2, { children: renderers.section(entry) });
251
402
  }
252
403
  const html = safeMarkdownHtml(entry.markdown);
253
- return /* @__PURE__ */ jsxs4("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
254
- entry.title ? /* @__PURE__ */ jsx8("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
255
- /* @__PURE__ */ jsx8(
404
+ return /* @__PURE__ */ jsxs5("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
405
+ entry.title ? /* @__PURE__ */ jsx9("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
406
+ /* @__PURE__ */ jsx9(
256
407
  "div",
257
408
  {
258
409
  className: "es-doc-prose prose prose-sm max-w-none",
@@ -263,11 +414,11 @@ function DocSection({ entry }) {
263
414
  }
264
415
 
265
416
  // src/components/doc/DocMermaid.tsx
266
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
417
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
267
418
  function MermaidSource({ entry, ref }) {
268
- return /* @__PURE__ */ jsxs5("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
269
- entry.title ? /* @__PURE__ */ jsx9("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
270
- /* @__PURE__ */ jsx9(
419
+ return /* @__PURE__ */ jsxs6("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
420
+ entry.title ? /* @__PURE__ */ jsx10("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
421
+ /* @__PURE__ */ jsx10(
271
422
  "pre",
272
423
  {
273
424
  "data-mermaid": true,
@@ -281,24 +432,24 @@ function MermaidSource({ entry, ref }) {
281
432
  function DocMermaid({ entry }) {
282
433
  const renderers = useBuiltinRenderers();
283
434
  if (renderers.mermaid) {
284
- return /* @__PURE__ */ jsx9(Fragment3, { children: renderers.mermaid(entry) });
435
+ return /* @__PURE__ */ jsx10(Fragment3, { children: renderers.mermaid(entry) });
285
436
  }
286
- return /* @__PURE__ */ jsx9(MermaidSource, { entry });
437
+ return /* @__PURE__ */ jsx10(MermaidSource, { entry });
287
438
  }
288
439
 
289
440
  // src/components/doc/DocScreenshot.tsx
290
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
441
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
291
442
  function DocScreenshot({ entry }) {
292
443
  const src = isLocalFsPath(entry.path) ? void 0 : safeImageUrl(entry.path);
293
444
  if (!src) {
294
- return /* @__PURE__ */ jsxs6("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
295
- /* @__PURE__ */ jsx10("div", { className: "font-medium", children: "Screenshot unavailable" }),
296
- /* @__PURE__ */ jsx10("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
297
- entry.alt ? /* @__PURE__ */ jsx10("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
445
+ return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
446
+ /* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Screenshot unavailable" }),
447
+ /* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
448
+ entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
298
449
  ] });
299
450
  }
300
- return /* @__PURE__ */ jsxs6("figure", { className: "my-3", children: [
301
- /* @__PURE__ */ jsx10(
451
+ return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
452
+ /* @__PURE__ */ jsx11(
302
453
  "img",
303
454
  {
304
455
  src,
@@ -307,24 +458,24 @@ function DocScreenshot({ entry }) {
307
458
  className: "max-w-full rounded-md border border-border"
308
459
  }
309
460
  ),
310
- entry.alt ? /* @__PURE__ */ jsx10("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
461
+ entry.alt ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
311
462
  ] });
312
463
  }
313
464
 
314
465
  // src/components/doc/DocVideo.tsx
315
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
466
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
316
467
  function DocVideo({ entry }) {
317
468
  if (isLocalFsPath(entry.path)) {
318
- return /* @__PURE__ */ jsxs7("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
319
- /* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Video unavailable" }),
320
- /* @__PURE__ */ jsx11("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
321
- entry.caption ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
469
+ return /* @__PURE__ */ jsxs8("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
470
+ /* @__PURE__ */ jsx12("div", { className: "font-medium", children: "Video unavailable" }),
471
+ /* @__PURE__ */ jsx12("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
472
+ entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
322
473
  ] });
323
474
  }
324
475
  const src = safeUrl(entry.path);
325
476
  const poster = safeUrl(entry.poster);
326
- return /* @__PURE__ */ jsxs7("figure", { className: "my-3", children: [
327
- /* @__PURE__ */ jsx11(
477
+ return /* @__PURE__ */ jsxs8("figure", { className: "my-3", children: [
478
+ /* @__PURE__ */ jsx12(
328
479
  "video",
329
480
  {
330
481
  controls: true,
@@ -334,21 +485,21 @@ function DocVideo({ entry }) {
334
485
  className: "max-w-full rounded-md border border-border"
335
486
  }
336
487
  ),
337
- entry.caption ? /* @__PURE__ */ jsx11("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
488
+ entry.caption ? /* @__PURE__ */ jsx12("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
338
489
  ] });
339
490
  }
340
491
 
341
492
  // src/components/doc/DocHtml.tsx
342
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
493
+ import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
343
494
  function DocHtml({ entry }) {
344
495
  const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
345
496
  const title = entry.title ?? "Embedded HTML";
346
497
  const safeHref = safeUrl(entry.url ?? entry.path);
347
498
  const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
348
- return /* @__PURE__ */ jsxs8("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
349
- /* @__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: [
350
- /* @__PURE__ */ jsx12("span", { children: entry.title ?? "HTML" }),
351
- safeHref ? /* @__PURE__ */ jsx12(
499
+ return /* @__PURE__ */ jsxs9("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
500
+ /* @__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: [
501
+ /* @__PURE__ */ jsx13("span", { children: entry.title ?? "HTML" }),
502
+ safeHref ? /* @__PURE__ */ jsx13(
352
503
  "a",
353
504
  {
354
505
  href: safeHref,
@@ -361,7 +512,7 @@ function DocHtml({ entry }) {
361
512
  }
362
513
  ) : null
363
514
  ] }),
364
- /* @__PURE__ */ jsx12(
515
+ /* @__PURE__ */ jsx13(
365
516
  "iframe",
366
517
  {
367
518
  ...frameProps,
@@ -376,7 +527,7 @@ function DocHtml({ entry }) {
376
527
  }
377
528
 
378
529
  // src/components/doc/DocState.tsx
379
- import { jsx as jsx13 } from "react/jsx-runtime";
530
+ import { jsx as jsx14 } from "react/jsx-runtime";
380
531
  function DocState({ entry }) {
381
532
  let json;
382
533
  try {
@@ -384,23 +535,23 @@ function DocState({ entry }) {
384
535
  } catch {
385
536
  json = String(entry.value);
386
537
  }
387
- return /* @__PURE__ */ jsx13(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx13("code", { className: "font-mono text-xs language-json", children: json }) });
538
+ return /* @__PURE__ */ jsx14(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ jsx14("code", { className: "font-mono text-xs language-json", children: json }) });
388
539
  }
389
540
 
390
541
  // src/components/doc/DocCustom.tsx
391
- import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
542
+ import { Fragment as Fragment4, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
392
543
  function DocCustom({ entry }) {
393
544
  const renderers = useCustomRenderers();
394
545
  const renderer = renderers[entry.type];
395
546
  if (renderer) {
396
- return /* @__PURE__ */ jsx14(Fragment4, { children: renderer(entry) });
547
+ return /* @__PURE__ */ jsx15(Fragment4, { children: renderer(entry) });
397
548
  }
398
- return /* @__PURE__ */ jsxs9("div", { className: "my-2", "data-type": entry.type, children: [
399
- /* @__PURE__ */ jsx14("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
400
- /* @__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) })
549
+ return /* @__PURE__ */ jsxs10("div", { className: "my-2", "data-type": entry.type, children: [
550
+ /* @__PURE__ */ jsx15("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
551
+ /* @__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) })
401
552
  ] });
402
553
  }
403
- function safeStringify(value) {
554
+ function safeStringify2(value) {
404
555
  try {
405
556
  return JSON.stringify(value, null, 2);
406
557
  } catch {
@@ -409,47 +560,47 @@ function safeStringify(value) {
409
560
  }
410
561
 
411
562
  // src/components/doc/DocEntry.tsx
412
- import { jsx as jsx15 } from "react/jsx-runtime";
563
+ import { jsx as jsx16 } from "react/jsx-runtime";
413
564
  function DocEntry({ entry }) {
414
565
  switch (entry.kind) {
415
566
  case "note":
416
- return /* @__PURE__ */ jsx15(DocNote, { entry });
567
+ return /* @__PURE__ */ jsx16(DocNote, { entry });
417
568
  case "tag":
418
- return /* @__PURE__ */ jsx15(DocTag, { entry });
569
+ return /* @__PURE__ */ jsx16(DocTag, { entry });
419
570
  case "kv":
420
- return /* @__PURE__ */ jsx15(DocKv, { entry });
571
+ return /* @__PURE__ */ jsx16(DocKv, { entry });
421
572
  case "code":
422
- return /* @__PURE__ */ jsx15(DocCode, { entry });
573
+ return /* @__PURE__ */ jsx16(DocCode, { entry });
423
574
  case "table":
424
- return /* @__PURE__ */ jsx15(DocTable, { entry });
575
+ return /* @__PURE__ */ jsx16(DocTable, { entry });
425
576
  case "link":
426
- return /* @__PURE__ */ jsx15(DocLink, { entry });
577
+ return /* @__PURE__ */ jsx16(DocLink, { entry });
427
578
  case "section":
428
- return /* @__PURE__ */ jsx15(DocSection, { entry });
579
+ return /* @__PURE__ */ jsx16(DocSection, { entry });
429
580
  case "mermaid":
430
- return /* @__PURE__ */ jsx15(DocMermaid, { entry });
581
+ return /* @__PURE__ */ jsx16(DocMermaid, { entry });
431
582
  case "screenshot":
432
- return /* @__PURE__ */ jsx15(DocScreenshot, { entry });
583
+ return /* @__PURE__ */ jsx16(DocScreenshot, { entry });
433
584
  case "video":
434
- return /* @__PURE__ */ jsx15(DocVideo, { entry });
585
+ return /* @__PURE__ */ jsx16(DocVideo, { entry });
435
586
  case "html":
436
- return /* @__PURE__ */ jsx15(DocHtml, { entry });
587
+ return /* @__PURE__ */ jsx16(DocHtml, { entry });
437
588
  case "state":
438
- return /* @__PURE__ */ jsx15(DocState, { entry });
589
+ return /* @__PURE__ */ jsx16(DocState, { entry });
439
590
  case "custom":
440
- return /* @__PURE__ */ jsx15(DocCustom, { entry });
591
+ return /* @__PURE__ */ jsx16(DocCustom, { entry });
441
592
  }
442
593
  }
443
594
 
444
595
  // src/components/ReportDocEntries.tsx
445
- import { Fragment as Fragment5, jsx as jsx16 } from "react/jsx-runtime";
596
+ import { Fragment as Fragment5, jsx as jsx17 } from "react/jsx-runtime";
446
597
  function ReportDocEntries({ entries }) {
447
598
  if (entries.length === 0) return null;
448
- return /* @__PURE__ */ jsx16(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx16(DocEntry, { entry }, i)) });
599
+ return /* @__PURE__ */ jsx17(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx17(DocEntry, { entry }, i)) });
449
600
  }
450
601
 
451
602
  // src/lib/step-params.tsx
452
- import { jsx as jsx17 } from "react/jsx-runtime";
603
+ import { jsx as jsx18 } from "react/jsx-runtime";
453
604
  var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
454
605
  function highlightStepParams(text) {
455
606
  const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
@@ -461,7 +612,7 @@ function highlightStepParams(text) {
461
612
  const end = start + match[0].length;
462
613
  if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
463
614
  nodes.push(
464
- /* @__PURE__ */ jsx17("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
615
+ /* @__PURE__ */ jsx18("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
465
616
  );
466
617
  lastIndex = end;
467
618
  });
@@ -470,7 +621,7 @@ function highlightStepParams(text) {
470
621
  }
471
622
 
472
623
  // src/components/ReportSteps.tsx
473
- import { jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
624
+ import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
474
625
  var STEP_GLYPH = {
475
626
  passed: "\u2713",
476
627
  failed: "\u2717",
@@ -485,21 +636,21 @@ var STEP_GLYPH_COLOR = {
485
636
  };
486
637
  function ReportSteps({ scenario }) {
487
638
  if (scenario.steps.length === 0) return null;
488
- return /* @__PURE__ */ jsx18("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx18(ReportStepItem, { step }, step.id)) });
639
+ return /* @__PURE__ */ jsx19("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ jsx19(ReportStepItem, { step }, step.id)) });
489
640
  }
490
641
  function ReportStepItem({ step }) {
491
642
  const kw = step.keyword.toLowerCase();
492
643
  const continuation = kw === "and" || kw === "but";
493
- return /* @__PURE__ */ jsxs10(
644
+ return /* @__PURE__ */ jsxs11(
494
645
  "li",
495
646
  {
496
647
  id: step.id,
497
648
  "data-status": step.status,
498
649
  className: cn("py-1.5 text-[0.8125rem] leading-normal", continuation && "pl-5"),
499
650
  children: [
500
- /* @__PURE__ */ jsxs10("div", { className: "flex items-baseline gap-2", children: [
501
- /* @__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" }),
502
- /* @__PURE__ */ jsx18(
651
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-baseline gap-2", children: [
652
+ /* @__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" }),
653
+ /* @__PURE__ */ jsx19(
503
654
  "span",
504
655
  {
505
656
  className: cn(
@@ -509,10 +660,10 @@ function ReportStepItem({ step }) {
509
660
  children: step.keyword
510
661
  }
511
662
  ),
512
- /* @__PURE__ */ jsx18("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
513
- step.durationMs > 0 ? /* @__PURE__ */ jsx18("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
663
+ /* @__PURE__ */ jsx19("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
664
+ step.durationMs > 0 ? /* @__PURE__ */ jsx19("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(step.durationMs) }) : null
514
665
  ] }),
515
- step.errorMessage ? /* @__PURE__ */ jsx18(
666
+ step.errorMessage ? /* @__PURE__ */ jsx19(
516
667
  "pre",
517
668
  {
518
669
  role: "alert",
@@ -520,132 +671,17 @@ function ReportStepItem({ step }) {
520
671
  children: step.errorMessage
521
672
  }
522
673
  ) : null,
523
- step.docEntries.length > 0 ? /* @__PURE__ */ jsx18("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx18(ReportDocEntries, { entries: step.docEntries }) }) : null
674
+ step.docEntries.length > 0 ? /* @__PURE__ */ jsx19("div", { "data-es-docs": true, className: "mt-2 ml-6", children: /* @__PURE__ */ jsx19(ReportDocEntries, { entries: step.docEntries }) }) : null
524
675
  ]
525
676
  }
526
677
  );
527
678
  }
528
679
 
529
- // ../executable-stories-core/dist/state-diff.js
530
- var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
531
- function deepEqual(a, b) {
532
- if (Object.is(a, b)) return true;
533
- if (Array.isArray(a) && Array.isArray(b)) {
534
- return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
535
- }
536
- if (isPlainObject(a) && isPlainObject(b)) {
537
- const ak = Object.keys(a);
538
- const bk = Object.keys(b);
539
- return ak.length === bk.length && ak.every((k) => k in b && deepEqual(a[k], b[k]));
540
- }
541
- return false;
542
- }
543
- var joinKey = (path, key) => path === "" ? key : `${path}.${key}`;
544
- function walk(path, before, after, out) {
545
- if (Object.is(before, after)) return;
546
- if (Array.isArray(before) && Array.isArray(after)) {
547
- const shared = Math.min(before.length, after.length);
548
- for (let i = 0; i < shared; i++) walk(`${path}[${i}]`, before[i], after[i], out);
549
- for (let i = shared; i < after.length; i++) out.push({ path: `${path}[${i}]`, kind: "added", after: after[i] });
550
- for (let i = shared; i < before.length; i++) out.push({ path: `${path}[${i}]`, kind: "removed", before: before[i] });
551
- return;
552
- }
553
- if (isPlainObject(before) && isPlainObject(after)) {
554
- for (const key of Object.keys(after)) {
555
- const p = joinKey(path, key);
556
- if (key in before) walk(p, before[key], after[key], out);
557
- else out.push({ path: p, kind: "added", after: after[key] });
558
- }
559
- for (const key of Object.keys(before)) {
560
- if (!(key in after)) out.push({ path: joinKey(path, key), kind: "removed", before: before[key] });
561
- }
562
- return;
563
- }
564
- if (!deepEqual(before, after)) out.push({ path, kind: "changed", before, after });
565
- }
566
- function diffStateValues(before, after) {
567
- const out = [];
568
- walk("", before, after, out);
569
- return out;
570
- }
571
- function formatStateValue(value, maxLen = 60) {
572
- const s = value === void 0 ? "undefined" : JSON.stringify(value);
573
- return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
574
- }
575
- function summarizeStateChanges(changes) {
576
- return changes.map((c) => {
577
- const at = c.path === "" ? "(root)" : c.path;
578
- switch (c.kind) {
579
- case "added":
580
- return `+ ${at}: ${formatStateValue(c.after)}`;
581
- case "removed":
582
- return `\u2212 ${at}`;
583
- case "changed":
584
- return `${at}: ${formatStateValue(c.before)} \u2192 ${formatStateValue(c.after)}`;
585
- }
586
- });
587
- }
588
-
589
- // ../executable-stories-core/dist/storyboard.js
590
- function findScreenshot(entries) {
591
- for (const entry of entries) {
592
- if (entry.kind === "screenshot") return entry;
593
- if (entry.children) {
594
- const nested = findScreenshot(entry.children);
595
- if (nested) return nested;
596
- }
597
- }
598
- return void 0;
599
- }
600
- function findStates(entries, out = []) {
601
- for (const entry of entries) {
602
- if (entry.kind === "state") out.push(entry);
603
- if (entry.children) findStates(entry.children, out);
604
- }
605
- return out;
606
- }
607
- var laneOf = (label) => label ?? "";
608
- function extractStoryboardFrames(scenario) {
609
- const frames = [];
610
- const laneOrder = [];
611
- const previous = /* @__PURE__ */ new Map();
612
- for (const step of scenario.steps) {
613
- const shot = findScreenshot(step.docEntries);
614
- const stateDocs = findStates(step.docEntries);
615
- if (!shot && stateDocs.length === 0) continue;
616
- const byLane = /* @__PURE__ */ new Map();
617
- for (const doc of stateDocs) {
618
- const lane = laneOf(doc.label);
619
- if (!byLane.has(lane) && !laneOrder.includes(lane)) laneOrder.push(lane);
620
- byLane.set(lane, doc);
621
- }
622
- const states = [];
623
- for (const lane of laneOrder) {
624
- const doc = byLane.get(lane);
625
- if (!doc) continue;
626
- const card = {
627
- ...doc.label !== void 0 && { label: doc.label },
628
- value: doc.value
629
- };
630
- if (previous.has(lane)) card.changes = diffStateValues(previous.get(lane), doc.value);
631
- previous.set(lane, doc.value);
632
- states.push(card);
633
- }
634
- frames.push({
635
- stepId: step.id,
636
- stepIndex: step.index,
637
- keyword: step.keyword,
638
- text: step.text,
639
- status: step.status,
640
- ...shot && { screenshot: { path: shot.path, ...shot.alt !== void 0 && { alt: shot.alt } } },
641
- states
642
- });
643
- }
644
- return frames;
645
- }
646
-
647
680
  // src/components/ReportStoryboard.tsx
648
- import { jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
681
+ import { extractStoryboardFrames } from "executable-stories-core/storyboard";
682
+ import { summarizeStateChanges } from "executable-stories-core/state-diff";
683
+ import { isLocalFsPath as isLocalFsPath2, safeImageUrl as safeImageUrl2 } from "executable-stories-core/utils/url";
684
+ import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
649
685
  var FRAME_GLYPH = {
650
686
  passed: "\u2713",
651
687
  failed: "\u2717",
@@ -661,46 +697,46 @@ var FRAME_GLYPH_COLOR = {
661
697
  function ReportStoryboard({ scenario }) {
662
698
  const frames = extractStoryboardFrames(scenario);
663
699
  if (frames.length < 2) return null;
664
- return /* @__PURE__ */ jsxs11("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
665
- /* @__PURE__ */ jsx19("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
666
- /* @__PURE__ */ jsx19("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx19(StoryboardFrameItem, { frame }, frame.stepId)) })
700
+ return /* @__PURE__ */ jsxs12("section", { "data-slot": "storyboard", "aria-label": "Storyboard", className: "mb-3", children: [
701
+ /* @__PURE__ */ jsx20("h4", { className: "mb-1.5 text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Storyboard" }),
702
+ /* @__PURE__ */ jsx20("ol", { className: "flex items-start gap-3 overflow-x-auto pb-2", children: frames.map((frame) => /* @__PURE__ */ jsx20(StoryboardFrameItem, { frame }, frame.stepId)) })
667
703
  ] });
668
704
  }
669
705
  function StoryboardFrameItem({ frame }) {
670
706
  const label = `${frame.keyword} ${frame.text}`;
671
707
  const hasStates = frame.states.length > 0;
672
- const caption = /* @__PURE__ */ jsxs11("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
673
- /* @__PURE__ */ jsx19("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
674
- /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
675
- /* @__PURE__ */ jsx19("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
708
+ const caption = /* @__PURE__ */ jsxs12("p", { className: "mt-1 flex items-baseline gap-1.5 text-[0.6875rem] leading-snug", children: [
709
+ /* @__PURE__ */ jsx20("span", { "aria-hidden": true, className: cn("shrink-0", FRAME_GLYPH_COLOR[frame.status]), children: FRAME_GLYPH[frame.status] ?? "\u25CB" }),
710
+ /* @__PURE__ */ jsxs12("span", { className: "min-w-0", children: [
711
+ /* @__PURE__ */ jsx20("span", { className: "font-mono font-semibold text-keyword", children: frame.keyword }),
676
712
  " ",
677
- /* @__PURE__ */ jsx19("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
713
+ /* @__PURE__ */ jsx20("span", { className: "text-muted-foreground", children: frame.screenshot?.alt ?? frame.text })
678
714
  ] })
679
715
  ] });
680
716
  return (
681
717
  // State cards need room for diff rows; screenshot-only frames keep the
682
718
  // original narrow film-cell width (and DOM) unchanged.
683
- /* @__PURE__ */ jsxs11("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
684
- frame.screenshot ? /* @__PURE__ */ jsx19(FrameScreenshot, { frame, label }) : null,
685
- 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,
719
+ /* @__PURE__ */ jsxs12("li", { className: cn("shrink-0", hasStates ? "w-64" : "w-40"), children: [
720
+ frame.screenshot ? /* @__PURE__ */ jsx20(FrameScreenshot, { frame, label }) : null,
721
+ 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,
686
722
  frame.screenshot ? caption : (
687
723
  // No screenshot means no image anchor, so the caption carries the
688
724
  // jump-to-step link instead.
689
- /* @__PURE__ */ jsx19("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
725
+ /* @__PURE__ */ jsx20("a", { href: `#${frame.stepId}`, "aria-label": `Jump to step: ${label}`, className: "block hover:underline", children: caption })
690
726
  )
691
727
  ] })
692
728
  );
693
729
  }
694
730
  function FrameScreenshot({ frame, label }) {
695
731
  const path = frame.screenshot?.path ?? "";
696
- const src = isLocalFsPath(path) ? void 0 : safeImageUrl(path);
697
- return /* @__PURE__ */ jsx19(
732
+ const src = isLocalFsPath2(path) ? void 0 : safeImageUrl2(path);
733
+ return /* @__PURE__ */ jsx20(
698
734
  "a",
699
735
  {
700
736
  href: `#${frame.stepId}`,
701
737
  "aria-label": `Jump to step: ${label}`,
702
738
  className: "block overflow-hidden rounded-md border border-border transition-colors hover:border-ring",
703
- children: src ? /* @__PURE__ */ jsx19(
739
+ children: src ? /* @__PURE__ */ jsx20(
704
740
  "img",
705
741
  {
706
742
  src,
@@ -708,7 +744,7 @@ function FrameScreenshot({ frame, label }) {
708
744
  loading: "lazy",
709
745
  className: "h-24 w-full bg-muted object-cover object-top"
710
746
  }
711
- ) : /* @__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" })
747
+ ) : /* @__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" })
712
748
  }
713
749
  );
714
750
  }
@@ -727,12 +763,12 @@ function prettyJson(value) {
727
763
  function StateCardView({ card }) {
728
764
  const json = prettyJson(card.value);
729
765
  const firstCapture = card.changes === void 0;
730
- return /* @__PURE__ */ jsxs11("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
731
- /* @__PURE__ */ jsxs11("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
732
- /* @__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" }),
733
- firstCapture ? /* @__PURE__ */ jsx19("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
766
+ return /* @__PURE__ */ jsxs12("div", { "data-slot": "state-card", className: "rounded-md border border-border bg-card p-2", children: [
767
+ /* @__PURE__ */ jsxs12("p", { className: "flex flex-wrap items-baseline gap-1.5", children: [
768
+ /* @__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" }),
769
+ firstCapture ? /* @__PURE__ */ jsx20("span", { className: "text-[0.625rem] text-muted-foreground italic", children: "first capture" }) : null
734
770
  ] }),
735
- !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(
771
+ !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(
736
772
  "li",
737
773
  {
738
774
  className: cn(
@@ -742,15 +778,15 @@ function StateCardView({ card }) {
742
778
  children: summarizeStateChanges([change])[0]
743
779
  },
744
780
  i
745
- )) }) : /* @__PURE__ */ jsx19("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
746
- /* @__PURE__ */ jsxs11(
781
+ )) }) : /* @__PURE__ */ jsx20("p", { className: "mt-1.5 text-[0.625rem] text-muted-foreground", children: "No changes" })),
782
+ /* @__PURE__ */ jsxs12(
747
783
  "details",
748
784
  {
749
785
  open: firstCapture && json.length <= 240,
750
786
  className: cn(firstCapture ? "mt-1.5" : "mt-1"),
751
787
  children: [
752
- /* @__PURE__ */ jsx19("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
753
- /* @__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 })
788
+ /* @__PURE__ */ jsx20("summary", { className: "cursor-pointer text-[0.625rem] text-muted-foreground hover:text-foreground", children: "Snapshot" }),
789
+ /* @__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 })
754
790
  ]
755
791
  }
756
792
  )
@@ -758,17 +794,17 @@ function StateCardView({ card }) {
758
794
  }
759
795
 
760
796
  // src/components/ReportAttachments.tsx
761
- import { Fragment as Fragment6, jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
797
+ import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
762
798
  function ReportAttachments({ attachments }) {
763
799
  if (attachments.length === 0) return null;
764
800
  return (
765
801
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
766
802
  // blocks don't create duplicate "Attachments" region landmarks.
767
- /* @__PURE__ */ jsx20("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
803
+ /* @__PURE__ */ jsx21("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
768
804
  const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
769
805
  const isImage = att.mediaType.startsWith("image/");
770
- return /* @__PURE__ */ jsx20("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs12(Fragment6, { children: [
771
- /* @__PURE__ */ jsx20(
806
+ return /* @__PURE__ */ jsx21("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
807
+ /* @__PURE__ */ jsx21(
772
808
  "img",
773
809
  {
774
810
  src: dataUri,
@@ -777,8 +813,8 @@ function ReportAttachments({ attachments }) {
777
813
  className: "max-w-full rounded-md border border-border"
778
814
  }
779
815
  ),
780
- /* @__PURE__ */ jsx20("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
781
- ] }) : /* @__PURE__ */ jsxs12(
816
+ /* @__PURE__ */ jsx21("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
817
+ ] }) : /* @__PURE__ */ jsxs13(
782
818
  "a",
783
819
  {
784
820
  href: dataUri,
@@ -787,7 +823,7 @@ function ReportAttachments({ attachments }) {
787
823
  children: [
788
824
  att.name,
789
825
  " ",
790
- /* @__PURE__ */ jsxs12("span", { className: "text-muted-foreground", children: [
826
+ /* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground", children: [
791
827
  "(",
792
828
  att.mediaType,
793
829
  ")"
@@ -799,6 +835,9 @@ function ReportAttachments({ attachments }) {
799
835
  );
800
836
  }
801
837
 
838
+ // src/components/ReportTrace.tsx
839
+ import { formatDuration as formatDuration2 } from "executable-stories-core/utils/duration";
840
+
802
841
  // src/lib/trace.ts
803
842
  var VALID = /* @__PURE__ */ new Set(["ok", "error", "unset"]);
804
843
  var safeStatus = (s) => VALID.has(s) ? s : "unset";
@@ -851,15 +890,15 @@ function toTraceRows(spans) {
851
890
  roots.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
852
891
  const rows = [];
853
892
  const seen = /* @__PURE__ */ new Set();
854
- const walk2 = (node, depth) => {
893
+ const walk = (node, depth) => {
855
894
  if (seen.has(node.span.spanId)) return;
856
895
  seen.add(node.span.spanId);
857
896
  rows.push({ ...node.span, depth });
858
- for (const child of node.children) walk2(child, depth + 1);
897
+ for (const child of node.children) walk(child, depth + 1);
859
898
  };
860
- for (const root of roots) walk2(root, 0);
899
+ for (const root of roots) walk(root, 0);
861
900
  for (const node of byId.values()) {
862
- if (!seen.has(node.span.spanId)) walk2(node, 0);
901
+ if (!seen.has(node.span.spanId)) walk(node, 0);
863
902
  }
864
903
  return rows;
865
904
  }
@@ -875,7 +914,7 @@ function traceWindow(rows) {
875
914
  }
876
915
 
877
916
  // src/components/ReportTrace.tsx
878
- import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
917
+ import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
879
918
  var BAR_COLOR = {
880
919
  ok: "bg-pass",
881
920
  error: "bg-fail",
@@ -886,21 +925,21 @@ function ReportTrace({ spans }) {
886
925
  const rows = toTraceRows(spans);
887
926
  if (rows.length === 0) return null;
888
927
  const { minStartMs, totalMs } = traceWindow(rows);
889
- return /* @__PURE__ */ jsxs13("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
890
- /* @__PURE__ */ jsxs13("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
928
+ return /* @__PURE__ */ jsxs14("details", { className: "mt-3 rounded-md border border-border bg-muted/30", children: [
929
+ /* @__PURE__ */ jsxs14("summary", { className: "cursor-pointer px-3 py-2 text-xs font-medium text-muted-foreground", children: [
891
930
  "Trace (",
892
931
  rows.length,
893
932
  " span",
894
933
  rows.length === 1 ? "" : "s",
895
934
  ", ",
896
- formatDuration(totalMs),
935
+ formatDuration2(totalMs),
897
936
  ")"
898
937
  ] }),
899
- /* @__PURE__ */ jsx21("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
938
+ /* @__PURE__ */ jsx22("ul", { className: "flex flex-col gap-0.5 px-3 pb-3 font-mono text-[0.6875rem]", children: rows.map((row, i) => {
900
939
  const leftPct = (row.startTimeMs - minStartMs) / totalMs * 100;
901
940
  const widthPct = Math.max(row.durationMs / totalMs * 100, 0.5);
902
- return /* @__PURE__ */ jsxs13("li", { className: "flex items-center gap-2", children: [
903
- /* @__PURE__ */ jsx21(
941
+ return /* @__PURE__ */ jsxs14("li", { className: "flex items-center gap-2", children: [
942
+ /* @__PURE__ */ jsx22(
904
943
  "span",
905
944
  {
906
945
  className: "min-w-0 flex-1 truncate text-foreground",
@@ -909,14 +948,14 @@ function ReportTrace({ spans }) {
909
948
  children: row.name
910
949
  }
911
950
  ),
912
- /* @__PURE__ */ jsx21("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx21(
951
+ /* @__PURE__ */ jsx22("span", { className: "relative h-3 w-1/2 shrink-0 overflow-hidden rounded-sm bg-border/40", children: /* @__PURE__ */ jsx22(
913
952
  "span",
914
953
  {
915
954
  className: cn("absolute top-0 h-full rounded-sm", BAR_COLOR[row.status]),
916
955
  style: { left: `${leftPct}%`, width: `${widthPct}%` }
917
956
  }
918
957
  ) }),
919
- /* @__PURE__ */ jsx21("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration(row.durationMs) })
958
+ /* @__PURE__ */ jsx22("span", { className: "w-16 shrink-0 text-right text-muted-foreground", children: formatDuration2(row.durationMs) })
920
959
  ] }, `${row.spanId}-${i}`);
921
960
  }) })
922
961
  ] });
@@ -967,7 +1006,7 @@ function flakinessOf(entries) {
967
1006
  }
968
1007
 
969
1008
  // src/components/ScenarioRunHistory.tsx
970
- import { Fragment as Fragment7, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1009
+ import { Fragment as Fragment7, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
971
1010
  var MAX_DOTS = 10;
972
1011
  var DOT_COLOR = {
973
1012
  passed: "bg-pass",
@@ -987,16 +1026,16 @@ function ScenarioRunHistory({ scenarioId }) {
987
1026
  const recent = entries.slice(-MAX_DOTS);
988
1027
  const flaky = flakinessOf(recent) === "flaky";
989
1028
  const summary = flaky ? `${describeRunHistory(recent)} \xB7 Flaky` : describeRunHistory(recent);
990
- return /* @__PURE__ */ jsxs14(Fragment7, { children: [
991
- flaky ? /* @__PURE__ */ jsx22(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
992
- /* @__PURE__ */ jsx22(
1029
+ return /* @__PURE__ */ jsxs15(Fragment7, { children: [
1030
+ flaky ? /* @__PURE__ */ jsx23(Badge, { variant: "pending", "aria-label": "Flaky: status flips between recent runs", title: summary, children: "Flaky" }) : null,
1031
+ /* @__PURE__ */ jsx23(
993
1032
  "span",
994
1033
  {
995
1034
  role: "img",
996
1035
  "aria-label": `Run history: ${summary}`,
997
1036
  title: summary,
998
1037
  className: "flex shrink-0 items-center gap-[3px]",
999
- children: recent.map((e, i) => /* @__PURE__ */ jsx22(
1038
+ children: recent.map((e, i) => /* @__PURE__ */ jsx23(
1000
1039
  "span",
1001
1040
  {
1002
1041
  "aria-hidden": true,
@@ -1027,6 +1066,7 @@ function useCollapse() {
1027
1066
 
1028
1067
  // src/interactive/scenario-actions.tsx
1029
1068
  import { createContext as createContext4, useContext as useContext5 } from "react";
1069
+ import { scenarioToMarkdown as coreScenarioToMarkdown } from "executable-stories-core/utils/scenario-markdown";
1030
1070
  var ScenarioActionsContext = createContext4(null);
1031
1071
  var ScenarioActionsProvider = ScenarioActionsContext.Provider;
1032
1072
  function useScenarioActions() {
@@ -1035,9 +1075,9 @@ function useScenarioActions() {
1035
1075
 
1036
1076
  // src/components/ui/card.tsx
1037
1077
  import "react";
1038
- import { jsx as jsx23 } from "react/jsx-runtime";
1078
+ import { jsx as jsx24 } from "react/jsx-runtime";
1039
1079
  function Card({ className, ...props }) {
1040
- return /* @__PURE__ */ jsx23(
1080
+ return /* @__PURE__ */ jsx24(
1041
1081
  "div",
1042
1082
  {
1043
1083
  "data-slot": "card",
@@ -1050,7 +1090,7 @@ function Card({ className, ...props }) {
1050
1090
  );
1051
1091
  }
1052
1092
  function CardHeader({ className, ...props }) {
1053
- return /* @__PURE__ */ jsx23(
1093
+ return /* @__PURE__ */ jsx24(
1054
1094
  "div",
1055
1095
  {
1056
1096
  "data-slot": "card-header",
@@ -1063,7 +1103,7 @@ function CardHeader({ className, ...props }) {
1063
1103
  );
1064
1104
  }
1065
1105
  function CardContent({ className, ...props }) {
1066
- return /* @__PURE__ */ jsx23(
1106
+ return /* @__PURE__ */ jsx24(
1067
1107
  "div",
1068
1108
  {
1069
1109
  "data-slot": "card-content",
@@ -1077,13 +1117,13 @@ function CardContent({ className, ...props }) {
1077
1117
  import "react";
1078
1118
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
1079
1119
  import { CheckIcon } from "lucide-react";
1080
- import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
1120
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1081
1121
  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";
1082
1122
  function DropdownMenu(props) {
1083
- return /* @__PURE__ */ jsx24(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1123
+ return /* @__PURE__ */ jsx25(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
1084
1124
  }
1085
1125
  function DropdownMenuTrigger(props) {
1086
- return /* @__PURE__ */ jsx24(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1126
+ return /* @__PURE__ */ jsx25(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
1087
1127
  }
1088
1128
  function DropdownMenuContent({
1089
1129
  className,
@@ -1091,14 +1131,14 @@ function DropdownMenuContent({
1091
1131
  sideOffset = 4,
1092
1132
  ...props
1093
1133
  }) {
1094
- return /* @__PURE__ */ jsx24(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx24(
1134
+ return /* @__PURE__ */ jsx25(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx25(
1095
1135
  MenuPrimitive.Positioner,
1096
1136
  {
1097
1137
  "data-slot": "dropdown-menu-positioner",
1098
1138
  className: "z-50",
1099
1139
  align,
1100
1140
  sideOffset,
1101
- children: /* @__PURE__ */ jsx24(
1141
+ children: /* @__PURE__ */ jsx25(
1102
1142
  MenuPrimitive.Popup,
1103
1143
  {
1104
1144
  "data-slot": "dropdown-menu-content",
@@ -1116,7 +1156,7 @@ function DropdownMenuItem({
1116
1156
  className,
1117
1157
  ...props
1118
1158
  }) {
1119
- return /* @__PURE__ */ jsx24(
1159
+ return /* @__PURE__ */ jsx25(
1120
1160
  MenuPrimitive.Item,
1121
1161
  {
1122
1162
  "data-slot": "dropdown-menu-item",
@@ -1128,7 +1168,7 @@ function DropdownMenuItem({
1128
1168
 
1129
1169
  // src/components/ReportScenario.tsx
1130
1170
  import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
1131
- import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1171
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1132
1172
  var STATUS_META = {
1133
1173
  passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
1134
1174
  failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
@@ -1144,7 +1184,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
1144
1184
  const collapsible = collapse !== null && !hideTitle;
1145
1185
  const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
1146
1186
  const actions = useScenarioActions();
1147
- return /* @__PURE__ */ jsxs16(
1187
+ return /* @__PURE__ */ jsxs17(
1148
1188
  Card,
1149
1189
  {
1150
1190
  id: scenario.id,
@@ -1152,10 +1192,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
1152
1192
  ...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
1153
1193
  className: "gap-0 py-0",
1154
1194
  children: [
1155
- /* @__PURE__ */ jsxs16(CardHeader, { className: "px-4 py-3", children: [
1156
- /* @__PURE__ */ jsxs16("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1157
- hideTitle ? null : /* @__PURE__ */ jsxs16("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1158
- collapsible ? /* @__PURE__ */ jsx25(
1195
+ /* @__PURE__ */ jsxs17(CardHeader, { className: "px-4 py-3", children: [
1196
+ /* @__PURE__ */ jsxs17("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
1197
+ hideTitle ? null : /* @__PURE__ */ jsxs17("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
1198
+ collapsible ? /* @__PURE__ */ jsx26(
1159
1199
  "button",
1160
1200
  {
1161
1201
  type: "button",
@@ -1164,49 +1204,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
1164
1204
  "aria-controls": bodyId,
1165
1205
  "aria-label": `Toggle ${scenario.title}`,
1166
1206
  className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
1167
- children: /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1207
+ children: /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
1168
1208
  }
1169
1209
  ) : null,
1170
- /* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1171
- /* @__PURE__ */ jsx25("span", { children: scenario.title })
1210
+ /* @__PURE__ */ jsx26("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
1211
+ /* @__PURE__ */ jsx26("span", { children: scenario.title })
1172
1212
  ] }),
1173
- /* @__PURE__ */ jsxs16("div", { className: "flex shrink-0 items-center gap-2", children: [
1174
- /* @__PURE__ */ jsx25(ScenarioRunHistory, { scenarioId: scenario.id }),
1175
- scenario.durationMs > 0 ? /* @__PURE__ */ jsx25("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
1176
- meta.showPill ? /* @__PURE__ */ jsx25(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx25("span", { className: "sr-only", children: `Status: ${label}` }),
1177
- actions ? /* @__PURE__ */ jsxs16(DropdownMenu, { children: [
1178
- /* @__PURE__ */ jsx25(
1213
+ /* @__PURE__ */ jsxs17("div", { className: "flex shrink-0 items-center gap-2", children: [
1214
+ /* @__PURE__ */ jsx26(ScenarioRunHistory, { scenarioId: scenario.id }),
1215
+ scenario.durationMs > 0 ? /* @__PURE__ */ jsx26("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration3(scenario.durationMs) }) : null,
1216
+ meta.showPill ? /* @__PURE__ */ jsx26(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx26("span", { className: "sr-only", children: `Status: ${label}` }),
1217
+ actions ? /* @__PURE__ */ jsxs17(DropdownMenu, { children: [
1218
+ /* @__PURE__ */ jsx26(
1179
1219
  DropdownMenuTrigger,
1180
1220
  {
1181
1221
  "aria-label": `Actions for ${scenario.title}`,
1182
1222
  className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
1183
- children: /* @__PURE__ */ jsx25(MoreHorizontalIcon, { "aria-hidden": true })
1223
+ children: /* @__PURE__ */ jsx26(MoreHorizontalIcon, { "aria-hidden": true })
1184
1224
  }
1185
1225
  ),
1186
- /* @__PURE__ */ jsxs16(DropdownMenuContent, { align: "end", children: [
1187
- /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1188
- /* @__PURE__ */ jsx25(LinkIcon, {}),
1226
+ /* @__PURE__ */ jsxs17(DropdownMenuContent, { align: "end", children: [
1227
+ /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
1228
+ /* @__PURE__ */ jsx26(LinkIcon, {}),
1189
1229
  " Copy link"
1190
1230
  ] }),
1191
- /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1192
- /* @__PURE__ */ jsx25(FileTextIcon, {}),
1231
+ /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
1232
+ /* @__PURE__ */ jsx26(FileTextIcon, {}),
1193
1233
  " Copy as Markdown"
1194
1234
  ] }),
1195
- /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1196
- /* @__PURE__ */ jsx25(SparklesIcon, {}),
1235
+ /* @__PURE__ */ jsxs17(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
1236
+ /* @__PURE__ */ jsx26(SparklesIcon, {}),
1197
1237
  " Explain with AI"
1198
1238
  ] })
1199
1239
  ] })
1200
1240
  ] }) : null
1201
1241
  ] })
1202
1242
  ] }),
1203
- 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: [
1204
- scenario.tags.map((t) => /* @__PURE__ */ jsx25("li", { children: /* @__PURE__ */ jsx25(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1205
- (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}`))
1243
+ 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: [
1244
+ scenario.tags.map((t) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsx26(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
1245
+ (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}`))
1206
1246
  ] }) : null
1207
1247
  ] }),
1208
- /* @__PURE__ */ jsxs16(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1209
- scenario.errorMessage ? /* @__PURE__ */ jsx25(
1248
+ /* @__PURE__ */ jsxs17(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
1249
+ scenario.errorMessage ? /* @__PURE__ */ jsx26(
1210
1250
  "pre",
1211
1251
  {
1212
1252
  role: "alert",
@@ -1214,11 +1254,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
1214
1254
  children: scenario.errorMessage
1215
1255
  }
1216
1256
  ) : null,
1217
- /* @__PURE__ */ jsx25(ReportStoryboard, { scenario }),
1218
- /* @__PURE__ */ jsx25(ReportSteps, { scenario }),
1219
- scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx25("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx25(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1220
- /* @__PURE__ */ jsx25(ReportAttachments, { attachments: scenario.attachments }),
1221
- /* @__PURE__ */ jsx25(ReportTrace, { spans: scenario.otelSpans })
1257
+ /* @__PURE__ */ jsx26(ReportStoryboard, { scenario }),
1258
+ /* @__PURE__ */ jsx26(ReportSteps, { scenario }),
1259
+ scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx26("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx26(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
1260
+ /* @__PURE__ */ jsx26(ReportAttachments, { attachments: scenario.attachments }),
1261
+ /* @__PURE__ */ jsx26(ReportTrace, { spans: scenario.otelSpans })
1222
1262
  ] })
1223
1263
  ]
1224
1264
  }
@@ -1226,13 +1266,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
1226
1266
  }
1227
1267
 
1228
1268
  // src/components/ReportScenarioList.tsx
1229
- import { Fragment as Fragment8, jsx as jsx26 } from "react/jsx-runtime";
1269
+ import { Fragment as Fragment8, jsx as jsx27 } from "react/jsx-runtime";
1230
1270
  function ReportScenarioList({ feature }) {
1231
- return /* @__PURE__ */ jsx26(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx26(ReportScenario, { scenario }, scenario.id)) });
1271
+ return /* @__PURE__ */ jsx27(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx27(ReportScenario, { scenario }, scenario.id)) });
1232
1272
  }
1233
1273
 
1234
1274
  // src/components/ReportFeature.tsx
1235
- import { Fragment as Fragment9, jsx as jsx27, jsxs as jsxs17 } from "react/jsx-runtime";
1275
+ import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1236
1276
  function ReportFeature({ feature }) {
1237
1277
  const titleId = `${feature.id}-title`;
1238
1278
  const bodyId = `${feature.id}-body`;
@@ -1243,7 +1283,7 @@ function ReportFeature({ feature }) {
1243
1283
  return (
1244
1284
  // Suite = a section heading + a divider, NOT another bordered card. Drops a
1245
1285
  // layer of nesting so the scenario cards below read as the primary unit.
1246
- /* @__PURE__ */ jsxs17(
1286
+ /* @__PURE__ */ jsxs18(
1247
1287
  "section",
1248
1288
  {
1249
1289
  id: feature.id,
@@ -1251,9 +1291,9 @@ function ReportFeature({ feature }) {
1251
1291
  "aria-labelledby": titleId,
1252
1292
  className: "flex flex-col gap-3",
1253
1293
  children: [
1254
- /* @__PURE__ */ jsxs17("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1255
- /* @__PURE__ */ jsxs17("div", { className: "min-w-0", children: [
1256
- /* @__PURE__ */ jsx27("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs17(
1294
+ /* @__PURE__ */ jsxs18("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
1295
+ /* @__PURE__ */ jsxs18("div", { className: "min-w-0", children: [
1296
+ /* @__PURE__ */ jsx28("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs18(
1257
1297
  "button",
1258
1298
  {
1259
1299
  type: "button",
@@ -1262,31 +1302,31 @@ function ReportFeature({ feature }) {
1262
1302
  "aria-controls": bodyId,
1263
1303
  className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
1264
1304
  children: [
1265
- /* @__PURE__ */ jsx27("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1266
- /* @__PURE__ */ jsx27("span", { className: "truncate", children: feature.title })
1305
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
1306
+ /* @__PURE__ */ jsx28("span", { className: "truncate", children: feature.title })
1267
1307
  ]
1268
1308
  }
1269
1309
  ) : feature.title }),
1270
- /* @__PURE__ */ jsx27("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1310
+ /* @__PURE__ */ jsx28("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
1271
1311
  ] }),
1272
- /* @__PURE__ */ jsxs17(
1312
+ /* @__PURE__ */ jsxs18(
1273
1313
  "p",
1274
1314
  {
1275
1315
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1276
1316
  "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1277
1317
  children: [
1278
- /* @__PURE__ */ jsxs17("span", { children: [
1318
+ /* @__PURE__ */ jsxs18("span", { children: [
1279
1319
  s.passed,
1280
1320
  " passed"
1281
1321
  ] }),
1282
- /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1283
- /* @__PURE__ */ jsxs17("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1322
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": true, children: "\xB7" }),
1323
+ /* @__PURE__ */ jsxs18("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
1284
1324
  s.failed,
1285
1325
  " failed"
1286
1326
  ] }),
1287
- skipped > 0 ? /* @__PURE__ */ jsxs17(Fragment9, { children: [
1288
- /* @__PURE__ */ jsx27("span", { "aria-hidden": true, children: "\xB7" }),
1289
- /* @__PURE__ */ jsxs17("span", { children: [
1327
+ skipped > 0 ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
1328
+ /* @__PURE__ */ jsx28("span", { "aria-hidden": true, children: "\xB7" }),
1329
+ /* @__PURE__ */ jsxs18("span", { children: [
1290
1330
  skipped,
1291
1331
  " skipped"
1292
1332
  ] })
@@ -1295,7 +1335,7 @@ function ReportFeature({ feature }) {
1295
1335
  }
1296
1336
  )
1297
1337
  ] }),
1298
- /* @__PURE__ */ jsx27("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx27(ReportScenarioList, { feature }) })
1338
+ /* @__PURE__ */ jsx28("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx28(ReportScenarioList, { feature }) })
1299
1339
  ]
1300
1340
  }
1301
1341
  )
@@ -1303,17 +1343,17 @@ function ReportFeature({ feature }) {
1303
1343
  }
1304
1344
 
1305
1345
  // src/components/ReportFeatureList.tsx
1306
- import { jsx as jsx28 } from "react/jsx-runtime";
1346
+ import { jsx as jsx29 } from "react/jsx-runtime";
1307
1347
  function ReportFeatureList() {
1308
1348
  const report = useReport();
1309
- return /* @__PURE__ */ jsx28("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx28(ReportFeature, { feature }, feature.id)) });
1349
+ return /* @__PURE__ */ jsx29("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx29(ReportFeature, { feature }, feature.id)) });
1310
1350
  }
1311
1351
 
1312
1352
  // src/components/ui/empty.tsx
1313
1353
  import { cva as cva2 } from "class-variance-authority";
1314
- import { jsx as jsx29 } from "react/jsx-runtime";
1354
+ import { jsx as jsx30 } from "react/jsx-runtime";
1315
1355
  function Empty({ className, ...props }) {
1316
- return /* @__PURE__ */ jsx29(
1356
+ return /* @__PURE__ */ jsx30(
1317
1357
  "div",
1318
1358
  {
1319
1359
  "data-slot": "empty",
@@ -1326,7 +1366,7 @@ function Empty({ className, ...props }) {
1326
1366
  );
1327
1367
  }
1328
1368
  function EmptyHeader({ className, ...props }) {
1329
- return /* @__PURE__ */ jsx29(
1369
+ return /* @__PURE__ */ jsx30(
1330
1370
  "div",
1331
1371
  {
1332
1372
  "data-slot": "empty-header",
@@ -1353,7 +1393,7 @@ var emptyMediaVariants = cva2(
1353
1393
  }
1354
1394
  );
1355
1395
  function EmptyTitle({ className, ...props }) {
1356
- return /* @__PURE__ */ jsx29(
1396
+ return /* @__PURE__ */ jsx30(
1357
1397
  "div",
1358
1398
  {
1359
1399
  "data-slot": "empty-title",
@@ -1364,16 +1404,16 @@ function EmptyTitle({ className, ...props }) {
1364
1404
  }
1365
1405
 
1366
1406
  // src/components/ReportEmpty.tsx
1367
- import { jsx as jsx30 } from "react/jsx-runtime";
1407
+ import { jsx as jsx31 } from "react/jsx-runtime";
1368
1408
  function ReportEmpty({ message }) {
1369
- return /* @__PURE__ */ jsx30(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx30(EmptyHeader, { children: /* @__PURE__ */ jsx30(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1409
+ return /* @__PURE__ */ jsx31(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx31(EmptyHeader, { children: /* @__PURE__ */ jsx31(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
1370
1410
  }
1371
1411
 
1372
1412
  // src/components/ReportSummary.tsx
1373
- import { jsx as jsx31, jsxs as jsxs18 } from "react/jsx-runtime";
1413
+ import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
1374
1414
  function ReportSummary({ className }) {
1375
1415
  const report = useReport();
1376
- return /* @__PURE__ */ jsx31(
1416
+ return /* @__PURE__ */ jsx32(
1377
1417
  ReportSummaryView,
1378
1418
  {
1379
1419
  summary: report.summary,
@@ -1399,7 +1439,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1399
1439
  if (summary.pending > 0) {
1400
1440
  cards.push({ status: "pending", label: "Pending", value: summary.pending });
1401
1441
  }
1402
- return /* @__PURE__ */ jsx31(
1442
+ return /* @__PURE__ */ jsx32(
1403
1443
  "dl",
1404
1444
  {
1405
1445
  className: cn(
@@ -1412,17 +1452,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1412
1452
  "aria-label": ariaLabel,
1413
1453
  children: cards.map((c) => {
1414
1454
  const tone = TONE[c.status];
1415
- return /* @__PURE__ */ jsxs18(
1455
+ return /* @__PURE__ */ jsxs19(
1416
1456
  "div",
1417
1457
  {
1418
1458
  "data-status": c.status,
1419
1459
  className: "rounded-lg border bg-card px-4 py-3",
1420
1460
  children: [
1421
- /* @__PURE__ */ jsxs18("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1422
- /* @__PURE__ */ jsx31("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1461
+ /* @__PURE__ */ jsxs19("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
1462
+ /* @__PURE__ */ jsx32("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
1423
1463
  c.label
1424
1464
  ] }),
1425
- /* @__PURE__ */ jsx31("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1465
+ /* @__PURE__ */ jsx32("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
1426
1466
  ]
1427
1467
  },
1428
1468
  c.status
@@ -1432,6 +1472,9 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
1432
1472
  );
1433
1473
  }
1434
1474
 
1475
+ // src/components/ReportMeta.tsx
1476
+ import { formatDuration as formatDuration4 } from "executable-stories-core/utils/duration";
1477
+
1435
1478
  // src/lib/provenance.ts
1436
1479
  var CI_DISPLAY_NAMES = {
1437
1480
  github: "GitHub Actions",
@@ -1463,7 +1506,7 @@ function prUrl(ci) {
1463
1506
  }
1464
1507
 
1465
1508
  // src/components/ReportMeta.tsx
1466
- import { jsx as jsx32, jsxs as jsxs19 } from "react/jsx-runtime";
1509
+ import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1467
1510
  function ReportMeta() {
1468
1511
  const report = useReport();
1469
1512
  const items = [];
@@ -1472,7 +1515,7 @@ function ReportMeta() {
1472
1515
  items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
1473
1516
  }
1474
1517
  if (report.durationMs) {
1475
- items.push({ label: "Duration", value: formatDuration(report.durationMs) });
1518
+ items.push({ label: "Duration", value: formatDuration4(report.durationMs) });
1476
1519
  }
1477
1520
  if (report.packageVersion) {
1478
1521
  items.push({ label: "Version", value: report.packageVersion });
@@ -1486,13 +1529,13 @@ function ReportMeta() {
1486
1529
  const short = sha.slice(0, 8);
1487
1530
  items.push({
1488
1531
  label: "Commit",
1489
- value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1532
+ value: url ? /* @__PURE__ */ jsx33("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
1490
1533
  });
1491
1534
  }
1492
1535
  if (report.ci) {
1493
1536
  items.push({
1494
1537
  label: "CI",
1495
- value: report.ci.url ? /* @__PURE__ */ jsxs19("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1538
+ value: report.ci.url ? /* @__PURE__ */ jsxs20("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
1496
1539
  ciDisplayName(report.ci.name),
1497
1540
  report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
1498
1541
  ] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
@@ -1503,7 +1546,7 @@ function ReportMeta() {
1503
1546
  const label = `#${report.ci.prNumber}`;
1504
1547
  items.push({
1505
1548
  label: "PR",
1506
- value: url ? /* @__PURE__ */ jsx32("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1549
+ value: url ? /* @__PURE__ */ jsx33("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
1507
1550
  });
1508
1551
  }
1509
1552
  if (items.length === 0) return null;
@@ -1511,48 +1554,48 @@ function ReportMeta() {
1511
1554
  // Run metadata is useful but secondary to "what failed" — tuck it into a
1512
1555
  // collapsed disclosure so it doesn't push the result down. Native <details>
1513
1556
  // so it works with no JS (the SSR/no-hydration report still toggles).
1514
- /* @__PURE__ */ jsxs19("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1515
- /* @__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: [
1516
- /* @__PURE__ */ jsx32("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1557
+ /* @__PURE__ */ jsxs20("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
1558
+ /* @__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: [
1559
+ /* @__PURE__ */ jsx33("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
1517
1560
  "Run details"
1518
1561
  ] }),
1519
- /* @__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: [
1520
- /* @__PURE__ */ jsx32("dt", { className: "font-medium", children: item.label }),
1521
- /* @__PURE__ */ jsx32("dd", { className: "font-mono text-foreground", children: item.value })
1562
+ /* @__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: [
1563
+ /* @__PURE__ */ jsx33("dt", { className: "font-medium", children: item.label }),
1564
+ /* @__PURE__ */ jsx33("dd", { className: "font-mono text-foreground", children: item.value })
1522
1565
  ] }, item.label)) })
1523
1566
  ] })
1524
1567
  );
1525
1568
  }
1526
1569
 
1527
1570
  // src/components/ReportSchemaError.tsx
1528
- import { jsx as jsx33, jsxs as jsxs20 } from "react/jsx-runtime";
1571
+ import { jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1529
1572
  function ReportSchemaError({ error }) {
1530
- return /* @__PURE__ */ jsxs20("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1531
- /* @__PURE__ */ jsx33("p", { children: /* @__PURE__ */ jsx33("strong", { children: "Report could not be displayed." }) }),
1532
- /* @__PURE__ */ jsx33("p", { children: error.message }),
1533
- error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs20("p", { children: [
1573
+ return /* @__PURE__ */ jsxs21("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
1574
+ /* @__PURE__ */ jsx34("p", { children: /* @__PURE__ */ jsx34("strong", { children: "Report could not be displayed." }) }),
1575
+ /* @__PURE__ */ jsx34("p", { children: error.message }),
1576
+ error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs21("p", { children: [
1534
1577
  "The report bundle is newer than this version of ",
1535
- /* @__PURE__ */ jsx33("code", { children: "executable-stories-react" }),
1578
+ /* @__PURE__ */ jsx34("code", { children: "executable-stories-react" }),
1536
1579
  ". Upgrade the package, or regenerate the report with an older formatters CLI."
1537
1580
  ] }) : null,
1538
- error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs20("details", { children: [
1539
- /* @__PURE__ */ jsxs20("summary", { children: [
1581
+ error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs21("details", { children: [
1582
+ /* @__PURE__ */ jsxs21("summary", { children: [
1540
1583
  error.issues.length,
1541
1584
  " validation issue",
1542
1585
  error.issues.length === 1 ? "" : "s"
1543
1586
  ] }),
1544
- /* @__PURE__ */ jsx33("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1587
+ /* @__PURE__ */ jsx34("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
1545
1588
  ] }) : null
1546
1589
  ] });
1547
1590
  }
1548
1591
 
1549
1592
  // src/components/ReportShell.tsx
1550
- import { Fragment as Fragment10, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
1593
+ import { Fragment as Fragment10, jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1551
1594
  function ReportTitleBlock({ title }) {
1552
- return /* @__PURE__ */ jsxs21(Fragment10, { children: [
1553
- /* @__PURE__ */ jsx34("h1", { children: title ?? "Story Report" }),
1554
- /* @__PURE__ */ jsx34(ReportSummary, {}),
1555
- /* @__PURE__ */ jsx34(ReportMeta, {})
1595
+ return /* @__PURE__ */ jsxs22(Fragment10, { children: [
1596
+ /* @__PURE__ */ jsx35("h1", { children: title ?? "Story Report" }),
1597
+ /* @__PURE__ */ jsx35(ReportSummary, {}),
1598
+ /* @__PURE__ */ jsx35(ReportMeta, {})
1556
1599
  ] });
1557
1600
  }
1558
1601
  function ReportErrorShell({
@@ -1561,22 +1604,22 @@ function ReportErrorShell({
1561
1604
  title,
1562
1605
  dataTheme
1563
1606
  }) {
1564
- return /* @__PURE__ */ jsx34("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx34(ReportSchemaError, { error }) });
1607
+ return /* @__PURE__ */ jsx35("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx35(ReportSchemaError, { error }) });
1565
1608
  }
1566
1609
 
1567
1610
  // src/components/Report.tsx
1568
- import { jsx as jsx35, jsxs as jsxs22 } from "react/jsx-runtime";
1611
+ import { jsx as jsx36, jsxs as jsxs23 } from "react/jsx-runtime";
1569
1612
  function Report(props) {
1570
1613
  const { report, customRenderers, renderers, className, title, dataTheme } = props;
1571
1614
  const result = unwrapReport(report);
1572
1615
  if (!result.ok) {
1573
- return /* @__PURE__ */ jsx35(ReportErrorShell, { error: result.error, className, title, dataTheme });
1616
+ return /* @__PURE__ */ jsx36(ReportErrorShell, { error: result.error, className, title, dataTheme });
1574
1617
  }
1575
1618
  const data = result.data;
1576
1619
  const hasContent = data.features.length > 0;
1577
- return /* @__PURE__ */ jsx35(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs22("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1578
- /* @__PURE__ */ jsx35("header", { className: "es-report-header", children: /* @__PURE__ */ jsx35(ReportTitleBlock, { title }) }),
1579
- hasContent ? /* @__PURE__ */ jsx35(ReportFeatureList, {}) : /* @__PURE__ */ jsx35(ReportEmpty, {})
1620
+ return /* @__PURE__ */ jsx36(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs23("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
1621
+ /* @__PURE__ */ jsx36("header", { className: "es-report-header", children: /* @__PURE__ */ jsx36(ReportTitleBlock, { title }) }),
1622
+ hasContent ? /* @__PURE__ */ jsx36(ReportFeatureList, {}) : /* @__PURE__ */ jsx36(ReportEmpty, {})
1580
1623
  ] }) });
1581
1624
  }
1582
1625