@bian-womp/spark-workbench 0.2.22 → 0.2.24
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 +158 -54
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/adapters/cli/index.d.ts +1 -1
- package/lib/cjs/src/adapters/cli/index.d.ts.map +1 -1
- package/lib/cjs/src/core/AbstractWorkbench.d.ts +3 -3
- package/lib/cjs/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +4 -3
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +4 -3
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/misc/NodeHandles.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +2 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/layout.d.ts +5 -5
- package/lib/cjs/src/misc/layout.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts +3 -1
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +2 -2
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +2 -2
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +2 -2
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +2 -2
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +158 -54
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/adapters/cli/index.d.ts +1 -1
- package/lib/esm/src/adapters/cli/index.d.ts.map +1 -1
- package/lib/esm/src/core/AbstractWorkbench.d.ts +3 -3
- package/lib/esm/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +4 -3
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +4 -3
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/misc/NodeHandles.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts +2 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/layout.d.ts +5 -5
- package/lib/esm/src/misc/layout.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts +3 -1
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +2 -2
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +2 -2
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +2 -2
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +2 -2
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -534,13 +534,19 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
534
534
|
const def = undefined; // UI will supply def/positions on download for local
|
|
535
535
|
const inputs = this.getInputs(this.runtime
|
|
536
536
|
? {
|
|
537
|
-
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
|
|
537
|
+
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
|
|
538
|
+
nodeId: id,
|
|
539
|
+
typeId: "",
|
|
540
|
+
})),
|
|
538
541
|
edges: [],
|
|
539
542
|
}
|
|
540
543
|
: { nodes: [], edges: [] });
|
|
541
544
|
const outputs = this.getOutputs(this.runtime
|
|
542
545
|
? {
|
|
543
|
-
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
|
|
546
|
+
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({
|
|
547
|
+
nodeId: id,
|
|
548
|
+
typeId: "",
|
|
549
|
+
})),
|
|
544
550
|
edges: [],
|
|
545
551
|
}
|
|
546
552
|
: { nodes: [], edges: [] });
|
|
@@ -1261,18 +1267,44 @@ function layoutNode(args) {
|
|
|
1261
1267
|
}
|
|
1262
1268
|
|
|
1263
1269
|
function toReactFlow(def, positions, registry, opts) {
|
|
1270
|
+
const EDGE_STYLE_MISSING = { stroke: "#f59e0b", strokeWidth: 2 }; // amber-500
|
|
1264
1271
|
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
1265
1272
|
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
1266
|
-
|
|
1267
|
-
|
|
1273
|
+
// Build a map of valid handles per node up-front
|
|
1274
|
+
const validHandleMap = {};
|
|
1275
|
+
for (const n of def.nodes) {
|
|
1276
|
+
const { inputs, outputs } = computeEffectiveHandles(n, registry);
|
|
1277
|
+
const inputOrder = Object.keys(inputs).filter((k) => !sparkGraph.isInputPrivate(inputs, k));
|
|
1278
|
+
const outputOrder = Object.keys(outputs);
|
|
1279
|
+
validHandleMap[n.nodeId] = {
|
|
1280
|
+
inputs: new Set(inputOrder),
|
|
1281
|
+
outputs: new Set(outputOrder),
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
// Track which inputs are connected (for UI) and which handles are missing (for layout)
|
|
1268
1285
|
const connectedInputs = {};
|
|
1286
|
+
const missingInputsByNode = {};
|
|
1287
|
+
const missingOutputsByNode = {};
|
|
1269
1288
|
for (const e of def.edges) {
|
|
1270
|
-
const
|
|
1271
|
-
const
|
|
1272
|
-
if (!connectedInputs[
|
|
1273
|
-
connectedInputs[
|
|
1274
|
-
connectedInputs[
|
|
1275
|
-
|
|
1289
|
+
const tgtId = e.target.nodeId;
|
|
1290
|
+
const tgtHandle = e.target.handle;
|
|
1291
|
+
if (!connectedInputs[tgtId])
|
|
1292
|
+
connectedInputs[tgtId] = new Set();
|
|
1293
|
+
connectedInputs[tgtId].add(tgtHandle);
|
|
1294
|
+
const tgtValid = !!validHandleMap[tgtId]?.inputs.has(tgtHandle);
|
|
1295
|
+
if (!tgtValid) {
|
|
1296
|
+
(missingInputsByNode[tgtId] || (missingInputsByNode[tgtId] = new Set())).add(tgtHandle);
|
|
1297
|
+
}
|
|
1298
|
+
const srcId = e.source.nodeId;
|
|
1299
|
+
const srcHandle = e.source.handle;
|
|
1300
|
+
const srcValid = !!validHandleMap[srcId]?.outputs.has(srcHandle);
|
|
1301
|
+
if (!srcValid) {
|
|
1302
|
+
(missingOutputsByNode[srcId] || (missingOutputsByNode[srcId] = new Set())).add(srcHandle);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
// This map is still used later for certain checks; align with valid handles
|
|
1306
|
+
const nodeHandleMap = {};
|
|
1307
|
+
Object.assign(nodeHandleMap, validHandleMap);
|
|
1276
1308
|
const nodes = def.nodes.map((n) => {
|
|
1277
1309
|
const { inputs: inputSource, outputs: outputSource } = computeEffectiveHandles(n, registry);
|
|
1278
1310
|
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
@@ -1294,11 +1326,61 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1294
1326
|
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
1295
1327
|
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
1296
1328
|
};
|
|
1297
|
-
//
|
|
1329
|
+
// Append placeholder entries for any missing handles (below valid ones)
|
|
1330
|
+
const baseLeftCount = geom.inputOrder.length;
|
|
1331
|
+
const baseRightCount = geom.outputOrder.length;
|
|
1332
|
+
const extraInputs = Array.from(missingInputsByNode[n.nodeId] || []);
|
|
1333
|
+
const extraOutputs = Array.from(missingOutputsByNode[n.nodeId] || []);
|
|
1334
|
+
const HEADER = NODE_HEADER_HEIGHT_PX;
|
|
1335
|
+
const ROW = NODE_ROW_HEIGHT_PX;
|
|
1336
|
+
const extraHandleLayoutLeft = extraInputs.map((id, i) => ({
|
|
1337
|
+
id,
|
|
1338
|
+
type: "target",
|
|
1339
|
+
position: react.Position.Left,
|
|
1340
|
+
y: HEADER + (baseLeftCount + i) * ROW + ROW / 2,
|
|
1341
|
+
missing: true,
|
|
1342
|
+
}));
|
|
1343
|
+
const extraHandleLayoutRight = extraOutputs.map((id, i) => ({
|
|
1344
|
+
id,
|
|
1345
|
+
type: "source",
|
|
1346
|
+
position: react.Position.Right,
|
|
1347
|
+
y: HEADER + (baseRightCount + i) * ROW + ROW / 2,
|
|
1348
|
+
missing: true,
|
|
1349
|
+
}));
|
|
1350
|
+
const handleLayout = [
|
|
1351
|
+
...geom.handleLayout,
|
|
1352
|
+
...extraHandleLayoutLeft,
|
|
1353
|
+
...extraHandleLayoutRight,
|
|
1354
|
+
];
|
|
1355
|
+
// Precompute handle bounds (including missing) so edges can render immediately
|
|
1356
|
+
const missingBoundsLeft = extraInputs.map((id, i) => ({
|
|
1357
|
+
id,
|
|
1358
|
+
type: "target",
|
|
1359
|
+
position: react.Position.Left,
|
|
1360
|
+
x: 0,
|
|
1361
|
+
y: HEADER + (baseLeftCount + i) * ROW,
|
|
1362
|
+
width: 1,
|
|
1363
|
+
height: ROW + 2,
|
|
1364
|
+
}));
|
|
1365
|
+
const missingBoundsRight = extraOutputs.map((id, i) => ({
|
|
1366
|
+
id,
|
|
1367
|
+
type: "source",
|
|
1368
|
+
position: react.Position.Right,
|
|
1369
|
+
x: geom.width - 1,
|
|
1370
|
+
y: HEADER + (baseRightCount + i) * ROW,
|
|
1371
|
+
width: 1,
|
|
1372
|
+
height: ROW + 2,
|
|
1373
|
+
}));
|
|
1374
|
+
const handles = [
|
|
1375
|
+
...geom.handles,
|
|
1376
|
+
...missingBoundsLeft,
|
|
1377
|
+
...missingBoundsRight,
|
|
1378
|
+
];
|
|
1379
|
+
// Adjust node height to accommodate missing handle rows
|
|
1380
|
+
const baseRows = Math.max(baseLeftCount, baseRightCount);
|
|
1381
|
+
const newRows = Math.max(baseLeftCount + extraInputs.length, baseRightCount + extraOutputs.length);
|
|
1298
1382
|
const initialWidth = geom.width;
|
|
1299
|
-
const initialHeight = geom.height;
|
|
1300
|
-
// Precompute handle bounds so edges can render immediately without waiting for measurement
|
|
1301
|
-
const handles = geom.handles;
|
|
1383
|
+
const initialHeight = geom.height + Math.max(0, newRows - baseRows) * ROW;
|
|
1302
1384
|
return {
|
|
1303
1385
|
id: n.nodeId,
|
|
1304
1386
|
data: {
|
|
@@ -1310,7 +1392,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1310
1392
|
h.id,
|
|
1311
1393
|
!!connectedInputs[n.nodeId]?.has(h.id),
|
|
1312
1394
|
])),
|
|
1313
|
-
handleLayout
|
|
1395
|
+
handleLayout,
|
|
1314
1396
|
showValues: opts.showValues,
|
|
1315
1397
|
renderWidth: initialWidth,
|
|
1316
1398
|
renderHeight: initialHeight,
|
|
@@ -1337,24 +1419,21 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1337
1419
|
height: initialHeight,
|
|
1338
1420
|
};
|
|
1339
1421
|
});
|
|
1340
|
-
const edges = def.edges
|
|
1341
|
-
.filter((e) => {
|
|
1342
|
-
const src = nodeHandleMap[e.source.nodeId];
|
|
1343
|
-
const dst = nodeHandleMap[e.target.nodeId];
|
|
1344
|
-
if (!src || !dst)
|
|
1345
|
-
return false;
|
|
1346
|
-
return (src.outputs.has(e.source.handle) && dst.inputs.has(e.target.handle));
|
|
1347
|
-
})
|
|
1348
|
-
.map((e) => {
|
|
1422
|
+
const edges = def.edges.map((e) => {
|
|
1349
1423
|
const st = opts.edgeStatus?.[e.id];
|
|
1350
1424
|
const isRunning = !!st?.activeRuns;
|
|
1351
1425
|
const hasError = !!st?.lastError;
|
|
1352
1426
|
const isInvalidEdge = !!opts.edgeValidation?.[e.id];
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1427
|
+
const sourceMissing = !validHandleMap[e.source.nodeId]?.outputs.has(e.source.handle);
|
|
1428
|
+
const targetMissing = !validHandleMap[e.target.nodeId]?.inputs.has(e.target.handle);
|
|
1429
|
+
const isMissing = sourceMissing || targetMissing;
|
|
1430
|
+
const style = isMissing
|
|
1431
|
+
? EDGE_STYLE_MISSING
|
|
1432
|
+
: hasError || isInvalidEdge
|
|
1433
|
+
? EDGE_STYLE_ERROR
|
|
1434
|
+
: isRunning
|
|
1435
|
+
? EDGE_STYLE_RUNNING
|
|
1436
|
+
: undefined;
|
|
1358
1437
|
return {
|
|
1359
1438
|
id: e.id,
|
|
1360
1439
|
source: e.source.nodeId,
|
|
@@ -1364,7 +1443,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1364
1443
|
selected: opts.selectedEdgeIds
|
|
1365
1444
|
? opts.selectedEdgeIds.has(e.id)
|
|
1366
1445
|
: undefined,
|
|
1367
|
-
animated: isRunning,
|
|
1446
|
+
animated: isRunning && !isMissing,
|
|
1368
1447
|
style,
|
|
1369
1448
|
label: e.typeId || undefined,
|
|
1370
1449
|
};
|
|
@@ -2161,6 +2240,17 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2161
2240
|
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) }), debug && (jsxRuntime.jsx("div", { className: "mt-3 flex-none min-h-0 h-[50%]", children: jsxRuntime.jsx(DebugEvents, { autoScroll: !!autoScroll, hideWorkbench: !!hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange }) }))] }));
|
|
2162
2241
|
}
|
|
2163
2242
|
|
|
2243
|
+
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
2244
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: id, type: type, position: position, isConnectable: isConnectable, className: className, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + (kind === "input" ? " left-2" : " right-2"), style: {
|
|
2245
|
+
top: (y ?? 0) - 8,
|
|
2246
|
+
...(kind === "input"
|
|
2247
|
+
? { right: "50%" }
|
|
2248
|
+
: { left: "50%", textAlign: "right" }),
|
|
2249
|
+
whiteSpace: "nowrap",
|
|
2250
|
+
overflow: "hidden",
|
|
2251
|
+
textOverflow: "ellipsis",
|
|
2252
|
+
}, children: renderLabel({ kind, id }) }))] }));
|
|
2253
|
+
}
|
|
2164
2254
|
function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray-600", outputClassName = "!w-2 !h-2 !bg-gray-600", getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
2165
2255
|
const layout = data.handleLayout ?? [];
|
|
2166
2256
|
const byId = React.useMemo(() => {
|
|
@@ -2171,40 +2261,49 @@ function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray
|
|
|
2171
2261
|
position: h.position,
|
|
2172
2262
|
y: h.y,
|
|
2173
2263
|
type: h.type,
|
|
2264
|
+
missing: h.missing,
|
|
2174
2265
|
});
|
|
2175
2266
|
// Back-compat: also store by id-only if not already set
|
|
2176
2267
|
if (!m.has(h.id))
|
|
2177
|
-
m.set(h.id, {
|
|
2268
|
+
m.set(h.id, {
|
|
2269
|
+
position: h.position,
|
|
2270
|
+
y: h.y,
|
|
2271
|
+
type: h.type,
|
|
2272
|
+
missing: h.missing,
|
|
2273
|
+
});
|
|
2178
2274
|
}
|
|
2179
2275
|
return m;
|
|
2180
2276
|
}, [layout]);
|
|
2277
|
+
const inputIds = React.useMemo(() => new Set((data.inputHandles ?? []).map((h) => h.id)), [data.inputHandles]);
|
|
2278
|
+
const outputIds = React.useMemo(() => new Set((data.outputHandles ?? []).map((h) => h.id)), [data.outputHandles]);
|
|
2279
|
+
const missingInputs = React.useMemo(() => (layout || []).filter((h) => h.type === "target" && (!inputIds.has(h.id) || h.missing)), [layout, inputIds]);
|
|
2280
|
+
const missingOutputs = React.useMemo(() => (layout || []).filter((h) => h.type === "source" && (!outputIds.has(h.id) || h.missing)), [layout, outputIds]);
|
|
2181
2281
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
2182
2282
|
const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
|
|
2183
2283
|
const position = placed?.position ?? react.Position.Left;
|
|
2184
2284
|
const y = placed?.y;
|
|
2185
2285
|
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ??
|
|
2186
2286
|
inputClassName;
|
|
2187
|
-
return (jsxRuntime.
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2287
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: isConnectable, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
2288
|
+
}), missingInputs.map((h) => {
|
|
2289
|
+
const key = `missing-input:${h.id}`;
|
|
2290
|
+
const position = h.position ?? react.Position.Left;
|
|
2291
|
+
const y = h.y;
|
|
2292
|
+
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500";
|
|
2293
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "input", id: h.id, type: "target", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
2194
2294
|
}), (data.outputHandles ?? []).map((h) => {
|
|
2195
2295
|
const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
|
|
2196
2296
|
const position = placed?.position ?? react.Position.Right;
|
|
2197
2297
|
const y = placed?.y;
|
|
2198
2298
|
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ??
|
|
2199
2299
|
outputClassName;
|
|
2200
|
-
return (jsxRuntime.
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
}, children: renderLabel({ kind: "output", id: h.id }) }))] }, h.id));
|
|
2300
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: isConnectable, className: `${cls} wb-nodrag wb-nowheel`, labelClassName: labelClassName, renderLabel: renderLabel }, h.id));
|
|
2301
|
+
}), missingOutputs.map((h) => {
|
|
2302
|
+
const key = `missing-output:${h.id}`;
|
|
2303
|
+
const position = h.position ?? react.Position.Right;
|
|
2304
|
+
const y = h.y;
|
|
2305
|
+
const cls = "!w-3 !h-3 !bg-amber-400 !border-amber-500 !rounded-none wb-nodrag wb-nowheel";
|
|
2306
|
+
return (jsxRuntime.jsx(NodeHandleItem, { kind: "output", id: h.id, type: "source", position: position, y: y, isConnectable: false, className: cls, labelClassName: labelClassName, renderLabel: renderLabel }, key));
|
|
2208
2307
|
})] }));
|
|
2209
2308
|
}
|
|
2210
2309
|
|
|
@@ -2297,7 +2396,7 @@ function DefaultNodeContent({ data, isConnectable, }) {
|
|
|
2297
2396
|
const entries = kind === "input" ? inputEntries : outputEntries;
|
|
2298
2397
|
const entry = entries.find((e) => e.id === handleId);
|
|
2299
2398
|
if (!entry)
|
|
2300
|
-
return handleId;
|
|
2399
|
+
return prettyHandle(handleId);
|
|
2301
2400
|
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === handleId);
|
|
2302
2401
|
const hasAny = vIssues.length > 0;
|
|
2303
2402
|
const hasErr = vIssues.some((v) => v.level === "error");
|
|
@@ -2738,7 +2837,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
2738
2837
|
})
|
|
2739
2838
|
.map((n) => n.id);
|
|
2740
2839
|
// Detect handle updates (ids/length changes) for targeted debug
|
|
2741
|
-
const toIds = (arr) => Array.isArray(arr) ? arr.map((h) => h
|
|
2840
|
+
const toIds = (arr) => Array.isArray(arr) ? arr.map((h) => (h && typeof h === "object" && "id" in h ? String(h.id) : "")).filter(Boolean) : [];
|
|
2742
2841
|
const handlesEqual = (a, b) => {
|
|
2743
2842
|
const aIds = toIds(a);
|
|
2744
2843
|
const bIds = toIds(b);
|
|
@@ -3010,7 +3109,8 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3010
3109
|
URL.revokeObjectURL(url);
|
|
3011
3110
|
}
|
|
3012
3111
|
catch (err) {
|
|
3013
|
-
|
|
3112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3113
|
+
alert(message);
|
|
3014
3114
|
}
|
|
3015
3115
|
}, [wb, runner]);
|
|
3016
3116
|
const onUploadPicked = React.useCallback(async (e) => {
|
|
@@ -3068,7 +3168,8 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3068
3168
|
runAutoLayout();
|
|
3069
3169
|
}
|
|
3070
3170
|
catch (err) {
|
|
3071
|
-
|
|
3171
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3172
|
+
alert(message);
|
|
3072
3173
|
}
|
|
3073
3174
|
finally {
|
|
3074
3175
|
// reset input so same file can be picked again
|
|
@@ -3318,8 +3419,9 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3318
3419
|
return pre;
|
|
3319
3420
|
if (typeof value === "object" &&
|
|
3320
3421
|
value !== null &&
|
|
3422
|
+
"url" in value &&
|
|
3321
3423
|
typeof value.url === "string") {
|
|
3322
|
-
const title = value.title
|
|
3424
|
+
const title = ("title" in value && typeof value.title === "string") ? value.title : "";
|
|
3323
3425
|
const url = String(value.url || "");
|
|
3324
3426
|
// value.ts handles data URL formatting
|
|
3325
3427
|
return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
|
|
@@ -3332,7 +3434,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3332
3434
|
const round4 = (n) => Math.round(Number(n) * 10000) / 10000;
|
|
3333
3435
|
if (typeId === "base.vec3" && Array.isArray(value)) {
|
|
3334
3436
|
const a = value;
|
|
3335
|
-
return [round4(a[0] ?? 0), round4(a[1] ?? 0), round4(a[2] ?? 0)].join(",");
|
|
3437
|
+
return [round4(Number(a[0] ?? 0)), round4(Number(a[1] ?? 0)), round4(Number(a[2] ?? 0))].join(",");
|
|
3336
3438
|
}
|
|
3337
3439
|
const stringifyRounded = (v) => {
|
|
3338
3440
|
try {
|
|
@@ -3383,7 +3485,8 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3383
3485
|
runner.launch(wb.export(), { engine: kind });
|
|
3384
3486
|
}
|
|
3385
3487
|
catch (err) {
|
|
3386
|
-
|
|
3488
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3489
|
+
alert(message);
|
|
3387
3490
|
}
|
|
3388
3491
|
}, disabled: !engine, title: engine ? "Start engine" : "Select an engine first", children: [jsxRuntime.jsx(react$1.PlayIcon, { size: 14, weight: "fill" }), jsxRuntime.jsx("span", { className: "font-medium ml-1", children: "Start" })] })), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: runAutoLayout, children: "Auto Layout" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: "Fit View" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: downloadGraph, children: "Download Graph" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: async () => {
|
|
3389
3492
|
try {
|
|
@@ -3411,7 +3514,8 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3411
3514
|
URL.revokeObjectURL(url);
|
|
3412
3515
|
}
|
|
3413
3516
|
catch (err) {
|
|
3414
|
-
|
|
3517
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3518
|
+
alert(message);
|
|
3415
3519
|
}
|
|
3416
3520
|
}, children: "Download Snapshot" }), jsxRuntime.jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: triggerUpload, children: "Upload Graph/Snapshot" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement, contextPanel: overrides?.contextPanel })] })] }));
|
|
3417
3521
|
}
|