@diagrammo/dgmo 0.8.4 → 0.8.6

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 (68) hide show
  1. package/.claude/commands/dgmo.md +300 -0
  2. package/.cursorrules +20 -2
  3. package/.github/copilot-instructions.md +20 -2
  4. package/.windsurfrules +20 -2
  5. package/AGENTS.md +23 -3
  6. package/dist/cli.cjs +191 -189
  7. package/dist/editor.cjs +5 -18
  8. package/dist/editor.cjs.map +1 -1
  9. package/dist/editor.js +5 -18
  10. package/dist/editor.js.map +1 -1
  11. package/dist/highlight.cjs +543 -0
  12. package/dist/highlight.cjs.map +1 -0
  13. package/dist/highlight.d.cts +32 -0
  14. package/dist/highlight.d.ts +32 -0
  15. package/dist/highlight.js +513 -0
  16. package/dist/highlight.js.map +1 -0
  17. package/dist/index.cjs +3253 -3356
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +77 -56
  20. package/dist/index.d.ts +77 -56
  21. package/dist/index.js +3247 -3349
  22. package/dist/index.js.map +1 -1
  23. package/docs/ai-integration.md +1 -1
  24. package/docs/language-reference.md +113 -33
  25. package/gallery/fixtures/boxes-and-lines.dgmo +64 -0
  26. package/gallery/fixtures/slope.dgmo +7 -6
  27. package/package.json +26 -6
  28. package/src/boxes-and-lines/collapse.ts +78 -0
  29. package/src/boxes-and-lines/layout.ts +319 -0
  30. package/src/boxes-and-lines/parser.ts +694 -0
  31. package/src/boxes-and-lines/renderer.ts +848 -0
  32. package/src/boxes-and-lines/types.ts +40 -0
  33. package/src/c4/parser.ts +10 -5
  34. package/src/c4/renderer.ts +232 -56
  35. package/src/chart.ts +9 -4
  36. package/src/cli.ts +49 -6
  37. package/src/completion.ts +25 -33
  38. package/src/d3.ts +187 -46
  39. package/src/dgmo-router.ts +3 -7
  40. package/src/echarts.ts +38 -2
  41. package/src/editor/highlight-api.ts +444 -0
  42. package/src/editor/keywords.ts +6 -19
  43. package/src/er/parser.ts +10 -4
  44. package/src/gantt/parser.ts +7 -4
  45. package/src/gantt/renderer.ts +3 -5
  46. package/src/index.ts +106 -50
  47. package/src/infra/parser.ts +7 -5
  48. package/src/infra/renderer.ts +2 -2
  49. package/src/kanban/parser.ts +7 -5
  50. package/src/kanban/renderer.ts +43 -18
  51. package/src/org/parser.ts +7 -4
  52. package/src/org/renderer.ts +40 -29
  53. package/src/sequence/parser.ts +11 -5
  54. package/src/sequence/renderer.ts +114 -45
  55. package/src/sitemap/parser.ts +8 -4
  56. package/src/sitemap/renderer.ts +137 -57
  57. package/src/utils/legend-svg.ts +44 -20
  58. package/src/utils/parsing.ts +1 -1
  59. package/src/utils/tag-groups.ts +21 -1
  60. package/gallery/fixtures/initiative-status-full.dgmo +0 -46
  61. package/gallery/fixtures/initiative-status-phases.dgmo +0 -29
  62. package/gallery/fixtures/initiative-status.dgmo +0 -9
  63. package/src/initiative-status/collapse.ts +0 -76
  64. package/src/initiative-status/filter.ts +0 -63
  65. package/src/initiative-status/layout.ts +0 -650
  66. package/src/initiative-status/parser.ts +0 -629
  67. package/src/initiative-status/renderer.ts +0 -1199
  68. package/src/initiative-status/types.ts +0 -57
@@ -105,4 +105,4 @@ dgmo --chart-types --json # Machine-readable chart type list
105
105
 
106
106
  Run `dgmo --chart-types` for the full list, or see `docs/language-reference.md`.
107
107
 
