@oh-my-pi/pi-utils 16.0.7 → 16.0.9

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 (87) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/types/mermaid-ascii.d.ts +1 -1
  3. package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
  4. package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
  5. package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
  6. package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
  7. package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
  8. package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
  9. package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
  10. package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
  11. package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
  12. package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
  13. package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
  14. package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
  15. package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
  16. package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
  17. package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
  18. package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
  19. package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
  20. package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
  21. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
  22. package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
  23. package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
  24. package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
  25. package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
  26. package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
  27. package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
  28. package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
  29. package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
  30. package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
  31. package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
  32. package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
  33. package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
  34. package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
  35. package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
  36. package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
  37. package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
  38. package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
  39. package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
  40. package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
  41. package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
  42. package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
  43. package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
  44. package/package.json +2 -3
  45. package/src/mermaid-ascii.ts +1 -1
  46. package/src/vendor/mermaid-ascii/NOTICE +33 -0
  47. package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
  48. package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
  49. package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
  50. package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
  51. package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
  52. package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
  53. package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
  54. package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
  55. package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
  56. package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
  57. package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
  58. package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +215 -0
  59. package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
  60. package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
  61. package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
  62. package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
  63. package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
  64. package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
  65. package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
  66. package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
  67. package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
  68. package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
  69. package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
  70. package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
  71. package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
  72. package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
  73. package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
  74. package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
  75. package/src/vendor/mermaid-ascii/class/types.ts +121 -0
  76. package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
  77. package/src/vendor/mermaid-ascii/er/types.ts +91 -0
  78. package/src/vendor/mermaid-ascii/index.ts +14 -0
  79. package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
  80. package/src/vendor/mermaid-ascii/parser.ts +645 -0
  81. package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
  82. package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
  83. package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
  84. package/src/vendor/mermaid-ascii/types.ts +164 -0
  85. package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
  86. package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
  87. package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
