@luxonis/depthai-pipeline-lib 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,8 @@ import type { ParsedNode, Pipeline } from '../services/pipeline.js';
4
4
  import type { PipelineState } from '../services/pipeline-state.js';
5
5
  export type PipelineCanvasProps = FlexProps & {
6
6
  pipeline: Pipeline | null;
7
- pipelineState?: PipelineState[] | null;
7
+ pipelineState: PipelineState[] | null;
8
+ action?: React.ReactNode;
8
9
  };
9
10
  export declare const updateNodesOnPipelineStateChange: (nodes: ParsedNode[], pipelineState: PipelineState[]) => ParsedNode[];
10
11
  export declare const PipelineCanvas: React.FC<PipelineCanvasProps>;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { ReactFlow, ReactFlowProvider, useEdgesState, useNodesInitialized, useNodesState, useReactFlow, useStore, } from '@xyflow/react';
3
+ import { Panel, ReactFlow, ReactFlowProvider, useEdgesState, useNodesState, useReactFlow, useStore, } from '@xyflow/react';
4
4
  import { Flex, Header } from '@luxonis/common-fe-components';
5
5
  import Dagre from '@dagrejs/dagre';
6
6
  import { PipelineNode } from './Node.js';
@@ -125,19 +125,13 @@ export const updateNodesOnPipelineStateChange = (nodes, pipelineState) => {
125
125
  }
126
126
  return parsedNodes;
127
127
  };
128
- const PipelineCanvasBody = ({ pipeline,
129
- // eslint-disable-next-line no-warning-comments
130
- // TODO: Remove optional after pipelineState is implemented in Viewer and Visualizer
131
- pipelineState = null, ...flexProps }) => {
128
+ const PipelineCanvasBody = ({ pipeline, pipelineState, action, ...flexProps }) => {
132
129
  const { fitView, setViewport, getViewport } = useReactFlow();
133
130
  const autoArrangeRef = React.useRef(true);
134
131
  const widthSelector = (state) => state.width;
135
132
  const heightSelector = (state) => state.height;
136
133
  const reactFlowWidth = useStore(widthSelector);
137
134
  const reactFlowHeight = useStore(heightSelector);
138
- const nodesInitialized = useNodesInitialized();
139
- const [isAutoLayoutingFinished, setIsAutoLayoutingFinished] = React.useState(false);
140
- const [isNodesUpdatedWithState, setIsNodesUpdatedWithState] = React.useState(false);
141
135
  const [shouldFitAndResize, setShouldFitAndResize] = React.useState(false);
142
136
  React.useEffect(() => {
143
137
  void fitView();
@@ -150,27 +144,24 @@ pipelineState = null, ...flexProps }) => {
150
144
  return;
151
145
  }
152
146
  const layouted = getLayoutedElements(pipeline?.nodes ?? [], pipeline?.edges ?? []);
153
- setNodes([...layouted.nodes]);
154
- setEdges([...layouted.edges]);
155
- }, [pipeline?.edges, pipeline?.nodes, setEdges, setNodes]);
156
- React.useEffect(() => {
157
- if (nodesInitialized && !isAutoLayoutingFinished) {
158
- const adjustedNodes = adjustNodes(nodes);
147
+ const adjustedNodes = adjustNodes([...layouted.nodes]);
148
+ if (pipelineState) {
149
+ const updatedNodes = updateNodesOnPipelineStateChange(adjustedNodes, pipelineState);
150
+ setNodes([...updatedNodes]);
151
+ }
152
+ else {
159
153
  setNodes([...adjustedNodes]);
160
- setIsAutoLayoutingFinished(true);
161
- setShouldFitAndResize(true);
162
154
  }
163
- // eslint-disable-next-line react-hooks/exhaustive-deps
164
- }, [nodesInitialized, isAutoLayoutingFinished]);
155
+ setEdges([...layouted.edges]);
156
+ setShouldFitAndResize(true);
157
+ }, [pipeline?.edges, pipeline?.nodes, setEdges, setNodes, pipelineState]);
165
158
  React.useEffect(() => {
166
- if (pipelineState && nodes.length > 0 && !isNodesUpdatedWithState) {
159
+ if (pipelineState && nodes.length > 0) {
167
160
  const updatedNodes = updateNodesOnPipelineStateChange(nodes, pipelineState);
168
161
  setNodes([...updatedNodes]);
169
- setIsNodesUpdatedWithState(true);
170
- setShouldFitAndResize(true);
171
162
  }
172
163
  // eslint-disable-next-line react-hooks/exhaustive-deps
173
- }, [pipelineState, nodes, isNodesUpdatedWithState]);
164
+ }, [pipelineState]);
174
165
  const setViewportAndFit = async (x, y, zoom, nds) => {
175
166
  await setViewport({ x, y, zoom });
176
167
  await fitView({ nodes: nds });
@@ -193,6 +184,6 @@ pipelineState = null, ...flexProps }) => {
193
184
  }
194
185
  // eslint-disable-next-line react-hooks/exhaustive-deps
