Fixed Copy and Paste, refactored tabsContext code
This commit is contained in:
parent
f465d9712d
commit
610beb18dd
4 changed files with 629 additions and 543 deletions
|
|
@ -6,13 +6,14 @@ import {
|
|||
ReactNode,
|
||||
useContext,
|
||||
} from "react";
|
||||
import { FlowType } from "../types/flow";
|
||||
import { FlowType, NodeType } from "../types/flow";
|
||||
import { LangFlowState, TabsContextType } from "../types/tabs";
|
||||
import { normalCaseToSnakeCase, updateObject, updateTemplate } from "../utils";
|
||||
import { alertContext } from "./alertContext";
|
||||
import { typesContext } from "./typesContext";
|
||||
import { APITemplateType, TemplateVariableType } from "../types/api";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { addEdge } from "reactflow";
|
||||
|
||||
const TabsContextInitialValue: TabsContextType = {
|
||||
save: () => {},
|
||||
|
|
@ -22,12 +23,14 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
removeFlow: (id: string) => {},
|
||||
addFlow: (flowData?: any) => {},
|
||||
updateFlow: (newFlow: FlowType) => {},
|
||||
incrementNodeId: () => 0,
|
||||
incrementNodeId: () => uuidv4(),
|
||||
downloadFlow: (flow: FlowType) => {},
|
||||
uploadFlow: () => {},
|
||||
hardReset: () => {},
|
||||
disableCP:false,
|
||||
setDisableCP:(state:boolean)=>{},
|
||||
getNodeId: () => "",
|
||||
paste: (selection: {nodes: any, edges: any}, position: {x: number, y: number}) => {},
|
||||
};
|
||||
|
||||
export const TabsContext = createContext<TabsContextType>(
|
||||
|
|
@ -39,22 +42,23 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
const [tabIndex, setTabIndex] = useState(0);
|
||||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||
const [id, setId] = useState(uuidv4());
|
||||
const { templates } = useContext(typesContext);
|
||||
const { templates, reactFlowInstance } = useContext(typesContext);
|
||||
|
||||
const newNodeId = useRef(0);
|
||||
const newNodeId = useRef(uuidv4());
|
||||
function incrementNodeId() {
|
||||
newNodeId.current = newNodeId.current + 1;
|
||||
newNodeId.current = uuidv4();
|
||||
return newNodeId.current;
|
||||
}
|
||||
function save() {
|
||||
if (flows.length !== 0)
|
||||
window.localStorage.setItem(
|
||||
"tabsData",
|
||||
JSON.stringify({ tabIndex, flows, id, nodeId: newNodeId.current })
|
||||
JSON.stringify({ tabIndex, flows, id})
|
||||
);
|
||||
}
|
||||
useEffect(() => {
|
||||
//save tabs locally
|
||||
// console.log(id)
|
||||
save();
|
||||
}, [flows, id, tabIndex, newNodeId]);
|
||||
|
||||
|
|
@ -64,46 +68,26 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
if (cookie && Object.keys(templates).length > 0) {
|
||||
let cookieObject: LangFlowState = JSON.parse(cookie);
|
||||
cookieObject.flows.forEach((flow) => {
|
||||
// check if flow.data is null
|
||||
if (flow.data) {
|
||||
flow.data.nodes.forEach((node) => {
|
||||
// check if node.data.type is in templates
|
||||
if (
|
||||
Object.keys(templates).includes(node.data.type) &&
|
||||
Object.keys(templates[node.data.type]["template"]).length > 0
|
||||
) {
|
||||
node.data.node.base_classes =
|
||||
templates[node.data.type]["base_classes"];
|
||||
flow.data.edges.forEach((edge) => {
|
||||
if (edge.source === node.id) {
|
||||
edge.sourceHandle = edge.sourceHandle
|
||||
.split("|")
|
||||
.slice(0, 2)
|
||||
.concat(templates[node.data.type]["base_classes"])
|
||||
.join("|");
|
||||
}
|
||||
});
|
||||
node.data.node.description =
|
||||
templates[node.data.type]["description"];
|
||||
if (Object.keys(templates[node.data.type]["template"]).length > 0) {
|
||||
node.data.node.template = updateTemplate(
|
||||
templates[node.data.type][
|
||||
"template"
|
||||
] as unknown as APITemplateType,
|
||||
|
||||
node.data.node.template as APITemplateType
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
setTabIndex(cookieObject.tabIndex);
|
||||
setFlows(cookieObject.flows);
|
||||
setId(cookieObject.id);
|
||||
newNodeId.current = cookieObject.nodeId;
|
||||
}
|
||||
}, [templates]);
|
||||
|
||||
function hardReset() {
|
||||
newNodeId.current = 0;
|
||||
newNodeId.current = uuidv4();
|
||||
setTabIndex(0);
|
||||
setFlows([]);
|
||||
setId(uuidv4());
|
||||
|
|
@ -130,6 +114,10 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
function getNodeId() {
|
||||
return `dndnode_` + incrementNodeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
@ -183,6 +171,90 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
* Add a new flow to the list of flows.
|
||||
* @param flow Optional flow to add.
|
||||
*/
|
||||
|
||||
function paste(selectionInstance, position){
|
||||
console.log(position);
|
||||
console.log(selectionInstance)
|
||||
let minimumX = Infinity;
|
||||
let minimumY = Infinity;
|
||||
let idsMap = {};
|
||||
let nodes = reactFlowInstance.getNodes();
|
||||
let edges = reactFlowInstance.getEdges();
|
||||
selectionInstance.nodes.forEach((n) => {
|
||||
if (n.position.y < minimumY) {
|
||||
minimumY = n.position.y;
|
||||
}
|
||||
if (n.position.x < minimumX) {
|
||||
minimumX = n.position.x;
|
||||
}
|
||||
});
|
||||
|
||||
const insidePosition = reactFlowInstance.project(position);
|
||||
|
||||
selectionInstance.nodes.forEach((n) => {
|
||||
// Generate a unique node ID
|
||||
let newId = getNodeId();
|
||||
idsMap[n.id] = newId;
|
||||
|
||||
// Create a new node object
|
||||
const newNode: NodeType = {
|
||||
id: newId,
|
||||
type: "genericNode",
|
||||
position: {
|
||||
x: insidePosition.x + n.position.x - minimumX,
|
||||
y: insidePosition.y + n.position.y - minimumY,
|
||||
},
|
||||
data: {
|
||||
...n.data,
|
||||
id: newId,
|
||||
},
|
||||
};
|
||||
|
||||
// Add the new node to the list of nodes in state
|
||||
nodes = nodes
|
||||
.map((e) => ({ ...e, selected: false }))
|
||||
.concat({ ...newNode, selected: false })
|
||||
console.log(nodes);
|
||||
});
|
||||
reactFlowInstance.setNodes(nodes);
|
||||
|
||||
selectionInstance.edges.forEach((e) => {
|
||||
let source = idsMap[e.source];
|
||||
let target = idsMap[e.target];
|
||||
let sourceHandleSplitted = e.sourceHandle.split("|");
|
||||
let sourceHandle =
|
||||
sourceHandleSplitted[0] +
|
||||
"|" +
|
||||
source +
|
||||
"|" +
|
||||
sourceHandleSplitted.slice(2).join("|");
|
||||
let targetHandleSplitted = e.targetHandle.split("|");
|
||||
let targetHandle =
|
||||
targetHandleSplitted.slice(0, -1).join("|") + "|" + target;
|
||||
let id =
|
||||
"reactflow__edge-" +
|
||||
source +
|
||||
sourceHandle +
|
||||
"-" +
|
||||
target +
|
||||
targetHandle;
|
||||
edges = addEdge(
|
||||
{
|
||||
source,
|
||||
target,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
id,
|
||||
className: "animate-pulse",
|
||||
selected: false,
|
||||
},
|
||||
edges.map((e) => ({ ...e, selected: false }))
|
||||
);
|
||||
console.log(edges);
|
||||
});
|
||||
reactFlowInstance.setEdges(edges);
|
||||
};
|
||||
|
||||
function addFlow(flow?: FlowType) {
|
||||
// Get data from the flow or set it to null if there's no flow provided.
|
||||
const data = flow?.data ? flow.data : null;
|
||||
|
|
@ -191,25 +263,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
if (data) {
|
||||
data.nodes.forEach((node) => {
|
||||
if (Object.keys(templates[node.data.type]["template"]).length > 0) {
|
||||
node.data.node.base_classes =
|
||||
templates[node.data.type]["base_classes"];
|
||||
data.edges.forEach((edge) => {
|
||||
if (edge.source === node.id) {
|
||||
edge.sourceHandle = edge.sourceHandle
|
||||
.split("|")
|
||||
.slice(0, 2)
|
||||
.concat(templates[node.data.type]["base_classes"])
|
||||
.join("|");
|
||||
}
|
||||
});
|
||||
node.data.node.description = templates[node.data.type]["description"];
|
||||
node.data.node.template = updateTemplate(
|
||||
templates[node.data.type]["template"] as unknown as APITemplateType,
|
||||
node.data.node.template as APITemplateType
|
||||
);
|
||||
}
|
||||
});
|
||||
console.log(data);
|
||||
}
|
||||
// Create a new flow with a default name if no flow is provided.
|
||||
let newFlow: FlowType = {
|
||||
|
|
@ -223,6 +282,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
setId(uuidv4());
|
||||
|
||||
// Add the new flow to the list of flows.
|
||||
|
||||
setFlows((prevState) => {
|
||||
const newFlows = [...prevState, newFlow];
|
||||
return newFlows;
|
||||
|
|
@ -265,6 +325,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
updateFlow,
|
||||
downloadFlow,
|
||||
uploadFlow,
|
||||
getNodeId,
|
||||
paste,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ export default function TabComponent({
|
|||
className="bg-transparent focus:border-none active:outline hover:outline focus:outline outline-gray-300 rounded-md w-28"
|
||||
onBlur={() => {
|
||||
setIsRename(false);
|
||||
if (value !== "") {
|
||||
setDisableCP(false);
|
||||
if (value !== "") {
|
||||
let newFlow = _.cloneDeep(flow);
|
||||
newFlow.name = value;
|
||||
updateFlow(newFlow);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ import ReactFlow, {
|
|||
Connection,
|
||||
Edge,
|
||||
useKeyPress,
|
||||
useOnSelectionChange,
|
||||
NodeDragHandler,
|
||||
OnEdgesDelete,
|
||||
OnNodesDelete,
|
||||
SelectionDragHandler,
|
||||
useOnViewportChange,
|
||||
OnSelectionChangeParams,
|
||||
OnNodesChange,
|
||||
} from "reactflow";
|
||||
import _ from "lodash";
|
||||
import { locationContext } from "../../contexts/locationContext";
|
||||
|
|
@ -28,16 +30,17 @@ import { typesContext } from "../../contexts/typesContext";
|
|||
import ConnectionLineComponent from "./components/ConnectionLineComponent";
|
||||
import { FlowType, NodeType } from "../../types/flow";
|
||||
import { APIClassType } from "../../types/api";
|
||||
import { isValidConnection } from "../../utils";
|
||||
import {
|
||||
isValidConnection,
|
||||
} from "../../utils";
|
||||
import useUndoRedo from "./hooks/useUndoRedo";
|
||||
|
||||
const nodeTypes = {
|
||||
genericNode: GenericNode,
|
||||
};
|
||||
|
||||
|
||||
export default function FlowPage({ flow }: { flow: FlowType }) {
|
||||
let { updateFlow, incrementNodeId, disableCP} =
|
||||
let { updateFlow, disableCP, addFlow, getNodeId, paste } =
|
||||
useContext(TabsContext);
|
||||
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
||||
useContext(typesContext);
|
||||
|
|
@ -45,86 +48,65 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
|
||||
const { undo, redo, canUndo, canRedo, takeSnapshot } = useUndoRedo();
|
||||
|
||||
const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if ((event.ctrlKey || event.metaKey) && (event.key === 'c') && lastSelection && !disableCP) {
|
||||
event.preventDefault();
|
||||
setLastCopiedSelection(lastSelection);
|
||||
}
|
||||
if ((event.ctrlKey || event.metaKey) && (event.key === 'v') && lastCopiedSelection && !disableCP) {
|
||||
event.preventDefault();
|
||||
paste();
|
||||
}
|
||||
}
|
||||
|
||||
const [lastSelection, setLastSelection] = useState(null);
|
||||
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
|
||||
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||||
const [lastSelection, setLastSelection] =
|
||||
useState<OnSelectionChangeParams>(null);
|
||||
|
||||
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// this effect is used to attach the global event handlers
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
console.log("keydownou", lastCopiedSelection, position)
|
||||
if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key === "c" &&
|
||||
lastSelection &&
|
||||
!disableCP
|
||||
) {
|
||||
event.preventDefault();
|
||||
setLastCopiedSelection(_.cloneDeep(lastSelection));
|
||||
}
|
||||
if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key === "v" &&
|
||||
lastCopiedSelection &&
|
||||
!disableCP
|
||||
) {
|
||||
event.preventDefault();
|
||||
let bounds = reactFlowWrapper.current.getBoundingClientRect();
|
||||
paste(lastCopiedSelection, {
|
||||
x: position.x - bounds.left,
|
||||
y: position.y - bounds.top,
|
||||
});
|
||||
}
|
||||
if (
|
||||
(event.ctrlKey || event.metaKey) &&
|
||||
event.key === "g" &&
|
||||
lastSelection
|
||||
) {
|
||||
event.preventDefault();
|
||||
// addFlow(newFlow, false);
|
||||
}
|
||||
};
|
||||
const handleMouseMove = (event) => {
|
||||
setPosition({ x: event.clientX, y: event.clientY });
|
||||
};
|
||||
|
||||
useOnSelectionChange({
|
||||
onChange: (flow) => { setLastSelection(flow); },
|
||||
})
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
|
||||
let paste = () => {
|
||||
let minimumX = Infinity;
|
||||
let minimumY = Infinity;
|
||||
let idsMap = {};
|
||||
lastCopiedSelection.nodes.forEach((n) => {
|
||||
if (n.position.y < minimumY) {
|
||||
minimumY = n.position.y
|
||||
}
|
||||
if (n.position.x < minimumX) {
|
||||
minimumX = n.position.x;
|
||||
}
|
||||
});
|
||||
|
||||
const bounds = reactFlowWrapper.current.getBoundingClientRect();
|
||||
const insidePosition = reactFlowInstance.project({
|
||||
x: position.x - bounds.left,
|
||||
y: position.y - bounds.top
|
||||
});
|
||||
|
||||
lastCopiedSelection.nodes.forEach((n) => {
|
||||
|
||||
// Generate a unique node ID
|
||||
let newId = getId();
|
||||
idsMap[n.id] = newId;
|
||||
|
||||
// Create a new node object
|
||||
const newNode: NodeType = {
|
||||
id: newId,
|
||||
type: "genericNode",
|
||||
position: {
|
||||
x: insidePosition.x + n.position.x - minimumX,
|
||||
y: insidePosition.y + n.position.y - minimumY,
|
||||
},
|
||||
data: {
|
||||
...n.data,
|
||||
id: newId,
|
||||
},
|
||||
return () => {
|
||||
document.removeEventListener('keydown', onKeyDown);
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
};
|
||||
}, [position, lastCopiedSelection, lastSelection]);
|
||||
|
||||
// Add the new node to the list of nodes in state
|
||||
setNodes((nds) => nds.map((e) => ({ ...e, selected: false })).concat({ ...newNode, selected: false }));
|
||||
})
|
||||
|
||||
lastCopiedSelection.edges.forEach((e) => {
|
||||
let source = idsMap[e.source];
|
||||
let target = idsMap[e.target];
|
||||
let sourceHandleSplitted = e.sourceHandle.split('|');
|
||||
let sourceHandle = sourceHandleSplitted[0] + '|' + source + '|' + sourceHandleSplitted.slice(2).join('|');
|
||||
let targetHandleSplitted = e.targetHandle.split('|');
|
||||
let targetHandle = targetHandleSplitted.slice(0, -1).join('|') + '|' + target;
|
||||
let id = "reactflow__edge-" + source + sourceHandle + "-" + target + targetHandle;
|
||||
setEdges((eds) =>
|
||||
addEdge({ source, target, sourceHandle, targetHandle, id, className: "animate-pulse", selected: false }, eds.map((e) => ({ ...e, selected: false })))
|
||||
);
|
||||
})
|
||||
}
|
||||
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
|
||||
|
||||
|
||||
|
||||
const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
|
||||
|
|
@ -137,11 +119,6 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
);
|
||||
const { setViewport } = useReactFlow();
|
||||
const edgeUpdateSuccessful = useRef(true);
|
||||
|
||||
function getId() {
|
||||
return `dndnode_` + incrementNodeId();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (reactFlowInstance && flow) {
|
||||
flow.data = reactFlowInstance.toObject();
|
||||
|
|
@ -178,7 +155,21 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
(params: Connection) => {
|
||||
takeSnapshot();
|
||||
setEdges((eds) =>
|
||||
addEdge({ ...params, className: "animate-pulse" }, eds)
|
||||
addEdge(
|
||||
{
|
||||
...params,
|
||||
style:
|
||||
params.targetHandle.split("|")[0] === "Text"
|
||||
? { stroke: "#333333", strokeWidth: 2 }
|
||||
: { stroke: "#222222" },
|
||||
className:
|
||||
params.targetHandle.split("|")[0] === "Text"
|
||||
? ""
|
||||
: "animate-pulse",
|
||||
animated: params.targetHandle.split("|")[0] === "Text",
|
||||
},
|
||||
eds
|
||||
)
|
||||
);
|
||||
setNodes((x) => {
|
||||
let newX = _.cloneDeep(x);
|
||||
|
|
@ -199,7 +190,6 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
takeSnapshot();
|
||||
}, [takeSnapshot]);
|
||||
|
||||
|
||||
const onEdgesDelete: OnEdgesDelete = useCallback(() => {
|
||||
// 👇 make deleting edges undoable
|
||||
takeSnapshot();
|
||||
|
|
@ -224,11 +214,6 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
);
|
||||
|
||||
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
|
||||
if (
|
||||
data.type !== "chatInput" ||
|
||||
(data.type === "chatInput" &&
|
||||
!reactFlowInstance.getNodes().some((n) => n.type === "chatInputNode"))
|
||||
) {
|
||||
// Calculate the position where the node should be created
|
||||
const position = reactFlowInstance.project({
|
||||
x: event.clientX - reactflowBounds.left,
|
||||
|
|
@ -236,10 +221,24 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
});
|
||||
|
||||
// Generate a unique node ID
|
||||
let newId = getId();
|
||||
let newId = getNodeId();
|
||||
let newNode: NodeType;
|
||||
|
||||
if (data.type !== "groupNode") {
|
||||
// Create a new node object
|
||||
const newNode: NodeType = {
|
||||
newNode = {
|
||||
id: newId,
|
||||
type: "genericNode",
|
||||
position,
|
||||
data: {
|
||||
...data,
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// Create a new node object
|
||||
newNode = {
|
||||
id: newId,
|
||||
type: "genericNode",
|
||||
position,
|
||||
|
|
@ -251,27 +250,24 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
};
|
||||
|
||||
// Add the new node to the list of nodes in state
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
} else {
|
||||
// If a chat input node already exists, set an error message
|
||||
setErrorData({
|
||||
title: "Error creating node",
|
||||
list: ["There can't be more than one chat input."],
|
||||
});
|
||||
}
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
},
|
||||
// Specify dependencies for useCallback
|
||||
[incrementNodeId, reactFlowInstance, setErrorData, setNodes, takeSnapshot]
|
||||
[getNodeId, reactFlowInstance, setErrorData, setNodes, takeSnapshot]
|
||||
);
|
||||
|
||||
const onDelete = useCallback((mynodes) => {
|
||||
const onDelete = useCallback(
|
||||
(mynodes) => {
|
||||
takeSnapshot();
|
||||
setEdges(
|
||||
edges.filter(
|
||||
(ns) => !mynodes.some((n) => ns.source === n.id || ns.target === n.id)
|
||||
)
|
||||
);
|
||||
}, [takeSnapshot, edges, setEdges]);
|
||||
},
|
||||
[takeSnapshot, edges, setEdges]
|
||||
);
|
||||
|
||||
const onEdgeUpdateStart = useCallback(() => {
|
||||
edgeUpdateSuccessful.current = false;
|
||||
|
|
@ -295,19 +291,43 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
edgeUpdateSuccessful.current = true;
|
||||
}, []);
|
||||
|
||||
const [selectionEnded, setSelectionEnded] = useState(false);
|
||||
|
||||
const onSelectionEnd = useCallback(() => {
|
||||
setSelectionEnded(true);
|
||||
}, []);
|
||||
const onSelectionStart = useCallback(() => {
|
||||
setSelectionEnded(false);
|
||||
}, []);
|
||||
|
||||
// Workaround to show the menu only after the selection has ended.
|
||||
useEffect(() => {
|
||||
if (selectionEnded && lastSelection && lastSelection.nodes.length > 1) {
|
||||
setSelectionMenuVisible(true);
|
||||
} else {
|
||||
setSelectionMenuVisible(false);
|
||||
}
|
||||
}, [selectionEnded, lastSelection]);
|
||||
|
||||
const onSelectionChange = useCallback((flow) => {
|
||||
setLastSelection(flow);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full" onMouseMove={handleMouseMove} ref={reactFlowWrapper}>
|
||||
<div
|
||||
className="w-full h-full"
|
||||
ref={reactFlowWrapper}
|
||||
>
|
||||
{Object.keys(templates).length > 0 && Object.keys(types).length > 0 ? (
|
||||
<>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
onMove={() =>
|
||||
updateFlow({ ...flow, data: reactFlowInstance.toObject() })
|
||||
}
|
||||
onMove={() => {
|
||||
updateFlow({ ...flow, data: reactFlowInstance.toObject() });
|
||||
}}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChangeMod}
|
||||
onKeyDown={(e) => onKeyDown(e)}
|
||||
onConnect={onConnect}
|
||||
onLoad={setReactFlowInstance}
|
||||
onInit={setReactFlowInstance}
|
||||
|
|
@ -317,16 +337,18 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||
onNodeDragStart={onNodeDragStart}
|
||||
onSelectionDragStart={onSelectionDragStart}
|
||||
onSelectionEnd={onSelectionEnd}
|
||||
onSelectionStart={onSelectionStart}
|
||||
onEdgesDelete={onEdgesDelete}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
onNodesDelete={onDelete}
|
||||
onSelectionChange={onSelectionChange}
|
||||
selectNodesOnDrag={false}
|
||||
>
|
||||
<Background className="dark:bg-gray-900" />
|
||||
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600">
|
||||
</Controls>
|
||||
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls>
|
||||
</ReactFlow>
|
||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -6,15 +6,17 @@ export type TabsContextType = {
|
|||
setTabIndex: (index: number) => void;
|
||||
flows: Array<FlowType>;
|
||||
removeFlow: (id: string) => void;
|
||||
addFlow: (flowData?: FlowType) => void;
|
||||
addFlow: (flowData?: FlowType,newFlow?:boolean) => void;
|
||||
updateFlow: (newFlow: FlowType) => void;
|
||||
incrementNodeId: () => number;
|
||||
incrementNodeId: () => string;
|
||||
downloadFlow: (flow: FlowType) => void;
|
||||
uploadFlow: () => void;
|
||||
uploadFlow: (newFlow?:boolean) => void;
|
||||
hardReset: () => void;
|
||||
//disable CopyPaste
|
||||
disableCP: boolean;
|
||||
setDisableCP: (value: boolean) => void;
|
||||
getNodeId: () => string;
|
||||
paste: (selection: {nodes: any, edges: any}, position: {x: number, y: number}) => void;
|
||||
};
|
||||
|
||||
export type LangFlowState = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue