@xenolithengine/graph-plugin-autolayout 0.7.0-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 strelok2012
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @xenolithengine/graph-plugin-autolayout
2
+
3
+ [![BETA](https://img.shields.io/badge/status-BETA-FCB400?style=flat-square)](https://github.com/XenolithEngine/xenolith-graph#status)
4
+ [![MIT](https://img.shields.io/badge/license-MIT-FCB400?style=flat-square)](https://github.com/XenolithEngine/xenolith-graph/blob/main/LICENSE)
5
+
6
+ Auto-layout plugin for XenolithGraph. Engine-agnostic: bring your own layout backend (dagre / elkjs / custom) via the `LayoutEngine` interface.
7
+
8
+ > **Beta** — public API in `STABLE-API.md` is the surface we plan to freeze, but it is **NOT frozen yet** — breaking changes can land at any point before v1.0. If you adopt now, pin an exact version.
9
+
10
+ Part of [XenolithGraph](https://github.com/XenolithEngine/xenolith-graph) — an AI-native, embeddable node-graph editor for the web with its own visual design language (Xen).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pnpm add @xenolithengine/graph-plugin-autolayout dagre
16
+ # or
17
+ pnpm add @xenolithengine/graph-plugin-autolayout elkjs
18
+ ```
19
+
20
+ Optional peer deps: `dagre@^0.8.5` (lighter, layered DAGs) **or** `elkjs@^0.9 || ^0.10 || ^0.11` (nested layouts, orthogonal routing, 9 algorithms). Install whichever backend you reach for; both engines lazy-load.
21
+
22
+ ## Usage
23
+
24
+ ```ts
25
+ import { autoLayoutPlugin } from '@xenolithengine/graph-plugin-autolayout'
26
+ import { dagreEngine } from '@xenolithengine/graph-plugin-autolayout/dagre'
27
+
28
+ const layout = autoLayoutPlugin({ engine: dagreEngine({ rankdir: 'LR' }) })
29
+ editor.use(layout)
30
+
31
+ await layout.arrange({ direction: 'LR', animate: { durationMs: 280 } })
32
+ ```
33
+
34
+ ELK variant — same shape:
35
+
36
+ ```ts
37
+ import { elkEngine } from '@xenolithengine/graph-plugin-autolayout/elk'
38
+
39
+ const layout = autoLayoutPlugin({ engine: elkEngine({ algorithm: 'layered' }) })
40
+ editor.use(layout)
41
+ await layout.arrange()
42
+ ```
43
+
44
+ `arrange()` commits all moves in a single undo step. Optional animated tween bypasses the command bus per frame and commits the final positions once.
45
+
46
+ ## What's exported
47
+
48
+ - `autoLayoutPlugin(config)` → `AutoLayoutPlugin` with `arrange(opts?)`
49
+ - `AutoLayoutConfig`, `AutoLayoutPlugin`
50
+ - `LayoutEngine`, `LayoutGraph`, `LayoutNode`, `LayoutEdge`, `LayoutOpts`, `LayoutResult`
51
+ - Subpath `@xenolithengine/graph-plugin-autolayout/dagre` → `dagreEngine`, `DagreEngineOpts`
52
+ - Subpath `@xenolithengine/graph-plugin-autolayout/elk` → `elkEngine`, `ElkEngineOpts`
53
+
54
+ ## Docs
55
+
56
+ - [API reference](https://xenolithengine.github.io/xenolith-graph/guides/api/) — every method exposed by `XenolithEditor`
57
+ - [GitHub](https://github.com/XenolithEngine/xenolith-graph)
58
+
59
+ MIT © XenolithEngine
@@ -0,0 +1,9 @@
1
+ import type { LayoutEngine } from '../engine.js';
2
+ /** Dagre adapter — fast, ~30 kB gzipped, single algorithm (Sugiyama layered). Good default for
3
+ * DAG-shaped graphs (ComfyUI, LLM flow, blueprint, DSP). Ignores port hints and hierarchy. */
4
+ export interface DagreEngineOpts {
5
+ /** Overrides applied to the dagre `setGraph()` call. See dagre docs for the full token list. */
6
+ graphOverrides?: Record<string, unknown>;
7
+ }
8
+ export declare function dagreEngine(opts?: DagreEngineOpts): LayoutEngine;
9
+ //# sourceMappingURL=dagre.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dagre.d.ts","sourceRoot":"","sources":["../../src/adapters/dagre.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,cAAc,CAAA;AA8B5D;+FAC+F;AAC/F,MAAM,WAAW,eAAe;IAC9B,gGAAgG;IAChG,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACzC;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,eAAoB,GAAG,YAAY,CAiCpE"}
@@ -0,0 +1,54 @@
1
+ let _dagre;
2
+ async function loadDagre() {
3
+ if (_dagre)
4
+ return _dagre;
5
+ try {
6
+ const mod = await import('dagre');
7
+ _dagre = (mod.default ?? mod);
8
+ return _dagre;
9
+ }
10
+ catch {
11
+ throw new Error('@xenolithengine/graph-plugin-autolayout/dagre: peer dependency `dagre` is not installed. Run `pnpm add dagre`.');
12
+ }
13
+ }
14
+ function mapDirection(d) {
15
+ return d ?? 'LR';
16
+ }
17
+ export function dagreEngine(opts = {}) {
18
+ return {
19
+ name: 'dagre',
20
+ async layout(g, layoutOpts) {
21
+ const dagre = await loadDagre();
22
+ const dg = new dagre.graphlib.Graph();
23
+ dg.setGraph({
24
+ rankdir: mapDirection(layoutOpts.direction),
25
+ nodesep: layoutOpts.spacing?.node ?? 50,
26
+ ranksep: layoutOpts.spacing?.layer ?? 80,
27
+ edgesep: layoutOpts.spacing?.edge ?? 20,
28
+ align: layoutOpts.align,
29
+ marginx: 0,
30
+ marginy: 0,
31
+ ...opts.graphOverrides,
32
+ });
33
+ dg.setDefaultEdgeLabel(() => ({}));
34
+ for (const n of g.nodes)
35
+ dg.setNode(n.id, { width: n.width, height: n.height });
36
+ for (const e of g.edges)
37
+ dg.setEdge(e.from.node, e.to.node);
38
+ dagre.layout(dg);
39
+ // dagre returns the CENTRE of each node; we want top-left to feed MoveNode.
40
+ const positions = new Map();
41
+ for (const n of g.nodes) {
42
+ const placed = dg.node(n.id);
43
+ if (!placed)
44
+ continue;
45
+ positions.set(n.id, {
46
+ x: placed.x - n.width / 2,
47
+ y: placed.y - n.height / 2,
48
+ });
49
+ }
50
+ return { positions };
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=dagre.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dagre.js","sourceRoot":"","sources":["../../src/adapters/dagre.ts"],"names":[],"mappings":"AAgBA,IAAI,MAA+B,CAAA;AACnC,KAAK,UAAU,SAAS;IACtB,IAAI,MAAM;QAAE,OAAO,MAAM,CAAA;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAiB,CAAC,CAAA;QAC3C,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAgB,CAAA;QAC5C,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gHAAgH,CAAC,CAAA;IACnI,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,CAA0B;IAC9C,OAAO,CAAC,IAAI,IAAI,CAAA;AAClB,CAAC;AASD,MAAM,UAAU,WAAW,CAAC,OAAwB,EAAE;IACpD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU;YACxB,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAA;YAC/B,MAAM,EAAE,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;YACrC,EAAE,CAAC,QAAQ,CAAC;gBACV,OAAO,EAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC;gBAC5C,OAAO,EAAG,UAAU,CAAC,OAAO,EAAE,IAAI,IAAK,EAAE;gBACzC,OAAO,EAAG,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;gBACzC,OAAO,EAAG,UAAU,CAAC,OAAO,EAAE,IAAI,IAAK,EAAE;gBACzC,KAAK,EAAK,UAAU,CAAC,KAAK;gBAC1B,OAAO,EAAG,CAAC;gBACX,OAAO,EAAG,CAAC;gBACX,GAAG,IAAI,CAAC,cAAc;aACvB,CAAC,CAAA;YACF,EAAE,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YAClC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;gBAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;YAC/E,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;gBAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;YAC3D,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YAChB,4EAA4E;YAC5E,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoC,CAAA;YAC7D,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACxB,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;gBAC5B,IAAI,CAAC,MAAM;oBAAE,SAAQ;gBACrB,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBAClB,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;oBACzB,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;iBAC3B,CAAC,CAAA;YACJ,CAAC;YACD,OAAO,EAAE,SAAS,EAAE,CAAA;QACtB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { LayoutEngine } from '../engine.js';
2
+ export interface ElkEngineOpts {
3
+ /** ELK algorithm. Default `'layered'` (Sugiyama — what dagre does, plus orthogonal routing).
4
+ * Others: `'mrtree'`, `'stress'`, `'force'`, `'radial'`, `'disco'`, `'sporeOverlap'`,
5
+ * `'sporeCompaction'`, `'rectpacking'`. See https://eclipse.dev/elk/reference/algorithms.html. */
6
+ algorithm?: 'layered' | 'mrtree' | 'stress' | 'force' | 'radial' | 'disco';
7
+ /** Merge these into ELK's `layoutOptions` after our defaults — last write wins.
8
+ * Example: `{ 'elk.layered.spacing.edgeNodeBetweenLayers': '40' }`. */
9
+ layoutOverrides?: Record<string, string>;
10
+ }
11
+ export declare function elkEngine(opts?: ElkEngineOpts): LayoutEngine;
12
+ //# sourceMappingURL=elk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elk.d.ts","sourceRoot":"","sources":["../../src/adapters/elk.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAqD,MAAM,cAAc,CAAA;AAqCnG,MAAM,WAAW,aAAa;IAC5B;;uGAEmG;IACnG,SAAS,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;IAC1E;4EACwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzC;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,aAAkB,GAAG,YAAY,CAyDhE"}
@@ -0,0 +1,85 @@
1
+ let _ElkCtor;
2
+ async function loadElk() {
3
+ if (_ElkCtor)
4
+ return _ElkCtor;
5
+ try {
6
+ const mod = await import('elkjs/lib/elk.bundled.js');
7
+ _ElkCtor = (mod.default ?? mod);
8
+ return _ElkCtor;
9
+ }
10
+ catch {
11
+ throw new Error('@xenolithengine/graph-plugin-autolayout/elk: peer dependency `elkjs` is not installed. Run `pnpm add elkjs`.');
12
+ }
13
+ }
14
+ function mapDirection(d) {
15
+ switch (d ?? 'LR') {
16
+ case 'LR': return 'RIGHT';
17
+ case 'RL': return 'LEFT';
18
+ case 'TB': return 'DOWN';
19
+ case 'BT': return 'UP';
20
+ }
21
+ }
22
+ export function elkEngine(opts = {}) {
23
+ const algorithm = opts.algorithm ?? 'layered';
24
+ return {
25
+ name: `elk:${algorithm}`,
26
+ async layout(g, layoutOpts) {
27
+ const Elk = await loadElk();
28
+ const elk = new Elk();
29
+ const layoutOptions = {
30
+ 'elk.algorithm': algorithm,
31
+ 'elk.direction': mapDirection(layoutOpts.direction),
32
+ 'elk.spacing.nodeNode': String(layoutOpts.spacing?.node ?? 50),
33
+ 'elk.layered.spacing.nodeNodeBetweenLayers': String(layoutOpts.spacing?.layer ?? 80),
34
+ 'elk.spacing.edgeNode': String(layoutOpts.spacing?.edge ?? 20),
35
+ 'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
36
+ ...(opts.layoutOverrides ?? {}),
37
+ };
38
+ // Build nested ELK tree from flat (id, parent?) list. Roots: nodes without a parent we know
39
+ // about; children: bucket by their parent id. Each ElkNode declares only its OWN width/height
40
+ // — ELK computes parent sizes from contained children.
41
+ const byId = new Map(g.nodes.map((n) => [n.id, n]));
42
+ const childrenOf = new Map();
43
+ const roots = [];
44
+ for (const n of g.nodes) {
45
+ const parent = n.parent && byId.has(n.parent) ? n.parent : undefined;
46
+ if (parent) {
47
+ (childrenOf.get(parent) ?? (childrenOf.set(parent, []), childrenOf.get(parent))).push(n);
48
+ }
49
+ else
50
+ roots.push(n);
51
+ }
52
+ const toElk = (n) => {
53
+ const kids = childrenOf.get(n.id) ?? [];
54
+ return {
55
+ id: n.id,
56
+ width: n.width,
57
+ height: n.height,
58
+ ...(kids.length > 0 ? { children: kids.map(toElk) } : {}),
59
+ };
60
+ };
61
+ const root = {
62
+ id: '$root',
63
+ children: roots.map(toElk),
64
+ edges: g.edges.map((e) => ({ id: e.id, sources: [e.from.node], targets: [e.to.node] })),
65
+ layoutOptions,
66
+ };
67
+ const laid = await elk.layout(root);
68
+ // Walk the laid-out tree and flatten ABSOLUTE positions. ELK reports each node's position
69
+ // relative to its parent, so accumulate offsets while descending. (top-left convention,
70
+ // matches dagre's normalised output — `MoveNode` consumes top-left.)
71
+ const positions = new Map();
72
+ const walk = (n, ox, oy) => {
73
+ const ax = ox + (n.x ?? 0);
74
+ const ay = oy + (n.y ?? 0);
75
+ if (n.id !== '$root')
76
+ positions.set(n.id, { x: ax, y: ay });
77
+ for (const c of n.children ?? [])
78
+ walk(c, ax, ay);
79
+ };
80
+ walk(laid, 0, 0);
81
+ return { positions };
82
+ },
83
+ };
84
+ }
85
+ //# sourceMappingURL=elk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elk.js","sourceRoot":"","sources":["../../src/adapters/elk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Graph } from '@xenolithengine/graph-core';
2
+ import type { LayoutGraph } from './engine.js';
3
+ /** Pull an engine-neutral `LayoutGraph` out of the editor's live graph. Pure — never mutates.
4
+ * Nodes without an explicit `size` get a sane default so dagre/elk don't pack overlapping rects.
5
+ * Macro membership is surfaced as `node.parent` so hierarchical engines (ELK) lay out children
6
+ * inside their parent; flat engines (dagre) just ignore the field. No coordinate data is
7
+ * included — that's the engine's output, not its input. */
8
+ export declare function buildLayoutGraph(graph: Graph): LayoutGraph;
9
+ //# sourceMappingURL=build-layout-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-layout-graph.d.ts","sourceRoot":"","sources":["../src/build-layout-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAsB,MAAM,4BAA4B,CAAA;AAE3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C;;;;4DAI4D;AAC5D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAyB1D"}
@@ -0,0 +1,37 @@
1
+ import { isMacro, macroMembers } from '@xenolithengine/graph-core';
2
+ /** Pull an engine-neutral `LayoutGraph` out of the editor's live graph. Pure — never mutates.
3
+ * Nodes without an explicit `size` get a sane default so dagre/elk don't pack overlapping rects.
4
+ * Macro membership is surfaced as `node.parent` so hierarchical engines (ELK) lay out children
5
+ * inside their parent; flat engines (dagre) just ignore the field. No coordinate data is
6
+ * included — that's the engine's output, not its input. */
7
+ export function buildLayoutGraph(graph) {
8
+ const nodes = [...graph.nodes()];
9
+ const edges = [...graph.edges()];
10
+ const parentOf = new Map();
11
+ for (const n of nodes) {
12
+ if (!isMacro(n))
13
+ continue;
14
+ for (const m of macroMembers(n))
15
+ parentOf.set(String(m), String(n.id));
16
+ }
17
+ return {
18
+ nodes: nodes.map((n) => {
19
+ const parent = parentOf.get(String(n.id));
20
+ return {
21
+ id: String(n.id),
22
+ width: Math.max(1, n.size?.x ?? DEFAULT_W),
23
+ height: Math.max(1, n.size?.y ?? DEFAULT_H),
24
+ ...(parent !== undefined ? { parent } : {}),
25
+ ports: n.pins.map((p) => ({ id: String(p.id), side: p.direction === 'in' ? 'in' : 'out' })),
26
+ };
27
+ }),
28
+ edges: edges.map((e) => ({
29
+ id: String(e.id),
30
+ from: { node: String(e.from.node), port: String(e.from.pin) },
31
+ to: { node: String(e.to.node), port: String(e.to.pin) },
32
+ })),
33
+ };
34
+ }
35
+ const DEFAULT_W = 180;
36
+ const DEFAULT_H = 80;
37
+ //# sourceMappingURL=build-layout-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-layout-graph.js","sourceRoot":"","sources":["../src/build-layout-graph.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAGlE;;;;4DAI4D;AAC5D,MAAM,UAAU,gBAAgB,CAAC,KAAY;IAC3C,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,CAAW,CAAA;IAC1C,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,CAAW,CAAA;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAA;IAC1C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,SAAQ;QACzB,KAAK,MAAM,CAAC,IAAI,YAAY,CAAC,CAAC,CAAa;YAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACpF,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACrB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACzC,OAAO;gBACL,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChB,KAAK,EAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,SAAS,CAAC;gBAC3C,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,SAAS,CAAC;gBAC3C,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAc,EAAE,CAAC,CAAC;aACrG,CAAA;QACH,CAAC,CAAC;QACF,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YAC7D,EAAE,EAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,EAAI,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAI;SAC9D,CAAC,CAAC;KACJ,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,GAAG,CAAA;AACrB,MAAM,SAAS,GAAG,EAAE,CAAA"}
@@ -0,0 +1,77 @@
1
+ /** The contract every layout backend (dagre / elkjs / WebCola / custom) must implement. Pure: a
2
+ * function from the structural sub-graph the plugin extracts to a per-node position map. The
3
+ * plugin handles everything else — pulling the snapshot, applying positions through MoveNode in
4
+ * one transaction, undo, events. Adapters live next door (`/dagre`, `/elk`) and are ~50 lines each.
5
+ * Swap engine without touching the plugin or the editor. */
6
+ export interface LayoutEngine {
7
+ /** Human-readable backend tag, e.g. `'dagre'`, `'elk:layered'`. Surfaced in logs / UI. */
8
+ readonly name: string;
9
+ /** Compute final positions for every node in `graph`. Async because some engines (ELK) are
10
+ * worker-backed. May honour `opts.signal` to bail out on cancellation. */
11
+ layout(graph: LayoutGraph, opts: LayoutOpts): Promise<LayoutResult>;
12
+ }
13
+ /** Engine-neutral structural view of the graph. Coordinates intentionally absent — that's the
14
+ * engine's job. Per-node `parent` lets nested layouts (macros, comments) inform engines that
15
+ * understand hierarchy (ELK); engines that don't (dagre) just ignore it. */
16
+ export interface LayoutGraph {
17
+ nodes: ReadonlyArray<LayoutNode>;
18
+ edges: ReadonlyArray<LayoutEdge>;
19
+ }
20
+ export interface LayoutNode {
21
+ id: string;
22
+ width: number;
23
+ height: number;
24
+ /** Containment for hierarchical engines. Omit for flat layout. */
25
+ parent?: string;
26
+ /** Optional port hints — engines that route per-port (ELK) use them; dagre ignores. */
27
+ ports?: ReadonlyArray<{
28
+ id: string;
29
+ side: 'in' | 'out';
30
+ }>;
31
+ }
32
+ export interface LayoutEdge {
33
+ id: string;
34
+ from: {
35
+ node: string;
36
+ port?: string;
37
+ };
38
+ to: {
39
+ node: string;
40
+ port?: string;
41
+ };
42
+ }
43
+ export interface LayoutAnimateOpts {
44
+ /** Total animation duration in milliseconds. Skip / set to 0 for an instant move. */
45
+ durationMs?: number;
46
+ /** Easing function `t ∈ [0,1] → eased ∈ [0,1]`. Default: cubic ease-in-out. */
47
+ easing?: (t: number) => number;
48
+ }
49
+ export interface LayoutOpts {
50
+ /** Flow direction. Default `'LR'` (left-to-right) — matches DAG editors. */
51
+ direction?: 'LR' | 'RL' | 'TB' | 'BT';
52
+ spacing?: {
53
+ /** Min gap between sibling nodes (same layer / branch). Default 50. */
54
+ node?: number;
55
+ /** Min gap between layers / rows. Default 80. */
56
+ layer?: number;
57
+ /** Min gap between routed edges (engines that route). Default 20. */
58
+ edge?: number;
59
+ };
60
+ /** Alignment within a layer for engines that support it (dagre). */
61
+ align?: 'UL' | 'UR' | 'DL' | 'DR' | 'center';
62
+ /** Cancel a long-running layout (ELK on big nested graphs can take seconds). */
63
+ signal?: AbortSignal;
64
+ /** When set, tween from current positions to the target over `durationMs` instead of snapping.
65
+ * Each per-frame step is committed ephemerally (no undo entry per frame); only the FINAL
66
+ * positions land as one MoveNode-per-node transaction (still ONE undo for the whole arrange).
67
+ * Rete `TransitionApplier` parity. */
68
+ animate?: LayoutAnimateOpts;
69
+ }
70
+ export interface LayoutResult {
71
+ /** Top-left position per node id. The plugin translates these into MoveNode commands. */
72
+ positions: ReadonlyMap<string, {
73
+ x: number;
74
+ y: number;
75
+ }>;
76
+ }
77
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;6DAI6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB;+EAC2E;IAC3E,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACpE;AAED;;6EAE6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;IAChC,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,uFAAuF;IACvF,KAAK,CAAC,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,GAAG,KAAK,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACrC,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACrC,OAAO,CAAC,EAAE;QACR,uEAAuE;QACvE,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,iDAAiD;QACjD,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,qEAAqE;QACrE,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;IACD,oEAAoE;IACpE,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAA;IAC5C,gFAAgF;IAChF,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;2CAGuC;IACvC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAC5B;AAED,MAAM,WAAW,YAAY;IAC3B,yFAAyF;IACzF,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACzD"}
package/dist/engine.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import type { XenolithPlugin } from '@xenolithengine/graph-editor';
2
+ import type { LayoutEngine, LayoutOpts, LayoutResult } from './engine.js';
3
+ export type { LayoutEngine, LayoutGraph, LayoutNode, LayoutEdge, LayoutOpts, LayoutResult, } from './engine.js';
4
+ export interface AutoLayoutConfig {
5
+ engine: LayoutEngine;
6
+ /** Override defaults applied when `arrange()` is called without explicit opts. */
7
+ defaults?: LayoutOpts;
8
+ }
9
+ /** The plugin object returned by `autoLayoutPlugin()`. Keep the reference handy — `arrange()` is
10
+ * the method you call to do the work. Multiple plugin instances can coexist on one editor under
11
+ * different `name`s (e.g. one configured for LR / DAG, another for TB / mind-map). */
12
+ export interface AutoLayoutPlugin extends XenolithPlugin {
13
+ /** Run the configured engine against the current graph and apply the resulting positions in a
14
+ * single transaction (= one undo step). Throws if the plugin wasn't installed yet, or if the
15
+ * engine rejects (e.g. AbortSignal fired). Resolves with the raw `LayoutResult` so the caller
16
+ * can also inspect edge routes when the engine returns them. */
17
+ arrange(opts?: LayoutOpts): Promise<LayoutResult>;
18
+ }
19
+ export declare function autoLayoutPlugin(config: AutoLayoutConfig & {
20
+ name?: string;
21
+ }): AutoLayoutPlugin;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,8BAA8B,CAAA;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAGzE,YAAY,EACV,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,GAC5E,MAAM,aAAa,CAAA;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAA;IACpB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,UAAU,CAAA;CACtB;AAED;;uFAEuF;AACvF,MAAM,WAAW,gBAAiB,SAAQ,cAAc;IACtD;;;qEAGiE;IACjE,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAClD;AAID,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAuC/F"}
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ import { MoveNode } from '@xenolithengine/graph-core';
2
+ import { buildLayoutGraph } from './build-layout-graph.js';
3
+ const DEFAULT_NAME = 'autolayout';
4
+ export function autoLayoutPlugin(config) {
5
+ let ctx = null;
6
+ return {
7
+ name: config.name ?? DEFAULT_NAME,
8
+ install(c) {
9
+ ctx = c;
10
+ return () => { ctx = null; };
11
+ },
12
+ async arrange(opts) {
13
+ if (!ctx)
14
+ throw new Error('autolayout: arrange() called before editor.use(plugin)');
15
+ const merged = { ...(config.defaults ?? {}), ...(opts ?? {}) };
16
+ const layoutGraph = buildLayoutGraph(ctx.graph);
17
+ if (layoutGraph.nodes.length === 0)
18
+ return { positions: new Map() };
19
+ const result = await config.engine.layout(layoutGraph, merged);
20
+ if (result.positions.size === 0)
21
+ return result;
22
+ // Capture START positions BEFORE we commit the final ones. Used by the animation path AND
23
+ // by the no-animation path (latter needs them to skip identity moves).
24
+ const starts = new Map();
25
+ for (const id of result.positions.keys()) {
26
+ const live = ctx.graph.getNode(id);
27
+ if (live)
28
+ starts.set(id, { x: live.position.x, y: live.position.y });
29
+ }
30
+ const animateMs = merged.animate?.durationMs ?? 0;
31
+ if (animateMs > 0)
32
+ await tweenPositions(ctx, starts, result.positions, animateMs, merged.animate?.easing ?? easeInOutCubic);
33
+ ctx.commandBus.transaction(() => {
34
+ for (const [id, pos] of result.positions) {
35
+ const live = ctx.graph.getNode(id);
36
+ if (!live)
37
+ continue;
38
+ const s = starts.get(id);
39
+ if (s && s.x === pos.x && s.y === pos.y)
40
+ continue;
41
+ ctx.commandBus.apply(new MoveNode(id, { x: pos.x, y: pos.y }));
42
+ }
43
+ });
44
+ return result;
45
+ },
46
+ };
47
+ }
48
+ const easeInOutCubic = (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2);
49
+ /** Per-frame ephemeral position updates from `starts` → `targets`. Bypasses the command bus
50
+ * (`MoveNode` would push N×frames undo entries for nothing — see #propagateToDisplayConsumers
51
+ * pattern for ephemeral writes). The plugin commits the FINAL positions in one transaction once
52
+ * the tween settles. */
53
+ async function tweenPositions(ctx, starts, targets, durationMs, easing) {
54
+ const start = performance.now();
55
+ return new Promise((resolve) => {
56
+ const step = () => {
57
+ const elapsed = performance.now() - start;
58
+ const t = Math.min(1, elapsed / durationMs);
59
+ const e = easing(t);
60
+ for (const [id, end] of targets) {
61
+ const s = starts.get(id);
62
+ if (!s)
63
+ continue;
64
+ const live = ctx.graph.getNode(id);
65
+ if (!live)
66
+ continue;
67
+ // Ephemeral move — syncs node data + view container + incident edges + requests render.
68
+ // setNodePositionEphemeral is the editor's contract for "I'll commit the final position
69
+ // through MoveNode at the end; in the meantime, just paint here".
70
+ ctx.setNodePositionEphemeral(id, s.x + (end.x - s.x) * e, s.y + (end.y - s.y) * e);
71
+ }
72
+ if (t < 1)
73
+ requestAnimationFrame(step);
74
+ else
75
+ resolve();
76
+ };
77
+ requestAnimationFrame(step);
78
+ });
79
+ }
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@xenolithengine/graph-plugin-autolayout",
3
+ "version": "0.7.0-beta.0",
4
+ "description": "Auto-layout plugin for XenolithGraph. Engine-agnostic: bring your own layout backend (dagre / elkjs / custom) via the LayoutEngine interface.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./dagre": {
16
+ "types": "./dist/adapters/dagre.d.ts",
17
+ "import": "./dist/adapters/dagre.js"
18
+ },
19
+ "./elk": {
20
+ "types": "./dist/adapters/elk.d.ts",
21
+ "import": "./dist/adapters/elk.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "README.md"
27
+ ],
28
+ "sideEffects": false,
29
+ "dependencies": {
30
+ "@xenolithengine/graph-core": "0.7.0-beta.0",
31
+ "@xenolithengine/graph-editor": "0.7.0-beta.0"
32
+ },
33
+ "peerDependencies": {
34
+ "dagre": "^0.8.5",
35
+ "elkjs": "^0.9.0 || ^0.10.0 || ^0.11.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "dagre": {
39
+ "optional": true
40
+ },
41
+ "elkjs": {
42
+ "optional": true
43
+ }
44
+ },
45
+ "devDependencies": {
46
+ "@types/dagre": "^0.7.54",
47
+ "dagre": "^0.8.5",
48
+ "typescript": "^5.7.0",
49
+ "vitest": "^3.0.0"
50
+ },
51
+ "keywords": [
52
+ "node-editor",
53
+ "auto-layout",
54
+ "dagre",
55
+ "elk",
56
+ "layout"
57
+ ],
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "repository": {
62
+ "type": "git",
63
+ "url": "git+https://github.com/XenolithEngine/xenolith-graph.git",
64
+ "directory": "packages/plugin-autolayout"
65
+ },
66
+ "homepage": "https://xenolithengine.github.io/xenolith-graph/",
67
+ "bugs": {
68
+ "url": "https://github.com/XenolithEngine/xenolith-graph/issues"
69
+ },
70
+ "author": "XenolithEngine",
71
+ "scripts": {
72
+ "build": "tsc -b",
73
+ "dev": "tsc -b --watch",
74
+ "test": "vitest run",
75
+ "test:watch": "vitest",
76
+ "lint": "eslint src",
77
+ "typecheck": "tsc --noEmit"
78
+ }
79
+ }