@graph-render/core 1.0.1

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 (161) hide show
  1. package/.eslintrc.json +6 -0
  2. package/CHANGELOG.md +45 -0
  3. package/dist/edges/collision.d.ts +8 -0
  4. package/dist/edges/collision.d.ts.map +1 -0
  5. package/dist/edges/collision.js +26 -0
  6. package/dist/edges/collision.js.map +1 -0
  7. package/dist/edges/geometry.d.ts +22 -0
  8. package/dist/edges/geometry.d.ts.map +1 -0
  9. package/dist/edges/geometry.js +75 -0
  10. package/dist/edges/geometry.js.map +1 -0
  11. package/dist/edges/index.d.ts +4 -0
  12. package/dist/edges/index.d.ts.map +1 -0
  13. package/dist/edges/index.js +4 -0
  14. package/dist/edges/index.js.map +1 -0
  15. package/dist/edges/pathBuilder.d.ts +11 -0
  16. package/dist/edges/pathBuilder.d.ts.map +1 -0
  17. package/dist/edges/pathBuilder.js +114 -0
  18. package/dist/edges/pathBuilder.js.map +1 -0
  19. package/dist/edges/pathCalculation.d.ts +14 -0
  20. package/dist/edges/pathCalculation.d.ts.map +1 -0
  21. package/dist/edges/pathCalculation.js +47 -0
  22. package/dist/edges/pathCalculation.js.map +1 -0
  23. package/dist/edges/routing.d.ts +6 -0
  24. package/dist/edges/routing.d.ts.map +1 -0
  25. package/dist/edges/routing.js +243 -0
  26. package/dist/edges/routing.js.map +1 -0
  27. package/dist/edges/sideSelection.d.ts +17 -0
  28. package/dist/edges/sideSelection.d.ts.map +1 -0
  29. package/dist/edges/sideSelection.js +45 -0
  30. package/dist/edges/sideSelection.js.map +1 -0
  31. package/dist/index.d.ts +7 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +6 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/layouts/centered.d.ts +6 -0
  36. package/dist/layouts/centered.d.ts.map +1 -0
  37. package/dist/layouts/centered.js +69 -0
  38. package/dist/layouts/centered.js.map +1 -0
  39. package/dist/layouts/compactBracket.d.ts +3 -0
  40. package/dist/layouts/compactBracket.d.ts.map +1 -0
  41. package/dist/layouts/compactBracket.js +7 -0
  42. package/dist/layouts/compactBracket.js.map +1 -0
  43. package/dist/layouts/dag.d.ts +3 -0
  44. package/dist/layouts/dag.d.ts.map +1 -0
  45. package/dist/layouts/dag.js +52 -0
  46. package/dist/layouts/dag.js.map +1 -0
  47. package/dist/layouts/forceDirected.d.ts +3 -0
  48. package/dist/layouts/forceDirected.d.ts.map +1 -0
  49. package/dist/layouts/forceDirected.js +176 -0
  50. package/dist/layouts/forceDirected.js.map +1 -0
  51. package/dist/layouts/grid.d.ts +6 -0
  52. package/dist/layouts/grid.d.ts.map +1 -0
  53. package/dist/layouts/grid.js +34 -0
  54. package/dist/layouts/grid.js.map +1 -0
  55. package/dist/layouts/index.d.ts +12 -0
  56. package/dist/layouts/index.d.ts.map +1 -0
  57. package/dist/layouts/index.js +88 -0
  58. package/dist/layouts/index.js.map +1 -0
  59. package/dist/layouts/orthogonalFlow.d.ts +3 -0
  60. package/dist/layouts/orthogonalFlow.d.ts.map +1 -0
  61. package/dist/layouts/orthogonalFlow.js +81 -0
  62. package/dist/layouts/orthogonalFlow.js.map +1 -0
  63. package/dist/layouts/radialTree.d.ts +3 -0
  64. package/dist/layouts/radialTree.d.ts.map +1 -0
  65. package/dist/layouts/radialTree.js +45 -0
  66. package/dist/layouts/radialTree.js.map +1 -0
  67. package/dist/layouts/tree.d.ts +6 -0
  68. package/dist/layouts/tree.d.ts.map +1 -0
  69. package/dist/layouts/tree.js +19 -0
  70. package/dist/layouts/tree.js.map +1 -0
  71. package/dist/layouts/treeAlignment.d.ts +10 -0
  72. package/dist/layouts/treeAlignment.d.ts.map +1 -0
  73. package/dist/layouts/treeAlignment.js +69 -0
  74. package/dist/layouts/treeAlignment.js.map +1 -0
  75. package/dist/layouts/treePositioning.d.ts +14 -0
  76. package/dist/layouts/treePositioning.d.ts.map +1 -0
  77. package/dist/layouts/treePositioning.js +30 -0
  78. package/dist/layouts/treePositioning.js.map +1 -0
  79. package/dist/layouts/treeTopology.d.ts +29 -0
  80. package/dist/layouts/treeTopology.d.ts.map +1 -0
  81. package/dist/layouts/treeTopology.js +137 -0
  82. package/dist/layouts/treeTopology.js.map +1 -0
  83. package/dist/rendering/defaultRenderers.d.ts +10 -0
  84. package/dist/rendering/defaultRenderers.d.ts.map +1 -0
  85. package/dist/rendering/defaultRenderers.js +85 -0
  86. package/dist/rendering/defaultRenderers.js.map +1 -0
  87. package/dist/rendering/index.d.ts +4 -0
  88. package/dist/rendering/index.d.ts.map +1 -0
  89. package/dist/rendering/index.js +4 -0
  90. package/dist/rendering/index.js.map +1 -0
  91. package/dist/rendering/svg.d.ts +7 -0
  92. package/dist/rendering/svg.d.ts.map +1 -0
  93. package/dist/rendering/svg.js +256 -0
  94. package/dist/rendering/svg.js.map +1 -0
  95. package/dist/rendering/utils.d.ts +5 -0
  96. package/dist/rendering/utils.d.ts.map +1 -0
  97. package/dist/rendering/utils.js +33 -0
  98. package/dist/rendering/utils.js.map +1 -0
  99. package/dist/utils/config.d.ts +36 -0
  100. package/dist/utils/config.d.ts.map +1 -0
  101. package/dist/utils/config.js +115 -0
  102. package/dist/utils/config.js.map +1 -0
  103. package/dist/utils/constants.d.ts +15 -0
  104. package/dist/utils/constants.d.ts.map +1 -0
  105. package/dist/utils/constants.js +19 -0
  106. package/dist/utils/constants.js.map +1 -0
  107. package/dist/utils/graphParser.d.ts +16 -0
  108. package/dist/utils/graphParser.d.ts.map +1 -0
  109. package/dist/utils/graphParser.js +277 -0
  110. package/dist/utils/graphParser.js.map +1 -0
  111. package/dist/utils/graphTraversal.d.ts +13 -0
  112. package/dist/utils/graphTraversal.d.ts.map +1 -0
  113. package/dist/utils/graphTraversal.js +28 -0
  114. package/dist/utils/graphTraversal.js.map +1 -0
  115. package/dist/utils/index.d.ts +8 -0
  116. package/dist/utils/index.d.ts.map +1 -0
  117. package/dist/utils/index.js +7 -0
  118. package/dist/utils/index.js.map +1 -0
  119. package/dist/utils/nodeMetrics.d.ts +8 -0
  120. package/dist/utils/nodeMetrics.d.ts.map +1 -0
  121. package/dist/utils/nodeMetrics.js +12 -0
  122. package/dist/utils/nodeMetrics.js.map +1 -0
  123. package/dist/utils/nodeSizing.d.ts +3 -0
  124. package/dist/utils/nodeSizing.d.ts.map +1 -0
  125. package/dist/utils/nodeSizing.js +77 -0
  126. package/dist/utils/nodeSizing.js.map +1 -0
  127. package/package.json +29 -0
  128. package/project.json +32 -0
  129. package/src/edges/collision.ts +31 -0
  130. package/src/edges/geometry.ts +85 -0
  131. package/src/edges/index.ts +3 -0
  132. package/src/edges/pathBuilder.ts +136 -0
  133. package/src/edges/pathCalculation.ts +69 -0
  134. package/src/edges/routing.ts +459 -0
  135. package/src/edges/sideSelection.ts +67 -0
  136. package/src/index.ts +50 -0
  137. package/src/layouts/centered.ts +114 -0
  138. package/src/layouts/compactBracket.ts +14 -0
  139. package/src/layouts/dag.ts +76 -0
  140. package/src/layouts/forceDirected.ts +224 -0
  141. package/src/layouts/grid.ts +50 -0
  142. package/src/layouts/index.ts +148 -0
  143. package/src/layouts/orthogonalFlow.ts +112 -0
  144. package/src/layouts/radialTree.ts +77 -0
  145. package/src/layouts/tree.ts +35 -0
  146. package/src/layouts/treeAlignment.ts +107 -0
  147. package/src/layouts/treePositioning.ts +55 -0
  148. package/src/layouts/treeTopology.ts +184 -0
  149. package/src/rendering/defaultRenderers.ts +110 -0
  150. package/src/rendering/index.ts +3 -0
  151. package/src/rendering/svg.ts +346 -0
  152. package/src/rendering/utils.ts +41 -0
  153. package/src/utils/config.ts +198 -0
  154. package/src/utils/constants.ts +24 -0
  155. package/src/utils/graphParser.ts +495 -0
  156. package/src/utils/graphTraversal.ts +32 -0
  157. package/src/utils/index.ts +19 -0
  158. package/src/utils/nodeMetrics.ts +23 -0
  159. package/src/utils/nodeSizing.ts +97 -0
  160. package/tsconfig.json +11 -0
  161. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,176 @@
