agentic-mermaid 0.1.1 → 0.2.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 (340) hide show
  1. package/AGENT_NATIVE.md +155 -91
  2. package/CHANGELOG.md +163 -4
  3. package/Instructions_for_agents.md +10 -10
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/README.md +31 -38
  6. package/THIRD_PARTY_NOTICES.md +29 -0
  7. package/assets/hero.png +0 -0
  8. package/dist/agent-core.d.ts +463 -0
  9. package/dist/agent-core.js +397 -0
  10. package/dist/agent.d.ts +73 -1868
  11. package/dist/agent.js +336 -37325
  12. package/dist/agentic-mermaid-mcp.js +7 -38399
  13. package/dist/am.js +33626 -40446
  14. package/dist/chunk-2P5FKUQC.js +3485 -0
  15. package/dist/chunk-2S3VSS6I.js +149 -0
  16. package/dist/chunk-2SILJDW2.js +37 -0
  17. package/dist/chunk-BJYMBT4I.js +61963 -0
  18. package/dist/chunk-FDM43XU7.js +615 -0
  19. package/dist/chunk-MYPDVZCO.js +4702 -0
  20. package/dist/index-DWPa2MJs.d.ts +6780 -0
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +310 -33014
  23. package/dist/mcp-cli-5J47WEY3.js +17 -0
  24. package/docs/README.md +39 -31
  25. package/docs/agent-api-cookbook.md +44 -35
  26. package/docs/agent-mutation-policy.md +1 -1
  27. package/docs/agent-workflow-examples.md +2 -2
  28. package/docs/api.md +284 -77
  29. package/docs/ascii.md +26 -16
  30. package/docs/code-mode-impact.md +214 -0
  31. package/docs/comparison.md +23 -11
  32. package/docs/computational-aesthetics-prototype-plan.md +252 -0
  33. package/docs/config.md +26 -10
  34. package/docs/custom-fonts.md +265 -0
  35. package/docs/custom-style-cookbook.md +130 -9
  36. package/docs/diagram-families.md +48 -23
  37. package/docs/features.md +59 -52
  38. package/docs/fork-differences.md +6 -3
  39. package/docs/getting-started.md +23 -16
  40. package/docs/issue-derived-test-cases.md +29 -22
  41. package/docs/mcp-code-mode-rationale.md +13 -6
  42. package/docs/mcp-http-transport.md +20 -13
  43. package/docs/mermaid-layout-complaints.md +66 -52
  44. package/docs/mutation-testing.md +145 -66
  45. package/docs/quality.md +23 -14
  46. package/docs/react.md +2 -2
  47. package/docs/schemas/style-spec.schema.json +1221 -30
  48. package/docs/style-authoring.md +241 -38
  49. package/docs/svg-semantic-contract.md +49 -0
  50. package/docs/testing-strategy.md +166 -58
  51. package/docs/theming.md +19 -32
  52. package/examples/styles/catalog.json +54 -0
  53. package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
  54. package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
  55. package/examples/styles/cupertino-prototype.mmd +10 -0
  56. package/examples/styles/cupertino-prototype.style.json +63 -0
  57. package/examples/styles/mid-century-report.style.json +2 -2
  58. package/examples/styles/star-chart-atlas.style.json +2 -1
  59. package/examples/styles/transit-route-map.style.json +2 -2
  60. package/examples/styles/vercel-inspired-prototype.mmd +6 -0
  61. package/examples/styles/vercel-inspired-prototype.style.json +87 -0
  62. package/package.json +85 -45
  63. package/server.json +2 -2
  64. package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
  65. package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
  66. package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
  67. package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
  68. package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
  69. package/TODO.md +0 -684
  70. package/dist/index-wjRjbsP-.d.ts +0 -1102
  71. package/docs/assets/improvements/box-start-connector.png +0 -0
  72. package/docs/assets/improvements/comment-dropped-lint.png +0 -0
  73. package/docs/assets/improvements/er-content-loss.png +0 -0
  74. package/docs/assets/improvements/fan-in-trunks.png +0 -0
  75. package/docs/assets/improvements/gantt-family.png +0 -0
  76. package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
  77. package/docs/assets/improvements/gantt-verify-seam.png +0 -0
  78. package/docs/assets/improvements/subgraph-container-edges.png +0 -0
  79. package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
  80. package/docs/assets/improvements/wrapper-init-directive.png +0 -0
  81. package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
  82. package/docs/assets/style-cookbook/mid-century-report.png +0 -0
  83. package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
  84. package/docs/assets/style-cookbook/transit-route-map.png +0 -0
  85. package/docs/contributing/adding-diagram-types.md +0 -144
  86. package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
  87. package/docs/contributing/diagram-family-citizenship.md +0 -82
  88. package/docs/contributing/harvesting-upstream-tests.md +0 -126
  89. package/docs/contributing/lessons-learned.md +0 -276
  90. package/docs/contributing/releasing.md +0 -82
  91. package/docs/contributing/visual-review-evidence.md +0 -53
  92. package/docs/design/families/README.md +0 -26
  93. package/docs/design/families/architecture-beta.md +0 -48
  94. package/docs/design/families/flowchart-parser-conformance.md +0 -22
  95. package/docs/design/families/gantt-research.md +0 -137
  96. package/docs/design/families/gantt.md +0 -329
  97. package/docs/design/families/journey-agentic-cards-before.png +0 -0
  98. package/docs/design/families/journey-experience-curve-implementation.png +0 -0
  99. package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
  100. package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
  101. package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
  102. package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
  103. package/docs/design/families/journey-migration-parity.md +0 -439
  104. package/docs/design/families/journey-section-overlap-after.png +0 -0
  105. package/docs/design/families/journey-section-overlap-before.png +0 -0
  106. package/docs/design/families/journey-usage-research.md +0 -423
  107. package/docs/design/families/journey.md +0 -153
  108. package/docs/design/families/xychart.md +0 -85
  109. package/docs/design/system/README.md +0 -45
  110. package/docs/design/system/abstraction-audit.md +0 -224
  111. package/docs/design/system/abstraction-recommendations.md +0 -655
  112. package/docs/design/system/architecture.mmd +0 -29
  113. package/docs/design/system/architecture.svg +0 -137
  114. package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
  115. package/docs/design/system/layout-rubric.md +0 -138
  116. package/docs/design/system/route-contracts.md +0 -848
  117. package/docs/design/system/source-preservation-ladder.md +0 -52
  118. package/docs/design/system/styles-rollout.md +0 -216
  119. package/docs/design/system/ugly-layouts.md +0 -119
  120. package/docs/layout-characterization/README.md +0 -245
  121. package/docs/layout-characterization/contact-sheet-families.md +0 -498
  122. package/docs/layout-characterization/contact-sheet.md +0 -638
  123. package/docs/layout-characterization/properties.md +0 -281
  124. package/docs/layout-characterization/visual-quality.md +0 -160
  125. package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
  126. package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
  127. package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
  128. package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
  129. package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
  130. package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
  131. package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
  132. package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
  133. package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
  134. package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
  135. package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
  136. package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
  137. package/docs/project/divergences.md +0 -98
  138. package/docs/project/dogfooding-docs-strategy.md +0 -66
  139. package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
  140. package/docs/project/lessons-learned.md +0 -1019
  141. package/docs/project/remove-role-styling-plan.md +0 -176
  142. package/examples/agent-improve-auth-flow.ts +0 -206
  143. package/examples/agent-loop.ts +0 -53
  144. package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
  145. package/llms.txt +0 -104
  146. package/skill-evals/README.md +0 -77
  147. package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
  148. package/skill-evals/fixtures/class-payment/input.mmd +0 -4
  149. package/skill-evals/fixtures/er-orders/input.mmd +0 -10
  150. package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
  151. package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
  152. package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
  153. package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
  154. package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
  155. package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
  156. package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
  157. package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
  158. package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
  159. package/skill-evals/shared-benchmark.json +0 -510
  160. package/src/agent/analyze.ts +0 -157
  161. package/src/agent/apply.ts +0 -146
  162. package/src/agent/architecture-body.ts +0 -458
  163. package/src/agent/certificates.ts +0 -110
  164. package/src/agent/class-body.ts +0 -319
  165. package/src/agent/core.ts +0 -207
  166. package/src/agent/create.ts +0 -106
  167. package/src/agent/describe.ts +0 -368
  168. package/src/agent/er-body.ts +0 -224
  169. package/src/agent/facts.ts +0 -360
  170. package/src/agent/families-builtin.ts +0 -920
  171. package/src/agent/families.ts +0 -232
  172. package/src/agent/family-layouts.ts +0 -832
  173. package/src/agent/flowchart-body.ts +0 -372
  174. package/src/agent/flowchart-unsupported.ts +0 -243
  175. package/src/agent/font-coverage.ts +0 -288
  176. package/src/agent/gantt-body.ts +0 -495
  177. package/src/agent/index.ts +0 -10
  178. package/src/agent/journey-body.ts +0 -374
  179. package/src/agent/label-metrics.ts +0 -28
  180. package/src/agent/layout-to-rendered.ts +0 -63
  181. package/src/agent/mutate.ts +0 -97
  182. package/src/agent/mutation-ops.ts +0 -51
  183. package/src/agent/op-schema.ts +0 -443
  184. package/src/agent/parse.ts +0 -223
  185. package/src/agent/pie-body.ts +0 -270
  186. package/src/agent/png.ts +0 -171
  187. package/src/agent/quadrant-body.ts +0 -336
  188. package/src/agent/quality.ts +0 -363
  189. package/src/agent/readability-audit.ts +0 -92
  190. package/src/agent/rendered-route-audit.ts +0 -61
  191. package/src/agent/sequence-body.ts +0 -345
  192. package/src/agent/serialize.ts +0 -214
  193. package/src/agent/state-body.ts +0 -589
  194. package/src/agent/structural-count.ts +0 -150
  195. package/src/agent/timeline-body.ts +0 -280
  196. package/src/agent/trace-log.ts +0 -43
  197. package/src/agent/types.ts +0 -995
  198. package/src/agent/verify.ts +0 -603
  199. package/src/agent/xychart-body.ts +0 -472
  200. package/src/architecture/config.ts +0 -282
  201. package/src/architecture/layout.ts +0 -496
  202. package/src/architecture/parser.ts +0 -375
  203. package/src/architecture/renderer.ts +0 -611
  204. package/src/architecture/types.ts +0 -113
  205. package/src/ascii/ansi.ts +0 -423
  206. package/src/ascii/architecture.ts +0 -210
  207. package/src/ascii/canvas.ts +0 -443
  208. package/src/ascii/class-diagram.ts +0 -697
  209. package/src/ascii/converter.ts +0 -475
  210. package/src/ascii/draw.ts +0 -1693
  211. package/src/ascii/edge-bundling.ts +0 -328
  212. package/src/ascii/edge-routing.ts +0 -330
  213. package/src/ascii/er-diagram.ts +0 -439
  214. package/src/ascii/gantt.ts +0 -276
  215. package/src/ascii/grid.ts +0 -747
  216. package/src/ascii/index.ts +0 -196
  217. package/src/ascii/journey.ts +0 -169
  218. package/src/ascii/meta.ts +0 -244
  219. package/src/ascii/multiline-utils.ts +0 -80
  220. package/src/ascii/pathfinder.ts +0 -301
  221. package/src/ascii/pie.ts +0 -67
  222. package/src/ascii/quadrant.ts +0 -158
  223. package/src/ascii/reverse.ts +0 -216
  224. package/src/ascii/sequence.ts +0 -499
  225. package/src/ascii/shapes/circle.ts +0 -27
  226. package/src/ascii/shapes/corners.ts +0 -143
  227. package/src/ascii/shapes/diamond.ts +0 -27
  228. package/src/ascii/shapes/hexagon.ts +0 -27
  229. package/src/ascii/shapes/index.ts +0 -106
  230. package/src/ascii/shapes/rectangle.ts +0 -168
  231. package/src/ascii/shapes/rounded.ts +0 -27
  232. package/src/ascii/shapes/special.ts +0 -332
  233. package/src/ascii/shapes/stadium.ts +0 -110
  234. package/src/ascii/shapes/state.ts +0 -192
  235. package/src/ascii/shapes/types.ts +0 -73
  236. package/src/ascii/timeline.ts +0 -141
  237. package/src/ascii/types.ts +0 -312
  238. package/src/ascii/validate.ts +0 -120
  239. package/src/ascii/width.ts +0 -81
  240. package/src/ascii/wrap.ts +0 -131
  241. package/src/ascii/xychart.ts +0 -919
  242. package/src/browser.ts +0 -29
  243. package/src/class/layout.ts +0 -240
  244. package/src/class/parser.ts +0 -332
  245. package/src/class/renderer.ts +0 -638
  246. package/src/class/types.ts +0 -129
  247. package/src/cli/agent-instructions.ts +0 -1
  248. package/src/cli/am-bin.ts +0 -6
  249. package/src/cli/exit-codes.ts +0 -18
  250. package/src/cli/index.ts +0 -1132
  251. package/src/cli/init-agent.ts +0 -147
  252. package/src/cli/run-entrypoint.ts +0 -10
  253. package/src/color-resolver.ts +0 -152
  254. package/src/elk-instance.ts +0 -114
  255. package/src/er/layout.ts +0 -187
  256. package/src/er/parser.ts +0 -226
  257. package/src/er/renderer.ts +0 -792
  258. package/src/er/types.ts +0 -99
  259. package/src/family-rubric.ts +0 -425
  260. package/src/font-manifest.ts +0 -31
  261. package/src/gantt/layout.ts +0 -365
  262. package/src/gantt/parser.ts +0 -282
  263. package/src/gantt/pipeline.ts +0 -27
  264. package/src/gantt/renderer.ts +0 -414
  265. package/src/gantt/schedule.ts +0 -513
  266. package/src/gantt/types.ts +0 -255
  267. package/src/index.ts +0 -368
  268. package/src/journey/layout.ts +0 -554
  269. package/src/journey/parse-core.ts +0 -251
  270. package/src/journey/parser.ts +0 -101
  271. package/src/journey/renderer.ts +0 -917
  272. package/src/journey/types.ts +0 -144
  273. package/src/layout/geometry.ts +0 -182
  274. package/src/layout/pass.ts +0 -142
  275. package/src/layout/passes/index.ts +0 -3916
  276. package/src/layout-engine.ts +0 -1565
  277. package/src/layout-rubric.ts +0 -659
  278. package/src/mcp/artifacts.ts +0 -137
  279. package/src/mcp/dynamic-harness.ts +0 -31
  280. package/src/mcp/dynamic-modules.d.ts +0 -7
  281. package/src/mcp/facade.ts +0 -608
  282. package/src/mcp/harness-runtime.ts +0 -203
  283. package/src/mcp/hosted-server.ts +0 -468
  284. package/src/mcp/mcp-bin.ts +0 -3
  285. package/src/mcp/mcp-cli.ts +0 -82
  286. package/src/mcp/protocol.ts +0 -13
  287. package/src/mcp/sandbox.ts +0 -195
  288. package/src/mcp/sdk-decl.ts +0 -391
  289. package/src/mcp/server.ts +0 -362
  290. package/src/mcp/tool-surface.ts +0 -146
  291. package/src/mermaid-source.ts +0 -655
  292. package/src/multiline-utils.ts +0 -228
  293. package/src/parser.ts +0 -1031
  294. package/src/pie/layout.ts +0 -168
  295. package/src/pie/parser.ts +0 -136
  296. package/src/pie/renderer.ts +0 -234
  297. package/src/pie/types.ts +0 -82
  298. package/src/quadrant/layout.ts +0 -191
  299. package/src/quadrant/parser.ts +0 -183
  300. package/src/quadrant/renderer.ts +0 -322
  301. package/src/quadrant/types.ts +0 -113
  302. package/src/render-family-hooks.ts +0 -244
  303. package/src/renderer.ts +0 -1099
  304. package/src/route-contracts.ts +0 -2723
  305. package/src/scene/backend.ts +0 -76
  306. package/src/scene/fidelity.ts +0 -183
  307. package/src/scene/hybrid-backend.ts +0 -174
  308. package/src/scene/ir.ts +0 -181
  309. package/src/scene/marks.ts +0 -127
  310. package/src/scene/rough-backend.ts +0 -378
  311. package/src/scene/seed.ts +0 -46
  312. package/src/scene/style-registry.ts +0 -556
  313. package/src/sequence/layout.ts +0 -456
  314. package/src/sequence/parser.ts +0 -255
  315. package/src/sequence/renderer.ts +0 -604
  316. package/src/sequence/types.ts +0 -154
  317. package/src/shape-clipping.ts +0 -351
  318. package/src/shared/accessibility-directives.ts +0 -22
  319. package/src/shared/batched.ts +0 -61
  320. package/src/shared/color-math.ts +0 -93
  321. package/src/shared/svg-a11y.ts +0 -29
  322. package/src/shared/syntax-error.ts +0 -17
  323. package/src/shared/unicode-ranges.ts +0 -135
  324. package/src/styles.ts +0 -318
  325. package/src/svg-paths.ts +0 -28
  326. package/src/text-metrics.ts +0 -248
  327. package/src/theme.ts +0 -710
  328. package/src/timeline/layout.ts +0 -381
  329. package/src/timeline/parser.ts +0 -215
  330. package/src/timeline/renderer.ts +0 -538
  331. package/src/timeline/types.ts +0 -105
  332. package/src/types.ts +0 -462
  333. package/src/website-worker-modules.d.ts +0 -18
  334. package/src/xychart/axis-utils.ts +0 -109
  335. package/src/xychart/colors.ts +0 -124
  336. package/src/xychart/config.ts +0 -88
  337. package/src/xychart/layout.ts +0 -656
  338. package/src/xychart/parser.ts +0 -504
  339. package/src/xychart/renderer.ts +0 -640
  340. package/src/xychart/types.ts +0 -282
