@bian-womp/spark-workbench 0.2.9 → 0.2.11
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 +337 -146
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/AbstractWorkbench.d.ts +2 -2
- package/lib/cjs/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +2 -2
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +2 -2
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/NodeContextMenu.d.ts +1 -1
- package/lib/cjs/src/misc/NodeContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts +7 -0
- package/lib/cjs/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/IGraphRunner.d.ts +7 -0
- package/lib/cjs/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts +12 -0
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts +7 -0
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +337 -146
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/AbstractWorkbench.d.ts +2 -2
- package/lib/esm/src/core/AbstractWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +2 -2
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +2 -2
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/NodeContextMenu.d.ts +1 -1
- package/lib/esm/src/misc/NodeContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts +7 -0
- package/lib/esm/src/runtime/AbstractGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/IGraphRunner.d.ts +7 -0
- package/lib/esm/src/runtime/IGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts +12 -0
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts +7 -0
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -169,6 +169,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
169
169
|
change: { type: "addNode", nodeId: id },
|
|
170
170
|
});
|
|
171
171
|
this.refreshValidation();
|
|
172
|
+
return id;
|
|
172
173
|
}
|
|
173
174
|
removeNode(nodeId) {
|
|
174
175
|
this.def.nodes = this.def.nodes.filter((n) => n.nodeId !== nodeId);
|
|
@@ -193,6 +194,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
193
194
|
change: { type: "connect", edgeId: id },
|
|
194
195
|
});
|
|
195
196
|
this.refreshValidation();
|
|
197
|
+
return id;
|
|
196
198
|
}
|
|
197
199
|
disconnect(edgeId) {
|
|
198
200
|
this.def.edges = this.def.edges.filter((e) => e.id !== edgeId);
|
|
@@ -409,6 +411,21 @@ class AbstractGraphRunner {
|
|
|
409
411
|
class LocalGraphRunner extends AbstractGraphRunner {
|
|
410
412
|
constructor(registry) {
|
|
411
413
|
super(registry, { kind: "local" });
|
|
414
|
+
this.setEnvironment = (env, opts) => {
|
|
415
|
+
if (!this.runtime)
|
|
416
|
+
return;
|
|
417
|
+
if (opts?.merge) {
|
|
418
|
+
const current = this.runtime.getEnvironment();
|
|
419
|
+
const next = { ...(current || {}), ...(env || {}) };
|
|
420
|
+
this.runtime.setEnvironment(next);
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
this.runtime.setEnvironment(env);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
this.getEnvironment = () => {
|
|
427
|
+
return this.runtime?.getEnvironment?.();
|
|
428
|
+
};
|
|
412
429
|
this.emit("transport", { state: "local" });
|
|
413
430
|
}
|
|
414
431
|
build(def) {
|
|
@@ -507,17 +524,36 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
507
524
|
const runtimeInputs = this.runtime
|
|
508
525
|
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
509
526
|
: {};
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
else {
|
|
514
|
-
const merged = { ...runtimeInputs, ...staged };
|
|
515
|
-
if (Object.keys(merged).length > 0)
|
|
516
|
-
out[n.nodeId] = merged;
|
|
517
|
-
}
|
|
527
|
+
const merged = { ...runtimeInputs, ...staged };
|
|
528
|
+
if (Object.keys(merged).length > 0)
|
|
529
|
+
out[n.nodeId] = merged;
|
|
518
530
|
}
|
|
519
531
|
return out;
|
|
520
532
|
}
|
|
533
|
+
async snapshotFull() {
|
|
534
|
+
const def = undefined; // UI will supply def/positions on download for local
|
|
535
|
+
const inputs = this.getInputs(this.runtime
|
|
536
|
+
? {
|
|
537
|
+
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({ nodeId: id, typeId: "" })),
|
|
538
|
+
edges: [],
|
|
539
|
+
}
|
|
540
|
+
: { nodes: [], edges: [] });
|
|
541
|
+
const outputs = this.getOutputs(this.runtime
|
|
542
|
+
? {
|
|
543
|
+
nodes: Array.from(this.runtime.getNodeIds()).map((id) => ({ nodeId: id, typeId: "" })),
|
|
544
|
+
edges: [],
|
|
545
|
+
}
|
|
546
|
+
: { nodes: [], edges: [] });
|
|
547
|
+
const environment = this.getEnvironment() || {};
|
|
548
|
+
return { def, environment, inputs, outputs };
|
|
549
|
+
}
|
|
550
|
+
async applySnapshotFull(payload) {
|
|
551
|
+
if (payload.def)
|
|
552
|
+
this.build(payload.def);
|
|
553
|
+
this.setEnvironment?.(payload.environment || {}, { merge: false });
|
|
554
|
+
// Hydrate via runtime for exact restore and re-emit
|
|
555
|
+
this.runtime?.hydrate({ inputs: payload.inputs || {}, outputs: payload.outputs || {} }, { reemit: true });
|
|
556
|
+
}
|
|
521
557
|
dispose() {
|
|
522
558
|
super.dispose();
|
|
523
559
|
this.runtime = undefined;
|
|
@@ -689,6 +725,36 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
689
725
|
return value;
|
|
690
726
|
}
|
|
691
727
|
}
|
|
728
|
+
async snapshotFull() {
|
|
729
|
+
const runner = await this.ensureRemoteRunner();
|
|
730
|
+
try {
|
|
731
|
+
return await runner.snapshotFull();
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
return { def: undefined, environment: {}, inputs: {}, outputs: {} };
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
async applySnapshotFull(payload) {
|
|
738
|
+
const runner = await this.ensureRemoteRunner();
|
|
739
|
+
await runner.applySnapshotFull(payload);
|
|
740
|
+
}
|
|
741
|
+
setEnvironment(env, opts) {
|
|
742
|
+
const t = this.transport;
|
|
743
|
+
if (!t)
|
|
744
|
+
return;
|
|
745
|
+
t.request({
|
|
746
|
+
message: {
|
|
747
|
+
type: "SetEnvironment",
|
|
748
|
+
payload: { environment: env, merge: opts?.merge },
|
|
749
|
+
},
|
|
750
|
+
}).catch(() => { });
|
|
751
|
+
}
|
|
752
|
+
getEnvironment() {
|
|
753
|
+
// Fetch from remote via lightweight command
|
|
754
|
+
// Note: returns undefined synchronously; callers needing value should use snapshotFull or call runner directly
|
|
755
|
+
// For now, we expose an async helper on RemoteRunner. Keep sync signature per interface.
|
|
756
|
+
return undefined;
|
|
757
|
+
}
|
|
692
758
|
getOutputs(def) {
|
|
693
759
|
const out = {};
|
|
694
760
|
const cache = this.valueCache;
|
|
@@ -722,7 +788,7 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
722
788
|
if (rec && rec.io === "input")
|
|
723
789
|
cur[h] = rec.value;
|
|
724
790
|
}
|
|
725
|
-
const merged =
|
|
791
|
+
const merged = { ...cur, ...staged };
|
|
726
792
|
if (Object.keys(merged).length > 0)
|
|
727
793
|
out[n.nodeId] = merged;
|
|
728
794
|
}
|
|
@@ -2083,7 +2149,7 @@ function DefaultNodeHeader({ id, title, status, validation, right, onInvalidate,
|
|
|
2083
2149
|
.join("; ") })), jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }));
|
|
2084
2150
|
}
|
|
2085
2151
|
function DefaultNodeContent({ id, data, isConnectable, }) {
|
|
2086
|
-
|
|
2152
|
+
useWorkbenchContext();
|
|
2087
2153
|
const { showValues, inputValues, outputValues, toString } = data;
|
|
2088
2154
|
const inputEntries = data.inputHandles ?? [];
|
|
2089
2155
|
const outputEntries = data.outputHandles ?? [];
|
|
@@ -2093,98 +2159,6 @@ function DefaultNodeContent({ id, data, isConnectable, }) {
|
|
|
2093
2159
|
outputs: []};
|
|
2094
2160
|
const isRunning = !!status.activeRuns;
|
|
2095
2161
|
const pct = Math.round(Math.max(0, Math.min(1, Number(status.progress) || 0)) * 100);
|
|
2096
|
-
const handleBake = React.useCallback(async (handleId) => {
|
|
2097
|
-
try {
|
|
2098
|
-
const typeId = ctx.outputTypesMap?.[id]?.[handleId];
|
|
2099
|
-
const rawValue = ctx.outputsMap?.[id]?.[handleId];
|
|
2100
|
-
if (!typeId || rawValue === undefined)
|
|
2101
|
-
return;
|
|
2102
|
-
const unwrap = (v) => sparkGraph.isTypedOutput(v) ? sparkGraph.getTypedOutputValue(v) : v;
|
|
2103
|
-
const clone = (v) => typeof structuredClone === "function"
|
|
2104
|
-
? structuredClone(v)
|
|
2105
|
-
: JSON.parse(JSON.stringify(v));
|
|
2106
|
-
const coerceIfNeeded = async (fromType, toType, value) => {
|
|
2107
|
-
if (!toType || toType === fromType || !ctx.runner?.coerce)
|
|
2108
|
-
return value;
|
|
2109
|
-
try {
|
|
2110
|
-
return await ctx.runner.coerce(fromType, toType, value);
|
|
2111
|
-
}
|
|
2112
|
-
catch {
|
|
2113
|
-
return value;
|
|
2114
|
-
}
|
|
2115
|
-
};
|
|
2116
|
-
const positions = ctx.wb.getPositions();
|
|
2117
|
-
const pos = positions[id] || { x: 0, y: 0 };
|
|
2118
|
-
const isArray = typeId.endsWith("[]");
|
|
2119
|
-
const baseTypeId = isArray ? typeId.slice(0, -2) : typeId;
|
|
2120
|
-
const tArr = isArray ? ctx.registry?.types.get(typeId) : undefined;
|
|
2121
|
-
const tElem = ctx.registry?.types.get(baseTypeId);
|
|
2122
|
-
const singleTarget = !isArray ? tElem?.bakeTarget : undefined;
|
|
2123
|
-
const arrTarget = isArray ? tArr?.bakeTarget : undefined;
|
|
2124
|
-
const elemTarget = isArray ? tElem?.bakeTarget : undefined;
|
|
2125
|
-
const makeTargetInfo = (bt) => {
|
|
2126
|
-
if (!bt)
|
|
2127
|
-
return undefined;
|
|
2128
|
-
const node = ctx.registry?.nodes.get(String(bt.nodeTypeId));
|
|
2129
|
-
const inType = sparkGraph.getInputTypeId(node?.inputs, String(bt.inputHandle || "Value"));
|
|
2130
|
-
return { bt, inType };
|
|
2131
|
-
};
|
|
2132
|
-
// Plan and execute
|
|
2133
|
-
if (singleTarget) {
|
|
2134
|
-
const info = makeTargetInfo(singleTarget);
|
|
2135
|
-
const v = unwrap(rawValue);
|
|
2136
|
-
const coerced = await coerceIfNeeded(typeId, info?.inType, v);
|
|
2137
|
-
ctx.wb.addNode({
|
|
2138
|
-
nodeId: undefined,
|
|
2139
|
-
typeId: String(singleTarget.nodeTypeId),
|
|
2140
|
-
position: { x: pos.x + 180, y: pos.y },
|
|
2141
|
-
params: {},
|
|
2142
|
-
initialInputs: {
|
|
2143
|
-
[String(singleTarget.inputHandle || "Value")]: clone(coerced),
|
|
2144
|
-
},
|
|
2145
|
-
});
|
|
2146
|
-
return;
|
|
2147
|
-
}
|
|
2148
|
-
if (isArray && arrTarget) {
|
|
2149
|
-
const info = makeTargetInfo(arrTarget);
|
|
2150
|
-
const v = unwrap(rawValue);
|
|
2151
|
-
const coerced = await coerceIfNeeded(typeId, info?.inType, v);
|
|
2152
|
-
ctx.wb.addNode({
|
|
2153
|
-
nodeId: undefined,
|
|
2154
|
-
typeId: String(arrTarget.nodeTypeId),
|
|
2155
|
-
position: { x: pos.x + 180, y: pos.y },
|
|
2156
|
-
params: {},
|
|
2157
|
-
initialInputs: {
|
|
2158
|
-
[String(arrTarget.inputHandle || "Value")]: clone(coerced),
|
|
2159
|
-
},
|
|
2160
|
-
});
|
|
2161
|
-
return;
|
|
2162
|
-
}
|
|
2163
|
-
if (isArray && elemTarget && Array.isArray(rawValue)) {
|
|
2164
|
-
const info = makeTargetInfo(elemTarget);
|
|
2165
|
-
const items = rawValue.map(unwrap);
|
|
2166
|
-
const coercedItems = await Promise.all(items.map((v) => coerceIfNeeded(baseTypeId, info?.inType, v)));
|
|
2167
|
-
const COLS = 4;
|
|
2168
|
-
const DX = 180;
|
|
2169
|
-
const DY = 160;
|
|
2170
|
-
coercedItems.forEach((cv, idx) => {
|
|
2171
|
-
const col = idx % COLS;
|
|
2172
|
-
const row = Math.floor(idx / COLS);
|
|
2173
|
-
ctx.wb.addNode({
|
|
2174
|
-
nodeId: undefined,
|
|
2175
|
-
typeId: String(elemTarget.nodeTypeId),
|
|
2176
|
-
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
2177
|
-
params: {},
|
|
2178
|
-
initialInputs: {
|
|
2179
|
-
[String(elemTarget.inputHandle || "Value")]: clone(cv),
|
|
2180
|
-
},
|
|
2181
|
-
});
|
|
2182
|
-
});
|
|
2183
|
-
return;
|
|
2184
|
-
}
|
|
2185
|
-
}
|
|
2186
|
-
catch { }
|
|
2187
|
-
}, [ctx, id]);
|
|
2188
2162
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: cx("h-px", (isRunning || pct > 0) && "bg-blue-200 dark:bg-blue-900"), children: jsxRuntime.jsx("div", { className: cx("h-px transition-all", (isRunning || pct > 0) && "bg-blue-500"), style: { width: isRunning || pct > 0 ? `${pct}%` : 0 } }) }), jsxRuntime.jsx(NodeHandles, { data: data, isConnectable: isConnectable, getClassName: ({ kind, id }) => {
|
|
2189
2163
|
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === id);
|
|
2190
2164
|
const hasAny = vIssues.length > 0;
|
|
@@ -2212,26 +2186,7 @@ function DefaultNodeContent({ id, data, isConnectable, }) {
|
|
|
2212
2186
|
const txt = toString(resolved.typeId, resolved.value);
|
|
2213
2187
|
return typeof txt === "string" ? txt : String(txt);
|
|
2214
2188
|
})();
|
|
2215
|
-
|
|
2216
|
-
let canBake = false;
|
|
2217
|
-
if (tId?.endsWith("[]")) {
|
|
2218
|
-
const base = tId.slice(0, -2);
|
|
2219
|
-
const tArr = ctx.registry?.types.get(tId);
|
|
2220
|
-
const tElem = ctx.registry?.types.get(base);
|
|
2221
|
-
const arrTarget = tArr?.bakeTarget;
|
|
2222
|
-
const elemTarget = tElem?.bakeTarget;
|
|
2223
|
-
canBake = !!((arrTarget && ctx.registry?.nodes?.has?.(arrTarget.nodeTypeId)) ||
|
|
2224
|
-
(elemTarget && ctx.registry?.nodes?.has?.(elemTarget.nodeTypeId)));
|
|
2225
|
-
}
|
|
2226
|
-
else if (tId) {
|
|
2227
|
-
const t = ctx.registry?.types.get(tId);
|
|
2228
|
-
const target = t?.bakeTarget;
|
|
2229
|
-
canBake = !!(target && ctx.registry?.nodes?.has?.(target.nodeTypeId));
|
|
2230
|
-
}
|
|
2231
|
-
return (jsxRuntime.jsxs("span", { className: "flex items-center gap-1 w-full", children: [kind === "output" ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [canBake && (jsxRuntime.jsx("button", { onClick: (e) => {
|
|
2232
|
-
e.stopPropagation();
|
|
2233
|
-
handleBake(handleId);
|
|
2234
|
-
}, title: "Bake value", className: "pointer-events-auto border border-gray-300 rounded px-1 py-0.5 text-[10px] bg-white/80 hover:bg-white mr-2", children: "Bake" })), valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pl-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText })), jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: handleId })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: handleId }), valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pr-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText }))] })), hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "shrink-0", title: title }))] }));
|
|
2189
|
+
return (jsxRuntime.jsxs("span", { className: "flex items-center gap-1 w-full", children: [kind === "output" ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pl-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText })), jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: handleId })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: handleId }), valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pr-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText }))] })), hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "shrink-0", title: title }))] }));
|
|
2235
2190
|
} })] }));
|
|
2236
2191
|
}
|
|
2237
2192
|
|
|
@@ -2322,7 +2277,7 @@ function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
|
2322
2277
|
}
|
|
2323
2278
|
|
|
2324
2279
|
function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
2325
|
-
const { wb, runner, engineKind } = useWorkbenchContext();
|
|
2280
|
+
const { wb, runner, engineKind, registry, outputsMap, outputTypesMap } = useWorkbenchContext();
|
|
2326
2281
|
const ref = React.useRef(null);
|
|
2327
2282
|
// outside click + ESC
|
|
2328
2283
|
React.useEffect(() => {
|
|
@@ -2349,47 +2304,182 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
|
|
|
2349
2304
|
if (open)
|
|
2350
2305
|
ref.current?.focus();
|
|
2351
2306
|
}, [open]);
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2307
|
+
// Bake helpers
|
|
2308
|
+
const getBakeableOutputs = () => {
|
|
2309
|
+
try {
|
|
2310
|
+
const def = wb.export();
|
|
2311
|
+
const node = def.nodes.find((n) => n.nodeId === nodeId);
|
|
2312
|
+
if (!node)
|
|
2313
|
+
return [];
|
|
2314
|
+
const desc = registry.nodes.get(node.typeId);
|
|
2315
|
+
const handles = Object.keys(desc?.outputs || {});
|
|
2316
|
+
const out = [];
|
|
2317
|
+
for (const h of handles) {
|
|
2318
|
+
const tId = outputTypesMap?.[nodeId]?.[h];
|
|
2319
|
+
if (!tId)
|
|
2320
|
+
continue;
|
|
2321
|
+
if (tId.endsWith("[]")) {
|
|
2322
|
+
const base = tId.slice(0, -2);
|
|
2323
|
+
const tArr = registry.types.get(tId);
|
|
2324
|
+
const tElem = registry.types.get(base);
|
|
2325
|
+
const arrT = tArr?.bakeTarget;
|
|
2326
|
+
const elemT = tElem?.bakeTarget;
|
|
2327
|
+
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
2328
|
+
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
2329
|
+
out.push(h);
|
|
2330
|
+
}
|
|
2331
|
+
else {
|
|
2332
|
+
const t = registry.types.get(tId);
|
|
2333
|
+
const bt = t?.bakeTarget;
|
|
2334
|
+
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
2335
|
+
out.push(h);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
return out;
|
|
2339
|
+
}
|
|
2340
|
+
catch {
|
|
2341
|
+
return [];
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2344
|
+
const doBake = async (handleId) => {
|
|
2345
|
+
try {
|
|
2346
|
+
const typeId = outputTypesMap?.[nodeId]?.[handleId];
|
|
2347
|
+
const raw = outputsMap?.[nodeId]?.[handleId];
|
|
2348
|
+
if (!typeId || raw === undefined)
|
|
2349
|
+
return;
|
|
2350
|
+
const unwrap = (v) => sparkGraph.isTypedOutput(v) ? sparkGraph.getTypedOutputValue(v) : v;
|
|
2351
|
+
const clone = (v) => typeof structuredClone === "function"
|
|
2352
|
+
? structuredClone(v)
|
|
2353
|
+
: JSON.parse(JSON.stringify(v));
|
|
2354
|
+
const coerceIfNeeded = async (fromType, toType, value) => {
|
|
2355
|
+
if (!toType || toType === fromType || !runner?.coerce)
|
|
2356
|
+
return value;
|
|
2357
|
+
try {
|
|
2358
|
+
return await runner.coerce(fromType, toType, value);
|
|
2359
|
+
}
|
|
2360
|
+
catch {
|
|
2361
|
+
return value;
|
|
2362
|
+
}
|
|
2363
|
+
};
|
|
2364
|
+
const pos = wb.getPositions?.()[nodeId] || { x: 0, y: 0 };
|
|
2365
|
+
const isArray = typeId.endsWith("[]");
|
|
2366
|
+
const baseTypeId = isArray ? typeId.slice(0, -2) : typeId;
|
|
2367
|
+
const tArr = isArray ? registry.types.get(typeId) : undefined;
|
|
2368
|
+
const tElem = registry.types.get(baseTypeId);
|
|
2369
|
+
const singleTarget = !isArray ? tElem?.bakeTarget : undefined;
|
|
2370
|
+
const arrTarget = isArray ? tArr?.bakeTarget : undefined;
|
|
2371
|
+
const elemTarget = isArray ? tElem?.bakeTarget : undefined;
|
|
2372
|
+
if (singleTarget) {
|
|
2373
|
+
const nodeDesc = registry.nodes.get(singleTarget.nodeTypeId);
|
|
2374
|
+
const inType = sparkGraph.getInputTypeId(nodeDesc?.inputs, singleTarget.inputHandle);
|
|
2375
|
+
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
2376
|
+
const newId = wb.addNode({
|
|
2377
|
+
typeId: singleTarget.nodeTypeId,
|
|
2378
|
+
position: { x: pos.x + 180, y: pos.y },
|
|
2379
|
+
params: {},
|
|
2380
|
+
});
|
|
2381
|
+
runner.update(wb.export());
|
|
2382
|
+
await runner.whenIdle();
|
|
2383
|
+
runner.setInputs(newId, { [singleTarget.inputHandle]: coerced });
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
if (isArray && arrTarget) {
|
|
2387
|
+
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
2388
|
+
const inType = sparkGraph.getInputTypeId(nodeDesc?.inputs, arrTarget.inputHandle);
|
|
2389
|
+
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
2390
|
+
const newId = `n${Math.random().toString(36).slice(2, 8)}`;
|
|
2391
|
+
wb.addNode({
|
|
2392
|
+
nodeId: newId,
|
|
2393
|
+
typeId: arrTarget.nodeTypeId,
|
|
2394
|
+
position: { x: pos.x + 180, y: pos.y },
|
|
2395
|
+
params: {},
|
|
2396
|
+
});
|
|
2397
|
+
runner.update(wb.export());
|
|
2398
|
+
await runner.whenIdle();
|
|
2399
|
+
runner.setInputs(newId, { [arrTarget.inputHandle]: coerced });
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
if (isArray && elemTarget) {
|
|
2403
|
+
const nodeDesc = registry.nodes.get(elemTarget.nodeTypeId);
|
|
2404
|
+
const inType = sparkGraph.getInputTypeId(nodeDesc?.inputs, elemTarget.inputHandle);
|
|
2405
|
+
const src = unwrap(raw);
|
|
2406
|
+
const items = Array.isArray(src) ? src : [src];
|
|
2407
|
+
const coercedItems = await Promise.all(items.map((v) => coerceIfNeeded(baseTypeId, inType, v)));
|
|
2408
|
+
const COLS = 4;
|
|
2409
|
+
const DX = 180;
|
|
2410
|
+
const DY = 160;
|
|
2411
|
+
for (let idx = 0; idx < coercedItems.length; idx++) {
|
|
2412
|
+
const cv = coercedItems[idx];
|
|
2413
|
+
const col = idx % COLS;
|
|
2414
|
+
const row = Math.floor(idx / COLS);
|
|
2415
|
+
const newId = wb.addNode({
|
|
2416
|
+
typeId: elemTarget.nodeTypeId,
|
|
2417
|
+
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
2418
|
+
params: {},
|
|
2419
|
+
initialInputs: { [elemTarget.inputHandle]: clone(cv) },
|
|
2420
|
+
});
|
|
2421
|
+
runner.update(wb.export());
|
|
2422
|
+
await runner.whenIdle();
|
|
2423
|
+
runner.setInputs(newId, { [elemTarget.inputHandle]: cv });
|
|
2424
|
+
}
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
catch { }
|
|
2429
|
+
};
|
|
2360
2430
|
// actions
|
|
2361
|
-
const handleDelete = () => {
|
|
2431
|
+
const handleDelete = React.useCallback(() => {
|
|
2362
2432
|
wb.removeNode(nodeId);
|
|
2363
2433
|
onClose();
|
|
2364
|
-
};
|
|
2365
|
-
const handleDuplicate = () => {
|
|
2434
|
+
}, [nodeId, wb, onClose]);
|
|
2435
|
+
const handleDuplicate = React.useCallback(() => {
|
|
2366
2436
|
const def = wb.export();
|
|
2367
2437
|
const n = def.nodes.find((n) => n.nodeId === nodeId);
|
|
2368
2438
|
if (!n)
|
|
2369
2439
|
return onClose();
|
|
2370
2440
|
const pos = wb.getPositions?.()[nodeId] || { x: 0, y: 0 };
|
|
2371
|
-
wb.addNode({
|
|
2441
|
+
wb.addNode({
|
|
2442
|
+
typeId: n.typeId,
|
|
2443
|
+
params: n.params,
|
|
2444
|
+
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
2445
|
+
});
|
|
2372
2446
|
onClose();
|
|
2373
|
-
};
|
|
2374
|
-
|
|
2447
|
+
}, [nodeId, wb, onClose]);
|
|
2448
|
+
React.useCallback(async (handleId) => {
|
|
2449
|
+
await doBake(handleId);
|
|
2450
|
+
onClose();
|
|
2451
|
+
}, [doBake, onClose]);
|
|
2452
|
+
const handleCopyId = React.useCallback(async () => {
|
|
2375
2453
|
try {
|
|
2376
2454
|
await navigator.clipboard.writeText(nodeId);
|
|
2377
2455
|
}
|
|
2378
2456
|
catch { }
|
|
2379
2457
|
onClose();
|
|
2380
|
-
};
|
|
2381
|
-
const
|
|
2382
|
-
const handleRunPull = async () => {
|
|
2458
|
+
}, [nodeId, onClose]);
|
|
2459
|
+
const handleRunPull = React.useCallback(async () => {
|
|
2383
2460
|
try {
|
|
2384
2461
|
await runner.computeNode(nodeId);
|
|
2385
2462
|
}
|
|
2386
2463
|
catch { }
|
|
2387
2464
|
onClose();
|
|
2388
|
-
};
|
|
2465
|
+
}, [nodeId, runner, onClose]);
|
|
2466
|
+
if (!open || !clientPos || !nodeId)
|
|
2467
|
+
return null;
|
|
2468
|
+
// clamp
|
|
2469
|
+
const MENU_MIN_WIDTH = 180;
|
|
2470
|
+
const PADDING = 16;
|
|
2471
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
2472
|
+
(MENU_MIN_WIDTH + PADDING));
|
|
2473
|
+
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
2474
|
+
const canRunPull = engineKind()?.toString() === "pull";
|
|
2475
|
+
const outs = getBakeableOutputs();
|
|
2389
2476
|
return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
2390
2477
|
e.preventDefault();
|
|
2391
2478
|
e.stopPropagation();
|
|
2392
|
-
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDelete, children: "Delete" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDuplicate, children: "Duplicate" }), canRunPull && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleRunPull, children: "Run (pull)" })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick:
|
|
2479
|
+
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDelete, children: "Delete" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDuplicate, children: "Duplicate" }), canRunPull && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleRunPull, children: "Run (pull)" })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), outs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), outs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: async () => {
|
|
2480
|
+
await doBake(h);
|
|
2481
|
+
onClose();
|
|
2482
|
+
}, children: ["Bake: ", h] }, h))), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" })] })), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleCopyId, children: "Copy Node ID" })] }));
|
|
2393
2483
|
}
|
|
2394
2484
|
|
|
2395
2485
|
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
|
|
@@ -2430,6 +2520,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
2430
2520
|
outputValues: n.data.outputValues,
|
|
2431
2521
|
status: n.data.status,
|
|
2432
2522
|
validation: n.data.validation,
|
|
2523
|
+
inputConnected: n.data.inputConnected,
|
|
2433
2524
|
},
|
|
2434
2525
|
});
|
|
2435
2526
|
return isEqual(pick(a), pick(b));
|
|
@@ -2619,10 +2710,21 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
2619
2710
|
setNodeMenuOpen(false);
|
|
2620
2711
|
}
|
|
2621
2712
|
};
|
|
2622
|
-
const addNodeAt = (typeId, pos) => {
|
|
2713
|
+
const addNodeAt = React.useCallback((typeId, pos) => {
|
|
2623
2714
|
wb.addNode({ typeId, position: pos });
|
|
2624
|
-
};
|
|
2625
|
-
|
|
2715
|
+
}, [wb]);
|
|
2716
|
+
React.useCallback((inst) => {
|
|
2717
|
+
rfInstanceRef.current = inst;
|
|
2718
|
+
}, []);
|
|
2719
|
+
const onCloseMenu = React.useCallback(() => {
|
|
2720
|
+
setMenuOpen(false);
|
|
2721
|
+
}, []);
|
|
2722
|
+
const onCloseNodeMenu = React.useCallback(() => {
|
|
2723
|
+
setNodeMenuOpen(false);
|
|
2724
|
+
}, []);
|
|
2725
|
+
return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsx(react.ReactFlowProvider, { children: jsxRuntime.jsxs(react.ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, selectionOnDrag: true, onInit: (inst) => {
|
|
2726
|
+
rfInstanceRef.current = inst;
|
|
2727
|
+
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", fitView: true, children: [jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 }), jsxRuntime.jsx(react.MiniMap, {}), jsxRuntime.jsx(react.Controls, {}), jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, onAdd: addNodeAt, onClose: onCloseMenu }), !!nodeAtMenu && (jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: onCloseNodeMenu }))] }) }) }));
|
|
2626
2728
|
});
|
|
2627
2729
|
|
|
2628
2730
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -2677,6 +2779,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2677
2779
|
const lastAutoLaunched = React.useRef(undefined);
|
|
2678
2780
|
const autoLayoutRan = React.useRef(false);
|
|
2679
2781
|
const canvasRef = React.useRef(null);
|
|
2782
|
+
const uploadInputRef = React.useRef(null);
|
|
2680
2783
|
// Expose init callback with setInitialGraph helper
|
|
2681
2784
|
const initCalled = React.useRef(false);
|
|
2682
2785
|
React.useEffect(() => {
|
|
@@ -2781,6 +2884,66 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2781
2884
|
alert(String(err?.message ?? err));
|
|
2782
2885
|
}
|
|
2783
2886
|
}, [wb, runner]);
|
|
2887
|
+
const onUploadPicked = React.useCallback(async (e) => {
|
|
2888
|
+
try {
|
|
2889
|
+
const file = e.target.files?.[0];
|
|
2890
|
+
if (!file)
|
|
2891
|
+
return;
|
|
2892
|
+
const text = await file.text();
|
|
2893
|
+
const parsed = JSON.parse(text);
|
|
2894
|
+
// Support both Graph and Snapshot payloads
|
|
2895
|
+
const isSnapshot = parsed &&
|
|
2896
|
+
typeof parsed === "object" &&
|
|
2897
|
+
(parsed.def || parsed.inputs || parsed.outputs || parsed.environment);
|
|
2898
|
+
if (isSnapshot) {
|
|
2899
|
+
const def = parsed.def;
|
|
2900
|
+
const positions = parsed.positions || {};
|
|
2901
|
+
const environment = parsed.environment || {};
|
|
2902
|
+
const inputs = parsed.inputs || {};
|
|
2903
|
+
if (def) {
|
|
2904
|
+
// Remote exact restore path
|
|
2905
|
+
await runner.applySnapshotFull({
|
|
2906
|
+
def,
|
|
2907
|
+
environment,
|
|
2908
|
+
inputs,
|
|
2909
|
+
outputs: parsed.outputs || {},
|
|
2910
|
+
});
|
|
2911
|
+
await wb.load(def);
|
|
2912
|
+
if (positions && typeof positions === "object")
|
|
2913
|
+
wb.setPositions(positions);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
else {
|
|
2917
|
+
const def = parsed?.def ?? parsed;
|
|
2918
|
+
const inputs = parsed?.inputs ?? {};
|
|
2919
|
+
await wb.load(def);
|
|
2920
|
+
try {
|
|
2921
|
+
runner.build(wb.export());
|
|
2922
|
+
}
|
|
2923
|
+
catch { }
|
|
2924
|
+
if (inputs && typeof inputs === "object") {
|
|
2925
|
+
for (const [nodeId, map] of Object.entries(inputs)) {
|
|
2926
|
+
try {
|
|
2927
|
+
runner.setInputs(nodeId, map);
|
|
2928
|
+
}
|
|
2929
|
+
catch { }
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
runAutoLayout();
|
|
2934
|
+
}
|
|
2935
|
+
catch (err) {
|
|
2936
|
+
alert(String(err?.message ?? err));
|
|
2937
|
+
}
|
|
2938
|
+
finally {
|
|
2939
|
+
// reset input so same file can be picked again
|
|
2940
|
+
if (uploadInputRef.current)
|
|
2941
|
+
uploadInputRef.current.value = "";
|
|
2942
|
+
}
|
|
2943
|
+
}, [wb, runner, runAutoLayout]);
|
|
2944
|
+
const triggerUpload = React.useCallback(() => {
|
|
2945
|
+
uploadInputRef.current?.click();
|
|
2946
|
+
}, []);
|
|
2784
2947
|
const hydrateFromBackend = React.useCallback(async (kind, base) => {
|
|
2785
2948
|
try {
|
|
2786
2949
|
const transport = kind === "remote-http"
|
|
@@ -3078,7 +3241,35 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
3078
3241
|
catch (err) {
|
|
3079
3242
|
alert(String(err?.message ?? err));
|
|
3080
3243
|
}
|
|
3081
|
-
}, disabled: !engine, 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.
|
|
3244
|
+
}, disabled: !engine, 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 () => {
|
|
3245
|
+
try {
|
|
3246
|
+
const def = wb.export();
|
|
3247
|
+
const positions = wb.getPositions();
|
|
3248
|
+
const snapshot = await runner.snapshotFull();
|
|
3249
|
+
const payload = {
|
|
3250
|
+
...snapshot,
|
|
3251
|
+
def,
|
|
3252
|
+
positions,
|
|
3253
|
+
schemaVersion: 1,
|
|
3254
|
+
};
|
|
3255
|
+
const pretty = JSON.stringify(payload, null, 2);
|
|
3256
|
+
const blob = new Blob([pretty], { type: "application/json" });
|
|
3257
|
+
const url = URL.createObjectURL(blob);
|
|
3258
|
+
const a = document.createElement("a");
|
|
3259
|
+
const d = new Date();
|
|
3260
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
3261
|
+
const ts = `${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`;
|
|
3262
|
+
a.href = url;
|
|
3263
|
+
a.download = `spark-snapshot-${ts}.json`;
|
|
3264
|
+
document.body.appendChild(a);
|
|
3265
|
+
a.click();
|
|
3266
|
+
a.remove();
|
|
3267
|
+
URL.revokeObjectURL(url);
|
|
3268
|
+
}
|
|
3269
|
+
catch (err) {
|
|
3270
|
+
alert(String(err?.message ?? err));
|
|
3271
|
+
}
|
|
3272
|
+
}, 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 })] })] }));
|
|
3082
3273
|
}
|
|
3083
3274
|
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, }) {
|
|
3084
3275
|
const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
|