1
+ import { DEFAULT_NODE_GAP, DEFAULT_NODE_SIZE, DEFAULT_PADDING } from '../utils';
2
+ import { gridLayout } from './grid';
3
+ const FORCE_LAYOUT_CACHE_LIMIT = 24;
4
+ const MAX_SYNC_FORCE_NODES = 250;
5
+ // NOTE: this cache is intentionally module-level so warm hits persist across
6
+ // sequential renders of the same graph (common during viewport-only updates).
7
+ // Trade-off: all <Graph> instances in the same JS bundle share the same 24-slot
8
+ // LRU. Keys include the full node/edge topology, so stale hits are extremely
9
+ // unlikely. If you mount many independent graphs with similar-but-distinct
10
+ // topologies and see layout lag, increase FORCE_LAYOUT_CACHE_LIMIT.
11
+ const forceLayoutCache = new Map();
12
+ const buildForceLayoutCacheKey = (nodes, edges, pad, width, height, gap) => {
13
+ try {
14
+ return JSON.stringify({
15
+ pad,
16
+ width,
17
+ height,
18
+ gap,
19
+ nodes: nodes.map((node) => ({
20
+ id: node.id,
21
+ size: node.size,
22
+ label: node.label,
23
+ })),
24
+ edges: edges.map((edge) => ({
25
+ id: edge.id,
26
+ source: edge.source,
27
+ target: edge.target,
28
+ type: edge.type,
29
+ })),
30
+ });
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ };
36
+ const getCachedForceLayout = (cacheKey) => {
37
+ if (!cacheKey) {
38
+ return undefined;
39
+ }
40
+ const cached = forceLayoutCache.get(cacheKey);
41
+ if (!cached) {
42
+ return undefined;
43
+ }
44
+ forceLayoutCache.delete(cacheKey);
45
+ forceLayoutCache.set(cacheKey, cached);
46
+ return cached.map((node) => ({
47
+ ...node,
48
+ position: { ...node.position },
49
+ size: node.size ? { ...node.size } : undefined,
50
+ }));
51
+ };
52
+ const setCachedForceLayout = (cacheKey, nodes) => {
53
+ if (!cacheKey) {
54
+ return;
55
+ }
56
+ if (forceLayoutCache.size >= FORCE_LAYOUT_CACHE_LIMIT) {
57
+ const oldestKey = forceLayoutCache.keys().next().value;
58
+ if (oldestKey) {
59
+ forceLayoutCache.delete(oldestKey);
60
+ }
61
+ }
62
+ forceLayoutCache.set(cacheKey, nodes.map((node) => ({
63
+ ...node,
64
+ position: { ...node.position },
65
+ size: node.size ? { ...node.size } : undefined,
66
+ })));
67
+ };
68
+ const clampPoint = (point, width, height, pad, node) => {
69
+ const nodeWidth = node.size?.width ?? DEFAULT_NODE_SIZE.width;
70
+ const nodeHeight = node.size?.height ?? DEFAULT_NODE_SIZE.height;
71
+ return {
72
+ x: Math.min(Math.max(point.x, pad), width - pad - nodeWidth),
73
+ y: Math.min(Math.max(point.y, pad), height - pad - nodeHeight),
74
+ };
75
+ };
76
+ const getRequiredPoint = (points, nodeId) => {
77
+ const point = points.get(nodeId);
78
+ if (!point) {
79
+ throw new Error(`Force-directed layout could not resolve point data for node "${nodeId}".`);
80
+ }
81
+ return point;
82
+ };
83
+ export const forceDirectedLayout = (nodes, edges, pad = DEFAULT_PADDING, width = 960, height = 720, gap = DEFAULT_NODE_GAP) => {
84
+ if (!nodes.length) {
85
+ return [];
86
+ }
87
+ if (nodes.length > MAX_SYNC_FORCE_NODES) {
88
+ return gridLayout(nodes, pad, gap);
89
+ }
90
+ const cacheKey = buildForceLayoutCacheKey(nodes, edges, pad, width, height, gap);
91
+ const cached = getCachedForceLayout(cacheKey);
92
+ if (cached) {
93
+ return cached;
94
+ }
95
+ const area = Math.max((width - pad * 2) * (height - pad * 2), 1);
96
+ const k = Math.sqrt(area / Math.max(nodes.length, 1));
97
+ const positions = new Map();
98
+ // FIX: removed the `adjacency` Map that was built here but never read by the
99
+ // algorithm. Repulsion iterates node pairs directly; attraction iterates the
100
+ // `edges` array directly. Building the map was O(e) wasted work per layout call.
101
+ nodes.forEach((node, index) => {
102
+ const angle = (2 * Math.PI * index) / Math.max(nodes.length, 1);
103
+ const radius = Math.min(width, height) * 0.25;
104
+ positions.set(node.id, {
105
+ x: width / 2 + radius * Math.cos(angle),
106
+ y: height / 2 + radius * Math.sin(angle),
107
+ });
108
+ });
109
+ // FIX: removed the edges.forEach that populated `adjacency` (now deleted).
110
+ // The attraction-force loop below already iterates `edges` directly.
111
+ for (let iteration = 0; iteration < 80; iteration += 1) {
112
+ const displacement = new Map();
113
+ nodes.forEach((node) => displacement.set(node.id, { x: 0, y: 0 }));
114
+ for (let i = 0; i < nodes.length; i += 1) {
115
+ for (let j = i + 1; j < nodes.length; j += 1) {
116
+ const source = nodes[i];
117
+ const target = nodes[j];
118
+ const sourcePos = getRequiredPoint(positions, source.id);
119
+ const targetPos = getRequiredPoint(positions, target.id);
120
+ const dx = sourcePos.x - targetPos.x;
121
+ const dy = sourcePos.y - targetPos.y;
122
+ const distance = Math.max(1, Math.hypot(dx, dy));
123
+ const force = (k * k) / distance;
124
+ const offsetX = (dx / distance) * force;
125
+ const offsetY = (dy / distance) * force;
126
+ const sourceDisp = getRequiredPoint(displacement, source.id);
127
+ const targetDisp = getRequiredPoint(displacement, target.id);
128
+ sourceDisp.x += offsetX;
129
+ sourceDisp.y += offsetY;
130
+ targetDisp.x -= offsetX;
131
+ targetDisp.y -= offsetY;
132
+ }
133
+ }
134
+ edges.forEach((edge) => {
135
+ const sourcePos = getRequiredPoint(positions, edge.source);
136
+ const targetPos = getRequiredPoint(positions, edge.target);
137
+ const dx = sourcePos.x - targetPos.x;
138
+ const dy = sourcePos.y - targetPos.y;
139
+ const distance = Math.max(1, Math.hypot(dx, dy));
140
+ const force = (distance * distance) / k;
141
+ const offsetX = (dx / distance) * force;
142
+ const offsetY = (dy / distance) * force;
143
+ const sourceDisp = getRequiredPoint(displacement, edge.source);
144
+ const targetDisp = getRequiredPoint(displacement, edge.target);
145
+ sourceDisp.x -= offsetX;
146
+ sourceDisp.y -= offsetY;
147
+ targetDisp.x += offsetX;
148
+ targetDisp.y += offsetY;
149
+ });
150
+ const temperature = Math.max(2, gap * (1 - iteration / 80));
151
+ nodes.forEach((node) => {
152
+ const point = getRequiredPoint(positions, node.id);
153
+ const disp = getRequiredPoint(displacement, node.id);
154
+ const magnitude = Math.max(1, Math.hypot(disp.x, disp.y));
155
+ const nextPoint = {
156
+ x: point.x + (disp.x / magnitude) * Math.min(magnitude, temperature),
157
+ y: point.y + (disp.y / magnitude) * Math.min(magnitude, temperature),
158
+ };
159
+ positions.set(node.id, clampPoint(nextPoint, width, height, pad, node));
160
+ });
161
+ }
162
+ const positionedNodes = nodes.map((node) => {
163
+ const point = getRequiredPoint(positions, node.id);
164
+ const size = node.size ?? DEFAULT_NODE_SIZE;
165
+ return {
166
+ ...node,
167
+ position: {
168
+ x: point.x - size.width / 2,
169
+ y: point.y - size.height / 2,
170
+ },
171
+ };
172
+ });
173
+ setCachedForceLayout(cacheKey, positionedNodes);
174
+ return positionedNodes;
175
+ };
176
+ //# sourceMappingURL=forceDirected.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forceDirected.js","sourceRoot":"","sources":["../../src/layouts/forceDirected.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { NodeData, PositionedNode } from '@graph-render/types';
2
+ /**
3
+ * Layout nodes in a grid pattern
4
+ */
5
+ export declare const gridLayout: (nodes: NodeData[], pad?: number, gap?: number) => PositionedNode[];
6
+ //# sourceMappingURL=grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../src/layouts/grid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAS,MAAM,qBAAqB,CAAC;AA6BtE;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,QAAQ,EAAE,EACjB,MAAK,MAAwB,EAC7B,MAAK,MAAyB,KAC7B,cAAc,EAahB,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { DEFAULT_NODE_SIZE, DEFAULT_PADDING, DEFAULT_NODE_GAP } from '../utils';
2
+ /**
3
+ * Calculate grid dimensions based on node count
4
+ */
5
+ const calculateGridColumns = (nodeCount) => {
6
+ return Math.ceil(Math.sqrt(nodeCount));
7
+ };
8
+ /**
9
+ * Calculate grid position for a node at a given index
10
+ */
11
+ const calculateGridPosition = (index, columns, nodeWidth, nodeHeight, padding, gap) => {
12
+ const col = index % columns;
13
+ const row = Math.floor(index / columns);
14
+ return {
15
+ x: padding + col * (nodeWidth + gap),
16
+ y: padding + row * (nodeHeight + gap),
17
+ };
18
+ };
19
+ /**
20
+ * Layout nodes in a grid pattern
21
+ */
22
+ export const gridLayout = (nodes, pad = DEFAULT_PADDING, gap = DEFAULT_NODE_GAP) => {
23
+ const count = nodes.length;
24
+ const cols = calculateGridColumns(count);
25
+ return nodes.map((node, idx) => {
26
+ if (node.position)
27
+ return node;
28
+ const nodeWidth = node.size?.width ?? DEFAULT_NODE_SIZE.width;
29
+ const nodeHeight = node.size?.height ?? DEFAULT_NODE_SIZE.height;
30
+ const position = calculateGridPosition(idx, cols, nodeWidth, nodeHeight, pad, gap);
31
+ return { ...node, position };
32
+ });
33
+ };
34
+ //# sourceMappingURL=grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.js","sourceRoot":"","sources":["../../src/layouts/grid.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEhF;;GAEG;AACH,MAAM,oBAAoB,GAAG,CAAC,SAAiB,EAAU,EAAE;IACzD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;AACzC,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAC5B,KAAa,EACb,OAAe,EACf,SAAiB,EACjB,UAAkB,EAClB,OAAe,EACf,GAAW,EACJ,EAAE;IACT,MAAM,GAAG,GAAG,KAAK,GAAG,OAAO,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC;IACxC,OAAO;QACL,CAAC,EAAE,OAAO,GAAG,GAAG,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC;QACpC,CAAC,EAAE,OAAO,GAAG,GAAG,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;KACtC,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,KAAiB,EACjB,MAAc,eAAe,EAC7B,MAAc,gBAAgB,EACZ,EAAE;IACpB,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAEzC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAC7B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAsB,CAAC;QAEjD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI,iBAAiB,CAAC,KAAK,CAAC;QAC9D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,IAAI,iBAAiB,CAAC,MAAM,CAAC;QACjE,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAEnF,OAAO,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAoB,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { PositionedNode, LayoutOptions } from '@graph-render/types';
2
+ import { gridLayout } from './grid';
3
+ import { centeredLayout } from './centered';
4
+ import { radialTreeLayout } from './radialTree';
5
+ import { treeLayout } from './tree';
6
+ import { dagLayout } from './dag';
7
+ import { forceDirectedLayout } from './forceDirected';
8
+ import { compactBracketLayout } from './compactBracket';
9
+ import { orthogonalFlowLayout } from './orthogonalFlow';
10
+ export declare const layoutNodes: (options: LayoutOptions) => PositionedNode[];
11
+ export { gridLayout, centeredLayout, radialTreeLayout, treeLayout, dagLayout, forceDirectedLayout, compactBracketLayout, orthogonalFlowLayout, };
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/layouts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EACd,aAAa,EAGd,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AA4FxD,eAAO,MAAM,WAAW,GAAI,SAAS,aAAa,KAAG,cAAc,EA6BlE,CAAC;AAEF,OAAO,EACL,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { LayoutType, LayoutDirection, } from '@graph-render/types';
2
+ import { DEFAULT_NODE_GAP, DEFAULT_PADDING, applyNodeSizing } from '../utils';
3
+ import { gridLayout } from './grid';
4
+ import { centeredLayout } from './centered';
5
+ import { radialTreeLayout } from './radialTree';
6
+ import { treeLayout } from './tree';
7
+ import { dagLayout } from './dag';
8
+ import { forceDirectedLayout } from './forceDirected';
9
+ import { compactBracketLayout } from './compactBracket';
10
+ import { orthogonalFlowLayout } from './orthogonalFlow';
11
+ const runSelectedLayout = (options, sizedNodes) => {
12
+ const { edges, padding, theme, layout, width, height, layoutDirection } = options;
13
+ const gap = theme?.nodeGap ?? DEFAULT_NODE_GAP;
14
+ const pad = padding ?? DEFAULT_PADDING;
15
+ const resolvedLayout = layout ?? LayoutType.Grid;
16
+ const assertUnreachable = (value) => {
17
+ throw new Error(`Unsupported layout type: ${String(value)}`);
18
+ };
19
+ switch (resolvedLayout) {
20
+ case LayoutType.Tree:
21
+ return treeLayout(sizedNodes, edges, pad, gap, layoutDirection ?? LayoutDirection.LTR, height);
22
+ case LayoutType.Radial:
23
+ return radialTreeLayout(sizedNodes, edges, pad, width, height, gap);
24
+ case LayoutType.Centered:
25
+ return centeredLayout(sizedNodes, pad, width, height);
26
+ case LayoutType.Dag:
27
+ return dagLayout(sizedNodes, edges, pad, gap, layoutDirection ?? LayoutDirection.LTR, width, height);
28
+ case LayoutType.ForceDirected:
29
+ return forceDirectedLayout(sizedNodes, edges, pad, width, height, gap);
30
+ case LayoutType.CompactBracket:
31
+ return compactBracketLayout(sizedNodes, edges, pad, gap, layoutDirection ?? LayoutDirection.LTR, height);
32
+ case LayoutType.OrthogonalFlow:
33
+ return orthogonalFlowLayout(sizedNodes, edges, pad, gap, layoutDirection ?? LayoutDirection.LTR, width, height);
34
+ case LayoutType.Grid:
35
+ return gridLayout(sizedNodes, pad, gap);
36
+ default:
37
+ return assertUnreachable(resolvedLayout);
38
+ }
39
+ };
40
+ const getAnchoredLayoutOffset = (autoLayout, fixedNodes) => {
41
+ if (!fixedNodes.length) {
42
+ return { x: 0, y: 0 };
43
+ }
44
+ const positionedFixed = fixedNodes
45
+ .map((fixedNode) => {
46
+ const laidOut = autoLayout.find((node) => node.id === fixedNode.id);
47
+ return laidOut ? { fixedNode, laidOut } : null;
48
+ })
49
+ .filter((entry) => entry !== null);
50
+ if (!positionedFixed.length) {
51
+ return { x: 0, y: 0 };
52
+ }
53
+ const totals = positionedFixed.reduce((acc, entry) => ({
54
+ x: acc.x + (entry.fixedNode.position.x - entry.laidOut.position.x),
55
+ y: acc.y + (entry.fixedNode.position.y - entry.laidOut.position.y),
56
+ }), { x: 0, y: 0 });
57
+ return {
58
+ x: totals.x / positionedFixed.length,
59
+ y: totals.y / positionedFixed.length,
60
+ };
61
+ };
62
+ export const layoutNodes = (options) => {
63
+ const sizedNodes = applyNodeSizing(options.nodes, options);
64
+ const missingPositions = sizedNodes.some((node) => !node.position);
65
+ if (!missingPositions) {
66
+ return sizedNodes;
67
+ }
68
+ const fixedNodes = sizedNodes.filter((node) => Boolean(node.position));
69
+ const autoLayoutInput = sizedNodes.map((node) => ({ ...node, position: undefined }));
70
+ const autoLayout = runSelectedLayout(options, autoLayoutInput);
71
+ const offset = getAnchoredLayoutOffset(autoLayout, fixedNodes);
72
+ const fixedNodeMap = new Map(fixedNodes.map((node) => [node.id, node]));
73
+ return autoLayout.map((node) => {
74
+ const fixedNode = fixedNodeMap.get(node.id);
75
+ if (fixedNode) {
76
+ return fixedNode;
77
+ }
78
+ return {
79
+ ...node,
80
+ position: {
81
+ x: node.position.x + offset.x,
82
+ y: node.position.y + offset.y,
83
+ },
84
+ };
85
+ });
86
+ };
87
+ export { gridLayout, centeredLayout, radialTreeLayout, treeLayout, dagLayout, forceDirectedLayout, compactBracketLayout, orthogonalFlowLayout, };
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/layouts/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { EdgeData, LayoutDirection, NodeData, PositionedNode } from '@graph-render/types';
2
+ export declare const orthogonalFlowLayout: (nodes: NodeData[], edges: EdgeData[], pad?: number, gap?: number, direction?: LayoutDirection, width?: number, height?: number) => PositionedNode[];
3
+ //# sourceMappingURL=orthogonalFlow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orthogonalFlow.d.ts","sourceRoot":"","sources":["../../src/layouts/orthogonalFlow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAU1F,eAAO,MAAM,oBAAoB,GAC/B,OAAO,QAAQ,EAAE,EACjB,OAAO,QAAQ,EAAE,EACjB,MAAK,MAAwB,EAC7B,MAAK,MAAyB,EAC9B,YAAW,eAAqC,EAChD,QAAO,MAAsB,EAC7B,SAAQ,MAAuB,KAC9B,cAAc,EA6FhB,CAAC"}
@@ -0,0 +1,81 @@
1
+ import { LayoutDirection } from '@graph-render/types';
2
+ import { DEFAULT_NODE_GAP, DEFAULT_NODE_SIZE, DEFAULT_PADDING } from '../utils';
3
+ import { assignDagLevels } from './treeTopology';
4
+ const VERTICAL_GAP_RATIO = 0.45;
5
+ const VERTICAL_GAP_MIN = 20;
6
+ const VERTICAL_GAP_HEIGHT_RATIO = 0.3;
7
+ const DEFAULT_WIDTH = 960;
8
+ const DEFAULT_HEIGHT = 720;
9
+ export const orthogonalFlowLayout = (nodes, edges, pad = DEFAULT_PADDING, gap = DEFAULT_NODE_GAP, direction = LayoutDirection.LTR, width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT) => {
10
+ if (!nodes.length) {
11
+ return [];
12
+ }
13
+ const safePad = Number.isFinite(pad) && pad >= 0 ? pad : DEFAULT_PADDING;
14
+ const safeGap = Number.isFinite(gap) && gap >= 0 ? gap : DEFAULT_NODE_GAP;
15
+ const safeWidth = Number.isFinite(width) && width > 0 ? width : DEFAULT_WIDTH;
16
+ const safeHeight = Number.isFinite(height) && height > 0 ? height : DEFAULT_HEIGHT;
17
+ const { levels } = assignDagLevels(nodes, edges);
18
+ const buckets = new Map();
19
+ nodes.forEach((node) => {
20
+ const level = levels.get(node.id);
21
+ if (level == null) {
22
+ throw new Error(`DAG layout could not assign a level to node "${node.id}".`);
23
+ }
24
+ const bucket = buckets.get(level) ?? [];
25
+ bucket.push(node);
26
+ buckets.set(level, bucket);
27
+ });
28
+ const isRTL = direction === LayoutDirection.RTL;
29
+ // Sort columns by level once so per-column x accumulation is deterministic.
30
+ const sortedColumns = Array.from(buckets.entries()).sort((a, b) => a[0] - b[0]);
31
+ // Per-column max node width drives column pitch; avoids a single wide node in
32
+ // one column inflating the spacing of every other column.
33
+ const colMaxWidths = new Map(sortedColumns.map(([level, levelNodes]) => [
34
+ level,
35
+ levelNodes.reduce((max, node) => Math.max(max, node.size?.width ?? DEFAULT_NODE_SIZE.width), 0),
36
+ ]));
37
+ // Accumulate column x start positions so each column occupies exactly its own
38
+ // content width rather than the global max width.
39
+ const colX = new Map();
40
+ if (isRTL) {
41
+ let xCursor = safeWidth - safePad;
42
+ for (const [level] of sortedColumns) {
43
+ xCursor -= colMaxWidths.get(level);
44
+ colX.set(level, xCursor);
45
+ xCursor -= safeGap;
46
+ }
47
+ }
48
+ else {
49
+ let xCursor = safePad;
50
+ for (const [level] of sortedColumns) {
51
+ colX.set(level, xCursor);
52
+ xCursor += colMaxWidths.get(level) + safeGap;
53
+ }
54
+ }
55
+ return sortedColumns.flatMap(([level, levelNodes]) => {
56
+ const colMaxWidth = colMaxWidths.get(level);
57
+ const colStartX = colX.get(level);
58
+ const colMaxNodeHeight = levelNodes.reduce((max, node) => Math.max(max, node.size?.height ?? DEFAULT_NODE_SIZE.height), 0);
59
+ // Gap is proportional to both the user-supplied spacing parameter and each
60
+ // column's tallest node so that visually dense columns stay readable.
61
+ const verticalGap = Math.max(VERTICAL_GAP_MIN, safeGap * VERTICAL_GAP_RATIO, colMaxNodeHeight * VERTICAL_GAP_HEIGHT_RATIO);
62
+ const contentHeight = levelNodes.reduce((sum, node) => sum + (node.size?.height ?? DEFAULT_NODE_SIZE.height), 0);
63
+ const totalGap = verticalGap * Math.max(levelNodes.length - 1, 0);
64
+ const centeredY = (safeHeight - contentHeight - totalGap) / 2;
65
+ // When content + gaps exceed the available height, centeredY goes negative
66
+ // and the column is top-anchored at safePad instead of disappearing off-canvas.
67
+ const maxY = safeHeight - safePad;
68
+ let y = Math.max(safePad, centeredY);
69
+ return levelNodes.map((node) => {
70
+ const nodeHeight = node.size?.height ?? DEFAULT_NODE_SIZE.height;
71
+ const nodeWidth = node.size?.width ?? DEFAULT_NODE_SIZE.width;
72
+ const position = {
73
+ x: colStartX + (isRTL ? colMaxWidth - nodeWidth : 0),
74
+ y: Math.min(y, maxY - nodeHeight),
75
+ };
76
+ y += nodeHeight + verticalGap;
77
+ return { ...node, position };
78
+ });
79
+ });
80
+ };
81
+ //# sourceMappingURL=orthogonalFlow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orthogonalFlow.js","sourceRoot":"","sources":["../../src/layouts/orthogonalFlow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { EdgeData, NodeData, PositionedNode } from '@graph-render/types';
2
+ export declare const radialTreeLayout: (nodes: NodeData[], edges: EdgeData[], pad?: number, width?: number, height?: number, gap?: number) => PositionedNode[];
3
+ //# sourceMappingURL=radialTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radialTree.d.ts","sourceRoot":"","sources":["../../src/layouts/radialTree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAWzE,eAAO,MAAM,gBAAgB,GAC3B,OAAO,QAAQ,EAAE,EACjB,OAAO,QAAQ,EAAE,EACjB,MAAK,MAAwB,EAC7B,QAAO,MAAY,EACnB,SAAQ,MAAY,EACpB,MAAK,MAAyB,KAC7B,cAAc,EA0DhB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { DEFAULT_NODE_GAP, DEFAULT_NODE_SIZE, DEFAULT_PADDING } from '../utils';
2
+ import { assertHierarchicalGraph, assignNodesToLevels, buildGraphTopology, findRootNodes, groupNodesByLevel, } from './treeTopology';
3
+ import { centeredLayout } from './centered';
4
+ export const radialTreeLayout = (nodes, edges, pad = DEFAULT_PADDING, width = 960, height = 720, gap = DEFAULT_NODE_GAP) => {
5
+ if (!nodes.length) {
6
+ return [];
7
+ }
8
+ if (!edges.length) {
9
+ return centeredLayout(nodes, pad, width, height);
10
+ }
11
+ assertHierarchicalGraph(nodes, edges);
12
+ const { incoming, outgoing } = buildGraphTopology(edges);
13
+ const rootIds = findRootNodes(nodes, incoming);
14
+ const levelsMap = assignNodesToLevels(nodes, rootIds, outgoing);
15
+ const levels = groupNodesByLevel(nodes, levelsMap);
16
+ const centerX = width / 2;
17
+ const centerY = height / 2;
18
+ // FIX: use reduce instead of spread+Math.max to avoid a RangeError when the
19
+ // node array exceeds the JS engine's argument-count limit (~125 k in V8).
20
+ const maxNodeSize = nodes.reduce((max, node) => Math.max(max, node.size?.width ?? DEFAULT_NODE_SIZE.width, node.size?.height ?? DEFAULT_NODE_SIZE.height), 0);
21
+ const maxRadius = Math.max(0, Math.min(width, height) / 2 - pad - maxNodeSize / 2);
22
+ const radiusStep = levels.length > 1 ? maxRadius / (levels.length - 1) : 0;
23
+ // FIX: pre-build an id→node map to avoid an O(n) Array.find inside the
24
+ // levels.flatMap loop, which was O(n²) overall.
25
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
26
+ return levels.flatMap((level, levelIndex) => {
27
+ const radius = levelIndex === 0 ? 0 : Math.max(radiusStep * levelIndex, maxNodeSize + gap * 0.4);
28
+ return level.map((nodeId, nodeIndex) => {
29
+ const node = nodeById.get(nodeId);
30
+ const size = node.size ?? DEFAULT_NODE_SIZE;
31
+ const angle = level.length === 1 ? -Math.PI / 2 : (2 * Math.PI * nodeIndex) / level.length - Math.PI / 2;
32
+ const position = levelIndex === 0
33
+ ? { x: centerX - size.width / 2, y: centerY - size.height / 2 }
34
+ : {
35
+ x: centerX + radius * Math.cos(angle) - size.width / 2,
36
+ y: centerY + radius * Math.sin(angle) - size.height / 2,
37
+ };
38
+ return {
39
+ ...node,
40
+ position,
41
+ };
42
+ });
43
+ });
44
+ };
45
+ //# sourceMappingURL=radialTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radialTree.js","sourceRoot":"","sources":["../../src/layouts/radialTree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { NodeData, EdgeData, PositionedNode, LayoutDirection } from '@graph-render/types';
2
+ /**
3
+ * Layout nodes in a tree/hierarchical structure
4
+ */
5
+ export declare const treeLayout: (nodes: NodeData[], edges: EdgeData[], pad?: number, gap?: number, direction?: LayoutDirection, containerHeight?: number) => PositionedNode[];
6
+ //# sourceMappingURL=tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/layouts/tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAY1F;;GAEG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,QAAQ,EAAE,EACjB,OAAO,QAAQ,EAAE,EACjB,MAAK,MAAwB,EAC7B,MAAK,MAAyB,EAC9B,YAAW,eAAqC,EAChD,kBAAkB,MAAM,KACvB,cAAc,EAYhB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { LayoutDirection } from '@graph-render/types';
2
+ import { DEFAULT_PADDING, DEFAULT_NODE_GAP } from '../utils';
3
+ import { assertHierarchicalGraph, buildGraphTopology, findRootNodes, assignNodesToLevels, groupNodesByLevel, } from './treeTopology';
4
+ import { calculateTreeMetrics } from './treePositioning';
5
+ import { positionNodesInLevels, alignNodesToParents } from './treeAlignment';
6
+ /**
7
+ * Layout nodes in a tree/hierarchical structure
8
+ */
9
+ export const treeLayout = (nodes, edges, pad = DEFAULT_PADDING, gap = DEFAULT_NODE_GAP, direction = LayoutDirection.LTR, containerHeight) => {
10
+ assertHierarchicalGraph(nodes, edges);
11
+ const { incoming, outgoing } = buildGraphTopology(edges);
12
+ const rootIds = findRootNodes(nodes, incoming);
13
+ const levelMap = assignNodesToLevels(nodes, rootIds, outgoing);
14
+ const levels = groupNodesByLevel(nodes, levelMap);
15
+ const metrics = calculateTreeMetrics(nodes, levels, gap, pad, containerHeight);
16
+ const positioned = positionNodesInLevels(nodes, levels, levelMap, metrics, gap, pad, direction);
17
+ return alignNodesToParents(positioned, edges, levels, metrics.maxLevel);
18
+ };
19
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/layouts/tree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE7E;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,KAAiB,EACjB,KAAiB,EACjB,MAAc,eAAe,EAC7B,MAAc,gBAAgB,EAC9B,YAA6B,eAAe,CAAC,GAAG,EAChD,eAAwB,EACN,EAAE;IACpB,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAEtC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,CAAC,CAAC;IAE/E,MAAM,UAAU,GAAG,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;IAEhG,OAAO,mBAAmB,CAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC1E,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { NodeData, EdgeData, PositionedNode, TreeMetrics, LayoutDirection } from '@graph-render/types';
2
+ /**
3
+ * Position all nodes initially based on their level
4
+ */
5
+ export declare const positionNodesInLevels: (nodes: NodeData[], levels: string[][], levelMap: Map<string, number>, metrics: TreeMetrics, gap: number, padding: number, direction: LayoutDirection) => PositionedNode[];
6
+ /**
7
+ * Align nodes with multiple parents to their parent's average position
8
+ */
9
+ export declare const alignNodesToParents: (positioned: PositionedNode[], edges: EdgeData[], levels: string[][], maxLevel: number) => PositionedNode[];
10
+ //# sourceMappingURL=treeAlignment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treeAlignment.d.ts","sourceRoot":"","sources":["../../src/layouts/treeAlignment.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,WAAW,EACX,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAI7B;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAChC,OAAO,QAAQ,EAAE,EACjB,QAAQ,MAAM,EAAE,EAAE,EAClB,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,SAAS,WAAW,EACpB,KAAK,MAAM,EACX,SAAS,MAAM,EACf,WAAW,eAAe,KACzB,cAAc,EAqBhB,CAAC;AAsBF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC9B,YAAY,cAAc,EAAE,EAC5B,OAAO,QAAQ,EAAE,EACjB,QAAQ,MAAM,EAAE,EAAE,EAClB,UAAU,MAAM,KACf,cAAc,EAkChB,CAAC"}