@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,380 @@
1
+ // ============================================================================
2
+ // ASCII renderer — ER diagrams
3
+ //
4
+ // Renders erDiagram text to ASCII/Unicode art.
5
+ // Each entity is a 2-section box (header | attributes).
6
+ // Relationships are drawn as lines with crow's foot notation at endpoints.
7
+ //
8
+ // Layout: entities are placed in a grid pattern (multiple rows if needed).
9
+ // Relationship lines use Manhattan routing between entity boxes.
10
+ // ============================================================================
11
+ import { parseErDiagram } from "../er/parser.js";
12
+ import { mkCanvas, mkRoleCanvas, canvasToString, increaseSize, increaseRoleCanvasSize, setRole } from "./canvas.js";
13
+ import { drawMultiBox } from "./draw.js";
14
+ import { splitLines } from "./multiline-utils.js";
15
+ import { displayWidth, toCells, WIDE_PAD } from "../text-metrics.js";
16
+ /** Classify a character from a box drawing as 'border' or 'text'. */
17
+ function classifyBoxChar(ch) {
18
+ if (/^[┌┐└┘├┤┬┴┼│─╭╮╰╯+\-|]$/.test(ch))
19
+ return 'border';
20
+ return 'text';
21
+ }
22
+ // ============================================================================
23
+ // Entity box content
24
+ // ============================================================================
25
+ /** Format an attribute line: "PK type name" or "FK type name" etc. */
26
+ function formatAttribute(attr) {
27
+ const keyStr = attr.keys.length > 0 ? attr.keys.join(',') + ' ' : ' ';
28
+ return `${keyStr}${attr.type} ${attr.name}`;
29
+ }
30
+ /** Build sections for an entity box: [header], [attributes] */
31
+ function buildEntitySections(entity) {
32
+ // Support multi-line entity names
33
+ const header = splitLines(entity.label);
34
+ const attrs = entity.attributes.map(formatAttribute);
35
+ if (attrs.length === 0)
36
+ return [header];
37
+ return [header, attrs];
38
+ }
39
+ // ============================================================================
40
+ // Crow's foot notation
41
+ // ============================================================================
42
+ /**
43
+ * Returns the ASCII/Unicode characters for a crow's foot cardinality marker.
44
+ * Markers are drawn adjacent to entity boxes at relationship endpoints.
45
+ *
46
+ * Standard ER notation:
47
+ * one: ─┤├─ perpendicular line (exactly one)
48
+ * zero-one: ─○┤─ circle + perpendicular (zero or one)
49
+ * many: ─<>─ crow's foot (one or more)
50
+ * zero-many: ─○<─ circle + crow's foot (zero or more)
51
+ *
52
+ * @param card - The cardinality type
53
+ * @param useAscii - Use ASCII-only characters
54
+ * @param isRight - True if this marker is on the right side of the relationship
55
+ */
56
+ function getCrowsFootChars(card, useAscii, isRight = false) {
57
+ if (useAscii) {
58
+ switch (card) {
59
+ case 'one': return '|';
60
+ case 'zero-one': return 'o|';
61
+ case 'many': return isRight ? '<' : '>';
62
+ case 'zero-many': return isRight ? 'o<' : '>o';
63
+ }
64
+ }
65
+ else {
66
+ // Use cleaner Unicode characters
67
+ switch (card) {
68
+ case 'one': return '│';
69
+ case 'zero-one': return '○│';
70
+ case 'many': return isRight ? '╟' : '╢';
71
+ case 'zero-many': return isRight ? '○╟' : '╢○';
72
+ }
73
+ }
74
+ }
75
+ // ============================================================================
76
+ // Connected Component Detection
77
+ // ============================================================================
78
+ /**
79
+ * Find connected components in the ER diagram using DFS.
80
+ * Treats relationships as undirected edges for connectivity.
81
+ *
82
+ * Returns an array of entity ID sets, one per connected component.
83
+ */
84
+ function findConnectedComponents(diagram) {
85
+ const visited = new Set();
86
+ const components = [];
87
+ // Build undirected adjacency list from relationships
88
+ const neighbors = new Map();
89
+ for (const ent of diagram.entities) {
90
+ neighbors.set(ent.id, new Set());
91
+ }
92
+ for (const rel of diagram.relationships) {
93
+ neighbors.get(rel.entity1)?.add(rel.entity2);
94
+ neighbors.get(rel.entity2)?.add(rel.entity1);
95
+ }
96
+ // DFS to find each component
97
+ function dfs(startId, component) {
98
+ const stack = [startId];
99
+ while (stack.length > 0) {
100
+ const nodeId = stack.pop();
101
+ if (visited.has(nodeId))
102
+ continue;
103
+ visited.add(nodeId);
104
+ component.add(nodeId);
105
+ for (const neighbor of neighbors.get(nodeId) ?? []) {
106
+ if (!visited.has(neighbor)) {
107
+ stack.push(neighbor);
108
+ }
109
+ }
110
+ }
111
+ }
112
+ // Find all components
113
+ for (const ent of diagram.entities) {
114
+ if (!visited.has(ent.id)) {
115
+ const component = new Set();
116
+ dfs(ent.id, component);
117
+ if (component.size > 0) {
118
+ components.push(component);
119
+ }
120
+ }
121
+ }
122
+ return components;
123
+ }
124
+ // ============================================================================
125
+ // Layout and rendering
126
+ // ============================================================================
127
+ /**
128
+ * Render a Mermaid ER diagram to ASCII/Unicode text.
129
+ *
130
+ * Pipeline: parse → build boxes → component-aware layout → draw boxes → draw relationships → string.
131
+ */
132
+ export function renderErAscii(text, config, colorMode, theme) {
133
+ const lines = text.split('\n').map(l => l.trim()).filter(l => l.length > 0 && !l.startsWith('%%'));
134
+ const diagram = parseErDiagram(lines);
135
+ if (diagram.entities.length === 0)
136
+ return '';
137
+ const useAscii = config.useAscii;
138
+ const hGap = 6; // horizontal gap between entity boxes
139
+ const vGap = 4; // vertical gap between rows (for relationship lines)
140
+ const componentGap = 6; // vertical gap between disconnected components
141
+ // --- Build entity box dimensions ---
142
+ const entitySections = new Map();
143
+ const entityBoxW = new Map();
144
+ const entityBoxH = new Map();
145
+ const entityById = new Map();
146
+ for (const ent of diagram.entities) {
147
+ entityById.set(ent.id, ent);
148
+ const sections = buildEntitySections(ent);
149
+ entitySections.set(ent.id, sections);
150
+ let maxTextW = 0;
151
+ for (const section of sections) {
152
+ for (const line of section)
153
+ maxTextW = Math.max(maxTextW, displayWidth(line));
154
+ }
155
+ const boxW = maxTextW + 4; // 2 border + 2 padding
156
+ let totalLines = 0;
157
+ for (const section of sections)
158
+ totalLines += Math.max(section.length, 1);
159
+ const boxH = totalLines + (sections.length - 1) + 2;
160
+ entityBoxW.set(ent.id, boxW);
161
+ entityBoxH.set(ent.id, boxH);
162
+ }
163
+ // --- Find connected components ---
164
+ const components = findConnectedComponents(diagram);
165
+ // --- Layout: place each component, then stack components vertically ---
166
+ const placed = new Map();
167
+ let currentY = 0;
168
+ for (const component of components) {
169
+ // Get entities in this component (preserve original order for consistency)
170
+ const componentEntities = diagram.entities.filter(e => component.has(e.id));
171
+ // Layout entities within this component horizontally
172
+ // Use sqrt-based row limit for larger components
173
+ const maxPerRow = Math.max(2, Math.ceil(Math.sqrt(componentEntities.length)));
174
+ let currentX = 0;
175
+ let maxRowH = 0;
176
+ let colCount = 0;
177
+ const componentStartY = currentY;
178
+ for (const ent of componentEntities) {
179
+ const w = entityBoxW.get(ent.id);
180
+ const h = entityBoxH.get(ent.id);
181
+ if (colCount >= maxPerRow) {
182
+ // Wrap to next row within this component
183
+ currentY += maxRowH + vGap;
184
+ currentX = 0;
185
+ maxRowH = 0;
186
+ colCount = 0;
187
+ }
188
+ placed.set(ent.id, {
189
+ entity: ent,
190
+ sections: entitySections.get(ent.id),
191
+ x: currentX,
192
+ y: currentY,
193
+ width: w,
194
+ height: h,
195
+ });
196
+ currentX += w + hGap;
197
+ maxRowH = Math.max(maxRowH, h);
198
+ colCount++;
199
+ }
200
+ // Move to next component row (add gap between components)
201
+ currentY += maxRowH + componentGap;
202
+ }
203
+ // --- Create canvas ---
204
+ let totalW = 0;
205
+ let totalH = 0;
206
+ for (const p of placed.values()) {
207
+ totalW = Math.max(totalW, p.x + p.width);
208
+ totalH = Math.max(totalH, p.y + p.height);
209
+ }
210
+ totalW += 4;
211
+ totalH += 2;
212
+ const canvas = mkCanvas(totalW - 1, totalH - 1);
213
+ const rc = mkRoleCanvas(totalW - 1, totalH - 1);
214
+ /** Set a character on the canvas and track its role. */
215
+ function setC(x, y, ch, role) {
216
+ if (x >= 0 && x < canvas.length && y >= 0 && y < (canvas[0]?.length ?? 0)) {
217
+ canvas[x][y] = ch;
218
+ setRole(rc, x, y, role);
219
+ }
220
+ }
221
+ // --- Draw entity boxes ---
222
+ for (const p of placed.values()) {
223
+ const boxCanvas = drawMultiBox(p.sections, useAscii);
224
+ for (let bx = 0; bx < boxCanvas.length; bx++) {
225
+ for (let by = 0; by < boxCanvas[0].length; by++) {
226
+ const ch = boxCanvas[bx][by];
227
+ if (ch !== ' ') {
228
+ const cx = p.x + bx;
229
+ const cy = p.y + by;
230
+ if (cx < totalW && cy < totalH) {
231
+ setC(cx, cy, ch, classifyBoxChar(ch));
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ // --- Draw relationships ---
238
+ const H = useAscii ? '-' : '─';
239
+ const V = useAscii ? '|' : '│';
240
+ const dashH = useAscii ? '.' : '╌';
241
+ const dashV = useAscii ? ':' : '┊';
242
+ for (const rel of diagram.relationships) {
243
+ const e1 = placed.get(rel.entity1);
244
+ const e2 = placed.get(rel.entity2);
245
+ if (!e1 || !e2)
246
+ continue;
247
+ const lineH = rel.identifying ? H : dashH;
248
+ const lineV = rel.identifying ? V : dashV;
249
+ // Determine connection direction based on relative position.
250
+ // Connect from right side of left entity to left side of right entity (horizontal),
251
+ // or from bottom of upper entity to top of lower entity (vertical).
252
+ const e1CX = e1.x + Math.floor(e1.width / 2);
253
+ const e1CY = e1.y + Math.floor(e1.height / 2);
254
+ const e2CX = e2.x + Math.floor(e2.width / 2);
255
+ const e2CY = e2.y + Math.floor(e2.height / 2);
256
+ // Check if entities are on the same row (horizontal connection)
257
+ const sameRow = Math.abs(e1CY - e2CY) < Math.max(e1.height, e2.height);
258
+ if (sameRow) {
259
+ // Horizontal connection: right side of left entity → left side of right entity
260
+ const [left, right] = e1CX < e2CX ? [e1, e2] : [e2, e1];
261
+ const [leftCard, rightCard] = e1CX < e2CX
262
+ ? [rel.cardinality1, rel.cardinality2]
263
+ : [rel.cardinality2, rel.cardinality1];
264
+ const startX = left.x + left.width;
265
+ const endX = right.x - 1;
266
+ const lineY = left.y + Math.floor(left.height / 2);
267
+ // Draw horizontal line
268
+ for (let x = startX; x <= endX; x++) {
269
+ setC(x, lineY, lineH, 'line');
270
+ }
271
+ // Draw crow's foot markers at endpoints
272
+ // Left marker (at left entity's right edge) - isRight=false
273
+ const leftChars = getCrowsFootChars(leftCard, useAscii, false);
274
+ for (let i = 0; i < leftChars.length; i++) {
275
+ setC(startX + i, lineY, leftChars[i], 'arrow');
276
+ }
277
+ // Right marker (at right entity's left edge) - isRight=true
278
+ const rightChars = getCrowsFootChars(rightCard, useAscii, true);
279
+ for (let i = 0; i < rightChars.length; i++) {
280
+ setC(endX - rightChars.length + 1 + i, lineY, rightChars[i], 'arrow');
281
+ }
282
+ // Relationship label centered in the gap between the two entities, below the line.
283
+ // Clamp label to the gap region [startX, endX] to avoid overwriting box borders.
284
+ // Supports multi-line labels.
285
+ if (rel.label) {
286
+ const lines = splitLines(rel.label);
287
+ const gapMid = Math.floor((startX + endX) / 2);
288
+ // Place lines below the relationship line (lineY + 1, lineY + 2, ...)
289
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
290
+ const line = lines[lineIdx];
291
+ const cells = toCells(line);
292
+ const labelStart = Math.max(startX, gapMid - Math.floor(cells.length / 2));
293
+ const labelY = lineY + 1 + lineIdx;
294
+ // Ensure canvas is tall enough
295
+ increaseSize(canvas, Math.max(labelStart + cells.length, 1), Math.max(labelY + 1, 1));
296
+ increaseRoleCanvasSize(rc, Math.max(labelStart + cells.length, 1), Math.max(labelY + 1, 1));
297
+ for (let i = 0; i < cells.length; i++) {
298
+ const cell = cells[i];
299
+ if (cell === WIDE_PAD)
300
+ continue; // written atomically with its lead
301
+ const lx = labelStart + i;
302
+ const wide = cells[i + 1] === WIDE_PAD;
303
+ // Keep wide-glyph pairs atomic within the gap region
304
+ if (lx < startX || lx + (wide ? 1 : 0) > endX)
305
+ continue;
306
+ setC(lx, labelY, cell, 'text');
307
+ if (wide)
308
+ setC(lx + 1, labelY, WIDE_PAD, 'text');
309
+ }
310
+ }
311
+ }
312
+ }
313
+ else {
314
+ // Vertical connection: bottom of upper entity → top of lower entity
315
+ const [upper, lower] = e1CY < e2CY ? [e1, e2] : [e2, e1];
316
+ const [upperCard, lowerCard] = e1CY < e2CY
317
+ ? [rel.cardinality1, rel.cardinality2]
318
+ : [rel.cardinality2, rel.cardinality1];
319
+ const startY = upper.y + upper.height;
320
+ const endY = lower.y - 1;
321
+ const lineX = upper.x + Math.floor(upper.width / 2);
322
+ // Vertical line
323
+ for (let y = startY; y <= endY; y++) {
324
+ setC(lineX, y, lineV, 'line');
325
+ }
326
+ // If horizontal offset needed, add a horizontal segment
327
+ const lowerCX = lower.x + Math.floor(lower.width / 2);
328
+ if (lineX !== lowerCX) {
329
+ const midY = Math.floor((startY + endY) / 2);
330
+ // Horizontal segment at midY
331
+ const lx = Math.min(lineX, lowerCX);
332
+ const rx = Math.max(lineX, lowerCX);
333
+ for (let x = lx; x <= rx; x++) {
334
+ setC(x, midY, lineH, 'line');
335
+ }
336
+ // Vertical from midY to lower entity
337
+ for (let y = midY + 1; y <= endY; y++) {
338
+ setC(lowerCX, y, lineV, 'line');
339
+ }
340
+ }
341
+ // Crow's foot markers (vertical direction)
342
+ // Upper marker (at upper entity's bottom edge) - treat as source side (isRight=false)
343
+ const upperChars = getCrowsFootChars(upperCard, useAscii, false);
344
+ for (let i = 0; i < upperChars.length; i++) {
345
+ setC(lineX - Math.floor(upperChars.length / 2) + i, startY, upperChars[i], 'arrow');
346
+ }
347
+ // Lower marker (at lower entity's top edge) - treat as target side (isRight=true)
348
+ const targetX = lineX !== lowerCX ? lowerCX : lineX;
349
+ const lowerChars = getCrowsFootChars(lowerCard, useAscii, true);
350
+ for (let i = 0; i < lowerChars.length; i++) {
351
+ setC(targetX - Math.floor(lowerChars.length / 2) + i, endY, lowerChars[i], 'arrow');
352
+ }
353
+ // Relationship label — placed to the right of the vertical line at the midpoint.
354
+ // We expand the canvas as needed since labels can extend beyond the initial bounds.
355
+ // Supports multi-line labels.
356
+ if (rel.label) {
357
+ const lines = splitLines(rel.label);
358
+ const midY = Math.floor((startY + endY) / 2);
359
+ // Center lines vertically around midY
360
+ const startLabelY = midY - Math.floor((lines.length - 1) / 2);
361
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
362
+ const cells = toCells(lines[lineIdx]);
363
+ const labelX = lineX + 2;
364
+ const y = startLabelY + lineIdx;
365
+ if (y >= 0) {
366
+ for (let i = 0; i < cells.length; i++) {
367
+ const lx = labelX + i;
368
+ if (lx >= 0) {
369
+ increaseSize(canvas, lx + 1, y + 1);
370
+ increaseRoleCanvasSize(rc, lx + 1, y + 1);
371
+ setC(lx, y, cells[i], 'text');
372
+ }
373
+ }
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
379
+ return canvasToString(canvas, { roleCanvas: rc, colorMode, theme });
380
+ }
@@ -0,0 +1,57 @@
1
+ import type { GridCoord, DrawingCoord, Direction, AsciiGraph, AsciiNode, AsciiSubgraph } from './types.ts';
2
+ /**
3
+ * Convert a grid coordinate to a drawing (character) coordinate.
4
+ * Sums column widths up to the target column, and row heights up to the target row,
5
+ * then centers within the cell.
6
+ */
7
+ export declare function gridToDrawingCoord(graph: AsciiGraph, c: GridCoord, dir?: Direction): DrawingCoord;
8
+ /** Convert a path of grid coords to drawing coords. */
9
+ export declare function lineToDrawing(graph: AsciiGraph, line: GridCoord[]): DrawingCoord[];
10
+ /**
11
+ * Reserve a 3x3 block in the grid for a node.
12
+ * If the requested position is occupied, recursively shift by 4 grid units
13
+ * (in the perpendicular direction based on effective direction) until a free spot is found.
14
+ *
15
+ * @param effectiveDir - Optional direction override. If not provided, uses the node's
16
+ * effective direction (subgraph direction if in a subgraph with override,
17
+ * otherwise graph direction).
18
+ */
19
+ export declare function reserveSpotInGrid(graph: AsciiGraph, node: AsciiNode, requested: GridCoord, effectiveDir?: 'LR' | 'TD'): GridCoord;
20
+ /**
21
+ * Set column widths and row heights for a node's 3x3 grid block.
22
+ * Each node occupies 3 columns (border, content, border) and 3 rows.
23
+ * Uses shape-aware dimensions to properly size non-rectangular shapes.
24
+ */
25
+ export declare function setColumnWidth(graph: AsciiGraph, node: AsciiNode): void;
26
+ /** Ensure grid has width/height entries for all cells along an edge path. */
27
+ export declare function increaseGridSizeForPath(graph: AsciiGraph, path: GridCoord[]): void;
28
+ /**
29
+ * Get the innermost subgraph that directly contains this node.
30
+ * Returns null if node is not in any subgraph.
31
+ */
32
+ export declare function getNodeSubgraph(graph: AsciiGraph, node: AsciiNode): AsciiSubgraph | null;
33
+ /**
34
+ * Get the effective direction for a node's layout.
35
+ * Returns the subgraph's direction override if the node is in a subgraph with one,
36
+ * otherwise returns the graph-level direction.
37
+ */
38
+ export declare function getEffectiveDirection(graph: AsciiGraph, node: AsciiNode): 'LR' | 'TD';
39
+ export declare function calculateSubgraphBoundingBoxes(graph: AsciiGraph): void;
40
+ /**
41
+ * Offset all drawing coordinates so subgraph borders don't go negative.
42
+ * If any subgraph has negative min coordinates, shift everything positive.
43
+ */
44
+ export declare function offsetDrawingForSubgraphs(graph: AsciiGraph): void;
45
+ /**
46
+ * createMapping performs the full grid layout:
47
+ * 1. Place root nodes on the grid
48
+ * 2. Place child nodes level by level
49
+ * 3. Compute column widths and row heights
50
+ * 4. Run A* pathfinding for all edges
51
+ * 5. Determine label placement
52
+ * 6. Convert grid coords → drawing coords
53
+ * 7. Generate node box drawings
54
+ * 8. Calculate subgraph bounding boxes
55
+ */
56
+ export declare function createMapping(graph: AsciiGraph): void;
57
+ //# sourceMappingURL=grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../../src/vendor/mermaid-ascii/ascii/grid.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EACzE,MAAM,YAAY,CAAA;AAoBnB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,CAAC,EAAE,SAAS,EACZ,GAAG,CAAC,EAAE,SAAS,GACd,YAAY,CAqBd;AAED,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAElF;AAMD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,SAAS,EACf,SAAS,EAAE,SAAS,EACpB,YAAY,CAAC,EAAE,IAAI,GAAG,IAAI,GACzB,SAAS,CAuBX;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CA0CvE;AAED,6EAA6E;AAC7E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CASlF;AAUD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,aAAa,GAAG,IAAI,CAYxF;AAYD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,IAAI,CAMrF;AAuHD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAKtE;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAsCjE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CA0KrD"}