@diagrammo/dgmo 0.2.5 → 0.2.7

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.
@@ -0,0 +1,222 @@
1
+ import dagre from '@dagrejs/dagre';
2
+ import type {
3
+ ParsedGraph,
4
+ GraphNode,
5
+ GraphEdge,
6
+ GraphShape,
7
+ } from './types';
8
+
9
+ export interface LayoutNode {
10
+ id: string;
11
+ label: string;
12
+ shape: GraphShape;
13
+ color?: string;
14
+ group?: string;
15
+ lineNumber: number;
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ }
21
+
22
+ export interface LayoutEdge {
23
+ source: string;
24
+ target: string;
25
+ points: { x: number; y: number }[];
26
+ label?: string;
27
+ color?: string;
28
+ lineNumber: number;
29
+ }
30
+
31
+ export interface LayoutGroup {
32
+ id: string;
33
+ label: string;
34
+ color?: string;
35
+ x: number;
36
+ y: number;
37
+ width: number;
38
+ height: number;
39
+ }
40
+
41
+ export interface LayoutResult {
42
+ nodes: LayoutNode[];
43
+ edges: LayoutEdge[];
44
+ groups: LayoutGroup[];
45
+ width: number;
46
+ height: number;
47
+ }
48
+
49
+ const GROUP_PADDING = 20;
50
+
51
+ function computeNodeWidth(label: string, shape: GraphShape): number {
52
+ const base = Math.max(120, label.length * 9 + 40);
53
+ if (shape === 'subroutine') return base + 10;
54
+ return base;
55
+ }
56
+
57
+ function computeNodeHeight(shape: GraphShape): number {
58
+ return shape === 'decision' ? 60 : 50;
59
+ }
60
+
61
+ export function layoutGraph(graph: ParsedGraph): LayoutResult {
62
+ if (graph.nodes.length === 0) {
63
+ return { nodes: [], edges: [], groups: [], width: 0, height: 0 };
64
+ }
65
+
66
+ const g = new dagre.graphlib.Graph({ compound: true });
67
+ g.setGraph({
68
+ rankdir: graph.direction,
69
+ nodesep: 50,
70
+ ranksep: 60,
71
+ edgesep: 20,
72
+ });
73
+ g.setDefaultEdgeLabel(() => ({}));
74
+
75
+ // Build a lookup for original node data
76
+ const nodeDataMap = new Map<string, GraphNode>();
77
+ for (const node of graph.nodes) {
78
+ nodeDataMap.set(node.id, node);
79
+ }
80
+
81
+ // Add group parent nodes
82
+ if (graph.groups) {
83
+ for (const group of graph.groups) {
84
+ g.setNode(group.id, {
85
+ label: group.label,
86
+ clusterLabelPos: 'top',
87
+ });
88
+ }
89
+ }
90
+
91
+ // Add nodes with computed dimensions
92
+ for (const node of graph.nodes) {
93
+ const width = computeNodeWidth(node.label, node.shape);
94
+ const height = computeNodeHeight(node.shape);
95
+ g.setNode(node.id, { label: node.label, width, height });
96
+
97
+ // Set parent for grouped nodes
98
+ if (node.group && graph.groups?.some((gr) => gr.id === node.group)) {
99
+ g.setParent(node.id, node.group);
100
+ }
101
+ }
102
+
103
+ // Build edge lookup for original data
104
+ const edgeDataMap = new Map<string, GraphEdge>();
105
+ for (const edge of graph.edges) {
106
+ const key = `${edge.source}->${edge.target}`;
107
+ edgeDataMap.set(key, edge);
108
+ g.setEdge(edge.source, edge.target, {
109
+ label: edge.label ?? '',
110
+ });
111
+ }
112
+
113
+ // Run layout
114
+ dagre.layout(g);
115
+
116
+ // Extract positioned nodes
117
+ const layoutNodes: LayoutNode[] = graph.nodes.map((node) => {
118
+ const pos = g.node(node.id);
119
+ return {
120
+ id: node.id,
121
+ label: node.label,
122
+ shape: node.shape,
123
+ color: node.color,
124
+ group: node.group,
125
+ lineNumber: node.lineNumber,
126
+ x: pos.x,
127
+ y: pos.y,
128
+ width: pos.width,
129
+ height: pos.height,
130
+ };
131
+ });
132
+
133
+ // Extract edge waypoints
134
+ const layoutEdges: LayoutEdge[] = graph.edges.map((edge) => {
135
+ const edgeData = g.edge(edge.source, edge.target);
136
+ return {
137
+ source: edge.source,
138
+ target: edge.target,
139
+ points: edgeData?.points ?? [],
140
+ label: edge.label,
141
+ color: edge.color,
142
+ lineNumber: edge.lineNumber,
143
+ };
144
+ });
145
+
146
+ // Compute group bounding boxes from member node positions
147
+ const layoutGroups: LayoutGroup[] = [];
148
+ if (graph.groups) {
149
+ const nodeMap = new Map(layoutNodes.map((n) => [n.id, n]));
150
+ for (const group of graph.groups) {
151
+ const members = group.nodeIds
152
+ .map((id) => nodeMap.get(id))
153
+ .filter((n): n is LayoutNode => n !== undefined);
154
+
155
+ if (members.length === 0) {
156
+ layoutGroups.push({
157
+ id: group.id,
158
+ label: group.label,
159
+ color: group.color,
160
+ x: 0,
161
+ y: 0,
162
+ width: 0,
163
+ height: 0,
164
+ });
165
+ continue;
166
+ }
167
+
168
+ let minX = Infinity;
169
+ let minY = Infinity;
170
+ let maxX = -Infinity;
171
+ let maxY = -Infinity;
172
+
173
+ for (const member of members) {
174
+ const left = member.x - member.width / 2;
175
+ const right = member.x + member.width / 2;
176
+ const top = member.y - member.height / 2;
177
+ const bottom = member.y + member.height / 2;
178
+ if (left < minX) minX = left;
179
+ if (right > maxX) maxX = right;
180
+ if (top < minY) minY = top;
181
+ if (bottom > maxY) maxY = bottom;
182
+ }
183
+
184
+ layoutGroups.push({
185
+ id: group.id,
186
+ label: group.label,
187
+ color: group.color,
188
+ x: minX - GROUP_PADDING,
189
+ y: minY - GROUP_PADDING,
190
+ width: maxX - minX + GROUP_PADDING * 2,
191
+ height: maxY - minY + GROUP_PADDING * 2,
192
+ });
193
+ }
194
+ }
195
+
196
+ // Compute total diagram dimensions
197
+ let totalWidth = 0;
198
+ let totalHeight = 0;
199
+ for (const node of layoutNodes) {
200
+ const right = node.x + node.width / 2;
201
+ const bottom = node.y + node.height / 2;
202
+ if (right > totalWidth) totalWidth = right;
203
+ if (bottom > totalHeight) totalHeight = bottom;
204
+ }
205
+ for (const group of layoutGroups) {
206
+ const right = group.x + group.width;
207
+ const bottom = group.y + group.height;
208
+ if (right > totalWidth) totalWidth = right;
209
+ if (bottom > totalHeight) totalHeight = bottom;
210
+ }
211
+ // Add margin
212
+ totalWidth += 40;
213
+ totalHeight += 40;
214
+
215
+ return {
216
+ nodes: layoutNodes,
217
+ edges: layoutEdges,
218
+ groups: layoutGroups,
219
+ width: totalWidth,
220
+ height: totalHeight,
221
+ };
222
+ }
@@ -0,0 +1,44 @@
1
+ export type GraphShape =
2
+ | 'terminal' // () — rounded/stadium
3
+ | 'process' // [] — rectangle
4
+ | 'decision' // <> — diamond
5
+ | 'io' // // — parallelogram
6
+ | 'subroutine' // [[]] — double-bordered rectangle
7
+ | 'document'; // [~] — wavy-bottom rectangle
8
+
9
+ export type GraphDirection = 'TB' | 'LR';
10
+
11
+ export interface GraphNode {
12
+ id: string;
13
+ label: string;
14
+ shape: GraphShape;
15
+ color?: string;
16
+ group?: string;
17
+ lineNumber: number;
18
+ }
19
+
20
+ export interface GraphEdge {
21
+ source: string;
22
+ target: string;
23
+ label?: string;
24
+ color?: string;
25
+ lineNumber: number;
26
+ }
27
+
28
+ export interface GraphGroup {
29
+ id: string;
30
+ label: string;
31
+ color?: string;
32
+ nodeIds: string[];
33
+ lineNumber: number;
34
+ }
35
+
36
+ export interface ParsedGraph {
37
+ type: 'flowchart';
38
+ title?: string;
39
+ direction: GraphDirection;
40
+ nodes: GraphNode[];
41
+ edges: GraphEdge[];
42
+ groups?: GraphGroup[];
43
+ error?: string;
44
+ }
package/src/index.ts CHANGED
@@ -37,13 +37,16 @@ export {
37
37
  parseSequenceDgmo,
38
38
  looksLikeSequence,
39
39
  isSequenceBlock,
40
+ isSequenceNote,
40
41
  } from './sequence/parser';
