@bian-womp/spark-workbench 0.2.30 → 0.2.32
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 +114 -82
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +1 -0
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +6 -0
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +115 -83
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +1 -0
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +6 -0
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -477,7 +477,7 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
477
477
|
this.engine.on("error", (e) => this.emit("error", e));
|
|
478
478
|
this.engine.on("invalidate", (e) => this.emit("invalidate", e));
|
|
479
479
|
this.engine.on("stats", (e) => this.emit("stats", e));
|
|
480
|
-
this.engine.launch();
|
|
480
|
+
this.engine.launch(opts.invalidate);
|
|
481
481
|
this.runningKind = opts.engine;
|
|
482
482
|
this.emit("status", { running: true, engine: this.runningKind });
|
|
483
483
|
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
@@ -710,7 +710,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
710
710
|
this.listenersBound = true;
|
|
711
711
|
}
|
|
712
712
|
this.engine = eng;
|
|
713
|
-
this.engine.launch();
|
|
713
|
+
this.engine.launch(opts.invalidate);
|
|
714
714
|
this.runningKind = "push";
|
|
715
715
|
this.emit("status", { running: true, engine: this.runningKind });
|
|
716
716
|
for (const [nodeId, map] of Object.entries(this.stagedInputs)) {
|
|
@@ -754,9 +754,39 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
754
754
|
}
|
|
755
755
|
}
|
|
756
756
|
async applySnapshotFull(payload) {
|
|
757
|
+
// Hydrate local cache first so UI can display values immediately
|
|
758
|
+
this.hydrateCacheFromSnapshot(payload);
|
|
759
|
+
// Then sync with backend
|
|
757
760
|
const runner = await this.ensureRemoteRunner();
|
|
758
761
|
await runner.applySnapshotFull(payload);
|
|
759
762
|
}
|
|
763
|
+
/**
|
|
764
|
+
* Hydrates the local valueCache from a snapshot and emits value events.
|
|
765
|
+
* This ensures the UI can display inputs/outputs immediately without waiting
|
|
766
|
+
* for value events from the remote backend.
|
|
767
|
+
*/
|
|
768
|
+
hydrateCacheFromSnapshot(snapshot) {
|
|
769
|
+
// Hydrate inputs
|
|
770
|
+
for (const [nodeId, map] of Object.entries(snapshot.inputs || {})) {
|
|
771
|
+
for (const [handle, value] of Object.entries(map || {})) {
|
|
772
|
+
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
773
|
+
io: "input",
|
|
774
|
+
value,
|
|
775
|
+
});
|
|
776
|
+
this.emit("value", { nodeId, handle, value, io: "input" });
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
// Hydrate outputs
|
|
780
|
+
for (const [nodeId, map] of Object.entries(snapshot.outputs || {})) {
|
|
781
|
+
for (const [handle, value] of Object.entries(map || {})) {
|
|
782
|
+
this.valueCache.set(`${nodeId}.${handle}`, {
|
|
783
|
+
io: "output",
|
|
784
|
+
value,
|
|
785
|
+
});
|
|
786
|
+
this.emit("value", { nodeId, handle, value, io: "output" });
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
760
790
|
setEnvironment(env, opts) {
|
|
761
791
|
const t = this.transport;
|
|
762
792
|
if (!t)
|
|
@@ -2331,9 +2361,6 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2331
2361
|
const globalValidationIssues = validationGlobal;
|
|
2332
2362
|
const selectedNode = def.nodes.find((n) => n.nodeId === selectedNodeId);
|
|
2333
2363
|
const selectedEdge = def.edges.find((e) => e.id === selectedEdgeId);
|
|
2334
|
-
selectedNode
|
|
2335
|
-
? registry.nodes.get(selectedNode.typeId)
|
|
2336
|
-
: undefined;
|
|
2337
2364
|
// Use computeEffectiveHandles to merge registry defaults with dynamically resolved handles
|
|
2338
2365
|
const effectiveHandles = selectedNode
|
|
2339
2366
|
? computeEffectiveHandles(selectedNode, registry)
|
|
@@ -2413,81 +2440,81 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2413
2440
|
}
|
|
2414
2441
|
catch { }
|
|
2415
2442
|
};
|
|
2416
|
-
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
e.stopPropagation();
|
|
2421
|
-
deleteEdgeById(selectedEdge.id);
|
|
2422
|
-
}, title: "Delete this edge", children: "Delete edge" }) }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: "Type" }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: "DataTypeId" })] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", value: selectedEdge.typeId ?? "", onChange: (e) => {
|
|
2423
|
-
const v = e.target.value;
|
|
2424
|
-
const next = v === "" ? undefined : v;
|
|
2425
|
-
updateEdgeType(selectedEdge.id, next);
|
|
2426
|
-
}, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(registry.types.keys()).map((tid) => (jsxRuntime.jsx("option", { value: tid, children: tid }, tid)))] })] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2443
|
+
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto`, children: [jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsxRuntime.jsx("div", { className: "mb-2", children: contextPanel }), systemErrors.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxRuntime.jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsxRuntime.jsx("div", { className: "break-words", children: err.message })] }), jsxRuntime.jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsxRuntime.jsx(react$1.XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsxRuntime.jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxRuntime.jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: "Registry Error" }), jsxRuntime.jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxRuntime.jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsxRuntime.jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsxRuntime.jsx(react$1.XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsxRuntime.jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsxRuntime.jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsxRuntime.jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), !!m.data?.edgeId && (jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2444
|
+
e.stopPropagation();
|
|
2445
|
+
deleteEdgeById(m.data?.edgeId);
|
|
2446
|
+
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx("button", { className: "text-xs px-2 py-1 border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2427
2447
|
e.stopPropagation();
|
|
2428
2448
|
deleteEdgeById(selectedEdge.id);
|
|
2429
|
-
}, title: "Delete this edge", children: "Delete edge" })
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
setInput(h, raw);
|
|
2465
|
-
// keep drafts/originals in sync with label for display elsewhere
|
|
2466
|
-
const display = safeToString(typeId, raw);
|
|
2467
|
-
setDrafts((d) => ({ ...d, [h]: display }));
|
|
2468
|
-
setOriginals((o) => ({ ...o, [h]: display }));
|
|
2469
|
-
}, ...commonProps, children: [jsxRuntime.jsx("option", { value: "", children: "(select)" }), registry.enums.get(typeId)?.options.map((opt) => (jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] })) : isLinked ? (toElement(typeId, current)) : (jsxRuntime.jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", placeholder: isLinked ? "wired" : undefined, value: value, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
2470
|
-
if (e.key === "Enter")
|
|
2471
|
-
commit();
|
|
2472
|
-
if (e.key === "Escape")
|
|
2473
|
-
revert();
|
|
2474
|
-
}, ...commonProps }))] }, h));
|
|
2475
|
-
}))] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Outputs" }), outputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No outputs" })) : (outputHandles.map((h) => (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: prettyHandle(h) }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: outputTypesMap[selectedNodeId]?.[h] ?? "" })] }), jsxRuntime.jsx("div", { className: "flex-1", children: (() => {
|
|
2476
|
-
const { typeId, value } = resolveOutputDisplay(nodeOutputs[h], effectiveHandles.outputs[h]);
|
|
2477
|
-
return toElement(typeId, value);
|
|
2478
|
-
})() }), (() => {
|
|
2479
|
-
const outIssues = selectedNodeHandleValidation.outputs.filter((m) => m.handle === h);
|
|
2480
|
-
if (outIssues.length === 0)
|
|
2481
|
-
return null;
|
|
2482
|
-
const outErr = outIssues.some((m) => m.level === "error");
|
|
2483
|
-
const outTitle = outIssues
|
|
2449
|
+
}, title: "Delete this edge", children: "Delete edge" }) }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: "Type" }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: "DataTypeId" })] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", value: selectedEdge.typeId ?? "", onChange: (e) => {
|
|
2450
|
+
const v = e.target.value;
|
|
2451
|
+
const next = v === "" ? undefined : v;
|
|
2452
|
+
updateEdgeType(selectedEdge.id, next);
|
|
2453
|
+
}, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(registry.types.keys()).map((tid) => (jsxRuntime.jsx("option", { value: tid, children: tid }, tid)))] })] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2454
|
+
e.stopPropagation();
|
|
2455
|
+
deleteEdgeById(selectedEdge.id);
|
|
2456
|
+
}, title: "Delete this edge", children: "Delete edge" })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsxRuntime.jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
2457
|
+
selectedNodeStatus.lastError) }))] })), jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
2458
|
+
const typeId = sparkGraph.getInputTypeId(effectiveHandles.inputs, h);
|
|
2459
|
+
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
2460
|
+
e.target.handle === h);
|
|
2461
|
+
const commonProps = {
|
|
2462
|
+
style: { flex: 1 },
|
|
2463
|
+
disabled: isLinked,
|
|
2464
|
+
};
|
|
2465
|
+
const current = nodeInputs[h];
|
|
2466
|
+
const value = drafts[h] ?? safeToString(typeId, current);
|
|
2467
|
+
const onChangeText = (text) => setDrafts((d) => ({ ...d, [h]: text }));
|
|
2468
|
+
const commit = () => {
|
|
2469
|
+
const draft = drafts[h];
|
|
2470
|
+
if (draft === undefined)
|
|
2471
|
+
return;
|
|
2472
|
+
setInput(h, draft);
|
|
2473
|
+
setOriginals((o) => ({ ...o, [h]: draft }));
|
|
2474
|
+
};
|
|
2475
|
+
const revert = () => {
|
|
2476
|
+
const orig = originals[h] ?? safeToString(typeId, current);
|
|
2477
|
+
setDrafts((d) => ({ ...d, [h]: orig }));
|
|
2478
|
+
};
|
|
2479
|
+
const isEnum = typeId?.startsWith("enum:");
|
|
2480
|
+
const inIssues = selectedNodeHandleValidation.inputs.filter((m) => m.handle === h);
|
|
2481
|
+
const hasValidation = inIssues.length > 0;
|
|
2482
|
+
const hasErr = inIssues.some((m) => m.level === "error");
|
|
2483
|
+
const title = inIssues
|
|
2484
2484
|
.map((v) => `${v.code}: ${v.message}`)
|
|
2485
2485
|
.join("; ");
|
|
2486
|
-
return (jsxRuntime.jsx(IssueBadge, { level:
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2486
|
+
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-32 flex flex-col", children: [jsxRuntime.jsx("span", { children: prettyHandle(h) }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: typeId })] }), hasValidation && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", value: current !== undefined && current !== null
|
|
2487
|
+
? String(current)
|
|
2488
|
+
: "", onChange: (e) => {
|
|
2489
|
+
const val = e.target.value;
|
|
2490
|
+
const raw = val === "" ? undefined : Number(val);
|
|
2491
|
+
setInput(h, raw);
|
|
2492
|
+
// keep drafts/originals in sync with label for display elsewhere
|
|
2493
|
+
const display = safeToString(typeId, raw);
|
|
2494
|
+
setDrafts((d) => ({ ...d, [h]: display }));
|
|
2495
|
+
setOriginals((o) => ({ ...o, [h]: display }));
|
|
2496
|
+
}, ...commonProps, children: [jsxRuntime.jsx("option", { value: "", children: "(select)" }), registry.enums.get(typeId)?.options.map((opt) => (jsxRuntime.jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] })) : isLinked ? (toElement(typeId, current)) : (jsxRuntime.jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", placeholder: isLinked ? "wired" : undefined, value: value, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
2497
|
+
if (e.key === "Enter")
|
|
2498
|
+
commit();
|
|
2499
|
+
if (e.key === "Escape")
|
|
2500
|
+
revert();
|
|
2501
|
+
}, ...commonProps }))] }, h));
|
|
2502
|
+
}))] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Outputs" }), outputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No outputs" })) : (outputHandles.map((h) => (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: prettyHandle(h) }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: outputTypesMap[selectedNodeId]?.[h] ?? "" })] }), jsxRuntime.jsx("div", { className: "flex-1", children: (() => {
|
|
2503
|
+
const { typeId, value } = resolveOutputDisplay(nodeOutputs[h], effectiveHandles.outputs[h]);
|
|
2504
|
+
return toElement(typeId, value);
|
|
2505
|
+
})() }), (() => {
|
|
2506
|
+
const outIssues = selectedNodeHandleValidation.outputs.filter((m) => m.handle === h);
|
|
2507
|
+
if (outIssues.length === 0)
|
|
2508
|
+
return null;
|
|
2509
|
+
const outErr = outIssues.some((m) => m.level === "error");
|
|
2510
|
+
const outTitle = outIssues
|
|
2511
|
+
.map((v) => `${v.code}: ${v.message}`)
|
|
2512
|
+
.join("; ");
|
|
2513
|
+
return (jsxRuntime.jsx(IssueBadge, { level: outErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: outTitle }));
|
|
2514
|
+
})()] }, h))))] }), selectedNodeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedNodeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), !!m.data?.edgeId && (jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2515
|
+
e.stopPropagation();
|
|
2516
|
+
deleteEdgeById(m.data?.edgeId);
|
|
2517
|
+
}, 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 }) }))] }));
|
|
2491
2518
|
}
|
|
2492
2519
|
|
|
2493
2520
|
function NodeHandleItem({ kind, id, type, position, y, isConnectable, className, labelClassName, renderLabel, }) {
|
|
@@ -3655,25 +3682,30 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3655
3682
|
return overrides.toElement(baseToElement, { registry });
|
|
3656
3683
|
return baseToElement;
|
|
3657
3684
|
}, [overrides, baseToElement, registry]);
|
|
3658
|
-
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [runner.isRunning() ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runner.getRunningEngine()] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && (jsxRuntime.jsx(react$1.PlugsConnectedIcon, { size: 14, className: "text-gray-500" })), transportStatus.state === "connecting" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && (jsxRuntime.jsx(react$1.WifiHighIcon, { size: 14, className: "text-green-600" })), transportStatus.state === "disconnected" && (jsxRuntime.jsx(react$1.WifiSlashIcon, { size: 14, className: "text-red-600" })), transportStatus.state === "retrying" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxRuntime.
|
|
3685
|
+
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [runner.isRunning() ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runner.getRunningEngine()] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && (jsxRuntime.jsx(react$1.PlugsConnectedIcon, { size: 14, className: "text-gray-500" })), transportStatus.state === "connecting" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && (jsxRuntime.jsx(react$1.WifiHighIcon, { size: 14, className: "text-green-600" })), transportStatus.state === "disconnected" && (jsxRuntime.jsx(react$1.WifiSlashIcon, { size: 14, className: "text-red-600" })), transportStatus.state === "retrying" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
3659
3686
|
? "Stop engine before switching example"
|
|
3660
|
-
: undefined, children: [jsxRuntime.jsx("option", { value: "", children: "Select Example\u2026" }), examples.map((ex) => (jsxRuntime.jsx("option", { value: ex.id, children: ex.label }, ex.id)))] }), jsxRuntime.
|
|
3687
|
+
: undefined, children: [jsxRuntime.jsx("option", { value: "", children: "Select Example\u2026" }), examples.map((ex) => (jsxRuntime.jsx("option", { value: ex.id, children: ex.label }, ex.id)))] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: backendKind, onChange: (e) => onBackendKindChange(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
3661
3688
|
? "Stop engine before switching backend"
|
|
3662
3689
|
: undefined, children: [jsxRuntime.jsx("option", { value: "local", children: "Local" }), jsxRuntime.jsx("option", { value: "remote-http", children: "Remote (HTTP)" }), jsxRuntime.jsx("option", { value: "remote-ws", children: "Remote (WebSocket)" })] }), backendKind === "remote-http" && !!onHttpBaseUrlChange && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "http://127.0.0.1:18080", value: httpBaseUrl, onChange: (e) => onHttpBaseUrlChange(e.target.value) })), backendKind === "remote-ws" && !!onWsUrlChange && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "ws://127.0.0.1:18081", value: wsUrl, onChange: (e) => onWsUrlChange(e.target.value) })), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: runner.getRunningEngine() ?? engine ?? "", onChange: (e) => {
|
|
3663
3690
|
const kind = e.target.value || undefined;
|
|
3664
3691
|
onEngineChange?.(kind);
|
|
3665
|
-
}, children: [jsxRuntime.jsx("option", { value: "", children: "Select Engine\u2026" }), jsxRuntime.jsx("option", { value: "push", children: "Push" }), jsxRuntime.jsx("option", { value: "batched", children: "Batched" }), jsxRuntime.jsx("option", { value: "pull", children: "Pull" }), jsxRuntime.jsx("option", { value: "hybrid", children: "Hybrid" }), jsxRuntime.jsx("option", { value: "step", children: "Step" })] }), runner.getRunningEngine() === "step" && (jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded
|
|
3692
|
+
}, children: [jsxRuntime.jsx("option", { value: "", children: "Select Engine\u2026" }), jsxRuntime.jsx("option", { value: "push", children: "Push" }), jsxRuntime.jsx("option", { value: "batched", children: "Batched" }), jsxRuntime.jsx("option", { value: "pull", children: "Pull" }), jsxRuntime.jsx("option", { value: "hybrid", children: "Hybrid" }), jsxRuntime.jsx("option", { value: "step", children: "Step" })] }), runner.getRunningEngine() === "step" && (jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded p-1", onClick: () => runner.step(), disabled: !runner.isRunning(), title: "Step", children: jsxRuntime.jsx(react$1.PlayPauseIcon, { size: 24 }) })), runner.getRunningEngine() === "batched" && (jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded p-1", onClick: () => runner.flush(), disabled: !runner.isRunning(), title: "Flush", children: jsxRuntime.jsx(react$1.LightningIcon, { size: 24 }) })), runner.isRunning() ? (jsxRuntime.jsxs("button", { className: "border rounded px-2 py-1.5 text-red-700 border-red-600 flex items-center gap-1", onClick: () => runner.dispose(), title: "Stop engine", children: [jsxRuntime.jsx(react$1.StopIcon, { size: 16, weight: "fill" }), jsxRuntime.jsx("span", { className: "font-medium ml-1", children: "Stop" })] })) : (jsxRuntime.jsxs("button", { className: "border rounded px-2 py-1.5 text-green-700 border-green-600 flex items-center gap-1 disabled:text-gray-400 disabled:border-gray-300", onClick: (evt) => {
|
|
3666
3693
|
const kind = engine;
|
|
3667
3694
|
if (!kind)
|
|
3668
3695
|
return alert("Select an engine first.");
|
|
3696
|
+
if (evt.shiftKey && !confirm("Invalidate and re-run graph?"))
|
|
3697
|
+
return;
|
|
3669
3698
|
try {
|
|
3670
|
-
runner.launch(wb.export(), {
|
|
3699
|
+
runner.launch(wb.export(), {
|
|
3700
|
+
engine: kind,
|
|
3701
|
+
invalidate: evt.shiftKey,
|
|
3702
|
+
});
|
|
3671
3703
|
}
|
|
3672
3704
|
catch (err) {
|
|
3673
3705
|
const message = err instanceof Error ? err.message : String(err);
|
|
3674
3706
|
alert(message);
|
|
3675
3707
|
}
|
|
3676
|
-
}, disabled: !engine, title: engine ? "Start engine" : "Select an engine first", children: [jsxRuntime.jsx(react$1.PlayIcon, { size:
|
|
3708
|
+
}, disabled: !engine, title: engine ? "Start engine" : "Select an engine first", children: [jsxRuntime.jsx(react$1.PlayIcon, { size: 16, weight: "fill" }), jsxRuntime.jsx("span", { className: "font-medium ml-1", children: "Start" })] })), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsxRuntime.jsx(react$1.TreeStructureIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsxRuntime.jsx(react$1.CornersOutIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded p-1", onClick: downloadGraph, children: jsxRuntime.jsx(react$1.DownloadSimpleIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded p-1", onClick: async () => {
|
|
3677
3709
|
try {
|
|
3678
3710
|
const def = wb.export();
|
|
3679
3711
|
const positions = wb.getPositions();
|
|
@@ -3702,7 +3734,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3702
3734
|
const message = err instanceof Error ? err.message : String(err);
|
|
3703
3735
|
alert(message);
|
|
3704
3736
|
}
|
|
3705
|
-
}, children:
|
|
3737
|
+
}, children: jsxRuntime.jsx(react$1.DownloadIcon, { size: 24 }) }), 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: jsxRuntime.jsx(react$1.UploadIcon, { size: 24 }) }), 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(react$1.BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), 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(react$1.ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), 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 })] })] }));
|
|
3706
3738
|
}
|
|
3707
3739
|
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
|
|
3708
3740
|
const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
|