@bian-womp/spark-workbench 0.2.27 → 0.2.28
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 +73 -27
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/DebugEvents.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/value.d.ts +6 -0
- package/lib/cjs/src/misc/value.d.ts.map +1 -1
- package/lib/cjs/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/cjs/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +74 -29
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/DebugEvents.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/value.d.ts +6 -0
- package/lib/esm/src/misc/value.d.ts.map +1 -1
- package/lib/esm/src/runtime/LocalGraphRunner.d.ts.map +1 -1
- package/lib/esm/src/runtime/RemoteGraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -524,7 +524,16 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
524
524
|
const runtimeInputs = this.runtime
|
|
525
525
|
? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
|
|
526
526
|
: {};
|
|
527
|
-
|
|
527
|
+
// Build inbound handle set for this node from current def
|
|
528
|
+
const inbound = new Set(def.edges
|
|
529
|
+
.filter((e) => e.target.nodeId === n.nodeId)
|
|
530
|
+
.map((e) => e.target.handle));
|
|
531
|
+
// Merge staged only for non-inbound handles so UI reflects runtime values for wired inputs
|
|
532
|
+
const merged = { ...runtimeInputs };
|
|
533
|
+
for (const [h, v] of Object.entries(staged)) {
|
|
534
|
+
if (!inbound.has(h))
|
|
535
|
+
merged[h] = v;
|
|
536
|
+
}
|
|
528
537
|
if (Object.keys(merged).length > 0)
|
|
529
538
|
out[n.nodeId] = merged;
|
|
530
539
|
}
|
|
@@ -795,12 +804,21 @@ class RemoteGraphRunner extends AbstractGraphRunner {
|
|
|
795
804
|
const desc = this.registry.nodes.get(n.typeId);
|
|
796
805
|
const handles = Object.keys(resolved ?? desc?.inputs ?? {});
|
|
797
806
|
const cur = {};
|
|
807
|
+
// Build inbound handle set for this node to honor wiring precedence
|
|
808
|
+
const inbound = new Set(def.edges
|
|
809
|
+
.filter((e) => e.target.nodeId === n.nodeId)
|
|
810
|
+
.map((e) => e.target.handle));
|
|
798
811
|
for (const h of handles) {
|
|
799
812
|
const rec = cache.get(`${n.nodeId}.${h}`);
|
|
800
813
|
if (rec && rec.io === "input")
|
|
801
814
|
cur[h] = rec.value;
|
|
802
815
|
}
|
|
803
|
-
|
|
816
|
+
// Merge staged only for non-inbound handles so UI doesn't override runtime values
|
|
817
|
+
const merged = { ...cur };
|
|
818
|
+
for (const [h, v] of Object.entries(staged)) {
|
|
819
|
+
if (!inbound.has(h))
|
|
820
|
+
merged[h] = v;
|
|
821
|
+
}
|
|
804
822
|
if (Object.keys(merged).length > 0)
|
|
805
823
|
out[n.nodeId] = merged;
|
|
806
824
|
}
|
|
@@ -1264,6 +1282,23 @@ function formatDeclaredTypeSignature(declared) {
|
|
|
1264
1282
|
return declared.join(" | ");
|
|
1265
1283
|
return declared ?? "";
|
|
1266
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Formats a handle ID for display in the UI.
|
|
1287
|
+
* For handles with format "prefix:middle:suffix:extra" (4 parts), displays only the middle part.
|
|
1288
|
+
* Otherwise returns the handle ID as-is.
|
|
1289
|
+
*/
|
|
1290
|
+
function prettyHandle(id) {
|
|
1291
|
+
try {
|
|
1292
|
+
const parts = String(id).split(":");
|
|
1293
|
+
// If there are exactly 3 colons (4 parts), display only the second part
|
|
1294
|
+
if (parts.length === 4)
|
|
1295
|
+
return parts[1] || id;
|
|
1296
|
+
return id;
|
|
1297
|
+
}
|
|
1298
|
+
catch {
|
|
1299
|
+
return id;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1267
1302
|
// Pre-format common structures for display; return undefined to defer to caller
|
|
1268
1303
|
function preformatValueForDisplay(typeId, value, registry) {
|
|
1269
1304
|
if (value === undefined || value === null)
|
|
@@ -2230,6 +2265,7 @@ function IssueBadge({ level, title, size = 12, className, }) {
|
|
|
2230
2265
|
function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWorkbenchChange, }) {
|
|
2231
2266
|
const { events, clearEvents } = useWorkbenchContext();
|
|
2232
2267
|
const scrollRef = React.useRef(null);
|
|
2268
|
+
const [copied, setCopied] = React.useState(false);
|
|
2233
2269
|
const rows = React.useMemo(() => {
|
|
2234
2270
|
const filtered = hideWorkbench
|
|
2235
2271
|
? events.filter((e) => e.source !== "workbench")
|
|
@@ -2253,7 +2289,25 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
2253
2289
|
return String(v);
|
|
2254
2290
|
}
|
|
2255
2291
|
};
|
|
2256
|
-
|
|
2292
|
+
const handleCopyLogs = async () => {
|
|
2293
|
+
try {
|
|
2294
|
+
const formattedEvents = rows.map((ev) => ({
|
|
2295
|
+
no: ev.no,
|
|
2296
|
+
at: ev.at,
|
|
2297
|
+
source: ev.source,
|
|
2298
|
+
type: ev.type,
|
|
2299
|
+
payload: summarizeDeep(ev.payload),
|
|
2300
|
+
}));
|
|
2301
|
+
const jsonString = JSON.stringify(formattedEvents, null, 2);
|
|
2302
|
+
await navigator.clipboard.writeText(jsonString);
|
|
2303
|
+
setCopied(true);
|
|
2304
|
+
setTimeout(() => setCopied(false), 2000);
|
|
2305
|
+
}
|
|
2306
|
+
catch (err) {
|
|
2307
|
+
console.error("Failed to copy logs:", err);
|
|
2308
|
+
}
|
|
2309
|
+
};
|
|
2310
|
+
return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full min-h-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: "Events" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: hideWorkbench, onChange: (e) => onHideWorkbenchChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Hide workbench" })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: autoScroll, onChange: (e) => onAutoScrollChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Auto scroll" })] }), jsxRuntime.jsx("button", { onClick: handleCopyLogs, className: "p-2 border border-gray-300 rounded flex items-center justify-center", title: copied ? "Copied!" : "Copy logs as formatted JSON", children: jsxRuntime.jsx(react$1.CopyIcon, { size: 14 }) }), jsxRuntime.jsx("button", { onClick: clearEvents, className: "p-2 border border-gray-300 rounded flex items-center justify-center", title: "Clear all events", children: jsxRuntime.jsx(react$1.TrashIcon, { size: 14 }) })] })] }), jsxRuntime.jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto text-[11px] leading-4 divide-y divide-gray-200", children: rows.map((ev) => (jsxRuntime.jsxs("div", { className: "opacity-85 odd:bg-gray-50 px-2 py-1", children: [jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [jsxRuntime.jsx("span", { className: "w-12 shrink-0 text-right text-gray-500 select-none", children: ev.no }), jsxRuntime.jsxs("span", { className: "text-gray-500", children: [new Date(ev.at).toLocaleTimeString(), " \u00B7 ", ev.source, ":", ev.type] })] }), jsxRuntime.jsx("pre", { className: "m-0 whitespace-pre-wrap ml-12", children: renderPayload(ev.payload) })] }, `${ev.at}:${ev.no}`))) })] }));
|
|
2257
2311
|
}
|
|
2258
2312
|
|
|
2259
2313
|
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, contextPanel, setInput, }) {
|
|
@@ -2275,13 +2329,17 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2275
2329
|
const globalValidationIssues = validationGlobal;
|
|
2276
2330
|
const selectedNode = def.nodes.find((n) => n.nodeId === selectedNodeId);
|
|
2277
2331
|
const selectedEdge = def.edges.find((e) => e.id === selectedEdgeId);
|
|
2278
|
-
|
|
2332
|
+
selectedNode
|
|
2279
2333
|
? registry.nodes.get(selectedNode.typeId)
|
|
2280
2334
|
: undefined;
|
|
2281
|
-
|
|
2282
|
-
|
|
2335
|
+
// Use computeEffectiveHandles to merge registry defaults with dynamically resolved handles
|
|
2336
|
+
const effectiveHandles = selectedNode
|
|
2337
|
+
? computeEffectiveHandles(selectedNode, registry)
|
|
2338
|
+
: { inputs: {}, outputs: {}};
|
|
2339
|
+
const inputHandles = Object.entries(effectiveHandles.inputs)
|
|
2340
|
+
.filter(([k]) => !sparkGraph.isInputPrivate(effectiveHandles.inputs, k))
|
|
2283
2341
|
.map(([k]) => k);
|
|
2284
|
-
const outputHandles = Object.keys(
|
|
2342
|
+
const outputHandles = Object.keys(effectiveHandles.outputs);
|
|
2285
2343
|
const nodeInputs = selectedNodeId ? inputsMap[selectedNodeId] ?? {} : {};
|
|
2286
2344
|
const nodeOutputs = selectedNodeId ? outputsMap[selectedNodeId] ?? {} : {};
|
|
2287
2345
|
const selectedNodeStatus = selectedNodeId
|
|
@@ -2322,12 +2380,11 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2322
2380
|
}
|
|
2323
2381
|
return;
|
|
2324
2382
|
}
|
|
2325
|
-
const
|
|
2326
|
-
const handles = Object.keys(desc?.inputs ?? {});
|
|
2383
|
+
const handles = Object.keys(effectiveHandles.inputs);
|
|
2327
2384
|
const nextDrafts = { ...drafts };
|
|
2328
2385
|
const nextOriginals = { ...originals };
|
|
2329
2386
|
for (const h of handles) {
|
|
2330
|
-
const typeId = sparkGraph.getInputTypeId(
|
|
2387
|
+
const typeId = sparkGraph.getInputTypeId(effectiveHandles.inputs, h);
|
|
2331
2388
|
const current = nodeInputs[h];
|
|
2332
2389
|
const display = safeToString(typeId, current);
|
|
2333
2390
|
const wasOriginal = originals[h];
|
|
@@ -2343,7 +2400,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2343
2400
|
setDrafts(nextDrafts);
|
|
2344
2401
|
if (!shallowEqual(originals, nextOriginals))
|
|
2345
2402
|
setOriginals(nextOriginals);
|
|
2346
|
-
}, [selectedNodeId,
|
|
2403
|
+
}, [selectedNodeId, selectedNode, registry, valuesTick]);
|
|
2347
2404
|
const widthClass = debug ? "w-[480px]" : "w-[320px]";
|
|
2348
2405
|
const { wb } = useWorkbenchContext();
|
|
2349
2406
|
const deleteEdgeById = (edgeId) => {
|
|
@@ -2369,7 +2426,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2369
2426
|
deleteEdgeById(selectedEdge.id);
|
|
2370
2427
|
}, 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 ??
|
|
2371
2428
|
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) => {
|
|
2372
|
-
const typeId = sparkGraph.getInputTypeId(
|
|
2429
|
+
const typeId = sparkGraph.getInputTypeId(effectiveHandles.inputs, h);
|
|
2373
2430
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
2374
2431
|
e.target.handle === h);
|
|
2375
2432
|
const commonProps = {
|
|
@@ -2397,7 +2454,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2397
2454
|
const title = inIssues
|
|
2398
2455
|
.map((v) => `${v.code}: ${v.message}`)
|
|
2399
2456
|
.join("; ");
|
|
2400
|
-
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: 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
|
|
2457
|
+
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
|
|
2401
2458
|
? String(current)
|
|
2402
2459
|
: "", onChange: (e) => {
|
|
2403
2460
|
const val = e.target.value;
|
|
@@ -2413,8 +2470,8 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2413
2470
|
if (e.key === "Escape")
|
|
2414
2471
|
revert();
|
|
2415
2472
|
}, ...commonProps }))] }, h));
|
|
2416
|
-
}))] }), 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: h }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: outputTypesMap[selectedNodeId]?.[h] ?? "" })] }), jsxRuntime.jsx("div", { className: "flex-1", children: (() => {
|
|
2417
|
-
const { typeId, value } = resolveOutputDisplay(nodeOutputs[h],
|
|
2473
|
+
}))] }), 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: (() => {
|
|
2474
|
+
const { typeId, value } = resolveOutputDisplay(nodeOutputs[h], effectiveHandles.outputs[h]);
|
|
2418
2475
|
return toElement(typeId, value);
|
|
2419
2476
|
})() }), (() => {
|
|
2420
2477
|
const outIssues = selectedNodeHandleValidation.outputs.filter((m) => m.handle === h);
|
|
@@ -2558,18 +2615,6 @@ function DefaultNodeHeader({ id, title, validation, right, showId, onInvalidate,
|
|
|
2558
2615
|
}
|
|
2559
2616
|
function DefaultNodeContent({ data, isConnectable, }) {
|
|
2560
2617
|
const { showValues, inputValues, outputValues, toString } = data;
|
|
2561
|
-
const prettyHandle = React.useCallback((id) => {
|
|
2562
|
-
try {
|
|
2563
|
-
const parts = String(id).split(":");
|
|
2564
|
-
// If there are exactly 3 colons (4 parts), display only the second part
|
|
2565
|
-
if (parts.length === 4)
|
|
2566
|
-
return parts[1] || id;
|
|
2567
|
-
return id;
|
|
2568
|
-
}
|
|
2569
|
-
catch {
|
|
2570
|
-
return id;
|
|
2571
|
-
}
|
|
2572
|
-
}, []);
|
|
2573
2618
|
const inputEntries = data.inputHandles ?? [];
|
|
2574
2619
|
const outputEntries = data.outputHandles ?? [];
|
|
2575
2620
|
const status = data.status ?? { activeRuns: 0 };
|
|
@@ -3741,6 +3786,7 @@ exports.formatDataUrlAsLabel = formatDataUrlAsLabel;
|
|
|
3741
3786
|
exports.formatDeclaredTypeSignature = formatDeclaredTypeSignature;
|
|
3742
3787
|
exports.getNodeBorderClassNames = getNodeBorderClassNames;
|
|
3743
3788
|
exports.preformatValueForDisplay = preformatValueForDisplay;
|
|
3789
|
+
exports.prettyHandle = prettyHandle;
|
|
3744
3790
|
exports.resolveOutputDisplay = resolveOutputDisplay;
|
|
3745
3791
|
exports.summarizeDeep = summarizeDeep;
|
|
3746
3792
|
exports.toReactFlow = toReactFlow;
|