@chengchenccc/tui 0.1.1-rc.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 (226) hide show
  1. package/dist/autocomplete.d.ts +56 -0
  2. package/dist/autocomplete.d.ts.map +1 -0
  3. package/dist/autocomplete.js +647 -0
  4. package/dist/components/box.d.ts +22 -0
  5. package/dist/components/box.d.ts.map +1 -0
  6. package/dist/components/box.js +103 -0
  7. package/dist/components/card.d.ts +33 -0
  8. package/dist/components/card.d.ts.map +1 -0
  9. package/dist/components/card.js +49 -0
  10. package/dist/components/editor.d.ts +257 -0
  11. package/dist/components/editor.d.ts.map +1 -0
  12. package/dist/components/editor.js +1980 -0
  13. package/dist/components/input.d.ts +37 -0
  14. package/dist/components/input.d.ts.map +1 -0
  15. package/dist/components/input.js +389 -0
  16. package/dist/components/loader.d.ts +33 -0
  17. package/dist/components/loader.d.ts.map +1 -0
  18. package/dist/components/loader.js +100 -0
  19. package/dist/components/markdown.d.ts +108 -0
  20. package/dist/components/markdown.d.ts.map +1 -0
  21. package/dist/components/markdown.js +847 -0
  22. package/dist/components/markdown.test.d.ts +2 -0
  23. package/dist/components/markdown.test.d.ts.map +1 -0
  24. package/dist/components/markdown.test.js +81 -0
  25. package/dist/components/output-block.d.ts +44 -0
  26. package/dist/components/output-block.d.ts.map +1 -0
  27. package/dist/components/output-block.js +131 -0
  28. package/dist/components/select-list.d.ts +50 -0
  29. package/dist/components/select-list.d.ts.map +1 -0
  30. package/dist/components/select-list.js +166 -0
  31. package/dist/components/spacer.d.ts +12 -0
  32. package/dist/components/spacer.d.ts.map +1 -0
  33. package/dist/components/spacer.js +22 -0
  34. package/dist/components/statusline.d.ts +30 -0
  35. package/dist/components/statusline.d.ts.map +1 -0
  36. package/dist/components/statusline.js +46 -0
  37. package/dist/components/text.d.ts +19 -0
  38. package/dist/components/text.d.ts.map +1 -0
  39. package/dist/components/text.js +90 -0
  40. package/dist/components/truncated-text.d.ts +13 -0
  41. package/dist/components/truncated-text.d.ts.map +1 -0
  42. package/dist/components/truncated-text.js +50 -0
  43. package/dist/editor-component.d.ts +39 -0
  44. package/dist/editor-component.d.ts.map +1 -0
  45. package/dist/editor-component.js +1 -0
  46. package/dist/fuzzy.d.ts +16 -0
  47. package/dist/fuzzy.d.ts.map +1 -0
  48. package/dist/fuzzy.js +109 -0
  49. package/dist/index.d.ts +25 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +36 -0
  52. package/dist/keybindings.d.ts +193 -0
  53. package/dist/keybindings.d.ts.map +1 -0
  54. package/dist/keybindings.js +191 -0
  55. package/dist/keys.d.ts +184 -0
  56. package/dist/keys.d.ts.map +1 -0
  57. package/dist/keys.js +1191 -0
  58. package/dist/kill-ring.d.ts +28 -0
  59. package/dist/kill-ring.d.ts.map +1 -0
  60. package/dist/kill-ring.js +43 -0
  61. package/dist/mermaid.d.ts +23 -0
  62. package/dist/mermaid.d.ts.map +1 -0
  63. package/dist/mermaid.js +77 -0
  64. package/dist/mouse.d.ts +40 -0
  65. package/dist/mouse.d.ts.map +1 -0
  66. package/dist/mouse.js +39 -0
  67. package/dist/mouse.test.d.ts +2 -0
  68. package/dist/mouse.test.d.ts.map +1 -0
  69. package/dist/mouse.test.js +36 -0
  70. package/dist/native-modifiers.d.ts +3 -0
  71. package/dist/native-modifiers.d.ts.map +1 -0
  72. package/dist/native-modifiers.js +52 -0
  73. package/dist/stdin-buffer.d.ts +50 -0
  74. package/dist/stdin-buffer.d.ts.map +1 -0
  75. package/dist/stdin-buffer.js +360 -0
  76. package/dist/terminal-colors.d.ts +10 -0
  77. package/dist/terminal-colors.d.ts.map +1 -0
  78. package/dist/terminal-colors.js +58 -0
  79. package/dist/terminal-image.d.ts +90 -0
  80. package/dist/terminal-image.d.ts.map +1 -0
  81. package/dist/terminal-image.js +375 -0
  82. package/dist/terminal.d.ts +110 -0
  83. package/dist/terminal.d.ts.map +1 -0
  84. package/dist/terminal.js +432 -0
  85. package/dist/tui-scrollback.test.d.ts +2 -0
  86. package/dist/tui-scrollback.test.d.ts.map +1 -0
  87. package/dist/tui-scrollback.test.js +51 -0
  88. package/dist/tui.d.ts +330 -0
  89. package/dist/tui.d.ts.map +1 -0
  90. package/dist/tui.js +1568 -0
  91. package/dist/undo-stack.d.ts +17 -0
  92. package/dist/undo-stack.d.ts.map +1 -0
  93. package/dist/undo-stack.js +24 -0
  94. package/dist/utils.d.ts +85 -0
  95. package/dist/utils.d.ts.map +1 -0
  96. package/dist/utils.js +1066 -0
  97. package/dist/vendor/mermaid-ascii/ascii/ansi.d.ts +42 -0
  98. package/dist/vendor/mermaid-ascii/ascii/ansi.d.ts.map +1 -0
  99. package/dist/vendor/mermaid-ascii/ascii/ansi.js +361 -0
  100. package/dist/vendor/mermaid-ascii/ascii/canvas.d.ts +90 -0
  101. package/dist/vendor/mermaid-ascii/ascii/canvas.d.ts.map +1 -0
  102. package/dist/vendor/mermaid-ascii/ascii/canvas.js +414 -0
  103. package/dist/vendor/mermaid-ascii/ascii/class-diagram.d.ts +8 -0
  104. package/dist/vendor/mermaid-ascii/ascii/class-diagram.d.ts.map +1 -0
  105. package/dist/vendor/mermaid-ascii/ascii/class-diagram.js +623 -0
  106. package/dist/vendor/mermaid-ascii/ascii/converter.d.ts +13 -0
  107. package/dist/vendor/mermaid-ascii/ascii/converter.d.ts.map +1 -0
  108. package/dist/vendor/mermaid-ascii/ascii/converter.js +233 -0
  109. package/dist/vendor/mermaid-ascii/ascii/draw.d.ts +67 -0
  110. package/dist/vendor/mermaid-ascii/ascii/draw.d.ts.map +1 -0
  111. package/dist/vendor/mermaid-ascii/ascii/draw.js +1313 -0
  112. package/dist/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +49 -0
  113. package/dist/vendor/mermaid-ascii/ascii/edge-bundling.d.ts.map +1 -0
  114. package/dist/vendor/mermaid-ascii/ascii/edge-bundling.js +289 -0
  115. package/dist/vendor/mermaid-ascii/ascii/edge-routing.d.ts +44 -0
  116. package/dist/vendor/mermaid-ascii/ascii/edge-routing.d.ts.map +1 -0
  117. package/dist/vendor/mermaid-ascii/ascii/edge-routing.js +302 -0
  118. package/dist/vendor/mermaid-ascii/ascii/er-diagram.d.ts +8 -0
  119. package/dist/vendor/mermaid-ascii/ascii/er-diagram.d.ts.map +1 -0
  120. package/dist/vendor/mermaid-ascii/ascii/er-diagram.js +380 -0
  121. package/dist/vendor/mermaid-ascii/ascii/grid.d.ts +57 -0
  122. package/dist/vendor/mermaid-ascii/ascii/grid.d.ts.map +1 -0
  123. package/dist/vendor/mermaid-ascii/ascii/grid.js +516 -0
  124. package/dist/vendor/mermaid-ascii/ascii/index.d.ts +66 -0
  125. package/dist/vendor/mermaid-ascii/ascii/index.d.ts.map +1 -0
  126. package/dist/vendor/mermaid-ascii/ascii/index.js +133 -0
  127. package/dist/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +28 -0
  128. package/dist/vendor/mermaid-ascii/ascii/multiline-utils.d.ts.map +1 -0
  129. package/dist/vendor/mermaid-ascii/ascii/multiline-utils.js +60 -0
  130. package/dist/vendor/mermaid-ascii/ascii/pathfinder.d.ts +18 -0
  131. package/dist/vendor/mermaid-ascii/ascii/pathfinder.d.ts.map +1 -0
  132. package/dist/vendor/mermaid-ascii/ascii/pathfinder.js +224 -0
  133. package/dist/vendor/mermaid-ascii/ascii/sequence.d.ts +8 -0
  134. package/dist/vendor/mermaid-ascii/ascii/sequence.d.ts.map +1 -0
  135. package/dist/vendor/mermaid-ascii/ascii/sequence.js +416 -0
  136. package/dist/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +12 -0
  137. package/dist/vendor/mermaid-ascii/ascii/shapes/circle.d.ts.map +1 -0
  138. package/dist/vendor/mermaid-ascii/ascii/shapes/circle.js +22 -0
  139. package/dist/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +35 -0
  140. package/dist/vendor/mermaid-ascii/ascii/shapes/corners.d.ts.map +1 -0
  141. package/dist/vendor/mermaid-ascii/ascii/shapes/corners.js +91 -0
  142. package/dist/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +12 -0
  143. package/dist/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts.map +1 -0
  144. package/dist/vendor/mermaid-ascii/ascii/shapes/diamond.js +22 -0
  145. package/dist/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +12 -0
  146. package/dist/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts.map +1 -0
  147. package/dist/vendor/mermaid-ascii/ascii/shapes/hexagon.js +22 -0
  148. package/dist/vendor/mermaid-ascii/ascii/shapes/index.d.ts +27 -0
  149. package/dist/vendor/mermaid-ascii/ascii/shapes/index.d.ts.map +1 -0
  150. package/dist/vendor/mermaid-ascii/ascii/shapes/index.js +66 -0
  151. package/dist/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +32 -0
  152. package/dist/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts.map +1 -0
  153. package/dist/vendor/mermaid-ascii/ascii/shapes/rectangle.js +149 -0
  154. package/dist/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +12 -0
  155. package/dist/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts.map +1 -0
  156. package/dist/vendor/mermaid-ascii/ascii/shapes/rounded.js +22 -0
  157. package/dist/vendor/mermaid-ascii/ascii/shapes/special.d.ts +60 -0
  158. package/dist/vendor/mermaid-ascii/ascii/shapes/special.d.ts.map +1 -0
  159. package/dist/vendor/mermaid-ascii/ascii/shapes/special.js +253 -0
  160. package/dist/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +18 -0
  161. package/dist/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts.map +1 -0
  162. package/dist/vendor/mermaid-ascii/ascii/shapes/stadium.js +103 -0
  163. package/dist/vendor/mermaid-ascii/ascii/shapes/state.d.ts +31 -0
  164. package/dist/vendor/mermaid-ascii/ascii/shapes/state.d.ts.map +1 -0
  165. package/dist/vendor/mermaid-ascii/ascii/shapes/state.js +167 -0
  166. package/dist/vendor/mermaid-ascii/ascii/shapes/types.d.ts +56 -0
  167. package/dist/vendor/mermaid-ascii/ascii/shapes/types.d.ts.map +1 -0
  168. package/dist/vendor/mermaid-ascii/ascii/shapes/types.js +4 -0
  169. package/dist/vendor/mermaid-ascii/ascii/types.d.ts +207 -0
  170. package/dist/vendor/mermaid-ascii/ascii/types.d.ts.map +1 -0
  171. package/dist/vendor/mermaid-ascii/ascii/types.js +39 -0
  172. package/dist/vendor/mermaid-ascii/ascii/validate.d.ts +52 -0
  173. package/dist/vendor/mermaid-ascii/ascii/validate.d.ts.map +1 -0
  174. package/dist/vendor/mermaid-ascii/ascii/validate.js +97 -0
  175. package/dist/vendor/mermaid-ascii/ascii/xychart.d.ts +3 -0
  176. package/dist/vendor/mermaid-ascii/ascii/xychart.d.ts.map +1 -0
  177. package/dist/vendor/mermaid-ascii/ascii/xychart.js +719 -0
  178. package/dist/vendor/mermaid-ascii/class/parser.d.ts +7 -0
  179. package/dist/vendor/mermaid-ascii/class/parser.d.ts.map +1 -0
  180. package/dist/vendor/mermaid-ascii/class/parser.js +265 -0
  181. package/dist/vendor/mermaid-ascii/class/types.d.ts +103 -0
  182. package/dist/vendor/mermaid-ascii/class/types.d.ts.map +1 -0
  183. package/dist/vendor/mermaid-ascii/class/types.js +7 -0
  184. package/dist/vendor/mermaid-ascii/er/parser.d.ts +7 -0
  185. package/dist/vendor/mermaid-ascii/er/parser.d.ts.map +1 -0
  186. package/dist/vendor/mermaid-ascii/er/parser.js +162 -0
  187. package/dist/vendor/mermaid-ascii/er/types.d.ts +77 -0
  188. package/dist/vendor/mermaid-ascii/er/types.d.ts.map +1 -0
  189. package/dist/vendor/mermaid-ascii/er/types.js +7 -0
  190. package/dist/vendor/mermaid-ascii/index.d.ts +2 -0
  191. package/dist/vendor/mermaid-ascii/index.d.ts.map +1 -0
  192. package/dist/vendor/mermaid-ascii/index.js +13 -0
  193. package/dist/vendor/mermaid-ascii/multiline-utils.d.ts +10 -0
  194. package/dist/vendor/mermaid-ascii/multiline-utils.d.ts.map +1 -0
  195. package/dist/vendor/mermaid-ascii/multiline-utils.js +29 -0
  196. package/dist/vendor/mermaid-ascii/parser.d.ts +8 -0
  197. package/dist/vendor/mermaid-ascii/parser.d.ts.map +1 -0
  198. package/dist/vendor/mermaid-ascii/parser.js +558 -0
  199. package/dist/vendor/mermaid-ascii/sequence/parser.d.ts +7 -0
  200. package/dist/vendor/mermaid-ascii/sequence/parser.d.ts.map +1 -0
  201. package/dist/vendor/mermaid-ascii/sequence/parser.js +182 -0
  202. package/dist/vendor/mermaid-ascii/sequence/types.d.ts +131 -0
  203. package/dist/vendor/mermaid-ascii/sequence/types.d.ts.map +1 -0
  204. package/dist/vendor/mermaid-ascii/sequence/types.js +7 -0
  205. package/dist/vendor/mermaid-ascii/text-metrics.d.ts +24 -0
  206. package/dist/vendor/mermaid-ascii/text-metrics.d.ts.map +1 -0
  207. package/dist/vendor/mermaid-ascii/text-metrics.js +69 -0
  208. package/dist/vendor/mermaid-ascii/types.d.ts +115 -0
  209. package/dist/vendor/mermaid-ascii/types.d.ts.map +1 -0
  210. package/dist/vendor/mermaid-ascii/types.js +4 -0
  211. package/dist/vendor/mermaid-ascii/xychart/colors.d.ts +26 -0
  212. package/dist/vendor/mermaid-ascii/xychart/colors.d.ts.map +1 -0
  213. package/dist/vendor/mermaid-ascii/xychart/colors.js +145 -0
  214. package/dist/vendor/mermaid-ascii/xychart/parser.d.ts +7 -0
  215. package/dist/vendor/mermaid-ascii/xychart/parser.d.ts.map +1 -0
  216. package/dist/vendor/mermaid-ascii/xychart/parser.js +105 -0
  217. package/dist/vendor/mermaid-ascii/xychart/types.d.ts +146 -0
  218. package/dist/vendor/mermaid-ascii/xychart/types.d.ts.map +1 -0
  219. package/dist/vendor/mermaid-ascii/xychart/types.js +8 -0
  220. package/dist/virtual-terminal.d.ts +80 -0
  221. package/dist/virtual-terminal.d.ts.map +1 -0
  222. package/dist/virtual-terminal.js +284 -0
  223. package/dist/word-navigation.d.ts +25 -0
  224. package/dist/word-navigation.d.ts.map +1 -0
  225. package/dist/word-navigation.js +97 -0
  226. package/package.json +38 -0