41
42
  export type {
42
43
  ParsedSequenceDgmo,
43
44
  SequenceParticipant,
44
45
  SequenceMessage,
45
46
  SequenceBlock,
47
+ ElseIfBranch,
46
48
  SequenceSection,
49
+ SequenceNote,
47
50
  SequenceElement,
48
51
  SequenceGroup,
49
52
  ParticipantType,
@@ -57,6 +60,27 @@ export {
57
60
  export { parseQuadrant } from './dgmo-mermaid';
58
61
  export type { ParsedQuadrant } from './dgmo-mermaid';
59
62
 
63
+ export { parseFlowchart, looksLikeFlowchart } from './graph/flowchart-parser';
64
+
65
+ export type {
66
+ ParsedGraph,
67
+ GraphNode,
68
+ GraphEdge,
69
+ GraphGroup,
70
+ GraphShape,
71
+ GraphDirection,
72
+ } from './graph/types';
73
+
74
+ export { layoutGraph } from './graph/layout';
75
+ export type {
76
+ LayoutResult,
77
+ LayoutNode,
78
+ LayoutEdge,
79
+ LayoutGroup,
80
+ } from './graph/layout';
81
+
82
+ export { renderFlowchart, renderFlowchartForExport } from './graph/flowchart-renderer';
83
+
60
84
  // ============================================================
61
85
  // Config Builders (produce framework-specific config objects)
62
86
  // ============================================================