This commit is contained in:
cristhianzl 2024-01-05 11:38:33 -03:00
commit 34592f87e8
30 changed files with 437 additions and 556 deletions

View file

@ -154,7 +154,6 @@ class OllamaLLM(CustomComponent):
# None Value remove # None Value remove
llm_params = {k: v for k, v in llm_params.items() if v is not None} llm_params = {k: v for k, v in llm_params.items() if v is not None}
try: try:
llm = Ollama(**llm_params) llm = Ollama(**llm_params)
except Exception as e: except Exception as e:

View file

@ -15,28 +15,20 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
display_name: str = "Vectara Self Query Retriever for Vectara Vector Store" display_name: str = "Vectara Self Query Retriever for Vectara Vector Store"
description: str = "Implementation of Vectara Self Query Retriever" description: str = "Implementation of Vectara Self Query Retriever"
documentation = ( documentation = "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
"https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
)
beta = True beta = True
field_config = { field_config = {
"code": {"show": True}, "code": {"show": True},
"vectorstore": { "vectorstore": {"display_name": "Vector Store", "info": "Input Vectara Vectore Store"},
"display_name": "Vector Store", "llm": {"display_name": "LLM", "info": "For self query retriever"},
"info": "Input Vectara Vectore Store"
},
"llm": {
"display_name": "LLM",
"info": "For self query retriever"
},
"document_content_description": { "document_content_description": {
"display_name": "Document Content Description", "display_name": "Document Content Description",
"info": "For self query retriever", "info": "For self query retriever",
}, },
"metadata_field_info": { "metadata_field_info": {
"display_name": "Metadata Field Info", "display_name": "Metadata Field Info",
"info": "Each metadata field info is a string in the form of key value pair dictionary containing additional search metadata.\nExample input: {\"name\":\"speech\",\"description\":\"what name of the speech\",\"type\":\"string or list[string]\"}.\nThe keys should remain constant(name, description, type)", "info": 'Each metadata field info is a string in the form of key value pair dictionary containing additional search metadata.\nExample input: {"name":"speech","description":"what name of the speech","type":"string or list[string]"}.\nThe keys should remain constant(name, description, type)',
}, },
} }
@ -47,24 +39,19 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
llm: BaseLanguageModel, llm: BaseLanguageModel,
metadata_field_info: List[str], metadata_field_info: List[str],
) -> BaseRetriever: ) -> BaseRetriever:
metadata_field_obj = [] metadata_field_obj = []
for meta in metadata_field_info: for meta in metadata_field_info:
meta_obj = json.loads(meta) meta_obj = json.loads(meta)
if 'name' not in meta_obj or 'description' not in meta_obj or 'type' not in meta_obj : if "name" not in meta_obj or "description" not in meta_obj or "type" not in meta_obj:
raise Exception('Incorrect metadata field info format.') raise Exception("Incorrect metadata field info format.")
attribute_info = AttributeInfo( attribute_info = AttributeInfo(
name = meta_obj['name'], name=meta_obj["name"],
description = meta_obj['description'], description=meta_obj["description"],
type = meta_obj['type'], type=meta_obj["type"],
) )
metadata_field_obj.append(attribute_info) metadata_field_obj.append(attribute_info)
return SelfQueryRetriever.from_llm( return SelfQueryRetriever.from_llm(
llm, llm, vectorstore, document_content_description, metadata_field_obj, verbose=True
vectorstore,
document_content_description,
metadata_field_obj,
verbose=True
) )

View file