@@ -0,0 +1,516 @@
1
+ // ============================================================================
2
+ // ASCII renderer — grid-based layout
3
+ //
4
+ // Ported from AlexanderGrooff/mermaid-ascii cmd/graph.go + cmd/mapping_node.go.
5
+ // Places nodes on a logical grid, computes column/row sizes,
6
+ // converts grid coordinates to character-level drawing coordinates,
7
+ // and handles subgraph bounding boxes.
8
+ // ============================================================================
9
+ import { gridKey } from "./types.js";
10
+ import { getCanvasSize, increaseRoleCanvasSize, increaseSize, setCanvasSizeToGrid, setRoleCanvasSizeToGrid, } from "./canvas.js";
11
+ import { determinePath, determineLabelLine } from "./edge-routing.js";
12
+ import { analyzeEdgeBundles, processBundles } from "./edge-bundling.js";
13
+ import { drawBox } from "./draw.js";
14
+ import { getShapeDimensions } from "./shapes/index.js";
15
+ // ============================================================================
16
+ // Grid coordinate → drawing coordinate conversion
17
+ // ============================================================================
18
+ /**
19
+ * Convert a grid coordinate to a drawing (character) coordinate.
20
+ * Sums column widths up to the target column, and row heights up to the target row,
21
+ * then centers within the cell.
22
+ */
23
+ export function gridToDrawingCoord(graph, c, dir) {
24
+ const target = dir
25
+ ? { x: c.x + dir.x, y: c.y + dir.y }
26
+ : c;
27
+ let x = 0;
28
+ for (let col = 0; col < target.x; col++) {
29
+ x += graph.columnWidth.get(col) ?? 0;
30
+ }
31
+ let y = 0;
32
+ for (let row = 0; row < target.y; row++) {
33
+ y += graph.rowHeight.get(row) ?? 0;
34
+ }
35
+ const colW = graph.columnWidth.get(target.x) ?? 0;
36
+ const rowH = graph.rowHeight.get(target.y) ?? 0;
37
+ return {
38
+ x: x + Math.floor(colW / 2) + graph.offsetX,
39
+ y: y + Math.floor(rowH / 2) + graph.offsetY,
40
+ };
41
+ }
42
+ /** Convert a path of grid coords to drawing coords. */
43
+ export function lineToDrawing(graph, line) {
44
+ return line.map(c => gridToDrawingCoord(graph, c));
45
+ }
46
+ // ============================================================================
47
+ // Node placement on the grid
48
+ // ============================================================================
49
+ /**
50
+ * Reserve a 3x3 block in the grid for a node.
51
+ * If the requested position is occupied, recursively shift by 4 grid units
52
+ * (in the perpendicular direction based on effective direction) until a free spot is found.
53
+ *
54
+ * @param effectiveDir - Optional direction override. If not provided, uses the node's
55
+ * effective direction (subgraph direction if in a subgraph with override,
56
+ * otherwise graph direction).
57
+ */
58
+ export function reserveSpotInGrid(graph, node, requested, effectiveDir) {
59
+ // Determine direction for collision handling
60
+ const dir = effectiveDir ?? getEffectiveDirection(graph, node);
61
+ if (graph.grid.has(gridKey(requested))) {
62
+ // Collision — shift perpendicular to main flow direction
63
+ if (dir === 'LR') {
64
+ return reserveSpotInGrid(graph, node, { x: requested.x, y: requested.y + 4 }, dir);
65
+ }
66
+ else {
67
+ return reserveSpotInGrid(graph, node, { x: requested.x + 4, y: requested.y }, dir);
68
+ }
69
+ }
70
+ // Reserve the 3x3 block
71
+ for (let dx = 0; dx < 3; dx++) {
72
+ for (let dy = 0; dy < 3; dy++) {
73
+ const reserved = { x: requested.x + dx, y: requested.y + dy };
74
+ graph.grid.set(gridKey(reserved), node);
75
+ }
76
+ }
77
+ node.gridCoord = requested;
78
+ return requested;
79
+ }
80
+ // ============================================================================
81
+ // Column width / row height computation
82
+ // ============================================================================
83
+ /**
84
+ * Set column widths and row heights for a node's 3x3 grid block.
85
+ * Each node occupies 3 columns (border, content, border) and 3 rows.
86
+ * Uses shape-aware dimensions to properly size non-rectangular shapes.
87
+ */
88
+ export function setColumnWidth(graph, node) {
89
+ const gc = node.gridCoord;
90
+ const padding = graph.config.boxBorderPadding;
91
+ // Get shape-aware dimensions
92
+ const shapeDims = getShapeDimensions(node.shape, node.displayLabel, {
93
+ useAscii: graph.config.useAscii,
94
+ padding,
95
+ });
96
+ // Use shape-provided grid dimensions
97
+ const colWidths = shapeDims.gridColumns;
98
+ const rowHeights = shapeDims.gridRows;
99
+ for (let idx = 0; idx < colWidths.length; idx++) {
100
+ const xCoord = gc.x + idx;
101
+ const current = graph.columnWidth.get(xCoord) ?? 0;
102
+ graph.columnWidth.set(xCoord, Math.max(current, colWidths[idx]));
103
+ }
104
+ for (let idx = 0; idx < rowHeights.length; idx++) {
105
+ const yCoord = gc.y + idx;
106
+ const current = graph.rowHeight.get(yCoord) ?? 0;
107
+ graph.rowHeight.set(yCoord, Math.max(current, rowHeights[idx]));
108
+ }
109
+ // Padding column/row before the node (spacing between nodes)
110
+ if (gc.x > 0) {
111
+ const current = graph.columnWidth.get(gc.x - 1) ?? 0;
112
+ graph.columnWidth.set(gc.x - 1, Math.max(current, graph.config.paddingX));
113
+ }
114
+ if (gc.y > 0) {
115
+ let basePadding = graph.config.paddingY;
116
+ // Extra vertical padding for nodes with incoming edges from outside their subgraph
117
+ if (hasIncomingEdgeFromOutsideSubgraph(graph, node)) {
118
+ const subgraphOverhead = 4;
119
+ basePadding += subgraphOverhead;
120
+ }
121
+ const current = graph.rowHeight.get(gc.y - 1) ?? 0;
122
+ graph.rowHeight.set(gc.y - 1, Math.max(current, basePadding));
123
+ }
124
+ }
125
+ /** Ensure grid has width/height entries for all cells along an edge path. */
126
+ export function increaseGridSizeForPath(graph, path) {
127
+ for (const c of path) {
128
+ if (!graph.columnWidth.has(c.x)) {
129
+ graph.columnWidth.set(c.x, Math.floor(graph.config.paddingX / 2));
130
+ }
131
+ if (!graph.rowHeight.has(c.y)) {
132
+ graph.rowHeight.set(c.y, Math.floor(graph.config.paddingY / 2));
133
+ }
134
+ }
135
+ }
136
+ // ============================================================================
137
+ // Subgraph helpers
138
+ // ============================================================================
139
+ function isNodeInAnySubgraph(graph, node) {
140
+ return graph.subgraphs.some(sg => sg.nodes.includes(node));
141
+ }
142
+ /**
143
+ * Get the innermost subgraph that directly contains this node.
144
+ * Returns null if node is not in any subgraph.
145
+ */
146
+ export function getNodeSubgraph(graph, node) {
147
+ // Find the innermost (most deeply nested) subgraph containing the node
148
+ let innermost = null;
149
+ for (const sg of graph.subgraphs) {
150
+ if (sg.nodes.includes(node)) {
151
+ // Check if this subgraph is deeper (more nested) than current innermost
152
+ if (!innermost || isAncestorOrSelf(innermost, sg)) {
153
+ innermost = sg;
154
+ }
155
+ }
156
+ }
157
+ return innermost;
158
+ }
159
+ /** Check if `candidate` is the same as or an ancestor of `target`. */
160
+ function isAncestorOrSelf(candidate, target) {
161
+ let current = target;
162
+ while (current !== null) {
163
+ if (current === candidate)
164
+ return true;
165
+ current = current.parent;
166
+ }
167
+ return false;
168
+ }
169
+ /**
170
+ * Get the effective direction for a node's layout.
171
+ * Returns the subgraph's direction override if the node is in a subgraph with one,
172
+ * otherwise returns the graph-level direction.
173
+ */
174
+ export function getEffectiveDirection(graph, node) {
175
+ const sg = getNodeSubgraph(graph, node);
176
+ if (sg?.direction) {
177
+ return sg.direction;
178
+ }
179
+ return graph.config.graphDirection;
180
+ }
181
+ /**
182
+ * Check if a node has an incoming edge from outside its subgraph
183
+ * AND is the topmost such node in its subgraph.
184
+ * Used to add extra vertical padding for subgraph borders.
185
+ */
186
+ function hasIncomingEdgeFromOutsideSubgraph(graph, node) {
187
+ const nodeSg = getNodeSubgraph(graph, node);
188
+ if (!nodeSg)
189
+ return false;
190
+ let hasExternalEdge = false;
191
+ for (const edge of graph.edges) {
192
+ if (edge.to === node) {
193
+ const sourceSg = getNodeSubgraph(graph, edge.from);
194
+ if (sourceSg !== nodeSg) {
195
+ hasExternalEdge = true;
196
+ break;
197
+ }
198
+ }
199
+ }
200
+ if (!hasExternalEdge)
201
+ return false;
202
+ // Only return true for the topmost node with an external incoming edge
203
+ for (const otherNode of nodeSg.nodes) {
204
+ if (otherNode === node || !otherNode.gridCoord)
205
+ continue;
206
+ let otherHasExternal = false;
207
+ for (const edge of graph.edges) {
208
+ if (edge.to === otherNode) {
209
+ const sourceSg = getNodeSubgraph(graph, edge.from);
210
+ if (sourceSg !== nodeSg) {
211
+ otherHasExternal = true;
212
+ break;
213
+ }
214
+ }
215
+ }
216
+ if (otherHasExternal && otherNode.gridCoord.y < node.gridCoord.y) {
217
+ return false;
218
+ }
219
+ }
220
+ return true;
221
+ }
222
+ // ============================================================================
223
+ // Subgraph bounding boxes
224
+ // ============================================================================
225
+ function calculateSubgraphBoundingBox(graph, sg) {
226
+ if (sg.nodes.length === 0)
227
+ return;
228
+ let minX = 1_000_000;
229
+ let minY = 1_000_000;
230
+ let maxX = -1_000_000;
231
+ let maxY = -1_000_000;
232
+ // Include children's bounding boxes
233
+ for (const child of sg.children) {
234
+ calculateSubgraphBoundingBox(graph, child);
235
+ if (child.nodes.length > 0) {
236
+ minX = Math.min(minX, child.minX);
237
+ minY = Math.min(minY, child.minY);
238
+ maxX = Math.max(maxX, child.maxX);
239
+ maxY = Math.max(maxY, child.maxY);
240
+ }
241
+ }
242
+ // Include node positions
243
+ for (const node of sg.nodes) {
244
+ if (!node.drawingCoord || !node.drawing)
245
+ continue;
246
+ const nodeMinX = node.drawingCoord.x;
247
+ const nodeMinY = node.drawingCoord.y;
248
+ const nodeMaxX = nodeMinX + node.drawing.length - 1;
249
+ const nodeMaxY = nodeMinY + node.drawing[0].length - 1;
250
+ minX = Math.min(minX, nodeMinX);
251
+ minY = Math.min(minY, nodeMinY);
252
+ maxX = Math.max(maxX, nodeMaxX);
253
+ maxY = Math.max(maxY, nodeMaxY);
254
+ }
255
+ const subgraphPadding = 2;
256
+ const subgraphLabelSpace = 2;
257
+ sg.minX = minX - subgraphPadding;
258
+ sg.minY = minY - subgraphPadding - subgraphLabelSpace;
259
+ sg.maxX = maxX + subgraphPadding;
260
+ sg.maxY = maxY + subgraphPadding;
261
+ }
262
+ /** Ensure non-overlapping root subgraphs have minimum spacing. */
263
+ function ensureSubgraphSpacing(graph) {
264
+ const minSpacing = 1;
265
+ const rootSubgraphs = graph.subgraphs.filter(sg => sg.parent === null && sg.nodes.length > 0);
266
+ for (let i = 0; i < rootSubgraphs.length; i++) {
267
+ for (let j = i + 1; j < rootSubgraphs.length; j++) {
268
+ const sg1 = rootSubgraphs[i];
269
+ const sg2 = rootSubgraphs[j];
270
+ // Horizontal overlap → adjust vertical
271
+ if (sg1.minX < sg2.maxX && sg1.maxX > sg2.minX) {
272
+ if (sg1.maxY >= sg2.minY - minSpacing && sg1.minY < sg2.minY) {
273
+ sg2.minY = sg1.maxY + minSpacing + 1;
274
+ }
275
+ else if (sg2.maxY >= sg1.minY - minSpacing && sg2.minY < sg1.minY) {
276
+ sg1.minY = sg2.maxY + minSpacing + 1;
277
+ }
278
+ }
279
+ // Vertical overlap → adjust horizontal
280
+ if (sg1.minY < sg2.maxY && sg1.maxY > sg2.minY) {
281
+ if (sg1.maxX >= sg2.minX - minSpacing && sg1.minX < sg2.minX) {
282
+ sg2.minX = sg1.maxX + minSpacing + 1;
283
+ }
284
+ else if (sg2.maxX >= sg1.minX - minSpacing && sg2.minX < sg1.minX) {
285
+ sg1.minX = sg2.maxX + minSpacing + 1;
286
+ }
287
+ }
288
+ }
289
+ }
290
+ }
291
+ export function calculateSubgraphBoundingBoxes(graph) {
292
+ for (const sg of graph.subgraphs) {
293
+ calculateSubgraphBoundingBox(graph, sg);
294
+ }
295
+ ensureSubgraphSpacing(graph);
296
+ }
297
+ /**
298
+ * Offset all drawing coordinates so subgraph borders don't go negative.
299
+ * If any subgraph has negative min coordinates, shift everything positive.
300
+ */
301
+ export function offsetDrawingForSubgraphs(graph) {
302
+ if (graph.subgraphs.length === 0)
303
+ return;
304
+ let minX = 0;
305
+ let minY = 0;
306
+ for (const sg of graph.subgraphs) {
307
+ minX = Math.min(minX, sg.minX);
308
+ minY = Math.min(minY, sg.minY);
309
+ }
310
+ const offsetX = -minX;
311
+ const offsetY = -minY;
312
+ if (offsetX === 0 && offsetY === 0)
313
+ return;
314
+ graph.offsetX = offsetX;
315
+ graph.offsetY = offsetY;
316
+ for (const sg of graph.subgraphs) {
317
+ sg.minX += offsetX;
318
+ sg.minY += offsetY;
319
+ sg.maxX += offsetX;
320
+ sg.maxY += offsetY;
321
+ }
322
+ for (const node of graph.nodes) {
323
+ if (node.drawingCoord) {
324
+ node.drawingCoord.x += offsetX;
325
+ node.drawingCoord.y += offsetY;
326
+ }
327
+ }
328
+ // The canvas was sized from the pre-shift grid extents, but every drawing
329
+ // coordinate — including edge path endpoints, which gridToDrawingCoord
330
+ // offsets on read — now sits `offset` cells further out. Grow it to match,
331
+ // or drawing the rightmost/bottom-most edges writes past the allocation.
332
+ const [maxX, maxY] = getCanvasSize(graph.canvas);
333
+ increaseSize(graph.canvas, maxX + offsetX, maxY + offsetY);
334
+ increaseRoleCanvasSize(graph.roleCanvas, maxX + offsetX, maxY + offsetY);
335
+ }
336
+ // ============================================================================
337
+ // Main layout orchestrator
338
+ // ============================================================================
339
+ /**
340
+ * createMapping performs the full grid layout:
341
+ * 1. Place root nodes on the grid
342
+ * 2. Place child nodes level by level
343
+ * 3. Compute column widths and row heights
344
+ * 4. Run A* pathfinding for all edges
345
+ * 5. Determine label placement
346
+ * 6. Convert grid coords → drawing coords
347
+ * 7. Generate node box drawings
348
+ * 8. Calculate subgraph bounding boxes
349
+ */
350
+ export function createMapping(graph) {
351
+ const dir = graph.config.graphDirection;
352
+ const highestPositionPerLevel = new Array(100).fill(0);
353
+ // Identify root nodes — nodes that aren't the target of any edge
354
+ const nodesFound = new Set();
355
+ const initialRoots = [];
356
+ for (const node of graph.nodes) {
357
+ if (!nodesFound.has(node.name)) {
358
+ initialRoots.push(node);
359
+ }
360
+ nodesFound.add(node.name);
361
+ for (const child of getChildren(graph, node)) {
362
+ nodesFound.add(child.name);
363
+ }
364
+ }
365
+ // Filter out subgraph nodes that have incoming edges from external sources.
366
+ // This handles the case where subgraph is declared before external nodes
367
+ // (e.g., `subgraph s; A-->B; end; X-->A` - A shouldn't be a root, X should).
368
+ const rootNodes = initialRoots.filter(node => {
369
+ const nodeSg = getNodeSubgraph(graph, node);
370
+ if (!nodeSg)
371
+ return true; // external nodes: keep as roots
372
+ // Check if this subgraph node has incoming edges from outside its subgraph
373
+ for (const edge of graph.edges) {
374
+ if (edge.to === node) {
375
+ const sourceSg = getNodeSubgraph(graph, edge.from);
376
+ if (sourceSg !== nodeSg) {
377
+ return false; // has external incoming edge → not a root
378
+ }
379
+ }
380
+ }
381
+ return true;
382
+ });
383
+ // In LR mode with both external and subgraph roots, separate them
384
+ // so subgraph roots are placed one level deeper
385
+ let hasExternalRoots = false;
386
+ let hasSubgraphRootsWithEdges = false;
387
+ for (const node of rootNodes) {
388
+ if (isNodeInAnySubgraph(graph, node)) {
389
+ if (getChildren(graph, node).length > 0)
390
+ hasSubgraphRootsWithEdges = true;
391
+ }
392
+ else {
393
+ hasExternalRoots = true;
394
+ }
395
+ }
396
+ const shouldSeparate = dir === 'LR' && hasExternalRoots && hasSubgraphRootsWithEdges;
397
+ let externalRootNodes;
398
+ let subgraphRootNodes = [];
399
+ if (shouldSeparate) {
400
+ externalRootNodes = rootNodes.filter(n => !isNodeInAnySubgraph(graph, n));
401
+ subgraphRootNodes = rootNodes.filter(n => isNodeInAnySubgraph(graph, n));
402
+ }
403
+ else {
404
+ externalRootNodes = rootNodes;
405
+ }
406
+ // Place external root nodes
407
+ for (const node of externalRootNodes) {
408
+ const requested = dir === 'LR'
409
+ ? { x: 0, y: highestPositionPerLevel[0] }
410
+ : { x: highestPositionPerLevel[0], y: 0 };
411
+ reserveSpotInGrid(graph, graph.nodes[node.index], requested);
412
+ highestPositionPerLevel[0] = highestPositionPerLevel[0] + 4;
413
+ }
414
+ // Place subgraph root nodes at level 4 (one level in from the edge)
415
+ if (shouldSeparate && subgraphRootNodes.length > 0) {
416
+ const subgraphLevel = 4;
417
+ for (const node of subgraphRootNodes) {
418
+ const requested = dir === 'LR'
419
+ ? { x: subgraphLevel, y: highestPositionPerLevel[subgraphLevel] }
420
+ : { x: highestPositionPerLevel[subgraphLevel], y: subgraphLevel };
421
+ reserveSpotInGrid(graph, graph.nodes[node.index], requested);
422
+ highestPositionPerLevel[subgraphLevel] = highestPositionPerLevel[subgraphLevel] + 4;
423
+ }
424
+ }
425
+ // Place child nodes level by level
426
+ // Use subgraph direction only when both parent and child are in the same subgraph
427
+ // Multi-pass: iterate until all nodes are placed (handles non-topological node order)
428
+ // Note: when shouldSeparate, externalRootNodes + subgraphRootNodes = rootNodes
429
+ // otherwise, externalRootNodes = rootNodes and subgraphRootNodes is empty
430
+ let placedCount = externalRootNodes.length + subgraphRootNodes.length;
431
+ while (placedCount < graph.nodes.length) {
432
+ const prevCount = placedCount;
433
+ for (const node of graph.nodes) {
434
+ if (node.gridCoord === null)
435
+ continue; // skip unplaced nodes
436
+ const gc = node.gridCoord;
437
+ for (const child of getChildren(graph, node)) {
438
+ if (child.gridCoord !== null)
439
+ continue; // already placed
440
+ // Determine direction for this edge (parent -> child)
441
+ // Use subgraph direction only if both are in the same subgraph with override
442
+ const parentSg = getNodeSubgraph(graph, node);
443
+ const childSg = getNodeSubgraph(graph, child);
444
+ const edgeDir = (parentSg && parentSg === childSg && parentSg.direction)
445
+ ? parentSg.direction
446
+ : graph.config.graphDirection;
447
+ const childLevel = edgeDir === 'LR' ? gc.x + 4 : gc.y + 4;
448
+ // Determine position based on direction context
449
+ let highestPosition;
450
+ if (edgeDir !== graph.config.graphDirection) {
451
+ // Cross-direction: use parent's perpendicular coordinate
452
+ // This keeps children aligned with parent when direction changes
453
+ highestPosition = edgeDir === 'LR' ? gc.y : gc.x;
454
+ }
455
+ else {
456
+ // Same direction: use level tracker
457
+ highestPosition = highestPositionPerLevel[childLevel];
458
+ }
459
+ const requested = edgeDir === 'LR'
460
+ ? { x: childLevel, y: highestPosition }
461
+ : { x: highestPosition, y: childLevel };
462
+ reserveSpotInGrid(graph, graph.nodes[child.index], requested, edgeDir);
463
+ // Only update level tracker for same-direction placements
464
+ if (edgeDir === graph.config.graphDirection) {
465
+ highestPositionPerLevel[childLevel] = highestPosition + 4;
466
+ }
467
+ placedCount++;
468
+ }
469
+ }
470
+ // Safety: break if no progress made (handles disconnected nodes)
471
+ if (placedCount === prevCount)
472
+ break;
473
+ }
474
+ // Compute column widths and row heights
475
+ for (const node of graph.nodes) {
476
+ setColumnWidth(graph, node);
477
+ }
478
+ // Analyze edges for bundling (parallel links like A & B --> C)
479
+ // This groups edges that share sources or targets for cleaner visualization
480
+ graph.bundles = analyzeEdgeBundles(graph);
481
+ // Route bundled edges through junction points
482
+ processBundles(graph);
483
+ // Route non-bundled edges via A* and determine label positions
484
+ for (const edge of graph.edges) {
485
+ // Skip edges that were already routed as part of a bundle
486
+ if (edge.bundle && edge.path.length > 0) {
487
+ increaseGridSizeForPath(graph, edge.path);
488
+ determineLabelLine(graph, edge);
489
+ continue;
490
+ }
491
+ determinePath(graph, edge);
492
+ increaseGridSizeForPath(graph, edge.path);
493
+ determineLabelLine(graph, edge);
494
+ }
495
+ // Convert grid coords → drawing coords and generate box drawings
496
+ for (const node of graph.nodes) {
497
+ node.drawingCoord = gridToDrawingCoord(graph, node.gridCoord);
498
+ node.drawing = drawBox(node, graph);
499
+ }
500
+ // Set canvas size and compute subgraph bounding boxes
501
+ setCanvasSizeToGrid(graph.canvas, graph.columnWidth, graph.rowHeight);
502
+ setRoleCanvasSizeToGrid(graph.roleCanvas, graph.columnWidth, graph.rowHeight);
503
+ calculateSubgraphBoundingBoxes(graph);
504
+ offsetDrawingForSubgraphs(graph);
505
+ }
506
+ // ============================================================================
507
+ // Graph traversal helpers
508
+ // ============================================================================
509
+ /** Get all edges originating from a node. */
510
+ function getEdgesFromNode(graph, node) {
511
+ return graph.edges.filter(e => e.from.name === node.name);
512
+ }
513
+ /** Get all direct children of a node (targets of outgoing edges). */
514
+ function getChildren(graph, node) {
515
+ return getEdgesFromNode(graph, node).map(e => e.to);
516
+ }
@@ -0,0 +1,66 @@
1
+ import { detectColorMode, DEFAULT_ASCII_THEME } from './ansi.ts';
2
+ import type { AsciiTheme, ColorMode } from './types.ts';
3
+ import type { Direction } from '../types.ts';
4
+ export type { AsciiTheme, ColorMode };
5
+ export { DEFAULT_ASCII_THEME, detectColorMode };
6
+ export interface AsciiRenderOptions {
7
+ /** true = ASCII chars (+,-,|,>), false = Unicode box-drawing (┌,─,│,►). Default: false */
8
+ useAscii?: boolean;
9
+ /** Horizontal spacing between nodes. Default: 5 */
10
+ paddingX?: number;
11
+ /** Vertical spacing between nodes. Default: 5 */
12
+ paddingY?: number;
13
+ /** Padding inside node boxes. Default: 1 */
14
+ boxBorderPadding?: number;
15
+ /**
16
+ * Force the layout direction, overriding the direction parsed from the
17
+ * diagram source. Applies to the flowchart + state-diagram grid pipeline
18
+ * (sequence/class/ER/xychart renderers ignore it). Useful for re-fitting a
19
+ * wide `LR` graph into a narrow viewport by laying it out top-down.
20
+ * Default: undefined (use the source's own direction).
21
+ */
22
+ direction?: Direction;
23
+ /**
24
+ * Color mode for output.
25
+ * - 'none': No colors (plain text)
26
+ * - 'auto': Auto-detect (terminal ANSI capabilities, or HTML in browsers)
27
+ * - 'ansi16': 16-color ANSI
28
+ * - 'ansi256': 256-color xterm
29
+ * - 'truecolor': 24-bit RGB
30
+ * - 'html': HTML <span> tags with inline color styles (for browser rendering)
31
+ * Default: 'auto'
32
+ */
33
+ colorMode?: ColorMode | 'auto';
34
+ /** Theme colors for ASCII output. Uses default theme if not provided. */
35
+ theme?: Partial<AsciiTheme>;
36
+ }
37
+ /**
38
+ * Render Mermaid diagram text to an ASCII/Unicode string.
39
+ *
40
+ * Synchronous — no async layout engine needed (unlike the SVG renderer).
41
+ * Auto-detects diagram type from the header line and dispatches to
42
+ * the appropriate renderer.
43
+ *
44
+ * @param text - Mermaid source text (any supported diagram type)
45
+ * @param options - Rendering options
46
+ * @returns Multi-line ASCII/Unicode string
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const result = renderMermaidAscii(`
51
+ * graph LR
52
+ * A --> B --> C
53
+ * `, { useAscii: true })
54
+ *
55
+ * // Output:
56
+ * // +---+ +---+ +---+
57
+ * // | | | | | |
58
+ * // | A |---->| B |---->| C |
59
+ * // | | | | | |
60
+ * // +---+ +---+ +---+
61
+ * ```
62
+ */
63
+ export declare function renderMermaidASCII(text: string, options?: AsciiRenderOptions): string;
64
+ /** Lowercase alias kept as the public name used by the pi-utils wrapper. */
65
+ export declare const renderMermaidAscii: typeof renderMermaidASCII;
66
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/vendor/mermaid-ascii/ascii/index.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,KAAK,EAAe,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,CAAA;AACrC,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,CAAA;AAE/C,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IAC9B,yEAAyE;IACzE,KAAK,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;CAC5B;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAuER;AAED,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,2BAAqB,CAAA"}