@statelyai/layout 0.1.3 → 0.3.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 +153 -6
- package/dist/elkjs/index.cjs +9 -0
- package/dist/elkjs/index.d.cts +155 -0
- package/dist/elkjs/index.d.mts +51 -39
- package/dist/elkjs/index.mjs +3 -1484
- 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-DaHMISnQ.mjs +1677 -0
- package/dist/elkjs-dMghiiWW.cjs +1706 -0
- package/dist/index-BEA_pzeT.d.cts +72 -0
- package/dist/{index-8xYkohbz.d.mts → index-D9dhxrRl.d.cts} +111 -5
- package/dist/index-DNTLRsDl.d.mts +72 -0
- package/dist/index-DNixtE3h.d.mts +1318 -0
- package/dist/index.cjs +215 -0
- package/dist/index.d.cts +78 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +25 -25
- package/dist/layered/index.cjs +38 -0
- package/dist/layered/index.d.cts +3 -0
- package/dist/layered/index.d.mts +3 -2
- package/dist/layered/index.mjs +1 -1
- package/dist/layered-B2buJqMf.cjs +12009 -0
- package/dist/{layered-CoeH9LRO.mjs → layered-DGFTeQSl.mjs} +2323 -149
- package/dist/{spore-BtTSZbb6.mjs → layout-engine-Cs5tatA6.mjs} +159 -5
- package/dist/layout-engine-Dn1PiFwP.cjs +712 -0
- package/dist/public-types-9NCPoqbp.d.mts +109 -0
- package/dist/public-types-BcRZoJE3.d.cts +109 -0
- package/dist/routing/index.cjs +28 -0
- package/dist/routing/index.d.cts +3 -0
- package/dist/routing/index.d.mts +3 -0
- package/dist/routing/index.mjs +3 -0
- package/dist/routing-A2hrK9MP.mjs +2396 -0
- package/dist/routing-ujEP7Qh6.cjs +2606 -0
- package/dist/types-CT2akdK0.d.mts +133 -0
- package/dist/types-uxPlGRof.d.cts +133 -0
- package/dist/worker-client-BNjwT1AJ.mjs +92 -0
- package/dist/worker-client-BwGGRD5y.d.mts +19 -0
- package/dist/worker-client-D4oEvhAl.cjs +98 -0
- package/dist/worker-client-D4zNyPEI.d.cts +19 -0
- package/package.json +45 -10
- package/src/authoring/coordinates.ts +114 -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 +104 -39
- package/src/elkjs/main.ts +19 -0
- package/src/elkjs/public-types.ts +125 -0
- package/src/elkjs/routes.ts +147 -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 +19 -0
- package/src/internal/elkjs-compatibility.ts +17 -0
- package/src/internal/layout-engine.ts +126 -0
- package/src/layered/compound.ts +417 -0
- package/src/layered/index.ts +58 -105
- package/src/layered/types.ts +21 -0
- package/src/layout.ts +35 -28
- package/src/packing.ts +22 -0
- package/src/random.ts +75 -3
- package/src/routing/adapters.ts +111 -0
- package/src/routing/algorithms.ts +676 -0
- package/src/routing/coordination.ts +86 -0
- package/src/routing/engine.ts +473 -0
- package/src/routing/index.ts +68 -0
- package/src/routing/layout-cache.ts +65 -0
- package/src/routing/model.ts +218 -0
- package/src/routing/path.ts +324 -0
- package/src/routing/persistent.ts +117 -0
- package/src/routing/search.ts +276 -0
- package/src/routing/spatial.ts +78 -0
- package/src/routing/types.ts +146 -0
- package/src/types.ts +17 -2
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { EntityRect, Graph, GraphDiff, Point } from "@statelyai/graph";
|
|
2
|
+
|
|
3
|
+
//#region src/routing/types.d.ts
|
|
4
|
+
type RoutePoint = Readonly<Point>;
|
|
5
|
+
type RouteSegment = {
|
|
6
|
+
readonly kind: "line";
|
|
7
|
+
readonly to: RoutePoint;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: "quadratic";
|
|
10
|
+
readonly control: RoutePoint;
|
|
11
|
+
readonly to: RoutePoint;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "cubic";
|
|
14
|
+
readonly control1: RoutePoint;
|
|
15
|
+
readonly control2: RoutePoint;
|
|
16
|
+
readonly to: RoutePoint;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: "arc";
|
|
19
|
+
readonly rx: number;
|
|
20
|
+
readonly ry: number;
|
|
21
|
+
readonly rotation: number;
|
|
22
|
+
readonly largeArc: boolean;
|
|
23
|
+
readonly sweep: boolean;
|
|
24
|
+
readonly to: RoutePoint;
|
|
25
|
+
};
|
|
26
|
+
interface RoutePath {
|
|
27
|
+
readonly start: RoutePoint;
|
|
28
|
+
readonly segments: readonly RouteSegment[];
|
|
29
|
+
}
|
|
30
|
+
type RouteEndpoint = {
|
|
31
|
+
readonly kind: "node";
|
|
32
|
+
readonly nodeId: string;
|
|
33
|
+
readonly port?: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: "label";
|
|
36
|
+
readonly edgeId: string;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: "junction";
|
|
39
|
+
readonly id: string;
|
|
40
|
+
};
|
|
41
|
+
interface RouteSection {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly from: RouteEndpoint;
|
|
44
|
+
readonly to: RouteEndpoint;
|
|
45
|
+
readonly path: RoutePath;
|
|
46
|
+
/** Same ID denotes a shared trunk; render it once. */
|
|
47
|
+
readonly sharedId?: string;
|
|
48
|
+
}
|
|
49
|
+
type RoutingDiagnosticCode = "MISSING_GEOMETRY" | "MISSING_PORT" | "ROUTE_CONFLICT" | "ROUTE_BLOCKED" | "SEARCH_BUDGET" | "CONSTRAINT_VIOLATION";
|
|
50
|
+
interface RoutingDiagnostic {
|
|
51
|
+
readonly code: RoutingDiagnosticCode;
|
|
52
|
+
readonly edgeId: string;
|
|
53
|
+
readonly message: string;
|
|
54
|
+
}
|
|
55
|
+
interface Route {
|
|
56
|
+
readonly edgeId: string;
|
|
57
|
+
readonly sections: readonly RouteSection[];
|
|
58
|
+
readonly status: "routed" | "fallback";
|
|
59
|
+
readonly diagnostics: readonly RoutingDiagnostic[];
|
|
60
|
+
}
|
|
61
|
+
type RoutePatch = {
|
|
62
|
+
readonly op: "set";
|
|
63
|
+
readonly edgeId: string;
|
|
64
|
+
readonly route: Route;
|
|
65
|
+
} | {
|
|
66
|
+
readonly op: "delete";
|
|
67
|
+
readonly edgeId: string;
|
|
68
|
+
};
|
|
69
|
+
type RouteStyle = "straight" | "bezier" | "orthogonal" | "polyline" | "octilinear" | "curved" | "organic" | "parallel" | "self-loop" | "fan" | "bus" | "bundle";
|
|
70
|
+
type RouteSide = "left" | "right" | "top" | "bottom";
|
|
71
|
+
interface RouteAttachment {
|
|
72
|
+
/** Node-local attachment rectangle, independent of its outer obstacle bounds. */
|
|
73
|
+
readonly bounds: RouteBounds;
|
|
74
|
+
readonly facing?: "inward" | "outward";
|
|
75
|
+
}
|
|
76
|
+
interface EdgeRoutingSettings {
|
|
77
|
+
readonly sourceAttachment?: RouteAttachment;
|
|
78
|
+
readonly targetAttachment?: RouteAttachment;
|
|
79
|
+
readonly sourceSide?: RouteSide;
|
|
80
|
+
readonly targetSide?: RouteSide;
|
|
81
|
+
readonly waypoints?: readonly RoutePoint[];
|
|
82
|
+
/** Explicit shared routing group. Native binary edges describe branches of a net. */
|
|
83
|
+
readonly group?: string;
|
|
84
|
+
}
|
|
85
|
+
interface RoutingSettings {
|
|
86
|
+
/** Native compound geometry is parent-relative; adapters may supply resolved world geometry. */
|
|
87
|
+
readonly coordinateSpace?: "parent" | "world";
|
|
88
|
+
readonly clearance?: number;
|
|
89
|
+
readonly radius?: number;
|
|
90
|
+
readonly edgeSpacing?: number;
|
|
91
|
+
readonly bendPenalty?: number;
|
|
92
|
+
/** Soft cost per crossing; finite costs keep detours proportionate. Default 80. */
|
|
93
|
+
readonly crossingPenalty?: number;
|
|
94
|
+
/** Cost per unit of parallel overlap within edgeSpacing. Default 8. */
|
|
95
|
+
readonly overlapPenalty?: number;
|
|
96
|
+
readonly maxSearchNodes?: number;
|
|
97
|
+
readonly organicIterations?: number;
|
|
98
|
+
readonly edges?: Readonly<Record<string, EdgeRoutingSettings>>;
|
|
99
|
+
}
|
|
100
|
+
interface RoutingMetrics {
|
|
101
|
+
readonly routedEdges: number;
|
|
102
|
+
readonly reusedEdges: number;
|
|
103
|
+
readonly affectedEdges: number;
|
|
104
|
+
readonly searchNodes: number;
|
|
105
|
+
readonly obstacleCandidates: number;
|
|
106
|
+
}
|
|
107
|
+
/** Immutable value. Retain it per canvas; the private state uses structural sharing. */
|
|
108
|
+
interface RoutingSnapshot {
|
|
109
|
+
readonly strategy: string;
|
|
110
|
+
readonly revision: number;
|
|
111
|
+
readonly routes: ReadonlyMap<string, Route>;
|
|
112
|
+
readonly metrics: RoutingMetrics;
|
|
113
|
+
}
|
|
114
|
+
interface RoutingUpdate {
|
|
115
|
+
readonly snapshot: RoutingSnapshot;
|
|
116
|
+
readonly patches: readonly RoutePatch[];
|
|
117
|
+
/** Compare identity before accepting an asynchronous result. */
|
|
118
|
+
readonly base: RoutingSnapshot;
|
|
119
|
+
}
|
|
120
|
+
type RoutingGraph = Graph<unknown, unknown, unknown, unknown>;
|
|
121
|
+
interface RoutingStrategy {
|
|
122
|
+
readonly id: string;
|
|
123
|
+
route(graph: RoutingGraph, settings?: RoutingSettings): RoutingSnapshot | Promise<RoutingSnapshot>;
|
|
124
|
+
update(graph: RoutingGraph, previous: RoutingSnapshot, diff: GraphDiff<unknown, unknown>, settings?: RoutingSettings): RoutingUpdate | Promise<RoutingUpdate>;
|
|
125
|
+
}
|
|
126
|
+
interface NativeRoutingStrategy extends RoutingStrategy {
|
|
127
|
+
readonly id: RouteStyle;
|
|
128
|
+
route(graph: RoutingGraph, settings?: RoutingSettings): RoutingSnapshot;
|
|
129
|
+
update(graph: RoutingGraph, previous: RoutingSnapshot, diff: GraphDiff<unknown, unknown>, settings?: RoutingSettings): RoutingUpdate;
|
|
130
|
+
}
|
|
131
|
+
type RouteBounds = Readonly<EntityRect>;
|
|
132
|
+
//#endregion
|
|
133
|
+
export { RoutingMetrics as _, RouteBounds as a, RoutingStrategy as b, RoutePath as c, RouteSegment as d, RouteSide as f, RoutingGraph as g, RoutingDiagnosticCode as h, RouteAttachment as i, RoutePoint as l, RoutingDiagnostic as m, NativeRoutingStrategy as n, RouteEndpoint as o, RouteStyle as p, Route as r, RoutePatch as s, EdgeRoutingSettings as t, RouteSection as u, RoutingSettings as v, RoutingUpdate as x, RoutingSnapshot as y };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { EntityRect, Graph, GraphDiff, Point } from "@statelyai/graph";
|
|
2
|
+
|
|
3
|
+
//#region src/routing/types.d.ts
|
|
4
|
+
type RoutePoint = Readonly<Point>;
|
|
5
|
+
type RouteSegment = {
|
|
6
|
+
readonly kind: "line";
|
|
7
|
+
readonly to: RoutePoint;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: "quadratic";
|
|
10
|
+
readonly control: RoutePoint;
|
|
11
|
+
readonly to: RoutePoint;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "cubic";
|
|
14
|
+
readonly control1: RoutePoint;
|
|
15
|
+
readonly control2: RoutePoint;
|
|
16
|
+
readonly to: RoutePoint;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: "arc";
|
|
19
|
+
readonly rx: number;
|
|
20
|
+
readonly ry: number;
|
|
21
|
+
readonly rotation: number;
|
|
22
|
+
readonly largeArc: boolean;
|
|
23
|
+
readonly sweep: boolean;
|
|
24
|
+
readonly to: RoutePoint;
|
|
25
|
+
};
|
|
26
|
+
interface RoutePath {
|
|
27
|
+
readonly start: RoutePoint;
|
|
28
|
+
readonly segments: readonly RouteSegment[];
|
|
29
|
+
}
|
|
30
|
+
type RouteEndpoint = {
|
|
31
|
+
readonly kind: "node";
|
|
32
|
+
readonly nodeId: string;
|
|
33
|
+
readonly port?: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly kind: "label";
|
|
36
|
+
readonly edgeId: string;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: "junction";
|
|
39
|
+
readonly id: string;
|
|
40
|
+
};
|
|
41
|
+
interface RouteSection {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly from: RouteEndpoint;
|
|
44
|
+
readonly to: RouteEndpoint;
|
|
45
|
+
readonly path: RoutePath;
|
|
46
|
+
/** Same ID denotes a shared trunk; render it once. */
|
|
47
|
+
readonly sharedId?: string;
|
|
48
|
+
}
|
|
49
|
+
type RoutingDiagnosticCode = "MISSING_GEOMETRY" | "MISSING_PORT" | "ROUTE_CONFLICT" | "ROUTE_BLOCKED" | "SEARCH_BUDGET" | "CONSTRAINT_VIOLATION";
|
|
50
|
+
interface RoutingDiagnostic {
|
|
51
|
+
readonly code: RoutingDiagnosticCode;
|
|
52
|
+
readonly edgeId: string;
|
|
53
|
+
readonly message: string;
|
|
54
|
+
}
|
|
55
|
+
interface Route {
|
|
56
|
+
readonly edgeId: string;
|
|
57
|
+
readonly sections: readonly RouteSection[];
|
|
58
|
+
readonly status: "routed" | "fallback";
|
|
59
|
+
readonly diagnostics: readonly RoutingDiagnostic[];
|
|
60
|
+
}
|
|
61
|
+
type RoutePatch = {
|
|
62
|
+
readonly op: "set";
|
|
63
|
+
readonly edgeId: string;
|
|
64
|
+
readonly route: Route;
|
|
65
|
+
} | {
|
|
66
|
+
readonly op: "delete";
|
|
67
|
+
readonly edgeId: string;
|
|
68
|
+
};
|
|
69
|
+
type RouteStyle = "straight" | "bezier" | "orthogonal" | "polyline" | "octilinear" | "curved" | "organic" | "parallel" | "self-loop" | "fan" | "bus" | "bundle";
|
|
70
|
+
type RouteSide = "left" | "right" | "top" | "bottom";
|
|
71
|
+
interface RouteAttachment {
|
|
72
|
+
/** Node-local attachment rectangle, independent of its outer obstacle bounds. */
|
|
73
|
+
readonly bounds: RouteBounds;
|
|
74
|
+
readonly facing?: "inward" | "outward";
|
|
75
|
+
}
|
|
76
|
+
interface EdgeRoutingSettings {
|
|
77
|
+
readonly sourceAttachment?: RouteAttachment;
|
|
78
|
+
readonly targetAttachment?: RouteAttachment;
|
|
79
|
+
readonly sourceSide?: RouteSide;
|
|
80
|
+
readonly targetSide?: RouteSide;
|
|
81
|
+
readonly waypoints?: readonly RoutePoint[];
|
|
82
|
+
/** Explicit shared routing group. Native binary edges describe branches of a net. */
|
|
83
|
+
readonly group?: string;
|
|
84
|
+
}
|
|
85
|
+
interface RoutingSettings {
|
|
86
|
+
/** Native compound geometry is parent-relative; adapters may supply resolved world geometry. */
|
|
87
|
+
readonly coordinateSpace?: "parent" | "world";
|
|
88
|
+
readonly clearance?: number;
|
|
89
|
+
readonly radius?: number;
|
|
90
|
+
readonly edgeSpacing?: number;
|
|
91
|
+
readonly bendPenalty?: number;
|
|
92
|
+
/** Soft cost per crossing; finite costs keep detours proportionate. Default 80. */
|
|
93
|
+
readonly crossingPenalty?: number;
|
|
94
|
+
/** Cost per unit of parallel overlap within edgeSpacing. Default 8. */
|
|
95
|
+
readonly overlapPenalty?: number;
|
|
96
|
+
readonly maxSearchNodes?: number;
|
|
97
|
+
readonly organicIterations?: number;
|
|
98
|
+
readonly edges?: Readonly<Record<string, EdgeRoutingSettings>>;
|
|
99
|
+
}
|
|
100
|
+
interface RoutingMetrics {
|
|
101
|
+
readonly routedEdges: number;
|
|
102
|
+
readonly reusedEdges: number;
|
|
103
|
+
readonly affectedEdges: number;
|
|
104
|
+
readonly searchNodes: number;
|
|
105
|
+
readonly obstacleCandidates: number;
|
|
106
|
+
}
|
|
107
|
+
/** Immutable value. Retain it per canvas; the private state uses structural sharing. */
|
|
108
|
+
interface RoutingSnapshot {
|
|
109
|
+
readonly strategy: string;
|
|
110
|
+
readonly revision: number;
|
|
111
|
+
readonly routes: ReadonlyMap<string, Route>;
|
|
112
|
+
readonly metrics: RoutingMetrics;
|
|
113
|
+
}
|
|
114
|
+
interface RoutingUpdate {
|
|
115
|
+
readonly snapshot: RoutingSnapshot;
|
|
116
|
+
readonly patches: readonly RoutePatch[];
|
|
117
|
+
/** Compare identity before accepting an asynchronous result. */
|
|
118
|
+
readonly base: RoutingSnapshot;
|
|
119
|
+
}
|
|
120
|
+
type RoutingGraph = Graph<unknown, unknown, unknown, unknown>;
|
|
121
|
+
interface RoutingStrategy {
|
|
122
|
+
readonly id: string;
|
|
123
|
+
route(graph: RoutingGraph, settings?: RoutingSettings): RoutingSnapshot | Promise<RoutingSnapshot>;
|
|
124
|
+
update(graph: RoutingGraph, previous: RoutingSnapshot, diff: GraphDiff<unknown, unknown>, settings?: RoutingSettings): RoutingUpdate | Promise<RoutingUpdate>;
|
|
125
|
+
}
|
|
126
|
+
interface NativeRoutingStrategy extends RoutingStrategy {
|
|
127
|
+
readonly id: RouteStyle;
|
|
128
|
+
route(graph: RoutingGraph, settings?: RoutingSettings): RoutingSnapshot;
|
|
129
|
+
update(graph: RoutingGraph, previous: RoutingSnapshot, diff: GraphDiff<unknown, unknown>, settings?: RoutingSettings): RoutingUpdate;
|
|
130
|
+
}
|
|
131
|
+
type RouteBounds = Readonly<EntityRect>;
|
|
132
|
+
//#endregion
|
|
133
|
+
export { RoutingMetrics as _, RouteBounds as a, RoutingStrategy as b, RoutePath as c, RouteSegment as d, RouteSide as f, RoutingGraph as g, RoutingDiagnosticCode as h, RouteAttachment as i, RoutePoint as l, RoutingDiagnostic as m, NativeRoutingStrategy as n, RouteEndpoint as o, RouteStyle as p, Route as r, RoutePatch as s, EdgeRoutingSettings as t, RouteSection as u, RoutingSettings as v, RoutingUpdate as x, RoutingSnapshot as y };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//#region src/elkjs/worker-client.ts
|
|
2
|
+
var PromisedWorker = class {
|
|
3
|
+
#worker;
|
|
4
|
+
#resolvers = /* @__PURE__ */ new Map();
|
|
5
|
+
#id = 0;
|
|
6
|
+
#failure;
|
|
7
|
+
constructor(worker) {
|
|
8
|
+
this.#worker = worker;
|
|
9
|
+
worker.onmessage = (event) => {
|
|
10
|
+
const resolver = this.#resolvers.get(event.data.id);
|
|
11
|
+
if (!resolver) return;
|
|
12
|
+
this.#resolvers.delete(event.data.id);
|
|
13
|
+
if (event.data.error !== void 0) resolver.reject(event.data.error);
|
|
14
|
+
else resolver.resolve(event.data.data);
|
|
15
|
+
};
|
|
16
|
+
worker.onerror = (event) => this.#rejectAll(event.error ?? event.message);
|
|
17
|
+
worker.onmessageerror = (event) => this.#rejectAll(event.data);
|
|
18
|
+
}
|
|
19
|
+
postMessage(message) {
|
|
20
|
+
if (this.#failure) return Promise.reject(this.#failure);
|
|
21
|
+
const id = this.#id++;
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
this.#resolvers.set(id, {
|
|
24
|
+
resolve,
|
|
25
|
+
reject
|
|
26
|
+
});
|
|
27
|
+
this.#worker.postMessage({
|
|
28
|
+
...message,
|
|
29
|
+
id
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
terminate() {
|
|
34
|
+
this.#worker.terminate();
|
|
35
|
+
}
|
|
36
|
+
#rejectAll(reason) {
|
|
37
|
+
const error = reason instanceof Error ? reason : new Error(String(reason ?? "Worker failed"));
|
|
38
|
+
this.#failure = error;
|
|
39
|
+
for (const resolver of this.#resolvers.values()) resolver.reject(error);
|
|
40
|
+
this.#resolvers.clear();
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var WorkerELK = class {
|
|
44
|
+
#defaultLayoutOptions;
|
|
45
|
+
#worker;
|
|
46
|
+
#ready;
|
|
47
|
+
constructor({ defaultLayoutOptions = {}, algorithms, workerFactory, workerUrl } = {}) {
|
|
48
|
+
if (workerUrl === void 0 && workerFactory === void 0) throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");
|
|
49
|
+
const worker = (workerFactory ?? ((url) => new globalThis.Worker(url)))(workerUrl);
|
|
50
|
+
if (!worker || typeof worker.postMessage !== "function") throw new TypeError("Created worker does not provide the required 'postMessage' function.");
|
|
51
|
+
this.#defaultLayoutOptions = defaultLayoutOptions;
|
|
52
|
+
this.#worker = new PromisedWorker(worker);
|
|
53
|
+
this.#ready = this.#worker.postMessage({
|
|
54
|
+
cmd: "register",
|
|
55
|
+
algorithms
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async layout(graph, args = {}) {
|
|
59
|
+
if (!graph) throw new Error("Missing mandatory parameter 'graph'.");
|
|
60
|
+
await this.#ready;
|
|
61
|
+
return this.#worker.postMessage({
|
|
62
|
+
cmd: "layout",
|
|
63
|
+
graph,
|
|
64
|
+
layoutOptions: {
|
|
65
|
+
...this.#defaultLayoutOptions,
|
|
66
|
+
...args.layoutOptions
|
|
67
|
+
},
|
|
68
|
+
options: {
|
|
69
|
+
logging: args.logging ?? false,
|
|
70
|
+
measureExecutionTime: args.measureExecutionTime ?? false
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async knownLayoutAlgorithms() {
|
|
75
|
+
await this.#ready;
|
|
76
|
+
return this.#worker.postMessage({ cmd: "algorithms" });
|
|
77
|
+
}
|
|
78
|
+
async knownLayoutOptions() {
|
|
79
|
+
await this.#ready;
|
|
80
|
+
return this.#worker.postMessage({ cmd: "options" });
|
|
81
|
+
}
|
|
82
|
+
async knownLayoutCategories() {
|
|
83
|
+
await this.#ready;
|
|
84
|
+
return this.#worker.postMessage({ cmd: "categories" });
|
|
85
|
+
}
|
|
86
|
+
terminateWorker() {
|
|
87
|
+
this.#worker.terminate();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
export { WorkerELK as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { d as ElkLayoutCategoryDescription, f as ElkLayoutOptionDescription, l as ElkLayoutAlgorithmDescription, n as ELKConstructorArguments, p as ElkNode, t as ELK, u as ElkLayoutArguments, v as LaidOutElkNode } from "./public-types-9NCPoqbp.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/elkjs/worker-client.d.ts
|
|
4
|
+
declare class WorkerELK implements ELK {
|
|
5
|
+
#private;
|
|
6
|
+
constructor({
|
|
7
|
+
defaultLayoutOptions,
|
|
8
|
+
algorithms,
|
|
9
|
+
workerFactory,
|
|
10
|
+
workerUrl
|
|
11
|
+
}?: ELKConstructorArguments);
|
|
12
|
+
layout<T extends ElkNode>(graph: T, args?: ElkLayoutArguments): Promise<LaidOutElkNode<T>>;
|
|
13
|
+
knownLayoutAlgorithms(): Promise<ElkLayoutAlgorithmDescription[]>;
|
|
14
|
+
knownLayoutOptions(): Promise<ElkLayoutOptionDescription[]>;
|
|
15
|
+
knownLayoutCategories(): Promise<ElkLayoutCategoryDescription[]>;
|
|
16
|
+
terminateWorker(): void;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { WorkerELK as t };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/elkjs/worker-client.ts
|
|
3
|
+
var PromisedWorker = class {
|
|
4
|
+
#worker;
|
|
5
|
+
#resolvers = /* @__PURE__ */ new Map();
|
|
6
|
+
#id = 0;
|
|
7
|
+
#failure;
|
|
8
|
+
constructor(worker) {
|
|
9
|
+
this.#worker = worker;
|
|
10
|
+
worker.onmessage = (event) => {
|
|
11
|
+
const resolver = this.#resolvers.get(event.data.id);
|
|
12
|
+
if (!resolver) return;
|
|
13
|
+
this.#resolvers.delete(event.data.id);
|
|
14
|
+
if (event.data.error !== void 0) resolver.reject(event.data.error);
|
|
15
|
+
else resolver.resolve(event.data.data);
|
|
16
|
+
};
|
|
17
|
+
worker.onerror = (event) => this.#rejectAll(event.error ?? event.message);
|
|
18
|
+
worker.onmessageerror = (event) => this.#rejectAll(event.data);
|
|
19
|
+
}
|
|
20
|
+
postMessage(message) {
|
|
21
|
+
if (this.#failure) return Promise.reject(this.#failure);
|
|
22
|
+
const id = this.#id++;
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
this.#resolvers.set(id, {
|
|
25
|
+
resolve,
|
|
26
|
+
reject
|
|
27
|
+
});
|
|
28
|
+
this.#worker.postMessage({
|
|
29
|
+
...message,
|
|
30
|
+
id
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
terminate() {
|
|
35
|
+
this.#worker.terminate();
|
|
36
|
+
}
|
|
37
|
+
#rejectAll(reason) {
|
|
38
|
+
const error = reason instanceof Error ? reason : new Error(String(reason ?? "Worker failed"));
|
|
39
|
+
this.#failure = error;
|
|
40
|
+
for (const resolver of this.#resolvers.values()) resolver.reject(error);
|
|
41
|
+
this.#resolvers.clear();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var WorkerELK = class {
|
|
45
|
+
#defaultLayoutOptions;
|
|
46
|
+
#worker;
|
|
47
|
+
#ready;
|
|
48
|
+
constructor({ defaultLayoutOptions = {}, algorithms, workerFactory, workerUrl } = {}) {
|
|
49
|
+
if (workerUrl === void 0 && workerFactory === void 0) throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");
|
|
50
|
+
const worker = (workerFactory ?? ((url) => new globalThis.Worker(url)))(workerUrl);
|
|
51
|
+
if (!worker || typeof worker.postMessage !== "function") throw new TypeError("Created worker does not provide the required 'postMessage' function.");
|
|
52
|
+
this.#defaultLayoutOptions = defaultLayoutOptions;
|
|
53
|
+
this.#worker = new PromisedWorker(worker);
|
|
54
|
+
this.#ready = this.#worker.postMessage({
|
|
55
|
+
cmd: "register",
|
|
56
|
+
algorithms
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async layout(graph, args = {}) {
|
|
60
|
+
if (!graph) throw new Error("Missing mandatory parameter 'graph'.");
|
|
61
|
+
await this.#ready;
|
|
62
|
+
return this.#worker.postMessage({
|
|
63
|
+
cmd: "layout",
|
|
64
|
+
graph,
|
|
65
|
+
layoutOptions: {
|
|
66
|
+
...this.#defaultLayoutOptions,
|
|
67
|
+
...args.layoutOptions
|
|
68
|
+
},
|
|
69
|
+
options: {
|
|
70
|
+
logging: args.logging ?? false,
|
|
71
|
+
measureExecutionTime: args.measureExecutionTime ?? false
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async knownLayoutAlgorithms() {
|
|
76
|
+
await this.#ready;
|
|
77
|
+
return this.#worker.postMessage({ cmd: "algorithms" });
|
|
78
|
+
}
|
|
79
|
+
async knownLayoutOptions() {
|
|
80
|
+
await this.#ready;
|
|
81
|
+
return this.#worker.postMessage({ cmd: "options" });
|
|
82
|
+
}
|
|
83
|
+
async knownLayoutCategories() {
|
|
84
|
+
await this.#ready;
|
|
85
|
+
return this.#worker.postMessage({ cmd: "categories" });
|
|
86
|
+
}
|
|
87
|
+
terminateWorker() {
|
|
88
|
+
this.#worker.terminate();
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
Object.defineProperty(exports, 'WorkerELK', {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
get: function () {
|
|
96
|
+
return WorkerELK;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { d as ElkLayoutCategoryDescription, f as ElkLayoutOptionDescription, l as ElkLayoutAlgorithmDescription, n as ELKConstructorArguments, p as ElkNode, t as ELK, u as ElkLayoutArguments, v as LaidOutElkNode } from "./public-types-BcRZoJE3.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/elkjs/worker-client.d.ts
|
|
4
|
+
declare class WorkerELK implements ELK {
|
|
5
|
+
#private;
|
|
6
|
+
constructor({
|
|
7
|
+
defaultLayoutOptions,
|
|
8
|
+
algorithms,
|
|
9
|
+
workerFactory,
|
|
10
|
+
workerUrl
|
|
11
|
+
}?: ELKConstructorArguments);
|
|
12
|
+
layout<T extends ElkNode>(graph: T, args?: ElkLayoutArguments): Promise<LaidOutElkNode<T>>;
|
|
13
|
+
knownLayoutAlgorithms(): Promise<ElkLayoutAlgorithmDescription[]>;
|
|
14
|
+
knownLayoutOptions(): Promise<ElkLayoutOptionDescription[]>;
|
|
15
|
+
knownLayoutCategories(): Promise<ElkLayoutCategoryDescription[]>;
|
|
16
|
+
terminateWorker(): void;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { WorkerELK as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/layout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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,32 +15,62 @@
|
|
|
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
|
-
"./package.json": "./package.json"
|
|
62
|
+
"./package.json": "./package.json",
|
|
63
|
+
"./routing": {
|
|
64
|
+
"types": "./src/routing/index.ts",
|
|
65
|
+
"import": "./dist/routing/index.mjs",
|
|
66
|
+
"require": "./dist/routing/index.cjs"
|
|
67
|
+
}
|
|
39
68
|
},
|
|
40
69
|
"publishConfig": {
|
|
41
70
|
"access": "public"
|
|
42
71
|
},
|
|
43
72
|
"dependencies": {
|
|
73
|
+
"@lume/kiwi": "^0.4.4",
|
|
44
74
|
"@statelyai/graph": "^2.1.0"
|
|
45
75
|
},
|
|
46
76
|
"devDependencies": {
|
|
@@ -48,6 +78,7 @@
|
|
|
48
78
|
"@codemirror/lang-javascript": "^6.2.5",
|
|
49
79
|
"@codemirror/lint": "^6.9.7",
|
|
50
80
|
"@statelyai/layout-pre-exterior-label-separation": "npm:@statelyai/layout@0.0.4",
|
|
81
|
+
"@storybook/react-vite": "^10.6.0",
|
|
51
82
|
"@types/node": "^25.0.3",
|
|
52
83
|
"@types/react": "^19.2.18",
|
|
53
84
|
"@types/react-dom": "^19.2.4",
|
|
@@ -63,6 +94,7 @@
|
|
|
63
94
|
"react": "^19.2.8",
|
|
64
95
|
"react-dom": "^19.2.8",
|
|
65
96
|
"react-resizable-panels": "^4.12.2",
|
|
97
|
+
"storybook": "^10.6.0",
|
|
66
98
|
"tsdown": "^0.18.1",
|
|
67
99
|
"tsx": "^4.22.4",
|
|
68
100
|
"typescript": "^5.9.3",
|
|
@@ -93,7 +125,10 @@
|
|
|
93
125
|
"test": "vitest",
|
|
94
126
|
"typecheck": "tsc --noEmit",
|
|
95
127
|
"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"
|
|
128
|
+
"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",
|
|
129
|
+
"validate:package": "publint --pack npm && tsx scripts/smoke-package.ts",
|
|
130
|
+
"storybook": "storybook dev -p 6018 --host 127.0.0.1 --no-open",
|
|
131
|
+
"storybook:build": "storybook build -o dist-storybook",
|
|
132
|
+
"storybook:typecheck": "tsc --project stories/tsconfig.json"
|
|
98
133
|
}
|
|
99
134
|
}
|