@bian-womp/spark-workbench 0.3.90 → 0.3.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.cjs +19 -6
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/layout.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/misc/value.d.ts +2 -1
- package/lib/cjs/src/misc/value.d.ts.map +1 -1
- package/lib/esm/index.js +19 -6
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/layout.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/misc/value.d.ts +2 -1
- package/lib/esm/src/misc/value.d.ts.map +1 -1
- package/package.json +4 -4
- package/lib/src/adapters/cli/index.d.ts +0 -22
- package/lib/src/adapters/cli/index.d.ts.map +0 -1
- package/lib/src/adapters/cli/index.js +0 -50
- package/lib/src/adapters/cli/index.js.map +0 -1
- package/lib/src/core/AbstractWorkbench.d.ts +0 -40
- package/lib/src/core/AbstractWorkbench.d.ts.map +0 -1
- package/lib/src/core/AbstractWorkbench.js +0 -15
- package/lib/src/core/AbstractWorkbench.js.map +0 -1
- package/lib/src/core/InMemoryWorkbench.d.ts +0 -304
- package/lib/src/core/InMemoryWorkbench.d.ts.map +0 -1
- package/lib/src/core/InMemoryWorkbench.js +0 -1016
- package/lib/src/core/InMemoryWorkbench.js.map +0 -1
- package/lib/src/core/contracts.d.ts +0 -172
- package/lib/src/core/contracts.d.ts.map +0 -1
- package/lib/src/core/contracts.js +0 -2
- package/lib/src/core/contracts.js.map +0 -1
- package/lib/src/core/ui-extensions.d.ts +0 -85
- package/lib/src/core/ui-extensions.d.ts.map +0 -1
- package/lib/src/core/ui-extensions.js +0 -111
- package/lib/src/core/ui-extensions.js.map +0 -1
- package/lib/src/examples/cli.d.ts +0 -2
- package/lib/src/examples/cli.d.ts.map +0 -1
- package/lib/src/examples/cli.js +0 -244
- package/lib/src/examples/cli.js.map +0 -1
- package/lib/src/examples/reactflow/App.d.ts +0 -2
- package/lib/src/examples/reactflow/App.d.ts.map +0 -1
- package/lib/src/examples/reactflow/App.js +0 -20
- package/lib/src/examples/reactflow/App.js.map +0 -1
- package/lib/src/index.d.ts +0 -30
- package/lib/src/index.d.ts.map +0 -1
- package/lib/src/index.js +0 -30
- package/lib/src/index.js.map +0 -1
- package/lib/src/misc/DebugEvents.d.ts +0 -7
- package/lib/src/misc/DebugEvents.d.ts.map +0 -1
- package/lib/src/misc/DebugEvents.js +0 -51
- package/lib/src/misc/DebugEvents.js.map +0 -1
- package/lib/src/misc/DefaultEdge.d.ts +0 -5
- package/lib/src/misc/DefaultEdge.d.ts.map +0 -1
- package/lib/src/misc/DefaultEdge.js +0 -15
- package/lib/src/misc/DefaultEdge.js.map +0 -1
- package/lib/src/misc/DefaultNode.d.ts +0 -5
- package/lib/src/misc/DefaultNode.d.ts.map +0 -1
- package/lib/src/misc/DefaultNode.js +0 -25
- package/lib/src/misc/DefaultNode.js.map +0 -1
- package/lib/src/misc/DefaultNodeContent.d.ts +0 -4
- package/lib/src/misc/DefaultNodeContent.d.ts.map +0 -1
- package/lib/src/misc/DefaultNodeContent.js +0 -58
- package/lib/src/misc/DefaultNodeContent.js.map +0 -1
- package/lib/src/misc/DefaultNodeHeader.d.ts +0 -13
- package/lib/src/misc/DefaultNodeHeader.d.ts.map +0 -1
- package/lib/src/misc/DefaultNodeHeader.js +0 -78
- package/lib/src/misc/DefaultNodeHeader.js.map +0 -1
- package/lib/src/misc/Inspector.d.ts +0 -12
- package/lib/src/misc/Inspector.d.ts.map +0 -1
- package/lib/src/misc/Inspector.js +0 -253
- package/lib/src/misc/Inspector.js.map +0 -1
- package/lib/src/misc/IssueBadge.d.ts +0 -7
- package/lib/src/misc/IssueBadge.d.ts.map +0 -1
- package/lib/src/misc/IssueBadge.js +0 -7
- package/lib/src/misc/IssueBadge.js.map +0 -1
- package/lib/src/misc/KeyboardShortcutToast.d.ts +0 -16
- package/lib/src/misc/KeyboardShortcutToast.d.ts.map +0 -1
- package/lib/src/misc/KeyboardShortcutToast.js +0 -40
- package/lib/src/misc/KeyboardShortcutToast.js.map +0 -1
- package/lib/src/misc/NodeHandles.d.ts +0 -18
- package/lib/src/misc/NodeHandles.d.ts.map +0 -1
- package/lib/src/misc/NodeHandles.js +0 -67
- package/lib/src/misc/NodeHandles.js.map +0 -1
- package/lib/src/misc/SelectionActiveSync.d.ts +0 -10
- package/lib/src/misc/SelectionActiveSync.d.ts.map +0 -1
- package/lib/src/misc/SelectionActiveSync.js +0 -21
- package/lib/src/misc/SelectionActiveSync.js.map +0 -1
- package/lib/src/misc/WorkbenchCanvas.d.ts +0 -23
- package/lib/src/misc/WorkbenchCanvas.d.ts.map +0 -1
- package/lib/src/misc/WorkbenchCanvas.js +0 -669
- package/lib/src/misc/WorkbenchCanvas.js.map +0 -1
- package/lib/src/misc/WorkbenchStudio.d.ts +0 -43
- package/lib/src/misc/WorkbenchStudio.d.ts.map +0 -1
- package/lib/src/misc/WorkbenchStudio.js +0 -463
- package/lib/src/misc/WorkbenchStudio.js.map +0 -1
- package/lib/src/misc/constants.d.ts +0 -4
- package/lib/src/misc/constants.d.ts.map +0 -1
- package/lib/src/misc/constants.js +0 -5
- package/lib/src/misc/constants.js.map +0 -1
- package/lib/src/misc/context/WorkbenchContext.d.ts +0 -133
- package/lib/src/misc/context/WorkbenchContext.d.ts.map +0 -1
- package/lib/src/misc/context/WorkbenchContext.js +0 -9
- package/lib/src/misc/context/WorkbenchContext.js.map +0 -1
- package/lib/src/misc/context/WorkbenchContext.provider.d.ts +0 -12
- package/lib/src/misc/context/WorkbenchContext.provider.d.ts.map +0 -1
- package/lib/src/misc/context/WorkbenchContext.provider.js +0 -995
- package/lib/src/misc/context/WorkbenchContext.provider.js.map +0 -1
- package/lib/src/misc/context-menu/ContextMenuButton.d.ts +0 -8
- package/lib/src/misc/context-menu/ContextMenuButton.d.ts.map +0 -1
- package/lib/src/misc/context-menu/ContextMenuButton.js +0 -10
- package/lib/src/misc/context-menu/ContextMenuButton.js.map +0 -1
- package/lib/src/misc/context-menu/ContextMenuHandlers.d.ts +0 -85
- package/lib/src/misc/context-menu/ContextMenuHandlers.d.ts.map +0 -1
- package/lib/src/misc/context-menu/ContextMenuHandlers.js +0 -2
- package/lib/src/misc/context-menu/ContextMenuHandlers.js.map +0 -1
- package/lib/src/misc/context-menu/ContextMenuHelpers.d.ts +0 -45
- package/lib/src/misc/context-menu/ContextMenuHelpers.d.ts.map +0 -1
- package/lib/src/misc/context-menu/ContextMenuHelpers.js +0 -182
- package/lib/src/misc/context-menu/ContextMenuHelpers.js.map +0 -1
- package/lib/src/misc/context-menu/DefaultContextMenu.d.ts +0 -3
- package/lib/src/misc/context-menu/DefaultContextMenu.d.ts.map +0 -1
- package/lib/src/misc/context-menu/DefaultContextMenu.js +0 -111
- package/lib/src/misc/context-menu/DefaultContextMenu.js.map +0 -1
- package/lib/src/misc/context-menu/NodeContextMenu.d.ts +0 -3
- package/lib/src/misc/context-menu/NodeContextMenu.d.ts.map +0 -1
- package/lib/src/misc/context-menu/NodeContextMenu.js +0 -51
- package/lib/src/misc/context-menu/NodeContextMenu.js.map +0 -1
- package/lib/src/misc/context-menu/SelectionContextMenu.d.ts +0 -3
- package/lib/src/misc/context-menu/SelectionContextMenu.d.ts.map +0 -1
- package/lib/src/misc/context-menu/SelectionContextMenu.js +0 -47
- package/lib/src/misc/context-menu/SelectionContextMenu.js.map +0 -1
- package/lib/src/misc/hooks.d.ts +0 -18
- package/lib/src/misc/hooks.d.ts.map +0 -1
- package/lib/src/misc/hooks.js +0 -275
- package/lib/src/misc/hooks.js.map +0 -1
- package/lib/src/misc/layout.d.ts +0 -117
- package/lib/src/misc/layout.d.ts.map +0 -1
- package/lib/src/misc/layout.js +0 -205
- package/lib/src/misc/layout.js.map +0 -1
- package/lib/src/misc/load.d.ts +0 -5
- package/lib/src/misc/load.d.ts.map +0 -1
- package/lib/src/misc/load.js +0 -106
- package/lib/src/misc/load.js.map +0 -1
- package/lib/src/misc/mapping.d.ts +0 -128
- package/lib/src/misc/mapping.d.ts.map +0 -1
- package/lib/src/misc/mapping.js +0 -270
- package/lib/src/misc/mapping.js.map +0 -1
- package/lib/src/misc/merge-utils.d.ts +0 -12
- package/lib/src/misc/merge-utils.d.ts.map +0 -1
- package/lib/src/misc/merge-utils.js +0 -51
- package/lib/src/misc/merge-utils.js.map +0 -1
- package/lib/src/misc/thumbnail-utils.d.ts +0 -53
- package/lib/src/misc/thumbnail-utils.d.ts.map +0 -1
- package/lib/src/misc/thumbnail-utils.js +0 -629
- package/lib/src/misc/thumbnail-utils.js.map +0 -1
- package/lib/src/misc/types.d.ts +0 -18
- package/lib/src/misc/types.d.ts.map +0 -1
- package/lib/src/misc/types.js +0 -2
- package/lib/src/misc/types.js.map +0 -1
- package/lib/src/misc/value.d.ts +0 -16
- package/lib/src/misc/value.d.ts.map +0 -1
- package/lib/src/misc/value.js +0 -114
- package/lib/src/misc/value.js.map +0 -1
- package/lib/src/misc/viewport-utils.d.ts +0 -6
- package/lib/src/misc/viewport-utils.d.ts.map +0 -1
- package/lib/src/misc/viewport-utils.js +0 -18
- package/lib/src/misc/viewport-utils.js.map +0 -1
- package/lib/src/runtime/AbstractGraphRunner.d.ts +0 -61
- package/lib/src/runtime/AbstractGraphRunner.d.ts.map +0 -1
- package/lib/src/runtime/AbstractGraphRunner.js +0 -63
- package/lib/src/runtime/AbstractGraphRunner.js.map +0 -1
- package/lib/src/runtime/IGraphRunner.d.ts +0 -100
- package/lib/src/runtime/IGraphRunner.d.ts.map +0 -1
- package/lib/src/runtime/IGraphRunner.js +0 -2
- package/lib/src/runtime/IGraphRunner.js.map +0 -1
- package/lib/src/runtime/LocalGraphRunner.d.ts +0 -60
- package/lib/src/runtime/LocalGraphRunner.d.ts.map +0 -1
- package/lib/src/runtime/LocalGraphRunner.js +0 -294
- package/lib/src/runtime/LocalGraphRunner.js.map +0 -1
- package/lib/src/runtime/RemoteGraphRunner.d.ts +0 -109
- package/lib/src/runtime/RemoteGraphRunner.d.ts.map +0 -1
- package/lib/src/runtime/RemoteGraphRunner.js +0 -696
- package/lib/src/runtime/RemoteGraphRunner.js.map +0 -1
|
@@ -1,995 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick } from "../hooks";
|
|
4
|
-
import { WorkbenchContext } from "./WorkbenchContext";
|
|
5
|
-
import { estimateNodeSize, computeEffectiveHandles } from "../layout";
|
|
6
|
-
import { resolveOutputDisplay } from "../value";
|
|
7
|
-
import { excludeViewportFromUIState, isValidViewport } from "../viewport-utils";
|
|
8
|
-
// Helper to compute invalidated status from runtime metadata
|
|
9
|
-
function computeInvalidatedFromMetadata(metadata) {
|
|
10
|
-
if (!metadata)
|
|
11
|
-
return true;
|
|
12
|
-
const { lastSuccessAt, lastInputAt, lastRunAt } = metadata;
|
|
13
|
-
if (!lastSuccessAt && !lastRunAt)
|
|
14
|
-
return true;
|
|
15
|
-
if (!lastInputAt || Object.keys(lastInputAt).length === 0)
|
|
16
|
-
return false;
|
|
17
|
-
const maxInputTime = Math.max(...Object.values(lastInputAt));
|
|
18
|
-
return maxInputTime > (lastSuccessAt ?? lastRunAt ?? 0);
|
|
19
|
-
}
|
|
20
|
-
export function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
|
|
21
|
-
const [nodeStatus, setNodeStatus] = useState({});
|
|
22
|
-
const [edgeStatus, setEdgeStatus] = useState({});
|
|
23
|
-
const [runMode, setRunModeState] = useState("manual");
|
|
24
|
-
const [events, setEvents] = useState([]);
|
|
25
|
-
const clearEvents = useCallback(() => setEvents([]), []);
|
|
26
|
-
const [systemErrors, setSystemErrors] = useState([]);
|
|
27
|
-
const [registryErrors, setRegistryErrors] = useState([]);
|
|
28
|
-
const [inputValidationErrors, setInputValidationErrors] = useState([]);
|
|
29
|
-
const [registryVersion, setRegistryVersion] = useState(0);
|
|
30
|
-
const [registryReady, setRegistryReady] = useState(false);
|
|
31
|
-
const clearSystemErrors = useCallback(() => setSystemErrors([]), []);
|
|
32
|
-
const clearRegistryErrors = useCallback(() => setRegistryErrors([]), []);
|
|
33
|
-
const clearInputValidationErrors = useCallback(() => setInputValidationErrors([]), []);
|
|
34
|
-
const removeSystemError = useCallback((index) => {
|
|
35
|
-
setSystemErrors((prev) => prev.filter((_, idx) => idx !== index));
|
|
36
|
-
}, []);
|
|
37
|
-
const removeRegistryError = useCallback((index) => {
|
|
38
|
-
setRegistryErrors((prev) => prev.filter((_, idx) => idx !== index));
|
|
39
|
-
}, []);
|
|
40
|
-
const removeInputValidationError = useCallback((index) => {
|
|
41
|
-
setInputValidationErrors((prev) => prev.filter((_, idx) => idx !== index));
|
|
42
|
-
}, []);
|
|
43
|
-
// Fallback progress animation: drive progress to 100% over ~2 minutes
|
|
44
|
-
const FALLBACK_TOTAL_MS = 2 * 60 * 1000;
|
|
45
|
-
const [fallbackStarts, setFallbackStarts] = useState({});
|
|
46
|
-
// Track runs that emitted an error so we can keep progress on completion
|
|
47
|
-
const errorRunsRef = useRef({});
|
|
48
|
-
// Periodically advance fallback progress for running nodes without explicit progress
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
const interval = setInterval(() => {
|
|
51
|
-
setNodeStatus((prev) => {
|
|
52
|
-
let changed = false;
|
|
53
|
-
const next = { ...prev };
|
|
54
|
-
const now = Date.now();
|
|
55
|
-
for (const id of Object.keys(prev)) {
|
|
56
|
-
const st = prev[id];
|
|
57
|
-
if (!st)
|
|
58
|
-
continue;
|
|
59
|
-
const runs = st.activeRuns ?? 0;
|
|
60
|
-
const startAt = fallbackStarts[id];
|
|
61
|
-
if (runs > 0 && startAt) {
|
|
62
|
-
const cur = Math.max(0, Math.min(1, Number(st.progress) || 0));
|
|
63
|
-
const elapsed = Math.max(0, now - startAt);
|
|
64
|
-
// Approach 100% over the target window, but cap below 1 until done
|
|
65
|
-
const target = Math.max(0, Math.min(0.99, elapsed / FALLBACK_TOTAL_MS));
|
|
66
|
-
const merged = Math.max(cur, target);
|
|
67
|
-
if (merged > cur + 0.005 && merged <= 1) {
|
|
68
|
-
next[id] = { ...st, progress: merged };
|
|
69
|
-
changed = true;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return changed ? next : prev;
|
|
74
|
-
});
|
|
75
|
-
}, 200);
|
|
76
|
-
return () => clearInterval(interval);
|
|
77
|
-
}, [fallbackStarts]);
|
|
78
|
-
// Validation
|
|
79
|
-
const [validation, setValidation] = useState(undefined);
|
|
80
|
-
// Selection (mirror workbench selectionChanged)
|
|
81
|
-
const [selectedNodeId, setSelectedNodeId] = useState();
|
|
82
|
-
const [selectedEdgeId, setSelectedEdgeId] = useState();
|
|
83
|
-
const setSelection = useCallback((sel) => wb.setSelection(sel), [wb]);
|
|
84
|
-
// Ticks
|
|
85
|
-
const graphTick = useWorkbenchGraphTick(wb);
|
|
86
|
-
const graphUiTick = useWorkbenchGraphUiTick(wb);
|
|
87
|
-
const versionTick = useWorkbenchVersionTick(runner);
|
|
88
|
-
const valuesTick = versionTick + graphTick + graphUiTick;
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
const offRunnerStatus = runner.on("status", (status) => {
|
|
91
|
-
if (status.runMode) {
|
|
92
|
-
setRunModeState(status.runMode);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
return () => {
|
|
96
|
-
offRunnerStatus();
|
|
97
|
-
};
|
|
98
|
-
}, [runner]);
|
|
99
|
-
const handlesMap = useMemo(() => {
|
|
100
|
-
const out = {};
|
|
101
|
-
for (const n of wb.def.nodes) {
|
|
102
|
-
out[n.nodeId] = computeEffectiveHandles(n, wb.registry);
|
|
103
|
-
}
|
|
104
|
-
return out;
|
|
105
|
-
}, [wb, wb.def, graphTick, wb.registry, registryVersion]);
|
|
106
|
-
// Def and IO values
|
|
107
|
-
const inputsMap = useMemo(() => runner.getInputs(wb.def), [runner, wb, wb.def, valuesTick]);
|
|
108
|
-
const inputDefaultsMap = useMemo(() => runner.getInputDefaults(wb.def), [runner, wb, wb.def, valuesTick]);
|
|
109
|
-
const outputsMap = useMemo(() => runner.getOutputs(wb.def), [runner, wb, wb.def, valuesTick]);
|
|
110
|
-
const outputTypesMap = useMemo(() => {
|
|
111
|
-
const out = {};
|
|
112
|
-
// Local: runtimeTypeId is not stored; derive from typed wrapper in outputsMap
|
|
113
|
-
for (const n of wb.def.nodes) {
|
|
114
|
-
const outputsDecl = handlesMap[n.nodeId]?.outputs ?? {};
|
|
115
|
-
const handles = Object.keys(outputsDecl);
|
|
116
|
-
const cur = {};
|
|
117
|
-
for (const h of handles) {
|
|
118
|
-
const v = outputsMap[n.nodeId]?.[h];
|
|
119
|
-
const declared = outputsDecl[h];
|
|
120
|
-
const { typeId } = resolveOutputDisplay(v, declared);
|
|
121
|
-
cur[h] = typeId;
|
|
122
|
-
}
|
|
123
|
-
if (Object.keys(cur).length > 0)
|
|
124
|
-
out[n.nodeId] = cur;
|
|
125
|
-
}
|
|
126
|
-
return out;
|
|
127
|
-
}, [wb, wb.def, outputsMap, handlesMap]);
|
|
128
|
-
// Initialize nodes and derive invalidated status from persisted metadata
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
const workbenchRuntimeState = wb.getRuntimeState() ?? { nodes: {} };
|
|
131
|
-
setNodeStatus((prev) => {
|
|
132
|
-
const next = { ...prev };
|
|
133
|
-
const metadata = workbenchRuntimeState;
|
|
134
|
-
for (const n of wb.def.nodes) {
|
|
135
|
-
const cur = next[n.nodeId] ?? (next[n.nodeId] = {});
|
|
136
|
-
const nodeMeta = metadata.nodes[n.nodeId];
|
|
137
|
-
const updates = {};
|
|
138
|
-
if (cur.invalidated === undefined) {
|
|
139
|
-
updates.invalidated = computeInvalidatedFromMetadata(nodeMeta);
|
|
140
|
-
}
|
|
141
|
-
if (cur.activeRunIds === undefined) {
|
|
142
|
-
updates.activeRunIds = [];
|
|
143
|
-
}
|
|
144
|
-
if (cur.activeRuns === undefined) {
|
|
145
|
-
updates.activeRuns = 0;
|
|
146
|
-
}
|
|
147
|
-
if (nodeMeta?.lastErrorSummary && cur.lastError === undefined) {
|
|
148
|
-
updates.lastError = nodeMeta.lastErrorSummary;
|
|
149
|
-
}
|
|
150
|
-
if (Object.keys(updates).length > 0) {
|
|
151
|
-
next[n.nodeId] = { ...cur, ...updates };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return next;
|
|
155
|
-
});
|
|
156
|
-
}, [wb.def, wb]);
|
|
157
|
-
// Auto layout (simple layered layout)
|
|
158
|
-
const runAutoLayout = useCallback(() => {
|
|
159
|
-
// Build DAG layers by indegree
|
|
160
|
-
const indegree = {};
|
|
161
|
-
const adj = {};
|
|
162
|
-
for (const n of wb.def.nodes) {
|
|
163
|
-
indegree[n.nodeId] = 0;
|
|
164
|
-
adj[n.nodeId] = [];
|
|
165
|
-
}
|
|
166
|
-
for (const e of wb.def.edges) {
|
|
167
|
-
indegree[e.target.nodeId] = (indegree[e.target.nodeId] ?? 0) + 1;
|
|
168
|
-
adj[e.source.nodeId].push(e.target.nodeId);
|
|
169
|
-
}
|
|
170
|
-
const q = Object.keys(indegree).filter((k) => indegree[k] === 0);
|
|
171
|
-
const layers = [];
|
|
172
|
-
while (q.length) {
|
|
173
|
-
const layer = [];
|
|
174
|
-
const next = [];
|
|
175
|
-
for (const id of q) {
|
|
176
|
-
layer.push(id);
|
|
177
|
-
for (const nb of adj[id]) {
|
|
178
|
-
indegree[nb] -= 1;
|
|
179
|
-
if (indegree[nb] === 0)
|
|
180
|
-
next.push(nb);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
layers.push(layer);
|
|
184
|
-
q.splice(0, q.length, ...next);
|
|
185
|
-
}
|
|
186
|
-
// Size-aware placement: columns by layer, stacking nodes vertically in each column
|
|
187
|
-
// Use the same sizing heuristic as mapping via estimateNodeSize
|
|
188
|
-
const H_GAP = 160;
|
|
189
|
-
const V_GAP = 24;
|
|
190
|
-
const pos = {};
|
|
191
|
-
let curX = 0;
|
|
192
|
-
for (const layer of layers) {
|
|
193
|
-
// Compute max width in this layer and individual heights
|
|
194
|
-
let maxWidth = 0;
|
|
195
|
-
const heights = {};
|
|
196
|
-
for (const id of layer) {
|
|
197
|
-
const node = wb.def.nodes.find((n) => n.nodeId === id);
|
|
198
|
-
if (!node)
|
|
199
|
-
continue;
|
|
200
|
-
// Prefer showValues sizing similar to node rendering
|
|
201
|
-
// Consider per-type overrides when available via UI
|
|
202
|
-
const overrideSize = overrides?.getDefaultNodeSize?.(node.typeId) ?? undefined;
|
|
203
|
-
const size = estimateNodeSize({
|
|
204
|
-
node,
|
|
205
|
-
registry: wb.registry,
|
|
206
|
-
showValues: true,
|
|
207
|
-
overrides: overrideSize,
|
|
208
|
-
});
|
|
209
|
-
heights[id] = size.height;
|
|
210
|
-
if (size.width > maxWidth)
|
|
211
|
-
maxWidth = size.width;
|
|
212
|
-
}
|
|
213
|
-
// Place nodes in this column
|
|
214
|
-
let curY = 0;
|
|
215
|
-
for (const id of layer) {
|
|
216
|
-
const h = heights[id] ?? 0;
|
|
217
|
-
pos[id] = { x: curX, y: curY };
|
|
218
|
-
curY += h + V_GAP;
|
|
219
|
-
}
|
|
220
|
-
curX += maxWidth + H_GAP;
|
|
221
|
-
}
|
|
222
|
-
wb.setPositions(pos, { commit: true, reason: "auto-layout" });
|
|
223
|
-
}, [wb, wb.def, wb.registry, registryVersion, overrides?.getDefaultNodeSize]);
|
|
224
|
-
const updateEdgeType = useCallback((edgeId, typeId) => wb.updateEdgeType(edgeId, typeId), [wb]);
|
|
225
|
-
const triggerExternal = useCallback((nodeId, event) => runner.triggerExternal(nodeId, event), [runner]);
|
|
226
|
-
const getNodeDisplayName = useCallback((nodeId) => {
|
|
227
|
-
const customName = wb.getNodeName(nodeId);
|
|
228
|
-
if (customName)
|
|
229
|
-
return customName;
|
|
230
|
-
const node = wb.def.nodes.find((n) => n.nodeId === nodeId);
|
|
231
|
-
if (!node)
|
|
232
|
-
return nodeId;
|
|
233
|
-
const desc = wb.registry.nodes.get(node.typeId);
|
|
234
|
-
return desc?.displayName || node.typeId;
|
|
235
|
-
}, [wb, wb.registry, registryVersion]);
|
|
236
|
-
const setNodeName = useCallback((nodeId, name) => {
|
|
237
|
-
wb.setNodeName(nodeId, name, { commit: true, reason: "rename-node" });
|
|
238
|
-
}, [wb]);
|
|
239
|
-
// Helper to save runtime metadata and UI state to extData
|
|
240
|
-
const saveUiRuntimeMetadata = useCallback(async (workbench, graphRunner) => {
|
|
241
|
-
try {
|
|
242
|
-
const currentRuntime = workbench.getRuntimeState() ?? { nodes: {} };
|
|
243
|
-
const runtimeMetaData = { nodes: { ...currentRuntime.nodes } };
|
|
244
|
-
const customMetaData = workbench.getCustomData();
|
|
245
|
-
// Clean up metadata for nodes that no longer exist
|
|
246
|
-
const nodeIds = new Set(workbench.def.nodes.map((n) => n.nodeId));
|
|
247
|
-
for (const nodeId of Object.keys(runtimeMetaData.nodes)) {
|
|
248
|
-
if (!nodeIds.has(nodeId)) {
|
|
249
|
-
delete runtimeMetaData.nodes[nodeId];
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
// Save cleaned metadata to workbench state
|
|
253
|
-
workbench.setRuntimeState(runtimeMetaData);
|
|
254
|
-
const fullUiState = workbench.getUIState();
|
|
255
|
-
const uiWithoutViewport = excludeViewportFromUIState(fullUiState);
|
|
256
|
-
// Use updateExtData for efficient batched partial updates
|
|
257
|
-
const updates = [];
|
|
258
|
-
if (Object.keys(uiWithoutViewport || {}).length > 0) {
|
|
259
|
-
updates.push({ path: "ui", value: uiWithoutViewport });
|
|
260
|
-
}
|
|
261
|
-
updates.push({ path: "runtime", value: runtimeMetaData });
|
|
262
|
-
updates.push({ path: "custom", value: customMetaData });
|
|
263
|
-
await graphRunner.updateExtData(updates);
|
|
264
|
-
}
|
|
265
|
-
catch (err) {
|
|
266
|
-
console.warn("[WorkbenchContext] Failed to save runtime metadata:", err);
|
|
267
|
-
}
|
|
268
|
-
}, []);
|
|
269
|
-
// Subscribe to runner/workbench events
|
|
270
|
-
useEffect(() => {
|
|
271
|
-
const add = (source, type) => (payload) => setEvents((prev) => {
|
|
272
|
-
if (source === "workbench" && (type === "graphChanged" || type === "graphUiChanged")) {
|
|
273
|
-
const changeType = payload.change?.type;
|
|
274
|
-
if (changeType === "moveNode" || changeType === "moveNodes" || changeType === "resizeNodes")
|
|
275
|
-
return prev;
|
|
276
|
-
}
|
|
277
|
-
const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
|
|
278
|
-
const next = [
|
|
279
|
-
{
|
|
280
|
-
no: nextNo,
|
|
281
|
-
at: Date.now(),
|
|
282
|
-
source,
|
|
283
|
-
type,
|
|
284
|
-
payload: structuredClone(payload),
|
|
285
|
-
},
|
|
286
|
-
...prev,
|
|
287
|
-
];
|
|
288
|
-
return next.length > 200 ? next.slice(0, 200) : next;
|
|
289
|
-
});
|
|
290
|
-
// Helper to apply resolved handles from event payload to workbench
|
|
291
|
-
const applyResolvedHandles = (resolvedHandles) => {
|
|
292
|
-
for (const n of wb.def.nodes) {
|
|
293
|
-
const updated = resolvedHandles[n.nodeId];
|
|
294
|
-
if (updated) {
|
|
295
|
-
const before = JSON.stringify(n.resolvedHandles || {});
|
|
296
|
-
const after = JSON.stringify(updated);
|
|
297
|
-
if (before !== after) {
|
|
298
|
-
wb.updateResolvedHandles(n.nodeId, updated, { dry: true });
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
const offRunnerValue = runner.on("value", (e) => {
|
|
304
|
-
if (e?.dry)
|
|
305
|
-
return;
|
|
306
|
-
const now = Date.now();
|
|
307
|
-
if (e?.io === "input") {
|
|
308
|
-
const nodeId = e.nodeId;
|
|
309
|
-
const handle = e.handle;
|
|
310
|
-
// Track input timestamp in workbench runtime state
|
|
311
|
-
wb.updateNodeRuntimeMetadata(nodeId, (nodeMeta) => ({
|
|
312
|
-
...nodeMeta,
|
|
313
|
-
lastInputAt: {
|
|
314
|
-
...(nodeMeta.lastInputAt ?? {}),
|
|
315
|
-
[handle]: now,
|
|
316
|
-
},
|
|
317
|
-
}));
|
|
318
|
-
// Clear validation errors for this input when a valid value is set
|
|
319
|
-
setInputValidationErrors((prev) => prev.filter((err) => !(err.nodeId === nodeId && err.handle === handle)));
|
|
320
|
-
}
|
|
321
|
-
else if (e?.io === "output") {
|
|
322
|
-
const nodeId = e.nodeId;
|
|
323
|
-
const handle = e.handle;
|
|
324
|
-
// Track output timestamp in workbench runtime state
|
|
325
|
-
wb.updateNodeRuntimeMetadata(nodeId, (nodeMeta) => ({
|
|
326
|
-
...nodeMeta,
|
|
327
|
-
lastOutputAt: {
|
|
328
|
-
...(nodeMeta.lastOutputAt ?? {}),
|
|
329
|
-
[handle]: now,
|
|
330
|
-
},
|
|
331
|
-
}));
|
|
332
|
-
}
|
|
333
|
-
return add("runner", "value")(e);
|
|
334
|
-
});
|
|
335
|
-
const offRunnerError = runner.on("error", (e) => {
|
|
336
|
-
const edgeError = e;
|
|
337
|
-
const nodeError = e;
|
|
338
|
-
const registryError = e;
|
|
339
|
-
const systemError = e;
|
|
340
|
-
const inputValidationError = e;
|
|
341
|
-
if (edgeError.kind === "edge-convert") {
|
|
342
|
-
const edgeId = edgeError.edgeId;
|
|
343
|
-
setEdgeStatus((s) => ({
|
|
344
|
-
...s,
|
|
345
|
-
[edgeId]: { ...s[edgeId], lastError: edgeError.err },
|
|
346
|
-
}));
|
|
347
|
-
}
|
|
348
|
-
else if (nodeError.kind === "node-run" && nodeError.nodeId) {
|
|
349
|
-
const nodeId = nodeError.nodeId;
|
|
350
|
-
const runId = nodeError.runId;
|
|
351
|
-
const now = Date.now();
|
|
352
|
-
// Track error timestamp and summary in workbench runtime state
|
|
353
|
-
const err = nodeError.err;
|
|
354
|
-
let errorSummary;
|
|
355
|
-
if (err && typeof err === "object") {
|
|
356
|
-
const errAny = err;
|
|
357
|
-
const message = errAny.userMessage || errAny.message || String(err);
|
|
358
|
-
const code = errAny.statusCode || errAny.code;
|
|
359
|
-
errorSummary = {
|
|
360
|
-
message: typeof message === "string" ? message : String(message),
|
|
361
|
-
code: typeof code === "number" ? code : undefined,
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
wb.updateNodeRuntimeMetadata(nodeId, (nodeMeta) => ({
|
|
365
|
-
...nodeMeta,
|
|
366
|
-
lastErrorAt: now,
|
|
367
|
-
lastRunAt: now,
|
|
368
|
-
...(errorSummary ? { lastErrorSummary: errorSummary } : {}),
|
|
369
|
-
}));
|
|
370
|
-
setNodeStatus((s) => ({
|
|
371
|
-
...s,
|
|
372
|
-
[nodeId]: {
|
|
373
|
-
...s[nodeId],
|
|
374
|
-
lastError: errorSummary,
|
|
375
|
-
},
|
|
376
|
-
}));
|
|
377
|
-
// Mark this runId as errored
|
|
378
|
-
if (runId) {
|
|
379
|
-
errorRunsRef.current[nodeId] = {
|
|
380
|
-
...errorRunsRef.current[nodeId],
|
|
381
|
-
[runId]: true,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
else if (registryError.kind === "registry") {
|
|
386
|
-
// Track registry errors for UI display
|
|
387
|
-
setRegistryErrors((prev) => {
|
|
388
|
-
// Avoid duplicates by checking message
|
|
389
|
-
if (prev.some((err) => err.message === registryError.message)) {
|
|
390
|
-
return prev;
|
|
391
|
-
}
|
|
392
|
-
return [...prev, registryError];
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
else if (inputValidationError.kind === "input-validation") {
|
|
396
|
-
// Track input validation errors for UI display
|
|
397
|
-
setInputValidationErrors((prev) => {
|
|
398
|
-
// Avoid duplicates by checking nodeId, handle, and typeId
|
|
399
|
-
if (prev.some((err) => err.nodeId === inputValidationError.nodeId &&
|
|
400
|
-
err.handle === inputValidationError.handle &&
|
|
401
|
-
err.typeId === inputValidationError.typeId)) {
|
|
402
|
-
return prev;
|
|
403
|
-
}
|
|
404
|
-
return [...prev, inputValidationError];
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
else if (systemError.kind === "system") {
|
|
408
|
-
// Track custom errors for UI display
|
|
409
|
-
setSystemErrors((prev) => {
|
|
410
|
-
// Avoid duplicates by checking message and code
|
|
411
|
-
if (prev.some((err) => err.message === systemError.message && err.code === systemError.code)) {
|
|
412
|
-
return prev;
|
|
413
|
-
}
|
|
414
|
-
return [...prev, systemError];
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
return add("runner", "error")(e);
|
|
418
|
-
});
|
|
419
|
-
const offRunnerInvalidate = runner.on("invalidate", (e) => {
|
|
420
|
-
// If resolvedHandles are included in the event, use them directly (more efficient)
|
|
421
|
-
if (e?.resolvedHandles && Object.keys(e.resolvedHandles).length > 0) {
|
|
422
|
-
applyResolvedHandles(e.resolvedHandles);
|
|
423
|
-
}
|
|
424
|
-
return add("runner", "invalidate")(e);
|
|
425
|
-
});
|
|
426
|
-
const offRunnerStats = runner.on("stats", (s) => {
|
|
427
|
-
if (!s)
|
|
428
|
-
return;
|
|
429
|
-
if (s.kind === "node-start") {
|
|
430
|
-
const id = s.nodeId;
|
|
431
|
-
const runId = s.runId;
|
|
432
|
-
const now = Date.now();
|
|
433
|
-
// Track run timestamp in workbench runtime state
|
|
434
|
-
wb.updateNodeRuntimeMetadata(id, (nodeMeta) => ({
|
|
435
|
-
...nodeMeta,
|
|
436
|
-
lastRunAt: now,
|
|
437
|
-
}));
|
|
438
|
-
// Validate runId is a non-empty string
|
|
439
|
-
const isValidRunId = runId && typeof runId === "string" && runId.length > 0;
|
|
440
|
-
if (!isValidRunId) {
|
|
441
|
-
console.warn(`[WorkbenchContext] node-start event missing or invalid runId for node ${id}`, {
|
|
442
|
-
runId,
|
|
443
|
-
event: s,
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
setNodeStatus((prev) => {
|
|
447
|
-
const current = prev[id]?.activeRuns ?? 0;
|
|
448
|
-
const currentRunIds = prev[id]?.activeRunIds ?? [];
|
|
449
|
-
return {
|
|
450
|
-
...prev,
|
|
451
|
-
[id]: {
|
|
452
|
-
...prev[id],
|
|
453
|
-
activeRuns: current + 1,
|
|
454
|
-
activeRunIds: isValidRunId ? [...currentRunIds, runId] : currentRunIds,
|
|
455
|
-
progress: 0,
|
|
456
|
-
},
|
|
457
|
-
};
|
|
458
|
-
});
|
|
459
|
-
// Start fallback animation window
|
|
460
|
-
setFallbackStarts((prev) => ({ ...prev, [id]: now }));
|
|
461
|
-
}
|
|
462
|
-
else if (s.kind === "node-custom-data") {
|
|
463
|
-
const id = s.nodeId;
|
|
464
|
-
wb.setCustomNodeData(id, s.data, { commit: false, merge: true });
|
|
465
|
-
}
|
|
466
|
-
else if (s.kind === "node-progress") {
|
|
467
|
-
const id = s.nodeId;
|
|
468
|
-
setNodeStatus((prev) => ({
|
|
469
|
-
...prev,
|
|
470
|
-
[id]: {
|
|
471
|
-
...prev[id],
|
|
472
|
-
progress: Number(s.progress) || 0,
|
|
473
|
-
},
|
|
474
|
-
}));
|
|
475
|
-
}
|
|
476
|
-
else if (s.kind === "node-done") {
|
|
477
|
-
const id = s.nodeId;
|
|
478
|
-
const runId = s.runId;
|
|
479
|
-
const now = Date.now();
|
|
480
|
-
// Validate runId is a non-empty string
|
|
481
|
-
const isValidRunId = runId && typeof runId === "string" && runId.length > 0;
|
|
482
|
-
const hadError = !!(isValidRunId && errorRunsRef.current[id]?.[runId]);
|
|
483
|
-
// Track success timestamp if no error in workbench runtime state
|
|
484
|
-
if (!hadError) {
|
|
485
|
-
wb.updateNodeRuntimeMetadata(id, (nodeMeta) => {
|
|
486
|
-
const updated = { ...nodeMeta, lastSuccessAt: now };
|
|
487
|
-
// Clear error summary on success
|
|
488
|
-
if (updated.lastErrorSummary) {
|
|
489
|
-
delete updated.lastErrorSummary;
|
|
490
|
-
}
|
|
491
|
-
return updated;
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
setNodeStatus((prev) => {
|
|
495
|
-
const current = prev[id]?.activeRuns ?? 0;
|
|
496
|
-
const currentRunIds = prev[id]?.activeRunIds ?? [];
|
|
497
|
-
if (isValidRunId && !currentRunIds.includes(runId)) {
|
|
498
|
-
console.warn(`[WorkbenchContext] node-done event for unknown runId: node=${id} runId=${runId}`, {
|
|
499
|
-
event: s,
|
|
500
|
-
currentRunIds,
|
|
501
|
-
});
|
|
502
|
-
return prev; // Ignore stale event
|
|
503
|
-
}
|
|
504
|
-
const nextActive = Math.max(0, current - 1); // Prevent negative values
|
|
505
|
-
const nextRunIds = isValidRunId ? currentRunIds.filter((rid) => rid !== runId) : currentRunIds;
|
|
506
|
-
const keepProgress = hadError || nextActive > 0;
|
|
507
|
-
// Clear error flag for this runId
|
|
508
|
-
if (isValidRunId && errorRunsRef.current[id]?.[runId]) {
|
|
509
|
-
delete errorRunsRef.current[id]?.[runId];
|
|
510
|
-
}
|
|
511
|
-
return {
|
|
512
|
-
...prev,
|
|
513
|
-
[id]: {
|
|
514
|
-
...prev[id],
|
|
515
|
-
activeRuns: nextActive,
|
|
516
|
-
activeRunIds: nextRunIds,
|
|
517
|
-
progress: keepProgress ? prev[id]?.progress : 0,
|
|
518
|
-
lastError: hadError ? prev[id]?.lastError : undefined,
|
|
519
|
-
},
|
|
520
|
-
};
|
|
521
|
-
});
|
|
522
|
-
// Clear fallback start timestamp if no more active runs
|
|
523
|
-
setFallbackStarts((prev) => {
|
|
524
|
-
const nextPrev = { ...prev };
|
|
525
|
-
// If we don't know nextActive here, conservatively clear to stop animation
|
|
526
|
-
delete nextPrev[id];
|
|
527
|
-
return nextPrev;
|
|
528
|
-
});
|
|
529
|
-
}
|
|
530
|
-
else if (s.kind === "edge-start") {
|
|
531
|
-
const id = s.edgeId;
|
|
532
|
-
setEdgeStatus((prev) => {
|
|
533
|
-
const current = prev[id]?.activeRuns ?? 0;
|
|
534
|
-
return {
|
|
535
|
-
...prev,
|
|
536
|
-
[id]: { ...prev[id], activeRuns: current + 1 },
|
|
537
|
-
};
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
else if (s.kind === "edge-done") {
|
|
541
|
-
const id = s.edgeId;
|
|
542
|
-
setEdgeStatus((prev) => {
|
|
543
|
-
const current = prev[id]?.activeRuns ?? 0;
|
|
544
|
-
return {
|
|
545
|
-
...prev,
|
|
546
|
-
[id]: { ...prev[id], activeRuns: Math.max(0, current - 1) }, // Prevent negative values
|
|
547
|
-
};
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
return add("runner", "stats")(s);
|
|
551
|
-
});
|
|
552
|
-
const offWbRegistryChanged = wb.on("registryChanged", (evt) => {
|
|
553
|
-
setRegistryVersion((v) => v + 1);
|
|
554
|
-
});
|
|
555
|
-
const offWbGraphChanged = wb.on("graphChanged", (event) => {
|
|
556
|
-
if (event.change?.type === "removeNode") {
|
|
557
|
-
const removedNodeId = event.change.nodeId;
|
|
558
|
-
setInputValidationErrors((prev) => prev.filter((err) => err.nodeId !== removedNodeId));
|
|
559
|
-
}
|
|
560
|
-
else if (event.change?.type === "batch") {
|
|
561
|
-
const removedIds = event.change.changes
|
|
562
|
-
.filter((c) => c.type === "removeNode")
|
|
563
|
-
.map((c) => c.nodeId);
|
|
564
|
-
if (removedIds.length > 0) {
|
|
565
|
-
setInputValidationErrors((prev) => prev.filter((err) => !removedIds.includes(err.nodeId)));
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
return add("workbench", "graphChanged")(event);
|
|
569
|
-
});
|
|
570
|
-
const offWbGraphUiChangedForLog = wb.on("graphUiChanged", add("workbench", "graphUiChanged"));
|
|
571
|
-
const offWbValidationChanged = wb.on("validationChanged", add("workbench", "validationChanged"));
|
|
572
|
-
// Ensure newly added nodes start as invalidated until first evaluation
|
|
573
|
-
const GRAPH_REASON_BY_CHANGE = {
|
|
574
|
-
addNode: "add-node",
|
|
575
|
-
removeNode: "remove-node",
|
|
576
|
-
connect: "connect-edge",
|
|
577
|
-
disconnect: "disconnect-edge",
|
|
578
|
-
updateParams: "update-node-params",
|
|
579
|
-
updateEdgeType: "update-edge-type",
|
|
580
|
-
setInputs: "set-inputs",
|
|
581
|
-
updateResolvedHandles: "update-resolved-handles",
|
|
582
|
-
};
|
|
583
|
-
const getGraphReason = (event) => {
|
|
584
|
-
const changeType = event.change?.type;
|
|
585
|
-
return (changeType && GRAPH_REASON_BY_CHANGE[changeType]) || "graph-changed";
|
|
586
|
-
};
|
|
587
|
-
const commitGraphChange = async (shouldCommit, reason, errorPrefix) => {
|
|
588
|
-
if (!shouldCommit)
|
|
589
|
-
return;
|
|
590
|
-
await saveUiRuntimeMetadata(wb, runner);
|
|
591
|
-
const history = await runner.commit(reason).catch((err) => {
|
|
592
|
-
console.error(errorPrefix, err);
|
|
593
|
-
return undefined;
|
|
594
|
-
});
|
|
595
|
-
if (history)
|
|
596
|
-
wb.setHistory(history);
|
|
597
|
-
};
|
|
598
|
-
const applyAddNodeEffects = async (nodeId, inputs, copyOutputsFrom, dry) => {
|
|
599
|
-
if (inputs) {
|
|
600
|
-
await runner.setInputs(nodeId, inputs, { dry });
|
|
601
|
-
}
|
|
602
|
-
if (copyOutputsFrom) {
|
|
603
|
-
await runner.copyOutputs(copyOutputsFrom, nodeId, { dry });
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
const applyBatchGraphChange = async (event) => {
|
|
607
|
-
if (event.change?.type !== "batch")
|
|
608
|
-
return;
|
|
609
|
-
const { changes } = event.change;
|
|
610
|
-
const dry = !!event.dry;
|
|
611
|
-
if (!runner.isRunning())
|
|
612
|
-
return;
|
|
613
|
-
const hasNodeOps = changes.some((c) => c.type === "addNode" && (c.inputs || c.copyOutputsFrom));
|
|
614
|
-
await runner.update(event.def, { dry: dry || hasNodeOps });
|
|
615
|
-
for (const change of changes) {
|
|
616
|
-
if (change.type === "addNode") {
|
|
617
|
-
await applyAddNodeEffects(change.nodeId, change.inputs, change.copyOutputsFrom, dry);
|
|
618
|
-
}
|
|
619
|
-
else if (change.type === "setInputs") {
|
|
620
|
-
await runner.setInputs(change.nodeId, change.inputs, { dry });
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
const applySingleGraphChange = async (event) => {
|
|
625
|
-
if (event.change?.type === "batch")
|
|
626
|
-
return;
|
|
627
|
-
if (event.change?.type === "setInputs") {
|
|
628
|
-
const { nodeId, inputs } = event.change;
|
|
629
|
-
await runner.setInputs(nodeId, inputs, { dry: event.dry });
|
|
630
|
-
}
|
|
631
|
-
if (!runner.isRunning())
|
|
632
|
-
return;
|
|
633
|
-
if (event.change?.type === "addNode") {
|
|
634
|
-
const { nodeId, inputs, copyOutputsFrom } = event.change;
|
|
635
|
-
if (event.dry) {
|
|
636
|
-
await runner.update(event.def, { dry: true });
|
|
637
|
-
await applyAddNodeEffects(nodeId, inputs, copyOutputsFrom, true);
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
await runner.update(event.def, { dry: !!inputs });
|
|
641
|
-
await applyAddNodeEffects(nodeId, inputs, undefined, false);
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
if (event.change?.type !== "setInputs" && event.change?.type !== "updateResolvedHandles") {
|
|
645
|
-
await runner.update(event.def, { dry: event.dry });
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
const offWbGraphChangedForUpdate = wb.on("graphChanged", async (event) => {
|
|
649
|
-
try {
|
|
650
|
-
if (event.change?.type === "batch") {
|
|
651
|
-
await applyBatchGraphChange(event);
|
|
652
|
-
await commitGraphChange(event.commit, event.reason ?? "batch", "[WorkbenchContext] Error committing batch:");
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
const reason = getGraphReason(event);
|
|
656
|
-
await applySingleGraphChange(event);
|
|
657
|
-
await commitGraphChange(event.commit, event.reason ?? reason, "[WorkbenchContext] Error committing:");
|
|
658
|
-
}
|
|
659
|
-
catch (err) {
|
|
660
|
-
console.error("[WorkbenchContext] Error processing graph change:", err);
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
const offWbSetValidation = wb.on("validationChanged", (r) => setValidation(r));
|
|
664
|
-
const offWbSelectionChanged = wb.on("selectionChanged", async (sel) => {
|
|
665
|
-
setSelectedNodeId(sel.nodes?.[0]);
|
|
666
|
-
setSelectedEdgeId(sel.edges?.[0]);
|
|
667
|
-
if (sel.commit) {
|
|
668
|
-
await saveUiRuntimeMetadata(wb, runner);
|
|
669
|
-
const history = await runner.commit(sel.reason ?? "selection").catch((err) => {
|
|
670
|
-
console.error("[WorkbenchContext] Error committing selection change:", err);
|
|
671
|
-
return undefined;
|
|
672
|
-
});
|
|
673
|
-
if (history) {
|
|
674
|
-
wb.setHistory(history);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
});
|
|
678
|
-
const offWbGraphUiChanged = wb.on("graphUiChanged", async (event) => {
|
|
679
|
-
// Handle viewport changes separately (send via SetViewport command, not commit)
|
|
680
|
-
if (event.change?.type === "viewport") {
|
|
681
|
-
const viewport = wb.getViewport();
|
|
682
|
-
if (viewport && runner.setViewport) {
|
|
683
|
-
runner.setViewport(viewport).catch((err) => {
|
|
684
|
-
console.warn("[WorkbenchContext] Failed to send viewport update:", err);
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
// Only commit if commit flag is true (e.g., drag end, not during dragging)
|
|
690
|
-
if (event.commit) {
|
|
691
|
-
// Build detailed reason from change type
|
|
692
|
-
let reason = "ui-changed";
|
|
693
|
-
if (event.change) {
|
|
694
|
-
const changeType = event.change.type;
|
|
695
|
-
if (changeType === "moveNode") {
|
|
696
|
-
reason = "move-node";
|
|
697
|
-
}
|
|
698
|
-
else if (changeType === "moveNodes") {
|
|
699
|
-
reason = "move-nodes";
|
|
700
|
-
}
|
|
701
|
-
else if (changeType === "resizeNodes") {
|
|
702
|
-
reason = "resize-nodes";
|
|
703
|
-
}
|
|
704
|
-
else if (changeType === "selection") {
|
|
705
|
-
reason = "selection";
|
|
706
|
-
}
|
|
707
|
-
else if (changeType === "customNodeData") {
|
|
708
|
-
reason = "custom-node-data";
|
|
709
|
-
}
|
|
710
|
-
else if (changeType === "customEdgeData") {
|
|
711
|
-
reason = "custom-edge-data";
|
|
712
|
-
}
|
|
713
|
-
else if (changeType === "customMetaData") {
|
|
714
|
-
reason = "custom-meta";
|
|
715
|
-
}
|
|
716
|
-
else if (changeType === "customData") {
|
|
717
|
-
reason = "custom";
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
await saveUiRuntimeMetadata(wb, runner);
|
|
721
|
-
const history = await runner.commit(event.reason ?? reason).catch((err) => {
|
|
722
|
-
console.error("[WorkbenchContext] Error committing UI changes:", err);
|
|
723
|
-
return undefined;
|
|
724
|
-
});
|
|
725
|
-
if (history) {
|
|
726
|
-
wb.setHistory(history);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
});
|
|
730
|
-
const offWbError = wb.on("error", add("workbench", "error"));
|
|
731
|
-
// Registry updates: swap registry and refresh graph validation/UI
|
|
732
|
-
const offRunnerRegistry = runner.on("registry", async (registry) => {
|
|
733
|
-
wb.setRegistry(registry);
|
|
734
|
-
setRegistryReady(true);
|
|
735
|
-
});
|
|
736
|
-
const offFlowViewport = runner.on("viewport", (event) => {
|
|
737
|
-
const viewport = event.viewport;
|
|
738
|
-
if (isValidViewport(viewport)) {
|
|
739
|
-
wb.setViewport(viewport);
|
|
740
|
-
}
|
|
741
|
-
});
|
|
742
|
-
const offRunnerTransport = runner.on("transport", (t) => {
|
|
743
|
-
// Remote registry is only considered ready once we've received at least one "registry" event.
|
|
744
|
-
// Reset readiness whenever transport is not stable.
|
|
745
|
-
if (t.state === "local") {
|
|
746
|
-
setRegistryReady(true);
|
|
747
|
-
}
|
|
748
|
-
else if (t.state === "connecting" || t.state === "retrying") {
|
|
749
|
-
setRegistryReady(false);
|
|
750
|
-
}
|
|
751
|
-
else if (t.state === "disconnected") {
|
|
752
|
-
setRegistryReady(false);
|
|
753
|
-
}
|
|
754
|
-
if (t.state === "disconnected") {
|
|
755
|
-
console.info("[WorkbenchContext] Transport disconnected, resetting node status");
|
|
756
|
-
// Reinitialize node status with invalidated=true for all nodes
|
|
757
|
-
setNodeStatus(() => {
|
|
758
|
-
const next = {};
|
|
759
|
-
const metadata = wb.getRuntimeState() ?? { nodes: {} };
|
|
760
|
-
for (const n of wb.def.nodes) {
|
|
761
|
-
const nodeMeta = metadata.nodes[n.nodeId];
|
|
762
|
-
next[n.nodeId] = {
|
|
763
|
-
activeRuns: 0,
|
|
764
|
-
activeRunIds: [],
|
|
765
|
-
invalidated: computeInvalidatedFromMetadata(nodeMeta),
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
return next;
|
|
769
|
-
});
|
|
770
|
-
setEdgeStatus({});
|
|
771
|
-
setFallbackStarts({});
|
|
772
|
-
errorRunsRef.current = {};
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
const offWbRuntimeMetadataChanged = wb.on("runtimeMetadataChanged", (event) => {
|
|
776
|
-
const workbenchRuntimeState = wb.getRuntimeState() ?? { nodes: {} };
|
|
777
|
-
setNodeStatus((prev) => {
|
|
778
|
-
const next = { ...prev };
|
|
779
|
-
const metadata = workbenchRuntimeState;
|
|
780
|
-
let changed = false;
|
|
781
|
-
// If nodeId is specified, only update that node; otherwise update all nodes
|
|
782
|
-
const nodesToUpdate = event.nodeId ? wb.def.nodes.filter((n) => n.nodeId === event.nodeId) : wb.def.nodes;
|
|
783
|
-
for (const n of nodesToUpdate) {
|
|
784
|
-
const cur = next[n.nodeId];
|
|
785
|
-
if (!cur)
|
|
786
|
-
continue;
|
|
787
|
-
const nodeMeta = metadata.nodes[n.nodeId];
|
|
788
|
-
const newInvalidated = computeInvalidatedFromMetadata(nodeMeta);
|
|
789
|
-
if (cur.invalidated !== newInvalidated) {
|
|
790
|
-
next[n.nodeId] = { ...cur, invalidated: newInvalidated };
|
|
791
|
-
changed = true;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
return changed ? next : prev;
|
|
795
|
-
});
|
|
796
|
-
});
|
|
797
|
-
wb.refreshValidation();
|
|
798
|
-
return () => {
|
|
799
|
-
offRunnerValue();
|
|
800
|
-
offRunnerError();
|
|
801
|
-
offRunnerInvalidate();
|
|
802
|
-
offRunnerStats();
|
|
803
|
-
offWbRegistryChanged();
|
|
804
|
-
offWbGraphChanged();
|
|
805
|
-
offWbGraphUiChangedForLog();
|
|
806
|
-
offWbGraphUiChanged();
|
|
807
|
-
offWbValidationChanged();
|
|
808
|
-
offWbError();
|
|
809
|
-
offWbGraphChangedForUpdate();
|
|
810
|
-
offWbSetValidation();
|
|
811
|
-
offWbSelectionChanged();
|
|
812
|
-
offRunnerRegistry();
|
|
813
|
-
offRunnerTransport();
|
|
814
|
-
offFlowViewport();
|
|
815
|
-
offWbRuntimeMetadataChanged();
|
|
816
|
-
};
|
|
817
|
-
}, [runner, wb]);
|
|
818
|
-
const isRunning = useCallback(() => runner.isRunning(), [runner]);
|
|
819
|
-
const setRunMode = useCallback((mode) => {
|
|
820
|
-
if (mode === runMode)
|
|
821
|
-
return;
|
|
822
|
-
runner.setRunMode(mode);
|
|
823
|
-
setRunModeState(mode);
|
|
824
|
-
}, [runMode, runner]);
|
|
825
|
-
const runNodeAction = useCallback(async (nodeId) => {
|
|
826
|
-
// Check if we're in auto mode or if node has autoRun policy
|
|
827
|
-
const nodeHasAutoRun = wb.shouldNodeAutoRun(nodeId);
|
|
828
|
-
const shouldUseTriggerExternal = runMode === "auto" || nodeHasAutoRun;
|
|
829
|
-
if (shouldUseTriggerExternal) {
|
|
830
|
-
// In auto mode or when node has autoRun, use triggerExternal with invalidate event
|
|
831
|
-
await runner.triggerExternal(nodeId, { action: "invalidate" });
|
|
832
|
-
}
|
|
833
|
-
else {
|
|
834
|
-
// In manual mode without autoRun, use computeNode
|
|
835
|
-
await runner.computeNode(nodeId);
|
|
836
|
-
}
|
|
837
|
-
}, [runner, wb, runMode]);
|
|
838
|
-
const runFromHereAction = useCallback(async (nodeId) => {
|
|
839
|
-
await runner.runFromHere(nodeId);
|
|
840
|
-
}, [runner]);
|
|
841
|
-
const abortNodeAction = useCallback((nodeId) => {
|
|
842
|
-
runner.cancelNodeRuns([nodeId]);
|
|
843
|
-
}, [runner]);
|
|
844
|
-
const validationByNode = useMemo(() => {
|
|
845
|
-
const inputs = {};
|
|
846
|
-
const outputs = {};
|
|
847
|
-
const issues = {};
|
|
848
|
-
if (!validation)
|
|
849
|
-
return { inputs, outputs, issues };
|
|
850
|
-
for (const is of validation.issues ?? []) {
|
|
851
|
-
const d = is.data;
|
|
852
|
-
const level = is.level;
|
|
853
|
-
const code = String(is.code ?? "");
|
|
854
|
-
const message = String(is.message ?? code);
|
|
855
|
-
if (!d)
|
|
856
|
-
continue;
|
|
857
|
-
if (d.nodeId) {
|
|
858
|
-
if (d.input) {
|
|
859
|
-
const arr = inputs[d.nodeId] ?? (inputs[d.nodeId] = []);
|
|
860
|
-
arr.push({ handle: String(d.input), level, message, code });
|
|
861
|
-
const nodeArr = issues[d.nodeId] ?? (issues[d.nodeId] = []);
|
|
862
|
-
nodeArr.push(is);
|
|
863
|
-
}
|
|
864
|
-
if (d.output) {
|
|
865
|
-
const arr = outputs[d.nodeId] ?? (outputs[d.nodeId] = []);
|
|
866
|
-
arr.push({ handle: String(d.output), level, message, code });
|
|
867
|
-
const nodeArr = issues[d.nodeId] ?? (issues[d.nodeId] = []);
|
|
868
|
-
nodeArr.push(is);
|
|
869
|
-
}
|
|
870
|
-
if (!d.input && !d.output) {
|
|
871
|
-
const arr = issues[d.nodeId] ?? (issues[d.nodeId] = []);
|
|
872
|
-
arr.push(is);
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
return { inputs, outputs, issues };
|
|
877
|
-
}, [validation]);
|
|
878
|
-
const validationGlobal = useMemo(() => {
|
|
879
|
-
const list = [];
|
|
880
|
-
if (!validation)
|
|
881
|
-
return list;
|
|
882
|
-
for (const is of validation.issues ?? []) {
|
|
883
|
-
const d = is.data;
|
|
884
|
-
if (!d || (!d.nodeId && !d.edgeId)) {
|
|
885
|
-
list.push(is);
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
return list;
|
|
889
|
-
}, [validation]);
|
|
890
|
-
const validationByEdge = useMemo(() => {
|
|
891
|
-
const errors = {};
|
|
892
|
-
const issues = {};
|
|
893
|
-
if (!validation)
|
|
894
|
-
return { errors, issues };
|
|
895
|
-
for (const is of validation.issues ?? []) {
|
|
896
|
-
const d = is.data;
|
|
897
|
-
const level = is.level;
|
|
898
|
-
if (d?.edgeId) {
|
|
899
|
-
if (level === "error")
|
|
900
|
-
errors[d.edgeId] = true;
|
|
901
|
-
const arr = issues[d.edgeId] ?? (issues[d.edgeId] = []);
|
|
902
|
-
arr.push(is);
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return { errors, issues };
|
|
906
|
-
}, [validation]);
|
|
907
|
-
const value = useMemo(() => ({
|
|
908
|
-
wb,
|
|
909
|
-
runner,
|
|
910
|
-
selectedNodeId,
|
|
911
|
-
selectedEdgeId,
|
|
912
|
-
setSelection,
|
|
913
|
-
nodeStatus,
|
|
914
|
-
edgeStatus,
|
|
915
|
-
valuesTick,
|
|
916
|
-
handlesMap,
|
|
917
|
-
inputsMap,
|
|
918
|
-
inputDefaultsMap,
|
|
919
|
-
outputsMap,
|
|
920
|
-
outputTypesMap,
|
|
921
|
-
validationByNode,
|
|
922
|
-
validationByEdge,
|
|
923
|
-
validationGlobal,
|
|
924
|
-
events,
|
|
925
|
-
clearEvents,
|
|
926
|
-
systemErrors,
|
|
927
|
-
registryErrors,
|
|
928
|
-
inputValidationErrors,
|
|
929
|
-
clearSystemErrors,
|
|
930
|
-
clearRegistryErrors,
|
|
931
|
-
clearInputValidationErrors,
|
|
932
|
-
removeSystemError,
|
|
933
|
-
removeRegistryError,
|
|
934
|
-
removeInputValidationError,
|
|
935
|
-
isRunning,
|
|
936
|
-
runMode,
|
|
937
|
-
setRunMode,
|
|
938
|
-
runNode: runNodeAction,
|
|
939
|
-
runFromHere: runFromHereAction,
|
|
940
|
-
abortNode: abortNodeAction,
|
|
941
|
-
runAutoLayout,
|
|
942
|
-
updateEdgeType,
|
|
943
|
-
triggerExternal,
|
|
944
|
-
uiVersion,
|
|
945
|
-
registryVersion,
|
|
946
|
-
registryReady,
|
|
947
|
-
overrides,
|
|
948
|
-
getNodeDisplayName,
|
|
949
|
-
setNodeName,
|
|
950
|
-
}), [
|
|
951
|
-
wb,
|
|
952
|
-
runner,
|
|
953
|
-
selectedNodeId,
|
|
954
|
-
selectedEdgeId,
|
|
955
|
-
setSelection,
|
|
956
|
-
nodeStatus,
|
|
957
|
-
edgeStatus,
|
|
958
|
-
valuesTick,
|
|
959
|
-
systemErrors,
|
|
960
|
-
registryErrors,
|
|
961
|
-
inputValidationErrors,
|
|
962
|
-
clearSystemErrors,
|
|
963
|
-
clearRegistryErrors,
|
|
964
|
-
clearInputValidationErrors,
|
|
965
|
-
removeSystemError,
|
|
966
|
-
removeRegistryError,
|
|
967
|
-
removeInputValidationError,
|
|
968
|
-
handlesMap,
|
|
969
|
-
inputsMap,
|
|
970
|
-
inputDefaultsMap,
|
|
971
|
-
outputsMap,
|
|
972
|
-
validationByNode,
|
|
973
|
-
validationByEdge,
|
|
974
|
-
validationGlobal,
|
|
975
|
-
events,
|
|
976
|
-
clearEvents,
|
|
977
|
-
isRunning,
|
|
978
|
-
runMode,
|
|
979
|
-
setRunMode,
|
|
980
|
-
runNodeAction,
|
|
981
|
-
runFromHereAction,
|
|
982
|
-
abortNodeAction,
|
|
983
|
-
runAutoLayout,
|
|
984
|
-
wb,
|
|
985
|
-
runner,
|
|
986
|
-
uiVersion,
|
|
987
|
-
registryVersion,
|
|
988
|
-
registryReady,
|
|
989
|
-
overrides,
|
|
990
|
-
getNodeDisplayName,
|
|
991
|
-
setNodeName,
|
|
992
|
-
]);
|
|
993
|
-
return _jsx(WorkbenchContext.Provider, { value: value, children: children });
|
|
994
|
-
}
|
|
995
|
-
//# sourceMappingURL=WorkbenchContext.provider.js.map
|