executable-stories-react 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +482 -451
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +440 -407
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +1430 -1246
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +32 -32
- package/dist/interactive.d.ts +32 -32
- package/dist/interactive.js +1473 -1286
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +65 -139
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +65 -139
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +30 -28
- package/dist/ssr.cjs +367 -254
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.js +325 -209
- package/dist/ssr.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +2 -2
package/dist/interactive.cjs
CHANGED
|
@@ -47,38 +47,135 @@ __export(interactive_exports, {
|
|
|
47
47
|
module.exports = __toCommonJS(interactive_exports);
|
|
48
48
|
|
|
49
49
|
// src/interactive/ReportInteractive.tsx
|
|
50
|
-
var
|
|
50
|
+
var import_lucide_react5 = require("lucide-react");
|
|
51
|
+
var import_react21 = require("react");
|
|
51
52
|
|
|
52
|
-
// src/
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
// src/components/ui/empty.tsx
|
|
54
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
55
|
+
|
|
56
|
+
// src/lib/utils.ts
|
|
57
|
+
var import_clsx = require("clsx");
|
|
58
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
59
|
+
function cn(...inputs) {
|
|
60
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/components/ui/empty.tsx
|
|
64
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
65
|
+
function Empty({ className, ...props }) {
|
|
66
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
"data-slot": "empty",
|
|
70
|
+
className: cn(
|
|
71
|
+
"flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
|
|
72
|
+
className
|
|
73
|
+
),
|
|
74
|
+
...props
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function EmptyHeader({ className, ...props }) {
|
|
79
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
80
|
+
"div",
|
|
81
|
+
{
|
|
82
|
+
"data-slot": "empty-header",
|
|
83
|
+
className: cn(
|
|
84
|
+
"flex max-w-sm flex-col items-center gap-2 text-center",
|
|
85
|
+
className
|
|
86
|
+
),
|
|
87
|
+
...props
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
var emptyMediaVariants = (0, import_class_variance_authority.cva)(
|
|
92
|
+
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
93
|
+
{
|
|
94
|
+
variants: {
|
|
95
|
+
variant: {
|
|
96
|
+
default: "bg-transparent",
|
|
97
|
+
icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
defaultVariants: {
|
|
101
|
+
variant: "default"
|
|
102
|
+
}
|
|
57
103
|
}
|
|
58
|
-
|
|
104
|
+
);
|
|
105
|
+
function EmptyTitle({ className, ...props }) {
|
|
106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
"data-slot": "empty-title",
|
|
110
|
+
className: cn("text-lg font-medium tracking-tight", className),
|
|
111
|
+
...props
|
|
112
|
+
}
|
|
113
|
+
);
|
|
59
114
|
}
|
|
60
115
|
|
|
61
|
-
// src/
|
|
116
|
+
// src/components/ReportEmpty.tsx
|
|
117
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
118
|
+
function ReportEmpty({ message }) {
|
|
119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/hooks/useReport.ts
|
|
62
123
|
var import_react2 = require("react");
|
|
63
124
|
|
|
64
125
|
// src/context/ReportContext.ts
|
|
65
126
|
var import_react = require("react");
|
|
66
127
|
var ReportContext = (0, import_react.createContext)(null);
|
|
67
128
|
|
|
68
|
-
// src/
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
129
|
+
// src/hooks/useReport.ts
|
|
130
|
+
function useReport() {
|
|
131
|
+
const ctx = (0, import_react2.useContext)(ReportContext);
|
|
132
|
+
if (!ctx) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return ctx.report;
|
|
138
|
+
}
|
|
72
139
|
|
|
73
|
-
// src/
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
140
|
+
// src/components/ReportScenario.tsx
|
|
141
|
+
var import_duration3 = require("executable-stories-core/utils/duration");
|
|
142
|
+
|
|
143
|
+
// src/lib/step-params.tsx
|
|
144
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
145
|
+
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
146
|
+
function highlightStepParams(text) {
|
|
147
|
+
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
148
|
+
if (matches.length === 0) return text;
|
|
149
|
+
const nodes = [];
|
|
150
|
+
let lastIndex = 0;
|
|
151
|
+
matches.forEach((match, i) => {
|
|
152
|
+
const start = match.index;
|
|
153
|
+
const end = start + match[0].length;
|
|
154
|
+
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
155
|
+
nodes.push(
|
|
156
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
157
|
+
);
|
|
158
|
+
lastIndex = end;
|
|
159
|
+
});
|
|
160
|
+
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
161
|
+
return nodes;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/components/ReportSteps.tsx
|
|
165
|
+
var import_assertive_steps = require("executable-stories-core/utils/assertive-steps");
|
|
166
|
+
var import_duration = require("executable-stories-core/utils/duration");
|
|
167
|
+
|
|
168
|
+
// src/components/doc/DocNote.tsx
|
|
169
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
170
|
+
function DocNote({ entry }) {
|
|
171
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
|
|
78
172
|
}
|
|
79
173
|
|
|
80
174
|
// src/components/ui/badge.tsx
|
|
81
|
-
var
|
|
175
|
+
var React = require("react");
|
|
176
|
+
var import_class_variance_authority2 = require("class-variance-authority");
|
|
177
|
+
var import_use_render = require("@base-ui/react/use-render");
|
|
178
|
+
var badgeVariants = (0, import_class_variance_authority2.cva)(
|
|
82
179
|
"inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
|
|
83
180
|
{
|
|
84
181
|
variants: {
|
|
@@ -123,334 +220,119 @@ function Badge({
|
|
|
123
220
|
});
|
|
124
221
|
}
|
|
125
222
|
|
|
126
|
-
// src/components/doc/
|
|
127
|
-
var
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
function isChangeKind(value) {
|
|
131
|
-
return value === "added" || value === "modified" || value === "removed" || value === "renamed";
|
|
223
|
+
// src/components/doc/DocTag.tsx
|
|
224
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
225
|
+
function DocTag({ entry }) {
|
|
226
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
|
|
132
227
|
}
|
|
133
|
-
|
|
134
|
-
|
|
228
|
+
|
|
229
|
+
// src/components/doc/DocKv.tsx
|
|
230
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
231
|
+
function formatValue(value) {
|
|
232
|
+
if (value === null) return "null";
|
|
233
|
+
if (typeof value === "string") return value;
|
|
234
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
235
|
+
try {
|
|
236
|
+
return JSON.stringify(value);
|
|
237
|
+
} catch {
|
|
238
|
+
return String(value);
|
|
239
|
+
}
|
|
135
240
|
}
|
|
136
|
-
function
|
|
137
|
-
return
|
|
241
|
+
function DocKv({ entry }) {
|
|
242
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
|
|
244
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
|
|
245
|
+
] });
|
|
138
246
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
247
|
+
|
|
248
|
+
// src/hooks/useRenderers.ts
|
|
249
|
+
var import_react3 = require("react");
|
|
250
|
+
var EMPTY_CUSTOM = {};
|
|
251
|
+
var EMPTY_RENDERERS = {};
|
|
252
|
+
function useCustomRenderers() {
|
|
253
|
+
const ctx = (0, import_react3.useContext)(ReportContext);
|
|
254
|
+
return ctx?.customRenderers ?? EMPTY_CUSTOM;
|
|
142
255
|
}
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
children
|
|
256
|
+
function useBuiltinRenderers() {
|
|
257
|
+
const ctx = (0, import_react3.useContext)(ReportContext);
|
|
258
|
+
return ctx?.renderers ?? EMPTY_RENDERERS;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/components/doc/DocCode.tsx
|
|
262
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
263
|
+
function CodeFigure({ label, children }) {
|
|
264
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("figure", { className: "my-3", children: [
|
|
265
|
+
label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
|
|
266
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
|
|
154
267
|
] });
|
|
155
268
|
}
|
|
156
|
-
function
|
|
157
|
-
|
|
269
|
+
function DocCode({ entry }) {
|
|
270
|
+
const renderers = useBuiltinRenderers();
|
|
271
|
+
if (renderers.code) {
|
|
272
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: renderers.code(entry) });
|
|
273
|
+
}
|
|
274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
158
275
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
276
|
+
|
|
277
|
+
// src/components/doc/DocTable.tsx
|
|
278
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
279
|
+
function DocTable({ entry }) {
|
|
280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("figure", { className: "my-3", children: [
|
|
281
|
+
entry.label ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
|
|
283
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
|
|
284
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
|
|
285
|
+
] }) })
|
|
286
|
+
] });
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/components/doc/DocLink.tsx
|
|
290
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
291
|
+
function DocLink({ entry }) {
|
|
292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
293
|
+
"a",
|
|
294
|
+
{
|
|
295
|
+
className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
|
|
296
|
+
href: entry.url,
|
|
297
|
+
rel: "noreferrer noopener",
|
|
298
|
+
target: "_blank",
|
|
299
|
+
children: entry.label
|
|
166
300
|
}
|
|
167
|
-
|
|
168
|
-
const path = str(raw.path);
|
|
169
|
-
if (!path) continue;
|
|
170
|
-
files.push({
|
|
171
|
-
path,
|
|
172
|
-
change: isChangeKind(raw.change) ? raw.change : void 0,
|
|
173
|
-
note: str(raw.note)
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
if (files.length === 0) return void 0;
|
|
177
|
-
return { title: str(data.title), authored: str(data.authored), files };
|
|
301
|
+
);
|
|
178
302
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if (index === segments.length - 1) child.file = file;
|
|
191
|
-
node = child;
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
return root;
|
|
303
|
+
|
|
304
|
+
// src/lib/markdown.ts
|
|
305
|
+
var import_marked = require("marked");
|
|
306
|
+
|
|
307
|
+
// src/lib/url.ts
|
|
308
|
+
var import_url = require("executable-stories-core/utils/url");
|
|
309
|
+
|
|
310
|
+
// src/lib/markdown.ts
|
|
311
|
+
function safeMarkdownHtml(markdown) {
|
|
312
|
+
const raw = import_marked.marked.parse(markdown, { async: false });
|
|
313
|
+
return raw.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<\/?(?:iframe|object|embed|form|base)\b[^>]*>/gi, "").replace(/\son[a-z]+\s*=\s*"[^"]*"/gi, "").replace(/\son[a-z]+\s*=\s*'[^']*'/gi, "").replace(/\son[a-z]+\s*=\s*[^\s>]+/gi, "").replace(/(href|src)\s*=\s*"([^"]*)"/gi, (_m, attr, val) => `${attr}="${neutralizeUrl(val)}"`).replace(/(href|src)\s*=\s*'([^']*)'/gi, (_m, attr, val) => `${attr}='${neutralizeUrl(val)}'`);
|
|
195
314
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return a.name.localeCompare(b.name);
|
|
202
|
-
});
|
|
315
|
+
function dedent(text) {
|
|
316
|
+
const lines = text.replace(/\t/g, " ").split("\n");
|
|
317
|
+
const indents = lines.filter((line) => line.trim().length > 0).map((line) => line.length - line.trimStart().length);
|
|
318
|
+
const shortest = indents.length > 0 ? Math.min(...indents) : 0;
|
|
319
|
+
return lines.map((line) => line.slice(shortest)).join("\n").trim();
|
|
203
320
|
}
|
|
204
|
-
function TreeBranch({ node, depth }) {
|
|
205
|
-
const children = sortedChildren(node);
|
|
206
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
|
|
207
|
-
const isDir = child.children.size > 0;
|
|
208
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
209
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-baseline gap-2", children: [
|
|
210
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
211
|
-
"span",
|
|
212
|
-
{
|
|
213
|
-
className: cn(
|
|
214
|
-
"font-mono text-xs",
|
|
215
|
-
isDir ? "text-muted-foreground" : "text-foreground"
|
|
216
|
-
),
|
|
217
|
-
children: isDir ? `${child.name}/` : child.name
|
|
218
|
-
}
|
|
219
|
-
),
|
|
220
|
-
child.file?.change ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeBadge, { change: child.file.change }) : null,
|
|
221
|
-
child.file?.note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
|
|
222
|
-
] }),
|
|
223
|
-
isDir ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeBranch, { node: child, depth: depth + 1 }) : null
|
|
224
|
-
] }, child.name);
|
|
225
|
-
}) });
|
|
226
|
-
}
|
|
227
|
-
function FileTreeBlock({ entry }) {
|
|
228
|
-
const data = parseFileTree(entry.data);
|
|
229
|
-
if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnreadableBlock, { entry });
|
|
230
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
|
|
231
|
-
}
|
|
232
|
-
function parseDataModel(data) {
|
|
233
|
-
if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
|
|
234
|
-
const fields = [];
|
|
235
|
-
for (const raw of data.fields) {
|
|
236
|
-
if (!isRecord(raw)) continue;
|
|
237
|
-
const name = str(raw.name);
|
|
238
|
-
if (!name) continue;
|
|
239
|
-
fields.push({
|
|
240
|
-
name,
|
|
241
|
-
type: str(raw.type),
|
|
242
|
-
note: str(raw.note),
|
|
243
|
-
change: isChangeKind(raw.change) ? raw.change : void 0
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
if (fields.length === 0) return void 0;
|
|
247
|
-
return {
|
|
248
|
-
title: str(data.title),
|
|
249
|
-
authored: str(data.authored),
|
|
250
|
-
name: str(data.name),
|
|
251
|
-
fields
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
function DataModelBlock({ entry }) {
|
|
255
|
-
const data = parseDataModel(entry.data);
|
|
256
|
-
if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnreadableBlock, { entry });
|
|
257
|
-
const hasNotes = data.fields.some((f) => f.note);
|
|
258
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
|
|
259
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: "bg-muted/50", children: [
|
|
260
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
|
|
261
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
|
|
262
|
-
hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
|
|
263
|
-
] }) }),
|
|
264
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: data.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
265
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
266
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono text-xs text-foreground", children: field.name }),
|
|
267
|
-
field.change ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeBadge, { change: field.change }) : null
|
|
268
|
-
] }) }),
|
|
269
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
|
|
270
|
-
hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
|
|
271
|
-
] }, field.name)) })
|
|
272
|
-
] }) }) });
|
|
273
|
-
}
|
|
274
|
-
function UnreadableBlock({ entry }) {
|
|
275
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
276
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
|
|
277
|
-
entry.type,
|
|
278
|
-
" (unrecognised shape)"
|
|
279
|
-
] }),
|
|
280
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
281
|
-
"pre",
|
|
282
|
-
{
|
|
283
|
-
tabIndex: 0,
|
|
284
|
-
className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
|
|
285
|
-
children: safeStringify(entry.data)
|
|
286
|
-
}
|
|
287
|
-
)
|
|
288
|
-
] });
|
|
289
|
-
}
|
|
290
|
-
function safeStringify(value) {
|
|
291
|
-
try {
|
|
292
|
-
return JSON.stringify(value, null, 2);
|
|
293
|
-
} catch {
|
|
294
|
-
return String(value);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
var narrativeBlockRenderers = {
|
|
298
|
-
[FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileTreeBlock, { entry }),
|
|
299
|
-
[DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataModelBlock, { entry })
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// src/context/ReportRoot.tsx
|
|
303
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
304
|
-
var EMPTY_RENDERERS = {};
|
|
305
|
-
function ReportRoot({
|
|
306
|
-
report,
|
|
307
|
-
customRenderers,
|
|
308
|
-
renderers,
|
|
309
|
-
children
|
|
310
|
-
}) {
|
|
311
|
-
const value = (0, import_react2.useMemo)(
|
|
312
|
-
() => ({
|
|
313
|
-
report,
|
|
314
|
-
// Narrative blocks render everywhere the report does (static SSR, the
|
|
315
|
-
// island, Astro) without every host wiring them up. A host renderer for
|
|
316
|
-
// the same type still wins.
|
|
317
|
-
customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
|
|
318
|
-
renderers: renderers ?? EMPTY_RENDERERS
|
|
319
|
-
}),
|
|
320
|
-
[report, customRenderers, renderers]
|
|
321
|
-
);
|
|
322
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ReportContext.Provider, { value, children });
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/hooks/useReport.ts
|
|
326
|
-
var import_react3 = require("react");
|
|
327
|
-
function useReport() {
|
|
328
|
-
const ctx = (0, import_react3.useContext)(ReportContext);
|
|
329
|
-
if (!ctx) {
|
|
330
|
-
throw new Error(
|
|
331
|
-
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
return ctx.report;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// src/components/ReportScenario.tsx
|
|
338
|
-
var import_duration3 = require("executable-stories-core/utils/duration");
|
|
339
|
-
|
|
340
|
-
// src/components/ReportSteps.tsx
|
|
341
|
-
var import_duration = require("executable-stories-core/utils/duration");
|
|
342
|
-
|
|
343
|
-
// src/components/doc/DocNote.tsx
|
|
344
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
345
|
-
function DocNote({ entry }) {
|
|
346
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "my-2 text-sm text-muted-foreground", children: entry.text });
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// src/components/doc/DocTag.tsx
|
|
350
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
351
|
-
function DocTag({ entry }) {
|
|
352
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "my-2 flex flex-wrap gap-1.5", "aria-label": "Tags", children: entry.names.map((n) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Badge, { variant: "tag", children: n }) }, n)) });
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// src/components/doc/DocKv.tsx
|
|
356
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
357
|
-
function formatValue(value) {
|
|
358
|
-
if (value === null) return "null";
|
|
359
|
-
if (typeof value === "string") return value;
|
|
360
|
-
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
361
|
-
try {
|
|
362
|
-
return JSON.stringify(value);
|
|
363
|
-
} catch {
|
|
364
|
-
return String(value);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
function DocKv({ entry }) {
|
|
368
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
|
|
369
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
|
|
370
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dd", { className: "font-mono text-foreground", children: formatValue(entry.value) })
|
|
371
|
-
] });
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// src/hooks/useRenderers.ts
|
|
375
|
-
var import_react4 = require("react");
|
|
376
|
-
var EMPTY_CUSTOM = {};
|
|
377
|
-
var EMPTY_RENDERERS2 = {};
|
|
378
|
-
function useCustomRenderers() {
|
|
379
|
-
const ctx = (0, import_react4.useContext)(ReportContext);
|
|
380
|
-
return ctx?.customRenderers ?? EMPTY_CUSTOM;
|
|
381
|
-
}
|
|
382
|
-
function useBuiltinRenderers() {
|
|
383
|
-
const ctx = (0, import_react4.useContext)(ReportContext);
|
|
384
|
-
return ctx?.renderers ?? EMPTY_RENDERERS2;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// src/components/doc/DocCode.tsx
|
|
388
|
-
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
389
|
-
function CodeFigure({ label, children }) {
|
|
390
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("figure", { className: "my-3", children: [
|
|
391
|
-
label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: label }) : null,
|
|
392
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3", children })
|
|
393
|
-
] });
|
|
394
|
-
}
|
|
395
|
-
function DocCode({ entry }) {
|
|
396
|
-
const renderers = useBuiltinRenderers();
|
|
397
|
-
if (renderers.code) {
|
|
398
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderers.code(entry) });
|
|
399
|
-
}
|
|
400
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CodeFigure, { label: entry.label, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// src/components/doc/DocTable.tsx
|
|
404
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
405
|
-
function DocTable({ entry }) {
|
|
406
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("figure", { className: "my-3", children: [
|
|
407
|
-
entry.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.label }) : null,
|
|
408
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
|
|
409
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { className: "bg-muted/50", children: entry.columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: c }, c)) }) }),
|
|
410
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: entry.rows.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: cell }, j)) }, i)) })
|
|
411
|
-
] }) })
|
|
412
|
-
] });
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// src/components/doc/DocLink.tsx
|
|
416
|
-
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
417
|
-
function DocLink({ entry }) {
|
|
418
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
419
|
-
"a",
|
|
420
|
-
{
|
|
421
|
-
className: "text-sm font-medium text-primary underline underline-offset-2 hover:no-underline",
|
|
422
|
-
href: entry.url,
|
|
423
|
-
rel: "noreferrer noopener",
|
|
424
|
-
target: "_blank",
|
|
425
|
-
children: entry.label
|
|
426
|
-
}
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// src/components/doc/DocSection.tsx
|
|
431
|
-
var import_marked = require("marked");
|
|
432
|
-
|
|
433
|
-
// src/lib/url.ts
|
|
434
|
-
var import_url = require("executable-stories-core/utils/url");
|
|
435
|
-
|
|
436
|
-
// src/components/doc/DocSection.tsx
|
|
437
|
-
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
438
321
|
function neutralizeUrl(value) {
|
|
439
322
|
return (0, import_url.safeUrl)(value) ?? "#";
|
|
440
323
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
}
|
|
324
|
+
|
|
325
|
+
// src/components/doc/DocSection.tsx
|
|
326
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
445
327
|
function DocSection({ entry }) {
|
|
446
328
|
const renderers = useBuiltinRenderers();
|
|
447
329
|
if (renderers.section) {
|
|
448
|
-
return /* @__PURE__ */ (0,
|
|
330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: renderers.section(entry) });
|
|
449
331
|
}
|
|
450
332
|
const html = safeMarkdownHtml(entry.markdown);
|
|
451
|
-
return /* @__PURE__ */ (0,
|
|
452
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
453
|
-
/* @__PURE__ */ (0,
|
|
333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
|
|
334
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
454
336
|
"div",
|
|
455
337
|
{
|
|
456
338
|
className: "es-doc-prose prose prose-sm max-w-none",
|
|
@@ -461,11 +343,11 @@ function DocSection({ entry }) {
|
|
|
461
343
|
}
|
|
462
344
|
|
|
463
345
|
// src/components/doc/DocMermaid.tsx
|
|
464
|
-
var
|
|
346
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
465
347
|
function MermaidSource({ entry, ref }) {
|
|
466
|
-
return /* @__PURE__ */ (0,
|
|
467
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
468
|
-
/* @__PURE__ */ (0,
|
|
348
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("figure", { ref, className: "my-3", "aria-label": entry.title ?? "Diagram", children: [
|
|
349
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
350
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
469
351
|
"pre",
|
|
470
352
|
{
|
|
471
353
|
"data-mermaid": true,
|
|
@@ -479,24 +361,24 @@ function MermaidSource({ entry, ref }) {
|
|
|
479
361
|
function DocMermaid({ entry }) {
|
|
480
362
|
const renderers = useBuiltinRenderers();
|
|
481
363
|
if (renderers.mermaid) {
|
|
482
|
-
return /* @__PURE__ */ (0,
|
|
364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: renderers.mermaid(entry) });
|
|
483
365
|
}
|
|
484
|
-
return /* @__PURE__ */ (0,
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MermaidSource, { entry });
|
|
485
367
|
}
|
|
486
368
|
|
|
487
369
|
// src/components/doc/DocScreenshot.tsx
|
|
488
|
-
var
|
|
370
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
489
371
|
function DocScreenshot({ entry }) {
|
|
490
372
|
const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
|
|
491
373
|
if (!src) {
|
|
492
|
-
return /* @__PURE__ */ (0,
|
|
493
|
-
/* @__PURE__ */ (0,
|
|
494
|
-
/* @__PURE__ */ (0,
|
|
495
|
-
entry.alt ? /* @__PURE__ */ (0,
|
|
374
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
375
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "font-medium", children: "Screenshot unavailable" }),
|
|
376
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
377
|
+
entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.alt }) : null
|
|
496
378
|
] });
|
|
497
379
|
}
|
|
498
|
-
return /* @__PURE__ */ (0,
|
|
499
|
-
/* @__PURE__ */ (0,
|
|
380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
500
382
|
"img",
|
|
501
383
|
{
|
|
502
384
|
src,
|
|
@@ -505,24 +387,24 @@ function DocScreenshot({ entry }) {
|
|
|
505
387
|
className: "max-w-full rounded-md border border-border"
|
|
506
388
|
}
|
|
507
389
|
),
|
|
508
|
-
entry.alt ? /* @__PURE__ */ (0,
|
|
390
|
+
entry.alt ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: entry.alt }) : null
|
|
509
391
|
] });
|
|
510
392
|
}
|
|
511
393
|
|
|
512
394
|
// src/components/doc/DocVideo.tsx
|
|
513
|
-
var
|
|
395
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
514
396
|
function DocVideo({ entry }) {
|
|
515
397
|
if ((0, import_url.isLocalFsPath)(entry.path)) {
|
|
516
|
-
return /* @__PURE__ */ (0,
|
|
517
|
-
/* @__PURE__ */ (0,
|
|
518
|
-
/* @__PURE__ */ (0,
|
|
519
|
-
entry.caption ? /* @__PURE__ */ (0,
|
|
398
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground", children: [
|
|
399
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "font-medium", children: "Video unavailable" }),
|
|
400
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-1 break-all font-mono text-xs", children: entry.path }),
|
|
401
|
+
entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("figcaption", { className: "mt-1.5 text-xs", children: entry.caption }) : null
|
|
520
402
|
] });
|
|
521
403
|
}
|
|
522
404
|
const src = (0, import_url.safeUrl)(entry.path);
|
|
523
405
|
const poster = (0, import_url.safeUrl)(entry.poster);
|
|
524
|
-
return /* @__PURE__ */ (0,
|
|
525
|
-
/* @__PURE__ */ (0,
|
|
406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3", children: [
|
|
407
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
526
408
|
"video",
|
|
527
409
|
{
|
|
528
410
|
controls: true,
|
|
@@ -532,21 +414,21 @@ function DocVideo({ entry }) {
|
|
|
532
414
|
className: "max-w-full rounded-md border border-border"
|
|
533
415
|
}
|
|
534
416
|
),
|
|
535
|
-
entry.caption ? /* @__PURE__ */ (0,
|
|
417
|
+
entry.caption ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: entry.caption }) : null
|
|
536
418
|
] });
|
|
537
419
|
}
|
|
538
420
|
|
|
539
421
|
// src/components/doc/DocHtml.tsx
|
|
540
|
-
var
|
|
422
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
541
423
|
function DocHtml({ entry }) {
|
|
542
424
|
const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
|
|
543
425
|
const title = entry.title ?? "Embedded HTML";
|
|
544
426
|
const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
|
|
545
427
|
const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
|
|
546
|
-
return /* @__PURE__ */ (0,
|
|
547
|
-
/* @__PURE__ */ (0,
|
|
548
|
-
/* @__PURE__ */ (0,
|
|
549
|
-
safeHref ? /* @__PURE__ */ (0,
|
|
428
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
430
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: entry.title ?? "HTML" }),
|
|
431
|
+
safeHref ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
550
432
|
"a",
|
|
551
433
|
{
|
|
552
434
|
href: safeHref,
|
|
@@ -559,7 +441,7 @@ function DocHtml({ entry }) {
|
|
|
559
441
|
}
|
|
560
442
|
) : null
|
|
561
443
|
] }),
|
|
562
|
-
/* @__PURE__ */ (0,
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
563
445
|
"iframe",
|
|
564
446
|
{
|
|
565
447
|
...frameProps,
|
|
@@ -574,7 +456,7 @@ function DocHtml({ entry }) {
|
|
|
574
456
|
}
|
|
575
457
|
|
|
576
458
|
// src/components/doc/DocState.tsx
|
|
577
|
-
var
|
|
459
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
578
460
|
function DocState({ entry }) {
|
|
579
461
|
let json;
|
|
580
462
|
try {
|
|
@@ -582,23 +464,23 @@ function DocState({ entry }) {
|
|
|
582
464
|
} catch {
|
|
583
465
|
json = String(entry.value);
|
|
584
466
|
}
|
|
585
|
-
return /* @__PURE__ */ (0,
|
|
467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(CodeFigure, { label: entry.label ?? "State", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("code", { className: "font-mono text-xs language-json", children: json }) });
|
|
586
468
|
}
|
|
587
469
|
|
|
588
470
|
// src/components/doc/DocCustom.tsx
|
|
589
|
-
var
|
|
471
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
590
472
|
function DocCustom({ entry }) {
|
|
591
473
|
const renderers = useCustomRenderers();
|
|
592
474
|
const renderer = renderers[entry.type];
|
|
593
475
|
if (renderer) {
|
|
594
|
-
return /* @__PURE__ */ (0,
|
|
476
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: renderer(entry) });
|
|
595
477
|
}
|
|
596
|
-
return /* @__PURE__ */ (0,
|
|
597
|
-
/* @__PURE__ */ (0,
|
|
598
|
-
/* @__PURE__ */ (0,
|
|
478
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
479
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: entry.type }),
|
|
480
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("pre", { tabIndex: 0, className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground", children: safeStringify(entry.data) })
|
|
599
481
|
] });
|
|
600
482
|
}
|
|
601
|
-
function
|
|
483
|
+
function safeStringify(value) {
|
|
602
484
|
try {
|
|
603
485
|
return JSON.stringify(value, null, 2);
|
|
604
486
|
} catch {
|
|
@@ -607,64 +489,43 @@ function safeStringify2(value) {
|
|
|
607
489
|
}
|
|
608
490
|
|
|
609
491
|
// src/components/doc/DocEntry.tsx
|
|
610
|
-
var
|
|
492
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
611
493
|
function DocEntry({ entry }) {
|
|
612
494
|
switch (entry.kind) {
|
|
613
495
|
case "note":
|
|
614
|
-
return /* @__PURE__ */ (0,
|
|
496
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocNote, { entry });
|
|
615
497
|
case "tag":
|
|
616
|
-
return /* @__PURE__ */ (0,
|
|
498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocTag, { entry });
|
|
617
499
|
case "kv":
|
|
618
|
-
return /* @__PURE__ */ (0,
|
|
500
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocKv, { entry });
|
|
619
501
|
case "code":
|
|
620
|
-
return /* @__PURE__ */ (0,
|
|
502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocCode, { entry });
|
|
621
503
|
case "table":
|
|
622
|
-
return /* @__PURE__ */ (0,
|
|
504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocTable, { entry });
|
|
623
505
|
case "link":
|
|
624
|
-
return /* @__PURE__ */ (0,
|
|
506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocLink, { entry });
|
|
625
507
|
case "section":
|
|
626
|
-
return /* @__PURE__ */ (0,
|
|
508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocSection, { entry });
|
|
627
509
|
case "mermaid":
|
|
628
|
-
return /* @__PURE__ */ (0,
|
|
510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocMermaid, { entry });
|
|
629
511
|
case "screenshot":
|
|
630
|
-
return /* @__PURE__ */ (0,
|
|
512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocScreenshot, { entry });
|
|
631
513
|
case "video":
|
|
632
|
-
return /* @__PURE__ */ (0,
|
|
514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocVideo, { entry });
|
|
633
515
|
case "html":
|
|
634
|
-
return /* @__PURE__ */ (0,
|
|
516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocHtml, { entry });
|
|
635
517
|
case "state":
|
|
636
|
-
return /* @__PURE__ */ (0,
|
|
518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocState, { entry });
|
|
637
519
|
case "custom":
|
|
638
|
-
return /* @__PURE__ */ (0,
|
|
520
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocCustom, { entry });
|
|
639
521
|
}
|
|
640
522
|
}
|
|
641
523
|
|
|
642
524
|
// src/components/ReportDocEntries.tsx
|
|
643
|
-
var
|
|
525
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
644
526
|
function ReportDocEntries({ entries }) {
|
|
645
527
|
if (entries.length === 0) return null;
|
|
646
|
-
return /* @__PURE__ */ (0,
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
// src/lib/step-params.tsx
|
|
650
|
-
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
651
|
-
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
652
|
-
function highlightStepParams(text) {
|
|
653
|
-
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
654
|
-
if (matches.length === 0) return text;
|
|
655
|
-
const nodes = [];
|
|
656
|
-
let lastIndex = 0;
|
|
657
|
-
matches.forEach((match, i) => {
|
|
658
|
-
const start = match.index;
|
|
659
|
-
const end = start + match[0].length;
|
|
660
|
-
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
661
|
-
nodes.push(
|
|
662
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
663
|
-
);
|
|
664
|
-
lastIndex = end;
|
|
665
|
-
});
|
|
666
|
-
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
667
|
-
return nodes;
|
|
528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_jsx_runtime18.Fragment, { children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DocEntry, { entry }, i)) });
|
|
668
529
|
}
|
|
669
530
|
|
|
670
531
|
// src/components/ReportSteps.tsx
|
|
@@ -683,9 +544,22 @@ var STEP_GLYPH_COLOR = {
|
|
|
683
544
|
};
|
|
684
545
|
function ReportSteps({ scenario }) {
|
|
685
546
|
if (scenario.steps.length === 0) return null;
|
|
686
|
-
|
|
547
|
+
const unasserted = new Set(
|
|
548
|
+
(0, import_assertive_steps.assertionState)(scenario.steps) === "unasserted" ? (0, import_assertive_steps.assertiveSteps)(scenario.steps).map((step) => step.id) : []
|
|
549
|
+
);
|
|
550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { "data-slot": "steps", className: "flex flex-col", children: scenario.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
551
|
+
ReportStepItem,
|
|
552
|
+
{
|
|
553
|
+
step,
|
|
554
|
+
unasserted: unasserted.has(step.id)
|
|
555
|
+
},
|
|
556
|
+
step.id
|
|
557
|
+
)) });
|
|
687
558
|
}
|
|
688
|
-
function ReportStepItem({
|
|
559
|
+
function ReportStepItem({
|
|
560
|
+
step,
|
|
561
|
+
unasserted = false
|
|
562
|
+
}) {
|
|
689
563
|
const kw = step.keyword.toLowerCase();
|
|
690
564
|
const continuation = kw === "and" || kw === "but";
|
|
691
565
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
@@ -693,10 +567,23 @@ function ReportStepItem({ step }) {
|
|
|
693
567
|
{
|
|
694
568
|
id: step.id,
|
|
695
569
|
"data-status": step.status,
|
|
696
|
-
className: cn(
|
|
570
|
+
className: cn(
|
|
571
|
+
"py-1.5 text-[0.8125rem] leading-normal",
|
|
572
|
+
continuation && "pl-5"
|
|
573
|
+
),
|
|
697
574
|
children: [
|
|
698
575
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
699
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
576
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
577
|
+
"span",
|
|
578
|
+
{
|
|
579
|
+
"aria-hidden": true,
|
|
580
|
+
className: cn(
|
|
581
|
+
"w-4 shrink-0 text-center text-xs",
|
|
582
|
+
STEP_GLYPH_COLOR[step.status]
|
|
583
|
+
),
|
|
584
|
+
children: STEP_GLYPH[step.status] ?? "\u25CB"
|
|
585
|
+
}
|
|
586
|
+
),
|
|
700
587
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
701
588
|
"span",
|
|
702
589
|
{
|
|
@@ -708,6 +595,7 @@ function ReportStepItem({ step }) {
|
|
|
708
595
|
}
|
|
709
596
|
),
|
|
710
597
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex-1 text-foreground", children: highlightStepParams(step.text) }),
|
|
598
|
+
unasserted ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 rounded-full border border-pend-border bg-pend-bg px-2 py-0.5 text-[0.6875rem] font-medium text-pend", children: "No assertion" }) : null,
|
|
711
599
|
step.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "shrink-0 whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration.formatDuration)(step.durationMs) }) : null
|
|
712
600
|
] }),
|
|
713
601
|
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
@@ -1009,11 +897,11 @@ function ReportTrace({ spans }) {
|
|
|
1009
897
|
}
|
|
1010
898
|
|
|
1011
899
|
// src/interactive/scenario-history-context.ts
|
|
1012
|
-
var
|
|
1013
|
-
var ScenarioHistoryContext = (0,
|
|
900
|
+
var import_react4 = require("react");
|
|
901
|
+
var ScenarioHistoryContext = (0, import_react4.createContext)(null);
|
|
1014
902
|
var ScenarioHistoryProvider = ScenarioHistoryContext.Provider;
|
|
1015
903
|
function useScenarioHistory() {
|
|
1016
|
-
return (0,
|
|
904
|
+
return (0, import_react4.useContext)(ScenarioHistoryContext);
|
|
1017
905
|
}
|
|
1018
906
|
|
|
1019
907
|
// src/lib/run-history.ts
|
|
@@ -1121,13 +1009,92 @@ function ScenarioRunHistory({ scenarioId }) {
|
|
|
1121
1009
|
] });
|
|
1122
1010
|
}
|
|
1123
1011
|
|
|
1124
|
-
// src/
|
|
1012
|
+
// src/components/ScenarioStaleness.tsx
|
|
1125
1013
|
var import_react6 = require("react");
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
function
|
|
1129
|
-
|
|
1130
|
-
|
|
1014
|
+
|
|
1015
|
+
// src/lib/provenance.ts
|
|
1016
|
+
function reportLastRunMs(report) {
|
|
1017
|
+
if (report.finishedAtMs && report.finishedAtMs > 0) return report.finishedAtMs;
|
|
1018
|
+
if (report.startedAtMs && report.startedAtMs > 0) return report.startedAtMs;
|
|
1019
|
+
return void 0;
|
|
1020
|
+
}
|
|
1021
|
+
function formatRelativeAge(thenMs, nowMs) {
|
|
1022
|
+
const deltaMs = Math.max(0, nowMs - thenMs);
|
|
1023
|
+
const minutes = Math.floor(deltaMs / 6e4);
|
|
1024
|
+
if (minutes < 1) return "just now";
|
|
1025
|
+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
|
|
1026
|
+
const hours = Math.floor(minutes / 60);
|
|
1027
|
+
if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
1028
|
+
const days = Math.floor(hours / 24);
|
|
1029
|
+
return `${days} day${days === 1 ? "" : "s"} ago`;
|
|
1030
|
+
}
|
|
1031
|
+
function isReportStale(lastRunMs, staleAfterDays, nowMs) {
|
|
1032
|
+
if (lastRunMs === void 0 || staleAfterDays <= 0) return false;
|
|
1033
|
+
return nowMs - lastRunMs >= staleAfterDays * 864e5;
|
|
1034
|
+
}
|
|
1035
|
+
var CI_DISPLAY_NAMES = {
|
|
1036
|
+
github: "GitHub Actions",
|
|
1037
|
+
gitlab: "GitLab CI",
|
|
1038
|
+
circleci: "CircleCI",
|
|
1039
|
+
jenkins: "Jenkins",
|
|
1040
|
+
azure: "Azure DevOps",
|
|
1041
|
+
buildkite: "Buildkite",
|
|
1042
|
+
travis: "Travis CI",
|
|
1043
|
+
ci: "CI"
|
|
1044
|
+
};
|
|
1045
|
+
function ciDisplayName(name) {
|
|
1046
|
+
return CI_DISPLAY_NAMES[name] ?? name;
|
|
1047
|
+
}
|
|
1048
|
+
function commitUrl(ci, sha) {
|
|
1049
|
+
const commitSha = ci?.commitSha ?? sha;
|
|
1050
|
+
if (!ci?.url || !commitSha) return void 0;
|
|
1051
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1052
|
+
if (github) return `${github[1]}/commit/${commitSha}`;
|
|
1053
|
+
const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
|
|
1054
|
+
if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
|
|
1055
|
+
return void 0;
|
|
1056
|
+
}
|
|
1057
|
+
function prUrl(ci) {
|
|
1058
|
+
if (!ci?.url || !ci.prNumber) return void 0;
|
|
1059
|
+
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1060
|
+
if (github) return `${github[1]}/pull/${ci.prNumber}`;
|
|
1061
|
+
return void 0;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// src/interactive/scenario-freshness-context.tsx
|
|
1065
|
+
var import_react5 = require("react");
|
|
1066
|
+
var ScenarioFreshnessContext = (0, import_react5.createContext)(null);
|
|
1067
|
+
var ScenarioFreshnessProvider = ScenarioFreshnessContext.Provider;
|
|
1068
|
+
function useScenarioFreshness() {
|
|
1069
|
+
return (0, import_react5.useContext)(ScenarioFreshnessContext);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// src/components/ScenarioStaleness.tsx
|
|
1073
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1074
|
+
function ScenarioStaleness({ scenario }) {
|
|
1075
|
+
const freshness = useScenarioFreshness();
|
|
1076
|
+
const fixedNow = freshness?.nowMs;
|
|
1077
|
+
const [now, setNow] = (0, import_react6.useState)(fixedNow);
|
|
1078
|
+
(0, import_react6.useEffect)(() => {
|
|
1079
|
+
if (fixedNow === void 0) setNow(Date.now());
|
|
1080
|
+
}, [fixedNow]);
|
|
1081
|
+
if (!freshness || now === void 0) return null;
|
|
1082
|
+
const lastRunAtMs = scenario.lastRunAtMs;
|
|
1083
|
+
if (!lastRunAtMs || lastRunAtMs <= 0) return null;
|
|
1084
|
+
if (!isReportStale(lastRunAtMs, freshness.staleAfterDays, now)) return null;
|
|
1085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Badge, { variant: "outline", "aria-label": "Scenario freshness", children: [
|
|
1086
|
+
"Last ran ",
|
|
1087
|
+
formatRelativeAge(lastRunAtMs, now)
|
|
1088
|
+
] });
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// src/interactive/collapse-context.ts
|
|
1092
|
+
var import_react7 = require("react");
|
|
1093
|
+
var CollapseContext = (0, import_react7.createContext)(null);
|
|
1094
|
+
var CollapseProvider = CollapseContext.Provider;
|
|
1095
|
+
function useCollapse() {
|
|
1096
|
+
return (0, import_react7.useContext)(CollapseContext);
|
|
1097
|
+
}
|
|
1131
1098
|
var STORAGE_KEY = "es-collapsed-ids";
|
|
1132
1099
|
function readPersisted() {
|
|
1133
1100
|
if (typeof localStorage === "undefined") return null;
|
|
@@ -1146,11 +1113,11 @@ function persist(ids) {
|
|
|
1146
1113
|
}
|
|
1147
1114
|
}
|
|
1148
1115
|
function useCollapseState(computeDefault) {
|
|
1149
|
-
const [collapsed, setCollapsed] = (0,
|
|
1116
|
+
const [collapsed, setCollapsed] = (0, import_react7.useState)(
|
|
1150
1117
|
() => readPersisted() ?? computeDefault?.() ?? /* @__PURE__ */ new Set()
|
|
1151
1118
|
);
|
|
1152
|
-
const isCollapsed = (0,
|
|
1153
|
-
const toggle = (0,
|
|
1119
|
+
const isCollapsed = (0, import_react7.useCallback)((id) => collapsed.has(id), [collapsed]);
|
|
1120
|
+
const toggle = (0, import_react7.useCallback)((id) => {
|
|
1154
1121
|
setCollapsed((prev) => {
|
|
1155
1122
|
const next = new Set(prev);
|
|
1156
1123
|
if (next.has(id)) next.delete(id);
|
|
@@ -1159,14 +1126,14 @@ function useCollapseState(computeDefault) {
|
|
|
1159
1126
|
return next;
|
|
1160
1127
|
});
|
|
1161
1128
|
}, []);
|
|
1162
|
-
const collapseAll = (0,
|
|
1129
|
+
const collapseAll = (0, import_react7.useCallback)((ids) => {
|
|
1163
1130
|
setCollapsed(() => {
|
|
1164
1131
|
const next = new Set(ids);
|
|
1165
1132
|
persist(next);
|
|
1166
1133
|
return next;
|
|
1167
1134
|
});
|
|
1168
1135
|
}, []);
|
|
1169
|
-
const expandAll = (0,
|
|
1136
|
+
const expandAll = (0, import_react7.useCallback)(() => {
|
|
1170
1137
|
setCollapsed(() => {
|
|
1171
1138
|
const next = /* @__PURE__ */ new Set();
|
|
1172
1139
|
persist(next);
|
|
@@ -1177,12 +1144,12 @@ function useCollapseState(computeDefault) {
|
|
|
1177
1144
|
}
|
|
1178
1145
|
|
|
1179
1146
|
// src/interactive/scenario-actions.tsx
|
|
1180
|
-
var
|
|
1147
|
+
var import_react8 = require("react");
|
|
1181
1148
|
var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
|
|
1182
|
-
var ScenarioActionsContext = (0,
|
|
1149
|
+
var ScenarioActionsContext = (0, import_react8.createContext)(null);
|
|
1183
1150
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
1184
1151
|
function useScenarioActions() {
|
|
1185
|
-
return (0,
|
|
1152
|
+
return (0, import_react8.useContext)(ScenarioActionsContext);
|
|
1186
1153
|
}
|
|
1187
1154
|
function scenarioToMarkdown(scenario) {
|
|
1188
1155
|
return (0, import_scenario_markdown.scenarioToMarkdown)(scenario, { variant: "compact" });
|
|
@@ -1213,9 +1180,9 @@ function scenarioPermalink(scenario) {
|
|
|
1213
1180
|
|
|
1214
1181
|
// src/components/ui/card.tsx
|
|
1215
1182
|
var React2 = require("react");
|
|
1216
|
-
var
|
|
1183
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1217
1184
|
function Card({ className, ...props }) {
|
|
1218
|
-
return /* @__PURE__ */ (0,
|
|
1185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1219
1186
|
"div",
|
|
1220
1187
|
{
|
|
1221
1188
|
"data-slot": "card",
|
|
@@ -1228,7 +1195,7 @@ function Card({ className, ...props }) {
|
|
|
1228
1195
|
);
|
|
1229
1196
|
}
|
|
1230
1197
|
function CardHeader({ className, ...props }) {
|
|
1231
|
-
return /* @__PURE__ */ (0,
|
|
1198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1232
1199
|
"div",
|
|
1233
1200
|
{
|
|
1234
1201
|
"data-slot": "card-header",
|
|
@@ -1241,7 +1208,7 @@ function CardHeader({ className, ...props }) {
|
|
|
1241
1208
|
);
|
|
1242
1209
|
}
|
|
1243
1210
|
function CardContent({ className, ...props }) {
|
|
1244
|
-
return /* @__PURE__ */ (0,
|
|
1211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1245
1212
|
"div",
|
|
1246
1213
|
{
|
|
1247
1214
|
"data-slot": "card-content",
|
|
@@ -1255,13 +1222,13 @@ function CardContent({ className, ...props }) {
|
|
|
1255
1222
|
var React3 = require("react");
|
|
1256
1223
|
var import_menu = require("@base-ui/react/menu");
|
|
1257
1224
|
var import_lucide_react = require("lucide-react");
|
|
1258
|
-
var
|
|
1225
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1259
1226
|
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";
|
|
1260
1227
|
function DropdownMenu(props) {
|
|
1261
|
-
return /* @__PURE__ */ (0,
|
|
1228
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
|
|
1262
1229
|
}
|
|
1263
1230
|
function DropdownMenuTrigger(props) {
|
|
1264
|
-
return /* @__PURE__ */ (0,
|
|
1231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
1265
1232
|
}
|
|
1266
1233
|
function DropdownMenuContent({
|
|
1267
1234
|
className,
|
|
@@ -1269,14 +1236,14 @@ function DropdownMenuContent({
|
|
|
1269
1236
|
sideOffset = 4,
|
|
1270
1237
|
...props
|
|
1271
1238
|
}) {
|
|
1272
|
-
return /* @__PURE__ */ (0,
|
|
1239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1273
1240
|
import_menu.Menu.Positioner,
|
|
1274
1241
|
{
|
|
1275
1242
|
"data-slot": "dropdown-menu-positioner",
|
|
1276
1243
|
className: "z-50",
|
|
1277
1244
|
align,
|
|
1278
1245
|
sideOffset,
|
|
1279
|
-
children: /* @__PURE__ */ (0,
|
|
1246
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1280
1247
|
import_menu.Menu.Popup,
|
|
1281
1248
|
{
|
|
1282
1249
|
"data-slot": "dropdown-menu-content",
|
|
@@ -1294,7 +1261,7 @@ function DropdownMenuItem({
|
|
|
1294
1261
|
className,
|
|
1295
1262
|
...props
|
|
1296
1263
|
}) {
|
|
1297
|
-
return /* @__PURE__ */ (0,
|
|
1264
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1298
1265
|
import_menu.Menu.Item,
|
|
1299
1266
|
{
|
|
1300
1267
|
"data-slot": "dropdown-menu-item",
|
|
@@ -1306,7 +1273,7 @@ function DropdownMenuItem({
|
|
|
1306
1273
|
|
|
1307
1274
|
// src/components/ReportScenario.tsx
|
|
1308
1275
|
var import_lucide_react2 = require("lucide-react");
|
|
1309
|
-
var
|
|
1276
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1310
1277
|
var STATUS_META = {
|
|
1311
1278
|
passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
|
|
1312
1279
|
failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
|
|
@@ -1322,7 +1289,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1322
1289
|
const collapsible = collapse !== null && !hideTitle;
|
|
1323
1290
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
1324
1291
|
const actions = useScenarioActions();
|
|
1325
|
-
return /* @__PURE__ */ (0,
|
|
1292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1326
1293
|
Card,
|
|
1327
1294
|
{
|
|
1328
1295
|
id: scenario.id,
|
|
@@ -1330,10 +1297,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1330
1297
|
...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
|
|
1331
1298
|
className: "gap-0 py-0",
|
|
1332
1299
|
children: [
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1334
|
-
/* @__PURE__ */ (0,
|
|
1335
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
1336
|
-
collapsible ? /* @__PURE__ */ (0,
|
|
1300
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
1302
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1303
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1337
1304
|
"button",
|
|
1338
1305
|
{
|
|
1339
1306
|
type: "button",
|
|
@@ -1342,49 +1309,50 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1342
1309
|
"aria-controls": bodyId,
|
|
1343
1310
|
"aria-label": `Toggle ${scenario.title}`,
|
|
1344
1311
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
1345
|
-
children: /* @__PURE__ */ (0,
|
|
1312
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
1346
1313
|
}
|
|
1347
1314
|
) : null,
|
|
1348
|
-
/* @__PURE__ */ (0,
|
|
1349
|
-
/* @__PURE__ */ (0,
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
|
|
1316
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: scenario.title })
|
|
1350
1317
|
] }),
|
|
1351
|
-
/* @__PURE__ */ (0,
|
|
1352
|
-
/* @__PURE__ */ (0,
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1318
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScenarioStaleness, { scenario }),
|
|
1321
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: (0, import_duration3.formatDuration)(scenario.durationMs) }) : null,
|
|
1322
|
+
meta.showPill ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "sr-only", children: `Status: ${label}` }),
|
|
1323
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenu, { children: [
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1357
1325
|
DropdownMenuTrigger,
|
|
1358
1326
|
{
|
|
1359
1327
|
"aria-label": `Actions for ${scenario.title}`,
|
|
1360
1328
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1361
|
-
children: /* @__PURE__ */ (0,
|
|
1329
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
|
|
1362
1330
|
}
|
|
1363
1331
|
),
|
|
1364
|
-
/* @__PURE__ */ (0,
|
|
1365
|
-
/* @__PURE__ */ (0,
|
|
1366
|
-
/* @__PURE__ */ (0,
|
|
1332
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuContent, { align: "end", children: [
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
|
|
1334
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.LinkIcon, {}),
|
|
1367
1335
|
" Copy link"
|
|
1368
1336
|
] }),
|
|
1369
|
-
/* @__PURE__ */ (0,
|
|
1370
|
-
/* @__PURE__ */ (0,
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
|
|
1338
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.FileTextIcon, {}),
|
|
1371
1339
|
" Copy as Markdown"
|
|
1372
1340
|
] }),
|
|
1373
|
-
/* @__PURE__ */ (0,
|
|
1374
|
-
/* @__PURE__ */ (0,
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.SparklesIcon, {}),
|
|
1375
1343
|
" Explain with AI"
|
|
1376
1344
|
] })
|
|
1377
1345
|
] })
|
|
1378
1346
|
] }) : null
|
|
1379
1347
|
] })
|
|
1380
1348
|
] }),
|
|
1381
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
1382
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
1383
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0,
|
|
1349
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1350
|
+
scenario.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1351
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { children: ticket.url ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
1384
1352
|
] }) : null
|
|
1385
1353
|
] }),
|
|
1386
|
-
/* @__PURE__ */ (0,
|
|
1387
|
-
scenario.errorMessage ? /* @__PURE__ */ (0,
|
|
1354
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1355
|
+
scenario.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1388
1356
|
"pre",
|
|
1389
1357
|
{
|
|
1390
1358
|
role: "alert",
|
|
@@ -1392,11 +1360,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1392
1360
|
children: scenario.errorMessage
|
|
1393
1361
|
}
|
|
1394
1362
|
) : null,
|
|
1395
|
-
/* @__PURE__ */ (0,
|
|
1396
|
-
/* @__PURE__ */ (0,
|
|
1397
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
1398
|
-
/* @__PURE__ */ (0,
|
|
1399
|
-
/* @__PURE__ */ (0,
|
|
1363
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportStoryboard, { scenario }),
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportSteps, { scenario }),
|
|
1365
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportAttachments, { attachments: scenario.attachments }),
|
|
1367
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ReportTrace, { spans: scenario.otelSpans })
|
|
1400
1368
|
] })
|
|
1401
1369
|
]
|
|
1402
1370
|
}
|
|
@@ -1404,13 +1372,40 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1404
1372
|
}
|
|
1405
1373
|
|
|
1406
1374
|
// src/components/ReportScenarioList.tsx
|
|
1407
|
-
var
|
|
1375
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1408
1376
|
function ReportScenarioList({ feature }) {
|
|
1409
|
-
return /* @__PURE__ */ (0,
|
|
1377
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ReportScenario, { scenario }, scenario.id)) });
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
// src/components/ReportFeatureIntro.tsx
|
|
1381
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1382
|
+
function ReportFeatureIntro({ feature }) {
|
|
1383
|
+
const hasNarrative = Boolean(feature.narrative?.trim());
|
|
1384
|
+
const hasGlossary = Boolean(feature.glossary?.length);
|
|
1385
|
+
if (!hasNarrative && !hasGlossary) return null;
|
|
1386
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-3 border-l-2 border-border pl-4 text-sm", children: [
|
|
1387
|
+
hasNarrative ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1388
|
+
"div",
|
|
1389
|
+
{
|
|
1390
|
+
className: "es-doc-prose prose prose-sm max-w-none text-muted-foreground",
|
|
1391
|
+
dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
|
|
1392
|
+
}
|
|
1393
|
+
) : null,
|
|
1394
|
+
hasGlossary ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dt", { className: "font-medium text-foreground", children: entry.term }),
|
|
1396
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
|
|
1397
|
+
] }, entry.term)) }) : null
|
|
1398
|
+
] });
|
|
1399
|
+
}
|
|
1400
|
+
function featureKindLabel(kind) {
|
|
1401
|
+
if (kind === "ability") return "Ability";
|
|
1402
|
+
if (kind === "business-need") return "Business need";
|
|
1403
|
+
if (kind === "feature") return "Feature";
|
|
1404
|
+
return null;
|
|
1410
1405
|
}
|
|
1411
1406
|
|
|
1412
1407
|
// src/components/ReportFeature.tsx
|
|
1413
|
-
var
|
|
1408
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1414
1409
|
function ReportFeature({ feature }) {
|
|
1415
1410
|
const titleId = `${feature.id}-title`;
|
|
1416
1411
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1418,10 +1413,11 @@ function ReportFeature({ feature }) {
|
|
|
1418
1413
|
const skipped = s.skipped + s.pending;
|
|
1419
1414
|
const collapse = useCollapse();
|
|
1420
1415
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
1416
|
+
const kindLabel = featureKindLabel(feature.kind);
|
|
1421
1417
|
return (
|
|
1422
1418
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1423
1419
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1424
|
-
/* @__PURE__ */ (0,
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1425
1421
|
"section",
|
|
1426
1422
|
{
|
|
1427
1423
|
id: feature.id,
|
|
@@ -1429,9 +1425,9 @@ function ReportFeature({ feature }) {
|
|
|
1429
1425
|
"aria-labelledby": titleId,
|
|
1430
1426
|
className: "flex flex-col gap-3",
|
|
1431
1427
|
children: [
|
|
1432
|
-
/* @__PURE__ */ (0,
|
|
1433
|
-
/* @__PURE__ */ (0,
|
|
1434
|
-
/* @__PURE__ */ (0,
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1429
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "min-w-0", children: [
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1435
1431
|
"button",
|
|
1436
1432
|
{
|
|
1437
1433
|
type: "button",
|
|
@@ -1440,31 +1436,34 @@ function ReportFeature({ feature }) {
|
|
|
1440
1436
|
"aria-controls": bodyId,
|
|
1441
1437
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1442
1438
|
children: [
|
|
1443
|
-
/* @__PURE__ */ (0,
|
|
1444
|
-
/* @__PURE__ */ (0,
|
|
1439
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate", children: feature.title })
|
|
1445
1441
|
]
|
|
1446
1442
|
}
|
|
1447
1443
|
) : feature.title }),
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: cn("mt-0.5 flex items-center gap-2 text-xs text-muted-foreground", collapse && "pl-5"), children: [
|
|
1445
|
+
kindLabel ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "shrink-0 rounded-sm bg-muted px-1.5 py-0.5 font-medium text-foreground", children: kindLabel }) : null,
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate font-mono", children: feature.sourceFile })
|
|
1447
|
+
] })
|
|
1449
1448
|
] }),
|
|
1450
|
-
/* @__PURE__ */ (0,
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1451
1450
|
"p",
|
|
1452
1451
|
{
|
|
1453
1452
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1454
1453
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1455
1454
|
children: [
|
|
1456
|
-
/* @__PURE__ */ (0,
|
|
1455
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
|
|
1457
1456
|
s.passed,
|
|
1458
1457
|
" passed"
|
|
1459
1458
|
] }),
|
|
1460
|
-
/* @__PURE__ */ (0,
|
|
1461
|
-
/* @__PURE__ */ (0,
|
|
1459
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1460
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1462
1461
|
s.failed,
|
|
1463
1462
|
" failed"
|
|
1464
1463
|
] }),
|
|
1465
|
-
skipped > 0 ? /* @__PURE__ */ (0,
|
|
1466
|
-
/* @__PURE__ */ (0,
|
|
1467
|
-
/* @__PURE__ */ (0,
|
|
1464
|
+
skipped > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1465
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
|
|
1468
1467
|
skipped,
|
|
1469
1468
|
" skipped"
|
|
1470
1469
|
] })
|
|
@@ -1473,7 +1472,10 @@ function ReportFeature({ feature }) {
|
|
|
1473
1472
|
}
|
|
1474
1473
|
)
|
|
1475
1474
|
] }),
|
|
1476
|
-
/* @__PURE__ */ (0,
|
|
1475
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: [
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportFeatureIntro, { feature }),
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ReportScenarioList, { feature })
|
|
1478
|
+
] })
|
|
1477
1479
|
]
|
|
1478
1480
|
}
|
|
1479
1481
|
)
|
|
@@ -1481,77 +1483,80 @@ function ReportFeature({ feature }) {
|
|
|
1481
1483
|
}
|
|
1482
1484
|
|
|
1483
1485
|
// src/components/ReportFeatureList.tsx
|
|
1484
|
-
var
|
|
1486
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1485
1487
|
function ReportFeatureList() {
|
|
1486
1488
|
const report = useReport();
|
|
1487
|
-
return /* @__PURE__ */ (0,
|
|
1489
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ReportFeature, { feature }, feature.id)) });
|
|
1488
1490
|
}
|
|
1489
1491
|
|
|
1490
|
-
// src/components/
|
|
1491
|
-
var
|
|
1492
|
-
var
|
|
1493
|
-
function
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
"flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
|
|
1500
|
-
className
|
|
1501
|
-
),
|
|
1502
|
-
...props
|
|
1503
|
-
}
|
|
1504
|
-
);
|
|
1505
|
-
}
|
|
1506
|
-
function EmptyHeader({ className, ...props }) {
|
|
1507
|
-
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1508
|
-
"div",
|
|
1509
|
-
{
|
|
1510
|
-
"data-slot": "empty-header",
|
|
1511
|
-
className: cn(
|
|
1512
|
-
"flex max-w-sm flex-col items-center gap-2 text-center",
|
|
1513
|
-
className
|
|
1514
|
-
),
|
|
1515
|
-
...props
|
|
1516
|
-
}
|
|
1517
|
-
);
|
|
1518
|
-
}
|
|
1519
|
-
var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
1520
|
-
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
1521
|
-
{
|
|
1522
|
-
variants: {
|
|
1523
|
-
variant: {
|
|
1524
|
-
default: "bg-transparent",
|
|
1525
|
-
icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6"
|
|
1526
|
-
}
|
|
1527
|
-
},
|
|
1528
|
-
defaultVariants: {
|
|
1529
|
-
variant: "default"
|
|
1530
|
-
}
|
|
1492
|
+
// src/components/ReportMeta.tsx
|
|
1493
|
+
var import_duration4 = require("executable-stories-core/utils/duration");
|
|
1494
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1495
|
+
function ReportMeta() {
|
|
1496
|
+
const report = useReport();
|
|
1497
|
+
const items = [];
|
|
1498
|
+
const linkClass = "text-primary underline underline-offset-2";
|
|
1499
|
+
if (report.startedAtMs) {
|
|
1500
|
+
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1531
1501
|
}
|
|
1532
|
-
)
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
{
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1502
|
+
if (report.durationMs) {
|
|
1503
|
+
items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
|
|
1504
|
+
}
|
|
1505
|
+
if (report.packageVersion) {
|
|
1506
|
+
items.push({ label: "Version", value: report.packageVersion });
|
|
1507
|
+
}
|
|
1508
|
+
if (report.ci?.branch) {
|
|
1509
|
+
items.push({ label: "Branch", value: report.ci.branch });
|
|
1510
|
+
}
|
|
1511
|
+
const sha = report.ci?.commitSha ?? report.gitSha;
|
|
1512
|
+
if (sha) {
|
|
1513
|
+
const url = commitUrl(report.ci, report.gitSha);
|
|
1514
|
+
const short = sha.slice(0, 8);
|
|
1515
|
+
items.push({
|
|
1516
|
+
label: "Commit",
|
|
1517
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
if (report.ci) {
|
|
1521
|
+
items.push({
|
|
1522
|
+
label: "CI",
|
|
1523
|
+
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1524
|
+
ciDisplayName(report.ci.name),
|
|
1525
|
+
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1526
|
+
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
if (report.ci?.prNumber) {
|
|
1530
|
+
const url = prUrl(report.ci);
|
|
1531
|
+
const label = `#${report.ci.prNumber}`;
|
|
1532
|
+
items.push({
|
|
1533
|
+
label: "PR",
|
|
1534
|
+
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
if (items.length === 0) return null;
|
|
1538
|
+
return (
|
|
1539
|
+
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1540
|
+
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1541
|
+
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1542
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1543
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1544
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1545
|
+
"Run details"
|
|
1546
|
+
] }),
|
|
1547
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1548
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1549
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1550
|
+
] }, item.label)) })
|
|
1551
|
+
] })
|
|
1541
1552
|
);
|
|
1542
1553
|
}
|
|
1543
1554
|
|
|
1544
|
-
// src/components/ReportEmpty.tsx
|
|
1545
|
-
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1546
|
-
function ReportEmpty({ message }) {
|
|
1547
|
-
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1548
|
-
}
|
|
1549
|
-
|
|
1550
1555
|
// src/components/ReportSummary.tsx
|
|
1551
|
-
var
|
|
1556
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1552
1557
|
function ReportSummary({ className }) {
|
|
1553
1558
|
const report = useReport();
|
|
1554
|
-
return /* @__PURE__ */ (0,
|
|
1559
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1555
1560
|
ReportSummaryView,
|
|
1556
1561
|
{
|
|
1557
1562
|
summary: report.summary,
|
|
@@ -1577,7 +1582,7 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1577
1582
|
if (summary.pending > 0) {
|
|
1578
1583
|
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1579
1584
|
}
|
|
1580
|
-
return /* @__PURE__ */ (0,
|
|
1585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1581
1586
|
"dl",
|
|
1582
1587
|
{
|
|
1583
1588
|
className: cn(
|
|
@@ -1590,17 +1595,17 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1590
1595
|
"aria-label": ariaLabel,
|
|
1591
1596
|
children: cards.map((c) => {
|
|
1592
1597
|
const tone = TONE[c.status];
|
|
1593
|
-
return /* @__PURE__ */ (0,
|
|
1598
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1594
1599
|
"div",
|
|
1595
1600
|
{
|
|
1596
1601
|
"data-status": c.status,
|
|
1597
1602
|
className: "rounded-lg border bg-card px-4 py-3",
|
|
1598
1603
|
children: [
|
|
1599
|
-
/* @__PURE__ */ (0,
|
|
1600
|
-
/* @__PURE__ */ (0,
|
|
1604
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1605
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1601
1606
|
c.label
|
|
1602
1607
|
] }),
|
|
1603
|
-
/* @__PURE__ */ (0,
|
|
1608
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
|
|
1604
1609
|
]
|
|
1605
1610
|
},
|
|
1606
1611
|
c.status
|
|
@@ -1610,155 +1615,38 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1610
1615
|
);
|
|
1611
1616
|
}
|
|
1612
1617
|
|
|
1613
|
-
// src/components/
|
|
1614
|
-
var
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1618
|
+
// src/components/ReportSchemaError.tsx
|
|
1619
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1620
|
+
function ReportSchemaError({ error }) {
|
|
1621
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1622
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1623
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: error.message }),
|
|
1624
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { children: [
|
|
1625
|
+
"The report bundle is newer than this version of ",
|
|
1626
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("code", { children: "executable-stories-react" }),
|
|
1627
|
+
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1628
|
+
] }) : null,
|
|
1629
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { children: [
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { children: [
|
|
1631
|
+
error.issues.length,
|
|
1632
|
+
" validation issue",
|
|
1633
|
+
error.issues.length === 1 ? "" : "s"
|
|
1634
|
+
] }),
|
|
1635
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1636
|
+
] }) : null
|
|
1637
|
+
] });
|
|
1631
1638
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1639
|
+
|
|
1640
|
+
// src/components/ReportShell.tsx
|
|
1641
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1642
|
+
function ReportErrorShell({
|
|
1643
|
+
error,
|
|
1644
|
+
className,
|
|
1645
|
+
title,
|
|
1646
|
+
dataTheme
|
|
1647
|
+
}) {
|
|
1648
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportSchemaError, { error }) });
|
|
1635
1649
|
}
|
|
1636
|
-
var CI_DISPLAY_NAMES = {
|
|
1637
|
-
github: "GitHub Actions",
|
|
1638
|
-
gitlab: "GitLab CI",
|
|
1639
|
-
circleci: "CircleCI",
|
|
1640
|
-
jenkins: "Jenkins",
|
|
1641
|
-
azure: "Azure DevOps",
|
|
1642
|
-
buildkite: "Buildkite",
|
|
1643
|
-
travis: "Travis CI",
|
|
1644
|
-
ci: "CI"
|
|
1645
|
-
};
|
|
1646
|
-
function ciDisplayName(name) {
|
|
1647
|
-
return CI_DISPLAY_NAMES[name] ?? name;
|
|
1648
|
-
}
|
|
1649
|
-
function commitUrl(ci, sha) {
|
|
1650
|
-
const commitSha = ci?.commitSha ?? sha;
|
|
1651
|
-
if (!ci?.url || !commitSha) return void 0;
|
|
1652
|
-
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1653
|
-
if (github) return `${github[1]}/commit/${commitSha}`;
|
|
1654
|
-
const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
|
|
1655
|
-
if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
|
|
1656
|
-
return void 0;
|
|
1657
|
-
}
|
|
1658
|
-
function prUrl(ci) {
|
|
1659
|
-
if (!ci?.url || !ci.prNumber) return void 0;
|
|
1660
|
-
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1661
|
-
if (github) return `${github[1]}/pull/${ci.prNumber}`;
|
|
1662
|
-
return void 0;
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
// src/components/ReportMeta.tsx
|
|
1666
|
-
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1667
|
-
function ReportMeta() {
|
|
1668
|
-
const report = useReport();
|
|
1669
|
-
const items = [];
|
|
1670
|
-
const linkClass = "text-primary underline underline-offset-2";
|
|
1671
|
-
if (report.startedAtMs) {
|
|
1672
|
-
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1673
|
-
}
|
|
1674
|
-
if (report.durationMs) {
|
|
1675
|
-
items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
|
|
1676
|
-
}
|
|
1677
|
-
if (report.packageVersion) {
|
|
1678
|
-
items.push({ label: "Version", value: report.packageVersion });
|
|
1679
|
-
}
|
|
1680
|
-
if (report.ci?.branch) {
|
|
1681
|
-
items.push({ label: "Branch", value: report.ci.branch });
|
|
1682
|
-
}
|
|
1683
|
-
const sha = report.ci?.commitSha ?? report.gitSha;
|
|
1684
|
-
if (sha) {
|
|
1685
|
-
const url = commitUrl(report.ci, report.gitSha);
|
|
1686
|
-
const short = sha.slice(0, 8);
|
|
1687
|
-
items.push({
|
|
1688
|
-
label: "Commit",
|
|
1689
|
-
value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1690
|
-
});
|
|
1691
|
-
}
|
|
1692
|
-
if (report.ci) {
|
|
1693
|
-
items.push({
|
|
1694
|
-
label: "CI",
|
|
1695
|
-
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1696
|
-
ciDisplayName(report.ci.name),
|
|
1697
|
-
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1698
|
-
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
1701
|
-
if (report.ci?.prNumber) {
|
|
1702
|
-
const url = prUrl(report.ci);
|
|
1703
|
-
const label = `#${report.ci.prNumber}`;
|
|
1704
|
-
items.push({
|
|
1705
|
-
label: "PR",
|
|
1706
|
-
value: url ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1707
|
-
});
|
|
1708
|
-
}
|
|
1709
|
-
if (items.length === 0) return null;
|
|
1710
|
-
return (
|
|
1711
|
-
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1712
|
-
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1713
|
-
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1714
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1715
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1716
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1717
|
-
"Run details"
|
|
1718
|
-
] }),
|
|
1719
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1720
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1721
|
-
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1722
|
-
] }, item.label)) })
|
|
1723
|
-
] })
|
|
1724
|
-
);
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
// src/components/ReportSchemaError.tsx
|
|
1728
|
-
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1729
|
-
function ReportSchemaError({ error }) {
|
|
1730
|
-
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1731
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1732
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: error.message }),
|
|
1733
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { children: [
|
|
1734
|
-
"The report bundle is newer than this version of ",
|
|
1735
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("code", { children: "executable-stories-react" }),
|
|
1736
|
-
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1737
|
-
] }) : null,
|
|
1738
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { children: [
|
|
1739
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("summary", { children: [
|
|
1740
|
-
error.issues.length,
|
|
1741
|
-
" validation issue",
|
|
1742
|
-
error.issues.length === 1 ? "" : "s"
|
|
1743
|
-
] }),
|
|
1744
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1745
|
-
] }) : null
|
|
1746
|
-
] });
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
// src/components/ReportShell.tsx
|
|
1750
|
-
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1751
|
-
function ReportErrorShell({
|
|
1752
|
-
error,
|
|
1753
|
-
className,
|
|
1754
|
-
title,
|
|
1755
|
-
dataTheme
|
|
1756
|
-
}) {
|
|
1757
|
-
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ReportSchemaError, { error }) });
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
// src/interactive/ReportInteractive.tsx
|
|
1761
|
-
var import_lucide_react5 = require("lucide-react");
|
|
1762
1650
|
|
|
1763
1651
|
// src/components/ui/switch.tsx
|
|
1764
1652
|
var React4 = require("react");
|
|
@@ -1843,99 +1731,207 @@ function ToggleGroupItem({
|
|
|
1843
1731
|
);
|
|
1844
1732
|
}
|
|
1845
1733
|
|
|
1846
|
-
// src/
|
|
1847
|
-
var
|
|
1848
|
-
var import_lucide_react3 = require("lucide-react");
|
|
1734
|
+
// src/context/ReportRoot.tsx
|
|
1735
|
+
var import_react9 = require("react");
|
|
1849
1736
|
|
|
1850
|
-
// src/components/
|
|
1851
|
-
var React6 = require("react");
|
|
1737
|
+
// src/components/doc/NarrativeBlocks.tsx
|
|
1852
1738
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
type,
|
|
1858
|
-
"data-slot": "input",
|
|
1859
|
-
className: cn(
|
|
1860
|
-
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
1861
|
-
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
1862
|
-
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
1863
|
-
className
|
|
1864
|
-
),
|
|
1865
|
-
...props
|
|
1866
|
-
}
|
|
1867
|
-
);
|
|
1739
|
+
var FILE_TREE_TYPE = "file-tree";
|
|
1740
|
+
var DATA_MODEL_TYPE = "data-model";
|
|
1741
|
+
function isChangeKind(value) {
|
|
1742
|
+
return value === "added" || value === "modified" || value === "removed" || value === "renamed";
|
|
1868
1743
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1744
|
+
function isRecord(value) {
|
|
1745
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1746
|
+
}
|
|
1747
|
+
function str(value) {
|
|
1748
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
1749
|
+
}
|
|
1750
|
+
function AuthoredMarker({ authored }) {
|
|
1751
|
+
if (authored !== "agent") return null;
|
|
1752
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Badge, { variant: "outline", className: "border-border text-[10px] text-muted-foreground", children: "AI-authored, not verified by a run" });
|
|
1753
|
+
}
|
|
1754
|
+
function BlockFrame({
|
|
1755
|
+
title,
|
|
1756
|
+
authored,
|
|
1757
|
+
children
|
|
1758
|
+
}) {
|
|
1759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("figure", { className: "my-3", children: [
|
|
1760
|
+
title || authored === "agent" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("figcaption", { className: "mb-1.5 flex flex-wrap items-center gap-2", children: [
|
|
1761
|
+
title ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-xs font-medium text-muted-foreground", children: title }) : null,
|
|
1762
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(AuthoredMarker, { authored })
|
|
1763
|
+
] }) : null,
|
|
1764
|
+
children
|
|
1765
|
+
] });
|
|
1766
|
+
}
|
|
1767
|
+
function ChangeBadge({ change }) {
|
|
1768
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Badge, { variant: "tag", className: "text-[10px]", children: change });
|
|
1769
|
+
}
|
|
1770
|
+
function parseFileTree(data) {
|
|
1771
|
+
if (!isRecord(data) || !Array.isArray(data.files)) return void 0;
|
|
1772
|
+
const files = [];
|
|
1773
|
+
for (const raw of data.files) {
|
|
1774
|
+
if (typeof raw === "string") {
|
|
1775
|
+
if (raw) files.push({ path: raw });
|
|
1776
|
+
continue;
|
|
1886
1777
|
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1778
|
+
if (!isRecord(raw)) continue;
|
|
1779
|
+
const path = str(raw.path);
|
|
1780
|
+
if (!path) continue;
|
|
1781
|
+
files.push({
|
|
1782
|
+
path,
|
|
1783
|
+
change: isChangeKind(raw.change) ? raw.change : void 0,
|
|
1784
|
+
note: str(raw.note)
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
if (files.length === 0) return void 0;
|
|
1788
|
+
return { title: str(data.title), authored: str(data.authored), files };
|
|
1789
|
+
}
|
|
1790
|
+
function buildTree(files) {
|
|
1791
|
+
const root = { name: "", children: /* @__PURE__ */ new Map() };
|
|
1792
|
+
for (const file of files) {
|
|
1793
|
+
const segments = file.path.split("/").filter(Boolean);
|
|
1794
|
+
let node = root;
|
|
1795
|
+
segments.forEach((segment, index) => {
|
|
1796
|
+
let child = node.children.get(segment);
|
|
1797
|
+
if (!child) {
|
|
1798
|
+
child = { name: segment, children: /* @__PURE__ */ new Map() };
|
|
1799
|
+
node.children.set(segment, child);
|
|
1891
1800
|
}
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1801
|
+
if (index === segments.length - 1) child.file = file;
|
|
1802
|
+
node = child;
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
return root;
|
|
1806
|
+
}
|
|
1807
|
+
function sortedChildren(node) {
|
|
1808
|
+
return [...node.children.values()].sort((a, b) => {
|
|
1809
|
+
const aDir = a.children.size > 0;
|
|
1810
|
+
const bDir = b.children.size > 0;
|
|
1811
|
+
if (aDir !== bDir) return aDir ? -1 : 1;
|
|
1812
|
+
return a.name.localeCompare(b.name);
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
function TreeBranch({ node, depth }) {
|
|
1816
|
+
const children = sortedChildren(node);
|
|
1817
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
|
|
1818
|
+
const isDir = child.children.size > 0;
|
|
1819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { children: [
|
|
1820
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-wrap items-baseline gap-2", children: [
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1822
|
+
"span",
|
|
1898
1823
|
{
|
|
1899
|
-
|
|
1900
|
-
|
|
1824
|
+
className: cn(
|
|
1825
|
+
"font-mono text-xs",
|
|
1826
|
+
isDir ? "text-muted-foreground" : "text-foreground"
|
|
1827
|
+
),
|
|
1828
|
+
children: isDir ? `${child.name}/` : child.name
|
|
1901
1829
|
}
|
|
1902
1830
|
),
|
|
1903
|
-
/* @__PURE__ */ (0,
|
|
1904
|
-
|
|
1905
|
-
{
|
|
1906
|
-
ref,
|
|
1907
|
-
id: inputId,
|
|
1908
|
-
type: "search",
|
|
1909
|
-
value,
|
|
1910
|
-
onChange: handleChange,
|
|
1911
|
-
onKeyDown: handleKeyDown,
|
|
1912
|
-
placeholder,
|
|
1913
|
-
autoComplete: "off",
|
|
1914
|
-
spellCheck: false,
|
|
1915
|
-
"aria-keyshortcuts": "/",
|
|
1916
|
-
className: "pl-8"
|
|
1917
|
-
}
|
|
1918
|
-
)
|
|
1831
|
+
child.file?.change ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ChangeBadge, { change: child.file.change }) : null,
|
|
1832
|
+
child.file?.note ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
|
|
1919
1833
|
] }),
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
);
|
|
1834
|
+
isDir ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TreeBranch, { node: child, depth: depth + 1 }) : null
|
|
1835
|
+
] }, child.name);
|
|
1836
|
+
}) });
|
|
1837
|
+
}
|
|
1838
|
+
function FileTreeBlock({ entry }) {
|
|
1839
|
+
const data = parseFileTree(entry.data);
|
|
1840
|
+
if (!data) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(UnreadableBlock, { entry });
|
|
1841
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
|
|
1842
|
+
}
|
|
1843
|
+
function parseDataModel(data) {
|
|
1844
|
+
if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
|
|
1845
|
+
const fields = [];
|
|
1846
|
+
for (const raw of data.fields) {
|
|
1847
|
+
if (!isRecord(raw)) continue;
|
|
1848
|
+
const name = str(raw.name);
|
|
1849
|
+
if (!name) continue;
|
|
1850
|
+
fields.push({
|
|
1851
|
+
name,
|
|
1852
|
+
type: str(raw.type),
|
|
1853
|
+
note: str(raw.note),
|
|
1854
|
+
change: isChangeKind(raw.change) ? raw.change : void 0
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
if (fields.length === 0) return void 0;
|
|
1858
|
+
return {
|
|
1859
|
+
title: str(data.title),
|
|
1860
|
+
authored: str(data.authored),
|
|
1861
|
+
name: str(data.name),
|
|
1862
|
+
fields
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
function DataModelBlock({ entry }) {
|
|
1866
|
+
const data = parseDataModel(entry.data);
|
|
1867
|
+
if (!data) return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(UnreadableBlock, { entry });
|
|
1868
|
+
const hasNotes = data.fields.some((f) => f.note);
|
|
1869
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
|
|
1870
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("tr", { className: "bg-muted/50", children: [
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
|
|
1872
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
|
|
1873
|
+
hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
|
|
1874
|
+
] }) }),
|
|
1875
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("tbody", { children: data.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("tr", { children: [
|
|
1876
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1877
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "font-mono text-xs text-foreground", children: field.name }),
|
|
1878
|
+
field.change ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ChangeBadge, { change: field.change }) : null
|
|
1879
|
+
] }) }),
|
|
1880
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
|
|
1881
|
+
hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
|
|
1882
|
+
] }, field.name)) })
|
|
1883
|
+
] }) }) });
|
|
1884
|
+
}
|
|
1885
|
+
function UnreadableBlock({ entry }) {
|
|
1886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
|
|
1888
|
+
entry.type,
|
|
1889
|
+
" (unrecognised shape)"
|
|
1890
|
+
] }),
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
1892
|
+
"pre",
|
|
1893
|
+
{
|
|
1894
|
+
tabIndex: 0,
|
|
1895
|
+
className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
|
|
1896
|
+
children: safeStringify2(entry.data)
|
|
1897
|
+
}
|
|
1898
|
+
)
|
|
1899
|
+
] });
|
|
1900
|
+
}
|
|
1901
|
+
function safeStringify2(value) {
|
|
1902
|
+
try {
|
|
1903
|
+
return JSON.stringify(value, null, 2);
|
|
1904
|
+
} catch {
|
|
1905
|
+
return String(value);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
var narrativeBlockRenderers = {
|
|
1909
|
+
[FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(FileTreeBlock, { entry }),
|
|
1910
|
+
[DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(DataModelBlock, { entry })
|
|
1911
|
+
};
|
|
1936
1912
|
|
|
1937
|
-
// src/
|
|
1938
|
-
var
|
|
1913
|
+
// src/context/ReportRoot.tsx
|
|
1914
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1915
|
+
var EMPTY_RENDERERS2 = {};
|
|
1916
|
+
function ReportRoot({
|
|
1917
|
+
report,
|
|
1918
|
+
customRenderers,
|
|
1919
|
+
renderers,
|
|
1920
|
+
children
|
|
1921
|
+
}) {
|
|
1922
|
+
const value = (0, import_react9.useMemo)(
|
|
1923
|
+
() => ({
|
|
1924
|
+
report,
|
|
1925
|
+
// Narrative blocks render everywhere the report does (static SSR, the
|
|
1926
|
+
// island, Astro) without every host wiring them up. A host renderer for
|
|
1927
|
+
// the same type still wins.
|
|
1928
|
+
customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
|
|
1929
|
+
renderers: renderers ?? EMPTY_RENDERERS2
|
|
1930
|
+
}),
|
|
1931
|
+
[report, customRenderers, renderers]
|
|
1932
|
+
);
|
|
1933
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ReportContext.Provider, { value, children });
|
|
1934
|
+
}
|
|
1939
1935
|
|
|
1940
1936
|
// src/lib/hash-state.ts
|
|
1941
1937
|
var DEFAULT_URL_STATE = {
|
|
@@ -2005,11 +2001,115 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
2005
2001
|
}
|
|
2006
2002
|
}
|
|
2007
2003
|
|
|
2004
|
+
// src/result.ts
|
|
2005
|
+
var ok = (data) => ({ ok: true, data });
|
|
2006
|
+
function unwrapReport(value) {
|
|
2007
|
+
if (typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean") {
|
|
2008
|
+
return value;
|
|
2009
|
+
}
|
|
2010
|
+
return ok(value);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
// src/interactive/filter.ts
|
|
2014
|
+
function normalizeQuery(query) {
|
|
2015
|
+
return query.trim().toLowerCase();
|
|
2016
|
+
}
|
|
2017
|
+
function allTags(report) {
|
|
2018
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2019
|
+
for (const feature of report.features) {
|
|
2020
|
+
for (const scenario of feature.scenarios) {
|
|
2021
|
+
for (const tag of scenario.tags) seen.add(tag);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
return [...seen];
|
|
2025
|
+
}
|
|
2026
|
+
function queryMatches(scenario, q) {
|
|
2027
|
+
if (q === "") return true;
|
|
2028
|
+
if (scenario.title.toLowerCase().includes(q)) return true;
|
|
2029
|
+
for (const tag of scenario.tags) {
|
|
2030
|
+
if (tag.toLowerCase().includes(q)) return true;
|
|
2031
|
+
}
|
|
2032
|
+
for (const step of scenario.steps) {
|
|
2033
|
+
if (step.text.toLowerCase().includes(q)) return true;
|
|
2034
|
+
}
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
function scenarioMatches(scenario, q, status, tags) {
|
|
2038
|
+
if (status !== "all" && scenario.status !== status) return false;
|
|
2039
|
+
if (tags.length > 0 && !tags.some((t) => scenario.tags.includes(t))) return false;
|
|
2040
|
+
return queryMatches(scenario, q);
|
|
2041
|
+
}
|
|
2042
|
+
function summarizeScenarios(scenarios) {
|
|
2043
|
+
let total = 0, passed = 0, failed = 0, skipped = 0, pending = 0, durationMs = 0;
|
|
2044
|
+
for (const s of scenarios) {
|
|
2045
|
+
total += 1;
|
|
2046
|
+
durationMs += s.durationMs;
|
|
2047
|
+
if (s.status === "passed") passed += 1;
|
|
2048
|
+
else if (s.status === "failed") failed += 1;
|
|
2049
|
+
else if (s.status === "skipped") skipped += 1;
|
|
2050
|
+
else pending += 1;
|
|
2051
|
+
}
|
|
2052
|
+
return { total, passed, failed, skipped, pending, durationMs };
|
|
2053
|
+
}
|
|
2054
|
+
function filterReport(report, criteria) {
|
|
2055
|
+
const c = typeof criteria === "string" ? { query: criteria } : criteria;
|
|
2056
|
+
const q = normalizeQuery(c.query ?? "");
|
|
2057
|
+
const status = c.status ?? "all";
|
|
2058
|
+
const tags = c.tags ?? [];
|
|
2059
|
+
if (q === "" && status === "all" && tags.length === 0) return report;
|
|
2060
|
+
const features = [];
|
|
2061
|
+
let topTotal = 0, topPassed = 0, topFailed = 0, topSkipped = 0, topPending = 0, topDuration = 0;
|
|
2062
|
+
for (const feature of report.features) {
|
|
2063
|
+
const matched = feature.scenarios.filter((s) => scenarioMatches(s, q, status, tags));
|
|
2064
|
+
if (matched.length === 0) continue;
|
|
2065
|
+
const summary = summarizeScenarios(matched);
|
|
2066
|
+
features.push({ ...feature, summary, scenarios: matched });
|
|
2067
|
+
topTotal += summary.total;
|
|
2068
|
+
topPassed += summary.passed;
|
|
2069
|
+
topFailed += summary.failed;
|
|
2070
|
+
topSkipped += summary.skipped;
|
|
2071
|
+
topPending += summary.pending;
|
|
2072
|
+
topDuration += summary.durationMs;
|
|
2073
|
+
}
|
|
2074
|
+
return {
|
|
2075
|
+
...report,
|
|
2076
|
+
summary: {
|
|
2077
|
+
total: topTotal,
|
|
2078
|
+
passed: topPassed,
|
|
2079
|
+
failed: topFailed,
|
|
2080
|
+
skipped: topSkipped,
|
|
2081
|
+
pending: topPending,
|
|
2082
|
+
durationMs: topDuration
|
|
2083
|
+
},
|
|
2084
|
+
features
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
function listFailures(report) {
|
|
2088
|
+
const out = [];
|
|
2089
|
+
for (const feature of report.features) {
|
|
2090
|
+
for (const scenario of feature.scenarios) {
|
|
2091
|
+
if (scenario.status === "failed") {
|
|
2092
|
+
const ref = {
|
|
2093
|
+
featureId: feature.id,
|
|
2094
|
+
scenarioId: scenario.id,
|
|
2095
|
+
scenarioTitle: scenario.title
|
|
2096
|
+
};
|
|
2097
|
+
if (scenario.errorMessage !== void 0) {
|
|
2098
|
+
ref.errorMessage = scenario.errorMessage;
|
|
2099
|
+
}
|
|
2100
|
+
out.push(ref);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
return out;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2008
2107
|
// src/interactive/ReportFailureBanner.tsx
|
|
2108
|
+
var import_react10 = require("react");
|
|
2009
2109
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2010
2110
|
function ReportFailureBanner({ failures }) {
|
|
2011
2111
|
const first = failures[0];
|
|
2012
|
-
const jumpToFirst = (0,
|
|
2112
|
+
const jumpToFirst = (0, import_react10.useCallback)(() => {
|
|
2013
2113
|
if (!first) return;
|
|
2014
2114
|
scrollToScenarioId(first.scenarioId);
|
|
2015
2115
|
}, [first]);
|
|
@@ -2043,17 +2143,91 @@ function ReportFailureBanner({ failures }) {
|
|
|
2043
2143
|
);
|
|
2044
2144
|
}
|
|
2045
2145
|
|
|
2046
|
-
// src/interactive/
|
|
2047
|
-
var
|
|
2146
|
+
// src/interactive/ReportFilters.tsx
|
|
2147
|
+
var import_react11 = require("react");
|
|
2048
2148
|
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2149
|
+
var TAG_LIMIT = 12;
|
|
2150
|
+
function ReportFilters({
|
|
2151
|
+
statuses,
|
|
2152
|
+
status,
|
|
2153
|
+
onStatus,
|
|
2154
|
+
tags,
|
|
2155
|
+
activeTags,
|
|
2156
|
+
onToggleTag
|
|
2157
|
+
}) {
|
|
2158
|
+
const [showAllTags, setShowAllTags] = (0, import_react11.useState)(false);
|
|
2159
|
+
if (statuses.length === 0 && tags.length === 0) return null;
|
|
2160
|
+
const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
|
|
2161
|
+
const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
|
|
2162
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2163
|
+
statuses.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
|
|
2164
|
+
const active = status === opt.key;
|
|
2165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2166
|
+
"button",
|
|
2167
|
+
{
|
|
2168
|
+
type: "button",
|
|
2169
|
+
"aria-pressed": active,
|
|
2170
|
+
onClick: () => onStatus(active && opt.key !== "all" ? "all" : opt.key),
|
|
2171
|
+
className: cn(
|
|
2172
|
+
"cursor-pointer rounded-md border px-2 py-1 text-xs transition-colors",
|
|
2173
|
+
active ? "border-primary bg-primary text-primary-foreground" : "border-border text-muted-foreground hover:bg-muted hover:text-foreground"
|
|
2174
|
+
),
|
|
2175
|
+
children: [
|
|
2176
|
+
opt.label,
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
|
|
2178
|
+
]
|
|
2179
|
+
},
|
|
2180
|
+
opt.key
|
|
2181
|
+
);
|
|
2182
|
+
}) }) : null,
|
|
2183
|
+
tags.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
|
|
2184
|
+
visibleTags.map((tag) => {
|
|
2185
|
+
const active = activeTags.includes(tag);
|
|
2186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2187
|
+
"button",
|
|
2188
|
+
{
|
|
2189
|
+
type: "button",
|
|
2190
|
+
"aria-pressed": active,
|
|
2191
|
+
onClick: () => onToggleTag(tag),
|
|
2192
|
+
className: "cursor-pointer",
|
|
2193
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2194
|
+
Badge,
|
|
2195
|
+
{
|
|
2196
|
+
variant: "tag",
|
|
2197
|
+
className: cn(
|
|
2198
|
+
active && "border-primary bg-primary text-primary-foreground"
|
|
2199
|
+
),
|
|
2200
|
+
children: tag
|
|
2201
|
+
}
|
|
2202
|
+
)
|
|
2203
|
+
}
|
|
2204
|
+
) }, tag);
|
|
2205
|
+
}),
|
|
2206
|
+
tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2207
|
+
"button",
|
|
2208
|
+
{
|
|
2209
|
+
type: "button",
|
|
2210
|
+
"aria-expanded": showAllTags,
|
|
2211
|
+
onClick: () => setShowAllTags((v) => !v),
|
|
2212
|
+
className: "cursor-pointer rounded-full px-2 py-0.5 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
2213
|
+
children: tagsCollapsed ? `+${tags.length - TAG_LIMIT} more` : "Show fewer"
|
|
2214
|
+
}
|
|
2215
|
+
) }) : null
|
|
2216
|
+
] }) : null
|
|
2217
|
+
] });
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
// src/interactive/ReportFreshness.tsx
|
|
2221
|
+
var import_react12 = require("react");
|
|
2222
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2049
2223
|
function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
2050
|
-
const [now, setNow] = (0,
|
|
2051
|
-
(0,
|
|
2224
|
+
const [now, setNow] = (0, import_react12.useState)(nowMs);
|
|
2225
|
+
(0, import_react12.useEffect)(() => {
|
|
2052
2226
|
if (nowMs === void 0) setNow(Date.now());
|
|
2053
2227
|
}, [nowMs]);
|
|
2054
2228
|
if (lastRunMs === void 0 || now === void 0) return null;
|
|
2055
2229
|
const age = formatRelativeAge(lastRunMs, now);
|
|
2056
|
-
const runLink = ciUrl ? /* @__PURE__ */ (0,
|
|
2230
|
+
const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2057
2231
|
"a",
|
|
2058
2232
|
{
|
|
2059
2233
|
href: ciUrl,
|
|
@@ -2064,9 +2238,9 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
2064
2238
|
}
|
|
2065
2239
|
) : null;
|
|
2066
2240
|
if (isReportStale(lastRunMs, staleAfterDays, now)) {
|
|
2067
|
-
return /* @__PURE__ */ (0,
|
|
2068
|
-
/* @__PURE__ */ (0,
|
|
2069
|
-
/* @__PURE__ */ (0,
|
|
2241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
|
|
2242
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("strong", { children: [
|
|
2070
2244
|
"Last verified ",
|
|
2071
2245
|
age,
|
|
2072
2246
|
"."
|
|
@@ -2076,10 +2250,10 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
2076
2250
|
runLink
|
|
2077
2251
|
] });
|
|
2078
2252
|
}
|
|
2079
|
-
return /* @__PURE__ */ (0,
|
|
2253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
|
|
2080
2254
|
"Verified ",
|
|
2081
2255
|
age,
|
|
2082
|
-
runLink ? /* @__PURE__ */ (0,
|
|
2256
|
+
runLink ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2083
2257
|
" \xB7 ",
|
|
2084
2258
|
runLink
|
|
2085
2259
|
] }) : null
|
|
@@ -2087,15 +2261,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
2087
2261
|
}
|
|
2088
2262
|
|
|
2089
2263
|
// src/interactive/ReportLastRunDelta.tsx
|
|
2090
|
-
var
|
|
2264
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2091
2265
|
var MAX_LINKS = 5;
|
|
2092
2266
|
function ScenarioLinks({ scenarios, tone }) {
|
|
2093
|
-
return /* @__PURE__ */ (0,
|
|
2094
|
-
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0,
|
|
2267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
|
|
2268
|
+
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
2095
2269
|
i > 0 ? ", " : "",
|
|
2096
|
-
/* @__PURE__ */ (0,
|
|
2270
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
|
|
2097
2271
|
] }, s.id)),
|
|
2098
|
-
scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0,
|
|
2272
|
+
scenarios.length > MAX_LINKS ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
2099
2273
|
" and ",
|
|
2100
2274
|
scenarios.length - MAX_LINKS,
|
|
2101
2275
|
" more"
|
|
@@ -2112,31 +2286,122 @@ function ReportLastRunDelta({
|
|
|
2112
2286
|
if (!delta) return null;
|
|
2113
2287
|
const { newlyFailing, newlyPassing, added } = delta;
|
|
2114
2288
|
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
|
|
2115
|
-
return /* @__PURE__ */ (0,
|
|
2289
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
|
|
2116
2290
|
}
|
|
2117
|
-
return /* @__PURE__ */ (0,
|
|
2118
|
-
/* @__PURE__ */ (0,
|
|
2119
|
-
newlyFailing.length > 0 ? /* @__PURE__ */ (0,
|
|
2291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
|
|
2292
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium text-foreground", children: "Since last run:" }),
|
|
2293
|
+
newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2 text-fail", children: [
|
|
2120
2294
|
newlyFailing.length,
|
|
2121
2295
|
" newly failing (",
|
|
2122
|
-
/* @__PURE__ */ (0,
|
|
2296
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
|
|
2123
2297
|
")"
|
|
2124
2298
|
] }) : null,
|
|
2125
|
-
newlyPassing.length > 0 ? /* @__PURE__ */ (0,
|
|
2299
|
+
newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2 text-pass", children: [
|
|
2126
2300
|
newlyPassing.length,
|
|
2127
2301
|
" fixed (",
|
|
2128
|
-
/* @__PURE__ */ (0,
|
|
2302
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
|
|
2129
2303
|
")"
|
|
2130
2304
|
] }) : null,
|
|
2131
|
-
added.length > 0 ? /* @__PURE__ */ (0,
|
|
2305
|
+
added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "ml-2", children: [
|
|
2132
2306
|
added.length,
|
|
2133
2307
|
" new"
|
|
2134
2308
|
] }) : null
|
|
2135
2309
|
] });
|
|
2136
2310
|
}
|
|
2137
2311
|
|
|
2138
|
-
// src/interactive/
|
|
2139
|
-
var
|
|
2312
|
+
// src/interactive/ReportSearch.tsx
|
|
2313
|
+
var import_react13 = require("react");
|
|
2314
|
+
var import_lucide_react3 = require("lucide-react");
|
|
2315
|
+
|
|
2316
|
+
// src/components/ui/input.tsx
|
|
2317
|
+
var React6 = require("react");
|
|
2318
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2319
|
+
function Input({ className, type, ...props }) {
|
|
2320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2321
|
+
"input",
|
|
2322
|
+
{
|
|
2323
|
+
type,
|
|
2324
|
+
"data-slot": "input",
|
|
2325
|
+
className: cn(
|
|
2326
|
+
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
2327
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
2328
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
2329
|
+
className
|
|
2330
|
+
),
|
|
2331
|
+
...props
|
|
2332
|
+
}
|
|
2333
|
+
);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
// src/interactive/ReportSearch.tsx
|
|
2337
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2338
|
+
var ReportSearch = (0, import_react13.forwardRef)(
|
|
2339
|
+
function ReportSearch2(props, ref) {
|
|
2340
|
+
const {
|
|
2341
|
+
value,
|
|
2342
|
+
onChange,
|
|
2343
|
+
matchedCount,
|
|
2344
|
+
totalCount,
|
|
2345
|
+
placeholder = "Search scenarios or tags\u2026",
|
|
2346
|
+
className
|
|
2347
|
+
} = props;
|
|
2348
|
+
const inputId = (0, import_react13.useId)();
|
|
2349
|
+
const showCounts = value.trim().length > 0 && typeof matchedCount === "number" && typeof totalCount === "number";
|
|
2350
|
+
function handleChange(e) {
|
|
2351
|
+
onChange(e.target.value);
|
|
2352
|
+
}
|
|
2353
|
+
function handleKeyDown(e) {
|
|
2354
|
+
if (e.key === "Escape" && value !== "") {
|
|
2355
|
+
onChange("");
|
|
2356
|
+
e.preventDefault();
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: cn("es-search flex flex-col gap-1", className), children: [
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
|
|
2361
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "relative w-full sm:w-72", children: [
|
|
2362
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2363
|
+
import_lucide_react3.Search,
|
|
2364
|
+
{
|
|
2365
|
+
"aria-hidden": true,
|
|
2366
|
+
className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
|
|
2367
|
+
}
|
|
2368
|
+
),
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2370
|
+
Input,
|
|
2371
|
+
{
|
|
2372
|
+
ref,
|
|
2373
|
+
id: inputId,
|
|
2374
|
+
type: "search",
|
|
2375
|
+
value,
|
|
2376
|
+
onChange: handleChange,
|
|
2377
|
+
onKeyDown: handleKeyDown,
|
|
2378
|
+
placeholder,
|
|
2379
|
+
autoComplete: "off",
|
|
2380
|
+
spellCheck: false,
|
|
2381
|
+
"aria-keyshortcuts": "/",
|
|
2382
|
+
className: "pl-8"
|
|
2383
|
+
}
|
|
2384
|
+
)
|
|
2385
|
+
] }),
|
|
2386
|
+
showCounts ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
2387
|
+
"span",
|
|
2388
|
+
{
|
|
2389
|
+
className: "text-xs tabular-nums text-muted-foreground",
|
|
2390
|
+
"aria-live": "polite",
|
|
2391
|
+
children: [
|
|
2392
|
+
matchedCount,
|
|
2393
|
+
" of ",
|
|
2394
|
+
totalCount,
|
|
2395
|
+
" scenarios"
|
|
2396
|
+
]
|
|
2397
|
+
}
|
|
2398
|
+
) : null
|
|
2399
|
+
] });
|
|
2400
|
+
}
|
|
2401
|
+
);
|
|
2402
|
+
|
|
2403
|
+
// src/interactive/ReportShortcutsHelp.tsx
|
|
2404
|
+
var import_react14 = require("react");
|
|
2140
2405
|
|
|
2141
2406
|
// src/components/ui/button.tsx
|
|
2142
2407
|
var React7 = require("react");
|
|
@@ -2187,327 +2452,58 @@ function Button({
|
|
|
2187
2452
|
"data-size": size,
|
|
2188
2453
|
className: cn(buttonVariants({ variant, size, className })),
|
|
2189
2454
|
...props
|
|
2190
|
-
}
|
|
2191
|
-
});
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
// src/interactive/ReportShortcutsHelp.tsx
|
|
2195
|
-
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2196
|
-
var SHORTCUTS = [
|
|
2197
|
-
{ keys: "/", description: "Focus search" },
|
|
2198
|
-
{ keys: "f", description: "Jump to next failure" },
|
|
2199
|
-
{ keys: "Shift F", description: "Jump to previous failure" },
|
|
2200
|
-
{ keys: "e", description: "Expand all" },
|
|
2201
|
-
{ keys: "c", description: "Collapse all" },
|
|
2202
|
-
{ keys: "?", description: "Toggle this help" },
|
|
2203
|
-
{ keys: "Esc", description: "Clear search / close" }
|
|
2204
|
-
];
|
|
2205
|
-
function Keys({ keys }) {
|
|
2206
|
-
const parts = keys.split(" ");
|
|
2207
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
|
|
2208
|
-
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
|
|
2209
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
|
|
2210
|
-
] }, k)) });
|
|
2211
|
-
}
|
|
2212
|
-
function ReportShortcutsHelp({ open, onClose }) {
|
|
2213
|
-
const dialogRef = (0, import_react11.useRef)(null);
|
|
2214
|
-
(0, import_react11.useEffect)(() => {
|
|
2215
|
-
const dialog = dialogRef.current;
|
|
2216
|
-
if (!dialog) return;
|
|
2217
|
-
if (open && !dialog.open) dialog.showModal();
|
|
2218
|
-
else if (!open && dialog.open) dialog.close();
|
|
2219
|
-
}, [open]);
|
|
2220
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
2221
|
-
"dialog",
|
|
2222
|
-
{
|
|
2223
|
-
ref: dialogRef,
|
|
2224
|
-
className: "es-shortcuts-help m-auto w-[min(90vw,28rem)] rounded-lg border border-border bg-card p-6 text-card-foreground shadow-lg [overscroll-behavior:contain] backdrop:bg-black/40",
|
|
2225
|
-
"aria-label": "Keyboard shortcuts",
|
|
2226
|
-
onClose,
|
|
2227
|
-
children: [
|
|
2228
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
|
|
2229
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
|
|
2230
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Keys, { keys: s.keys }) }),
|
|
2231
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
|
|
2232
|
-
] }, s.keys)) }),
|
|
2233
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
|
|
2234
|
-
]
|
|
2235
|
-
}
|
|
2236
|
-
);
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
|
-
// src/interactive/use-keyboard-shortcuts.ts
|
|
2240
|
-
var import_react12 = require("react");
|
|
2241
|
-
function isEditableTarget(target) {
|
|
2242
|
-
if (!(target instanceof HTMLElement)) return false;
|
|
2243
|
-
const tag = target.tagName;
|
|
2244
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
2245
|
-
if (target.isContentEditable) return true;
|
|
2246
|
-
return false;
|
|
2247
|
-
}
|
|
2248
|
-
function useKeyboardShortcuts(handlers) {
|
|
2249
|
-
const ref = (0, import_react12.useRef)(handlers);
|
|
2250
|
-
ref.current = handlers;
|
|
2251
|
-
(0, import_react12.useEffect)(() => {
|
|
2252
|
-
function onKeyDown(e) {
|
|
2253
|
-
const h = ref.current;
|
|
2254
|
-
if (e.key === "Escape") {
|
|
2255
|
-
h.onEscape?.();
|
|
2256
|
-
return;
|
|
2257
|
-
}
|
|
2258
|
-
if (isEditableTarget(e.target)) return;
|
|
2259
|
-
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
2260
|
-
switch (e.key) {
|
|
2261
|
-
case "/":
|
|
2262
|
-
h.onFocusSearch?.();
|
|
2263
|
-
e.preventDefault();
|
|
2264
|
-
return;
|
|
2265
|
-
case "?":
|
|
2266
|
-
h.onToggleHelp?.();
|
|
2267
|
-
e.preventDefault();
|
|
2268
|
-
return;
|
|
2269
|
-
case "f":
|
|
2270
|
-
h.onNextFailure?.();
|
|
2271
|
-
e.preventDefault();
|
|
2272
|
-
return;
|
|
2273
|
-
case "F":
|
|
2274
|
-
h.onPrevFailure?.();
|
|
2275
|
-
e.preventDefault();
|
|
2276
|
-
return;
|
|
2277
|
-
case "e":
|
|
2278
|
-
h.onExpandAll?.();
|
|
2279
|
-
e.preventDefault();
|
|
2280
|
-
return;
|
|
2281
|
-
case "c":
|
|
2282
|
-
h.onCollapseAll?.();
|
|
2283
|
-
e.preventDefault();
|
|
2284
|
-
return;
|
|
2285
|
-
default:
|
|
2286
|
-
return;
|
|
2287
|
-
}
|
|
2288
|
-
}
|
|
2289
|
-
window.addEventListener("keydown", onKeyDown);
|
|
2290
|
-
return () => window.removeEventListener("keydown", onKeyDown);
|
|
2291
|
-
}, []);
|
|
2292
|
-
}
|
|
2293
|
-
|
|
2294
|
-
// src/interactive/use-deep-link-scroll.ts
|
|
2295
|
-
var import_react13 = require("react");
|
|
2296
|
-
function useDeepLinkScroll() {
|
|
2297
|
-
const lastAnchor = (0, import_react13.useRef)(null);
|
|
2298
|
-
(0, import_react13.useEffect)(() => {
|
|
2299
|
-
function scrollToHash() {
|
|
2300
|
-
const { anchor } = splitHash(window.location.hash);
|
|
2301
|
-
if (!anchor || anchor === lastAnchor.current) return;
|
|
2302
|
-
lastAnchor.current = anchor;
|
|
2303
|
-
requestAnimationFrame(() => {
|
|
2304
|
-
scrollToScenarioId(anchor, { updateHash: false });
|
|
2305
|
-
});
|
|
2306
|
-
}
|
|
2307
|
-
scrollToHash();
|
|
2308
|
-
window.addEventListener("hashchange", scrollToHash);
|
|
2309
|
-
return () => window.removeEventListener("hashchange", scrollToHash);
|
|
2310
|
-
}, []);
|
|
2311
|
-
}
|
|
2312
|
-
|
|
2313
|
-
// src/interactive/filter.ts
|
|
2314
|
-
function normalizeQuery(query) {
|
|
2315
|
-
return query.trim().toLowerCase();
|
|
2316
|
-
}
|
|
2317
|
-
function allTags(report) {
|
|
2318
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2319
|
-
for (const feature of report.features) {
|
|
2320
|
-
for (const scenario of feature.scenarios) {
|
|
2321
|
-
for (const tag of scenario.tags) seen.add(tag);
|
|
2322
|
-
}
|
|
2323
|
-
}
|
|
2324
|
-
return [...seen];
|
|
2325
|
-
}
|
|
2326
|
-
function queryMatches(scenario, q) {
|
|
2327
|
-
if (q === "") return true;
|
|
2328
|
-
if (scenario.title.toLowerCase().includes(q)) return true;
|
|
2329
|
-
for (const tag of scenario.tags) {
|
|
2330
|
-
if (tag.toLowerCase().includes(q)) return true;
|
|
2331
|
-
}
|
|
2332
|
-
for (const step of scenario.steps) {
|
|
2333
|
-
if (step.text.toLowerCase().includes(q)) return true;
|
|
2334
|
-
}
|
|
2335
|
-
return false;
|
|
2336
|
-
}
|
|
2337
|
-
function scenarioMatches(scenario, q, status, tags) {
|
|
2338
|
-
if (status !== "all" && scenario.status !== status) return false;
|
|
2339
|
-
if (tags.length > 0 && !tags.some((t) => scenario.tags.includes(t))) return false;
|
|
2340
|
-
return queryMatches(scenario, q);
|
|
2341
|
-
}
|
|
2342
|
-
function summarizeScenarios(scenarios) {
|
|
2343
|
-
let total = 0, passed = 0, failed = 0, skipped = 0, pending = 0, durationMs = 0;
|
|
2344
|
-
for (const s of scenarios) {
|
|
2345
|
-
total += 1;
|
|
2346
|
-
durationMs += s.durationMs;
|
|
2347
|
-
if (s.status === "passed") passed += 1;
|
|
2348
|
-
else if (s.status === "failed") failed += 1;
|
|
2349
|
-
else if (s.status === "skipped") skipped += 1;
|
|
2350
|
-
else pending += 1;
|
|
2351
|
-
}
|
|
2352
|
-
return { total, passed, failed, skipped, pending, durationMs };
|
|
2353
|
-
}
|
|
2354
|
-
function filterReport(report, criteria) {
|
|
2355
|
-
const c = typeof criteria === "string" ? { query: criteria } : criteria;
|
|
2356
|
-
const q = normalizeQuery(c.query ?? "");
|
|
2357
|
-
const status = c.status ?? "all";
|
|
2358
|
-
const tags = c.tags ?? [];
|
|
2359
|
-
if (q === "" && status === "all" && tags.length === 0) return report;
|
|
2360
|
-
const features = [];
|
|
2361
|
-
let topTotal = 0, topPassed = 0, topFailed = 0, topSkipped = 0, topPending = 0, topDuration = 0;
|
|
2362
|
-
for (const feature of report.features) {
|
|
2363
|
-
const matched = feature.scenarios.filter((s) => scenarioMatches(s, q, status, tags));
|
|
2364
|
-
if (matched.length === 0) continue;
|
|
2365
|
-
const summary = summarizeScenarios(matched);
|
|
2366
|
-
features.push({ ...feature, summary, scenarios: matched });
|
|
2367
|
-
topTotal += summary.total;
|
|
2368
|
-
topPassed += summary.passed;
|
|
2369
|
-
topFailed += summary.failed;
|
|
2370
|
-
topSkipped += summary.skipped;
|
|
2371
|
-
topPending += summary.pending;
|
|
2372
|
-
topDuration += summary.durationMs;
|
|
2373
|
-
}
|
|
2374
|
-
return {
|
|
2375
|
-
...report,
|
|
2376
|
-
summary: {
|
|
2377
|
-
total: topTotal,
|
|
2378
|
-
passed: topPassed,
|
|
2379
|
-
failed: topFailed,
|
|
2380
|
-
skipped: topSkipped,
|
|
2381
|
-
pending: topPending,
|
|
2382
|
-
durationMs: topDuration
|
|
2383
|
-
},
|
|
2384
|
-
features
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
function listFailures(report) {
|
|
2388
|
-
const out = [];
|
|
2389
|
-
for (const feature of report.features) {
|
|
2390
|
-
for (const scenario of feature.scenarios) {
|
|
2391
|
-
if (scenario.status === "failed") {
|
|
2392
|
-
const ref = {
|
|
2393
|
-
featureId: feature.id,
|
|
2394
|
-
scenarioId: scenario.id,
|
|
2395
|
-
scenarioTitle: scenario.title
|
|
2396
|
-
};
|
|
2397
|
-
if (scenario.errorMessage !== void 0) {
|
|
2398
|
-
ref.errorMessage = scenario.errorMessage;
|
|
2399
|
-
}
|
|
2400
|
-
out.push(ref);
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
return out;
|
|
2405
|
-
}
|
|
2406
|
-
|
|
2407
|
-
// src/interactive/triage-default.ts
|
|
2408
|
-
function needsAttention(status) {
|
|
2409
|
-
return status === "failed";
|
|
2410
|
-
}
|
|
2411
|
-
function defaultCollapsedIds(report) {
|
|
2412
|
-
const collapsed = /* @__PURE__ */ new Set();
|
|
2413
|
-
if (report.summary.failed === 0) return collapsed;
|
|
2414
|
-
for (const feature of report.features) {
|
|
2415
|
-
const featureHasFailure = feature.scenarios.some((s) => needsAttention(s.status));
|
|
2416
|
-
if (!featureHasFailure) collapsed.add(feature.id);
|
|
2417
|
-
for (const scenario of feature.scenarios) {
|
|
2418
|
-
if (!needsAttention(scenario.status)) collapsed.add(scenario.id);
|
|
2419
|
-
}
|
|
2420
|
-
}
|
|
2421
|
-
return collapsed;
|
|
2422
|
-
}
|
|
2423
|
-
function failuresFirst(report) {
|
|
2424
|
-
if (report.summary.failed === 0) return report;
|
|
2425
|
-
const withFailures = [];
|
|
2426
|
-
const rest = [];
|
|
2427
|
-
for (const feature of report.features) {
|
|
2428
|
-
(feature.scenarios.some((s) => needsAttention(s.status)) ? withFailures : rest).push(feature);
|
|
2429
|
-
}
|
|
2430
|
-
if (withFailures.length === 0) return report;
|
|
2431
|
-
return { ...report, features: [...withFailures, ...rest] };
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2432
2457
|
}
|
|
2433
2458
|
|
|
2434
|
-
// src/interactive/
|
|
2435
|
-
var
|
|
2436
|
-
var
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
}) {
|
|
2446
|
-
const
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
type: "button",
|
|
2478
|
-
"aria-pressed": active,
|
|
2479
|
-
onClick: () => onToggleTag(tag),
|
|
2480
|
-
className: "cursor-pointer",
|
|
2481
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2482
|
-
Badge,
|
|
2483
|
-
{
|
|
2484
|
-
variant: "tag",
|
|
2485
|
-
className: cn(
|
|
2486
|
-
active && "border-primary bg-primary text-primary-foreground"
|
|
2487
|
-
),
|
|
2488
|
-
children: tag
|
|
2489
|
-
}
|
|
2490
|
-
)
|
|
2491
|
-
}
|
|
2492
|
-
) }, tag);
|
|
2493
|
-
}),
|
|
2494
|
-
tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2495
|
-
"button",
|
|
2496
|
-
{
|
|
2497
|
-
type: "button",
|
|
2498
|
-
"aria-expanded": showAllTags,
|
|
2499
|
-
onClick: () => setShowAllTags((v) => !v),
|
|
2500
|
-
className: "cursor-pointer rounded-full px-2 py-0.5 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
2501
|
-
children: tagsCollapsed ? `+${tags.length - TAG_LIMIT} more` : "Show fewer"
|
|
2502
|
-
}
|
|
2503
|
-
) }) : null
|
|
2504
|
-
] }) : null
|
|
2505
|
-
] });
|
|
2459
|
+
// src/interactive/ReportShortcutsHelp.tsx
|
|
2460
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2461
|
+
var SHORTCUTS = [
|
|
2462
|
+
{ keys: "/", description: "Focus search" },
|
|
2463
|
+
{ keys: "f", description: "Jump to next failure" },
|
|
2464
|
+
{ keys: "Shift F", description: "Jump to previous failure" },
|
|
2465
|
+
{ keys: "e", description: "Expand all" },
|
|
2466
|
+
{ keys: "c", description: "Collapse all" },
|
|
2467
|
+
{ keys: "?", description: "Toggle this help" },
|
|
2468
|
+
{ keys: "Esc", description: "Clear search / close" }
|
|
2469
|
+
];
|
|
2470
|
+
function Keys({ keys }) {
|
|
2471
|
+
const parts = keys.split(" ");
|
|
2472
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
|
|
2473
|
+
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
|
|
2474
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
|
|
2475
|
+
] }, k)) });
|
|
2476
|
+
}
|
|
2477
|
+
function ReportShortcutsHelp({ open, onClose }) {
|
|
2478
|
+
const dialogRef = (0, import_react14.useRef)(null);
|
|
2479
|
+
(0, import_react14.useEffect)(() => {
|
|
2480
|
+
const dialog = dialogRef.current;
|
|
2481
|
+
if (!dialog) return;
|
|
2482
|
+
if (open && !dialog.open) dialog.showModal();
|
|
2483
|
+
else if (!open && dialog.open) dialog.close();
|
|
2484
|
+
}, [open]);
|
|
2485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
2486
|
+
"dialog",
|
|
2487
|
+
{
|
|
2488
|
+
ref: dialogRef,
|
|
2489
|
+
className: "es-shortcuts-help m-auto w-[min(90vw,28rem)] rounded-lg border border-border bg-card p-6 text-card-foreground shadow-lg [overscroll-behavior:contain] backdrop:bg-black/40",
|
|
2490
|
+
"aria-label": "Keyboard shortcuts",
|
|
2491
|
+
onClose,
|
|
2492
|
+
children: [
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Keys, { keys: s.keys }) }),
|
|
2496
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
|
|
2497
|
+
] }, s.keys)) }),
|
|
2498
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
|
|
2499
|
+
]
|
|
2500
|
+
}
|
|
2501
|
+
);
|
|
2506
2502
|
}
|
|
2507
2503
|
|
|
2508
2504
|
// src/interactive/ReportToc.tsx
|
|
2509
2505
|
var import_react15 = require("react");
|
|
2510
|
-
var
|
|
2506
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2511
2507
|
var DOT = {
|
|
2512
2508
|
passed: "bg-pass",
|
|
2513
2509
|
failed: "bg-fail",
|
|
@@ -2536,11 +2532,11 @@ function TocContent({ onNavigate }) {
|
|
|
2536
2532
|
return () => observer.disconnect();
|
|
2537
2533
|
}, [scenarioIds]);
|
|
2538
2534
|
if (report.features.length === 0) return null;
|
|
2539
|
-
return /* @__PURE__ */ (0,
|
|
2540
|
-
/* @__PURE__ */ (0,
|
|
2541
|
-
/* @__PURE__ */ (0,
|
|
2535
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("li", { children: [
|
|
2536
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
|
|
2537
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
|
|
2542
2538
|
const active = activeId === scenario.id;
|
|
2543
|
-
return /* @__PURE__ */ (0,
|
|
2539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
2544
2540
|
"a",
|
|
2545
2541
|
{
|
|
2546
2542
|
href: `#${scenario.id}`,
|
|
@@ -2555,14 +2551,14 @@ function TocContent({ onNavigate }) {
|
|
|
2555
2551
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
2556
2552
|
),
|
|
2557
2553
|
children: [
|
|
2558
|
-
/* @__PURE__ */ (0,
|
|
2554
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2559
2555
|
"span",
|
|
2560
2556
|
{
|
|
2561
2557
|
"aria-hidden": true,
|
|
2562
2558
|
className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
|
|
2563
2559
|
}
|
|
2564
2560
|
),
|
|
2565
|
-
/* @__PURE__ */ (0,
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "break-words", children: scenario.title })
|
|
2566
2562
|
]
|
|
2567
2563
|
}
|
|
2568
2564
|
) }, scenario.id);
|
|
@@ -2572,14 +2568,14 @@ function TocContent({ onNavigate }) {
|
|
|
2572
2568
|
function ReportToc() {
|
|
2573
2569
|
const report = useReport();
|
|
2574
2570
|
if (report.features.length === 0) return null;
|
|
2575
|
-
return /* @__PURE__ */ (0,
|
|
2571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
2576
2572
|
"nav",
|
|
2577
2573
|
{
|
|
2578
2574
|
"aria-label": "Table of contents",
|
|
2579
2575
|
className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
|
|
2580
2576
|
children: [
|
|
2581
|
-
/* @__PURE__ */ (0,
|
|
2582
|
-
/* @__PURE__ */ (0,
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
|
|
2578
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(TocContent, {})
|
|
2583
2579
|
]
|
|
2584
2580
|
}
|
|
2585
2581
|
);
|
|
@@ -2589,38 +2585,139 @@ function ReportToc() {
|
|
|
2589
2585
|
var import_react16 = require("react");
|
|
2590
2586
|
var import_dialog = require("@base-ui/react/dialog");
|
|
2591
2587
|
var import_lucide_react4 = require("lucide-react");
|
|
2592
|
-
var
|
|
2588
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
2593
2589
|
function ReportTocDrawer() {
|
|
2594
2590
|
const report = useReport();
|
|
2595
2591
|
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
2596
2592
|
if (report.features.length === 0) return null;
|
|
2597
|
-
return /* @__PURE__ */ (0,
|
|
2598
|
-
/* @__PURE__ */ (0,
|
|
2599
|
-
/* @__PURE__ */ (0,
|
|
2593
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
|
|
2594
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_dialog.Dialog.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
|
|
2595
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react4.ListIcon, { "aria-hidden": true }),
|
|
2600
2596
|
" Contents"
|
|
2601
2597
|
] }),
|
|
2602
|
-
/* @__PURE__ */ (0,
|
|
2603
|
-
/* @__PURE__ */ (0,
|
|
2604
|
-
/* @__PURE__ */ (0,
|
|
2605
|
-
/* @__PURE__ */ (0,
|
|
2606
|
-
/* @__PURE__ */ (0,
|
|
2607
|
-
/* @__PURE__ */ (0,
|
|
2598
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_dialog.Dialog.Portal, { children: [
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_dialog.Dialog.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
|
|
2600
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_dialog.Dialog.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
|
|
2601
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2602
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
|
|
2603
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2608
2604
|
import_dialog.Dialog.Close,
|
|
2609
2605
|
{
|
|
2610
2606
|
"aria-label": "Close contents",
|
|
2611
2607
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
2612
|
-
children: /* @__PURE__ */ (0,
|
|
2608
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
|
|
2613
2609
|
}
|
|
2614
2610
|
)
|
|
2615
2611
|
] }),
|
|
2616
|
-
/* @__PURE__ */ (0,
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(TocContent, { onNavigate: () => setOpen(false) })
|
|
2617
2613
|
] })
|
|
2618
2614
|
] })
|
|
2619
2615
|
] });
|
|
2620
2616
|
}
|
|
2621
2617
|
|
|
2622
|
-
// src/interactive/
|
|
2618
|
+
// src/interactive/triage-default.ts
|
|
2619
|
+
function needsAttention(status) {
|
|
2620
|
+
return status === "failed";
|
|
2621
|
+
}
|
|
2622
|
+
function defaultCollapsedIds(report) {
|
|
2623
|
+
const collapsed = /* @__PURE__ */ new Set();
|
|
2624
|
+
if (report.summary.failed === 0) return collapsed;
|
|
2625
|
+
for (const feature of report.features) {
|
|
2626
|
+
const featureHasFailure = feature.scenarios.some((s) => needsAttention(s.status));
|
|
2627
|
+
if (!featureHasFailure) collapsed.add(feature.id);
|
|
2628
|
+
for (const scenario of feature.scenarios) {
|
|
2629
|
+
if (!needsAttention(scenario.status)) collapsed.add(scenario.id);
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
return collapsed;
|
|
2633
|
+
}
|
|
2634
|
+
function failuresFirst(report) {
|
|
2635
|
+
if (report.summary.failed === 0) return report;
|
|
2636
|
+
const withFailures = [];
|
|
2637
|
+
const rest = [];
|
|
2638
|
+
for (const feature of report.features) {
|
|
2639
|
+
(feature.scenarios.some((s) => needsAttention(s.status)) ? withFailures : rest).push(feature);
|
|
2640
|
+
}
|
|
2641
|
+
if (withFailures.length === 0) return report;
|
|
2642
|
+
return { ...report, features: [...withFailures, ...rest] };
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
// src/interactive/use-deep-link-scroll.ts
|
|
2623
2646
|
var import_react17 = require("react");
|
|
2647
|
+
function useDeepLinkScroll() {
|
|
2648
|
+
const lastAnchor = (0, import_react17.useRef)(null);
|
|
2649
|
+
(0, import_react17.useEffect)(() => {
|
|
2650
|
+
function scrollToHash() {
|
|
2651
|
+
const { anchor } = splitHash(window.location.hash);
|
|
2652
|
+
if (!anchor || anchor === lastAnchor.current) return;
|
|
2653
|
+
lastAnchor.current = anchor;
|
|
2654
|
+
requestAnimationFrame(() => {
|
|
2655
|
+
scrollToScenarioId(anchor, { updateHash: false });
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
scrollToHash();
|
|
2659
|
+
window.addEventListener("hashchange", scrollToHash);
|
|
2660
|
+
return () => window.removeEventListener("hashchange", scrollToHash);
|
|
2661
|
+
}, []);
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
// src/interactive/use-keyboard-shortcuts.ts
|
|
2665
|
+
var import_react18 = require("react");
|
|
2666
|
+
function isEditableTarget(target) {
|
|
2667
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
2668
|
+
const tag = target.tagName;
|
|
2669
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
2670
|
+
if (target.isContentEditable) return true;
|
|
2671
|
+
return false;
|
|
2672
|
+
}
|
|
2673
|
+
function useKeyboardShortcuts(handlers) {
|
|
2674
|
+
const ref = (0, import_react18.useRef)(handlers);
|
|
2675
|
+
ref.current = handlers;
|
|
2676
|
+
(0, import_react18.useEffect)(() => {
|
|
2677
|
+
function onKeyDown(e) {
|
|
2678
|
+
const h = ref.current;
|
|
2679
|
+
if (e.key === "Escape") {
|
|
2680
|
+
h.onEscape?.();
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
if (isEditableTarget(e.target)) return;
|
|
2684
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
2685
|
+
switch (e.key) {
|
|
2686
|
+
case "/":
|
|
2687
|
+
h.onFocusSearch?.();
|
|
2688
|
+
e.preventDefault();
|
|
2689
|
+
return;
|
|
2690
|
+
case "?":
|
|
2691
|
+
h.onToggleHelp?.();
|
|
2692
|
+
e.preventDefault();
|
|
2693
|
+
return;
|
|
2694
|
+
case "f":
|
|
2695
|
+
h.onNextFailure?.();
|
|
2696
|
+
e.preventDefault();
|
|
2697
|
+
return;
|
|
2698
|
+
case "F":
|
|
2699
|
+
h.onPrevFailure?.();
|
|
2700
|
+
e.preventDefault();
|
|
2701
|
+
return;
|
|
2702
|
+
case "e":
|
|
2703
|
+
h.onExpandAll?.();
|
|
2704
|
+
e.preventDefault();
|
|
2705
|
+
return;
|
|
2706
|
+
case "c":
|
|
2707
|
+
h.onCollapseAll?.();
|
|
2708
|
+
e.preventDefault();
|
|
2709
|
+
return;
|
|
2710
|
+
default:
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
window.addEventListener("keydown", onKeyDown);
|
|
2715
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
2716
|
+
}, []);
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// src/interactive/use-theme.ts
|
|
2720
|
+
var import_react19 = require("react");
|
|
2624
2721
|
var KEY = "es-theme";
|
|
2625
2722
|
function systemDark() {
|
|
2626
2723
|
return typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches;
|
|
@@ -2641,13 +2738,13 @@ function initialPref() {
|
|
|
2641
2738
|
return "system";
|
|
2642
2739
|
}
|
|
2643
2740
|
function useTheme() {
|
|
2644
|
-
const [pref, setPrefState] = (0,
|
|
2645
|
-
(0,
|
|
2741
|
+
const [pref, setPrefState] = (0, import_react19.useState)("system");
|
|
2742
|
+
(0, import_react19.useEffect)(() => {
|
|
2646
2743
|
const p = initialPref();
|
|
2647
2744
|
setPrefState(p);
|
|
2648
2745
|
applyTheme(p);
|
|
2649
2746
|
}, []);
|
|
2650
|
-
(0,
|
|
2747
|
+
(0, import_react19.useEffect)(() => {
|
|
2651
2748
|
if (typeof matchMedia === "undefined") return;
|
|
2652
2749
|
const mq = matchMedia("(prefers-color-scheme: dark)");
|
|
2653
2750
|
const onChange = () => {
|
|
@@ -2656,7 +2753,7 @@ function useTheme() {
|
|
|
2656
2753
|
mq.addEventListener("change", onChange);
|
|
2657
2754
|
return () => mq.removeEventListener("change", onChange);
|
|
2658
2755
|
}, [pref]);
|
|
2659
|
-
const setPref = (0,
|
|
2756
|
+
const setPref = (0, import_react19.useCallback)((p) => {
|
|
2660
2757
|
setPrefState(p);
|
|
2661
2758
|
applyTheme(p);
|
|
2662
2759
|
try {
|
|
@@ -2668,19 +2765,19 @@ function useTheme() {
|
|
|
2668
2765
|
}
|
|
2669
2766
|
|
|
2670
2767
|
// src/interactive/use-url-state.ts
|
|
2671
|
-
var
|
|
2768
|
+
var import_react20 = require("react");
|
|
2672
2769
|
var WRITE_DELAY_MS = 250;
|
|
2673
2770
|
function useUrlState() {
|
|
2674
|
-
const [state, setState] = (0,
|
|
2675
|
-
const ready = (0,
|
|
2676
|
-
(0,
|
|
2771
|
+
const [state, setState] = (0, import_react20.useState)(DEFAULT_URL_STATE);
|
|
2772
|
+
const ready = (0, import_react20.useRef)(false);
|
|
2773
|
+
(0, import_react20.useEffect)(() => {
|
|
2677
2774
|
const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
|
|
2678
2775
|
read();
|
|
2679
2776
|
ready.current = true;
|
|
2680
2777
|
window.addEventListener("hashchange", read);
|
|
2681
2778
|
return () => window.removeEventListener("hashchange", read);
|
|
2682
2779
|
}, []);
|
|
2683
|
-
(0,
|
|
2780
|
+
(0, import_react20.useEffect)(() => {
|
|
2684
2781
|
if (!ready.current) return;
|
|
2685
2782
|
const timer = setTimeout(
|
|
2686
2783
|
() => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
|
|
@@ -2688,7 +2785,7 @@ function useUrlState() {
|
|
|
2688
2785
|
);
|
|
2689
2786
|
return () => clearTimeout(timer);
|
|
2690
2787
|
}, [state]);
|
|
2691
|
-
const update = (0,
|
|
2788
|
+
const update = (0, import_react20.useCallback)(
|
|
2692
2789
|
(patch) => setState((prev) => ({ ...prev, ...patch })),
|
|
2693
2790
|
[]
|
|
2694
2791
|
);
|
|
@@ -2696,22 +2793,33 @@ function useUrlState() {
|
|
|
2696
2793
|
}
|
|
2697
2794
|
|
|
2698
2795
|
// src/interactive/ReportInteractive.tsx
|
|
2699
|
-
var
|
|
2796
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2700
2797
|
function ReportInteractive(props) {
|
|
2701
2798
|
const { report, className, title, dataTheme } = props;
|
|
2702
2799
|
const result = unwrapReport(report);
|
|
2703
2800
|
if (!result.ok) {
|
|
2704
|
-
return /* @__PURE__ */ (0,
|
|
2801
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2802
|
+
ReportErrorShell,
|
|
2803
|
+
{
|
|
2804
|
+
error: result.error,
|
|
2805
|
+
className,
|
|
2806
|
+
title,
|
|
2807
|
+
dataTheme
|
|
2808
|
+
}
|
|
2809
|
+
);
|
|
2705
2810
|
}
|
|
2706
|
-
return /* @__PURE__ */ (0,
|
|
2811
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportInteractiveView, { ...props, report: result.data });
|
|
2707
2812
|
}
|
|
2708
2813
|
var THEME_OPTIONS = [
|
|
2709
2814
|
{ value: "light", label: "Light", Icon: import_lucide_react5.Sun },
|
|
2710
2815
|
{ value: "system", label: "System", Icon: import_lucide_react5.Monitor },
|
|
2711
2816
|
{ value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
|
|
2712
2817
|
];
|
|
2713
|
-
function ThemeSegment({
|
|
2714
|
-
|
|
2818
|
+
function ThemeSegment({
|
|
2819
|
+
pref,
|
|
2820
|
+
onPref
|
|
2821
|
+
}) {
|
|
2822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2715
2823
|
ToggleGroup,
|
|
2716
2824
|
{
|
|
2717
2825
|
size: "sm",
|
|
@@ -2721,7 +2829,17 @@ function ThemeSegment({ pref, onPref }) {
|
|
|
2721
2829
|
if (next) onPref(next);
|
|
2722
2830
|
},
|
|
2723
2831
|
"aria-label": "Color theme",
|
|
2724
|
-
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0,
|
|
2832
|
+
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2833
|
+
ToggleGroupItem,
|
|
2834
|
+
{
|
|
2835
|
+
value,
|
|
2836
|
+
"aria-label": label,
|
|
2837
|
+
title: label,
|
|
2838
|
+
className: "px-2",
|
|
2839
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Icon, {})
|
|
2840
|
+
},
|
|
2841
|
+
value
|
|
2842
|
+
))
|
|
2725
2843
|
}
|
|
2726
2844
|
);
|
|
2727
2845
|
}
|
|
@@ -2739,63 +2857,79 @@ function ReportInteractiveView({
|
|
|
2739
2857
|
}) {
|
|
2740
2858
|
const [urlState, setUrlState] = useUrlState();
|
|
2741
2859
|
const { query, status: statusFilter, tags: activeTags, detail } = urlState;
|
|
2742
|
-
const setQuery = (0,
|
|
2743
|
-
|
|
2860
|
+
const setQuery = (0, import_react21.useCallback)(
|
|
2861
|
+
(next) => setUrlState({ query: next }),
|
|
2862
|
+
[setUrlState]
|
|
2863
|
+
);
|
|
2864
|
+
const setStatusFilter = (0, import_react21.useCallback)(
|
|
2744
2865
|
(next) => setUrlState({ status: next }),
|
|
2745
2866
|
[setUrlState]
|
|
2746
2867
|
);
|
|
2747
|
-
const setDetail = (0,
|
|
2868
|
+
const setDetail = (0, import_react21.useCallback)(
|
|
2748
2869
|
(next) => setUrlState({ detail: next }),
|
|
2749
2870
|
[setUrlState]
|
|
2750
2871
|
);
|
|
2751
|
-
const [helpOpen, setHelpOpen] = (0,
|
|
2872
|
+
const [helpOpen, setHelpOpen] = (0, import_react21.useState)(false);
|
|
2752
2873
|
const { pref: themePref, setPref: setThemePref } = useTheme();
|
|
2753
|
-
const searchRef = (0,
|
|
2754
|
-
const expandId = (0,
|
|
2755
|
-
const docsId = (0,
|
|
2756
|
-
const deferredQuery = (0,
|
|
2757
|
-
const failures = (0,
|
|
2758
|
-
const filtered = (0,
|
|
2874
|
+
const searchRef = (0, import_react21.useRef)(null);
|
|
2875
|
+
const expandId = (0, import_react21.useId)();
|
|
2876
|
+
const docsId = (0, import_react21.useId)();
|
|
2877
|
+
const deferredQuery = (0, import_react21.useDeferredValue)(query);
|
|
2878
|
+
const failures = (0, import_react21.useMemo)(() => listFailures(report), [report]);
|
|
2879
|
+
const filtered = (0, import_react21.useMemo)(
|
|
2759
2880
|
// Features carrying failures float to the top so the first thing on screen
|
|
2760
2881
|
// is the thing that needs attention; an all-green run keeps source order.
|
|
2761
|
-
() => failuresFirst(
|
|
2882
|
+
() => failuresFirst(
|
|
2883
|
+
filterReport(report, {
|
|
2884
|
+
query: deferredQuery,
|
|
2885
|
+
status: statusFilter,
|
|
2886
|
+
tags: activeTags
|
|
2887
|
+
})
|
|
2888
|
+
),
|
|
2762
2889
|
[report, deferredQuery, statusFilter, activeTags]
|
|
2763
2890
|
);
|
|
2764
2891
|
const isFiltering = query !== deferredQuery;
|
|
2765
|
-
const statusOptions = (0,
|
|
2892
|
+
const statusOptions = (0, import_react21.useMemo)(() => {
|
|
2766
2893
|
const s = report.summary;
|
|
2767
2894
|
const opts = [
|
|
2768
2895
|
{ key: "all", label: "All", count: s.total }
|
|
2769
2896
|
];
|
|
2770
|
-
if (s.passed)
|
|
2771
|
-
|
|
2772
|
-
if (s.
|
|
2773
|
-
|
|
2897
|
+
if (s.passed)
|
|
2898
|
+
opts.push({ key: "passed", label: "Passed", count: s.passed });
|
|
2899
|
+
if (s.failed)
|
|
2900
|
+
opts.push({ key: "failed", label: "Failed", count: s.failed });
|
|
2901
|
+
if (s.skipped)
|
|
2902
|
+
opts.push({ key: "skipped", label: "Skipped", count: s.skipped });
|
|
2903
|
+
if (s.pending)
|
|
2904
|
+
opts.push({ key: "pending", label: "Pending", count: s.pending });
|
|
2774
2905
|
return opts;
|
|
2775
2906
|
}, [report]);
|
|
2776
|
-
const tagOptions = (0,
|
|
2777
|
-
const toggleTag = (0,
|
|
2907
|
+
const tagOptions = (0, import_react21.useMemo)(() => allTags(report), [report]);
|
|
2908
|
+
const toggleTag = (0, import_react21.useCallback)(
|
|
2778
2909
|
(tag) => setUrlState({
|
|
2779
2910
|
tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
|
|
2780
2911
|
}),
|
|
2781
2912
|
[activeTags, setUrlState]
|
|
2782
2913
|
);
|
|
2783
|
-
const [toast, setToast] = (0,
|
|
2784
|
-
const toastTimer = (0,
|
|
2785
|
-
const showToast = (0,
|
|
2914
|
+
const [toast, setToast] = (0, import_react21.useState)(null);
|
|
2915
|
+
const toastTimer = (0, import_react21.useRef)(null);
|
|
2916
|
+
const showToast = (0, import_react21.useCallback)((msg) => {
|
|
2786
2917
|
setToast(msg);
|
|
2787
2918
|
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
2788
2919
|
toastTimer.current = setTimeout(() => setToast(null), 1800);
|
|
2789
2920
|
}, []);
|
|
2790
|
-
const copy = (0,
|
|
2921
|
+
const copy = (0, import_react21.useCallback)(
|
|
2791
2922
|
(text, msg) => {
|
|
2792
2923
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
2793
|
-
navigator.clipboard.writeText(text).then(
|
|
2924
|
+
navigator.clipboard.writeText(text).then(
|
|
2925
|
+
() => showToast(msg),
|
|
2926
|
+
() => showToast("Copy failed")
|
|
2927
|
+
);
|
|
2794
2928
|
}
|
|
2795
2929
|
},
|
|
2796
2930
|
[showToast]
|
|
2797
2931
|
);
|
|
2798
|
-
const scenarioActions = (0,
|
|
2932
|
+
const scenarioActions = (0, import_react21.useMemo)(
|
|
2799
2933
|
() => ({
|
|
2800
2934
|
copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
|
|
2801
2935
|
copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
|
|
@@ -2803,15 +2937,27 @@ function ReportInteractiveView({
|
|
|
2803
2937
|
}),
|
|
2804
2938
|
[copy]
|
|
2805
2939
|
);
|
|
2806
|
-
const failureIndexRef = (0,
|
|
2807
|
-
const allCollapsibleIds = (0,
|
|
2940
|
+
const failureIndexRef = (0, import_react21.useRef)(0);
|
|
2941
|
+
const allCollapsibleIds = (0, import_react21.useMemo)(
|
|
2808
2942
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
2809
2943
|
[report]
|
|
2810
2944
|
);
|
|
2811
|
-
const collapse = useCollapseState(
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
const
|
|
2945
|
+
const collapse = useCollapseState(
|
|
2946
|
+
(0, import_react21.useCallback)(() => defaultCollapsedIds(report), [report])
|
|
2947
|
+
);
|
|
2948
|
+
const revealFilteredResults = deferredQuery.trim() !== "" || statusFilter !== "all" || activeTags.length > 0;
|
|
2949
|
+
const visibleCollapse = (0, import_react21.useMemo)(
|
|
2950
|
+
() => revealFilteredResults ? { ...collapse.api, isCollapsed: () => false } : collapse.api,
|
|
2951
|
+
[collapse.api, revealFilteredResults]
|
|
2952
|
+
);
|
|
2953
|
+
const collapseAll = (0, import_react21.useCallback)(
|
|
2954
|
+
() => collapse.collapseAll(allCollapsibleIds),
|
|
2955
|
+
[collapse, allCollapsibleIds]
|
|
2956
|
+
);
|
|
2957
|
+
const [expandedAll, setExpandedAll] = (0, import_react21.useState)(
|
|
2958
|
+
() => report.summary.failed === 0
|
|
2959
|
+
);
|
|
2960
|
+
const setExpanded = (0, import_react21.useCallback)(
|
|
2815
2961
|
(expanded) => {
|
|
2816
2962
|
if (expanded) collapse.expandAll();
|
|
2817
2963
|
else collapseAll();
|
|
@@ -2819,10 +2965,10 @@ function ReportInteractiveView({
|
|
|
2819
2965
|
},
|
|
2820
2966
|
[collapse, collapseAll]
|
|
2821
2967
|
);
|
|
2822
|
-
const focusSearch = (0,
|
|
2968
|
+
const focusSearch = (0, import_react21.useCallback)(() => {
|
|
2823
2969
|
searchRef.current?.focus();
|
|
2824
2970
|
}, []);
|
|
2825
|
-
const stepFailure = (0,
|
|
2971
|
+
const stepFailure = (0, import_react21.useCallback)(
|
|
2826
2972
|
(direction) => {
|
|
2827
2973
|
if (failures.length === 0) return;
|
|
2828
2974
|
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
@@ -2831,13 +2977,13 @@ function ReportInteractiveView({
|
|
|
2831
2977
|
},
|
|
2832
2978
|
[failures]
|
|
2833
2979
|
);
|
|
2834
|
-
const toggleHelp = (0,
|
|
2980
|
+
const toggleHelp = (0, import_react21.useCallback)(() => {
|
|
2835
2981
|
setHelpOpen((v) => !v);
|
|
2836
2982
|
}, []);
|
|
2837
|
-
const escape = (0,
|
|
2983
|
+
const escape = (0, import_react21.useCallback)(() => {
|
|
2838
2984
|
if (helpOpen) setHelpOpen(false);
|
|
2839
2985
|
else if (query !== "") setQuery("");
|
|
2840
|
-
}, [helpOpen, query]);
|
|
2986
|
+
}, [helpOpen, query, setQuery]);
|
|
2841
2987
|
useKeyboardShortcuts({
|
|
2842
2988
|
onFocusSearch: focusSearch,
|
|
2843
2989
|
onNextFailure: () => stepFailure(1),
|
|
@@ -2852,13 +2998,13 @@ function ReportInteractiveView({
|
|
|
2852
2998
|
const totalScenarios = report.summary.total;
|
|
2853
2999
|
const matchedScenarios = filtered.summary.total;
|
|
2854
3000
|
const showToc = !hideToc && totalScenarios > 3;
|
|
2855
|
-
return /* @__PURE__ */ (0,
|
|
3001
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(CollapseProvider, { value: visibleCollapse, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ScenarioFreshnessProvider, { value: { staleAfterDays, report }, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2856
3002
|
ReportRoot,
|
|
2857
3003
|
{
|
|
2858
3004
|
report: filtered,
|
|
2859
3005
|
customRenderers,
|
|
2860
3006
|
renderers,
|
|
2861
|
-
children: /* @__PURE__ */ (0,
|
|
3007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
2862
3008
|
"main",
|
|
2863
3009
|
{
|
|
2864
3010
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -2867,8 +3013,8 @@ function ReportInteractiveView({
|
|
|
2867
3013
|
"data-theme": dataTheme,
|
|
2868
3014
|
"data-detail-level": detail,
|
|
2869
3015
|
children: [
|
|
2870
|
-
/* @__PURE__ */ (0,
|
|
2871
|
-
/* @__PURE__ */ (0,
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("header", { className: "es-report-header", children: [
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
2872
3018
|
"div",
|
|
2873
3019
|
{
|
|
2874
3020
|
"data-es-navbar": true,
|
|
@@ -2877,9 +3023,9 @@ function ReportInteractiveView({
|
|
|
2877
3023
|
hideHeader ? "justify-end" : "justify-between"
|
|
2878
3024
|
),
|
|
2879
3025
|
children: [
|
|
2880
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
2881
|
-
/* @__PURE__ */ (0,
|
|
2882
|
-
/* @__PURE__ */ (0,
|
|
3026
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
|
|
3027
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3028
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2883
3029
|
ReportSearch,
|
|
2884
3030
|
{
|
|
2885
3031
|
ref: searchRef,
|
|
@@ -2889,16 +3035,16 @@ function ReportInteractiveView({
|
|
|
2889
3035
|
totalCount: totalScenarios
|
|
2890
3036
|
}
|
|
2891
3037
|
),
|
|
2892
|
-
/* @__PURE__ */ (0,
|
|
3038
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
|
|
2893
3039
|
] })
|
|
2894
3040
|
]
|
|
2895
3041
|
}
|
|
2896
3042
|
),
|
|
2897
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
2898
|
-
/* @__PURE__ */ (0,
|
|
2899
|
-
/* @__PURE__ */ (0,
|
|
3043
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
|
|
3044
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportSummary, {}),
|
|
3045
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportMeta, {})
|
|
2900
3046
|
] }),
|
|
2901
|
-
/* @__PURE__ */ (0,
|
|
3047
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2902
3048
|
ReportFreshness,
|
|
2903
3049
|
{
|
|
2904
3050
|
lastRunMs: reportLastRunMs(report),
|
|
@@ -2906,8 +3052,14 @@ function ReportInteractiveView({
|
|
|
2906
3052
|
staleAfterDays
|
|
2907
3053
|
}
|
|
2908
3054
|
),
|
|
2909
|
-
/* @__PURE__ */ (0,
|
|
2910
|
-
|
|
3055
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3056
|
+
ReportLastRunDelta,
|
|
3057
|
+
{
|
|
3058
|
+
history: scenarioHistory,
|
|
3059
|
+
report
|
|
3060
|
+
}
|
|
3061
|
+
),
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2911
3063
|
ReportFilters,
|
|
2912
3064
|
{
|
|
2913
3065
|
statuses: statusOptions,
|
|
@@ -2918,14 +3070,28 @@ function ReportInteractiveView({
|
|
|
2918
3070
|
onToggleTag: toggleTag
|
|
2919
3071
|
}
|
|
2920
3072
|
),
|
|
2921
|
-
/* @__PURE__ */ (0,
|
|
2922
|
-
showToc ? /* @__PURE__ */ (0,
|
|
2923
|
-
/* @__PURE__ */ (0,
|
|
2924
|
-
/* @__PURE__ */ (0,
|
|
2925
|
-
|
|
3073
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
|
|
3074
|
+
showToc ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportTocDrawer, {}) : null,
|
|
3075
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3076
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3077
|
+
Switch,
|
|
3078
|
+
{
|
|
3079
|
+
id: expandId,
|
|
3080
|
+
checked: expandedAll,
|
|
3081
|
+
onCheckedChange: setExpanded
|
|
3082
|
+
}
|
|
3083
|
+
),
|
|
3084
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3085
|
+
"label",
|
|
3086
|
+
{
|
|
3087
|
+
htmlFor: expandId,
|
|
3088
|
+
className: "cursor-pointer text-sm text-muted-foreground select-none",
|
|
3089
|
+
children: "Expand all"
|
|
3090
|
+
}
|
|
3091
|
+
)
|
|
2926
3092
|
] }),
|
|
2927
|
-
/* @__PURE__ */ (0,
|
|
2928
|
-
/* @__PURE__ */ (0,
|
|
3093
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3094
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2929
3095
|
Switch,
|
|
2930
3096
|
{
|
|
2931
3097
|
id: docsId,
|
|
@@ -2933,16 +3099,28 @@ function ReportInteractiveView({
|
|
|
2933
3099
|
onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
|
|
2934
3100
|
}
|
|
2935
3101
|
),
|
|
2936
|
-
/* @__PURE__ */ (0,
|
|
3102
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3103
|
+
"label",
|
|
3104
|
+
{
|
|
3105
|
+
htmlFor: docsId,
|
|
3106
|
+
className: "cursor-pointer text-sm text-muted-foreground select-none",
|
|
3107
|
+
children: "Show documentation"
|
|
3108
|
+
}
|
|
3109
|
+
)
|
|
2937
3110
|
] })
|
|
2938
3111
|
] })
|
|
2939
3112
|
] }),
|
|
2940
|
-
/* @__PURE__ */ (0,
|
|
2941
|
-
hasContent ? showToc ? /* @__PURE__ */ (0,
|
|
2942
|
-
/* @__PURE__ */ (0,
|
|
2943
|
-
/* @__PURE__ */ (0,
|
|
2944
|
-
] }) : /* @__PURE__ */ (0,
|
|
2945
|
-
|
|
3113
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportFailureBanner, { failures }),
|
|
3114
|
+
hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex gap-6", children: [
|
|
3115
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportToc, {}),
|
|
3116
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportFeatureList, {}) })
|
|
3117
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3118
|
+
ReportEmpty,
|
|
3119
|
+
{
|
|
3120
|
+
message: query ? "No scenarios match the search." : void 0
|
|
3121
|
+
}
|
|
3122
|
+
),
|
|
3123
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2946
3124
|
"button",
|
|
2947
3125
|
{
|
|
2948
3126
|
type: "button",
|
|
@@ -2953,8 +3131,14 @@ function ReportInteractiveView({
|
|
|
2953
3131
|
children: "?"
|
|
2954
3132
|
}
|
|
2955
3133
|
),
|
|
2956
|
-
/* @__PURE__ */ (0,
|
|
2957
|
-
|
|
3134
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3135
|
+
ReportShortcutsHelp,
|
|
3136
|
+
{
|
|
3137
|
+
open: helpOpen,
|
|
3138
|
+
onClose: () => setHelpOpen(false)
|
|
3139
|
+
}
|
|
3140
|
+
),
|
|
3141
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2958
3142
|
"div",
|
|
2959
3143
|
{
|
|
2960
3144
|
role: "status",
|
|
@@ -2967,18 +3151,18 @@ function ReportInteractiveView({
|
|
|
2967
3151
|
}
|
|
2968
3152
|
)
|
|
2969
3153
|
}
|
|
2970
|
-
) }) }) });
|
|
3154
|
+
) }) }) }) });
|
|
2971
3155
|
}
|
|
2972
3156
|
|
|
2973
3157
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
2974
|
-
var
|
|
3158
|
+
var import_react24 = require("react");
|
|
2975
3159
|
|
|
2976
3160
|
// src/interactive/island-renderers.tsx
|
|
2977
|
-
var
|
|
3161
|
+
var import_react23 = require("react");
|
|
2978
3162
|
|
|
2979
3163
|
// src/components/doc/MermaidView.tsx
|
|
2980
|
-
var
|
|
2981
|
-
var
|
|
3164
|
+
var import_react22 = require("react");
|
|
3165
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
2982
3166
|
function prefersDark(el) {
|
|
2983
3167
|
const themed = el?.closest("[data-theme]");
|
|
2984
3168
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -2986,10 +3170,10 @@ function prefersDark(el) {
|
|
|
2986
3170
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
2987
3171
|
}
|
|
2988
3172
|
function MermaidView({ entry, load }) {
|
|
2989
|
-
const ref = (0,
|
|
2990
|
-
const [svg, setSvg] = (0,
|
|
2991
|
-
const [error, setError] = (0,
|
|
2992
|
-
(0,
|
|
3173
|
+
const ref = (0, import_react22.useRef)(null);
|
|
3174
|
+
const [svg, setSvg] = (0, import_react22.useState)(null);
|
|
3175
|
+
const [error, setError] = (0, import_react22.useState)(null);
|
|
3176
|
+
(0, import_react22.useEffect)(() => {
|
|
2993
3177
|
let cancelled = false;
|
|
2994
3178
|
const draw = async () => {
|
|
2995
3179
|
let mermaid;
|
|
@@ -3032,9 +3216,9 @@ function MermaidView({ entry, load }) {
|
|
|
3032
3216
|
};
|
|
3033
3217
|
}, [entry.code, load]);
|
|
3034
3218
|
if (svg) {
|
|
3035
|
-
return /* @__PURE__ */ (0,
|
|
3036
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
3037
|
-
/* @__PURE__ */ (0,
|
|
3219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("figure", { ref, className: "my-3", children: [
|
|
3220
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
3221
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3038
3222
|
"div",
|
|
3039
3223
|
{
|
|
3040
3224
|
role: "img",
|
|
@@ -3045,8 +3229,8 @@ function MermaidView({ entry, load }) {
|
|
|
3045
3229
|
)
|
|
3046
3230
|
] });
|
|
3047
3231
|
}
|
|
3048
|
-
return /* @__PURE__ */ (0,
|
|
3049
|
-
error ? /* @__PURE__ */ (0,
|
|
3232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
|
|
3233
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
|
|
3050
3234
|
"p",
|
|
3051
3235
|
{
|
|
3052
3236
|
role: "alert",
|
|
@@ -3058,12 +3242,12 @@ function MermaidView({ entry, load }) {
|
|
|
3058
3242
|
]
|
|
3059
3243
|
}
|
|
3060
3244
|
) : null,
|
|
3061
|
-
/* @__PURE__ */ (0,
|
|
3245
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MermaidSource, { entry, ref })
|
|
3062
3246
|
] });
|
|
3063
3247
|
}
|
|
3064
3248
|
|
|
3065
3249
|
// src/interactive/island-renderers.tsx
|
|
3066
|
-
var
|
|
3250
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
3067
3251
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
3068
3252
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
3069
3253
|
function cachedCdnModule(url) {
|
|
@@ -3080,8 +3264,8 @@ function cachedCdnModule(url) {
|
|
|
3080
3264
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
3081
3265
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
3082
3266
|
function CdnHighlightedCode({ entry }) {
|
|
3083
|
-
const [html, setHtml] = (0,
|
|
3084
|
-
(0,
|
|
3267
|
+
const [html, setHtml] = (0, import_react23.useState)(null);
|
|
3268
|
+
(0, import_react23.useEffect)(() => {
|
|
3085
3269
|
let cancelled = false;
|
|
3086
3270
|
void (async () => {
|
|
3087
3271
|
try {
|
|
@@ -3097,27 +3281,27 @@ function CdnHighlightedCode({ entry }) {
|
|
|
3097
3281
|
cancelled = true;
|
|
3098
3282
|
};
|
|
3099
3283
|
}, [entry.content, entry.lang]);
|
|
3100
|
-
return /* @__PURE__ */ (0,
|
|
3284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
3101
3285
|
"code",
|
|
3102
3286
|
{
|
|
3103
3287
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
3104
3288
|
dangerouslySetInnerHTML: { __html: html }
|
|
3105
3289
|
}
|
|
3106
|
-
) : /* @__PURE__ */ (0,
|
|
3290
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
3107
3291
|
}
|
|
3108
3292
|
function buildIslandRenderers(opts) {
|
|
3109
3293
|
const renderers = {};
|
|
3110
3294
|
if (opts.mermaid) {
|
|
3111
|
-
renderers.mermaid = (entry) => (0,
|
|
3295
|
+
renderers.mermaid = (entry) => (0, import_react23.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
|
|
3112
3296
|
}
|
|
3113
3297
|
if (opts.syntaxHighlighting) {
|
|
3114
|
-
renderers.code = (entry) => (0,
|
|
3298
|
+
renderers.code = (entry) => (0, import_react23.createElement)(CdnHighlightedCode, { entry });
|
|
3115
3299
|
}
|
|
3116
3300
|
return renderers;
|
|
3117
3301
|
}
|
|
3118
3302
|
|
|
3119
3303
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
3120
|
-
var
|
|
3304
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
3121
3305
|
function ReportInteractiveIsland({
|
|
3122
3306
|
report,
|
|
3123
3307
|
title,
|
|
@@ -3127,11 +3311,11 @@ function ReportInteractiveIsland({
|
|
|
3127
3311
|
syntaxHighlighting = true,
|
|
3128
3312
|
mermaid = true
|
|
3129
3313
|
}) {
|
|
3130
|
-
const renderers = (0,
|
|
3314
|
+
const renderers = (0, import_react24.useMemo)(
|
|
3131
3315
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
3132
3316
|
[syntaxHighlighting, mermaid]
|
|
3133
3317
|
);
|
|
3134
|
-
return /* @__PURE__ */ (0,
|
|
3318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3135
3319
|
ReportInteractive,
|
|
3136
3320
|
{
|
|
3137
3321
|
report,
|