refactor(builder): Update ReactFlow to version 12 & split up Flow.tsx (#7808)

Update ReactFlow to version 12 and split `Flow.tsx` into `useAgentGraph` hook that takes care of agent state and API calls to the server.

- Update ReactFlow to v12 ([migration guide](https://reactflow.dev/learn/troubleshooting/migrate-to-v12))
- Move `setIsAnyModalOpen` to `FlowContext`
- Make `setHardcodedValues` and `setErrors` functions of `CustomNode` and utilize new `updateNodeData` ReactFlow API
- Fix type errors
- `useAgentGraph` hook
  - Take care of all API calls, websocket, agent state and logic
  - Make saving and execution async and thus more consistent and reliable
	- Save&run requests are state
	- Wait for node ids to sync with backend reactively
	- Queue execution updates
  - Memoize functions using `useCallback`
This commit is contained in:
Krzysztof Czerwinski
2024-08-26 11:45:05 +01:00
committed by GitHub
parent 1df7d527dd
commit 299530cf95
12 changed files with 977 additions and 1020 deletions

View File

@@ -28,6 +28,7 @@
"@radix-ui/react-tooltip": "^1.1.2",
"@supabase/ssr": "^0.4.0",
"@supabase/supabase-js": "^2.45.0",
"@xyflow/react": "^12.1.0",
"ajv": "^8.17.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
@@ -45,7 +46,6 @@
"react-icons": "^5.2.1",
"react-markdown": "^9.0.1",
"react-modal": "^3.16.1",
"reactflow": "^11.11.4",
"recharts": "^2.12.7",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",

View File

@@ -1,6 +1,6 @@
"use client";
import { useMemo, useState } from "react";
import { useState } from "react";
import Link from "next/link";
import {
ArrowLeft,
@@ -13,27 +13,24 @@ import {
import { Button } from "@/components/ui/button";
import { AgentDetailResponse } from "@/lib/marketplace-api";
import dynamic from "next/dynamic";
import { Node, Edge, NodeTypes, EdgeTypes } from "reactflow";
import { Node, Edge } from "@xyflow/react";
import MarketplaceAPI from "@/lib/marketplace-api";
import AutoGPTServerAPI, { GraphCreatable } from "@/lib/autogpt-server-api";
const ReactFlow = dynamic(
() => import("reactflow").then((mod) => mod.default),
() => import("@xyflow/react").then((mod) => mod.ReactFlow),
{ ssr: false },
);
const Controls = dynamic(
() => import("reactflow").then((mod) => mod.Controls),
() => import("@xyflow/react").then((mod) => mod.Controls),
{ ssr: false },
);
const Background = dynamic(
() => import("reactflow").then((mod) => mod.Background),
() => import("@xyflow/react").then((mod) => mod.Background),
{ ssr: false },
);
import "reactflow/dist/style.css";
import CustomNode from "./CustomNode";
import { CustomEdge } from "./CustomEdge";
import ConnectionLine from "./ConnectionLine";
import "@xyflow/react/dist/style.css";
import { beautifyString } from "@/lib/utils";
function convertGraphToReactFlow(graph: any): { nodes: Node[]; edges: Edge[] } {
@@ -111,9 +108,6 @@ function AgentDetailContent({ agent }: { agent: AgentDetailResponse }) {
const [isGraphExpanded, setIsGraphExpanded] = useState(false);
const { nodes, edges } = convertGraphToReactFlow(agent.graph);
const nodeTypes: NodeTypes = useMemo(() => ({ custom: CustomNode }), []);
const edgeTypes: EdgeTypes = useMemo(() => ({ custom: CustomEdge }), []);
return (
<div className="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8">
<div className="mb-4 flex items-center justify-between">

View File

@@ -3,7 +3,7 @@ import {
ConnectionLineComponentProps,
getBezierPath,
Position,
} from "reactflow";
} from "@xyflow/react";
const ConnectionLine: React.FC<ConnectionLineComponentProps> = ({
fromPosition,

View File

@@ -1,11 +1,13 @@
import React, { FC, memo, useContext, useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import {
BaseEdge,
EdgeLabelRenderer,
EdgeProps,
useReactFlow,
XYPosition,
} from "reactflow";
Edge,
Node,
} from "@xyflow/react";
import "./customedge.css";
import { X } from "lucide-react";
import { useBezierPath } from "@/hooks/useBezierPath";
@@ -26,7 +28,9 @@ type Bead = {
startTime: number;
};
const CustomEdgeFC: FC<EdgeProps<CustomEdgeData>> = ({
export type CustomEdge = Edge<CustomEdgeData, "custom">;
export function CustomEdge({
id,
data,
selected,
@@ -35,7 +39,7 @@ const CustomEdgeFC: FC<EdgeProps<CustomEdgeData>> = ({
targetX,
targetY,
markerEnd,
}) => {
}: EdgeProps<CustomEdge>) {
const [isHovered, setIsHovered] = useState(false);
const [beads, setBeads] = useState<{
beads: Bead[];
@@ -48,7 +52,7 @@ const CustomEdgeFC: FC<EdgeProps<CustomEdgeData>> = ({
targetX + 3,
targetY,
);
const { deleteElements } = useReactFlow<any, CustomEdgeData>();
const { deleteElements } = useReactFlow<Node, CustomEdge>();
const { visualizeBeads } = useContext(FlowContext) ?? {
visualizeBeads: "no",
};
@@ -225,6 +229,4 @@ const CustomEdgeFC: FC<EdgeProps<CustomEdgeData>> = ({
})}
</>
);
};
export const CustomEdge = memo(CustomEdgeFC);
}

View File

@@ -1,18 +1,18 @@
import React, {
useState,
useEffect,
FC,
memo,
useCallback,
useRef,
useContext,
} from "react";
import { NodeProps, useReactFlow } from "reactflow";
import "reactflow/dist/style.css";
import { NodeProps, useReactFlow, Node, Edge } from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import "./customnode.css";
import InputModalComponent from "./InputModalComponent";
import OutputModalComponent from "./OutputModalComponent";
import {
BlockIORootSchema,
Category,
NodeExecutionResult,
} from "@/lib/autogpt-server-api/types";
import { beautifyString, setNestedProperty } from "@/lib/utils";
@@ -24,6 +24,7 @@ import NodeHandle from "./NodeHandle";
import { NodeGenericInputField } from "./node-input-components";
import SchemaTooltip from "./SchemaTooltip";
import { getPrimaryCategoryColor } from "@/lib/utils";
import { FlowContext } from "./Flow";
type ParsedKey = { key: string; index?: number };
@@ -39,24 +40,23 @@ export type CustomNodeData = {
blockType: string;
title: string;
description: string;
categories: string[];
categories: Category[];
inputSchema: BlockIORootSchema;
outputSchema: BlockIORootSchema;
hardcodedValues: { [key: string]: any };
setHardcodedValues: (values: { [key: string]: any }) => void;
connections: ConnectionData;
isOutputOpen: boolean;
status?: NodeExecutionResult["status"];
output_data?: NodeExecutionResult["output_data"];
block_id: string;
backend_id?: string;
errors?: { [key: string]: string | null };
setErrors: (errors: { [key: string]: string | null }) => void;
setIsAnyModalOpen?: (isOpen: boolean) => void;
errors?: { [key: string]: string };
isOutputStatic?: boolean;
};
const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
export type CustomNode = Node<CustomNodeData, "custom">;
export function CustomNode({ data, id }: NodeProps<CustomNode>) {
const [isOutputOpen, setIsOutputOpen] = useState(data.isOutputOpen || false);
const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -64,11 +64,15 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
const [modalValue, setModalValue] = useState<string>("");
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
const [isHovered, setIsHovered] = useState(false);
const { deleteElements } = useReactFlow();
const outputDataRef = useRef<HTMLDivElement>(null);
const { updateNodeData, deleteElements } = useReactFlow<CustomNode, Edge>();
const isInitialSetup = useRef(true);
const flowContext = useContext(FlowContext);
if (!flowContext) {
throw new Error("FlowContext consumer must be inside FlowEditor component");
}
const { setIsAnyModalOpen } = flowContext;
useEffect(() => {
if (data.output_data || data.status) {
@@ -81,13 +85,21 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
}, [data.isOutputOpen]);
useEffect(() => {
data.setIsAnyModalOpen?.(isModalOpen || isOutputModalOpen);
setIsAnyModalOpen?.(isModalOpen || isOutputModalOpen);
}, [isModalOpen, isOutputModalOpen, data]);
useEffect(() => {
isInitialSetup.current = false;
}, []);
const setHardcodedValues = (values: any) => {
updateNodeData(id, { hardcodedValues: values });
};
const setErrors = (errors: { [key: string]: string }) => {
updateNodeData(id, { errors });
};
const toggleOutput = (checked: boolean) => {
setIsOutputOpen(checked);
};
@@ -148,19 +160,20 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
history.push({
type: "UPDATE_INPUT",
payload: { nodeId: id, oldValues: data.hardcodedValues, newValues },
undo: () => data.setHardcodedValues(data.hardcodedValues),
redo: () => data.setHardcodedValues(newValues),
undo: () => setHardcodedValues(data.hardcodedValues),
redo: () => setHardcodedValues(newValues),
});
}
data.setHardcodedValues(newValues);
setHardcodedValues(newValues);
const errors = data.errors || {};
// Remove error with the same key
setNestedProperty(errors, path, null);
data.setErrors({ ...errors });
setErrors({ ...errors });
};
// Helper function to parse keys with array indices
//TODO move to utils
const parseKeys = (key: string): ParsedKey[] => {
const splits = key.split(/_@_|_#_|_\$_|\./);
const keys: ParsedKey[] = [];
@@ -252,14 +265,6 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
);
};
const isTextTruncated = (element: HTMLElement | null): boolean => {
if (!element) return false;
return (
element.scrollHeight > element.clientHeight ||
element.scrollWidth > element.clientWidth
);
};
const handleHovered = () => {
setIsHovered(true);
};
@@ -412,6 +417,4 @@ const CustomNode: FC<NodeProps<CustomNodeData>> = ({ data, id }) => {
/>
</div>
);
};
export default memo(CustomNode);
}

View File

@@ -3,64 +3,47 @@ import React, {
useState,
useCallback,
useEffect,
useMemo,
useRef,
MouseEvent,
createContext,
} from "react";
import { shallow } from "zustand/vanilla/shallow";
import ReactFlow, {
import {
ReactFlow,
ReactFlowProvider,
Controls,
Background,
Node,
Edge,
OnConnect,
NodeTypes,
Connection,
EdgeTypes,
MarkerType,
NodeChange,
EdgeChange,
useStore,
useReactFlow,
applyEdgeChanges,
applyNodeChanges,
useViewport,
} from "reactflow";
import "reactflow/dist/style.css";
import CustomNode, { CustomNodeData } from "./CustomNode";
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import { CustomNode } from "./CustomNode";
import "./flow.css";
import AutoGPTServerAPI, {
Block,
BlockIOSubSchema,
Graph,
Link,
NodeExecutionResult,
} from "@/lib/autogpt-server-api";
import {
deepEquals,
getTypeColor,
removeEmptyStringsAndNulls,
setNestedProperty,
} from "@/lib/utils";
import { Link } from "@/lib/autogpt-server-api";
import { getTypeColor } from "@/lib/utils";
import { history } from "./history";
import { CustomEdge, CustomEdgeData } from "./CustomEdge";
import { CustomEdge } from "./CustomEdge";
import ConnectionLine from "./ConnectionLine";
import Ajv from "ajv";
import { Control, ControlPanel } from "@/components/edit/control/ControlPanel";
import { SaveControl } from "@/components/edit/control/SaveControl";
import { BlocksControl } from "@/components/edit/control/BlocksControl";
import { IconPlay, IconRedo2, IconUndo2 } from "@/components/ui/icons";
import useAgentGraph from "@/hooks/useAgentGraph";
// This is for the history, this is the minimum distance a block must move before it is logged
// It helps to prevent spamming the history with small movements especially when pressing on a input in a block
const MINIMUM_MOVE_BEFORE_LOG = 50;
const ajv = new Ajv({ strict: false, allErrors: true });
type FlowContextType = {
visualizeBeads: "no" | "static" | "animate";
setIsAnyModalOpen: (isOpen: boolean) => void;
};
export const FlowContext = createContext<FlowContextType | null>(null);
@@ -70,79 +53,36 @@ const FlowEditor: React.FC<{
template?: boolean;
className?: string;
}> = ({ flowID, template, className }) => {
const { _setNodes, _setEdges } = useStore(
useCallback(
({ setNodes, setEdges }) => ({
_setNodes: setNodes,
_setEdges: setEdges,
}),
[],
),
shallow,
);
const {
addNodes,
addEdges,
getNode,
getNodes,
getEdges,
setNodes,
setEdges,
deleteElements,
} = useReactFlow<CustomNodeData, CustomEdgeData>();
const { addNodes, addEdges, getNode, deleteElements, updateNode } =
useReactFlow<CustomNode, CustomEdge>();
const [nodeId, setNodeId] = useState<number>(1);
const [availableNodes, setAvailableNodes] = useState<Block[]>([]);
const [savedAgent, setSavedAgent] = useState<Graph | null>(null);
const [agentDescription, setAgentDescription] = useState<string>("");
const [agentName, setAgentName] = useState<string>("");
const [copiedNodes, setCopiedNodes] = useState<Node<CustomNodeData>[]>([]);
const [copiedEdges, setCopiedEdges] = useState<Edge<CustomEdgeData>[]>([]);
const [isAnyModalOpen, setIsAnyModalOpen] = useState(false); // Track if any modal is open
const [copiedNodes, setCopiedNodes] = useState<CustomNode[]>([]);
const [copiedEdges, setCopiedEdges] = useState<CustomEdge[]>([]);
const [isAnyModalOpen, setIsAnyModalOpen] = useState(false);
const [visualizeBeads, setVisualizeBeads] = useState<
"no" | "static" | "animate"
>("animate");
const {
agentName,
setAgentName,
agentDescription,
setAgentDescription,
savedAgent,
availableNodes,
getOutputType,
requestSave,
requestSaveRun,
nodes,
setNodes,
edges,
setEdges,
} = useAgentGraph(flowID, template, visualizeBeads !== "no");
const apiUrl = process.env.NEXT_PUBLIC_AGPT_SERVER_URL!;
const api = useMemo(() => new AutoGPTServerAPI(apiUrl), [apiUrl]);
const initialPositionRef = useRef<{
[key: string]: { x: number; y: number };
}>({});
const isDragging = useRef(false);
useEffect(() => {
api
.connectWebSocket()
.then(() => {
console.log("WebSocket connected");
api.onWebSocketMessage("execution_event", (data) => {
updateNodesWithExecutionData([data]);
});
})
.catch((error) => {
console.error("Failed to connect WebSocket:", error);
});
return () => {
api.disconnectWebSocket();
};
}, [api]);
useEffect(() => {
api
.getBlocks()
.then((blocks) => setAvailableNodes(blocks))
.catch();
}, []);
// Load existing graph
useEffect(() => {
if (!flowID || availableNodes.length == 0) return;
(template ? api.getTemplate(flowID) : api.getGraph(flowID)).then((graph) =>
loadGraph(graph),
);
}, [flowID, template, availableNodes]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0;
@@ -170,9 +110,6 @@ const FlowEditor: React.FC<{
};
}, []);
const nodeTypes: NodeTypes = useMemo(() => ({ custom: CustomNode }), []);
const edgeTypes: EdgeTypes = useMemo(() => ({ custom: CustomEdge }), []);
const onNodeDragStart = (_: MouseEvent, node: Node) => {
initialPositionRef.current[node.id] = { ...node.position };
isDragging.current = true;
@@ -198,37 +135,13 @@ const FlowEditor: React.FC<{
history.push({
type: "UPDATE_NODE_POSITION",
payload: { nodeId: node.id, oldPosition, newPosition },
undo: () =>
// TODO: replace with updateNodes() after upgrade to ReactFlow v12
setNodes((nds) =>
nds.map((n) =>
n.id === node.id ? { ...n, position: oldPosition } : n,
),
),
redo: () =>
// TODO: replace with updateNodes() after upgrade to ReactFlow v12
setNodes((nds) =>
nds.map((n) =>
n.id === node.id ? { ...n, position: newPosition } : n,
),
),
undo: () => updateNode(node.id, { position: oldPosition }),
redo: () => updateNode(node.id, { position: newPosition }),
});
}
delete initialPositionRef.current[node.id];
};
const getOutputType = (id: string, handleId: string) => {
const node = getNode(id);
if (!node) return "unknown";
const outputSchema = node.data.outputSchema;
if (!outputSchema) return "unknown";
const outputHandle = outputSchema.properties[handleId];
if (!("type" in outputHandle)) return "unknown";
return outputHandle.type;
};
// Function to clear status, output, and close the output info dropdown of all nodes
// and reset data beads on edges
const clearNodesStatusAndOutput = useCallback(() => {
@@ -239,7 +152,7 @@ const FlowEditor: React.FC<{
...node.data,
status: undefined,
output_data: undefined,
isOutputOpen: false, // Close the output info dropdown
isOutputOpen: false,
},
}));
@@ -248,9 +161,9 @@ const FlowEditor: React.FC<{
}, [setNodes]);
const onNodesChange = useCallback(
(nodeChanges: NodeChange[]) => {
(nodeChanges: NodeChange<CustomNode>[]) => {
// Persist the changes
_setNodes(applyNodeChanges(nodeChanges, getNodes()));
setNodes((prev) => applyNodeChanges(nodeChanges, prev));
// Remove all edges that were connected to deleted nodes
nodeChanges
@@ -258,7 +171,7 @@ const FlowEditor: React.FC<{
.forEach((deletedNode) => {
const nodeID = deletedNode.id;
const connectedEdges = getEdges().filter((edge) =>
const connectedEdges = edges.filter((edge) =>
[edge.source, edge.target].includes(nodeID),
);
deleteElements({
@@ -266,16 +179,24 @@ const FlowEditor: React.FC<{
});
});
},
[getNodes, getEdges, _setNodes, deleteElements],
[deleteElements, setNodes],
);
const formatEdgeID = useCallback((conn: Link | Connection): string => {
if ("sink_id" in conn) {
return `${conn.source_id}_${conn.source_name}_${conn.sink_id}_${conn.sink_name}`;
} else {
return `${conn.source}_${conn.sourceHandle}_${conn.target}_${conn.targetHandle}`;
}
}, []);
const onConnect: OnConnect = useCallback(
(connection: Connection) => {
const edgeColor = getTypeColor(
getOutputType(connection.source!, connection.sourceHandle!),
);
const sourceNode = getNode(connection.source!);
const newEdge: Edge<CustomEdgeData> = {
const newEdge: CustomEdge = {
id: formatEdgeID(connection),
type: "custom",
markerEnd: {
@@ -310,19 +231,23 @@ const FlowEditor: React.FC<{
);
const onEdgesChange = useCallback(
(edgeChanges: EdgeChange[]) => {
(edgeChanges: EdgeChange<CustomEdge>[]) => {
// Persist the changes
_setEdges(applyEdgeChanges(edgeChanges, getEdges()));
setEdges((prev) => applyEdgeChanges(edgeChanges, prev));
// Propagate edge changes to node data
const addedEdges = edgeChanges.filter((change) => change.type == "add"),
resetEdges = edgeChanges.filter((change) => change.type == "reset"),
removedEdges = edgeChanges.filter((change) => change.type == "remove"),
selectedEdges = edgeChanges.filter((change) => change.type == "select");
const addedEdges = edgeChanges.filter((change) => change.type === "add"),
replaceEdges = edgeChanges.filter(
(change) => change.type === "replace",
),
removedEdges = edgeChanges.filter((change) => change.type === "remove"),
selectedEdges = edgeChanges.filter(
(change) => change.type === "select",
);
if (addedEdges.length > 0 || removedEdges.length > 0) {
setNodes((nds) =>
nds.map((node) => ({
setNodes((nds) => {
const newNodes = nds.map((node) => ({
...node,
data: {
...node.data,
@@ -331,7 +256,7 @@ const FlowEditor: React.FC<{
...node.data.connections.filter(
(conn) =>
!removedEdges.some(
(removedEdge) => removedEdge.id == conn.edge_id,
(removedEdge) => removedEdge.id === conn.edge_id,
),
),
// Add node connections for added edges
@@ -344,20 +269,22 @@ const FlowEditor: React.FC<{
})),
],
},
})),
);
}));
return newNodes;
});
if (removedEdges.length > 0) {
clearNodesStatusAndOutput(); // Clear status and output on edge deletion
}
}
if (resetEdges.length > 0) {
if (replaceEdges.length > 0) {
// Reset node connections for all edges
console.warn(
"useReactFlow().setEdges was used to overwrite all edges. " +
"useReactFlow().setRootEdges was used to overwrite all edges. " +
"Use addEdges, deleteElements, or reconnectEdge for incremental changes.",
resetEdges,
replaceEdges,
);
setNodes((nds) =>
nds.map((node) => ({
@@ -365,12 +292,12 @@ const FlowEditor: React.FC<{
data: {
...node.data,
connections: [
...resetEdges.map((resetEdge) => ({
edge_id: resetEdge.item.id,
source: resetEdge.item.source,
target: resetEdge.item.target,
sourceHandle: resetEdge.item.sourceHandle!,
targetHandle: resetEdge.item.targetHandle!,
...replaceEdges.map((replaceEdge) => ({
edge_id: replaceEdge.item.id,
source: replaceEdge.item.source,
target: replaceEdge.item.target,
sourceHandle: replaceEdge.item.sourceHandle!,
targetHandle: replaceEdge.item.targetHandle!,
})),
],
},
@@ -379,7 +306,7 @@ const FlowEditor: React.FC<{
clearNodesStatusAndOutput();
}
},
[getEdges, _setEdges, setNodes, clearNodesStatusAndOutput],
[setNodes, clearNodesStatusAndOutput],
);
const { x, y, zoom } = useViewport();
@@ -398,7 +325,7 @@ const FlowEditor: React.FC<{
y: (window.innerHeight / 2 - y) / zoom,
};
const newNode: Node<CustomNodeData> = {
const newNode: CustomNode = {
id: nodeId.toString(),
type: "custom",
position: viewportCenter, // Set the position to the calculated viewport center
@@ -410,30 +337,9 @@ const FlowEditor: React.FC<{
inputSchema: nodeSchema.inputSchema,
outputSchema: nodeSchema.outputSchema,
hardcodedValues: {},
setHardcodedValues: (values) => {
// TODO: replace with updateNodes() after upgrade to ReactFlow v12
setNodes((nds) =>
nds.map((node) =>
node.id === newNode.id
? { ...node, data: { ...node.data, hardcodedValues: values } }
: node,
),
);
},
connections: [],
isOutputOpen: false,
block_id: blockId,
setIsAnyModalOpen,
setErrors: (errors: { [key: string]: string | null }) => {
// TODO: replace with updateNodes() after upgrade to ReactFlow v12
setNodes((nds) =>
nds.map((node) =>
node.id === newNode.id
? { ...node, data: { ...node.data, errors } }
: node,
),
);
},
isOutputStatic: nodeSchema.staticOutput,
},
};
@@ -470,453 +376,6 @@ const FlowEditor: React.FC<{
history.redo();
};
function loadGraph(graph: Graph) {
setSavedAgent(graph);
setAgentName(graph.name);
setAgentDescription(graph.description);
setNodes(() => {
const newNodes = graph.nodes.map((node) => {
const block = availableNodes.find(
(block) => block.id === node.block_id,
)!;
const newNode: Node<CustomNodeData> = {
id: node.id,
type: "custom",
position: {
x: node.metadata.position.x,
y: node.metadata.position.y,
},
data: {
block_id: block.id,
blockType: block.name,
categories: block.categories,
description: block.description,
title: `${block.name} ${node.id}`,
inputSchema: block.inputSchema,
outputSchema: block.outputSchema,
hardcodedValues: node.input_default,
setHardcodedValues: (values: { [key: string]: any }) => {
setNodes((nds) =>
nds.map((node) =>
node.id === newNode.id
? {
...node,
data: { ...node.data, hardcodedValues: values },
}
: node,
),
);
},
connections: graph.links
.filter((l) => [l.source_id, l.sink_id].includes(node.id))
.map((link) => ({
edge_id: formatEdgeID(link),
source: link.source_id,
sourceHandle: link.source_name,
target: link.sink_id,
targetHandle: link.sink_name,
})),
isOutputOpen: false,
setIsAnyModalOpen,
setErrors: (errors: { [key: string]: string | null }) => {
setNodes((nds) =>
nds.map((node) =>
node.id === newNode.id
? { ...node, data: { ...node.data, errors } }
: node,
),
);
},
},
};
return newNode;
});
setEdges(
graph.links.map(
(link) =>
({
id: formatEdgeID(link),
type: "custom",
data: {
edgeColor: getTypeColor(
getOutputType(link.source_id, link.source_name!),
),
sourcePos: getNode(link.source_id)?.position,
isStatic: link.is_static,
beadUp: 0,
beadDown: 0,
beadData: [],
},
markerEnd: {
type: MarkerType.ArrowClosed,
strokeWidth: 2,
color: getTypeColor(
getOutputType(link.source_id, link.source_name!),
),
},
source: link.source_id,
target: link.sink_id,
sourceHandle: link.source_name || undefined,
targetHandle: link.sink_name || undefined,
}) as Edge<CustomEdgeData>,
),
);
return newNodes;
});
}
const prepareNodeInputData = (node: Node<CustomNodeData>) => {
console.log("Preparing input data for node:", node.id, node.data.blockType);
const blockSchema = availableNodes.find(
(n) => n.id === node.data.block_id,
)?.inputSchema;
if (!blockSchema) {
console.error(`Schema not found for block ID: ${node.data.block_id}`);
return {};
}
const getNestedData = (
schema: BlockIOSubSchema,
values: { [key: string]: any },
): { [key: string]: any } => {
let inputData: { [key: string]: any } = {};
if ("properties" in schema) {
Object.keys(schema.properties).forEach((key) => {
if (values[key] !== undefined) {
if (
"properties" in schema.properties[key] ||
"additionalProperties" in schema.properties[key]
) {
inputData[key] = getNestedData(
schema.properties[key],
values[key],
);
} else {
inputData[key] = values[key];
}
}
});
}
if ("additionalProperties" in schema) {
inputData = { ...inputData, ...values };
}
return inputData;
};
let inputData = getNestedData(blockSchema, node.data.hardcodedValues);
console.log(
`Final prepared input for ${node.data.blockType} (${node.id}):`,
inputData,
);
return inputData;
};
async function saveAgent(asTemplate: boolean = false) {
setNodes((nds) =>
nds.map((node) => ({
...node,
data: {
...node.data,
hardcodedValues: removeEmptyStringsAndNulls(
node.data.hardcodedValues,
),
status: undefined,
},
})),
);
// Reset bead count
setEdges((edges) => {
return edges.map(
(edge) =>
({
...edge,
data: {
...edge.data,
beadUp: 0,
beadDown: 0,
beadData: [],
},
}) as Edge<CustomEdgeData>,
);
});
await new Promise((resolve) => setTimeout(resolve, 100));
const nodes = getNodes();
const edges = getEdges();
console.log("All nodes before formatting:", nodes);
const blockIdToNodeIdMap: Record<string, string> = {};
const formattedNodes = nodes.map((node) => {
nodes.forEach((node) => {
const key = `${node.data.block_id}_${node.position.x}_${node.position.y}`;
blockIdToNodeIdMap[key] = node.id;
});
const inputDefault = prepareNodeInputData(node);
const inputNodes = edges
.filter((edge) => edge.target === node.id)
.map((edge) => ({
name: edge.targetHandle || "",
node_id: edge.source,
}));
const outputNodes = edges
.filter((edge) => edge.source === node.id)
.map((edge) => ({
name: edge.sourceHandle || "",
node_id: edge.target,
}));
return {
id: node.id,
block_id: node.data.block_id,
input_default: inputDefault,
input_nodes: inputNodes,
output_nodes: outputNodes,
data: {
...node.data,
hardcodedValues: removeEmptyStringsAndNulls(
node.data.hardcodedValues,
),
},
metadata: { position: node.position },
};
});
const links = edges.map((edge) => ({
source_id: edge.source,
sink_id: edge.target,
source_name: edge.sourceHandle || "",
sink_name: edge.targetHandle || "",
}));
const payload = {
id: savedAgent?.id!,
name: agentName || "Agent Name",
description: agentDescription || "Agent Description",
nodes: formattedNodes,
links: links, // Ensure this field is included
};
if (savedAgent && deepEquals(payload, savedAgent)) {
console.debug("No need to save: Graph is the same as version on server");
return;
} else {
console.debug(
"Saving new Graph version; old vs new:",
savedAgent,
payload,
);
}
const newSavedAgent = savedAgent
? await (savedAgent.is_template
? api.updateTemplate(savedAgent.id, payload)
: api.updateGraph(savedAgent.id, payload))
: await (asTemplate
? api.createTemplate(payload)
: api.createGraph(payload));
console.debug("Response from the API:", newSavedAgent);
setSavedAgent(newSavedAgent);
// Update the node IDs in the frontend
const updatedNodes = newSavedAgent.nodes
.map((backendNode) => {
const key = `${backendNode.block_id}_${backendNode.metadata.position.x}_${backendNode.metadata.position.y}`;
const frontendNodeId = blockIdToNodeIdMap[key];
const frontendNode = nodes.find((node) => node.id === frontendNodeId);
return frontendNode
? {
...frontendNode,
position: backendNode.metadata.position,
data: {
...frontendNode.data,
backend_id: backendNode.id,
},
}
: null;
})
.filter((node) => node !== null);
setNodes(updatedNodes);
return newSavedAgent.id;
}
const validateNodes = (): boolean => {
let isValid = true;
getNodes().forEach((node) => {
const validate = ajv.compile(node.data.inputSchema);
const errors = {} as { [key: string]: string | null };
// Validate values against schema using AJV
const valid = validate(node.data.hardcodedValues);
if (!valid) {
// Populate errors if validation fails
validate.errors?.forEach((error) => {
// Skip error if there's an edge connected
const path =
"dataPath" in error
? (error.dataPath as string)
: error.instancePath;
const handle = path.split(/[\/.]/)[0];
if (
node.data.connections.some(
(conn) => conn.target === node.id || conn.targetHandle === handle,
)
) {
return;
}
isValid = false;
if (path && error.message) {
const key = path.slice(1);
console.log("Error", key, error.message);
setNestedProperty(
errors,
key,
error.message[0].toUpperCase() + error.message.slice(1),
);
} else if (error.keyword === "required") {
const key = error.params.missingProperty;
setNestedProperty(errors, key, "This field is required");
}
});
}
node.data.setErrors(errors);
});
return isValid;
};
const runAgent = async () => {
try {
const newAgentId = await saveAgent();
if (!newAgentId) {
console.error("Error saving agent; aborting run");
return;
}
if (!validateNodes()) {
console.error("Validation failed; aborting run");
return;
}
api.subscribeToExecution(newAgentId);
await api.executeGraph(newAgentId);
} catch (error) {
console.error("Error running agent:", error);
}
};
function getFrontendId(nodeId: string, nodes: Node<CustomNodeData>[]) {
const node = nodes.find((node) => node.data.backend_id === nodeId);
return node?.id;
}
function updateEdges(
executionData: NodeExecutionResult[],
nodes: Node<CustomNodeData>[],
) {
setEdges((edges) => {
const newEdges = JSON.parse(
JSON.stringify(edges),
) as Edge<CustomEdgeData>[];
executionData.forEach((exec) => {
if (exec.status === "COMPLETED") {
// Produce output beads
for (let key in exec.output_data) {
const outputEdges = newEdges.filter(
(edge) =>
edge.source === getFrontendId(exec.node_id, nodes) &&
edge.sourceHandle === key,
);
outputEdges.forEach((edge) => {
edge.data!.beadUp = (edge.data!.beadUp ?? 0) + 1;
// For static edges beadDown is always one less than beadUp
// Because there's no queueing and one bead is always at the connection point
if (edge.data?.isStatic) {
edge.data!.beadDown = (edge.data!.beadUp ?? 0) - 1;
edge.data!.beadData! = edge.data!.beadData!.slice(0, -1);
}
//todo kcze this assumes output at key is always array with one element
edge.data!.beadData = [
exec.output_data[key][0],
...edge.data!.beadData!,
];
});
}
} else if (exec.status === "RUNNING") {
// Consume input beads
for (let key in exec.input_data) {
const inputEdges = newEdges.filter(
(edge) =>
edge.target === getFrontendId(exec.node_id, nodes) &&
edge.targetHandle === key,
);
inputEdges.forEach((edge) => {
// Skip decreasing bead count if edge doesn't match or if it's static
if (
edge.data!.beadData![edge.data!.beadData!.length - 1] !==
exec.input_data[key] ||
edge.data?.isStatic
) {
return;
}
edge.data!.beadDown = (edge.data!.beadDown ?? 0) + 1;
edge.data!.beadData! = edge.data!.beadData!.slice(0, -1);
});
}
}
});
return newEdges;
});
}
const updateNodesWithExecutionData = (
executionData: NodeExecutionResult[],
) => {
console.log("Updating nodes with execution data:", executionData);
setNodes((nodes) => {
if (visualizeBeads !== "no") {
updateEdges(executionData, nodes);
}
const updatedNodes = nodes.map((node) => {
const nodeExecution = executionData.find(
(exec) => exec.node_id === node.data.backend_id,
);
if (!nodeExecution || node.data.status === nodeExecution.status) {
return node;
}
return {
...node,
data: {
...node.data,
status: nodeExecution.status,
output_data: nodeExecution.output_data,
isOutputOpen: true,
},
};
});
return updatedNodes;
});
};
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
if (isAnyModalOpen) return; // Prevent copy/paste if any modal is open
@@ -924,8 +383,8 @@ const FlowEditor: React.FC<{
if (event.ctrlKey || event.metaKey) {
if (event.key === "c" || event.key === "C") {
// Copy selected nodes
const selectedNodes = getNodes().filter((node) => node.selected);
const selectedEdges = getEdges().filter((edge) => edge.selected);
const selectedNodes = nodes.filter((node) => node.selected);
const selectedEdges = edges.filter((edge) => edge.selected);
setCopiedNodes(selectedNodes);
setCopiedEdges(selectedEdges);
}
@@ -947,18 +406,6 @@ const FlowEditor: React.FC<{
...node.data,
status: undefined, // Reset status
output_data: undefined, // Clear output data
setHardcodedValues: (values: { [key: string]: any }) => {
setNodes((nds) =>
nds.map((n) =>
n.id === newNodeId
? {
...n,
data: { ...n.data, hardcodedValues: values },
}
: n,
),
);
},
},
};
});
@@ -987,8 +434,6 @@ const FlowEditor: React.FC<{
[
addNodes,
addEdges,
getNodes,
getEdges,
setNodes,
copiedNodes,
copiedEdges,
@@ -1022,16 +467,18 @@ const FlowEditor: React.FC<{
{
label: "Run",
icon: <IconPlay />,
onClick: runAgent,
onClick: requestSaveRun,
},
];
return (
<FlowContext.Provider value={{ visualizeBeads }}>
<FlowContext.Provider value={{ visualizeBeads, setIsAnyModalOpen }}>
<div className={className}>
<ReactFlow
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
nodes={nodes}
edges={edges}
nodeTypes={{ custom: CustomNode }}
edgeTypes={{ custom: CustomEdge }}
connectionLineComponent={ConnectionLine}
onConnect={onConnect}
onNodesChange={onNodesChange}
@@ -1049,7 +496,7 @@ const FlowEditor: React.FC<{
<BlocksControl blocks={availableNodes} addBlock={addNode} />
<SaveControl
agentMeta={savedAgent}
onSave={saveAgent}
onSave={(isTemplate) => requestSave(isTemplate ?? false)}
onDescriptionChange={setAgentDescription}
onNameChange={setAgentName}
/>
@@ -1067,11 +514,3 @@ const WrappedFlowEditor: typeof FlowEditor = (props) => (
);
export default WrappedFlowEditor;
function formatEdgeID(conn: Link | Connection): string {
if ("sink_id" in conn) {
return `${conn.source_id}_${conn.source_name}_${conn.sink_id}_${conn.sink_name}`;
} else {
return `${conn.source}_${conn.sourceHandle}_${conn.target}_${conn.targetHandle}`;
}
}

View File

@@ -1,7 +1,7 @@
import { BlockIOSubSchema } from "@/lib/autogpt-server-api/types";
import { beautifyString, getTypeBgColor, getTypeTextColor } from "@/lib/utils";
import { FC } from "react";
import { Handle, Position } from "reactflow";
import { Handle, Position } from "@xyflow/react";
import SchemaTooltip from "./SchemaTooltip";
type HandleProps = {

View File

@@ -1,7 +1,7 @@
// history.ts
import { CustomNodeData } from "./CustomNode";
import { CustomEdgeData } from "./CustomEdge";
import { Edge } from "reactflow";
import { Edge } from "@xyflow/react";
type ActionType =
| "ADD_NODE"

View File

@@ -0,0 +1,674 @@
import { CustomEdge } from "@/components/CustomEdge";
import { CustomNode } from "@/components/CustomNode";
import AutoGPTServerAPI, {
Block,
BlockIOSubSchema,
Graph,
Link,
NodeExecutionResult,
} from "@/lib/autogpt-server-api";
import {
deepEquals,
getTypeColor,
removeEmptyStringsAndNulls,
setNestedProperty,
} from "@/lib/utils";
import { Connection, MarkerType } from "@xyflow/react";
import Ajv from "ajv";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
const ajv = new Ajv({ strict: false, allErrors: true });
export default function useAgentGraph(
flowID?: string,
template?: boolean,
passDataToBeads?: boolean,
) {
const [savedAgent, setSavedAgent] = useState<Graph | null>(null);
const [agentDescription, setAgentDescription] = useState<string>("");
const [agentName, setAgentName] = useState<string>("");
const [availableNodes, setAvailableNodes] = useState<Block[]>([]);
const [updateQueue, setUpdateQueue] = useState<NodeExecutionResult[]>([]);
const processedUpdates = useRef<NodeExecutionResult[]>([]);
/**
* User `request` to save or save&run the agent
* `state` is used to track the request status:
* - none: no request
* - saving: request was sent to save the agent
* and nodes are pending sync to update their backend ids
* - running: request was sent to run the agent
* and frontend is enqueueing execution results
* - error: request failed
*
* As of now, state will be stuck at 'running' (if run requested)
* because there's no way to know when the execution is done
*/
const [saveRunRequest, setSaveRunRequest] = useState<{
request: "none" | "save" | "run";
state: "none" | "saving" | "running" | "error";
}>({
request: "none",
state: "none",
});
// Determines if nodes backend ids are synced with saved agent (actual ids on the backend)
const [nodesSyncedWithSavedAgent, setNodesSyncedWithSavedAgent] =
useState(false);
const [nodes, setNodes] = useState<CustomNode[]>([]);
const [edges, setEdges] = useState<CustomEdge[]>([]);
const apiUrl = process.env.NEXT_PUBLIC_AGPT_SERVER_URL!;
const api = useMemo(() => new AutoGPTServerAPI(apiUrl), [apiUrl]);
// Connect to WebSocket
useEffect(() => {
api
.connectWebSocket()
.then(() => {
console.debug("WebSocket connected");
api.onWebSocketMessage("execution_event", (data) => {
setUpdateQueue((prev) => [...prev, data]);
});
})
.catch((error) => {
console.error("Failed to connect WebSocket:", error);
});
return () => {
api.disconnectWebSocket();
};
}, [api]);
// Load available blocks
useEffect(() => {
api
.getBlocks()
.then((blocks) => setAvailableNodes(blocks))
.catch();
}, []);
// Load existing graph
useEffect(() => {
if (!flowID || availableNodes.length == 0) return;
(template ? api.getTemplate(flowID) : api.getGraph(flowID)).then((graph) =>
loadGraph(graph),
);
}, [flowID, template, availableNodes]);
// Update nodes with execution data
useEffect(() => {
if (updateQueue.length === 0 || !nodesSyncedWithSavedAgent) {
return;
}
setUpdateQueue((prev) => {
prev.forEach((data) => {
// Skip already processed updates by checking
// if the data is in the processedUpdates array by reference
// This is not to process twice in react dev mode
// because it'll add double the beads
if (processedUpdates.current.includes(data)) {
return;
}
updateNodesWithExecutionData(data);
processedUpdates.current.push(data);
});
return [];
});
}, [updateQueue, nodesSyncedWithSavedAgent]);
// Handle user requests
useEffect(() => {
// Ignore none request
if (saveRunRequest.request === "none") {
return;
}
// Display error message
if (saveRunRequest.state === "error") {
if (saveRunRequest.request === "save") {
console.error("Error saving agent");
} else if (saveRunRequest.request === "run") {
console.error(`Error saving&running agent`);
}
// Reset request
setSaveRunRequest((prev) => ({
...prev,
request: "none",
state: "none",
}));
return;
}
// When saving request is done
if (
saveRunRequest.state === "saving" &&
savedAgent &&
nodesSyncedWithSavedAgent
) {
// Reset request if only save was requested
if (saveRunRequest.request === "save") {
setSaveRunRequest((prev) => ({
...prev,
request: "none",
state: "none",
}));
// If run was requested, run the agent
} else if (saveRunRequest.request === "run") {
if (!validateNodes()) {
console.error("Validation failed; aborting run");
setSaveRunRequest({
request: "none",
state: "none",
});
return;
}
api.subscribeToExecution(savedAgent.id);
api.executeGraph(savedAgent.id);
processedUpdates.current = processedUpdates.current = [];
setSaveRunRequest((prev) => ({
...prev,
request: "run",
state: "running",
}));
}
}
}, [saveRunRequest, savedAgent, nodesSyncedWithSavedAgent]);
// Check if node ids are synced with saved agent
useEffect(() => {
// Check if all node ids are synced with saved agent (frontend and backend)
if (!savedAgent || nodes?.length === 0) {
setNodesSyncedWithSavedAgent(false);
return;
}
// Find at least one node that has backend id existing on any saved agent node
// This will works as long as ALL ids are replaced each time the graph is run
const oneNodeSynced = savedAgent.nodes.some(
(backendNode) => backendNode.id === nodes[0].data.backend_id,
);
setNodesSyncedWithSavedAgent(oneNodeSynced);
}, [savedAgent, nodes]);
const validateNodes = useCallback((): boolean => {
let isValid = true;
nodes.forEach((node) => {
const validate = ajv.compile(node.data.inputSchema);
const errors = {} as { [key: string]: string };
// Validate values against schema using AJV
const valid = validate(node.data.hardcodedValues);
if (!valid) {
// Populate errors if validation fails
validate.errors?.forEach((error) => {
// Skip error if there's an edge connected
const path =
"dataPath" in error
? (error.dataPath as string)
: error.instancePath;
const handle = path.split(/[\/.]/)[0];
if (
node.data.connections.some(
(conn) => conn.target === node.id || conn.targetHandle === handle,
)
) {
return;
}
console.warn("Error", error);
isValid = false;
if (path && error.message) {
const key = path.slice(1);
console.log("Error", key, error.message);
setNestedProperty(
errors,
key,
error.message[0].toUpperCase() + error.message.slice(1),
);
} else if (error.keyword === "required") {
const key = error.params.missingProperty;
setNestedProperty(errors, key, "This field is required");
}
});
}
// Set errors
setNodes((nodes) => {
return nodes.map((n) => {
if (n.id === node.id) {
return {
...n,
data: {
...n.data,
errors,
},
};
}
return n;
});
});
});
return isValid;
}, [nodes]);
const getFrontendId = useCallback(
(backendId: string, nodes: CustomNode[]) => {
const node = nodes.find((node) => node.data.backend_id === backendId);
return node?.id;
},
[],
);
const updateEdgeBeads = useCallback(
(executionData: NodeExecutionResult) => {
setEdges((edges) => {
return edges.map((e) => {
const edge = { ...e, data: { ...e.data } } as CustomEdge;
if (executionData.status === "COMPLETED") {
// Produce output beads
for (let key in executionData.output_data) {
if (
edge.source !== getFrontendId(executionData.node_id, nodes) ||
edge.sourceHandle !== key
) {
continue;
}
edge.data!.beadUp = (edge.data!.beadUp ?? 0) + 1;
// For static edges beadDown is always one less than beadUp
// Because there's no queueing and one bead is always at the connection point
if (edge.data?.isStatic) {
edge.data!.beadDown = (edge.data!.beadUp ?? 0) - 1;
edge.data!.beadData = edge.data!.beadData!.slice(0, -1);
continue;
}
//todo kcze this assumes output at key is always array with one element
edge.data!.beadData = [
executionData.output_data[key][0],
...edge.data!.beadData!,
];
}
} else if (executionData.status === "RUNNING") {
// Consume input beads
for (let key in executionData.input_data) {
if (
edge.target !== getFrontendId(executionData.node_id, nodes) ||
edge.targetHandle !== key
) {
continue;
}
// Skip decreasing bead count if edge doesn't match or if it's static
if (
edge.data!.beadData![edge.data!.beadData!.length - 1] !==
executionData.input_data[key] ||
edge.data?.isStatic
) {
continue;
}
edge.data!.beadDown = (edge.data!.beadDown ?? 0) + 1;
edge.data!.beadData = edge.data!.beadData!.slice(0, -1);
}
}
return edge;
});
});
},
[edges],
);
const updateNodesWithExecutionData = useCallback(
(executionData: NodeExecutionResult) => {
//todo kcze turning off beads
if (passDataToBeads) {
updateEdgeBeads(executionData);
}
setNodes((nodes) => {
const nodeId = nodes.find(
(node) => node.data.backend_id === executionData.node_id,
)?.id;
if (!nodeId) {
console.error(
"Node not found for execution data:",
executionData,
"This shouldn't happen and means that the frontend and backend are out of sync.",
);
return nodes;
}
return nodes.map((node) =>
node.id === nodeId
? {
...node,
data: {
...node.data,
status: executionData.status,
output_data: executionData.output_data,
isOutputOpen: true,
},
}
: node,
);
});
},
[nodes],
);
//kcze to utils? repeated in Flow
const formatEdgeID = useCallback((conn: Link | Connection): string => {
if ("sink_id" in conn) {
return `${conn.source_id}_${conn.source_name}_${conn.sink_id}_${conn.sink_name}`;
} else {
return `${conn.source}_${conn.sourceHandle}_${conn.target}_${conn.targetHandle}`;
}
}, []);
const getOutputType = useCallback(
(nodeId: string, handleId: string) => {
const node = nodes.find((n) => n.id === nodeId);
if (!node) return "unknown";
const outputSchema = node.data.outputSchema;
if (!outputSchema) return "unknown";
const outputHandle = outputSchema.properties[handleId];
if (!("type" in outputHandle)) return "unknown";
return outputHandle.type;
},
[nodes],
);
const loadGraph = useCallback(
(graph: Graph) => {
setSavedAgent(graph);
setAgentName(graph.name);
setAgentDescription(graph.description);
setNodes(() => {
const newNodes = graph.nodes.map((node) => {
const block = availableNodes.find(
(block) => block.id === node.block_id,
)!;
const newNode: CustomNode = {
id: node.id,
type: "custom",
position: {
x: node.metadata.position.x,
y: node.metadata.position.y,
},
data: {
block_id: block.id,
blockType: block.name,
categories: block.categories,
description: block.description,
title: `${block.name} ${node.id}`,
inputSchema: block.inputSchema,
outputSchema: block.outputSchema,
hardcodedValues: node.input_default,
connections: graph.links
.filter((l) => [l.source_id, l.sink_id].includes(node.id))
.map((link) => ({
edge_id: formatEdgeID(link),
source: link.source_id,
sourceHandle: link.source_name,
target: link.sink_id,
targetHandle: link.sink_name,
})),
isOutputOpen: false,
},
};
return newNode;
});
setEdges((_) =>
graph.links.map((link) => ({
id: formatEdgeID(link),
type: "custom",
data: {
edgeColor: getTypeColor(
getOutputType(link.source_id, link.source_name!),
),
sourcePos: nodes.find((node) => node.id === link.source_id)
?.position,
isStatic: link.is_static,
beadUp: 0,
beadDown: 0,
beadData: [],
},
markerEnd: {
type: MarkerType.ArrowClosed,
strokeWidth: 2,
color: getTypeColor(
getOutputType(link.source_id, link.source_name!),
),
},
source: link.source_id,
target: link.sink_id,
sourceHandle: link.source_name || undefined,
targetHandle: link.sink_name || undefined,
})),
);
return newNodes;
});
},
[availableNodes],
);
const prepareNodeInputData = useCallback(
(node: CustomNode) => {
console.debug(
"Preparing input data for node:",
node.id,
node.data.blockType,
);
const blockSchema = availableNodes.find(
(n) => n.id === node.data.block_id,
)?.inputSchema;
if (!blockSchema) {
console.error(`Schema not found for block ID: ${node.data.block_id}`);
return {};
}
const getNestedData = (
schema: BlockIOSubSchema,
values: { [key: string]: any },
): { [key: string]: any } => {
let inputData: { [key: string]: any } = {};
if ("properties" in schema) {
Object.keys(schema.properties).forEach((key) => {
if (values[key] !== undefined) {
if (
"properties" in schema.properties[key] ||
"additionalProperties" in schema.properties[key]
) {
inputData[key] = getNestedData(
schema.properties[key],
values[key],
);
} else {
inputData[key] = values[key];
}
}
});
}
if ("additionalProperties" in schema) {
inputData = { ...inputData, ...values };
}
return inputData;
};
let inputData = getNestedData(blockSchema, node.data.hardcodedValues);
console.debug(
`Final prepared input for ${node.data.blockType} (${node.id}):`,
inputData,
);
return inputData;
},
[availableNodes],
);
const saveAgent = useCallback(
async (asTemplate: boolean = false) => {
//FIXME frontend ids should be resolved better (e.g. returned from the server)
// currently this relays on block_id and position
const blockIdToNodeIdMap: Record<string, string> = {};
nodes.forEach((node) => {
const key = `${node.data.block_id}_${node.position.x}_${node.position.y}`;
blockIdToNodeIdMap[key] = node.id;
});
const formattedNodes = nodes.map((node) => {
const inputDefault = prepareNodeInputData(node);
const inputNodes = edges
.filter((edge) => edge.target === node.id)
.map((edge) => ({
name: edge.targetHandle || "",
node_id: edge.source,
}));
const outputNodes = edges
.filter((edge) => edge.source === node.id)
.map((edge) => ({
name: edge.sourceHandle || "",
node_id: edge.target,
}));
return {
id: node.id,
block_id: node.data.block_id,
input_default: inputDefault,
input_nodes: inputNodes,
output_nodes: outputNodes,
data: {
...node.data,
hardcodedValues: removeEmptyStringsAndNulls(
node.data.hardcodedValues,
),
},
metadata: { position: node.position },
};
});
const links = edges.map((edge) => ({
source_id: edge.source,
sink_id: edge.target,
source_name: edge.sourceHandle || "",
sink_name: edge.targetHandle || "",
}));
const payload = {
id: savedAgent?.id!,
name: agentName || "Agent Name",
description: agentDescription || "Agent Description",
nodes: formattedNodes,
links: links,
};
if (savedAgent && deepEquals(payload, savedAgent)) {
console.debug(
"No need to save: Graph is the same as version on server",
);
// Trigger state change
setSavedAgent(savedAgent);
return;
} else {
console.debug(
"Saving new Graph version; old vs new:",
savedAgent,
payload,
);
}
setNodesSyncedWithSavedAgent(false);
const newSavedAgent = savedAgent
? await (savedAgent.is_template
? api.updateTemplate(savedAgent.id, payload)
: api.updateGraph(savedAgent.id, payload))
: await (asTemplate
? api.createTemplate(payload)
: api.createGraph(payload));
console.debug("Response from the API:", newSavedAgent);
// Update the node IDs on the frontend
setSavedAgent(newSavedAgent);
setNodes((prev) => {
return newSavedAgent.nodes
.map((backendNode) => {
const key = `${backendNode.block_id}_${backendNode.metadata.position.x}_${backendNode.metadata.position.y}`;
const frontendNodeId = blockIdToNodeIdMap[key];
const frontendNode = prev.find(
(node) => node.id === frontendNodeId,
);
return frontendNode
? {
...frontendNode,
position: backendNode.metadata.position,
data: {
...frontendNode.data,
hardcodedValues: removeEmptyStringsAndNulls(
frontendNode.data.hardcodedValues,
),
status: undefined,
backend_id: backendNode.id,
},
}
: null;
})
.filter((node) => node !== null);
});
// Reset bead count
setEdges((edges) => {
return edges.map((edge) => ({
...edge,
data: {
...edge.data,
edgeColor: edge.data?.edgeColor!,
beadUp: 0,
beadDown: 0,
beadData: [],
},
}));
});
},
[nodes, edges, savedAgent],
);
const requestSave = useCallback(
(asTemplate: boolean) => {
saveAgent(asTemplate);
setSaveRunRequest({
request: "save",
state: "saving",
});
},
[saveAgent],
);
const requestSaveRun = useCallback(() => {
saveAgent();
setSaveRunRequest({
request: "run",
state: "saving",
});
}, [saveAgent]);
return {
agentName,
setAgentName,
agentDescription,
setAgentDescription,
savedAgent,
availableNodes,
getOutputType,
requestSave,
requestSaveRun,
nodes,
setNodes,
edges,
setEdges,
};
}

View File

@@ -190,12 +190,12 @@ export default class AutoGPTServerAPI {
this.webSocket = new WebSocket(wsUrlWithToken);
this.webSocket.onopen = () => {
console.log("WebSocket connection established");
console.debug("WebSocket connection established");
resolve();
};
this.webSocket.onclose = (event) => {
console.log("WebSocket connection closed", event);
console.debug("WebSocket connection closed", event);
this.webSocket = null;
};

View File

@@ -35,7 +35,7 @@ type BlockIOSimpleTypeSubSchema =
| BlockIOBooleanSubSchema
| BlockIONullSubSchema;
type BlockIOSubSchemaMeta = {
export type BlockIOSubSchemaMeta = {
title?: string;
description?: string;
placeholder?: string;
@@ -121,7 +121,7 @@ export type Link = {
is_static: boolean;
};
export type LinkCreatable = Omit<Link, "id"> & {
export type LinkCreatable = Omit<Link, "id" | "is_static"> & {
id?: string;
};

View File

@@ -158,6 +158,46 @@
resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.4.tgz"
integrity sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==
"@next/swc-darwin-x64@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.4.tgz#46dedb29ec5503bf171a72a3ecb8aac6e738e9d6"
integrity sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==
"@next/swc-linux-arm64-gnu@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.4.tgz#c9697ab9eb422bd1d7ffd0eb0779cc2aefa9d4a1"
integrity sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==
"@next/swc-linux-arm64-musl@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.4.tgz#cbbceb2008571c743b5a310a488d2e166d200a75"
integrity sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==
"@next/swc-linux-x64-gnu@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.4.tgz#d79184223f857bacffb92f643cb2943a43632568"
integrity sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==
"@next/swc-linux-x64-musl@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.4.tgz#6b6c3e5ac02ca5e63394d280ec8ee607491902df"
integrity sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==
"@next/swc-win32-arm64-msvc@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.4.tgz#dbad3906e870dba84c5883d9d4c4838472e0697f"
integrity sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==
"@next/swc-win32-ia32-msvc@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.4.tgz#6074529b91ba49132922ce89a2e16d25d2ec235d"
integrity sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==
"@next/swc-win32-x64-msvc@14.2.4":
version "14.2.4"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.4.tgz#e65a1c6539a671f97bb86d5183d6e3a1733c29c7"
integrity sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==
"@next/third-parties@^14.2.5":
version "14.2.6"
resolved "https://registry.npmjs.org/@next/third-parties/-/third-parties-14.2.6.tgz"
@@ -173,7 +213,7 @@
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
@@ -287,26 +327,6 @@
resolved "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz"
integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
"@radix-ui/react-dialog@^1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz"
integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.0"
"@radix-ui/react-dismissable-layer" "1.1.0"
"@radix-ui/react-focus-guards" "1.1.0"
"@radix-ui/react-focus-scope" "1.1.0"
"@radix-ui/react-id" "1.1.0"
"@radix-ui/react-portal" "1.1.1"
"@radix-ui/react-presence" "1.1.0"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "2.5.7"
"@radix-ui/react-dialog@1.0.5":
version "1.0.5"
resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz"
@@ -328,6 +348,26 @@
aria-hidden "^1.1.1"
react-remove-scroll "2.5.5"
"@radix-ui/react-dialog@^1.1.1":
version "1.1.1"
resolved "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz"
integrity sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==
dependencies:
"@radix-ui/primitive" "1.1.0"
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-context" "1.1.0"
"@radix-ui/react-dismissable-layer" "1.1.0"
"@radix-ui/react-focus-guards" "1.1.0"
"@radix-ui/react-focus-scope" "1.1.0"
"@radix-ui/react-id" "1.1.0"
"@radix-ui/react-portal" "1.1.1"
"@radix-ui/react-presence" "1.1.0"
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-slot" "1.1.0"
"@radix-ui/react-use-controllable-state" "1.1.0"
aria-hidden "^1.1.1"
react-remove-scroll "2.5.7"
"@radix-ui/react-direction@1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz"
@@ -600,13 +640,6 @@
dependencies:
"@radix-ui/react-primitive" "2.0.0"
"@radix-ui/react-slot@^1.1.0", "@radix-ui/react-slot@1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz"
integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
dependencies:
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-slot@1.0.2":
version "1.0.2"
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz"
@@ -615,6 +648,13 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-slot@1.1.0", "@radix-ui/react-slot@^1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz"
integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
dependencies:
"@radix-ui/react-compose-refs" "1.1.0"
"@radix-ui/react-switch@^1.1.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.0.tgz"
@@ -731,71 +771,10 @@
resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz"
integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==
"@reactflow/background@11.3.14":
version "11.3.14"
resolved "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz"
integrity sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==
dependencies:
"@reactflow/core" "11.11.4"
classcat "^5.0.3"
zustand "^4.4.1"
"@reactflow/controls@11.2.14":
version "11.2.14"
resolved "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz"
integrity sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==
dependencies:
"@reactflow/core" "11.11.4"
classcat "^5.0.3"
zustand "^4.4.1"
"@reactflow/core@11.11.4":
version "11.11.4"
resolved "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz"
integrity sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==
dependencies:
"@types/d3" "^7.4.0"
"@types/d3-drag" "^3.0.1"
"@types/d3-selection" "^3.0.3"
"@types/d3-zoom" "^3.0.1"
classcat "^5.0.3"
d3-drag "^3.0.0"
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
zustand "^4.4.1"
"@reactflow/minimap@11.7.14":
version "11.7.14"
resolved "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz"
integrity sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==
dependencies:
"@reactflow/core" "11.11.4"
"@types/d3-selection" "^3.0.3"
"@types/d3-zoom" "^3.0.1"
classcat "^5.0.3"
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
zustand "^4.4.1"
"@reactflow/node-resizer@2.2.14":
version "2.2.14"
resolved "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz"
integrity sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==
dependencies:
"@reactflow/core" "11.11.4"
classcat "^5.0.4"
d3-drag "^3.0.0"
d3-selection "^3.0.0"
zustand "^4.4.1"
"@reactflow/node-toolbar@1.3.14":
version "1.3.14"
resolved "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz"
integrity sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==
dependencies:
"@reactflow/core" "11.11.4"
classcat "^5.0.3"
zustand "^4.4.1"
"@rollup/rollup-linux-x64-gnu@^4.9.5":
version "4.21.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz#4fd36a6a41f3406d8693321b13d4f9b7658dd4b9"
integrity sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==
"@rushstack/eslint-patch@^1.3.3":
version "1.10.4"
@@ -816,7 +795,7 @@
dependencies:
"@supabase/node-fetch" "^2.6.14"
"@supabase/node-fetch@^2.6.14", "@supabase/node-fetch@2.6.15":
"@supabase/node-fetch@2.6.15", "@supabase/node-fetch@^2.6.14":
version "2.6.15"
resolved "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz"
integrity sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==
@@ -856,7 +835,7 @@
dependencies:
"@supabase/node-fetch" "^2.6.14"
"@supabase/supabase-js@^2.43.4", "@supabase/supabase-js@^2.45.0":
"@supabase/supabase-js@^2.45.0":
version "2.45.0"
resolved "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.0.tgz"
integrity sha512-j66Mfs8RhzCQCKxKogAFQYH9oNhRmgIdKk6pexguI2Oc7hi+nL9UNJug5aL1tKnBdaBM3h65riPLQSdL6sWa3Q==
@@ -881,99 +860,28 @@
"@swc/counter" "^0.1.3"
tslib "^2.4.0"
"@types/d3-array@*", "@types/d3-array@^3.0.3":
"@types/d3-array@^3.0.3":
version "3.2.1"
resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz"
integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==
"@types/d3-axis@*":
version "3.0.6"
resolved "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz"
integrity sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==
dependencies:
"@types/d3-selection" "*"
"@types/d3-brush@*":
version "3.0.6"
resolved "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz"
integrity sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==
dependencies:
"@types/d3-selection" "*"
"@types/d3-chord@*":
version "3.0.6"
resolved "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz"
integrity sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==
"@types/d3-color@*":
version "3.1.3"
resolved "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz"
integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
"@types/d3-contour@*":
version "3.0.6"
resolved "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz"
integrity sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==
dependencies:
"@types/d3-array" "*"
"@types/geojson" "*"
"@types/d3-delaunay@*":
version "6.0.4"
resolved "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz"
integrity sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==
"@types/d3-dispatch@*":
version "3.0.6"
resolved "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz"
integrity sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==
"@types/d3-drag@*", "@types/d3-drag@^3.0.1":
"@types/d3-drag@^3.0.7":
version "3.0.7"
resolved "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz"
resolved "https://registry.yarnpkg.com/@types/d3-drag/-/d3-drag-3.0.7.tgz#b13aba8b2442b4068c9a9e6d1d82f8bcea77fc02"
integrity sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==
dependencies:
"@types/d3-selection" "*"
"@types/d3-dsv@*":
version "3.0.7"
resolved "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz"
integrity sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==
"@types/d3-ease@*", "@types/d3-ease@^3.0.0":
"@types/d3-ease@^3.0.0":
version "3.0.2"
resolved "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz"
integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
"@types/d3-fetch@*":
version "3.0.7"
resolved "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz"
integrity sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==
dependencies:
"@types/d3-dsv" "*"
"@types/d3-force@*":
version "3.0.10"
resolved "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz"
integrity sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==
"@types/d3-format@*":
version "3.0.4"
resolved "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz"
integrity sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==
"@types/d3-geo@*":
version "3.1.0"
resolved "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz"
integrity sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==
dependencies:
"@types/geojson" "*"
"@types/d3-hierarchy@*":
version "3.1.7"
resolved "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz"
integrity sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==
"@types/d3-interpolate@*", "@types/d3-interpolate@^3.0.1":
version "3.0.4"
resolved "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz"
@@ -986,111 +894,50 @@
resolved "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz"
integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==
"@types/d3-polygon@*":
version "3.0.2"
resolved "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz"
integrity sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==
"@types/d3-quadtree@*":
version "3.0.6"
resolved "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz"
integrity sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==
"@types/d3-random@*":
version "3.0.3"
resolved "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz"
integrity sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==
"@types/d3-scale-chromatic@*":
version "3.0.3"
resolved "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz"
integrity sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==
"@types/d3-scale@*", "@types/d3-scale@^4.0.2":
"@types/d3-scale@^4.0.2":
version "4.0.8"
resolved "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz"
integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==
dependencies:
"@types/d3-time" "*"
"@types/d3-selection@*", "@types/d3-selection@^3.0.3":
"@types/d3-selection@*", "@types/d3-selection@^3.0.10":
version "3.0.10"
resolved "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz"
resolved "https://registry.yarnpkg.com/@types/d3-selection/-/d3-selection-3.0.10.tgz#98cdcf986d0986de6912b5892e7c015a95ca27fe"
integrity sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==
"@types/d3-shape@*", "@types/d3-shape@^3.1.0":
"@types/d3-shape@^3.1.0":
version "3.1.6"
resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz"
integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==
dependencies:
"@types/d3-path" "*"
"@types/d3-time-format@*":
version "4.0.3"
resolved "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz"
integrity sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==
"@types/d3-time@*", "@types/d3-time@^3.0.0":
version "3.0.3"
resolved "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz"
integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==
"@types/d3-timer@*", "@types/d3-timer@^3.0.0":
"@types/d3-timer@^3.0.0":
version "3.0.2"
resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz"
integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
"@types/d3-transition@*":
"@types/d3-transition@^3.0.8":
version "3.0.8"
resolved "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz"
resolved "https://registry.yarnpkg.com/@types/d3-transition/-/d3-transition-3.0.8.tgz#677707f5eed5b24c66a1918cde05963021351a8f"
integrity sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==
dependencies:
"@types/d3-selection" "*"
"@types/d3-zoom@*", "@types/d3-zoom@^3.0.1":
"@types/d3-zoom@^3.0.8":
version "3.0.8"
resolved "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz"
resolved "https://registry.yarnpkg.com/@types/d3-zoom/-/d3-zoom-3.0.8.tgz#dccb32d1c56b1e1c6e0f1180d994896f038bc40b"
integrity sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==
dependencies:
"@types/d3-interpolate" "*"
"@types/d3-selection" "*"
"@types/d3@^7.4.0":
version "7.4.3"
resolved "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz"
integrity sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==
dependencies:
"@types/d3-array" "*"
"@types/d3-axis" "*"
"@types/d3-brush" "*"
"@types/d3-chord" "*"
"@types/d3-color" "*"
"@types/d3-contour" "*"
"@types/d3-delaunay" "*"
"@types/d3-dispatch" "*"
"@types/d3-drag" "*"
"@types/d3-dsv" "*"
"@types/d3-ease" "*"
"@types/d3-fetch" "*"
"@types/d3-force" "*"
"@types/d3-format" "*"
"@types/d3-geo" "*"
"@types/d3-hierarchy" "*"
"@types/d3-interpolate" "*"
"@types/d3-path" "*"
"@types/d3-polygon" "*"
"@types/d3-quadtree" "*"
"@types/d3-random" "*"
"@types/d3-scale" "*"
"@types/d3-scale-chromatic" "*"
"@types/d3-selection" "*"
"@types/d3-shape" "*"
"@types/d3-time" "*"
"@types/d3-time-format" "*"
"@types/d3-timer" "*"
"@types/d3-transition" "*"
"@types/d3-zoom" "*"
"@types/debug@^4.0.0":
version "4.1.12"
resolved "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz"
@@ -1110,11 +957,6 @@
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/geojson@*":
version "7946.0.14"
resolved "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz"
integrity sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==
"@types/hast@^3.0.0":
version "3.0.4"
resolved "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz"
@@ -1156,7 +998,7 @@
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz"
integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==
"@types/react-dom@*", "@types/react-dom@^18":
"@types/react-dom@^18":
version "18.3.0"
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz"
integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==
@@ -1170,7 +1012,7 @@
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^16.8.0 || ^17.0.0 || ^18.0.0", "@types/react@^16.9.0 || ^17.0.0 || ^18.0.0", "@types/react@^18", "@types/react@>=16.8", "@types/react@>=18":
"@types/react@*", "@types/react@^18":
version "18.3.3"
resolved "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz"
integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==
@@ -1246,12 +1088,34 @@
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
"@xyflow/react@^12.1.0":
version "12.1.0"
resolved "https://registry.yarnpkg.com/@xyflow/react/-/react-12.1.0.tgz#71a93eb514497b0ae3835b45df21ac14ce6e0812"
integrity sha512-tlYBV8S27kTwLuKvKa3dX0yEJ6NgUup9BwXLWcdNKdZq+UvLfDr5JXHwzyRqT1XSlYFv+YgcfroGmir8krYXIg==
dependencies:
"@xyflow/system" "0.0.38"
classcat "^5.0.3"
zustand "^4.4.0"
"@xyflow/system@0.0.38":
version "0.0.38"
resolved "https://registry.yarnpkg.com/@xyflow/system/-/system-0.0.38.tgz#4bf67fa1adfba5cd3760d182ce645841fc652c69"
integrity sha512-auJU8djbT59S5Afb9lFds1lQJvKIb0zUoHhO+il/ogDDG5BbFds6D8g5a8Q3oHdyR6dy0TaD1oZq9s7Ydhn41g==
dependencies:
"@types/d3-drag" "^3.0.7"
"@types/d3-selection" "^3.0.10"
"@types/d3-transition" "^3.0.8"
"@types/d3-zoom" "^3.0.8"
d3-drag "^3.0.0"
d3-selection "^3.0.0"
d3-zoom "^3.0.0"
acorn-jsx@^5.3.2:
version "5.3.2"
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0:
acorn@^8.9.0:
version "8.12.1"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz"
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
@@ -1578,7 +1442,7 @@ class-variance-authority@^0.7.0:
dependencies:
clsx "2.0.0"
classcat@^5.0.3, classcat@^5.0.4:
classcat@^5.0.3:
version "5.0.5"
resolved "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz"
integrity sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==
@@ -1588,16 +1452,16 @@ client-only@0.0.1:
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
clsx@^2.0.0, clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
clsx@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz"
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
clsx@^2.0.0, clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
cmdk@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz"
@@ -1657,7 +1521,7 @@ csstype@^3.0.2:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
d3-array@^3.1.6, "d3-array@2 - 3", "d3-array@2.10.0 - 3":
"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6:
version "3.2.4"
resolved "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz"
integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
@@ -1674,7 +1538,7 @@ d3-array@^3.1.6, "d3-array@2 - 3", "d3-array@2.10.0 - 3":
resolved "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz"
integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
d3-drag@^3.0.0, "d3-drag@2 - 3":
"d3-drag@2 - 3", d3-drag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz"
integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
@@ -1682,7 +1546,7 @@ d3-drag@^3.0.0, "d3-drag@2 - 3":
d3-dispatch "1 - 3"
d3-selection "3"
d3-ease@^3.0.1, "d3-ease@1 - 3":
"d3-ease@1 - 3", d3-ease@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz"
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
@@ -1692,7 +1556,7 @@ d3-ease@^3.0.1, "d3-ease@1 - 3":
resolved "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz"
integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
d3-interpolate@^3.0.1, "d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3":
"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
@@ -1715,7 +1579,7 @@ d3-scale@^4.0.2:
d3-time "2.1.1 - 3"
d3-time-format "2 - 4"
d3-selection@^3.0.0, "d3-selection@2 - 3", d3-selection@3:
"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz"
integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
@@ -1734,14 +1598,14 @@ d3-shape@^3.1.0:
dependencies:
d3-time "1 - 3"
d3-time@^3.0.0, "d3-time@1 - 3", "d3-time@2.1.1 - 3":
"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz"
integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
dependencies:
d3-array "2 - 3"
d3-timer@^3.0.1, "d3-timer@1 - 3":
"d3-timer@1 - 3", d3-timer@^3.0.1:
version "3.0.1"
resolved "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz"
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
@@ -1800,7 +1664,7 @@ data-view-byte-offset@^1.0.0:
es-errors "^1.3.0"
is-data-view "^1.0.1"
"date-fns@^2.28.0 || ^3.0.0", date-fns@^3.6.0:
date-fns@^3.6.0:
version "3.6.0"
resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz"
integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
@@ -2142,7 +2006,7 @@ eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0:
dependencies:
debug "^3.2.7"
eslint-plugin-import@*, eslint-plugin-import@^2.28.1:
eslint-plugin-import@^2.28.1:
version "2.29.1"
resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz"
integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==
@@ -2229,7 +2093,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.23.0 || ^8.0.0", eslint@^8, eslint@^8.56.0:
eslint@^8:
version "8.57.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz"
integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
@@ -2482,7 +2346,7 @@ get-tsconfig@^4.5.0:
dependencies:
resolve-pkg-maps "^1.0.0"
glob-parent@^5.1.2:
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -2496,14 +2360,7 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob@^10.3.10, glob@10.3.10:
glob@10.3.10, glob@^10.3.10:
version "10.3.10"
resolved "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz"
integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==
@@ -3396,6 +3253,13 @@ micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.3"
picomatch "^2.3.1"
minimatch@9.0.3:
version "9.0.3"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
dependencies:
brace-expansion "^2.0.1"
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
@@ -3410,13 +3274,6 @@ minimatch@^9.0.1:
dependencies:
brace-expansion "^2.0.1"
minimatch@9.0.3:
version "9.0.3"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
dependencies:
brace-expansion "^2.0.1"
minimist@^1.2.0, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
@@ -3432,7 +3289,7 @@ moment@^2.30.1:
resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
ms@^2.1.1, ms@2.1.2:
ms@2.1.2, ms@^2.1.1:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
@@ -3461,7 +3318,7 @@ next-themes@^0.3.0:
resolved "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz"
integrity sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==
"next@^13.0.0 || ^14.0.0", next@14.2.4:
next@14.2.4:
version "14.2.4"
resolved "https://registry.npmjs.org/next/-/next-14.2.4.tgz"
integrity sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==
@@ -3720,15 +3577,6 @@ postcss-value-parser@^4.0.0:
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8, postcss@^8.0.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.23, postcss@>=8.0.9:
version "8.4.38"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz"
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
dependencies:
nanoid "^3.3.7"
picocolors "^1.0.0"
source-map-js "^1.2.0"
postcss@8.4.31:
version "8.4.31"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz"
@@ -3738,6 +3586,15 @@ postcss@8.4.31:
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^8, postcss@^8.4.23:
version "8.4.38"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz"
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
dependencies:
nanoid "^3.3.7"
picocolors "^1.0.0"
source-map-js "^1.2.0"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
@@ -3748,7 +3605,7 @@ prettier-plugin-tailwindcss@^0.6.6:
resolved "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.6.tgz"
integrity sha512-OPva5S7WAsPLEsOuOWXATi13QrCKACCiIonFgIR6V4lYv4QLp++UXVhZSzRbZxXGimkQtQT86CC6fQqTOybGng==
prettier@^3.0, prettier@^3.3.3:
prettier@^3.3.3:
version "3.3.3"
resolved "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz"
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
@@ -3782,7 +3639,7 @@ react-day-picker@^8.10.1:
resolved "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz"
integrity sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==
"react-dom@^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", "react-dom@^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8 || ^17 || ^18", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", react-dom@^18, react-dom@^18.0.0, react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8.0, react-dom@>=17:
react-dom@^18:
version "18.3.1"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
@@ -3790,7 +3647,7 @@ react-day-picker@^8.10.1:
loose-envify "^1.1.0"
scheduler "^0.23.2"
react-hook-form@^7.0.0, react-hook-form@^7.52.1:
react-hook-form@^7.52.1:
version "7.52.1"
resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.1.tgz"
integrity sha512-uNKIhaoICJ5KQALYZ4TOaOLElyM+xipord+Ha3crEFhTntdLvWZqVY49Wqd/0GiVCA/f9NjemLeiNPjG7Hpurg==
@@ -3894,25 +3751,13 @@ react-transition-group@^4.4.5:
loose-envify "^1.4.0"
prop-types "^15.6.2"
react@*, "react@^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17 || ^18", "react@^16.8 || ^17.0 || ^18.0", "react@^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react@^16.8.0 || ^17 || ^18 || ^19", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.x || ^17.x || ^18.x", react@^18, react@^18.0.0, react@^18.2.0, react@^18.3.1, "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=17, react@>=18:
react@^18:
version "18.3.1"
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
dependencies:
loose-envify "^1.1.0"
reactflow@^11.11.4:
version "11.11.4"
resolved "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz"
integrity sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==
dependencies:
"@reactflow/background" "11.3.14"
"@reactflow/controls" "11.2.14"
"@reactflow/core" "11.11.4"
"@reactflow/minimap" "11.7.14"
"@reactflow/node-resizer" "2.2.14"
"@reactflow/node-toolbar" "1.3.14"
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz"
@@ -4340,7 +4185,7 @@ tailwindcss-animate@^1.0.7:
resolved "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz"
integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==
tailwindcss@^3.4.1, "tailwindcss@>=3.0.0 || insiders":
tailwindcss@^3.4.1:
version "3.4.4"
resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz"
integrity sha512-ZoyXOdJjISB7/BcLTR6SEsLgKtDStYyYZVLsUtWChO4Ps20CBad7lfJKVDiejocV4ME1hLmyY0WJE3hSDcmQ2A==
@@ -4505,7 +4350,7 @@ typed-array-length@^1.0.6:
is-typed-array "^1.1.13"
possible-typed-array-names "^1.0.0"
typescript@^5, typescript@>=3.3.1, typescript@>=4.2.0:
typescript@^5:
version "5.5.2"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz"
integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==
@@ -4606,10 +4451,10 @@ use-sidecar@^1.1.2:
detect-node-es "^1.1.0"
tslib "^2.0.0"
use-sync-external-store@1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
use-sync-external-store@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9"
integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==
util-deprecate@^1.0.2:
version "1.0.2"
@@ -4778,12 +4623,12 @@ zod@^3.23.8:
resolved "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz"
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
zustand@^4.4.1:
version "4.5.4"
resolved "https://registry.npmjs.org/zustand/-/zustand-4.5.4.tgz"
integrity sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==
zustand@^4.4.0:
version "4.5.5"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.5.tgz#f8c713041543715ec81a2adda0610e1dc82d4ad1"
integrity sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==
dependencies:
use-sync-external-store "1.2.0"
use-sync-external-store "1.2.2"
zwitch@^2.0.0:
version "2.0.4"