executable-stories-react 0.11.0 → 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 +414 -464
- 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 +313 -361
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +1440 -1298
- 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 +1394 -1249
- package/dist/interactive.js.map +1 -1
- package/dist/parse.cjs +34 -147
- package/dist/parse.cjs.map +1 -1
- package/dist/parse.js +34 -147
- package/dist/parse.js.map +1 -1
- package/dist/report-island.global.js +29 -29
- package/dist/ssr.cjs +330 -259
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.js +249 -175
- 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,342 +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
|
-
});
|
|
203
|
-
}
|
|
204
|
-
function TreeBranch({ node, depth }) {
|
|
205
|
-
const children = sortedChildren(node);
|
|
206
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: cn("space-y-0.5", depth > 0 && "ml-3 border-l border-border/60 pl-3"), children: children.map((child) => {
|
|
207
|
-
const isDir = child.children.size > 0;
|
|
208
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { children: [
|
|
209
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-baseline gap-2", children: [
|
|
210
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
211
|
-
"span",
|
|
212
|
-
{
|
|
213
|
-
className: cn(
|
|
214
|
-
"font-mono text-xs",
|
|
215
|
-
isDir ? "text-muted-foreground" : "text-foreground"
|
|
216
|
-
),
|
|
217
|
-
children: isDir ? `${child.name}/` : child.name
|
|
218
|
-
}
|
|
219
|
-
),
|
|
220
|
-
child.file?.change ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeBadge, { change: child.file.change }) : null,
|
|
221
|
-
child.file?.note ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-muted-foreground", children: child.file.note }) : null
|
|
222
|
-
] }),
|
|
223
|
-
isDir ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeBranch, { node: child, depth: depth + 1 }) : null
|
|
224
|
-
] }, child.name);
|
|
225
|
-
}) });
|
|
226
|
-
}
|
|
227
|
-
function FileTreeBlock({ entry }) {
|
|
228
|
-
const data = parseFileTree(entry.data);
|
|
229
|
-
if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnreadableBlock, { entry });
|
|
230
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockFrame, { title: data.title ?? "Files changed", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border bg-card p-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TreeBranch, { node: buildTree(data.files), depth: 0 }) }) });
|
|
231
|
-
}
|
|
232
|
-
function parseDataModel(data) {
|
|
233
|
-
if (!isRecord(data) || !Array.isArray(data.fields)) return void 0;
|
|
234
|
-
const fields = [];
|
|
235
|
-
for (const raw of data.fields) {
|
|
236
|
-
if (!isRecord(raw)) continue;
|
|
237
|
-
const name = str(raw.name);
|
|
238
|
-
if (!name) continue;
|
|
239
|
-
fields.push({
|
|
240
|
-
name,
|
|
241
|
-
type: str(raw.type),
|
|
242
|
-
note: str(raw.note),
|
|
243
|
-
change: isChangeKind(raw.change) ? raw.change : void 0
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
if (fields.length === 0) return void 0;
|
|
247
|
-
return {
|
|
248
|
-
title: str(data.title),
|
|
249
|
-
authored: str(data.authored),
|
|
250
|
-
name: str(data.name),
|
|
251
|
-
fields
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
function DataModelBlock({ entry }) {
|
|
255
|
-
const data = parseDataModel(entry.data);
|
|
256
|
-
if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UnreadableBlock, { entry });
|
|
257
|
-
const hasNotes = data.fields.some((f) => f.note);
|
|
258
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockFrame, { title: data.title ?? data.name ?? "Data model", authored: data.authored, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { tabIndex: 0, className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "w-full border-collapse text-sm", children: [
|
|
259
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { className: "bg-muted/50", children: [
|
|
260
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Field" }),
|
|
261
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Type" }),
|
|
262
|
-
hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { scope: "col", className: "border-b border-border px-3 py-2 text-left font-semibold", children: "Note" }) : null
|
|
263
|
-
] }) }),
|
|
264
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: data.fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
265
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex flex-wrap items-center gap-2", children: [
|
|
266
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-mono text-xs text-foreground", children: field.name }),
|
|
267
|
-
field.change ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeBadge, { change: field.change }) : null
|
|
268
|
-
] }) }),
|
|
269
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2 font-mono text-xs text-muted-foreground", children: field.type ?? "" }),
|
|
270
|
-
hasNotes ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "border-b border-border px-3 py-2 text-foreground", children: field.note ?? "" }) : null
|
|
271
|
-
] }, field.name)) })
|
|
272
|
-
] }) }) });
|
|
273
|
-
}
|
|
274
|
-
function UnreadableBlock({ entry }) {
|
|
275
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "my-2", "data-type": entry.type, children: [
|
|
276
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: [
|
|
277
|
-
entry.type,
|
|
278
|
-
" (unrecognised shape)"
|
|
279
|
-
] }),
|
|
280
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
281
|
-
"pre",
|
|
282
|
-
{
|
|
283
|
-
tabIndex: 0,
|
|
284
|
-
className: "overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs text-foreground",
|
|
285
|
-
children: safeStringify(entry.data)
|
|
286
|
-
}
|
|
287
|
-
)
|
|
288
|
-
] });
|
|
289
|
-
}
|
|
290
|
-
function safeStringify(value) {
|
|
291
|
-
try {
|
|
292
|
-
return JSON.stringify(value, null, 2);
|
|
293
|
-
} catch {
|
|
294
|
-
return String(value);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
var narrativeBlockRenderers = {
|
|
298
|
-
[FILE_TREE_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileTreeBlock, { entry }),
|
|
299
|
-
[DATA_MODEL_TYPE]: (entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DataModelBlock, { entry })
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// src/context/ReportRoot.tsx
|
|
303
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
304
|
-
var EMPTY_RENDERERS = {};
|
|
305
|
-
function ReportRoot({
|
|
306
|
-
report,
|
|
307
|
-
customRenderers,
|
|
308
|
-
renderers,
|
|
309
|
-
children
|
|
310
|
-
}) {
|
|
311
|
-
const value = (0, import_react2.useMemo)(
|
|
312
|
-
() => ({
|
|
313
|
-
report,
|
|
314
|
-
// Narrative blocks render everywhere the report does (static SSR, the
|
|
315
|
-
// island, Astro) without every host wiring them up. A host renderer for
|
|
316
|
-
// the same type still wins.
|
|
317
|
-
customRenderers: { ...narrativeBlockRenderers, ...customRenderers },
|
|
318
|
-
renderers: renderers ?? EMPTY_RENDERERS
|
|
319
|
-
}),
|
|
320
|
-
[report, customRenderers, renderers]
|
|
321
|
-
);
|
|
322
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ReportContext.Provider, { value, children });
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// src/hooks/useReport.ts
|
|
326
|
-
var import_react3 = require("react");
|
|
327
|
-
function useReport() {
|
|
328
|
-
const ctx = (0, import_react3.useContext)(ReportContext);
|
|
329
|
-
if (!ctx) {
|
|
330
|
-
throw new Error(
|
|
331
|
-
"useReport must be used inside <ReportRoot> or <Report>. Wrap your tree with one of those."
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
return ctx.report;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// src/components/ReportScenario.tsx
|
|
338
|
-
var import_duration3 = require("executable-stories-core/utils/duration");
|
|
339
|
-
|
|
340
|
-
// src/components/ReportSteps.tsx
|
|
341
|
-
var import_duration = require("executable-stories-core/utils/duration");
|
|
342
|
-
|
|
343
|
-
// src/components/doc/DocNote.tsx
|
|
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/lib/markdown.ts
|
|
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/lib/markdown.ts
|
|
437
|
-
function safeMarkdownHtml(markdown) {
|
|
438
|
-
const raw = import_marked.marked.parse(markdown, { async: false });
|
|
439
|
-
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)}'`);
|
|
440
|
-
}
|
|
441
|
-
function dedent(text) {
|
|
442
|
-
const lines = text.replace(/\t/g, " ").split("\n");
|
|
443
|
-
const indents = lines.filter((line) => line.trim().length > 0).map((line) => line.length - line.trimStart().length);
|
|
444
|
-
const shortest = indents.length > 0 ? Math.min(...indents) : 0;
|
|
445
|
-
return lines.map((line) => line.slice(shortest)).join("\n").trim();
|
|
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();
|
|
446
320
|
}
|
|
447
321
|
function neutralizeUrl(value) {
|
|
448
322
|
return (0, import_url.safeUrl)(value) ?? "#";
|
|
449
323
|
}
|
|
450
324
|
|
|
451
325
|
// src/components/doc/DocSection.tsx
|
|
452
|
-
var
|
|
326
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
453
327
|
function DocSection({ entry }) {
|
|
454
328
|
const renderers = useBuiltinRenderers();
|
|
455
329
|
if (renderers.section) {
|
|
456
|
-
return /* @__PURE__ */ (0,
|
|
330
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_jsx_runtime10.Fragment, { children: renderers.section(entry) });
|
|
457
331
|
}
|
|
458
332
|
const html = safeMarkdownHtml(entry.markdown);
|
|
459
|
-
return /* @__PURE__ */ (0,
|
|
460
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
461
|
-
/* @__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)(
|
|
462
336
|
"div",
|
|
463
337
|
{
|
|
464
338
|
className: "es-doc-prose prose prose-sm max-w-none",
|
|
@@ -469,11 +343,11 @@ function DocSection({ entry }) {
|
|
|
469
343
|
}
|
|
470
344
|
|
|
471
345
|
// src/components/doc/DocMermaid.tsx
|
|
472
|
-
var
|
|
346
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
473
347
|
function MermaidSource({ entry, ref }) {
|
|
474
|
-
return /* @__PURE__ */ (0,
|
|
475
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
476
|
-
/* @__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)(
|
|
477
351
|
"pre",
|
|
478
352
|
{
|
|
479
353
|
"data-mermaid": true,
|
|
@@ -487,24 +361,24 @@ function MermaidSource({ entry, ref }) {
|
|
|
487
361
|
function DocMermaid({ entry }) {
|
|
488
362
|
const renderers = useBuiltinRenderers();
|
|
489
363
|
if (renderers.mermaid) {
|
|
490
|
-
return /* @__PURE__ */ (0,
|
|
364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_jsx_runtime11.Fragment, { children: renderers.mermaid(entry) });
|
|
491
365
|
}
|
|
492
|
-
return /* @__PURE__ */ (0,
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MermaidSource, { entry });
|
|
493
367
|
}
|
|
494
368
|
|
|
495
369
|
// src/components/doc/DocScreenshot.tsx
|
|
496
|
-
var
|
|
370
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
497
371
|
function DocScreenshot({ entry }) {
|
|
498
372
|
const src = (0, import_url.isLocalFsPath)(entry.path) ? void 0 : (0, import_url.safeImageUrl)(entry.path);
|
|
499
373
|
if (!src) {
|
|
500
|
-
return /* @__PURE__ */ (0,
|
|
501
|
-
/* @__PURE__ */ (0,
|
|
502
|
-
/* @__PURE__ */ (0,
|
|
503
|
-
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
|
|
504
378
|
] });
|
|
505
379
|
}
|
|
506
|
-
return /* @__PURE__ */ (0,
|
|
507
|
-
/* @__PURE__ */ (0,
|
|
380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("figure", { className: "my-3", children: [
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
508
382
|
"img",
|
|
509
383
|
{
|
|
510
384
|
src,
|
|
@@ -513,24 +387,24 @@ function DocScreenshot({ entry }) {
|
|
|
513
387
|
className: "max-w-full rounded-md border border-border"
|
|
514
388
|
}
|
|
515
389
|
),
|
|
516
|
-
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
|
|
517
391
|
] });
|
|
518
392
|
}
|
|
519
393
|
|
|
520
394
|
// src/components/doc/DocVideo.tsx
|
|
521
|
-
var
|
|
395
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
522
396
|
function DocVideo({ entry }) {
|
|
523
397
|
if ((0, import_url.isLocalFsPath)(entry.path)) {
|
|
524
|
-
return /* @__PURE__ */ (0,
|
|
525
|
-
/* @__PURE__ */ (0,
|
|
526
|
-
/* @__PURE__ */ (0,
|
|
527
|
-
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
|
|
528
402
|
] });
|
|
529
403
|
}
|
|
530
404
|
const src = (0, import_url.safeUrl)(entry.path);
|
|
531
405
|
const poster = (0, import_url.safeUrl)(entry.poster);
|
|
532
|
-
return /* @__PURE__ */ (0,
|
|
533
|
-
/* @__PURE__ */ (0,
|
|
406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("figure", { className: "my-3", children: [
|
|
407
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
534
408
|
"video",
|
|
535
409
|
{
|
|
536
410
|
controls: true,
|
|
@@ -540,21 +414,21 @@ function DocVideo({ entry }) {
|
|
|
540
414
|
className: "max-w-full rounded-md border border-border"
|
|
541
415
|
}
|
|
542
416
|
),
|
|
543
|
-
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
|
|
544
418
|
] });
|
|
545
419
|
}
|
|
546
420
|
|
|
547
421
|
// src/components/doc/DocHtml.tsx
|
|
548
|
-
var
|
|
422
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
549
423
|
function DocHtml({ entry }) {
|
|
550
424
|
const height = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
|
|
551
425
|
const title = entry.title ?? "Embedded HTML";
|
|
552
426
|
const safeHref = (0, import_url.safeUrl)(entry.url ?? entry.path);
|
|
553
427
|
const frameProps = entry.content !== void 0 ? { srcDoc: entry.content } : { src: safeHref ?? "" };
|
|
554
|
-
return /* @__PURE__ */ (0,
|
|
555
|
-
/* @__PURE__ */ (0,
|
|
556
|
-
/* @__PURE__ */ (0,
|
|
557
|
-
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)(
|
|
558
432
|
"a",
|
|
559
433
|
{
|
|
560
434
|
href: safeHref,
|
|
@@ -567,7 +441,7 @@ function DocHtml({ entry }) {
|
|
|
567
441
|
}
|
|
568
442
|
) : null
|
|
569
443
|
] }),
|
|
570
|
-
/* @__PURE__ */ (0,
|
|
444
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
571
445
|
"iframe",
|
|
572
446
|
{
|
|
573
447
|
...frameProps,
|
|
@@ -582,7 +456,7 @@ function DocHtml({ entry }) {
|
|
|
582
456
|
}
|
|
583
457
|
|
|
584
458
|
// src/components/doc/DocState.tsx
|
|
585
|
-
var
|
|
459
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
586
460
|
function DocState({ entry }) {
|
|
587
461
|
let json;
|
|
588
462
|
try {
|
|
@@ -590,23 +464,23 @@ function DocState({ entry }) {
|
|
|
590
464
|
} catch {
|
|
591
465
|
json = String(entry.value);
|
|
592
466
|
}
|
|
593
|
-
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 }) });
|
|
594
468
|
}
|
|
595
469
|
|
|
596
470
|
// src/components/doc/DocCustom.tsx
|
|
597
|
-
var
|
|
471
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
598
472
|
function DocCustom({ entry }) {
|
|
599
473
|
const renderers = useCustomRenderers();
|
|
600
474
|
const renderer = renderers[entry.type];
|
|
601
475
|
if (renderer) {
|
|
602
|
-
return /* @__PURE__ */ (0,
|
|
476
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: renderer(entry) });
|
|
603
477
|
}
|
|
604
|
-
return /* @__PURE__ */ (0,
|
|
605
|
-
/* @__PURE__ */ (0,
|
|
606
|
-
/* @__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) })
|
|
607
481
|
] });
|
|
608
482
|
}
|
|
609
|
-
function
|
|
483
|
+
function safeStringify(value) {
|
|
610
484
|
try {
|
|
611
485
|
return JSON.stringify(value, null, 2);
|
|
612
486
|
} catch {
|
|
@@ -615,64 +489,43 @@ function safeStringify2(value) {
|
|
|
615
489
|
}
|
|
616
490
|
|
|
617
491
|
// src/components/doc/DocEntry.tsx
|
|
618
|
-
var
|
|
492
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
619
493
|
function DocEntry({ entry }) {
|
|
620
494
|
switch (entry.kind) {
|
|
621
495
|
case "note":
|
|
622
|
-
return /* @__PURE__ */ (0,
|
|
496
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocNote, { entry });
|
|
623
497
|
case "tag":
|
|
624
|
-
return /* @__PURE__ */ (0,
|
|
498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocTag, { entry });
|
|
625
499
|
case "kv":
|
|
626
|
-
return /* @__PURE__ */ (0,
|
|
500
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocKv, { entry });
|
|
627
501
|
case "code":
|
|
628
|
-
return /* @__PURE__ */ (0,
|
|
502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocCode, { entry });
|
|
629
503
|
case "table":
|
|
630
|
-
return /* @__PURE__ */ (0,
|
|
504
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocTable, { entry });
|
|
631
505
|
case "link":
|
|
632
|
-
return /* @__PURE__ */ (0,
|
|
506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocLink, { entry });
|
|
633
507
|
case "section":
|
|
634
|
-
return /* @__PURE__ */ (0,
|
|
508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocSection, { entry });
|
|
635
509
|
case "mermaid":
|
|
636
|
-
return /* @__PURE__ */ (0,
|
|
510
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocMermaid, { entry });
|
|
637
511
|
case "screenshot":
|
|
638
|
-
return /* @__PURE__ */ (0,
|
|
512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocScreenshot, { entry });
|
|
639
513
|
case "video":
|
|
640
|
-
return /* @__PURE__ */ (0,
|
|
514
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocVideo, { entry });
|
|
641
515
|
case "html":
|
|
642
|
-
return /* @__PURE__ */ (0,
|
|
516
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocHtml, { entry });
|
|
643
517
|
case "state":
|
|
644
|
-
return /* @__PURE__ */ (0,
|
|
518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocState, { entry });
|
|
645
519
|
case "custom":
|
|
646
|
-
return /* @__PURE__ */ (0,
|
|
520
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(DocCustom, { entry });
|
|
647
521
|
}
|
|
648
522
|
}
|
|
649
523
|
|
|
650
524
|
// src/components/ReportDocEntries.tsx
|
|
651
|
-
var
|
|
525
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
652
526
|
function ReportDocEntries({ entries }) {
|
|
653
527
|
if (entries.length === 0) return null;
|
|
654
|
-
return /* @__PURE__ */ (0,
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// src/lib/step-params.tsx
|
|
658
|
-
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
659
|
-
var STEP_PARAM_PATTERN = /"[^"]*"|(?<![\w.-])\d+(?:\.\d+)?(?![\w.-])/g;
|
|
660
|
-
function highlightStepParams(text) {
|
|
661
|
-
const matches = Array.from(text.matchAll(STEP_PARAM_PATTERN));
|
|
662
|
-
if (matches.length === 0) return text;
|
|
663
|
-
const nodes = [];
|
|
664
|
-
let lastIndex = 0;
|
|
665
|
-
matches.forEach((match, i) => {
|
|
666
|
-
const start = match.index;
|
|
667
|
-
const end = start + match[0].length;
|
|
668
|
-
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
669
|
-
nodes.push(
|
|
670
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "font-medium text-step-param italic", children: match[0] }, i)
|
|
671
|
-
);
|
|
672
|
-
lastIndex = end;
|
|
673
|
-
});
|
|
674
|
-
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
675
|
-
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)) });
|
|
676
529
|
}
|
|
677
530
|
|
|
678
531
|
// src/components/ReportSteps.tsx
|
|
@@ -691,9 +544,22 @@ var STEP_GLYPH_COLOR = {
|
|
|
691
544
|
};
|
|
692
545
|
function ReportSteps({ scenario }) {
|
|
693
546
|
if (scenario.steps.length === 0) return null;
|
|
694
|
-
|
|
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
|
+
)) });
|
|
695
558
|
}
|
|
696
|
-
function ReportStepItem({
|
|
559
|
+
function ReportStepItem({
|
|
560
|
+
step,
|
|
561
|
+
unasserted = false
|
|
562
|
+
}) {
|
|
697
563
|
const kw = step.keyword.toLowerCase();
|
|
698
564
|
const continuation = kw === "and" || kw === "but";
|
|
699
565
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
@@ -701,21 +567,35 @@ function ReportStepItem({ step }) {
|
|
|
701
567
|
{
|
|
702
568
|
id: step.id,
|
|
703
569
|
"data-status": step.status,
|
|
704
|
-
className: cn(
|
|
570
|
+
className: cn(
|
|
571
|
+
"py-1.5 text-[0.8125rem] leading-normal",
|
|
572
|
+
continuation && "pl-5"
|
|
573
|
+
),
|
|
705
574
|
children: [
|
|
706
575
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-baseline gap-2", children: [
|
|
707
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "aria-hidden": true, className: cn("w-4 shrink-0 text-center text-xs", STEP_GLYPH_COLOR[step.status]), children: STEP_GLYPH[step.status] ?? "\u25CB" }),
|
|
708
576
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
709
577
|
"span",
|
|
710
578
|
{
|
|
579
|
+
"aria-hidden": true,
|
|
711
580
|
className: cn(
|
|
712
|
-
"
|
|
713
|
-
|
|
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
|
+
),
|
|
587
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
588
|
+
"span",
|
|
589
|
+
{
|
|
590
|
+
className: cn(
|
|
591
|
+
"min-w-[52px] shrink-0 font-mono text-xs font-semibold capitalize",
|
|
592
|
+
continuation ? "font-medium text-muted-foreground" : "text-keyword"
|
|
714
593
|
),
|
|
715
594
|
children: step.keyword
|
|
716
595
|
}
|
|
717
596
|
),
|
|
718
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,
|
|
719
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
|
|
720
600
|
] }),
|
|
721
601
|
step.errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
@@ -1017,11 +897,11 @@ function ReportTrace({ spans }) {
|
|
|
1017
897
|
}
|
|
1018
898
|
|
|
1019
899
|
// src/interactive/scenario-history-context.ts
|
|
1020
|
-
var
|
|
1021
|
-
var ScenarioHistoryContext = (0,
|
|
900
|
+
var import_react4 = require("react");
|
|
901
|
+
var ScenarioHistoryContext = (0, import_react4.createContext)(null);
|
|
1022
902
|
var ScenarioHistoryProvider = ScenarioHistoryContext.Provider;
|
|
1023
903
|
function useScenarioHistory() {
|
|
1024
|
-
return (0,
|
|
904
|
+
return (0, import_react4.useContext)(ScenarioHistoryContext);
|
|
1025
905
|
}
|
|
1026
906
|
|
|
1027
907
|
// src/lib/run-history.ts
|
|
@@ -1129,12 +1009,91 @@ function ScenarioRunHistory({ scenarioId }) {
|
|
|
1129
1009
|
] });
|
|
1130
1010
|
}
|
|
1131
1011
|
|
|
1132
|
-
// src/
|
|
1012
|
+
// src/components/ScenarioStaleness.tsx
|
|
1133
1013
|
var import_react6 = require("react");
|
|
1134
|
-
|
|
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);
|
|
1135
1094
|
var CollapseProvider = CollapseContext.Provider;
|
|
1136
1095
|
function useCollapse() {
|
|
1137
|
-
return (0,
|
|
1096
|
+
return (0, import_react7.useContext)(CollapseContext);
|
|
1138
1097
|
}
|
|
1139
1098
|
var STORAGE_KEY = "es-collapsed-ids";
|
|
1140
1099
|
function readPersisted() {
|
|
@@ -1154,11 +1113,11 @@ function persist(ids) {
|
|
|
1154
1113
|
}
|
|
1155
1114
|
}
|
|
1156
1115
|
function useCollapseState(computeDefault) {
|
|
1157
|
-
const [collapsed, setCollapsed] = (0,
|
|
1116
|
+
const [collapsed, setCollapsed] = (0, import_react7.useState)(
|
|
1158
1117
|
() => readPersisted() ?? computeDefault?.() ?? /* @__PURE__ */ new Set()
|
|
1159
1118
|
);
|
|
1160
|
-
const isCollapsed = (0,
|
|
1161
|
-
const toggle = (0,
|
|
1119
|
+
const isCollapsed = (0, import_react7.useCallback)((id) => collapsed.has(id), [collapsed]);
|
|
1120
|
+
const toggle = (0, import_react7.useCallback)((id) => {
|
|
1162
1121
|
setCollapsed((prev) => {
|
|
1163
1122
|
const next = new Set(prev);
|
|
1164
1123
|
if (next.has(id)) next.delete(id);
|
|
@@ -1167,14 +1126,14 @@ function useCollapseState(computeDefault) {
|
|
|
1167
1126
|
return next;
|
|
1168
1127
|
});
|
|
1169
1128
|
}, []);
|
|
1170
|
-
const collapseAll = (0,
|
|
1129
|
+
const collapseAll = (0, import_react7.useCallback)((ids) => {
|
|
1171
1130
|
setCollapsed(() => {
|
|
1172
1131
|
const next = new Set(ids);
|
|
1173
1132
|
persist(next);
|
|
1174
1133
|
return next;
|
|
1175
1134
|
});
|
|
1176
1135
|
}, []);
|
|
1177
|
-
const expandAll = (0,
|
|
1136
|
+
const expandAll = (0, import_react7.useCallback)(() => {
|
|
1178
1137
|
setCollapsed(() => {
|
|
1179
1138
|
const next = /* @__PURE__ */ new Set();
|
|
1180
1139
|
persist(next);
|
|
@@ -1185,12 +1144,12 @@ function useCollapseState(computeDefault) {
|
|
|
1185
1144
|
}
|
|
1186
1145
|
|
|
1187
1146
|
// src/interactive/scenario-actions.tsx
|
|
1188
|
-
var
|
|
1147
|
+
var import_react8 = require("react");
|
|
1189
1148
|
var import_scenario_markdown = require("executable-stories-core/utils/scenario-markdown");
|
|
1190
|
-
var ScenarioActionsContext = (0,
|
|
1149
|
+
var ScenarioActionsContext = (0, import_react8.createContext)(null);
|
|
1191
1150
|
var ScenarioActionsProvider = ScenarioActionsContext.Provider;
|
|
1192
1151
|
function useScenarioActions() {
|
|
1193
|
-
return (0,
|
|
1152
|
+
return (0, import_react8.useContext)(ScenarioActionsContext);
|
|
1194
1153
|
}
|
|
1195
1154
|
function scenarioToMarkdown(scenario) {
|
|
1196
1155
|
return (0, import_scenario_markdown.scenarioToMarkdown)(scenario, { variant: "compact" });
|
|
@@ -1221,9 +1180,9 @@ function scenarioPermalink(scenario) {
|
|
|
1221
1180
|
|
|
1222
1181
|
// src/components/ui/card.tsx
|
|
1223
1182
|
var React2 = require("react");
|
|
1224
|
-
var
|
|
1183
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1225
1184
|
function Card({ className, ...props }) {
|
|
1226
|
-
return /* @__PURE__ */ (0,
|
|
1185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1227
1186
|
"div",
|
|
1228
1187
|
{
|
|
1229
1188
|
"data-slot": "card",
|
|
@@ -1236,7 +1195,7 @@ function Card({ className, ...props }) {
|
|
|
1236
1195
|
);
|
|
1237
1196
|
}
|
|
1238
1197
|
function CardHeader({ className, ...props }) {
|
|
1239
|
-
return /* @__PURE__ */ (0,
|
|
1198
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1240
1199
|
"div",
|
|
1241
1200
|
{
|
|
1242
1201
|
"data-slot": "card-header",
|
|
@@ -1249,7 +1208,7 @@ function CardHeader({ className, ...props }) {
|
|
|
1249
1208
|
);
|
|
1250
1209
|
}
|
|
1251
1210
|
function CardContent({ className, ...props }) {
|
|
1252
|
-
return /* @__PURE__ */ (0,
|
|
1211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1253
1212
|
"div",
|
|
1254
1213
|
{
|
|
1255
1214
|
"data-slot": "card-content",
|
|
@@ -1263,13 +1222,13 @@ function CardContent({ className, ...props }) {
|
|
|
1263
1222
|
var React3 = require("react");
|
|
1264
1223
|
var import_menu = require("@base-ui/react/menu");
|
|
1265
1224
|
var import_lucide_react = require("lucide-react");
|
|
1266
|
-
var
|
|
1225
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1267
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";
|
|
1268
1227
|
function DropdownMenu(props) {
|
|
1269
|
-
return /* @__PURE__ */ (0,
|
|
1228
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Root, { "data-slot": "dropdown-menu", ...props });
|
|
1270
1229
|
}
|
|
1271
1230
|
function DropdownMenuTrigger(props) {
|
|
1272
|
-
return /* @__PURE__ */ (0,
|
|
1231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
1273
1232
|
}
|
|
1274
1233
|
function DropdownMenuContent({
|
|
1275
1234
|
className,
|
|
@@ -1277,14 +1236,14 @@ function DropdownMenuContent({
|
|
|
1277
1236
|
sideOffset = 4,
|
|
1278
1237
|
...props
|
|
1279
1238
|
}) {
|
|
1280
|
-
return /* @__PURE__ */ (0,
|
|
1239
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_menu.Menu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1281
1240
|
import_menu.Menu.Positioner,
|
|
1282
1241
|
{
|
|
1283
1242
|
"data-slot": "dropdown-menu-positioner",
|
|
1284
1243
|
className: "z-50",
|
|
1285
1244
|
align,
|
|
1286
1245
|
sideOffset,
|
|
1287
|
-
children: /* @__PURE__ */ (0,
|
|
1246
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1288
1247
|
import_menu.Menu.Popup,
|
|
1289
1248
|
{
|
|
1290
1249
|
"data-slot": "dropdown-menu-content",
|
|
@@ -1302,7 +1261,7 @@ function DropdownMenuItem({
|
|
|
1302
1261
|
className,
|
|
1303
1262
|
...props
|
|
1304
1263
|
}) {
|
|
1305
|
-
return /* @__PURE__ */ (0,
|
|
1264
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1306
1265
|
import_menu.Menu.Item,
|
|
1307
1266
|
{
|
|
1308
1267
|
"data-slot": "dropdown-menu-item",
|
|
@@ -1314,7 +1273,7 @@ function DropdownMenuItem({
|
|
|
1314
1273
|
|
|
1315
1274
|
// src/components/ReportScenario.tsx
|
|
1316
1275
|
var import_lucide_react2 = require("lucide-react");
|
|
1317
|
-
var
|
|
1276
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1318
1277
|
var STATUS_META = {
|
|
1319
1278
|
passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
|
|
1320
1279
|
failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
|
|
@@ -1330,7 +1289,7 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1330
1289
|
const collapsible = collapse !== null && !hideTitle;
|
|
1331
1290
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
1332
1291
|
const actions = useScenarioActions();
|
|
1333
|
-
return /* @__PURE__ */ (0,
|
|
1292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1334
1293
|
Card,
|
|
1335
1294
|
{
|
|
1336
1295
|
id: scenario.id,
|
|
@@ -1338,10 +1297,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1338
1297
|
...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
|
|
1339
1298
|
className: "gap-0 py-0",
|
|
1340
1299
|
children: [
|
|
1341
|
-
/* @__PURE__ */ (0,
|
|
1342
|
-
/* @__PURE__ */ (0,
|
|
1343
|
-
hideTitle ? null : /* @__PURE__ */ (0,
|
|
1344
|
-
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)(
|
|
1345
1304
|
"button",
|
|
1346
1305
|
{
|
|
1347
1306
|
type: "button",
|
|
@@ -1350,49 +1309,50 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1350
1309
|
"aria-controls": bodyId,
|
|
1351
1310
|
"aria-label": `Toggle ${scenario.title}`,
|
|
1352
1311
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
1353
|
-
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" })
|
|
1354
1313
|
}
|
|
1355
1314
|
) : null,
|
|
1356
|
-
/* @__PURE__ */ (0,
|
|
1357
|
-
/* @__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 })
|
|
1358
1317
|
] }),
|
|
1359
|
-
/* @__PURE__ */ (0,
|
|
1360
|
-
/* @__PURE__ */ (0,
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
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)(
|
|
1365
1325
|
DropdownMenuTrigger,
|
|
1366
1326
|
{
|
|
1367
1327
|
"aria-label": `Actions for ${scenario.title}`,
|
|
1368
1328
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1369
|
-
children: /* @__PURE__ */ (0,
|
|
1329
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
|
|
1370
1330
|
}
|
|
1371
1331
|
),
|
|
1372
|
-
/* @__PURE__ */ (0,
|
|
1373
|
-
/* @__PURE__ */ (0,
|
|
1374
|
-
/* @__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, {}),
|
|
1375
1335
|
" Copy link"
|
|
1376
1336
|
] }),
|
|
1377
|
-
/* @__PURE__ */ (0,
|
|
1378
|
-
/* @__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, {}),
|
|
1379
1339
|
" Copy as Markdown"
|
|
1380
1340
|
] }),
|
|
1381
|
-
/* @__PURE__ */ (0,
|
|
1382
|
-
/* @__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, {}),
|
|
1383
1343
|
" Explain with AI"
|
|
1384
1344
|
] })
|
|
1385
1345
|
] })
|
|
1386
1346
|
] }) : null
|
|
1387
1347
|
] })
|
|
1388
1348
|
] }),
|
|
1389
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ (0,
|
|
1390
|
-
scenario.tags.map((t) => /* @__PURE__ */ (0,
|
|
1391
|
-
(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}`))
|
|
1392
1352
|
] }) : null
|
|
1393
1353
|
] }),
|
|
1394
|
-
/* @__PURE__ */ (0,
|
|
1395
|
-
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)(
|
|
1396
1356
|
"pre",
|
|
1397
1357
|
{
|
|
1398
1358
|
role: "alert",
|
|
@@ -1400,11 +1360,11 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1400
1360
|
children: scenario.errorMessage
|
|
1401
1361
|
}
|
|
1402
1362
|
) : null,
|
|
1403
|
-
/* @__PURE__ */ (0,
|
|
1404
|
-
/* @__PURE__ */ (0,
|
|
1405
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */ (0,
|
|
1406
|
-
/* @__PURE__ */ (0,
|
|
1407
|
-
/* @__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 })
|
|
1408
1368
|
] })
|
|
1409
1369
|
]
|
|
1410
1370
|
}
|
|
@@ -1412,28 +1372,28 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1412
1372
|
}
|
|
1413
1373
|
|
|
1414
1374
|
// src/components/ReportScenarioList.tsx
|
|
1415
|
-
var
|
|
1375
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1416
1376
|
function ReportScenarioList({ feature }) {
|
|
1417
|
-
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)) });
|
|
1418
1378
|
}
|
|
1419
1379
|
|
|
1420
1380
|
// src/components/ReportFeatureIntro.tsx
|
|
1421
|
-
var
|
|
1381
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1422
1382
|
function ReportFeatureIntro({ feature }) {
|
|
1423
1383
|
const hasNarrative = Boolean(feature.narrative?.trim());
|
|
1424
1384
|
const hasGlossary = Boolean(feature.glossary?.length);
|
|
1425
1385
|
if (!hasNarrative && !hasGlossary) return null;
|
|
1426
|
-
return /* @__PURE__ */ (0,
|
|
1427
|
-
hasNarrative ? /* @__PURE__ */ (0,
|
|
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)(
|
|
1428
1388
|
"div",
|
|
1429
1389
|
{
|
|
1430
1390
|
className: "es-doc-prose prose prose-sm max-w-none text-muted-foreground",
|
|
1431
1391
|
dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
|
|
1432
1392
|
}
|
|
1433
1393
|
) : null,
|
|
1434
|
-
hasGlossary ? /* @__PURE__ */ (0,
|
|
1435
|
-
/* @__PURE__ */ (0,
|
|
1436
|
-
/* @__PURE__ */ (0,
|
|
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 })
|
|
1437
1397
|
] }, entry.term)) }) : null
|
|
1438
1398
|
] });
|
|
1439
1399
|
}
|
|
@@ -1445,7 +1405,7 @@ function featureKindLabel(kind) {
|
|
|
1445
1405
|
}
|
|
1446
1406
|
|
|
1447
1407
|
// src/components/ReportFeature.tsx
|
|
1448
|
-
var
|
|
1408
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1449
1409
|
function ReportFeature({ feature }) {
|
|
1450
1410
|
const titleId = `${feature.id}-title`;
|
|
1451
1411
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1457,7 +1417,7 @@ function ReportFeature({ feature }) {
|
|
|
1457
1417
|
return (
|
|
1458
1418
|
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1459
1419
|
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1460
|
-
/* @__PURE__ */ (0,
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1461
1421
|
"section",
|
|
1462
1422
|
{
|
|
1463
1423
|
id: feature.id,
|
|
@@ -1465,9 +1425,9 @@ function ReportFeature({ feature }) {
|
|
|
1465
1425
|
"aria-labelledby": titleId,
|
|
1466
1426
|
className: "flex flex-col gap-3",
|
|
1467
1427
|
children: [
|
|
1468
|
-
/* @__PURE__ */ (0,
|
|
1469
|
-
/* @__PURE__ */ (0,
|
|
1470
|
-
/* @__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)(
|
|
1471
1431
|
"button",
|
|
1472
1432
|
{
|
|
1473
1433
|
type: "button",
|
|
@@ -1476,34 +1436,34 @@ function ReportFeature({ feature }) {
|
|
|
1476
1436
|
"aria-controls": bodyId,
|
|
1477
1437
|
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1478
1438
|
children: [
|
|
1479
|
-
/* @__PURE__ */ (0,
|
|
1480
|
-
/* @__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 })
|
|
1481
1441
|
]
|
|
1482
1442
|
}
|
|
1483
1443
|
) : feature.title }),
|
|
1484
|
-
/* @__PURE__ */ (0,
|
|
1485
|
-
kindLabel ? /* @__PURE__ */ (0,
|
|
1486
|
-
/* @__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 })
|
|
1487
1447
|
] })
|
|
1488
1448
|
] }),
|
|
1489
|
-
/* @__PURE__ */ (0,
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1490
1450
|
"p",
|
|
1491
1451
|
{
|
|
1492
1452
|
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1493
1453
|
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1494
1454
|
children: [
|
|
1495
|
-
/* @__PURE__ */ (0,
|
|
1455
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { children: [
|
|
1496
1456
|
s.passed,
|
|
1497
1457
|
" passed"
|
|
1498
1458
|
] }),
|
|
1499
|
-
/* @__PURE__ */ (0,
|
|
1500
|
-
/* @__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: [
|
|
1501
1461
|
s.failed,
|
|
1502
1462
|
" failed"
|
|
1503
1463
|
] }),
|
|
1504
|
-
skipped > 0 ? /* @__PURE__ */ (0,
|
|
1505
|
-
/* @__PURE__ */ (0,
|
|
1506
|
-
/* @__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: [
|
|
1507
1467
|
skipped,
|
|
1508
1468
|
" skipped"
|
|
1509
1469
|
] })
|
|
@@ -1512,9 +1472,9 @@ function ReportFeature({ feature }) {
|
|
|
1512
1472
|
}
|
|
1513
1473
|
)
|
|
1514
1474
|
] }),
|
|
1515
|
-
/* @__PURE__ */ (0,
|
|
1516
|
-
/* @__PURE__ */ (0,
|
|
1517
|
-
/* @__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 })
|
|
1518
1478
|
] })
|
|
1519
1479
|
]
|
|
1520
1480
|
}
|
|
@@ -1523,72 +1483,75 @@ function ReportFeature({ feature }) {
|
|
|
1523
1483
|
}
|
|
1524
1484
|
|
|
1525
1485
|
// src/components/ReportFeatureList.tsx
|
|
1526
|
-
var
|
|
1486
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1527
1487
|
function ReportFeatureList() {
|
|
1528
1488
|
const report = useReport();
|
|
1529
|
-
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)) });
|
|
1530
1490
|
}
|
|
1531
1491
|
|
|
1532
|
-
// src/components/
|
|
1533
|
-
var
|
|
1534
|
-
var
|
|
1535
|
-
function
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
"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",
|
|
1542
|
-
className
|
|
1543
|
-
),
|
|
1544
|
-
...props
|
|
1545
|
-
}
|
|
1546
|
-
);
|
|
1547
|
-
}
|
|
1548
|
-
function EmptyHeader({ className, ...props }) {
|
|
1549
|
-
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1550
|
-
"div",
|
|
1551
|
-
{
|
|
1552
|
-
"data-slot": "empty-header",
|
|
1553
|
-
className: cn(
|
|
1554
|
-
"flex max-w-sm flex-col items-center gap-2 text-center",
|
|
1555
|
-
className
|
|
1556
|
-
),
|
|
1557
|
-
...props
|
|
1558
|
-
}
|
|
1559
|
-
);
|
|
1560
|
-
}
|
|
1561
|
-
var emptyMediaVariants = (0, import_class_variance_authority2.cva)(
|
|
1562
|
-
"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
1563
|
-
{
|
|
1564
|
-
variants: {
|
|
1565
|
-
variant: {
|
|
1566
|
-
default: "bg-transparent",
|
|
1567
|
-
icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6"
|
|
1568
|
-
}
|
|
1569
|
-
},
|
|
1570
|
-
defaultVariants: {
|
|
1571
|
-
variant: "default"
|
|
1572
|
-
}
|
|
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() });
|
|
1573
1501
|
}
|
|
1574
|
-
)
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
{
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
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
|
+
] })
|
|
1583
1552
|
);
|
|
1584
1553
|
}
|
|
1585
1554
|
|
|
1586
|
-
// src/components/ReportEmpty.tsx
|
|
1587
|
-
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1588
|
-
function ReportEmpty({ message }) {
|
|
1589
|
-
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EmptyHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
1555
|
// src/components/ReportSummary.tsx
|
|
1593
1556
|
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1594
1557
|
function ReportSummary({ className }) {
|
|
@@ -1652,182 +1615,65 @@ function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
|
1652
1615
|
);
|
|
1653
1616
|
}
|
|
1654
1617
|
|
|
1655
|
-
// src/components/
|
|
1656
|
-
var
|
|
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
|
+
] });
|
|
1638
|
+
}
|
|
1657
1639
|
|
|
1658
|
-
// src/
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
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 }) });
|
|
1663
1649
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
function commitUrl(ci, sha) {
|
|
1692
|
-
const commitSha = ci?.commitSha ?? sha;
|
|
1693
|
-
if (!ci?.url || !commitSha) return void 0;
|
|
1694
|
-
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1695
|
-
if (github) return `${github[1]}/commit/${commitSha}`;
|
|
1696
|
-
const gitlab = ci.url.match(/^(https?:\/\/.+)\/-\/pipelines\//);
|
|
1697
|
-
if (gitlab) return `${gitlab[1]}/-/commit/${commitSha}`;
|
|
1698
|
-
return void 0;
|
|
1699
|
-
}
|
|
1700
|
-
function prUrl(ci) {
|
|
1701
|
-
if (!ci?.url || !ci.prNumber) return void 0;
|
|
1702
|
-
const github = ci.url.match(/^(https?:\/\/[^/]+\/[^/]+\/[^/]+)\/actions\/runs\//);
|
|
1703
|
-
if (github) return `${github[1]}/pull/${ci.prNumber}`;
|
|
1704
|
-
return void 0;
|
|
1705
|
-
}
|
|
1706
|
-
|
|
1707
|
-
// src/components/ReportMeta.tsx
|
|
1708
|
-
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1709
|
-
function ReportMeta() {
|
|
1710
|
-
const report = useReport();
|
|
1711
|
-
const items = [];
|
|
1712
|
-
const linkClass = "text-primary underline underline-offset-2";
|
|
1713
|
-
if (report.startedAtMs) {
|
|
1714
|
-
items.push({ label: "Started", value: new Date(report.startedAtMs).toLocaleString() });
|
|
1715
|
-
}
|
|
1716
|
-
if (report.durationMs) {
|
|
1717
|
-
items.push({ label: "Duration", value: (0, import_duration4.formatDuration)(report.durationMs) });
|
|
1718
|
-
}
|
|
1719
|
-
if (report.packageVersion) {
|
|
1720
|
-
items.push({ label: "Version", value: report.packageVersion });
|
|
1721
|
-
}
|
|
1722
|
-
if (report.ci?.branch) {
|
|
1723
|
-
items.push({ label: "Branch", value: report.ci.branch });
|
|
1724
|
-
}
|
|
1725
|
-
const sha = report.ci?.commitSha ?? report.gitSha;
|
|
1726
|
-
if (sha) {
|
|
1727
|
-
const url = commitUrl(report.ci, report.gitSha);
|
|
1728
|
-
const short = sha.slice(0, 8);
|
|
1729
|
-
items.push({
|
|
1730
|
-
label: "Commit",
|
|
1731
|
-
value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1732
|
-
});
|
|
1733
|
-
}
|
|
1734
|
-
if (report.ci) {
|
|
1735
|
-
items.push({
|
|
1736
|
-
label: "CI",
|
|
1737
|
-
value: report.ci.url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1738
|
-
ciDisplayName(report.ci.name),
|
|
1739
|
-
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1740
|
-
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
1741
|
-
});
|
|
1742
|
-
}
|
|
1743
|
-
if (report.ci?.prNumber) {
|
|
1744
|
-
const url = prUrl(report.ci);
|
|
1745
|
-
const label = `#${report.ci.prNumber}`;
|
|
1746
|
-
items.push({
|
|
1747
|
-
label: "PR",
|
|
1748
|
-
value: url ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1749
|
-
});
|
|
1750
|
-
}
|
|
1751
|
-
if (items.length === 0) return null;
|
|
1752
|
-
return (
|
|
1753
|
-
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1754
|
-
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1755
|
-
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1756
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1757
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.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: [
|
|
1758
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1759
|
-
"Run details"
|
|
1760
|
-
] }),
|
|
1761
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.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_runtime34.jsxs)("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1762
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1763
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1764
|
-
] }, item.label)) })
|
|
1765
|
-
] })
|
|
1766
|
-
);
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
// src/components/ReportSchemaError.tsx
|
|
1770
|
-
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1771
|
-
function ReportSchemaError({ error }) {
|
|
1772
|
-
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1773
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("strong", { children: "Report could not be displayed." }) }),
|
|
1774
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: error.message }),
|
|
1775
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { children: [
|
|
1776
|
-
"The report bundle is newer than this version of ",
|
|
1777
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("code", { children: "executable-stories-react" }),
|
|
1778
|
-
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1779
|
-
] }) : null,
|
|
1780
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("details", { children: [
|
|
1781
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("summary", { children: [
|
|
1782
|
-
error.issues.length,
|
|
1783
|
-
" validation issue",
|
|
1784
|
-
error.issues.length === 1 ? "" : "s"
|
|
1785
|
-
] }),
|
|
1786
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1787
|
-
] }) : null
|
|
1788
|
-
] });
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
// src/components/ReportShell.tsx
|
|
1792
|
-
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1793
|
-
function ReportErrorShell({
|
|
1794
|
-
error,
|
|
1795
|
-
className,
|
|
1796
|
-
title,
|
|
1797
|
-
dataTheme
|
|
1798
|
-
}) {
|
|
1799
|
-
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ReportSchemaError, { error }) });
|
|
1800
|
-
}
|
|
1801
|
-
|
|
1802
|
-
// src/interactive/ReportInteractive.tsx
|
|
1803
|
-
var import_lucide_react5 = require("lucide-react");
|
|
1804
|
-
|
|
1805
|
-
// src/components/ui/switch.tsx
|
|
1806
|
-
var React4 = require("react");
|
|
1807
|
-
var import_switch = require("@base-ui/react/switch");
|
|
1808
|
-
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1809
|
-
function Switch({
|
|
1810
|
-
className,
|
|
1811
|
-
...props
|
|
1812
|
-
}) {
|
|
1813
|
-
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1814
|
-
import_switch.Switch.Root,
|
|
1815
|
-
{
|
|
1816
|
-
"data-slot": "switch",
|
|
1817
|
-
className: cn(
|
|
1818
|
-
"peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-primary data-[unchecked]:bg-input",
|
|
1819
|
-
className
|
|
1820
|
-
),
|
|
1821
|
-
...props,
|
|
1822
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1823
|
-
import_switch.Switch.Thumb,
|
|
1824
|
-
{
|
|
1825
|
-
"data-slot": "switch-thumb",
|
|
1826
|
-
className: "pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[checked]:translate-x-[calc(100%-2px)] data-[unchecked]:translate-x-0"
|
|
1827
|
-
}
|
|
1828
|
-
)
|
|
1829
|
-
}
|
|
1830
|
-
);
|
|
1650
|
+
|
|
1651
|
+
// src/components/ui/switch.tsx
|
|
1652
|
+
var React4 = require("react");
|
|
1653
|
+
var import_switch = require("@base-ui/react/switch");
|
|
1654
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1655
|
+
function Switch({
|
|
1656
|
+
className,
|
|
1657
|
+
...props
|
|
1658
|
+
}) {
|
|
1659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1660
|
+
import_switch.Switch.Root,
|
|
1661
|
+
{
|
|
1662
|
+
"data-slot": "switch",
|
|
1663
|
+
className: cn(
|
|
1664
|
+
"peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-primary data-[unchecked]:bg-input",
|
|
1665
|
+
className
|
|
1666
|
+
),
|
|
1667
|
+
...props,
|
|
1668
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
1669
|
+
import_switch.Switch.Thumb,
|
|
1670
|
+
{
|
|
1671
|
+
"data-slot": "switch-thumb",
|
|
1672
|
+
className: "pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[checked]:translate-x-[calc(100%-2px)] data-[unchecked]:translate-x-0"
|
|
1673
|
+
}
|
|
1674
|
+
)
|
|
1675
|
+
}
|
|
1676
|
+
);
|
|
1831
1677
|
}
|
|
1832
1678
|
|
|
1833
1679
|
// src/components/ui/toggle-group.tsx
|
|
@@ -1835,7 +1681,7 @@ var React5 = __toESM(require("react"), 1);
|
|
|
1835
1681
|
var import_toggle_group = require("@base-ui/react/toggle-group");
|
|
1836
1682
|
var import_toggle = require("@base-ui/react/toggle");
|
|
1837
1683
|
var import_class_variance_authority3 = require("class-variance-authority");
|
|
1838
|
-
var
|
|
1684
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
1839
1685
|
var toggleItemVariants = (0, import_class_variance_authority3.cva)(
|
|
1840
1686
|
"inline-flex items-center justify-center gap-1.5 rounded-md font-medium whitespace-nowrap text-muted-foreground transition-[color,background-color,box-shadow] outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-background data-[pressed]:text-foreground data-[pressed]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1841
1687
|
{
|
|
@@ -1857,13 +1703,13 @@ function ToggleGroup({
|
|
|
1857
1703
|
children,
|
|
1858
1704
|
...props
|
|
1859
1705
|
}) {
|
|
1860
|
-
return /* @__PURE__ */ (0,
|
|
1706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1861
1707
|
import_toggle_group.ToggleGroup,
|
|
1862
1708
|
{
|
|
1863
1709
|
"data-slot": "toggle-group",
|
|
1864
1710
|
className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
|
|
1865
1711
|
...props,
|
|
1866
|
-
children: /* @__PURE__ */ (0,
|
|
1712
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ToggleGroupContext.Provider, { value: { size }, children })
|
|
1867
1713
|
}
|
|
1868
1714
|
);
|
|
1869
1715
|
}
|
|
@@ -1874,7 +1720,7 @@ function ToggleGroupItem({
|
|
|
1874
1720
|
...props
|
|
1875
1721
|
}) {
|
|
1876
1722
|
const context = React5.useContext(ToggleGroupContext);
|
|
1877
|
-
return /* @__PURE__ */ (0,
|
|
1723
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
1878
1724
|
import_toggle.Toggle,
|
|
1879
1725
|
{
|
|
1880
1726
|
"data-slot": "toggle-group-item",
|
|
@@ -1885,99 +1731,207 @@ function ToggleGroupItem({
|
|
|
1885
1731
|
);
|
|
1886
1732
|
}
|
|
1887
1733
|
|
|
1888
|
-
// src/
|
|
1889
|
-
var
|
|
1890
|
-
var import_lucide_react3 = require("lucide-react");
|
|
1734
|
+
// src/context/ReportRoot.tsx
|
|
1735
|
+
var import_react9 = require("react");
|
|
1891
1736
|
|
|
1892
|
-
// src/components/
|
|
1893
|
-
var
|
|
1894
|
-
var
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
{
|
|
1899
|
-
type,
|
|
1900
|
-
"data-slot": "input",
|
|
1901
|
-
className: cn(
|
|
1902
|
-
"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",
|
|
1903
|
-
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
1904
|
-
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
1905
|
-
className
|
|
1906
|
-
),
|
|
1907
|
-
...props
|
|
1908
|
-
}
|
|
1909
|
-
);
|
|
1737
|
+
// src/components/doc/NarrativeBlocks.tsx
|
|
1738
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
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";
|
|
1910
1743
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
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;
|
|
1928
1777
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
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);
|
|
1933
1800
|
}
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
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",
|
|
1823
|
+
{
|
|
1824
|
+
className: cn(
|
|
1825
|
+
"font-mono text-xs",
|
|
1826
|
+
isDir ? "text-muted-foreground" : "text-foreground"
|
|
1827
|
+
),
|
|
1828
|
+
children: isDir ? `${child.name}/` : child.name
|
|
1959
1829
|
}
|
|
1960
|
-
)
|
|
1830
|
+
),
|
|
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
|
|
1961
1833
|
] }),
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
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
|
+
});
|
|
1976
1856
|
}
|
|
1977
|
-
);
|
|
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
|
+
};
|
|
1978
1912
|
|
|
1979
|
-
// src/
|
|
1980
|
-
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
|
+
}
|
|
1981
1935
|
|
|
1982
1936
|
// src/lib/hash-state.ts
|
|
1983
1937
|
var DEFAULT_URL_STATE = {
|
|
@@ -2047,16 +2001,120 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
2047
2001
|
}
|
|
2048
2002
|
}
|
|
2049
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
|
+
|
|
2050
2107
|
// src/interactive/ReportFailureBanner.tsx
|
|
2051
|
-
var
|
|
2108
|
+
var import_react10 = require("react");
|
|
2109
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2052
2110
|
function ReportFailureBanner({ failures }) {
|
|
2053
2111
|
const first = failures[0];
|
|
2054
|
-
const jumpToFirst = (0,
|
|
2112
|
+
const jumpToFirst = (0, import_react10.useCallback)(() => {
|
|
2055
2113
|
if (!first) return;
|
|
2056
2114
|
scrollToScenarioId(first.scenarioId);
|
|
2057
2115
|
}, [first]);
|
|
2058
2116
|
if (failures.length === 0) return null;
|
|
2059
|
-
return /* @__PURE__ */ (0,
|
|
2117
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2060
2118
|
"div",
|
|
2061
2119
|
{
|
|
2062
2120
|
className: "es-failure-banner",
|
|
@@ -2064,13 +2122,13 @@ function ReportFailureBanner({ failures }) {
|
|
|
2064
2122
|
"aria-live": "polite",
|
|
2065
2123
|
"aria-label": "Failure summary",
|
|
2066
2124
|
children: [
|
|
2067
|
-
/* @__PURE__ */ (0,
|
|
2068
|
-
/* @__PURE__ */ (0,
|
|
2125
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "es-failure-banner-text", children: [
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("strong", { children: failures.length }),
|
|
2069
2127
|
" ",
|
|
2070
2128
|
"failure",
|
|
2071
2129
|
failures.length === 1 ? "" : "s"
|
|
2072
2130
|
] }),
|
|
2073
|
-
/* @__PURE__ */ (0,
|
|
2131
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2074
2132
|
"button",
|
|
2075
2133
|
{
|
|
2076
2134
|
type: "button",
|
|
@@ -2085,37 +2143,111 @@ function ReportFailureBanner({ failures }) {
|
|
|
2085
2143
|
);
|
|
2086
2144
|
}
|
|
2087
2145
|
|
|
2088
|
-
// src/interactive/
|
|
2089
|
-
var
|
|
2090
|
-
var
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
"
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2146
|
+
// src/interactive/ReportFilters.tsx
|
|
2147
|
+
var import_react11 = require("react");
|
|
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");
|
|
2223
|
+
function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
2224
|
+
const [now, setNow] = (0, import_react12.useState)(nowMs);
|
|
2225
|
+
(0, import_react12.useEffect)(() => {
|
|
2226
|
+
if (nowMs === void 0) setNow(Date.now());
|
|
2227
|
+
}, [nowMs]);
|
|
2228
|
+
if (lastRunMs === void 0 || now === void 0) return null;
|
|
2229
|
+
const age = formatRelativeAge(lastRunMs, now);
|
|
2230
|
+
const runLink = ciUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2231
|
+
"a",
|
|
2232
|
+
{
|
|
2233
|
+
href: ciUrl,
|
|
2234
|
+
target: "_blank",
|
|
2235
|
+
rel: "noreferrer noopener",
|
|
2236
|
+
className: "underline underline-offset-2",
|
|
2237
|
+
children: "View CI run"
|
|
2238
|
+
}
|
|
2239
|
+
) : null;
|
|
2240
|
+
if (isReportStale(lastRunMs, staleAfterDays, now)) {
|
|
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: [
|
|
2244
|
+
"Last verified ",
|
|
2245
|
+
age,
|
|
2246
|
+
"."
|
|
2247
|
+
] }),
|
|
2248
|
+
" This report may no longer match the current behavior. Look for a newer run."
|
|
2249
|
+
] }),
|
|
2250
|
+
runLink
|
|
2119
2251
|
] });
|
|
2120
2252
|
}
|
|
2121
2253
|
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
|
|
@@ -2177,8 +2309,99 @@ function ReportLastRunDelta({
|
|
|
2177
2309
|
] });
|
|
2178
2310
|
}
|
|
2179
2311
|
|
|
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
|
+
|
|
2180
2403
|
// src/interactive/ReportShortcutsHelp.tsx
|
|
2181
|
-
var
|
|
2404
|
+
var import_react14 = require("react");
|
|
2182
2405
|
|
|
2183
2406
|
// src/components/ui/button.tsx
|
|
2184
2407
|
var React7 = require("react");
|
|
@@ -2230,326 +2453,57 @@ function Button({
|
|
|
2230
2453
|
className: cn(buttonVariants({ variant, size, className })),
|
|
2231
2454
|
...props
|
|
2232
2455
|
}
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2235
|
-
|
|
2236
|
-
// src/interactive/ReportShortcutsHelp.tsx
|
|
2237
|
-
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2238
|
-
var SHORTCUTS = [
|
|
2239
|
-
{ keys: "/", description: "Focus search" },
|
|
2240
|
-
{ keys: "f", description: "Jump to next failure" },
|
|
2241
|
-
{ keys: "Shift F", description: "Jump to previous failure" },
|
|
2242
|
-
{ keys: "e", description: "Expand all" },
|
|
2243
|
-
{ keys: "c", description: "Collapse all" },
|
|
2244
|
-
{ keys: "?", description: "Toggle this help" },
|
|
2245
|
-
{ keys: "Esc", description: "Clear search / close" }
|
|
2246
|
-
];
|
|
2247
|
-
function Keys({ keys }) {
|
|
2248
|
-
const parts = keys.split(" ");
|
|
2249
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
|
|
2250
|
-
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
|
|
2251
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.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 })
|
|
2252
|
-
] }, k)) });
|
|
2253
|
-
}
|
|
2254
|
-
function ReportShortcutsHelp({ open, onClose }) {
|
|
2255
|
-
const dialogRef = (0, import_react11.useRef)(null);
|
|
2256
|
-
(0, import_react11.useEffect)(() => {
|
|
2257
|
-
const dialog = dialogRef.current;
|
|
2258
|
-
if (!dialog) return;
|
|
2259
|
-
if (open && !dialog.open) dialog.showModal();
|
|
2260
|
-
else if (!open && dialog.open) dialog.close();
|
|
2261
|
-
}, [open]);
|
|
2262
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
2263
|
-
"dialog",
|
|
2264
|
-
{
|
|
2265
|
-
ref: dialogRef,
|
|
2266
|
-
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",
|
|
2267
|
-
"aria-label": "Keyboard shortcuts",
|
|
2268
|
-
onClose,
|
|
2269
|
-
children: [
|
|
2270
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
|
|
2271
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.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_runtime44.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
|
|
2272
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Keys, { keys: s.keys }) }),
|
|
2273
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
|
|
2274
|
-
] }, s.keys)) }),
|
|
2275
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
|
|
2276
|
-
]
|
|
2277
|
-
}
|
|
2278
|
-
);
|
|
2279
|
-
}
|
|
2280
|
-
|
|
2281
|
-
// src/interactive/use-keyboard-shortcuts.ts
|
|
2282
|
-
var import_react12 = require("react");
|
|
2283
|
-
function isEditableTarget(target) {
|
|
2284
|
-
if (!(target instanceof HTMLElement)) return false;
|
|
2285
|
-
const tag = target.tagName;
|
|
2286
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
2287
|
-
if (target.isContentEditable) return true;
|
|
2288
|
-
return false;
|
|
2289
|
-
}
|
|
2290
|
-
function useKeyboardShortcuts(handlers) {
|
|
2291
|
-
const ref = (0, import_react12.useRef)(handlers);
|
|
2292
|
-
ref.current = handlers;
|
|
2293
|
-
(0, import_react12.useEffect)(() => {
|
|
2294
|
-
function onKeyDown(e) {
|
|
2295
|
-
const h = ref.current;
|
|
2296
|
-
if (e.key === "Escape") {
|
|
2297
|
-
h.onEscape?.();
|
|
2298
|
-
return;
|
|
2299
|
-
}
|
|
2300
|
-
if (isEditableTarget(e.target)) return;
|
|
2301
|
-
if (e.metaKey || e.ctrlKey || e.altKey) return;
|
|
2302
|
-
switch (e.key) {
|
|
2303
|
-
case "/":
|
|
2304
|
-
h.onFocusSearch?.();
|
|
2305
|
-
e.preventDefault();
|
|
2306
|
-
return;
|
|
2307
|
-
case "?":
|
|
2308
|
-
h.onToggleHelp?.();
|
|
2309
|
-
e.preventDefault();
|
|
2310
|
-
return;
|
|
2311
|
-
case "f":
|
|
2312
|
-
h.onNextFailure?.();
|
|
2313
|
-
e.preventDefault();
|
|
2314
|
-
return;
|
|
2315
|
-
case "F":
|
|
2316
|
-
h.onPrevFailure?.();
|
|
2317
|
-
e.preventDefault();
|
|
2318
|
-
return;
|
|
2319
|
-
case "e":
|
|
2320
|
-
h.onExpandAll?.();
|
|
2321
|
-
e.preventDefault();
|
|
2322
|
-
return;
|
|
2323
|
-
case "c":
|
|
2324
|
-
h.onCollapseAll?.();
|
|
2325
|
-
e.preventDefault();
|
|
2326
|
-
return;
|
|
2327
|
-
default:
|
|
2328
|
-
return;
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
window.addEventListener("keydown", onKeyDown);
|
|
2332
|
-
return () => window.removeEventListener("keydown", onKeyDown);
|
|
2333
|
-
}, []);
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
// src/interactive/use-deep-link-scroll.ts
|
|
2337
|
-
var import_react13 = require("react");
|
|
2338
|
-
function useDeepLinkScroll() {
|
|
2339
|
-
const lastAnchor = (0, import_react13.useRef)(null);
|
|
2340
|
-
(0, import_react13.useEffect)(() => {
|
|
2341
|
-
function scrollToHash() {
|
|
2342
|
-
const { anchor } = splitHash(window.location.hash);
|
|
2343
|
-
if (!anchor || anchor === lastAnchor.current) return;
|
|
2344
|
-
lastAnchor.current = anchor;
|
|
2345
|
-
requestAnimationFrame(() => {
|
|
2346
|
-
scrollToScenarioId(anchor, { updateHash: false });
|
|
2347
|
-
});
|
|
2348
|
-
}
|
|
2349
|
-
scrollToHash();
|
|
2350
|
-
window.addEventListener("hashchange", scrollToHash);
|
|
2351
|
-
return () => window.removeEventListener("hashchange", scrollToHash);
|
|
2352
|
-
}, []);
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2355
|
-
// src/interactive/filter.ts
|
|
2356
|
-
function normalizeQuery(query) {
|
|
2357
|
-
return query.trim().toLowerCase();
|
|
2358
|
-
}
|
|
2359
|
-
function allTags(report) {
|
|
2360
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2361
|
-
for (const feature of report.features) {
|
|
2362
|
-
for (const scenario of feature.scenarios) {
|
|
2363
|
-
for (const tag of scenario.tags) seen.add(tag);
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
return [...seen];
|
|
2367
|
-
}
|
|
2368
|
-
function queryMatches(scenario, q) {
|
|
2369
|
-
if (q === "") return true;
|
|
2370
|
-
if (scenario.title.toLowerCase().includes(q)) return true;
|
|
2371
|
-
for (const tag of scenario.tags) {
|
|
2372
|
-
if (tag.toLowerCase().includes(q)) return true;
|
|
2373
|
-
}
|
|
2374
|
-
for (const step of scenario.steps) {
|
|
2375
|
-
if (step.text.toLowerCase().includes(q)) return true;
|
|
2376
|
-
}
|
|
2377
|
-
return false;
|
|
2378
|
-
}
|
|
2379
|
-
function scenarioMatches(scenario, q, status, tags) {
|
|
2380
|
-
if (status !== "all" && scenario.status !== status) return false;
|
|
2381
|
-
if (tags.length > 0 && !tags.some((t) => scenario.tags.includes(t))) return false;
|
|
2382
|
-
return queryMatches(scenario, q);
|
|
2383
|
-
}
|
|
2384
|
-
function summarizeScenarios(scenarios) {
|
|
2385
|
-
let total = 0, passed = 0, failed = 0, skipped = 0, pending = 0, durationMs = 0;
|
|
2386
|
-
for (const s of scenarios) {
|
|
2387
|
-
total += 1;
|
|
2388
|
-
durationMs += s.durationMs;
|
|
2389
|
-
if (s.status === "passed") passed += 1;
|
|
2390
|
-
else if (s.status === "failed") failed += 1;
|
|
2391
|
-
else if (s.status === "skipped") skipped += 1;
|
|
2392
|
-
else pending += 1;
|
|
2393
|
-
}
|
|
2394
|
-
return { total, passed, failed, skipped, pending, durationMs };
|
|
2395
|
-
}
|
|
2396
|
-
function filterReport(report, criteria) {
|
|
2397
|
-
const c = typeof criteria === "string" ? { query: criteria } : criteria;
|
|
2398
|
-
const q = normalizeQuery(c.query ?? "");
|
|
2399
|
-
const status = c.status ?? "all";
|
|
2400
|
-
const tags = c.tags ?? [];
|
|
2401
|
-
if (q === "" && status === "all" && tags.length === 0) return report;
|
|
2402
|
-
const features = [];
|
|
2403
|
-
let topTotal = 0, topPassed = 0, topFailed = 0, topSkipped = 0, topPending = 0, topDuration = 0;
|
|
2404
|
-
for (const feature of report.features) {
|
|
2405
|
-
const matched = feature.scenarios.filter((s) => scenarioMatches(s, q, status, tags));
|
|
2406
|
-
if (matched.length === 0) continue;
|
|
2407
|
-
const summary = summarizeScenarios(matched);
|
|
2408
|
-
features.push({ ...feature, summary, scenarios: matched });
|
|
2409
|
-
topTotal += summary.total;
|
|
2410
|
-
topPassed += summary.passed;
|
|
2411
|
-
topFailed += summary.failed;
|
|
2412
|
-
topSkipped += summary.skipped;
|
|
2413
|
-
topPending += summary.pending;
|
|
2414
|
-
topDuration += summary.durationMs;
|
|
2415
|
-
}
|
|
2416
|
-
return {
|
|
2417
|
-
...report,
|
|
2418
|
-
summary: {
|
|
2419
|
-
total: topTotal,
|
|
2420
|
-
passed: topPassed,
|
|
2421
|
-
failed: topFailed,
|
|
2422
|
-
skipped: topSkipped,
|
|
2423
|
-
pending: topPending,
|
|
2424
|
-
durationMs: topDuration
|
|
2425
|
-
},
|
|
2426
|
-
features
|
|
2427
|
-
};
|
|
2428
|
-
}
|
|
2429
|
-
function listFailures(report) {
|
|
2430
|
-
const out = [];
|
|
2431
|
-
for (const feature of report.features) {
|
|
2432
|
-
for (const scenario of feature.scenarios) {
|
|
2433
|
-
if (scenario.status === "failed") {
|
|
2434
|
-
const ref = {
|
|
2435
|
-
featureId: feature.id,
|
|
2436
|
-
scenarioId: scenario.id,
|
|
2437
|
-
scenarioTitle: scenario.title
|
|
2438
|
-
};
|
|
2439
|
-
if (scenario.errorMessage !== void 0) {
|
|
2440
|
-
ref.errorMessage = scenario.errorMessage;
|
|
2441
|
-
}
|
|
2442
|
-
out.push(ref);
|
|
2443
|
-
}
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
return out;
|
|
2447
|
-
}
|
|
2448
|
-
|
|
2449
|
-
// src/interactive/triage-default.ts
|
|
2450
|
-
function needsAttention(status) {
|
|
2451
|
-
return status === "failed";
|
|
2452
|
-
}
|
|
2453
|
-
function defaultCollapsedIds(report) {
|
|
2454
|
-
const collapsed = /* @__PURE__ */ new Set();
|
|
2455
|
-
if (report.summary.failed === 0) return collapsed;
|
|
2456
|
-
for (const feature of report.features) {
|
|
2457
|
-
const featureHasFailure = feature.scenarios.some((s) => needsAttention(s.status));
|
|
2458
|
-
if (!featureHasFailure) collapsed.add(feature.id);
|
|
2459
|
-
for (const scenario of feature.scenarios) {
|
|
2460
|
-
if (!needsAttention(scenario.status)) collapsed.add(scenario.id);
|
|
2461
|
-
}
|
|
2462
|
-
}
|
|
2463
|
-
return collapsed;
|
|
2464
|
-
}
|
|
2465
|
-
function failuresFirst(report) {
|
|
2466
|
-
if (report.summary.failed === 0) return report;
|
|
2467
|
-
const withFailures = [];
|
|
2468
|
-
const rest = [];
|
|
2469
|
-
for (const feature of report.features) {
|
|
2470
|
-
(feature.scenarios.some((s) => needsAttention(s.status)) ? withFailures : rest).push(feature);
|
|
2471
|
-
}
|
|
2472
|
-
if (withFailures.length === 0) return report;
|
|
2473
|
-
return { ...report, features: [...withFailures, ...rest] };
|
|
2456
|
+
});
|
|
2474
2457
|
}
|
|
2475
2458
|
|
|
2476
|
-
// src/interactive/
|
|
2477
|
-
var
|
|
2478
|
-
var
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
}) {
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
type: "button",
|
|
2520
|
-
"aria-pressed": active,
|
|
2521
|
-
onClick: () => onToggleTag(tag),
|
|
2522
|
-
className: "cursor-pointer",
|
|
2523
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2524
|
-
Badge,
|
|
2525
|
-
{
|
|
2526
|
-
variant: "tag",
|
|
2527
|
-
className: cn(
|
|
2528
|
-
active && "border-primary bg-primary text-primary-foreground"
|
|
2529
|
-
),
|
|
2530
|
-
children: tag
|
|
2531
|
-
}
|
|
2532
|
-
)
|
|
2533
|
-
}
|
|
2534
|
-
) }, tag);
|
|
2535
|
-
}),
|
|
2536
|
-
tags.length > TAG_LIMIT ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2537
|
-
"button",
|
|
2538
|
-
{
|
|
2539
|
-
type: "button",
|
|
2540
|
-
"aria-expanded": showAllTags,
|
|
2541
|
-
onClick: () => setShowAllTags((v) => !v),
|
|
2542
|
-
className: "cursor-pointer rounded-full px-2 py-0.5 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
2543
|
-
children: tagsCollapsed ? `+${tags.length - TAG_LIMIT} more` : "Show fewer"
|
|
2544
|
-
}
|
|
2545
|
-
) }) : null
|
|
2546
|
-
] }) : null
|
|
2547
|
-
] });
|
|
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
|
+
);
|
|
2548
2502
|
}
|
|
2549
2503
|
|
|
2550
2504
|
// src/interactive/ReportToc.tsx
|
|
2551
2505
|
var import_react15 = require("react");
|
|
2552
|
-
var
|
|
2506
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2553
2507
|
var DOT = {
|
|
2554
2508
|
passed: "bg-pass",
|
|
2555
2509
|
failed: "bg-fail",
|
|
@@ -2578,11 +2532,11 @@ function TocContent({ onNavigate }) {
|
|
|
2578
2532
|
return () => observer.disconnect();
|
|
2579
2533
|
}, [scenarioIds]);
|
|
2580
2534
|
if (report.features.length === 0) return null;
|
|
2581
|
-
return /* @__PURE__ */ (0,
|
|
2582
|
-
/* @__PURE__ */ (0,
|
|
2583
|
-
/* @__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) => {
|
|
2584
2538
|
const active = activeId === scenario.id;
|
|
2585
|
-
return /* @__PURE__ */ (0,
|
|
2539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
2586
2540
|
"a",
|
|
2587
2541
|
{
|
|
2588
2542
|
href: `#${scenario.id}`,
|
|
@@ -2597,14 +2551,14 @@ function TocContent({ onNavigate }) {
|
|
|
2597
2551
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
2598
2552
|
),
|
|
2599
2553
|
children: [
|
|
2600
|
-
/* @__PURE__ */ (0,
|
|
2554
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2601
2555
|
"span",
|
|
2602
2556
|
{
|
|
2603
2557
|
"aria-hidden": true,
|
|
2604
2558
|
className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
|
|
2605
2559
|
}
|
|
2606
2560
|
),
|
|
2607
|
-
/* @__PURE__ */ (0,
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "break-words", children: scenario.title })
|
|
2608
2562
|
]
|
|
2609
2563
|
}
|
|
2610
2564
|
) }, scenario.id);
|
|
@@ -2614,14 +2568,14 @@ function TocContent({ onNavigate }) {
|
|
|
2614
2568
|
function ReportToc() {
|
|
2615
2569
|
const report = useReport();
|
|
2616
2570
|
if (report.features.length === 0) return null;
|
|
2617
|
-
return /* @__PURE__ */ (0,
|
|
2571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
2618
2572
|
"nav",
|
|
2619
2573
|
{
|
|
2620
2574
|
"aria-label": "Table of contents",
|
|
2621
2575
|
className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
|
|
2622
2576
|
children: [
|
|
2623
|
-
/* @__PURE__ */ (0,
|
|
2624
|
-
/* @__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, {})
|
|
2625
2579
|
]
|
|
2626
2580
|
}
|
|
2627
2581
|
);
|
|
@@ -2631,38 +2585,139 @@ function ReportToc() {
|
|
|
2631
2585
|
var import_react16 = require("react");
|
|
2632
2586
|
var import_dialog = require("@base-ui/react/dialog");
|
|
2633
2587
|
var import_lucide_react4 = require("lucide-react");
|
|
2634
|
-
var
|
|
2588
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
2635
2589
|
function ReportTocDrawer() {
|
|
2636
2590
|
const report = useReport();
|
|
2637
2591
|
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
2638
2592
|
if (report.features.length === 0) return null;
|
|
2639
|
-
return /* @__PURE__ */ (0,
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2641
|
-
/* @__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 }),
|
|
2642
2596
|
" Contents"
|
|
2643
2597
|
] }),
|
|
2644
|
-
/* @__PURE__ */ (0,
|
|
2645
|
-
/* @__PURE__ */ (0,
|
|
2646
|
-
/* @__PURE__ */ (0,
|
|
2647
|
-
/* @__PURE__ */ (0,
|
|
2648
|
-
/* @__PURE__ */ (0,
|
|
2649
|
-
/* @__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)(
|
|
2650
2604
|
import_dialog.Dialog.Close,
|
|
2651
2605
|
{
|
|
2652
2606
|
"aria-label": "Close contents",
|
|
2653
2607
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
2654
|
-
children: /* @__PURE__ */ (0,
|
|
2608
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react4.XIcon, { "aria-hidden": true })
|
|
2655
2609
|
}
|
|
2656
2610
|
)
|
|
2657
2611
|
] }),
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(TocContent, { onNavigate: () => setOpen(false) })
|
|
2659
2613
|
] })
|
|
2660
2614
|
] })
|
|
2661
2615
|
] });
|
|
2662
2616
|
}
|
|
2663
2617
|
|
|
2664
|
-
// 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
|
|
2665
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");
|
|
2666
2721
|
var KEY = "es-theme";
|
|
2667
2722
|
function systemDark() {
|
|
2668
2723
|
return typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches;
|
|
@@ -2683,13 +2738,13 @@ function initialPref() {
|
|
|
2683
2738
|
return "system";
|
|
2684
2739
|
}
|
|
2685
2740
|
function useTheme() {
|
|
2686
|
-
const [pref, setPrefState] = (0,
|
|
2687
|
-
(0,
|
|
2741
|
+
const [pref, setPrefState] = (0, import_react19.useState)("system");
|
|
2742
|
+
(0, import_react19.useEffect)(() => {
|
|
2688
2743
|
const p = initialPref();
|
|
2689
2744
|
setPrefState(p);
|
|
2690
2745
|
applyTheme(p);
|
|
2691
2746
|
}, []);
|
|
2692
|
-
(0,
|
|
2747
|
+
(0, import_react19.useEffect)(() => {
|
|
2693
2748
|
if (typeof matchMedia === "undefined") return;
|
|
2694
2749
|
const mq = matchMedia("(prefers-color-scheme: dark)");
|
|
2695
2750
|
const onChange = () => {
|
|
@@ -2698,7 +2753,7 @@ function useTheme() {
|
|
|
2698
2753
|
mq.addEventListener("change", onChange);
|
|
2699
2754
|
return () => mq.removeEventListener("change", onChange);
|
|
2700
2755
|
}, [pref]);
|
|
2701
|
-
const setPref = (0,
|
|
2756
|
+
const setPref = (0, import_react19.useCallback)((p) => {
|
|
2702
2757
|
setPrefState(p);
|
|
2703
2758
|
applyTheme(p);
|
|
2704
2759
|
try {
|
|
@@ -2710,19 +2765,19 @@ function useTheme() {
|
|
|
2710
2765
|
}
|
|
2711
2766
|
|
|
2712
2767
|
// src/interactive/use-url-state.ts
|
|
2713
|
-
var
|
|
2768
|
+
var import_react20 = require("react");
|
|
2714
2769
|
var WRITE_DELAY_MS = 250;
|
|
2715
2770
|
function useUrlState() {
|
|
2716
|
-
const [state, setState] = (0,
|
|
2717
|
-
const ready = (0,
|
|
2718
|
-
(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)(() => {
|
|
2719
2774
|
const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
|
|
2720
2775
|
read();
|
|
2721
2776
|
ready.current = true;
|
|
2722
2777
|
window.addEventListener("hashchange", read);
|
|
2723
2778
|
return () => window.removeEventListener("hashchange", read);
|
|
2724
2779
|
}, []);
|
|
2725
|
-
(0,
|
|
2780
|
+
(0, import_react20.useEffect)(() => {
|
|
2726
2781
|
if (!ready.current) return;
|
|
2727
2782
|
const timer = setTimeout(
|
|
2728
2783
|
() => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
|
|
@@ -2730,7 +2785,7 @@ function useUrlState() {
|
|
|
2730
2785
|
);
|
|
2731
2786
|
return () => clearTimeout(timer);
|
|
2732
2787
|
}, [state]);
|
|
2733
|
-
const update = (0,
|
|
2788
|
+
const update = (0, import_react20.useCallback)(
|
|
2734
2789
|
(patch) => setState((prev) => ({ ...prev, ...patch })),
|
|
2735
2790
|
[]
|
|
2736
2791
|
);
|
|
@@ -2738,22 +2793,33 @@ function useUrlState() {
|
|
|
2738
2793
|
}
|
|
2739
2794
|
|
|
2740
2795
|
// src/interactive/ReportInteractive.tsx
|
|
2741
|
-
var
|
|
2796
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2742
2797
|
function ReportInteractive(props) {
|
|
2743
2798
|
const { report, className, title, dataTheme } = props;
|
|
2744
2799
|
const result = unwrapReport(report);
|
|
2745
2800
|
if (!result.ok) {
|
|
2746
|
-
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
|
+
);
|
|
2747
2810
|
}
|
|
2748
|
-
return /* @__PURE__ */ (0,
|
|
2811
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ReportInteractiveView, { ...props, report: result.data });
|
|
2749
2812
|
}
|
|
2750
2813
|
var THEME_OPTIONS = [
|
|
2751
2814
|
{ value: "light", label: "Light", Icon: import_lucide_react5.Sun },
|
|
2752
2815
|
{ value: "system", label: "System", Icon: import_lucide_react5.Monitor },
|
|
2753
2816
|
{ value: "dark", label: "Dark", Icon: import_lucide_react5.Moon }
|
|
2754
2817
|
];
|
|
2755
|
-
function ThemeSegment({
|
|
2756
|
-
|
|
2818
|
+
function ThemeSegment({
|
|
2819
|
+
pref,
|
|
2820
|
+
onPref
|
|
2821
|
+
}) {
|
|
2822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2757
2823
|
ToggleGroup,
|
|
2758
2824
|
{
|
|
2759
2825
|
size: "sm",
|
|
@@ -2763,7 +2829,17 @@ function ThemeSegment({ pref, onPref }) {
|
|
|
2763
2829
|
if (next) onPref(next);
|
|
2764
2830
|
},
|
|
2765
2831
|
"aria-label": "Color theme",
|
|
2766
|
-
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
|
+
))
|
|
2767
2843
|
}
|
|
2768
2844
|
);
|
|
2769
2845
|
}
|
|
@@ -2781,63 +2857,79 @@ function ReportInteractiveView({
|
|
|
2781
2857
|
}) {
|
|
2782
2858
|
const [urlState, setUrlState] = useUrlState();
|
|
2783
2859
|
const { query, status: statusFilter, tags: activeTags, detail } = urlState;
|
|
2784
|
-
const setQuery = (0,
|
|
2785
|
-
|
|
2860
|
+
const setQuery = (0, import_react21.useCallback)(
|
|
2861
|
+
(next) => setUrlState({ query: next }),
|
|
2862
|
+
[setUrlState]
|
|
2863
|
+
);
|
|
2864
|
+
const setStatusFilter = (0, import_react21.useCallback)(
|
|
2786
2865
|
(next) => setUrlState({ status: next }),
|
|
2787
2866
|
[setUrlState]
|
|
2788
2867
|
);
|
|
2789
|
-
const setDetail = (0,
|
|
2868
|
+
const setDetail = (0, import_react21.useCallback)(
|
|
2790
2869
|
(next) => setUrlState({ detail: next }),
|
|
2791
2870
|
[setUrlState]
|
|
2792
2871
|
);
|
|
2793
|
-
const [helpOpen, setHelpOpen] = (0,
|
|
2872
|
+
const [helpOpen, setHelpOpen] = (0, import_react21.useState)(false);
|
|
2794
2873
|
const { pref: themePref, setPref: setThemePref } = useTheme();
|
|
2795
|
-
const searchRef = (0,
|
|
2796
|
-
const expandId = (0,
|
|
2797
|
-
const docsId = (0,
|
|
2798
|
-
const deferredQuery = (0,
|
|
2799
|
-
const failures = (0,
|
|
2800
|
-
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)(
|
|
2801
2880
|
// Features carrying failures float to the top so the first thing on screen
|
|
2802
2881
|
// is the thing that needs attention; an all-green run keeps source order.
|
|
2803
|
-
() => failuresFirst(
|
|
2882
|
+
() => failuresFirst(
|
|
2883
|
+
filterReport(report, {
|
|
2884
|
+
query: deferredQuery,
|
|
2885
|
+
status: statusFilter,
|
|
2886
|
+
tags: activeTags
|
|
2887
|
+
})
|
|
2888
|
+
),
|
|
2804
2889
|
[report, deferredQuery, statusFilter, activeTags]
|
|
2805
2890
|
);
|
|
2806
2891
|
const isFiltering = query !== deferredQuery;
|
|
2807
|
-
const statusOptions = (0,
|
|
2892
|
+
const statusOptions = (0, import_react21.useMemo)(() => {
|
|
2808
2893
|
const s = report.summary;
|
|
2809
2894
|
const opts = [
|
|
2810
2895
|
{ key: "all", label: "All", count: s.total }
|
|
2811
2896
|
];
|
|
2812
|
-
if (s.passed)
|
|
2813
|
-
|
|
2814
|
-
if (s.
|
|
2815
|
-
|
|
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 });
|
|
2816
2905
|
return opts;
|
|
2817
2906
|
}, [report]);
|
|
2818
|
-
const tagOptions = (0,
|
|
2819
|
-
const toggleTag = (0,
|
|
2907
|
+
const tagOptions = (0, import_react21.useMemo)(() => allTags(report), [report]);
|
|
2908
|
+
const toggleTag = (0, import_react21.useCallback)(
|
|
2820
2909
|
(tag) => setUrlState({
|
|
2821
2910
|
tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
|
|
2822
2911
|
}),
|
|
2823
2912
|
[activeTags, setUrlState]
|
|
2824
2913
|
);
|
|
2825
|
-
const [toast, setToast] = (0,
|
|
2826
|
-
const toastTimer = (0,
|
|
2827
|
-
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) => {
|
|
2828
2917
|
setToast(msg);
|
|
2829
2918
|
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
2830
2919
|
toastTimer.current = setTimeout(() => setToast(null), 1800);
|
|
2831
2920
|
}, []);
|
|
2832
|
-
const copy = (0,
|
|
2921
|
+
const copy = (0, import_react21.useCallback)(
|
|
2833
2922
|
(text, msg) => {
|
|
2834
2923
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
2835
|
-
navigator.clipboard.writeText(text).then(
|
|
2924
|
+
navigator.clipboard.writeText(text).then(
|
|
2925
|
+
() => showToast(msg),
|
|
2926
|
+
() => showToast("Copy failed")
|
|
2927
|
+
);
|
|
2836
2928
|
}
|
|
2837
2929
|
},
|
|
2838
2930
|
[showToast]
|
|
2839
2931
|
);
|
|
2840
|
-
const scenarioActions = (0,
|
|
2932
|
+
const scenarioActions = (0, import_react21.useMemo)(
|
|
2841
2933
|
() => ({
|
|
2842
2934
|
copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
|
|
2843
2935
|
copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
|
|
@@ -2845,15 +2937,27 @@ function ReportInteractiveView({
|
|
|
2845
2937
|
}),
|
|
2846
2938
|
[copy]
|
|
2847
2939
|
);
|
|
2848
|
-
const failureIndexRef = (0,
|
|
2849
|
-
const allCollapsibleIds = (0,
|
|
2940
|
+
const failureIndexRef = (0, import_react21.useRef)(0);
|
|
2941
|
+
const allCollapsibleIds = (0, import_react21.useMemo)(
|
|
2850
2942
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
2851
2943
|
[report]
|
|
2852
2944
|
);
|
|
2853
|
-
const collapse = useCollapseState(
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
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)(
|
|
2857
2961
|
(expanded) => {
|
|
2858
2962
|
if (expanded) collapse.expandAll();
|
|
2859
2963
|
else collapseAll();
|
|
@@ -2861,10 +2965,10 @@ function ReportInteractiveView({
|
|
|
2861
2965
|
},
|
|
2862
2966
|
[collapse, collapseAll]
|
|
2863
2967
|
);
|
|
2864
|
-
const focusSearch = (0,
|
|
2968
|
+
const focusSearch = (0, import_react21.useCallback)(() => {
|
|
2865
2969
|
searchRef.current?.focus();
|
|
2866
2970
|
}, []);
|
|
2867
|
-
const stepFailure = (0,
|
|
2971
|
+
const stepFailure = (0, import_react21.useCallback)(
|
|
2868
2972
|
(direction) => {
|
|
2869
2973
|
if (failures.length === 0) return;
|
|
2870
2974
|
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
@@ -2873,13 +2977,13 @@ function ReportInteractiveView({
|
|
|
2873
2977
|
},
|
|
2874
2978
|
[failures]
|
|
2875
2979
|
);
|
|
2876
|
-
const toggleHelp = (0,
|
|
2980
|
+
const toggleHelp = (0, import_react21.useCallback)(() => {
|
|
2877
2981
|
setHelpOpen((v) => !v);
|
|
2878
2982
|
}, []);
|
|
2879
|
-
const escape = (0,
|
|
2983
|
+
const escape = (0, import_react21.useCallback)(() => {
|
|
2880
2984
|
if (helpOpen) setHelpOpen(false);
|
|
2881
2985
|
else if (query !== "") setQuery("");
|
|
2882
|
-
}, [helpOpen, query]);
|
|
2986
|
+
}, [helpOpen, query, setQuery]);
|
|
2883
2987
|
useKeyboardShortcuts({
|
|
2884
2988
|
onFocusSearch: focusSearch,
|
|
2885
2989
|
onNextFailure: () => stepFailure(1),
|
|
@@ -2894,13 +2998,13 @@ function ReportInteractiveView({
|
|
|
2894
2998
|
const totalScenarios = report.summary.total;
|
|
2895
2999
|
const matchedScenarios = filtered.summary.total;
|
|
2896
3000
|
const showToc = !hideToc && totalScenarios > 3;
|
|
2897
|
-
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)(
|
|
2898
3002
|
ReportRoot,
|
|
2899
3003
|
{
|
|
2900
3004
|
report: filtered,
|
|
2901
3005
|
customRenderers,
|
|
2902
3006
|
renderers,
|
|
2903
|
-
children: /* @__PURE__ */ (0,
|
|
3007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
2904
3008
|
"main",
|
|
2905
3009
|
{
|
|
2906
3010
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -2909,8 +3013,8 @@ function ReportInteractiveView({
|
|
|
2909
3013
|
"data-theme": dataTheme,
|
|
2910
3014
|
"data-detail-level": detail,
|
|
2911
3015
|
children: [
|
|
2912
|
-
/* @__PURE__ */ (0,
|
|
2913
|
-
/* @__PURE__ */ (0,
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("header", { className: "es-report-header", children: [
|
|
3017
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
2914
3018
|
"div",
|
|
2915
3019
|
{
|
|
2916
3020
|
"data-es-navbar": true,
|
|
@@ -2919,9 +3023,9 @@ function ReportInteractiveView({
|
|
|
2919
3023
|
hideHeader ? "justify-end" : "justify-between"
|
|
2920
3024
|
),
|
|
2921
3025
|
children: [
|
|
2922
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
2923
|
-
/* @__PURE__ */ (0,
|
|
2924
|
-
/* @__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)(
|
|
2925
3029
|
ReportSearch,
|
|
2926
3030
|
{
|
|
2927
3031
|
ref: searchRef,
|
|
@@ -2931,16 +3035,16 @@ function ReportInteractiveView({
|
|
|
2931
3035
|
totalCount: totalScenarios
|
|
2932
3036
|
}
|
|
2933
3037
|
),
|
|
2934
|
-
/* @__PURE__ */ (0,
|
|
3038
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
|
|
2935
3039
|
] })
|
|
2936
3040
|
]
|
|
2937
3041
|
}
|
|
2938
3042
|
),
|
|
2939
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
2940
|
-
/* @__PURE__ */ (0,
|
|
2941
|
-
/* @__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, {})
|
|
2942
3046
|
] }),
|
|
2943
|
-
/* @__PURE__ */ (0,
|
|
3047
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2944
3048
|
ReportFreshness,
|
|
2945
3049
|
{
|
|
2946
3050
|
lastRunMs: reportLastRunMs(report),
|
|
@@ -2948,8 +3052,14 @@ function ReportInteractiveView({
|
|
|
2948
3052
|
staleAfterDays
|
|
2949
3053
|
}
|
|
2950
3054
|
),
|
|
2951
|
-
/* @__PURE__ */ (0,
|
|
2952
|
-
|
|
3055
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3056
|
+
ReportLastRunDelta,
|
|
3057
|
+
{
|
|
3058
|
+
history: scenarioHistory,
|
|
3059
|
+
report
|
|
3060
|
+
}
|
|
3061
|
+
),
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2953
3063
|
ReportFilters,
|
|
2954
3064
|
{
|
|
2955
3065
|
statuses: statusOptions,
|
|
@@ -2960,14 +3070,28 @@ function ReportInteractiveView({
|
|
|
2960
3070
|
onToggleTag: toggleTag
|
|
2961
3071
|
}
|
|
2962
3072
|
),
|
|
2963
|
-
/* @__PURE__ */ (0,
|
|
2964
|
-
showToc ? /* @__PURE__ */ (0,
|
|
2965
|
-
/* @__PURE__ */ (0,
|
|
2966
|
-
/* @__PURE__ */ (0,
|
|
2967
|
-
|
|
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
|
+
)
|
|
2968
3092
|
] }),
|
|
2969
|
-
/* @__PURE__ */ (0,
|
|
2970
|
-
/* @__PURE__ */ (0,
|
|
3093
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3094
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2971
3095
|
Switch,
|
|
2972
3096
|
{
|
|
2973
3097
|
id: docsId,
|
|
@@ -2975,16 +3099,28 @@ function ReportInteractiveView({
|
|
|
2975
3099
|
onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
|
|
2976
3100
|
}
|
|
2977
3101
|
),
|
|
2978
|
-
/* @__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
|
+
)
|
|
2979
3110
|
] })
|
|
2980
3111
|
] })
|
|
2981
3112
|
] }),
|
|
2982
|
-
/* @__PURE__ */ (0,
|
|
2983
|
-
hasContent ? showToc ? /* @__PURE__ */ (0,
|
|
2984
|
-
/* @__PURE__ */ (0,
|
|
2985
|
-
/* @__PURE__ */ (0,
|
|
2986
|
-
] }) : /* @__PURE__ */ (0,
|
|
2987
|
-
|
|
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)(
|
|
2988
3124
|
"button",
|
|
2989
3125
|
{
|
|
2990
3126
|
type: "button",
|
|
@@ -2995,8 +3131,14 @@ function ReportInteractiveView({
|
|
|
2995
3131
|
children: "?"
|
|
2996
3132
|
}
|
|
2997
3133
|
),
|
|
2998
|
-
/* @__PURE__ */ (0,
|
|
2999
|
-
|
|
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)(
|
|
3000
3142
|
"div",
|
|
3001
3143
|
{
|
|
3002
3144
|
role: "status",
|
|
@@ -3009,18 +3151,18 @@ function ReportInteractiveView({
|
|
|
3009
3151
|
}
|
|
3010
3152
|
)
|
|
3011
3153
|
}
|
|
3012
|
-
) }) }) });
|
|
3154
|
+
) }) }) }) });
|
|
3013
3155
|
}
|
|
3014
3156
|
|
|
3015
3157
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
3016
|
-
var
|
|
3158
|
+
var import_react24 = require("react");
|
|
3017
3159
|
|
|
3018
3160
|
// src/interactive/island-renderers.tsx
|
|
3019
|
-
var
|
|
3161
|
+
var import_react23 = require("react");
|
|
3020
3162
|
|
|
3021
3163
|
// src/components/doc/MermaidView.tsx
|
|
3022
|
-
var
|
|
3023
|
-
var
|
|
3164
|
+
var import_react22 = require("react");
|
|
3165
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
3024
3166
|
function prefersDark(el) {
|
|
3025
3167
|
const themed = el?.closest("[data-theme]");
|
|
3026
3168
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -3028,10 +3170,10 @@ function prefersDark(el) {
|
|
|
3028
3170
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
3029
3171
|
}
|
|
3030
3172
|
function MermaidView({ entry, load }) {
|
|
3031
|
-
const ref = (0,
|
|
3032
|
-
const [svg, setSvg] = (0,
|
|
3033
|
-
const [error, setError] = (0,
|
|
3034
|
-
(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)(() => {
|
|
3035
3177
|
let cancelled = false;
|
|
3036
3178
|
const draw = async () => {
|
|
3037
3179
|
let mermaid;
|
|
@@ -3074,9 +3216,9 @@ function MermaidView({ entry, load }) {
|
|
|
3074
3216
|
};
|
|
3075
3217
|
}, [entry.code, load]);
|
|
3076
3218
|
if (svg) {
|
|
3077
|
-
return /* @__PURE__ */ (0,
|
|
3078
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
3079
|
-
/* @__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)(
|
|
3080
3222
|
"div",
|
|
3081
3223
|
{
|
|
3082
3224
|
role: "img",
|
|
@@ -3087,8 +3229,8 @@ function MermaidView({ entry, load }) {
|
|
|
3087
3229
|
)
|
|
3088
3230
|
] });
|
|
3089
3231
|
}
|
|
3090
|
-
return /* @__PURE__ */ (0,
|
|
3091
|
-
error ? /* @__PURE__ */ (0,
|
|
3232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
|
|
3233
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
|
|
3092
3234
|
"p",
|
|
3093
3235
|
{
|
|
3094
3236
|
role: "alert",
|
|
@@ -3100,12 +3242,12 @@ function MermaidView({ entry, load }) {
|
|
|
3100
3242
|
]
|
|
3101
3243
|
}
|
|
3102
3244
|
) : null,
|
|
3103
|
-
/* @__PURE__ */ (0,
|
|
3245
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MermaidSource, { entry, ref })
|
|
3104
3246
|
] });
|
|
3105
3247
|
}
|
|
3106
3248
|
|
|
3107
3249
|
// src/interactive/island-renderers.tsx
|
|
3108
|
-
var
|
|
3250
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
3109
3251
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
3110
3252
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
3111
3253
|
function cachedCdnModule(url) {
|
|
@@ -3122,8 +3264,8 @@ function cachedCdnModule(url) {
|
|
|
3122
3264
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
3123
3265
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
3124
3266
|
function CdnHighlightedCode({ entry }) {
|
|
3125
|
-
const [html, setHtml] = (0,
|
|
3126
|
-
(0,
|
|
3267
|
+
const [html, setHtml] = (0, import_react23.useState)(null);
|
|
3268
|
+
(0, import_react23.useEffect)(() => {
|
|
3127
3269
|
let cancelled = false;
|
|
3128
3270
|
void (async () => {
|
|
3129
3271
|
try {
|
|
@@ -3139,27 +3281,27 @@ function CdnHighlightedCode({ entry }) {
|
|
|
3139
3281
|
cancelled = true;
|
|
3140
3282
|
};
|
|
3141
3283
|
}, [entry.content, entry.lang]);
|
|
3142
|
-
return /* @__PURE__ */ (0,
|
|
3284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
3143
3285
|
"code",
|
|
3144
3286
|
{
|
|
3145
3287
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
3146
3288
|
dangerouslySetInnerHTML: { __html: html }
|
|
3147
3289
|
}
|
|
3148
|
-
) : /* @__PURE__ */ (0,
|
|
3290
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
3149
3291
|
}
|
|
3150
3292
|
function buildIslandRenderers(opts) {
|
|
3151
3293
|
const renderers = {};
|
|
3152
3294
|
if (opts.mermaid) {
|
|
3153
|
-
renderers.mermaid = (entry) => (0,
|
|
3295
|
+
renderers.mermaid = (entry) => (0, import_react23.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
|
|
3154
3296
|
}
|
|
3155
3297
|
if (opts.syntaxHighlighting) {
|
|
3156
|
-
renderers.code = (entry) => (0,
|
|
3298
|
+
renderers.code = (entry) => (0, import_react23.createElement)(CdnHighlightedCode, { entry });
|
|
3157
3299
|
}
|
|
3158
3300
|
return renderers;
|
|
3159
3301
|
}
|
|
3160
3302
|
|
|
3161
3303
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
3162
|
-
var
|
|
3304
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
3163
3305
|
function ReportInteractiveIsland({
|
|
3164
3306
|
report,
|
|
3165
3307
|
title,
|
|
@@ -3169,11 +3311,11 @@ function ReportInteractiveIsland({
|
|
|
3169
3311
|
syntaxHighlighting = true,
|
|
3170
3312
|
mermaid = true
|
|
3171
3313
|
}) {
|
|
3172
|
-
const renderers = (0,
|
|
3314
|
+
const renderers = (0, import_react24.useMemo)(
|
|
3173
3315
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
3174
3316
|
[syntaxHighlighting, mermaid]
|
|
3175
3317
|
);
|
|
3176
|
-
return /* @__PURE__ */ (0,
|
|
3318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3177
3319
|
ReportInteractive,
|
|
3178
3320
|
{
|
|
3179
3321
|
report,
|