executable-stories-react 0.13.0 → 0.15.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 +73 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +73 -52
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +485 -227
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +7 -1
- package/dist/interactive.d.ts +7 -1
- package/dist/interactive.js +443 -185
- package/dist/interactive.js.map +1 -1
- package/dist/report-island.global.js +43 -41
- package/dist/ssr.cjs +78 -54
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +4 -0
- package/dist/ssr.d.ts +4 -0
- package/dist/ssr.js +78 -54
- package/dist/ssr.js.map +1 -1
- package/dist/styles.css +1 -3
- package/dist/tailwind.css +1 -1
- package/package.json +2 -2
package/dist/interactive.cjs
CHANGED
|
@@ -47,9 +47,9 @@ __export(interactive_exports, {
|
|
|
47
47
|
module.exports = __toCommonJS(interactive_exports);
|
|
48
48
|
|
|
49
49
|
// src/interactive/ReportInteractive.tsx
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
var
|
|
50
|
+
var import_lucide_react7 = require("lucide-react");
|
|
51
|
+
var import_react23 = require("webmcpable/react");
|
|
52
|
+
var import_react24 = require("react");
|
|
53
53
|
|
|
54
54
|
// src/components/ui/empty.tsx
|
|
55
55
|
var import_class_variance_authority = require("class-variance-authority");
|
|
@@ -242,7 +242,7 @@ function formatValue(value) {
|
|
|
242
242
|
function DocKv({ entry }) {
|
|
243
243
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
|
|
244
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) })
|
|
245
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("dd", { className: "min-w-0 break-words font-mono text-foreground", children: formatValue(entry.value) })
|
|
246
246
|
] });
|
|
247
247
|
}
|
|
248
248
|
|
|
@@ -450,7 +450,7 @@ function DocHtml({ entry }) {
|
|
|
450
450
|
loading: "lazy",
|
|
451
451
|
title,
|
|
452
452
|
className: "block w-full border-0",
|
|
453
|
-
style: { height }
|
|
453
|
+
style: { height, colorScheme: "light", background: "#fff" }
|
|
454
454
|
}
|
|
455
455
|
)
|
|
456
456
|
] });
|
|
@@ -1174,6 +1174,24 @@ function scenarioToPrompt(scenario) {
|
|
|
1174
1174
|
}
|
|
1175
1175
|
return lines.join("\n");
|
|
1176
1176
|
}
|
|
1177
|
+
function failuresToPrompt(scenarios) {
|
|
1178
|
+
if (scenarios.length === 0) return "";
|
|
1179
|
+
const noun = scenarios.length === 1 ? "test scenario is" : "test scenarios are";
|
|
1180
|
+
const lines = [
|
|
1181
|
+
`${scenarios.length} ${noun} failing. Investigate the likely cause (they may share one) and propose a fix.`
|
|
1182
|
+
];
|
|
1183
|
+
scenarios.forEach((scenario, i) => {
|
|
1184
|
+
lines.push("", `${i + 1}. ${scenario.title}`, "Steps:");
|
|
1185
|
+
for (const step of scenario.steps) {
|
|
1186
|
+
const mark = step.status === "failed" ? " \u27F5 failed here" : "";
|
|
1187
|
+
lines.push(` ${step.keyword} ${step.text}${mark}`);
|
|
1188
|
+
}
|
|
1189
|
+
if (scenario.errorMessage) {
|
|
1190
|
+
lines.push("Error:", scenario.errorMessage.trim());
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
return lines.join("\n");
|
|
1194
|
+
}
|
|
1177
1195
|
function scenarioPermalink(scenario) {
|
|
1178
1196
|
if (typeof location === "undefined") return `#${scenario.id}`;
|
|
1179
1197
|
return `${location.href.split("#")[0]}#${scenario.id}`;
|
|
@@ -1299,57 +1317,68 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1299
1317
|
className: "gap-0 py-0",
|
|
1300
1318
|
children: [
|
|
1301
1319
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(CardHeader, { className: "px-4 py-3", children: [
|
|
1302
|
-
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1321
|
+
"div",
|
|
1322
|
+
{
|
|
1323
|
+
className: cn(
|
|
1324
|
+
// Wraps rather than squeezing: on a phone the status/duration group
|
|
1325
|
+
// is ~160px, which leaves a title column a few characters wide.
|
|
1326
|
+
"flex flex-wrap items-start gap-x-4 gap-y-1",
|
|
1327
|
+
hideTitle ? "justify-end" : "justify-between"
|
|
1328
|
+
),
|
|
1329
|
+
children: [
|
|
1330
|
+
hideTitle ? null : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("h3", { id: titleId, className: "flex min-w-0 flex-1 basis-48 items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1331
|
+
collapsible ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1332
|
+
"button",
|
|
1333
|
+
{
|
|
1334
|
+
type: "button",
|
|
1335
|
+
onClick: () => collapse.toggle(scenario.id),
|
|
1336
|
+
"aria-expanded": !collapsed,
|
|
1337
|
+
"aria-controls": bodyId,
|
|
1338
|
+
"aria-label": `Toggle ${scenario.title}`,
|
|
1339
|
+
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
1340
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
1341
|
+
}
|
|
1342
|
+
) : null,
|
|
1343
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: scenario.title })
|
|
1345
|
+
] }),
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1347
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1348
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ScenarioStaleness, { scenario }),
|
|
1349
|
+
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,
|
|
1350
|
+
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}` }),
|
|
1351
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenu, { children: [
|
|
1352
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1353
|
+
DropdownMenuTrigger,
|
|
1354
|
+
{
|
|
1355
|
+
"aria-label": `Actions for ${scenario.title}`,
|
|
1356
|
+
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1357
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.MoreHorizontalIcon, { "aria-hidden": true })
|
|
1358
|
+
}
|
|
1359
|
+
),
|
|
1360
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuContent, { align: "end", children: [
|
|
1361
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
|
|
1362
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.LinkIcon, {}),
|
|
1363
|
+
" Copy link"
|
|
1364
|
+
] }),
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.FileTextIcon, {}),
|
|
1367
|
+
" Copy as Markdown"
|
|
1368
|
+
] }),
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
|
|
1370
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react2.SparklesIcon, {}),
|
|
1371
|
+
" Explain with AI"
|
|
1372
|
+
] })
|
|
1373
|
+
] })
|
|
1374
|
+
] }) : null
|
|
1346
1375
|
] })
|
|
1347
|
-
]
|
|
1348
|
-
|
|
1349
|
-
|
|
1376
|
+
]
|
|
1377
|
+
}
|
|
1378
|
+
),
|
|
1350
1379
|
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
1380
|
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}`))
|
|
1381
|
+
(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", className: "inline-flex min-h-6 items-center", 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}`))
|
|
1353
1382
|
] }) : null
|
|
1354
1383
|
] }),
|
|
1355
1384
|
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
@@ -1492,6 +1521,7 @@ function ReportFeatureList() {
|
|
|
1492
1521
|
|
|
1493
1522
|
// src/components/ReportMeta.tsx
|
|
1494
1523
|
var import_duration4 = require("executable-stories-core/utils/duration");
|
|
1524
|
+
var import_report_queries = require("executable-stories-core/report-queries");
|
|
1495
1525
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1496
1526
|
function ReportMeta() {
|
|
1497
1527
|
const report = useReport();
|
|
@@ -1535,7 +1565,9 @@ function ReportMeta() {
|
|
|
1535
1565
|
value: url ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1536
1566
|
});
|
|
1537
1567
|
}
|
|
1538
|
-
|
|
1568
|
+
const slowest = (0, import_report_queries.slowestScenarios)(report, 5);
|
|
1569
|
+
const showSlowest = slowest.length >= 3;
|
|
1570
|
+
if (items.length === 0 && !showSlowest) return null;
|
|
1539
1571
|
return (
|
|
1540
1572
|
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1541
1573
|
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
@@ -1548,7 +1580,14 @@ function ReportMeta() {
|
|
|
1548
1580
|
/* @__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
1581
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dt", { className: "font-medium", children: item.label }),
|
|
1550
1582
|
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1551
|
-
] }, item.label)) })
|
|
1583
|
+
] }, item.label)) }),
|
|
1584
|
+
showSlowest ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "border-t border-border px-4 py-3", children: [
|
|
1585
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "mb-1.5 font-medium text-muted-foreground", children: "Slowest scenarios" }),
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ol", { className: "flex flex-col gap-1", "aria-label": "Slowest scenarios", children: slowest.map((s) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("li", { className: "flex items-baseline justify-between gap-4", children: [
|
|
1587
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href: `#${s.id}`, className: "min-w-0 text-primary underline underline-offset-2", children: s.title }),
|
|
1588
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "shrink-0 font-mono tabular-nums text-muted-foreground", children: (0, import_duration4.formatDuration)(s.durationMs) })
|
|
1589
|
+
] }, s.id)) })
|
|
1590
|
+
] }) : null
|
|
1552
1591
|
] })
|
|
1553
1592
|
);
|
|
1554
1593
|
}
|
|
@@ -2024,21 +2063,26 @@ function allTags(report) {
|
|
|
2024
2063
|
}
|
|
2025
2064
|
return [...seen];
|
|
2026
2065
|
}
|
|
2027
|
-
function queryMatches(scenario, q) {
|
|
2066
|
+
function queryMatches(scenario, q, sourceFile) {
|
|
2028
2067
|
if (q === "") return true;
|
|
2029
2068
|
if (scenario.title.toLowerCase().includes(q)) return true;
|
|
2069
|
+
if (sourceFile?.toLowerCase().includes(q)) return true;
|
|
2070
|
+
if (scenario.errorMessage?.toLowerCase().includes(q)) return true;
|
|
2030
2071
|
for (const tag of scenario.tags) {
|
|
2031
2072
|
if (tag.toLowerCase().includes(q)) return true;
|
|
2032
2073
|
}
|
|
2074
|
+
for (const ticket of scenario.tickets ?? []) {
|
|
2075
|
+
if (ticket.id.toLowerCase().includes(q)) return true;
|
|
2076
|
+
}
|
|
2033
2077
|
for (const step of scenario.steps) {
|
|
2034
2078
|
if (step.text.toLowerCase().includes(q)) return true;
|
|
2035
2079
|
}
|
|
2036
2080
|
return false;
|
|
2037
2081
|
}
|
|
2038
|
-
function scenarioMatches(scenario, q, status, tags) {
|
|
2082
|
+
function scenarioMatches(scenario, q, status, tags, sourceFile) {
|
|
2039
2083
|
if (status !== "all" && scenario.status !== status) return false;
|
|
2040
2084
|
if (tags.length > 0 && !tags.some((t) => scenario.tags.includes(t))) return false;
|
|
2041
|
-
return queryMatches(scenario, q);
|
|
2085
|
+
return queryMatches(scenario, q, sourceFile);
|
|
2042
2086
|
}
|
|
2043
2087
|
function summarizeScenarios(scenarios) {
|
|
2044
2088
|
let total = 0, passed = 0, failed = 0, skipped = 0, pending = 0, durationMs = 0;
|
|
@@ -2061,7 +2105,9 @@ function filterReport(report, criteria) {
|
|
|
2061
2105
|
const features = [];
|
|
2062
2106
|
let topTotal = 0, topPassed = 0, topFailed = 0, topSkipped = 0, topPending = 0, topDuration = 0;
|
|
2063
2107
|
for (const feature of report.features) {
|
|
2064
|
-
const matched = feature.scenarios.filter(
|
|
2108
|
+
const matched = feature.scenarios.filter(
|
|
2109
|
+
(s) => scenarioMatches(s, q, status, tags, feature.sourceFile)
|
|
2110
|
+
);
|
|
2065
2111
|
if (matched.length === 0) continue;
|
|
2066
2112
|
const summary = summarizeScenarios(matched);
|
|
2067
2113
|
features.push({ ...feature, summary, scenarios: matched });
|
|
@@ -2085,6 +2131,9 @@ function filterReport(report, criteria) {
|
|
|
2085
2131
|
features
|
|
2086
2132
|
};
|
|
2087
2133
|
}
|
|
2134
|
+
function failedScenarios(report) {
|
|
2135
|
+
return report.features.flatMap((f) => f.scenarios.filter((s) => s.status === "failed"));
|
|
2136
|
+
}
|
|
2088
2137
|
function listFailures(report) {
|
|
2089
2138
|
const out = [];
|
|
2090
2139
|
for (const feature of report.features) {
|
|
@@ -2168,7 +2217,7 @@ function ReportAgentFilterNotice({
|
|
|
2168
2217
|
// src/interactive/ReportFailureBanner.tsx
|
|
2169
2218
|
var import_react10 = require("react");
|
|
2170
2219
|
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2171
|
-
function ReportFailureBanner({ failures }) {
|
|
2220
|
+
function ReportFailureBanner({ failures, onCopyAll }) {
|
|
2172
2221
|
const first = failures[0];
|
|
2173
2222
|
const jumpToFirst = (0, import_react10.useCallback)(() => {
|
|
2174
2223
|
if (!first) return;
|
|
@@ -2198,7 +2247,23 @@ function ReportFailureBanner({ failures }) {
|
|
|
2198
2247
|
"aria-label": "View first failure",
|
|
2199
2248
|
children: "View first failure \u2193"
|
|
2200
2249
|
}
|
|
2201
|
-
)
|
|
2250
|
+
),
|
|
2251
|
+
onCopyAll ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2252
|
+
"button",
|
|
2253
|
+
{
|
|
2254
|
+
type: "button",
|
|
2255
|
+
className: "es-failure-banner-jump",
|
|
2256
|
+
onClick: onCopyAll,
|
|
2257
|
+
"aria-label": "Copy failures for an agent",
|
|
2258
|
+
children: [
|
|
2259
|
+
"Copy ",
|
|
2260
|
+
failures.length,
|
|
2261
|
+
" failure",
|
|
2262
|
+
failures.length === 1 ? "" : "s",
|
|
2263
|
+
" for an agent"
|
|
2264
|
+
]
|
|
2265
|
+
}
|
|
2266
|
+
) : null
|
|
2202
2267
|
]
|
|
2203
2268
|
}
|
|
2204
2269
|
);
|
|
@@ -2403,7 +2468,7 @@ var ReportSearch = (0, import_react13.forwardRef)(
|
|
|
2403
2468
|
onChange,
|
|
2404
2469
|
matchedCount,
|
|
2405
2470
|
totalCount,
|
|
2406
|
-
placeholder = "Search scenarios
|
|
2471
|
+
placeholder = "Search scenarios, tags, tickets, errors\u2026",
|
|
2407
2472
|
className
|
|
2408
2473
|
} = props;
|
|
2409
2474
|
const inputId = (0, import_react13.useId)();
|
|
@@ -2461,7 +2526,8 @@ var ReportSearch = (0, import_react13.forwardRef)(
|
|
|
2461
2526
|
}
|
|
2462
2527
|
);
|
|
2463
2528
|
|
|
2464
|
-
// src/interactive/
|
|
2529
|
+
// src/interactive/ReportShare.tsx
|
|
2530
|
+
var import_lucide_react5 = require("lucide-react");
|
|
2465
2531
|
var import_react14 = require("react");
|
|
2466
2532
|
|
|
2467
2533
|
// src/components/ui/button.tsx
|
|
@@ -2517,8 +2583,192 @@ function Button({
|
|
|
2517
2583
|
});
|
|
2518
2584
|
}
|
|
2519
2585
|
|
|
2520
|
-
// src/interactive/
|
|
2586
|
+
// src/interactive/ReportShare.tsx
|
|
2521
2587
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2588
|
+
var DEFAULT_SHARE_COMMAND = "npx executable-stories share reports/";
|
|
2589
|
+
function ReportShare({
|
|
2590
|
+
command = DEFAULT_SHARE_COMMAND,
|
|
2591
|
+
onCopy
|
|
2592
|
+
}) {
|
|
2593
|
+
const [open, setOpen] = (0, import_react14.useState)(false);
|
|
2594
|
+
const dialogRef = (0, import_react14.useRef)(null);
|
|
2595
|
+
(0, import_react14.useEffect)(() => {
|
|
2596
|
+
const dialog = dialogRef.current;
|
|
2597
|
+
if (!dialog) return;
|
|
2598
|
+
if (open && !dialog.open) dialog.showModal();
|
|
2599
|
+
else if (!open && dialog.open) dialog.close();
|
|
2600
|
+
}, [open]);
|
|
2601
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
|
|
2602
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
2603
|
+
Button,
|
|
2604
|
+
{
|
|
2605
|
+
type: "button",
|
|
2606
|
+
variant: "ghost",
|
|
2607
|
+
size: "sm",
|
|
2608
|
+
"aria-label": "Share this report",
|
|
2609
|
+
title: "Share this report",
|
|
2610
|
+
onClick: () => setOpen(true),
|
|
2611
|
+
children: [
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "size-4" }),
|
|
2613
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sr-only sm:not-sr-only", children: "Share" })
|
|
2614
|
+
]
|
|
2615
|
+
}
|
|
2616
|
+
),
|
|
2617
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
2618
|
+
"dialog",
|
|
2619
|
+
{
|
|
2620
|
+
ref: dialogRef,
|
|
2621
|
+
className: "es-report-share m-auto w-[min(90vw,32rem)] rounded-lg border border-border bg-card p-6 text-card-foreground shadow-lg [overscroll-behavior:contain] backdrop:bg-black/40",
|
|
2622
|
+
"aria-label": "Share this report",
|
|
2623
|
+
onClose: () => setOpen(false),
|
|
2624
|
+
children: [
|
|
2625
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("h2", { className: "text-base font-semibold", children: "Share this report" }),
|
|
2626
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mt-2 text-sm text-muted-foreground", children: "Publish it to Executable Stories Cloud and send a link. Screenshots and videos go with it. Run this from your project:" }),
|
|
2627
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "mt-4 flex items-center gap-2", children: [
|
|
2628
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("code", { className: "min-w-0 flex-1 truncate rounded border border-border bg-muted px-3 py-2 font-mono text-xs", children: command }),
|
|
2629
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2630
|
+
Button,
|
|
2631
|
+
{
|
|
2632
|
+
type: "button",
|
|
2633
|
+
size: "sm",
|
|
2634
|
+
onClick: () => onCopy(command, "Command copied"),
|
|
2635
|
+
children: "Copy"
|
|
2636
|
+
}
|
|
2637
|
+
)
|
|
2638
|
+
] }),
|
|
2639
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "mt-3 text-xs text-muted-foreground", children: "The file you are reading cannot upload itself, so the command does it: only the CLI can read the assets this report links to." }),
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "mt-6 flex items-center justify-between gap-4", children: [
|
|
2641
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2642
|
+
"a",
|
|
2643
|
+
{
|
|
2644
|
+
className: "text-xs underline underline-offset-2",
|
|
2645
|
+
href: "https://executablestories.com/guides/sharing-reports/",
|
|
2646
|
+
target: "_blank",
|
|
2647
|
+
rel: "noreferrer",
|
|
2648
|
+
children: "How sharing works"
|
|
2649
|
+
}
|
|
2650
|
+
),
|
|
2651
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setOpen(false), children: "Close" })
|
|
2652
|
+
] })
|
|
2653
|
+
]
|
|
2654
|
+
}
|
|
2655
|
+
)
|
|
2656
|
+
] });
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// src/interactive/ReportSinceLastVisit.tsx
|
|
2660
|
+
var import_react15 = require("react");
|
|
2661
|
+
|
|
2662
|
+
// src/interactive/last-visit.ts
|
|
2663
|
+
function snapshotOf(report, atMs) {
|
|
2664
|
+
const statuses = {};
|
|
2665
|
+
for (const feature of report.features) {
|
|
2666
|
+
for (const scenario of feature.scenarios) {
|
|
2667
|
+
statuses[scenario.id] = scenario.status;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
return { runId: report.runId, atMs, statuses };
|
|
2671
|
+
}
|
|
2672
|
+
function diffSinceVisit(previous, report) {
|
|
2673
|
+
if (!previous) return null;
|
|
2674
|
+
if (previous.runId === report.runId) return null;
|
|
2675
|
+
const newlyFailing = [];
|
|
2676
|
+
const newlyPassing = [];
|
|
2677
|
+
const added = [];
|
|
2678
|
+
for (const feature of report.features) {
|
|
2679
|
+
for (const scenario of feature.scenarios) {
|
|
2680
|
+
const ref = { id: scenario.id, title: scenario.title };
|
|
2681
|
+
const before = previous.statuses[scenario.id];
|
|
2682
|
+
if (before === void 0) {
|
|
2683
|
+
added.push(ref);
|
|
2684
|
+
} else if (before !== "failed" && scenario.status === "failed") {
|
|
2685
|
+
newlyFailing.push(ref);
|
|
2686
|
+
} else if (before === "failed" && scenario.status === "passed") {
|
|
2687
|
+
newlyPassing.push(ref);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) return null;
|
|
2692
|
+
return { sinceMs: previous.atMs, newlyFailing, newlyPassing, added };
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
// src/interactive/ReportSinceLastVisit.tsx
|
|
2696
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
2697
|
+
var KEY = "es-last-visit";
|
|
2698
|
+
var MAX_LINKS2 = 5;
|
|
2699
|
+
function readVisit() {
|
|
2700
|
+
try {
|
|
2701
|
+
const raw = localStorage.getItem(KEY);
|
|
2702
|
+
if (!raw) return null;
|
|
2703
|
+
const parsed = JSON.parse(raw);
|
|
2704
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
2705
|
+
const v = parsed;
|
|
2706
|
+
if (typeof v.runId !== "string" || typeof v.atMs !== "number" || typeof v.statuses !== "object") {
|
|
2707
|
+
return null;
|
|
2708
|
+
}
|
|
2709
|
+
return { runId: v.runId, atMs: v.atMs, statuses: v.statuses };
|
|
2710
|
+
} catch {
|
|
2711
|
+
return null;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
function writeVisit(snapshot) {
|
|
2715
|
+
try {
|
|
2716
|
+
localStorage.setItem(KEY, JSON.stringify(snapshot));
|
|
2717
|
+
} catch {
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
function ScenarioLinks2({ scenarios, tone }) {
|
|
2721
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
|
|
2722
|
+
scenarios.slice(0, MAX_LINKS2).map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
|
|
2723
|
+
i > 0 ? ", " : "",
|
|
2724
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
|
|
2725
|
+
] }, s.id)),
|
|
2726
|
+
scenarios.length > MAX_LINKS2 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
|
|
2727
|
+
" and ",
|
|
2728
|
+
scenarios.length - MAX_LINKS2,
|
|
2729
|
+
" more"
|
|
2730
|
+
] }) : null
|
|
2731
|
+
] });
|
|
2732
|
+
}
|
|
2733
|
+
function ReportSinceLastVisit({ report }) {
|
|
2734
|
+
const [delta, setDelta] = (0, import_react15.useState)(null);
|
|
2735
|
+
const [nowMs, setNowMs] = (0, import_react15.useState)(0);
|
|
2736
|
+
(0, import_react15.useEffect)(() => {
|
|
2737
|
+
const now = Date.now();
|
|
2738
|
+
setDelta(diffSinceVisit(readVisit(), report));
|
|
2739
|
+
setNowMs(now);
|
|
2740
|
+
writeVisit(snapshotOf(report, now));
|
|
2741
|
+
}, [report]);
|
|
2742
|
+
if (!delta) return null;
|
|
2743
|
+
const { newlyFailing, newlyPassing, added } = delta;
|
|
2744
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("p", { className: "text-xs text-muted-foreground", "data-testid": "es-since-last-visit", children: [
|
|
2745
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "font-medium text-foreground", children: [
|
|
2746
|
+
"Since your last visit (",
|
|
2747
|
+
formatRelativeAge(delta.sinceMs, nowMs),
|
|
2748
|
+
"):"
|
|
2749
|
+
] }),
|
|
2750
|
+
newlyFailing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "ml-2 text-fail", children: [
|
|
2751
|
+
newlyFailing.length,
|
|
2752
|
+
" started failing (",
|
|
2753
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ScenarioLinks2, { scenarios: newlyFailing, tone: "text-fail" }),
|
|
2754
|
+
")"
|
|
2755
|
+
] }) : null,
|
|
2756
|
+
newlyPassing.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "ml-2 text-pass", children: [
|
|
2757
|
+
newlyPassing.length,
|
|
2758
|
+
" fixed (",
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ScenarioLinks2, { scenarios: newlyPassing, tone: "text-pass" }),
|
|
2760
|
+
")"
|
|
2761
|
+
] }) : null,
|
|
2762
|
+
added.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "ml-2", children: [
|
|
2763
|
+
added.length,
|
|
2764
|
+
" new"
|
|
2765
|
+
] }) : null
|
|
2766
|
+
] });
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
// src/interactive/ReportShortcutsHelp.tsx
|
|
2770
|
+
var import_react16 = require("react");
|
|
2771
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2522
2772
|
var SHORTCUTS = [
|
|
2523
2773
|
{ keys: "/", description: "Focus search" },
|
|
2524
2774
|
{ keys: "f", description: "Jump to next failure" },
|
|
@@ -2530,20 +2780,20 @@ var SHORTCUTS = [
|
|
|
2530
2780
|
];
|
|
2531
2781
|
function Keys({ keys }) {
|
|
2532
2782
|
const parts = keys.split(" ");
|
|
2533
|
-
return /* @__PURE__ */ (0,
|
|
2534
|
-
i > 0 ? /* @__PURE__ */ (0,
|
|
2535
|
-
/* @__PURE__ */ (0,
|
|
2783
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "inline-flex items-center gap-1", children: [
|
|
2784
|
+
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
|
|
2785
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.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
2786
|
] }, k)) });
|
|
2537
2787
|
}
|
|
2538
2788
|
function ReportShortcutsHelp({ open, onClose }) {
|
|
2539
|
-
const dialogRef = (0,
|
|
2540
|
-
(0,
|
|
2789
|
+
const dialogRef = (0, import_react16.useRef)(null);
|
|
2790
|
+
(0, import_react16.useEffect)(() => {
|
|
2541
2791
|
const dialog = dialogRef.current;
|
|
2542
2792
|
if (!dialog) return;
|
|
2543
2793
|
if (open && !dialog.open) dialog.showModal();
|
|
2544
2794
|
else if (!open && dialog.open) dialog.close();
|
|
2545
2795
|
}, [open]);
|
|
2546
|
-
return /* @__PURE__ */ (0,
|
|
2796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
2547
2797
|
"dialog",
|
|
2548
2798
|
{
|
|
2549
2799
|
ref: dialogRef,
|
|
@@ -2551,20 +2801,20 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
2551
2801
|
"aria-label": "Keyboard shortcuts",
|
|
2552
2802
|
onClose,
|
|
2553
2803
|
children: [
|
|
2554
|
-
/* @__PURE__ */ (0,
|
|
2555
|
-
/* @__PURE__ */ (0,
|
|
2556
|
-
/* @__PURE__ */ (0,
|
|
2557
|
-
/* @__PURE__ */ (0,
|
|
2804
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
|
|
2805
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.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_runtime49.jsxs)("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
|
|
2806
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("dt", { className: "justify-self-start", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Keys, { keys: s.keys }) }),
|
|
2807
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("dd", { className: "text-sm text-muted-foreground", children: s.description })
|
|
2558
2808
|
] }, s.keys)) }),
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2809
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
|
|
2560
2810
|
]
|
|
2561
2811
|
}
|
|
2562
2812
|
);
|
|
2563
2813
|
}
|
|
2564
2814
|
|
|
2565
2815
|
// src/interactive/ReportToc.tsx
|
|
2566
|
-
var
|
|
2567
|
-
var
|
|
2816
|
+
var import_react17 = require("react");
|
|
2817
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
2568
2818
|
var DOT = {
|
|
2569
2819
|
passed: "bg-pass",
|
|
2570
2820
|
failed: "bg-fail",
|
|
@@ -2573,12 +2823,12 @@ var DOT = {
|
|
|
2573
2823
|
};
|
|
2574
2824
|
function TocContent({ onNavigate }) {
|
|
2575
2825
|
const report = useReport();
|
|
2576
|
-
const [activeId, setActiveId] = (0,
|
|
2577
|
-
const scenarioIds = (0,
|
|
2826
|
+
const [activeId, setActiveId] = (0, import_react17.useState)(null);
|
|
2827
|
+
const scenarioIds = (0, import_react17.useMemo)(
|
|
2578
2828
|
() => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
|
|
2579
2829
|
[report]
|
|
2580
2830
|
);
|
|
2581
|
-
(0,
|
|
2831
|
+
(0, import_react17.useEffect)(() => {
|
|
2582
2832
|
if (typeof IntersectionObserver === "undefined") return;
|
|
2583
2833
|
const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
|
|
2584
2834
|
if (els.length === 0) return;
|
|
@@ -2593,11 +2843,11 @@ function TocContent({ onNavigate }) {
|
|
|
2593
2843
|
return () => observer.disconnect();
|
|
2594
2844
|
}, [scenarioIds]);
|
|
2595
2845
|
if (report.features.length === 0) return null;
|
|
2596
|
-
return /* @__PURE__ */ (0,
|
|
2597
|
-
/* @__PURE__ */ (0,
|
|
2598
|
-
/* @__PURE__ */ (0,
|
|
2846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("li", { children: [
|
|
2847
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
|
|
2848
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
|
|
2599
2849
|
const active = activeId === scenario.id;
|
|
2600
|
-
return /* @__PURE__ */ (0,
|
|
2850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
|
|
2601
2851
|
"a",
|
|
2602
2852
|
{
|
|
2603
2853
|
href: `#${scenario.id}`,
|
|
@@ -2612,14 +2862,14 @@ function TocContent({ onNavigate }) {
|
|
|
2612
2862
|
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
2613
2863
|
),
|
|
2614
2864
|
children: [
|
|
2615
|
-
/* @__PURE__ */ (0,
|
|
2865
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
2616
2866
|
"span",
|
|
2617
2867
|
{
|
|
2618
2868
|
"aria-hidden": true,
|
|
2619
2869
|
className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
|
|
2620
2870
|
}
|
|
2621
2871
|
),
|
|
2622
|
-
/* @__PURE__ */ (0,
|
|
2872
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "break-words", children: scenario.title })
|
|
2623
2873
|
]
|
|
2624
2874
|
}
|
|
2625
2875
|
) }, scenario.id);
|
|
@@ -2629,48 +2879,48 @@ function TocContent({ onNavigate }) {
|
|
|
2629
2879
|
function ReportToc() {
|
|
2630
2880
|
const report = useReport();
|
|
2631
2881
|
if (report.features.length === 0) return null;
|
|
2632
|
-
return /* @__PURE__ */ (0,
|
|
2882
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
|
|
2633
2883
|
"nav",
|
|
2634
2884
|
{
|
|
2635
2885
|
"aria-label": "Table of contents",
|
|
2636
2886
|
className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
|
|
2637
2887
|
children: [
|
|
2638
|
-
/* @__PURE__ */ (0,
|
|
2639
|
-
/* @__PURE__ */ (0,
|
|
2888
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(TocContent, {})
|
|
2640
2890
|
]
|
|
2641
2891
|
}
|
|
2642
2892
|
);
|
|
2643
2893
|
}
|
|
2644
2894
|
|
|
2645
2895
|
// src/interactive/ReportTocDrawer.tsx
|
|
2646
|
-
var
|
|
2896
|
+
var import_react18 = require("react");
|
|
2647
2897
|
var import_dialog = require("@base-ui/react/dialog");
|
|
2648
|
-
var
|
|
2649
|
-
var
|
|
2898
|
+
var import_lucide_react6 = require("lucide-react");
|
|
2899
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
2650
2900
|
function ReportTocDrawer() {
|
|
2651
2901
|
const report = useReport();
|
|
2652
|
-
const [open, setOpen] = (0,
|
|
2902
|
+
const [open, setOpen] = (0, import_react18.useState)(false);
|
|
2653
2903
|
if (report.features.length === 0) return null;
|
|
2654
|
-
return /* @__PURE__ */ (0,
|
|
2655
|
-
/* @__PURE__ */ (0,
|
|
2656
|
-
/* @__PURE__ */ (0,
|
|
2904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_dialog.Dialog.Root, { open, onOpenChange: setOpen, children: [
|
|
2905
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.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: [
|
|
2906
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react6.ListIcon, { "aria-hidden": true }),
|
|
2657
2907
|
" Contents"
|
|
2658
2908
|
] }),
|
|
2659
|
-
/* @__PURE__ */ (0,
|
|
2660
|
-
/* @__PURE__ */ (0,
|
|
2661
|
-
/* @__PURE__ */ (0,
|
|
2662
|
-
/* @__PURE__ */ (0,
|
|
2663
|
-
/* @__PURE__ */ (0,
|
|
2664
|
-
/* @__PURE__ */ (0,
|
|
2909
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_dialog.Dialog.Portal, { children: [
|
|
2910
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.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" }),
|
|
2911
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.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: [
|
|
2912
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2913
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_dialog.Dialog.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
|
|
2914
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
2665
2915
|
import_dialog.Dialog.Close,
|
|
2666
2916
|
{
|
|
2667
2917
|
"aria-label": "Close contents",
|
|
2668
2918
|
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
2669
|
-
children: /* @__PURE__ */ (0,
|
|
2919
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react6.XIcon, { "aria-hidden": true })
|
|
2670
2920
|
}
|
|
2671
2921
|
)
|
|
2672
2922
|
] }),
|
|
2673
|
-
/* @__PURE__ */ (0,
|
|
2923
|
+
/* @__PURE__ */ (0, import_jsx_runtime51.jsx)(TocContent, { onNavigate: () => setOpen(false) })
|
|
2674
2924
|
] })
|
|
2675
2925
|
] })
|
|
2676
2926
|
] });
|
|
@@ -2704,10 +2954,10 @@ function failuresFirst(report) {
|
|
|
2704
2954
|
}
|
|
2705
2955
|
|
|
2706
2956
|
// src/interactive/use-deep-link-scroll.ts
|
|
2707
|
-
var
|
|
2957
|
+
var import_react19 = require("react");
|
|
2708
2958
|
function useDeepLinkScroll() {
|
|
2709
|
-
const lastAnchor = (0,
|
|
2710
|
-
(0,
|
|
2959
|
+
const lastAnchor = (0, import_react19.useRef)(null);
|
|
2960
|
+
(0, import_react19.useEffect)(() => {
|
|
2711
2961
|
function scrollToHash() {
|
|
2712
2962
|
const { anchor } = splitHash(window.location.hash);
|
|
2713
2963
|
if (!anchor || anchor === lastAnchor.current) return;
|
|
@@ -2723,7 +2973,7 @@ function useDeepLinkScroll() {
|
|
|
2723
2973
|
}
|
|
2724
2974
|
|
|
2725
2975
|
// src/interactive/use-keyboard-shortcuts.ts
|
|
2726
|
-
var
|
|
2976
|
+
var import_react20 = require("react");
|
|
2727
2977
|
function isEditableTarget(target) {
|
|
2728
2978
|
if (!(target instanceof HTMLElement)) return false;
|
|
2729
2979
|
const tag = target.tagName;
|
|
@@ -2732,9 +2982,9 @@ function isEditableTarget(target) {
|
|
|
2732
2982
|
return false;
|
|
2733
2983
|
}
|
|
2734
2984
|
function useKeyboardShortcuts(handlers) {
|
|
2735
|
-
const ref = (0,
|
|
2985
|
+
const ref = (0, import_react20.useRef)(handlers);
|
|
2736
2986
|
ref.current = handlers;
|
|
2737
|
-
(0,
|
|
2987
|
+
(0, import_react20.useEffect)(() => {
|
|
2738
2988
|
function onKeyDown(e) {
|
|
2739
2989
|
const h = ref.current;
|
|
2740
2990
|
if (e.key === "Escape") {
|
|
@@ -2778,8 +3028,8 @@ function useKeyboardShortcuts(handlers) {
|
|
|
2778
3028
|
}
|
|
2779
3029
|
|
|
2780
3030
|
// src/interactive/use-theme.ts
|
|
2781
|
-
var
|
|
2782
|
-
var
|
|
3031
|
+
var import_react21 = require("react");
|
|
3032
|
+
var KEY2 = "es-theme";
|
|
2783
3033
|
function systemDark() {
|
|
2784
3034
|
return typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches;
|
|
2785
3035
|
}
|
|
@@ -2793,19 +3043,19 @@ function applyTheme(pref) {
|
|
|
2793
3043
|
}
|
|
2794
3044
|
function initialPref() {
|
|
2795
3045
|
if (typeof localStorage !== "undefined") {
|
|
2796
|
-
const stored = localStorage.getItem(
|
|
3046
|
+
const stored = localStorage.getItem(KEY2);
|
|
2797
3047
|
if (stored === "light" || stored === "dark" || stored === "system") return stored;
|
|
2798
3048
|
}
|
|
2799
3049
|
return "system";
|
|
2800
3050
|
}
|
|
2801
3051
|
function useTheme() {
|
|
2802
|
-
const [pref, setPrefState] = (0,
|
|
2803
|
-
(0,
|
|
3052
|
+
const [pref, setPrefState] = (0, import_react21.useState)("system");
|
|
3053
|
+
(0, import_react21.useEffect)(() => {
|
|
2804
3054
|
const p = initialPref();
|
|
2805
3055
|
setPrefState(p);
|
|
2806
3056
|
applyTheme(p);
|
|
2807
3057
|
}, []);
|
|
2808
|
-
(0,
|
|
3058
|
+
(0, import_react21.useEffect)(() => {
|
|
2809
3059
|
if (typeof matchMedia === "undefined") return;
|
|
2810
3060
|
const mq = matchMedia("(prefers-color-scheme: dark)");
|
|
2811
3061
|
const onChange = () => {
|
|
@@ -2814,11 +3064,11 @@ function useTheme() {
|
|
|
2814
3064
|
mq.addEventListener("change", onChange);
|
|
2815
3065
|
return () => mq.removeEventListener("change", onChange);
|
|
2816
3066
|
}, [pref]);
|
|
2817
|
-
const setPref = (0,
|
|
3067
|
+
const setPref = (0, import_react21.useCallback)((p) => {
|
|
2818
3068
|
setPrefState(p);
|
|
2819
3069
|
applyTheme(p);
|
|
2820
3070
|
try {
|
|
2821
|
-
localStorage.setItem(
|
|
3071
|
+
localStorage.setItem(KEY2, p);
|
|
2822
3072
|
} catch {
|
|
2823
3073
|
}
|
|
2824
3074
|
}, []);
|
|
@@ -2826,19 +3076,19 @@ function useTheme() {
|
|
|
2826
3076
|
}
|
|
2827
3077
|
|
|
2828
3078
|
// src/interactive/use-url-state.ts
|
|
2829
|
-
var
|
|
3079
|
+
var import_react22 = require("react");
|
|
2830
3080
|
var WRITE_DELAY_MS = 250;
|
|
2831
3081
|
function useUrlState() {
|
|
2832
|
-
const [state, setState] = (0,
|
|
2833
|
-
const ready = (0,
|
|
2834
|
-
(0,
|
|
3082
|
+
const [state, setState] = (0, import_react22.useState)(DEFAULT_URL_STATE);
|
|
3083
|
+
const ready = (0, import_react22.useRef)(false);
|
|
3084
|
+
(0, import_react22.useEffect)(() => {
|
|
2835
3085
|
const read = () => setState(decodeUrlState(splitHash(window.location.hash).params));
|
|
2836
3086
|
read();
|
|
2837
3087
|
ready.current = true;
|
|
2838
3088
|
window.addEventListener("hashchange", read);
|
|
2839
3089
|
return () => window.removeEventListener("hashchange", read);
|
|
2840
3090
|
}, []);
|
|
2841
|
-
(0,
|
|
3091
|
+
(0, import_react22.useEffect)(() => {
|
|
2842
3092
|
if (!ready.current) return;
|
|
2843
3093
|
const timer = setTimeout(
|
|
2844
3094
|
() => writeHash(splitHash(window.location.hash).anchor, encodeUrlState(state)),
|
|
@@ -2846,7 +3096,7 @@ function useUrlState() {
|
|
|
2846
3096
|
);
|
|
2847
3097
|
return () => clearTimeout(timer);
|
|
2848
3098
|
}, [state]);
|
|
2849
|
-
const update = (0,
|
|
3099
|
+
const update = (0, import_react22.useCallback)(
|
|
2850
3100
|
(patch) => setState((prev) => ({ ...prev, ...patch })),
|
|
2851
3101
|
[]
|
|
2852
3102
|
);
|
|
@@ -2854,7 +3104,7 @@ function useUrlState() {
|
|
|
2854
3104
|
}
|
|
2855
3105
|
|
|
2856
3106
|
// src/interactive/webmcp-tools.ts
|
|
2857
|
-
var
|
|
3107
|
+
var import_report_queries2 = require("executable-stories-core/report-queries");
|
|
2858
3108
|
var STATUSES2 = [
|
|
2859
3109
|
"passed",
|
|
2860
3110
|
"failed",
|
|
@@ -2937,9 +3187,9 @@ var READ_ONLY = { readOnlyHint: true, untrustedContentHint: true };
|
|
|
2937
3187
|
function reportTools(deps) {
|
|
2938
3188
|
const { report, setView, onAgentFilter } = deps;
|
|
2939
3189
|
const now = deps.now ?? Date.now;
|
|
2940
|
-
const provenance = () => ({ run: (0,
|
|
3190
|
+
const provenance = () => ({ run: (0, import_report_queries2.runProvenance)(report, now()) });
|
|
2941
3191
|
const list = (filters) => {
|
|
2942
|
-
const scenarios = (0,
|
|
3192
|
+
const scenarios = (0, import_report_queries2.listScenarioSummaries)(report, filters);
|
|
2943
3193
|
return { ...provenance(), total: scenarios.length, scenarios };
|
|
2944
3194
|
};
|
|
2945
3195
|
return {
|
|
@@ -2973,7 +3223,7 @@ function reportTools(deps) {
|
|
|
2973
3223
|
execute: () => ({
|
|
2974
3224
|
...provenance(),
|
|
2975
3225
|
summary: report.summary,
|
|
2976
|
-
features: (0,
|
|
3226
|
+
features: (0, import_report_queries2.getFeatureSummary)(report)
|
|
2977
3227
|
})
|
|
2978
3228
|
},
|
|
2979
3229
|
get_scenario: {
|
|
@@ -2992,7 +3242,7 @@ function reportTools(deps) {
|
|
|
2992
3242
|
},
|
|
2993
3243
|
execute: (input) => guarded(() => {
|
|
2994
3244
|
const idOrTitle = readString(asRecord(input), "idOrTitle") ?? "";
|
|
2995
|
-
const found = (0,
|
|
3245
|
+
const found = (0, import_report_queries2.getScenario)(report, idOrTitle);
|
|
2996
3246
|
if (!found) {
|
|
2997
3247
|
return {
|
|
2998
3248
|
...provenance(),
|
|
@@ -3009,7 +3259,7 @@ function reportTools(deps) {
|
|
|
3009
3259
|
// The summary projection, not the raw scenario: doc entry bodies and
|
|
3010
3260
|
// base64 attachments are unbounded, and this payload goes straight
|
|
3011
3261
|
// into a model's context window.
|
|
3012
|
-
scenario: (0,
|
|
3262
|
+
scenario: (0, import_report_queries2.toScenarioSummary)(found.feature, found.scenario)
|
|
3013
3263
|
};
|
|
3014
3264
|
})
|
|
3015
3265
|
},
|
|
@@ -3068,12 +3318,12 @@ function reportTools(deps) {
|
|
|
3068
3318
|
}
|
|
3069
3319
|
|
|
3070
3320
|
// src/interactive/ReportInteractive.tsx
|
|
3071
|
-
var
|
|
3321
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
3072
3322
|
function ReportInteractive(props) {
|
|
3073
3323
|
const { report, className, title, dataTheme } = props;
|
|
3074
3324
|
const result = unwrapReport(report);
|
|
3075
3325
|
if (!result.ok) {
|
|
3076
|
-
return /* @__PURE__ */ (0,
|
|
3326
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3077
3327
|
ReportErrorShell,
|
|
3078
3328
|
{
|
|
3079
3329
|
error: result.error,
|
|
@@ -3083,18 +3333,18 @@ function ReportInteractive(props) {
|
|
|
3083
3333
|
}
|
|
3084
3334
|
);
|
|
3085
3335
|
}
|
|
3086
|
-
return /* @__PURE__ */ (0,
|
|
3336
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportInteractiveView, { ...props, report: result.data });
|
|
3087
3337
|
}
|
|
3088
3338
|
var THEME_OPTIONS = [
|
|
3089
|
-
{ value: "light", label: "Light", Icon:
|
|
3090
|
-
{ value: "system", label: "System", Icon:
|
|
3091
|
-
{ value: "dark", label: "Dark", Icon:
|
|
3339
|
+
{ value: "light", label: "Light", Icon: import_lucide_react7.Sun },
|
|
3340
|
+
{ value: "system", label: "System", Icon: import_lucide_react7.Monitor },
|
|
3341
|
+
{ value: "dark", label: "Dark", Icon: import_lucide_react7.Moon }
|
|
3092
3342
|
];
|
|
3093
3343
|
function ThemeSegment({
|
|
3094
3344
|
pref,
|
|
3095
3345
|
onPref
|
|
3096
3346
|
}) {
|
|
3097
|
-
return /* @__PURE__ */ (0,
|
|
3347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3098
3348
|
ToggleGroup,
|
|
3099
3349
|
{
|
|
3100
3350
|
size: "sm",
|
|
@@ -3104,14 +3354,14 @@ function ThemeSegment({
|
|
|
3104
3354
|
if (next) onPref(next);
|
|
3105
3355
|
},
|
|
3106
3356
|
"aria-label": "Color theme",
|
|
3107
|
-
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0,
|
|
3357
|
+
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3108
3358
|
ToggleGroupItem,
|
|
3109
3359
|
{
|
|
3110
3360
|
value,
|
|
3111
3361
|
"aria-label": label,
|
|
3112
3362
|
title: label,
|
|
3113
3363
|
className: "px-2",
|
|
3114
|
-
children: /* @__PURE__ */ (0,
|
|
3364
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Icon, {})
|
|
3115
3365
|
},
|
|
3116
3366
|
value
|
|
3117
3367
|
))
|
|
@@ -3128,30 +3378,32 @@ function ReportInteractiveView({
|
|
|
3128
3378
|
hideHeader = false,
|
|
3129
3379
|
hideToc = false,
|
|
3130
3380
|
staleAfterDays = 7,
|
|
3131
|
-
scenarioHistory
|
|
3381
|
+
scenarioHistory,
|
|
3382
|
+
share = false,
|
|
3383
|
+
shareCommand
|
|
3132
3384
|
}) {
|
|
3133
3385
|
const [urlState, setUrlState] = useUrlState();
|
|
3134
3386
|
const { query, status: statusFilter, tags: activeTags, detail } = urlState;
|
|
3135
|
-
const setQuery = (0,
|
|
3387
|
+
const setQuery = (0, import_react24.useCallback)(
|
|
3136
3388
|
(next) => setUrlState({ query: next }),
|
|
3137
3389
|
[setUrlState]
|
|
3138
3390
|
);
|
|
3139
|
-
const setStatusFilter = (0,
|
|
3391
|
+
const setStatusFilter = (0, import_react24.useCallback)(
|
|
3140
3392
|
(next) => setUrlState({ status: next }),
|
|
3141
3393
|
[setUrlState]
|
|
3142
3394
|
);
|
|
3143
|
-
const setDetail = (0,
|
|
3395
|
+
const setDetail = (0, import_react24.useCallback)(
|
|
3144
3396
|
(next) => setUrlState({ detail: next }),
|
|
3145
3397
|
[setUrlState]
|
|
3146
3398
|
);
|
|
3147
|
-
const [helpOpen, setHelpOpen] = (0,
|
|
3399
|
+
const [helpOpen, setHelpOpen] = (0, import_react24.useState)(false);
|
|
3148
3400
|
const { pref: themePref, setPref: setThemePref } = useTheme();
|
|
3149
|
-
const searchRef = (0,
|
|
3150
|
-
const expandId = (0,
|
|
3151
|
-
const docsId = (0,
|
|
3152
|
-
const deferredQuery = (0,
|
|
3153
|
-
const failures = (0,
|
|
3154
|
-
const filtered = (0,
|
|
3401
|
+
const searchRef = (0, import_react24.useRef)(null);
|
|
3402
|
+
const expandId = (0, import_react24.useId)();
|
|
3403
|
+
const docsId = (0, import_react24.useId)();
|
|
3404
|
+
const deferredQuery = (0, import_react24.useDeferredValue)(query);
|
|
3405
|
+
const failures = (0, import_react24.useMemo)(() => listFailures(report), [report]);
|
|
3406
|
+
const filtered = (0, import_react24.useMemo)(
|
|
3155
3407
|
// Features carrying failures float to the top so the first thing on screen
|
|
3156
3408
|
// is the thing that needs attention; an all-green run keeps source order.
|
|
3157
3409
|
() => failuresFirst(
|
|
@@ -3164,7 +3416,7 @@ function ReportInteractiveView({
|
|
|
3164
3416
|
[report, deferredQuery, statusFilter, activeTags]
|
|
3165
3417
|
);
|
|
3166
3418
|
const isFiltering = query !== deferredQuery;
|
|
3167
|
-
const statusOptions = (0,
|
|
3419
|
+
const statusOptions = (0, import_react24.useMemo)(() => {
|
|
3168
3420
|
const s = report.summary;
|
|
3169
3421
|
const opts = [
|
|
3170
3422
|
{ key: "all", label: "All", count: s.total }
|
|
@@ -3179,21 +3431,21 @@ function ReportInteractiveView({
|
|
|
3179
3431
|
opts.push({ key: "pending", label: "Pending", count: s.pending });
|
|
3180
3432
|
return opts;
|
|
3181
3433
|
}, [report]);
|
|
3182
|
-
const tagOptions = (0,
|
|
3183
|
-
const toggleTag = (0,
|
|
3434
|
+
const tagOptions = (0, import_react24.useMemo)(() => allTags(report), [report]);
|
|
3435
|
+
const toggleTag = (0, import_react24.useCallback)(
|
|
3184
3436
|
(tag) => setUrlState({
|
|
3185
3437
|
tags: activeTags.includes(tag) ? activeTags.filter((t) => t !== tag) : [...activeTags, tag]
|
|
3186
3438
|
}),
|
|
3187
3439
|
[activeTags, setUrlState]
|
|
3188
3440
|
);
|
|
3189
|
-
const [toast, setToast] = (0,
|
|
3190
|
-
const toastTimer = (0,
|
|
3191
|
-
const showToast = (0,
|
|
3441
|
+
const [toast, setToast] = (0, import_react24.useState)(null);
|
|
3442
|
+
const toastTimer = (0, import_react24.useRef)(null);
|
|
3443
|
+
const showToast = (0, import_react24.useCallback)((msg) => {
|
|
3192
3444
|
setToast(msg);
|
|
3193
3445
|
if (toastTimer.current) clearTimeout(toastTimer.current);
|
|
3194
3446
|
toastTimer.current = setTimeout(() => setToast(null), 1800);
|
|
3195
3447
|
}, []);
|
|
3196
|
-
const copy = (0,
|
|
3448
|
+
const copy = (0, import_react24.useCallback)(
|
|
3197
3449
|
(text, msg) => {
|
|
3198
3450
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
3199
3451
|
navigator.clipboard.writeText(text).then(
|
|
@@ -3204,7 +3456,7 @@ function ReportInteractiveView({
|
|
|
3204
3456
|
},
|
|
3205
3457
|
[showToast]
|
|
3206
3458
|
);
|
|
3207
|
-
const scenarioActions = (0,
|
|
3459
|
+
const scenarioActions = (0, import_react24.useMemo)(
|
|
3208
3460
|
() => ({
|
|
3209
3461
|
copyLink: (s) => copy(scenarioPermalink(s), "Link copied"),
|
|
3210
3462
|
copyMarkdown: (s) => copy(scenarioToMarkdown(s), "Markdown copied"),
|
|
@@ -3212,27 +3464,31 @@ function ReportInteractiveView({
|
|
|
3212
3464
|
}),
|
|
3213
3465
|
[copy]
|
|
3214
3466
|
);
|
|
3215
|
-
const
|
|
3216
|
-
|
|
3467
|
+
const copyAllFailures = (0, import_react24.useCallback)(
|
|
3468
|
+
() => copy(failuresToPrompt(failedScenarios(filtered)), "Failures copied"),
|
|
3469
|
+
[copy, filtered]
|
|
3470
|
+
);
|
|
3471
|
+
const failureIndexRef = (0, import_react24.useRef)(0);
|
|
3472
|
+
const allCollapsibleIds = (0, import_react24.useMemo)(
|
|
3217
3473
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
3218
3474
|
[report]
|
|
3219
3475
|
);
|
|
3220
3476
|
const collapse = useCollapseState(
|
|
3221
|
-
(0,
|
|
3477
|
+
(0, import_react24.useCallback)(() => defaultCollapsedIds(report), [report])
|
|
3222
3478
|
);
|
|
3223
3479
|
const revealFilteredResults = deferredQuery.trim() !== "" || statusFilter !== "all" || activeTags.length > 0;
|
|
3224
|
-
const visibleCollapse = (0,
|
|
3480
|
+
const visibleCollapse = (0, import_react24.useMemo)(
|
|
3225
3481
|
() => revealFilteredResults ? { ...collapse.api, isCollapsed: () => false } : collapse.api,
|
|
3226
3482
|
[collapse.api, revealFilteredResults]
|
|
3227
3483
|
);
|
|
3228
|
-
const collapseAll = (0,
|
|
3484
|
+
const collapseAll = (0, import_react24.useCallback)(
|
|
3229
3485
|
() => collapse.collapseAll(allCollapsibleIds),
|
|
3230
3486
|
[collapse, allCollapsibleIds]
|
|
3231
3487
|
);
|
|
3232
|
-
const [expandedAll, setExpandedAll] = (0,
|
|
3488
|
+
const [expandedAll, setExpandedAll] = (0, import_react24.useState)(
|
|
3233
3489
|
() => report.summary.failed === 0
|
|
3234
3490
|
);
|
|
3235
|
-
const setExpanded = (0,
|
|
3491
|
+
const setExpanded = (0, import_react24.useCallback)(
|
|
3236
3492
|
(expanded) => {
|
|
3237
3493
|
if (expanded) collapse.expandAll();
|
|
3238
3494
|
else collapseAll();
|
|
@@ -3240,8 +3496,8 @@ function ReportInteractiveView({
|
|
|
3240
3496
|
},
|
|
3241
3497
|
[collapse, collapseAll]
|
|
3242
3498
|
);
|
|
3243
|
-
const [agentFilter, setAgentFilter] = (0,
|
|
3244
|
-
(0,
|
|
3499
|
+
const [agentFilter, setAgentFilter] = (0, import_react24.useState)(null);
|
|
3500
|
+
(0, import_react23.useTools)(
|
|
3245
3501
|
reportTools({
|
|
3246
3502
|
report,
|
|
3247
3503
|
view: urlState,
|
|
@@ -3250,14 +3506,14 @@ function ReportInteractiveView({
|
|
|
3250
3506
|
})
|
|
3251
3507
|
);
|
|
3252
3508
|
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,
|
|
3509
|
+
const clearFilters = (0, import_react24.useCallback)(
|
|
3254
3510
|
() => setUrlState({ query: "", status: "all", tags: [] }),
|
|
3255
3511
|
[setUrlState]
|
|
3256
3512
|
);
|
|
3257
|
-
const focusSearch = (0,
|
|
3513
|
+
const focusSearch = (0, import_react24.useCallback)(() => {
|
|
3258
3514
|
searchRef.current?.focus();
|
|
3259
3515
|
}, []);
|
|
3260
|
-
const stepFailure = (0,
|
|
3516
|
+
const stepFailure = (0, import_react24.useCallback)(
|
|
3261
3517
|
(direction) => {
|
|
3262
3518
|
if (failures.length === 0) return;
|
|
3263
3519
|
failureIndexRef.current = (failureIndexRef.current + direction + failures.length) % failures.length;
|
|
@@ -3266,10 +3522,10 @@ function ReportInteractiveView({
|
|
|
3266
3522
|
},
|
|
3267
3523
|
[failures]
|
|
3268
3524
|
);
|
|
3269
|
-
const toggleHelp = (0,
|
|
3525
|
+
const toggleHelp = (0, import_react24.useCallback)(() => {
|
|
3270
3526
|
setHelpOpen((v) => !v);
|
|
3271
3527
|
}, []);
|
|
3272
|
-
const escape = (0,
|
|
3528
|
+
const escape = (0, import_react24.useCallback)(() => {
|
|
3273
3529
|
if (helpOpen) setHelpOpen(false);
|
|
3274
3530
|
else if (query !== "") setQuery("");
|
|
3275
3531
|
}, [helpOpen, query, setQuery]);
|
|
@@ -3287,13 +3543,13 @@ function ReportInteractiveView({
|
|
|
3287
3543
|
const totalScenarios = report.summary.total;
|
|
3288
3544
|
const matchedScenarios = filtered.summary.total;
|
|
3289
3545
|
const showToc = !hideToc && totalScenarios > 3;
|
|
3290
|
-
return /* @__PURE__ */ (0,
|
|
3546
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(CollapseProvider, { value: visibleCollapse, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ScenarioFreshnessProvider, { value: { staleAfterDays, report }, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3291
3547
|
ReportRoot,
|
|
3292
3548
|
{
|
|
3293
3549
|
report: filtered,
|
|
3294
3550
|
customRenderers,
|
|
3295
3551
|
renderers,
|
|
3296
|
-
children: /* @__PURE__ */ (0,
|
|
3552
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
3297
3553
|
"main",
|
|
3298
3554
|
{
|
|
3299
3555
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -3302,8 +3558,8 @@ function ReportInteractiveView({
|
|
|
3302
3558
|
"data-theme": dataTheme,
|
|
3303
3559
|
"data-detail-level": detail,
|
|
3304
3560
|
children: [
|
|
3305
|
-
/* @__PURE__ */ (0,
|
|
3306
|
-
/* @__PURE__ */ (0,
|
|
3561
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("header", { className: "es-report-header", children: [
|
|
3562
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
3307
3563
|
"div",
|
|
3308
3564
|
{
|
|
3309
3565
|
"data-es-navbar": true,
|
|
@@ -3312,9 +3568,9 @@ function ReportInteractiveView({
|
|
|
3312
3568
|
hideHeader ? "justify-end" : "justify-between"
|
|
3313
3569
|
),
|
|
3314
3570
|
children: [
|
|
3315
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
3316
|
-
/* @__PURE__ */ (0,
|
|
3317
|
-
/* @__PURE__ */ (0,
|
|
3571
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("h1", { className: "min-w-0", children: title ?? "Story Report" }),
|
|
3572
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3573
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3318
3574
|
ReportSearch,
|
|
3319
3575
|
{
|
|
3320
3576
|
ref: searchRef,
|
|
@@ -3324,16 +3580,17 @@ function ReportInteractiveView({
|
|
|
3324
3580
|
totalCount: totalScenarios
|
|
3325
3581
|
}
|
|
3326
3582
|
),
|
|
3327
|
-
/* @__PURE__ */ (0,
|
|
3583
|
+
share ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportShare, { command: shareCommand, onCopy: copy }) : null,
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ThemeSegment, { pref: themePref, onPref: setThemePref })
|
|
3328
3585
|
] })
|
|
3329
3586
|
]
|
|
3330
3587
|
}
|
|
3331
3588
|
),
|
|
3332
|
-
hideHeader ? null : /* @__PURE__ */ (0,
|
|
3333
|
-
/* @__PURE__ */ (0,
|
|
3334
|
-
/* @__PURE__ */ (0,
|
|
3589
|
+
hideHeader ? null : /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_jsx_runtime52.Fragment, { children: [
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportSummary, {}),
|
|
3591
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportMeta, {})
|
|
3335
3592
|
] }),
|
|
3336
|
-
/* @__PURE__ */ (0,
|
|
3593
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3337
3594
|
ReportFreshness,
|
|
3338
3595
|
{
|
|
3339
3596
|
lastRunMs: reportLastRunMs(report),
|
|
@@ -3341,14 +3598,15 @@ function ReportInteractiveView({
|
|
|
3341
3598
|
staleAfterDays
|
|
3342
3599
|
}
|
|
3343
3600
|
),
|
|
3344
|
-
/* @__PURE__ */ (0,
|
|
3601
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3345
3602
|
ReportLastRunDelta,
|
|
3346
3603
|
{
|
|
3347
3604
|
history: scenarioHistory,
|
|
3348
3605
|
report
|
|
3349
3606
|
}
|
|
3350
3607
|
),
|
|
3351
|
-
/* @__PURE__ */ (0,
|
|
3608
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportSinceLastVisit, { report }),
|
|
3609
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3352
3610
|
ReportFilters,
|
|
3353
3611
|
{
|
|
3354
3612
|
statuses: statusOptions,
|
|
@@ -3359,10 +3617,10 @@ function ReportInteractiveView({
|
|
|
3359
3617
|
onToggleTag: toggleTag
|
|
3360
3618
|
}
|
|
3361
3619
|
),
|
|
3362
|
-
/* @__PURE__ */ (0,
|
|
3363
|
-
showToc ? /* @__PURE__ */ (0,
|
|
3364
|
-
/* @__PURE__ */ (0,
|
|
3365
|
-
/* @__PURE__ */ (0,
|
|
3620
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
|
|
3621
|
+
showToc ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportTocDrawer, {}) : null,
|
|
3622
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3623
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3366
3624
|
Switch,
|
|
3367
3625
|
{
|
|
3368
3626
|
id: expandId,
|
|
@@ -3370,7 +3628,7 @@ function ReportInteractiveView({
|
|
|
3370
3628
|
onCheckedChange: setExpanded
|
|
3371
3629
|
}
|
|
3372
3630
|
),
|
|
3373
|
-
/* @__PURE__ */ (0,
|
|
3631
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3374
3632
|
"label",
|
|
3375
3633
|
{
|
|
3376
3634
|
htmlFor: expandId,
|
|
@@ -3379,8 +3637,8 @@ function ReportInteractiveView({
|
|
|
3379
3637
|
}
|
|
3380
3638
|
)
|
|
3381
3639
|
] }),
|
|
3382
|
-
/* @__PURE__ */ (0,
|
|
3383
|
-
/* @__PURE__ */ (0,
|
|
3640
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3384
3642
|
Switch,
|
|
3385
3643
|
{
|
|
3386
3644
|
id: docsId,
|
|
@@ -3388,7 +3646,7 @@ function ReportInteractiveView({
|
|
|
3388
3646
|
onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
|
|
3389
3647
|
}
|
|
3390
3648
|
),
|
|
3391
|
-
/* @__PURE__ */ (0,
|
|
3649
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3392
3650
|
"label",
|
|
3393
3651
|
{
|
|
3394
3652
|
htmlFor: docsId,
|
|
@@ -3399,7 +3657,7 @@ function ReportInteractiveView({
|
|
|
3399
3657
|
] })
|
|
3400
3658
|
] })
|
|
3401
3659
|
] }),
|
|
3402
|
-
showAgentFilter ? /* @__PURE__ */ (0,
|
|
3660
|
+
showAgentFilter ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3403
3661
|
ReportAgentFilterNotice,
|
|
3404
3662
|
{
|
|
3405
3663
|
applied: agentFilter,
|
|
@@ -3407,17 +3665,17 @@ function ReportInteractiveView({
|
|
|
3407
3665
|
onDismiss: () => setAgentFilter(null)
|
|
3408
3666
|
}
|
|
3409
3667
|
) : null,
|
|
3410
|
-
/* @__PURE__ */ (0,
|
|
3411
|
-
hasContent ? showToc ? /* @__PURE__ */ (0,
|
|
3412
|
-
/* @__PURE__ */ (0,
|
|
3413
|
-
/* @__PURE__ */ (0,
|
|
3414
|
-
] }) : /* @__PURE__ */ (0,
|
|
3668
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportFailureBanner, { failures, onCopyAll: copyAllFailures }),
|
|
3669
|
+
hasContent ? showToc ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex gap-6", children: [
|
|
3670
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportToc, {}),
|
|
3671
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportFeatureList, {}) })
|
|
3672
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(ReportFeatureList, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3415
3673
|
ReportEmpty,
|
|
3416
3674
|
{
|
|
3417
3675
|
message: query ? "No scenarios match the search." : void 0
|
|
3418
3676
|
}
|
|
3419
3677
|
),
|
|
3420
|
-
/* @__PURE__ */ (0,
|
|
3678
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3421
3679
|
"button",
|
|
3422
3680
|
{
|
|
3423
3681
|
type: "button",
|
|
@@ -3428,14 +3686,14 @@ function ReportInteractiveView({
|
|
|
3428
3686
|
children: "?"
|
|
3429
3687
|
}
|
|
3430
3688
|
),
|
|
3431
|
-
/* @__PURE__ */ (0,
|
|
3689
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3432
3690
|
ReportShortcutsHelp,
|
|
3433
3691
|
{
|
|
3434
3692
|
open: helpOpen,
|
|
3435
3693
|
onClose: () => setHelpOpen(false)
|
|
3436
3694
|
}
|
|
3437
3695
|
),
|
|
3438
|
-
toast ? /* @__PURE__ */ (0,
|
|
3696
|
+
toast ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3439
3697
|
"div",
|
|
3440
3698
|
{
|
|
3441
3699
|
role: "status",
|
|
@@ -3452,14 +3710,14 @@ function ReportInteractiveView({
|
|
|
3452
3710
|
}
|
|
3453
3711
|
|
|
3454
3712
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
3455
|
-
var
|
|
3713
|
+
var import_react27 = require("react");
|
|
3456
3714
|
|
|
3457
3715
|
// src/interactive/island-renderers.tsx
|
|
3458
|
-
var
|
|
3716
|
+
var import_react26 = require("react");
|
|
3459
3717
|
|
|
3460
3718
|
// src/components/doc/MermaidView.tsx
|
|
3461
|
-
var
|
|
3462
|
-
var
|
|
3719
|
+
var import_react25 = require("react");
|
|
3720
|
+
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
3463
3721
|
function prefersDark(el) {
|
|
3464
3722
|
const themed = el?.closest("[data-theme]");
|
|
3465
3723
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -3467,10 +3725,10 @@ function prefersDark(el) {
|
|
|
3467
3725
|
return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-color-scheme: dark)").matches;
|
|
3468
3726
|
}
|
|
3469
3727
|
function MermaidView({ entry, load }) {
|
|
3470
|
-
const ref = (0,
|
|
3471
|
-
const [svg, setSvg] = (0,
|
|
3472
|
-
const [error, setError] = (0,
|
|
3473
|
-
(0,
|
|
3728
|
+
const ref = (0, import_react25.useRef)(null);
|
|
3729
|
+
const [svg, setSvg] = (0, import_react25.useState)(null);
|
|
3730
|
+
const [error, setError] = (0, import_react25.useState)(null);
|
|
3731
|
+
(0, import_react25.useEffect)(() => {
|
|
3474
3732
|
let cancelled = false;
|
|
3475
3733
|
const draw = async () => {
|
|
3476
3734
|
let mermaid;
|
|
@@ -3513,9 +3771,9 @@ function MermaidView({ entry, load }) {
|
|
|
3513
3771
|
};
|
|
3514
3772
|
}, [entry.code, load]);
|
|
3515
3773
|
if (svg) {
|
|
3516
|
-
return /* @__PURE__ */ (0,
|
|
3517
|
-
entry.title ? /* @__PURE__ */ (0,
|
|
3518
|
-
/* @__PURE__ */ (0,
|
|
3774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("figure", { ref, className: "my-3", children: [
|
|
3775
|
+
entry.title ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
3776
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
3519
3777
|
"div",
|
|
3520
3778
|
{
|
|
3521
3779
|
role: "img",
|
|
@@ -3526,8 +3784,8 @@ function MermaidView({ entry, load }) {
|
|
|
3526
3784
|
)
|
|
3527
3785
|
] });
|
|
3528
3786
|
}
|
|
3529
|
-
return /* @__PURE__ */ (0,
|
|
3530
|
-
error ? /* @__PURE__ */ (0,
|
|
3787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_jsx_runtime53.Fragment, { children: [
|
|
3788
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
3531
3789
|
"p",
|
|
3532
3790
|
{
|
|
3533
3791
|
role: "alert",
|
|
@@ -3539,12 +3797,12 @@ function MermaidView({ entry, load }) {
|
|
|
3539
3797
|
]
|
|
3540
3798
|
}
|
|
3541
3799
|
) : null,
|
|
3542
|
-
/* @__PURE__ */ (0,
|
|
3800
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(MermaidSource, { entry, ref })
|
|
3543
3801
|
] });
|
|
3544
3802
|
}
|
|
3545
3803
|
|
|
3546
3804
|
// src/interactive/island-renderers.tsx
|
|
3547
|
-
var
|
|
3805
|
+
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
3548
3806
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
3549
3807
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
3550
3808
|
function cachedCdnModule(url) {
|
|
@@ -3561,8 +3819,8 @@ function cachedCdnModule(url) {
|
|
|
3561
3819
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
3562
3820
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
3563
3821
|
function CdnHighlightedCode({ entry }) {
|
|
3564
|
-
const [html, setHtml] = (0,
|
|
3565
|
-
(0,
|
|
3822
|
+
const [html, setHtml] = (0, import_react26.useState)(null);
|
|
3823
|
+
(0, import_react26.useEffect)(() => {
|
|
3566
3824
|
let cancelled = false;
|
|
3567
3825
|
void (async () => {
|
|
3568
3826
|
try {
|
|
@@ -3578,27 +3836,27 @@ function CdnHighlightedCode({ entry }) {
|
|
|
3578
3836
|
cancelled = true;
|
|
3579
3837
|
};
|
|
3580
3838
|
}, [entry.content, entry.lang]);
|
|
3581
|
-
return /* @__PURE__ */ (0,
|
|
3839
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3582
3840
|
"code",
|
|
3583
3841
|
{
|
|
3584
3842
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
3585
3843
|
dangerouslySetInnerHTML: { __html: html }
|
|
3586
3844
|
}
|
|
3587
|
-
) : /* @__PURE__ */ (0,
|
|
3845
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
3588
3846
|
}
|
|
3589
3847
|
function buildIslandRenderers(opts) {
|
|
3590
3848
|
const renderers = {};
|
|
3591
3849
|
if (opts.mermaid) {
|
|
3592
|
-
renderers.mermaid = (entry) => (0,
|
|
3850
|
+
renderers.mermaid = (entry) => (0, import_react26.createElement)(MermaidView, { entry, load: loadMermaidFromCdn });
|
|
3593
3851
|
}
|
|
3594
3852
|
if (opts.syntaxHighlighting) {
|
|
3595
|
-
renderers.code = (entry) => (0,
|
|
3853
|
+
renderers.code = (entry) => (0, import_react26.createElement)(CdnHighlightedCode, { entry });
|
|
3596
3854
|
}
|
|
3597
3855
|
return renderers;
|
|
3598
3856
|
}
|
|
3599
3857
|
|
|
3600
3858
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
3601
|
-
var
|
|
3859
|
+
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
3602
3860
|
function ReportInteractiveIsland({
|
|
3603
3861
|
report,
|
|
3604
3862
|
title,
|
|
@@ -3608,11 +3866,11 @@ function ReportInteractiveIsland({
|
|
|
3608
3866
|
syntaxHighlighting = true,
|
|
3609
3867
|
mermaid = true
|
|
3610
3868
|
}) {
|
|
3611
|
-
const renderers = (0,
|
|
3869
|
+
const renderers = (0, import_react27.useMemo)(
|
|
3612
3870
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
3613
3871
|
[syntaxHighlighting, mermaid]
|
|
3614
3872
|
);
|
|
3615
|
-
return /* @__PURE__ */ (0,
|
|
3873
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
3616
3874
|
ReportInteractive,
|
|
3617
3875
|
{
|
|
3618
3876
|
report,
|