@qaick/runner 0.31.2 → 0.32.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/package-dist/package.json +5 -5
- package/package-dist/src/ScreenshotButton.d.ts +5 -0
- package/package-dist/src/ScreenshotButton.d.ts.map +1 -0
- package/package-dist/src/ScreenshotButton.js +54 -0
- package/package-dist/src/ScreenshotButton.js.map +1 -0
- package/package-dist/src/ThemeToggle.d.ts +6 -0
- package/package-dist/src/ThemeToggle.d.ts.map +1 -0
- package/package-dist/src/ThemeToggle.js +6 -0
- package/package-dist/src/ThemeToggle.js.map +1 -0
- package/package-dist/src/runner/HeaderHelp.d.ts +4 -0
- package/package-dist/src/runner/HeaderHelp.d.ts.map +1 -0
- package/package-dist/src/runner/HeaderHelp.js +15 -0
- package/package-dist/src/runner/HeaderHelp.js.map +1 -0
- package/package-dist/src/runner/RunnerModule.d.ts.map +1 -1
- package/package-dist/src/runner/RunnerModule.js +47 -12
- package/package-dist/src/runner/RunnerModule.js.map +1 -1
- package/package-dist/src/runner/catalog.d.ts +6 -0
- package/package-dist/src/runner/catalog.d.ts.map +1 -1
- package/package-dist/src/runner/catalog.js +38 -8
- package/package-dist/src/runner/catalog.js.map +1 -1
- package/package-dist/src/runner/loaded-target.d.ts +13 -2
- package/package-dist/src/runner/loaded-target.d.ts.map +1 -1
- package/package-dist/src/runner/loaded-target.js +20 -4
- package/package-dist/src/runner/loaded-target.js.map +1 -1
- package/package-dist/src/runner/studio-hierarchy-loader.d.ts.map +1 -1
- package/package-dist/src/runner/studio-hierarchy-loader.js +17 -3
- package/package-dist/src/runner/studio-hierarchy-loader.js.map +1 -1
- package/package-dist/styles.css +217 -73
- package/package.json +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qaick/runner",
|
|
3
3
|
"description": "Reusable QAick Runner module and standalone execution application.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.32.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package-dist",
|
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
"publish:release": "npm run check:package && npm publish --tag latest"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@qaick/contract": "0.
|
|
62
|
-
"@qaick/executor": "0.2.
|
|
63
|
-
"@qaick/http-executor": "0.2.
|
|
64
|
-
"@qaick/runtime": "0.2.
|
|
61
|
+
"@qaick/contract": "0.5.0",
|
|
62
|
+
"@qaick/executor": "0.2.6",
|
|
63
|
+
"@qaick/http-executor": "0.2.6",
|
|
64
|
+
"@qaick/runtime": "0.2.7",
|
|
65
65
|
"zustand": "^5.0.15"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScreenshotButton.d.ts","sourceRoot":"","sources":["../../src/ScreenshotButton.tsx"],"names":[],"mappings":"AA0BA,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAMpD;AAED,wBAAgB,gBAAgB,CAAC,EAAC,SAAS,EAAC,EAAE;IAAC,SAAS,EAAC,MAAM,CAAA;CAAC,+BAe/D"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
async function captureBrowserView() {
|
|
4
|
+
if (!navigator.mediaDevices?.getDisplayMedia)
|
|
5
|
+
throw new Error('Screen capture is unavailable in this browser.');
|
|
6
|
+
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false, preferCurrentTab: true });
|
|
7
|
+
const video = document.createElement('video');
|
|
8
|
+
try {
|
|
9
|
+
video.muted = true;
|
|
10
|
+
video.srcObject = stream;
|
|
11
|
+
await video.play();
|
|
12
|
+
if (!video.videoWidth || !video.videoHeight)
|
|
13
|
+
throw new Error('The selected view could not be captured.');
|
|
14
|
+
const canvas = document.createElement('canvas');
|
|
15
|
+
canvas.width = video.videoWidth;
|
|
16
|
+
canvas.height = video.videoHeight;
|
|
17
|
+
const context = canvas.getContext('2d');
|
|
18
|
+
if (!context)
|
|
19
|
+
throw new Error('Screenshot rendering is unavailable.');
|
|
20
|
+
context.drawImage(video, 0, 0);
|
|
21
|
+
return await new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('Screenshot could not be created.')), 'image/png'));
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
stream.getTracks().forEach(track => track.stop());
|
|
25
|
+
video.srcObject = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function copyScreenshot() {
|
|
29
|
+
const desktop = window.qaickScreenshot;
|
|
30
|
+
if (desktop)
|
|
31
|
+
return desktop.copy();
|
|
32
|
+
if (!navigator.clipboard?.write || typeof ClipboardItem === 'undefined')
|
|
33
|
+
throw new Error('Image clipboard access is unavailable in this browser.');
|
|
34
|
+
// Start clipboard access in the click gesture; resolve its PNG after capture permission.
|
|
35
|
+
await navigator.clipboard.write([new ClipboardItem({ 'image/png': captureBrowserView() })]);
|
|
36
|
+
}
|
|
37
|
+
export function ScreenshotButton({ className }) {
|
|
38
|
+
const [state, setState] = useState('idle');
|
|
39
|
+
const [error, setError] = useState('');
|
|
40
|
+
async function copy() {
|
|
41
|
+
setError('');
|
|
42
|
+
setState('copying');
|
|
43
|
+
try {
|
|
44
|
+
await copyScreenshot();
|
|
45
|
+
setState('copied');
|
|
46
|
+
}
|
|
47
|
+
catch (cause) {
|
|
48
|
+
setState('idle');
|
|
49
|
+
setError(cause instanceof DOMException && cause.name === 'NotAllowedError' ? 'Screenshot cancelled or permission denied.' : cause instanceof Error ? cause.message : 'Screenshot could not be copied.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: 6 }, children: [_jsx("button", { type: "button", className: className, onClick: () => void copy(), disabled: state === 'copying', title: "Copy the visible application view as an image. In a browser, select the current QAick tab.", "aria-label": "Copy screenshot", children: state === 'copying' ? 'Copying…' : state === 'copied' ? 'Copied!' : 'Copy screenshot' }), _jsx("span", { role: "status", style: { fontSize: 11, maxWidth: 200 }, children: error })] });
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=ScreenshotButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScreenshotButton.js","sourceRoot":"","sources":["../../src/ScreenshotButton.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAI/B,KAAK,UAAU,kBAAkB;IAC/B,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,eAAe;QAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IAChH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,YAAY,CAAC,eAAe,CAAC,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,KAAK,EAAC,gBAAgB,EAAC,IAAI,EAA8B,CAAC,CAAC;IACzI,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC;QACH,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;QACnB,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;QACzB,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,CAAC,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QACzG,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;QAChC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC;QAClC,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACtE,OAAO,CAAC,SAAS,CAAC,KAAK,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAC7B,OAAO,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAC,MAAM,EAAC,EAAE,CAAA,MAAM,CAAC,MAAM,CAAC,IAAI,CAAA,EAAE,CAAA,IAAI,CAAA,CAAC,CAAA,OAAO,CAAC,IAAI,CAAC,CAAA,CAAC,CAAA,MAAM,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC,EAAC,WAAW,CAAC,CAAC,CAAC;IAC9J,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,KAAK,CAAA,EAAE,CAAA,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAChD,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;IACzB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,OAAO,GAAI,MAA2B,CAAC,eAAe,CAAC;IAC7D,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,IAAI,OAAO,aAAa,KAAK,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IACnJ,yFAAyF;IACzF,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,aAAa,CAAC,EAAC,WAAW,EAAC,kBAAkB,EAAE,EAAC,CAAC,CAAC,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAC,SAAS,EAAqB;IAC9D,MAAM,CAAC,KAAK,EAAC,QAAQ,CAAC,GAAG,QAAQ,CAA4B,MAAM,CAAC,CAAC;IACrE,MAAM,CAAC,KAAK,EAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtC,KAAK,UAAU,IAAI;QACjB,QAAQ,CAAC,EAAE,CAAC,CAAC;QAAA,QAAQ,CAAC,SAAS,CAAC,CAAC;QACjC,IAAI,CAAC;YAAA,MAAM,cAAc,EAAE,CAAC;YAAA,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAAA,CAAC;QAChD,OAAM,KAAK,EAAE,CAAC;YACZ,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjB,QAAQ,CAAC,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAG,iBAAiB,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC;QACxM,CAAC;IACH,CAAC;IACD,OAAO,gBAAM,KAAK,EAAE,EAAC,OAAO,EAAC,aAAa,EAAC,UAAU,EAAC,QAAQ,EAAC,GAAG,EAAC,CAAC,EAAC,aACnE,iBAAQ,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,GAAE,EAAE,CAAA,KAAK,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,KAAG,SAAS,EAAE,KAAK,EAAC,4FAA4F,gBAAY,iBAAiB,YAAE,KAAK,KAAG,SAAS,CAAA,CAAC,CAAA,UAAU,CAAA,CAAC,CAAA,KAAK,KAAG,QAAQ,CAAA,CAAC,CAAA,SAAS,CAAA,CAAC,CAAA,iBAAiB,GAAU,EACvT,eAAM,IAAI,EAAC,QAAQ,EAAC,KAAK,EAAE,EAAC,QAAQ,EAAC,EAAE,EAAC,QAAQ,EAAC,GAAG,EAAC,YAAG,KAAK,GAAQ,IAChE,CAAC;AACV,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../src/ThemeToggle.tsx"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAC,EAAE;IAAC,KAAK,EAAC,OAAO,GAAC,MAAM,CAAC;IAAA,QAAQ,EAAC,CAAC,KAAK,EAAC,OAAO,GAAC,MAAM,KAAG,IAAI,CAAC;IAAA,SAAS,EAAC,MAAM,CAAA;CAAC,+BAGpI"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function ThemeToggle({ theme, onChange, className }) {
|
|
3
|
+
const next = theme === 'dark' ? 'light' : 'dark';
|
|
4
|
+
return _jsx("button", { type: "button", className: className, "aria-label": `Switch to ${next} theme`, title: `Switch to ${next} theme`, onClick: () => onChange(next), children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: theme === 'dark' ? _jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "4" }), _jsx("path", { d: "M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1.5 1.5m11 11L19 19M5 19l1.5-1.5m11-11L19 5" })] }) : _jsx("path", { d: "M20.5 14A8.5 8.5 0 0 1 10 3.5 8.5 8.5 0 1 0 20.5 14Z" }) }) });
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=ThemeToggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeToggle.js","sourceRoot":"","sources":["../../src/ThemeToggle.tsx"],"names":[],"mappings":";AAAA,MAAM,UAAU,WAAW,CAAC,EAAC,KAAK,EAAC,QAAQ,EAAC,SAAS,EAAgF;IACpI,MAAM,IAAI,GAAC,KAAK,KAAG,MAAM,CAAA,CAAC,CAAA,OAAO,CAAA,CAAC,CAAA,MAAM,CAAC;IACzC,OAAO,iBAAQ,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAE,SAAS,gBAAc,aAAa,IAAI,QAAQ,EAAE,KAAK,EAAE,aAAa,IAAI,QAAQ,EAAE,OAAO,EAAE,GAAE,EAAE,CAAA,QAAQ,CAAC,IAAI,CAAC,YAAE,cAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,iBAAa,MAAM,YAAE,KAAK,KAAG,MAAM,CAAA,CAAC,CAAA,8BAAE,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,GAAE,EAAA,eAAM,CAAC,EAAC,8EAA8E,GAAE,IAAG,CAAA,CAAC,CAAA,eAAM,CAAC,EAAC,sDAAsD,GAAE,GAAO,GAAS,CAAC;AAC5hB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeaderHelp.d.ts","sourceRoot":"","sources":["../../../src/runner/HeaderHelp.tsx"],"names":[],"mappings":"AACA,wBAAgB,UAAU,CAAC,EAAC,OAAO,EAAC,EAAC;IAAC,OAAO,EAAC,MAAI,IAAI,CAAA;CAAC,+BAItD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
export function HeaderHelp({ onAbout }) {
|
|
4
|
+
const [open, setOpen] = useState(false);
|
|
5
|
+
const root = useRef(null);
|
|
6
|
+
const trigger = useRef(null);
|
|
7
|
+
useEffect(() => { if (!open)
|
|
8
|
+
return; const close = (event) => { if (event.target instanceof Node && !root.current?.contains(event.target))
|
|
9
|
+
setOpen(false); }; const key = (event) => { if (event.key === 'Escape') {
|
|
10
|
+
setOpen(false);
|
|
11
|
+
trigger.current?.focus();
|
|
12
|
+
} }; window.addEventListener('pointerdown', close); window.addEventListener('keydown', key); return () => { window.removeEventListener('pointerdown', close); window.removeEventListener('keydown', key); }; }, [open]);
|
|
13
|
+
return _jsxs("div", { ref: root, className: "runner-help", children: [_jsx("button", { type: "button", ref: trigger, className: "about-button", "aria-haspopup": "menu", "aria-expanded": open, onClick: () => setOpen(!open), children: "Help" }), open && _jsxs("div", { role: "menu", "aria-label": "Help", className: "runner-help-menu", onClick: () => setOpen(false), children: [_jsx("a", { href: "?demo", children: "View Demo" }), _jsx("button", { type: "button", onClick: onAbout, children: "About QAick" })] })] });
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=HeaderHelp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeaderHelp.js","sourceRoot":"","sources":["../../../src/runner/HeaderHelp.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,EAAC,QAAQ,EAAC,MAAM,OAAO,CAAC;AAChD,MAAM,UAAU,UAAU,CAAC,EAAC,OAAO,EAAoB;IACtD,MAAM,CAAC,IAAI,EAAC,OAAO,CAAC,GAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAAA,MAAM,IAAI,GAAC,MAAM,CAAiB,IAAI,CAAC,CAAC;IAAA,MAAM,OAAO,GAAC,MAAM,CAAoB,IAAI,CAAC,CAAC;IAC3H,SAAS,CAAC,GAAE,EAAE,GAAC,IAAG,CAAC,IAAI;QAAC,OAAO,CAAA,MAAM,KAAK,GAAC,CAAC,KAAkB,EAAC,EAAE,GAAC,IAAG,KAAK,CAAC,MAAM,YAAY,IAAI,IAAE,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;QAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC,CAAA,MAAM,GAAG,GAAC,CAAC,KAAmB,EAAC,EAAE,GAAC,IAAG,KAAK,CAAC,GAAG,KAAG,QAAQ,EAAC,CAAC;QAAA,OAAO,CAAC,KAAK,CAAC,CAAC;QAAA,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;IAAA,CAAC,CAAA,CAAC,CAAC,CAAA,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAC,KAAK,CAAC,CAAC,CAAA,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAC,GAAG,CAAC,CAAC,CAAA,OAAM,GAAE,EAAE,GAAC,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAC,KAAK,CAAC,CAAC,CAAA,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAC,GAAG,CAAC,CAAC,CAAA,CAAC,CAAC,CAAA,CAAC,EAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrc,OAAO,eAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAC,aAAa,aAAC,iBAAQ,IAAI,EAAC,QAAQ,EAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAC,cAAc,mBAAe,MAAM,mBAAgB,IAAI,EAAE,OAAO,EAAE,GAAE,EAAE,CAAA,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAe,EAAC,IAAI,IAAE,eAAK,IAAI,EAAC,MAAM,gBAAY,MAAM,EAAC,SAAS,EAAC,kBAAkB,EAAC,OAAO,EAAE,GAAE,EAAE,CAAA,OAAO,CAAC,KAAK,CAAC,aAAE,YAAG,IAAI,EAAC,OAAO,0BAAc,EAAA,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,OAAO,4BAAsB,IAAM,IAAO,CAAC;AAC5Y,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunnerModule.d.ts","sourceRoot":"","sources":["../../../src/runner/RunnerModule.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RunnerModule.d.ts","sourceRoot":"","sources":["../../../src/runner/RunnerModule.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAQtD,OAAO,KAAK,EAAkB,gBAAgB,EAAmB,eAAe,EAAE,UAAU,EAAE,MAAM,YAAS,CAAC;AAC9G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAkB,CAAC;AAkB5D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AASD,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,QAAoC,EAAE,MAAM,EAAE,EAAE,iBAAiB,+BAEvG;AAkJD,KAAK,WAAW,GAAG;IAAE,iBAAiB,CAAC,EAAC,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAC,OAAO,CAAC;IAAC,YAAY,CAAC,EAAC,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAC,gBAAgB,CAAC;IAAC,cAAc,CAAC,EAAC,MAAM,CAAA;CAAE,CAAC;AAkBvW,wBAAgB,yBAAyB,CAAC,KAAK,EAAC,WAAW,EAAE,EAAC,QAAQ,EAAC,UAAU,EAAE,GAAE,WAAW,EAAE,CAAe;AACjH,eAAO,MAAM,aAAa,GAAE,UAAS,gBAAgB,WAA8D,CAAC;AACpH,wBAAgB,oBAAoB,CAAC,OAAO,EAAC,UAAU,EAAE,EAAC,OAAO,CAAC,EAAC,gBAAgB,GAAE,UAAU,EAAE,CAAkG;AACnM,wBAAgB,mBAAmB,CAAC,OAAO,EAAC,UAAU,EAAE,EAAC,OAAO,CAAC,EAAC,gBAAgB,GAAE,UAAU,GAAC,SAAS,CAAsG;AAC9M,wBAAgB,kBAAkB,CAAC,OAAO,EAAC,UAAU,EAAE,EAAC,OAAO,CAAC,EAAC,gBAAgB,GAAE,eAAe,GAAC,SAAS,CAS3G;AAID,wBAAgB,mBAAmB,CAAC,EAAC,OAAO,EAAC,UAAU,EAAC,EAAC;IAAC,OAAO,EAAC,UAAU,EAAE,CAAC;IAAA,UAAU,CAAC,EAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAA;CAAC,+BAK5H"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { ThemeToggle } from "../ThemeToggle.js";
|
|
3
|
+
import { HeaderHelp } from "./HeaderHelp.js";
|
|
4
|
+
import { ScreenshotButton } from "../ScreenshotButton.js";
|
|
2
5
|
import { invocationBranches } from "./invocation-branches.js";
|
|
3
6
|
import { loopPresentation } from "./loop-presentation.js";
|
|
4
7
|
import { presentationState, eventPresentationEvidence } from "./presentation-state.js";
|
|
@@ -50,7 +53,7 @@ packageCatalog.addPackage(checkoutProjectPackage);
|
|
|
50
53
|
packageCatalog.addPackage(checkoutNestedPackage);
|
|
51
54
|
const restoredImportedPackage = readImportedPackage();
|
|
52
55
|
if (restoredImportedPackage)
|
|
53
|
-
packageCatalog.
|
|
56
|
+
packageCatalog.addLoadedTarget(restoredImportedPackage);
|
|
54
57
|
export function RunnerModule({ client, showDemo = standaloneDemoRequested(), onExit }) {
|
|
55
58
|
return showDemo ? _jsx(RunnerWalkthrough, {}) : _jsx(RunnerApp, { client: client, onExit: onExit });
|
|
56
59
|
}
|
|
@@ -59,6 +62,8 @@ function RunnerApp({ client, onExit }) {
|
|
|
59
62
|
const [theme, setTheme] = useState(readTheme);
|
|
60
63
|
const [showAbout, setShowAbout] = useState(false);
|
|
61
64
|
const [environment, setEnvironment] = useState('test');
|
|
65
|
+
const [selectedTargetId, setSelectedTargetId] = useState(restoredImportedPackage?.executionPackage?.packageId ?? restoredImportedPackage?.authoringSource?.targets[0]?.packageId ?? checkoutPackage.packageId);
|
|
66
|
+
const selectedTarget = packageCatalog.targets().find(t => t.packageId === selectedTargetId);
|
|
62
67
|
const [executionPackage, setExecutionPackage] = useState(restoredImportedPackage?.executionPackage ?? checkoutPackage);
|
|
63
68
|
const [inputEdits, setInputEdits] = useState(() => inputDrafts(inspectDeclaredInputs(restoredImportedPackage?.executionPackage ?? checkoutPackage).declarations));
|
|
64
69
|
const { declarations, conflict, unresolved } = inspectDeclaredInputs(executionPackage);
|
|
@@ -102,9 +107,27 @@ function RunnerApp({ client, onExit }) {
|
|
|
102
107
|
const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress));
|
|
103
108
|
const selectedInvocations = resultsForOccurrence(allResults, displaySteps[selectedStep]?.structuralAddress);
|
|
104
109
|
const visibleEvents = events.filter(event => { const categoryMatches = eventFilter === 'all' || eventFilter === 'parallel' && event.parallel || eventFilter === 'steps' && event.stepId || eventFilter === 'lifecycle' && !event.stepId && !event.parallel || eventFilter === 'failures' && (event.state === 'failed' || event.type.includes('error')); const searchMatches = !eventSearch.trim() || `${event.type} ${event.state} ${event.message} ${event.stepName ?? ''}`.toLowerCase().includes(eventSearch.trim().toLowerCase()); return categoryMatches && searchMatches; });
|
|
110
|
+
function selectTarget(id) { setSelectedTargetId(id); const descriptor = packageCatalog.targets().find(t => t.packageId === id); if (descriptor?.issues?.length) {
|
|
111
|
+
setRequestId(undefined);
|
|
112
|
+
setViewingHistoryEntry(undefined);
|
|
113
|
+
setControlState("idle");
|
|
114
|
+
setRunState("ready");
|
|
115
|
+
return;
|
|
116
|
+
} const next = packageCatalog.get(id); setExecutionPackage(next); if (!inspectDeclaredInputs(next).conflict && !inspectDeclaredInputs(next).unresolved)
|
|
117
|
+
setInputEdits(inputDrafts(inspectDeclaredInputs(next).declarations)); setBreakpoints(new Set()); breakpointsRef.current = new Set(); setStopAfterStepId(undefined); setLoadError(undefined); setLoadIssues([]); setRuntimeValues(Object.fromEntries((next.runtimeRequirements ?? []).map(name => [name, '']))); setStepStates((next.target.kind === 'project' ? next.flows?.flatMap(flow => flattenDisplaySteps(flow.steps)) ?? [] : flattenDisplaySteps(next.steps)).map(() => 'pending')); setSelectedStep(0); setResult(undefined); setEvents([]); setRequestId(undefined); setViewingHistoryEntry(undefined); setControlState('idle'); setRunState('ready'); }
|
|
105
118
|
async function run(dryRun = false) {
|
|
106
119
|
if (inputBlocker || inputError)
|
|
107
120
|
return;
|
|
121
|
+
let currentPackage;
|
|
122
|
+
try {
|
|
123
|
+
if (selectedTarget?.issues?.length)
|
|
124
|
+
throw new Error(selectedTarget.issues.join("\n"));
|
|
125
|
+
currentPackage = packageCatalog.prepare(executionPackage);
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
setLoadError(error instanceof Error ? error.message : "Source target cannot execute.");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
108
131
|
const id = crypto.randomUUID();
|
|
109
132
|
setViewingHistoryEntry(undefined);
|
|
110
133
|
setRequestId(id);
|
|
@@ -114,7 +137,7 @@ function RunnerApp({ client, onExit }) {
|
|
|
114
137
|
setStepStates(displaySteps.map(() => 'pending'));
|
|
115
138
|
setRunState('preflighting');
|
|
116
139
|
setControlState('running');
|
|
117
|
-
await client.submit({ requestId: id, executionPackage, inputs, environment, runtimeValues, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfterStepId } : {}) }, options: { ...(timeoutMs === 'none' ? {} : { timeoutMs: Number(timeoutMs) }), dryRun } });
|
|
140
|
+
await client.submit({ requestId: id, executionPackage: currentPackage, inputs, environment, runtimeValues, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfterStepId } : {}) }, options: { ...(timeoutMs === 'none' ? {} : { timeoutMs: Number(timeoutMs) }), dryRun } });
|
|
118
141
|
void (async () => {
|
|
119
142
|
for await (const event of client.events(id)) {
|
|
120
143
|
setEvents(previous => [...previous, redactExecutionEvent(event, collectSecretValues(inputs, runtimeValues))]);
|
|
@@ -171,6 +194,7 @@ function RunnerApp({ client, onExit }) {
|
|
|
171
194
|
try {
|
|
172
195
|
loadedPackage = packageCatalog.get(entry.packageId);
|
|
173
196
|
setExecutionPackage(loadedPackage);
|
|
197
|
+
setSelectedTargetId(loadedPackage.packageId);
|
|
174
198
|
}
|
|
175
199
|
catch { /* The result remains viewable when its package is no longer loaded. */ }
|
|
176
200
|
} const steps = loadedPackage.target.kind === 'project' ? loadedPackage.flows?.flatMap(flow => flow.steps) ?? [] : loadedPackage.steps; const storedSteps = flattenResultSteps(entry.result.children?.flatMap(child => child.steps) ?? entry.result.steps); const states = new Map(flattenDisplaySteps(steps).map(step => [occurrenceKey(step.structuralAddress), stateForOccurrence(storedSteps, step.structuralAddress) ?? 'pending'])); setRequestId(entry.requestId); setEnvironment(entry.environment); setResult(entry.result); setViewingHistoryEntry(entry.requestId); setEvents([]); setSelectedStep(0); setStepStates(flattenDisplaySteps(steps).map(step => states.get(occurrenceKey(step.structuralAddress)) ?? 'pending')); setRunState(entry.state); document.querySelector('.runner-workspace')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
|
|
@@ -190,7 +214,19 @@ function RunnerApp({ client, onExit }) {
|
|
|
190
214
|
try {
|
|
191
215
|
const target = loadTarget(await file.text());
|
|
192
216
|
const loaded = target.executionPackage;
|
|
193
|
-
packageCatalog.
|
|
217
|
+
const selected = packageCatalog.addLoadedTarget(target);
|
|
218
|
+
setSelectedTargetId(selected);
|
|
219
|
+
if (!loaded) {
|
|
220
|
+
localStorage.setItem(IMPORTED_PACKAGE_KEY, JSON.stringify(target));
|
|
221
|
+
setCatalogVersion(v => v + 1);
|
|
222
|
+
setRequestId(undefined);
|
|
223
|
+
setViewingHistoryEntry(undefined);
|
|
224
|
+
setControlState("idle");
|
|
225
|
+
setRunState("ready");
|
|
226
|
+
setLoadError(undefined);
|
|
227
|
+
setLoadIssues([]);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
194
230
|
localStorage.setItem(IMPORTED_PACKAGE_KEY, JSON.stringify(target));
|
|
195
231
|
setCatalogVersion(version => version + 1);
|
|
196
232
|
setExecutionPackage(loaded);
|
|
@@ -232,17 +268,16 @@ function RunnerApp({ client, onExit }) {
|
|
|
232
268
|
close(); }; document.addEventListener('click', close); document.addEventListener('keydown', keydown); return () => { document.removeEventListener('click', close); document.removeEventListener('keydown', keydown); }; }, []);
|
|
233
269
|
useEffect(() => { const navigateIssue = (event) => { const target = event.target; const issue = target.closest('.validation-issue'); const stepId = issue?.dataset.stepId; const eventRow = target.closest('.event-row'); const index = stepId ? displaySteps.findIndex(step => step.id === stepId || step.id.endsWith(`.${stepId}`)) : eventRow ? displaySteps.findIndex(step => eventRow.textContent?.includes(step.name)) : -1; if (index >= 0)
|
|
234
270
|
setSelectedStep(index); }; document.addEventListener('click', navigateIssue); return () => document.removeEventListener('click', navigateIssue); }, [displaySteps]);
|
|
235
|
-
return _jsxs("div", { className: "runner-shell", children: [_jsxs("header", { className: "runner-header", children: [_jsxs("div", { className: "runner-brand", children: [_jsx("span", { className: "runner-brand-mark", children: "QAICK" }), _jsx("span", { className: "runner-brand-product", children: "Runner" }), _jsx("span", { className: "alpha-label", children: RUNNER_UI_VERSION }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), _jsx("span", { className: "header-spacer" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json", download: true, title: "Download QAick Local HTTP Regression. Start the local fixture on port 5197, then load this file. Runner executes both Flows. Editing is restricted in Studio.", children: "HTTP example" }), _jsx("a", { className: "demo-entry", href: "?demo", children: "View demo" }), _jsx("button", { className: "about-button", onClick: () => setShowAbout(true), children: "About" }), _jsxs("label", { className: "theme-picker", children: ["Theme", _jsxs("select", { value: theme, onChange: event => setTheme(event.target.value), children: [_jsx("option", { value: "light", children: "Light" }), _jsx("option", { value: "dark", children: "Dark" })] })] })] }), _jsxs("main", { className: "runner-main", children: [_jsxs("nav", { className: "runner-nav", children: [_jsx("p", { className: "nav-label", children: "Workspace" }), _jsx(NavItem, { active: true, icon: "\u25C8", text: "Execution Package" }), _jsx(NavItem, { icon: "\u25A1", text: "Projects" }), _jsx(NavItem, { icon: "\u2318", text: "Flows" }), _jsx(NavItem, { icon: "\u21B6", text: "Run History" }), _jsxs("div", { className: "package-card", children: [_jsx("small", { children: "Loaded package" }), _jsxs("strong", { children: [executionPackage.packageId, ".qaick.json"] }), _jsxs("small", { children: ["Execution v", executionPackage.schemaVersion, " \u00B7 ", executionPackage.definitions.length, " definitions"] })] })] }), _jsxs("section", { className: "runner-workspace", children: [_jsx("div", { className: "eyebrow", children: "EXECUTION PACKAGE" }), _jsx("h1", { children: executionPackage.target.name }), _jsxs("p", { className: "description", children: ["Execute the selected QAick ", executionPackage.target.kind === 'project' ? 'Project' : 'Flow', " and capture structured evidence."] }), _jsxs("div", { className: "run-controls", children: [_jsxs("label", { children: ["Target", _jsx("select", { value: executionPackage.packageId, onChange: event => { const next = packageCatalog.get(event.target.value); setExecutionPackage(next); if (!inspectDeclaredInputs(next).conflict && !inspectDeclaredInputs(next).unresolved)
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
setSelectedStep(Math.max(0, index - 1));
|
|
242
|
-
} }, onClick: () => setSelectedStep(index), onContextMenu: event => { event.preventDefault(); setSelectedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { children: [_jsx("span", { className: `step-icon ${isLoop ? 'loop-icon' : displayState}`, children: isLoop ? '↻' : displayState === 'finished' ? '✓' : displayState === 'waiting' ? '◷' : displayState === 'skipped' ? '–' : '' }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.depth > 0 && _jsx("span", { className: "branch-marker", children: "\u2514" }), step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${collapsedSteps.has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: collapsedSteps.has(step.id) ? '+' : '−' }), _jsx("span", { children: step.name }), breakpoints.has(step.id) && _jsx("small", { className: "breakpoint-marker", "aria-label": "Breakpoint armed", title: "Breakpoint armed", children: "STOP" }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "LOOP" }), step.depth > 0 ? _jsx("small", { className: "kind-label nested-label", children: "NESTED STEP" }) : !isLoop && _jsx("small", { className: "kind-label", children: step.kind.toUpperCase() })] }), selectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [loopContexts.map(context => _jsxs("div", { className: "loop-progress", children: [context.context.length > 0 && _jsxs("small", { children: ["Enclosing iterations: ", context.context.map(s => s.iteration).join(' / ')] }), _jsx("strong", { children: context.label }), _jsx("small", { children: context.state.toUpperCase() })] }, JSON.stringify(context.context))), _jsx("span", { className: `status ${displayState}`, children: displayState.toUpperCase() }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: displayResults[index] ? new Date(result?.finishedAt ?? '').toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }), contextMenu && _jsxs("div", { className: "runner-context-menu", style: { left: contextMenu.x, top: contextMenu.y }, onClick: event => event.stopPropagation(), onContextMenu: event => event.preventDefault(), children: [_jsx("strong", { children: "Breakpoint" }), breakpoints.has(contextMenu.stepId) ? _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Remove breakpoint" }) : _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Add breakpoint" })] })] }), _jsxs("section", { className: "panel event-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Event Log" }), _jsx("span", { className: "panel-spacer" }), _jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { className: "event-filter", value: eventFilter, onChange: event => setEventFilter(event.target.value), children: [_jsx("option", { value: "all", children: "All events" }), _jsx("option", { value: "steps", children: "Step events" }), _jsx("option", { value: "parallel", children: "Parallel groups" }), _jsx("option", { value: "lifecycle", children: "Lifecycle" }), _jsx("option", { value: "failures", children: "Failures" })] })] }), _jsx("table", { children: _jsx("tbody", { children: visibleEvents.map(event => _jsxs("tr", { className: "event-row", children: [_jsx("td", { children: new Date(event.timestamp).toLocaleTimeString() }), _jsx("td", { children: event.state.toUpperCase() }), _jsx("td", { children: _jsx("code", { children: event.type.toUpperCase() }) }), _jsxs("td", { children: [event.message, event.parallel && _jsxs("small", { className: "parallel-event", children: [" \u00B7 Parallel ", event.parallel.completed, "/", event.parallel.total, " (", event.parallel.join, ")"] })] })] }, event.sequence)) }) }), events.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." })] }), _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport: exportResult })] }), _jsxs("aside", { className: "inspector", children: [_jsx("h2", { children: "Selected execution" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[selectedStep]?.name ?? 'No step selected' }) }), _jsxs("p", { className: "run-id", children: ["Run ID: ", requestId ?? 'No run started'] }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", executionPackage.target.name, "Type", executionPackage.target.kind, "Package", executionPackage.packageId, "Execution format", `Execution v${executionPackage.schemaVersion}`, "Definitions", String(executionPackage.definitions.length), "Required inputs", (executionPackage.requiredInputs ?? []).join(", ") || "None", "Runtime values", (executionPackage.runtimeRequirements ?? []).join(", ") || "None"] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: result?.state.toUpperCase() ?? 'NOT STARTED' }), _jsx("br", {}), result ? `${result.environment} · ${result.state}` : 'Run the package to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { values: Object.entries(displayResults[selectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { values: displayResults[selectedStep]?.outputs ? Object.entries(displayResults[selectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (result?.error || displayResults[selectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [_jsx("strong", { children: displayResults[selectedStep]?.error?.code ?? result?.error?.code }), _jsx("br", {}), displayResults[selectedStep]?.error?.message ?? result?.error?.message] }) }), result?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: result.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: _jsx("div", { className: "notice", children: displayResults[selectedStep]?.invocationPath?.join(" › ") ?? "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(packageSteps, displaySteps[selectedStep]?.structuralAddress) }, JSON.stringify([requestId, displaySteps[selectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", result?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", result?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", result?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[selectedStep]?.attempts?.map(attempt => _jsxs("div", { className: "iteration-entry", children: [_jsxs("strong", { children: ["Attempt ", attempt.attempt] }), _jsxs("span", { children: [(attempt.kind ?? 'automatic').toUpperCase(), " \u00B7 ", attempt.state.toUpperCase(), " \u00B7 ", attempt.durationMs, " ms"] })] }, `${attempt.attempt}-${attempt.kind}`)) ?? _jsx("div", { className: "notice", children: "No retry attempts recorded." }) }) }), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[selectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[selectedStep] ? displayResults[selectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[selectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the package to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: !displayResults[selectedStep] || (!displayResults[selectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[selectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[selectedStep] && toggleStopAfter(displaySteps[selectedStep].id), disabled: !displaySteps[selectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: !displayResults[selectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: selectedStep <= 0, onClick: () => setSelectedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${selectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: selectedStep >= displaySteps.length - 1, onClick: () => setSelectedStep(index => Math.min(displaySteps.length - 1, index + 1)), children: "Next \u203A" })] })] })] })] }), showAbout && _jsx("div", { className: "about-backdrop", onClick: () => setShowAbout(false), children: _jsxs("section", { className: "about-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "about-title", onClick: event => event.stopPropagation(), children: [_jsx("button", { className: "about-close", onClick: () => setShowAbout(false), "aria-label": "Close About", children: "\u00D7" }), _jsx("div", { className: "runner-logo", children: "\u25B6" }), _jsx("h2", { id: "about-title", children: "QAick Runner" }), _jsx("p", { children: "Standalone execution and evidence viewer for QAICK packages." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
271
|
+
return _jsxs("div", { className: "runner-shell", children: [_jsxs("header", { className: "runner-header", children: [_jsxs("div", { className: "runner-brand", children: [_jsx("span", { className: "runner-brand-mark", children: "QAICK" }), _jsx("span", { className: "runner-brand-product", children: "Runner" }), _jsx("span", { className: "alpha-label", children: RUNNER_UI_VERSION }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), _jsx("span", { className: "header-spacer" }), _jsx(ScreenshotButton, { className: "about-button" }), _jsx("a", { className: "demo-entry", href: "./examples/qaick-local-http-regression.qaick.json", download: true, title: "Download QAick Local HTTP Regression. Start the local fixture on port 5197, then load this file. Runner executes both Flows. Editing is restricted in Studio.", children: "HTTP example" }), _jsx(HeaderHelp, { onAbout: () => setShowAbout(true) }), _jsx(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsxs("main", { className: "runner-main", children: [selectedTarget?.issues?.length ? _jsxs("section", { className: "runner-workspace runner-authoring-source", children: [_jsx("div", { className: "eyebrow", children: "AUTHORING SOURCE" }), _jsx("h1", { children: selectedTarget.target.name }), _jsx("div", { className: "run-settings", children: _jsxs("label", { children: ["Target", _jsx("select", { value: selectedTargetId, onChange: event => selectTarget(event.target.value), children: packageCatalog.targets().map(t => _jsx("option", { value: t.packageId, children: t.target.name }, t.packageId)) })] }) }), _jsxs("div", { className: "notice", children: [_jsx("strong", { children: "Not executable" }), _jsx("p", { children: "This source has no validated executable structure. Complete and validate it in Studio, then reload the saved Project. Valid sibling Flows remain available in Target." })] }), _jsx("ul", { role: "alert", children: selectedTarget.issues.map((issue, index) => _jsx("li", { children: issue }, index)) }), _jsxs("label", { className: "file-button", children: ["Load package", _jsx("input", { type: "file", accept: "application/json,.json", onChange: event => void loadPackage(event.target.files?.[0]) })] }), loadError && _jsx("p", { role: "alert", children: loadError })] }) : _jsxs(_Fragment, { children: [_jsxs("nav", { className: "runner-nav", children: [_jsx("p", { className: "nav-label", children: "Workspace" }), _jsx(NavItem, { active: true, icon: "\u25C8", text: "Execution Package" }), _jsx(NavItem, { icon: "\u25A1", text: "Projects" }), _jsx(NavItem, { icon: "\u2318", text: "Flows" }), _jsx(NavItem, { icon: "\u21B6", text: "Run History" }), _jsxs("div", { className: "package-card", children: [_jsx("small", { children: "Loaded package" }), _jsxs("strong", { children: [executionPackage.packageId, ".qaick.json"] }), _jsxs("small", { children: ["Execution v", executionPackage.schemaVersion, " \u00B7 ", executionPackage.definitions.length, " definitions"] })] })] }), _jsxs("section", { className: "runner-workspace", children: [_jsx("div", { className: "eyebrow", children: "EXECUTION PACKAGE" }), _jsx("h1", { children: executionPackage.target.name }), _jsxs("p", { className: "description", children: ["Execute the selected QAick ", executionPackage.target.kind === 'project' ? 'Project' : 'Flow', " and capture structured evidence."] }), _jsxs("div", { className: "run-controls", children: [_jsxs("div", { className: "run-settings", role: "group", "aria-label": "Execution setup", children: [_jsxs("label", { children: ["Target", _jsx("select", { value: selectedTargetId, onChange: event => selectTarget(event.target.value), children: packageCatalog.targets().map(item => _jsx("option", { value: item.packageId, children: item.target.name }, item.packageId)) })] }), _jsxs("label", { children: ["Environment", _jsxs("select", { value: environment, onChange: event => setEnvironment(event.target.value), children: [_jsx("option", { children: "test" }), _jsx("option", { children: "local" }), _jsx("option", { children: "staging" })] })] }), _jsxs("label", { children: ["Timeout", _jsxs("select", { value: timeoutMs, onChange: event => setTimeoutMs(event.target.value), children: [_jsx("option", { value: "none", children: "None" }), _jsx("option", { value: "1000", children: "1 second" }), _jsx("option", { value: "3000", children: "3 seconds" }), _jsx("option", { value: "10000", children: "10 seconds" })] })] }), _jsxs("label", { children: ["Run configuration", _jsxs("select", { defaultValue: "Default", children: [_jsx("option", { children: "Default" }), _jsx("option", { children: "Debug" })] })] })] }), _jsxs("div", { className: "run-actions", role: "group", "aria-label": "Execution actions", children: [_jsxs("label", { className: "file-button", children: ["Load package", _jsx("input", { type: "file", accept: "application/json,.json", onChange: event => void loadPackage(event.target.files?.[0]) })] }), _jsx("button", { className: "button", onClick: () => setEditingInputs(value => !value), children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: !!inputBlocker || runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: !!inputBlocker || runState === "preflighting" || runState === "running", children: "\u25CE\u00A0 Pre-run validation" }), _jsx("button", { className: "button stop", onClick: () => void stop(), disabled: runState !== 'preflighting' && runState !== 'running', children: "\u25A0\u00A0 Stop" }), _jsx("button", { className: "button", onClick: () => void pause(), disabled: controlState !== 'running', children: "\u2161\u00A0 Pause" }), _jsx("button", { className: `button ${controlState === 'paused' ? 'resume-highlight' : ''}`, onClick: () => void resume(), disabled: controlState !== 'paused', children: "\u25B6\u00A0 Resume" }), _jsx("button", { className: "button", onClick: () => void stepExecution(), disabled: controlState !== 'paused', children: "\u21A6\u00A0 Step" }), breakpoints.size > 0 && _jsxs("span", { className: "debug-session-badge", children: [breakpoints.size, " breakpoint", breakpoints.size === 1 ? "" : "s", " armed"] })] })] }), viewingHistoryEntry && _jsxs("div", { className: "history-view-banner", children: ["Viewing stored results \u00B7 ", viewingHistoryEntry] }), conflict && _jsxs("section", { role: "alert", children: [_jsx("p", { children: conflict.message }), _jsx("ul", { children: conflict.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.declaration.name, " \u00B7 ", entry.declaration.type, " \u00B7 ", entry.declaration.required ? 'required' : 'optional', " \u00B7 default: ", Object.hasOwn(entry.declaration, 'defaultValue') ? JSON.stringify(entry.declaration.defaultValue) : 'none'] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), unresolved && _jsxs("section", { role: "alert", children: [_jsx("p", { children: unresolved.message }), _jsx("ul", { children: unresolved.entries.map((entry, index) => _jsxs("li", { children: [entry.owner, " \u00B7 ", entry.name, " \u00B7 required \u00B7 type unresolved"] }, index)) }), _jsx("p", { children: "Retained input drafts:" }), _jsx("ul", { children: Object.entries(inputEdits).map(([name, draft]) => _jsxs("li", { children: [name, " \u00B7 ", draft.supplied ? 'supplied' : 'omitted'] }, name)) })] }), inputError && _jsx("p", { role: "alert", children: inputError }), loadError && _jsx("p", { className: "load-error", children: loadError }), loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), result?.issues !== undefined && _jsx(PreRunValidation, { issues: result.issues }), result?.warnings !== undefined && result.warnings.length > 0 && _jsx(PreRunValidation, { issues: result.warnings, title: "Browser execution warnings" }), editingInputs && _jsxs("section", { className: "inputs-panel panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Invocation Inputs" }), _jsx("span", { className: "panel-spacer" }), _jsx("span", { className: "input-hint", children: "Values are sent with the next run" })] }), _jsxs("div", { className: "input-grid", children: [inputBlocker ? _jsx("p", { children: "Invocation input editing is unavailable until requirements are resolved." }) : declarations.map(declaration => { const draft = inputEdits[declaration.name] ?? { supplied: false, text: '' }; const change = (text) => setInputEdits(previous => ({ ...previous, [declaration.name]: { supplied: true, text } })); return _jsxs("div", { children: [_jsxs("label", { children: [_jsx("input", { type: "checkbox", "aria-label": `Supply ${declaration.name}`, checked: draft.supplied, onChange: event => setInputEdits(previous => ({ ...previous, [declaration.name]: { ...draft, supplied: event.target.checked } })) }), "Supply ", declaration.name] }), declaration.type === 'boolean' ? _jsxs("label", { className: "input-field", children: [declaration.name, _jsxs("select", { value: draft.text, onChange: event => change(event.target.value), children: [_jsx("option", { value: "false", children: "false" }), _jsx("option", { value: "true", children: "true" })] })] }) : _jsx(InputField, { label: declaration.name, value: draft.text, onChange: change }), _jsxs("small", { children: [declaration.type, " \u00B7 ", draft.supplied ? 'supplied' : Object.hasOwn(declaration, 'defaultValue') ? 'omitted — declared default applies' : 'omitted', declaration.required ? ' · required' : ''] })] }, declaration.name); }), (executionPackage.runtimeRequirements ?? []).map(name => _jsx(InputField, { label: `Runtime · ${name}`, value: runtimeValues[name] ?? '', onChange: value => setRuntimeValues(previous => ({ ...previous, [name]: value })) }, name))] })] }), _jsxs("section", { className: "panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Live Execution" }), _jsx("span", { className: "panel-spacer" }), _jsxs("span", { className: "execution-menu-label", children: ["Right-click a row to manage its breakpoint \u00B7 Selected: ", displaySteps[selectedStep]?.name ?? 'None'] }), _jsxs("span", { className: runState === 'running' ? 'running' : runState === 'failed' ? 'stopped' : 'run-status', children: ["\u25CF ", statusLabel] })] }), hasNestedSteps && _jsxs("div", { className: "hierarchy-hint", children: [_jsx("strong", { children: "Nested execution" }), _jsx("span", { children: "Use \u2212 / + to collapse or expand nested Operations, Control Blocks, and loops." }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(true), children: "Collapse all" }), _jsx("button", { className: "hierarchy-action", onClick: () => setAllCollapsed(false), children: "Expand all" })] }), _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Execution steps", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "#" }), _jsx("th", { children: "Step hierarchy" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Start time" }), _jsx("th", { children: "Duration" })] }) }), _jsx("tbody", { children: displaySteps.map((step, index) => { const isLoop = step.loopRepeat; const loopContexts = isLoop ? loopPresentation(step.structuralAddress, step.loopTotal, step.loopDuration, result ? allResults : eventPresentationEvidence(events), Boolean(result)) : []; const rowEvidence = result ? allResults : eventPresentationEvidence(events); const repeatedMatches = rowEvidence.filter(r => occurrenceKey(r.structuralAddress) === occurrenceKey(step.structuralAddress)); const latestQualifier = step.branchRow ? rowEvidence.filter(r => occurrenceKey(r.structuralAddress) === occurrenceKey(step.branchOwner)).length > 1 : repeatedMatches.length > 1; const displayState = presentationState(step, step.branchRow ? rowEvidence : latestQualifier ? [repeatedMatches.at(-1)] : rowEvidence, Boolean(result), step.branchRow ? 'pending' : stepStates[index] ?? 'pending'); const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; return _jsxs("tr", { className: `${selectedStep === index ? 'selected ' : ''}${isLoop ? 'loop-row ' : ''}${isLoopContent ? 'loop-content ' : ''}${isLoopEnd ? 'loop-end ' : ''}${step.depth > 0 ? 'nested-row' : 'parent-row'}`, tabIndex: 0, onKeyDown: event => { if (event.key === "ArrowDown") {
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
setSelectedStep(Math.min(displaySteps.length - 1, index + 1));
|
|
274
|
+
} if (event.key === "ArrowUp") {
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
setSelectedStep(Math.max(0, index - 1));
|
|
277
|
+
} }, onClick: () => setSelectedStep(index), onContextMenu: event => { event.preventDefault(); setSelectedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { children: [_jsx("span", { className: `step-icon ${isLoop ? 'loop-icon' : displayState}`, children: isLoop ? '↻' : displayState === 'finished' ? '✓' : displayState === 'waiting' ? '◷' : displayState === 'skipped' ? '–' : '' }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.depth > 0 && _jsx("span", { className: "branch-marker", children: "\u2514" }), step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${collapsedSteps.has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: collapsedSteps.has(step.id) ? '+' : '−' }), _jsx("span", { children: step.name }), breakpoints.has(step.id) && _jsx("small", { className: "breakpoint-marker", "aria-label": "Breakpoint armed", title: "Breakpoint armed", children: "STOP" }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "LOOP" }), step.depth > 0 ? _jsx("small", { className: "kind-label nested-label", children: "NESTED STEP" }) : !isLoop && _jsx("small", { className: "kind-label", children: step.kind.toUpperCase() })] }), selectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [loopContexts.map(context => _jsxs("div", { className: "loop-progress", children: [context.context.length > 0 && _jsxs("small", { children: ["Enclosing iterations: ", context.context.map(s => s.iteration).join(' / ')] }), _jsx("strong", { children: context.label }), _jsx("small", { children: context.state.toUpperCase() })] }, JSON.stringify(context.context))), _jsx("span", { className: `status ${displayState}`, children: displayState.toUpperCase() }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: displayResults[index] ? new Date(result?.finishedAt ?? '').toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }) }), contextMenu && _jsxs("div", { className: "runner-context-menu", style: { left: contextMenu.x, top: contextMenu.y }, onClick: event => event.stopPropagation(), onContextMenu: event => event.preventDefault(), children: [_jsx("strong", { children: "Breakpoint" }), breakpoints.has(contextMenu.stepId) ? _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Remove breakpoint" }) : _jsx("button", { onClick: () => { toggleBreakpoint(contextMenu.stepId); setContextMenu(undefined); }, children: "Add breakpoint" })] })] }), _jsxs("section", { className: "panel event-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Event Log" }), _jsx("span", { className: "panel-spacer" }), _jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { className: "event-filter", value: eventFilter, onChange: event => setEventFilter(event.target.value), children: [_jsx("option", { value: "all", children: "All events" }), _jsx("option", { value: "steps", children: "Step events" }), _jsx("option", { value: "parallel", children: "Parallel groups" }), _jsx("option", { value: "lifecycle", children: "Lifecycle" }), _jsx("option", { value: "failures", children: "Failures" })] })] }), _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Execution events", tabIndex: 0, children: _jsx("table", { children: _jsx("tbody", { children: visibleEvents.map(event => _jsxs("tr", { className: "event-row", children: [_jsx("td", { children: new Date(event.timestamp).toLocaleTimeString() }), _jsx("td", { children: event.state.toUpperCase() }), _jsx("td", { children: _jsx("code", { children: event.type.toUpperCase() }) }), _jsxs("td", { children: [event.message, event.parallel && _jsxs("small", { className: "parallel-event", children: [" \u00B7 Parallel ", event.parallel.completed, "/", event.parallel.total, " (", event.parallel.join, ")"] })] })] }, event.sequence)) }) }) }), events.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." })] }), _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport: exportResult })] }), _jsxs("aside", { className: "inspector", children: [_jsx("h2", { children: "Selected execution" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[selectedStep]?.name ?? 'No step selected' }) }), _jsxs("p", { className: "run-id", children: ["Run ID: ", requestId ?? 'No run started'] }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", executionPackage.target.name, "Type", executionPackage.target.kind, "Package", executionPackage.packageId, "Execution format", `Execution v${executionPackage.schemaVersion}`, "Definitions", String(executionPackage.definitions.length), "Required inputs", (executionPackage.requiredInputs ?? []).join(", ") || "None", "Runtime values", (executionPackage.runtimeRequirements ?? []).join(", ") || "None"] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: result?.state.toUpperCase() ?? 'NOT STARTED' }), _jsx("br", {}), result ? `${result.environment} · ${result.state}` : 'Run the package to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { values: Object.entries(displayResults[selectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { values: displayResults[selectedStep]?.outputs ? Object.entries(displayResults[selectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (result?.error || displayResults[selectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [_jsx("strong", { children: displayResults[selectedStep]?.error?.code ?? result?.error?.code }), _jsx("br", {}), displayResults[selectedStep]?.error?.message ?? result?.error?.message] }) }), result?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: result.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: _jsx("div", { className: "notice", children: displayResults[selectedStep]?.invocationPath?.join(" › ") ?? "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(packageSteps, displaySteps[selectedStep]?.structuralAddress) }, JSON.stringify([requestId, displaySteps[selectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", result?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", result?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", result?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[selectedStep]?.attempts?.map(attempt => _jsxs("div", { className: "iteration-entry", children: [_jsxs("strong", { children: ["Attempt ", attempt.attempt] }), _jsxs("span", { children: [(attempt.kind ?? 'automatic').toUpperCase(), " \u00B7 ", attempt.state.toUpperCase(), " \u00B7 ", attempt.durationMs, " ms"] })] }, `${attempt.attempt}-${attempt.kind}`)) ?? _jsx("div", { className: "notice", children: "No retry attempts recorded." }) }) }), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[selectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[selectedStep] ? displayResults[selectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[selectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the package to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: !displayResults[selectedStep] || (!displayResults[selectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[selectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[selectedStep] && toggleStopAfter(displaySteps[selectedStep].id), disabled: !displaySteps[selectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: !displayResults[selectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: selectedStep <= 0, onClick: () => setSelectedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${selectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: selectedStep >= displaySteps.length - 1, onClick: () => setSelectedStep(index => Math.min(displaySteps.length - 1, index + 1)), children: "Next \u203A" })] })] })] })] }), " "] }), showAbout && _jsx("div", { className: "about-backdrop", onClick: () => setShowAbout(false), children: _jsxs("section", { className: "about-dialog", role: "dialog", "aria-modal": "true", "aria-labelledby": "about-title", onClick: event => event.stopPropagation(), children: [_jsx("button", { className: "about-close", onClick: () => setShowAbout(false), "aria-label": "Close About", children: "\u00D7" }), _jsx("div", { className: "runner-logo", children: "\u25B6" }), _jsx("h2", { id: "about-title", children: "QAick Runner" }), _jsx("p", { children: "Standalone execution and evidence viewer for QAICK packages." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
243
278
|
}
|
|
244
279
|
function NavItem({ icon, text, active = false }) { const scrollTarget = text === 'Run History' ? '.history-panel' : text === 'Flows' ? '.runner-workspace table' : '.runner-workspace'; return _jsxs("a", { className: `nav-item ${active ? 'active' : ''}`, href: `#${text.toLowerCase().replaceAll(' ', '-')}`, onClick: event => { event.preventDefault(); document.querySelector(scrollTarget)?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, children: [_jsx("span", { children: icon }), text] }); }
|
|
245
|
-
function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete, onExport, onReload }) { return _jsxs("section", { className: "panel history-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Run History" }), _jsx("span", { className: "panel-spacer" }), _jsxs("label", { className: "history-retention", children: ["Keep", _jsxs("select", { "aria-label": "Run history retention", value: retention, onChange: event => onRetentionChange(Number(event.target.value)), children: [_jsx("option", { value: "10", children: "10 runs" }), _jsx("option", { value: "20", children: "20 runs" }), _jsx("option", { value: "50", children: "50 runs" }), _jsx("option", { value: "100", children: "100 runs" })] })] }), _jsx("button", { className: "button", onClick: onClear, disabled: !entries.length, children: "Clear history" })] }), entries.length ? _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Target" }), _jsx("th", { children: "Started" }), _jsx("th", { children: "State" }), _jsx("th", { children: "Duration" }), _jsx("th", { children: "Result" })] }) }), _jsx("tbody", { children: entries.map(entry => _jsxs("tr", { children: [_jsxs("td", { children: [entry.target, _jsxs("small", { className: "history-meta", children: [entry.result.provenance ? 'Run From Here' : entry.result.steps.some(step => step.attempts?.some(attempt => attempt.kind === 'interactive')) ? 'Interactive retry' : 'Normal run', " \u00B7 ", entry.packageId ?? 'legacy result', " \u00B7 Artifact v", entry.artifactProvenance?.originalVersion ?? 'unknown', " \u00B7 Execution v", entry.executionSchemaVersion ?? 'unknown', _jsx("br", {}), "hash ", entry.result.artifactHash ?? 'legacy result'] })] }), _jsx("td", { children: new Date(entry.startedAt).toLocaleString() }), _jsx("td", { children: _jsx("span", { className: `status ${entry.state}`, children: entry.state.toUpperCase() }) }), _jsxs("td", { children: [entry.durationMs, " ms"] }), _jsxs("td", { children: [_jsx("button", { className: "button", onClick: () => onReload(entry), children: "View results" }), _jsx("button", { className: "button", onClick: () => onExport(entry.result, entry.packageId, entry.artifactProvenance?.originalVersion, entry.artifactProvenance, entry.executionSchemaVersion), children: "Export JSON" }), _jsx("button", { className: "button history-delete", onClick: () => onDelete(entry.requestId), children: "Delete" })] })] }, entry.requestId)) })] }) : _jsx("p", { className: "empty-state", children: "Completed and failed runs will be kept here in this browser." })] }); }
|
|
280
|
+
function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete, onExport, onReload }) { return _jsxs("section", { className: "panel history-panel", children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: "Run History" }), _jsx("span", { className: "panel-spacer" }), _jsxs("label", { className: "history-retention", children: ["Keep", _jsxs("select", { "aria-label": "Run history retention", value: retention, onChange: event => onRetentionChange(Number(event.target.value)), children: [_jsx("option", { value: "10", children: "10 runs" }), _jsx("option", { value: "20", children: "20 runs" }), _jsx("option", { value: "50", children: "50 runs" }), _jsx("option", { value: "100", children: "100 runs" })] })] }), _jsx("button", { className: "button", onClick: onClear, disabled: !entries.length, children: "Clear history" })] }), entries.length ? _jsx("div", { className: "table-scroll", role: "region", "aria-label": "Stored runs", tabIndex: 0, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Target" }), _jsx("th", { children: "Started" }), _jsx("th", { children: "State" }), _jsx("th", { children: "Duration" }), _jsx("th", { children: "Result" })] }) }), _jsx("tbody", { children: entries.map(entry => _jsxs("tr", { children: [_jsxs("td", { children: [entry.target, _jsxs("small", { className: "history-meta", children: [entry.result.provenance ? 'Run From Here' : entry.result.steps.some(step => step.attempts?.some(attempt => attempt.kind === 'interactive')) ? 'Interactive retry' : 'Normal run', " \u00B7 ", entry.packageId ?? 'legacy result', " \u00B7 Artifact v", entry.artifactProvenance?.originalVersion ?? 'unknown', " \u00B7 Execution v", entry.executionSchemaVersion ?? 'unknown', _jsx("br", {}), "hash ", entry.result.artifactHash ?? 'legacy result'] })] }), _jsx("td", { children: new Date(entry.startedAt).toLocaleString() }), _jsx("td", { children: _jsx("span", { className: `status ${entry.state}`, children: entry.state.toUpperCase() }) }), _jsxs("td", { children: [entry.durationMs, " ms"] }), _jsxs("td", { children: [_jsx("button", { className: "button", onClick: () => onReload(entry), children: "View results" }), _jsx("button", { className: "button", onClick: () => onExport(entry.result, entry.packageId, entry.artifactProvenance?.originalVersion, entry.artifactProvenance, entry.executionSchemaVersion), children: "Export JSON" }), _jsx("button", { className: "button history-delete", onClick: () => onDelete(entry.requestId), children: "Delete" })] })] }, entry.requestId)) })] }) }) : _jsx("p", { className: "empty-state", children: "Completed and failed runs will be kept here in this browser." })] }); }
|
|
246
281
|
function InspectorSection({ title, children }) { return _jsxs("section", { className: "inspector-section", children: [_jsx("h3", { children: title }), children] }); }
|
|
247
282
|
function KeyValues({ values }) { return _jsx("div", { className: "key-values", children: redactKeyValuePairs(values).map((value, index) => _jsx("span", { children: value }, `${value}-${index}`)) }); }
|
|
248
283
|
function IterationHistory({ result }) { return _jsxs(_Fragment, { children: [_jsx("div", { className: "iteration-history", children: result?.children?.length ? result.children.map((iteration, index) => _jsxs("div", { className: "iteration-entry", children: [_jsx("strong", { children: iteration.name }), _jsxs("span", { children: [iteration.state.toUpperCase(), " \u00B7 ", iteration.durationMs, " ms"] }), _jsx("small", { children: iteration.children?.map(child => `${child.name}: ${Object.entries(child.outputs).map(([key, value]) => `${key}=${String(value)}`).join(', ') || 'no outputs'}`).join(' · ') || 'No nested output.' })] }, `${iteration.id}-${index}`)) : _jsx("div", { className: "notice", children: "No iteration history available." }) }), result?.terminalReason && _jsxs("div", { className: "timing-details", children: [_jsx("strong", { children: "Terminal reason" }), _jsx("span", { children: result.terminalReason })] }), _jsx(TransportTiming, { timing: result?.transportTiming }), _jsx(BranchExecutionPath, { result: result }), _jsx(ParallelGroupDetails, { result: result }), _jsx(ResultSummary, { result: result })] }); }
|