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