@bian-womp/spark-workbench 0.2.5 → 0.2.7

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
@@ -6,8 +6,8 @@ var React = require('react');
6
6
  var react = require('@xyflow/react');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var react$1 = require('@phosphor-icons/react');
9
- var isEqual = require('lodash/isEqual');
10
9
  var cx = require('classnames');
10
+ var isEqual = require('lodash/isEqual');
11
11
 
12
12
  class DefaultUIExtensionRegistry {
13
13
  constructor() {
@@ -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;
@@ -1989,7 +1981,7 @@ function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray
1989
1981
  const y = placed?.y;
1990
1982
  const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ??
1991
1983
  outputClassName;
1992
- return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: h.id, type: "source", position: position, isConnectable: isConnectable, className: cls, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + " right-2", style: {
1984
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: h.id, type: "source", position: position, isConnectable: isConnectable, className: `${cls} wb-nodrag wb-nowheel`, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + " right-2", style: {
1993
1985
  top: (y ?? 0) - 8,
1994
1986
  left: "50%",
1995
1987
  textAlign: "right",
@@ -2002,7 +1994,7 @@ function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray
2002
1994
 
2003
1995
  const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
2004
1996
  const updateNodeInternals = react.useUpdateNodeInternals();
2005
- const { typeId, showValues, inputValues, outputValues, toString } = data;
1997
+ const { typeId, showValues } = data;
2006
1998
  const inputEntries = data.inputHandles ?? [];
2007
1999
  const outputEntries = data.outputHandles ?? [];
2008
2000
  React.useEffect(() => {
@@ -2020,14 +2012,11 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
2020
2012
  outputs: [],
2021
2013
  issues: [],
2022
2014
  };
2023
- const hasError = !!status.lastError;
2024
- const isRunning = !!status.activeRuns;
2025
2015
  const containerBorder = getNodeBorderClassNames({
2026
2016
  selected,
2027
2017
  status,
2028
2018
  validation,
2029
2019
  });
2030
- const pct = Math.round(Math.max(0, Math.min(1, Number(status.progress) || 0)) * 100);
2031
2020
  return (jsxRuntime.jsxs("div", { className: cx("rounded-lg bg-white/70 !dark:bg-stone-900", containerBorder), style: {
2032
2021
  position: "relative",
2033
2022
  minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
@@ -2035,11 +2024,24 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
2035
2024
  }, children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-center px-2 border-b border-solid border-gray-500 dark:border-gray-400 text-gray-600 dark:text-gray-300", style: {
2036
2025
  maxHeight: NODE_HEADER_HEIGHT_PX,
2037
2026
  minHeight: NODE_HEADER_HEIGHT_PX,
2038
- }, children: [jsxRuntime.jsx("strong", { className: "flex-1 h-full text-sm", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, children: typeId }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [hasError && (jsxRuntime.jsx("span", { title: String(status.lastError?.message ?? status.lastError), children: jsxRuntime.jsx(react$1.XCircleIcon, { size: 12, weight: "fill", className: "text-red-500" }) })), validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
2027
+ }, children: [jsxRuntime.jsx("strong", { className: "flex-1 h-full text-sm", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, children: typeId }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
2039
2028
  ? "error"
2040
2029
  : "warning", size: 12, className: "w-3 h-3", title: validation.issues
2041
2030
  .map((v) => `${v.code}: ${v.message}`)
2042
- .join("; ") })), jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }), 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 }) => {
2031
+ .join("; ") })), jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }), jsxRuntime.jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
2032
+ });
2033
+ DefaultNode.displayName = "DefaultNode";
2034
+ function DefaultNodeContent({ data, isConnectable, }) {
2035
+ const { showValues, inputValues, outputValues, toString } = data;
2036
+ const inputEntries = data.inputHandles ?? [];
2037
+ const outputEntries = data.outputHandles ?? [];
2038
+ const status = data.status ?? { activeRuns: 0 };
2039
+ const validation = data.validation ?? {
2040
+ inputs: [],
2041
+ outputs: []};
2042
+ const isRunning = !!status.activeRuns;
2043
+ const pct = Math.round(Math.max(0, Math.min(1, Number(status.progress) || 0)) * 100);
2044
+ 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 }) => {
2043
2045
  const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === id);
2044
2046
  const hasAny = vIssues.length > 0;
2045
2047
  const hasErr = vIssues.some((v) => v.level === "error");
@@ -2055,7 +2057,6 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
2055
2057
  const title = vIssues
2056
2058
  .map((v) => `${v.code}: ${v.message}`)
2057
2059
  .join("; ");
2058
- // Compose label with truncated value to prevent layout growth
2059
2060
  const valueText = (() => {
2060
2061
  if (!showValues)
2061
2062
  return undefined;
@@ -2069,8 +2070,7 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
2069
2070
  })();
2070
2071
  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: id })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: id }), 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 }))] }));
2071
2072
  } })] }));
2072
- });
2073
- DefaultNode.displayName = "DefaultNode";
2073
+ }
2074
2074
 
2075
2075
  function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
2076
2076
  const { registry } = useWorkbenchContext();
@@ -2459,7 +2459,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
2459
2459
  const addNodeAt = (typeId, pos) => {
2460
2460
  wb.addNode({ typeId, position: pos });
2461
2461
  };
2462
- 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) })] }) }) }));
2462
+ 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) })] }) }) }));
2463
2463
  });
2464
2464
 
2465
2465
  function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
@@ -2945,6 +2945,8 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
2945
2945
 
2946
2946
  exports.AbstractWorkbench = AbstractWorkbench;
2947
2947
  exports.CLIWorkbench = CLIWorkbench;
2948
+ exports.DefaultNode = DefaultNode;
2949
+ exports.DefaultNodeContent = DefaultNodeContent;
2948
2950
  exports.DefaultUIExtensionRegistry = DefaultUIExtensionRegistry;
2949
2951
  exports.InMemoryWorkbench = InMemoryWorkbench;
2950
2952
  exports.Inspector = Inspector;