@qaick/runner 0.32.2 → 0.32.4

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.
Files changed (41) hide show
  1. package/package-dist/package.json +5 -5
  2. package/package-dist/src/demo/RunnerWalkthrough.js +4 -4
  3. package/package-dist/src/demo/RunnerWalkthrough.js.map +1 -1
  4. package/package-dist/src/runner/RunnerModule.d.ts +5 -3
  5. package/package-dist/src/runner/RunnerModule.d.ts.map +1 -1
  6. package/package-dist/src/runner/RunnerModule.js +241 -65
  7. package/package-dist/src/runner/RunnerModule.js.map +1 -1
  8. package/package-dist/src/runner/catalog.js +1 -1
  9. package/package-dist/src/runner/execution-context.d.ts +1 -1
  10. package/package-dist/src/runner/execution-context.d.ts.map +1 -1
  11. package/package-dist/src/runner/execution-context.js +3 -3
  12. package/package-dist/src/runner/execution-context.js.map +1 -1
  13. package/package-dist/src/runner/hierarchy-presentation.d.ts +20 -0
  14. package/package-dist/src/runner/hierarchy-presentation.d.ts.map +1 -0
  15. package/package-dist/src/runner/hierarchy-presentation.js +47 -0
  16. package/package-dist/src/runner/hierarchy-presentation.js.map +1 -0
  17. package/package-dist/src/runner/history.d.ts +20 -1
  18. package/package-dist/src/runner/history.d.ts.map +1 -1
  19. package/package-dist/src/runner/history.js +14 -0
  20. package/package-dist/src/runner/history.js.map +1 -1
  21. package/package-dist/src/runner/invocation-inputs.d.ts +6 -2
  22. package/package-dist/src/runner/invocation-inputs.d.ts.map +1 -1
  23. package/package-dist/src/runner/invocation-inputs.js.map +1 -1
  24. package/package-dist/src/runner/loop-presentation.d.ts +4 -4
  25. package/package-dist/src/runner/loop-presentation.d.ts.map +1 -1
  26. package/package-dist/src/runner/loop-presentation.js +4 -4
  27. package/package-dist/src/runner/loop-presentation.js.map +1 -1
  28. package/package-dist/src/runner/presentation-state.d.ts +19 -4
  29. package/package-dist/src/runner/presentation-state.d.ts.map +1 -1
  30. package/package-dist/src/runner/presentation-state.js +68 -3
  31. package/package-dist/src/runner/presentation-state.js.map +1 -1
  32. package/package-dist/src/runner/runtime-client-adapter.d.ts +1 -1
  33. package/package-dist/src/runner/studio-execution-document.d.ts +4 -0
  34. package/package-dist/src/runner/studio-execution-document.d.ts.map +1 -0
  35. package/package-dist/src/runner/studio-execution-document.js +239 -0
  36. package/package-dist/src/runner/studio-execution-document.js.map +1 -0
  37. package/package-dist/src/runner/studio-hierarchy-loader.js +2 -2
  38. package/package-dist/src/runner/types.d.ts +26 -6
  39. package/package-dist/src/runner/types.d.ts.map +1 -1
  40. package/package-dist/styles.css +114 -43
  41. package/package.json +5 -5
@@ -3,9 +3,10 @@ import { commandLinks, runnerMenus } from "../shared/application-menu.js";
3
3
  import { ThemeToggle } from "../ThemeToggle.js";
4
4
  import { HeaderHelp } from "./HeaderHelp.js";
5
5
  import { ScreenshotButton } from "../ScreenshotButton.js";
6
+ import { StructuralIcon, hierarchyGuides, HierarchyGuides } from "./hierarchy-presentation.js";
6
7
  import { invocationBranches } from "./invocation-branches.js";
7
8
  import { loopPresentation } from "./loop-presentation.js";
8
- import { presentationState, eventPresentationEvidence } from "./presentation-state.js";
9
+ import { rowPresentation, scopeIncludes, eventPresentationEvidence } from "./presentation-state.js";
9
10
  import { useEffect, useRef, useState } from 'react';
10
11
  import { checkoutNestedPackage, checkoutPackage, checkoutProjectPackage } from "./fixture.js";
11
12
  import { PackageLoadError } from "./package-loader.js";
@@ -17,12 +18,30 @@ import { RUNNER_BUILD_LABEL, RUNNER_UI_VERSION } from "../build-info.js";
17
18
  import { collectSecretValues, redactExecutionEvent, redactExecutionResult, redactKeyValuePairs } from "./redaction.js";
18
19
  import { createResultExport } from "./result-export.js";
19
20
  import { inspectDeclaredInputs, inputDrafts, resolvedArguments } from "./invocation-inputs.js";
20
- import { persistRunHistory } from "./history.js";
21
+ import { persistRunHistory, captureHistoryStructure, assertHistoryIdentity } from "./history.js";
22
+ import { prepareStudioDocument } from "./studio-execution-document.js";
21
23
  const statusGlyph = { pending: '', 'not executed': '', running: '', finished: '✓', failed: '✕', waiting: '◷', skipped: '–', cancelled: '⊘' };
22
24
  const HISTORY_KEY = 'qaick-runner.history.v1';
23
25
  const HISTORY_RETENTION_KEY = 'qaick-runner.history-retention.v1';
24
26
  const IMPORTED_PACKAGE_KEY = 'qaick-runner.imported-package.v1';
25
27
  const THEME_STORAGE_KEY = 'qaick-studio-theme';
28
+ const INSPECTOR_WIDTH_KEY = 'qaick-runner.inspector-width.v1';
29
+ const INSPECTOR_MIN_WIDTH = 280;
30
+ const INSPECTOR_MAX_WIDTH = 640;
31
+ const inspectorWidthWithinBounds = (width) => Math.max(INSPECTOR_MIN_WIDTH, Math.min(INSPECTOR_MAX_WIDTH, width));
32
+ function readInspectorWidth() {
33
+ try {
34
+ const value = Number(localStorage.getItem(INSPECTOR_WIDTH_KEY));
35
+ return Number.isFinite(value) && value > 0 ? inspectorWidthWithinBounds(value) : 320;
36
+ }
37
+ catch {
38
+ return 320;
39
+ }
40
+ }
41
+ function saveInspectorWidth(width) { try {
42
+ localStorage.setItem(INSPECTOR_WIDTH_KEY, String(width));
43
+ }
44
+ catch { /* Resizing still works when preferences cannot be stored. */ } }
26
45
  function readRunHistory() { try {
27
46
  return JSON.parse(localStorage.getItem(HISTORY_KEY) ?? '[]');
28
47
  }
@@ -63,12 +82,68 @@ function standaloneDemoRequested() { return typeof window !== 'undefined' && new
63
82
  function RunnerApp({ client, onExit, applicationMenu }) {
64
83
  const [theme, setTheme] = useState(readTheme);
65
84
  const [showAbout, setShowAbout] = useState(false);
66
- const [environment, setEnvironment] = useState('test');
85
+ const [inspectorWidth, setInspectorWidth] = useState(readInspectorWidth);
86
+ const inspectorDrag = useRef(undefined);
87
+ function startInspectorResize(event) {
88
+ if (event.button !== 0)
89
+ return;
90
+ event.preventDefault();
91
+ event.currentTarget.setPointerCapture(event.pointerId);
92
+ inspectorDrag.current = { pointerId: event.pointerId, startX: event.clientX, startWidth: inspectorWidth, width: inspectorWidth };
93
+ }
94
+ function moveInspectorResize(event) {
95
+ const drag = inspectorDrag.current;
96
+ if (!drag || drag.pointerId !== event.pointerId)
97
+ return;
98
+ drag.width = inspectorWidthWithinBounds(drag.startWidth + drag.startX - event.clientX);
99
+ setInspectorWidth(drag.width);
100
+ }
101
+ function finishInspectorResize(event) {
102
+ const drag = inspectorDrag.current;
103
+ if (!drag || drag.pointerId !== event.pointerId)
104
+ return;
105
+ inspectorDrag.current = undefined;
106
+ if (event.type === 'pointercancel')
107
+ setInspectorWidth(drag.startWidth);
108
+ else
109
+ saveInspectorWidth(drag.width);
110
+ if (event.currentTarget.hasPointerCapture(event.pointerId))
111
+ event.currentTarget.releasePointerCapture(event.pointerId);
112
+ }
113
+ function inspectorResizeKey(event) {
114
+ const delta = event.shiftKey ? 50 : 10;
115
+ const width = event.key === 'Home' ? INSPECTOR_MIN_WIDTH : event.key === 'End' ? INSPECTOR_MAX_WIDTH : event.key === 'ArrowLeft' ? inspectorWidth + delta : event.key === 'ArrowRight' ? inspectorWidth - delta : undefined;
116
+ if (width === undefined)
117
+ return;
118
+ event.preventDefault();
119
+ const next = inspectorWidthWithinBounds(width);
120
+ setInspectorWidth(next);
121
+ saveInspectorWidth(next);
122
+ }
67
123
  const [selectedTargetId, setSelectedTargetId] = useState(restoredImportedPackage?.executionPackage?.packageId ?? restoredImportedPackage?.authoringSource?.targets[0]?.packageId ?? checkoutPackage.packageId);
68
124
  const selectedTarget = packageCatalog.targets().find(t => t.packageId === selectedTargetId);
125
+ const [authoringSource, setAuthoringSource] = useState(restoredImportedPackage?.authoringSource?.serialized);
126
+ const [selectedRoot, setSelectedRoot] = useState('');
127
+ const [preparedDocument, setPreparedDocument] = useState();
128
+ const [preparing, setPreparing] = useState(Boolean(restoredImportedPackage?.authoringSource));
129
+ const [preparationError, setPreparationError] = useState();
130
+ useEffect(() => { let current = true; if (!authoringSource) {
131
+ setPreparedDocument(undefined);
132
+ setPreparing(false);
133
+ return;
134
+ } setPreparing(true); setPreparationError(undefined); void prepareStudioDocument(authoringSource, selectedRoot ? { kind: 'occurrence', key: selectedRoot } : { kind: 'document' }).then(doc => { if (current) {
135
+ setPreparedDocument(doc);
136
+ setPreparing(false);
137
+ } }).catch(error => { if (current) {
138
+ setPreparationError(error instanceof Error ? error.message : 'Cannot prepare this Project.');
139
+ setPreparing(false);
140
+ } }); return () => { current = false; }; }, [authoringSource, selectedRoot]);
69
141
  const [executionPackage, setExecutionPackage] = useState(restoredImportedPackage?.executionPackage ?? checkoutPackage);
70
142
  const [inputEdits, setInputEdits] = useState(() => inputDrafts(inspectDeclaredInputs(restoredImportedPackage?.executionPackage ?? checkoutPackage).declarations));
71
- const { declarations, conflict, unresolved } = inspectDeclaredInputs(executionPackage);
143
+ const inputContract = preparedDocument ? { target: preparedDocument.target, inputDeclarations: preparedDocument.selection.inputDeclarations, requiredInputs: preparedDocument.selection.requiredInputs, flows: preparedDocument.selection.root.kind === 'document' ? preparedDocument.flows.map(f => ({ ...f, name: preparedDocument.context.nodes.find(n => n.key === f.occurrence).name })) : [] } : executionPackage;
144
+ const { declarations, conflict, unresolved } = inspectDeclaredInputs(inputContract);
145
+ useEffect(() => { if (preparedDocument && !conflict && !unresolved)
146
+ setInputEdits(inputDrafts(declarations)); }, [preparedDocument]);
72
147
  const inputBlocker = conflict ?? unresolved;
73
148
  let inputError;
74
149
  let inputs = {};
@@ -82,6 +157,8 @@ function RunnerApp({ client, onExit, applicationMenu }) {
82
157
  const [timeoutMs, setTimeoutMs] = useState('none');
83
158
  const [editingInputs, setEditingInputs] = useState(false);
84
159
  const [runState, setRunState] = useState('ready');
160
+ const activeInputRequest = useRef(undefined);
161
+ const inputsLocked = activeInputRequest.current !== undefined;
85
162
  const [controlState, setControlState] = useState('idle');
86
163
  const [breakpoints, setBreakpoints] = useState(new Set());
87
164
  const [contextMenu, setContextMenu] = useState();
@@ -95,7 +172,14 @@ function RunnerApp({ client, onExit, applicationMenu }) {
95
172
  const [runHistory, setRunHistory] = useState(readRunHistory);
96
173
  const [historyRetention, setHistoryRetention] = useState(readHistoryRetention);
97
174
  const [result, setResult] = useState();
98
- const [viewingHistoryEntry, setViewingHistoryEntry] = useState();
175
+ const [workspace, setWorkspace] = useState('execution');
176
+ const [historicalEntry, setHistoricalEntry] = useState();
177
+ const [historySelectedStep, setHistorySelectedStep] = useState(0);
178
+ const [historyCollapsedSteps, setHistoryCollapsedSteps] = useState(new Set());
179
+ const [loadedContexts, setLoadedContexts] = useState({});
180
+ const loadedContext = loadedContexts[selectedTargetId];
181
+ const contextTarget = preparedDocument?.target ?? loadedContext?.owner ?? selectedTarget?.target ?? executionPackage.target;
182
+ const readOnly = workspace === 'history';
99
183
  const [selectedStep, setSelectedStep] = useState(0);
100
184
  const [collapsedSteps, setCollapsedSteps] = useState(new Set());
101
185
  const [requestId, setRequestId] = useState();
@@ -103,43 +187,76 @@ function RunnerApp({ client, onExit, applicationMenu }) {
103
187
  const [loadIssues, setLoadIssues] = useState([]);
104
188
  const [, setCatalogVersion] = useState(0);
105
189
  const packageSteps = executionPackage.target.kind === 'project' ? executionPackage.flows?.flatMap(flow => flow.steps) ?? [] : executionPackage.steps;
106
- const allResults = result?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(result?.steps ?? []);
107
- const displaySteps = orderDisplayStepsByResult(flattenDisplaySteps(packageSteps, 0, undefined, undefined, collapsedSteps), allResults);
108
- const hasNestedSteps = flattenDisplaySteps(packageSteps).some(step => step.depth > 0);
109
- const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress));
110
- const selectedInvocations = resultsForOccurrence(allResults, displaySteps[selectedStep]?.structuralAddress);
111
- 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; });
112
- function selectTarget(id) { setSelectedTargetId(id); const descriptor = packageCatalog.targets().find(t => t.packageId === id); if (descriptor?.issues?.length) {
113
- setRequestId(undefined);
114
- setViewingHistoryEntry(undefined);
115
- setControlState("idle");
116
- setRunState("ready");
117
- return;
118
- } const next = packageCatalog.get(id); setExecutionPackage(next); if (!inspectDeclaredInputs(next).conflict && !inspectDeclaredInputs(next).unresolved)
119
- 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'); }
190
+ const legacyDisplaySteps = orderDisplayStepsByResult(flattenDisplaySteps(packageSteps, 0, undefined, undefined, collapsedSteps), result?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(result?.steps ?? []));
191
+ const liveDisplaySteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, collapsedSteps, preparedDocument) : authoringSource ? [] : legacyDisplaySteps;
192
+ const historyPackage = undefined;
193
+ const shownResult = readOnly ? historicalEntry?.result : result;
194
+ const shownPackage = readOnly ? historyPackage : executionPackage;
195
+ const shownPackageSteps = shownPackage ? packageStepsFor(shownPackage) : [];
196
+ const shownSelectedStep = readOnly ? historySelectedStep : selectedStep;
197
+ const shownRequestId = readOnly ? historicalEntry?.requestId : requestId;
198
+ const shownRunState = readOnly ? historicalEntry?.state ?? 'ready' : runState;
199
+ const allResults = shownResult?.children?.flatMap(child => flattenResultSteps(child.steps)) ?? flattenResultSteps(shownResult?.steps ?? []);
200
+ const displaySteps = readOnly ? historicalEntry?.structure ? contextDisplaySteps(historicalEntry.structure.context, historyCollapsedSteps) : recordedDisplaySteps(allResults) : liveDisplaySteps;
201
+ const guides = hierarchyGuides(displaySteps);
202
+ const hasNestedSteps = displaySteps.some(step => step.depth > 0 || step.hasChildren);
203
+ const displayResults = displaySteps.map(step => resultForOccurrence(allResults, step.structuralAddress, step.occurrence));
204
+ const selectedInvocations = resultsForOccurrence(allResults, displaySteps[shownSelectedStep]?.structuralAddress, displaySteps[shownSelectedStep]?.occurrence);
205
+ function selectDisplayedStep(value) { if (readOnly)
206
+ setHistorySelectedStep(value);
207
+ else
208
+ setSelectedStep(value); }
209
+ function switchWorkspace(value) { setContextMenu(undefined); setWorkspace(value); }
210
+ function tabKey(event) {
211
+ if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key))
212
+ return;
213
+ event.preventDefault();
214
+ const next = event.key === 'Home' ? 'execution' : event.key === 'End' ? 'history' : workspace === 'execution' ? 'history' : 'execution';
215
+ switchWorkspace(next);
216
+ document.getElementById(`runner-tab-${next}`)?.focus();
217
+ }
218
+ const executionEvents = events.filter(event => event.requestId === requestId);
219
+ const visibleEvents = executionEvents.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; });
220
+ function selectRoot(key) { if (activeInputRequest.current)
221
+ return; setSelectedRoot(key); setSelectedStep(0); stepStateCache.current = {}; setStepStates(liveDisplaySteps.map(() => 'pending')); setResult(undefined); setEvents([]); setRequestId(undefined); setRunState('ready'); setControlState('idle'); setLoadError(undefined); }
120
222
  async function run(dryRun = false) {
121
- if (inputBlocker || inputError)
223
+ if (activeInputRequest.current || inputBlocker || inputError || preparing || preparationError)
122
224
  return;
123
225
  let currentPackage;
124
226
  try {
125
- if (selectedTarget?.issues?.length)
126
- throw new Error(selectedTarget.issues.join("\n"));
127
- currentPackage = packageCatalog.prepare(executionPackage);
227
+ if (authoringSource && !preparedDocument)
228
+ throw new Error('Project preparation is not ready.');
229
+ currentPackage = preparedDocument ?? packageCatalog.prepare(executionPackage);
128
230
  }
129
231
  catch (error) {
130
232
  setLoadError(error instanceof Error ? error.message : "Source target cannot execute.");
131
233
  return;
132
234
  }
133
235
  const id = crypto.randomUUID();
134
- setViewingHistoryEntry(undefined);
236
+ activeInputRequest.current = id;
135
237
  setRequestId(id);
136
238
  setResult(undefined);
137
239
  setEvents([]);
138
240
  stepStateCache.current = Object.fromEntries(flattenDisplaySteps(packageSteps).filter(step => step.structuralAddress).map(step => [occurrenceKey(step.structuralAddress), 'pending']));
139
- setStepStates(displaySteps.map(() => 'pending'));
241
+ setStepStates(liveDisplaySteps.map(() => 'pending'));
140
242
  setRunState('preflighting');
141
243
  setControlState('running');
142
- await client.submit({ requestId: id, executionPackage: currentPackage, inputs, environment, runtimeValues, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfterStepId } : {}) }, options: { ...(timeoutMs === 'none' ? {} : { timeoutMs: Number(timeoutMs) }), dryRun } });
244
+ let structure;
245
+ try {
246
+ structure = await captureHistoryStructure(currentPackage);
247
+ const common = { requestId: id, inputs, runtimeValues, options: { ...(timeoutMs === 'none' ? {} : { timeoutMs: Number(timeoutMs) }), dryRun } };
248
+ if (currentPackage.schemaVersion === 8)
249
+ await client.submit({ ...common, executionPackage: currentPackage, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfter: stopAfterStepId } : {}) } });
250
+ else
251
+ await client.submit({ ...common, executionPackage: currentPackage, debugSession: { breakpoints: [...breakpointsRef.current], ...(stopAfterStepId ? { stopAfterStepId } : {}) } });
252
+ }
253
+ catch (error) {
254
+ activeInputRequest.current = undefined;
255
+ setRunState('failed');
256
+ setControlState('failed');
257
+ setLoadError(error instanceof Error ? error.message : 'Execution admission failed.');
258
+ return;
259
+ }
143
260
  let completed = false;