195
186
  }, [shouldFitAndResize]);
196
- return (_jsxs(Flex, { align: "center", justify: "center", full: true, height: "container.xs", rounded: "common", border: "base", ...flexProps, children: [!pipeline && _jsx(Header, { text: "Loading pipeline..." }), pipeline && (_jsx(ReactFlow, { nodes: nodes, edges: edges, onNodeDragStart: () => (autoArrangeRef.current = false), onNodesChange: onNodesChange, fitView: true, nodeTypes: { generic: PipelineNode }, minZoom: 0.4 }))] }));
187
+ return (_jsxs(Flex, { align: "center", justify: "center", full: true, height: "container.xs", rounded: "common", border: "base", ...flexProps, children: [!pipeline && _jsx(Header, { text: "Loading pipeline..." }), pipeline && (_jsx(ReactFlow, { nodes: nodes, edges: edges, onNodeDragStart: () => (autoArrangeRef.current = false), onNodesChange: onNodesChange, fitView: true, nodeTypes: { generic: PipelineNode }, minZoom: 0.4, children: action && _jsx(Panel, { position: "top-right", children: action }) }))] }));
197
188
  };
198
189
  export const PipelineCanvas = props => (_jsx(ReactFlowProvider, { children: _jsx(PipelineCanvasBody, { ...props }) }));
@@ -19,6 +19,7 @@ function parseHandles(handles) {
19
19
  }
20
20
  export function parsePipeline(rawPayload) {
21
21
  const { pipeline } = rawPayload;
22
+ // Set all nodes as generic nodes
22
23
  const nodes = pipeline.nodes
23
24
  .filter(([_, node]) => pipeline.connections.some(connection => connection.node1Id === node.id || connection.node2Id === node.id))
24
25
  .map(([id, node]) => ({
@@ -33,6 +34,7 @@ export function parsePipeline(rawPayload) {
33
34
  handles: parseHandles(node.ioInfo),
34
35
  },
35
36
  })) ?? [];
37
+ // Set all parent nodes as group nodes
36
38
  const mappedParentNodes = nodes.map(node => node.parentId).filter(id => id !== undefined);
37
39
  const filteredParentNodes = mappedParentNodes.filter(id => id !== '-1');
38
40
  const parentNodes = [...new Set(filteredParentNodes)];
@@ -55,6 +57,39 @@ export function parsePipeline(rawPayload) {
55
57
  background: 'rgba(0,0,0,0.125)',
56
58
  },
57
59
  })) ?? [];
60
+ const parentNodesIds = parentNodes.map(id => id.toString());
61
+ const childrenNodes = nodes
62
+ .map(node => {
63
+ if (node.parentId && parentNodesIds.includes(node.parentId)) {
64
+ return {
65
+ ...node,
66
+ parentId: `${node.parentId}-parent`,
67
+ extent: 'parent',
68
+ };
69
+ }
70
+ else if (parentNodesIds.includes(node.id)) {
71
+ return {
72
+ ...node,
73
+ parentId: `${node.id}-parent`,
74
+ extent: 'parent',
75
+ };
76
+ }
77
+ else {
78
+ return null;
79
+ }
80
+ })
81
+ .filter(node => node !== null);
82
+ const filteredNodes = nodes
83
+ .filter(node => childrenNodes.find(childNode => childNode.id === node.id) !== undefined ? null : node)
84
+ .filter(node => node !== null);
85
+ const newFormattedParentNodes = formattedParentNodes.map(node => ({
86
+ ...node,
87
+ id: `${node.id}-parent`,
88
+ data: {
89
+ ...node.data,
90
+ id: `${node.data.id}-parent`,
91
+ },
92
+ }));
58
93
  const edges = pipeline.connections.map(connection => ({
59
94
  id: `${connection.node1Id}-${connection.node2Id}-${connection.node1Output}-${connection.node2Input}-edge`,
60
95
  source: connection.node1Id.toString(),
@@ -77,8 +112,8 @@ export function parsePipeline(rawPayload) {
77
112
  },
78
113
  type: 'step',
79
114
  })) ?? [];
80
- const groupedNodes = [...formattedParentNodes, ...nodes];
81
115
  // NOTE: Parent nodes should be rendered before child nodes
116
+ const groupedNodes = [...newFormattedParentNodes, ...childrenNodes, ...filteredNodes];
82
117
  return { nodes: [...groupedNodes], edges: [...edges, ...bridges] };
83
118
  }
