@statelyai/layout 0.0.0 → 0.0.2

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.
Files changed (46) hide show
  1. package/NOTICE.md +9 -1
  2. package/README.md +50 -25
  3. package/dist/elkjs/index.d.mts +3 -0
  4. package/dist/elkjs/index.mjs +904 -97
  5. package/dist/index-8xYkohbz.d.mts +1212 -0
  6. package/dist/index.d.mts +2 -2
  7. package/dist/index.mjs +3 -3
  8. package/dist/layered/index.d.mts +2 -2
  9. package/dist/layered/index.mjs +2 -2
  10. package/dist/layered-QwJn2gb2.mjs +8978 -0
  11. package/dist/{spore-cihb_Aht.mjs → spore-D15xIQKj.mjs} +1 -31
  12. package/package.json +59 -26
  13. package/src/box.ts +135 -0
  14. package/src/elkjs/index.ts +1825 -0
  15. package/src/elkjs/types.ts +103 -0
  16. package/src/errors.ts +16 -0
  17. package/src/fixed.ts +80 -0
  18. package/src/index.ts +92 -0
  19. package/src/java-random.ts +46 -0
  20. package/src/layered/bk-node-placement.ts +715 -0
  21. package/src/layered/elk-enum-values.ts +171 -0
  22. package/src/layered/elk-options.generated.ts +315 -0
  23. package/src/layered/elk-options.ts +98 -0
  24. package/src/layered/flexible-ports.ts +11 -0
  25. package/src/layered/high-degree.ts +127 -0
  26. package/src/layered/index.ts +2138 -0
  27. package/src/layered/layer-unzipping.ts +217 -0
  28. package/src/layered/linear-segments-node-placement.ts +447 -0
  29. package/src/layered/long-edges.ts +405 -0
  30. package/src/layered/min-width.ts +159 -0
  31. package/src/layered/multi-edge-wrapping.ts +460 -0
  32. package/src/layered/network-simplex-node-placement.ts +500 -0
  33. package/src/layered/network-simplex.ts +346 -0
  34. package/src/layered/node-promotion.ts +197 -0
  35. package/src/layered/spacing.ts +37 -0
  36. package/src/layered/spline-bezier.ts +102 -0
  37. package/src/layered/strategies.ts +5343 -0
  38. package/src/layered/stretch-width.ts +136 -0
  39. package/src/layered/types.ts +111 -0
  40. package/src/layout.ts +202 -0
  41. package/src/packing.ts +74 -0
  42. package/src/random.ts +142 -0
  43. package/src/spore.ts +103 -0
  44. package/src/types.ts +84 -0
  45. package/dist/index-v0P1Ake8.d.mts +0 -149
  46. package/dist/layered-ByNCZQgJ.mjs +0 -439
@@ -1,4 +1,4 @@
1
- import { a as getPolylineMidpoint, c as placePorts } from "./layered-ByNCZQgJ.mjs";
1
+ import { F as placePorts, O as getPolylineMidpoint, z as JavaRandom } from "./layered-QwJn2gb2.mjs";
2
2
  import { getNodeSize } from "@statelyai/graph/layout";
3
3
 
4
4
  //#region src/fixed.ts