144
261
  void (async () => {
145
262
  for await (const event of client.events(id)) {
@@ -156,25 +273,42 @@ function RunnerApp({ client, onExit, applicationMenu }) {
156
273
  continue;
157
274
  setControlState(state ?? 'running');
158
275
  }
159
- if (event.structuralAddress) {
276
+ if ('occurrence' in event && event.occurrence) {
277
+ const nextState = event.state;
278
+ stepStateCache.current[event.occurrence] = nextState;
279
+ setStepStates(previous => previous.map((state, index) => liveDisplaySteps[index]?.occurrence === event.occurrence ? nextState : state));
280
+ }
281
+ else if (event.structuralAddress) {
160
282
  const nextState = event.state;
161
283
  stepStateCache.current[occurrenceKey(event.structuralAddress)] = nextState;
162
- setStepStates(previous => previous.map((state, index) => occurrenceKey(displaySteps[index]?.structuralAddress) === occurrenceKey(event.structuralAddress) ? nextState : state));
284
+ setStepStates(previous => previous.map((state, index) => occurrenceKey(liveDisplaySteps[index]?.structuralAddress) === occurrenceKey(event.structuralAddress) ? nextState : state));
163
285
  }
164
286
  }
165
287
  })();
166
288
  const secretValues = collectSecretValues(inputs, runtimeValues);
167
289
  const executionResult = redactExecutionResult(await client.result(id), secretValues);
168
290
  completed = true;
291
+ try {
292
+ assertHistoryIdentity(id, structure, executionResult);
293
+ }
294
+ catch (error) {
295
+ activeInputRequest.current = undefined;
296
+ setRunState('failed');
297
+ setControlState('failed');
298
+ setLoadError(error.message);
299
+ return;
300
+ }
301
+ if (activeInputRequest.current === id)
302
+ activeInputRequest.current = undefined;
169
303
  const finalSteps = flattenResultSteps(executionResult.children?.flatMap(child => child.steps) ?? executionResult.steps);
170
304
  const finalStateById = Object.fromEntries(flattenDisplaySteps(packageSteps).filter(step => step.structuralAddress).map(step => [occurrenceKey(step.structuralAddress), stateForOccurrence(finalSteps, step.structuralAddress) ?? 'pending']));
171
305
  stepStateCache.current = { ...stepStateCache.current, ...finalStateById };
172
306
  setResult(executionResult);
173
- setStepStates(displaySteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending'));
307
+ setStepStates(liveDisplaySteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending'));
174
308
  setRunState(executionResult.state);
175
309
  setControlState(executionResult.state === 'cancelled' ? 'stopped' : executionResult.state);
176
310
  const finishedAt = executionResult.finishedAt ?? new Date().toISOString();
177
- const entry = { requestId: id, startedAt: executionResult.startedAt, finishedAt, target: executionPackage.target.name, packageId: executionPackage.packageId, artifactSchemaVersion: packageCatalog.artifactProvenance(executionPackage.packageId)?.originalVersion, artifactProvenance: packageCatalog.artifactProvenance(executionPackage.packageId), executionSchemaVersion: executionPackage.schemaVersion, environment: executionResult.environment, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
311
+ const entry = { historyVersion: 2, structure, requestId: id, startedAt: executionResult.startedAt, finishedAt, target: currentPackage.target.name, packageId: currentPackage.packageId, artifactSchemaVersion: packageCatalog.artifactProvenance(executionPackage.packageId)?.originalVersion, artifactProvenance: packageCatalog.artifactProvenance(executionPackage.packageId), executionSchemaVersion: currentPackage.schemaVersion, state: executionResult.state, durationMs: Math.max(0, new Date(finishedAt).getTime() - new Date(executionResult.startedAt).getTime()), result: executionResult };
178
312
  setRunHistory(previous => persistRunHistory(localStorage, HISTORY_KEY, [entry, ...previous.filter(item => item.requestId !== id)], historyRetention, secretValues));
179
313
  }
180
314
  async function stop() { if (requestId)
@@ -198,38 +332,53 @@ function RunnerApp({ client, onExit, applicationMenu }) {
198
332
  next.add(stepId); breakpointsRef.current = next; return next; }); }
199
333
  function toggleStopAfter(stepId) { setStopAfterStepId(previous => previous === stepId ? undefined : stepId); }
200
334
  function exportResult(value, packageId, schemaVersion, provenance, executionVersion) { if (!value)
201
- return; const exportPayload = createResultExport(value, packageId ?? 'unknown', schemaVersion, collectSecretValues(inputs, runtimeValues), provenance, executionVersion); const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${value.target.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}-${value.requestId}.result.json`; link.click(); URL.revokeObjectURL(url); }
202
- function reloadHistoryEntry(entry) { let loadedPackage = executionPackage; if (entry.packageId) {
203
- try {
204
- loadedPackage = packageCatalog.get(entry.packageId);
205
- setExecutionPackage(loadedPackage);
206
- setSelectedTargetId(loadedPackage.packageId);
207
- }
208
- catch { /* The result remains viewable when its package is no longer loaded. */ }
209
- } 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' }); }
335
+ return; const storedEntry = runHistory.find(entry => entry.requestId === value.requestId); const exportPayload = storedEntry?.historyVersion === 2 ? { runnerResultVersion: 2, entry: storedEntry } : createResultExport(value, packageId ?? 'unknown', schemaVersion, collectSecretValues(inputs, runtimeValues), provenance, executionVersion); const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${value.target.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase()}-${value.requestId}.result.json`; link.click(); URL.revokeObjectURL(url); }
336
+ function reloadHistoryEntry(entry) { setHistoricalEntry(entry); setHistorySelectedStep(0); setHistoryCollapsedSteps(new Set()); }
210
337
  function clearHistory() { setRunHistory([]); localStorage.removeItem(HISTORY_KEY); }
211
338
  function deleteHistoryEntry(requestIdToDelete) { setRunHistory(previous => { const next = previous.filter(item => item.requestId !== requestIdToDelete); localStorage.setItem(HISTORY_KEY, JSON.stringify(next)); return next; }); }
212
339
  function changeHistoryRetention(value) { setHistoryRetention(value); localStorage.setItem(HISTORY_RETENTION_KEY, String(value)); setRunHistory(previous => { const next = previous.slice(0, value); localStorage.setItem(HISTORY_KEY, JSON.stringify(next)); return next; }); }
213
- function rememberVisibleStates() { displaySteps.forEach((step, index) => { const state = stepStates[index]; if (state && step.structuralAddress)
340
+ function rememberVisibleStates() { liveDisplaySteps.forEach((step, index) => { const state = stepStates[index]; if (state && step.structuralAddress)
214
341
  stepStateCache.current[occurrenceKey(step.structuralAddress)] = state; }); }
215
- function toggleStep(stepId) { rememberVisibleStates(); const next = new Set(collapsedSteps); if (next.has(stepId))
342
+ function toggleStep(stepId) { if (readOnly) {
343
+ setHistoryCollapsedSteps(previous => { const next = new Set(previous); if (next.has(stepId))
344
+ next.delete(stepId);
345
+ else
346
+ next.add(stepId); return next; });
347
+ return;
348
+ } rememberVisibleStates(); const next = new Set(collapsedSteps); if (next.has(stepId))
216
349
  next.delete(stepId);
217
350
  else
218
- next.add(stepId); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
219
- function setAllCollapsed(collapsed) { rememberVisibleStates(); const next = collapsed ? new Set(containerStepIds(packageSteps)) : new Set(); const nextSteps = flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
351
+ next.add(stepId); const nextSteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, next, preparedDocument) : flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
352
+ function setAllCollapsed(collapsed) { if (readOnly) {
353
+ setHistoryCollapsedSteps(collapsed ? new Set(historicalEntry?.structure?.context.nodes.filter(n => n.children.length).map(n => n.key) ?? containerStepIds(shownPackageSteps)) : new Set());
354
+ return;
355
+ } rememberVisibleStates(); const next = collapsed ? new Set(preparedDocument?.context.nodes.filter(n => n.children.length).map(n => n.key) ?? containerStepIds(packageSteps)) : new Set(); const nextSteps = preparedDocument ? contextDisplaySteps(preparedDocument.context, next, preparedDocument) : flattenDisplaySteps(packageSteps, 0, undefined, undefined, next); setCollapsedSteps(next); setStepStates(nextSteps.map(step => stepStateCache.current[occurrenceKey(step.structuralAddress)] ?? 'pending')); }
220
356
  async function loadPackage(file) {
221
- if (!file)
357
+ if (!file || activeInputRequest.current)
222
358
  return;
223
359
  try {
224
- const target = loadTarget(await file.text());
360
+ const text = await file.text();
361
+ if (activeInputRequest.current)
362
+ return;
363
+ const target = loadTarget(text);
225
364
  const loaded = target.executionPackage;
365
+ const owner = target.authoringSource?.targets.find(item => item.target.kind === 'project')?.target ?? loaded?.target ?? target.authoringSource?.targets[0]?.target;
366
+ setLoadedContexts(previous => ({ ...previous, ...Object.fromEntries([loaded?.packageId, ...(target.authoringSource?.targets ?? []).map(item => item.packageId)].filter((id) => Boolean(id)).map(id => [id, { filename: file.name, owner }])) }));
226
367
  const selected = packageCatalog.addLoadedTarget(target);
227
368
  setSelectedTargetId(selected);
369
+ setAuthoringSource(target.authoringSource?.serialized);
370
+ setSelectedRoot('');
371
+ setPreparedDocument(undefined);
372
+ setPreparing(Boolean(target.authoringSource));
373
+ setResult(undefined);
374
+ setEvents([]);
375
+ setBreakpoints(new Set());
376
+ breakpointsRef.current = new Set();
377
+ setStopAfterStepId(undefined);
228
378
  if (!loaded) {
229
379
  localStorage.setItem(IMPORTED_PACKAGE_KEY, JSON.stringify(target));
230
380
  setCatalogVersion(v => v + 1);
231
381
  setRequestId(undefined);
232
- setViewingHistoryEntry(undefined);
233
382
  setControlState("idle");
234
383
  setRunState("ready");
235
384
  setLoadError(undefined);
@@ -252,18 +401,17 @@ function RunnerApp({ client, onExit, applicationMenu }) {
252
401
  setEvents([]);
253
402
  setResult(undefined);
254
403
  setRequestId(undefined);
255
- setViewingHistoryEntry(undefined);
256
404
  setControlState('idle');
257
405
  setRunState('ready');
258
406
  setLoadError(undefined);
259
407
  setLoadIssues([]);
260
408
  }
261
409
  catch (error) {
262
- setLoadError(error instanceof PackageLoadError ? `${error.category}: ${error.message}` : 'Unable to load the selected package.');
410
+ setLoadError(error instanceof PackageLoadError ? `${error.category}: ${error.message}` : 'Unable to load the selected Project.');
263
411
  setLoadIssues(error instanceof PackageLoadError ? error.issues : []);
264
412
  }
265
413
  }
266
- const statusLabel = runState === 'ready' ? 'Ready' : runState[0].toUpperCase() + runState.slice(1);
414
+ const statusLabel = !readOnly && runState === 'running' && controlState === 'paused' ? 'Paused' : shownRunState === 'ready' ? 'Ready' : shownRunState[0].toUpperCase() + shownRunState.slice(1);
267
415
  useEffect(() => {
268
416
  document.documentElement.dataset.theme = theme;
269
417
  document.documentElement.style.colorScheme = theme;
@@ -276,11 +424,11 @@ function RunnerApp({ client, onExit, applicationMenu }) {
276
424
  useEffect(() => { const close = () => setContextMenu(undefined); const keydown = (event) => { if (event.key === 'Escape')
277
425
  close(); }; document.addEventListener('click', close); document.addEventListener('keydown', keydown); return () => { document.removeEventListener('click', close); document.removeEventListener('keydown', keydown); }; }, []);
278
426
  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)
279
- setSelectedStep(index); }; document.addEventListener('click', navigateIssue); return () => document.removeEventListener('click', navigateIssue); }, [displaySteps]);
427
+ selectDisplayedStep(index); }; document.addEventListener('click', navigateIssue); return () => document.removeEventListener('click', navigateIssue); }, [displaySteps]);
280
428
  const projectFileInput = useRef(null);
281
429
  const menuActions = useRef({ enabled: () => false, openProject: async () => { }, about: () => { }, demo: () => { } });
282
430
  menuActions.current = {
283
- enabled: id => runnerMenus.some(menu => menu.items.some(([command]) => command === id)) && (!showAbout || id.startsWith('help.')),
431
+ enabled: id => runnerMenus.some(menu => menu.items.some(([command]) => command === id)) && (!showAbout || id.startsWith('help.')) && (id !== 'project.open' || !inputsLocked),
284
432
  openProject: file => loadPackage(new File([file.text], file.name, { type: 'application/json' })),
285
433
  about: () => setShowAbout(true),
286
434
  // Preserve the current standalone/combined destination exactly.
@@ -293,16 +441,39 @@ function RunnerApp({ client, onExit, applicationMenu }) {
293
441
  about: () => menuActions.current.about(),
294
442
  demo: () => menuActions.current.demo(),
295
443
  }), [registerMenu]);
296
- 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" })] }), !applicationMenu?.native && _jsx(HeaderHelp, { enabled: id => menuActions.current.enabled(id), onAbout: () => menuActions.current.about(), onOpenProject: () => projectFileInput.current?.click() }), _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(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsx("input", { ref: projectFileInput, type: "file", accept: "application/json,.json", hidden: true, "aria-label": "Open Project\u2026", onChange: event => { const file = event.target.files?.[0]; event.target.value = ''; void loadPackage(file); } }), _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)) }), 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: [_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") {
297
- event.preventDefault();
298
- setSelectedStep(Math.min(displaySteps.length - 1, index + 1));
299
- } if (event.key === "ArrowUp") {
300
- event.preventDefault();
301
- setSelectedStep(Math.max(0, index - 1));
302
- } }, 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 ${displayState.replaceAll(' ', '-')}${isLoop ? ' loop-icon' : ''}`, children: statusGlyph[displayState] }), _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: "\u21BB 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.replaceAll(' ', '-')}`, 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." })] }) })] });
444
+ 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" })] }), !applicationMenu?.native && _jsx(HeaderHelp, { enabled: id => menuActions.current.enabled(id), onAbout: () => menuActions.current.about(), onOpenProject: () => projectFileInput.current?.click() }), _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(ThemeToggle, { theme: theme, onChange: setTheme, className: "runner-theme-toggle" })] }), _jsx("input", { ref: projectFileInput, type: "file", accept: "application/json,.json", hidden: true, "aria-label": "Open Project\u2026", onChange: event => { const file = event.target.files?.[0]; event.target.value = ''; void loadPackage(file); } }), _jsx("div", { className: "runner-tabs", role: "tablist", "aria-label": "Runner workspace", children: ['execution', 'history'].map(tab => _jsx("button", { id: `runner-tab-${tab}`, role: "tab", "aria-selected": workspace === tab, "aria-controls": `runner-panel-${tab}`, tabIndex: workspace === tab ? 0 : -1, onKeyDown: tabKey, onClick: () => switchWorkspace(tab), children: tab === 'execution' ? 'Execution' : 'History' }, tab)) }), _jsx("div", { className: "runner-pane-scroll", role: "region", "aria-label": "Runner workspace and Inspector", tabIndex: 0, children: _jsxs("main", { style: { gridTemplateColumns: `minmax(560px, 1fr) 8px ${inspectorWidth}px`, minWidth: `${568 + inspectorWidth}px` }, className: "runner-main runner-tabbed-main", id: `runner-panel-${workspace}`, role: "tabpanel", "aria-labelledby": `runner-tab-${workspace}`, children: [_jsxs(_Fragment, { children: [_jsxs("section", { className: "runner-workspace", children: [readOnly && loadError && _jsx("p", { className: "load-error", role: "alert", children: loadError }), readOnly && loadIssues.length > 0 && _jsx(PreRunValidation, { issues: loadIssues, title: "Artifact validation" }), readOnly && historicalEntry && !historicalEntry.structure && _jsx("p", { className: "notice", children: "Original Project structure was not recorded for this run. Only recorded execution evidence is available." }), readOnly && _jsx(HistoryPanel, { entries: runHistory, onReload: reloadHistoryEntry, retention: historyRetention, onRetentionChange: changeHistoryRetention, onClear: clearHistory, onDelete: deleteHistoryEntry, onExport: exportResult }), _jsxs("div", { hidden: readOnly, children: [_jsx("h1", { children: contextTarget.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: ["Run only", _jsxs("select", { disabled: inputsLocked || preparing, value: selectedRoot, onChange: event => selectRoot(event.target.value), children: [_jsx("option", { value: "", children: "Entire project" }), preparedDocument && contextDisplaySteps(preparedDocument.context, new Set(), preparedDocument).filter(n => n.kind !== 'body' && n.kind !== 'branch').map(n => _jsxs("option", { value: n.id, children: [' '.repeat(n.depth), n.name] }, n.id))] })] }), _jsxs("label", { children: ["Timeout", _jsxs("select", { disabled: inputsLocked, 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("div", { className: "run-actions", role: "group", "aria-label": "Execution actions", children: [_jsx("button", { className: "button", disabled: inputsLocked, onClick: () => { if (!activeInputRequest.current)
445
+ setEditingInputs(value => !value); }, children: editingInputs ? 'Done inputs' : 'Edit inputs' }), _jsx("button", { className: "button primary", onClick: () => void run(), disabled: !!inputBlocker || preparing || !!preparationError || runState === 'preflighting' || runState === 'running', children: "\u25B6\u00A0 Run" }), _jsx("button", { className: "button dry-run", onClick: () => void run(true), disabled: !!inputBlocker || preparing || !!preparationError || 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"] })] })] }), preparing && _jsx("p", { role: "status", children: "Preparing Project\u2026" }), preparationError && _jsx("p", { role: "alert", children: preparationError }), preparedDocument && !preparedDocument.selection.executable && _jsx("section", { role: "alert", children: preparedDocument.selection.issues.map((i, index) => _jsxs("p", { children: [preparedDocument.context.nodes.find(n => n.key === i.at)?.name, ": ", i.message] }, index)) }), 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: inputsLocked ? 'Inputs are locked while execution is active.' : 'Values are sent with the next run' })] }), _jsxs("fieldset", { className: "input-grid input-lock-fields", "aria-label": "Invocation input values", disabled: inputsLocked, 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) => { if (!activeInputRequest.current)
446
+ 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 => { if (!activeInputRequest.current)
447
+ 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 => { if (!activeInputRequest.current)
448
+ setRuntimeValues(previous => ({ ...previous, [name]: value })); } }, name))] })] })] }), readOnly && historicalEntry && _jsxs("div", { className: "history-view-banner", children: ["Viewing stored results: ", historyRecordName(historicalEntry), " \u00B7 ", historicalEntry.state.toUpperCase(), " \u00B7 ", new Date(historicalEntry.startedAt).toLocaleString()] }), _jsxs("section", { className: "panel", hidden: readOnly && !historicalEntry, children: [_jsxs("div", { className: "panel-heading", children: [_jsx("h2", { children: readOnly ? 'Recorded execution' : 'Live Execution' }), _jsx("span", { className: "panel-spacer" }), _jsxs("span", { className: "execution-menu-label", hidden: readOnly, children: ["Right-click a row to manage its breakpoint \u00B7 Selected: ", displaySteps[shownSelectedStep]?.name ?? 'None'] }), _jsxs("span", { className: shownRunState === 'running' ? 'running' : shownRunState === '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, shownResult ? allResults : eventPresentationEvidence(events, requestId), Boolean(shownResult), step.occurrence) : []; const rowEvidence = shownResult ? allResults : eventPresentationEvidence(events, requestId); const repeatedMatches = rowEvidence.filter(r => step.occurrence ? r.occurrence === step.occurrence : 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 scope = readOnly ? historicalEntry?.structure?.scope : preparedDocument?.selection.root; const context = readOnly ? historicalEntry?.structure?.context : preparedDocument?.context; const outside = Boolean(step.occurrence && scope && context && !scopeIncludes(context, scope, step.occurrence)); const presentation = rowPresentation(step, rowEvidence, Boolean(shownResult), step.branchRow ? 'pending' : readOnly ? 'pending' : stepStates[index] ?? 'pending', { context, scope, result: shownResult }); const displayState = presentation.state; const isLoopContent = Boolean(step.loopId); const isLoopEnd = isLoopContent && displaySteps[index + 1]?.loopId !== step.loopId; return _jsxs("tr", { className: `${outside ? 'outside-scope ' : ''}${shownSelectedStep === 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") {
449
+ event.preventDefault();
450
+ selectDisplayedStep(Math.min(displaySteps.length - 1, index + 1));
451
+ } if (event.key === "ArrowUp") {
452
+ event.preventDefault();
453
+ selectDisplayedStep(Math.max(0, index - 1));
454
+ } }, onClick: () => selectDisplayedStep(index), onContextMenu: event => { event.preventDefault(); if (readOnly)
455
+ return; selectDisplayedStep(index); setContextMenu({ x: event.clientX, y: event.clientY, stepId: step.id }); }, children: [_jsx("td", { children: index + 1 }), _jsxs("td", { className: "hierarchy-cell", children: [_jsx(HierarchyGuides, { segments: guides.get(step.id) ?? [] }), _jsxs("span", { className: "hierarchy-name", style: { paddingLeft: `${step.depth * 18}px` }, children: [step.hasChildren && _jsx("button", { className: "collapse-toggle", "aria-label": `${(readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? 'Expand' : 'Collapse'} ${step.name}`, onClick: event => { event.stopPropagation(); toggleStep(step.id); }, children: (readOnly ? historyCollapsedSteps : collapsedSteps).has(step.id) ? '+' : '−' }), !step.hasChildren && _jsx("span", { className: "collapse-spacer", "aria-hidden": "true" }), _jsx(StructuralIcon, { kind: step.structuralKind }), !readOnly && breakpoints.has(step.id) && _jsx("span", { className: "breakpoint-marker", role: "img", "aria-label": "Breakpoint armed", title: "Breakpoint armed" }), _jsx("span", { children: step.name }), isLoop && _jsx("small", { className: "kind-label loop-label", children: "\u21BB LOOP" }), step.structuralKind === 'branch' && _jsx("small", { className: "kind-label", children: "BRANCH" }), (!step.structuralKind || step.structuralKind === 'unknown') && _jsx("small", { className: "kind-label", children: "TYPE NOT RECORDED" })] }), shownSelectedStep === index && _jsx("small", { className: "selected-label", children: "SELECTED" })] }), _jsxs("td", { children: [!outside && 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))), _jsxs("span", { className: "execution-status", children: [_jsx("span", { className: `step-icon execution-status-icon ${displayState.replaceAll(' ', '-')}`, "aria-hidden": "true", children: statusGlyph[displayState] }), _jsx("span", { className: `status ${displayState.replaceAll(' ', '-')}`, children: presentation.label })] }), latestQualifier && _jsx("small", { className: "latest-invocation", children: "Latest invocation" })] }), _jsx("td", { children: displayResults[index] ? new Date(shownResult?.finishedAt ?? '').toLocaleTimeString() : '—' }), _jsx("td", { children: displayResults[index] ? `${displayResults[index].durationMs} ms` : '—' })] }, `${step.id}-${index}`); }) })] }) }), !readOnly && 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" })] })] }), !readOnly && _jsxs("details", { className: "panel runner-event-log execution-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsxs("div", { className: "event-log-content", children: [_jsxs("div", { className: "event-log-tools", children: [_jsx("input", { className: "event-search", "aria-label": "Search event log", placeholder: "Search events", value: eventSearch, onChange: event => setEventSearch(event.target.value) }), _jsxs("select", { "aria-label": "Filter execution events", 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: "event-log-scroll", role: "region", "aria-label": "Execution events", tabIndex: 0, children: _jsx("ul", { className: "event-log-list", children: visibleEvents.map(event => _jsx("li", { children: _jsxs("button", { type: "button", className: "event-row", children: [_jsxs("span", { className: "event-row-meta", children: [_jsx("time", { dateTime: event.timestamp, children: new Date(event.timestamp).toLocaleTimeString() }), _jsx("code", { children: event.type.toUpperCase() }), _jsx("span", { className: "event-state", children: event.state.toUpperCase() })] }), _jsx("span", { className: "event-message", children: event.message }), event.parallel && _jsxs("small", { className: "parallel-event", children: ["Parallel ", event.parallel.completed, "/", event.parallel.total, " (", event.parallel.join, ")"] })] }) }, event.sequence)) }) }), executionEvents.length > 0 && visibleEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No events match the current filter." }), executionEvents.length === 0 && _jsx("p", { className: "empty-state", children: "No execution events yet." })] })] }, JSON.stringify(['execution', requestId ?? 'ready', selectedTargetId]))] }), _jsx("div", { className: "inspector-divider", role: "separator", "aria-label": "Resize Inspector", "aria-orientation": "vertical", "aria-controls": "runner-inspector", "aria-valuemin": INSPECTOR_MIN_WIDTH, "aria-valuemax": INSPECTOR_MAX_WIDTH, "aria-valuenow": inspectorWidth, "aria-valuetext": `${inspectorWidth} pixels`, tabIndex: 0, hidden: readOnly && !historicalEntry, onPointerDown: startInspectorResize, onPointerMove: moveInspectorResize, onPointerUp: finishInspectorResize, onPointerCancel: finishInspectorResize, onLostPointerCapture: finishInspectorResize, onKeyDown: inspectorResizeKey }), _jsxs("aside", { id: "runner-inspector", className: "inspector", hidden: readOnly && !historicalEntry, children: [_jsx("h2", { children: "Selected execution" }), _jsx("p", { className: "selected-detail-name", children: _jsx("strong", { children: displaySteps[shownSelectedStep]?.name ?? 'No step selected' }) }), _jsxs("p", { className: "run-id", children: ["Run ID: ", shownRequestId ?? 'No run started'] }), _jsx(InspectorSection, { title: "Artifact summary", children: _jsx(KeyValues, { values: ["Target", (readOnly ? historicalEntry?.result.target.name : executionPackage.target.name) ?? 'Unknown', "Type", (readOnly ? historicalEntry?.result.target.kind : executionPackage.target.kind) ?? 'Unknown', "Project reference", (readOnly ? historicalEntry?.packageId : executionPackage.packageId) ?? 'Not recorded', "Execution format", `Execution v${readOnly ? historicalEntry?.executionSchemaVersion ?? 'unknown' : preparedDocument?.schemaVersion ?? executionPackage.schemaVersion}`, "Definitions", shownPackage ? String(shownPackage.definitions.length) : 'Source Project unavailable', "Required inputs", shownPackage ? shownPackage.requiredInputs?.join(', ') || 'None' : 'Not recorded', "Runtime values", shownPackage ? shownPackage.runtimeRequirements?.join(', ') || 'None' : 'Not recorded'] }) }), _jsx(InspectorSection, { title: "Run outcome", children: _jsxs("div", { className: "notice", children: [_jsx("strong", { children: shownResult?.state.toUpperCase() ?? 'NOT STARTED' }), _jsx("br", {}), shownResult ? shownResult.state : 'Run the Project to inspect its terminal outcome.'] }) }), _jsx(InspectorSection, { title: "Selected step inputs", children: _jsx(KeyValues, { values: Object.entries(displayResults[shownSelectedStep]?.inputs ?? (selectedInvocations.length > 1 ? {} : readOnly ? {} : inputs)).flatMap(([key, value]) => [key, String(value)]) }) }), _jsx(InspectorSection, { title: "Selected step outputs", children: _jsx(KeyValues, { values: displayResults[shownSelectedStep]?.outputs ? Object.entries(displayResults[shownSelectedStep]?.outputs ?? {}).flatMap(([key, value]) => [key, String(value)]) : ['—', selectedInvocations.length > 1 ? `${selectedInvocations.length} recorded invocations; see iteration history` : 'Available after execution'] }) }), (shownResult?.error || displayResults[shownSelectedStep]?.error) && _jsx(InspectorSection, { title: "Error", children: _jsxs("div", { className: "error-notice", children: [_jsx("strong", { children: displayResults[shownSelectedStep]?.error?.code ?? shownResult?.error?.code }), _jsx("br", {}), displayResults[shownSelectedStep]?.error?.message ?? shownResult?.error?.message] }) }), shownResult?.issues !== undefined && _jsx(InspectorSection, { title: "Pre-run validation", children: _jsx(PreRunValidation, { issues: shownResult.issues }) }), _jsx(InspectorSection, { title: "Invocation path", children: _jsx("div", { className: "notice", children: displayResults[shownSelectedStep]?.invocationPath?.join(" › ") ?? "No invocation path available." }) }), _jsx(InspectorSection, { title: "Iteration history", children: _jsx(RecordedInvocations, { results: selectedInvocations, definition: findInspectionDefinition(shownPackageSteps, displaySteps[shownSelectedStep]?.structuralAddress) }, JSON.stringify([shownRequestId, displaySteps[shownSelectedStep]?.structuralAddress])) }), _jsx(InspectorSection, { title: "Execution timing", children: _jsxs("div", { className: "timing-details", children: [_jsxs("span", { children: ["Wall ", shownResult?.executionTiming?.wallDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Active ", shownResult?.executionTiming?.activeDurationMs ?? "—", " ms"] }), _jsxs("span", { children: ["Paused ", shownResult?.executionTiming?.pausedDurationMs ?? "—", " ms"] })] }) }), _jsx(InspectorSection, { title: "Selected step attempts", children: _jsx("div", { className: "attempt-history", children: displayResults[shownSelectedStep]?.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." }) }) }), readOnly && _jsxs("details", { className: "inspector-section inspector-event-log runner-event-log", children: [_jsx("summary", { children: "Event Log" }), _jsx("div", { className: "event-log-content", children: _jsx("p", { className: "empty-state", children: "Recorded events are unavailable for this run." }) })] }, JSON.stringify(['history', shownRequestId ?? 'ready', historicalEntry?.packageId])), _jsxs(InspectorSection, { title: "Selected step", children: [_jsxs("div", { className: "notice", children: [_jsx("strong", { children: displaySteps[shownSelectedStep]?.name ?? 'No step selected' }), _jsx("br", {}), displayResults[shownSelectedStep] ? displayResults[shownSelectedStep].error ? 'This step failed. See the error details above.' : `Completed in ${displayResults[shownSelectedStep].durationMs} ms.` : selectedInvocations.length > 1 ? `${selectedInvocations.length} invocations recorded. No single invocation selected.` : 'Run the Project to inspect execution details.', _jsxs("div", { className: "inspector-actions", children: [_jsx("button", { className: "button", onClick: () => void retrySelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || (!displayResults[shownSelectedStep]?.error && runState !== "completed"), children: "Retry Action" }), _jsx("button", { className: `button ${stopAfterStepId === displaySteps[shownSelectedStep]?.id ? "active" : ""}`, onClick: () => displaySteps[shownSelectedStep] && toggleStopAfter(displaySteps[shownSelectedStep].id), disabled: readOnly || !displaySteps[shownSelectedStep], children: "Stop After" }), _jsx("button", { className: "button", onClick: () => void runFromSelected(), disabled: readOnly || !!preparedDocument || !displayResults[shownSelectedStep] || runState === "running", children: "Run From Here" })] })] }), _jsxs("div", { className: "selection-nav", children: [_jsx("button", { className: "button", disabled: shownSelectedStep <= 0, onClick: () => selectDisplayedStep(index => Math.max(0, index - 1)), children: "\u2039 Previous" }), _jsx("span", { children: displaySteps.length ? `${shownSelectedStep + 1} / ${displaySteps.length}` : '0 / 0' }), _jsx("button", { className: "button", disabled: shownSelectedStep >= displaySteps.length - 1, onClick: () => selectDisplayedStep(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 Projects." }), _jsxs("small", { children: [RUNNER_UI_VERSION, " \u00B7 ", RUNNER_BUILD_LABEL, " \u00B7 QAick contract"] }), _jsx("footer", { children: "\u00A9 2006\u20132026 QAick. All rights reserved." })] }) })] });
456
+ }
457
+ function recordedDisplaySteps(results) {
458
+ const unique = new Map();
459
+ for (const step of results) {
460
+ const key = step.occurrence ?? occurrenceKey(step.structuralAddress);
461
+ if (!unique.has(key))
462
+ unique.set(key, { occurrence: step.occurrence, id: step.occurrence ?? step.id, name: step.name, kind: step.kind ?? 'action', structuralKind: step.kind, structuralAddress: step.structuralAddress, depth: Math.max(0, (step.structuralAddress?.segments.filter(segment => segment.kind === 'step').length ?? 1) - 1), hasChildren: false });
463
+ }
464
+ return [...unique.values()];
465
+ }
466
+ function historyRecordName(entry) { const context = entry.structure?.context; return context?.nodes.find(node => node.key === context.root)?.name ?? entry.target; }
467
+ function HistoryPanel({ entries, retention, onRetentionChange, onClear, onDelete, onExport, onReload }) {
468
+ 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: "Project" }), _jsx("th", { children: "Started" }), _jsx("th", { children: "Outcome" }), _jsx("th", { children: "Duration" }), _jsx("th", { children: "Result" })] }) }), _jsx("tbody", { children: entries.map(entry => {
469
+ const context = entry.structure?.context;
470
+ const projectName = historyRecordName(entry);
471
+ const scope = entry.structure?.scope;
472
+ const scopeName = !scope ? 'Scope not recorded' : scope.kind === 'document' ? 'Entire project' : context?.nodes.find(node => node.key === scope.key)?.name ?? 'Unavailable';
473
+ const runKind = entry.result.provenance ? 'Run From Here' : entry.result.steps.some(step => step.attempts?.some(attempt => attempt.kind === 'interactive')) ? 'Interactive retry' : 'Normal run';
474
+ return _jsxs("tr", { children: [_jsxs("td", { children: [_jsx("strong", { className: "history-project-name", children: projectName }), _jsxs("small", { className: "history-scope", children: [scope ? `Run only: ${scopeName}` : scopeName, runKind !== 'Normal run' && ` · ${runKind}`] }), entry.structure && entry.historyVersion !== 2 && _jsxs("small", { className: "history-meta", children: [entry.structure.context.coverage, " \u00B7 hash ", entry.structure.documentHash] })] }), _jsx("td", { children: _jsx("time", { dateTime: entry.startedAt, 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);
475
+ }) })] }) }) : _jsx("p", { className: "empty-state", children: "Completed and failed runs will be kept here in this browser." })] });
303
476
  }
