@bian-womp/spark-workbench 0.1.14 → 0.1.16
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 +100 -4
- package/lib/cjs/index.cjs.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/WorkbenchStudio.d.ts +3 -3
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.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 -0
- package/lib/cjs/src/runtime/GraphRunner.d.ts +4 -2
- package/lib/cjs/src/runtime/GraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +101 -5
- package/lib/esm/index.js.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/WorkbenchStudio.d.ts +3 -3
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.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 -0
- package/lib/esm/src/runtime/GraphRunner.d.ts +4 -2
- package/lib/esm/src/runtime/GraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -439,6 +439,7 @@ class GraphRunner {
|
|
|
439
439
|
rc.valueCache.set(`${e.nodeId}.${e.handle}`, {
|
|
440
440
|
io: e.io,
|
|
441
441
|
value: e.value,
|
|
442
|
+
runtimeTypeId: e.runtimeTypeId,
|
|
442
443
|
});
|
|
443
444
|
this.emit("value", e);
|
|
444
445
|
});
|
|
@@ -808,12 +809,31 @@ function useQueryParamString(key, defaultValue) {
|
|
|
808
809
|
return [val, set];
|
|
809
810
|
}
|
|
810
811
|
|
|
812
|
+
function resolveOutputDisplay(raw, declared) {
|
|
813
|
+
if (sparkGraph.isTypedOutput(raw)) {
|
|
814
|
+
return { typeId: String(raw.__spark_type), value: raw.__spark_value };
|
|
815
|
+
}
|
|
816
|
+
let typeId = undefined;
|
|
817
|
+
if (Array.isArray(declared)) {
|
|
818
|
+
typeId = declared.length === 1 ? declared[0] : undefined;
|
|
819
|
+
}
|
|
820
|
+
else if (typeof declared === "string") {
|
|
821
|
+
typeId = declared.includes("|") ? undefined : declared;
|
|
822
|
+
}
|
|
823
|
+
return { typeId, value: raw };
|
|
824
|
+
}
|
|
825
|
+
function formatDeclaredTypeSignature(declared) {
|
|
826
|
+
if (Array.isArray(declared))
|
|
827
|
+
return declared.join(" | ");
|
|
828
|
+
return declared ?? "";
|
|
829
|
+
}
|
|
830
|
+
|
|
811
831
|
function toReactFlow(def, positions, registry, opts) {
|
|
812
832
|
const nodeHandleMap = {};
|
|
813
833
|
const nodes = def.nodes.map((n) => {
|
|
814
834
|
const desc = registry.nodes.get(n.typeId);
|
|
815
835
|
const inputHandles = Object.entries(desc?.inputs ?? {}).map(([id, typeId]) => ({ id, typeId }));
|
|
816
|
-
const outputHandles = Object.entries(desc?.outputs ?? {}).map(([id, typeId]) => ({ id, typeId }));
|
|
836
|
+
const outputHandles = Object.entries(desc?.outputs ?? {}).map(([id, typeId]) => ({ id, typeId: formatDeclaredTypeSignature(typeId) }));
|
|
817
837
|
nodeHandleMap[n.nodeId] = {
|
|
818
838
|
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
819
839
|
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
@@ -913,6 +933,41 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
913
933
|
const [edgeStatus, setEdgeStatus] = React.useState({});
|
|
914
934
|
const [events, setEvents] = React.useState([]);
|
|
915
935
|
const clearEvents = React.useCallback(() => setEvents([]), []);
|
|
936
|
+
// Fallback progress animation: drive progress to 100% over ~2 minutes
|
|
937
|
+
const FALLBACK_TOTAL_MS = 2 * 60 * 1000;
|
|
938
|
+
const [fallbackStarts, setFallbackStarts] = React.useState({});
|
|
939
|
+
// Track runs that emitted an error so we can keep progress on completion
|
|
940
|
+
const [errorRuns, setErrorRuns] = React.useState({});
|
|
941
|
+
// Periodically advance fallback progress for running nodes without explicit progress
|
|
942
|
+
React.useEffect(() => {
|
|
943
|
+
const interval = setInterval(() => {
|
|
944
|
+
setNodeStatus((prev) => {
|
|
945
|
+
let changed = false;
|
|
946
|
+
const next = { ...prev };
|
|
947
|
+
const now = Date.now();
|
|
948
|
+
for (const id of Object.keys(prev)) {
|
|
949
|
+
const st = prev[id];
|
|
950
|
+
if (!st)
|
|
951
|
+
continue;
|
|
952
|
+
const runs = st.activeRuns ?? 0;
|
|
953
|
+
const startAt = fallbackStarts[id];
|
|
954
|
+
if (runs > 0 && startAt) {
|
|
955
|
+
const cur = Math.max(0, Math.min(1, Number(st.progress) || 0));
|
|
956
|
+
const elapsed = Math.max(0, now - startAt);
|
|
957
|
+
// Approach 100% over the target window, but cap below 1 until done
|
|
958
|
+
const target = Math.max(0, Math.min(0.99, elapsed / FALLBACK_TOTAL_MS));
|
|
959
|
+
const merged = Math.max(cur, target);
|
|
960
|
+
if (merged > cur + 0.005 && merged <= 1) {
|
|
961
|
+
next[id] = { ...st, progress: merged };
|
|
962
|
+
changed = true;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return changed ? next : prev;
|
|
967
|
+
});
|
|
968
|
+
}, 200);
|
|
969
|
+
return () => clearInterval(interval);
|
|
970
|
+
}, [fallbackStarts]);
|
|
916
971
|
// Validation
|
|
917
972
|
const [validation, setValidation] = React.useState(undefined);
|
|
918
973
|
// Selection (mirror workbench selectionChanged)
|
|
@@ -1025,6 +1080,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1025
1080
|
lastError: nodeError.err,
|
|
1026
1081
|
},
|
|
1027
1082
|
}));
|
|
1083
|
+
// Mark this runId as errored
|
|
1084
|
+
if (nodeError.runId) {
|
|
1085
|
+
setErrorRuns((prev) => ({
|
|
1086
|
+
...prev,
|
|
1087
|
+
[nodeId]: { ...(prev[nodeId] || {}), [nodeError.runId]: true },
|
|
1088
|
+
}));
|
|
1089
|
+
}
|
|
1028
1090
|
}
|
|
1029
1091
|
return add("runner", "error")(e);
|
|
1030
1092
|
});
|
|
@@ -1057,6 +1119,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1057
1119
|
},
|
|
1058
1120
|
};
|
|
1059
1121
|
});
|
|
1122
|
+
// Start fallback animation window
|
|
1123
|
+
setFallbackStarts((prev) => ({ ...prev, [id]: Date.now() }));
|
|
1060
1124
|
}
|
|
1061
1125
|
else if (s.kind === "node-progress") {
|
|
1062
1126
|
const id = s.nodeId;
|
|
@@ -1070,16 +1134,42 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1070
1134
|
}
|
|
1071
1135
|
else if (s.kind === "node-done") {
|
|
1072
1136
|
const id = s.nodeId;
|
|
1137
|
+
const runId = s.runId;
|
|
1073
1138
|
setNodeStatus((prev) => {
|
|
1074
1139
|
const current = prev[id]?.activeRuns ?? 0;
|
|
1140
|
+
const nextActive = current - 1;
|
|
1141
|
+
const hadError = !!(runId && errorRuns[id]?.[runId]);
|
|
1142
|
+
const keepProgress = hadError || nextActive > 0;
|
|
1075
1143
|
return {
|
|
1076
1144
|
...prev,
|
|
1077
1145
|
[id]: {
|
|
1078
1146
|
...prev[id],
|
|
1079
|
-
activeRuns:
|
|
1147
|
+
activeRuns: nextActive,
|
|
1148
|
+
progress: keepProgress ? prev[id]?.progress : 0,
|
|
1080
1149
|
},
|
|
1081
1150
|
};
|
|
1082
1151
|
});
|
|
1152
|
+
// Clear fallback start timestamp if no more active runs
|
|
1153
|
+
setFallbackStarts((prev) => {
|
|
1154
|
+
prev[id];
|
|
1155
|
+
const nextPrev = { ...prev };
|
|
1156
|
+
// If we don't know nextActive here, conservatively clear to stop animation
|
|
1157
|
+
delete nextPrev[id];
|
|
1158
|
+
return nextPrev;
|
|
1159
|
+
});
|
|
1160
|
+
// Clear error flag for this runId
|
|
1161
|
+
if (runId) {
|
|
1162
|
+
setErrorRuns((prev) => {
|
|
1163
|
+
const nodeMap = { ...(prev[id] || {}) };
|
|
1164
|
+
delete nodeMap[runId];
|
|
1165
|
+
const next = { ...prev };
|
|
1166
|
+
if (Object.keys(nodeMap).length === 0)
|
|
1167
|
+
delete next[id];
|
|
1168
|
+
else
|
|
1169
|
+
next[id] = nodeMap;
|
|
1170
|
+
return next;
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1083
1173
|
}
|
|
1084
1174
|
else if (s.kind === "edge-start") {
|
|
1085
1175
|
const id = s.edgeId;
|
|
@@ -1447,7 +1537,10 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1447
1537
|
if (e.key === "Escape")
|
|
1448
1538
|
revert();
|
|
1449
1539
|
}, ...commonProps }))] }, h));
|
|
1450
|
-
}))] }), 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.jsx("label", { className: "w-20", children: h }), jsxRuntime.jsx("div", { className: "flex-1", children:
|
|
1540
|
+
}))] }), 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.jsx("label", { className: "w-20", children: h }), jsxRuntime.jsx("div", { className: "flex-1", children: (() => {
|
|
1541
|
+
const { typeId, value } = resolveOutputDisplay(nodeOutputs[h], selectedDesc?.outputs?.[h]);
|
|
1542
|
+
return toElement(typeId, value);
|
|
1543
|
+
})() }), (() => {
|
|
1451
1544
|
const outIssues = selectedNodeHandleValidation.outputs.filter((m) => m.handle === h);
|
|
1452
1545
|
if (outIssues.length === 0)
|
|
1453
1546
|
return null;
|
|
@@ -1514,7 +1607,10 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
|
|
|
1514
1607
|
const title = vIssues
|
|
1515
1608
|
.map((v) => `${v.code}: ${v.message}`)
|
|
1516
1609
|
.join("; ");
|
|
1517
|
-
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(ReactFlow.Handle, { id: entry.id, type: "source", position: ReactFlow.Position.Right, isConnectable: isConnectable, className: cx("!w-3 !h-3 !bg-white !dark:bg-stone-900 !border-gray-500 dark:!border-gray-400 !rounded-none", hasAny && (hasErr ? "!border-red-500" : "!border-amber-500")), style: { right: -5, top: topFor(i) } }), jsxRuntime.jsxs("div", { className: "absolute right-2 text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", style: { top: topFor(i) - 8, textAlign: "right" }, title: `${entry.id}: ${entry.typeId}`, children: [entry.id, hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "ml-1", title: title })), showValues && (jsxRuntime.jsx("span", { className: "ml-1 opacity-60", children:
|
|
1610
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(ReactFlow.Handle, { id: entry.id, type: "source", position: ReactFlow.Position.Right, isConnectable: isConnectable, className: cx("!w-3 !h-3 !bg-white !dark:bg-stone-900 !border-gray-500 dark:!border-gray-400 !rounded-none", hasAny && (hasErr ? "!border-red-500" : "!border-amber-500")), style: { right: -5, top: topFor(i) } }), jsxRuntime.jsxs("div", { className: "absolute right-2 text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", style: { top: topFor(i) - 8, textAlign: "right" }, title: `${entry.id}: ${entry.typeId}`, children: [entry.id, hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "ml-1", title: title })), showValues && (jsxRuntime.jsx("span", { className: "ml-1 opacity-60", children: (() => {
|
|
1611
|
+
const { typeId, value } = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
|
|
1612
|
+
return toString(typeId, value);
|
|
1613
|
+
})() }))] })] }, `out-${entry.id}`));
|
|
1518
1614
|
})] }));
|
|
1519
1615
|
});
|
|
1520
1616
|
DefaultNode.displayName = "DefaultNode";
|