diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 5c618bd41..754b61d4a 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -86,6 +86,9 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { const templates = useTypesStore((state) => state.templates); const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const reactFlowWrapper = useRef(null); + const setPositionDictionary = useFlowStore( + (state) => state.setPositionDictionary, + ); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); const setReactFlowInstance = useFlowStore( @@ -338,7 +341,15 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { // 👇 make moving the canvas undoable autoSaveFlow(); updateCurrentFlow({ nodes }); - }, [takeSnapshot, autoSaveFlow, nodes, edges, reactFlowInstance]); + setPositionDictionary({}); + }, [ + takeSnapshot, + autoSaveFlow, + nodes, + edges, + reactFlowInstance, + setPositionDictionary, + ]); const onSelectionDragStart: SelectionDragHandler = useCallback(() => { // 👇 make dragging a selection undoable diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index dbf51ce11..56215a677 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -31,6 +31,7 @@ import { import { FlowStoreType, VertexLayerElementType } from "../types/zustand/flow"; import { buildFlowVerticesWithFallback } from "../utils/buildUtils"; import { + buildPositionDictionary, checkChatInput, cleanEdges, detectBrokenEdgesEdges, @@ -52,6 +53,19 @@ import { useTypesStore } from "./typesStore"; // this is our useStore hook that we can use in our components to get parts of the store and call actions const useFlowStore = create((set, get) => ({ + positionDictionary: {}, + setPositionDictionary: (positionDictionary) => { + set({ positionDictionary }); + }, + isPositionAvailable: (position: { x: number; y: number }) => { + if ( + get().positionDictionary[position.x] && + get().positionDictionary[position.x] === position.y + ) { + return false; + } + return true; + }, fitViewNode: (nodeId) => { if (get().reactFlowInstance && get().nodes.find((n) => n.id === nodeId)) { get().reactFlowInstance?.fitView({ nodes: [{ id: nodeId }] }); @@ -211,6 +225,7 @@ const useFlowStore = create((set, get) => ({ hasIO: inputs.length > 0 || outputs.length > 0, flowPool: {}, currentFlow: flow, + positionDictionary: {}, }); }, setIsBuilding: (isBuilding) => { @@ -386,6 +401,17 @@ const useFlowStore = create((set, get) => ({ y: position.y, }); + let internalPostionDictionary = get().positionDictionary; + if (Object.keys(internalPostionDictionary).length === 0) { + internalPostionDictionary = buildPositionDictionary(get().nodes); + } + while (!get().isPositionAvailable(insidePosition)) { + insidePosition.x += 10; + insidePosition.y += 10; + } + internalPostionDictionary[insidePosition.x] = insidePosition.y; + get().setPositionDictionary(internalPostionDictionary); + selection.nodes.forEach((node: AllNodeType) => { // Generate a unique node ID let newId = getNodeId(node.data.type); diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index 680a142ca..8cdaffa07 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -53,6 +53,12 @@ export type FlowPoolType = { }; export type FlowStoreType = { + //key x, y + positionDictionary: { [key: number]: number }; + isPositionAvailable: (position: { x: number; y: number }) => boolean; + setPositionDictionary: (positionDictionary: { + [key: number]: number; + }) => void; fitViewNode: (nodeId: string) => void; autoSaveFlow: (() => void) | undefined; componentsToUpdate: string[]; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index b90fafd78..8aa4dff72 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -1746,3 +1746,11 @@ export function someFlowTemplateFields( export function checkHasToolMode(template: APITemplateType) { return template && Object.values(template).some((field) => field.tool_mode); } + +export function buildPositionDictionary(nodes: AllNodeType[]) { + const positionDictionary = {}; + nodes.forEach((node) => { + positionDictionary[node.position.x] = node.position.y; + }); + return positionDictionary; +}