304
- 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] }); }
305
- 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." })] }); }
306
477
  function InspectorSection({ title, children }) { return _jsxs("section", { className: "inspector-section", children: [_jsx("h3", { children: title }), children] }); }
307
478
  function KeyValues({ values }) { return _jsx("div", { className: "key-values", children: redactKeyValuePairs(values).map((value, index) => _jsx("span", { children: value }, `${value}-${index}`)) }); }
308
479
  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 })] }); }
@@ -314,7 +485,7 @@ function ParallelGroupDetails({ result }) { const group = result?.terminalReason
314
485
  function ResultSummary({ result }) { if (!result)
315
486
  return null; const steps = flattenResultSteps([result]); const counts = Object.fromEntries(['finished', 'failed', 'skipped', 'cancelled'].map(state => [state, steps.filter(step => step.state === state).length])); return _jsxs("div", { className: "result-summary", children: [_jsx("strong", { children: "Result summary" }), _jsxs("span", { children: [steps.length, " invocations \u00B7 ", result.durationMs, " ms"] }), _jsxs("small", { children: ["Finished ", counts.finished, " \u00B7 Failed ", counts.failed, " \u00B7 Skipped ", counts.skipped, " \u00B7 Cancelled ", counts.cancelled] })] }); }
316
487
  function PreRunValidation({ issues, title = 'Pre-run validation' }) { if (!issues.length)
317
- return _jsxs("section", { className: "validation-panel validation-pass", children: [_jsx("span", { className: "validation-icon", children: "\u2713" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " passed"] }), _jsx("small", { children: "No issues found. The next Run can invoke adapters." })] })] }); return _jsxs("section", { className: "validation-panel validation-failed", children: [_jsxs("div", { className: "validation-heading", children: [_jsx("span", { className: "validation-icon", children: "!" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " found ", issues.length, " issue", issues.length === 1 ? '' : 's'] }), _jsx("small", { children: "Resolve these items before running the package." })] })] }), _jsx("div", { className: "validation-issues", children: issues.map((issue, index) => _jsxs("div", { className: "validation-issue", "data-step-id": issue.stepId, children: [_jsx("span", { className: "issue-icon", children: "\u00D7" }), _jsxs("div", { children: [_jsxs("strong", { children: [diagnosticCategory(issue.code), " \u00B7 ", issue.code] }), _jsx("span", { children: issue.message }), issue.path && _jsxs("small", { children: ["Path: ", issue.path] }), issue.stepId && _jsxs("small", { children: ["Step: ", issue.stepId, " \u00B7 click to inspect"] })] })] }, `${issue.code}-${issue.stepId ?? issue.path ?? 'run'}-${index}`)) })] }); }
488
+ return _jsxs("section", { className: "validation-panel validation-pass", children: [_jsx("span", { className: "validation-icon", children: "\u2713" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " passed"] }), _jsx("small", { children: "No issues found. The next Run can invoke adapters." })] })] }); return _jsxs("section", { className: "validation-panel validation-failed", children: [_jsxs("div", { className: "validation-heading", children: [_jsx("span", { className: "validation-icon", children: "!" }), _jsxs("div", { children: [_jsxs("strong", { children: [title, " found ", issues.length, " issue", issues.length === 1 ? '' : 's'] }), _jsx("small", { children: "Resolve these items before running the Project." })] })] }), _jsx("div", { className: "validation-issues", children: issues.map((issue, index) => _jsxs("div", { className: "validation-issue", "data-step-id": issue.stepId, children: [_jsx("span", { className: "issue-icon", children: "\u00D7" }), _jsxs("div", { children: [_jsxs("strong", { children: [diagnosticCategory(issue.code), " \u00B7 ", issue.code] }), _jsx("span", { children: issue.message }), issue.path && _jsxs("small", { children: ["Path: ", issue.path] }), issue.stepId && _jsxs("small", { children: ["Step: ", issue.stepId, " \u00B7 click to inspect"] })] })] }, `${issue.code}-${issue.stepId ?? issue.path ?? 'run'}-${index}`)) })] }); }
318
489
  function diagnosticCategory(code) { if (code === 'CONTRACT_INVALID' || code === 'UNSUPPORTED_SCHEMA')
319
490
  return 'contract-invalid'; if (code === 'INVALID_BINDING' || code === 'MISSING_INPUT_BINDING' || code === 'MISSING_STEP_BINDING' || code === 'MISSING_STEP_OUTPUT' || code === 'BINDING_FAILED')
320
491
  return 'binding-unresolved'; if (code.startsWith('HTTP_') || code === 'ACTION_FAILED' || code === 'MISSING_ACTION_ADAPTER')
@@ -326,11 +497,11 @@ function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new
326
497
  const isLoop = step.control?.type === 'repeat';
327
498
  const currentLoopId = isLoop ? step.id : loopId;
328
499
  const branchMode = (step.control?.type === 'branch' || step.control?.type === 'parallel') && step.control.branches?.length;
329
- const row = { structuralAddress: step.structuralAddress, id: step.id, name: step.name, depth, kind: step.kind ?? (step.children ? 'operation' : 'action'), loopRepeat: isLoop, loopTotal: step.control?.iterations, loopDuration: step.control?.durationMs, loopId: currentLoopId, parentId, hasChildren: Boolean(step.children?.length || branchMode) };
500
+ const row = { structuralAddress: step.structuralAddress, id: step.id, name: step.name, depth, kind: step.kind ?? (step.children ? 'operation' : 'action'), structuralKind: step.kind, loopRepeat: isLoop, loopTotal: step.control?.iterations, loopDuration: step.control?.durationMs, loopId: currentLoopId, parentId, hasChildren: Boolean(step.children?.length || branchMode) };
330
501
  if (branchMode && !collapsed.has(step.id)) {
331
502
  const branchRows = step.control.branches.flatMap((branch, branchIndex) => {
332
503
  const branchId = `${step.id}.${branch.id}`;
333
- const branchRow = { id: branchId, name: branch.name ?? branch.id, depth: depth + 1, kind: 'control-block', loopId: currentLoopId, parentId: step.id, hasChildren: branch.children.length > 0, branchRow: true, branchOwner: step.structuralAddress, branchPosition: branchIndex + 1 };
504
+ const branchRow = { id: branchId, name: branch.name ?? branch.id, depth: depth + 1, kind: 'control-block', structuralKind: 'branch', loopId: currentLoopId, parentId: step.id, hasChildren: branch.children.length > 0, branchRow: true, branchOwner: step.structuralAddress, branchPosition: branchIndex + 1 };
334
505
  return [branchRow, ...(collapsed.has(branchId) ? [] : flattenDisplaySteps(branch.children, depth + 2, currentLoopId, branchId, collapsed))];
335
506
  });
336
507
  return [row, ...branchRows];
@@ -340,8 +511,8 @@ function flattenDisplaySteps(steps, depth = 0, loopId, parentId, collapsed = new
340
511
  }
341
512
  export function orderDisplayStepsByResult(steps, _results) { return steps; }
342
513
  export const occurrenceKey = (address) => address ? JSON.stringify([address.scope, address.segments]) : '';
343
- export function resultsForOccurrence(results, address) { return results.filter(r => address && occurrenceKey(r.structuralAddress) === occurrenceKey(address)); }
344
- export function resultForOccurrence(results, address) { const matches = resultsForOccurrence(results, address); return matches.length === 1 ? matches[0] : undefined; }
514
+ export function resultsForOccurrence(results, address, occurrence) { return results.filter(r => occurrence ? r.occurrence === occurrence : address && occurrenceKey(r.structuralAddress) === occurrenceKey(address)); }
515
+ export function resultForOccurrence(results, address, occurrence) { const matches = resultsForOccurrence(results, address, occurrence); return matches.length === 1 ? matches[0] : undefined; }
345
516
  export function stateForOccurrence(results, address) {
346
517
  const matches = resultsForOccurrence(results, address);
347
518
  if (!matches.length)
@@ -379,4 +550,9 @@ export function RecordedInvocations({ results, definition }) {
379
550
  }
380
551
  function containerStepIds(steps) { return steps.flatMap(step => { const branchIds = step.control?.branches?.flatMap(branch => [`${step.id}.${branch.id}`, ...containerStepIds(branch.children)]) ?? []; return step.children?.length || branchIds.length ? [step.id, ...branchIds, ...(step.children ? containerStepIds(step.children) : [])] : []; }); }
381
552
  function flattenResultSteps(steps) { return steps.flatMap(step => [step, ...(step.children ? flattenResultSteps(step.children) : [])]); }
553
+ function contextDisplaySteps(context, collapsed, document) {
554
+ const byKey = new Map(context.nodes.map(n => [n.key, n]));
555
+ function visit(key, depth, parentId) { const n = byKey.get(key); const visible = !['project', 'body'].includes(n.kind); const payload = document?.nodes.find(p => p.occurrence === key); const row = { parentId, structuralKind: n.kind, id: key, occurrence: key, name: n.name, kind: n.kind === 'project' ? 'flow' : n.kind === 'unknown' ? 'action' : n.kind, depth, structuralAddress: n.originalAddress, hasChildren: n.children.length > 0, loopRepeat: payload?.control?.type === 'repeat', loopTotal: payload?.control?.iterations, loopDuration: payload?.control?.durationMs }; return [...(visible ? [row] : []), ...(collapsed.has(key) ? [] : [...(n.sequence ?? []), ...n.children.filter(child => !n.sequence?.includes(child))].flatMap(child => visit(child, depth + (visible ? 1 : 0), visible ? key : parentId)))]; }
556
+ return visit(context.root, 0);
557
+ }
382
558
  //# sourceMappingURL=RunnerModule.js.map