Merge branch 'bug/undo-copy' into feature/store

This commit is contained in:
Lucas Oliveira 2023-11-28 20:47:45 -03:00
commit 52338db3c5
12 changed files with 155 additions and 171 deletions

View file

@ -1,4 +1,3 @@
import { cloneDeep } from "lodash";
import React, { import React, {
ReactNode, ReactNode,
useContext, useContext,
@ -29,6 +28,7 @@ import {
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 { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { postCustomComponentUpdate } from "../../../../controllers/API"; import { postCustomComponentUpdate } from "../../../../controllers/API";
import { ParameterComponentType } from "../../../../types/components"; import { ParameterComponentType } from "../../../../types/components";
import { NodeDataType } from "../../../../types/flow"; import { NodeDataType } from "../../../../types/flow";
@ -50,7 +50,6 @@ export default function ParameterComponent({
left, left,
id, id,
data, data,
setData,
tooltipTitle, tooltipTitle,
title, title,
color, color,
@ -99,6 +98,8 @@ export default function ParameterComponent({
const { data: myData } = useContext(typesContext); const { data: myData } = useContext(typesContext);
const { takeSnapshot } = useContext(undoRedoContext);
const handleUpdateValues = async (name: string, data: NodeDataType) => { const handleUpdateValues = async (name: string, data: NodeDataType) => {
const code = data.node?.template["code"]?.value; const code = data.node?.template["code"]?.value;
if (!code) { if (!code) {
@ -121,9 +122,8 @@ export default function ParameterComponent({
const handleOnNewValue = ( const handleOnNewValue = (
newValue: string | string[] | boolean | Object[] newValue: string | string[] | boolean | Object[]
): void => { ): void => {
let newData = cloneDeep(data); takeSnapshot();
newData.node!.template[name].value = newValue; data.node!.template[name].value = newValue;
setData(newData);
// Set state to pending // Set state to pending
//@ts-ignore //@ts-ignore
setTabsState((prev: TabsState) => { setTabsState((prev: TabsState) => {
@ -484,9 +484,6 @@ export default function ParameterComponent({
field_name={name} field_name={name}
setNodeClass={(nodeClass) => { setNodeClass={(nodeClass) => {
data.node = nodeClass; data.node = nodeClass;
const clone = cloneDeep(data);
clone.node = nodeClass;
setData(clone);
}} }}
nodeClass={data.node} nodeClass={data.node}
disabled={disabled} disabled={disabled}

View file

@ -9,6 +9,7 @@ import { Textarea } from "../../components/ui/textarea";
import { useSSE } from "../../contexts/SSEContext"; import { useSSE } from "../../contexts/SSEContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { undoRedoContext } from "../../contexts/undoRedoContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { validationStatusType } from "../../types/components"; import { validationStatusType } from "../../types/components";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
@ -22,7 +23,7 @@ import { classNames, getFieldTitle } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
export default function GenericNode({ export default function GenericNode({
data: olddata, data,
xPos, xPos,
yPos, yPos,
selected, selected,
@ -32,7 +33,6 @@ export default function GenericNode({
xPos: number; xPos: number;
yPos: number; yPos: number;
}): JSX.Element { }): JSX.Element {
const [data, setData] = useState(olddata);
const { updateFlow, flows, tabId } = useContext(FlowsContext); const { updateFlow, flows, tabId } = useContext(FlowsContext);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } = const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } =
@ -46,10 +46,11 @@ export default function GenericNode({
); );
const [validationStatus, setValidationStatus] = const [validationStatus, setValidationStatus] =
useState<validationStatusType | null>(null); useState<validationStatusType | null>(null);
const [showNode, setShowNode] = useState<boolean>(true);
const [handles, setHandles] = useState<boolean[] | []>([]); const [handles, setHandles] = useState<boolean[] | []>([]);
let numberOfInputs: boolean[] = []; let numberOfInputs: boolean[] = [];
const { takeSnapshot } = useContext(undoRedoContext);
function countHandles(): void { function countHandles(): void {
numberOfInputs = Object.keys(data.node!.template) numberOfInputs = Object.keys(data.node!.template)
.filter((templateField) => templateField.charAt(0) !== "_") .filter((templateField) => templateField.charAt(0) !== "_")
@ -86,7 +87,6 @@ export default function GenericNode({
// State for outline color // State for outline color
const { sseData, isBuilding } = useSSE(); const { sseData, isBuilding } = useSSE();
useEffect(() => { useEffect(() => {
olddata.node = data.node;
let myFlow = flows.find((flow) => flow.id === tabId); let myFlow = flows.find((flow) => flow.id === tabId);
if (reactFlowInstance && myFlow) { if (reactFlowInstance && myFlow) {
let flow = cloneDeep(myFlow); let flow = cloneDeep(myFlow);
@ -108,10 +108,12 @@ export default function GenericNode({
}, [data]); }, [data]);
useEffect(() => { useEffect(() => {
setTimeout(() => { setNodeDescription(data.node!.description);
updateNodeInternals(data.id); }, [data.node!.description]);
}, 300);
}, [showNode]); useEffect(() => {
setNodeName(data.node!.display_name);
}, [data.node!.display_name]);
// New useEffect to watch for changes in sseData and update validation status // New useEffect to watch for changes in sseData and update validation status
useEffect(() => { useEffect(() => {
@ -123,15 +125,17 @@ export default function GenericNode({
setValidationStatus(null); setValidationStatus(null);
} }
}, [sseData, data.id]); }, [sseData, data.id]);
const showNode = data.showNode ?? true;
return ( return (
<> <>
<NodeToolbar> <NodeToolbar>
<NodeToolbarComponent <NodeToolbarComponent
position={{ x: xPos, y: yPos }} position={{ x: xPos, y: yPos }}
data={data} data={data}
setData={setData}
deleteNode={deleteNode} deleteNode={deleteNode}
setShowNode={setShowNode} setShowNode={(showNode: boolean) => {data.showNode = showNode}}
numberOfHandles={handles} numberOfHandles={handles}
showNode={showNode} showNode={showNode}
></NodeToolbarComponent> ></NodeToolbarComponent>
@ -180,7 +184,6 @@ export default function GenericNode({
{data.node?.flow && inputName ? ( {data.node?.flow && inputName ? (
<div> <div>
<InputComponent <InputComponent
autoFocus
onBlur={() => { onBlur={() => {
setInputName(false); setInputName(false);
if (nodeName.trim() !== "") { if (nodeName.trim() !== "") {
@ -200,7 +203,10 @@ export default function GenericNode({
<ShadTooltip content={data.node?.display_name}> <ShadTooltip content={data.node?.display_name}>
<div <div
className="flex" className="flex"
onDoubleClick={() => setInputName(true)} onDoubleClick={() => {
setInputName(true);
takeSnapshot();
}}
> >
<div className="generic-node-tooltip-div pr-3 text-primary"> <div className="generic-node-tooltip-div pr-3 text-primary">
{data.node?.display_name} {data.node?.display_name}
@ -237,7 +243,6 @@ export default function GenericNode({
proxy: data.node!.template[templateField].proxy, proxy: data.node!.template[templateField].proxy,
})} })}
data={data} data={data}
setData={setData}
color={ color={
nodeColors[ nodeColors[
types[data.node?.template[templateField].type!] types[data.node?.template[templateField].type!]
@ -286,7 +291,6 @@ export default function GenericNode({
dataType: data.type, dataType: data.type,
})} })}
data={data} data={data}
setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown} color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={ title={
data.node?.output_types && data.node?.output_types &&
@ -417,7 +421,10 @@ export default function GenericNode({
) : ( ) : (
<div <div
className="generic-node-desc-text break-all" className="generic-node-desc-text break-all"
onDoubleClick={() => setInputDescription(true)} onDoubleClick={() => {
setInputDescription(true);
takeSnapshot();
}}
> >
{data.node?.description} {data.node?.description}
</div> </div>
@ -449,7 +456,6 @@ export default function GenericNode({
proxy: data.node!.template[templateField].proxy, proxy: data.node!.template[templateField].proxy,
})} })}
data={data} data={data}
setData={setData}
color={ color={
nodeColors[ nodeColors[
types[data.node?.template[templateField].type!] types[data.node?.template[templateField].type!]
@ -507,8 +513,7 @@ export default function GenericNode({
dataType: data.type, dataType: data.type,
})} })}
data={data} data={data}
setData={setData} color={nodeColors[types[data.type]] ?? nodeColors.unknown}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
title={ title={
data.node?.output_types && data.node.output_types.length > 0 data.node?.output_types && data.node.output_types.length > 0
? data.node.output_types.join("|") ? data.node.output_types.join("|")

View file

@ -12,8 +12,6 @@ export default function KeypairListComponent({
disabled, disabled,
editNode = false, editNode = false,
duplicateKey, duplicateKey,
advanced = false,
dataValue,
}: KeyPairListComponentType): JSX.Element { }: KeyPairListComponentType): JSX.Element {
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {

View file

@ -418,7 +418,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const insidePosition = position.paneX const insidePosition = position.paneX
? { x: position.paneX + position.x, y: position.paneY! + position.y } ? { x: position.paneX + position.x, y: position.paneY! + position.y }
: reactFlowInstance!.project({ x: position.x, y: position.y }); : reactFlowInstance!.screenToFlowPosition({ x: position.x, y: position.y });
selectionInstance.nodes.forEach((node: NodeType) => { selectionInstance.nodes.forEach((node: NodeType) => {
// Generate a unique node ID // Generate a unique node ID

View file

@ -52,15 +52,23 @@ export function UndoRedoProvider({ children }) {
const takeSnapshot = useCallback(() => { const takeSnapshot = useCallback(() => {
// push the current graph to the past state // push the current graph to the past state
setPast((old) => { let newPast = cloneDeep(past);
let newPast = cloneDeep(old); let newState = {
newPast[tabIndex] = old[tabIndex].slice( nodes: cloneDeep(getNodes()),
old[tabIndex].length - defaultOptions.maxHistorySize + 1, edges: cloneDeep(getEdges()),
old[tabIndex].length };
if (
past[tabIndex] &&
JSON.stringify(past[tabIndex][past[tabIndex].length - 1]) !==
JSON.stringify(newState)
) {
newPast[tabIndex] = past[tabIndex].slice(
past[tabIndex].length - defaultOptions.maxHistorySize + 1,
past[tabIndex].length
); );
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() }); newPast[tabIndex].push(newState);
return newPast; }
}); setPast(newPast);
// whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches // whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches
setFuture((old) => { setFuture((old) => {

View file

@ -45,13 +45,11 @@ const EditNodeModal = forwardRef(
( (
{ {
data, data,
setData,
nodeLength, nodeLength,
open, open,
setOpen, setOpen,
}: { }: {
data: NodeDataType; data: NodeDataType;
setData: (data: NodeDataType) => void;
nodeLength: number; nodeLength: number;
open: boolean; open: boolean;
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
@ -60,7 +58,7 @@ const EditNodeModal = forwardRef(
) => { ) => {
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
let myData = useRef(data); const [myData, setMyData] = useState(data);
const { setTabsState, tabId } = useContext(FlowsContext); const { setTabsState, tabId } = useContext(FlowsContext);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
@ -70,30 +68,31 @@ const EditNodeModal = forwardRef(
.some((edge) => edge.targetHandle === data.id) ?? false; .some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(n) { function changeAdvanced(n) {
myData.current.node!.template[n].advanced = setMyData((old) => {
!myData.current.node!.template[n].advanced; let newData = cloneDeep(old);
newData.node!.template[n].advanced =
myData.current = { !newData.node!.template[n].advanced;
...myData.current, return newData;
}; });
setAdv(!adv);
} }
const handleOnNewValue = (newValue: any, name) => { const handleOnNewValue = (newValue: any, name) => {
myData.current.node!.template[name].value = newValue; setMyData((old) => {
setDataValue(newValue); let newData = cloneDeep(old);
newData.node!.template[name].value = newValue;
return newData;
});
updateNodeInternals(data.id); updateNodeInternals(data.id);
}; };
useEffect(() => { useEffect(() => {
if (open) { if (open) {
myData.current = data; // reset data to what it is on node when opening modal setMyData(data); // reset data to what it is on node when opening modal
} }
}, [open]); }, [open]);
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
const [adv, setAdv] = useState<boolean | null>(null);
const [dataValue, setDataValue] = useState(data);
return ( return (
<BaseModal <BaseModal
@ -102,15 +101,15 @@ const EditNodeModal = forwardRef(
open={open} open={open}
setOpen={setOpen} setOpen={setOpen}
onChangeOpenModal={(open) => { onChangeOpenModal={(open) => {
myData.current = data; setMyData(data);
}} }}
> >
<BaseModal.Trigger> <BaseModal.Trigger>
<></> <></>
</BaseModal.Trigger> </BaseModal.Trigger>
<BaseModal.Header description={myData.current.node?.description!}> <BaseModal.Header description={myData.node?.description!}>
<span className="pr-2">{myData.current.type}</span> <span className="pr-2">{myData.type}</span>
<Badge variant="secondary">ID: {myData.current.id}</Badge> <Badge variant="secondary">ID: {myData.id}</Badge>
</BaseModal.Header> </BaseModal.Header>
<BaseModal.Content> <BaseModal.Content>
<div className="flex pb-2"> <div className="flex pb-2">
@ -143,13 +142,13 @@ const EditNodeModal = forwardRef(
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody className="p-0"> <TableBody className="p-0">
{Object.keys(myData.current.node!.template) {Object.keys(myData.node!.template)
.filter( .filter(
(templateParam) => (templateParam) =>
templateParam.charAt(0) !== "_" && templateParam.charAt(0) !== "_" &&
myData.current.node?.template[templateParam].show && myData.node?.template[templateParam].show &&
LANGFLOW_SUPPORTED_TYPES.has( LANGFLOW_SUPPORTED_TYPES.has(
myData.current.node.template[templateParam].type myData.node.template[templateParam].type
) )
) )
.map((templateParam, index) => ( .map((templateParam, index) => (
@ -157,46 +156,46 @@ const EditNodeModal = forwardRef(
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3"> <TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
<ShadTooltip <ShadTooltip
content={ content={
myData.current.node?.template[templateParam] myData.node?.template[templateParam]
.proxy .proxy
? myData.current.node?.template[ ? myData.node?.template[
templateParam templateParam
].proxy?.id ].proxy?.id
: null : null
} }
> >
<span> <span>
{myData.current.node?.template[templateParam] {myData.node?.template[templateParam]
.display_name .display_name
? myData.current.node.template[ ? myData.node.template[
templateParam templateParam
].display_name ].display_name
: myData.current.node?.template[ : myData.node?.template[
templateParam templateParam
].name} ].name}
</span> </span>
</ShadTooltip> </ShadTooltip>
</TableCell> </TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground "> <TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.current.node?.template[templateParam] {myData.node?.template[templateParam]
.type === "str" && .type === "str" &&
!myData.current.node.template[templateParam] !myData.node.template[templateParam]
.options ? ( .options ? (
<div className="mx-auto"> <div className="mx-auto">
{myData.current.node.template[templateParam] {myData.node.template[templateParam]
.list ? ( .list ? (
<InputListComponent <InputListComponent
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
value={ value={
!myData.current.node.template[ !myData.node.template[
templateParam templateParam
].value || ].value ||
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value === "" ].value === ""
? [""] ? [""]
: myData.current.node.template[ : myData.node.template[
templateParam templateParam
].value ].value
} }
@ -204,7 +203,7 @@ const EditNodeModal = forwardRef(
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
/> />
) : myData.current.node.template[ ) : myData.node.template[
templateParam templateParam
].multiline ? ( ].multiline ? (
<TextAreaComponent <TextAreaComponent
@ -212,7 +211,7 @@ const EditNodeModal = forwardRef(
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -226,12 +225,12 @@ const EditNodeModal = forwardRef(
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
password={ password={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].password ?? false ].password ?? false
} }
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -241,25 +240,25 @@ const EditNodeModal = forwardRef(
/> />
)} )}
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "NestedDict" ? ( .type === "NestedDict" ? (
<div className=" w-full"> <div className=" w-full">
<DictComponent <DictComponent
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData?.current?.node!.template[ myData.node!.template[
templateParam templateParam
]?.value?.toString() === "{}" ]?.value?.toString() === "{}"
? { ? {
yourkey: "value", yourkey: "value",
} }
: myData.current.node!.template[ : myData.node!.template[
templateParam templateParam
].value ].value
} }
onChange={(newValue) => { onChange={(newValue) => {
myData.current.node!.template[ myData.node!.template[
templateParam templateParam
].value = newValue; ].value = newValue;
handleOnNewValue(newValue, templateParam); handleOnNewValue(newValue, templateParam);
@ -267,32 +266,30 @@ const EditNodeModal = forwardRef(
id="editnode-div-dict-input" id="editnode-div-dict-input"
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "dict" ? ( .type === "dict" ? (
<div <div
className={classNames( className={classNames(
"max-h-48 w-full overflow-auto custom-scroll", "max-h-48 w-full overflow-auto custom-scroll",
myData.current.node!.template[templateParam] myData.node!.template[templateParam]
.value?.length > 1 .value?.length > 1
? "my-3" ? "my-3"
: "" : ""
)} )}
> >
<KeypairListComponent <KeypairListComponent
dataValue={dataValue}
advanced={adv}
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.current.node!.template[ myData.node!.template[
templateParam templateParam
].value?.length === 0 || ].value?.length === 0 ||
!myData.current.node!.template[ !myData.node!.template[
templateParam templateParam
].value ].value
? [{ "": "" }] ? [{ "": "" }]
: convertObjToArray( : convertObjToArray(
myData.current.node!.template[ myData.node!.template[
templateParam templateParam
].value ].value
) )
@ -301,7 +298,7 @@ const EditNodeModal = forwardRef(
onChange={(newValue) => { onChange={(newValue) => {
const valueToNumbers = const valueToNumbers =
convertValuesToNumbers(newValue); convertValuesToNumbers(newValue);
myData.current.node!.template[ myData.node!.template[
templateParam templateParam
].value = valueToNumbers; ].value = valueToNumbers;
setErrorDuplicateKey( setErrorDuplicateKey(
@ -314,7 +311,7 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "bool" ? ( .type === "bool" ? (
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
@ -322,7 +319,7 @@ const EditNodeModal = forwardRef(
id={"toggle-edit-" + index} id={"toggle-edit-" + index}
disabled={disabled} disabled={disabled}
enabled={ enabled={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ].value
} }
@ -335,14 +332,14 @@ const EditNodeModal = forwardRef(
size="small" size="small"
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "float" ? ( .type === "float" ? (
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -351,16 +348,16 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "str" && .type === "str" &&
myData.current.node.template[templateParam] myData.node.template[templateParam]
.options ? ( .options ? (
<div className="mx-auto"> <div className="mx-auto">
<Dropdown <Dropdown
numberOfOptions={nodeLength} numberOfOptions={nodeLength}
editNode={true} editNode={true}
options={ options={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].options ].options
} }
@ -368,14 +365,14 @@ const EditNodeModal = forwardRef(
handleOnNewValue(value, templateParam) handleOnNewValue(value, templateParam)
} }
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "Choose an option" ].value ?? "Choose an option"
} }
id={"dropdown-edit-" + index} id={"dropdown-edit-" + index}
></Dropdown> ></Dropdown>
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "int" ? ( .type === "int" ? (
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
@ -383,7 +380,7 @@ const EditNodeModal = forwardRef(
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -392,14 +389,14 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "file" ? ( .type === "file" ? (
<div className="mx-auto"> <div className="mx-auto">
<InputFileComponent <InputFileComponent
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -407,12 +404,12 @@ const EditNodeModal = forwardRef(
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
fileTypes={ fileTypes={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].fileTypes ].fileTypes
} }
suffixes={ suffixes={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].suffixes ].suffixes
} }
@ -423,22 +420,22 @@ const EditNodeModal = forwardRef(
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "prompt" ? ( .type === "prompt" ? (
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
readonly={ readonly={
myData.current.node?.flow ? true : false myData.node?.flow ? true : false
} }
field_name={templateParam} field_name={templateParam}
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
nodeClass={myData.current.node} nodeClass={myData.node}
setNodeClass={(nodeClass) => { setNodeClass={(nodeClass) => {
myData.current.node = nodeClass; myData.node = nodeClass;
}} }}
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -448,13 +445,13 @@ const EditNodeModal = forwardRef(
id={"prompt-area-edit" + index} id={"prompt-area-edit" + index}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "code" ? ( .type === "code" ? (
<div className="mx-auto"> <div className="mx-auto">
<CodeAreaComponent <CodeAreaComponent
readonly={ readonly={
myData.current.node?.flow && myData.node?.flow &&
myData.current.node.template[ myData.node.template[
templateParam templateParam
].dynamic ].dynamic
? true ? true
@ -471,7 +468,7 @@ const EditNodeModal = forwardRef(
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.current.node.template[ myData.node.template[
templateParam templateParam
].value ?? "" ].value ?? ""
} }
@ -481,7 +478,7 @@ const EditNodeModal = forwardRef(
id={"code-area-edit" + index} id={"code-area-edit" + index}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.node?.template[templateParam]
.type === "Any" ? ( .type === "Any" ? (
"-" "-"
) : ( ) : (
@ -493,11 +490,11 @@ const EditNodeModal = forwardRef(
<ToggleShadComponent <ToggleShadComponent
id={ id={
"show" + "show" +
myData.current.node?.template[templateParam] myData.node?.template[templateParam]
.name .name
} }
enabled={ enabled={
!myData.current.node?.template[ !myData.node?.template[
templateParam templateParam
].advanced ].advanced
} }
@ -524,8 +521,7 @@ const EditNodeModal = forwardRef(
id={"saveChangesBtn"} id={"saveChangesBtn"}
className="mt-3" className="mt-3"
onClick={() => { onClick={() => {
const newData = cloneDeep(myData.current); data.node = myData.node
myData.current = newData;
//@ts-ignore //@ts-ignore
setTabsState((prev: FlowsState) => { setTabsState((prev: FlowsState) => {
return { return {

View file

@ -90,8 +90,6 @@ export default function Page({
useState<OnSelectionChangeParams | null>(null); useState<OnSelectionChangeParams | null>(null);
useEffect(() => { useEffect(() => {
// this effect is used to attach the global event handlers
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
if (!isWrappedWithClass(event, "nocopy")) { if (!isWrappedWithClass(event, "nocopy")) {
if ( if (
@ -108,10 +106,9 @@ export default function Page({
lastCopiedSelection lastCopiedSelection
) { ) {
event.preventDefault(); event.preventDefault();
let bounds = reactFlowWrapper.current?.getBoundingClientRect();
paste(lastCopiedSelection, { paste(lastCopiedSelection, {
x: position.x - bounds!.left, x: position.x,
y: position.y - bounds!.top, y: position.y,
}); });
} }
if ( if (
@ -133,18 +130,26 @@ export default function Page({
} }
} }
}; };
document.addEventListener("keydown", onKeyDown);
return () => {
document.removeEventListener("keydown", onKeyDown);
};
}, [lastCopiedSelection, lastSelection]);
useEffect(() => {
const handleMouseMove = (event) => { const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY }); setPosition({ x: event.clientX, y: event.clientY });
}; };
document.addEventListener("keydown", onKeyDown);
document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mousemove", handleMouseMove);
return () => { return () => {
document.removeEventListener("keydown", onKeyDown);
document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mousemove", handleMouseMove);
}; };
}, [position, lastCopiedSelection, lastSelection]); }, [position]);
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false); const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
const { setExtraComponent, setExtraNavigation } = useContext(locationContext); const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
@ -152,58 +157,34 @@ export default function Page({
const [nodes, setNodes, onNodesChange] = useNodesState( const [nodes, setNodes, onNodesChange] = useNodesState(
flow.data?.nodes ?? [] flow.data?.nodes ?? []
); );
const [edges, setEdges, onEdgesChange] = useEdgesState( const [edges, setEdges, onEdgesChange] = useEdgesState(
flow.data?.edges ?? [] flow.data?.edges ?? []
); );
const { setViewport } = useReactFlow(); const { setViewport } = useReactFlow();
const edgeUpdateSuccessful = useRef(true); const edgeUpdateSuccessful = useRef(true);
useEffect(() => {
if (reactFlowInstance && flow) {
flow.data = reactFlowInstance.toObject();
updateFlow(flow);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [edges]);
//update flow when tabs change //update flow when tabs change
useEffect(() => { useEffect(() => {
setNodes(flow?.data?.nodes ?? []); setNodes(flow?.data?.nodes ?? []);
setEdges(flow?.data?.edges ?? []); setEdges(flow?.data?.edges ?? []);
if (reactFlowInstance) { if (reactFlowInstance) {
setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 }); setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 });
reactFlowInstance.fitView();
} }
}, [flow, reactFlowInstance, setEdges, setNodes, setViewport]); }, [flow, reactFlowInstance]);
//set extra sidebar
useEffect(() => {
setExtraComponent(<ExtraSidebar />);
setExtraNavigation({ title: "Components" });
}, [setExtraComponent, setExtraNavigation]);
const [seconds, setSeconds] = useState(0);
useEffect(() => { useEffect(() => {
const index = flows.findIndex((flowId) => flowId.id === flow.id); const index = flows.findIndex((flowId) => flowId.id === flow.id);
const interval = setInterval(() => { const interval = setInterval(() => {
setSeconds((prevSeconds) => { saveFlow(
let updatedSeconds = prevSeconds + 1; {
...flows[index]!,
if (updatedSeconds % 30 === 0) { data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data,
saveFlow( },
{ true
...flows[index]!, );
data: reactFlowInstance }, 30000);
? reactFlowInstance!.toObject()
: flow!.data,
},
true
);
updatedSeconds = 0;
}
return updatedSeconds;
});
}, 1000);
return () => { return () => {
clearInterval(interval); clearInterval(interval);
@ -212,11 +193,11 @@ export default function Page({
const onEdgesChangeMod = useCallback( const onEdgesChangeMod = useCallback(
(change: EdgeChange[]) => { (change: EdgeChange[]) => {
onEdgesChange(change); updateFlow({
setNodes((node) => { ...flow!,
let newX = _.cloneDeep(node); data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data,
return newX;
}); });
onEdgesChange(change);
//@ts-ignore //@ts-ignore
setTabsState((prev: FlowsState) => { setTabsState((prev: FlowsState) => {
return { return {
@ -383,6 +364,9 @@ export default function Page({
); );
useEffect(() => { useEffect(() => {
setExtraComponent(<ExtraSidebar />);
setExtraNavigation({ title: "Components" });
return () => { return () => {
if (tabsState && tabsState[flow.id]?.isPending) { if (tabsState && tabsState[flow.id]?.isPending) {
saveFlow({ saveFlow({
@ -520,6 +504,7 @@ export default function Page({
isVisible={selectionMenuVisible} isVisible={selectionMenuVisible}
nodes={lastSelection?.nodes} nodes={lastSelection?.nodes}
onClick={() => { onClick={() => {
takeSnapshot();
if ( if (
validateSelection(lastSelection!, edges).length === 0 validateSelection(lastSelection!, edges).length === 0
) { ) {

View file

@ -97,7 +97,6 @@ export default function ExtraSidebar(): JSX.Element {
useEffect(() => { useEffect(() => {
if (getFilterEdge.length === 0 && search === "") { if (getFilterEdge.length === 0 && search === "") {
setFilterData(data); setFilterData(data);
setFilterEdge([]);
setSearch(""); setSearch("");
} }
}, [getFilterEdge]); }, [getFilterEdge]);

View file

@ -23,10 +23,10 @@ import {
updateFlowPosition, updateFlowPosition,
} from "../../../../utils/reactflowUtils"; } from "../../../../utils/reactflowUtils";
import { classNames } from "../../../../utils/utils"; import { classNames } from "../../../../utils/utils";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
export default function NodeToolbarComponent({ export default function NodeToolbarComponent({
data, data,
setData,
deleteNode, deleteNode,
position, position,
setShowNode, setShowNode,
@ -66,6 +66,7 @@ export default function NodeToolbarComponent({
const isGroup = data.node?.flow ? true : false; const isGroup = data.node?.flow ? true : false;
const { paste, saveComponent, version, flows } = useContext(FlowsContext); const { paste, saveComponent, version, flows } = useContext(FlowsContext);
const { takeSnapshot } = useContext(undoRedoContext);
const reactFlowInstance = useReactFlow(); const reactFlowInstance = useReactFlow();
const [showModalAdvanced, setShowModalAdvanced] = useState(false); const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false); const [showconfirmShare, setShowconfirmShare] = useState(false);
@ -84,6 +85,7 @@ export default function NodeToolbarComponent({
setShowModalAdvanced(true); setShowModalAdvanced(true);
break; break;
case "show": case "show":
takeSnapshot();
setShowNode((prev) => !prev); setShowNode((prev) => !prev);
updateNodeInternals(data.id); updateNodeInternals(data.id);
break; break;
@ -99,6 +101,7 @@ export default function NodeToolbarComponent({
case "disabled": case "disabled":
break; break;
case "ungroup": case "ungroup":
takeSnapshot();
updateFlowPosition(position, data.node?.flow!); updateFlowPosition(position, data.node?.flow!);
expandGroupNode(data, reactFlowInstance, getNodeId); expandGroupNode(data, reactFlowInstance, getNodeId);
break; break;
@ -301,7 +304,6 @@ export default function NodeToolbarComponent({
</ConfirmationModal> </ConfirmationModal>
<EditNodeModal <EditNodeModal
data={data} data={data}
setData={setData}
nodeLength={nodeLength} nodeLength={nodeLength}
open={showModalAdvanced} open={showModalAdvanced}
setOpen={setShowModalAdvanced} setOpen={setShowModalAdvanced}

View file

@ -40,7 +40,6 @@ export type DropDownComponentType = {
}; };
export type ParameterComponentType = { export type ParameterComponentType = {
data: NodeDataType; data: NodeDataType;
setData: (value: NodeDataType) => void;
title: string; title: string;
id: sourceHandleType | targetHandleType; id: sourceHandleType | targetHandleType;
color: string; color: string;
@ -69,8 +68,6 @@ export type KeyPairListComponentType = {
disabled: boolean; disabled: boolean;
editNode?: boolean; editNode?: boolean;
duplicateKey?: boolean; duplicateKey?: boolean;
advanced?: boolean | null;
dataValue?: any;
editNodeModal?: boolean; editNodeModal?: boolean;
}; };
@ -470,7 +467,6 @@ export type fileCardPropsType = {
export type nodeToolbarPropsType = { export type nodeToolbarPropsType = {
data: NodeDataType; data: NodeDataType;
deleteNode: (idx: string) => void; deleteNode: (idx: string) => void;
setData: (newState: NodeDataType) => void;
position: XYPosition; position: XYPosition;
setShowNode: (boolean: any) => void; setShowNode: (boolean: any) => void;
numberOfHandles: boolean[] | []; numberOfHandles: boolean[] | [];

View file

@ -22,6 +22,7 @@ export type NodeType = {
}; };
export type NodeDataType = { export type NodeDataType = {
showNode?: boolean;
type: string; type: string;
node?: APIClassType; node?: APIClassType;
id: string; id: string;

View file

@ -934,8 +934,6 @@ export function expandGroupNode(
updateEdgesIds(flowEdges, idsMap); updateEdgesIds(flowEdges, idsMap);
const gNodes: NodeType[] = flow?.data?.nodes!; const gNodes: NodeType[] = flow?.data?.nodes!;
const gEdges = flow!.data!.edges; const gEdges = flow!.data!.edges;
//TODO update ids of intern nodes and proxy on edges before expanding
console.log(gEdges);
//redirect edges to correct proxy node //redirect edges to correct proxy node
let updatedEdges: Edge[] = []; let updatedEdges: Edge[] = [];
flowEdges.forEach((edge) => { flowEdges.forEach((edge) => {
@ -1046,7 +1044,6 @@ export function getGroupStatus(
let status = { valid: true, params: "Built sucessfully ✨" }; let status = { valid: true, params: "Built sucessfully ✨" };
const { nodes } = flow.data!; const { nodes } = flow.data!;
const ids = nodes.map((n: NodeType) => n.data.id); const ids = nodes.map((n: NodeType) => n.data.id);
ids.forEach((id) => console.log(ssData[id]));
ids.forEach((id) => { ids.forEach((id) => {
if (!ssData[id]) { if (!ssData[id]) {
status = ssData[id]; status = ssData[id];