diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 9720a9862..7201bba3b 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -57,7 +57,6 @@ export default function ParameterComponent({ proxy, showNode, index = "", - isMinimized, }: ParameterComponentType): JSX.Element { const ref = useRef(null); const refHtml = useRef(null); @@ -289,7 +288,7 @@ export default function ParameterComponent({ className={classNames( left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ", "h-3 w-3 rounded-full border-2 bg-background", - isMinimized ? "mt-0" : "" + !showNode ? "mt-0" : "" )} style={{ borderColor: color, diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 4e03c94fe..e94491941 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { NodeToolbar, useUpdateNodeInternals } from "reactflow"; +import { NodeToolbar } from "reactflow"; import ShadTooltip from "../../components/ShadTooltipComponent"; import Tooltip from "../../components/TooltipComponent"; import IconComponent from "../../components/genericIconComponent"; @@ -43,15 +43,12 @@ export default function GenericNode({ ); const [validationStatus, setValidationStatus] = useState(null); - const [handles, setHandles] = useState([]); - const [isMinimized, setIsMinimized] = useState(false); - let numberOfInputs: boolean[] = []; - const updateNodeInternals = useUpdateNodeInternals(); + const [handles, setHandles] = useState(0); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); function countHandles(): void { - numberOfInputs = Object.keys(data.node!.template) + let count = Object.keys(data.node!.template) .filter((templateField) => templateField.charAt(0) !== "_") .map((templateCamp) => { const { template } = data.node!; @@ -59,24 +56,20 @@ export default function GenericNode({ if (!template[templateCamp].show) return false; switch (template[templateCamp].type) { case "str": - return false; case "bool": - return false; case "float": - return false; case "code": - return false; case "prompt": - return false; case "file": - return false; case "int": return false; default: return true; } - }); - setHandles(numberOfInputs); + }) + .reduce((total, value) => total + (value ? 1 : 0), 0); + + setHandles(count); } useEffect(() => { @@ -119,10 +112,6 @@ export default function GenericNode({ const nameEditable = data.node?.flow || data.type === "CustomComponent"; - useEffect(() => { - updateNodeInternals(data.id); - }, [isMinimized]); - return ( <> @@ -141,7 +130,6 @@ export default function GenericNode({ }} numberOfHandles={handles} showNode={showNode} - setIsMinimized={setIsMinimized} > @@ -295,7 +283,6 @@ export default function GenericNode({ } proxy={data.node?.template[templateField].proxy} showNode={showNode} - isMinimized={isMinimized} /> ) )} @@ -322,7 +309,6 @@ export default function GenericNode({ type={data.node?.base_classes.join("|")} left={false} showNode={showNode} - isMinimized={isMinimized} /> )} @@ -563,7 +549,6 @@ export default function GenericNode({ } proxy={data.node?.template[templateField].proxy} showNode={showNode} - isMinimized={isMinimized} /> ) : ( <> @@ -607,7 +592,6 @@ export default function GenericNode({ type={data.node?.base_classes.join("|")} left={false} showNode={showNode} - isMinimized={isMinimized} /> )} diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index 5edebf0a5..959685500 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -146,6 +146,7 @@ export default function CodeAreaModal({ diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 99e94f233..ec07193b8 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -32,7 +32,6 @@ export default function NodeToolbarComponent({ setShowNode, numberOfHandles, showNode, - setIsMinimized, }: nodeToolbarPropsType): JSX.Element { const nodeLength = Object.keys(data.node!.template).filter( (templateField) => @@ -54,15 +53,7 @@ export default function NodeToolbarComponent({ const hasApiKey = useStoreStore((state) => state.hasApiKey); const validApiKey = useStoreStore((state) => state.validApiKey); - function canMinimize() { - let countHandles: number = 0; - numberOfHandles.forEach((bool) => { - if (bool) countHandles += 1; - }); - if (countHandles > 1) return false; - return true; - } - const isMinimal = canMinimize(); + const isMinimal = numberOfHandles <= 1; const isGroup = data.node?.flow ? true : false; const paste = useFlowStore((state) => state.paste); @@ -77,7 +68,6 @@ export default function NodeToolbarComponent({ const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const [showModalAdvanced, setShowModalAdvanced] = useState(false); const [showconfirmShare, setShowconfirmShare] = useState(false); - const [selectedValue, setSelectedValue] = useState(""); const [showOverrideModal, setShowOverrideModal] = useState(false); const [flowComponent, setFlowComponent] = useState(); @@ -98,10 +88,6 @@ export default function NodeToolbarComponent({ showconfirmShare, ]); - useEffect(() => { - setIsMinimized(!showNode); - }, [showNode]); - const handleSelectChange = (event) => { switch (event) { case "advanced": @@ -112,7 +98,7 @@ export default function NodeToolbarComponent({ setShowNode(data.showNode ?? true ? false : true); break; case "Download": - downloadNode(createFlowComponent(cloneDeep(data), version)); + downloadNode(flowComponent!); break; case "SaveAll": saveComponent(cloneDeep(data), false); @@ -124,8 +110,7 @@ export default function NodeToolbarComponent({ break; case "ungroup": takeSnapshot(); - updateFlowPosition(position, data.node?.flow!); - expandGroupNode(data, nodes, edges, setNodes, setEdges); + expandGroupNode(data.id, updateFlowPosition(position, data.node?.flow!), data.node!.template, nodes, edges, setNodes, setEdges); break; case "override": setShowOverrideModal(true); @@ -196,7 +181,7 @@ export default function NodeToolbarComponent({ )} -
diff --git a/src/frontend/src/stores/flowsManagerStore.ts b/src/frontend/src/stores/flowsManagerStore.ts index a75b68015..90d818ef9 100644 --- a/src/frontend/src/stores/flowsManagerStore.ts +++ b/src/frontend/src/stores/flowsManagerStore.ts @@ -24,6 +24,7 @@ import useAlertStore from "./alertStore"; import { useDarkStore } from "./darkStore"; import useFlowStore from "./flowStore"; import { useTypesStore } from "./typesStore"; +import { cloneDeep } from "lodash"; let saveTimeoutId: NodeJS.Timeout | null = null; @@ -328,13 +329,16 @@ const useFlowsManagerStore = create((set, get) => ({ takeSnapshot: () => { const currentFlowId = get().currentFlowId; // push the current graph to the past state - const newState = useFlowStore.getState(); + const flowStore = useFlowStore.getState(); + const newState = {nodes: cloneDeep(flowStore.nodes), edges: cloneDeep(flowStore.edges)}; const pastLength = past[currentFlowId]?.length ?? 0; if ( pastLength > 0 && - JSON.stringify(past[currentFlowId][pastLength - 1]) !== + JSON.stringify(past[currentFlowId][pastLength - 1]) === JSON.stringify(newState) - ) { + ) + return; + if (pastLength > 0) { past[currentFlowId] = past[currentFlowId].slice( pastLength - defaultOptions.maxHistorySize + 1, pastLength diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 837ba7cfc..714d77711 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -53,7 +53,6 @@ export type ParameterComponentType = { showNode?: boolean; index?: string; onCloseModal?: (close: boolean) => void; - isMinimized?: boolean; }; export type InputListComponentType = { value: string[]; @@ -479,9 +478,8 @@ export type nodeToolbarPropsType = { deleteNode: (idx: string) => void; position: XYPosition; setShowNode: (boolean: any) => void; - numberOfHandles: boolean[] | []; + numberOfHandles: number; showNode: boolean; - setIsMinimized: (boolean: boolean) => void; }; export type parsedDataType = { diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index 2c525ce7e..b25d25a8b 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -1,4 +1,4 @@ -import _, { cloneDeep } from "lodash"; +import { cloneDeep } from "lodash"; import { Connection, Edge, @@ -45,7 +45,7 @@ import { const uid = new ShortUniqueId({ length: 5 }); export function cleanEdges(nodes: Node[], edges: Edge[]) { - let newEdges = _.cloneDeep(edges); + let newEdges = cloneDeep(edges); edges.forEach((edge) => { // check if the source and target node still exists const sourceNode = nodes.find((node) => node.id === edge.source); @@ -88,7 +88,7 @@ export function cleanEdges(nodes: Node[], edges: Edge[]) { } export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) { - let newNodes = _.cloneDeep(data); + let newNodes = cloneDeep(data); newNodes.forEach((node: Node) => { node.selected = false; }); @@ -129,7 +129,7 @@ export function isValidConnection( } export function removeApiKeys(flow: FlowType): FlowType { - let cleanFLow = _.cloneDeep(flow); + let cleanFLow = cloneDeep(flow); cleanFLow.data!.nodes.forEach((node) => { for (const key in node.data.node.template) { if (node.data.node.template[key].password) { @@ -144,7 +144,7 @@ export function updateTemplate( reference: APITemplateType, objectToUpdate: APITemplateType ): APITemplateType { - let clonedObject: APITemplateType = _.cloneDeep(reference); + let clonedObject: APITemplateType = cloneDeep(reference); // Loop through each key in the reference object for (const key in clonedObject) { @@ -349,7 +349,7 @@ export function updateEdgesHandleIds({ edges, nodes, }: updateEdgesHandleIdsType): Edge[] { - let newEdges = _.cloneDeep(edges); + let newEdges = cloneDeep(edges); newEdges.forEach((edge) => { const sourceNodeId = edge.source; const targetNodeId = edge.target; @@ -653,10 +653,13 @@ export function updateFlowPosition(NewPosition: XYPosition, flow: FlowType) { x: NewPosition.x - middlePoint.x, y: NewPosition.y - middlePoint.y, }; - flow.data!.nodes.forEach((node) => { - node.position.x += deltaPosition.x; - node.position.y += deltaPosition.y; - }); + return {...flow, data: {...flow.data!, nodes: flow.data!.nodes.map((node) => ({ + ...node, + position: { + x: node.position.x + deltaPosition.x, + y: node.position.y + deltaPosition.y, + }, + }))}}; } export function concatFlows( @@ -759,7 +762,7 @@ export function mergeNodeTemplates({ */ let template: APITemplateType = {}; nodes.forEach((node) => { - let nodeTemplate = _.cloneDeep(node.data.node!.template); + let nodeTemplate = cloneDeep(node.data.node!.template); Object.keys(nodeTemplate) .filter((field_name) => field_name.charAt(0) !== "_") .forEach((key) => { @@ -844,9 +847,9 @@ export function generateNodeFromFlow( getNodeId: (type: string) => string ): NodeType { const { nodes } = flow.data!; - const outputNode = _.cloneDeep(findLastNode(flow.data!)); + const outputNode = cloneDeep(findLastNode(flow.data!)); const position = getMiddlePoint(nodes); - let data = _.cloneDeep(flow); + let data = cloneDeep(flow); const id = getNodeId(outputNode?.data.type!); const newGroupNode: NodeType = { data: { @@ -907,94 +910,6 @@ export function connectedInputNodesOnHandle( return connectedNodes; } -export function ungroupNode( - groupNode: NodeDataType, - BaseFlow: ReactFlowJsonObject -) { - const { template, flow } = groupNode.node!; - const gNodes: NodeType[] = flow!.data!.nodes; - const gEdges = flow!.data!.edges; - //redirect edges to correct proxy node - let updatedEdges: Edge[] = []; - BaseFlow.edges.forEach((edge) => { - let newEdge = _.cloneDeep(edge); - if (newEdge.target === groupNode.id) { - const targetHandle: targetHandleType = newEdge.data.targetHandle; - if (targetHandle.proxy) { - let type = targetHandle.type; - let field = targetHandle.proxy.field; - let proxyId = targetHandle.proxy.id; - let inputTypes = targetHandle.inputTypes; - let node: NodeType = gNodes.find((n) => n.id === proxyId)!; - if (node) { - newEdge.target = proxyId; - let newTargetHandle: targetHandleType = { - fieldName: field, - type, - id: proxyId, - inputTypes: inputTypes, - }; - if (node.data.node?.flow) { - newTargetHandle.proxy = { - field: node.data.node.template[field].proxy?.field!, - id: node.data.node.template[field].proxy?.id!, - }; - } - newEdge.data.targetHandle = newTargetHandle; - newEdge.targetHandle = scapedJSONStringfy(newTargetHandle); - } - } - } - if (newEdge.source === groupNode.id) { - const lastNode = _.cloneDeep(findLastNode(flow!.data!)); - newEdge.source = lastNode!.id; - let newSourceHandle: sourceHandleType = scapeJSONParse( - newEdge.sourceHandle! - ); - newSourceHandle.id = lastNode!.id; - newEdge.data.sourceHandle = newSourceHandle; - newEdge.sourceHandle = scapedJSONStringfy(newSourceHandle); - } - if (edge.target === groupNode.id || edge.source === groupNode.id) { - updatedEdges.push(newEdge); - } - }); - //update template values - Object.keys(template).forEach((key) => { - let { field, id } = template[key].proxy!; - let nodeIndex = gNodes.findIndex((n) => n.id === id); - if (nodeIndex !== -1) { - let display_name: string | undefined; - let show = gNodes[nodeIndex].data.node!.template[field].show; - let advanced = gNodes[nodeIndex].data.node!.template[field].advanced; - if (gNodes[nodeIndex].data.node!.template[field].display_name) { - display_name = - gNodes[nodeIndex].data.node!.template[field].display_name; - } else { - display_name = gNodes[nodeIndex].data.node!.template[field].name; - } - gNodes[nodeIndex].data.node!.template[field] = template[key]; - gNodes[nodeIndex].data.node!.template[field].show = show; - gNodes[nodeIndex].data.node!.template[field].advanced = advanced; - gNodes[nodeIndex].data.node!.template[field].display_name = display_name; - } - }); - - const nodes = [ - ...BaseFlow.nodes.filter((n) => n.id !== groupNode.id), - ...gNodes, - ]; - const edges = [ - ...BaseFlow.edges.filter( - (e) => e.target !== groupNode.id && e.source !== groupNode.id - ), - ...gEdges, - ...updatedEdges, - ]; - BaseFlow.nodes = nodes; - BaseFlow.edges = edges; -} - function updateProxyIdsOnTemplate( template: APITemplateType, idsMap: { [key: string]: string } @@ -1031,24 +946,25 @@ export function processFlowEdges(flow: FlowType) { } export function expandGroupNode( - groupNode: NodeDataType, + id: string, + flow: FlowType, + template: APITemplateType, nodes: Node[], edges: Edge[], setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, - setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void + setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void, ) { - const { template, flow } = _.cloneDeep(groupNode.node!); const idsMap = updateIds(flow!.data!); updateProxyIdsOnTemplate(template, idsMap); let flowEdges = edges; updateEdgesIds(flowEdges, idsMap); - const gNodes: NodeType[] = flow?.data?.nodes!; - const gEdges = flow!.data!.edges; + const gNodes: NodeType[] = cloneDeep(flow?.data?.nodes!); + const gEdges = cloneDeep(flow!.data!.edges); //redirect edges to correct proxy node let updatedEdges: Edge[] = []; flowEdges.forEach((edge) => { - let newEdge = _.cloneDeep(edge); - if (newEdge.target === groupNode.id) { + let newEdge = cloneDeep(edge); + if (newEdge.target === id) { const targetHandle: targetHandleType = newEdge.data.targetHandle; if (targetHandle.proxy) { let type = targetHandle.type; @@ -1075,8 +991,8 @@ export function expandGroupNode( } } } - if (newEdge.source === groupNode.id) { - const lastNode = _.cloneDeep(findLastNode(flow!.data!)); + if (newEdge.source === id) { + const lastNode = cloneDeep(findLastNode(flow!.data!)); newEdge.source = lastNode!.id; let newSourceHandle: sourceHandleType = scapeJSONParse( newEdge.sourceHandle! @@ -1085,7 +1001,7 @@ export function expandGroupNode( newEdge.data.sourceHandle = newSourceHandle; newEdge.sourceHandle = scapedJSONStringfy(newSourceHandle); } - if (edge.target === groupNode.id || edge.source === groupNode.id) { + if (edge.target === id || edge.source === id) { updatedEdges.push(newEdge); } }); @@ -1122,12 +1038,12 @@ export function expandGroupNode( }); const filteredNodes = [ - ...nodes.filter((n) => n.id !== groupNode.id), + ...nodes.filter((n) => n.id !== id), ...gNodes, ]; const filteredEdges = [ ...edges.filter( - (e) => e.target !== groupNode.id && e.source !== groupNode.id + (e) => e.target !== id && e.source !== id ), ...gEdges, ...updatedEdges, @@ -1136,17 +1052,6 @@ export function expandGroupNode( setEdges(filteredEdges); } -export function processFlow(FlowObject: ReactFlowJsonObject) { - let clonedFLow = _.cloneDeep(FlowObject); - clonedFLow.nodes.forEach((node: NodeType) => { - if (node.data.node?.flow) { - processFlow(node.data.node!.flow!.data!); - ungroupNode(node.data, clonedFLow); - } - }); - return clonedFLow; -} - export function getGroupStatus( flow: FlowType, ssData: { [key: string]: { valid: boolean; params: string } } @@ -1170,13 +1075,12 @@ export function createFlowComponent( nodeData: NodeDataType, version: string ): FlowType { - nodeData.node!.official = false; const flowNode: FlowType = { data: { edges: [], nodes: [ { - data: nodeData, + data: {...nodeData, node: {...nodeData.node, official: false}}, id: nodeData.id, position: { x: 0, y: 0 }, type: "genericNode",