callman-core 1.28.1 → 1.29.0

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 (252) hide show
  1. package/dist/ai/index.d.ts +1 -1
  2. package/dist/ai/index.d.ts.map +1 -1
  3. package/dist/ai/index.js.map +1 -1
  4. package/dist/ai/outputSchemas.d.ts +3 -0
  5. package/dist/ai/outputSchemas.d.ts.map +1 -1
  6. package/dist/ai/outputSchemas.js +1 -0
  7. package/dist/ai/outputSchemas.js.map +1 -1
  8. package/dist/ai/parse/diagram.d.ts.map +1 -1
  9. package/dist/ai/parse/diagram.js +6 -3
  10. package/dist/ai/parse/diagram.js.map +1 -1
  11. package/dist/ai/prompts/diagram.d.ts +7 -3
  12. package/dist/ai/prompts/diagram.d.ts.map +1 -1
  13. package/dist/ai/prompts/diagram.js +44 -5
  14. package/dist/ai/prompts/diagram.js.map +1 -1
  15. package/dist/ai/types.d.ts +6 -0
  16. package/dist/ai/types.d.ts.map +1 -1
  17. package/dist/diagram/bpmn/derive.d.ts +74 -0
  18. package/dist/diagram/bpmn/derive.d.ts.map +1 -0
  19. package/dist/diagram/bpmn/derive.js +392 -0
  20. package/dist/diagram/bpmn/derive.js.map +1 -0
  21. package/dist/diagram/bpmn/index.d.ts +7 -0
  22. package/dist/diagram/bpmn/index.d.ts.map +1 -0
  23. package/dist/diagram/bpmn/index.js +12 -0
  24. package/dist/diagram/bpmn/index.js.map +1 -0
  25. package/dist/diagram/bpmn/patch.d.ts +14 -0
  26. package/dist/diagram/bpmn/patch.d.ts.map +1 -0
  27. package/dist/diagram/bpmn/patch.js +770 -0
  28. package/dist/diagram/bpmn/patch.js.map +1 -0
  29. package/dist/diagram/bpmn/place.d.ts +4 -0
  30. package/dist/diagram/bpmn/place.d.ts.map +1 -0
  31. package/dist/diagram/bpmn/place.js +65 -0
  32. package/dist/diagram/bpmn/place.js.map +1 -0
  33. package/dist/diagram/bpmn/serialize.d.ts +12 -0
  34. package/dist/diagram/bpmn/serialize.d.ts.map +1 -0
  35. package/dist/diagram/bpmn/serialize.js +292 -0
  36. package/dist/diagram/bpmn/serialize.js.map +1 -0
  37. package/dist/diagram/bpmn/types.d.ts +42 -0
  38. package/dist/diagram/bpmn/types.d.ts.map +1 -0
  39. package/dist/diagram/bpmn/types.js +84 -0
  40. package/dist/diagram/bpmn/types.js.map +1 -0
  41. package/dist/diagram/bpmn/xml.d.ts +60 -0
  42. package/dist/diagram/bpmn/xml.d.ts.map +1 -0
  43. package/dist/diagram/bpmn/xml.js +342 -0
  44. package/dist/diagram/bpmn/xml.js.map +1 -0
  45. package/dist/diagram/elkGraph.d.ts +1 -3
  46. package/dist/diagram/elkGraph.d.ts.map +1 -1
  47. package/dist/diagram/elkGraph.js +1 -0
  48. package/dist/diagram/elkGraph.js.map +1 -1
  49. package/dist/diagram/fixtures.d.ts +10 -0
  50. package/dist/diagram/fixtures.d.ts.map +1 -0
  51. package/dist/diagram/fixtures.js +113 -0
  52. package/dist/diagram/fixtures.js.map +1 -0
  53. package/dist/diagram/geometry.d.ts +27 -0
  54. package/dist/diagram/geometry.d.ts.map +1 -0
  55. package/dist/diagram/geometry.js +122 -0
  56. package/dist/diagram/geometry.js.map +1 -0
  57. package/dist/diagram/history.d.ts +36 -0
  58. package/dist/diagram/history.d.ts.map +1 -0
  59. package/dist/diagram/history.js +54 -0
  60. package/dist/diagram/history.js.map +1 -0
  61. package/dist/diagram/ids.d.ts +5 -0
  62. package/dist/diagram/ids.d.ts.map +1 -0
  63. package/dist/diagram/ids.js +52 -0
  64. package/dist/diagram/ids.js.map +1 -0
  65. package/dist/diagram/index.d.ts +14 -2
  66. package/dist/diagram/index.d.ts.map +1 -1
  67. package/dist/diagram/index.js +20 -8
  68. package/dist/diagram/index.js.map +1 -1
  69. package/dist/diagram/layout.d.ts +107 -0
  70. package/dist/diagram/layout.d.ts.map +1 -0
  71. package/dist/diagram/layout.js +356 -0
  72. package/dist/diagram/layout.js.map +1 -0
  73. package/dist/diagram/mermaid/derive.d.ts +55 -0
  74. package/dist/diagram/mermaid/derive.d.ts.map +1 -0
  75. package/dist/diagram/mermaid/derive.js +343 -0
  76. package/dist/diagram/mermaid/derive.js.map +1 -0
  77. package/dist/diagram/mermaid/index.d.ts +8 -0
  78. package/dist/diagram/mermaid/index.d.ts.map +1 -0
  79. package/dist/diagram/mermaid/index.js +24 -0
  80. package/dist/diagram/mermaid/index.js.map +1 -0
  81. package/dist/diagram/mermaid/parse.d.ts +96 -0
  82. package/dist/diagram/mermaid/parse.d.ts.map +1 -0
  83. package/dist/diagram/mermaid/parse.js +672 -0
  84. package/dist/diagram/mermaid/parse.js.map +1 -0
  85. package/dist/diagram/mermaid/patch.d.ts +16 -0
  86. package/dist/diagram/mermaid/patch.d.ts.map +1 -0
  87. package/dist/diagram/mermaid/patch.js +463 -0
  88. package/dist/diagram/mermaid/patch.js.map +1 -0
  89. package/dist/diagram/mermaid/serialize.d.ts +14 -0
  90. package/dist/diagram/mermaid/serialize.d.ts.map +1 -0
  91. package/dist/diagram/mermaid/serialize.js +125 -0
  92. package/dist/diagram/mermaid/serialize.js.map +1 -0
  93. package/dist/diagram/mermaid/shapes.d.ts +11 -0
  94. package/dist/diagram/mermaid/shapes.d.ts.map +1 -0
  95. package/dist/diagram/mermaid/shapes.js +320 -0
  96. package/dist/diagram/mermaid/shapes.js.map +1 -0
  97. package/dist/diagram/migrations.d.ts +1 -0
  98. package/dist/diagram/migrations.d.ts.map +1 -1
  99. package/dist/diagram/migrations.js +7 -2
  100. package/dist/diagram/migrations.js.map +1 -1
  101. package/dist/diagram/normalize.d.ts +9 -1
  102. package/dist/diagram/normalize.d.ts.map +1 -1
  103. package/dist/diagram/normalize.js +194 -0
  104. package/dist/diagram/normalize.js.map +1 -1
  105. package/dist/diagram/ops.d.ts +146 -0
  106. package/dist/diagram/ops.d.ts.map +1 -0
  107. package/dist/diagram/ops.js +823 -0
  108. package/dist/diagram/ops.js.map +1 -0
  109. package/dist/diagram/opsReference.d.ts +3 -0
  110. package/dist/diagram/opsReference.d.ts.map +1 -0
  111. package/dist/diagram/opsReference.js +51 -0
  112. package/dist/diagram/opsReference.js.map +1 -0
  113. package/dist/diagram/patchInput.d.ts +193 -0
  114. package/dist/diagram/patchInput.d.ts.map +1 -0
  115. package/dist/diagram/patchInput.js +621 -0
  116. package/dist/diagram/patchInput.js.map +1 -0
  117. package/dist/diagram/routing/batch.d.ts +45 -0
  118. package/dist/diagram/routing/batch.d.ts.map +1 -0
  119. package/dist/diagram/routing/batch.js +136 -0
  120. package/dist/diagram/routing/batch.js.map +1 -0
  121. package/dist/diagram/routing/curves.d.ts +7 -0
  122. package/dist/diagram/routing/curves.d.ts.map +1 -0
  123. package/dist/diagram/routing/curves.js +120 -0
  124. package/dist/diagram/routing/curves.js.map +1 -0
  125. package/dist/diagram/routing/grid.d.ts +15 -0
  126. package/dist/diagram/routing/grid.d.ts.map +1 -0
  127. package/dist/diagram/routing/grid.js +245 -0
  128. package/dist/diagram/routing/grid.js.map +1 -0
  129. package/dist/diagram/routing/index.d.ts +10 -0
  130. package/dist/diagram/routing/index.d.ts.map +1 -0
  131. package/dist/diagram/routing/index.js +10 -0
  132. package/dist/diagram/routing/index.js.map +1 -0
  133. package/dist/diagram/routing/legacy.d.ts +6 -0
  134. package/dist/diagram/routing/legacy.d.ts.map +1 -0
  135. package/dist/diagram/routing/legacy.js +27 -0
  136. package/dist/diagram/routing/legacy.js.map +1 -0
  137. package/dist/diagram/routing/nudge.d.ts +4 -0
  138. package/dist/diagram/routing/nudge.d.ts.map +1 -0
  139. package/dist/diagram/routing/nudge.js +96 -0
  140. package/dist/diagram/routing/nudge.js.map +1 -0
  141. package/dist/diagram/routing/orthogonal.d.ts +6 -0
  142. package/dist/diagram/routing/orthogonal.d.ts.map +1 -0
  143. package/dist/diagram/routing/orthogonal.js +56 -0
  144. package/dist/diagram/routing/orthogonal.js.map +1 -0
  145. package/dist/diagram/routing/path.d.ts +16 -0
  146. package/dist/diagram/routing/path.d.ts.map +1 -0
  147. package/dist/diagram/routing/path.js +128 -0
  148. package/dist/diagram/routing/path.js.map +1 -0
  149. package/dist/diagram/routing/sides.d.ts +22 -0
  150. package/dist/diagram/routing/sides.d.ts.map +1 -0
  151. package/dist/diagram/routing/sides.js +74 -0
  152. package/dist/diagram/routing/sides.js.map +1 -0
  153. package/dist/diagram/routing/types.d.ts +46 -0
  154. package/dist/diagram/routing/types.d.ts.map +1 -0
  155. package/dist/diagram/routing/types.js +11 -0
  156. package/dist/diagram/routing/types.js.map +1 -0
  157. package/dist/diagram/schema.d.ts +70 -37
  158. package/dist/diagram/schema.d.ts.map +1 -1
  159. package/dist/diagram/schema.js +62 -8
  160. package/dist/diagram/schema.js.map +1 -1
  161. package/dist/diagram/styleInput.d.ts +41 -0
  162. package/dist/diagram/styleInput.d.ts.map +1 -0
  163. package/dist/diagram/styleInput.js +105 -0
  164. package/dist/diagram/styleInput.js.map +1 -0
  165. package/dist/diagram/views.d.ts +9 -0
  166. package/dist/diagram/views.d.ts.map +1 -0
  167. package/dist/diagram/views.js +42 -0
  168. package/dist/diagram/views.js.map +1 -0
  169. package/dist-cjs/ai/index.js.map +1 -1
  170. package/dist-cjs/ai/outputSchemas.js +1 -0
  171. package/dist-cjs/ai/outputSchemas.js.map +1 -1
  172. package/dist-cjs/ai/parse/diagram.js +6 -3
  173. package/dist-cjs/ai/parse/diagram.js.map +1 -1
  174. package/dist-cjs/ai/prompts/diagram.js +44 -5
  175. package/dist-cjs/ai/prompts/diagram.js.map +1 -1
  176. package/dist-cjs/diagram/bpmn/derive.js +398 -0
  177. package/dist-cjs/diagram/bpmn/derive.js.map +1 -0
  178. package/dist-cjs/diagram/bpmn/index.js +31 -0
  179. package/dist-cjs/diagram/bpmn/index.js.map +1 -0
  180. package/dist-cjs/diagram/bpmn/patch.js +774 -0
  181. package/dist-cjs/diagram/bpmn/patch.js.map +1 -0
  182. package/dist-cjs/diagram/bpmn/place.js +69 -0
  183. package/dist-cjs/diagram/bpmn/place.js.map +1 -0
  184. package/dist-cjs/diagram/bpmn/serialize.js +299 -0
  185. package/dist-cjs/diagram/bpmn/serialize.js.map +1 -0
  186. package/dist-cjs/diagram/bpmn/types.js +99 -0
  187. package/dist-cjs/diagram/bpmn/types.js.map +1 -0
  188. package/dist-cjs/diagram/bpmn/xml.js +367 -0
  189. package/dist-cjs/diagram/bpmn/xml.js.map +1 -0
  190. package/dist-cjs/diagram/elkGraph.js +1 -0
  191. package/dist-cjs/diagram/elkGraph.js.map +1 -1
  192. package/dist-cjs/diagram/fixtures.js +117 -0
  193. package/dist-cjs/diagram/fixtures.js.map +1 -0
  194. package/dist-cjs/diagram/geometry.js +143 -0
  195. package/dist-cjs/diagram/geometry.js.map +1 -0
  196. package/dist-cjs/diagram/history.js +65 -0
  197. package/dist-cjs/diagram/history.js.map +1 -0
  198. package/dist-cjs/diagram/ids.js +58 -0
  199. package/dist-cjs/diagram/ids.js.map +1 -0
  200. package/dist-cjs/diagram/index.js +23 -8
  201. package/dist-cjs/diagram/index.js.map +1 -1
  202. package/dist-cjs/diagram/layout.js +364 -0
  203. package/dist-cjs/diagram/layout.js.map +1 -0
  204. package/dist-cjs/diagram/mermaid/derive.js +353 -0
  205. package/dist-cjs/diagram/mermaid/derive.js.map +1 -0
  206. package/dist-cjs/diagram/mermaid/index.js +44 -0
  207. package/dist-cjs/diagram/mermaid/index.js.map +1 -0
  208. package/dist-cjs/diagram/mermaid/parse.js +681 -0
  209. package/dist-cjs/diagram/mermaid/parse.js.map +1 -0
  210. package/dist-cjs/diagram/mermaid/patch.js +468 -0
  211. package/dist-cjs/diagram/mermaid/patch.js.map +1 -0
  212. package/dist-cjs/diagram/mermaid/serialize.js +133 -0
  213. package/dist-cjs/diagram/mermaid/serialize.js.map +1 -0
  214. package/dist-cjs/diagram/mermaid/shapes.js +327 -0
  215. package/dist-cjs/diagram/mermaid/shapes.js.map +1 -0
  216. package/dist-cjs/diagram/migrations.js +9 -3
  217. package/dist-cjs/diagram/migrations.js.map +1 -1
  218. package/dist-cjs/diagram/normalize.js +198 -1
  219. package/dist-cjs/diagram/normalize.js.map +1 -1
  220. package/dist-cjs/diagram/ops.js +829 -0
  221. package/dist-cjs/diagram/ops.js.map +1 -0
  222. package/dist-cjs/diagram/opsReference.js +55 -0
  223. package/dist-cjs/diagram/opsReference.js.map +1 -0
  224. package/dist-cjs/diagram/patchInput.js +636 -0
  225. package/dist-cjs/diagram/patchInput.js.map +1 -0
  226. package/dist-cjs/diagram/routing/batch.js +142 -0
  227. package/dist-cjs/diagram/routing/batch.js.map +1 -0
  228. package/dist-cjs/diagram/routing/curves.js +127 -0
  229. package/dist-cjs/diagram/routing/curves.js.map +1 -0
  230. package/dist-cjs/diagram/routing/grid.js +250 -0
  231. package/dist-cjs/diagram/routing/grid.js.map +1 -0
  232. package/dist-cjs/diagram/routing/index.js +29 -0
  233. package/dist-cjs/diagram/routing/index.js.map +1 -0
  234. package/dist-cjs/diagram/routing/legacy.js +32 -0
  235. package/dist-cjs/diagram/routing/legacy.js.map +1 -0
  236. package/dist-cjs/diagram/routing/nudge.js +100 -0
  237. package/dist-cjs/diagram/routing/nudge.js.map +1 -0
  238. package/dist-cjs/diagram/routing/orthogonal.js +62 -0
  239. package/dist-cjs/diagram/routing/orthogonal.js.map +1 -0
  240. package/dist-cjs/diagram/routing/path.js +137 -0
  241. package/dist-cjs/diagram/routing/path.js.map +1 -0
  242. package/dist-cjs/diagram/routing/sides.js +82 -0
  243. package/dist-cjs/diagram/routing/sides.js.map +1 -0
  244. package/dist-cjs/diagram/routing/types.js +15 -0
  245. package/dist-cjs/diagram/routing/types.js.map +1 -0
  246. package/dist-cjs/diagram/schema.js +64 -9
  247. package/dist-cjs/diagram/schema.js.map +1 -1
  248. package/dist-cjs/diagram/styleInput.js +113 -0
  249. package/dist-cjs/diagram/styleInput.js.map +1 -0
  250. package/dist-cjs/diagram/views.js +46 -0
  251. package/dist-cjs/diagram/views.js.map +1 -0
  252. package/package.json +1 -1
