@bian-womp/spark-workbench 0.3.27 → 0.3.29

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
@@ -2760,11 +2760,11 @@ function toReactFlow(def, positions, sizes, registry, opts) {
2760
2760
  const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
2761
2761
  // Build a map of valid handles per node up-front and cache handle data
2762
2762
  const validHandleMap = {};
2763
- const nodeHandlesCache = {};
2764
2763
  for (const n of def.nodes) {
2765
- const handles = computeEffectiveHandles(n, registry);
2766
- nodeHandlesCache[n.nodeId] = handles;
2767
- const { inputs, outputs } = handles;
2764
+ const { inputs, outputs } = opts.handles[n.nodeId] || {
2765
+ inputs: {},
2766
+ outputs: {},
2767
+ };
2768
2768
  const inputOrder = Object.keys(inputs).filter((k) => !sparkGraph.isInputPrivate(inputs, k));
2769
2769
  const outputOrder = Object.keys(outputs);
2770
2770
  validHandleMap[n.nodeId] = {
@@ -2822,7 +2822,11 @@ function toReactFlow(def, positions, sizes, registry, opts) {
2822
2822
  });
2823
2823
  };
2824
2824
  const nodes = def.nodes.map((n) => {
2825
- const { inputs: inputSource, outputs: outputSource } = nodeHandlesCache[n.nodeId];
2825
+ const effectiveHandles = opts.handles[n.nodeId] || {
2826
+ inputs: {},
2827
+ outputs: {},
2828
+ };
2829
+ const { inputs: inputSource, outputs: outputSource } = effectiveHandles;
2826
2830
  const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
2827
2831
  const customSize = sizes?.[n.nodeId];
2828
2832
  const sizeOverrides = customSize
@@ -2855,6 +2859,7 @@ function toReactFlow(def, positions, sizes, registry, opts) {
2855
2859
  params: n.params,
2856
2860
  inputHandles,
2857
2861
  outputHandles,
2862
+ handles: opts.handles[n.nodeId],
2858
2863
  inputConnected: Object.fromEntries(inputHandles.map((h) => [
2859
2864
  h.id,
2860
2865
  !!connectedInputs[n.nodeId]?.has(h.id),
@@ -2866,7 +2871,10 @@ function toReactFlow(def, positions, sizes, registry, opts) {
2866
2871
  initialWidth: initialGeom.width,
2867
2872
  initialHeight: initialGeom.height,
2868
2873
  inputValues: opts.inputs?.[n.nodeId],
2869
- inputDefaults: opts.inputDefaults?.[n.nodeId],
2874
+ inputDefaults: {
2875
+ ...opts.handles?.[n.nodeId]?.inputDefaults,
2876
+ ...opts.inputDefaults?.[n.nodeId],
2877
+ },
2870
2878
  outputValues: opts.outputs?.[n.nodeId],
2871
2879
  handleMetadata,
2872
2880
  status: opts.nodeStatus?.[n.nodeId],
@@ -2916,8 +2924,8 @@ function toReactFlow(def, positions, sizes, registry, opts) {
2916
2924
  : undefined;
2917
2925
  const edgeTypeId = e.typeId || undefined;
2918
2926
  // Get handle type information from cached handles
2919
- const sourceHandles = nodeHandlesCache[e.source.nodeId];
2920
- const targetHandles = nodeHandlesCache[e.target.nodeId];
2927
+ const sourceHandles = opts.handles[e.source.nodeId];
2928
+ const targetHandles = opts.handles[e.target.nodeId];
2921
2929
  const sourceNode = def.nodes.find((n) => n.nodeId === e.source.nodeId);
2922
2930
  const targetNode = def.nodes.find((n) => n.nodeId === e.target.nodeId);
2923
2931
  const sourceHandleTypeId = sourceHandles?.outputs[e.source.handle]
@@ -3943,6 +3951,13 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
3943
3951
  offRunnerStatus();
3944
3952
  };
3945
3953
  }, [runner]);
3954
+ const handlesMap = React.useMemo(() => {
3955
+ const out = {};
3956
+ for (const n of wb.def.nodes) {
3957
+ out[n.nodeId] = computeEffectiveHandles(n, wb.registry);
3958
+ }
3959
+ return out;
3960
+ }, [wb, wb.def, graphTick, wb.registry, registryVersion]);
3946
3961
  // Def and IO values
3947
3962
  const inputsMap = React.useMemo(() => runner.getInputs(wb.def), [runner, wb, wb.def, valuesTick]);
3948
3963
  const inputDefaultsMap = React.useMemo(() => runner.getInputDefaults(wb.def), [runner, wb, wb.def, valuesTick]);
@@ -3951,8 +3966,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
3951
3966
  const out = {};
3952
3967
  // Local: runtimeTypeId is not stored; derive from typed wrapper in outputsMap
3953
3968
  for (const n of wb.def.nodes) {
3954
- const effectiveHandles = computeEffectiveHandles(n, wb.registry);
3955
- const outputsDecl = effectiveHandles.outputs;
3969
+ const outputsDecl = handlesMap[n.nodeId]?.outputs ?? {};
3956
3970
  const handles = Object.keys(outputsDecl);
3957
3971
  const cur = {};
3958
3972
  for (const h of handles) {
@@ -3965,7 +3979,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
3965
3979
  out[n.nodeId] = cur;
3966
3980
  }
3967
3981
  return out;
3968
- }, [wb, wb.def, outputsMap, wb.registry, registryVersion]);
3982
+ }, [wb, wb.def, outputsMap, handlesMap]);
3969
3983
  // Initialize nodes and derive invalidated status from persisted metadata
3970
3984
  React.useEffect(() => {
3971
3985
  const workbenchRuntimeState = wb.getRuntimeState() ?? { nodes: {} };
@@ -4738,6 +4752,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
4738
4752
  nodeStatus,
4739
4753
  edgeStatus,
4740
4754
  valuesTick,
4755
+ handlesMap,
4741
4756
  inputsMap,
4742
4757
  inputDefaultsMap,
4743
4758
  outputsMap,
@@ -4788,6 +4803,7 @@ function WorkbenchProvider({ wb, runner, overrides, uiVersion, children, }) {
4788
4803
  removeSystemError,
4789
4804
  removeRegistryError,
4790
4805
  removeInputValidationError,
4806
+ handlesMap,
4791
4807
  inputsMap,
4792
4808
  inputDefaultsMap,
4793
4809
  outputsMap,
@@ -5298,24 +5314,31 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
5298
5314
  return String(value ?? "");
5299
5315
  }
5300
5316
  };
5301
- const { wb, registryVersion, selectedNodeId, selectedEdgeId, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, inputValidationErrors, clearSystemErrors, clearRegistryErrors, clearInputValidationErrors, removeSystemError, removeRegistryError, removeInputValidationError, } = useWorkbenchContext();
5317
+ const { wb, registryVersion, selectedNodeId, selectedEdgeId, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, inputValidationErrors, clearSystemErrors, clearRegistryErrors, clearInputValidationErrors, removeSystemError, removeRegistryError, removeInputValidationError, handlesMap, } = useWorkbenchContext();
5302
5318
  const nodeValidationIssues = validationByNode.issues;
5303
5319
  const edgeValidationIssues = validationByEdge.issues;
5304
5320
  const nodeValidationHandles = validationByNode;
5305
5321
  const globalValidationIssues = validationGlobal;
5306
5322
  const selectedNode = wb.def.nodes.find((n) => n.nodeId === selectedNodeId);
5307
5323
  const selectedEdge = wb.def.edges.find((e) => e.id === selectedEdgeId);
5308
- // Use computeEffectiveHandles to merge registry defaults with dynamically resolved handles
5324
+ // Use precomputed handles map from context
5309
5325
  const effectiveHandles = selectedNode
5310
- ? computeEffectiveHandles(selectedNode, wb.registry)
5311
- : { inputs: {}, outputs: {}};
5326
+ ? handlesMap[selectedNode.nodeId] ?? {
5327
+ inputs: {},
5328
+ outputs: {},
5329
+ inputDefaults: {},
5330
+ }
5331
+ : { inputs: {}, outputs: {}, inputDefaults: {} };
5312
5332
  const inputHandles = Object.entries(effectiveHandles.inputs)
5313
5333
  .filter(([k]) => !sparkGraph.isInputPrivate(effectiveHandles.inputs, k))
5314
5334
  .map(([k]) => k);
5315
5335
  const outputHandles = Object.keys(effectiveHandles.outputs);
5316
5336
  const nodeInputsRaw = selectedNodeId ? inputsMap[selectedNodeId] ?? {} : {};
5317
5337
  const nodeInputsDefaults = selectedNodeId
5318
- ? inputDefaultsMap[selectedNodeId] ?? {}
5338
+ ? {
5339
+ ...effectiveHandles.inputDefaults,
5340
+ ...inputDefaultsMap[selectedNodeId],
5341
+ }
5319
5342
  : {};
5320
5343
  // Keep defaults separate for placeholder use (don't merge into nodeInputs)
5321
5344
  const nodeInputs = nodeInputsRaw;
@@ -5877,7 +5900,7 @@ const SelectionBoundOverlay = ({ selection, rfInstance, viewportTick }) => {
5877
5900
 
5878
5901
  const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
5879
5902
  const { showValues, toString, toElement, getDefaultNodeSize, reactFlowProps, } = props;
5880
- const { wb, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
5903
+ const { wb, handlesMap, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
5881
5904
  const nodeValidation = validationByNode;
5882
5905
  const edgeValidation = validationByEdge.errors;
5883
5906
  const [historyState, setHistoryState] = React.useState(wb.getHistory());
@@ -5943,6 +5966,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
5943
5966
  };
5944
5967
  // Expose imperative API
5945
5968
  const rfInstanceRef = React.useRef(null);
5969
+ const containerRef = React.useRef(null);
5946
5970
  React.useImperativeHandle(ref, () => ({
5947
5971
  fitView: () => {
5948
5972
  try {
@@ -5992,14 +6016,13 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
5992
6016
  });
5993
6017
  return () => off();
5994
6018
  }, [wb]);
5995
- // Memoize customData to prevent unnecessary recomputations
5996
- const customData = React.useMemo(() => wb.getCustomData(), [wb]);
5997
6019
  const rfData = React.useMemo(() => {
5998
6020
  const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), wb.registry, {
5999
6021
  showValues,
6000
6022
  inputs: inputsMap,
6001
6023
  inputDefaults: inputDefaultsMap,
6002
6024
  outputs: outputsMap,
6025
+ handles: handlesMap,
6003
6026
  resolveNodeType,
6004
6027
  toString,
6005
6028
  toElement,
@@ -6011,7 +6034,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6011
6034
  selectedEdgeIds: new Set(selection.edges),
6012
6035
  getDefaultNodeSize,
6013
6036
  ui,
6014
- customData,
6037
+ customData: wb.getCustomData(),
6015
6038
  });
6016
6039
  // Retain references for unchanged items
6017
6040
  const stableNodes = retainStabilityById(prevNodesRef.current, out.nodes, isSameNode);
@@ -6107,8 +6130,8 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6107
6130
  inputsMap,
6108
6131
  inputDefaultsMap,
6109
6132
  outputsMap,
6133
+ handlesMap,
6110
6134
  valuesTick,
6111
- registryVersion,
6112
6135
  toString,
6113
6136
  toElement,
6114
6137
  nodeStatus,
@@ -6117,7 +6140,6 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6117
6140
  edgeValidation,
6118
6141
  resolveNodeType,
6119
6142
  selection,
6120
- customData,
6121
6143
  ui,
6122
6144
  getDefaultNodeSize,
6123
6145
  wb,
@@ -6350,8 +6372,14 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6350
6372
  if (!enableKeyboardShortcuts)
6351
6373
  return;
6352
6374
  const handleKeyDown = async (e) => {
6353
- // Ignore if typing in input/textarea
6375
+ // Check if target is inside WorkbenchCanvas container
6354
6376
  const target = e.target;
6377
+ if (!containerRef.current ||
6378
+ !(containerRef.current.contains(target) ||
6379
+ containerRef.current == target)) {
6380
+ return;
6381
+ }
6382
+ // Ignore if typing in input/textarea
6355
6383
  if (target.tagName === "INPUT" ||
6356
6384
  target.tagName === "TEXTAREA" ||
6357
6385
  target.isContentEditable) {
@@ -6525,7 +6553,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6525
6553
  return () => off();
6526
6554
  }, [wb]);
6527
6555
  const { onInit: userOnInit, ...restReactFlowProps } = reactFlowProps || {};
6528
- return (jsxRuntime.jsxs("div", { className: "w-full h-full relative overflow-hidden", onContextMenu: onContextMenu, children: [jsxRuntime.jsxs(react.ReactFlowProvider, { children: [jsxRuntime.jsxs(react.ReactFlow, { ...restReactFlowProps, nodes: rfData.nodes, edges: rfData.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
6556
+ return (jsxRuntime.jsxs("div", { ref: containerRef, className: "w-full h-full relative overflow-hidden", tabIndex: 0, onContextMenu: onContextMenu, children: [jsxRuntime.jsxs(react.ReactFlowProvider, { children: [jsxRuntime.jsxs(react.ReactFlow, { ...restReactFlowProps, nodes: rfData.nodes, edges: rfData.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
6529
6557
  rfInstanceRef.current = inst;
6530
6558
  const savedViewport = wb.getViewport();
6531
6559
  if (savedViewport) {
@@ -6547,13 +6575,17 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6547
6575
  const WorkbenchCanvas = WorkbenchCanvasComponent;
6548
6576
 
6549
6577
  function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
6550
- const { wb, registryVersion, runner, selectedNodeId, runAutoLayout, runMode, setRunMode, isRunning, } = useWorkbenchContext();
6578
+ const { wb, registryVersion, runner, selectedNodeId, handlesMap, runAutoLayout, runMode, setRunMode, isRunning, } = useWorkbenchContext();
6551
6579
  const [transportStatus, setTransportStatus] = React.useState({
6552
6580
  state: "local",
6553
6581
  });
6554
6582
  const selectedNode = wb.def.nodes.find((n) => n.nodeId === selectedNodeId);
6555
6583
  const effectiveHandles = selectedNode
6556
- ? computeEffectiveHandles(selectedNode, wb.registry)
6584
+ ? handlesMap[selectedNode.nodeId] ?? {
6585
+ inputs: {},
6586
+ outputs: {},
6587
+ inputDefaults: {},
6588
+ }
6557
6589
  : { inputs: {}, outputs: {}, inputDefaults: {} };
6558
6590
  const [exampleState, setExampleState] = React.useState(example ?? "");
6559
6591
  const isGraphRunning = isRunning();