@bian-womp/spark-workbench 0.3.3 → 0.3.4
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
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ReactFlowInstance, Node, Edge } from "@xyflow/react";
|
|
3
|
+
export declare const SelectionBoundOverlay: React.FC<{
|
|
4
|
+
selection: {
|
|
5
|
+
nodes: string[];
|
|
6
|
+
edges: string[];
|
|
7
|
+
};
|
|
8
|
+
rfInstance: ReactFlowInstance<Node, Edge> | null;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=SelectionBoundOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelectionBoundOverlay.d.ts","sourceRoot":"","sources":["../../../../src/misc/SelectionBoundOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE9D,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC;IAC3C,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChD,UAAU,EAAE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC;CAClD,CAgEA,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { ReactFlowInstance, Node, Edge, type ReactFlowProps } from "@xyflow/react";
|
|
2
3
|
import type { Viewport } from "@bian-womp/spark-protocol";
|
|
3
4
|
export type WorkbenchCanvasHandle = {
|
|
4
5
|
fitView: () => void;
|
|
@@ -12,5 +13,8 @@ export declare const WorkbenchCanvas: React.ForwardRefExoticComponent<{
|
|
|
12
13
|
width: number;
|
|
13
14
|
height: number;
|
|
14
15
|
} | undefined;
|
|
16
|
+
reactFlowProps?: Partial<Omit<ReactFlowProps<Node, Edge>, "nodes" | "edges" | "nodeTypes" | "edgeTypes" | "connectionLineComponent" | "onInit" | "onConnect" | "onEdgesChange" | "onEdgesDelete" | "onNodesDelete" | "onNodesChange" | "onMoveEnd">> & {
|
|
17
|
+
onInit?: (instance: ReactFlowInstance<Node, Edge>) => void;
|
|
18
|
+
};
|
|
15
19
|
} & React.RefAttributes<WorkbenchCanvasHandle>>;
|
|
16
20
|
//# sourceMappingURL=WorkbenchCanvas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkbenchCanvas.d.ts","sourceRoot":"","sources":["../../../../src/misc/WorkbenchCanvas.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"WorkbenchCanvas.d.ts","sourceRoot":"","sources":["../../../../src/misc/WorkbenchCanvas.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AACf,OAAO,EAOL,iBAAiB,EACjB,IAAI,EACJ,IAAI,EACJ,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAuB1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC3C,CAAC;AAEF,eAAO,MAAM,eAAe;iBAGX,OAAO;cACV,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,MAAM;eAC3C,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,GAAG,CAAC,OAAO;yBACvC,CACnB,MAAM,EAAE,MAAM,KACX;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS;qBACjC,OAAO,CACtB,IAAI,CACF,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,EACxB,OAAO,GACP,OAAO,GACP,WAAW,GACX,WAAW,GACX,yBAAyB,GACzB,QAAQ,GACR,WAAW,GACX,eAAe,GACf,eAAe,GACf,eAAe,GACf,eAAe,GACf,WAAW,CACd,CACF,GAAG;QACF,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;KAC5D;+CAs9BJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkbenchStudio.d.ts","sourceRoot":"","sources":["../../../../src/misc/WorkbenchStudio.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAW,MAAM,2BAA2B,CAAC;AAgB1E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAkBvE,OAAO,EAGL,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAMpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAK9D,OAAO,EAEL,oBAAoB,EACpB,YAAY,EAGb,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"WorkbenchStudio.d.ts","sourceRoot":"","sources":["../../../../src/misc/WorkbenchStudio.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAW,MAAM,2BAA2B,CAAC;AAgB1E,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAkBvE,OAAO,EAGL,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAMpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAK9D,OAAO,EAEL,oBAAoB,EACpB,YAAY,EAGb,MAAM,yBAAyB,CAAC;AAIjC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACtC;AA+sBD,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,aAAa,EACb,KAAK,EACL,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,SAAS,EACT,MAAM,EACN,QAAQ,GACT,EAAE;IACD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAClD,WAAW,EAAE,oBAAoB,CAAC;IAClC,mBAAmB,EAAE,CAAC,CAAC,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,qBAAqB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE;QACd,EAAE,EAAE,iBAAiB,CAAC;QACtB,MAAM,EAAE,YAAY,CAAC;QACrB,eAAe,EAAE,CACf,GAAG,EAAE,eAAe,EACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAC7C,OAAO,CAAC,IAAI,CAAC,CAAC;KACpB,KAAK,IAAI,CAAC;IACX,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE;QAChB,GAAG,EAAE,eAAe,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACjD,KAAK,IAAI,CAAC;CACZ,2CA8GA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { NodeContextMenuProps } from "./ContextMenuHandlers";
|
|
2
|
-
export declare function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, enableKeyboardShortcuts, keyboardShortcuts, }: NodeContextMenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
2
|
+
export declare function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, wb, enableKeyboardShortcuts, keyboardShortcuts, }: NodeContextMenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
3
3
|
//# sourceMappingURL=NodeContextMenu.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeContextMenu.d.ts","sourceRoot":"","sources":["../../../../../src/misc/context-menu/NodeContextMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,SAAS,EACT,MAAM,EACN,QAAQ,EACR,eAAe,EACf,OAAO,EACP,uBAA8B,EAC9B,iBAKC,GACF,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"NodeContextMenu.d.ts","sourceRoot":"","sources":["../../../../../src/misc/context-menu/NodeContextMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGlE,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,SAAS,EACT,MAAM,EACN,QAAQ,EACR,eAAe,EACf,OAAO,EACP,EAAE,EACF,uBAA8B,EAC9B,iBAKC,GACF,EAAE,oBAAoB,kDAkItB"}
|
package/lib/esm/index.js
CHANGED
|
@@ -5516,7 +5516,7 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, enab
|
|
|
5516
5516
|
!handlers.onRedo && jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsx("div", { className: "px-2 pb-1", children: 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() }) }), jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
5517
5517
|
}
|
|
5518
5518
|
|
|
5519
|
-
function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
5519
|
+
function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, runMode, wb, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
5520
5520
|
copy: "⌘/Ctrl + C",
|
|
5521
5521
|
duplicate: "⌘/Ctrl + E",
|
|
5522
5522
|
duplicateWithEdges: "⌘/Ctrl + Shift + E",
|
|
@@ -5550,6 +5550,10 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
|
|
|
5550
5550
|
}, [open]);
|
|
5551
5551
|
if (!open || !clientPos || !nodeId)
|
|
5552
5552
|
return null;
|
|
5553
|
+
// Determine if this is a start node (no inbound edges)
|
|
5554
|
+
const isStartNode = wb
|
|
5555
|
+
? !wb.def.edges.some((e) => e.target.nodeId === nodeId)
|
|
5556
|
+
: false;
|
|
5553
5557
|
// clamp
|
|
5554
5558
|
const MENU_MIN_WIDTH = 180;
|
|
5555
5559
|
const PADDING = 16;
|
|
@@ -5559,7 +5563,7 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, bakeableOutputs, r
|
|
|
5559
5563
|
return (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) => {
|
|
5560
5564
|
e.preventDefault();
|
|
5561
5565
|
e.stopPropagation();
|
|
5562
|
-
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxs(Fragment, { children: [handlers.onRunNode && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunFromHere, children: "Run from here" }))] })), jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), 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 && (jsxs(Fragment, { children: [jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
|
|
5566
|
+
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx(ContextMenuButton, { label: "Delete", onClick: handlers.onDelete, shortcut: keyboardShortcuts.delete, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate", onClick: handlers.onDuplicate, shortcut: keyboardShortcuts.duplicate, enableKeyboardShortcuts: enableKeyboardShortcuts }), jsx(ContextMenuButton, { label: "Duplicate with edges", onClick: handlers.onDuplicateWithEdges, shortcut: keyboardShortcuts.duplicateWithEdges, enableKeyboardShortcuts: enableKeyboardShortcuts }), runMode === "manual" && (jsxs(Fragment, { children: [handlers.onRunNode && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunNode, children: "Run node" })), handlers.onRunFromHere && (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" }))] })), jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx(ContextMenuButton, { label: "Copy", onClick: handlers.onCopy, shortcut: keyboardShortcuts.copy, enableKeyboardShortcuts: enableKeyboardShortcuts }), 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 && (jsxs(Fragment, { children: [jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
|
|
5563
5567
|
}
|
|
5564
5568
|
|
|
5565
5569
|
function SelectionContextMenu({ open, clientPos, handlers, enableKeyboardShortcuts = true, keyboardShortcuts = {
|
|
@@ -5645,7 +5649,56 @@ function useKeyboardShortcutToast() {
|
|
|
5645
5649
|
return { toast, showToast, hideToast };
|
|
5646
5650
|
}
|
|
5647
5651
|
|
|
5648
|
-
const
|
|
5652
|
+
const SelectionBoundOverlay = ({ selection, rfInstance }) => {
|
|
5653
|
+
const selectionBounds = useMemo(() => {
|
|
5654
|
+
if (typeof document === "undefined" ||
|
|
5655
|
+
!rfInstance ||
|
|
5656
|
+
selection.nodes.length < 2) {
|
|
5657
|
+
return null;
|
|
5658
|
+
}
|
|
5659
|
+
let bounds = null;
|
|
5660
|
+
for (const nodeId of selection.nodes) {
|
|
5661
|
+
const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
|
|
5662
|
+
if (!el)
|
|
5663
|
+
continue;
|
|
5664
|
+
const rect = el.getBoundingClientRect();
|
|
5665
|
+
if (!bounds) {
|
|
5666
|
+
bounds = {
|
|
5667
|
+
left: rect.left,
|
|
5668
|
+
top: rect.top,
|
|
5669
|
+
right: rect.right,
|
|
5670
|
+
bottom: rect.bottom,
|
|
5671
|
+
};
|
|
5672
|
+
}
|
|
5673
|
+
else {
|
|
5674
|
+
bounds.left = Math.min(bounds.left, rect.left);
|
|
5675
|
+
bounds.top = Math.min(bounds.top, rect.top);
|
|
5676
|
+
bounds.right = Math.max(bounds.right, rect.right);
|
|
5677
|
+
bounds.bottom = Math.max(bounds.bottom, rect.bottom);
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
return bounds;
|
|
5681
|
+
}, [selection.nodes, rfInstance]);
|
|
5682
|
+
if (!selectionBounds || selection.nodes.length < 2) {
|
|
5683
|
+
return null;
|
|
5684
|
+
}
|
|
5685
|
+
const { left, top, right, bottom } = selectionBounds;
|
|
5686
|
+
const width = right - left;
|
|
5687
|
+
const height = bottom - top;
|
|
5688
|
+
return (jsx("div", { style: {
|
|
5689
|
+
position: "fixed",
|
|
5690
|
+
left: `${left}px`,
|
|
5691
|
+
top: `${top}px`,
|
|
5692
|
+
width: `${width}px`,
|
|
5693
|
+
height: `${height}px`,
|
|
5694
|
+
border: "1px dashed #0ea5e9",
|
|
5695
|
+
pointerEvents: "none",
|
|
5696
|
+
zIndex: 4,
|
|
5697
|
+
boxSizing: "border-box",
|
|
5698
|
+
} }));
|
|
5699
|
+
};
|
|
5700
|
+
|
|
5701
|
+
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize, reactFlowProps }, ref) => {
|
|
5649
5702
|
const { wb, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, registryVersion, runner, overrides, runNode, runFromHere, runMode, } = useWorkbenchContext();
|
|
5650
5703
|
const nodeValidation = validationByNode;
|
|
5651
5704
|
const edgeValidation = validationByEdge.errors;
|
|
@@ -5760,27 +5813,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5760
5813
|
}, [uiVersion, ui]);
|
|
5761
5814
|
const { nodes, edges } = useMemo(() => {
|
|
5762
5815
|
const sel = wb.getSelection();
|
|
5763
|
-
// Merge defaults with inputs for node display (defaults shown in lighter gray)
|
|
5764
|
-
const inputsWithDefaults = {};
|
|
5765
|
-
for (const n of wb.def.nodes) {
|
|
5766
|
-
const nodeInputs = inputsMap[n.nodeId] ?? {};
|
|
5767
|
-
const nodeDefaults = inputDefaultsMap[n.nodeId] ?? {};
|
|
5768
|
-
const inbound = new Set(wb.def.edges
|
|
5769
|
-
.filter((e) => e.target.nodeId === n.nodeId)
|
|
5770
|
-
.map((e) => e.target.handle));
|
|
5771
|
-
const merged = { ...nodeInputs };
|
|
5772
|
-
for (const [h, v] of Object.entries(nodeDefaults)) {
|
|
5773
|
-
if (!inbound.has(h) && merged[h] === undefined) {
|
|
5774
|
-
merged[h] = v;
|
|
5775
|
-
}
|
|
5776
|
-
}
|
|
5777
|
-
if (Object.keys(merged).length > 0) {
|
|
5778
|
-
inputsWithDefaults[n.nodeId] = merged;
|
|
5779
|
-
}
|
|
5780
|
-
}
|
|
5781
5816
|
const out = toReactFlow(wb.def, wb.getPositions(), wb.getSizes(), wb.registry, {
|
|
5782
5817
|
showValues,
|
|
5783
|
-
inputs:
|
|
5818
|
+
inputs: inputsMap,
|
|
5784
5819
|
inputDefaults: inputDefaultsMap,
|
|
5785
5820
|
outputs: outputsMap,
|
|
5786
5821
|
resolveNodeType,
|
|
@@ -5899,13 +5934,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5899
5934
|
resolveNodeType,
|
|
5900
5935
|
]);
|
|
5901
5936
|
const throttled = useThrottledValue({ nodes, edges }, 100);
|
|
5902
|
-
const [
|
|
5903
|
-
const [menuPos, setMenuPos] = useState(null);
|
|
5904
|
-
const [nodeMenuOpen, setNodeMenuOpen] = useState(false);
|
|
5905
|
-
const [nodeMenuPos, setNodeMenuPos] = useState(null);
|
|
5906
|
-
const [nodeAtMenu, setNodeAtMenu] = useState(null);
|
|
5907
|
-
const [selectionMenuPos, setSelectionMenuPos] = useState(null);
|
|
5908
|
-
const [selectionMenuOpen, setSelectionMenuOpen] = useState(false);
|
|
5937
|
+
const [menuState, setMenuState] = useState(null);
|
|
5909
5938
|
// Compute the rectangular screen-space bounds of the current selection
|
|
5910
5939
|
const getSelectionScreenBounds = () => {
|
|
5911
5940
|
if (typeof document === "undefined")
|
|
@@ -5946,31 +5975,24 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5946
5975
|
if (target) {
|
|
5947
5976
|
// Resolve node id from data-id attribute React Flow sets
|
|
5948
5977
|
const nodeId = target.getAttribute("data-id");
|
|
5949
|
-
|
|
5950
|
-
if (isSelected && isSingleNodeSelected) {
|
|
5951
|
-
// Right-clicked on the single selected node - show node menu
|
|
5952
|
-
setNodeAtMenu(nodeId);
|
|
5953
|
-
setNodeMenuPos({ x: e.clientX, y: e.clientY });
|
|
5954
|
-
setNodeMenuOpen(true);
|
|
5955
|
-
setMenuOpen(false);
|
|
5956
|
-
setSelectionMenuOpen(false);
|
|
5978
|
+
if (!nodeId)
|
|
5957
5979
|
return;
|
|
5958
|
-
|
|
5959
|
-
|
|
5980
|
+
const isSelected = selection.nodes.includes(nodeId);
|
|
5981
|
+
if (isSelected && !isSingleNodeSelected) {
|
|
5960
5982
|
// Right-clicked on a node that's part of multi-selection - show selection menu
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5983
|
+
setMenuState({
|
|
5984
|
+
type: "selection",
|
|
5985
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
5986
|
+
});
|
|
5965
5987
|
return;
|
|
5966
5988
|
}
|
|
5967
5989
|
else {
|
|
5968
5990
|
// Right-clicked on a non-selected node - show node menu
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5991
|
+
setMenuState({
|
|
5992
|
+
type: "node",
|
|
5993
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
5994
|
+
nodeId,
|
|
5995
|
+
});
|
|
5974
5996
|
return;
|
|
5975
5997
|
}
|
|
5976
5998
|
}
|
|
@@ -5982,32 +6004,22 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
5982
6004
|
if (isSelected && isSingleNodeSelected) {
|
|
5983
6005
|
// Right-clicked on an edge, but only one node is selected - show node menu
|
|
5984
6006
|
const nodeId = selection.nodes[0];
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
6007
|
+
setMenuState({
|
|
6008
|
+
type: "node",
|
|
6009
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6010
|
+
nodeId,
|
|
6011
|
+
});
|
|
5990
6012
|
return;
|
|
5991
6013
|
}
|
|
5992
6014
|
else if (isSelected) {
|
|
5993
6015
|
// Right-clicked on a selected edge with multiple nodes - show selection menu
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
6016
|
+
setMenuState({
|
|
6017
|
+
type: "selection",
|
|
6018
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6019
|
+
});
|
|
5998
6020
|
return;
|
|
5999
6021
|
}
|
|
6000
6022
|
}
|
|
6001
|
-
// If only one node is selected (even with edges), show node menu for empty space clicks
|
|
6002
|
-
if (isSingleNodeSelected) {
|
|
6003
|
-
const nodeId = selection.nodes[0];
|
|
6004
|
-
setNodeAtMenu(nodeId);
|
|
6005
|
-
setNodeMenuPos({ x: e.clientX, y: e.clientY });
|
|
6006
|
-
setNodeMenuOpen(true);
|
|
6007
|
-
setMenuOpen(false);
|
|
6008
|
-
setSelectionMenuOpen(false);
|
|
6009
|
-
return;
|
|
6010
|
-
}
|
|
6011
6023
|
// Check if the cursor is inside the rectangular bounds of the current selection
|
|
6012
6024
|
// (for multi-selection when right-clicking on empty space within selection bounds)
|
|
6013
6025
|
const selectionBounds = getSelectionScreenBounds();
|
|
@@ -6017,28 +6029,38 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6017
6029
|
e.clientX <= right &&
|
|
6018
6030
|
e.clientY >= top &&
|
|
6019
6031
|
e.clientY <= bottom) {
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6032
|
+
// If only one node is selected (even with edges), show node menu for empty space clicks
|
|
6033
|
+
if (isSingleNodeSelected) {
|
|
6034
|
+
const nodeId = selection.nodes[0];
|
|
6035
|
+
setMenuState({
|
|
6036
|
+
type: "node",
|
|
6037
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6038
|
+
nodeId,
|
|
6039
|
+
});
|
|
6040
|
+
return;
|
|
6041
|
+
}
|
|
6042
|
+
setMenuState({
|
|
6043
|
+
type: "selection",
|
|
6044
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6045
|
+
});
|
|
6024
6046
|
return;
|
|
6025
6047
|
}
|
|
6026
6048
|
}
|
|
6027
6049
|
// Right-clicked on empty space with no selection - show default menu
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6050
|
+
setMenuState({
|
|
6051
|
+
type: "default",
|
|
6052
|
+
menuPos: { x: e.clientX, y: e.clientY },
|
|
6053
|
+
});
|
|
6032
6054
|
};
|
|
6033
6055
|
const addNodeAt = useCallback(async (typeId, opts) => wb.addNode({ typeId }, { inputs: opts.inputs, position: opts.position, commit: true }), [wb]);
|
|
6034
6056
|
const onCloseMenu = useCallback(() => {
|
|
6035
|
-
|
|
6057
|
+
setMenuState(null);
|
|
6036
6058
|
}, []);
|
|
6037
6059
|
const onCloseNodeMenu = useCallback(() => {
|
|
6038
|
-
|
|
6060
|
+
setMenuState(null);
|
|
6039
6061
|
}, []);
|
|
6040
6062
|
const onCloseSelectionMenu = useCallback(() => {
|
|
6041
|
-
|
|
6063
|
+
setMenuState(null);
|
|
6042
6064
|
}, []);
|
|
6043
6065
|
useEffect(() => {
|
|
6044
6066
|
const off = wb.on("historyChanged", (event) => {
|
|
@@ -6085,8 +6107,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6085
6107
|
return baseHandlers;
|
|
6086
6108
|
}, [wb, runner, overrides, onCloseSelectionMenu]);
|
|
6087
6109
|
const nodeContextMenuHandlers = useMemo(() => {
|
|
6088
|
-
if (
|
|
6110
|
+
if (menuState?.type !== "node")
|
|
6089
6111
|
return null;
|
|
6112
|
+
const nodeAtMenu = menuState.nodeId;
|
|
6090
6113
|
// Get storage from override or use workbench's internal storage
|
|
6091
6114
|
const storage = overrides?.getCopiedDataStorage
|
|
6092
6115
|
? overrides.getCopiedDataStorage()
|
|
@@ -6102,7 +6125,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6102
6125
|
}
|
|
6103
6126
|
return baseHandlers;
|
|
6104
6127
|
}, [
|
|
6105
|
-
|
|
6128
|
+
menuState,
|
|
6106
6129
|
wb,
|
|
6107
6130
|
runner,
|
|
6108
6131
|
wb.registry,
|
|
@@ -6115,10 +6138,10 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6115
6138
|
overrides?.getCopiedDataStorage,
|
|
6116
6139
|
]);
|
|
6117
6140
|
const bakeableOutputs = useMemo(() => {
|
|
6118
|
-
if (
|
|
6141
|
+
if (menuState?.type !== "node")
|
|
6119
6142
|
return [];
|
|
6120
|
-
return getBakeableOutputs(
|
|
6121
|
-
}, [
|
|
6143
|
+
return getBakeableOutputs(menuState.nodeId, wb, wb.registry, outputTypesMap);
|
|
6144
|
+
}, [menuState, wb, wb.registry, registryVersion, outputTypesMap]);
|
|
6122
6145
|
// Keyboard shortcuts configuration
|
|
6123
6146
|
const enableKeyboardShortcuts = overrides?.enableKeyboardShortcuts !== false; // Default to true
|
|
6124
6147
|
const keyboardShortcuts = overrides?.keyboardShortcuts || {
|
|
@@ -6188,7 +6211,8 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6188
6211
|
const modKeyLabel = isMac ? "⌘" : "Ctrl";
|
|
6189
6212
|
showToast(`Copy (${modKeyLabel} + C)`);
|
|
6190
6213
|
// If single node selected, use node context menu handler; otherwise use selection handler
|
|
6191
|
-
if (selection.nodes.length === 1 &&
|
|
6214
|
+
if (selection.nodes.length === 1 &&
|
|
6215
|
+
nodeContextMenuHandlers?.onCopy) {
|
|
6192
6216
|
nodeContextMenuHandlers.onCopy();
|
|
6193
6217
|
}
|
|
6194
6218
|
else if (selectionContextMenuHandlers.onCopy) {
|
|
@@ -6308,21 +6332,25 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
6308
6332
|
});
|
|
6309
6333
|
return () => off();
|
|
6310
6334
|
}, [wb]);
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6335
|
+
const { onInit: userOnInit, ...restReactFlowProps } = reactFlowProps || {};
|
|
6336
|
+
return (jsxs("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: [jsxs(ReactFlowProvider, { children: [jsxs(ReactFlow, { ...restReactFlowProps, nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineComponent: connectionLineRenderer, selectionOnDrag: true, onInit: (inst) => {
|
|
6337
|
+
rfInstanceRef.current = inst;
|
|
6338
|
+
const savedViewport = wb.getViewport();
|
|
6339
|
+
if (savedViewport) {
|
|
6340
|
+
inst.setViewport(lod.clone(savedViewport));
|
|
6341
|
+
}
|
|
6342
|
+
if (userOnInit) {
|
|
6343
|
+
userOnInit(inst);
|
|
6344
|
+
}
|
|
6345
|
+
}, 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 ? (jsx(BackgroundRenderer, {})) : (jsx(Background, { id: "workbench-canvas-background", variant: BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsx(MinimapRenderer, {}) : jsx(MiniMap, {}), ControlsRenderer ? jsx(ControlsRenderer, {}) : jsx(Controls, {}), menuState?.type === "default" &&
|
|
6346
|
+
(DefaultContextMenuRenderer ? (jsx(DefaultContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, ...(enableKeyboardShortcuts !== false
|
|
6347
|
+
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
6348
|
+
: {}) })) : (jsx(DefaultContextMenu, { open: true, clientPos: menuState.menuPos, handlers: defaultContextMenuHandlers, registry: wb.registry, nodeIds: nodeIds, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts }))), menuState?.type === "node" &&
|
|
6349
|
+
nodeContextMenuHandlers &&
|
|
6350
|
+
(NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode, wb: wb, ...(enableKeyboardShortcuts !== false
|
|
6351
|
+
? { enableKeyboardShortcuts, keyboardShortcuts }
|
|
6352
|
+
: {}) })) : (jsx(NodeContextMenu, { open: true, clientPos: menuState.menuPos, nodeId: menuState.nodeId, handlers: nodeContextMenuHandlers, bakeableOutputs: bakeableOutputs, runMode: runMode }))), menuState?.type === "selection" &&
|
|
6353
|
+
(SelectionContextMenuRenderer ? (jsx(SelectionContextMenuRenderer, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })) : (jsx(SelectionContextMenu, { open: true, clientPos: menuState.menuPos, handlers: selectionContextMenuHandlers, enableKeyboardShortcuts: enableKeyboardShortcuts, keyboardShortcuts: keyboardShortcuts })))] }), jsx(SelectionBoundOverlay, { selection: wb.getSelection(), rfInstance: rfInstanceRef.current })] }), toast && (jsx(KeyboardShortcutToast, { message: toast.message, onClose: hideToast }, toast.id))] }));
|
|
6326
6354
|
});
|
|
6327
6355
|
|
|
6328
6356
|
function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -6738,7 +6766,10 @@ function WorkbenchStudioCanvas({ autoScroll, onAutoScrollChange, example, onExam
|
|
|
6738
6766
|
}
|
|
6739
6767
|
}, title: "Select run mode", children: [jsx("option", { value: "manual", children: "Manual" }), jsx("option", { value: "auto", children: "Auto" })] }), renderStartStopButton(), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: runAutoLayout, children: jsx(TreeStructureIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: jsx(CornersOutIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: download$1, children: jsx(DownloadIcon, { size: 24 }) }), jsx("input", { ref: uploadInputRef, type: "file", accept: "application/json,.json", className: "hidden", onChange: onUploadPicked }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: triggerUpload, children: jsx(UploadIcon, { size: 24 }) }), jsx("button", { className: "border border-gray-300 rounded p-1", onClick: async () => {
|
|
6740
6768
|
await downloadCanvasThumbnail(canvasContainerRef.current);
|
|
6741
|
-
}, title: "Download Flow Thumbnail (SVG)", children: jsx(ImageIcon, { size: 24 }) }), jsxs("label", { className: "flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsx(BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxs("label", { className: "flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsx(ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxs("div", { className: "flex flex-1 min-h-0", children: [jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize
|
|
6769
|
+
}, title: "Download Flow Thumbnail (SVG)", children: jsx(ImageIcon, { size: 24 }) }), jsxs("label", { className: "flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsx(BugBeetleIcon, { size: 24, weight: debug ? "fill" : undefined })] }), jsxs("label", { className: "flex items-center gap-1", children: [jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsx(ListBulletsIcon, { size: 24, weight: showValues ? "fill" : undefined })] })] }), jsxs("div", { className: "flex flex-1 min-h-0", children: [jsx("div", { className: "flex-1 min-w-0", ref: canvasContainerRef, children: jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement, getDefaultNodeSize: overrides?.getDefaultNodeSize, reactFlowProps: {
|
|
6770
|
+
minZoom: 0.1,
|
|
6771
|
+
maxZoom: 5,
|
|
6772
|
+
} }) }), jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, contextPanel: overrides?.contextPanel })] })] }));
|
|
6742
6773
|
}
|
|
6743
6774
|
function WorkbenchStudio({ example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, backendOptions, overrides, onInit, onChange, }) {
|
|
6744
6775
|
const [registry, setRegistry] = useState(createSimpleGraphRegistry());
|