@oh-my-pi/pi-utils 16.0.6 → 16.0.8

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,207 @@
1
+ import type { SequenceDiagram, Actor, Message, Block, Note } from './types'
2
+ import { normalizeBrTags } from '../multiline-utils'
3
+
4
+ // ============================================================================
5
+ // Sequence diagram parser
6
+ //
7
+ // Parses Mermaid sequenceDiagram syntax into a SequenceDiagram structure.
8
+ //
9
+ // Supported syntax:
10
+ // participant A as Alice
11
+ // actor B as Bob
12
+ // A->>B: Solid arrow
13
+ // A-->>B: Dashed arrow
14
+ // A-)B: Open arrow
15
+ // A--)B: Dashed open arrow
16
+ // A->>+B: Activate target
17
+ // A-->>-B: Deactivate source
18
+ // loop Label ... end
19
+ // alt Label ... else Label ... end
20
+ // opt Label ... end
21
+ // par Label ... and Label ... end
22
+ // Note left of A: Text
23
+ // Note right of A: Text
24
+ // Note over A,B: Text
25
+ // ============================================================================
26
+
27
+ /**
28
+ * Parse a Mermaid sequence diagram.
29
+ * Expects the first line to be "sequenceDiagram".
30
+ */
31
+ export function parseSequenceDiagram(lines: string[]): SequenceDiagram {
32
+ const diagram: SequenceDiagram = {
33
+ actors: [],
34
+ messages: [],
35
+ blocks: [],
36
+ notes: [],
37
+ }
38
+
39
+ // Track actor IDs to auto-create actors referenced in messages
40
+ const actorIds = new Set<string>()
41
+ // Track block nesting with a stack
42
+ const blockStack: Array<{ type: Block['type']; label: string; startIndex: number; dividers: Block['dividers'] }> = []
43
+
44
+ for (let i = 1; i < lines.length; i++) {
45
+ const line = lines[i]!
46
+
47
+ // --- Participant / Actor declaration ---
48
+ // "participant A as Alice" or "participant Alice"
49
+ // "actor B as Bob" or "actor Bob"
50
+ const actorMatch = line.match(/^(participant|actor)\s+(\S+?)(?:\s+as\s+(.+))?$/)
51
+ if (actorMatch) {
52
+ const type = actorMatch[1] as 'participant' | 'actor'
53
+ const id = actorMatch[2]!
54
+ const rawLabel = actorMatch[3]?.trim() ?? id
55
+ const label = normalizeBrTags(rawLabel)
56
+ if (!actorIds.has(id)) {
57
+ actorIds.add(id)
58
+ diagram.actors.push({ id, label, type })
59
+ }
60
+ continue
61
+ }
62
+
63
+ // --- Note ---
64
+ // "Note left of A: text" / "Note right of A: text" / "Note over A,B: text"
65
+ const noteMatch = line.match(/^Note\s+(left of|right of|over)\s+([^:]+):\s*(.+)$/i)
66
+ if (noteMatch) {
67
+ const posStr = noteMatch[1]!.toLowerCase()
68
+ const actorsStr = noteMatch[2]!.trim()
69
+ const text = normalizeBrTags(noteMatch[3]!.trim())
70
+ const noteActorIds = actorsStr.split(',').map(s => s.trim())
71
+
72
+ // Ensure actors exist
73
+ for (const aid of noteActorIds) {
74
+ ensureActor(diagram, actorIds, aid)
75
+ }
76
+
77
+ let position: 'left' | 'right' | 'over' = 'over'
78
+ if (posStr === 'left of') position = 'left'
79
+ else if (posStr === 'right of') position = 'right'
80
+
81
+ diagram.notes.push({
82
+ actorIds: noteActorIds,
83
+ text,
84
+ position,
85
+ afterIndex: diagram.messages.length - 1,
86
+ })
87
+ continue
88
+ }
89
+
90
+ // --- Block start: loop, alt, opt, par, critical, break, rect ---
91
+ const blockMatch = line.match(/^(loop|alt|opt|par|critical|break|rect)\s*(.*)$/)
92
+ if (blockMatch) {
93
+ const blockType = blockMatch[1] as Block['type']
94
+ const rawBlockLabel = blockMatch[2]?.trim() ?? ''
95
+ const label = normalizeBrTags(rawBlockLabel)
96
+ blockStack.push({
97
+ type: blockType,
98
+ label,
99
+ startIndex: diagram.messages.length,
100
+ dividers: [],
101
+ })
102
+ continue
103
+ }
104
+
105
+ // --- Block divider: else, and ---
106
+ const dividerMatch = line.match(/^(else|and)\s*(.*)$/)
107
+ if (dividerMatch && blockStack.length > 0) {
108
+ const rawDividerLabel = dividerMatch[2]?.trim() ?? ''
109
+ const label = normalizeBrTags(rawDividerLabel)
110
+ blockStack[blockStack.length - 1]!.dividers.push({
111
+ index: diagram.messages.length,
112
+ label,
113
+ })
114
+ continue
115
+ }
116
+
117
+ // --- Block end ---
118
+ if (line === 'end' && blockStack.length > 0) {
119
+ const completed = blockStack.pop()!
120
+ diagram.blocks.push({
121
+ type: completed.type,
122
+ label: completed.label,
123
+ startIndex: completed.startIndex,
124
+ endIndex: Math.max(diagram.messages.length - 1, completed.startIndex),
125
+ dividers: completed.dividers,
126
+ })
127
+ continue
128
+ }
129
+
130
+ // --- Message ---
131
+ // Patterns: A->>B, A-->>B, A-)B, A--)B, with optional +/- activation
132
+ // Format: FROM ARROW TO: LABEL
133
+ const msgMatch = line.match(
134
+ /^(\S+?)\s*(--?>?>|--?[)x]|--?>>|--?>)\s*([+-]?)(\S+?)\s*:\s*(.+)$/
135
+ )
136
+ if (msgMatch) {
137
+ const from = msgMatch[1]!
138
+ const arrow = msgMatch[2]!
139
+ const activationMark = msgMatch[3]
140
+ const to = msgMatch[4]!
141
+ const label = normalizeBrTags(msgMatch[5]!.trim())
142
+
143
+ // Ensure both actors exist
144
+ ensureActor(diagram, actorIds, from)
145
+ ensureActor(diagram, actorIds, to)
146
+
147
+ // Determine line style and arrow head from the arrow operator
148
+ const lineStyle = arrow.startsWith('--') ? 'dashed' : 'solid'
149
+ // ">>" = filled arrow, ")" or ">" alone = open arrow, "x" = cross (treat as filled)
150
+ const arrowHead = arrow.includes('>>') || arrow.includes('x') ? 'filled' : 'open'
151
+
152
+ const msg: Message = {
153
+ from,
154
+ to,
155
+ label,
156
+ lineStyle,
157
+ arrowHead,
158
+ }
159
+
160
+ // Activation/deactivation via +/- prefix on target
161
+ if (activationMark === '+') msg.activate = true
162
+ if (activationMark === '-') msg.deactivate = true
163
+
164
+ diagram.messages.push(msg)
165
+ continue
166
+ }
167
+
168
+ // --- Simplified message format: A->>B: Label (fallback with more relaxed regex) ---
169
+ const simpleMsgMatch = line.match(
170
+ /^(\S+?)\s*(->>|-->>|-\)|--\)|-x|--x|->|-->)\s*([+-]?)(\S+?)\s*:\s*(.+)$/
171
+ )
172
+ if (simpleMsgMatch) {
173
+ const from = simpleMsgMatch[1]!
174
+ const arrow = simpleMsgMatch[2]!
175
+ const activationMark = simpleMsgMatch[3]
176
+ const to = simpleMsgMatch[4]!
177
+ const label = normalizeBrTags(simpleMsgMatch[5]!.trim())
178
+
179
+ ensureActor(diagram, actorIds, from)
180
+ ensureActor(diagram, actorIds, to)
181
+
182
+ const lineStyle = arrow.startsWith('--') ? 'dashed' : 'solid'
183
+ const arrowHead = arrow.includes('>>') || arrow.includes('x') ? 'filled' : 'open'
184
+
185
+ const msg: Message = { from, to, label, lineStyle, arrowHead }
186
+ if (activationMark === '+') msg.activate = true
187
+ if (activationMark === '-') msg.deactivate = true
188
+
189
+ diagram.messages.push(msg)
190
+ continue
191
+ }
192
+
193
+ // --- activate / deactivate explicit commands ---
194
+ // These are handled implicitly via +/- on messages but can also appear standalone
195
+ // For now, we skip explicit activate/deactivate lines (they affect rendering only)
196
+ }
197
+
198
+ return diagram
199
+ }
200
+
201
+ /** Ensure an actor exists, creating a default participant if not */
202
+ function ensureActor(diagram: SequenceDiagram, actorIds: Set<string>, id: string): void {
203
+ if (!actorIds.has(id)) {
204
+ actorIds.add(id)
205
+ diagram.actors.push({ id, label: id, type: 'participant' })
206
+ }
207
+ }
@@ -0,0 +1,146 @@
1
+ // ============================================================================
2
+ // Sequence diagram types
3
+ //
4
+ // Models the parsed and positioned representations of a Mermaid sequence diagram.
5
+ // Sequence diagrams show actor interactions over time (vertical timeline).
6
+ // ============================================================================
7
+
8
+ /** Parsed sequence diagram — logical structure from mermaid text */
9
+ export interface SequenceDiagram {
10
+ /** Ordered list of actors/participants */
11
+ actors: Actor[]
12
+ /** Messages between actors in chronological order */
13
+ messages: Message[]
14
+ /** Structural blocks (loop, alt, opt, par, critical) */
15
+ blocks: Block[]
16
+ /** Notes attached to actors */
17
+ notes: Note[]
18
+ }
19
+
20
+ export interface Actor {
21
+ id: string
22
+ label: string
23
+ /** 'participant' renders as a box, 'actor' renders as a stick figure */
24
+ type: 'participant' | 'actor'
25
+ }
26
+
27
+ export interface Message {
28
+ from: string
29
+ to: string
30
+ label: string
31
+ /** Arrow style: solid line or dashed line */
32
+ lineStyle: 'solid' | 'dashed'
33
+ /** Arrow head: filled (closed) or open */
34
+ arrowHead: 'filled' | 'open'
35
+ /** Activate the target lifeline (+) */
36
+ activate?: boolean
37
+ /** Deactivate the source lifeline (-) */
38
+ deactivate?: boolean
39
+ }
40
+
41
+ export interface Block {
42
+ /** Block type keyword */
43
+ type: 'loop' | 'alt' | 'opt' | 'par' | 'critical' | 'break' | 'rect'
44
+ /** Label for the block header */
45
+ label: string
46
+ /** Index of the first message inside this block */
47
+ startIndex: number
48
+ /** Index of the last message inside this block (inclusive) */
49
+ endIndex: number
50
+ /** For alt/par blocks: indices where "else"/"and" dividers appear (message indices) */
51
+ dividers: Array<{ index: number; label: string }>
52
+ }
53
+
54
+ export interface Note {
55
+ /** Which actor(s) the note is attached to */
56
+ actorIds: string[]
57
+ /** Note text content */
58
+ text: string
59
+ /** Position relative to the actor(s) */
60
+ position: 'left' | 'right' | 'over'
61
+ /** Message index after which this note appears */
62
+ afterIndex: number
63
+ }
64
+
65
+ // ============================================================================
66
+ // Positioned sequence diagram — ready for SVG rendering
67
+ // ============================================================================
68
+
69
+ export interface PositionedSequenceDiagram {
70
+ width: number
71
+ height: number
72
+ actors: PositionedActor[]
73
+ lifelines: Lifeline[]
74
+ messages: PositionedMessage[]
75
+ activations: Activation[]
76
+ blocks: PositionedBlock[]
77
+ notes: PositionedNote[]
78
+ }
79
+
80
+ export interface PositionedActor {
81
+ id: string
82
+ label: string
83
+ type: 'participant' | 'actor'
84
+ /** Center x of the actor box */
85
+ x: number
86
+ /** Top y of the actor box */
87
+ y: number
88
+ width: number
89
+ height: number
90
+ }
91
+
92
+ /** Vertical dashed line from actor to bottom of diagram */
93
+ export interface Lifeline {
94
+ actorId: string
95
+ x: number
96
+ topY: number
97
+ bottomY: number
98
+ }
99
+
100
+ export interface PositionedMessage {
101
+ from: string
102
+ to: string
103
+ label: string
104
+ lineStyle: 'solid' | 'dashed'
105
+ arrowHead: 'filled' | 'open'
106
+ /** Start point (from actor's lifeline) */
107
+ x1: number
108
+ /** End point (to actor's lifeline) */
109
+ x2: number
110
+ /** Vertical position */
111
+ y: number
112
+ /** Whether this is a self-message (same actor) */
113
+ isSelf: boolean
114
+ }
115
+
116
+ /** Narrow rectangle on a lifeline showing active processing */
117
+ export interface Activation {
118
+ actorId: string
119
+ x: number
120
+ topY: number
121
+ bottomY: number
122
+ width: number
123
+ }
124
+
125
+ export interface PositionedBlock {
126
+ type: Block['type']
127
+ label: string
128
+ x: number
129
+ y: number
130
+ width: number
131
+ height: number
132
+ /** Divider lines within the block (for alt/par) */
133
+ dividers: Array<{ y: number; label: string }>
134
+ }
135
+
136
+ export interface PositionedNote {
137
+ text: string
138
+ x: number
139
+ y: number
140
+ width: number
141
+ height: number
142
+ /** Actor IDs this note is attached to (for SVG attribution) */
143
+ actors?: string[]
144
+ /** Note position relative to actors (for SVG attribution) */
145
+ position?: 'left' | 'right' | 'over'
146
+ }
@@ -0,0 +1,71 @@
1
+ // ============================================================================
2
+ // Terminal display width — used by the ASCII renderer
3
+ //
4
+ // Terminals render most characters in 1 column, but East Asian wide
5
+ // characters (CJK ideographs, Hangul, kana, fullwidth forms) and
6
+ // emoji-presentation glyphs occupy 2 columns. The ASCII canvas is a
7
+ // cell-per-column grid, so its width math counts display columns rather
8
+ // than code units.
9
+ //
10
+ // Width is delegated to Bun.stringWidth (wcwidth-style, locale-insensitive):
11
+ // East Asian Wide/Fullwidth and emoji-presentation graphemes (incl. ZWJ
12
+ // sequences and regional-indicator flags) measure 2; box-drawing, the
13
+ // renderer's narrow arrowheads (◀ ▶ ►), and East Asian Ambiguous glyphs
14
+ // measure 1. This matches the renderer's structural glyph set exactly.
15
+ //
16
+ // Text is measured in grapheme clusters (Intl.Segmenter): a cluster that
17
+ // renders 2 columns is stored as its full string in one canvas cell
18
+ // followed by WIDE_PAD in the continuation cell it covers. The ASCII
19
+ // serializers drop WIDE_PAD cells when joining a row, so the emitted line
20
+ // occupies exactly as many terminal columns as the canvas has cells.
21
+ // ============================================================================
22
+
23
+ /**
24
+ * Placeholder occupying the second cell of a fullwidth glyph on the ASCII
25
+ * canvas. U+0000 cannot appear in parsed Mermaid labels, is treated as
26
+ * occupied label content by canvas merging, and is stripped at
27
+ * serialization time. Invariant: a WIDE_PAD cell always sits immediately
28
+ * right of its lead cell; canvas writes keep the pair atomic.
29
+ */
30
+ export const WIDE_PAD = '\u0000'
31
+
32
+ const graphemeSegmenter = new Intl.Segmenter()
33
+
34
+ /**
35
+ * Display width of a string in terminal columns, summed over grapheme
36
+ * clusters so it always equals `toCells(text).length`. ASCII-only strings
37
+ * take a fast path.
38
+ */
39
+ export function displayWidth(text: string): number {
40
+ let ascii = true
41
+ for (let i = 0; i < text.length; i++) {
42
+ if (text.charCodeAt(i) > 0x7e) {
43
+ ascii = false
44
+ break
45
+ }
46
+ }
47
+ if (ascii) return text.length
48
+
49
+ let width = 0
50
+ for (const seg of graphemeSegmenter.segment(text)) {
51
+ width += Bun.stringWidth(seg.segment) >= 2 ? 2 : 1
52
+ }
53
+ return width
54
+ }
55
+
56
+ /**
57
+ * Expand a string into ASCII-canvas cells: each 2-column grapheme cluster
58
+ * is stored whole in one cell and followed by WIDE_PAD, so that
59
+ * `cells.length === displayWidth(text)`. Per-character placement loops can
60
+ * iterate the result with plain cell offsets.
61
+ */
62
+ export function toCells(text: string): string[] {
63
+ const cells: string[] = []
64
+ for (const seg of graphemeSegmenter.segment(text)) {
65
+ cells.push(seg.segment)
66
+ if (Bun.stringWidth(seg.segment) >= 2) {
67
+ cells.push(WIDE_PAD)
68
+ }
69
+ }
70
+ return cells
71
+ }
@@ -0,0 +1,164 @@
1
+ // ============================================================================
2
+ // Parsed graph — logical structure extracted from Mermaid text
3
+ // ============================================================================
4
+
5
+ export interface MermaidGraph {
6
+ direction: Direction
7
+ nodes: Map<string, MermaidNode>
8
+ edges: MermaidEdge[]
9
+ subgraphs: MermaidSubgraph[]
10
+ classDefs: Map<string, Record<string, string>>
11
+ /** Maps node IDs to their class names (from `class X className` or `:::className` shorthand) */
12
+ classAssignments: Map<string, string>
13
+ /** Maps node IDs to inline styles (from `style X fill:#f00,stroke:#333`) */
14
+ nodeStyles: Map<string, Record<string, string>>
15
+ /** Maps edge indices (or 'default') to inline styles from `linkStyle` directives */
16
+ linkStyles: Map<number | 'default', Record<string, string>>
17
+ }
18
+
19
+ export type Direction = 'TD' | 'TB' | 'LR' | 'BT' | 'RL'
20
+
21
+ export interface MermaidNode {
22
+ id: string
23
+ label: string
24
+ shape: NodeShape
25
+ }
26
+
27
+ export type NodeShape =
28
+ | 'rectangle'
29
+ | 'rounded'
30
+ | 'diamond'
31
+ | 'stadium'
32
+ | 'circle'
33
+ // Batch 1 additions
34
+ | 'subroutine' // [[text]] — double-bordered rectangle
35
+ | 'doublecircle' // (((text))) — concentric circles
36
+ | 'hexagon' // {{text}} — six-sided polygon
37
+ // Batch 2 additions
38
+ | 'cylinder' // [(text)] — database cylinder
39
+ | 'asymmetric' // >text] — flag/banner shape
40
+ | 'trapezoid' // [/text\] — wider bottom
41
+ | 'trapezoid-alt' // [\text/] — wider top
42
+ // Batch 3 state diagram pseudostates
43
+ | 'state-start' // filled circle (start pseudostate)
44
+ | 'state-end' // bullseye circle (end pseudostate)
45
+
46
+ export interface MermaidEdge {
47
+ source: string
48
+ target: string
49
+ label?: string
50
+ style: EdgeStyle
51
+ /** Whether to render an arrowhead at the start (source end) of the edge */
52
+ hasArrowStart: boolean
53
+ /** Whether to render an arrowhead at the end (target end) of the edge */
54
+ hasArrowEnd: boolean
55
+ }
56
+
57
+ export type EdgeStyle = 'solid' | 'dotted' | 'thick'
58
+
59
+ export interface MermaidSubgraph {
60
+ id: string
61
+ label: string
62
+ nodeIds: string[]
63
+ children: MermaidSubgraph[]
64
+ /** Optional direction override for this subgraph's internal layout */
65
+ direction?: Direction
66
+ }
67
+
68
+ // ============================================================================
69
+ // Positioned graph — after ELK layout, ready for SVG rendering
70
+ // ============================================================================
71
+
72
+ export interface PositionedGraph {
73
+ width: number
74
+ height: number
75
+ nodes: PositionedNode[]
76
+ edges: PositionedEdge[]
77
+ groups: PositionedGroup[]
78
+ }
79
+
80
+ export interface PositionedNode {
81
+ id: string
82
+ label: string
83
+ shape: NodeShape
84
+ x: number
85
+ y: number
86
+ width: number
87
+ height: number
88
+ /** Inline styles resolved from classDef + explicit `style` statements — override theme defaults */
89
+ inlineStyle?: Record<string, string>
90
+ }
91
+
92
+ export interface PositionedEdge {
93
+ source: string
94
+ target: string
95
+ label?: string
96
+ style: EdgeStyle
97
+ hasArrowStart: boolean
98
+ hasArrowEnd: boolean
99
+ /** Full path including bends — array of {x, y} points */
100
+ points: Point[]
101
+ /** Layout-computed label center position (avoids label-label collisions) */
102
+ labelPosition?: Point
103
+ /** Inline styles resolved from `linkStyle` directives — override theme defaults */
104
+ inlineStyle?: Record<string, string>
105
+ }
106
+
107
+ export interface Point {
108
+ x: number
109
+ y: number
110
+ }
111
+
112
+ export interface PositionedGroup {
113
+ id: string
114
+ label: string
115
+ x: number
116
+ y: number
117
+ width: number
118
+ height: number
119
+ children: PositionedGroup[]
120
+ }
121
+
122
+ // ============================================================================
123
+ // Render options — user-facing configuration
124
+ //
125
+ // Color theming uses CSS custom properties: --bg and --fg are required,
126
+ // optional enrichment variables (--line, --accent, --muted, --surface,
127
+ // --border) add richer color from Shiki themes or custom palettes.
128
+ // See src/theme.ts for the full variable system.
129
+ // ============================================================================
130
+
131
+ export interface RenderOptions {
132
+ /** Background color → CSS variable --bg. Default: '#FFFFFF' */
133
+ bg?: string
134
+ /** Foreground / primary text color → CSS variable --fg. Default: '#27272A' */
135
+ fg?: string
136
+
137
+ // -- Optional enrichment colors (fall back to color-mix from bg/fg) --
138
+
139
+ /** Edge/connector color → CSS variable --line */
140
+ line?: string
141
+ /** Arrow heads, highlights → CSS variable --accent */
142
+ accent?: string
143
+ /** Secondary text, edge labels → CSS variable --muted */
144
+ muted?: string
145
+ /** Node/box fill tint → CSS variable --surface */
146
+ surface?: string
147
+ /** Node/group stroke color → CSS variable --border */
148
+ border?: string
149
+
150
+ /** Font family for all text. Default: 'Inter' */
151
+ font?: string
152
+ /** Canvas padding in px. Default: 40 */
153
+ padding?: number
154
+ /** Horizontal spacing between sibling nodes. Default: 24 */
155
+ nodeSpacing?: number
156
+ /** Vertical spacing between layers. Default: 40 */
157
+ layerSpacing?: number
158
+ /** Spacing between disconnected components. Default: nodeSpacing (24) */
159
+ componentSpacing?: number
160
+ /** Render with transparent background (no background style on SVG). Default: false */
161
+ transparent?: boolean
162
+ /** Enable hover tooltips on chart data points (xychart only). Default: false */
163
+ interactive?: boolean
164
+ }