108
- 29 types: bar, line, area, multi-line, pie, doughnut, radar, polar-area, bar-stacked, scatter, sankey, chord, function, heatmap, funnel, slope, wordcloud, arc, timeline, venn, quadrant, sequence, flowchart, class, er, org, kanban, c4, initiative-status, infra.
108
+ 34 types: bar, line, area, multi-line, pie, doughnut, radar, polar-area, bar-stacked, scatter, sankey, chord, function, heatmap, funnel, slope, wordcloud, arc, timeline, venn, quadrant, sequence, flowchart, state, class, er, org, kanban, c4, initiative-status, sitemap, infra, gantt, boxes-and-lines.
@@ -51,10 +51,11 @@ Syntax changes introduced in the consistency cleanup. Old forms now produce erro
51
51
  11. [Initiative-Status Diagrams](#11-initiative-status-diagrams)
52
52
  12. [Sitemap Diagrams](#12-sitemap-diagrams)
53
53
  13. [Gantt Charts](#13-gantt-charts)
54
- 14. [Timeline Diagrams](#14-timeline-diagrams)
55
- 15. [Data Charts](#15-data-charts)
56
- 16. [Visualizations](#16-visualizations)
57
- 17. [Colon Usage Summary](#17-colon-usage-summary)
54
+ 14. [Boxes and Lines Diagrams](#14-boxes-and-lines-diagrams)
55
+ 15. [Timeline Diagrams](#15-timeline-diagrams)
56
+ 16. [Data Charts](#16-data-charts)
57
+ 17. [Visualizations](#17-visualizations)
58
+ 18. [Colon Usage Summary](#18-colon-usage-summary)
58
59
 
59
60
  ---
60
61
 
@@ -95,7 +96,7 @@ tag GroupName [alias X]
95
96
  - First entry is default unless another is marked `default`
96
97
  - Must appear before diagram content
97
98
 
98
- **Diagram types that support tags**: sequence, infra, org, c4, er, kanban, gantt, initiative-status, sitemap, timeline
99
+ **Diagram types that support tags**: sequence, infra, org, c4, er, kanban, gantt, initiative-status, sitemap, timeline, boxes-and-lines
99
100
 
100
101
  ### 1.4 Pipe Metadata
101
102
 
@@ -925,15 +926,86 @@ parallel
925
926
 
926
927
  ---
927
928
 
928
- ## 14. Timeline Diagrams
929
+ ## 14. Boxes and Lines Diagrams
929
930
 
930
931
  ### 14.1 Declaration
931
932
 
933
+ ```
934
+ boxes-and-lines [Title]
935
+ ```
936
+
937
+ Requires explicit first line — no heuristic detection. Default layout direction is left-to-right.
938
+
939
+ ### 14.2 Nodes
940
+
941
+ ```
942
+ NodeLabel
943
+ NodeLabel | key: value, key2: value2
944
+ NodeLabel | description: Some text here
945
+ NodeLabel [type]
946
+ NodeLabel [type] | key: value
947
+ ```
948
+
949
+ Nodes are created explicitly or implicitly (when referenced in edges). Shape is inferred from the name unless overridden with `[type]`.
950
+
951
+ Valid types: `service`, `database`, `actor`, `queue`, `cache`, `gateway`, `external`, `networking`, `frontend`, `default`.
952
+
953
+ ### 14.3 Edges
954
+
955
+ ```
956
+ Source -> Target
957
+ Source -> Target | key: value
958
+ Source -label-> Target
959
+ Source <-> Target
960
+ Source <-label-> Target
961
+ ```
962
+
963
+ Indented shorthand (source from preceding node):
964
+ ```
965
+ API | description: Main gateway
966
+ -routes-> UserService
967
+ -routes-> ProductService
968
+ ```
969
+
970
+ ### 14.4 Groups
971
+
972
+ ```
973
+ [Group Name]
974
+ indented nodes...
975
+ ```
976
+
977
+ Nested groups (max depth 2):
978
+ ```
979
+ [AWS]
980
+ [us-east-1]
981
+ API
982
+ DB
983
+ ```
984
+
985
+ Group metadata cascades to children. Group-to-group edges:
986
+ ```
987
+ [Region A] -> [Region B]
988
+ [Region A] -VPN-> [Region B]
989
+ ```
990
+
991
+ ### 14.5 Options
992
+
993
+ - `direction TB` — top-to-bottom layout (default: `LR`)
994
+ - `mode shapes` — render as inferred shapes (default: `rectangles`)
995
+ - `active-tag GroupName`
996
+ - `hide team:Backend` (colon syntax for tag:value)
997
+
998
+ ---
999
+
1000
+ ## 15. Timeline Diagrams
1001
+
1002
+ ### 15.1 Declaration
1003
+
932
1004
  ```
933
1005
  timeline [Title]
934
1006
  ```
935
1007
 
936
- ### 14.2 Events
1008
+ ### 15.2 Events
937
1009
 
938
1010
  **Point event:**
939
1011
  ```
@@ -958,7 +1030,7 @@ timeline [Title]
958
1030
  Date formats: `YYYY`, `YYYY-MM`, `YYYY-MM-DD`, `YYYY-MM-DD HH:MM`
959
1031
  Duration units: `min`, `h`, `d`, `w`, `m`, `y`
960
1032
 
961
- ### 14.3 Eras
1033
+ ### 15.3 Eras
962
1034
 
963
1035
  **Flat form:**
964
1036
  ```
@@ -972,7 +1044,7 @@ era
972
1044
  1718 -> 1720 Woodes Rogers Era (orange)
973
1045
  ```
974
1046
 
975
- ### 14.4 Markers
1047
+ ### 15.4 Markers
976
1048
 
977
1049
  **Flat form:**
978
1050
  ```
@@ -986,7 +1058,7 @@ marker
986
1058
  1720-01 End of Golden Age (red)
987
1059
  ```
988
1060
 
989
- ### 14.5 Groups
1061
+ ### 15.5 Groups
990
1062
 
991
1063
  ```
992
1064
  [Royal Navy]
@@ -995,9 +1067,9 @@ marker
995
1067
 
996
1068
  ---
997
1069
 
998
- ## 15. Data Charts
1070
+ ## 16. Data Charts
999
1071
 
1000
- ### 15.1 Simple Charts (bar, line, pie, doughnut, area, polar-area, radar, bar-stacked)
1072
+ ### 16.1 Simple Charts (bar, line, pie, doughnut, area, polar-area, radar, bar-stacked)
1001
1073
 
1002
1074
  **Declaration:** `bar [Title]`, `line [Title]`, etc.
1003
1075
 
@@ -1043,7 +1115,7 @@ stacked
1043
1115
  era Day 1 -> Day 3 Rough Seas (red)
1044
1116
  ```
1045
1117
 
1046
- ### 15.2 Scatter / Bubble Charts
1118
+ ### 16.2 Scatter / Bubble Charts
1047
1119
 
1048
1120
  **Data rows (space-separated, NO colon):**
1049
1121
  ```
@@ -1069,7 +1141,7 @@ no-labels
1069
1141
 
1070
1142
  Labels are on by default. Use `no-labels` to hide point names.
1071
1143
 
1072
- ### 15.3 Heatmap
1144
+ ### 16.3 Heatmap
1073
1145
 
1074
1146
  **Columns:**
1075
1147
  ```
@@ -1085,7 +1157,7 @@ RowLabel 5 4 3
1085
1157
 
1086
1158
  Commas between values are optional. Thousands commas supported.
1087
1159
 
1088
- ### 15.4 Function Charts (Colon REQUIRED)
1160
+ ### 16.4 Function Charts (Colon REQUIRED)
1089
1161
 
1090
1162
  ```
1091
1163
  function Trajectories
@@ -1102,7 +1174,7 @@ The colon between name and expression is **required** — both sides can contain
1102
1174
  **Options:**
1103
1175
  - `shade` (boolean; off by default, shades area below curves when enabled)
1104
1176
 
1105
- ### 15.5 Sankey Charts
1177
+ ### 16.5 Sankey Charts
1106
1178
 
1107
1179
  **Tree structure (indented, space-separated):**
1108
1180
  ```
@@ -1119,7 +1191,7 @@ Source -- Target 2000
1119
1191
 
1120
1192
  `->` = directed, `--` = undirected. Thousands commas supported in values.
1121
1193
 
1122
- ### 15.6 Chord Charts
1194
+ ### 16.6 Chord Charts
1123
1195
 
1124
1196
  ```
1125
1197
  Blackbeard -- Bonnet 150 // undirected
@@ -1128,7 +1200,7 @@ Roberts -> Rackham 20 // directed
1128
1200
 
1129
1201
  Thousands commas supported in values.
1130
1202
 
1131
- ### 15.7 Funnel Charts
1203
+ ### 16.7 Funnel Charts
1132
1204
 
1133
1205
  **Data rows (space-separated, NO colon):**
1134
1206
  ```
@@ -1141,24 +1213,31 @@ Thousands commas supported.
1141
1213
 
1142
1214
  ---
1143
1215
 
1144
- ## 16. Visualizations
1216
+ ## 17. Visualizations
1145
1217
 
1146
- ### 16.1 Slope Charts (Colon REQUIRED for data)
1218
+ ### 17.1 Slope Charts
1147
1219
 
1148
1220
  ```
1149
1221
  slope Fleet Strength
1150
1222
 
1151
- 1715 1725
1223
+ period 1715 1725
1152
1224
 
1153
- Blackbeard: 40 4
1154
- Roberts: 12 52
1225
+ Blackbeard 40 4
1226
+ Roberts 12 52
1155
1227
  ```
1156
1228
 
1157
- - Period labels on their own line, commas optional
1158
- - Data rows: `Label: value1 value2` — colon required, commas between values optional
1159
- - Thousands commas supported in values
1229
+ - Period directive required: `period Label1 Label2` (one-line) or indented block for multi-token labels:
1230
+ ```
1231
+ period
1232
+ Before COVID
1233
+ After COVID
1234
+ ```
1235
+ - Data rows: `Label value1 value2` — space-separated, no colons, no commas between values
1236
+ - Thousands commas within values supported (e.g., `1,000`)
1237
+ - Color annotations: `Label (color) value1 value2`
1238
+ - Minimum 2 periods required
1160
1239
 
1161
- ### 16.2 Wordcloud
1240
+ ### 17.2 Wordcloud
1162
1241
 
1163
1242
  ```
1164
1243
  wordcloud Pirate Skills
@@ -1173,7 +1252,7 @@ navigation 88
1173
1252
  - Data: space-separated only (`word value`)
1174
1253
  - Options: `rotate none|mixed|angled`, `max N`, `size min max`
1175
1254
 
1176
- ### 16.3 Arc Diagrams
1255
+ ### 17.3 Arc Diagrams
1177
1256
 
1178
1257
  ```
1179
1258
  arc Pirate Alliances
@@ -1188,7 +1267,7 @@ order group
1188
1267
  - Link: `Source -> Target weight` — space before optional weight
1189
1268
  - Options: `order appearance|name|group|degree`
1190
1269
 
1191
- ### 16.4 Venn Diagrams
1270
+ ### 17.4 Venn Diagrams
1192
1271
 
1193
1272
  ```
1194
1273
  venn Skill Overlap
@@ -1204,7 +1283,7 @@ sw + nav + lead Legendary Pirates
1204
1283
  - Set declaration: `Name(color) alias X`
1205
1284
  - Intersections: `Set1 + Set2 Label` — label follows the last set reference (no colon)
1206
1285
 
1207
- ### 16.5 Quadrant Diagrams
1286
+ ### 17.5 Quadrant Diagrams
1208
1287
 
1209
1288
  ```
1210
1289
  quadrant Crew Assessment
@@ -1227,7 +1306,7 @@ Navigator 0.85, 0.8
1227
1306
 
1228
1307
  ---
1229
1308
 
1230
- ## 17. Colon Usage Summary
1309
+ ## 18. Colon Usage Summary
1231
1310
 
1232
1311
  ### Constructs Where Colons Are REQUIRED
1233
1312
 
@@ -1239,7 +1318,6 @@ Navigator 0.85, 0.8
1239
1318
  | Class field types | class | `+ name: string` |
1240
1319
  | Class method returns | class | `+ sail(): void` |
1241
1320
  | Function expressions | function | `f(x): x^2 + 1` |
1242
- | Slope data rows | slope | `Blackbeard: 40 4` |
1243
1321
  | Hide tag values | initiative-status | `hide phase:Planning` |
1244
1322
 
1245
1323
  ### Colons OPTIONAL
@@ -1265,13 +1343,15 @@ Navigator 0.85, 0.8
1265
1343
  | Section dividers | sequence | `== Phase ==` |
1266
1344
  | Comments | all | `// comment` |
1267
1345
  | Wordcloud data | wordcloud | `swordsmanship 95` |
1346
+ | Slope data rows | slope | `Blackbeard 40 4` |
1347
+ | Slope period directive | slope | `period 1715 1725` |
1268
1348
  | Venn intersections | venn | `sw + nav Sea Raiders` |
1269
1349
 
1270
1350
  ### The Rule
1271
1351
 
1272
1352
  **Colons appear in two contexts:**
1273
1353
  1. **Value assignment** — `key: value` in pipe metadata, indented tag/metadata assignment (org, c4), and hide directives
1274
- 2. **Type/expression separation** — where labels can contain spaces and a delimiter is needed (function expressions, slope data, class members)
1354
+ 2. **Type/expression separation** — where labels can contain spaces and a delimiter is needed (function expressions, class members)
1275
1355
 
1276
1356
  **Exception**: Known-schema properties (infra node properties, ER columns) remain space-separated even though they are indented. The colon rule applies to open-ended metadata, not fixed property schemas.
1277
1357
 
@@ -0,0 +1,64 @@
1
+ boxes-and-lines E-Commerce Platform
2
+
3
+ tag Team t Backend(blue), Frontend(green), Platform(purple)
4
+ tag Priority p High(red), Medium(orange), Low(gray)
5
+
6
+ active-tag Team
7
+ hide priority:Low
8
+
9
+ direction LR
10
+
11
+ // --- Services ---
12
+ API Gateway | t: Backend, description: Main entry point for all requests
13
+ -routes-> UserService
14
+ -routes-> ProductService
15
+ -routes-> OrderService
16
+
17
+ UserService | t: Backend, description: Handles auth and profiles
18
+ -reads-> UserDB
19
+ -checks-> SessionCache
20
+
21
+ ProductService | t: Frontend, description: Product catalog and search
22
+ -queries-> ProductDB
23
+ -invalidates-> ProductCache
24
+
25
+ OrderService | t: Backend, description: Order processing pipeline
26
+ -writes-> OrderDB
27
+ -publishes-> OrderQueue
28
+
29
+ // --- Data Stores ---
30
+ UserDB | t: Platform
31
+ ProductDB | t: Platform
32
+ OrderDB | t: Platform
33
+ SessionCache | t: Platform
34
+ ProductCache | t: Platform
35
+ OrderQueue | t: Platform
36
+
37
+ // --- External ---
38
+ PaymentGateway | t: Backend, description: Stripe integration
39
+ OrderService -charges-> PaymentGateway
40
+
41
+ // --- Consumer ---
42
+ WebApp | t: Frontend
43
+ WebApp -> API Gateway
44
+
45
+ Admin | t: Backend
46
+ Admin -> API Gateway
47
+
48
+ // --- Groups ---
49
+ [AWS us-east-1]
50
+ [Compute]
51
+ API Gateway
52
+ UserService
53
+ ProductService
54
+ OrderService
55
+ [Data]
56
+ UserDB
57
+ ProductDB
58
+ OrderDB
59
+
60
+ // --- Group edge ---
61
+ OrderQueue -> PaymentGateway
62
+
63
+ // --- Bidirectional ---
64
+ SessionCache <-syncs-> ProductCache | p: Medium
@@ -1,8 +1,9 @@
1
1
  slope Programming Language Popularity
2
2
 
3
- 2020, 2022, 2025
4
- Python (blue) 3, 1, 1
5
- JavaScript (yellow) 1, 2, 2
6
- TypeScript (cyan) 7, 4, 3
7
- Rust (orange) 18, 12, 5
8
- Go (green) 10, 8, 7
3
+ period 2020 2022 2025
4
+
5
+ Python (blue) 3 1 1
6
+ JavaScript (yellow) 1 2 2
7
+ TypeScript (cyan) 7 4 3
8
+ Rust (orange) 18 12 5
9
+ Go (green) 10 8 7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diagrammo/dgmo",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "DGMO diagram markup language — parser, renderer, and color system",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,6 +30,16 @@
30
30
  "types": "./dist/editor.d.cts",
31
31
  "default": "./dist/editor.cjs"
32
32
  }
33
+ },
34
+ "./highlight": {
35
+ "import": {
36
+ "types": "./dist/highlight.d.ts",
37
+ "default": "./dist/highlight.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/highlight.d.cts",
41
+ "default": "./dist/highlight.cjs"
42
+ }
33
43
  }
34
44
  },
35
45
  "files": [
@@ -116,10 +126,20 @@
116
126
  "@lezer/lr": "^1.4.8"
117
127
  },
118
128
  "peerDependenciesMeta": {
119
- "@codemirror/language": { "optional": true },
120
- "@codemirror/state": { "optional": true },
121
- "@lezer/common": { "optional": true },
122
- "@lezer/highlight": { "optional": true },
123
- "@lezer/lr": { "optional": true }
129
+ "@codemirror/language": {
130
+ "optional": true
131
+ },
132
+ "@codemirror/state": {
133
+ "optional": true
134
+ },
135
+ "@lezer/common": {
136
+ "optional": true
137
+ },
138
+ "@lezer/highlight": {
139
+ "optional": true
140
+ },
141
+ "@lezer/lr": {
142
+ "optional": true
143
+ }
124
144
  }
125
145
  }
@@ -0,0 +1,78 @@
1
+ // ============================================================
2
+ // Boxes and Lines — Collapse/Expand Transform
3
+ // ============================================================
4
+
5
+ import type { ParsedBoxesAndLines, BLGroup } from './types';
6
+
7
+ export interface BLCollapseResult {
8
+ parsed: ParsedBoxesAndLines;
9
+ collapsedChildCounts: Map<string, number>;
10
+ originalGroups: BLGroup[];
11
+ }
12
+
13
+ /**
14
+ * Pure transform: returns a new ParsedBoxesAndLines with collapsed groups
15
+ * removed from the diagram content.
16
+ *
17
+ * - Children of collapsed groups removed from nodes
18
+ * - Edges redirected: endpoints in collapsed groups → group ID
19
+ * - Internal edges (both in same collapsed group) dropped
20
+ * - Duplicate edges (same source, target, label) deduplicated
21
+ * - Collapsed groups removed from groups[] (layout handles as nodes)
22
+ */
23
+ export function collapseBoxesAndLines(
24
+ parsed: ParsedBoxesAndLines,
25
+ collapsedGroups: Set<string>
26
+ ): BLCollapseResult {
27
+ const originalGroups = parsed.groups;
28
+
29
+ if (collapsedGroups.size === 0) {
30
+ return { parsed, collapsedChildCounts: new Map(), originalGroups };
31
+ }
32
+
33
+ // Build group lookup by label
34
+ const groupByLabel = new Map<string, BLGroup>();
35
+ for (const group of parsed.groups) {
36
+ groupByLabel.set(group.label, group);
37
+ }
38
+
39
+ // Build node → collapsed group lookup
40
+ const nodeToGroup = new Map<string, string>();
41
+ const collapsedChildCounts = new Map<string, number>();
42
+
43
+ for (const groupLabel of collapsedGroups) {
44
+ const group = groupByLabel.get(groupLabel);
45
+ if (!group) continue;
46
+ const groupId = `__group_${groupLabel}`;
47
+
48
+ for (const child of group.children) {
49
+ nodeToGroup.set(child, groupId);
50
+ }
51
+ collapsedChildCounts.set(groupLabel, group.children.length);
52
+ }
53
+
54
+ // Filter nodes: remove children of collapsed groups
55
+ const nodes = parsed.nodes.filter((n) => !nodeToGroup.has(n.label));
56
+
57
+ // Remap and deduplicate edges
58
+ const edgeKeys = new Set<string>();
59
+ const edges: typeof parsed.edges = [];
60
+ for (const edge of parsed.edges) {
61
+ const src = nodeToGroup.get(edge.source) ?? edge.source;
62
+ const tgt = nodeToGroup.get(edge.target) ?? edge.target;
63
+ if (src === tgt) continue;
64
+ const key = `${src}|${tgt}|${edge.label ?? ''}`;
65
+ if (edgeKeys.has(key)) continue;
66
+ edgeKeys.add(key);
67
+ edges.push({ ...edge, source: src, target: tgt });
68
+ }
69
+
70
+ // Keep only groups that are not collapsed
71
+ const groups = parsed.groups.filter((g) => !collapsedGroups.has(g.label));
72
+
73
+ return {
74
+ parsed: { ...parsed, nodes, edges, groups },
75
+ collapsedChildCounts,
76
+ originalGroups,
77
+ };
78
+ }