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