@bian-womp/spark-workbench 0.3.3 → 0.3.5
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 +131 -100
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/SelectionBoundOverlay.d.ts +10 -0
- package/lib/cjs/src/misc/SelectionBoundOverlay.d.ts.map +1 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts +4 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts +1 -1
- package/lib/cjs/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
- package/lib/esm/index.js +131 -100
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/SelectionBoundOverlay.d.ts +10 -0
- package/lib/esm/src/misc/SelectionBoundOverlay.d.ts.map +1 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts +4 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts +1 -1
- package/lib/esm/src/misc/context-menu/NodeContextMenu.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -5518,7 +5518,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
|
|
|
5518
5518
|
!handlers.onRedo && jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxRuntime.jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsxRuntime.jsx("div", { className: "px-2 pb-1", children: jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 rounded px-2 py-1 text-sm outline-none focus:border-gray-400 select-text", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsxRuntime.jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsxRuntime.jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
5519
5519
|
}
|
|
5520
5520
|
|
|
5521
|
-
function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
5521
|
+
function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, wb, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
5522
5522
|
copy: "⌘/Ctrl + C",
|
|
5523
5523
|
duplicate: "⌘/Ctrl + E",
|
|
5524
5524
|
duplicateWithEdges: "⌘/Ctrl + Shift + E",
|
|
@@ -5552,6 +5552,10 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
|
|
|
5552
5552
|
}, [open]);
|
|
5553
5553
|
if (!open || !clientPos || !nodeId)
|
|
5554
5554
|
return null;
|
|
5555
|
+
// Determine if this is a start node (no inbound edges)
|
|
5556
|
+
const isStartNode = wb
|
|
5557
|
+
? !wb.def.edges.some((e) => e.target.nodeId === nodeId)
|
|
5558
|
+
: false;
|
|
5555
5559
|
// clamp
|
|
5556
5560
|
const MENU_MIN_WIDTH = 180;
|
|
5557
5561
|
const PADDING = 16;
|
|
@@ -5561,7 +5565,7 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
|
|
|
5561
5565
|
return (jsxRuntime.jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
5562
5566
|
e.preventDefault();
|
|
5563
5567
|
e.stopPropagation();
|
|
5564
|
-
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: "Run from here" }))] })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
|
|
5568
|
+
}, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [handlers.onRunNode && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: isStartNode ? "Run workflow" : "Run from here" }))] })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxRuntime.jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
|
|
5565
5569
|
}
|
|
5566
5570
|
|
|
5567
5571
|
function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
@@ -5647,7 +5651,56 @@ function useKeyboardShortcutToast() {
|
|
|
5647
5651
|
return { toast, showToast, hideToast };
|
|
5648
5652
|
}
|
|
5649
5653
|
|
|
5650
|
-
const
|
|
5654
|
+
const SelectionBoundOverlay = ({ selection, rfInstance }) => {
|
|
5655
|
+
const selectionBounds = React.useMemo(() => {
|
|
5656
|
+
if (typeof document === "undefined" ||
|
|
5657
|
+
!rfInstance ||
|
|
5658
|
+
selection.nodes.length < 2) {
|
|
5659
|
+
return null;
|
|
5660
|
+
}
|
|
5661
|
+
let bounds = null;
|
|
5662
|
+
for (const nodeId of selection.nodes) {
|
|
5663
|
+
const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
|
|
5664
|
+
if (!el)
|
|
5665
|
+
continue;
|
|
5666
|
+
const rect = el.getBoundingClientRect();
|
|
5667
|
+
if (!bounds) {
|
|
5668
|
+
bounds = {
|
|
5669
|
+
left: rect.left,
|
|
5670
|
+
top: rect.top,
|
|
5671
|
+
right: rect.right,
|
|
5672
|
+
bottom: rect.bottom,
|
|
5673
|
+
};
|
|
5674
|
+
}
|
|
5675
|
+
else {
|
|
5676
|
+
bounds.left = Math.min(bounds.left, rect.left);
|
|
5677
|
+
bounds.top = Math.min(bounds.top, rect.top);
|
|
5678
|
+
bounds.right = Math.max(bounds.right, rect.right);
|
|
5679
|
+
bounds.bottom = Math.max(bounds.bottom, rect.bottom);
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
return bounds;
|
|
5683
|
+
}, [selection.nodes, rfInstance]);
|
|
5684
|
+
if (!selectionBounds || selection.nodes.length < 2) {
|
|
5685
|
+
return null;
|
|
5686
|
+
}
|
|
5687
|
+
const { left, top, right, bottom } = selectionBounds;
|
|
5688
|
+
const width = right - left;
|
|
5689
|
+
const height = bottom - top;
|
|
5690
|
+
return (jsxRuntime.jsx("div", { style: {
|
|
5691
|
+
position: "fixed",
|
|
5692
|
+
left: `${left}px`,
|
|
5693
|
+
top: `${top}px`,
|
|
5694
|
+
width: `${width}px`,
|
|
5695
|
+
height: `${height}px`,
|
|
5696
|
+
border: "1px dashed #0ea5e9",
|
|
5697
|
+
pointerEvents: "none",
|
|
5698
|
+
zIndex: 4,
|
|
5699
|
+
boxSizing: "border-box",
|
|
5700
|
+
} }));
|
|
5701
|
+
};
|
|
5702
|
+
|
|
5703
|
+
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize, reactFlowProps }, ref) => {
|
|
5651
5704
|
const { wb, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
|
|
5652
5705
|
const nodeValidation = validationByNode;
|
|
5653
5706
|
const edgeValidation = validationByEdge.errors;
|
|
@@ -5762,27 +5815,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5762
5815
|
}, [uiVersion, ui]);
|
|
5763
5816
|
const { nodes, edges } = React.useMemo(() => {
|
|
5764
5817
|
const sel = wb.getSelection();
|
|
5765
|
-
// Merge defaults with inputs for node display (defaults shown in lighter gray)
|
|
5766
|
-
const inputsWithDefaults = {};
|
|
5767
|
-
for (const n of wb.def.nodes) {
|
|
5768
|
-
const nodeInputs = inputsMap[n.nodeId] ?? {};
|
|
5769
|
-
const nodeDefaults = inputDefaultsMap[n.nodeId] ?? {};
|
|
5770
|
-
const inbound = new Set(wb.def.edges
|
|
5771
|
-
.filter((e) => e.target.nodeId === n.nodeId)
|
|
5772
|
-
.map((e) => e.target.handle));
|
|
5773
|
-
const merged = { ...nodeInputs };
|
|
5774
|
-
for (const [h, v] of Object.entries(nodeDefaults)) {
|
|
5775
|
-
if (!inbound.has(h) && merged[h] === undefined) {
|
|
5776
|
-
merged[h] = v;
|
|
5777
|
-
}
|
|
5778
|
-
}
|
|
5779
|
-
if (Object.keys(merged).length > 0) {
|
|
5780
|
-
inputsWithDefaults[n.nodeId] = merged;
|
|
5781
|
-
}
|
|
5782
|
-
}
|
|
5783
5818
|
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), wb.registry, {
|
|
5784
5819
|
showValues,
|
|
5785
|
-
inputs:
|
|
5820
|
+
inputs: inputsMap,
|
|
5786
5821
|
inputDefaults: inputDefaultsMap,
|
|
5787
5822
|
outputs: outputsMap,
|
|
5788
5823
|
resolveNodeType,
|
|
@@ -5901,13 +5936,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5901
5936
|
resolveNodeType,
|
|
5902
5937
|
]);
|
|
5903
5938
|
const throttled = useThrottledValue({ nodes, edges }, 100);
|
|
5904
|
-
const [
|
|
5905
|
-
const [menuPos, setMenuPos] = React.useState(null);
|
|
5906
|
-
const [nodeMenuOpen, setNodeMenuOpen] = React.useState(false);
|
|
5907
|
-
const [nodeMenuPos, setNodeMenuPos] = React.useState(null);
|
|
5908
|
-
const [nodeAtMenu, setNodeAtMenu] = React.useState(null);
|
|
5909
|
-
const [selectionMenuPos, setSelectionMenuPos] = React.useState(null);
|
|
5910
|
-
const [selectionMenuOpen, setSelectionMenuOpen] = React.useState(false);
|
|
5939
|
+
const [menuState, setMenuState] = React.useState(null);
|
|
5911
5940
|
// Compute the rectangular screen-space bounds of the current selection
|
|
5912
5941
|
const getSelectionScreenBounds = () => {
|
|
5913
5942
|
if (typeof document === "undefined")
|
|
@@ -5948,31 +5977,24 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5948
5977
|
if (target) {
|
|
5949
5978
|
// Resolve node id from data-id attribute React Flow sets
|
|
5950
5979
|
const nodeId = target.getAttribute("data-id");
|
|
5951
|
-
|
|
5952
|
-
if (isSelected && isSingleNodeSelected) {
|
|
5953
|
-
// Right-clicked on the single selected node - show node menu
|
|
5954
|
-
setNodeAtMenu(nodeId);
|
|
5955
|
-
setNodeMenuPos({ x: e.clientX, y: e.clientY });
|
|
5956
|
-
setNodeMenuOpen(true);
|
|
5957
|
-
setMenuOpen(false);
|
|
5958
|
-
setSelectionMenuOpen(false);
|
|
5980
|
+
if (!nodeId)
|
|
5959
5981
|
return;
|
|
5960
|
-
|
|
5961
|
-
|
|
5982
|
+
const isSelected = selection.nodes.includes(nodeId);
|
|
5983
|
+
if (isSelected && !isSingleNodeSelected) {
|
|
5962
5984
|
// Right-clicked on a node that's part of multi-selection - show selection menu
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5985
|
+
setMenuState({
|
|
5986
|
+
type: "selection",
|
|
5987
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
5988
|
+
});
|
|
5967
5989
|
return;
|
|
5968
5990
|
}
|
|
5969
5991
|
else {
|
|
5970
5992
|
// Right-clicked on a non-selected node - show node menu
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5993
|
+
setMenuState({
|
|
5994
|
+
type: "node",
|
|
5995
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
5996
|
+
nodeId,
|
|
5997
|
+
});
|
|
5976
5998
|
return;
|
|
5977
5999
|
}
|
|
5978
6000
|
}
|
|
@@ -5984,32 +6006,22 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5984
6006
|
if (isSelected && isSingleNodeSelected) {
|
|
5985
6007
|
// Right-clicked on an edge, but only one node is selected - show node menu
|
|
5986
6008
|
const nodeId = selection.nodes[0];
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
6009
|
+
setMenuState({
|
|
6010
|
+
type: "node",
|
|
6011
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6012
|
+
nodeId,
|
|
6013
|
+
});
|
|
5992
6014
|
return;
|
|
5993
6015
|
}
|
|
5994
6016
|
else if (isSelected) {
|
|
5995
6017
|
// Right-clicked on a selected edge with multiple nodes - show selection menu
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6018
|
+
setMenuState({
|
|
6019
|
+
type: "selection",
|
|
6020
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6021
|
+
});
|
|
6000
6022
|
return;
|
|
6001
6023
|
}
|
|
6002
6024
|
}
|
|
6003
|
-
// If only one node is selected (even with edges), show node menu for empty space clicks
|
|
6004
|
-
if (isSingleNodeSelected) {
|
|
6005
|
-
const nodeId = selection.nodes[0];
|
|
6006
|
-
setNodeAtMenu(nodeId);
|
|
6007
|
-
setNodeMenuPos({ x: e.clientX, y: e.clientY });
|
|
6008
|
-
setNodeMenuOpen(true);
|
|
6009
|
-
setMenuOpen(false);
|
|
6010
|
-
setSelectionMenuOpen(false);
|
|
6011
|
-
return;
|
|
6012
|
-
}
|
|
6013
6025
|
// Check if the cursor is inside the rectangular bounds of the current selection
|
|
6014
6026
|
// (for multi-selection when right-clicking on empty space within selection bounds)
|
|
6015
6027
|
const selectionBounds = getSelectionScreenBounds();
|
|
@@ -6019,28 +6031,38 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6019
6031
|
e.clientX <= right &&
|
|
6020
6032
|
e.clientY >= top &&
|
|
6021
6033
|
e.clientY <= bottom) {
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6034
|
+
// If only one node is selected (even with edges), show node menu for empty space clicks
|
|
6035
|
+
if (isSingleNodeSelected) {
|
|
6036
|
+
const nodeId = selection.nodes[0];
|
|
6037
|
+
setMenuState({
|
|
6038
|
+
type: "node",
|
|
6039
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6040
|
+
nodeId,
|
|
6041
|
+
});
|
|
6042
|
+
return;
|
|
6043
|
+
}
|
|
6044
|
+
setMenuState({
|
|
6045
|
+
type: "selection",
|
|
6046
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6047
|
+
});
|
|
6026
6048
|
return;
|
|
6027
6049
|
}
|
|
6028
6050
|
}
|
|
6029
6051
|
// Right-clicked on empty space with no selection - show default menu
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6052
|
+
setMenuState({
|
|
6053
|
+
type: "default",
|
|
6054
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6055
|
+
});
|
|
6034
6056
|
};
|
|
6035
6057
|
const addNodeAt = React.useCallback(async (typeId, opts) => wb.addNode({ typeId }, { inputs: opts.inputs, position: opts.position, commit: true }), [wb]);
|
|
6036
6058
|
const onCloseMenu = React.useCallback(() => {
|
|
6037
|
-
|
|
6059
|
+
setMenuState(null);
|
|
6038
6060
|
}, []);
|
|
6039
6061
|
const onCloseNodeMenu = React.useCallback(() => {
|
|
6040
|
-
|
|
6062
|
+
setMenuState(null);
|
|
6041
6063
|
}, []);
|
|
6042
6064
|
const onCloseSelectionMenu = React.useCallback(() => {
|
|
6043
|
-
|
|
6065
|
+
setMenuState(null);
|
|
6044
6066
|
}, []);
|
|
6045
6067
|
React.useEffect(() => {
|
|
6046
6068
|
const off = wb.on("historyChanged", (event) => {
|
|
@@ -6087,8 +6109,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6087
6109
|
return baseHandlers;
|
|
6088
6110
|
}, [wb, runner, overrides, onCloseSelectionMenu]);
|
|
6089
6111
|
const nodeContextMenuHandlers = React.useMemo(() => {
|
|
6090
|
-
if (
|
|
6112
|
+
if (menuState?.type !== "node")
|
|
6091
6113
|
return null;
|
|
6114
|
+
const nodeAtMenu = menuState.nodeId;
|
|
6092
6115
|
// Get storage from override or use workbench's internal storage
|
|
6093
6116
|
const storage = overrides?.getCopiedDataStorage
|
|
6094
6117
|
? overrides.getCopiedDataStorage()
|
|
@@ -6104,7 +6127,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6104
6127
|
}
|
|
6105
6128
|
return baseHandlers;
|
|
6106
6129
|
}, [
|
|
6107
|
-
|
|
6130
|
+
menuState,
|
|
6108
6131
|
wb,
|
|
6109
6132
|
runner,
|
|
6110
6133
|
wb.registry,
|
|
@@ -6117,10 +6140,10 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6117
6140
|
overrides?.getCopiedDataStorage,
|
|
6118
6141
|
]);
|
|
6119
6142
|
const bakeableOutputs = React.useMemo(() => {
|
|
6120
|
-
if (
|
|
6143
|
+
if (menuState?.type !== "node")
|
|
6121
6144
|
return [];
|
|
6122
|
-
return getBakeableOutputs(
|
|
6123
|
-
}, [
|
|
6145
|
+
return getBakeableOutputs(menuState.nodeId, wb, wb.registry, outputTypesMap);
|
|
6146
|
+
}, [menuState, wb, wb.registry, registryVersion, outputTypesMap]);
|
|
6124
6147
|
// Keyboard shortcuts configuration
|
|
6125
6148
|
const enableKeyboardShortcuts = overrides?.enableKeyboardShortcuts !== false; // Default to true
|
|
6126
6149
|
const keyboardShortcuts = overrides?.keyboardShortcuts || {
|
|
@@ -6190,7 +6213,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6190
6213
|
const modKeyLabel = isMac ? "⌘" : "Ctrl";
|
|
6191
6214
|
showToast(`Copy (${modKeyLabel} + C)`);
|
|
6192
6215
|
// If single node selected, use node context menu handler; otherwise use selection handler
|
|
6193
|
-
if (selection.nodes.length === 1 &&
|
|
6216
|
+
if (selection.nodes.length === 1 &&
|
|
6217
|
+
nodeContextMenuHandlers?.onCopy) {
|
|
6194
6218
|
nodeContextMenuHandlers.onCopy();
|
|
6195
6219
|
}
|
|
6196
6220
|
else if (selectionContextMenuHandlers.onCopy) {
|
|
@@ -6310,21 +6334,25 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6310
6334
|
});
|
|
6311
6335
|
return () => off();
|
|
6312
6336
|
}, [wb]);
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6337
|
+
const { onInit: userOnInit, ...restReactFlowProps } = reactFlowProps || {};
|
|
6338
|
+
return (jsxRuntime.jsxs("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: [jsxRuntime.jsxs(react.ReactFlowProvider, { children: [jsxRuntime.jsxs(react.ReactFlow, { ...restReactFlowProps, nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
|
|
6339
|
+
rfInstanceRef.current = inst;
|
|
6340
|
+
const savedViewport = wb.getViewport();
|
|
6341
|
+
if (savedViewport) {
|
|
6342
|
+
inst.setViewport(lod.clone(savedViewport));
|
|
6343
|
+
}
|
|
6344
|
+
if (userOnInit) {
|
|
6345
|
+
userOnInit(inst);
|
|
6346
|
+
}
|
|
6347
|
+
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", children: [BackgroundRenderer ? (jsxRuntime.jsx(BackgroundRenderer, {})) : (jsxRuntime.jsx(react.Background, { id: "workbench-canvas-background", variant: react.BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsxRuntime.jsx(MinimapRenderer, {}) : jsxRuntime.jsx(react.MiniMap, {}), ControlsRenderer ? jsxRuntime.jsx(ControlsRenderer, {}) : jsxRuntime.jsx(react.Controls, {}), menuState?.type === "default" &&
|
|
6348
|
+
(DefaultContextMenuRenderer ? (jsxRuntime.jsx(DefaultContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
|
|
6349
|
+
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
6350
|
+
: {}) })) : (jsxRuntime.jsx(DefaultContextMenu, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), menuState?.type === "node" &&
|
|
6351
|
+
nodeContextMenuHandlers &&
|
|
6352
|
+
(NodeContextMenuRenderer ? (jsxRuntime.jsx(NodeContextMenuRenderer, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, ...(enableKeyboardShortcuts !== false
|
|
6353
|
+
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
6354
|
+
: {}) })) : (jsxRuntime.jsx(NodeContextMenu, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), menuState?.type === "selection" &&
|
|
6355
|
+
(SelectionContextMenuRenderer ? (jsxRuntime.jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsxRuntime.jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }), jsxRuntime.jsx(SelectionBoundOverlay, { selection: wb.getSelection(), rfInstance: rfInstanceRef.current })] }), toast && (jsxRuntime.jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
|
|
6328
6356
|
});
|
|
6329
6357
|
|
|
6330
6358
|
function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -6740,7 +6768,10 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6740
6768
|
}
|
|
6741
6769
|
}, title: "Select run mode", children: [jsxRuntime.jsx("option", { value: "manual", children: "Manual" }), jsxRuntime.jsx("option", { value: "auto", children: "Auto" })] }), renderStartStopButton(), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsxRuntime.jsx(react$1.TreeStructureIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsxRuntime.jsx(react$1.CornersOutIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: download$1, children: jsxRuntime.jsx(react$1.DownloadIcon, { size: 24 }) }), jsxRuntime.jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: triggerUpload, children: jsxRuntime.jsx(react$1.UploadIcon, { size: 24 }) }), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded p-1", onClick: async () => {
|
|
6742
6770
|
await downloadCanvasThumbnail(canvasContainerRef.current);
|
|
6743
|
-
}, title: "Download Flow Thumbnail (SVG)", children: jsxRuntime.jsx(react$1.ImageIcon, { size: 24 }) }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx(react$1.BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx(react$1.ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize
|
|
6771
|
+
}, title: "Download Flow Thumbnail (SVG)", children: jsxRuntime.jsx(react$1.ImageIcon, { size: 24 }) }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx(react$1.BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx(react$1.ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize, reactFlowProps: {
|
|
6772
|
+
minZoom: 0.1,
|
|
6773
|
+
maxZoom: 5,
|
|
6774
|
+
} }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, contextPanel: overrides?.contextPanel })] })] }));
|
|
6744
6775
|
}
|
|
6745
6776
|
function WorkbenchStudio({ example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
|
|
6746
6777
|
const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
|