otomato-sdk 2.0.66 → 2.0.67
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.
- package/dist/src/constants/version.js +1 -1
- package/dist/src/utils/WorkflowNodePositioner.js +87 -74
- package/dist/types/examples/UseCases/NFTs/blur.d.ts +1 -0
- package/dist/types/examples/UseCases/X/x.d.ts +1 -0
- package/dist/types/src/constants/version.d.ts +1 -1
- package/dist/types/src/utils/WorkflowNodePositioner.d.ts +6 -9
- package/package.json +1 -1
|
@@ -1,48 +1,11 @@
|
|
|
1
|
+
// workflowNodePositioner.ts
|
|
1
2
|
export const xSpacing = 500;
|
|
2
3
|
export const ySpacing = 120;
|
|
3
4
|
export const ROOT_X = 400;
|
|
4
5
|
export const ROOT_Y = 120;
|
|
5
|
-
export function positionWorkflowNodesAvoidOverlap(workflow) {
|
|
6
|
-
// 1) Lay out nodes using existing logic
|
|
7
|
-
positionWorkflowNodes(workflow);
|
|
8
|
-
// 2) Group nodes by 'level' based on vertical position
|
|
9
|
-
const levels = new Map();
|
|
10
|
-
workflow.nodes.forEach((node) => {
|
|
11
|
-
if (node.position) {
|
|
12
|
-
const level = Math.round(node.position.y / ySpacing);
|
|
13
|
-
if (!levels.has(level)) {
|
|
14
|
-
levels.set(level, []);
|
|
15
|
-
}
|
|
16
|
-
levels.get(level).push(node);
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
// 3) Resolve horizontal overlaps among *all* nodes in each level
|
|
20
|
-
levels.forEach((levelNodes) => {
|
|
21
|
-
// Sort the nodes in this level by X
|
|
22
|
-
levelNodes.sort((a, b) => { var _a, _b; return ((_a = a.position.x) !== null && _a !== void 0 ? _a : 0) - ((_b = b.position.x) !== null && _b !== void 0 ? _b : 0); });
|
|
23
|
-
// Walk left-to-right, shifting nodes that would collide with the previous one
|
|
24
|
-
for (let i = 1; i < levelNodes.length; i++) {
|
|
25
|
-
const prev = levelNodes[i - 1];
|
|
26
|
-
const current = levelNodes[i];
|
|
27
|
-
// Compute how close they are
|
|
28
|
-
const dx = current.position.x - prev.position.x;
|
|
29
|
-
// If they are too close, shift the current node (and its children) to the right
|
|
30
|
-
if (dx < xSpacing) {
|
|
31
|
-
const shift = xSpacing - dx;
|
|
32
|
-
moveNodeAndChildren(current, shift, workflow.edges);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
// 4) Re-center parents above their children in a bottom-up pass
|
|
37
|
-
centerParentXPositions(workflow);
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Lays out the workflow nodes in a rough top-down manner,
|
|
41
|
-
* then positions each node relative to its parent.
|
|
42
|
-
*/
|
|
43
6
|
export function positionWorkflowNodes(workflow) {
|
|
44
7
|
try {
|
|
45
|
-
// Step 1: Find the starting nodes
|
|
8
|
+
// Step 1: Find the starting nodes using identityStartingNodes function
|
|
46
9
|
const startingNodes = identityStartingNodes(workflow);
|
|
47
10
|
// Step 2: Place the starting nodes
|
|
48
11
|
let xPosition = ROOT_X;
|
|
@@ -50,7 +13,7 @@ export function positionWorkflowNodes(workflow) {
|
|
|
50
13
|
startNode.setPosition(xPosition, ROOT_Y);
|
|
51
14
|
xPosition += xSpacing;
|
|
52
15
|
});
|
|
53
|
-
// Step 3:
|
|
16
|
+
// Step 3: Place all other nodes relative to their parents
|
|
54
17
|
const nodesToPosition = workflow.nodes.filter((node) => !startingNodes.includes(node));
|
|
55
18
|
nodesToPosition.forEach((node) => positionNode(node, workflow.edges, xSpacing, ySpacing, workflow));
|
|
56
19
|
}
|
|
@@ -60,13 +23,12 @@ export function positionWorkflowNodes(workflow) {
|
|
|
60
23
|
}
|
|
61
24
|
export function positionNode(node, edges, xSpacing, ySpacing, workflow) {
|
|
62
25
|
const parents = getParents(node, edges);
|
|
63
|
-
if
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const children = getChildren(parents[0], edges).sort((a, b) => {
|
|
26
|
+
// todo: what if we have multiple parents?
|
|
27
|
+
const children = getChildren(parents[0], edges);
|
|
28
|
+
const sortedChildren = children.sort((a, b) => {
|
|
67
29
|
var _a, _b;
|
|
68
|
-
const edgeA = edges.find(
|
|
69
|
-
const edgeB = edges.find(
|
|
30
|
+
const edgeA = edges.find(edge => edge.source === parents[0] && edge.target === a);
|
|
31
|
+
const edgeB = edges.find(edge => edge.source === parents[0] && edge.target === b);
|
|
70
32
|
const labelA = (_a = edgeA === null || edgeA === void 0 ? void 0 : edgeA.label) !== null && _a !== void 0 ? _a : "";
|
|
71
33
|
const labelB = (_b = edgeB === null || edgeB === void 0 ? void 0 : edgeB.label) !== null && _b !== void 0 ? _b : "";
|
|
72
34
|
if (labelA === "true" && labelB !== "true")
|
|
@@ -79,25 +41,78 @@ export function positionNode(node, edges, xSpacing, ySpacing, workflow) {
|
|
|
79
41
|
return -1;
|
|
80
42
|
return 0;
|
|
81
43
|
});
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
const parentY = Math.max(...parents.map(
|
|
85
|
-
if (
|
|
44
|
+
const childrenCountOfParent = sortedChildren.length;
|
|
45
|
+
const parentX = parents.reduce((sum, parent) => { var _a, _b; return sum + ((_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : ROOT_X); }, 0) / parents.length;
|
|
46
|
+
const parentY = Math.max(...parents.map(parent => { var _a, _b; return (_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : ROOT_Y; }));
|
|
47
|
+
if (childrenCountOfParent === 1) {
|
|
86
48
|
node.setPosition(parentX, parentY + ySpacing);
|
|
87
49
|
}
|
|
88
50
|
else {
|
|
89
|
-
//
|
|
90
|
-
const
|
|
91
|
-
|
|
51
|
+
const index = sortedChildren.indexOf(node); // Get the position of this node among its siblings
|
|
52
|
+
const totalChildren = sortedChildren.length;
|
|
53
|
+
// Compute the x position for this node
|
|
54
|
+
const offset = index - (totalChildren - 1) / 2; // Center the children around the parent
|
|
92
55
|
node.setPosition(parentX + offset * xSpacing, parentY + ySpacing);
|
|
93
56
|
}
|
|
94
57
|
}
|
|
58
|
+
export function positionWorkflowNodesAvoidOverlap(workflow) {
|
|
59
|
+
const levels = new Map();
|
|
60
|
+
function addToLevel(node) {
|
|
61
|
+
const level = Math.round(node.position.y / ySpacing);
|
|
62
|
+
if (!levels.has(level)) {
|
|
63
|
+
levels.set(level, []);
|
|
64
|
+
}
|
|
65
|
+
levels.get(level).push(node);
|
|
66
|
+
}
|
|
67
|
+
// 1) Lay out nodes using existing logic
|
|
68
|
+
positionWorkflowNodes(workflow);
|
|
69
|
+
// 2) Fill the `levels` map
|
|
70
|
+
workflow.nodes.forEach((node) => {
|
|
71
|
+
if (node.position) {
|
|
72
|
+
addToLevel(node);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// 3) Resolve horizontal overlaps and enforce parent grouping
|
|
76
|
+
levels.forEach((nodes, level) => {
|
|
77
|
+
// Group nodes by their parent
|
|
78
|
+
const parentGroups = new Map();
|
|
79
|
+
nodes.forEach((node) => {
|
|
80
|
+
const parents = getParents(node, workflow.edges);
|
|
81
|
+
if (parents.length > 0) {
|
|
82
|
+
const parent = parents[0]; // Assuming single parent for simplicity
|
|
83
|
+
if (!parentGroups.has(parent)) {
|
|
84
|
+
parentGroups.set(parent, []);
|
|
85
|
+
}
|
|
86
|
+
parentGroups.get(parent).push(node);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// Flatten the groups back into a sorted array
|
|
90
|
+
const groupedNodes = [];
|
|
91
|
+
parentGroups.forEach((group) => {
|
|
92
|
+
groupedNodes.push(...group);
|
|
93
|
+
});
|
|
94
|
+
// Sort nodes by x within each group to detect overlaps
|
|
95
|
+
groupedNodes.sort((a, b) => { var _a, _b; return ((_a = a.position.x) !== null && _a !== void 0 ? _a : 0) - ((_b = b.position.x) !== null && _b !== void 0 ? _b : 0); });
|
|
96
|
+
// Shift nodes within groups to resolve overlaps
|
|
97
|
+
for (let i = 1; i < groupedNodes.length; i++) {
|
|
98
|
+
const prev = groupedNodes[i - 1];
|
|
99
|
+
const current = groupedNodes[i];
|
|
100
|
+
if (current.position.x - prev.position.x < xSpacing) {
|
|
101
|
+
const shift = xSpacing - (current.position.x - prev.position.x);
|
|
102
|
+
moveNodeAndChildren(current, shift, workflow.edges);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// 4) **Center each parent over its children** (the existing step)
|
|
107
|
+
centerParentXPositions(workflow);
|
|
108
|
+
}
|
|
95
109
|
/**
|
|
96
|
-
* Repositions each parent node so that its X
|
|
110
|
+
* Repositions each parent node so that its X is the average of the children’s X.
|
|
111
|
+
* We do a simple bottom-up pass: start with all leaves, then move upward to parents.
|
|
97
112
|
*/
|
|
98
113
|
function centerParentXPositions(workflow) {
|
|
99
114
|
var _a, _b;
|
|
100
|
-
// Identify the leaf nodes
|
|
115
|
+
// Identify the “leaf” nodes
|
|
101
116
|
const queue = identifyLeafNodes(workflow);
|
|
102
117
|
while (queue.length > 0) {
|
|
103
118
|
const child = queue.shift();
|
|
@@ -105,59 +120,57 @@ function centerParentXPositions(workflow) {
|
|
|
105
120
|
for (const parent of parents) {
|
|
106
121
|
const children = getChildren(parent, workflow.edges);
|
|
107
122
|
if (children.length) {
|
|
123
|
+
// Average x of all children
|
|
108
124
|
const sumX = children.reduce((acc, c) => { var _a, _b; return acc + ((_b = (_a = c.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0); }, 0);
|
|
109
125
|
const avgX = sumX / children.length;
|
|
126
|
+
// Move parent to that average, keep same y
|
|
110
127
|
parent.setPosition(avgX, (_b = (_a = parent.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : 0);
|
|
111
128
|
}
|
|
112
|
-
//
|
|
129
|
+
// Add this parent to the queue so we can recurse upward
|
|
113
130
|
if (!queue.includes(parent)) {
|
|
114
131
|
queue.push(parent);
|
|
115
132
|
}
|
|
116
133
|
}
|
|
117
134
|
}
|
|
118
135
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Recursively shifts a node and all its descendants by `shift` in the x-direction.
|
|
121
|
-
*/
|
|
122
136
|
function moveNodeAndChildren(node, shift, edges) {
|
|
137
|
+
// Move the node
|
|
123
138
|
node.setPosition(node.position.x + shift, node.position.y);
|
|
139
|
+
// Propagate to children
|
|
124
140
|
edges
|
|
125
141
|
.filter((edge) => edge.source === node)
|
|
126
142
|
.forEach((edge) => {
|
|
127
143
|
moveNodeAndChildren(edge.target, shift, edges);
|
|
128
144
|
});
|
|
129
145
|
}
|
|
130
|
-
/**
|
|
131
|
-
* A "leaf" node is one that has no children (it’s never the source of an edge).
|
|
132
|
-
*/
|
|
133
146
|
export function identifyLeafNodes(workflow) {
|
|
134
|
-
const
|
|
135
|
-
return workflow.nodes.filter(
|
|
147
|
+
const nonLeafNodes = new Set(workflow.edges.map(edge => edge.source.getRef()));
|
|
148
|
+
return workflow.nodes.filter(node => !nonLeafNodes.has(node.getRef()));
|
|
136
149
|
}
|
|
137
150
|
/**
|
|
138
|
-
*
|
|
151
|
+
* Identifies starting nodes (nodes with no incoming edges).
|
|
152
|
+
* A starting node is defined as one that is not a target of any edge.
|
|
153
|
+
*
|
|
154
|
+
* @param workflow The workflow to analyze.
|
|
155
|
+
* @returns An array of nodes that have no incoming edges.
|
|
139
156
|
*/
|
|
140
157
|
export function identityStartingNodes(workflow) {
|
|
141
158
|
const childRefs = new Set(workflow.edges.map((edge) => edge.target.getRef()));
|
|
142
159
|
return workflow.nodes.filter((node) => !childRefs.has(node.getRef()));
|
|
143
160
|
}
|
|
144
161
|
export function getChildren(node, edges) {
|
|
145
|
-
return edges
|
|
146
|
-
.filter((edge) => edge.source === node)
|
|
147
|
-
.map((edge) => edge.target);
|
|
162
|
+
return edges.filter(edge => edge.source === node).map(edge => edge.target);
|
|
148
163
|
}
|
|
149
164
|
export function getParents(node, edges) {
|
|
150
|
-
return edges
|
|
151
|
-
.filter((edge) => edge.target === node)
|
|
152
|
-
.map((edge) => edge.source);
|
|
165
|
+
return edges.filter(edge => edge.target === node).map(edge => edge.source);
|
|
153
166
|
}
|
|
154
167
|
export function getEdges(node, edges) {
|
|
155
|
-
return edges.filter(
|
|
168
|
+
return edges.filter(edge => edge.source === node || edge.target === node);
|
|
156
169
|
}
|
|
157
170
|
export function getEndNodePositions(workflow) {
|
|
158
171
|
return workflow.nodes
|
|
159
|
-
.filter(
|
|
160
|
-
.map(
|
|
172
|
+
.filter(node => getChildren(node, workflow.edges).length === 0) // node with no children
|
|
173
|
+
.map(node => {
|
|
161
174
|
var _a, _b, _c, _d;
|
|
162
175
|
return ({
|
|
163
176
|
x: (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.0.
|
|
1
|
+
export declare const SDK_VERSION = "2.0.67";
|
|
2
2
|
export declare function compareVersions(v1: string, v2: string): number;
|
|
@@ -5,19 +5,16 @@ export declare const xSpacing = 500;
|
|
|
5
5
|
export declare const ySpacing = 120;
|
|
6
6
|
export declare const ROOT_X = 400;
|
|
7
7
|
export declare const ROOT_Y = 120;
|
|
8
|
-
export declare function positionWorkflowNodesAvoidOverlap(workflow: Workflow): void;
|
|
9
|
-
/**
|
|
10
|
-
* Lays out the workflow nodes in a rough top-down manner,
|
|
11
|
-
* then positions each node relative to its parent.
|
|
12
|
-
*/
|
|
13
8
|
export declare function positionWorkflowNodes(workflow: Workflow): void;
|
|
14
9
|
export declare function positionNode(node: Node, edges: Edge[], xSpacing: number, ySpacing: number, workflow: Workflow): void;
|
|
15
|
-
|
|
16
|
-
* A "leaf" node is one that has no children (it’s never the source of an edge).
|
|
17
|
-
*/
|
|
10
|
+
export declare function positionWorkflowNodesAvoidOverlap(workflow: Workflow): void;
|
|
18
11
|
export declare function identifyLeafNodes(workflow: Workflow): Node[];
|
|
19
12
|
/**
|
|
20
|
-
*
|
|
13
|
+
* Identifies starting nodes (nodes with no incoming edges).
|
|
14
|
+
* A starting node is defined as one that is not a target of any edge.
|
|
15
|
+
*
|
|
16
|
+
* @param workflow The workflow to analyze.
|
|
17
|
+
* @returns An array of nodes that have no incoming edges.
|
|
21
18
|
*/
|
|
22
19
|
export declare function identityStartingNodes(workflow: Workflow): Node[];
|
|
23
20
|
export declare function getChildren(node: Node, edges: Edge[]): Node[];
|