@statelyai/graph 0.12.0 → 0.13.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/README.md +23 -15
- package/dist/{algorithms-BHHg7lGq.mjs → algorithms-BNDQcHU3.mjs} +7 -7
- package/dist/algorithms.mjs +1 -1
- package/dist/format-support.d.mts +6 -0
- package/dist/format-support.mjs +38 -32
- package/dist/formats/cytoscape/index.mjs +16 -0
- package/dist/formats/d3/index.d.mts +7 -0
- package/dist/formats/d3/index.mjs +34 -6
- package/dist/formats/elk/index.mjs +86 -23
- package/dist/formats/gexf/index.mjs +93 -1
- package/dist/formats/gml/index.mjs +40 -13
- package/dist/formats/jgf/index.mjs +16 -0
- package/dist/formats/mermaid/index.d.mts +1 -0
- package/dist/formats/mermaid/index.mjs +39 -9
- package/dist/formats/xyflow/index.d.mts +1 -0
- package/dist/formats/xyflow/index.mjs +97 -40
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{indexing-CJc-ul8e.mjs → indexing-DUl3kTqm.mjs} +18 -4
- package/dist/queries.mjs +1 -1
- package/dist/schemas.d.mts +8 -7
- package/dist/schemas.mjs +127 -4
- package/package.json +1 -1
- package/schemas/graph.schema.json +1 -0
- package/schemas/node.schema.json +1 -0
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { getChildren } from "../../queries.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/formats/elk/index.ts
|
|
4
|
+
const STATELYAI_METADATA_KEY = "statelyai.metadata";
|
|
5
|
+
function addMetadata(target, metadata) {
|
|
6
|
+
target.layoutOptions = {
|
|
7
|
+
...target.layoutOptions ?? {},
|
|
8
|
+
[STATELYAI_METADATA_KEY]: JSON.stringify(metadata)
|
|
9
|
+
};
|
|
10
|
+
return target;
|
|
11
|
+
}
|
|
12
|
+
function readMetadata(value) {
|
|
13
|
+
const raw = value.layoutOptions?.[STATELYAI_METADATA_KEY];
|
|
14
|
+
if (typeof raw !== "string") return void 0;
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
} catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
4
21
|
const DIRECTION_TO_ELK = {
|
|
5
22
|
down: "DOWN",
|
|
6
23
|
up: "UP",
|
|
@@ -20,7 +37,21 @@ function convertEdge(edge) {
|
|
|
20
37
|
targets: [edge.targetPort ?? edge.targetId]
|
|
21
38
|
};
|
|
22
39
|
if (edge.label) elkEdge.labels = [{ text: edge.label }];
|
|
23
|
-
return elkEdge
|
|
40
|
+
return addMetadata(elkEdge, { edge: {
|
|
41
|
+
sourceId: edge.sourceId,
|
|
42
|
+
targetId: edge.targetId,
|
|
43
|
+
sourcePort: edge.sourcePort,
|
|
44
|
+
targetPort: edge.targetPort,
|
|
45
|
+
label: edge.label,
|
|
46
|
+
data: edge.data,
|
|
47
|
+
weight: edge.weight,
|
|
48
|
+
color: edge.color,
|
|
49
|
+
style: edge.style,
|
|
50
|
+
x: edge.x,
|
|
51
|
+
y: edge.y,
|
|
52
|
+
width: edge.width,
|
|
53
|
+
height: edge.height
|
|
54
|
+
} });
|
|
24
55
|
}
|
|
25
56
|
function convertPort(port) {
|
|
26
57
|
const elkPort = {
|
|
@@ -32,7 +63,10 @@ function convertPort(port) {
|
|
|
32
63
|
};
|
|
33
64
|
if (port.label) elkPort.labels = [{ text: port.label }];
|
|
34
65
|
if (port.direction !== "inout") elkPort.layoutOptions = { "org.eclipse.elk.port.side": port.direction === "in" ? "WEST" : "EAST" };
|
|
35
|
-
return elkPort
|
|
66
|
+
return addMetadata(elkPort, { port: {
|
|
67
|
+
data: port.data,
|
|
68
|
+
style: port.style
|
|
69
|
+
} });
|
|
36
70
|
}
|
|
37
71
|
function convertNode(graph, node) {
|
|
38
72
|
const elkNode = {
|
|
@@ -44,6 +78,13 @@ function convertNode(graph, node) {
|
|
|
44
78
|
};
|
|
45
79
|
if (node.label) elkNode.labels = [{ text: node.label }];
|
|
46
80
|
if (node.ports && node.ports.length > 0) elkNode.ports = node.ports.map(convertPort);
|
|
81
|
+
addMetadata(elkNode, { node: {
|
|
82
|
+
initialNodeId: node.initialNodeId,
|
|
83
|
+
data: node.data,
|
|
84
|
+
shape: node.shape,
|
|
85
|
+
color: node.color,
|
|
86
|
+
style: node.style
|
|
87
|
+
} });
|
|
47
88
|
const children = getChildren(graph, node.id);
|
|
48
89
|
if (children.length > 0) {
|
|
49
90
|
elkNode.children = children.map((child) => convertNode(graph, child));
|
|
@@ -85,6 +126,14 @@ function toELK(graph) {
|
|
|
85
126
|
const root = { id: graph.id };
|
|
86
127
|
const elkDir = DIRECTION_TO_ELK[graph.direction];
|
|
87
128
|
if (elkDir) root.layoutOptions = { "elk.direction": elkDir };
|
|
129
|
+
addMetadata(root, { graph: {
|
|
130
|
+
id: graph.id,
|
|
131
|
+
type: graph.type,
|
|
132
|
+
initialNodeId: graph.initialNodeId,
|
|
133
|
+
data: graph.data,
|
|
134
|
+
direction: graph.direction,
|
|
135
|
+
style: graph.style
|
|
136
|
+
} });
|
|
88
137
|
const roots = getChildren(graph, null);
|
|
89
138
|
if (roots.length > 0) root.children = roots.map((node) => convertNode(graph, node));
|
|
90
139
|
const allInnerEdgeIds = /* @__PURE__ */ new Set();
|
|
@@ -100,20 +149,25 @@ function toELK(graph) {
|
|
|
100
149
|
function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
|
|
101
150
|
if (elkNode.children) for (const child of elkNode.children) {
|
|
102
151
|
const label = child.labels?.[0]?.text ?? "";
|
|
152
|
+
const metadata = readMetadata(child)?.node;
|
|
103
153
|
const node = {
|
|
104
154
|
type: "node",
|
|
105
155
|
id: child.id,
|
|
106
156
|
parentId,
|
|
107
|
-
initialNodeId: null,
|
|
157
|
+
initialNodeId: metadata && "initialNodeId" in metadata ? metadata.initialNodeId : null,
|
|
108
158
|
label,
|
|
109
|
-
data: void 0,
|
|
159
|
+
data: metadata && "data" in metadata ? metadata.data : void 0,
|
|
110
160
|
x: child.x ?? 0,
|
|
111
161
|
y: child.y ?? 0,
|
|
112
162
|
width: child.width ?? 0,
|
|
113
|
-
height: child.height ?? 0
|
|
163
|
+
height: child.height ?? 0,
|
|
164
|
+
...metadata?.shape !== void 0 && { shape: metadata.shape },
|
|
165
|
+
...metadata?.color !== void 0 && { color: metadata.color },
|
|
166
|
+
...metadata?.style !== void 0 && { style: metadata.style }
|
|
114
167
|
};
|
|
115
168
|
if (child.ports && child.ports.length > 0) node.ports = child.ports.map((elkPort) => {
|
|
116
169
|
portOwner.set(elkPort.id, child.id);
|
|
170
|
+
const metadata$1 = readMetadata(elkPort)?.port;
|
|
117
171
|
const sideOpt = elkPort.layoutOptions?.["org.eclipse.elk.port.side"];
|
|
118
172
|
let direction = "inout";
|
|
119
173
|
if (sideOpt === "WEST") direction = "in";
|
|
@@ -122,33 +176,40 @@ function flattenElkNodes(elkNode, parentId, nodes, edges, edgeIdx, portOwner) {
|
|
|
122
176
|
name: elkPort.id,
|
|
123
177
|
direction,
|
|
124
178
|
label: elkPort.labels?.[0]?.text,
|
|
125
|
-
data: void 0,
|
|
179
|
+
data: metadata$1 && "data" in metadata$1 ? metadata$1.data : void 0,
|
|
126
180
|
x: elkPort.x ?? 0,
|
|
127
181
|
y: elkPort.y ?? 0,
|
|
128
182
|
width: elkPort.width ?? 0,
|
|
129
|
-
height: elkPort.height ?? 0
|
|
183
|
+
height: elkPort.height ?? 0,
|
|
184
|
+
...metadata$1?.style !== void 0 && { style: metadata$1.style }
|
|
130
185
|
};
|
|
131
186
|
});
|
|
132
187
|
nodes.push(node);
|
|
133
188
|
flattenElkNodes(child, child.id, nodes, edges, edgeIdx, portOwner);
|
|
134
189
|
}
|
|
135
190
|
if (elkNode.edges) for (const elkEdge of elkNode.edges) for (const source of elkEdge.sources) for (const target of elkEdge.targets) {
|
|
191
|
+
const metadata = readMetadata(elkEdge)?.edge;
|
|
136
192
|
const sourceNodeId = portOwner.get(source);
|
|
137
193
|
const targetNodeId = portOwner.get(target);
|
|
138
194
|
const edge = {
|
|
139
195
|
type: "edge",
|
|
140
196
|
id: elkEdge.id ?? `e${edgeIdx.value++}`,
|
|
141
|
-
sourceId: sourceNodeId ?? source,
|
|
142
|
-
targetId: targetNodeId ?? target,
|
|
143
|
-
label: elkEdge.labels?.[0]?.text ?? "",
|
|
144
|
-
data: void 0,
|
|
145
|
-
x: 0,
|
|
146
|
-
y: 0,
|
|
147
|
-
width: 0,
|
|
148
|
-
height: 0
|
|
197
|
+
sourceId: metadata?.sourceId ?? sourceNodeId ?? source,
|
|
198
|
+
targetId: metadata?.targetId ?? targetNodeId ?? target,
|
|
199
|
+
label: metadata && "label" in metadata ? metadata.label : elkEdge.labels?.[0]?.text ?? "",
|
|
200
|
+
data: metadata && "data" in metadata ? metadata.data : void 0,
|
|
201
|
+
x: metadata?.x ?? 0,
|
|
202
|
+
y: metadata?.y ?? 0,
|
|
203
|
+
width: metadata?.width ?? 0,
|
|
204
|
+
height: metadata?.height ?? 0,
|
|
205
|
+
...metadata?.weight !== void 0 && { weight: metadata.weight },
|
|
206
|
+
...metadata?.color !== void 0 && { color: metadata.color },
|
|
207
|
+
...metadata?.style !== void 0 && { style: metadata.style }
|
|
149
208
|
};
|
|
150
|
-
if (
|
|
151
|
-
if (
|
|
209
|
+
if (metadata && "sourcePort" in metadata) edge.sourcePort = metadata.sourcePort;
|
|
210
|
+
else if (sourceNodeId) edge.sourcePort = source;
|
|
211
|
+
if (metadata && "targetPort" in metadata) edge.targetPort = metadata.targetPort;
|
|
212
|
+
else if (targetNodeId) edge.targetPort = target;
|
|
152
213
|
edges.push(edge);
|
|
153
214
|
}
|
|
154
215
|
}
|
|
@@ -179,15 +240,17 @@ function fromELK(elkRoot) {
|
|
|
179
240
|
const seenEdges = /* @__PURE__ */ new Map();
|
|
180
241
|
for (const edge of edges) if (!seenEdges.has(edge.id)) seenEdges.set(edge.id, edge);
|
|
181
242
|
const elkDir = elkRoot.layoutOptions?.["elk.direction"];
|
|
182
|
-
const
|
|
243
|
+
const graphMetadata = readMetadata(elkRoot)?.graph;
|
|
244
|
+
const direction = graphMetadata?.direction ?? (elkDir ? ELK_TO_DIRECTION[elkDir] : void 0) ?? "down";
|
|
183
245
|
return {
|
|
184
|
-
id: elkRoot.id,
|
|
185
|
-
type: "directed",
|
|
186
|
-
initialNodeId: null,
|
|
246
|
+
id: graphMetadata?.id ?? elkRoot.id,
|
|
247
|
+
type: graphMetadata?.type === "undirected" ? "undirected" : "directed",
|
|
248
|
+
initialNodeId: graphMetadata && "initialNodeId" in graphMetadata ? graphMetadata.initialNodeId : null,
|
|
187
249
|
nodes,
|
|
188
250
|
edges: [...seenEdges.values()],
|
|
189
|
-
data: void 0,
|
|
190
|
-
direction
|
|
251
|
+
data: graphMetadata && "data" in graphMetadata ? graphMetadata.data : void 0,
|
|
252
|
+
direction,
|
|
253
|
+
...graphMetadata?.style !== void 0 && { style: graphMetadata.style }
|
|
191
254
|
};
|
|
192
255
|
}
|
|
193
256
|
/**
|
|
@@ -24,6 +24,21 @@ function toGEXF(graph) {
|
|
|
24
24
|
"@_title": "shape",
|
|
25
25
|
"@_type": "string"
|
|
26
26
|
},
|
|
27
|
+
{
|
|
28
|
+
"@_id": "a_width",
|
|
29
|
+
"@_title": "width",
|
|
30
|
+
"@_type": "double"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"@_id": "a_height",
|
|
34
|
+
"@_title": "height",
|
|
35
|
+
"@_type": "double"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"@_id": "a_style",
|
|
39
|
+
"@_title": "style",
|
|
40
|
+
"@_type": "string"
|
|
41
|
+
},
|
|
27
42
|
{
|
|
28
43
|
"@_id": "a_ports",
|
|
29
44
|
"@_title": "ports",
|
|
@@ -36,6 +51,36 @@ function toGEXF(graph) {
|
|
|
36
51
|
"@_title": "data",
|
|
37
52
|
"@_type": "string"
|
|
38
53
|
},
|
|
54
|
+
{
|
|
55
|
+
"@_id": "a_edgeWeight",
|
|
56
|
+
"@_title": "weight",
|
|
57
|
+
"@_type": "double"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"@_id": "a_edgeX",
|
|
61
|
+
"@_title": "x",
|
|
62
|
+
"@_type": "double"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"@_id": "a_edgeY",
|
|
66
|
+
"@_title": "y",
|
|
67
|
+
"@_type": "double"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"@_id": "a_edgeWidth",
|
|
71
|
+
"@_title": "width",
|
|
72
|
+
"@_type": "double"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"@_id": "a_edgeHeight",
|
|
76
|
+
"@_title": "height",
|
|
77
|
+
"@_type": "double"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"@_id": "a_edgeStyle",
|
|
81
|
+
"@_title": "style",
|
|
82
|
+
"@_type": "string"
|
|
83
|
+
},
|
|
39
84
|
{
|
|
40
85
|
"@_id": "a_sourcePort",
|
|
41
86
|
"@_title": "sourcePort",
|
|
@@ -65,6 +110,18 @@ function toGEXF(graph) {
|
|
|
65
110
|
"@_for": "a_shape",
|
|
66
111
|
"@_value": n.shape
|
|
67
112
|
});
|
|
113
|
+
if (n.width !== void 0) attvalues.push({
|
|
114
|
+
"@_for": "a_width",
|
|
115
|
+
"@_value": n.width
|
|
116
|
+
});
|
|
117
|
+
if (n.height !== void 0) attvalues.push({
|
|
118
|
+
"@_for": "a_height",
|
|
119
|
+
"@_value": n.height
|
|
120
|
+
});
|
|
121
|
+
if (n.style !== void 0) attvalues.push({
|
|
122
|
+
"@_for": "a_style",
|
|
123
|
+
"@_value": JSON.stringify(n.style)
|
|
124
|
+
});
|
|
68
125
|
if (n.ports !== void 0) attvalues.push({
|
|
69
126
|
"@_for": "a_ports",
|
|
70
127
|
"@_value": JSON.stringify(n.ports)
|
|
@@ -102,6 +159,30 @@ function toGEXF(graph) {
|
|
|
102
159
|
"@_value": JSON.stringify(e.data)
|
|
103
160
|
}] };
|
|
104
161
|
const edgeAttvalues = edge.attvalues?.attvalue ?? [];
|
|
162
|
+
if (e.weight !== void 0) edgeAttvalues.push({
|
|
163
|
+
"@_for": "a_edgeWeight",
|
|
164
|
+
"@_value": e.weight
|
|
165
|
+
});
|
|
166
|
+
if (e.x !== void 0) edgeAttvalues.push({
|
|
167
|
+
"@_for": "a_edgeX",
|
|
168
|
+
"@_value": e.x
|
|
169
|
+
});
|
|
170
|
+
if (e.y !== void 0) edgeAttvalues.push({
|
|
171
|
+
"@_for": "a_edgeY",
|
|
172
|
+
"@_value": e.y
|
|
173
|
+
});
|
|
174
|
+
if (e.width !== void 0) edgeAttvalues.push({
|
|
175
|
+
"@_for": "a_edgeWidth",
|
|
176
|
+
"@_value": e.width
|
|
177
|
+
});
|
|
178
|
+
if (e.height !== void 0) edgeAttvalues.push({
|
|
179
|
+
"@_for": "a_edgeHeight",
|
|
180
|
+
"@_value": e.height
|
|
181
|
+
});
|
|
182
|
+
if (e.style !== void 0) edgeAttvalues.push({
|
|
183
|
+
"@_for": "a_edgeStyle",
|
|
184
|
+
"@_value": JSON.stringify(e.style)
|
|
185
|
+
});
|
|
105
186
|
if (e.sourcePort !== void 0) edgeAttvalues.push({
|
|
106
187
|
"@_for": "a_sourcePort",
|
|
107
188
|
"@_value": e.sourcePort
|
|
@@ -136,6 +217,7 @@ function toGEXF(graph) {
|
|
|
136
217
|
...graph.initialNodeId && { "@_initialNodeId": graph.initialNodeId },
|
|
137
218
|
...graph.direction && { "@_direction": graph.direction },
|
|
138
219
|
...graph.data !== void 0 && { "@_data": JSON.stringify(graph.data) },
|
|
220
|
+
...graph.style !== void 0 && { "@_style": JSON.stringify(graph.style) },
|
|
139
221
|
attributes: [{
|
|
140
222
|
"@_class": "node",
|
|
141
223
|
attribute: nodeAttrs
|
|
@@ -193,6 +275,7 @@ function fromGEXF(xml) {
|
|
|
193
275
|
data: attvals["data"] !== void 0 ? tryParseJSON(attvals["data"]) : void 0
|
|
194
276
|
};
|
|
195
277
|
if (attvals["shape"]) node.shape = attvals["shape"];
|
|
278
|
+
if (attvals["style"] !== void 0) node.style = tryParseJSON(attvals["style"]);
|
|
196
279
|
if (attvals["ports"] !== void 0) node.ports = tryParseJSON(attvals["ports"]);
|
|
197
280
|
const pos = n["viz:position"];
|
|
198
281
|
if (pos) {
|
|
@@ -204,6 +287,8 @@ function fromGEXF(xml) {
|
|
|
204
287
|
node.width = Number(size["@_value"] ?? 0);
|
|
205
288
|
node.height = Number(size["@_value"] ?? 0);
|
|
206
289
|
}
|
|
290
|
+
if (attvals["width"] !== void 0) node.width = Number(attvals["width"]);
|
|
291
|
+
if (attvals["height"] !== void 0) node.height = Number(attvals["height"]);
|
|
207
292
|
const color = n["viz:color"];
|
|
208
293
|
if (color) {
|
|
209
294
|
const r = Number(color["@_r"] ?? 0);
|
|
@@ -226,6 +311,12 @@ function fromGEXF(xml) {
|
|
|
226
311
|
targetId: String(e["@_target"]),
|
|
227
312
|
label: e["@_label"] ?? "",
|
|
228
313
|
data: attvals["data"] !== void 0 ? tryParseJSON(attvals["data"]) : void 0,
|
|
314
|
+
...attvals["weight"] !== void 0 && { weight: Number(attvals["weight"]) },
|
|
315
|
+
...attvals["x"] !== void 0 && { x: Number(attvals["x"]) },
|
|
316
|
+
...attvals["y"] !== void 0 && { y: Number(attvals["y"]) },
|
|
317
|
+
...attvals["width"] !== void 0 && { width: Number(attvals["width"]) },
|
|
318
|
+
...attvals["height"] !== void 0 && { height: Number(attvals["height"]) },
|
|
319
|
+
...attvals["style"] !== void 0 && { style: tryParseJSON(attvals["style"]) },
|
|
229
320
|
...attvals["sourcePort"] !== void 0 && { sourcePort: attvals["sourcePort"] },
|
|
230
321
|
...attvals["targetPort"] !== void 0 && { targetPort: attvals["targetPort"] }
|
|
231
322
|
};
|
|
@@ -245,7 +336,8 @@ function fromGEXF(xml) {
|
|
|
245
336
|
nodes,
|
|
246
337
|
edges,
|
|
247
338
|
data: graphEl["@_data"] !== void 0 ? tryParseJSON(String(graphEl["@_data"])) : void 0,
|
|
248
|
-
...graphEl["@_direction"] && { direction: graphEl["@_direction"] }
|
|
339
|
+
...graphEl["@_direction"] && { direction: graphEl["@_direction"] },
|
|
340
|
+
...graphEl["@_style"] !== void 0 && { style: tryParseJSON(String(graphEl["@_style"])) }
|
|
249
341
|
};
|
|
250
342
|
}
|
|
251
343
|
function asArray(val) {
|
|
@@ -28,6 +28,10 @@ function toGML(graph) {
|
|
|
28
28
|
lines.push("graph [");
|
|
29
29
|
lines.push(` directed ${graph.type === "directed" ? 1 : 0}`);
|
|
30
30
|
if (graph.id) lines.push(` id ${gmlString(graph.id)}`);
|
|
31
|
+
if (graph.initialNodeId) lines.push(` initialNodeId ${gmlString(graph.initialNodeId)}`);
|
|
32
|
+
if (graph.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(graph.data))}`);
|
|
33
|
+
if (graph.direction) lines.push(` direction ${gmlString(graph.direction)}`);
|
|
34
|
+
if (graph.style !== void 0) lines.push(` style ${gmlString(JSON.stringify(graph.style))}`);
|
|
31
35
|
const childrenMap = /* @__PURE__ */ new Map();
|
|
32
36
|
for (const node of graph.nodes) {
|
|
33
37
|
const pid = node.parentId ?? null;
|
|
@@ -43,6 +47,7 @@ function toGML(graph) {
|
|
|
43
47
|
if (node.ports !== void 0) lines.push(`${indent} ports ${gmlString(JSON.stringify(node.ports))}`);
|
|
44
48
|
if (node.shape) lines.push(`${indent} shape ${gmlString(node.shape)}`);
|
|
45
49
|
if (node.color) lines.push(`${indent} color ${gmlString(node.color)}`);
|
|
50
|
+
if (node.style !== void 0) lines.push(`${indent} style ${gmlString(JSON.stringify(node.style))}`);
|
|
46
51
|
if (node.x !== void 0 || node.y !== void 0 || node.width !== void 0 || node.height !== void 0) {
|
|
47
52
|
lines.push(`${indent} graphics [`);
|
|
48
53
|
if (node.x !== void 0) lines.push(`${indent} x ${node.x}`);
|
|
@@ -64,9 +69,19 @@ function toGML(graph) {
|
|
|
64
69
|
lines.push(` target ${gmlString(edge.targetId)}`);
|
|
65
70
|
if (edge.label) lines.push(` label ${gmlString(edge.label)}`);
|
|
66
71
|
if (edge.data !== void 0) lines.push(` data ${gmlString(JSON.stringify(edge.data))}`);
|
|
72
|
+
if (edge.weight !== void 0) lines.push(` weight ${edge.weight}`);
|
|
67
73
|
if (edge.sourcePort !== void 0) lines.push(` sourcePort ${gmlString(edge.sourcePort)}`);
|
|
68
74
|
if (edge.targetPort !== void 0) lines.push(` targetPort ${gmlString(edge.targetPort)}`);
|
|
69
75
|
if (edge.color) lines.push(` color ${gmlString(edge.color)}`);
|
|
76
|
+
if (edge.style !== void 0) lines.push(` style ${gmlString(JSON.stringify(edge.style))}`);
|
|
77
|
+
if (edge.x !== void 0 || edge.y !== void 0 || edge.width !== void 0 || edge.height !== void 0) {
|
|
78
|
+
lines.push(" graphics [");
|
|
79
|
+
if (edge.x !== void 0) lines.push(` x ${edge.x}`);
|
|
80
|
+
if (edge.y !== void 0) lines.push(` y ${edge.y}`);
|
|
81
|
+
if (edge.width !== void 0) lines.push(` w ${edge.width}`);
|
|
82
|
+
if (edge.height !== void 0) lines.push(` h ${edge.height}`);
|
|
83
|
+
lines.push(" ]");
|
|
84
|
+
}
|
|
70
85
|
lines.push(" ]");
|
|
71
86
|
}
|
|
72
87
|
lines.push("]");
|
|
@@ -118,6 +133,7 @@ function fromGML(gml) {
|
|
|
118
133
|
...n["ports"] !== void 0 && { ports: tryParseJSON(n["ports"]) },
|
|
119
134
|
...n["shape"] && { shape: n["shape"] },
|
|
120
135
|
...n["color"] && { color: n["color"] },
|
|
136
|
+
...n["style"] !== void 0 && { style: tryParseJSON(n["style"]) },
|
|
121
137
|
...gfx?.x !== void 0 && { x: gfx.x },
|
|
122
138
|
...gfx?.y !== void 0 && { y: gfx.y },
|
|
123
139
|
...gfx?.w !== void 0 && { width: gfx.w },
|
|
@@ -128,24 +144,35 @@ function fromGML(gml) {
|
|
|
128
144
|
}
|
|
129
145
|
parseNodes(graphBlock, null);
|
|
130
146
|
const edgeEntries = asArray(graphBlock["edge"]);
|
|
131
|
-
for (const e of edgeEntries)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
for (const e of edgeEntries) {
|
|
148
|
+
const gfx = e["graphics"];
|
|
149
|
+
edges.push({
|
|
150
|
+
type: "edge",
|
|
151
|
+
id: String(e["id"] ?? `e${edges.length}`),
|
|
152
|
+
sourceId: String(e["source"] ?? ""),
|
|
153
|
+
targetId: String(e["target"] ?? ""),
|
|
154
|
+
label: e["label"] ?? "",
|
|
155
|
+
data: e["data"] !== void 0 ? tryParseJSON(e["data"]) : void 0,
|
|
156
|
+
...e["weight"] !== void 0 && { weight: Number(e["weight"]) },
|
|
157
|
+
...e["sourcePort"] !== void 0 && { sourcePort: String(e["sourcePort"]) },
|
|
158
|
+
...e["targetPort"] !== void 0 && { targetPort: String(e["targetPort"]) },
|
|
159
|
+
...e["color"] && { color: e["color"] },
|
|
160
|
+
...e["style"] !== void 0 && { style: tryParseJSON(e["style"]) },
|
|
161
|
+
...gfx?.x !== void 0 && { x: gfx.x },
|
|
162
|
+
...gfx?.y !== void 0 && { y: gfx.y },
|
|
163
|
+
...gfx?.w !== void 0 && { width: gfx.w },
|
|
164
|
+
...gfx?.h !== void 0 && { height: gfx.h }
|
|
165
|
+
});
|
|
166
|
+
}
|
|
142
167
|
return {
|
|
143
168
|
id: graphId,
|
|
144
169
|
type: directed ? "directed" : "undirected",
|
|
145
|
-
initialNodeId: null,
|
|
170
|
+
initialNodeId: graphBlock["initialNodeId"] ?? null,
|
|
146
171
|
nodes,
|
|
147
172
|
edges,
|
|
148
|
-
data: void 0
|
|
173
|
+
data: graphBlock["data"] !== void 0 ? tryParseJSON(graphBlock["data"]) : void 0,
|
|
174
|
+
...graphBlock["direction"] && { direction: String(graphBlock["direction"]) },
|
|
175
|
+
...graphBlock["style"] !== void 0 && { style: tryParseJSON(graphBlock["style"]) }
|
|
149
176
|
};
|
|
150
177
|
}
|
|
151
178
|
function tokenize(input) {
|
|
@@ -23,6 +23,7 @@ function toJGF(graph) {
|
|
|
23
23
|
if (graph.initialNodeId) metadata.initialNodeId = graph.initialNodeId;
|
|
24
24
|
if (graph.data !== void 0) metadata.data = graph.data;
|
|
25
25
|
if (graph.direction) metadata.direction = graph.direction;
|
|
26
|
+
if (graph.style !== void 0) metadata.style = graph.style;
|
|
26
27
|
return { graph: {
|
|
27
28
|
id: graph.id || void 0,
|
|
28
29
|
directed: graph.type === "directed",
|
|
@@ -38,6 +39,7 @@ function toJGF(graph) {
|
|
|
38
39
|
if (n.height !== void 0) meta.height = n.height;
|
|
39
40
|
if (n.shape) meta.shape = n.shape;
|
|
40
41
|
if (n.color) meta.color = n.color;
|
|
42
|
+
if (n.style !== void 0) meta.style = n.style;
|
|
41
43
|
if (n.ports !== void 0) meta.ports = n.ports;
|
|
42
44
|
return {
|
|
43
45
|
id: n.id,
|
|
@@ -48,7 +50,13 @@ function toJGF(graph) {
|
|
|
48
50
|
edges: graph.edges.map((e) => {
|
|
49
51
|
const meta = {};
|
|
50
52
|
if (e.data !== void 0) meta.data = e.data;
|
|
53
|
+
if (e.weight !== void 0) meta.weight = e.weight;
|
|
54
|
+
if (e.x !== void 0) meta.x = e.x;
|
|
55
|
+
if (e.y !== void 0) meta.y = e.y;
|
|
56
|
+
if (e.width !== void 0) meta.width = e.width;
|
|
57
|
+
if (e.height !== void 0) meta.height = e.height;
|
|
51
58
|
if (e.color) meta.color = e.color;
|
|
59
|
+
if (e.style !== void 0) meta.style = e.style;
|
|
52
60
|
if (e.sourcePort !== void 0) meta.sourcePort = e.sourcePort;
|
|
53
61
|
if (e.targetPort !== void 0) meta.targetPort = e.targetPort;
|
|
54
62
|
return {
|
|
@@ -89,6 +97,7 @@ function fromJGF(jgf) {
|
|
|
89
97
|
initialNodeId: g.metadata?.initialNodeId ?? null,
|
|
90
98
|
data: g.metadata?.data,
|
|
91
99
|
...g.metadata?.direction && { direction: g.metadata.direction },
|
|
100
|
+
...g.metadata?.style !== void 0 && { style: g.metadata.style },
|
|
92
101
|
nodes: g.nodes.map((n) => ({
|
|
93
102
|
type: "node",
|
|
94
103
|
id: n.id,
|
|
@@ -102,6 +111,7 @@ function fromJGF(jgf) {
|
|
|
102
111
|
...n.metadata?.height !== void 0 && { height: n.metadata.height },
|
|
103
112
|
...n.metadata?.shape && { shape: n.metadata.shape },
|
|
104
113
|
...n.metadata?.color && { color: n.metadata.color },
|
|
114
|
+
...n.metadata?.style !== void 0 && { style: n.metadata.style },
|
|
105
115
|
...n.metadata?.ports !== void 0 && { ports: n.metadata.ports }
|
|
106
116
|
})),
|
|
107
117
|
edges: g.edges.map((e, i) => ({
|
|
@@ -111,7 +121,13 @@ function fromJGF(jgf) {
|
|
|
111
121
|
targetId: e.target,
|
|
112
122
|
label: e.label ?? "",
|
|
113
123
|
data: e.metadata?.data,
|
|
124
|
+
...e.metadata?.weight !== void 0 && { weight: e.metadata.weight },
|
|
125
|
+
...e.metadata?.x !== void 0 && { x: e.metadata.x },
|
|
126
|
+
...e.metadata?.y !== void 0 && { y: e.metadata.y },
|
|
127
|
+
...e.metadata?.width !== void 0 && { width: e.metadata.width },
|
|
128
|
+
...e.metadata?.height !== void 0 && { height: e.metadata.height },
|
|
114
129
|
...e.metadata?.color && { color: e.metadata.color },
|
|
130
|
+
...e.metadata?.style !== void 0 && { style: e.metadata.style },
|
|
115
131
|
...e.metadata?.sourcePort !== void 0 && { sourcePort: e.metadata.sourcePort },
|
|
116
132
|
...e.metadata?.targetPort !== void 0 && { targetPort: e.metadata.targetPort }
|
|
117
133
|
}))
|
|
@@ -1415,10 +1415,16 @@ function fromMermaidState(input) {
|
|
|
1415
1415
|
const parentStack = [null];
|
|
1416
1416
|
const regionCounters = /* @__PURE__ */ new Map();
|
|
1417
1417
|
function ensureNode(id) {
|
|
1418
|
-
|
|
1418
|
+
const parentId = parentStack[parentStack.length - 1];
|
|
1419
|
+
const existing = nodeMap.get(id);
|
|
1420
|
+
if (existing) {
|
|
1421
|
+
if (parentId && existing.parentId === null && existing.id !== parentId) existing.parentId = parentId;
|
|
1422
|
+
return existing;
|
|
1423
|
+
}
|
|
1424
|
+
nodeMap.set(id, {
|
|
1419
1425
|
type: "node",
|
|
1420
1426
|
id,
|
|
1421
|
-
parentId
|
|
1427
|
+
parentId,
|
|
1422
1428
|
initialNodeId: null,
|
|
1423
1429
|
label: id,
|
|
1424
1430
|
data: {}
|
|
@@ -1462,6 +1468,15 @@ function fromMermaidState(input) {
|
|
|
1462
1468
|
parentStack.push(stateId);
|
|
1463
1469
|
continue;
|
|
1464
1470
|
}
|
|
1471
|
+
const compositeStateAsMatch = line.match(/^state\s+"([^"]+)"\s+as\s+(\S+)\s*\{\s*$/);
|
|
1472
|
+
if (compositeStateAsMatch) {
|
|
1473
|
+
const description = compositeStateAsMatch[1];
|
|
1474
|
+
const stateId = compositeStateAsMatch[2];
|
|
1475
|
+
const node = ensureNode(stateId);
|
|
1476
|
+
node.data.description = description;
|
|
1477
|
+
parentStack.push(stateId);
|
|
1478
|
+
continue;
|
|
1479
|
+
}
|
|
1465
1480
|
const stereotypeMatch = line.match(/^state\s+(\S+)\s+<<(choice|fork|join)>>\s*$/);
|
|
1466
1481
|
if (stereotypeMatch) {
|
|
1467
1482
|
const stateId = stereotypeMatch[1];
|
|
@@ -1542,16 +1557,27 @@ function fromMermaidState(input) {
|
|
|
1542
1557
|
}
|
|
1543
1558
|
continue;
|
|
1544
1559
|
}
|
|
1545
|
-
const noteMatch = line.match(/^note\s+(left|right)\s+of\s+(\S+)\s
|
|
1560
|
+
const noteMatch = line.match(/^note\s+(left|right)\s+of\s+(\S+)\s*(?::\s*(.*))?$/i);
|
|
1546
1561
|
if (noteMatch) {
|
|
1547
1562
|
const position = noteMatch[1].toLowerCase();
|
|
1548
1563
|
const stateId = noteMatch[2];
|
|
1549
|
-
const
|
|
1564
|
+
const inlineText = noteMatch[3]?.trim();
|
|
1565
|
+
const text = inlineText && inlineText.length > 0 ? inlineText : (() => {
|
|
1566
|
+
const content = [];
|
|
1567
|
+
while (i + 1 < lines.length) {
|
|
1568
|
+
i++;
|
|
1569
|
+
const noteLine = lines[i].trim();
|
|
1570
|
+
if (/^end\s+note$/i.test(noteLine)) break;
|
|
1571
|
+
content.push(noteLine);
|
|
1572
|
+
}
|
|
1573
|
+
return content.join("\n").trim();
|
|
1574
|
+
})();
|
|
1550
1575
|
const node = ensureNode(stateId);
|
|
1551
1576
|
if (!node.data.notes) node.data.notes = [];
|
|
1552
1577
|
node.data.notes.push({
|
|
1553
1578
|
position,
|
|
1554
|
-
text
|
|
1579
|
+
text,
|
|
1580
|
+
format: inlineText && inlineText.length > 0 ? "inline" : "block"
|
|
1555
1581
|
});
|
|
1556
1582
|
continue;
|
|
1557
1583
|
}
|
|
@@ -1687,9 +1713,9 @@ function toMermaidState(graph) {
|
|
|
1687
1713
|
if (node.data?.isStart || node.data?.isEnd) continue;
|
|
1688
1714
|
if (node.id.includes("_region_")) continue;
|
|
1689
1715
|
if (node.data?.stateType && node.data.stateType !== "parallel") lines.push(`${indent}state ${node.id} <<${node.data.stateType}>>`);
|
|
1690
|
-
if (node.data?.description) lines.push(`${indent}state "${escapeMermaidLabel(node.data.description)}" as ${node.id}`);
|
|
1691
1716
|
if (isParent.has(node.id)) {
|
|
1692
|
-
|
|
1717
|
+
const stateDecl = node.data?.description ? `state "${escapeMermaidLabel(node.data.description)}" as ${node.id} {` : `state ${node.id} {`;
|
|
1718
|
+
lines.push(`${indent}${stateDecl}`);
|
|
1693
1719
|
if (node.data?.direction) {
|
|
1694
1720
|
const mDir = DIRECTION_TO_MERMAID[node.data.direction];
|
|
1695
1721
|
if (mDir) lines.push(`${indent} direction ${mDir}`);
|
|
@@ -1702,8 +1728,12 @@ function toMermaidState(graph) {
|
|
|
1702
1728
|
}
|
|
1703
1729
|
} else writeNodes(node.id, indent + " ");
|
|
1704
1730
|
lines.push(`${indent}}`);
|
|
1705
|
-
}
|
|
1706
|
-
if (node.data?.notes) for (const note of node.data.notes)
|
|
1731
|
+
} else if (node.data?.description) lines.push(`${indent}state "${escapeMermaidLabel(node.data.description)}" as ${node.id}`);
|
|
1732
|
+
if (node.data?.notes) for (const note of node.data.notes) if (note.format === "block" || note.text.includes("\n")) {
|
|
1733
|
+
lines.push(`${indent}note ${note.position} of ${node.id}`);
|
|
1734
|
+
for (const noteLine of note.text.split("\n")) lines.push(`${indent} ${escapeMermaidLabel(noteLine)}`);
|
|
1735
|
+
lines.push(`${indent}end note`);
|
|
1736
|
+
} else lines.push(`${indent}note ${note.position} of ${node.id} : ${escapeMermaidLabel(note.text)}`);
|
|
1707
1737
|
}
|
|
1708
1738
|
}
|
|
1709
1739
|
writeNodes(null, " ");
|
|
@@ -9,6 +9,7 @@ type XYFlowEdge<TEdgeData extends Record<string, unknown> = Record<string, unkno
|
|
|
9
9
|
interface XYFlow<TNodeData extends Record<string, unknown> = Record<string, unknown>, TEdgeData extends Record<string, unknown> = Record<string, unknown>> {
|
|
10
10
|
nodes: XYFlowNode<TNodeData>[];
|
|
11
11
|
edges: XYFlowEdge<TEdgeData>[];
|
|
12
|
+
data?: Record<string, unknown>;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
15
|
* Converts a visual graph to xyflow (React Flow / Svelte Flow) format.
|