@@ -0,0 +1,672 @@
1
+ // A tolerant parser for the Mermaid FLOWCHART dialect that produces a
2
+ // concrete syntax tree WITH SOURCE RANGES. The ranges are the point: the
3
+ // patcher (patch.ts) edits exactly the statement an operation concerns and
4
+ // leaves every other byte — comments, formatting, constructs we do not model
5
+ // — untouched. Lines the parser does not understand become `unknown`
6
+ // statements that are preserved verbatim and reported as diagnostics.
7
+ //
8
+ // Covered: `flowchart|graph <DIR>`, frontmatter (`---` blocks), `%%`
9
+ // comments and `%%{init}%%` directives, node declarations with every
10
+ // classic bracket and `@{ shape, label, … }`, `:::class`, chains
11
+ // (`A --> B --> C`), fans (`A & B --> C`), all link forms (`-->`, `---`,
12
+ // `-.->`, `==>`, `<-->`, `--o`, `--x`, `~~~`, extra length, `|label|`,
13
+ // `-- label -->`), edge ids (`e1@-->`) and edge attributes (`e1@{ curve }`),
14
+ // nested `subgraph id [title] … end` with `direction`, and the statement
15
+ // kinds classDef/class/style/linkStyle/click, kept as opaque text.
16
+ import { BRACKET_SHAPE, canonicalMermaidShape } from "./shapes.js";
17
+ // --- helpers ------------------------------------------------------------------
18
+ const ID_START = /[A-Za-z0-9_]/;
19
+ const isIdChar = (text, i) => {
20
+ const ch = text[i];
21
+ if (ch === undefined)
22
+ return false;
23
+ if (/[A-Za-z0-9_.]/.test(ch))
24
+ return true;
25
+ // A dash belongs to the id unless it starts a link (`-->`, `--`, `-.`).
26
+ if (ch === "-") {
27
+ const next = text[i + 1];
28
+ return next !== undefined && next !== ">" && next !== "-" && next !== "." && next !== "=" && /[A-Za-z0-9_]/.test(next);
29
+ }
30
+ return false;
31
+ };
32
+ const RESERVED = new Set(["end", "subgraph", "graph", "flowchart", "style", "classDef", "class", "click", "linkStyle", "direction"]);
33
+ export const unescapeLabel = (raw) => raw
34
+ .replace(/<br\s*\/?>/gi, "\n")
35
+ .replace(/#quot;/g, '"')
36
+ .replace(/#(\d+);/g, (_m, code) => String.fromCharCode(Number(code)))
37
+ .replace(/&quot;/g, '"')
38
+ .replace(/&amp;/g, "&")
39
+ .trim();
40
+ // Parses `key: value, key2: "v 2"` (the inside of `@{ … }`).
41
+ export const parseAttrs = (inner) => {
42
+ const out = {};
43
+ let i = 0;
44
+ while (i < inner.length) {
45
+ while (i < inner.length && /[\s,]/.test(inner[i]))
46
+ i += 1;
47
+ const keyStart = i;
48
+ while (i < inner.length && /[A-Za-z0-9_-]/.test(inner[i]))
49
+ i += 1;
50
+ const key = inner.slice(keyStart, i).trim();
51
+ if (!key)
52
+ break;
53
+ while (i < inner.length && /\s/.test(inner[i]))
54
+ i += 1;
55
+ if (inner[i] !== ":") {
56
+ out[key] = "true";
57
+ continue;
58
+ }
59
+ i += 1;
60
+ while (i < inner.length && /\s/.test(inner[i]))
61
+ i += 1;
62
+ let value = "";
63
+ if (inner[i] === '"' || inner[i] === "'") {
64
+ const quote = inner[i];
65
+ i += 1;
66
+ const start = i;
67
+ while (i < inner.length && inner[i] !== quote)
68
+ i += 1;
69
+ value = inner.slice(start, i);
70
+ i += 1;
71
+ }
72
+ else {
73
+ const start = i;
74
+ while (i < inner.length && inner[i] !== ",")
75
+ i += 1;
76
+ value = inner.slice(start, i).trim();
77
+ }
78
+ out[key] = value;
79
+ }
80
+ return out;
81
+ };
82
+ const splitLines = (text) => {
83
+ const lines = [];
84
+ let offset = 0;
85
+ let line = 1;
86
+ for (const part of text.split("\n")) {
87
+ const body = part.endsWith("\r") ? part.slice(0, -1) : part;
88
+ lines.push({ text: body, start: offset, line });
89
+ offset += part.length + 1;
90
+ line += 1;
91
+ }
92
+ return lines;
93
+ };
94
+ // --- the chain scanner ---------------------------------------------------------------
95
+ class Scanner {
96
+ text;
97
+ base; // offset of text[0] in the document
98
+ pos = 0;
99
+ constructor(text, base) {
100
+ this.text = text;
101
+ this.base = base;
102
+ }
103
+ get done() {
104
+ return this.pos >= this.text.length;
105
+ }
106
+ peek(offset = 0) {
107
+ return this.text[this.pos + offset] ?? "";
108
+ }
109
+ skipSpaces() {
110
+ while (!this.done && /\s/.test(this.peek()))
111
+ this.pos += 1;
112
+ }
113
+ startsWith(s) {
114
+ return this.text.startsWith(s, this.pos);
115
+ }
116
+ span(start, end) {
117
+ return { start: this.base + start, end: this.base + end };
118
+ }
119
+ }
120
+ const readBalanced = (scanner, open, close) => {
121
+ // scanner.pos is at `open`. Quotes protect the closer.
122
+ if (!scanner.startsWith(open))
123
+ return null;
124
+ let i = scanner.pos + open.length;
125
+ const innerStart = i;
126
+ let quote = null;
127
+ let depth = 0;
128
+ while (i < scanner.text.length) {
129
+ const ch = scanner.text[i];
130
+ if (quote) {
131
+ if (ch === quote)
132
+ quote = null;
133
+ i += 1;
134
+ continue;
135
+ }
136
+ if (ch === '"' || (ch === "'" && open === "@{")) {
137
+ quote = ch;
138
+ i += 1;
139
+ continue;
140
+ }
141
+ if (open === "@{") {
142
+ if (ch === "{")
143
+ depth += 1;
144
+ if (ch === "}") {
145
+ if (depth === 0)
146
+ return { inner: scanner.text.slice(innerStart, i), innerStart, end: i + 1 };
147
+ depth -= 1;
148
+ }
149
+ i += 1;
150
+ continue;
151
+ }
152
+ if (scanner.text.startsWith(close, i))
153
+ return { inner: scanner.text.slice(innerStart, i), innerStart, end: i + close.length };
154
+ i += 1;
155
+ }
156
+ return null;
157
+ };
158
+ // Bracket kinds ordered so longer openers win.
159
+ const BRACKETS = [
160
+ ["((()))", "(((", ")))"],
161
+ ["(())", "((", "))"],
162
+ ["([])", "([", "])"],
163
+ ["[[]]", "[[", "]]"],
164
+ ["[()]", "[(", ")]"],
165
+ ["[//]", "[/", "/]"],
166
+ ["[\\\\]", "[\\", "\\]"],
167
+ ["[/\\]", "[/", "\\]"],
168
+ ["[\\/]", "[\\", "/]"],
169
+ ["{{}}", "{{", "}}"],
170
+ ["{}", "{", "}"],
171
+ ["()", "(", ")"],
172
+ ["[]", "[", "]"],
173
+ [">]", ">", "]"],
174
+ ];
175
+ const readLabelText = (raw) => {
176
+ const trimmed = raw.trim();
177
+ if (trimmed.length >= 2 && trimmed.startsWith('"') && trimmed.endsWith('"')) {
178
+ return { label: unescapeLabel(trimmed.slice(1, -1)), quoted: true };
179
+ }
180
+ return { label: unescapeLabel(trimmed), quoted: false };
181
+ };
182
+ const readNodeRef = (scanner, diagnostics, line) => {
183
+ scanner.skipSpaces();
184
+ const start = scanner.pos;
185
+ if (!ID_START.test(scanner.peek()))
186
+ return null;
187
+ let i = scanner.pos;
188
+ while (isIdChar(scanner.text, i))
189
+ i += 1;
190
+ const id = scanner.text.slice(scanner.pos, i);
191
+ if (RESERVED.has(id))
192
+ return null;
193
+ const idSpan = scanner.span(scanner.pos, i);
194
+ scanner.pos = i;
195
+ const ref = { id, span: scanner.span(start, i), idSpan, classes: [] };
196
+ // `@{ … }` attributes.
197
+ if (scanner.startsWith("@{")) {
198
+ const block = readBalanced(scanner, "@{", "}");
199
+ if (!block) {
200
+ diagnostics.push({ line, column: scanner.pos - 0 + 1, message: "Unterminated @{ … } block", severity: "error" });
201
+ return null;
202
+ }
203
+ ref.attrs = parseAttrs(block.inner);
204
+ ref.attrsSpan = scanner.span(scanner.pos, block.end);
205
+ if (ref.attrs.label !== undefined) {
206
+ ref.label = unescapeLabel(ref.attrs.label);
207
+ const labelIndex = block.inner.indexOf(ref.attrs.label);
208
+ if (labelIndex >= 0)
209
+ ref.labelSpan = scanner.span(block.innerStart + labelIndex, block.innerStart + labelIndex + ref.attrs.label.length);
210
+ }
211
+ if (ref.attrs.shape !== undefined)
212
+ ref.shape = canonicalMermaidShape(ref.attrs.shape) ?? ref.attrs.shape;
213
+ scanner.pos = block.end;
214
+ }
215
+ else {
216
+ // Classic bracket label. Some openers belong to several kinds (`[/` opens
217
+ // `[/ … /]` and `[/ … \]`, `[\` opens `[\ … \]` and `[\ … /]`): the
218
+ // NEAREST closer decides, so a closer of a later node never wins.
219
+ let unterminated = null;
220
+ let best = null;
221
+ for (const [kind, open, close] of BRACKETS) {
222
+ if (!scanner.startsWith(open))
223
+ continue;
224
+ if (best && open.length < best.open.length)
225
+ break; // longer openers already matched
226
+ const block = readBalanced(scanner, open, close);
227
+ if (!block) {
228
+ unterminated ??= `Unterminated ${open} … ${close} label`;
229
+ continue;
230
+ }
231
+ if (!best || block.end < best.block.end)
232
+ best = { kind, open, block };
233
+ }
234
+ if (best) {
235
+ unterminated = null;
236
+ const { kind, block } = best;
237
+ const { label, quoted } = readLabelText(block.inner);
238
+ ref.label = label;
239
+ const trimmedStart = block.inner.indexOf(block.inner.trim());
240
+ const innerStart = block.innerStart + trimmedStart + (quoted ? 1 : 0);
241
+ ref.labelSpan = scanner.span(innerStart, innerStart + block.inner.trim().length - (quoted ? 2 : 0));
242
+ ref.bracket = kind;
243
+ ref.bracketSpan = scanner.span(scanner.pos, block.end);
244
+ ref.shape = BRACKET_SHAPE[kind];
245
+ scanner.pos = block.end;
246
+ }
247
+ if (unterminated) {
248
+ diagnostics.push({ line, column: scanner.pos + 1, message: unterminated, severity: "error" });
249
+ return null;
250
+ }
251
+ }
252
+ // `:::class1:::class2`
253
+ const classStart = scanner.pos;
254
+ while (scanner.startsWith(":::")) {
255
+ let j = scanner.pos + 3;
256
+ while (j < scanner.text.length && /[A-Za-z0-9_-]/.test(scanner.text[j]))
257
+ j += 1;
258
+ ref.classes.push(scanner.text.slice(scanner.pos + 3, j));
259
+ scanner.pos = j;
260
+ }
261
+ if (scanner.pos > classStart)
262
+ ref.classSpan = scanner.span(classStart, scanner.pos);
263
+ ref.span = scanner.span(start, scanner.pos);
264
+ return ref;
265
+ };
266
+ // `-.` alone (followed by a space) opens an inline-labelled dotted link.
267
+ const LINK_BODY = /^(?:-{2,}|={2,}|-\.+-|-\.(?=\s)|~{3,})/;
268
+ const readLink = (scanner) => {
269
+ scanner.skipSpaces();
270
+ const start = scanner.pos;
271
+ const token = { labelStyle: "none" };
272
+ // Optional `id@` prefix.
273
+ const idMatch = /^([A-Za-z0-9_-]+)@(?=[<ox]?(?:-|=|~))/.exec(scanner.text.slice(scanner.pos));
274
+ if (idMatch) {
275
+ token.id = idMatch[1];
276
+ token.idSpan = scanner.span(scanner.pos, scanner.pos + idMatch[1].length);
277
+ scanner.pos += idMatch[0].length;
278
+ }
279
+ // Optional start marker.
280
+ const startCh = scanner.peek();
281
+ if ((startCh === "<" || startCh === "o" || startCh === "x") && /[-=.]/.test(scanner.peek(1))) {
282
+ token.startMarker = startCh;
283
+ scanner.pos += 1;
284
+ }
285
+ const bodyMatch = LINK_BODY.exec(scanner.text.slice(scanner.pos));
286
+ if (!bodyMatch) {
287
+ scanner.pos = start;
288
+ return null;
289
+ }
290
+ let body = bodyMatch[0];
291
+ scanner.pos += body.length;
292
+ const strokeOf = (b) => (b.startsWith("~") ? "invisible" : b.startsWith("=") ? "thick" : b.includes(".") ? "dotted" : "normal");
293
+ token.stroke = strokeOf(body);
294
+ token.length = body.startsWith("~") ? 1 : Math.max(1, body.replace(/[^-=.]/g, "").length - 2);
295
+ // Inline label: `-- text -->`, `-. text .->`, `== text ==>`.
296
+ const inlineOpener = body === "--" || body === "==" || body === "-.";
297
+ if (inlineOpener && /\s/.test(scanner.peek())) {
298
+ const rest = scanner.text.slice(scanner.pos);
299
+ const closer = /\s(-{2,}|={2,}|\.-+|\.{1,}-)([>ox])?/.exec(rest);
300
+ if (closer && closer.index >= 0) {
301
+ const labelRaw = rest.slice(0, closer.index);
302
+ const labelStart = scanner.pos + (labelRaw.length - labelRaw.trimStart().length);
303
+ token.label = unescapeLabel(labelRaw);
304
+ token.labelSpan = scanner.span(labelStart, labelStart + labelRaw.trim().length);
305
+ token.labelStyle = "inline";
306
+ scanner.pos += closer.index + closer[0].length;
307
+ body = closer[1];
308
+ if (closer[2])
309
+ token.endMarker = closer[2];
310
+ token.stroke = body.includes("=") ? "thick" : body.includes(".") || token.stroke === "dotted" ? "dotted" : "normal";
311
+ }
312
+ }
313
+ if (token.endMarker === undefined) {
314
+ const endCh = scanner.peek();
315
+ if (endCh === ">" || endCh === "o" || endCh === "x") {
316
+ // `o`/`x` are markers only when followed by a space/pipe (else they start a node id).
317
+ const after = scanner.peek(1);
318
+ if (endCh === ">" || after === "" || /[\s|;]/.test(after)) {
319
+ token.endMarker = endCh;
320
+ scanner.pos += 1;
321
+ }
322
+ }
323
+ }
324
+ // Pipe label `|text|` (the span grows only when one is found).
325
+ const beforeSpaces = scanner.pos;
326
+ scanner.skipSpaces();
327
+ if (scanner.peek() === "|") {
328
+ const close = scanner.text.indexOf("|", scanner.pos + 1);
329
+ if (close > scanner.pos) {
330
+ const raw = scanner.text.slice(scanner.pos + 1, close);
331
+ token.label = readLabelText(raw).label;
332
+ token.labelSpan = scanner.span(scanner.pos + 1, close);
333
+ token.labelStyle = "pipe";
334
+ scanner.pos = close + 1;
335
+ }
336
+ }
337
+ else {
338
+ scanner.pos = beforeSpaces;
339
+ }
340
+ token.span = scanner.span(start, scanner.pos);
341
+ return token;
342
+ };
343
+ // Mermaid lets an edge take attributes on its own line (`e1@{ curve: linear }`)
344
+ // once it was declared with an id (`a e1@--> b`). Such a line reads as a lone
345
+ // node reference with attributes; when its id is an edge id anywhere in the
346
+ // text it is an edge-attribute statement instead. Runs after the whole text
347
+ // was read because the declaration may come later.
348
+ const reclassifyEdgeAttrs = (statements, text) => {
349
+ const edgeIds = new Set();
350
+ const collect = (list) => {
351
+ for (const statement of list) {
352
+ if (statement.kind === "chain") {
353
+ for (const link of statement.links)
354
+ if (link.id)
355
+ edgeIds.add(link.id);
356
+ }
357
+ else if (statement.kind === "subgraph") {
358
+ collect(statement.statements);
359
+ }
360
+ }
361
+ };
362
+ collect(statements);
363
+ if (edgeIds.size === 0)
364
+ return;
365
+ const rewrite = (list) => {
366
+ for (let index = 0; index < list.length; index += 1) {
367
+ const statement = list[index];
368
+ if (statement.kind === "subgraph") {
369
+ rewrite(statement.statements);
370
+ continue;
371
+ }
372
+ if (statement.kind !== "chain" || statement.links.length > 0 || statement.groups.length !== 1 || statement.groups[0].length !== 1)
373
+ continue;
374
+ const ref = statement.groups[0][0];
375
+ if (!ref.attrs || !edgeIds.has(ref.id) || ref.bracket || ref.classes.length > 0)
376
+ continue;
377
+ list[index] = {
378
+ kind: "edgeAttrs",
379
+ span: statement.span,
380
+ line: statement.line,
381
+ indent: statement.indent,
382
+ text: text.slice(statement.span.start, statement.span.end),
383
+ edgeId: ref.id,
384
+ attrs: ref.attrs,
385
+ };
386
+ }
387
+ };
388
+ rewrite(statements);
389
+ };
390
+ const parseChain = (raw, base, line, diagnostics) => {
391
+ const scanner = new Scanner(raw, base);
392
+ const groups = [];
393
+ const links = [];
394
+ let group = [];
395
+ const indent = raw.match(/^\s*/)?.[0] ?? "";
396
+ for (;;) {
397
+ const ref = readNodeRef(scanner, diagnostics, line);
398
+ if (!ref) {
399
+ if (group.length === 0)
400
+ return null;
401
+ break;
402
+ }
403
+ group.push(ref);
404
+ scanner.skipSpaces();
405
+ if (scanner.peek() === "&") {
406
+ scanner.pos += 1;
407
+ continue;
408
+ }
409
+ const link = readLink(scanner);
410
+ if (!link)
411
+ break;
412
+ groups.push(group);
413
+ group = [];
414
+ links.push(link);
415
+ }
416
+ if (group.length > 0)
417
+ groups.push(group);
418
+ scanner.skipSpaces();
419
+ if (!scanner.done) {
420
+ // Trailing junk we did not understand: this line is not ours to edit.
421
+ return null;
422
+ }
423
+ if (links.length !== groups.length - 1)
424
+ return null;
425
+ return { kind: "chain", span: { start: base + indent.length, end: base + raw.length }, line, indent, groups, links };
426
+ };
427
+ // --- top level ----------------------------------------------------------------------------
428
+ const HEADER = /^\s*(flowchart|graph)\b\s*([A-Za-z]{2})?\s*;?\s*$/;
429
+ export const parseMermaidFlowchart = (text) => {
430
+ const diagnostics = [];
431
+ const cst = { text, diagramType: null, direction: "TB", statements: [], diagnostics };
432
+ const lines = splitLines(text);
433
+ const stack = [{ statements: cst.statements }];
434
+ let index = 0;
435
+ // Frontmatter: `---` … `---` at the very top.
436
+ if (lines[0]?.text.trim() === "---") {
437
+ let close = 1;
438
+ while (close < lines.length && lines[close].text.trim() !== "---")
439
+ close += 1;
440
+ if (close < lines.length) {
441
+ const block = lines.slice(0, close + 1);
442
+ const raw = block.map((l) => l.text).join("\n");
443
+ const titleLine = block.find((l) => /^\s*title\s*:/.test(l.text));
444
+ const fm = { span: { start: block[0].start, end: block[close].start + block[close].text.length }, raw };
445
+ if (titleLine) {
446
+ const match = /^(\s*title\s*:\s*)(.*)$/.exec(titleLine.text);
447
+ if (match) {
448
+ fm.title = match[2].trim().replace(/^["']|["']$/g, "");
449
+ fm.titleSpan = { start: titleLine.start + match[1].length, end: titleLine.start + titleLine.text.length };
450
+ }
451
+ }
452
+ cst.frontmatter = fm;
453
+ cst.statements.push({ kind: "frontmatter", span: fm.span, line: 1, indent: "", text: raw });
454
+ index = close + 1;
455
+ }
456
+ }
457
+ const pushOther = (kind, rawLine) => {
458
+ const indent = rawLine.text.match(/^\s*/)?.[0] ?? "";
459
+ stack[stack.length - 1].statements.push({
460
+ kind,
461
+ span: { start: rawLine.start, end: rawLine.start + rawLine.text.length },
462
+ line: rawLine.line,
463
+ indent,
464
+ text: rawLine.text,
465
+ });
466
+ };
467
+ for (; index < lines.length; index += 1) {
468
+ const rawLine = lines[index];
469
+ const trimmed = rawLine.text.trim();
470
+ const current = stack[stack.length - 1];
471
+ if (trimmed === "") {
472
+ // Trailing empty line after the final newline is not a statement.
473
+ if (index === lines.length - 1 && rawLine.text === "")
474
+ break;
475
+ pushOther("blank", rawLine);
476
+ continue;
477
+ }
478
+ if (trimmed.startsWith("%%{")) {
479
+ pushOther("directive", rawLine);
480
+ continue;
481
+ }
482
+ if (trimmed.startsWith("%%")) {
483
+ pushOther("comment", rawLine);
484
+ continue;
485
+ }
486
+ if (cst.diagramType === null) {
487
+ const header = HEADER.exec(rawLine.text);
488
+ if (header) {
489
+ cst.diagramType = "flowchart";
490
+ cst.direction = (header[2] ?? "TB").toUpperCase();
491
+ cst.headerSpan = { start: rawLine.start, end: rawLine.start + rawLine.text.length };
492
+ pushOther("header", rawLine);
493
+ continue;
494
+ }
495
+ const otherType = /^\s*([A-Za-z]+(?:-beta)?)\b/.exec(rawLine.text);
496
+ if (otherType) {
497
+ cst.diagramType = otherType[1];
498
+ cst.headerSpan = { start: rawLine.start, end: rawLine.start + rawLine.text.length };
499
+ diagnostics.push({ line: rawLine.line, column: 1, message: `"${otherType[1]}" diagrams are previewed only — the canvas and diagram operations need a flowchart`, severity: "warning" });
500
+ pushOther("header", rawLine);
501
+ // Everything below belongs to a dialect we do not model.
502
+ for (index += 1; index < lines.length; index += 1) {
503
+ if (lines[index].text.trim() === "" && index === lines.length - 1)
504
+ break;
505
+ pushOther("unknown", lines[index]);
506
+ }
507
+ break;
508
+ }
509
+ }
510
+ const subgraph = /^(\s*)subgraph\s+(.*)$/.exec(rawLine.text);
511
+ if (subgraph) {
512
+ const indent = subgraph[1];
513
+ const rest = subgraph[2].trim();
514
+ let id = "";
515
+ let title;
516
+ let titleSpan;
517
+ let attrs;
518
+ const restStart = rawLine.start + rawLine.text.indexOf(rest, indent.length + "subgraph".length);
519
+ const attrMatch = /^([A-Za-z0-9_-]+)\s*@\{(.*)\}\s*$/.exec(rest);
520
+ const titled = /^([A-Za-z0-9_-]+)\s*\[(.*)\]\s*$/.exec(rest);
521
+ if (attrMatch) {
522
+ id = attrMatch[1];
523
+ attrs = parseAttrs(attrMatch[2]);
524
+ if (attrs.label !== undefined)
525
+ title = unescapeLabel(attrs.label);
526
+ }
527
+ else if (titled) {
528
+ id = titled[1];
529
+ const inner = readLabelText(titled[2]);
530
+ title = inner.label;
531
+ const bracketAt = rest.indexOf("[");
532
+ titleSpan = { start: restStart + bracketAt + 1 + (inner.quoted ? 1 : 0), end: restStart + rest.lastIndexOf("]") - (inner.quoted ? 1 : 0) };
533
+ }
534
+ else {
535
+ const quoted = /^"(.*)"$/.exec(rest);
536
+ if (quoted) {
537
+ title = unescapeLabel(quoted[1]);
538
+ id = title.replace(/[^A-Za-z0-9_]+/g, "_");
539
+ titleSpan = { start: restStart + 1, end: restStart + rest.length - 1 };
540
+ }
541
+ else {
542
+ id = rest;
543
+ title = rest.includes(" ") ? rest : undefined;
544
+ if (rest.includes(" ")) {
545
+ id = rest.replace(/[^A-Za-z0-9_]+/g, "_");
546
+ titleSpan = { start: restStart, end: restStart + rest.length };
547
+ }
548
+ }
549
+ }
550
+ const idSpanStart = rawLine.start + rawLine.text.indexOf(rest);
551
+ const statement = {
552
+ kind: "subgraph",
553
+ span: { start: rawLine.start, end: rawLine.start + rawLine.text.length },
554
+ line: rawLine.line,
555
+ indent,
556
+ id,
557
+ idSpan: { start: idSpanStart, end: idSpanStart + id.length },
558
+ title,
559
+ titleSpan,
560
+ headerSpan: { start: rawLine.start, end: rawLine.start + rawLine.text.length },
561
+ endSpan: { start: rawLine.start + rawLine.text.length, end: rawLine.start + rawLine.text.length },
562
+ attrs,
563
+ statements: [],
564
+ };
565
+ current.statements.push(statement);
566
+ stack.push({ statements: statement.statements, subgraph: statement });
567
+ continue;
568
+ }
569
+ if (/^\s*end\s*;?\s*$/.test(rawLine.text)) {
570
+ if (stack.length > 1) {
571
+ const frame = stack.pop();
572
+ const sg = frame.subgraph;
573
+ sg.endSpan = { start: rawLine.start, end: rawLine.start + rawLine.text.length };
574
+ sg.span = { start: sg.span.start, end: sg.endSpan.end };
575
+ }
576
+ else {
577
+ diagnostics.push({ line: rawLine.line, column: 1, message: "`end` without a matching `subgraph`", severity: "error" });
578
+ pushOther("unknown", rawLine);
579
+ }
580
+ continue;
581
+ }
582
+ const directionMatch = /^\s*direction\s+([A-Za-z]{2})\s*$/.exec(rawLine.text);
583
+ if (directionMatch) {
584
+ if (current.subgraph)
585
+ current.subgraph.direction = directionMatch[1].toUpperCase();
586
+ pushOther("direction", rawLine);
587
+ continue;
588
+ }
589
+ const keyword = /^\s*(classDef|class|style|linkStyle|click)\b/.exec(rawLine.text);
590
+ if (keyword) {
591
+ pushOther(keyword[1], rawLine);
592
+ continue;
593
+ }
594
+ // Statements separated by `;` on one line — only split outside quotes/brackets.
595
+ const pieces = splitStatements(rawLine.text);
596
+ let anyChain = false;
597
+ for (const piece of pieces) {
598
+ const chain = parseChain(piece.text, rawLine.start + piece.offset, rawLine.line, diagnostics);
599
+ if (chain) {
600
+ current.statements.push(chain);
601
+ anyChain = true;
602
+ }
603
+ else if (piece.text.trim() !== "") {
604
+ diagnostics.push({ line: rawLine.line, column: piece.offset + 1, message: `Not understood: "${piece.text.trim().slice(0, 60)}" (kept as is)`, severity: "warning" });
605
+ current.statements.push({
606
+ kind: "unknown",
607
+ span: { start: rawLine.start + piece.offset, end: rawLine.start + piece.offset + piece.text.length },
608
+ line: rawLine.line,
609
+ indent: piece.text.match(/^\s*/)?.[0] ?? "",
610
+ text: piece.text,
611
+ });
612
+ }
613
+ }
614
+ if (!anyChain && pieces.length === 0)
615
+ pushOther("unknown", rawLine);
616
+ }
617
+ while (stack.length > 1) {
618
+ const frame = stack.pop();
619
+ diagnostics.push({ line: frame.subgraph?.line ?? 1, column: 1, message: `subgraph "${frame.subgraph?.id}" is missing its \`end\``, severity: "error" });
620
+ }
621
+ reclassifyEdgeAttrs(cst.statements, cst.text);
622
+ if (cst.diagramType === null) {
623
+ diagnostics.push({ line: 1, column: 1, message: "Missing `flowchart <direction>` header", severity: "error" });
624
+ }
625
+ return cst;
626
+ };
627
+ const splitStatements = (line) => {
628
+ const out = [];
629
+ let depth = 0;
630
+ let quote = null;
631
+ let start = 0;
632
+ for (let i = 0; i < line.length; i += 1) {
633
+ const ch = line[i];
634
+ if (quote) {
635
+ if (ch === quote)
636
+ quote = null;
637
+ continue;
638
+ }
639
+ if (ch === '"') {
640
+ quote = ch;
641
+ continue;
642
+ }
643
+ if ("[({".includes(ch))
644
+ depth += 1;
645
+ else if ("])}".includes(ch))
646
+ depth = Math.max(0, depth - 1);
647
+ else if (ch === ";" && depth === 0) {
648
+ out.push({ text: line.slice(start, i), offset: start });
649
+ start = i + 1;
650
+ }
651
+ }
652
+ out.push({ text: line.slice(start), offset: start });
653
+ return out;
654
+ };
655
+ // Walk every statement (depth first) — subgraph bodies included.
656
+ export const walkStatements = (statements, visit, parent = null) => {
657
+ for (const statement of statements) {
658
+ visit(statement, parent);
659
+ if (statement.kind === "subgraph")
660
+ walkStatements(statement.statements, visit, statement);
661
+ }
662
+ };
663
+ export const isMermaidFlowchart = (text) => {
664
+ const withoutFrontmatter = text.replace(/^---[\s\S]*?\n---\s*\n/, "");
665
+ const firstMeaningful = withoutFrontmatter.split("\n").map((l) => l.trim()).find((l) => l !== "" && !l.startsWith("%%"));
666
+ return Boolean(firstMeaningful && /^(flowchart|graph)\b/.test(firstMeaningful));
667
+ };
668
+ export const looksLikeMermaid = (text) => {
669
+ const first = text.replace(/^---[\s\S]*?\n---\s*\n/, "").split("\n").map((l) => l.trim()).find((l) => l !== "" && !l.startsWith("%%"));
670
+ return Boolean(first && /^(flowchart|graph|sequenceDiagram|classDiagram|stateDiagram(-v2)?|erDiagram|gantt|pie|mindmap|timeline|journey|gitGraph|quadrantChart|requirementDiagram|C4Context|sankey-beta|xychart-beta|block-beta|packet-beta|kanban|architecture-beta|radar-beta|treemap-beta|agentflow-beta)\b/.test(first));
671
+ };
672
+ //# sourceMappingURL=parse.js.map