@qaick/runner 0.2.0-alpha.11 → 0.2.0-alpha.12
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.
|
@@ -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.2.0-alpha.
|
|
4
|
+
"version": "0.2.0-alpha.12",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package-dist",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@qaick/contract": "0.2.10",
|
|
56
56
|
"@qaick/executor": "0.1.0-alpha.1",
|
|
57
|
-
"@qaick/http-executor": "0.1.0-alpha.
|
|
57
|
+
"@qaick/http-executor": "0.1.0-alpha.2",
|
|
58
58
|
"@qaick/runtime": "0.1.0-alpha.2",
|
|
59
59
|
"zustand": "^5.0.15"
|
|
60
60
|
},
|
|
@@ -63,12 +63,12 @@
|
|
|
63
63
|
"react-dom": ">=19.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@vitejs/plugin-react": "^6.1.1",
|
|
67
66
|
"@testing-library/dom": "^10.4.2",
|
|
68
67
|
"@testing-library/react": "^16.3.3",
|
|
69
68
|
"@types/node": "^24.13.3",
|
|
70
69
|
"@types/react": "^19.2.18",
|
|
71
70
|
"@types/react-dom": "^19.2.7",
|
|
71
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
72
72
|
"jsdom": "^30.0.1",
|
|
73
73
|
"oxlint": "^1.81.0",
|
|
74
74
|
"react": "^19.2.8",
|
|
@@ -215,13 +215,13 @@ function RunnerApp({ client, onExit }) {
|
|
|
215
215
|
return undefined; const timer = window.setInterval(() => setClock(Date.now()), 250); return () => window.clearInterval(timer); }, [runState]);
|
|
216
216
|
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)
|
|
217
217
|
setSelectedStep(index); }; document.addEventListener('click', navigateIssue); return () => document.removeEventListener('click', navigateIssue); }, [displaySteps]);
|
|
218
|
-
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: "Alpha
|
|
218
|
+
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: "Alpha 12" }), onExit && _jsx("button", { className: "about-button", onClick: onExit, children: "Studio" })] }), _jsx("span", { className: "header-spacer" }), _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: ["Schema ", 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); 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')); setLoopProgress(loopProgressFor(next.target.kind === 'project' ? next.flows?.flatMap(flow => flow.steps) ?? [] : next.steps)); setSelectedStep(0); setResult(undefined); setEvents([]); setRunState('ready'); }, 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("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: runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: 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] }), 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: [_jsx(InputField, { label: "customerId", value: String(inputs.customerId ?? ''), onChange: value => setInputs(previous => ({ ...previous, customerId: value })) }), _jsx(InputField, { label: "orderId", value: String(inputs.orderId ?? ''), onChange: value => setInputs(previous => ({ ...previous, orderId: value })) }), _jsx(InputField, { label: "amount", value: String(inputs.amount ?? ''), onChange: value => setInputs(previous => ({ ...previous, amount: value })) }), (executionPackage.requiredInputs ?? []).filter(name => !['customerId', 'orderId', 'amount'].includes(name)).map(name => _jsx(InputField, { label: name, value: String(inputs[name] ?? ''), onChange: value => setInputs(previous => ({ ...previous, [name]: value })) }, 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" })] }), _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 progress = step.loopTotal ? loopProgress[step.id] ?? { current: 0, total: step.loopTotal } : undefined; const percent = progress ? Math.round(progress.current / progress.total * 100) : 0; const isLoop = Boolean(step.loopTotal); const displayState = displayResults[index]?.state ?? stepStates[index] ?? 'pending'; const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; const elapsed = step.loopId && loopStartedAt[step.loopId] ? formatElapsed(clock - loopStartedAt[step.loopId]) : undefined; 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") {
|
|
219
219
|
event.preventDefault();
|
|
220
220
|
setSelectedStep(Math.min(displaySteps.length - 1, index + 1));
|
|
221
221
|
} if (event.key === "ArrowUp") {
|
|
222
222
|
event.preventDefault();
|
|
223
223
|
setSelectedStep(Math.max(0, index - 1));
|
|
224
|
-
} }, 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: [progress && _jsxs("div", { className: "loop-progress", children: [_jsxs("strong", { children: ["REPEAT LOOP \u00B7 ITERATION ", progress.current, " OF ", progress.total] }), _jsx("span", { className: "loop-bar", children: _jsx("i", { style: { width: `${percent}%` } }) }), _jsx("small", { children: elapsed ? `Elapsed ${elapsed}` : 'Waiting to start' })] }), _jsx("span", { className: `status ${displayState}`, children: isLoop ? 'LOOP' : displayState.toUpperCase() })] }), _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, "Artifact schema", String(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?.terminalReason?.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 ?? 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)]) : ['—', '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(IterationHistory, { result: displayResults[selectedStep] }) }), _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.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.` : '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: ["Alpha 11 \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
224
|
+
} }, 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: [progress && _jsxs("div", { className: "loop-progress", children: [_jsxs("strong", { children: ["REPEAT LOOP \u00B7 ITERATION ", progress.current, " OF ", progress.total] }), _jsx("span", { className: "loop-bar", children: _jsx("i", { style: { width: `${percent}%` } }) }), _jsx("small", { children: elapsed ? `Elapsed ${elapsed}` : 'Waiting to start' })] }), _jsx("span", { className: `status ${displayState}`, children: isLoop ? 'LOOP' : displayState.toUpperCase() })] }), _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, "Artifact schema", String(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?.terminalReason?.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 ?? 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)]) : ['—', '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(IterationHistory, { result: displayResults[selectedStep] }) }), _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.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.` : '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: ["Alpha 12 \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
|
|
225
225
|
}
|
|
226
226
|
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] }); }
|
|
227
227
|
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 schema ", entry.artifactSchemaVersion ?? '—', _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.artifactSchemaVersion), 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." })] }); }
|
package/package-dist/styles.css
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
:root { font-family: Inter, ui-sans-serif, system-ui, sans-serif; color: #182234; background: #
|
|
1
|
+
:root { font-family: Inter, ui-sans-serif, system-ui, sans-serif; color: #182234; background: #f7f8fc; font-size: 13px; color-scheme: light; }
|
|
2
2
|
:root[data-theme='dark'] { color: #e6edf7; background: #111827; color-scheme: dark; }
|
|
3
3
|
* { box-sizing: border-box; } body { margin: 0; min-width: 1120px; } button, select { font: inherit; }
|
|
4
|
-
.runner-shell { height: 100vh; min-height: 100vh; display: grid; grid-template-rows: 44px minmax(0, 1fr); } .runner-header { display: flex; align-items: center; gap: 18px; padding: 0 12px 0 16px; border-bottom: 1px solid #
|
|
5
|
-
.runner-brand { display: flex; align-items: baseline; gap: 6px; flex: none; } .runner-brand-mark { color: #
|
|
4
|
+
.runner-shell { height: 100vh; min-height: 100vh; display: grid; grid-template-rows: 44px minmax(0, 1fr); } .runner-header { display: flex; align-items: center; gap: 18px; padding: 0 12px 0 16px; border-bottom: 1px solid #cbd1d8; background: #fff; font-size: 13px; }
|
|
5
|
+
.runner-brand { display: flex; align-items: baseline; gap: 6px; flex: none; } .runner-brand-mark { color: #4f46d8; font-size: 13px; font-weight: 700; letter-spacing: .04em; } .runner-brand-product { color: #182234; font-size: 13px; font-weight: 500; } .runner-brand .about-button { height: 25px; margin-left: 6px; padding: 0 9px; border: 0; border-radius: 4px; background: transparent; color: #34435b; font-size: 12px; font-weight: 650; } .runner-brand .about-button:hover { background: #f0f3f8; color: #182234; } .runner-logo { width: 30px; height: 30px; display: grid; place-items: center; border-radius: 8px; color: white; background: #4f46d8; font-size: 13px; } .alpha-label { color: #70809a; font-size: 13px; } .header-spacer, .panel-spacer { flex: 1; }
|
|
6
6
|
.engine-status { display: flex; align-items: center; gap: 7px; color: #14804a; font-size: 12px; font-weight: 700; } .engine-status i { width: 8px; height: 8px; border-radius: 50%; background: #19a463; }
|
|
7
|
-
.theme-picker { display: flex; align-items: center; gap: 7px; color: #
|
|
8
|
-
.runner-main { display: grid; grid-template-columns: 218px minmax(560px, 1fr) 350px; min-height: 0; overflow: hidden; } .runner-nav, .inspector { min-height: 0; overflow-y: auto; background: #fff; } .runner-nav { padding: 18px 12px; border-right: 1px solid #
|
|
7
|
+
.theme-picker { display: flex; align-items: center; gap: 7px; color: #617089; font-size: 12px; font-weight: 700; } .theme-picker select { min-width: 82px; height: 30px; padding: 0 8px; border: 1px solid #cfd5dd; border-radius: 6px; background: white; color: #182234; }
|
|
8
|
+
.runner-main { display: grid; grid-template-columns: 218px minmax(560px, 1fr) 350px; min-height: 0; overflow: hidden; } .runner-nav, .inspector { min-height: 0; overflow-y: auto; background: #fff; } .runner-nav { padding: 18px 12px; border-right: 1px solid #cbd1d8; }
|
|
9
9
|
.nav-label, .eyebrow { color: #8794a8; font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; } .nav-label { margin: 4px 12px 10px; }
|
|
10
|
-
.nav-item { display: flex; align-items: center; gap: 10px; padding: 11px 12px; border-radius: 7px; color: #
|
|
11
|
-
.package-card { margin: 24px 8px 0; padding: 12px; border: 1px solid #
|
|
12
|
-
.runner-workspace { min-height: 0; overflow-y: auto; padding: 26px 30px; background: #
|
|
13
|
-
.run-controls { display: flex; align-items: end; flex-wrap: wrap; gap: 10px 14px; margin-top: 25px; } label { display: grid; gap: 6px; color: #
|
|
14
|
-
.button { height: 36px; padding: 0 15px; border: 1px solid #
|
|
15
|
-
.file-button { position: relative; display: inline-grid; height: 36px; padding: 0 15px; place-items: center; border: 1px solid #
|
|
16
|
-
.inputs-panel { margin-top: 18px; } .input-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding: 16px; } .input-field { color: #
|
|
10
|
+
.nav-item { display: flex; align-items: center; gap: 10px; padding: 11px 12px; border-radius: 7px; color: #34435b; font-size: 14px; text-decoration: none; cursor: pointer; } .nav-item span { width: 20px; text-align: center; } .nav-item.active { color: #3f37b8; background: #e8eeff; font-weight: 700; }
|
|
11
|
+
.package-card { margin: 24px 8px 0; padding: 12px; border: 1px solid #e5e7eb; border-radius: 8px; background: #f8faff; } .package-card small { display: block; color: #77869b; } .package-card strong { display: block; margin: 5px 0; font-size: 12px; overflow-wrap: anywhere; }
|
|
12
|
+
.runner-workspace { min-height: 0; overflow-y: auto; padding: 26px 30px; background: #f7f8fc; } .eyebrow { color: #7c8aa0; } h1 { margin: 5px 0 4px; font-size: 25px; letter-spacing: -.02em; } .description { margin: 0; color: #617089; }
|
|
13
|
+
.run-controls { display: flex; align-items: end; flex-wrap: wrap; gap: 10px 14px; margin-top: 25px; } label { display: grid; gap: 6px; color: #617089; font-size: 12px; font-weight: 700; } select { min-width: 145px; height: 36px; padding: 0 10px; border: 1px solid #cfd5dd; border-radius: 6px; background: white; color: #182234; }
|
|
14
|
+
.button { height: 36px; padding: 0 15px; border: 1px solid #cfd5dd; border-radius: 6px; background: white; color: #334560; font-weight: 700; cursor: pointer; } .button.primary { border-color: #4f46d8; background: #4f46d8; color: white; } .button.dry-run { border-color: #8a9fc4; color: #315da9; background: #eef3fc; } .button.stop { color: #b33a30; }
|
|
15
|
+
.file-button { position: relative; display: inline-grid; height: 36px; padding: 0 15px; place-items: center; border: 1px solid #cfd5dd; border-radius: 6px; background: white; color: #334560; cursor: pointer; } .file-button input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; } .load-error { margin: 12px 0 -8px; color: #b33a30; font-size: 12px; font-weight: 700; }
|
|
16
|
+
.inputs-panel { margin-top: 18px; } .input-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding: 16px; } .input-field { color: #617089; } .input-field input { width: 100%; height: 34px; padding: 0 9px; border: 1px solid #cfd5dd; border-radius: 6px; color: #182234; background: #fff; font: inherit; font-weight: 500; } .input-hint { color: #7a899d; font-size: 11px; }
|
|
17
17
|
.history-view-banner { margin-top: 12px; padding: 9px 12px; border: 1px solid #b9cbed; border-radius: 6px; color: #315da9; background: #eaf2ff; font-size: 12px; font-weight: 700; }
|
|
18
|
-
.panel { margin-top: 24px; border: 1px solid #
|
|
19
|
-
table { width: 100%; border-collapse: collapse; font-size: 13px; } th { padding: 10px 14px; color: #7b899d; background: #fafbfd; font-size: 11px; text-align: left; text-transform: uppercase; letter-spacing: .04em; } td { padding: 13px 14px; border-top: 1px solid #
|
|
20
|
-
.hierarchy-hint { display: flex; align-items: center; gap: 10px; padding: 9px 16px; border-bottom: 1px solid #dbe6fa; color: #315da9; background: #f2f6ff; font-size: 11px; } .hierarchy-hint span { color: #637897; } .hierarchy-action { padding: 4px 7px; border: 1px solid #b9cbed; border-radius: 4px; color: #315da9; background: #fff; font-size: 10px; font-weight: 700; cursor: pointer; } .hierarchy-action:first-of-type { margin-left: auto; } .hierarchy-name { display: inline-flex; align-items: center; gap: 7px; min-height: 22px; } .collapse-toggle { display: inline-grid; width: 18px; height: 18px; place-items: center; padding: 0; border: 1px solid #9eb5df; border-radius: 4px; color: #315da9; background: #eaf1ff; font-size: 14px; line-height: 1; cursor: pointer; } .branch-marker { color: #5d82ca; font-size: 18px; font-weight: 400; } .kind-label { display: inline-flex; padding: 3px 5px; border-radius: 4px; color: #6c7890; background: #
|
|
18
|
+
.panel { margin-top: 24px; border: 1px solid #e5e7eb; border-radius: 8px; background: white; overflow: hidden; } .panel-heading { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid #e5e7eb; } h2 { margin: 0; font-size: 16px; } .running { color: #14804a; font-size: 12px; font-weight: 750; } .stopped { color: #b33a30; font-size: 12px; font-weight: 750; }
|
|
19
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; } th { padding: 10px 14px; color: #7b899d; background: #fafbfd; font-size: 11px; text-align: left; text-transform: uppercase; letter-spacing: .04em; } td { padding: 13px 14px; border-top: 1px solid #edf0f7; } tr.parent-row td { background: #ffffff; } tr.nested-row td { background: #f3f7fd; } tr.selected td { background: #dce8ff !important; border-top-color: #9db9ed; } tr.selected td:first-child { border-left: 4px solid #4f46d8; } tr.selected .hierarchy-name { color: #173e91; font-weight: 700; } tbody tr { cursor: pointer; }
|
|
20
|
+
.hierarchy-hint { display: flex; align-items: center; gap: 10px; padding: 9px 16px; border-bottom: 1px solid #dbe6fa; color: #315da9; background: #f2f6ff; font-size: 11px; } .hierarchy-hint span { color: #637897; } .hierarchy-action { padding: 4px 7px; border: 1px solid #b9cbed; border-radius: 4px; color: #315da9; background: #fff; font-size: 10px; font-weight: 700; cursor: pointer; } .hierarchy-action:first-of-type { margin-left: auto; } .hierarchy-name { display: inline-flex; align-items: center; gap: 7px; min-height: 22px; } .collapse-toggle { display: inline-grid; width: 18px; height: 18px; place-items: center; padding: 0; border: 1px solid #9eb5df; border-radius: 4px; color: #315da9; background: #eaf1ff; font-size: 14px; line-height: 1; cursor: pointer; } .branch-marker { color: #5d82ca; font-size: 18px; font-weight: 400; } .kind-label { display: inline-flex; padding: 3px 5px; border-radius: 4px; color: #6c7890; background: #edf0f7; font-size: 9px; font-weight: 800; letter-spacing: .05em; } .nested-label { color: #315da9; background: #e5efff; } .nested-row td:first-child { border-left: 3px solid #8eaded; } .parent-row td:first-child { border-left: 3px solid transparent; }
|
|
21
21
|
.step-icon { display: inline-grid; width: 20px; height: 20px; margin-right: 7px; place-items: center; border-radius: 50%; color: white; background: #18a15d; font-size: 11px; } .nested-row .step-icon { margin-left: 18px; } .nested-row .hierarchy-name { padding-left: 0 !important; } .step-icon.waiting { background: #efaa18; } .step-icon.skipped { color: #62738c; background: #edf1f6; } .step-icon.failed, .step-icon.cancelled { background: #c94b42; } .step-icon.pending { border: 2px solid #aab6c7; background: transparent; }
|
|
22
|
-
.selected-label { display: inline-flex; margin-left: 8px; padding: 3px 5px; border-radius: 4px; color: #
|
|
22
|
+
.selected-label { display: inline-flex; margin-left: 8px; padding: 3px 5px; border-radius: 4px; color: #3f37b8; background: #c4d7ff; font-size: 9px; font-weight: 800; letter-spacing: .05em; }
|
|
23
23
|
.status { display: inline-flex; padding: 4px 8px; border-radius: 5px; font-size: 10px; font-weight: 800; letter-spacing: .03em; } .status.finished { color: #14804a; background: #e7f6ed; } .status.waiting { color: #98660b; background: #fff2d5; } .status.pending, .status.skipped { color: #62738c; background: #edf1f6; } .status.failed, .status.cancelled { color: #a33a32; background: #ffebe8; }
|
|
24
|
-
.loop-row td { background: #f5f8fe; border-top: 1px solid #c8d7f0; border-bottom: 1px solid #dce6f6; } .loop-row td:first-child { border-left: 3px solid #668bd2; border-radius: 9px 0 0 0; } .loop-row td:last-child { border-right: 1px solid #c8d7f0; border-radius: 0 9px 0 0; } .loop-icon { color: #fff; background: #668bd2; font-size: 15px; } .loop-label { color: #315da9; background: #e7efff; } .loop-progress { display: flex; align-items: center; flex-wrap: wrap; gap: 5px 8px; min-width: 220px; margin-bottom: 4px; color: #315da9; font-size: 10px; letter-spacing: .02em; } .loop-progress small { flex-basis: 100%; color: #71839c; font-size: 10px; } .loop-bar { width: 92px; height: 6px; overflow: hidden; border-radius: 99px; background: #dce6f6; } .loop-bar i { display: block; height: 100%; border-radius: inherit; background: #668bd2; transition: width .2s ease; } .loop-content td { border-top: 0; border-bottom: 0; background: #
|
|
24
|
+
.loop-row td { background: #f5f8fe; border-top: 1px solid #c8d7f0; border-bottom: 1px solid #dce6f6; } .loop-row td:first-child { border-left: 3px solid #668bd2; border-radius: 9px 0 0 0; } .loop-row td:last-child { border-right: 1px solid #c8d7f0; border-radius: 0 9px 0 0; } .loop-icon { color: #fff; background: #668bd2; font-size: 15px; } .loop-label { color: #315da9; background: #e7efff; } .loop-progress { display: flex; align-items: center; flex-wrap: wrap; gap: 5px 8px; min-width: 220px; margin-bottom: 4px; color: #315da9; font-size: 10px; letter-spacing: .02em; } .loop-progress small { flex-basis: 100%; color: #71839c; font-size: 10px; } .loop-bar { width: 92px; height: 6px; overflow: hidden; border-radius: 99px; background: #dce6f6; } .loop-bar i { display: block; height: 100%; border-radius: inherit; background: #668bd2; transition: width .2s ease; } .loop-content td { border-top: 0; border-bottom: 0; background: #ffffff; } .loop-content td:first-child { border-left: 3px solid #c8d7f0; } .loop-content td:last-child { border-right: 1px solid #c8d7f0; } .loop-end td { border-bottom: 1px solid #c8d7f0; } .loop-end td:first-child { border-radius: 0 0 0 9px; } .loop-end td:last-child { border-radius: 0 0 9px 0; }
|
|
25
25
|
.event-panel { margin-top: 18px; } .event-row td { padding: 9px 14px; font-size: 12px; } code { color: #294d9c; font-family: ui-monospace, monospace; font-size: 11px; }
|
|
26
|
-
.inspector { padding: 22px 18px; border-left: 1px solid #
|
|
26
|
+
.inspector { padding: 22px 18px; border-left: 1px solid #cbd1d8; } .inspector h2 { font-size: 17px; } .selected-detail-name { margin: 6px 0 0; color: #3f37b8; font-size: 13px; } .run-id { margin: 5px 0 20px; color: #7a899d; font-family: ui-monospace, monospace; font-size: 10px; } .inspector-section { padding: 17px 0; border-top: 1px solid #e5e7eb; } .inspector-section h3 { margin: 0 0 11px; font-size: 13px; }
|
|
27
27
|
.selection-nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 10px; } .selection-nav .button { padding: 5px 8px; font-size: 11px; }
|
|
28
|
-
.parallel-event { color: #
|
|
28
|
+
.parallel-event { color: #617089; font-size: 10px; }
|
|
29
29
|
.event-filter { min-width: 120px; padding: 6px 8px; font-size: 11px; }
|
|
30
30
|
.key-values { display: grid; grid-template-columns: 1fr 1fr; border: 1px solid #e0e6ef; border-radius: 6px; overflow: hidden; font-size: 12px; } .key-values span { padding: 9px 10px; border-bottom: 1px solid #e9edf3; } .key-values span:nth-child(odd) { color: #63738a; background: #fafbfd; } .key-values span:nth-last-child(-n+2) { border-bottom: 0; }
|
|
31
|
-
.iteration-history { display: grid; gap: 7px; } .iteration-entry { display: grid; gap: 3px; padding: 9px 10px; border: 1px solid #
|
|
32
|
-
.notice { padding: 12px; border-radius: 6px; color: #59708e; background: #
|
|
33
|
-
.about-button { height: 30px; padding: 0 10px; border: 1px solid #
|
|
31
|
+
.iteration-history { display: grid; gap: 7px; } .iteration-entry { display: grid; gap: 3px; padding: 9px 10px; border: 1px solid #e5e7eb; border-radius: 6px; background: #ffffff; font-size: 11px; } .iteration-entry span { color: #637897; font-size: 10px; } .iteration-entry small { color: #71839c; line-height: 1.35; } .timing-details { display: grid; gap: 4px; margin-top: 8px; padding: 9px 10px; border: 1px solid #e5e7eb; border-radius: 6px; color: #637897; background: #ffffff; font-size: 10px; } .timing-details strong { color: #34435b; font-size: 11px; }
|
|
32
|
+
.notice { padding: 12px; border-radius: 6px; color: #59708e; background: #edf0f7; font-size: 12px; line-height: 1.5; } .draft-badge { position: fixed; right: 18px; bottom: 14px; padding: 7px 10px; border: 1px solid #e5c987; border-radius: 5px; color: #85600e; background: #fff7df; font-size: 11px; font-weight: 750; }
|
|
33
|
+
.about-button { height: 30px; padding: 0 10px; border: 1px solid #cfd5dd; border-radius: 6px; background: white; color: #34435b; font-size: 12px; font-weight: 700; cursor: pointer; }
|
|
34
34
|
.about-backdrop { position: fixed; inset: 0; z-index: 20; display: grid; place-items: center; background: rgb(7 12 24 / 48%); }
|
|
35
|
-
.about-dialog { position: relative; width: min(360px, calc(100vw - 40px)); padding: 28px; border: 1px solid #
|
|
36
|
-
.about-dialog .runner-logo { margin: 0 auto 14px; }.about-dialog h2 { margin: 0 0 8px; color: #182234; }.about-dialog p { margin: 0 0 8px; color: #
|
|
35
|
+
.about-dialog { position: relative; width: min(360px, calc(100vw - 40px)); padding: 28px; border: 1px solid #e5e7eb; border-radius: 12px; background: white; color: #34435b; box-shadow: 0 20px 60px rgb(0 0 0 / 24%); text-align: center; }
|
|
36
|
+
.about-dialog .runner-logo { margin: 0 auto 14px; }.about-dialog h2 { margin: 0 0 8px; color: #182234; }.about-dialog p { margin: 0 0 8px; color: #617089; line-height: 1.5; }.about-dialog small { color: #8794a8; }.about-dialog footer { margin-top: 24px; color: #7a899d; font-size: 11px; }.about-close { position: absolute; top: 10px; right: 12px; border: 0; background: transparent; color: #7a899d; font-size: 22px; cursor: pointer; }
|
|
37
37
|
.error-notice { padding: 12px; border: 1px solid #f0c5c1; border-radius: 6px; color: #9d332b; background: #fff1ef; font-size: 12px; line-height: 1.5; } .dry-run-issues { display: grid; gap: 8px; } .dry-run-issues .error-notice { font-size: 11px; } .dry-run-issues small { display: block; margin-top: 5px; color: inherit; opacity: .8; }
|
|
38
38
|
.validation-panel { display: flex; gap: 11px; align-items: flex-start; margin-top: 18px; padding: 13px 16px; border: 1px solid; border-radius: 8px; font-size: 12px; } .validation-panel strong { display: block; } .validation-panel small { display: block; margin-top: 3px; color: inherit; opacity: .8; } .validation-pass { border-color: #b9dfca; color: #176d41; background: #effaf3; } .validation-failed { display: grid; gap: 11px; border-color: #edc2bd; color: #963b33; background: #fff5f3; } .validation-heading { display: flex; gap: 11px; align-items: flex-start; } .validation-icon, .issue-icon { display: inline-grid; flex: 0 0 auto; width: 22px; height: 22px; place-items: center; border-radius: 50%; color: white; background: #1b9a5b; font-size: 14px; font-weight: 800; } .validation-failed .validation-icon, .issue-icon { background: #c94b42; } .validation-issues { display: grid; gap: 6px; padding-left: 33px; } .validation-issue { display: flex; gap: 8px; align-items: flex-start; padding: 8px 9px; border: 1px solid #f0d1cd; border-radius: 5px; background: rgba(255,255,255,.55); } .validation-issue .issue-icon { width: 16px; height: 16px; font-size: 12px; } .validation-issue span, .validation-issue small { display: block; } .validation-issue strong { font-size: 10px; letter-spacing: .04em; } .validation-issue span { margin-top: 2px; font-size: 11px; } .validation-issue small { margin-top: 3px; opacity: .75; }
|
|
39
39
|
|
|
@@ -64,13 +64,13 @@ table { width: 100%; border-collapse: collapse; font-size: 13px; } th { padding:
|
|
|
64
64
|
.history-retention select { min-width: 92px; height: 30px; font-size: 11px; }
|
|
65
65
|
.history-delete { margin-left: 6px; color: #a33a32; }
|
|
66
66
|
.validation-issue[data-step-id] { cursor: pointer; }
|
|
67
|
-
.branch-path { display: grid; gap: 6px; margin-top: 8px; padding: 9px 10px; border: 1px solid #
|
|
67
|
+
.branch-path { display: grid; gap: 6px; margin-top: 8px; padding: 9px 10px; border: 1px solid #e5e7eb; border-radius: 6px; background: #ffffff; font-size: 11px; }
|
|
68
68
|
.branch-path-row { display: grid; grid-template-columns: auto 1fr auto; gap: 7px; align-items: center; }
|
|
69
69
|
.branch-path-row small { color: #71839c; font-size: 10px; }
|
|
70
70
|
.parallel-details { color: #637897; }
|
|
71
71
|
.parallel-details > span { font-size: 10px; }
|
|
72
|
-
.result-summary { display: grid; gap: 4px; margin-top: 8px; padding: 9px 10px; border: 1px solid #
|
|
73
|
-
.result-summary strong { color: #
|
|
72
|
+
.result-summary { display: grid; gap: 4px; margin-top: 8px; padding: 9px 10px; border: 1px solid #e5e7eb; border-radius: 6px; color: #637897; background: #ffffff; font-size: 10px; }
|
|
73
|
+
.result-summary strong { color: #34435b; font-size: 11px; }
|
|
74
74
|
.demo-entry { padding: 7px 10px; border: 1px solid var(--border); border-radius: 5px; background: var(--panel); color: var(--accent); font-size: 12px; font-weight: 650; text-decoration: none; }
|
|
75
75
|
|
|
76
76
|
.breakpoint-toggle { border: 0; background: transparent; color: var(--muted, #718096); cursor: pointer; margin-left: .4rem; padding: 0 .2rem; }
|
|
@@ -87,7 +87,7 @@ table { width: 100%; border-collapse: collapse; font-size: 13px; } th { padding:
|
|
|
87
87
|
.runner-context-menu button:hover { background: #edf3ff; color: #173e91; }
|
|
88
88
|
.breakpoint-marker { display: inline-grid; width: 22px; height: 22px; place-items: center; color: #fff; background: #b93645; clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%); filter: drop-shadow(0 1px 1px rgba(62, 20, 27, .25)); font-family: Arial, sans-serif; font-size: 4.5px; font-weight: 900; letter-spacing: .06em; line-height: 1; }
|
|
89
89
|
|
|
90
|
-
.button.resume-highlight { border-color: #
|
|
90
|
+
.button.resume-highlight { border-color: #4f46d8; background: #4f46d8; color: #fff; box-shadow: 0 0 0 3px rgba(53, 105, 212, .18); }
|
|
91
91
|
|
|
92
92
|
/* Match Studio's compact toolbar rhythm and quiet menu controls. */
|
|
93
93
|
.runner-header { border-color: var(--runner-line); background: var(--runner-surface); }
|
package/package.json
CHANGED
|
@@ -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.2.0-alpha.
|
|
4
|
+
"version": "0.2.0-alpha.12",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"package-dist",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@qaick/contract": "0.2.10",
|
|
56
56
|
"@qaick/executor": "0.1.0-alpha.1",
|
|
57
|
-
"@qaick/http-executor": "0.1.0-alpha.
|
|
57
|
+
"@qaick/http-executor": "0.1.0-alpha.2",
|
|
58
58
|
"@qaick/runtime": "0.1.0-alpha.2",
|
|
59
59
|
"zustand": "^5.0.15"
|
|
60
60
|
},
|
|
@@ -63,12 +63,12 @@
|
|
|
63
63
|
"react-dom": ">=19.0.0"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@vitejs/plugin-react": "^6.1.1",
|
|
67
66
|
"@testing-library/dom": "^10.4.2",
|
|
68
67
|
"@testing-library/react": "^16.3.3",
|
|
69
68
|
"@types/node": "^24.13.3",
|
|
70
69
|
"@types/react": "^19.2.18",
|
|
71
70
|
"@types/react-dom": "^19.2.7",
|
|
71
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
72
72
|
"jsdom": "^30.0.1",
|
|
73
73
|
"oxlint": "^1.81.0",
|
|
74
74
|
"react": "^19.2.8",
|