@@ -262,36 +262,6 @@ const rectanglePackingAlgorithm = {
262
262
 
263
263
  //#endregion
264
264
  //#region src/random.ts
265
- var JavaRandom = class JavaRandom {
266
- static #multiplier = 25214903917n;
267
- static #addend = 11n;
268
- static #mask = (1n << 48n) - 1n;
269
- #seed;
270
- constructor(seed) {
271
- this.#seed = (BigInt(seed) ^ JavaRandom.#multiplier) & JavaRandom.#mask;
272
- }
273
- #next(bits) {
274
- this.#seed = this.#seed * JavaRandom.#multiplier + JavaRandom.#addend & JavaRandom.#mask;
275
- return Number(this.#seed >> BigInt(48 - bits));
276
- }
277
- nextDouble() {
278
- return (this.#next(26) * 2 ** 27 + this.#next(27)) / 2 ** 53;
279
- }
280
- nextFloat() {
281
- return this.#next(24) / 2 ** 24;
282
- }
283
- nextInt(bound) {
284
- if (bound <= 0) throw new RangeError("bound must be positive");
285
- if ((bound & -bound) === bound) return Math.floor(bound * this.#next(31) / 2 ** 31);
286
- let bits;
287
- let value;
288
- do {
289
- bits = this.#next(31);
290
- value = bits % bound;
291
- } while (bits - value + (bound - 1) >= 2 ** 31);
292
- return value;
293
- }
294
- };
295
265
  function getPadding$1(value) {
296
266
  if (typeof value === "number") return {
297
267
  top: value,
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "@statelyai/layout",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "Extensible, graph-native layout algorithms for @statelyai/graph",
5
5
  "license": "EPL-2.0 OR GPL-3.0-or-later",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/statelyai/layout.git"
9
+ },
6
10
  "files": [
7
11
  "dist",
12
+ "src",
8
13
  "LICENSE.md",
9
14
  "NOTICE.md"
10
15
  ],
@@ -12,42 +17,47 @@
12
17
  "sideEffects": false,
13
18
  "main": "./dist/index.mjs",
14
19
  "module": "./dist/index.mjs",
15
- "types": "./dist/index.d.mts",
20
+ "types": "./src/index.ts",
16
21
  "exports": {
17
- ".": "./dist/index.mjs",
18
- "./elkjs": "./dist/elkjs/index.mjs",
19
- "./layered": "./dist/layered/index.mjs",
22
+ ".": {
23
+ "types": "./src/index.ts",
24
+ "default": "./dist/index.mjs"
25
+ },
26
+ "./elkjs": {
27
+ "types": "./src/elkjs/index.ts",
28
+ "default": "./dist/elkjs/index.mjs"
29
+ },
30
+ "./layered": {
31
+ "types": "./src/layered/index.ts",
32
+ "default": "./dist/layered/index.mjs"
33
+ },
20
34
  "./package.json": "./package.json"
21
35
  },
22
- "scripts": {
23
- "build": "tsdown",
24
- "bench": "vitest bench --run",
25
- "demo": "vite --config demo/vite.config.ts",
26
- "demo:build": "vite build --config demo/vite.config.ts",
27
- "demo:check": "tsx scripts/generate-demo-corpus.ts --check",
28
- "demo:generate": "tsx scripts/generate-demo-corpus.ts",
29
- "demo:typecheck": "tsc --project demo/tsconfig.json",
30
- "dev": "tsdown --watch",
31
- "format": "oxfmt .",
32
- "format:check": "oxfmt --check .",
33
- "lint": "oxlint",
34
- "lint:fix": "oxlint --fix",
35
- "test": "vitest",
36
- "typecheck": "tsc --noEmit",
37
- "typecheck:repo": "tsc --project tsconfig.repo.json",
38
- "verify": "oxfmt --check . && oxlint && tsc --noEmit && tsc --project tsconfig.repo.json && tsc --project demo/tsconfig.json && 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",
39
- "validate:package": "publint --pack npm && tsx scripts/smoke-package.ts"
36
+ "publishConfig": {
37
+ "access": "public"
40
38
  },
41
39
  "dependencies": {
42
40
  "@statelyai/graph": "^2.1.0"
43
41
  },
44
42
  "devDependencies": {
45
- "@statelyai/sdk": "^0.20.1",
43
+ "@changesets/cli": "3.0.1",
44
+ "@codemirror/lang-javascript": "^6.2.5",
45
+ "@codemirror/lint": "^6.9.7",
46
46
  "@types/node": "^25.0.3",
47
+ "@types/react": "^19.2.18",
48
+ "@types/react-dom": "^19.2.4",
49
+ "@vitejs/plugin-react": "^6.0.5",
50
+ "codemirror": "^6.0.2",
47
51
  "elkjs": "^0.11.1",
52
+ "json5": "^2.2.3",
53
+ "lucide-react": "^1.31.0",
48
54
  "oxfmt": "0.62.0",
49
55
  "oxlint": "1.77.0",
56
+ "portless": "^0.15.5",
50
57
  "publint": "^0.3.15",
58
+ "react": "^19.2.8",
59
+ "react-dom": "^19.2.8",
60
+ "react-resizable-panels": "^4.12.2",
51
61
  "tsdown": "^0.18.1",
52
62
  "tsx": "^4.22.4",
53
63
  "typescript": "^5.9.3",
@@ -57,5 +67,28 @@
57
67
  "peerDependencies": {
58
68
  "@statelyai/graph": "^2.1.0"
59
69
  },
60
- "packageManager": "pnpm@10.28.2"
61
- }
70
+ "scripts": {
71
+ "build": "tsdown --config tsdown.config.mjs",
72
+ "bench": "vitest bench --run",
73
+ "demo": "portless --name layout vite --config demo/vite.config.ts",
74
+ "demo:build": "vite build --config demo/vite.config.ts",
75
+ "demo:check": "tsx scripts/generate-demo-corpus.ts --check",
76
+ "demo:generate": "tsx scripts/generate-demo-corpus.ts",
77
+ "demo:sync": "tsx scripts/sync-elk-live-examples.ts && pnpm demo:generate",
78
+ "demo:typecheck": "tsc --project demo/tsconfig.json",
79
+ "generate:elk-layered-options": "tsx scripts/generate-elk-layered-options.ts",
80
+ "generate:elk-layered-options:check": "tsx scripts/generate-elk-layered-options.ts --check",
81
+ "dev": "tsdown --watch",
82
+ "changeset": "changeset",
83
+ "release": "pnpm build && pnpm changeset publish",
84
+ "format": "oxfmt .",
85
+ "format:check": "oxfmt --check .",
86
+ "lint": "oxlint",
87
+ "lint:fix": "oxlint --fix",
88
+ "test": "vitest",
89
+ "typecheck": "tsc --noEmit",
90
+ "typecheck:repo": "tsc --project tsconfig.repo.json",
91
+ "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",
92
+ "validate:package": "publint --pack npm && tsx scripts/smoke-package.ts"
93
+ }
94
+ }
package/src/box.ts ADDED
@@ -0,0 +1,135 @@
1
+ /*******************************************************************************
2
+ * Copyright (c) 2009, 2020 Kiel University and others.
3
+ *
4
+ * Translated from ELK v0.11.0 BoxLayoutProvider.java SIMPLE mode.
5
+ * Source commit: 54123e884b1ae743b453260f713b20c9bf5787f2
6
+ * SPDX-License-Identifier: EPL-2.0
7
+ *******************************************************************************/
8
+ import type { Graph, GraphNode, VisualGraph, VisualNode } from "@statelyai/graph";
9
+ import { getNodeSize, type LayoutOptions } from "@statelyai/graph/layout";
10
+ import { getFixedLayout } from "./fixed";
11
+ import type { LayoutPadding } from "./layered";
12
+ import type { LayoutAlgorithm } from "./types";
13
+
14
+ export interface BoxLayoutOptions extends Pick<LayoutOptions, "direction" | "measure"> {
15
+ spacing?: number;
16
+ padding?: number | Partial<LayoutPadding>;
17
+ aspectRatio?: number;
18
+ interactive?: boolean;
19
+ expandNodes?: boolean;
20
+ priority?: (node: GraphNode) => number | undefined;
21
+ }
22
+
23
+ function getPadding(value: BoxLayoutOptions["padding"]): LayoutPadding {
24
+ if (typeof value === "number") {
25
+ return { top: value, right: value, bottom: value, left: value };
26
+ }
27
+ return {
28
+ top: value?.top ?? 15,
29
+ right: value?.right ?? 15,
30
+ bottom: value?.bottom ?? 15,
31
+ left: value?.left ?? 15,
32
+ };
33
+ }
34
+
35
+ function standardDeviation(values: readonly number[], mean: number): number {
36
+ if (values.length < 2) return 0;
37
+ const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0);
38
+ return Math.sqrt(variance / (values.length - 1));
39
+ }
40
+
41
+ /** ELK Box SIMPLE packing translated onto `@statelyai/graph`. */
42
+ export function getBoxLayout<N, E, G, P>(
43
+ graph: Graph<N, E, G, P> | VisualGraph<N, E, G, P>,
44
+ options: BoxLayoutOptions = {},
45
+ ): VisualGraph<N, E, G, P> {
46
+ const spacing = Math.fround(options.spacing ?? 15);
47
+ const padding = getPadding(options.padding);
48
+ const aspectRatio = (options.aspectRatio ?? 1.3) > 0 ? (options.aspectRatio ?? 1.3) : 1.3;
49
+ const sizes = new Map(graph.nodes.map((node) => [node.id, getNodeSize(node, options)]));
50
+ const sortedNodes = [...graph.nodes].sort((first, second) => {
51
+ const priorityDifference = (options.priority?.(second) ?? 0) - (options.priority?.(first) ?? 0);
52
+ if (priorityDifference !== 0) return priorityDifference;
53
+ if (options.interactive) {
54
+ const yDifference = (first.y ?? 0) - (second.y ?? 0);
55
+ if (yDifference !== 0) return yDifference;
56
+ const xDifference = (first.x ?? 0) - (second.x ?? 0);
57
+ if (xDifference !== 0) return xDifference;
58
+ }
59
+ const firstSize = sizes.get(first.id) ?? { width: 0, height: 0 };
60
+ const secondSize = sizes.get(second.id) ?? { width: 0, height: 0 };
61
+ return firstSize.width * firstSize.height - secondSize.width * secondSize.height;
62
+ });
63
+
64
+ const areas = sortedNodes.map((node) => {
65
+ const size = sizes.get(node.id) ?? { width: 0, height: 0 };
66
+ return size.width * size.height;
67
+ });
68
+ let totalArea = areas.reduce((sum, area) => sum + area, 0);
69
+ const mean = areas.length === 0 ? 0 : totalArea / areas.length;
70
+ totalArea += areas.length * standardDeviation(areas, mean);
71
+ totalArea += Math.sqrt(totalArea) * (padding.bottom + padding.top);
72
+ totalArea += Math.sqrt(totalArea) * padding.right;
73
+ const widestNode = Math.max(0, ...sortedNodes.map((node) => sizes.get(node.id)?.width ?? 0));
74
+ const maximumRowWidth = Math.max(widestNode, Math.sqrt(totalArea * aspectRatio)) + padding.left;
75
+
76
+ let x = padding.left;
77
+ let y = padding.top;
78
+ let rowHeight = 0;
79
+ let rowStart = 0;
80
+ const rows: Array<{ start: number; end: number; height: number }> = [];
81
+ const nodes: VisualNode<N, P>[] = [];
82
+ for (const node of sortedNodes) {
83
+ const size = sizes.get(node.id) ?? { width: 0, height: 0 };
84
+ if (x + size.width > maximumRowWidth) {
85
+ rows.push({ start: rowStart, end: nodes.length, height: rowHeight });
86
+ rowStart = nodes.length;
87
+ x = padding.left;
88
+ y += rowHeight + spacing;
89
+ rowHeight = 0;
90
+ }
91
+ nodes.push({ ...node, x, y, ...size } as VisualNode<N, P>);
92
+ x += size.width + spacing;
93
+ rowHeight = Math.max(rowHeight, size.height);
94
+ }
95
+ rows.push({ start: rowStart, end: nodes.length, height: rowHeight });
96
+
97
+ if (options.expandNodes) {
98
+ const broadestRow = Math.max(
99
+ padding.left + padding.right,
100
+ ...nodes.map((node) => node.x + node.width + padding.right),
101
+ );
102
+ for (const row of rows) {
103
+ for (let index = row.start; index < row.end; index++) {
104
+ const node = nodes[index];
105
+ if (!node) continue;
106
+ node.height = row.height;
107
+ if (index === row.end - 1) node.width = broadestRow - node.x - padding.right;
108
+ }
109
+ }
110
+ }
111
+
112
+ const placedById = new Map(nodes.map((node) => [node.id, node]));
113
+ return getFixedLayout(
114
+ {
115
+ ...graph,
116
+ nodes: graph.nodes.map((node) => placedById.get(node.id) ?? node) as VisualNode<N, P>[],
117
+ },
118
+ { direction: options.direction ?? graph.direction },
119
+ );
120
+ }
121
+
122
+ export const boxAlgorithm: LayoutAlgorithm<BoxLayoutOptions> = {
123
+ id: "box",
124
+ capabilities: {
125
+ full: true,
126
+ incremental: false,
127
+ partial: false,
128
+ routeOnly: false,
129
+ hierarchy: false,
130
+ ports: true,
131
+ },
132
+ layout(graph, options) {
133
+ return getBoxLayout(graph, options ?? {});
134
+ },
135
+ };