@@ -1,655 +0,0 @@
1
- // ============================================================================
2
- // Mermaid source normalization + config extraction
3
- //
4
- // Handles:
5
- // - UTF-8 BOM stripping
6
- // - leading YAML frontmatter
7
- // - Mermaid directives / init blocks (%%{init: ...}%%)
8
- // - Mermaid comment stripping for downstream line-based parsers
9
- // - normalized, trimmed diagram lines for downstream parsers
10
- // ============================================================================
11
-
12
- import YAML from 'yaml'
13
-
14
- export type MermaidConfigScalar = string | number | boolean | null
15
- export type MermaidConfigValue = MermaidConfigScalar | MermaidConfigValue[] | MermaidConfigMap
16
-
17
- export interface MermaidConfigMap {
18
- [key: string]: MermaidConfigValue | undefined
19
- }
20
-
21
- export type MermaidFrontmatterScalar = MermaidConfigScalar
22
- export type MermaidFrontmatterValue = MermaidConfigValue
23
- export type MermaidFrontmatterList = MermaidFrontmatterValue[]
24
-
25
- export interface MermaidFrontmatterMap extends MermaidConfigMap {}
26
-
27
- export interface MermaidThemeVariables extends MermaidConfigMap {
28
- fontFamily?: string
29
- }
30
-
31
- export interface TimelineRuntimeConfig extends MermaidConfigMap {
32
- disableMulticolor?: boolean
33
- sectionFills?: string[]
34
- sectionColours?: string[]
35
- }
36
-
37
- export interface JourneyRuntimeConfig extends MermaidConfigMap {
38
- diagramMarginX?: number
39
- diagramMarginY?: number
40
- leftMargin?: number
41
- maxLabelWidth?: number
42
- width?: number
43
- height?: number
44
- boxMargin?: number
45
- boxTextMargin?: number
46
- noteMargin?: number
47
- messageMargin?: number
48
- messageAlign?: 'left' | 'center' | 'right'
49
- bottomMarginAdj?: number
50
- useMaxWidth?: boolean
51
- rightAngles?: boolean
52
- taskFontSize?: string | number
53
- taskFontFamily?: string
54
- taskMargin?: number
55
- activationWidth?: number
56
- textPlacement?: string
57
- actorColours?: string[]
58
- sectionFills?: string[]
59
- sectionColours?: string[]
60
- titleColor?: string
61
- titleFontFamily?: string
62
- titleFontSize?: string | number
63
- }
64
-
65
- export interface GanttRuntimeConfig extends MermaidConfigMap {
66
- displayMode?: string
67
- }
68
-
69
- export interface MermaidRuntimeConfig extends MermaidConfigMap {
70
- theme?: string
71
- fontFamily?: string
72
- themeVariables?: MermaidThemeVariables
73
- timeline?: TimelineRuntimeConfig
74
- journey?: JourneyRuntimeConfig
75
- xyChart?: MermaidConfigMap
76
- gantt?: GanttRuntimeConfig
77
- useMaxWidth?: boolean
78
- useWidth?: number
79
- themeCSS?: string
80
- }
81
-
82
- export interface ProcessedMermaidSource {
83
- body: string
84
- lines: string[]
85
- frontmatter: MermaidFrontmatterMap
86
- }
87
-
88
- export interface NormalizedMermaidSource {
89
- text: string
90
- body: string
91
- lines: string[]
92
- firstLine: string
93
- config: MermaidRuntimeConfig
94
- frontmatter: MermaidFrontmatterMap
95
- }
96
-
97
- const FRONTMATTER_REGEX = /^\uFEFF?\s*---\s*\r?\n([\s\S]*?)\r?\n\s*---\s*(?:\r?\n|$)/
98
- const INIT_DIRECTIVE_REGEX = /^\s*%%\{\s*(?:init|initialize)\s*:\s*([\s\S]*?)\}\s*%%\s*(?:\r?\n|$)?/gm
99
-
100
- export function normalizeMermaidSource(
101
- text: string,
102
- baseConfig: MermaidRuntimeConfig = {},
103
- ): NormalizedMermaidSource {
104
- const processed = preprocessMermaidSource(text, runtimeConfigToFrontmatterMap(baseConfig))
105
-
106
- return {
107
- text: processed.lines.join('\n'),
108
- body: processed.body,
109
- lines: processed.lines,
110
- firstLine: processed.lines[0]?.toLowerCase() ?? '',
111
- config: normalizeMermaidRuntimeConfig(processed.frontmatter),
112
- frontmatter: processed.frontmatter,
113
- }
114
- }
115
-
116
- export function preprocessMermaidSource(
117
- text: string,
118
- baseFrontmatter: MermaidFrontmatterMap = {},
119
- ): ProcessedMermaidSource {
120
- const frontmatterMatch = text.match(FRONTMATTER_REGEX)
121
- const yamlFrontmatter = frontmatterMatch ? canonicalizeFrontmatterMap(parseYamlDocument(frontmatterMatch[1]!)) : {}
122
- const rawBody = frontmatterMatch ? text.slice(frontmatterMatch[0].length) : text
123
- const { body, frontmatter: directiveFrontmatter } = extractInitDirectives(rawBody)
124
- const frontmatter = mergeFrontmatterMaps(
125
- mergeFrontmatterMaps(canonicalizeFrontmatterMap(baseFrontmatter), yamlFrontmatter),
126
- canonicalizeFrontmatterMap(directiveFrontmatter),
127
- )
128
-
129
- return {
130
- body,
131
- lines: toMermaidLines(body),
132
- frontmatter,
133
- }
134
- }
135
-
136
- export function toMermaidLines(text: string): string[] {
137
- return text
138
- .replace(/^\uFEFF/, '')
139
- .split(/\r?\n/)
140
- .map(line => line.trim())
141
- .filter(line => line.length > 0 && !line.startsWith('%%'))
142
- }
143
-
144
- export function mergeMermaidConfigs(...configs: MermaidRuntimeConfig[]): MermaidRuntimeConfig {
145
- const merged: MermaidFrontmatterMap = {}
146
-
147
- for (const config of configs) {
148
- mergeInto(merged, runtimeConfigToFrontmatterMap(config))
149
- }
150
-
151
- return normalizeMermaidRuntimeConfig(merged)
152
- }
153
-
154
- export function mergeFrontmatterMaps(
155
- base: MermaidFrontmatterMap,
156
- override: MermaidFrontmatterMap,
157
- ): MermaidFrontmatterMap {
158
- const merged = cloneFrontmatterMap(base)
159
-
160
- for (const [key, value] of Object.entries(override)) {
161
- if (value === undefined) continue
162
-
163
- const existing = merged[key]
164
- if (isFrontmatterMap(existing) && isFrontmatterMap(value)) {
165
- merged[key] = mergeFrontmatterMaps(existing, value)
166
- continue
167
- }
168
-
169
- merged[key] = cloneFrontmatterValue(value)
170
- }
171
-
172
- return merged
173
- }
174
-
175
- export function getFrontmatterMap(
176
- root: MermaidFrontmatterMap,
177
- path: readonly string[],
178
- ): MermaidFrontmatterMap | undefined {
179
- let current: MermaidFrontmatterValue | undefined = root
180
- for (const segment of path) {
181
- if (!isFrontmatterMap(current)) return undefined
182
- current = current[segment]
183
- }
184
- return isFrontmatterMap(current) ? current : undefined
185
- }
186
-
187
- export function getFrontmatterScalar<T extends MermaidFrontmatterScalar>(
188
- root: MermaidFrontmatterMap,
189
- path: readonly string[],
190
- ): T | undefined {
191
- let current: MermaidFrontmatterValue | undefined = root
192
- for (const segment of path) {
193
- if (!isFrontmatterMap(current)) return undefined
194
- current = current[segment]
195
- }
196
- return current !== undefined && !Array.isArray(current) && (typeof current !== 'object' || current === null)
197
- ? current as T
198
- : undefined
199
- }
200
-
201
- export function getFrontmatterList<T extends MermaidFrontmatterValue = MermaidFrontmatterValue>(
202
- root: MermaidFrontmatterMap,
203
- path: readonly string[],
204
- ): T[] | undefined {
205
- let current: MermaidFrontmatterValue | undefined = root
206
- for (const segment of path) {
207
- if (!isFrontmatterMap(current)) return undefined
208
- current = current[segment]
209
- }
210
- return Array.isArray(current) ? current as T[] : undefined
211
- }
212
-
213
- function runtimeConfigToFrontmatterMap(config: MermaidRuntimeConfig): MermaidFrontmatterMap {
214
- return canonicalizeFrontmatterMap(toFrontmatterMap(config) ?? {})
215
- }
216
-
217
- function normalizeMermaidRuntimeConfig(raw: MermaidFrontmatterMap): MermaidRuntimeConfig {
218
- const config = cloneFrontmatterMap(raw) as MermaidRuntimeConfig
219
-
220
- if (isFrontmatterMap(config.themeVariables)) {
221
- config.themeVariables = cloneFrontmatterMap(config.themeVariables) as MermaidThemeVariables
222
- }
223
-
224
- if (isFrontmatterMap(config.timeline)) {
225
- config.timeline = normalizeTimelineRuntimeConfig(config.timeline)
226
- }
227
-
228
- if (isFrontmatterMap(config.journey)) {
229
- config.journey = normalizeJourneyRuntimeConfig(config.journey)
230
- }
231
-
232
- return config
233
- }
234
-
235
- function normalizeTimelineRuntimeConfig(raw: MermaidFrontmatterMap): TimelineRuntimeConfig {
236
- const config = cloneFrontmatterMap(raw) as TimelineRuntimeConfig
237
-
238
- if (typeof config.disableMulticolor !== 'boolean') {
239
- delete config.disableMulticolor
240
- }
241
-
242
- const sectionFills = normalizeStringArray(config.sectionFills)
243
- if (sectionFills.length > 0) {
244
- config.sectionFills = sectionFills
245
- } else {
246
- delete config.sectionFills
247
- }
248
-
249
- const sectionColours = normalizeStringArray(config.sectionColours)
250
- const sectionColors = normalizeStringArray((config as MermaidFrontmatterMap).sectionColors)
251
- if (sectionColours.length > 0) {
252
- config.sectionColours = sectionColours
253
- } else if (sectionColors.length > 0) {
254
- config.sectionColours = sectionColors
255
- } else {
256
- delete config.sectionColours
257
- }
258
-
259
- delete (config as MermaidFrontmatterMap).sectionColors
260
- return config
261
- }
262
-
263
- function normalizeJourneyRuntimeConfig(raw: MermaidFrontmatterMap): JourneyRuntimeConfig {
264
- const config = cloneFrontmatterMap(raw) as JourneyRuntimeConfig
265
-
266
- for (const key of [
267
- 'diagramMarginX', 'diagramMarginY', 'leftMargin', 'maxLabelWidth',
268
- 'width', 'height', 'boxMargin', 'boxTextMargin', 'noteMargin',
269
- 'messageMargin', 'bottomMarginAdj', 'taskMargin', 'activationWidth',
270
- ] as const) {
271
- const value = config[key]
272
- if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
273
- delete config[key]
274
- }
275
- }
276
-
277
- if (typeof config.useMaxWidth !== 'boolean') delete config.useMaxWidth
278
- if (typeof config.rightAngles !== 'boolean') delete config.rightAngles
279
-
280
- if (config.messageAlign !== 'left' && config.messageAlign !== 'center' && config.messageAlign !== 'right') {
281
- delete config.messageAlign
282
- }
283
-
284
- const taskFontSize = normalizeCssFontSize(config.taskFontSize)
285
- if (taskFontSize !== undefined) config.taskFontSize = taskFontSize
286
- else delete config.taskFontSize
287
-
288
- for (const key of ['taskFontFamily', 'textPlacement', 'titleColor', 'titleFontFamily'] as const) {
289
- if (typeof config[key] !== 'string' || config[key]!.trim().length === 0) delete config[key]
290
- }
291
-
292
- const titleFontSize = normalizeCssFontSize(config.titleFontSize)
293
- if (titleFontSize !== undefined) config.titleFontSize = titleFontSize
294
- else delete config.titleFontSize
295
-
296
- for (const key of ['actorColours', 'sectionFills', 'sectionColours'] as const) {
297
- const colors = normalizeStringArray(config[key])
298
- if (colors.length > 0) config[key] = colors
299
- else delete config[key]
300
- }
301
-
302
- return config
303
- }
304
-
305
- function normalizeCssFontSize(value: MermaidConfigValue | undefined): string | number | undefined {
306
- if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value
307
- if (typeof value === 'string' && value.trim().length > 0) return value.trim()
308
- return undefined
309
- }
310
-
311
- function normalizeStringArray(value: MermaidConfigValue | undefined): string[] {
312
- return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : []
313
- }
314
-
315
- function mergeInto(target: MermaidFrontmatterMap, source: MermaidFrontmatterMap | undefined): void {
316
- if (!source) return
317
-
318
- for (const [key, value] of Object.entries(source)) {
319
- if (value === undefined) continue
320
-
321
- if (Array.isArray(value)) {
322
- target[key] = value.map(entry => cloneFrontmatterValue(entry)!)
323
- continue
324
- }
325
-
326
- if (isFrontmatterMap(value)) {
327
- const existing = isFrontmatterMap(target[key]) ? target[key] as MermaidFrontmatterMap : {}
328
- target[key] = existing
329
- mergeInto(existing, value)
330
- continue
331
- }
332
-
333
- target[key] = value
334
- }
335
- }
336
-
337
- function canonicalizeFrontmatterMap(raw: MermaidFrontmatterMap): MermaidFrontmatterMap {
338
- const topLevel = cloneFrontmatterMap(raw)
339
- const configRoot = isFrontmatterMap(topLevel.config) ? topLevel.config : undefined
340
- delete topLevel.config
341
-
342
- return configRoot ? mergeFrontmatterMaps(configRoot, topLevel) : topLevel
343
- }
344
-
345
- function parseYamlDocument(text: string): MermaidFrontmatterMap {
346
- try {
347
- return toFrontmatterMap(YAML.parse(text)) ?? {}
348
- } catch {
349
- return {}
350
- }
351
- }
352
-
353
- function extractInitDirectives(text: string): { body: string; frontmatter: MermaidFrontmatterMap } {
354
- let merged: MermaidFrontmatterMap = {}
355
-
356
- const body = text.replace(INIT_DIRECTIVE_REGEX, (_match, payload: string) => {
357
- const parsed = parseDirectiveMap(payload)
358
- if (parsed) merged = mergeFrontmatterMaps(merged, canonicalizeFrontmatterMap(parsed))
359
- return ''
360
- })
361
-
362
- return { body, frontmatter: merged }
363
- }
364
-
365
- function parseDirectiveMap(text: string): MermaidFrontmatterMap | undefined {
366
- try {
367
- return toFrontmatterMap(YAML.parse(text))
368
- } catch {
369
- return parseLooseObjectLiteral(text)
370
- }
371
- }
372
-
373
- function toFrontmatterMap(value: unknown): MermaidFrontmatterMap | undefined {
374
- if (!isPlainObject(value)) return undefined
375
-
376
- const map: MermaidFrontmatterMap = {}
377
- for (const [key, entry] of Object.entries(value)) {
378
- const parsed = toFrontmatterValue(entry)
379
- if (parsed !== undefined) map[key] = parsed
380
- }
381
- return map
382
- }
383
-
384
- function toFrontmatterValue(value: unknown): MermaidFrontmatterValue | undefined {
385
- if (value === null) return null
386
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value
387
-
388
- if (Array.isArray(value)) {
389
- const items: MermaidFrontmatterList = []
390
- for (const entry of value) {
391
- const parsed = toFrontmatterValue(entry)
392
- if (parsed === undefined) return undefined
393
- items.push(parsed)
394
- }
395
- return items
396
- }
397
-
398
- return toFrontmatterMap(value)
399
- }
400
-
401
- function cloneFrontmatterMap(value: MermaidFrontmatterMap): MermaidFrontmatterMap {
402
- const clone: MermaidFrontmatterMap = {}
403
- for (const [key, entry] of Object.entries(value)) {
404
- clone[key] = cloneFrontmatterValue(entry)
405
- }
406
- return clone
407
- }
408
-
409
- function cloneFrontmatterValue(value: MermaidFrontmatterValue | undefined): MermaidFrontmatterValue | undefined {
410
- if (value === undefined || value === null) return value
411
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return value
412
- if (Array.isArray(value)) return value.map(entry => cloneFrontmatterValue(entry)!)
413
- return cloneFrontmatterMap(value)
414
- }
415
-
416
- function isFrontmatterMap(value: MermaidFrontmatterValue | undefined): value is MermaidFrontmatterMap {
417
- return !!value && typeof value === 'object' && !Array.isArray(value)
418
- }
419
-
420
- function isPlainObject(value: unknown): value is Record<string, unknown> {
421
- return !!value && typeof value === 'object' && !Array.isArray(value)
422
- }
423
-
424
- function parseScalar(valueText: string): MermaidFrontmatterScalar {
425
- if ((valueText.startsWith('"') && valueText.endsWith('"')) || (valueText.startsWith("'") && valueText.endsWith("'"))) {
426
- return unescapeQuotedString(valueText)
427
- }
428
- if (valueText === 'true') return true
429
- if (valueText === 'false') return false
430
- if (valueText === 'null') return null
431
- if (/^[+-]?(?:\d+(?:\.\d+)?|\.\d+)$/.test(valueText)) return Number(valueText)
432
- return valueText
433
- }
434
-
435
- function parseLooseObjectLiteral(text: string): MermaidFrontmatterMap | undefined {
436
- const parsed = parseFlowValue(text.trim())
437
- return isFrontmatterMap(parsed) ? parsed : undefined
438
- }
439
-
440
- function parseFlowValue(text: string): MermaidFrontmatterValue | undefined {
441
- const trimmed = text.trim()
442
- if (trimmed.length === 0) return undefined
443
-
444
- if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
445
- return parseFlowMap(trimmed.slice(1, -1))
446
- }
447
- if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
448
- return parseFlowList(trimmed.slice(1, -1))
449
- }
450
-
451
- return parseScalar(trimmed)
452
- }
453
-
454
- function parseFlowMap(text: string): MermaidFrontmatterMap | undefined {
455
- const map: MermaidFrontmatterMap = {}
456
- for (const entry of splitFlowEntries(text)) {
457
- const colonIdx = findSeparatorIndex(entry, ':')
458
- if (colonIdx === -1) return undefined
459
-
460
- const rawKey = entry.slice(0, colonIdx).trim()
461
- const rawValue = entry.slice(colonIdx + 1).trim()
462
- const key = parseFlowKey(rawKey)
463
- if (!key) return undefined
464
-
465
- const value = parseFlowValue(rawValue)
466
- if (value === undefined) return undefined
467
- map[key] = value
468
- }
469
- return map
470
- }
471
-
472
- function parseFlowList(text: string): MermaidFrontmatterList | undefined {
473
- const values: MermaidFrontmatterList = []
474
- for (const entry of splitFlowEntries(text)) {
475
- const value = parseFlowValue(entry)
476
- if (value === undefined) return undefined
477
- values.push(value)
478
- }
479
- return values
480
- }
481
-
482
- function parseFlowKey(text: string): string | undefined {
483
- const trimmed = text.trim()
484
- if (!trimmed) return undefined
485
- if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
486
- return unescapeQuotedString(trimmed)
487
- }
488
- return trimmed
489
- }
490
-
491
- function splitFlowEntries(text: string): string[] {
492
- const entries: string[] = []
493
- let current = ''
494
- let quote: '"' | "'" | null = null
495
- let braceDepth = 0
496
- let bracketDepth = 0
497
-
498
- for (let i = 0; i < text.length; i++) {
499
- const char = text[i]!
500
- if (quote) {
501
- current += char
502
- if (char === quote && text[i - 1] !== '\\') quote = null
503
- continue
504
- }
505
-
506
- if (char === '"' || char === "'") {
507
- quote = char
508
- current += char
509
- continue
510
- }
511
- if (char === '{') {
512
- braceDepth++
513
- current += char
514
- continue
515
- }
516
- if (char === '}') {
517
- braceDepth--
518
- current += char
519
- continue
520
- }
521
- if (char === '[') {
522
- bracketDepth++
523
- current += char
524
- continue
525
- }
526
- if (char === ']') {
527
- bracketDepth--
528
- current += char
529
- continue
530
- }
531
- if (char === ',' && braceDepth === 0 && bracketDepth === 0) {
532
- const value = current.trim()
533
- if (value) entries.push(value)
534
- current = ''
535
- continue
536
- }
537
-
538
- current += char
539
- }
540
-
541
- const trailing = current.trim()
542
- if (trailing) entries.push(trailing)
543
- return entries
544
- }
545
-
546
- function findSeparatorIndex(text: string, separator: ':' | ','): number {
547
- let quote: '"' | "'" | null = null
548
- let braceDepth = 0
549
- let bracketDepth = 0
550
-
551
- for (let i = 0; i < text.length; i++) {
552
- const char = text[i]!
553
- if (quote) {
554
- if (char === quote && text[i - 1] !== '\\') quote = null
555
- continue
556
- }
557
- if (char === '"' || char === "'") {
558
- quote = char
559
- continue
560
- }
561
- if (char === '{') {
562
- braceDepth++
563
- continue
564
- }
565
- if (char === '}') {
566
- braceDepth--
567
- continue
568
- }
569
- if (char === '[') {
570
- bracketDepth++
571
- continue
572
- }
573
- if (char === ']') {
574
- bracketDepth--
575
- continue
576
- }
577
- if (char === separator && braceDepth === 0 && bracketDepth === 0) return i
578
- }
579
-
580
- return -1
581
- }
582
-
583
- function unescapeQuotedString(valueText: string): string {
584
- try {
585
- if (valueText.startsWith("'")) {
586
- return valueText
587
- .slice(1, -1)
588
- .replace(/\\\\/g, '\\')
589
- .replace(/\\'/g, "'")
590
- }
591
- return JSON.parse(valueText)
592
- } catch {
593
- return valueText.slice(1, -1)
594
- }
595
- }
596
-
597
- export type RoutedDiagramType = 'flowchart' | 'architecture' | 'sequence' | 'class' | 'er' | 'timeline' | 'journey' | 'xychart' | 'pie' | 'quadrant' | 'gantt'
598
-
599
- /**
600
- * Return the logical Mermaid lines after frontmatter/init/comment normalization.
601
- * This is a thin wrapper around the richer source preprocessing used by the
602
- * public SVG and ASCII entry points.
603
- */
604
- export function preprocessMermaidLines(text: string): string[] {
605
- return preprocessMermaidSource(text).lines
606
- }
607
-
608
- /**
609
- * Detect the routed Mermaid diagram family from a normalized first logical line.
610
- * Returns null for headers that are known not to be routed by this renderer.
611
- */
612
- export function detectDiagramTypeFromFirstLine(firstLine: string): RoutedDiagramType | null {
613
- const line = firstLine.split(';')[0]?.trim().toLowerCase() ?? ''
614
- if (/^architecture-beta\s*$/.test(line)) return 'architecture'
615
- if (/^xychart(-beta)?\b/.test(line)) return 'xychart'
616
- if (/^pie\b/.test(line)) return 'pie'
617
- if (/^quadrantchart\s*$/.test(line)) return 'quadrant'
618
- // Upstream accepts (and ignores) a direction token after the header.
619
- if (/^timeline(?:\s+(?:td|tb|lr|bt|rl))?\s*$/.test(line)) return 'timeline'
620
- if (/^gantt\s*$/.test(line)) return 'gantt'
621
- if (/^journey\s*$/.test(line)) return 'journey'
622
- if (/^sequencediagram\s*$/.test(line)) return 'sequence'
623
- if (/^classdiagram\s*$/.test(line)) return 'class'
624
- if (/^erdiagram\s*$/.test(line)) return 'er'
625
- if (/^(?:flowchart|graph|swimlane)\b/.test(line) || /^statediagram(?:-v2)?\s*$/.test(line)) return 'flowchart'
626
- return null
627
- }
628
-
629
- /**
630
- * Looser family recognition for agent parsing: malformed known-family headers
631
- * should become opaque round-trip bodies instead of UNKNOWN_HEADER errors.
632
- */
633
- export function detectLooseDiagramTypeFromFirstLine(firstLine: string): RoutedDiagramType | null {
634
- const line = firstLine.split(';')[0]?.trim().toLowerCase() ?? ''
635
- if (/^architecture-beta\b/.test(line)) return 'architecture'
636
- if (/^xychart(-beta)?\b/.test(line)) return 'xychart'
637
- if (/^pie\b/.test(line)) return 'pie'
638
- if (/^quadrantchart\b/.test(line)) return 'quadrant'
639
- if (/^timeline\b/.test(line)) return 'timeline'
640
- if (/^gantt\b/.test(line)) return 'gantt'
641
- if (/^journey\b/.test(line)) return 'journey'
642
- if (/^sequencediagram\b/.test(line)) return 'sequence'
643
- if (/^classdiagram\b/.test(line)) return 'class'
644
- if (/^erdiagram\b/.test(line)) return 'er'
645
- if (/^(?:flowchart|graph|swimlane)\b/.test(line) || /^statediagram(?:-v2)?\b/.test(line)) return 'flowchart'
646
- return null
647
- }
648
-
649
- /**
650
- * Detect the routed Mermaid diagram family from source text. Unknown headers
651
- * intentionally route to the legacy flowchart path for backwards compatibility.
652
- */
653
- export function detectDiagramType(text: string): RoutedDiagramType {
654
- return detectDiagramTypeFromFirstLine(preprocessMermaidLines(text)[0] ?? '') ?? 'flowchart'
655
- }