84
119
  export const DOCS_BASE_URL = `https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes`;
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
- "name": "@luxonis/depthai-pipeline-lib",
3
- "version": "1.7.0",
4
- "type": "module",
5
- "license": "UNLICENSED",
6
- "main": "./dist/src/index.js",
7
- "scripts": {
8
- "build": "./build.sh",
9
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10
- "gen:styles": "panda codegen",
11
- "prepublishOnly": "npm run build"
12
- },
13
- "dependencies": {
14
- "@dagrejs/dagre": "^1.1.3",
15
- "@luxonis/common-fe-components": "1.25.9",
16
- "@xyflow/react": "^12.0.4",
17
- "postcss-import": "^16.1.0",
18
- "postcss-nested": "^6.2.0",
19
- "postcss-preset-env": "^10.0.0",
20
- "rehype-sanitize": "^6.0.0"
21
- },
22
- "peerDependencies": {
23
- "react": "^18.3.1 || ^19.0.0",
24
- "react-dom": "^18.3.1 || ^19.0.0"
25
- },
26
- "devDependencies": {
27
- "@pandacss/dev": "^0.53.0",
28
- "@stylistic/eslint-plugin": "^2.6.1",
29
- "@types/react": "^18.3.1 || ^19.0.0",
30
- "@types/react-dom": "^18.3.1 || ^19.0.0",
31
- "@typescript-eslint/eslint-plugin": "^6.21.0",
32
- "@typescript-eslint/parser": "^6.21.0",
33
- "eslint": "^8.56.0",
34
- "eslint-config-prettier": "^9.1.0",
35
- "eslint-import-resolver-typescript": "^3.6.1",
36
- "eslint-interactive": "^10.8.0",
37
- "eslint-plugin-cypress": "^3.3.0",
38
- "eslint-plugin-functional": "^6.0.0",
39
- "eslint-plugin-github": "^4.10.1",
40
- "eslint-plugin-import": "^2.29.1",
41
- "eslint-plugin-prettier": "^5.1.3",
42
- "eslint-plugin-react": "^7.33.2",
43
- "eslint-plugin-react-hooks": "^4.6.0",
44
- "eslint-plugin-react-refresh": "^0.4.5",
45
- "postcss": "^8.4.31",
46
- "prettier": "^3.2.5",
47
- "prettier-eslint": "^16.3.0",
48
- "typescript": "^5.2.2"
49
- },
50
- "overrides": {
51
- "@radix-ui/react-alert-dialog": "1.0.5"
52
- },
53
- "files": [
54
- "dist/src/*",
55
- "dist/*.css"
56
- ],
57
- "exports": {
58
- ".": "./dist/src/index.js",
59
- "./styles": "./dist/index.css"
60
- },
61
- "publishConfig": {
62
- "access": "public"
63
- }
2
+ "name": "@luxonis/depthai-pipeline-lib",
3
+ "version": "1.9.0",
4
+ "type": "module",
5
+ "license": "UNLICENSED",
6
+ "main": "./dist/src/index.js",
7
+ "scripts": {
8
+ "build": "./build.sh",
9
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10
+ "gen:styles": "panda codegen",
11
+ "prepublishOnly": "npm run build"
12
+ },
13
+ "dependencies": {
14
+ "@dagrejs/dagre": "^1.1.3",
15
+ "@luxonis/common-fe-components": "1.25.9",
16
+ "@xyflow/react": "^12.0.4",
17
+ "postcss-import": "^16.1.0",
18
+ "postcss-nested": "^6.2.0",
19
+ "postcss-preset-env": "^10.0.0",
20
+ "rehype-sanitize": "^6.0.0"
21
+ },
22
+ "peerDependencies": {
23
+ "react": "^18.3.1 || ^19.0.0",
24
+ "react-dom": "^18.3.1 || ^19.0.0"
25
+ },
26
+ "devDependencies": {
27
+ "@pandacss/dev": "^0.53.0",
28
+ "@stylistic/eslint-plugin": "^2.6.1",
29
+ "@types/react": "^18.3.1 || ^19.0.0",
30
+ "@types/react-dom": "^18.3.1 || ^19.0.0",
31
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
32
+ "@typescript-eslint/parser": "^6.21.0",
33
+ "eslint": "^8.56.0",
34
+ "eslint-config-prettier": "^9.1.0",
35
+ "eslint-import-resolver-typescript": "^3.6.1",
36
+ "eslint-interactive": "^10.8.0",
37
+ "eslint-plugin-cypress": "^3.3.0",
38
+ "eslint-plugin-functional": "^6.0.0",
39
+ "eslint-plugin-github": "^4.10.1",
40
+ "eslint-plugin-import": "^2.29.1",
41
+ "eslint-plugin-prettier": "^5.1.3",
42
+ "eslint-plugin-react": "^7.33.2",
43
+ "eslint-plugin-react-hooks": "^4.6.0",
44
+ "eslint-plugin-react-refresh": "^0.4.5",
45
+ "postcss": "^8.4.31",
46
+ "prettier": "^3.2.5",
47
+ "prettier-eslint": "^16.3.0",
48
+ "typescript": "^5.2.2"
49
+ },
50
+ "overrides": {
51
+ "@radix-ui/react-alert-dialog": "1.0.5"
52
+ },
53
+ "files": [
54
+ "dist/src/*",
55
+ "dist/*.css"
56
+ ],
57
+ "exports": {
58
+ ".": "./dist/src/index.js",
59
+ "./styles": "./dist/index.css"
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ }
64
64
  }