@ -16,7 +16,6 @@ import {
FETCH_ERROR_MESSAGE, FETCH_ERROR_MESSAGE,
} from "./constants/constants"; } from "./constants/constants";
import { alertContext } from "./contexts/alertContext"; import { alertContext } from "./contexts/alertContext";
import { FlowsContext } from "./contexts/flowsContext";
import { locationContext } from "./contexts/locationContext"; import { locationContext } from "./contexts/locationContext";
import { typesContext } from "./contexts/typesContext"; import { typesContext } from "./contexts/typesContext";
import Router from "./routes"; import Router from "./routes";
@ -30,7 +29,6 @@ export default function App() {
setShowSideBar(true); setShowSideBar(true);
setIsStackedOpen(true); setIsStackedOpen(true);
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]); }, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
const { hardReset } = useContext(FlowsContext);
const { const {
errorData, errorData,
@ -136,10 +134,7 @@ export default function App() {
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<ErrorBoundary <ErrorBoundary
onReset={() => { onReset={() => {
window.localStorage.removeItem("tabsData"); // any reset function
window.localStorage.clear();
hardReset();
window.location.href = window.location.href;
}} }}
FallbackComponent={CrashErrorComponent} FallbackComponent={CrashErrorComponent}
> >

View file

@ -31,6 +31,7 @@ import { FlowsContext } from "../../../../contexts/flowsContext";
import { typesContext } from "../../../../contexts/typesContext"; import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { postCustomComponentUpdate } from "../../../../controllers/API"; import { postCustomComponentUpdate } from "../../../../controllers/API";
import useFlow from "../../../../stores/flowManagerStore";
import { APIClassType } from "../../../../types/api"; import { APIClassType } from "../../../../types/api";
import { ParameterComponentType } from "../../../../types/components"; import { ParameterComponentType } from "../../../../types/components";
import { NodeDataType } from "../../../../types/flow"; import { NodeDataType } from "../../../../types/flow";
@ -70,13 +71,8 @@ export default function ParameterComponent({
const { setErrorData, modalContextOpen } = useContext(alertContext); const { setErrorData, modalContextOpen } = useContext(alertContext);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0); const [position, setPosition] = useState(0);
const { const { tabId, flows } = useContext(FlowsContext);
tabId, const { nodes, edges, setNode } = useFlow();
flows,
nodes,
edges,
setNode,
} = useContext(FlowsContext);
const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null; const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null;
@ -133,8 +129,8 @@ export default function ParameterComponent({
let newNode = cloneDeep(oldNode); let newNode = cloneDeep(oldNode);
newNode.data = { newNode.data = {
...newNode.data ...newNode.data,
} };
newNode.data.node.template[name].value = newValue; newNode.data.node.template[name].value = newValue;

View file

@ -7,7 +7,6 @@ import InputComponent from "../../components/inputComponent";
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";
import { priorityFields } from "../../constants/constants"; import { priorityFields } from "../../constants/constants";
import { useSSE } from "../../contexts/SSEContext"; import { useSSE } from "../../contexts/SSEContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { undoRedoContext } from "../../contexts/undoRedoContext"; import { undoRedoContext } from "../../contexts/undoRedoContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
@ -17,6 +16,7 @@ import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils"; import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, cn, getFieldTitle } from "../../utils/utils"; import { classNames, cn, getFieldTitle } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
import useFlow from "../../stores/flowManagerStore";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -30,7 +30,7 @@ export default function GenericNode({
yPos: number; yPos: number;
}): JSX.Element { }): JSX.Element {
const { types } = useContext(typesContext); const { types } = useContext(typesContext);
const { deleteNode, setNode } = useContext(FlowsContext); const { deleteNode, setNode } = useFlow();
const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
const [inputName, setInputName] = useState(false); const [inputName, setInputName] = useState(false);
const [nodeName, setNodeName] = useState(data.node!.display_name); const [nodeName, setNodeName] = useState(data.node!.display_name);

View file

@ -3,7 +3,6 @@ import { useContext, useState } from "react";
import Loading from "../../../components/ui/loading"; import Loading from "../../../components/ui/loading";
import { useSSE } from "../../../contexts/SSEContext"; import { useSSE } from "../../../contexts/SSEContext";
import { alertContext } from "../../../contexts/alertContext"; import { alertContext } from "../../../contexts/alertContext";
import { typesContext } from "../../../contexts/typesContext";
import { postBuildInit } from "../../../controllers/API"; import { postBuildInit } from "../../../controllers/API";
import { FlowType } from "../../../types/flow"; import { FlowType } from "../../../types/flow";
@ -13,6 +12,7 @@ import { FlowsState } from "../../../types/tabs";
import { validateNodes } from "../../../utils/reactflowUtils"; import { validateNodes } from "../../../utils/reactflowUtils";
import RadialProgressComponent from "../../RadialProgress"; import RadialProgressComponent from "../../RadialProgress";
import IconComponent from "../../genericIconComponent"; import IconComponent from "../../genericIconComponent";
import useFlow from "../../../stores/flowManagerStore";
export default function BuildTrigger({ export default function BuildTrigger({
open, open,
@ -25,7 +25,8 @@ export default function BuildTrigger({
isBuilt: boolean; isBuilt: boolean;
}): JSX.Element { }): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE(); const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
const { setTabsState, saveFlow, nodes, edges } = useContext(FlowsContext); const { setTabsState, saveFlow } = useContext(FlowsContext);
const { nodes, edges } = useFlow();
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);
const [isIconTouched, setIsIconTouched] = useState(false); const [isIconTouched, setIsIconTouched] = useState(false);
const eventClick = isBuilding ? "pointer-events-none" : ""; const eventClick = isBuilding ? "pointer-events-none" : "";
@ -36,10 +37,7 @@ export default function BuildTrigger({
if (isBuilding) { if (isBuilding) {
return; return;
} }
const errors = validateNodes( const errors = validateNodes(nodes, edges);
nodes,
edges
);
if (errors.length > 0) { if (errors.length > 0) {
setErrorData({ setErrorData({
title: "Oops! Looks like you missed something", title: "Oops! Looks like you missed something",

View file

@ -9,11 +9,14 @@ import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API"; import { getBuildStatus } from "../../controllers/API";
import FormModal from "../../modals/formModal"; import FormModal from "../../modals/formModal";
import { NodeType } from "../../types/flow"; import { NodeType } from "../../types/flow";
import useFlow from "../../stores/flowManagerStore";
export default function Chat({ flow }: ChatType): JSX.Element { export default function Chat({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [canOpen, setCanOpen] = useState(false); const [canOpen, setCanOpen] = useState(false);
const { tabsState, isBuilt, setIsBuilt, isPending } = useContext(FlowsContext); const { isBuilt, setIsBuilt, isPending } = useFlow();
const { tabsState } =
useContext(FlowsContext);
useEffect(() => { useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {

View file

@ -28,7 +28,6 @@ import {
TabsTrigger, TabsTrigger,
} from "../../components/ui/tabs"; } from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants"; import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
import { FlowsContext } from "../../contexts/flowsContext";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import { codeTabsPropsType } from "../../types/components"; import { codeTabsPropsType } from "../../types/components";
import { import {
@ -41,6 +40,7 @@ import { classNames } from "../../utils/utils";
import DictComponent from "../dictComponent"; import DictComponent from "../dictComponent";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import KeypairListComponent from "../keypairListComponent"; import KeypairListComponent from "../keypairListComponent";
import useFlow from "../../stores/flowManagerStore";
export default function CodeTabsComponent({ export default function CodeTabsComponent({
flow, flow,
@ -53,9 +53,9 @@ export default function CodeTabsComponent({
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null); const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState<string[]>([]); const [openAccordion, setOpenAccordion] = useState<string[]>([]);
const dark = useDarkStore((state) => state.dark); const {dark} = useDarkStore();
const { setNodes } = useContext(FlowsContext); const { setNodes } = useFlow();
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
useEffect(() => { useEffect(() => {

View file

@ -8,7 +8,6 @@ import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import { useStoreStore } from "../../stores/storeStore";
import { gradients } from "../../utils/styleUtils"; import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
@ -30,12 +29,7 @@ export default function Header(): JSX.Element {
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext); const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const navigate = useNavigate(); const navigate = useNavigate();
const hasStore = useStoreStore((state) => state.hasStore); const { dark, setDark, stars, gradientIndex } = useDarkStore();
const dark = useDarkStore((state) => state.dark);
const setDark = useDarkStore((state) => state.updateDark);
const stars = useDarkStore((state) => state.stars);
const gradientIndex = useDarkStore((state) => state.gradientIndex);
useEffect(() => { useEffect(() => {
if (dark) { if (dark) {

View file

@ -12,7 +12,7 @@ export default function PageLayout({
description: string; description: string;
children: React.ReactNode; children: React.ReactNode;
button?: React.ReactNode; button?: React.ReactNode;
betaIcon: boolean; betaIcon?: boolean;
}) { }) {
return ( return (
<div className="flex h-screen w-full flex-col"> <div className="flex h-screen w-full flex-col">

View file

@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useDarkStore } from "../../stores/darkStore";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import { Badge } from "../ui/badge"; import { Badge } from "../ui/badge";
@ -24,7 +23,6 @@ export function TagsSelector({
: selectedTags.filter((_, i) => i !== index); : selectedTags.filter((_, i) => i !== index);
setSelectedTags(newArray); setSelectedTags(newArray);
}; };
const dark = useDarkStore((state) => state.dark);
const scrollContainerRef = useRef<HTMLDivElement>(null); const scrollContainerRef = useRef<HTMLDivElement>(null);
const fadeContainerRef = useRef<HTMLDivElement>(null); const fadeContainerRef = useRef<HTMLDivElement>(null);

View file

@ -31,7 +31,7 @@ import {
updateFlowInDatabase, updateFlowInDatabase,
uploadFlowsToDatabase, uploadFlowsToDatabase,
} from "../controllers/API"; } from "../controllers/API";
import { APIClassType, APITemplateType } from "../types/api"; import { APIClassType } from "../types/api";
import { tweakType } from "../types/components"; import { tweakType } from "../types/components";
import { import {
FlowType, FlowType,
@ -46,12 +46,13 @@ import {
checkOldEdgesHandles, checkOldEdgesHandles,
cleanEdges, cleanEdges,
createFlowComponent, createFlowComponent,
processFlowEdges,
removeFileNameFromComponents, removeFileNameFromComponents,
scapeJSONParse, scapeJSONParse,
scapedJSONStringfy, scapedJSONStringfy,
updateEdges,
updateEdgesHandleIds, updateEdgesHandleIds,
updateIds, updateIds,
updateTemplate,
} from "../utils/reactflowUtils"; } from "../utils/reactflowUtils";
import { import {
createRandomKey, createRandomKey,
@ -61,10 +62,12 @@ import {
import { alertContext } from "./alertContext"; import { alertContext } from "./alertContext";
import { AuthContext } from "./authContext"; import { AuthContext } from "./authContext";
import { typesContext } from "./typesContext"; import { typesContext } from "./typesContext";
import useFlow from "../stores/flowManagerStore";
const uid = new ShortUniqueId({ length: 5 }); const uid = new ShortUniqueId({ length: 5 });
const FlowsContextInitialValue: FlowsContextType = { const FlowsContextInitialValue: FlowsContextType = {
//Remove tab id and get current id from url
tabId: "", tabId: "",
setTabId: (index: string) => {}, setTabId: (index: string) => {},
isLoading: true, isLoading: true,
@ -75,41 +78,16 @@ const FlowsContextInitialValue: FlowsContextType = {
flowData?: FlowType, flowData?: FlowType,
override?: boolean override?: boolean
) => "", ) => "",
deleteNode: () => {},
deleteEdge: () => {},
incrementNodeId: () => uid(),
downloadFlow: (flow: FlowType) => {}, downloadFlow: (flow: FlowType) => {},
downloadFlows: () => {}, downloadFlows: () => {},
uploadFlows: () => {}, uploadFlows: () => {},
uploadFlow: async () => "", uploadFlow: async () => "",
isBuilt: false,
setIsBuilt: (state: boolean) => {},
hardReset: () => {},
saveFlow: async (flow?: FlowType, silent?: boolean) => {}, saveFlow: async (flow?: FlowType, silent?: boolean) => {},
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
isPending: false,
setPending: (pending: boolean) => {},
tabsState: {}, tabsState: {},
setTabsState: () => {}, setTabsState: () => {},
getNodeId: (nodeType: string) => "",
setTweak: (tweak: any) => {},
getTweak: [],
paste: (
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
) => {},
saveComponent: async (component: NodeDataType, override: boolean) => "", saveComponent: async (component: NodeDataType, override: boolean) => "",
deleteComponent: (key: string) => {}, deleteComponent: (key: string) => {},
version: "", version: "",
nodes: [],
setNodes: () => {},
setNode: () => {},
getNode: () => undefined,
onNodesChange: () => {},
edges: [],
setEdges: () => {},
onEdgesChange: () => {},
}; };
export const FlowsContext = createContext<FlowsContextType>( export const FlowsContext = createContext<FlowsContextType>(
@ -123,107 +101,10 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const [tabId, setTabId] = useState(""); const [tabId, setTabId] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [flows, setFlows] = useState<Array<FlowType>>([]); const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uid()); const { setData } = useContext(typesContext);
const { reactFlowInstance, setData } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const [tabsState, setTabsState] = useState<FlowsState>({}); const [tabsState, setTabsState] = useState<FlowsState>({});
const [getTweak, setTweak] = useState<tweakType>([]);
const [nodes, setNodesInternal, onNodesChangeInternal] = useNodesState([]); const {nodes, edges, paste, setPending, reactFlowInstance} = useFlow();
const [edges, setEdgesInternal, onEdgesChangeInternal] = useEdgesState([]);
const setPending = (pending: boolean) => {
setTabsState((prev: FlowsState) => {
return {
...prev,
[tabId]: {
...prev[tabId],
isPending: pending,
},
};
});
};
const isPending = tabsState[tabId]?.isPending ?? false;
const onNodesChange = useCallback(
(change: NodeChange[]) => {
onNodesChangeInternal(change);
if(!isPending)
setPending(true);
},
[onNodesChangeInternal, setPending, isPending]
);
const onEdgesChange = useCallback(
(edges: EdgeChange[]) => {
onEdgesChangeInternal(edges);
if(!isPending)
setPending(true);
},
[onEdgesChangeInternal, setPending, isPending]
);
const setNodes = (change: Node[] | ((oldState: Node[]) => Node[])) => {
let newChange = typeof change === "function" ? change(nodes) : change;
let newEdges = cleanEdges(newChange, edges);
saveCurrentFlow(
newChange,
newEdges,
reactFlowInstance?.getViewport() ?? { zoom: 1, x: 0, y: 0 }
);
setEdgesInternal(newEdges);
setNodesInternal(newChange);
};
const setNode = (id: string, change: Node | ((oldState: Node) => Node)) => {
let newChange =
typeof change === "function"
? change(nodes.find((node) => node.id === id)!)
: change;
setNodes((oldNodes) =>
oldNodes.map((node) => {
if (node.id === id) {
return newChange;
}
return node;
})
);
};
const getNode = (id: string) => {
return nodes.find((node) => node.id === id);
};
const setEdges = (change: Edge[] | ((oldState: Edge[]) => Edge[])) => {
let newChange = typeof change === "function" ? change(edges) : change;
saveCurrentFlow(
nodes,
newChange,
reactFlowInstance?.getViewport() ?? { zoom: 1, x: 0, y: 0 }
);
setEdgesInternal(newChange);
};
useEffect(() => {
if (!isAuthenticated) {
hardReset();
}
}, [isAuthenticated]);
const newNodeId = useRef(uid());
function incrementNodeId() {
newNodeId.current = uid();
return newNodeId.current;
}
function refreshFlows() { function refreshFlows() {
setIsLoading(true); setIsLoading(true);
@ -231,7 +112,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
if (DbData) { if (DbData) {
try { try {
processFlows(DbData, false); processFlows(DbData, false);
updateStateWithDbData(DbData); setFlows(DbData);
setIsLoading(false); setIsLoading(false);
} catch (e) {} } catch (e) {}
} }
@ -281,31 +162,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
}); });
} }
function processFlowEdges(flow: FlowType) {
if (!flow.data || !flow.data.edges) return;
if (checkOldEdgesHandles(flow.data.edges)) {
const newEdges = updateEdgesHandleIds(flow.data);
flow.data.edges = newEdges;
}
//update edges colors
flow.data.edges.forEach((edge) => {
edge.className = "";
edge.style = { stroke: "#555" };
});
}
function updateStateWithDbData(tabsData: FlowType[]) {
setFlows(tabsData);
}
function hardReset() {
newNodeId.current = uid();
setTabId("");
setFlows([]);
setIsLoading(true);
setId(uid());
}
/** /**
* Downloads the current flow as a JSON file * Downloads the current flow as a JSON file
*/ */
@ -353,11 +209,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
link.click(); link.click();
}); });
} }
function getNodeId(nodeType: string) {
return nodeType + "-" + incrementNodeId();
}
/** /**
* Creates a file input and listens to a change event to upload a JSON flow file. * Creates a file input and listens to a change event to upload a JSON flow file.
* If the file type is application/json, the file is read and parsed into a JSON object. * If the file type is application/json, the file is read and parsed into a JSON object.
@ -466,111 +317,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
processFlows(flows.filter((flow) => flow.id !== id)); processFlows(flows.filter((flow) => flow.id !== id));
} }
} }
/**
* Add a new flow to the list of flows.
* @param flow Optional flow to add.
*/
function paste(
selectionInstance: { nodes: Node[]; edges: Edge[] },
position: { x: number; y: number; paneX?: number; paneY?: number }
) {
let minimumX = Infinity;
let minimumY = Infinity;
let idsMap = {};
let newNodes: Node<NodeDataType>[] = nodes;
let newEdges = edges;
selectionInstance.nodes.forEach((node: Node) => {
if (node.position.y < minimumY) {
minimumY = node.position.y;
}
if (node.position.x < minimumX) {
minimumX = node.position.x;
}
});
const insidePosition = position.paneX
? { x: position.paneX + position.x, y: position.paneY! + position.y }
: reactFlowInstance!.screenToFlowPosition({
x: position.x,
y: position.y,
});
selectionInstance.nodes.forEach((node: NodeType) => {
// Generate a unique node ID
let newId = getNodeId(node.data.type);
idsMap[node.id] = newId;
// Create a new node object
const newNode: NodeType = {
id: newId,
type: "genericNode",
position: {
x: insidePosition.x + node.position!.x - minimumX,
y: insidePosition.y + node.position!.y - minimumY,
},
data: {
..._.cloneDeep(node.data),
id: newId,
},
};
// Add the new node to the list of nodes in state
newNodes = newNodes
.map((node) => ({ ...node, selected: false }))
.concat({ ...newNode, selected: false });
});
setNodes(newNodes);
selectionInstance.edges.forEach((edge: Edge) => {
let source = idsMap[edge.source];
let target = idsMap[edge.target];
const sourceHandleObject: sourceHandleType = scapeJSONParse(
edge.sourceHandle!
);
let sourceHandle = scapedJSONStringfy({
...sourceHandleObject,
id: source,
});
sourceHandleObject.id = source;
edge.data.sourceHandle = sourceHandleObject;
const targetHandleObject: targetHandleType = scapeJSONParse(
edge.targetHandle!
);
let targetHandle = scapedJSONStringfy({
...targetHandleObject,
id: target,
});
targetHandleObject.id = target;
edge.data.targetHandle = targetHandleObject;
let id =
"reactflow__edge-" +
source +
sourceHandle +
"-" +
target +
targetHandle;
newEdges = addEdge(
{
source,
target,
sourceHandle,
targetHandle,
id,
data: cloneDeep(edge.data),
style: { stroke: "#555" },
className:
targetHandleObject.type === "Text"
? "stroke-gray-800 "
: "stroke-gray-900 ",
animated: targetHandleObject.type === "Text",
selected: false,
},
newEdges.map((edge) => ({ ...edge, selected: false }))
);
});
setEdges(newEdges);
}
const addFlow = async ( const addFlow = async (
newProject: Boolean, newProject: Boolean,
@ -634,26 +380,12 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
//add animation to text type edges //add animation to text type edges
updateEdges(data.edges); updateEdges(data.edges);
// updateNodes(data.nodes, data.edges); // updateNodes(data.nodes, data.edges);
if (refreshIds) updateIds(data, getNodeId); // Assuming updateIds is defined elsewhere if (refreshIds) updateIds(data); // Assuming updateIds is defined elsewhere
} }
return data; return data;
}; };
const updateEdges = (edges: Edge[]) => {
if (edges)
edges.forEach((edge) => {
const targetHandleObject: targetHandleType = scapeJSONParse(
edge.targetHandle!
);
edge.className =
(targetHandleObject.type === "Text"
? "stroke-gray-800 "
: "stroke-gray-900 ") + " stroke-connection";
edge.animated = targetHandleObject.type === "Text";
});
};
const createNewFlow = ( const createNewFlow = (
flowData: ReactFlowJsonObject | null, flowData: ReactFlowJsonObject | null,
flow: FlowType flow: FlowType
@ -697,7 +429,11 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const saveTimeoutId = useRef<NodeJS.Timeout | null>(null); const saveTimeoutId = useRef<NodeJS.Timeout | null>(null);
const saveCurrentFlow = (nodes: Node[], edges: Edge[], viewport: Viewport) => { const saveCurrentFlow = (
nodes: Node[],
edges: Edge[],
viewport: Viewport
) => {
// Clear the previous timeout if it exists. // Clear the previous timeout if it exists.
if (saveTimeoutId.current) { if (saveTimeoutId.current) {
clearTimeout(saveTimeoutId.current); clearTimeout(saveTimeoutId.current);
@ -710,8 +446,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
saveFlow({ ...currentFlow, data: { nodes, edges, viewport } }, true); saveFlow({ ...currentFlow, data: { nodes, edges, viewport } }, true);
} }
}, 300); // Delay of 300ms. }, 300); // Delay of 300ms.
} };
async function saveFlow(flow?: FlowType, silent?: boolean) { async function saveFlow(flow?: FlowType, silent?: boolean) {
let newFlow; let newFlow;
@ -765,7 +500,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
} }
} }
const [isBuilt, setIsBuilt] = useState(false);
// Initialize state variable for the version // Initialize state variable for the version
const [version, setVersion] = useState(""); const [version, setVersion] = useState("");
useEffect(() => { useEffect(() => {
@ -774,63 +508,26 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
}); });
}, []); }, []);
function deleteNode(idx: string | Array<string>) {
setNodes((oldNodes) =>
oldNodes.filter((node) =>
typeof idx === "string" ? node.id !== idx : !idx.includes(node.id)
)
);
}
function deleteEdge(idx: string | Array<string>) {
setEdges((oldEdges) =>
oldEdges.filter((edge) =>
typeof idx === "string" ? edge.id !== idx : !idx.includes(edge.id)
)
);
}
return ( return (
<FlowsContext.Provider <FlowsContext.Provider
value={{ value={{
version, version,
flows,
saveFlow, saveFlow,
isBuilt,
setIsBuilt,
lastCopiedSelection,
setLastCopiedSelection,
hardReset,
tabId, tabId,
setTabId, setTabId,
flows,
incrementNodeId,
removeFlow, removeFlow,
addFlow, addFlow,
downloadFlow, downloadFlow,
downloadFlows, downloadFlows,
uploadFlows, uploadFlows,
uploadFlow, uploadFlow,
getNodeId,
deleteNode,
deleteEdge,
isPending,
setPending,
tabsState, tabsState,
setTabsState, setTabsState,
paste,
getTweak,
setTweak,
isLoading, isLoading,
saveComponent, saveComponent,
deleteComponent, deleteComponent,
nodes,
setNodes,
setNode,
getNode,
onNodesChange,
edges,
setEdges,
onEdgesChange,
}} }}
> >
{children} {children}

View file

@ -16,8 +16,6 @@ import { AuthContext } from "./authContext";
//context to share types adn functions from nodes to flow //context to share types adn functions from nodes to flow
const initialValue: typesContextType = { const initialValue: typesContextType = {
reactFlowInstance: null,
setReactFlowInstance: (newState: ReactFlowInstance) => {},
types: {}, types: {},
setTypes: () => {}, setTypes: () => {},
templates: {}, templates: {},
@ -34,8 +32,6 @@ export const typesContext = createContext<typesContextType>(initialValue);
export function TypesProvider({ children }: { children: ReactNode }) { export function TypesProvider({ children }: { children: ReactNode }) {
const [types, setTypes] = useState({}); const [types, setTypes] = useState({});
const [reactFlowInstance, setReactFlowInstance] =
useState<ReactFlowInstance | null>(null);
const [templates, setTemplates] = useState({}); const [templates, setTemplates] = useState({});
const [data, setData] = useState({}); const [data, setData] = useState({});
const [fetchError, setFetchError] = useState(false); const [fetchError, setFetchError] = useState(false);
@ -95,14 +91,11 @@ export function TypesProvider({ children }: { children: ReactNode }) {
} }
} }
return ( return (
<typesContext.Provider <typesContext.Provider
value={{ value={{
types, types,
setTypes, setTypes,
reactFlowInstance,
setReactFlowInstance,
setTemplates, setTemplates,
templates, templates,
data, data,

View file

@ -6,7 +6,6 @@ import {
useEffect, useEffect,
useState, useState,
} from "react"; } from "react";
import { useReactFlow } from "reactflow";
import { import {
HistoryItem, HistoryItem,
UseUndoRedoOptions, UseUndoRedoOptions,
@ -14,6 +13,7 @@ import {
} from "../types/typesContext"; } from "../types/typesContext";
import { isWrappedWithClass } from "../utils/utils"; import { isWrappedWithClass } from "../utils/utils";
import { FlowsContext } from "./flowsContext"; import { FlowsContext } from "./flowsContext";
import useFlow from "../stores/flowManagerStore";
const initialValue = { const initialValue = {
undo: () => {}, undo: () => {},
@ -29,7 +29,10 @@ const defaultOptions: UseUndoRedoOptions = {
export const undoRedoContext = createContext<undoRedoContextType>(initialValue); export const undoRedoContext = createContext<undoRedoContextType>(initialValue);
export function UndoRedoProvider({ children }) { export function UndoRedoProvider({ children }) {
const { tabId, flows, setNodes, setEdges, nodes, edges } = useContext(FlowsContext); const { tabId, flows } =
useContext(FlowsContext);
const {setNodes, setEdges, nodes, edges} = useFlow();
const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => [])); const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => [])); const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));

View file

@ -48,7 +48,8 @@ const ApiModal = forwardRef(
const [activeTab, setActiveTab] = useState("0"); const [activeTab, setActiveTab] = useState("0");
const tweak = useRef<tweakType>([]); const tweak = useRef<tweakType>([]);
const tweaksList = useRef<string[]>([]); const tweaksList = useRef<string[]>([]);
const { setTweak, getTweak, tabsState } = useContext(FlowsContext); const { tabsState } = useContext(FlowsContext);
const [getTweak, setTweak] = useState<tweakType>([]);
const pythonApiCode = getPythonApiCode( const pythonApiCode = getPythonApiCode(
flow, flow,
autoLogin, autoLogin,

View file

@ -29,10 +29,7 @@ import {
limitScrollFieldsModal, limitScrollFieldsModal,
} from "../../constants/constants"; } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { FlowsState } from "../../types/tabs";
import { import {
convertObjToArray, convertObjToArray,
convertValuesToNumbers, convertValuesToNumbers,
@ -41,6 +38,7 @@ import {
} from "../../utils/reactflowUtils"; } from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
import useFlow from "../../stores/flowManagerStore";
const EditNodeModal = forwardRef( const EditNodeModal = forwardRef(
( (
@ -59,7 +57,7 @@ const EditNodeModal = forwardRef(
) => { ) => {
const [myData, setMyData] = useState(data); const [myData, setMyData] = useState(data);
const { setPending, edges, setNode } = useContext(FlowsContext); const { setPending, edges, setNode } = useFlow();
const { setModalContextOpen } = useContext(alertContext); const { setModalContextOpen } = useContext(alertContext);
function changeAdvanced(n) { function changeAdvanced(n) {

View file

@ -26,7 +26,7 @@ export default function CodeAreaModal({
readonly = false, readonly = false,
}: codeAreaModalPropsType): JSX.Element { }: codeAreaModalPropsType): JSX.Element {
const [code, setCode] = useState(value); const [code, setCode] = useState(value);
const dark = useDarkStore((state) => state.dark); const {dark} = useDarkStore();
const [height, setHeight] = useState<string | null>(null); const [height, setHeight] = useState<string | null>(null);
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);

View file

@ -6,7 +6,6 @@ import { Checkbox } from "../../components/ui/checkbox";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants"; import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { removeApiKeys } from "../../utils/reactflowUtils"; import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";

View file

@ -1,6 +1,5 @@
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { typesContext } from "../../contexts/typesContext";
import { sendAllProps } from "../../types/api"; import { sendAllProps } from "../../types/api";
import { ChatMessageType } from "../../types/chat"; import { ChatMessageType } from "../../types/chat";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
@ -26,6 +25,7 @@ import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API"; import { getBuildStatus } from "../../controllers/API";
import useFlow from "../../stores/flowManagerStore";
import { FlowsState } from "../../types/tabs"; import { FlowsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils"; import { validateNodes } from "../../utils/reactflowUtils";
@ -38,7 +38,8 @@ export default function FormModal({
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
flow: FlowType; flow: FlowType;
}): JSX.Element { }): JSX.Element {
const { tabsState, setTabsState, nodes, edges } = useContext(FlowsContext); const { tabsState, setTabsState } = useContext(FlowsContext);
const { nodes, edges } = useFlow();
const [chatValue, setChatValue] = useState(() => { const [chatValue, setChatValue] = useState(() => {
try { try {
const { formKeysData } = tabsState[flow.id]; const { formKeysData } = tabsState[flow.id];
@ -383,10 +384,7 @@ export default function FormModal({
}, [open]); }, [open]);
function sendMessage(): void { function sendMessage(): void {
let nodeValidationErrors = validateNodes( let nodeValidationErrors = validateNodes(nodes, edges);
nodes,
edges
);
if (nodeValidationErrors.length === 0) { if (nodeValidationErrors.length === 0) {
setLockChat(true); setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys; let inputs = tabsState[id.current].formKeysData.input_keys;

View file

@ -17,7 +17,6 @@ import ReactFlow, {
SelectionDragHandler, SelectionDragHandler,
addEdge, addEdge,
updateEdge, updateEdge,
useReactFlow,
} from "reactflow"; } from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode"; import GenericNode from "../../../../CustomNodes/GenericNode";
import Chat from "../../../../components/chatComponent"; import Chat from "../../../../components/chatComponent";
@ -27,14 +26,14 @@ import { FlowsContext } from "../../../../contexts/flowsContext";
import { locationContext } from "../../../../contexts/locationContext"; import { locationContext } from "../../../../contexts/locationContext";
import { typesContext } from "../../../../contexts/typesContext"; import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import useFlow from "../../../../stores/flowManagerStore";
import { APIClassType } from "../../../../types/api"; import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType, targetHandleType } from "../../../../types/flow"; import { FlowType, NodeType } from "../../../../types/flow";
import { FlowsState } from "../../../../types/tabs";
import { import {
generateFlow, generateFlow,
generateNodeFromFlow, generateNodeFromFlow,
getNodeId,
isValidConnection, isValidConnection,
scapeJSONParse,
validateSelection, validateSelection,
} from "../../../../utils/reactflowUtils"; } from "../../../../utils/reactflowUtils";
import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils"; import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils";
@ -53,26 +52,33 @@ export default function Page({
flow: FlowType; flow: FlowType;
view?: boolean; view?: boolean;
}): JSX.Element { }): JSX.Element {
let { let { uploadFlow, saveFlow } = useContext(FlowsContext);
uploadFlow, const { types, templates, setFilterEdge } = useContext(typesContext);
getNodeId,
paste,
lastCopiedSelection,
setLastCopiedSelection,
deleteNode,
deleteEdge,
} = useContext(FlowsContext);
const {
types,
reactFlowInstance,
setReactFlowInstance,
templates,
setFilterEdge,
} = useContext(typesContext);
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const { takeSnapshot } = useContext(undoRedoContext); const { takeSnapshot } = useContext(undoRedoContext);
const { nodes, edges, setNodes, setEdges, onNodesChange, onEdgesChange, setPending, saveFlow, isPending } = useContext(FlowsContext);
const {
reactFlowInstance,
setReactFlowInstance,
nodes,
edges,
onNodesChange,
onEdgesChange,
onConnect,
setNodes,
setEdges,
deleteNode,
deleteEdge,
setPending,
isPending,
paste,
} = useFlow();
const position = useRef({ x: 0, y: 0 }); const position = useRef({ x: 0, y: 0 });
const [lastSelection, setLastSelection] = const [lastSelection, setLastSelection] =
@ -136,11 +142,7 @@ export default function Page({
document.removeEventListener("keydown", onKeyDown); document.removeEventListener("keydown", onKeyDown);
document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mousemove", handleMouseMove);
}; };
}, [ }, [lastCopiedSelection, lastSelection, takeSnapshot]);
lastCopiedSelection,
lastSelection,
takeSnapshot,
]);
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false); const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
@ -158,7 +160,9 @@ export default function Page({
if (reactFlowInstance) { if (reactFlowInstance) {
reactFlowInstance.setNodes(flow?.data?.nodes ?? []); reactFlowInstance.setNodes(flow?.data?.nodes ?? []);
reactFlowInstance.setEdges(flow?.data?.edges ?? []); reactFlowInstance.setEdges(flow?.data?.edges ?? []);
reactFlowInstance.setViewport(flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 }); reactFlowInstance.setViewport(
flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 }
);
} }
// Clear the previous timeout // Clear the previous timeout
@ -177,30 +181,10 @@ export default function Page({
}; };
}, [flow, reactFlowInstance]); }, [flow, reactFlowInstance]);
const onConnect = useCallback( const onConnectMod = useCallback(
(params: Connection) => { (params: Connection) => {
takeSnapshot(); takeSnapshot();
setEdges((eds) => onConnect(params);
addEdge(
{
...params,
data: {
targetHandle: scapeJSONParse(params.targetHandle!),
sourceHandle: scapeJSONParse(params.sourceHandle!),
},
style: { stroke: "#555" },
className:
((scapeJSONParse(params.targetHandle!) as targetHandleType)
.type === "Text"
? "stroke-foreground "
: "stroke-foreground ") + " stroke-connection",
animated:
(scapeJSONParse(params.targetHandle!) as targetHandleType)
.type === "Text",
},
eds
)
);
}, },
[setEdges, takeSnapshot, addEdge] [setEdges, takeSnapshot, addEdge]
); );
@ -237,10 +221,6 @@ export default function Page({
if (event.dataTransfer.types.some((types) => types === "nodedata")) { if (event.dataTransfer.types.some((types) => types === "nodedata")) {
takeSnapshot(); takeSnapshot();
// Get the current bounds of the ReactFlow wrapper element
const reactflowBounds =
reactFlowWrapper.current?.getBoundingClientRect();
// Extract the data from the drag event and parse it as a JSON object // Extract the data from the drag event and parse it as a JSON object
let data: { type: string; node?: APIClassType } = JSON.parse( let data: { type: string; node?: APIClassType } = JSON.parse(
event.dataTransfer.getData("nodedata") event.dataTransfer.getData("nodedata")
@ -370,8 +350,7 @@ export default function Page({
}, []); }, []);
const onMove = useCallback(() => { const onMove = useCallback(() => {
if(!isPending) if (!isPending) setPending(true);
setPending(true);
}, [setPending]); }, [setPending]);
return ( return (
@ -399,7 +378,7 @@ export default function Page({
edges={edges} edges={edges}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
onConnect={onConnect} onConnect={onConnectMod}
disableKeyboardA11y={true} disableKeyboardA11y={true}
onInit={setReactFlowInstance} onInit={setReactFlowInstance}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
@ -479,9 +458,7 @@ export default function Page({
}} }}
/> />
</ReactFlow> </ReactFlow>
{!view && ( {!view && <Chat flow={flow} />}
<Chat flow={flow} />
)}
</div> </div>
) : ( ) : (
<></> <></>

View file

@ -10,6 +10,7 @@ import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal"; import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal"; import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal"; import ShareModal from "../../../../modals/shareModal";
import useFlow from "../../../../stores/flowManagerStore";
import { useStoreStore } from "../../../../stores/storeStore"; import { useStoreStore } from "../../../../stores/storeStore";
import { APIClassType, APIObjectType } from "../../../../types/api"; import { APIClassType, APIObjectType } from "../../../../types/api";
import { import {
@ -28,13 +29,11 @@ import SidebarDraggableComponent from "./sideBarDraggableComponent";
export default function ExtraSidebar(): JSX.Element { export default function ExtraSidebar(): JSX.Element {
const { data, templates, getFilterEdge, setFilterEdge } = const { data, templates, getFilterEdge, setFilterEdge } =
useContext(typesContext); useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt, isPending } = const { flows, tabId, uploadFlow, saveFlow } = useContext(FlowsContext);
useContext(FlowsContext);
const hasStore = useStoreStore((state) => state.hasStore); const { hasStore, hasApiKey, validApiKey } = useStoreStore();
const hasApiKey = useStoreStore((state) => state.hasApiKey);
const validApiKey = useStoreStore((state) => state.validApiKey);
const { isBuilt, isPending } = useFlow();
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data); const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@ -292,12 +291,9 @@ export default function ExtraSidebar(): JSX.Element {
{flow && flow.data && ( {flow && flow.data && (
<ShadTooltip content="Save" side="top"> <ShadTooltip content="Save" side="top">
<button <button
disabled={flow?.data?.nodes.length === 0}
className={ className={
"extra-side-bar-buttons " + "extra-side-bar-buttons " +
(isPending && flow!.data!.nodes?.length > 0 (isPending ? "" : "button-disable")
? ""
: "button-disable")
} }
onClick={(event) => { onClick={(event) => {
saveFlow(); saveFlow();
@ -307,9 +303,7 @@ export default function ExtraSidebar(): JSX.Element {
name="Save" name="Save"
className={ className={
"side-bar-button-size" + "side-bar-button-size" +
(isPending && flow!.data!.nodes?.length > 0 (isPending ? " " : " extra-side-bar-save-disable")
? " "
: " extra-side-bar-save-disable")
} }
/> />
</button> </button>

View file

@ -12,6 +12,7 @@ import { APIClassType } from "../../../../../types/api";
import { import {
createFlowComponent, createFlowComponent,
downloadNode, downloadNode,
getNodeId,
} from "../../../../../utils/reactflowUtils"; } from "../../../../../utils/reactflowUtils";
import { removeCountFromString } from "../../../../../utils/utils"; import { removeCountFromString } from "../../../../../utils/utils";
@ -35,7 +36,7 @@ export default function SidebarDraggableComponent({
official: boolean; official: boolean;
}) { }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { getNodeId, deleteComponent, version } = useContext(FlowsContext); const { deleteComponent, version } = useContext(FlowsContext);
const { autoLogin, userData } = useContext(AuthContext); const { autoLogin, userData } = useContext(AuthContext);
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 }); const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
const popoverRef = useRef<HTMLDivElement>(null); const popoverRef = useRef<HTMLDivElement>(null);

View file

@ -13,6 +13,7 @@ import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import ConfirmationModal from "../../../../modals/ConfirmationModal"; import ConfirmationModal from "../../../../modals/ConfirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal"; import EditNodeModal from "../../../../modals/EditNodeModal";
import ShareModal from "../../../../modals/shareModal"; import ShareModal from "../../../../modals/shareModal";
import useFlow from "../../../../stores/flowManagerStore";
import { useStoreStore } from "../../../../stores/storeStore"; import { useStoreStore } from "../../../../stores/storeStore";
import { nodeToolbarPropsType } from "../../../../types/components"; import { nodeToolbarPropsType } from "../../../../types/components";
import { FlowType } from "../../../../types/flow"; import { FlowType } from "../../../../types/flow";
@ -49,11 +50,8 @@ export default function NodeToolbarComponent({
data.node.template[templateField].type === "NestedDict") data.node.template[templateField].type === "NestedDict")
).length ).length
); );
const { getNodeId } = useContext(FlowsContext);
const hasStore = useStoreStore((state) => state.hasStore); const { hasStore, hasApiKey, validApiKey } = useStoreStore();
const hasApiKey = useStoreStore((state) => state.hasApiKey);
const validApiKey = useStoreStore((state) => state.validApiKey);
function canMinimize() { function canMinimize() {
let countHandles: number = 0; let countHandles: number = 0;
@ -66,16 +64,9 @@ export default function NodeToolbarComponent({
const isMinimal = canMinimize(); const isMinimal = canMinimize();
const isGroup = data.node?.flow ? true : false; const isGroup = data.node?.flow ? true : false;
const { const { paste, nodes, edges, setNodes, setEdges } = useFlow();
paste,
saveComponent, const { saveComponent, flows, version } = useContext(FlowsContext);
version,
flows,
nodes,
edges,
setNodes,
setEdges,
} = useContext(FlowsContext);
const { takeSnapshot } = useContext(undoRedoContext); const { takeSnapshot } = useContext(undoRedoContext);
const [showModalAdvanced, setShowModalAdvanced] = useState(false); const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false); const [showconfirmShare, setShowconfirmShare] = useState(false);
@ -123,7 +114,7 @@ export default function NodeToolbarComponent({
case "ungroup": case "ungroup":
takeSnapshot(); takeSnapshot();
updateFlowPosition(position, data.node?.flow!); updateFlowPosition(position, data.node?.flow!);
expandGroupNode(data, getNodeId, nodes, edges, setNodes, setEdges); expandGroupNode(data, nodes, edges, setNodes, setEdges);
break; break;
case "override": case "override":
setShowOverrideModal(true); setShowOverrideModal(true);

View file

@ -8,9 +8,9 @@ type State = {
}; };
type Action = { type Action = {
updateDark: (dark: State["dark"]) => void; setDark: (dark: State["dark"]) => void;
updateStars: (starts: State["stars"]) => void; setStars: (starts: State["stars"]) => void;
updateGradientIndex: (gradientIndex: State["gradientIndex"]) => void; setGradientIndex: (gradientIndex: State["gradientIndex"]) => void;
}; };
function gradientIndexInitialState() { function gradientIndexInitialState() {
@ -23,9 +23,9 @@ export const useDarkStore = create<State & Action>((set) => ({
dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false, dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false,
stars: 0, stars: 0,
gradientIndex: gradientIndexInitialState(), gradientIndex: gradientIndexInitialState(),
updateDark: (dark) => set(() => ({ dark: dark })), setDark: (dark) => set(() => ({ dark: dark })),
updateStars: (starts) => set(() => ({ stars: starts })), setStars: (starts) => set(() => ({ stars: starts })),
updateGradientIndex: (gradientIndex) => setGradientIndex: (gradientIndex) =>
set(() => ({ gradientIndex: gradientIndex })), set(() => ({ gradientIndex: gradientIndex })),
})); }));

View file

@ -0,0 +1,248 @@
import { cloneDeep } from "lodash";
import {
Connection,
Edge,
EdgeChange,
Node,
NodeChange,
OnConnect,
OnEdgesChange,
OnNodesChange,
ReactFlowInstance,
addEdge,
applyEdgeChanges,
applyNodeChanges,
} from "reactflow";
import { create } from "zustand";
import {
NodeDataType,
NodeType,
sourceHandleType,
targetHandleType,
} from "../types/flow";
import {
cleanEdges,
getHandleId,
getNodeId,
scapeJSONParse,
scapedJSONStringfy,
} from "../utils/reactflowUtils";
type RFState = {
reactFlowInstance: ReactFlowInstance | null;
setReactFlowInstance: (newState: ReactFlowInstance) => void;
nodes: Node[];
edges: Edge[];
onNodesChange: OnNodesChange;
onEdgesChange: OnEdgesChange;
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
getNode: (id: string) => Node | undefined;
onConnect: OnConnect;
deleteNode: (nodeId: string | Array<string>) => void;
deleteEdge: (edgeId: string | Array<string>) => void;
paste: (
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
) => void;
isBuilt: boolean;
setIsBuilt: (isBuilt: boolean) => void;
isPending: boolean;
setPending: (pending: boolean) => void;
};
// this is our useStore hook that we can use in our components to get parts of the store and call actions
const useFlow = create<RFState>((set, get) => ({
reactFlowInstance: null,
setReactFlowInstance: (newState) => {
set({ reactFlowInstance: newState });
},
nodes: [],
edges: [],
isBuilt: false,
setIsBuilt: (isBuilt) => {
set({ isBuilt });
},
onNodesChange: (changes: NodeChange[]) => {
set({
nodes: applyNodeChanges(changes, get().nodes),
});
if (!get().isPending) set({ isPending: true });
},
onEdgesChange: (changes: EdgeChange[]) => {
set({
edges: applyEdgeChanges(changes, get().edges),
});
if (!get().isPending) set({ isPending: true });
},
setNodes: (change) => {
let newChange = typeof change === "function" ? change(get().nodes) : change;
let newEdges = cleanEdges(newChange, get().edges);
set({ edges: newEdges });
set({ nodes: newChange });
},
setEdges: (change) => {
let newChange = typeof change === "function" ? change(get().edges) : change;
set({ edges: newChange });
},
setNode: (id: string, change: Node | ((oldState: Node) => Node)) => {
let newChange =
typeof change === "function"
? change(get().nodes.find((node) => node.id === id)!)
: change;
get().setNodes((oldNodes) =>
oldNodes.map((node) => {
if (node.id === id) {
return newChange;
}
return node;
})
);
},
getNode: (id: string) => {
return get().nodes.find((node) => node.id === id);
},
onConnect: (connection: Connection) => {
set({
edges: addEdge(
{
...connection,
data: {
targetHandle: scapeJSONParse(connection.targetHandle!),
sourceHandle: scapeJSONParse(connection.sourceHandle!),
},
style: { stroke: "#555" },
className:
((scapeJSONParse(connection.targetHandle!) as targetHandleType)
.type === "Text"
? "stroke-foreground "
: "stroke-foreground ") + " stroke-connection",
animated:
(scapeJSONParse(connection.targetHandle!) as targetHandleType)
.type === "Text",
},
get().edges
),
});
},
deleteNode: (nodeId) => {
get().setNodes(
get().nodes.filter((node) =>
typeof nodeId === "string"
? node.id !== nodeId
: !nodeId.includes(node.id)
)
);
},
deleteEdge: (edgeId) => {
get().setEdges(
get().edges.filter((edge) =>
typeof edgeId === "string"
? edge.id !== edgeId
: !edgeId.includes(edge.id)
)
);
},
paste: (selection, position) => {
let minimumX = Infinity;
let minimumY = Infinity;
let idsMap = {};
let newNodes: Node<NodeDataType>[] = get().nodes;
let newEdges = get().edges;
selection.nodes.forEach((node: Node) => {
if (node.position.y < minimumY) {
minimumY = node.position.y;
}
if (node.position.x < minimumX) {
minimumX = node.position.x;
}
});
const insidePosition = position.paneX
? { x: position.paneX + position.x, y: position.paneY! + position.y }
: get().reactFlowInstance!.screenToFlowPosition({
x: position.x,
y: position.y,
});
selection.nodes.forEach((node: NodeType) => {
// Generate a unique node ID
let newId = getNodeId(node.data.type);
idsMap[node.id] = newId;
// Create a new node object
const newNode: NodeType = {
id: newId,
type: "genericNode",
position: {
x: insidePosition.x + node.position!.x - minimumX,
y: insidePosition.y + node.position!.y - minimumY,
},
data: {
...cloneDeep(node.data),
id: newId,
},
};
// Add the new node to the list of nodes in state
newNodes = newNodes
.map((node) => ({ ...node, selected: false }))
.concat({ ...newNode, selected: false });
});
set({ nodes: newNodes });
selection.edges.forEach((edge: Edge) => {
let source = idsMap[edge.source];
let target = idsMap[edge.target];
const sourceHandleObject: sourceHandleType = scapeJSONParse(
edge.sourceHandle!
);
let sourceHandle = scapedJSONStringfy({
...sourceHandleObject,
id: source,
});
sourceHandleObject.id = source;
edge.data.sourceHandle = sourceHandleObject;
const targetHandleObject: targetHandleType = scapeJSONParse(
edge.targetHandle!
);
let targetHandle = scapedJSONStringfy({
...targetHandleObject,
id: target,
});
targetHandleObject.id = target;
edge.data.targetHandle = targetHandleObject;
let id = getHandleId(source, sourceHandle, target, targetHandle);
newEdges = addEdge(
{
source,
target,
sourceHandle,
targetHandle,
id,
data: cloneDeep(edge.data),
style: { stroke: "#555" },
className:
targetHandleObject.type === "Text"
? "stroke-gray-800 "
: "stroke-gray-900 ",
animated: targetHandleObject.type === "Text",
selected: false,
},
newEdges.map((edge) => ({ ...edge, selected: false }))
);
});
set({ edges: newEdges });
},
isPending: false,
setPending: (pending: boolean) => {
set({ isPending: pending });
},
}));
export default useFlow;

View file

@ -1,6 +1,6 @@
import { FlowType } from "../flow"; import { FlowType } from "../flow";
export type ChatType = { flow: FlowType; }; export type ChatType = { flow: FlowType };
export type ChatMessageType = { export type ChatMessageType = {
message: string | Object; message: string | Object;
template?: string; template?: string;

View file

@ -1,35 +1,34 @@
import { XYPosition, Node, NodeChange, Edge, EdgeChange } from "reactflow"; import { Edge, EdgeChange, Node, NodeChange, XYPosition } from "reactflow";
import { tweakType } from "../components"; import { tweakType } from "../components";
import { FlowType, NodeDataType } from "../flow"; import { FlowType, NodeDataType } from "../flow";
import { Dispatch, SetStateAction } from "react";
type OnChange<ChangesType> = (changes: ChangesType[]) => void; type OnChange<ChangesType> = (changes: ChangesType[]) => void;
export type FlowsContextType = { export type FlowsContextType = {
//keep
saveFlow: (flow?: FlowType, silent?: boolean) => Promise<void>; saveFlow: (flow?: FlowType, silent?: boolean) => Promise<void>;
tabId: string; tabId: string;
//keep
isLoading: boolean; isLoading: boolean;
setTabId: (index: string) => void; setTabId: (index: string) => void;
flows: Array<FlowType>; //keep
deleteNode: (idx: string | Array<string>) => void;
deleteEdge: (idx: string | Array<string>) => void;
removeFlow: (id: string) => void; removeFlow: (id: string) => void;
//keep
addFlow: ( addFlow: (
newProject: boolean, newProject: boolean,
flow?: FlowType, flow?: FlowType,
override?: boolean, override?: boolean,
position?: XYPosition position?: XYPosition
) => Promise<String | undefined>; ) => Promise<String | undefined>;
incrementNodeId: () => string;
downloadFlow: ( downloadFlow: (
flow: FlowType, flow: FlowType,
flowName: string, flowName: string,
flowDescription?: string flowDescription?: string
) => void; ) => void;
//keep
downloadFlows: () => void; downloadFlows: () => void;
//keep
uploadFlows: () => void; uploadFlows: () => void;
isBuilt: boolean;
setIsBuilt: (state: boolean) => void;
uploadFlow: ({ uploadFlow: ({
newProject, newProject,
file, file,
@ -41,34 +40,17 @@ export type FlowsContextType = {
isComponent?: boolean; isComponent?: boolean;
position?: XYPosition; position?: XYPosition;
}) => Promise<String | never>; }) => Promise<String | never>;
hardReset: () => void;
getNodeId: (nodeType: string) => string;
isPending: boolean;
setPending: (pending: boolean) => void;
tabsState: FlowsState; tabsState: FlowsState;
setTabsState: (update: FlowsState | ((oldState: FlowsState) => FlowsState)) => void; setTabsState: (
paste: ( update: FlowsState | ((oldState: FlowsState) => FlowsState)
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
) => void; ) => void;
lastCopiedSelection: { nodes: any; edges: any } | null;
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
setTweak: (tweak: tweakType) => tweakType | void;
getTweak: tweakType;
saveComponent: ( saveComponent: (
component: NodeDataType, component: NodeDataType,
override: boolean override: boolean
) => Promise<String | undefined>; ) => Promise<String | undefined>;
deleteComponent: (key: string) => void; deleteComponent: (key: string) => void;
version: string; version: string;
nodes: Array<Node>; flows: Array<FlowType>;
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
getNode: (id: string) => Node | undefined;
onNodesChange: OnChange<NodeChange>;
edges: Array<Edge>;
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
onEdgesChange: OnChange<EdgeChange>;
}; };
export type FlowsState = { export type FlowsState = {

View file

@ -7,8 +7,6 @@ const template: { [char: string]: APIClassType } = {};
const data: { [char: string]: string } = {}; const data: { [char: string]: string } = {};
export type typesContextType = { export type typesContextType = {
reactFlowInstance: ReactFlowInstance | null;
setReactFlowInstance: (newState: ReactFlowInstance) => void;
types: typeof types; types: typeof types;
setTypes: (newState: {}) => void; setTypes: (newState: {}) => void;
templates: typeof template; templates: typeof template;

View file

@ -27,6 +27,7 @@ import {
updateEdgesHandleIdsType, updateEdgesHandleIdsType,
} from "../types/utils/reactflowUtils"; } from "../types/utils/reactflowUtils";
import { getFieldTitle, toTitleCase } from "./utils"; import { getFieldTitle, toTitleCase } from "./utils";
const uid = new ShortUniqueId({ length: 5 });
export function cleanEdges(nodes: Node[], edges: Edge[]) { export function cleanEdges(nodes: Node[], edges: Edge[]) {
let newEdges = _.cloneDeep(edges); let newEdges = _.cloneDeep(edges);
@ -82,7 +83,7 @@ export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) {
export function isValidConnection( export function isValidConnection(
{ source, target, sourceHandle, targetHandle }: Connection, { source, target, sourceHandle, targetHandle }: Connection,
nodes: Node[], nodes: Node[],
edges: Edge[], edges: Edge[]
) { ) {
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!); const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!); const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
@ -98,16 +99,12 @@ export function isValidConnection(
) { ) {
let targetNode = nodes.find((node) => node.id === target!)?.data?.node; let targetNode = nodes.find((node) => node.id === target!)?.data?.node;
if (!targetNode) { if (!targetNode) {
if ( if (!edges.find((e) => e.targetHandle === targetHandle)) {
!edges
.find((e) => e.targetHandle === targetHandle)
) {
return true; return true;
} }
} else if ( } else if (
(!targetNode.template[targetHandleObject.fieldName].list && (!targetNode.template[targetHandleObject.fieldName].list &&
!edges !edges.find((e) => e.targetHandle === targetHandle)) ||
.find((e) => e.targetHandle === targetHandle)) ||
targetNode.template[targetHandleObject.fieldName].list targetNode.template[targetHandleObject.fieldName].list
) { ) {
return true; return true;
@ -153,7 +150,6 @@ export function updateTemplate(
export function updateIds( export function updateIds(
newFlow: ReactFlowJsonObject, newFlow: ReactFlowJsonObject,
getNodeId: (type: string) => string
) { ) {
let idsMap = {}; let idsMap = {};
@ -272,6 +268,20 @@ export function validateNodes(nodes: Node[], edges: Edge[]) {
return nodes.flatMap((n: NodeType) => validateNode(n, edges)); return nodes.flatMap((n: NodeType) => validateNode(n, edges));
} }
export function updateEdges(edges: Edge[]) {
if (edges)
edges.forEach((edge) => {
const targetHandleObject: targetHandleType = scapeJSONParse(
edge.targetHandle!
);
edge.className =
(targetHandleObject.type === "Text"
? "stroke-gray-800 "
: "stroke-gray-900 ") + " stroke-connection";
edge.animated = targetHandleObject.type === "Text";
});
};
export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) { export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
const existingNames = flows.map((item) => item.name); const existingNames = flows.map((item) => item.name);
let newName = flow.name; let newName = flow.name;
@ -499,6 +509,19 @@ export function getMiddlePoint(nodes: Node[]) {
return { x: averageX, y: averageY }; return { x: averageX, y: averageY };
} }
export function getNodeId(nodeType: string) {
return nodeType + "-" + uid();
}
export function getHandleId(source: string, sourceHandle: string, target: string, targetHandle: string){
return "reactflow__edge-" +
source +
sourceHandle +
"-" +
target +
targetHandle;
}
export function generateFlow( export function generateFlow(
selection: OnSelectionChangeParams, selection: OnSelectionChangeParams,
nodes: Node[], nodes: Node[],
@ -539,14 +562,10 @@ export function generateFlow(
export function filterFlow( export function filterFlow(
selection: OnSelectionChangeParams, selection: OnSelectionChangeParams,
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void, setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) { ) {
setNodes((nodes) => setNodes((nodes) => nodes.filter((node) => !selection.nodes.includes(node)));
nodes.filter((node) => !selection.nodes.includes(node)) setEdges((edges) => edges.filter((edge) => !selection.edges.includes(edge)));
);
setEdges((edges) =>
edges.filter((edge) => !selection.edges.includes(edge))
);
} }
export function findLastNode({ nodes, edges }: findLastNodeType) { export function findLastNode({ nodes, edges }: findLastNodeType) {
@ -572,7 +591,7 @@ export function updateFlowPosition(NewPosition: XYPosition, flow: FlowType) {
export function concatFlows( export function concatFlows(
flow: FlowType, flow: FlowType,
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void, setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) { ) {
const { nodes, edges } = flow.data!; const { nodes, edges } = flow.data!;
setNodes((old) => [...old, ...nodes]); setNodes((old) => [...old, ...nodes]);
@ -916,16 +935,28 @@ function updateEdgesIds(edges: Edge[], idsMap: { [key: string]: string }) {
}); });
} }
export function processFlowEdges(flow: FlowType) {
if (!flow.data || !flow.data.edges) return;
if (checkOldEdgesHandles(flow.data.edges)) {
const newEdges = updateEdgesHandleIds(flow.data);
flow.data.edges = newEdges;
}
//update edges colors
flow.data.edges.forEach((edge) => {
edge.className = "";
edge.style = { stroke: "#555" };
});
}
export function expandGroupNode( export function expandGroupNode(
groupNode: NodeDataType, groupNode: NodeDataType,
getNodeId: (type: string) => string,
nodes: Node[], nodes: Node[],
edges: Edge[], edges: Edge[],
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, 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 { template, flow } = _.cloneDeep(groupNode.node!);
const idsMap = updateIds(flow!.data!, getNodeId); const idsMap = updateIds(flow!.data!);
updateProxyIdsOnTemplate(template, idsMap); updateProxyIdsOnTemplate(template, idsMap);
let flowEdges = edges; let flowEdges = edges;
updateEdgesIds(flowEdges, idsMap); updateEdgesIds(flowEdges, idsMap);