@@ -0,0 +1,114 @@
1
+ // ============================================================================
2
+ // Stadium (pill) shape renderer — special parentheses-based rendering
3
+ // ============================================================================
4
+ //
5
+ // Stadium has unique rendering: single-line is inline `(Label)`, multi-line
6
+ // uses parentheses or rounded corners. This differs from other shapes that
7
+ // use corner decorators with box lines.
8
+
9
+ import type { Canvas, DrawingCoord, Direction } from '../types'
10
+ import { mkCanvas } from '../canvas'
11
+ import { splitLines } from '../multiline-utils'
12
+ import type { ShapeRenderer, ShapeDimensions, ShapeRenderOptions } from './types'
13
+ import { getBoxAttachmentPoint } from './rectangle'
14
+ import { displayWidth, toCells } from '../../text-metrics'
15
+
16
+ /**
17
+ * Stadium (pill) shape renderer.
18
+ *
19
+ * Single-line: ( Label )
20
+ *
21
+ * Multi-line unicode:
22
+ * ╭──────────╮
23
+ * │ Label │
24
+ * ╰──────────╯
25
+ *
26
+ * Multi-line ASCII:
27
+ * (----------)
28
+ * ( Label )
29
+ * (----------)
30
+ */
31
+ export const stadiumRenderer: ShapeRenderer = {
32
+ getDimensions(label: string, options: ShapeRenderOptions): ShapeDimensions {
33
+ const lines = splitLines(label)
34
+ const maxLineWidth = Math.max(...lines.map(l => displayWidth(l)), 0)
35
+ const lineCount = lines.length
36
+
37
+ const innerWidth = 2 * options.padding + maxLineWidth
38
+ const width = innerWidth + 4 // Extra for rounded ends
39
+ const innerHeight = lineCount + 2 * options.padding
40
+ const height = Math.max(innerHeight + 2, 3)
41
+
42
+ return {
43
+ width,
44
+ height,
45
+ labelArea: {
46
+ x: 2 + options.padding,
47
+ y: 1 + options.padding,
48
+ width: maxLineWidth,
49
+ height: lineCount,
50
+ },
51
+ gridColumns: [2, innerWidth, 2],
52
+ gridRows: [1, innerHeight, 1],
53
+ }
54
+ },
55
+
56
+ render(label: string, dimensions: ShapeDimensions, options: ShapeRenderOptions): Canvas {
57
+ const { width, height } = dimensions
58
+ const canvas = mkCanvas(width - 1, height - 1)
59
+
60
+ const centerY = Math.floor(height / 2)
61
+ const hChar = options.useAscii ? '-' : '─'
62
+
63
+ if (height === 3) {
64
+ // Single row pill: ( Label )
65
+ canvas[0]![centerY] = '('
66
+ canvas[width - 1]![centerY] = ')'
67
+ } else if (!options.useAscii) {
68
+ // Multi-row stadium with rounded corners (unicode)
69
+ canvas[0]![0] = '╭'
70
+ for (let x = 1; x < width - 1; x++) canvas[x]![0] = hChar
71
+ canvas[width - 1]![0] = '╮'
72
+
73
+ for (let y = 1; y < height - 1; y++) {
74
+ canvas[0]![y] = '│'
75
+ canvas[width - 1]![y] = '│'
76
+ }
77
+
78
+ canvas[0]![height - 1] = '╰'
79
+ for (let x = 1; x < width - 1; x++) canvas[x]![height - 1] = hChar
80
+ canvas[width - 1]![height - 1] = '╯'
81
+ } else {
82
+ // Multi-row stadium ASCII — parentheses on all sides
83
+ for (let y = 0; y < height; y++) {
84
+ canvas[0]![y] = '('
85
+ canvas[width - 1]![y] = ')'
86
+ }
87
+ for (let x = 1; x < width - 1; x++) {
88
+ canvas[x]![0] = hChar
89
+ canvas[x]![height - 1] = hChar
90
+ }
91
+ }
92
+
93
+ // Center the label
94
+ const lines = splitLines(label)
95
+ const startY = centerY - Math.floor((lines.length - 1) / 2)
96
+
97
+ for (let i = 0; i < lines.length; i++) {
98
+ const line = lines[i]!
99
+ const cells = toCells(line)
100
+ const textX = Math.floor(width / 2) - Math.floor(cells.length / 2)
101
+ for (let j = 0; j < cells.length; j++) {
102
+ const x = textX + j
103
+ const y = startY + i
104
+ if (x > 0 && x < width - 1 && y >= 0 && y < height) {
105
+ canvas[x]![y] = cells[j]!
106
+ }
107
+ }
108
+ }
109
+
110
+ return canvas
111
+ },
112
+
113
+ getAttachmentPoint: getBoxAttachmentPoint,
114
+ }
@@ -0,0 +1,192 @@
1
+ // ============================================================================
2
+ // State pseudo-state renderers — UML start and end states
3
+ // ============================================================================
4
+
5
+ import type { Canvas, DrawingCoord, Direction } from '../types'
6
+ import { Up, Down, Left, Right, UpperLeft, UpperRight, LowerLeft, LowerRight } from '../types'
7
+ import { mkCanvas } from '../canvas'
8
+ import type { ShapeRenderer, ShapeDimensions, ShapeRenderOptions } from './types'
9
+ import { dirEquals } from '../edge-routing'
10
+
11
+ /**
12
+ * State start pseudo-state renderer — filled circle in rounded box.
13
+ * Renders as:
14
+ * ╭───╮
15
+ * │ ● │ (Unicode)
16
+ * ╰───╯
17
+ *
18
+ * .---.
19
+ * | * | (ASCII)
20
+ * '---'
21
+ *
22
+ * This represents the UML initial pseudo-state.
23
+ */
24
+ export const stateStartRenderer: ShapeRenderer = {
25
+ getDimensions(_label: string, _options: ShapeRenderOptions): ShapeDimensions {
26
+ // Start state is a 5x3 rounded box with centered symbol
27
+ const width = 5
28
+ const height = 3
29
+
30
+ return {
31
+ width,
32
+ height,
33
+ labelArea: { x: 2, y: 1, width: 1, height: 1 },
34
+ gridColumns: [1, 3, 1],
35
+ gridRows: [1, 1, 1],
36
+ }
37
+ },
38
+
39
+ render(_label: string, dimensions: ShapeDimensions, options: ShapeRenderOptions): Canvas {
40
+ const { width, height } = dimensions
41
+ const canvas = mkCanvas(width - 1, height - 1)
42
+
43
+ const centerX = Math.floor(width / 2) // = 2
44
+
45
+ if (!options.useAscii) {
46
+ // Unicode rounded box with filled circle: ╭───╮ │ ● │ ╰───╯
47
+ canvas[0]![0] = '╭'
48
+ canvas[1]![0] = '─'
49
+ canvas[2]![0] = '─'
50
+ canvas[3]![0] = '─'
51
+ canvas[4]![0] = '╮'
52
+
53
+ canvas[0]![1] = '│'
54
+ canvas[centerX]![1] = '●'
55
+ canvas[4]![1] = '│'
56
+
57
+ canvas[0]![2] = '╰'
58
+ canvas[1]![2] = '─'
59
+ canvas[2]![2] = '─'
60
+ canvas[3]![2] = '─'
61
+ canvas[4]![2] = '╯'
62
+ } else {
63
+ // ASCII rounded box: .---. | * | '---'
64
+ canvas[0]![0] = '.'
65
+ canvas[1]![0] = '-'
66
+ canvas[2]![0] = '-'
67
+ canvas[3]![0] = '-'
68
+ canvas[4]![0] = '.'
69
+
70
+ canvas[0]![1] = '|'
71
+ canvas[centerX]![1] = '*'
72
+ canvas[4]![1] = '|'
73
+
74
+ canvas[0]![2] = '\''
75
+ canvas[1]![2] = '-'
76
+ canvas[2]![2] = '-'
77
+ canvas[3]![2] = '-'
78
+ canvas[4]![2] = '\''
79
+ }
80
+
81
+ return canvas
82
+ },
83
+
84
+ getAttachmentPoint(
85
+ dir: Direction,
86
+ dimensions: ShapeDimensions,
87
+ baseCoord: DrawingCoord
88
+ ): DrawingCoord {
89
+ const { width, height } = dimensions
90
+ const centerX = baseCoord.x + Math.floor(width / 2)
91
+ const centerY = baseCoord.y + Math.floor(height / 2)
92
+
93
+ if (dirEquals(dir, Up)) return { x: centerX, y: baseCoord.y }
94
+ if (dirEquals(dir, Down)) return { x: centerX, y: baseCoord.y + height - 1 }
95
+ if (dirEquals(dir, Left)) return { x: baseCoord.x, y: centerY }
96
+ if (dirEquals(dir, Right)) return { x: baseCoord.x + width - 1, y: centerY }
97
+ // All diagonals and middle point to center
98
+ return { x: centerX, y: centerY }
99
+ },
100
+ }
101
+
102
+ /**
103
+ * State end pseudo-state renderer — bullseye in double-bordered box.
104
+ * Renders as:
105
+ * ╔═══╗
106
+ * ║ ◎ ║ (Unicode)
107
+ * ╚═══╝
108
+ *
109
+ * #===#
110
+ * # * # (ASCII)
111
+ * #===#
112
+ *
113
+ * This represents the UML final state. The double border distinguishes it
114
+ * from the start state's single rounded border.
115
+ */
116
+ export const stateEndRenderer: ShapeRenderer = {
117
+ getDimensions(_label: string, _options: ShapeRenderOptions): ShapeDimensions {
118
+ // End state is a 5x3 double-bordered box with centered symbol
119
+ const width = 5
120
+ const height = 3
121
+
122
+ return {
123
+ width,
124
+ height,
125
+ labelArea: { x: 2, y: 1, width: 1, height: 1 },
126
+ gridColumns: [1, 3, 1],
127
+ gridRows: [1, 1, 1],
128
+ }
129
+ },
130
+
131
+ render(_label: string, dimensions: ShapeDimensions, options: ShapeRenderOptions): Canvas {
132
+ const { width, height } = dimensions
133
+ const canvas = mkCanvas(width - 1, height - 1)
134
+
135
+ const centerX = Math.floor(width / 2) // = 2
136
+
137
+ if (!options.useAscii) {
138
+ // Unicode double-bordered box with bullseye: ╔═══╗ ║ ◎ ║ ╚═══╝
139
+ canvas[0]![0] = '╔'
140
+ canvas[1]![0] = '═'
141
+ canvas[2]![0] = '═'
142
+ canvas[3]![0] = '═'
143
+ canvas[4]![0] = '╗'
144
+
145
+ canvas[0]![1] = '║'
146
+ canvas[centerX]![1] = '◎'
147
+ canvas[4]![1] = '║'
148
+
149
+ canvas[0]![2] = '╚'
150
+ canvas[1]![2] = '═'
151
+ canvas[2]![2] = '═'
152
+ canvas[3]![2] = '═'
153
+ canvas[4]![2] = '╝'
154
+ } else {
155
+ // ASCII double-bordered box: #===# # * # #===#
156
+ canvas[0]![0] = '#'
157
+ canvas[1]![0] = '='
158
+ canvas[2]![0] = '='
159
+ canvas[3]![0] = '='
160
+ canvas[4]![0] = '#'
161
+
162
+ canvas[0]![1] = '#'
163
+ canvas[centerX]![1] = '*'
164
+ canvas[4]![1] = '#'
165
+
166
+ canvas[0]![2] = '#'
167
+ canvas[1]![2] = '='
168
+ canvas[2]![2] = '='
169
+ canvas[3]![2] = '='
170
+ canvas[4]![2] = '#'
171
+ }
172
+
173
+ return canvas
174
+ },
175
+
176
+ getAttachmentPoint(
177
+ dir: Direction,
178
+ dimensions: ShapeDimensions,
179
+ baseCoord: DrawingCoord
180
+ ): DrawingCoord {
181
+ const { width, height } = dimensions
182
+ const centerX = baseCoord.x + Math.floor(width / 2)
183
+ const centerY = baseCoord.y + Math.floor(height / 2)
184
+
185
+ if (dirEquals(dir, Up)) return { x: centerX, y: baseCoord.y }
186
+ if (dirEquals(dir, Down)) return { x: centerX, y: baseCoord.y + height - 1 }
187
+ if (dirEquals(dir, Left)) return { x: baseCoord.x, y: centerY }
188
+ if (dirEquals(dir, Right)) return { x: baseCoord.x + width - 1, y: centerY }
189
+ // All diagonals and middle point to center
190
+ return { x: centerX, y: centerY }
191
+ },
192
+ }
@@ -0,0 +1,73 @@
1
+ // ============================================================================
2
+ // Shape renderer types — interface for pluggable ASCII shape renderers
3
+ // ============================================================================
4
+
5
+ import type { Canvas, DrawingCoord, Direction, AsciiNodeShape } from '../types'
6
+
7
+ /**
8
+ * Dimensions calculated for a shape, used by layout and rendering.
9
+ */
10
+ export interface ShapeDimensions {
11
+ /** Total width in characters including borders */
12
+ width: number
13
+ /** Total height in characters including borders */
14
+ height: number
15
+ /** Label area bounds (where text can be placed) */
16
+ labelArea: {
17
+ x: number
18
+ y: number
19
+ width: number
20
+ height: number
21
+ }
22
+ /** Grid column widths for the 3-column layout [left, center, right] */
23
+ gridColumns: [number, number, number]
24
+ /** Grid row heights for the 3-row layout [top, middle, bottom] */
25
+ gridRows: [number, number, number]
26
+ }
27
+
28
+ /**
29
+ * Options passed to shape renderers.
30
+ */
31
+ export interface ShapeRenderOptions {
32
+ /** Use ASCII chars (+,-,|) vs Unicode box-drawing (┌,─,│) */
33
+ useAscii: boolean
34
+ /** Padding inside the shape */
35
+ padding: number
36
+ }
37
+
38
+ /**
39
+ * Interface for pluggable shape renderers.
40
+ * Each shape type implements this interface.
41
+ */
42
+ export interface ShapeRenderer {
43
+ /**
44
+ * Calculate dimensions for this shape given a label.
45
+ * Used during layout to determine node size.
46
+ */
47
+ getDimensions(label: string, options: ShapeRenderOptions): ShapeDimensions
48
+
49
+ /**
50
+ * Render the shape to a canvas.
51
+ * Returns a standalone canvas containing just the shape.
52
+ */
53
+ render(
54
+ label: string,
55
+ dimensions: ShapeDimensions,
56
+ options: ShapeRenderOptions
57
+ ): Canvas
58
+
59
+ /**
60
+ * Get the edge attachment point for a given direction.
61
+ * Used by edge routing to determine where edges connect.
62
+ */
63
+ getAttachmentPoint(
64
+ dir: Direction,
65
+ dimensions: ShapeDimensions,
66
+ baseCoord: DrawingCoord
67
+ ): DrawingCoord
68
+ }
69
+
70
+ /**
71
+ * Registry of shape renderers keyed by shape type.
72
+ */
73
+ export type ShapeRegistry = Map<AsciiNodeShape, ShapeRenderer>
@@ -0,0 +1,273 @@
1
+ // ============================================================================
2
+ // ASCII renderer — type definitions
3
+ //
4
+ // Ported from AlexanderGrooff/mermaid-ascii (Go).
5
+ // These types model the grid-based coordinate system, 2D text canvas,
6
+ // and graph structures used by the ASCII/Unicode renderer.
7
+ // ============================================================================
8
+
9
+ import type { NodeShape } from '../types'
10
+
11
+ // Re-export NodeShape for convenience
12
+ export type { NodeShape }
13
+
14
+ /**
15
+ * Shape type for ASCII rendering — maps parser shapes to ASCII renderers.
16
+ * Most shapes from the parser are supported, with fallback to 'rectangle'.
17
+ */
18
+ export type AsciiNodeShape = NodeShape
19
+
20
+ /** Logical grid coordinate — nodes occupy 3x3 blocks on this grid. */
21
+ export interface GridCoord {
22
+ x: number
23
+ y: number
24
+ }
25
+
26
+ /** Character-level coordinate on the 2D text canvas. */
27
+ export interface DrawingCoord {
28
+ x: number
29
+ y: number
30
+ }
31
+
32
+ /**
33
+ * Direction constants model positions on a node's 3x3 grid block.
34
+ * Each node occupies grid cells [x..x+2, y..y+2].
35
+ * Directions are offsets into that block, used for edge attachment points.
36
+ *
37
+ * (0,0) UL (1,0) Up (2,0) UR
38
+ * (0,1) Left (1,1) Mid (2,1) Right
39
+ * (0,2) LL (1,2) Down (2,2) LR
40
+ */
41
+ export interface Direction {
42
+ readonly x: number
43
+ readonly y: number
44
+ }
45
+
46
+ export const Up: Direction = { x: 1, y: 0 }
47
+ export const Down: Direction = { x: 1, y: 2 }
48
+ export const Left: Direction = { x: 0, y: 1 }
49
+ export const Right: Direction = { x: 2, y: 1 }
50
+ export const UpperRight: Direction = { x: 2, y: 0 }
51
+ export const UpperLeft: Direction = { x: 0, y: 0 }
52
+ export const LowerRight: Direction = { x: 2, y: 2 }
53
+ export const LowerLeft: Direction = { x: 0, y: 2 }
54
+ export const Middle: Direction = { x: 1, y: 1 }
55
+
56
+ /** All named directions for iteration. */
57
+ export const ALL_DIRECTIONS: readonly Direction[] = [
58
+ Up, Down, Left, Right, UpperRight, UpperLeft, LowerRight, LowerLeft, Middle,
59
+ ]
60
+
61
+ /**
62
+ * 2D text canvas — column-major (canvas[x][y]).
63
+ * Each cell holds a single character (or space).
64
+ */
65
+ export type Canvas = string[][]
66
+
67
+ /** A node in the ASCII graph, positioned on the grid. */
68
+ export interface AsciiNode {
69
+ /** Unique identity key — the original node ID from the parser (e.g. "A", "B"). */
70
+ name: string
71
+ /** Human-readable label for rendering inside the box (e.g. "Web Server"). */
72
+ displayLabel: string
73
+ /** Node shape from the parser (e.g. "rectangle", "diamond", "circle"). */
74
+ shape: AsciiNodeShape
75
+ index: number
76
+ gridCoord: GridCoord | null
77
+ drawingCoord: DrawingCoord | null
78
+ drawing: Canvas | null
79
+ drawn: boolean
80
+ styleClassName: string
81
+ styleClass: AsciiStyleClass
82
+ }
83
+
84
+ /** Style class for colored node text (ported from Go's classDef). */
85
+ export interface AsciiStyleClass {
86
+ name: string
87
+ styles: Record<string, string>
88
+ }
89
+
90
+ /** Edge line style for ASCII rendering. */
91
+ export type AsciiEdgeStyle = 'solid' | 'dotted' | 'thick'
92
+
93
+ /** An edge in the ASCII graph, with a routed path. */
94
+ export interface AsciiEdge {
95
+ from: AsciiNode
96
+ to: AsciiNode
97
+ text: string
98
+ path: GridCoord[]
99
+ labelLine: GridCoord[]
100
+ startDir: Direction
101
+ endDir: Direction
102
+ /** Line style: solid (default), dotted (-.->) or thick (==>) */
103
+ style: AsciiEdgeStyle
104
+ /** Whether to render an arrowhead at the start (source end) of the edge */
105
+ hasArrowStart: boolean
106
+ /** Whether to render an arrowhead at the end (target end) of the edge */
107
+ hasArrowEnd: boolean
108
+ /** Bundle this edge belongs to (if any). Set during bundling analysis. */
109
+ bundle?: EdgeBundle
110
+ /**
111
+ * For bundled edges: path from source/target to the junction point.
112
+ * The full visual path is: pathToJunction + bundle.sharedPath (for fan-in)
113
+ * or bundle.sharedPath + pathToJunction (for fan-out).
114
+ */
115
+ pathToJunction?: GridCoord[]
116
+ }
117
+
118
+ /** A subgraph container with bounding box for rendering. */
119
+ export interface AsciiSubgraph {
120
+ name: string
121
+ nodes: AsciiNode[]
122
+ parent: AsciiSubgraph | null
123
+ children: AsciiSubgraph[]
124
+ minX: number
125
+ minY: number
126
+ maxX: number
127
+ maxY: number
128
+ /** Optional direction override for layout within this subgraph (LR or TD). */
129
+ direction?: 'LR' | 'TD'
130
+ }
131
+
132
+ /** Configuration for ASCII rendering. */
133
+ export interface AsciiConfig {
134
+ /** true = ASCII chars (+,-,|), false = Unicode box-drawing (┌,─,│). Default: false */
135
+ useAscii: boolean
136
+ /** Horizontal spacing between nodes. Default: 5 */
137
+ paddingX: number
138
+ /** Vertical spacing between nodes. Default: 5 */
139
+ paddingY: number
140
+ /** Padding inside node boxes. Default: 1 */
141
+ boxBorderPadding: number
142
+ /** Graph direction: "LR" or "TD". */
143
+ graphDirection: 'LR' | 'TD'
144
+ }
145
+
146
+ /** Full ASCII graph state used during layout and rendering. */
147
+ export interface AsciiGraph {
148
+ nodes: AsciiNode[]
149
+ edges: AsciiEdge[]
150
+ canvas: Canvas
151
+ /** Role canvas — tracks the role of each character for colored output. */
152
+ roleCanvas: RoleCanvas
153
+ /** Grid occupancy map — maps "x,y" keys to node references. */
154
+ grid: Map<string, AsciiNode>
155
+ columnWidth: Map<number, number>
156
+ rowHeight: Map<number, number>
157
+ subgraphs: AsciiSubgraph[]
158
+ config: AsciiConfig
159
+ /** Offset applied to all drawing coords to accommodate subgraph borders. */
160
+ offsetX: number
161
+ offsetY: number
162
+ /** Edge bundles for parallel link visualization. Set during bundling analysis. */
163
+ bundles: EdgeBundle[]
164
+ }
165
+
166
+ // ============================================================================
167
+ // Coordinate helpers
168
+ // ============================================================================
169
+
170
+ export function gridCoordEquals(a: GridCoord, b: GridCoord): boolean {
171
+ return a.x === b.x && a.y === b.y
172
+ }
173
+
174
+ export function drawingCoordEquals(a: DrawingCoord, b: DrawingCoord): boolean {
175
+ return a.x === b.x && a.y === b.y
176
+ }
177
+
178
+ /** Apply a direction offset to a grid coordinate (move into the 3x3 block). */
179
+ export function gridCoordDirection(c: GridCoord, dir: Direction): GridCoord {
180
+ return { x: c.x + dir.x, y: c.y + dir.y }
181
+ }
182
+
183
+ /** Key for storing GridCoord in a Map. */
184
+ export function gridKey(c: GridCoord): string {
185
+ return `${c.x},${c.y}`
186
+ }
187
+
188
+ /** Default empty style class. */
189
+ export const EMPTY_STYLE: AsciiStyleClass = { name: '', styles: {} }
190
+
191
+ // ============================================================================
192
+ // Character role types for colored output
193
+ // ============================================================================
194
+
195
+ /**
196
+ * Role of a character in the ASCII diagram, used for theming.
197
+ * Each role maps to a different color when colors are enabled.
198
+ */
199
+ export type CharRole =
200
+ | 'text' // Node labels, edge labels
201
+ | 'border' // Node box borders, subgraph borders
202
+ | 'line' // Edge lines (paths between nodes)
203
+ | 'arrow' // Arrowheads (▲▼◄► or ^v<>)
204
+ | 'corner' // Corner characters at path bends
205
+ | 'junction' // Junction characters (┬┴├┤ where edges meet boxes)
206
+
207
+ /**
208
+ * Role canvas — parallel to Canvas, tracks the role of each character.
209
+ * Same column-major structure: roleCanvas[x][y] gives the role at (x, y).
210
+ * null means the character has no role (whitespace).
211
+ */
212
+ export type RoleCanvas = (CharRole | null)[][]
213
+
214
+ /**
215
+ * Theme colors for ASCII output — hex color strings.
216
+ * Derived from the SVG theme system for visual consistency.
217
+ */
218
+ export interface AsciiTheme {
219
+ /** Text color (node labels, edge labels) */
220
+ fg: string
221
+ /** Box border color (node borders, subgraph borders) */
222
+ border: string
223
+ /** Edge line color (paths between nodes) */
224
+ line: string
225
+ /** Arrowhead color (▲▼◄► or ^v<>) */
226
+ arrow: string
227
+ /** Theme accent color (optional, used by xycharts for series 0) */
228
+ accent?: string
229
+ /** Background color (optional, used by xycharts for dark-mode-aware shading) */
230
+ bg?: string
231
+ /** Corner character color (optional, defaults to line) */
232
+ corner?: string
233
+ /** Junction character color (optional, defaults to border) */
234
+ junction?: string
235
+ }
236
+
237
+ /** Color mode for output. */
238
+ export type ColorMode =
239
+ | 'none' // No colors (plain text)
240
+ | 'ansi16' // 16-color ANSI (basic terminals)
241
+ | 'ansi256' // 256-color ANSI (xterm)
242
+ | 'truecolor' // 24-bit RGB (modern terminals)
243
+ | 'html' // HTML <span> tags with inline color styles (browsers)
244
+
245
+ // ============================================================================
246
+ // Edge bundling types
247
+ // ============================================================================
248
+
249
+ /**
250
+ * Edge bundle — groups edges that share a common source or target.
251
+ * Used to visually merge parallel links before they reach the shared node.
252
+ *
253
+ * For fan-in (A & B --> C): multiple sources converge to one target.
254
+ * For fan-out (A --> B & C): one source diverges to multiple targets.
255
+ */
256
+ export interface EdgeBundle {
257
+ /** Bundle type: fan-in = many→one, fan-out = one→many */
258
+ type: 'fan-in' | 'fan-out'
259
+ /** Edges in this bundle */
260
+ edges: AsciiEdge[]
261
+ /** The common node (target for fan-in, source for fan-out) */
262
+ sharedNode: AsciiNode
263
+ /** The non-shared nodes (sources for fan-in, targets for fan-out) */
264
+ otherNodes: AsciiNode[]
265
+ /** Junction point where edges merge/split — set during routing */
266
+ junctionPoint: GridCoord | null
267
+ /** Path from junction to shared node (drawn once for all edges) */
268
+ sharedPath: GridCoord[]
269
+ /** Direction when entering/exiting the junction */
270
+ junctionDir: Direction
271
+ /** Direction when entering/exiting the shared node */
272
+ sharedNodeDir: Direction
273
+ }