@statelyai/layout 0.1.3 → 0.2.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.
- package/NOTICE.md +43 -0
- package/README.md +82 -4
- package/dist/elkjs/index.cjs +8 -0
- package/dist/elkjs/index.d.cts +146 -0
- package/dist/elkjs/index.d.mts +42 -39
- package/dist/elkjs/index.mjs +2 -1483
- package/dist/elkjs/main.cjs +17 -0
- package/dist/elkjs/main.d.cts +10 -0
- package/dist/elkjs/main.d.mts +11 -0
- package/dist/elkjs/main.mjs +17 -0
- package/dist/elkjs/worker-api.cjs +3 -0
- package/dist/elkjs/worker-api.d.cts +3 -0
- package/dist/elkjs/worker-api.d.mts +3 -0
- package/dist/elkjs/worker-api.mjs +3 -0
- package/dist/elkjs/worker.cjs +86 -0
- package/dist/elkjs/worker.d.cts +17 -0
- package/dist/elkjs/worker.d.mts +17 -0
- package/dist/elkjs/worker.mjs +86 -0
- package/dist/elkjs-BVnDwnBQ.cjs +1551 -0
- package/dist/elkjs-CFlfkEBz.mjs +1528 -0
- package/dist/{index-8xYkohbz.d.mts → index-BSH2bloA.d.mts} +77 -3
- package/dist/index-CG712jzV.d.cts +1286 -0
- package/dist/index.cjs +188 -0
- package/dist/index.d.cts +77 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +24 -25
- package/dist/layered/index.cjs +38 -0
- package/dist/layered/index.d.cts +2 -0
- package/dist/layered/index.d.mts +1 -1
- package/dist/layered/index.mjs +1 -1
- package/dist/{layered-CoeH9LRO.mjs → layered-Bz2nWmVp.mjs} +2261 -47
- package/dist/layered-Cf9Rn8l7.cjs +12061 -0
- package/dist/layout-engine-BNex9jxg.cjs +712 -0
- package/dist/{spore-BtTSZbb6.mjs → layout-engine-WbC0YjjT.mjs} +159 -5
- package/dist/public-types-B8rAALJq.d.mts +109 -0
- package/dist/public-types-CVB0hXGk.d.cts +109 -0
- package/dist/worker-client-D3HMK33r.cjs +98 -0
- package/dist/worker-client-DAT_z2OR.d.mts +19 -0
- package/dist/worker-client-DVhklF6A.d.cts +19 -0
- package/dist/worker-client-DkwS4sHx.mjs +92 -0
- package/package.json +39 -9
- package/src/authoring/coordinates.ts +106 -0
- package/src/authoring/partial.ts +584 -0
- package/src/authoring/routing.ts +211 -0
- package/src/box.ts +22 -0
- package/src/constraints.ts +222 -0
- package/src/elkjs/index.ts +100 -37
- package/src/elkjs/main.ts +19 -0
- package/src/elkjs/public-types.ts +125 -0
- package/src/elkjs/worker-api.ts +29 -0
- package/src/elkjs/worker-client.ts +138 -0
- package/src/elkjs/worker.ts +117 -0
- package/src/index.ts +13 -0
- package/src/internal/elkjs-compatibility.ts +17 -0
- package/src/internal/layout-engine.ts +126 -0
- package/src/layered/index.ts +31 -3
- package/src/layout.ts +35 -28
- package/src/packing.ts +22 -0
- package/src/random.ts +75 -3
- package/src/types.ts +17 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Extensible, graph-native layout algorithms for @statelyai/graph",
|
|
5
5
|
"license": "EPL-2.0 OR GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -15,25 +15,49 @@
|
|
|
15
15
|
],
|
|
16
16
|
"type": "module",
|
|
17
17
|
"sideEffects": false,
|
|
18
|
-
"main": "./dist/index.
|
|
18
|
+
"main": "./dist/index.cjs",
|
|
19
19
|
"module": "./dist/index.mjs",
|
|
20
20
|
"types": "./src/index.ts",
|
|
21
21
|
"exports": {
|
|
22
22
|
".": {
|
|
23
23
|
"types": "./src/index.ts",
|
|
24
|
-
"
|
|
24
|
+
"import": "./dist/index.mjs",
|
|
25
|
+
"require": "./dist/index.cjs"
|
|
25
26
|
},
|
|
26
27
|
"./elkjs": {
|
|
27
28
|
"types": "./src/elkjs/index.ts",
|
|
28
|
-
"
|
|
29
|
+
"import": "./dist/elkjs/index.mjs",
|
|
30
|
+
"require": "./dist/elkjs/index.cjs"
|
|
31
|
+
},
|
|
32
|
+
"./lib/elk-api.js": {
|
|
33
|
+
"types": "./src/elkjs/worker-api.ts",
|
|
34
|
+
"import": "./dist/elkjs/worker-api.mjs",
|
|
35
|
+
"require": "./dist/elkjs/worker-api.cjs"
|
|
29
36
|
},
|
|
30
37
|
"./lib/elk.bundled.js": {
|
|
31
|
-
"types": "./src/elkjs/
|
|
32
|
-
"
|
|
38
|
+
"types": "./src/elkjs/main.ts",
|
|
39
|
+
"import": "./dist/elkjs/main.mjs",
|
|
40
|
+
"require": "./dist/elkjs/main.cjs"
|
|
41
|
+
},
|
|
42
|
+
"./lib/elk-worker.js": {
|
|
43
|
+
"types": "./src/elkjs/worker.ts",
|
|
44
|
+
"import": "./dist/elkjs/worker.mjs",
|
|
45
|
+
"require": "./dist/elkjs/worker.cjs"
|
|
46
|
+
},
|
|
47
|
+
"./lib/elk-worker.min.js": {
|
|
48
|
+
"types": "./src/elkjs/worker.ts",
|
|
49
|
+
"import": "./dist/elkjs/worker.mjs",
|
|
50
|
+
"require": "./dist/elkjs/worker.cjs"
|
|
51
|
+
},
|
|
52
|
+
"./lib/main.js": {
|
|
53
|
+
"types": "./src/elkjs/main.ts",
|
|
54
|
+
"import": "./dist/elkjs/main.mjs",
|
|
55
|
+
"require": "./dist/elkjs/main.cjs"
|
|
33
56
|
},
|
|
34
57
|
"./layered": {
|
|
35
58
|
"types": "./src/layered/index.ts",
|
|
36
|
-
"
|
|
59
|
+
"import": "./dist/layered/index.mjs",
|
|
60
|
+
"require": "./dist/layered/index.cjs"
|
|
37
61
|
},
|
|
38
62
|
"./package.json": "./package.json"
|
|
39
63
|
},
|
|
@@ -41,6 +65,7 @@
|
|
|
41
65
|
"access": "public"
|
|
42
66
|
},
|
|
43
67
|
"dependencies": {
|
|
68
|
+
"@lume/kiwi": "^0.4.4",
|
|
44
69
|
"@statelyai/graph": "^2.1.0"
|
|
45
70
|
},
|
|
46
71
|
"devDependencies": {
|
|
@@ -48,6 +73,7 @@
|
|
|
48
73
|
"@codemirror/lang-javascript": "^6.2.5",
|
|
49
74
|
"@codemirror/lint": "^6.9.7",
|
|
50
75
|
"@statelyai/layout-pre-exterior-label-separation": "npm:@statelyai/layout@0.0.4",
|
|
76
|
+
"@storybook/react-vite": "^10.6.0",
|
|
51
77
|
"@types/node": "^25.0.3",
|
|
52
78
|
"@types/react": "^19.2.18",
|
|
53
79
|
"@types/react-dom": "^19.2.4",
|
|
@@ -63,6 +89,7 @@
|
|
|
63
89
|
"react": "^19.2.8",
|
|
64
90
|
"react-dom": "^19.2.8",
|
|
65
91
|
"react-resizable-panels": "^4.12.2",
|
|
92
|
+
"storybook": "^10.6.0",
|
|
66
93
|
"tsdown": "^0.18.1",
|
|
67
94
|
"tsx": "^4.22.4",
|
|
68
95
|
"typescript": "^5.9.3",
|
|
@@ -93,7 +120,10 @@
|
|
|
93
120
|
"test": "vitest",
|
|
94
121
|
"typecheck": "tsc --noEmit",
|
|
95
122
|
"typecheck:repo": "tsc --project tsconfig.repo.json",
|
|
96
|
-
"verify": "oxfmt --check . && oxlint && tsc --noEmit && tsc --project tsconfig.repo.json && tsc --project demo/tsconfig.json && tsx scripts/generate-elk-layered-options.ts --check && tsx scripts/generate-demo-corpus.ts --check && vitest --run && tsdown && vite build --config demo/vite.config.ts && publint --pack npm && tsx scripts/smoke-package.ts",
|
|
97
|
-
"validate:package": "publint --pack npm && tsx scripts/smoke-package.ts"
|
|
123
|
+
"verify": "oxfmt --check . && oxlint && tsc --noEmit && tsc --project tsconfig.repo.json && tsc --project demo/tsconfig.json && tsc --project stories/tsconfig.json && tsx scripts/generate-elk-layered-options.ts --check && tsx scripts/generate-demo-corpus.ts --check && vitest --run && tsdown && vite build --config demo/vite.config.ts && storybook build -o dist-storybook --quiet && publint --pack npm && tsx scripts/smoke-package.ts",
|
|
124
|
+
"validate:package": "publint --pack npm && tsx scripts/smoke-package.ts",
|
|
125
|
+
"storybook": "storybook dev -p 6018 --host 127.0.0.1 --no-open",
|
|
126
|
+
"storybook:build": "storybook build -o dist-storybook",
|
|
127
|
+
"storybook:typecheck": "tsc --project stories/tsconfig.json"
|
|
98
128
|
}
|
|
99
129
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { LayoutError } from "../errors";
|
|
2
|
+
import type { VisualGraph, Point } from "@statelyai/graph";
|
|
3
|
+
|
|
4
|
+
/** Native compound nodes and sibling edges are parent-relative; crossing edges are world-relative. */
|
|
5
|
+
function frames<N, E, G, P>(graph: VisualGraph<N, E, G, P>) {
|
|
6
|
+
const nodes = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
7
|
+
const offsets = new Map<string, Point>();
|
|
8
|
+
const visiting = new Set<string>();
|
|
9
|
+
function offset(id: string): Point {
|
|
10
|
+
const cached = offsets.get(id);
|
|
11
|
+
if (cached) return cached;
|
|
12
|
+
if (visiting.has(id))
|
|
13
|
+
throw new LayoutError(`Cyclic parent hierarchy at ${id}`, "INVALID_GRAPH");
|
|
14
|
+
visiting.add(id);
|
|
15
|
+
const parentId = nodes.get(id)?.parentId;
|
|
16
|
+
if (parentId == null) {
|
|
17
|
+
const origin = { x: 0, y: 0 };
|
|
18
|
+
visiting.delete(id);
|
|
19
|
+
offsets.set(id, origin);
|
|
20
|
+
return origin;
|
|
21
|
+
}
|
|
22
|
+
const parent = nodes.get(parentId)!;
|
|
23
|
+
const ancestor = offset(parentId);
|
|
24
|
+
const result = { x: ancestor.x + parent.x, y: ancestor.y + parent.y };
|
|
25
|
+
visiting.delete(id);
|
|
26
|
+
offsets.set(id, result);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
for (const node of graph.nodes) offsets.set(node.id, offset(node.id));
|
|
30
|
+
const edgeOffsets = new Map(
|
|
31
|
+
graph.edges.map((edge) => {
|
|
32
|
+
const source = nodes.get(edge.sourceId)!;
|
|
33
|
+
const target = nodes.get(edge.targetId)!;
|
|
34
|
+
return [
|
|
35
|
+
edge.id,
|
|
36
|
+
(source.parentId ?? null) === (target.parentId ?? null)
|
|
37
|
+
? offset(source.id)
|
|
38
|
+
: { x: 0, y: 0 },
|
|
39
|
+
] as const;
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
return { nodes: offsets, edges: edgeOffsets };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function worldGeometry<N, E, G, P>(graph: VisualGraph<N, E, G, P>): VisualGraph<N, E, G, P> {
|
|
46
|
+
const offsets = frames(graph);
|
|
47
|
+
return {
|
|
48
|
+
...graph,
|
|
49
|
+
nodes: graph.nodes.map((n) => {
|
|
50
|
+
const p = offsets.nodes.get(n.id)!;
|
|
51
|
+
return p.x === 0 && p.y === 0 ? n : { ...n, x: n.x + p.x, y: n.y + p.y };
|
|
52
|
+
}),
|
|
53
|
+
edges: graph.edges.map((e) => {
|
|
54
|
+
const p = offsets.edges.get(e.id)!;
|
|
55
|
+
return p.x === 0 && p.y === 0
|
|
56
|
+
? e
|
|
57
|
+
: {
|
|
58
|
+
...e,
|
|
59
|
+
x: e.x + p.x,
|
|
60
|
+
y: e.y + p.y,
|
|
61
|
+
...(e.points ? { points: e.points.map((v) => ({ x: v.x + p.x, y: v.y + p.y })) } : {}),
|
|
62
|
+
};
|
|
63
|
+
}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Ancestor frames stay fixed during authoring. Preserve untouched coordinates exactly. */
|
|
68
|
+
export function nativeGeometry<N, E, G, P>(
|
|
69
|
+
graph: VisualGraph<N, E, G, P>,
|
|
70
|
+
original: VisualGraph<N, E, G, P>,
|
|
71
|
+
): VisualGraph<N, E, G, P> {
|
|
72
|
+
const offsets = frames(original);
|
|
73
|
+
const world = worldGeometry(original);
|
|
74
|
+
const originalNodes = new Map(original.nodes.map((n) => [n.id, n]));
|
|
75
|
+
const originalEdges = new Map(original.edges.map((e) => [e.id, e]));
|
|
76
|
+
const worldNodes = new Map(world.nodes.map((n) => [n.id, n]));
|
|
77
|
+
const worldEdges = new Map(world.edges.map((e) => [e.id, e]));
|
|
78
|
+
return {
|
|
79
|
+
...graph,
|
|
80
|
+
nodes: graph.nodes.map((n) => {
|
|
81
|
+
const p = offsets.nodes.get(n.id)!,
|
|
82
|
+
before = originalNodes.get(n.id)!,
|
|
83
|
+
prior = worldNodes.get(n.id)!;
|
|
84
|
+
return {
|
|
85
|
+
...n,
|
|
86
|
+
x: n.x === prior.x ? before.x : n.x - p.x,
|
|
87
|
+
y: n.y === prior.y ? before.y : n.y - p.y,
|
|
88
|
+
};
|
|
89
|
+
}),
|
|
90
|
+
edges: graph.edges.map((e) => {
|
|
91
|
+
const p = offsets.edges.get(e.id)!,
|
|
92
|
+
before = originalEdges.get(e.id)!,
|
|
93
|
+
prior = worldEdges.get(e.id)!;
|
|
94
|
+
const points =
|
|
95
|
+
JSON.stringify(e.points) === JSON.stringify(prior.points)
|
|
96
|
+
? before.points
|
|
97
|
+
: e.points?.map((v) => ({ x: v.x - p.x, y: v.y - p.y }));
|
|
98
|
+
return {
|
|
99
|
+
...e,
|
|
100
|
+
x: e.x === prior.x ? before.x : e.x - p.x,
|
|
101
|
+
y: e.y === prior.y ? before.y : e.y - p.y,
|
|
102
|
+
...(points === undefined ? {} : { points }),
|
|
103
|
+
};
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
}
|