@bian-womp/spark-workbench 0.3.28 → 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 {
@@ -5998,6 +6022,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
5998
6022
  inputs: inputsMap,
5999
6023
  inputDefaults: inputDefaultsMap,
6000
6024
  outputs: outputsMap,
6025
+ handles: handlesMap,
6001
6026
  resolveNodeType,
6002
6027
  toString,
6003
6028
  toElement,
@@ -6105,8 +6130,8 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6105
6130
  inputsMap,
6106
6131
  inputDefaultsMap,
6107
6132
  outputsMap,
6133
+ handlesMap,
6108
6134
  valuesTick,
6109
- registryVersion,
6110
6135
  toString,
6111
6136
  toElement,
6112
6137
  nodeStatus,
@@ -6347,8 +6372,14 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6347
6372
  if (!enableKeyboardShortcuts)
6348
6373
  return;
6349
6374
  const handleKeyDown = async (e) => {
6350
- // Ignore if typing in input/textarea
6375
+ // Check if target is inside WorkbenchCanvas container
6351
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
6352
6383
  if (target.tagName === "INPUT" ||
6353
6384
  target.tagName === "TEXTAREA" ||
6354
6385
  target.isContentEditable) {
@@ -6522,7 +6553,7 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6522
6553
  return () => off();
6523
6554
  }, [wb]);
6524
6555
  const { onInit: userOnInit, ...restReactFlowProps } = reactFlowProps || {};
6525
- 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) => {
6526
6557
  rfInstanceRef.current = inst;
6527
6558
  const savedViewport = wb.getViewport();
6528
6559
  if (savedViewport) {
@@ -6544,13 +6575,17 @@ const WorkbenchCanvasComponent = React.forwardRef((props, ref) => {
6544
6575
  const WorkbenchCanvas = WorkbenchCanvasComponent;
6545
6576
 
6546
6577
  function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
6547
- const { wb, registryVersion, runner, selectedNodeId, runAutoLayout, runMode, setRunMode, isRunning, } = useWorkbenchContext();
6578
+ const { wb, registryVersion, runner, selectedNodeId, handlesMap, runAutoLayout, runMode, setRunMode, isRunning, } = useWorkbenchContext();
6548
6579
  const [transportStatus, setTransportStatus] = React.useState({
6549
6580
  state: "local",
6550
6581
  });
6551
6582
  const selectedNode = wb.def.nodes.find((n) => n.nodeId === selectedNodeId);
6552
6583
  const effectiveHandles = selectedNode
6553
- ? computeEffectiveHandles(selectedNode, wb.registry)
6584
+ ? handlesMap[selectedNode.nodeId] ?? {
6585
+ inputs: {},
6586
+ outputs: {},
6587
+ inputDefaults: {},
6588
+ }
6554
6589
  : { inputs: {}, outputs: {}, inputDefaults: {} };
6555
6590
  const [exampleState, setExampleState] = React.useState(example ?? "");
6556
6591
  const isGraphRunning = isRunning();