Skip to content

Commit d6cc27a

Browse files
authored
refactor: custom nodes and edges (#929)
* refactor: custom nodes and edges * fix: error * fix: reset workspace after app loaded
1 parent 05cf4f7 commit d6cc27a

File tree

12 files changed

+318
-407
lines changed

12 files changed

+318
-407
lines changed

core/src/ten_manager/designer_frontend/src/App.tsx

+7-9
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ import { ThemeProvider } from "@/components/ThemeProvider";
1818
import AppBar from "@/components/AppBar";
1919
import StatusBar from "@/components/StatusBar";
2020
import FlowCanvas, { type FlowCanvasRef } from "@/flow/FlowCanvas";
21-
import { CustomNodeType } from "@/flow/CustomNode";
22-
import { CustomEdgeType } from "@/flow/CustomEdge";
23-
import { getLayoutedElements } from "@/flow/graph";
21+
import { generateNodesAndEdges } from "@/flow/graph";
2422
import {
2523
ResizableHandle,
2624
ResizablePanel,
@@ -37,6 +35,8 @@ import { usePreferences } from "@/api/services/common";
3735
import { SpinnerLoading } from "@/components/Status/Loading";
3836
import { PREFERENCES_SCHEMA_LOG } from "@/types/apps";
3937

38+
import type { TCustomEdge, TCustomNode } from "@/types/flow";
39+
4040
const App: React.FC = () => {
4141
const { nodes, setNodes, edges, setEdges, setNodesAndEdges } = useFlowStore();
4242
const [resizablePanelMode, setResizablePanelMode] = useState<
@@ -63,16 +63,14 @@ const App: React.FC = () => {
6363
);
6464

6565
const performAutoLayout = useCallback(() => {
66-
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
67-
nodes,
68-
edges
69-
);
66+
const { nodes: layoutedNodes, edges: layoutedEdges } =
67+
generateNodesAndEdges(nodes, edges);
7068
setNodesAndEdges(layoutedNodes, layoutedEdges);
7169
// eslint-disable-next-line react-hooks/exhaustive-deps
7270
}, [nodes, edges]);
7371

7472
const handleNodesChange = useCallback(
75-
(changes: NodeChange<CustomNodeType>[]) => {
73+
(changes: NodeChange<TCustomNode>[]) => {
7674
const newNodes = applyNodeChanges(changes, nodes);
7775
setNodes(newNodes);
7876
},
@@ -81,7 +79,7 @@ const App: React.FC = () => {
8179
);
8280

8381
const handleEdgesChange = useCallback(
84-
(changes: EdgeChange<CustomEdgeType>[]) => {
82+
(changes: EdgeChange<TCustomEdge>[]) => {
8583
const newEdges = applyEdgeChanges(changes, edges);
8684
setEdges(newEdges);
8785
},

core/src/ten_manager/designer_frontend/src/components/DataTable/ConnectionTable.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export const commonConnectionColumns: ColumnDef<TConnection>[] = [
108108
cell: ({ row }) => {
109109
const name = row.getValue("name") as string;
110110
if (!name) return null;
111-
return <div className="text-xs">{name}</div>;
111+
return name;
112112
},
113113
},
114114
];

core/src/ten_manager/designer_frontend/src/components/Popup/AppPopup.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,18 @@ export const AppFolderPopup = () => {
4848

4949
const { removeWidget } = useWidgetStore();
5050
const { setNodesAndEdges } = useFlowStore();
51-
const { folderPath } = useAppStore();
51+
const { folderPath, updateCurrentWorkspace } = useAppStore();
5252

5353
const { mutate: mutateApps } = useApps();
5454

5555
const handleSetBaseDir = async (folderPath: string) => {
5656
try {
5757
await postLoadDir(folderPath.trim());
5858
setNodesAndEdges([], []); // Clear the contents of the FlowCanvas.
59+
updateCurrentWorkspace({
60+
baseDir: folderPath.trim(),
61+
graphName: null,
62+
});
5963
mutateApps();
6064
toast.success(t("header.menuApp.loadAppSuccess"));
6165
} catch (error: unknown) {

core/src/ten_manager/designer_frontend/src/components/Popup/GraphSelectPopup.tsx

+15-40
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import { toast } from "sonner";
1010
import { CheckIcon } from "lucide-react";
1111

1212
import {
13-
enhanceNodesWithCommands,
14-
fetchAddonInfoForNodes,
15-
getLayoutedElements,
16-
processConnections,
17-
processNodes,
13+
generateRawNodes,
14+
generateRawEdges,
15+
updateNodesWithConnections,
16+
updateNodesWithAddonInfo,
17+
generateNodesAndEdges,
1818
} from "@/flow/graph";
1919
import { Popup } from "@/components/Popup/Popup";
2020
import {
@@ -38,8 +38,6 @@ import { useApps } from "@/api/services/apps";
3838
import { useWidgetStore, useFlowStore, useAppStore } from "@/store";
3939
import { GRAPH_SELECT_POPUP_ID } from "@/constants/widgets";
4040

41-
import type { CustomNodeType } from "@/flow/CustomNode";
42-
4341
export function GraphSelectPopup() {
4442
const { t } = useTranslation();
4543

@@ -79,43 +77,20 @@ export function GraphSelectPopup() {
7977
graphName,
8078
baseDir
8179
);
82-
83-
let initialNodes: CustomNodeType[] = processNodes(backendNodes);
84-
85-
const {
86-
initialEdges,
87-
nodeSourceCmdMap,
88-
nodeSourceDataMap,
89-
nodeSourceAudioFrameMap,
90-
nodeSourceVideoFrameMap,
91-
nodeTargetCmdMap,
92-
nodeTargetDataMap,
93-
nodeTargetAudioFrameMap,
94-
nodeTargetVideoFrameMap,
95-
} = processConnections(backendConnections);
96-
97-
// Write back the cmd information to nodes, so that CustomNode could
98-
// generate corresponding handles.
99-
initialNodes = enhanceNodesWithCommands(initialNodes, {
100-
nodeSourceCmdMap,
101-
nodeTargetCmdMap,
102-
nodeSourceDataMap,
103-
nodeTargetDataMap,
104-
nodeSourceAudioFrameMap,
105-
nodeSourceVideoFrameMap,
106-
nodeTargetAudioFrameMap,
107-
nodeTargetVideoFrameMap,
108-
});
109-
110-
// Fetch additional addon information for each node.
111-
const nodesWithAddonInfo = await fetchAddonInfoForNodes(
80+
const rawNodes = generateRawNodes(backendNodes);
81+
const [rawEdges, rawEdgeAddressMap] =
82+
generateRawEdges(backendConnections);
83+
const nodesWithConnections = updateNodesWithConnections(
84+
rawNodes,
85+
rawEdgeAddressMap
86+
);
87+
const nodesWithAddonInfo = await updateNodesWithAddonInfo(
11288
baseDir ?? "",
113-
initialNodes
89+
nodesWithConnections
11490
);
11591

116-
// Auto-layout the nodes and edges.
11792
const { nodes: layoutedNodes, edges: layoutedEdges } =
118-
getLayoutedElements(nodesWithAddonInfo, initialEdges);
93+
generateNodesAndEdges(nodesWithAddonInfo, rawEdges);
11994

12095
setNodesAndEdges(layoutedNodes, layoutedEdges);
12196
} catch (err: unknown) {

core/src/ten_manager/designer_frontend/src/flow/ContextMenu/EdgeContextMenu.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import { useTranslation } from "react-i18next";
99
import { PencilIcon, ListCollapseIcon, TrashIcon } from "lucide-react";
1010

1111
import ContextMenu, { ContextMenuItem } from "@/flow/ContextMenu/ContextMenu";
12-
import { CustomEdgeType } from "@/flow/CustomEdge";
1312
import { dispatchCustomNodeActionPopup } from "@/utils/popup";
1413

14+
import type { TCustomEdge } from "@/types/flow";
15+
1516
interface EdgeContextMenuProps {
1617
visible: boolean;
1718
x: number;
1819
y: number;
19-
edge: CustomEdgeType;
20+
edge: TCustomEdge;
2021
onClose: () => void;
2122
}
2223

core/src/ten_manager/designer_frontend/src/flow/ContextMenu/NodeContextMenu.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ import {
1414
} from "lucide-react";
1515

1616
import ContextMenu, { ContextMenuItem } from "@/flow/ContextMenu/ContextMenu";
17-
import { CustomNodeType } from "@/flow/CustomNode";
1817

1918
import type { TerminalData, EditorData } from "@/types/widgets";
19+
import type { TCustomNode } from "@/types/flow";
2020

2121
interface NodeContextMenuProps {
2222
visible: boolean;
2323
x: number;
2424
y: number;
25-
node: CustomNodeType;
25+
node: TCustomNode;
2626
onClose: () => void;
2727
onLaunchTerminal: (data: TerminalData) => void;
2828
onLaunchEditor: (data: EditorData) => void;
@@ -49,7 +49,7 @@ const NodeContextMenu: React.FC<NodeContextMenuProps> = ({
4949
onClose();
5050
if (node?.data.url)
5151
onLaunchEditor({
52-
title: `${node.data.addon} manifest.json`,
52+
title: `${node.data.name} manifest.json`,
5353
content: "",
5454
url: `${node.data.url}/manifest.json`,
5555
});
@@ -62,7 +62,7 @@ const NodeContextMenu: React.FC<NodeContextMenuProps> = ({
6262
onClose();
6363
if (node?.data.url)
6464
onLaunchEditor({
65-
title: `${node.data.addon} property.json`,
65+
title: `${node.data.name} property.json`,
6666
content: "",
6767
url: `${node.data.url}/property.json`,
6868
});

core/src/ten_manager/designer_frontend/src/flow/CustomEdge.tsx

+2-15
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,12 @@
66
//
77
import {
88
type EdgeProps,
9-
type Edge,
109
BaseEdge,
1110
getSmoothStepPath,
1211
Position,
1312
} from "@xyflow/react";
1413

15-
import { type EConnectionType } from "@/types/graphs";
16-
17-
export type CustomEdgeType = Edge<
18-
{
19-
labelOffsetX: number;
20-
labelOffsetY: number;
21-
connectionType: EConnectionType;
22-
name: string;
23-
srcApp: string;
24-
destApp: string;
25-
},
26-
"customEdge"
27-
>;
14+
import type { TCustomEdge } from "@/types/flow";
2815

2916
export function CustomEdge({
3017
sourceX,
@@ -35,7 +22,7 @@ export function CustomEdge({
3522
style,
3623
selected,
3724
markerEnd,
38-
}: EdgeProps<CustomEdgeType>) {
25+
}: EdgeProps<TCustomEdge>) {
3926
const [path] = getSmoothStepPath({
4027
sourceX: sourceX,
4128
sourceY: sourceY,

core/src/ten_manager/designer_frontend/src/flow/CustomNode.tsx

+20-34
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Refer to the "LICENSE" file in the root directory for more information.
66
//
77
import * as React from "react";
8-
import { Position, NodeProps, Connection, Edge, Node } from "@xyflow/react";
8+
import { Position, NodeProps, Connection, Edge } from "@xyflow/react";
99
import {
1010
BlocksIcon as ExtensionIcon,
1111
TerminalIcon,
@@ -18,28 +18,14 @@ import { Separator } from "@/components/ui/Separator";
1818
import { cn } from "@/lib/utils";
1919
import { dispatchCustomNodeActionPopup } from "@/utils/popup";
2020
import CustomHandle from "@/flow/CustomHandle";
21-
import { EConnectionType, type TConnectionItem } from "@/types/graphs";
21+
import { EConnectionType } from "@/types/graphs";
22+
23+
import type { TCustomNode, TCustomNodeData } from "@/types/flow";
2224

2325
const onConnect = (params: Connection | Edge) =>
2426
console.log("Handle onConnect", params);
2527

26-
export type TCustomNodeData = {
27-
name: string;
28-
addon: string;
29-
sourceCmds: TConnectionItem[];
30-
targetCmds: TConnectionItem[];
31-
sourceData: TConnectionItem[];
32-
targetData: TConnectionItem[];
33-
sourceAudioFrame: TConnectionItem[];
34-
targetAudioFrame: TConnectionItem[];
35-
sourceVideoFrame: TConnectionItem[];
36-
targetVideoFrame: TConnectionItem[];
37-
url?: string;
38-
};
39-
40-
export type CustomNodeType = Node<TCustomNodeData, "customNode">;
41-
42-
export function CustomNode({ data, isConnectable }: NodeProps<CustomNodeType>) {
28+
export function CustomNode({ data, isConnectable }: NodeProps<TCustomNode>) {
4329
return (
4430
<>
4531
<div
@@ -119,7 +105,7 @@ const HandleGroupItem = (props: {
119105
target?: boolean;
120106
}) =>
121107
() => {
122-
dispatchCustomNodeActionPopup("connections", data.addon, undefined, {
108+
dispatchCustomNodeActionPopup("connections", data.name, undefined, {
123109
filters: {
124110
type,
125111
source,
@@ -132,11 +118,11 @@ const HandleGroupItem = (props: {
132118
<div className="flex items-center gap-x-4 justify-between">
133119
<div className="flex items-center gap-x-2">
134120
<CustomHandle
135-
key={`target-${data.addon}-${connectionType}`}
121+
key={`target-${data.name}-${connectionType}`}
136122
type="target"
137123
position={Position.Left}
138-
id={`target-${data.addon}-${connectionType}`}
139-
label={data.addon}
124+
id={`target-${data.name}-${connectionType}`}
125+
label={data.name}
140126
labelOffsetX={0}
141127
isConnectable={isConnectable}
142128
onConnect={onConnect}
@@ -148,16 +134,16 @@ const HandleGroupItem = (props: {
148134
})}
149135
>
150136
{connectionType === EConnectionType.CMD && (
151-
<span>{data.targetCmds?.length || 0}</span>
137+
<span>{data.src[connectionType]?.length || 0}</span>
152138
)}
153139
{connectionType === EConnectionType.DATA && (
154-
<span>{data.targetData?.length || 0}</span>
140+
<span>{data.src[connectionType]?.length || 0}</span>
155141
)}
156142
{connectionType === EConnectionType.AUDIO_FRAME && (
157-
<span>{data.targetAudioFrame?.length || 0}</span>
143+
<span>{data.src[connectionType]?.length || 0}</span>
158144
)}
159145
{connectionType === EConnectionType.VIDEO_FRAME && (
160-
<span>{data.targetVideoFrame?.length || 0}</span>
146+
<span>{data.src[connectionType]?.length || 0}</span>
161147
)}
162148
</ConnectionCount>
163149
</div>
@@ -191,24 +177,24 @@ const HandleGroupItem = (props: {
191177
})}
192178
>
193179
{connectionType === EConnectionType.CMD && (
194-
<span>{data.sourceCmds?.length || 0}</span>
180+
<span>{data.target[connectionType]?.length || 0}</span>
195181
)}
196182
{connectionType === EConnectionType.DATA && (
197-
<span>{data.sourceData?.length || 0}</span>
183+
<span>{data.target[connectionType]?.length || 0}</span>
198184
)}
199185
{connectionType === EConnectionType.AUDIO_FRAME && (
200-
<span>{data.sourceAudioFrame?.length || 0}</span>
186+
<span>{data.target[connectionType]?.length || 0}</span>
201187
)}
202188
{connectionType === EConnectionType.VIDEO_FRAME && (
203-
<span>{data.sourceVideoFrame?.length || 0}</span>
189+
<span>{data.target[connectionType]?.length || 0}</span>
204190
)}
205191
</ConnectionCount>
206192
<CustomHandle
207-
key={`source-${data.addon}-${connectionType}`}
193+
key={`source-${data.name}-${connectionType}`}
208194
type="source"
209195
position={Position.Right}
210-
id={`source-${data.addon}-${connectionType}`}
211-
label={data.addon}
196+
id={`source-${data.name}-${connectionType}`}
197+
label={data.name}
212198
labelOffsetX={0}
213199
isConnectable={isConnectable}
214200
/>

0 commit comments

Comments
 (0)