@bian-womp/spark-workbench 0.2.6 → 0.2.8

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 CHANGED
@@ -1288,7 +1288,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
1288
1288
  const FALLBACK_TOTAL_MS = 2 * 60 * 1000;
1289
1289
  const [fallbackStarts, setFallbackStarts] = React.useState({});
1290
1290
  // Track runs that emitted an error so we can keep progress on completion
1291
- const [errorRuns, setErrorRuns] = React.useState({});
1291
+ const errorRunsRef = React.useRef({});
1292
1292
  // Periodically advance fallback progress for running nodes without explicit progress
1293
1293
  React.useEffect(() => {
1294
1294
  const interval = setInterval(() => {
@@ -1451,6 +1451,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
1451
1451
  }
1452
1452
  else if (nodeError.nodeId) {
1453
1453
  const nodeId = nodeError.nodeId;
1454
+ const runId = nodeError.runId;
1454
1455
  setNodeStatus((s) => ({
1455
1456
  ...s,
1456
1457
  [nodeId]: {
@@ -1459,11 +1460,11 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
1459
1460
  },
1460
1461
  }));
1461
1462
  // Mark this runId as errored
1462
- if (nodeError.runId) {
1463
- setErrorRuns((prev) => ({
1464
- ...prev,
1465
- [nodeId]: { ...(prev[nodeId] || {}), [nodeError.runId]: true },
1466
- }));
1463
+ if (runId) {
1464
+ errorRunsRef.current[nodeId] = {
1465
+ ...errorRunsRef.current[nodeId],
1466
+ [runId]: true,
1467
+ };
1467
1468
  }
1468
1469
  }
1469
1470
  return add("runner", "error")(e);
@@ -1516,38 +1517,29 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
1516
1517
  setNodeStatus((prev) => {
1517
1518
  const current = prev[id]?.activeRuns ?? 0;
1518
1519
  const nextActive = current - 1;
1519
- const hadError = !!(runId && errorRuns[id]?.[runId]);
1520
+ const hadError = !!(runId && errorRunsRef.current[id]?.[runId]);
1520
1521
  const keepProgress = hadError || nextActive > 0;
1522
+ // Clear error flag for this runId
1523
+ if (runId && errorRunsRef.current[id]?.[runId]) {
1524
+ delete errorRunsRef.current[id]?.[runId];
1525
+ }
1521
1526
  return {
1522
1527
  ...prev,
1523
1528
  [id]: {
1524
1529
  ...prev[id],
1525
1530
  activeRuns: nextActive,
1526
1531
  progress: keepProgress ? prev[id]?.progress : 0,
1532
+ lastError: hadError ? prev[id]?.lastError : undefined,
1527
1533
  },
1528
1534
  };
1529
1535
  });
1530
1536
  // Clear fallback start timestamp if no more active runs
1531
1537
  setFallbackStarts((prev) => {
1532
- prev[id];
1533
1538
  const nextPrev = { ...prev };
1534
1539
  // If we don't know nextActive here, conservatively clear to stop animation
1535
1540
  delete nextPrev[id];
1536
1541
  return nextPrev;
1537
1542
  });
1538
- // Clear error flag for this runId
1539
- if (runId) {
1540
- setErrorRuns((prev) => {
1541
- const nodeMap = { ...(prev[id] || {}) };
1542
- delete nodeMap[runId];
1543
- const next = { ...prev };
1544
- if (Object.keys(nodeMap).length === 0)
1545
- delete next[id];
1546
- else
1547
- next[id] = nodeMap;
1548
- return next;
1549
- });
1550
- }
1551
1543
  }
1552
1544
  else if (s.kind === "edge-start") {
1553
1545
  const id = s.edgeId;
@@ -1966,12 +1958,20 @@ function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray
1966
1958
  const byId = React.useMemo(() => {
1967
1959
  const m = new Map();
1968
1960
  for (const h of layout) {
1969
- m.set(h.id, { position: h.position, y: h.y, type: h.type });
1961
+ // Prefer namespaced key to disambiguate inputs/outputs that share id
1962
+ m.set(`${h.type}:${h.id}`, {
1963
+ position: h.position,
1964
+ y: h.y,
1965
+ type: h.type,
1966
+ });
1967
+ // Back-compat: also store by id-only if not already set
1968
+ if (!m.has(h.id))
1969
+ m.set(h.id, { position: h.position, y: h.y, type: h.type });
1970
1970
  }
1971
1971
  return m;
1972
1972
  }, [layout]);
1973
1973
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
1974
- const placed = byId.get(h.id);
1974
+ const placed = byId.get(`target:${h.id}`) ?? byId.get(h.id);
1975
1975
  const position = placed?.position ?? react.Position.Left;
1976
1976
  const y = placed?.y;
1977
1977
  const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ??
@@ -1984,7 +1984,7 @@ function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray
1984
1984
  textOverflow: "ellipsis",
1985
1985
  }, children: renderLabel({ kind: "input", id: h.id }) }))] }, h.id));
1986
1986
  }), (data.outputHandles ?? []).map((h) => {
1987
- const placed = byId.get(h.id);
1987
+ const placed = byId.get(`source:${h.id}`) ?? byId.get(h.id);
1988
1988
  const position = placed?.position ?? react.Position.Right;
1989
1989
  const y = placed?.y;
1990
1990
  const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ??
@@ -2467,7 +2467,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
2467
2467
  const addNodeAt = (typeId, pos) => {
2468
2468
  wb.addNode({ typeId, position: pos });
2469
2469
  };
2470
- 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, onlyRenderVisibleElements: true, selectionOnDrag: true, onInit: (inst) => (rfInstanceRef.current = inst), 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, { 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: () => setMenuOpen(false) }), jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: () => setNodeMenuOpen(false) })] }) }) }));
2470
+ 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) => (rfInstanceRef.current = inst), 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: () => setMenuOpen(false) }), jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: () => setNodeMenuOpen(false) })] }) }) }));
2471
2471
  });
2472
2472
 
2473
2473
  function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {