@aetherwing/fcp-drawio 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.
Files changed (105) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/adapter.d.ts +38 -0
  4. package/dist/adapter.js +259 -0
  5. package/dist/adapter.js.map +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +6 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/layout/elk-layout.d.ts +49 -0
  10. package/dist/layout/elk-layout.js +144 -0
  11. package/dist/layout/elk-layout.js.map +1 -0
  12. package/dist/lib/drawio-cli.d.ts +22 -0
  13. package/dist/lib/drawio-cli.js +88 -0
  14. package/dist/lib/drawio-cli.js.map +1 -0
  15. package/dist/lib/node-types.d.ts +22 -0
  16. package/dist/lib/node-types.js +174 -0
  17. package/dist/lib/node-types.js.map +1 -0
  18. package/dist/lib/stencils/aws.d.ts +2 -0
  19. package/dist/lib/stencils/aws.js +69 -0
  20. package/dist/lib/stencils/aws.js.map +1 -0
  21. package/dist/lib/stencils/azure.d.ts +2 -0
  22. package/dist/lib/stencils/azure.js +54 -0
  23. package/dist/lib/stencils/azure.js.map +1 -0
  24. package/dist/lib/stencils/cisco.d.ts +2 -0
  25. package/dist/lib/stencils/cisco.js +30 -0
  26. package/dist/lib/stencils/cisco.js.map +1 -0
  27. package/dist/lib/stencils/gcp.d.ts +2 -0
  28. package/dist/lib/stencils/gcp.js +38 -0
  29. package/dist/lib/stencils/gcp.js.map +1 -0
  30. package/dist/lib/stencils/ibm.d.ts +2 -0
  31. package/dist/lib/stencils/ibm.js +32 -0
  32. package/dist/lib/stencils/ibm.js.map +1 -0
  33. package/dist/lib/stencils/index.d.ts +10 -0
  34. package/dist/lib/stencils/index.js +33 -0
  35. package/dist/lib/stencils/index.js.map +1 -0
  36. package/dist/lib/stencils/k8s.d.ts +2 -0
  37. package/dist/lib/stencils/k8s.js +32 -0
  38. package/dist/lib/stencils/k8s.js.map +1 -0
  39. package/dist/lib/stencils/types.d.ts +14 -0
  40. package/dist/lib/stencils/types.js +2 -0
  41. package/dist/lib/stencils/types.js.map +1 -0
  42. package/dist/lib/themes.d.ts +8 -0
  43. package/dist/lib/themes.js +32 -0
  44. package/dist/lib/themes.js.map +1 -0
  45. package/dist/model/defaults.d.ts +3 -0
  46. package/dist/model/defaults.js +26 -0
  47. package/dist/model/defaults.js.map +1 -0
  48. package/dist/model/diagram-model.d.ts +110 -0
  49. package/dist/model/diagram-model.js +938 -0
  50. package/dist/model/diagram-model.js.map +1 -0
  51. package/dist/model/event-log.d.ts +30 -0
  52. package/dist/model/event-log.js +112 -0
  53. package/dist/model/event-log.js.map +1 -0
  54. package/dist/model/id.d.ts +9 -0
  55. package/dist/model/id.js +35 -0
  56. package/dist/model/id.js.map +1 -0
  57. package/dist/model/reference-registry.d.ts +33 -0
  58. package/dist/model/reference-registry.js +143 -0
  59. package/dist/model/reference-registry.js.map +1 -0
  60. package/dist/model/spatial.d.ts +20 -0
  61. package/dist/model/spatial.js +59 -0
  62. package/dist/model/spatial.js.map +1 -0
  63. package/dist/parser/parse-op.d.ts +18 -0
  64. package/dist/parser/parse-op.js +430 -0
  65. package/dist/parser/parse-op.js.map +1 -0
  66. package/dist/parser/resolve-ref.d.ts +27 -0
  67. package/dist/parser/resolve-ref.js +232 -0
  68. package/dist/parser/resolve-ref.js.map +1 -0
  69. package/dist/parser/tokenizer.d.ts +6 -0
  70. package/dist/parser/tokenizer.js +7 -0
  71. package/dist/parser/tokenizer.js.map +1 -0
  72. package/dist/serialization/connector-intelligence.d.ts +35 -0
  73. package/dist/serialization/connector-intelligence.js +336 -0
  74. package/dist/serialization/connector-intelligence.js.map +1 -0
  75. package/dist/serialization/deserialize.d.ts +6 -0
  76. package/dist/serialization/deserialize.js +511 -0
  77. package/dist/serialization/deserialize.js.map +1 -0
  78. package/dist/serialization/serialize.d.ts +15 -0
  79. package/dist/serialization/serialize.js +332 -0
  80. package/dist/serialization/serialize.js.map +1 -0
  81. package/dist/server/intent-layer.d.ts +48 -0
  82. package/dist/server/intent-layer.js +1322 -0
  83. package/dist/server/intent-layer.js.map +1 -0
  84. package/dist/server/mcp-server.d.ts +7 -0
  85. package/dist/server/mcp-server.js +26 -0
  86. package/dist/server/mcp-server.js.map +1 -0
  87. package/dist/server/model-map.d.ts +8 -0
  88. package/dist/server/model-map.js +240 -0
  89. package/dist/server/model-map.js.map +1 -0
  90. package/dist/server/query-handler.d.ts +19 -0
  91. package/dist/server/query-handler.js +148 -0
  92. package/dist/server/query-handler.js.map +1 -0
  93. package/dist/server/response-formatter.d.ts +56 -0
  94. package/dist/server/response-formatter.js +351 -0
  95. package/dist/server/response-formatter.js.map +1 -0
  96. package/dist/server/session-handler.d.ts +6 -0
  97. package/dist/server/session-handler.js +127 -0
  98. package/dist/server/session-handler.js.map +1 -0
  99. package/dist/types/index.d.ts +238 -0
  100. package/dist/types/index.js +3 -0
  101. package/dist/types/index.js.map +1 -0
  102. package/dist/verb-specs.d.ts +6 -0
  103. package/dist/verb-specs.js +144 -0
  104. package/dist/verb-specs.js.map +1 -0
  105. package/package.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Scott Meyer
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,119 @@
1
+ # fcp-drawio
2
+
3
+ MCP server for creating and editing draw.io diagrams through intent-level commands.
4
+
5
+ ## What It Does
6
+
7
+ fcp-drawio lets LLMs build architecture diagrams, flowcharts, and system maps by describing what they want -- not how to draw it. The LLM sends high-level operations like `add svc AuthService theme:blue` and `connect AuthService -> UserDB`, and fcp-drawio renders them into fully styled draw.io XML with automatic layout via ELK. Built on the [FCP](https://github.com/aetherwing-io/fcp) framework.
8
+
9
+ ## Quick Example
10
+
11
+ ```
12
+ drawio([
13
+ 'add svc AuthService theme:blue',
14
+ 'add db UserDB theme:green near:AuthService dir:right',
15
+ 'connect AuthService -> UserDB label:queries',
16
+ ])
17
+ ```
18
+
19
+ Response:
20
+
21
+ ```
22
+ +svc AuthService @(200,200 140x60) blue
23
+ +db UserDB @(400,200 120x80) green
24
+ ~AuthService->UserDB "queries" solid
25
+ digest: 3s 1e 0g
26
+ ```
27
+
28
+ This produces a draw.io diagram with a blue rounded-rectangle service node, a green database cylinder, and a labeled edge between them -- all positioned automatically.
29
+
30
+ ### Available MCP Tools
31
+
32
+ | Tool | Purpose |
33
+ |------|---------|
34
+ | `drawio(ops)` | Batch mutations -- add shapes, connect, style, group, layout |
35
+ | `drawio_query(q)` | Inspect the diagram -- map, list, describe, connections, find |
36
+ | `drawio_session(action)` | Lifecycle -- new, open, save, checkpoint, undo, redo |
37
+ | `drawio_help()` | Full reference card |
38
+
39
+ ### Component Library
40
+
41
+ | Type | Shape | Use For |
42
+ |------|-------|---------|
43
+ | `svc` | Rounded rect | Services, components |
44
+ | `db` | Cylinder | Databases, storage |
45
+ | `api` | Hexagon | APIs, gateways |
46
+ | `queue` | Parallelogram | Queues, streams |
47
+ | `cloud` | Cloud | External services |
48
+ | `actor` | Person | Users, personas |
49
+ | `doc` | Document | Files, reports |
50
+ | `box` | Rectangle | Generic |
51
+ | `decision` | Diamond | Decisions, conditions |
52
+ | `circle` | Ellipse | States, events |
53
+ | `process` | Double-bordered rect | Predefined processes |
54
+ | `triangle` | Triangle | Warnings, deltas |
55
+
56
+ ### Themes
57
+
58
+ Apply color themes to any shape: `blue`, `green`, `red`, `orange`, `purple`, `yellow`, `gray`, `dark`.
59
+
60
+ ## Installation
61
+
62
+ Requires Node >= 22.
63
+
64
+ ```bash
65
+ npm install @aetherwing/fcp-drawio
66
+ ```
67
+
68
+ ### MCP Client Configuration
69
+
70
+ ```json
71
+ {
72
+ "mcpServers": {
73
+ "drawio": {
74
+ "command": "node",
75
+ "args": ["node_modules/@aetherwing/fcp-drawio/dist/index.js"]
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Architecture
82
+
83
+ 4-layer architecture:
84
+
85
+ ```
86
+ MCP Server (Intent Layer)
87
+ src/server/ -- Parses op strings, resolves refs, dispatches
88
+ |
89
+ Semantic Model (Domain Brain)
90
+ src/model/ -- In-memory entity graph, event sourcing
91
+ |
92
+ Layout (ELK)
93
+ Auto-layout via elkjs -- flow:TB, flow:LR, near/dir positioning
94
+ |
95
+ Serialization (XML)
96
+ src/serialization/ -- Semantic model <-> mxGraphModel XML
97
+ ```
98
+
99
+ Supporting modules:
100
+
101
+ - `src/parser/` -- Operation string parser
102
+ - `src/lib/` -- Component library, themes, stencils, draw.io CLI integration
103
+
104
+ See [`docs/`](docs/) for design documents and example outputs.
105
+
106
+ ## Development
107
+
108
+ ```bash
109
+ npm install
110
+ npm run build # tsc
111
+ npm test # vitest, 465 tests
112
+ npm run test:watch
113
+ npm run dev # tsc --watch
114
+ ```
115
+
116
+ ## License
117
+
118
+ MIT
119
+
@@ -0,0 +1,38 @@
1
+ import type { FcpDomainAdapter, OpResult, QueryResult } from "@aetherwing/fcp-core";
2
+ import { EventLog } from "@aetherwing/fcp-core";
3
+ import type { ParsedOp } from "@aetherwing/fcp-core";
4
+ import type { DiagramEvent } from "./types/index.js";
5
+ import { DiagramModel } from "./model/diagram-model.js";
6
+ import { IntentLayer } from "./server/intent-layer.js";
7
+ /**
8
+ * Bridge between the generic fcp-core ParsedOp (Record<string, string> params,
9
+ * positionals array) and drawio's domain-specific ParsedOp (Map<string, string>
10
+ * params, verb-specific fields like type, target, targets, arrows).
11
+ *
12
+ * The existing IntentLayer already has a full string-based dispatch pipeline
13
+ * (string -> local parseOp -> domain ParsedOp -> handler). Rather than
14
+ * rewriting the entire IntentLayer, this adapter delegates to it via the
15
+ * original string-based path while satisfying the FcpDomainAdapter interface.
16
+ */
17
+ export declare class DrawioAdapter implements FcpDomainAdapter<DiagramModel, DiagramEvent> {
18
+ private intent;
19
+ constructor(options?: {
20
+ drawioCliPath?: string | null;
21
+ });
22
+ /** Access the intent layer (for direct interaction if needed). */
23
+ get intentLayer(): IntentLayer;
24
+ createEmpty(title: string, _params: Record<string, string>): DiagramModel;
25
+ serialize(model: DiagramModel): string;
26
+ deserialize(data: Buffer | string): DiagramModel;
27
+ rebuildIndices(model: DiagramModel): void;
28
+ getDigest(model: DiagramModel): string;
29
+ /**
30
+ * Dispatch an operation. We use the raw string from the ParsedOp to feed
31
+ * back through the existing IntentLayer pipeline, which has its own parser.
32
+ * This avoids rewriting the entire IntentLayer to accept generic ParsedOps.
33
+ */
34
+ dispatchOp(op: ParsedOp, _model: DiagramModel, _log: EventLog<DiagramEvent>): Promise<OpResult>;
35
+ dispatchQuery(query: string, _model: DiagramModel): Promise<string | QueryResult>;
36
+ reverseEvent(event: DiagramEvent, model: DiagramModel): void;
37
+ replayEvent(event: DiagramEvent, model: DiagramModel): void;
38
+ }
@@ -0,0 +1,259 @@
1
+ import { IntentLayer } from "./server/intent-layer.js";
2
+ import { serializeDiagram } from "./serialization/serialize.js";
3
+ import { deserializeDiagram } from "./serialization/deserialize.js";
4
+ /**
5
+ * Bridge between the generic fcp-core ParsedOp (Record<string, string> params,
6
+ * positionals array) and drawio's domain-specific ParsedOp (Map<string, string>
7
+ * params, verb-specific fields like type, target, targets, arrows).
8
+ *
9
+ * The existing IntentLayer already has a full string-based dispatch pipeline
10
+ * (string -> local parseOp -> domain ParsedOp -> handler). Rather than
11
+ * rewriting the entire IntentLayer, this adapter delegates to it via the
12
+ * original string-based path while satisfying the FcpDomainAdapter interface.
13
+ */
14
+ export class DrawioAdapter {
15
+ intent;
16
+ constructor(options) {
17
+ this.intent = new IntentLayer(options);
18
+ }
19
+ /** Access the intent layer (for direct interaction if needed). */
20
+ get intentLayer() {
21
+ return this.intent;
22
+ }
23
+ createEmpty(title, _params) {
24
+ // Use the intent layer's session handler to create the new diagram
25
+ // This ensures the QueryHandler's model reference stays consistent
26
+ this.intent.executeSession(`new "${title}"`);
27
+ return this.intent.model;
28
+ }
29
+ serialize(model) {
30
+ return serializeDiagram(model.diagram);
31
+ }
32
+ deserialize(data) {
33
+ const xml = typeof data === "string" ? data : data.toString("utf-8");
34
+ const diagram = deserializeDiagram(xml);
35
+ // Update the intent layer's model in-place to preserve QueryHandler reference
36
+ this.intent.model.diagram = diagram;
37
+ if (diagram.pages.length > 0) {
38
+ this.intent.model.diagram.activePage = diagram.pages[0].id;
39
+ }
40
+ this.intent.model.rebuildRegistry();
41
+ this.intent.restoreStencilPacks();
42
+ return this.intent.model;
43
+ }
44
+ rebuildIndices(model) {
45
+ model.rebuildRegistry();
46
+ }
47
+ getDigest(model) {
48
+ return model.getDigest();
49
+ }
50
+ /**
51
+ * Dispatch an operation. We use the raw string from the ParsedOp to feed
52
+ * back through the existing IntentLayer pipeline, which has its own parser.
53
+ * This avoids rewriting the entire IntentLayer to accept generic ParsedOps.
54
+ */
55
+ async dispatchOp(op, _model, _log) {
56
+ return this.intent.executeSingleOp(op.raw);
57
+ }
58
+ async dispatchQuery(query, _model) {
59
+ const result = this.intent.executeQuery(query);
60
+ // Handle async results (e.g., snapshot rendering)
61
+ if (result instanceof Promise) {
62
+ const resolved = await result;
63
+ // Map the internal QueryResult to fcp-core's QueryResult
64
+ if (typeof resolved === "string")
65
+ return resolved;
66
+ const qr = resolved;
67
+ return qr;
68
+ }
69
+ if (typeof result === "string")
70
+ return result;
71
+ // Map the internal QueryResult to fcp-core's QueryResult
72
+ const qr = result;
73
+ return qr;
74
+ }
75
+ reverseEvent(event, model) {
76
+ reverseEventOnModel(event, model);
77
+ }
78
+ replayEvent(event, model) {
79
+ replayEventOnModel(event, model);
80
+ }
81
+ }
82
+ /**
83
+ * Reverse a single event on the model (undo).
84
+ * Extracted from DiagramModel.reverseEvent for use by the adapter.
85
+ */
86
+ function reverseEventOnModel(event, model) {
87
+ const page = model.getActivePage();
88
+ switch (event.type) {
89
+ case "shape_created":
90
+ page.shapes.delete(event.shape.id);
91
+ break;
92
+ case "shape_deleted":
93
+ page.shapes.set(event.shape.id, { ...event.shape });
94
+ break;
95
+ case "shape_modified": {
96
+ const shape = page.shapes.get(event.id);
97
+ if (shape)
98
+ Object.assign(shape, event.before);
99
+ break;
100
+ }
101
+ case "edge_created":
102
+ page.edges.delete(event.edge.id);
103
+ break;
104
+ case "edge_deleted":
105
+ page.edges.set(event.edge.id, { ...event.edge });
106
+ break;
107
+ case "edge_modified": {
108
+ const edge = page.edges.get(event.id);
109
+ if (edge)
110
+ Object.assign(edge, event.before);
111
+ break;
112
+ }
113
+ case "group_created":
114
+ page.groups.delete(event.group.id);
115
+ for (const id of event.group.memberIds) {
116
+ const shape = page.shapes.get(id);
117
+ if (shape)
118
+ shape.parentGroup = null;
119
+ }
120
+ break;
121
+ case "group_dissolved":
122
+ page.groups.set(event.group.id, {
123
+ ...event.group,
124
+ memberIds: new Set(event.group.memberIds),
125
+ });
126
+ for (const id of event.group.memberIds) {
127
+ const shape = page.shapes.get(id);
128
+ if (shape)
129
+ shape.parentGroup = event.group.id;
130
+ }
131
+ break;
132
+ case "page_added": {
133
+ const idx = model.diagram.pages.findIndex((p) => p.id === event.page.id);
134
+ if (idx !== -1)
135
+ model.diagram.pages.splice(idx, 1);
136
+ break;
137
+ }
138
+ case "page_removed":
139
+ model.diagram.pages.push(event.page);
140
+ break;
141
+ case "layer_created": {
142
+ const p = model.diagram.pages.find((pg) => pg.id === event.pageId);
143
+ if (p) {
144
+ const idx = p.layers.findIndex((l) => l.id === event.layer.id);
145
+ if (idx !== -1)
146
+ p.layers.splice(idx, 1);
147
+ }
148
+ break;
149
+ }
150
+ case "layer_modified": {
151
+ const p = model.diagram.pages.find((pg) => pg.id === event.pageId);
152
+ if (p) {
153
+ const layer = p.layers.find((l) => l.id === event.layerId);
154
+ if (layer)
155
+ Object.assign(layer, event.before);
156
+ }
157
+ break;
158
+ }
159
+ case "flow_direction_changed": {
160
+ const p = model.diagram.pages.find((pg) => pg.id === event.pageId);
161
+ if (p)
162
+ p.flowDirection = event.before;
163
+ break;
164
+ }
165
+ case "title_changed":
166
+ model.diagram.title = event.before;
167
+ break;
168
+ case "checkpoint":
169
+ break;
170
+ }
171
+ }
172
+ /**
173
+ * Replay a single event on the model (redo).
174
+ * Extracted from DiagramModel.replayEvent for use by the adapter.
175
+ */
176
+ function replayEventOnModel(event, model) {
177
+ const page = model.getActivePage();
178
+ switch (event.type) {
179
+ case "shape_created":
180
+ page.shapes.set(event.shape.id, { ...event.shape });
181
+ break;
182
+ case "shape_deleted":
183
+ page.shapes.delete(event.shape.id);
184
+ break;
185
+ case "shape_modified": {
186
+ const shape = page.shapes.get(event.id);
187
+ if (shape)
188
+ Object.assign(shape, event.after);
189
+ break;
190
+ }
191
+ case "edge_created":
192
+ page.edges.set(event.edge.id, { ...event.edge });
193
+ break;
194
+ case "edge_deleted":
195
+ page.edges.delete(event.edge.id);
196
+ break;
197
+ case "edge_modified": {
198
+ const edge = page.edges.get(event.id);
199
+ if (edge)
200
+ Object.assign(edge, event.after);
201
+ break;
202
+ }
203
+ case "group_created":
204
+ page.groups.set(event.group.id, {
205
+ ...event.group,
206
+ memberIds: new Set(event.group.memberIds),
207
+ });
208
+ for (const id of event.group.memberIds) {
209
+ const shape = page.shapes.get(id);
210
+ if (shape)
211
+ shape.parentGroup = event.group.id;
212
+ }
213
+ break;
214
+ case "group_dissolved":
215
+ page.groups.delete(event.group.id);
216
+ for (const id of event.group.memberIds) {
217
+ const shape = page.shapes.get(id);
218
+ if (shape)
219
+ shape.parentGroup = null;
220
+ }
221
+ break;
222
+ case "page_added":
223
+ model.diagram.pages.push(event.page);
224
+ break;
225
+ case "page_removed": {
226
+ const idx = model.diagram.pages.findIndex((p) => p.id === event.page.id);
227
+ if (idx !== -1)
228
+ model.diagram.pages.splice(idx, 1);
229
+ break;
230
+ }
231
+ case "layer_created": {
232
+ const p = model.diagram.pages.find((pg) => pg.id === event.pageId);
233
+ if (p)
234
+ p.layers.push({ ...event.layer });
235
+ break;
236
+ }
237
+ case "layer_modified": {
238
+ const p = model.diagram.pages.find((pg) => pg.id === event.pageId);
239
+ if (p) {
240
+ const layer = p.layers.find((l) => l.id === event.layerId);
241
+ if (layer)
242
+ Object.assign(layer, event.after);
243
+ }
244
+ break;
245
+ }
246
+ case "flow_direction_changed": {
247
+ const p = model.diagram.pages.find((pg) => pg.id === event.pageId);
248
+ if (p)
249
+ p.flowDirection = event.after;
250
+ break;
251
+ }
252
+ case "title_changed":
253
+ model.diagram.title = event.after;
254
+ break;
255
+ case "checkpoint":
256
+ break;
257
+ }
258
+ }
259
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IAChB,MAAM,CAAc;IAE5B,YAAY,OAA2C;QACrD,IAAI,CAAC,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,kEAAkE;IAClE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,WAAW,CAAC,KAAa,EAAE,OAA+B;QACxD,mEAAmE;QACnE,mEAAmE;QACnE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IAC3B,CAAC;IAED,SAAS,CAAC,KAAmB;QAC3B,OAAO,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,WAAW,CAAC,IAAqB;QAC/B,MAAM,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACxC,8EAA8E;QAC9E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QACpC,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IAC3B,CAAC;IAED,cAAc,CAAC,KAAmB;QAChC,KAAK,CAAC,eAAe,EAAE,CAAC;IAC1B,CAAC;IAED,SAAS,CAAC,KAAmB;QAC3B,OAAO,KAAK,CAAC,SAAS,EAAE,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,EAAY,EAAE,MAAoB,EAAE,IAA4B;QAC/E,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAa,EAAE,MAAoB;QACrD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAC/C,kDAAkD;QAClD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC;YAC9B,yDAAyD;YACzD,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAAE,OAAO,QAAQ,CAAC;YAClD,MAAM,EAAE,GAAG,QAA0E,CAAC;YACtF,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,OAAO,MAAM,CAAC;QAC9C,yDAAyD;QACzD,MAAM,EAAE,GAAG,MAAwE,CAAC;QACpF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,YAAY,CAAC,KAAmB,EAAE,KAAmB;QACnD,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,WAAW,CAAC,KAAmB,EAAE,KAAmB;QAClD,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,KAAmB,EAAE,KAAmB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;IACnC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,eAAe;YAClB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM;QACR,KAAK,eAAe;YAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACpD,MAAM;QACR,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,KAAK;gBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAC9C,MAAM;QACR,CAAC;QACD,KAAK,cAAc;YACjB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjC,MAAM;QACR,KAAK,cAAc;YACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACjD,MAAM;QACR,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,IAAI;gBAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM;QACR,CAAC;QACD,KAAK,eAAe;YAClB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClC,IAAI,KAAK;oBAAE,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;YACtC,CAAC;YACD,MAAM;QACR,KAAK,iBAAiB;YACpB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE;gBAC9B,GAAG,KAAK,CAAC,KAAK;gBACd,SAAS,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;aAC1C,CAAC,CAAC;YACH,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClC,IAAI,KAAK;oBAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,CAAC;YACD,MAAM;QACR,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzE,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACnD,MAAM;QACR,CAAC;QACD,KAAK,cAAc;YACjB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,MAAM;QACR,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;YACnE,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC/D,IAAI,GAAG,KAAK,CAAC,CAAC;oBAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YAC1C,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;YACnE,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC3D,IAAI,KAAK;oBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YAChD,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,wBAAwB,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;YACnE,IAAI,CAAC;gBAAE,CAAC,CAAC,aAAa,GAAG,KAAK,CAAC,MAA8D,CAAC;YAC9F,MAAM;QACR,CAAC;QACD,KAAK,eAAe;YAClB,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;YACnC,MAAM;QACR,KAAK,YAAY;YACf,MAAM;IACV,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CAAC,KAAmB,EAAE,KAAmB;IAClE,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,EAAE,CAAC;IACnC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,eAAe;YAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACpD,MAAM;QACR,KAAK,eAAe;YAClB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM;QACR,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,KAAK;gBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM;QACR,CAAC;QACD,KAAK,cAAc;YACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACjD,MAAM;QACR,KAAK,cAAc;YACjB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACjC,MAAM;QACR,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,IAAI;gBAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM;QACR,CAAC;QACD,KAAK,eAAe;YAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE;gBAC9B,GAAG,KAAK,CAAC,KAAK;gBACd,SAAS,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC;aAC1C,CAAC,CAAC;YACH,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClC,IAAI,KAAK;oBAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,CAAC;YACD,MAAM;QACR,KAAK,iBAAiB;YACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;gBACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAClC,IAAI,KAAK;oBAAE,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC;YACtC,CAAC;YACD,MAAM;QACR,KAAK,YAAY;YACf,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,MAAM;QACR,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzE,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACnD,MAAM;QACR,CAAC;QACD,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;YACnE,IAAI,CAAC;gBAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACzC,MAAM;QACR,CAAC;QACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;YACtB,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;YACnE,IAAI,CAAC,EAAE,CAAC;gBACN,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC3D,IAAI,KAAK;oBAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAC/C,CAAC;YACD,MAAM;QACR,CAAC;QACD,KAAK,wBAAwB,CAAC,CAAC,CAAC;YAC9B,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC;YACnE,IAAI,CAAC;gBAAE,CAAC,CAAC,aAAa,GAAG,KAAK,CAAC,KAAiD,CAAC;YACjF,MAAM;QACR,CAAC;QACD,KAAK,eAAe;YAClB,KAAK,CAAC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAClC,MAAM;QACR,KAAK,YAAY;YACf,MAAM;IACV,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import { startServer } from "./server/mcp-server.js";
2
+ startServer().catch((err) => {
3
+ console.error("Failed to start MCP server:", err);
4
+ process.exit(1);
5
+ });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC1B,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { Page, Point } from "../types/index.js";
2
+ export interface LayoutOptions {
3
+ algorithm: "layered" | "force" | "tree";
4
+ direction: "TB" | "LR" | "BT" | "RL";
5
+ spacing?: number;
6
+ }
7
+ export interface LayoutResult {
8
+ shapePositions: Map<string, {
9
+ x: number;
10
+ y: number;
11
+ }>;
12
+ edgeWaypoints: Map<string, Point[]>;
13
+ }
14
+ interface ElkNode {
15
+ id: string;
16
+ width: number;
17
+ height: number;
18
+ x?: number;
19
+ y?: number;
20
+ children?: ElkNode[];
21
+ layoutOptions?: Record<string, string>;
22
+ }
23
+ interface ElkEdge {
24
+ id: string;
25
+ sources: string[];
26
+ targets: string[];
27
+ sections?: ElkSection[];
28
+ }
29
+ interface ElkSection {
30
+ startPoint: Point;
31
+ endPoint: Point;
32
+ bendPoints?: Point[];
33
+ }
34
+ interface ElkGraph {
35
+ id: string;
36
+ layoutOptions: Record<string, string>;
37
+ children: ElkNode[];
38
+ edges: ElkEdge[];
39
+ }
40
+ /**
41
+ * Build an ELK graph from a Page, respecting group hierarchy.
42
+ * Groups become parent nodes with children arrays.
43
+ */
44
+ export declare function buildElkGraph(page: Page, options: LayoutOptions): ElkGraph;
45
+ /**
46
+ * Run ELK layout on a page and return new positions/waypoints.
47
+ */
48
+ export declare function runElkLayout(page: Page, options: LayoutOptions): Promise<LayoutResult>;
49
+ export {};
@@ -0,0 +1,144 @@
1
+ // elkjs CJS default export needs interop cast under Node16 module resolution
2
+ import _ELK from "elkjs/lib/elk.bundled.js";
3
+ const ELK = _ELK;
4
+ // ── Algorithm + direction mapping ────────────────────────────
5
+ const ALGORITHM_MAP = {
6
+ layered: "org.eclipse.elk.layered",
7
+ force: "org.eclipse.elk.force",
8
+ tree: "org.eclipse.elk.mrtree",
9
+ };
10
+ const DIRECTION_MAP = {
11
+ TB: "DOWN",
12
+ LR: "RIGHT",
13
+ BT: "UP",
14
+ RL: "LEFT",
15
+ };
16
+ /**
17
+ * Build an ELK graph from a Page, respecting group hierarchy.
18
+ * Groups become parent nodes with children arrays.
19
+ */
20
+ export function buildElkGraph(page, options) {
21
+ const spacing = String(options.spacing ?? 60);
22
+ const layoutOptions = {
23
+ "elk.algorithm": ALGORITHM_MAP[options.algorithm],
24
+ "elk.direction": DIRECTION_MAP[options.direction],
25
+ "elk.edgeRouting": "ORTHOGONAL",
26
+ "elk.spacing.nodeNode": spacing,
27
+ "elk.layered.spacing.nodeNodeBetweenLayers": spacing,
28
+ };
29
+ // Collect shapes by group
30
+ const groupChildren = new Map();
31
+ const ungroupedShapes = [];
32
+ for (const shape of page.shapes.values()) {
33
+ if (shape.parentGroup) {
34
+ if (!groupChildren.has(shape.parentGroup)) {
35
+ groupChildren.set(shape.parentGroup, []);
36
+ }
37
+ groupChildren.get(shape.parentGroup).push(shape);
38
+ }
39
+ else {
40
+ ungroupedShapes.push(shape);
41
+ }
42
+ }
43
+ const children = [];
44
+ // Add ungrouped shapes as top-level children
45
+ for (const shape of ungroupedShapes) {
46
+ children.push({
47
+ id: shape.id,
48
+ width: shape.bounds.width,
49
+ height: shape.bounds.height,
50
+ });
51
+ }
52
+ // Add groups as hierarchical nodes with children
53
+ for (const [groupId, group] of page.groups) {
54
+ const members = groupChildren.get(groupId) ?? [];
55
+ const elkChildren = members.map((shape) => ({
56
+ id: shape.id,
57
+ width: shape.bounds.width,
58
+ height: shape.bounds.height,
59
+ }));
60
+ children.push({
61
+ id: groupId,
62
+ width: group.bounds.width,
63
+ height: group.bounds.height,
64
+ children: elkChildren,
65
+ layoutOptions: {
66
+ "elk.algorithm": ALGORITHM_MAP[options.algorithm],
67
+ "elk.direction": DIRECTION_MAP[options.direction],
68
+ "elk.spacing.nodeNode": spacing,
69
+ "elk.padding": "[top=50,left=40,bottom=35,right=40]",
70
+ },
71
+ });
72
+ }
73
+ // Build edges — ELK expects sources/targets to reference node IDs
74
+ const edges = [];
75
+ for (const edge of page.edges.values()) {
76
+ edges.push({
77
+ id: edge.id,
78
+ sources: [edge.sourceId],
79
+ targets: [edge.targetId],
80
+ });
81
+ }
82
+ return {
83
+ id: "root",
84
+ layoutOptions,
85
+ children,
86
+ edges,
87
+ };
88
+ }
89
+ // ── Layout execution ────────────────────────────────────────
90
+ /**
91
+ * Run ELK layout on a page and return new positions/waypoints.
92
+ */
93
+ export async function runElkLayout(page, options) {
94
+ const elk = new ELK();
95
+ const graph = buildElkGraph(page, options);
96
+ const layouted = await elk.layout(graph);
97
+ return extractLayoutResult(layouted, page);
98
+ }
99
+ // ── Result extraction ───────────────────────────────────────
100
+ /**
101
+ * Extract shape positions and edge waypoints from an ELK layout result.
102
+ * Converts relative coords within groups to absolute coords.
103
+ */
104
+ function extractLayoutResult(elkResult, page) {
105
+ const shapePositions = new Map();
106
+ const edgeWaypoints = new Map();
107
+ if (!elkResult.children)
108
+ return { shapePositions, edgeWaypoints };
109
+ for (const node of elkResult.children) {
110
+ const x = node.x ?? 0;
111
+ const y = node.y ?? 0;
112
+ // Check if this is a group node (has children) or a shape
113
+ if (node.children && node.children.length > 0) {
114
+ // This is a group — set group position and convert children to absolute
115
+ shapePositions.set(node.id, { x, y });
116
+ for (const child of node.children) {
117
+ const childX = (child.x ?? 0) + x;
118
+ const childY = (child.y ?? 0) + y;
119
+ shapePositions.set(child.id, { x: childX, y: childY });
120
+ }
121
+ }
122
+ else {
123
+ // This is a top-level shape
124
+ shapePositions.set(node.id, { x, y });
125
+ }
126
+ }
127
+ // Extract edge waypoints from sections
128
+ if (elkResult.edges) {
129
+ for (const edge of elkResult.edges) {
130
+ if (edge.sections && edge.sections.length > 0) {
131
+ const section = edge.sections[0];
132
+ const waypoints = [];
133
+ if (section.bendPoints) {
134
+ for (const bp of section.bendPoints) {
135
+ waypoints.push({ x: bp.x, y: bp.y });
136
+ }
137
+ }
138
+ edgeWaypoints.set(edge.id, waypoints);
139
+ }
140
+ }
141
+ }
142
+ return { shapePositions, edgeWaypoints };
143
+ }
144
+ //# sourceMappingURL=elk-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elk-layout.js","sourceRoot":"","sources":["../../src/layout/elk-layout.ts"],"names":[],"mappings":"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"}