format code
This commit is contained in:
parent
7aa437e92f
commit
bba2942d37
13 changed files with 210 additions and 226 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
from langflow.database.models.flow import Flow
|
from langflow.database.models.flow import Flow
|
||||||
from langflow.processing.process import process_graph_cached, process_tweaks
|
from langflow.processing.process import process_graph_cached, process_tweaks
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
from importlib.metadata import version
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from fastapi.security import HTTPBearer
|
from fastapi.security import HTTPBearer
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ from langflow.graph.vertex.types import (
|
||||||
)
|
)
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
from langflow.utils.logger import logger
|
|
||||||
|
|
||||||
|
|
||||||
class Graph:
|
class Graph:
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,6 @@ export default function ParameterComponent({
|
||||||
}
|
}
|
||||||
onChange={(t: string[]) => {
|
onChange={(t: string[]) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : data.node.template[name].multiline ? (
|
) : data.node.template[name].multiline ? (
|
||||||
|
|
@ -176,7 +175,6 @@ export default function ParameterComponent({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -187,7 +185,6 @@ export default function ParameterComponent({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -200,7 +197,6 @@ export default function ParameterComponent({
|
||||||
setEnabled={(t) => {
|
setEnabled={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
setEnabled(t);
|
setEnabled(t);
|
||||||
|
|
||||||
}}
|
}}
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
|
|
@ -213,7 +209,7 @@ export default function ParameterComponent({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : left === true &&
|
) : left === true &&
|
||||||
|
|
@ -234,7 +230,6 @@ export default function ParameterComponent({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : left === true && type === "file" ? (
|
) : left === true && type === "file" ? (
|
||||||
|
|
@ -248,7 +243,6 @@ export default function ParameterComponent({
|
||||||
suffixes={data.node.template[name].suffixes}
|
suffixes={data.node.template[name].suffixes}
|
||||||
onFileChange={(t: string) => {
|
onFileChange={(t: string) => {
|
||||||
data.node.template[name].content = t;
|
data.node.template[name].content = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
></InputFileComponent>
|
></InputFileComponent>
|
||||||
) : left === true && type === "int" ? (
|
) : left === true && type === "int" ? (
|
||||||
|
|
@ -259,7 +253,6 @@ export default function ParameterComponent({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t) => {
|
onChange={(t) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -269,7 +262,6 @@ export default function ParameterComponent({
|
||||||
value={data.node.template[name].value ?? ""}
|
value={data.node.template[name].value ?? ""}
|
||||||
onChange={(t: string) => {
|
onChange={(t: string) => {
|
||||||
data.node.template[name].value = t;
|
data.node.template[name].value = t;
|
||||||
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,3 @@
|
||||||
import {
|
|
||||||
BugAntIcon,
|
|
||||||
Cog6ToothIcon,
|
|
||||||
InformationCircleIcon,
|
|
||||||
TrashIcon,
|
|
||||||
} from "@heroicons/react/24/outline";
|
|
||||||
|
|
||||||
import {
|
|
||||||
CheckCircleIcon,
|
|
||||||
EllipsisHorizontalCircleIcon,
|
|
||||||
ExclamationCircleIcon,
|
|
||||||
} from "@heroicons/react/24/solid";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
classNames,
|
classNames,
|
||||||
nodeColors,
|
nodeColors,
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,25 @@ import { Link } from "react-router-dom";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import { FlowType } from "../../types/flow";
|
import { FlowType } from "../../types/flow";
|
||||||
import { gradients } from "../../utils";
|
import { gradients } from "../../utils";
|
||||||
import { CardTitle, CardDescription, CardFooter, Card, CardHeader } from "../ui/card";
|
import {
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
} from "../ui/card";
|
||||||
|
|
||||||
|
export const CardComponent = ({
|
||||||
export const CardComponent = ({ flow, id, onDelete, button }: { flow: FlowType; id: string, onDelete?: () => void, button?: JSX.Element }) => {
|
flow,
|
||||||
|
id,
|
||||||
|
onDelete,
|
||||||
|
button,
|
||||||
|
}: {
|
||||||
|
flow: FlowType;
|
||||||
|
id: string;
|
||||||
|
onDelete?: () => void;
|
||||||
|
button?: JSX.Element;
|
||||||
|
}) => {
|
||||||
const { removeFlow } = useContext(TabsContext);
|
const { removeFlow } = useContext(TabsContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -20,19 +35,14 @@ export const CardComponent = ({ flow, id, onDelete, button }: { flow: FlowType;
|
||||||
"rounded-full w-8 h-8 flex items-center justify-center text-2xl " +
|
"rounded-full w-8 h-8 flex items-center justify-center text-2xl " +
|
||||||
gradients[parseInt(flow.id.slice(0, 6), 16) % gradients.length]
|
gradients[parseInt(flow.id.slice(0, 6), 16) % gradients.length]
|
||||||
}
|
}
|
||||||
>
|
></span>
|
||||||
</span>
|
<span className="flex-1 truncate-doubleline">{flow.name}</span>
|
||||||
<span className="flex-1 truncate-doubleline">
|
|
||||||
{flow.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
{onDelete &&
|
{onDelete && (
|
||||||
<button
|
<button onClick={onDelete}>
|
||||||
onClick={onDelete}
|
<Trash2 className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
||||||
>
|
</button>
|
||||||
<Trash2 className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
)}
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="pt-2 pb-2">
|
<CardDescription className="pt-2 pb-2">
|
||||||
<div className="truncate-doubleline">
|
<div className="truncate-doubleline">
|
||||||
|
|
@ -53,10 +63,7 @@ export const CardComponent = ({ flow, id, onDelete, button }: { flow: FlowType;
|
||||||
<span className="text-base"> </span>OpenAI+
|
<span className="text-base"> </span>OpenAI+
|
||||||
</Badge> */}
|
</Badge> */}
|
||||||
</div>
|
</div>
|
||||||
{button &&
|
{button && button}
|
||||||
button
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,7 @@ export default function Header() {
|
||||||
<span className="text-2xl ml-4">⛓️</span>
|
<span className="text-2xl ml-4">⛓️</span>
|
||||||
</Link>
|
</Link>
|
||||||
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
||||||
<MenuBar
|
<MenuBar flows={flows} tabId={tabId} />
|
||||||
flows={flows}
|
|
||||||
tabId={tabId}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ export default function ToggleShadComponent({
|
||||||
enabled,
|
enabled,
|
||||||
setEnabled,
|
setEnabled,
|
||||||
disabled,
|
disabled,
|
||||||
size
|
size,
|
||||||
}: ToggleComponentType) {
|
}: ToggleComponentType) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
|
|
@ -14,7 +14,7 @@ export default function ToggleShadComponent({
|
||||||
}
|
}
|
||||||
}, [disabled, setEnabled]);
|
}, [disabled, setEnabled]);
|
||||||
let scaleX, scaleY;
|
let scaleX, scaleY;
|
||||||
switch(size){
|
switch (size) {
|
||||||
case "small":
|
case "small":
|
||||||
scaleX = 0.6;
|
scaleX = 0.6;
|
||||||
scaleY = 0.6;
|
scaleY = 0.6;
|
||||||
|
|
|
||||||
|
|
@ -126,4 +126,3 @@ export const EXPORT_CODE_DIALOG =
|
||||||
*/
|
*/
|
||||||
export const INPUT_STYLE =
|
export const INPUT_STYLE =
|
||||||
" focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
|
" focus:ring-1 focus:ring-offset-1 focus:ring-ring focus:outline-none ";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,17 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
||||||
<>
|
<>
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<DarkProvider>
|
<DarkProvider>
|
||||||
<TypesProvider>
|
<TypesProvider>
|
||||||
<LocationProvider>
|
<LocationProvider>
|
||||||
<AlertProvider>
|
<AlertProvider>
|
||||||
<TabsProvider>
|
<TabsProvider>
|
||||||
<UndoRedoProvider>
|
<UndoRedoProvider>
|
||||||
<PopUpProvider>{children}</PopUpProvider>
|
<PopUpProvider>{children}</PopUpProvider>
|
||||||
</UndoRedoProvider>
|
</UndoRedoProvider>
|
||||||
</TabsProvider>
|
</TabsProvider>
|
||||||
</AlertProvider>
|
</AlertProvider>
|
||||||
</LocationProvider>
|
</LocationProvider>
|
||||||
</TypesProvider>
|
</TypesProvider>
|
||||||
</DarkProvider>
|
</DarkProvider>
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
import { createContext, useCallback, useContext, useEffect, useState } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
useCallback,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { Edge, Node, useReactFlow } from "reactflow";
|
import { Edge, Node, useReactFlow } from "reactflow";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { TabsContext } from "./tabsContext";
|
import { TabsContext } from "./tabsContext";
|
||||||
|
|
@ -10,22 +16,22 @@ type undoRedoContextType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type UseUndoRedoOptions = {
|
type UseUndoRedoOptions = {
|
||||||
maxHistorySize: number;
|
maxHistorySize: number;
|
||||||
enableShortcuts: boolean;
|
enableShortcuts: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UseUndoRedo = (options?: UseUndoRedoOptions) => {
|
type UseUndoRedo = (options?: UseUndoRedoOptions) => {
|
||||||
undo: () => void;
|
undo: () => void;
|
||||||
redo: () => void;
|
redo: () => void;
|
||||||
takeSnapshot: () => void;
|
takeSnapshot: () => void;
|
||||||
canUndo: boolean;
|
canUndo: boolean;
|
||||||
canRedo: boolean;
|
canRedo: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HistoryItem = {
|
type HistoryItem = {
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialValue = {
|
const initialValue = {
|
||||||
undo: () => {},
|
undo: () => {},
|
||||||
|
|
@ -34,152 +40,144 @@ const initialValue = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultOptions: UseUndoRedoOptions = {
|
const defaultOptions: UseUndoRedoOptions = {
|
||||||
maxHistorySize: 100,
|
maxHistorySize: 100,
|
||||||
enableShortcuts: true,
|
enableShortcuts: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const undoRedoContext = createContext<undoRedoContextType>(initialValue);
|
export const undoRedoContext = createContext<undoRedoContextType>(initialValue);
|
||||||
|
|
||||||
export function UndoRedoProvider({ children }) {
|
export function UndoRedoProvider({ children }) {
|
||||||
const { tabId, flows } = useContext(TabsContext);
|
const { tabId, flows } = useContext(TabsContext);
|
||||||
|
|
||||||
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(() => []));
|
||||||
const [tabIndex, setTabIndex] = useState(flows.findIndex((f) => f.id === tabId));
|
const [tabIndex, setTabIndex] = useState(
|
||||||
|
flows.findIndex((f) => f.id === tabId)
|
||||||
useEffect(() => {
|
);
|
||||||
// whenever the flows variable changes, we need to add one array to the past and future states
|
|
||||||
setPast((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
|
||||||
setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
|
||||||
setTabIndex(flows.findIndex((f) => f.id === tabId));
|
|
||||||
|
|
||||||
}, [flows, tabId]);
|
useEffect(() => {
|
||||||
|
// whenever the flows variable changes, we need to add one array to the past and future states
|
||||||
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();
|
setPast((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
||||||
|
setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
|
||||||
const takeSnapshot = useCallback(() => {
|
setTabIndex(flows.findIndex((f) => f.id === tabId));
|
||||||
// push the current graph to the past state
|
}, [flows, tabId]);
|
||||||
console.log(past);
|
|
||||||
console.log(tabIndex);
|
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();
|
||||||
|
|
||||||
|
const takeSnapshot = useCallback(() => {
|
||||||
|
// push the current graph to the past state
|
||||||
|
console.log(past);
|
||||||
|
console.log(tabIndex);
|
||||||
|
setPast((old) => {
|
||||||
|
let newPast = cloneDeep(old);
|
||||||
|
newPast[tabIndex] = old[tabIndex].slice(
|
||||||
|
old[tabIndex].length - defaultOptions.maxHistorySize + 1,
|
||||||
|
old[tabIndex].length
|
||||||
|
);
|
||||||
|
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||||
|
return newPast;
|
||||||
|
});
|
||||||
|
|
||||||
|
// whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches
|
||||||
|
setFuture((old) => {
|
||||||
|
let newFuture = cloneDeep(old);
|
||||||
|
newFuture[tabIndex] = [];
|
||||||
|
return newFuture;
|
||||||
|
});
|
||||||
|
}, [getNodes, getEdges, past, future, flows, tabId, setPast, setFuture]);
|
||||||
|
|
||||||
|
const undo = useCallback(() => {
|
||||||
|
// get the last state that we want to go back to
|
||||||
|
const pastState = past[tabIndex][past[tabIndex].length - 1];
|
||||||
|
|
||||||
|
if (pastState) {
|
||||||
|
// first we remove the state from the history
|
||||||
setPast((old) => {
|
setPast((old) => {
|
||||||
let newPast = cloneDeep(old);
|
let newPast = cloneDeep(old);
|
||||||
newPast[tabIndex] = old[tabIndex].slice(
|
newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
||||||
old[tabIndex].length - defaultOptions.maxHistorySize + 1,
|
return newPast;
|
||||||
old[tabIndex].length
|
});
|
||||||
);
|
// we store the current graph for the redo operation
|
||||||
|
setFuture((old) => {
|
||||||
|
let newFuture = cloneDeep(old);
|
||||||
|
newFuture[tabIndex] = old[tabIndex];
|
||||||
|
newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||||
|
return newFuture;
|
||||||
|
});
|
||||||
|
// now we can set the graph to the past state
|
||||||
|
setNodes(pastState.nodes);
|
||||||
|
setEdges(pastState.edges);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
setNodes,
|
||||||
|
setEdges,
|
||||||
|
getNodes,
|
||||||
|
getEdges,
|
||||||
|
future,
|
||||||
|
past,
|
||||||
|
setFuture,
|
||||||
|
setPast,
|
||||||
|
tabIndex,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const redo = useCallback(() => {
|
||||||
|
const futureState = future[tabIndex][future[tabIndex].length - 1];
|
||||||
|
|
||||||
|
if (futureState) {
|
||||||
|
setFuture((old) => {
|
||||||
|
let newFuture = cloneDeep(old);
|
||||||
|
newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
||||||
|
return newFuture;
|
||||||
|
});
|
||||||
|
setPast((old) => {
|
||||||
|
let newPast = cloneDeep(old);
|
||||||
|
newPast[tabIndex] = old[tabIndex];
|
||||||
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
||||||
return newPast;
|
return newPast;
|
||||||
});
|
});
|
||||||
|
setNodes(futureState.nodes);
|
||||||
// whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches
|
setEdges(futureState.edges);
|
||||||
setFuture((old) => {
|
}
|
||||||
let newFuture = cloneDeep(old);
|
}, [
|
||||||
newFuture[tabIndex] = [];
|
future,
|
||||||
return newFuture;
|
past,
|
||||||
});
|
setFuture,
|
||||||
}, [
|
setPast,
|
||||||
getNodes,
|
setNodes,
|
||||||
getEdges,
|
setEdges,
|
||||||
past,
|
getNodes,
|
||||||
future,
|
getEdges,
|
||||||
flows,
|
future,
|
||||||
tabId,
|
tabIndex,
|
||||||
setPast,
|
]);
|
||||||
setFuture,
|
|
||||||
]);
|
useEffect(() => {
|
||||||
|
// this effect is used to attach the global event handlers
|
||||||
const undo = useCallback(() => {
|
if (!defaultOptions.enableShortcuts) {
|
||||||
// get the last state that we want to go back to
|
return;
|
||||||
const pastState = past[tabIndex][past[tabIndex].length - 1];
|
}
|
||||||
|
|
||||||
if (pastState) {
|
const keyDownHandler = (event: KeyboardEvent) => {
|
||||||
// first we remove the state from the history
|
if (
|
||||||
setPast((old) => {
|
event.key === "z" &&
|
||||||
let newPast = cloneDeep(old);
|
(event.ctrlKey || event.metaKey) &&
|
||||||
newPast[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
event.shiftKey
|
||||||
return newPast;
|
) {
|
||||||
});
|
redo();
|
||||||
// we store the current graph for the redo operation
|
} else if (event.key === "y" && (event.ctrlKey || event.metaKey)) {
|
||||||
setFuture((old) => {
|
event.preventDefault(); // prevent the default action
|
||||||
let newFuture = cloneDeep(old);
|
redo();
|
||||||
newFuture[tabIndex] = old[tabIndex];
|
} else if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
|
||||||
newFuture[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
undo();
|
||||||
return newFuture;
|
|
||||||
});
|
|
||||||
// now we can set the graph to the past state
|
|
||||||
setNodes(pastState.nodes);
|
|
||||||
setEdges(pastState.edges);
|
|
||||||
}
|
}
|
||||||
}, [
|
};
|
||||||
setNodes,
|
|
||||||
setEdges,
|
document.addEventListener("keydown", keyDownHandler);
|
||||||
getNodes,
|
|
||||||
getEdges,
|
return () => {
|
||||||
future,
|
document.removeEventListener("keydown", keyDownHandler);
|
||||||
past,
|
};
|
||||||
setFuture,
|
}, [undo, redo]);
|
||||||
setPast,
|
|
||||||
tabIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const redo = useCallback(() => {
|
|
||||||
const futureState = future[tabIndex][future[tabIndex].length - 1];
|
|
||||||
|
|
||||||
if (futureState) {
|
|
||||||
setFuture((old) => {
|
|
||||||
let newFuture = cloneDeep(old);
|
|
||||||
newFuture[tabIndex] = old[tabIndex].slice(0, old[tabIndex].length - 1);
|
|
||||||
return newFuture;
|
|
||||||
});
|
|
||||||
setPast((old) => {
|
|
||||||
let newPast = cloneDeep(old);
|
|
||||||
newPast[tabIndex] = old[tabIndex];
|
|
||||||
newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() });
|
|
||||||
return newPast;
|
|
||||||
});
|
|
||||||
setNodes(futureState.nodes);
|
|
||||||
setEdges(futureState.edges);
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
future,
|
|
||||||
past,
|
|
||||||
setFuture,
|
|
||||||
setPast,
|
|
||||||
setNodes,
|
|
||||||
setEdges,
|
|
||||||
getNodes,
|
|
||||||
getEdges,
|
|
||||||
future,
|
|
||||||
tabIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// this effect is used to attach the global event handlers
|
|
||||||
if (!defaultOptions.enableShortcuts) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyDownHandler = (event: KeyboardEvent) => {
|
|
||||||
if (
|
|
||||||
event.key === "z" &&
|
|
||||||
(event.ctrlKey || event.metaKey) &&
|
|
||||||
event.shiftKey
|
|
||||||
) {
|
|
||||||
redo();
|
|
||||||
} else if (event.key === "y" && (event.ctrlKey || event.metaKey)) {
|
|
||||||
event.preventDefault(); // prevent the default action
|
|
||||||
redo();
|
|
||||||
} else if (event.key === "z" && (event.ctrlKey || event.metaKey)) {
|
|
||||||
undo();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("keydown", keyDownHandler);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("keydown", keyDownHandler);
|
|
||||||
};
|
|
||||||
}, [undo, redo]);
|
|
||||||
return (
|
return (
|
||||||
<undoRedoContext.Provider
|
<undoRedoContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { PromptTypeAPI, errorsTypeAPI } from "./../../types/api/index";
|
||||||
import { APIObjectType, sendAllProps } from "../../types/api/index";
|
import { APIObjectType, sendAllProps } from "../../types/api/index";
|
||||||
import axios, { AxiosResponse } from "axios";
|
import axios, { AxiosResponse } from "axios";
|
||||||
import { FlowStyleType, FlowType } from "../../types/flow";
|
import { FlowStyleType, FlowType } from "../../types/flow";
|
||||||
|
import { ReactFlowJsonObject } from "reactflow";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all objects from the API endpoint.
|
* Fetches all objects from the API endpoint.
|
||||||
|
|
@ -76,7 +77,13 @@ export async function getExamples(): Promise<FlowType[]> {
|
||||||
* @returns {Promise<any>} The saved flow data.
|
* @returns {Promise<any>} The saved flow data.
|
||||||
* @throws Will throw an error if saving fails.
|
* @throws Will throw an error if saving fails.
|
||||||
*/
|
*/
|
||||||
export async function saveFlowToDatabase(newFlow: FlowType): Promise<FlowType> {
|
export async function saveFlowToDatabase(newFlow: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
data: ReactFlowJsonObject;
|
||||||
|
description: string;
|
||||||
|
style?: FlowStyleType;
|
||||||
|
}): Promise<FlowType> {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post("/api/v1/flows/", {
|
const response = await axios.post("/api/v1/flows/", {
|
||||||
name: newFlow.name,
|
name: newFlow.name,
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ export default function FlowSettingsModal() {
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center">
|
<DialogTitle className="flex items-center">
|
||||||
<span className="pr-2">Settings</span>
|
<span className="pr-2">Settings</span>
|
||||||
|
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>{SETTINGS_DIALOG_SUBTITLE}</DialogDescription>
|
<DialogDescription>{SETTINGS_DIALOG_SUBTITLE}</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
import { Route, Routes } from "react-router-dom"
|
import { Route, Routes } from "react-router-dom";
|
||||||
import HomePage from "./pages/MainPage";
|
import HomePage from "./pages/MainPage";
|
||||||
import FlowPage from "./pages/FlowPage";
|
import FlowPage from "./pages/FlowPage";
|
||||||
import CommunityPage from "./pages/CommunityPage";
|
import CommunityPage from "./pages/CommunityPage";
|
||||||
|
|
||||||
const Router = () => {
|
const Router = () => {
|
||||||
return(
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<HomePage/>}/>
|
<Route path="/" element={<HomePage />} />
|
||||||
<Route path="/community" element={<CommunityPage/>}/>
|
<Route path="/community" element={<CommunityPage />} />
|
||||||
<Route path="/flow/:id/">
|
<Route path="/flow/:id/">
|
||||||
<Route path="" element={<FlowPage/>}/>
|
<Route path="" element={<FlowPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Router;
|
export default Router;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue