sirena 0.1.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 (382) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build_deploy.yml +59 -0
  3. data/.github/workflows/links.yml +85 -0
  4. data/.github/workflows/rake.yml +15 -0
  5. data/.github/workflows/release.yml +27 -0
  6. data/.gitignore +68 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +14 -0
  9. data/.rubocop_todo.yml +70 -0
  10. data/ARCHITECTURE.md +744 -0
  11. data/Gemfile +12 -0
  12. data/LICENSE +25 -0
  13. data/README.adoc +357 -0
  14. data/Rakefile +11 -0
  15. data/docs/.gitignore +1 -0
  16. data/docs/Gemfile +13 -0
  17. data/docs/_config.yml +182 -0
  18. data/docs/_diagram_types/architecture-diagram.adoc +314 -0
  19. data/docs/_diagram_types/block-diagram.adoc +345 -0
  20. data/docs/_diagram_types/c4-diagram.adoc +559 -0
  21. data/docs/_diagram_types/class-diagram.adoc +816 -0
  22. data/docs/_diagram_types/er-diagram.adoc +719 -0
  23. data/docs/_diagram_types/error-diagram.adoc +114 -0
  24. data/docs/_diagram_types/examples/flowchart-examples.adoc +29 -0
  25. data/docs/_diagram_types/flowchart.adoc +488 -0
  26. data/docs/_diagram_types/gantt-chart.adoc +502 -0
  27. data/docs/_diagram_types/git-graph.adoc +600 -0
  28. data/docs/_diagram_types/index.adoc +192 -0
  29. data/docs/_diagram_types/info-diagram.adoc +103 -0
  30. data/docs/_diagram_types/kanban-diagram.adoc +262 -0
  31. data/docs/_diagram_types/mindmap.adoc +603 -0
  32. data/docs/_diagram_types/packet-diagram.adoc +378 -0
  33. data/docs/_diagram_types/pie-chart.adoc +335 -0
  34. data/docs/_diagram_types/quadrant-chart.adoc +406 -0
  35. data/docs/_diagram_types/radar-chart.adoc +528 -0
  36. data/docs/_diagram_types/requirement-diagram.adoc +416 -0
  37. data/docs/_diagram_types/sankey-diagram.adoc +357 -0
  38. data/docs/_diagram_types/sequence-diagram.adoc +664 -0
  39. data/docs/_diagram_types/state-diagram.adoc +658 -0
  40. data/docs/_diagram_types/timeline.adoc +352 -0
  41. data/docs/_diagram_types/treemap-diagram.adoc +462 -0
  42. data/docs/_diagram_types/user-journey.adoc +602 -0
  43. data/docs/_features/index.adoc +129 -0
  44. data/docs/_guides/cli-reference.adoc +203 -0
  45. data/docs/_guides/index.adoc +56 -0
  46. data/docs/_guides/installation.adoc +100 -0
  47. data/docs/_guides/quick-start.adoc +132 -0
  48. data/docs/_pages/comparison.adoc +441 -0
  49. data/docs/_pages/compatibility.adoc +300 -0
  50. data/docs/_pages/index.adoc +39 -0
  51. data/docs/_references/index.adoc +103 -0
  52. data/docs/_tutorials/index.adoc +57 -0
  53. data/docs/index.adoc +166 -0
  54. data/docs/lychee.toml +54 -0
  55. data/examples/.gitignore +10 -0
  56. data/examples/README.adoc +196 -0
  57. data/examples/README.md +64 -0
  58. data/examples/architecture/01-basic-services.mmd +9 -0
  59. data/examples/architecture/01-basic-services.svg +37 -0
  60. data/examples/architecture/02-service-groups.mmd +16 -0
  61. data/examples/architecture/02-service-groups.svg +55 -0
  62. data/examples/architecture/README.adoc +79 -0
  63. data/examples/block/01-basic-blocks.mmd +13 -0
  64. data/examples/block/01-basic-blocks.svg +44 -0
  65. data/examples/block/02-block-shapes.mmd +13 -0
  66. data/examples/block/02-block-shapes.svg +47 -0
  67. data/examples/block/README.adoc +85 -0
  68. data/examples/c4/01-context-diagram.mmd +10 -0
  69. data/examples/c4/01-context-diagram.svg +45 -0
  70. data/examples/c4/02-container-diagram.mmd +24 -0
  71. data/examples/c4/02-container-diagram.svg +105 -0
  72. data/examples/c4/README.adoc +92 -0
  73. data/examples/class_diagram/01-basic-classes.mmd +61 -0
  74. data/examples/class_diagram/01-basic-classes.svg +117 -0
  75. data/examples/class_diagram/02-relationships.mmd +61 -0
  76. data/examples/class_diagram/02-relationships.svg +129 -0
  77. data/examples/class_diagram/README.adoc +93 -0
  78. data/examples/er_diagram/01-basic-entities.mmd +64 -0
  79. data/examples/er_diagram/01-basic-entities.svg +5 -0
  80. data/examples/er_diagram/02-cardinality.mmd +57 -0
  81. data/examples/er_diagram/02-cardinality.svg +125 -0
  82. data/examples/er_diagram/README.adoc +88 -0
  83. data/examples/error/01-basic-error.mmd +1 -0
  84. data/examples/error/01-basic-error.svg +13 -0
  85. data/examples/error/02-error-display.mmd +1 -0
  86. data/examples/error/02-error-display.svg +13 -0
  87. data/examples/error/README.adoc +71 -0
  88. data/examples/error_message_example.svg +13 -0
  89. data/examples/flowchart/00-original.mmd +13 -0
  90. data/examples/flowchart/00-original.svg +5 -0
  91. data/examples/flowchart/01-basic-flow.mmd +7 -0
  92. data/examples/flowchart/01-basic-flow.svg +52 -0
  93. data/examples/flowchart/01-basic-flow.yml +13 -0
  94. data/examples/flowchart/02*.svg +87 -0
  95. data/examples/flowchart/02-node-shapes.mmd +9 -0
  96. data/examples/flowchart/02-node-shapes.svg +33 -0
  97. data/examples/flowchart/03-edge-types.mmd +7 -0
  98. data/examples/flowchart/03-edge-types.svg +53 -0
  99. data/examples/flowchart/04-subgraphs.mmd +9 -0
  100. data/examples/flowchart/04-subgraphs.svg +33 -0
  101. data/examples/flowchart/05-styling.mmd +9 -0
  102. data/examples/flowchart/05-styling.svg +33 -0
  103. data/examples/flowchart/06-complex-flow.mmd +8 -0
  104. data/examples/flowchart/06-complex-flow.svg +59 -0
  105. data/examples/flowchart/README.adoc +167 -0
  106. data/examples/gantt/01-simple-timeline.* +14 -0
  107. data/examples/gantt/01-simple-timeline.mmd +6 -0
  108. data/examples/gantt/01-simple-timeline.svg +26 -0
  109. data/examples/gantt/02-task-dependencies.mmd +6 -0
  110. data/examples/gantt/02-task-dependencies.svg +26 -0
  111. data/examples/gantt/README.adoc +86 -0
  112. data/examples/git_graph/01-linear-history.mmd +12 -0
  113. data/examples/git_graph/01-linear-history.svg +26 -0
  114. data/examples/git_graph/02-branching.mmd +12 -0
  115. data/examples/git_graph/02-branching.svg +26 -0
  116. data/examples/git_graph/README.adoc +73 -0
  117. data/examples/info/02-showinfo.mmd +1 -0
  118. data/examples/info/02-showinfo.svg +10 -0
  119. data/examples/info/README.adoc +58 -0
  120. data/examples/info_showinfo_example.svg +10 -0
  121. data/examples/kanban/01-simple-board.mmd +8 -0
  122. data/examples/kanban/01-simple-board.svg +43 -0
  123. data/examples/kanban/02-workflow.mmd +8 -0
  124. data/examples/kanban/02-workflow.svg +43 -0
  125. data/examples/kanban/README.adoc +79 -0
  126. data/examples/mindmap/01-simple-tree.mmd +19 -0
  127. data/examples/mindmap/01-simple-tree.svg +61 -0
  128. data/examples/mindmap/02-knowledge-map.mmd +19 -0
  129. data/examples/mindmap/02-knowledge-map.svg +61 -0
  130. data/examples/mindmap/README.adoc +77 -0
  131. data/examples/packet/01-basic-packet.* +17 -0
  132. data/examples/packet/01-basic-packet.mmd +4 -0
  133. data/examples/packet/01-basic-packet.svg +82 -0
  134. data/examples/packet/README.adoc +58 -0
  135. data/examples/pie/01-simple-chart.mmd +5 -0
  136. data/examples/pie/01-simple-chart.svg +17 -0
  137. data/examples/pie/02-labeled-slices.mmd +6 -0
  138. data/examples/pie/02-labeled-slices.svg +19 -0
  139. data/examples/pie/README.adoc +75 -0
  140. data/examples/quadrant/01-basic-quadrant.mmd +13 -0
  141. data/examples/quadrant/01-basic-quadrant.svg +33 -0
  142. data/examples/quadrant/02-positioned-items.mmd +14 -0
  143. data/examples/quadrant/02-positioned-items.svg +35 -0
  144. data/examples/quadrant/README.adoc +84 -0
  145. data/examples/radar/01-simple-radar.* +5 -0
  146. data/examples/radar/01-simple-radar.mmd +3 -0
  147. data/examples/radar/01-simple-radar.svg +25 -0
  148. data/examples/radar/02-multiple-curves.mmd +4 -0
  149. data/examples/radar/02-multiple-curves.svg +43 -0
  150. data/examples/radar/README.adoc +75 -0
  151. data/examples/requirement/01-basic-requirements.mmd +23 -0
  152. data/examples/requirement/01-basic-requirements.svg +49 -0
  153. data/examples/requirement/02-risk-levels.mmd +23 -0
  154. data/examples/requirement/02-risk-levels.svg +49 -0
  155. data/examples/requirement/README.adoc +85 -0
  156. data/examples/sankey/01-simple-flow.mmd +7 -0
  157. data/examples/sankey/01-simple-flow.svg +34 -0
  158. data/examples/sankey/02-multi-stage.mmd +11 -0
  159. data/examples/sankey/02-multi-stage.svg +44 -0
  160. data/examples/sankey/README.adoc +74 -0
  161. data/examples/sequence/01-basic-sequence.mmd +27 -0
  162. data/examples/sequence/01-basic-sequence.svg +5 -0
  163. data/examples/sequence/02-activations.mmd +17 -0
  164. data/examples/sequence/02-activations.svg +78 -0
  165. data/examples/sequence/README.adoc +86 -0
  166. data/examples/state_diagram/01-simple-states.mmd +29 -0
  167. data/examples/state_diagram/01-simple-states.svg +5 -0
  168. data/examples/state_diagram/02-composite.mmd +19 -0
  169. data/examples/state_diagram/02-composite.svg +81 -0
  170. data/examples/state_diagram/README.adoc +90 -0
  171. data/examples/timeline/01-simple-timeline.mmd +11 -0
  172. data/examples/timeline/01-simple-timeline.svg +36 -0
  173. data/examples/timeline/02-periods.mmd +15 -0
  174. data/examples/timeline/02-periods.svg +47 -0
  175. data/examples/timeline/README.adoc +78 -0
  176. data/examples/treemap/01-basic-treemap.mmd +12 -0
  177. data/examples/treemap/01-basic-treemap.svg +59 -0
  178. data/examples/treemap/README.adoc +59 -0
  179. data/examples/user_journey/01-simple-journey.mmd +23 -0
  180. data/examples/user_journey/01-simple-journey.svg +5 -0
  181. data/examples/user_journey/02-multi-actor.mmd +18 -0
  182. data/examples/user_journey/02-multi-actor.svg +129 -0
  183. data/examples/user_journey/README.adoc +81 -0
  184. data/examples/xychart/01-line-chart.mmd +5 -0
  185. data/examples/xychart/01-line-chart.svg +43 -0
  186. data/examples/xychart/02-bar-chart.mmd +7 -0
  187. data/examples/xychart/02-bar-chart.svg +48 -0
  188. data/examples/xychart/README.adoc +80 -0
  189. data/exe/sirena +7 -0
  190. data/lib/sirena/cli.rb +138 -0
  191. data/lib/sirena/commands/batch.rb +117 -0
  192. data/lib/sirena/commands/render.rb +80 -0
  193. data/lib/sirena/commands/types.rb +29 -0
  194. data/lib/sirena/commands/version.rb +24 -0
  195. data/lib/sirena/diagram/architecture.rb +46 -0
  196. data/lib/sirena/diagram/base.rb +61 -0
  197. data/lib/sirena/diagram/block.rb +81 -0
  198. data/lib/sirena/diagram/c4.rb +328 -0
  199. data/lib/sirena/diagram/class_diagram.rb +385 -0
  200. data/lib/sirena/diagram/er_diagram.rb +238 -0
  201. data/lib/sirena/diagram/error.rb +38 -0
  202. data/lib/sirena/diagram/flowchart.rb +160 -0
  203. data/lib/sirena/diagram/gantt.rb +71 -0
  204. data/lib/sirena/diagram/git_graph.rb +36 -0
  205. data/lib/sirena/diagram/info.rb +38 -0
  206. data/lib/sirena/diagram/kanban.rb +178 -0
  207. data/lib/sirena/diagram/mindmap.rb +54 -0
  208. data/lib/sirena/diagram/packet.rb +79 -0
  209. data/lib/sirena/diagram/pie.rb +115 -0
  210. data/lib/sirena/diagram/quadrant.rb +138 -0
  211. data/lib/sirena/diagram/radar.rb +52 -0
  212. data/lib/sirena/diagram/requirement.rb +133 -0
  213. data/lib/sirena/diagram/sankey.rb +217 -0
  214. data/lib/sirena/diagram/sequence.rb +242 -0
  215. data/lib/sirena/diagram/state_diagram.rb +237 -0
  216. data/lib/sirena/diagram/timeline.rb +171 -0
  217. data/lib/sirena/diagram/treemap.rb +84 -0
  218. data/lib/sirena/diagram/user_journey.rb +149 -0
  219. data/lib/sirena/diagram/xy_chart.rb +76 -0
  220. data/lib/sirena/diagram.rb +8 -0
  221. data/lib/sirena/diagram_registry.rb +101 -0
  222. data/lib/sirena/engine.rb +292 -0
  223. data/lib/sirena/parser/architecture.rb +41 -0
  224. data/lib/sirena/parser/base.rb +41 -0
  225. data/lib/sirena/parser/block.rb +72 -0
  226. data/lib/sirena/parser/c4.rb +53 -0
  227. data/lib/sirena/parser/class_diagram.rb +63 -0
  228. data/lib/sirena/parser/er_diagram.rb +40 -0
  229. data/lib/sirena/parser/error.rb +49 -0
  230. data/lib/sirena/parser/flowchart.rb +71 -0
  231. data/lib/sirena/parser/gantt.rb +60 -0
  232. data/lib/sirena/parser/git_graph.rb +95 -0
  233. data/lib/sirena/parser/grammars/architecture.rb +145 -0
  234. data/lib/sirena/parser/grammars/block.rb +190 -0
  235. data/lib/sirena/parser/grammars/c4.rb +226 -0
  236. data/lib/sirena/parser/grammars/class_diagram.rb +284 -0
  237. data/lib/sirena/parser/grammars/common.rb +84 -0
  238. data/lib/sirena/parser/grammars/er_diagram.rb +114 -0
  239. data/lib/sirena/parser/grammars/error.rb +40 -0
  240. data/lib/sirena/parser/grammars/flowchart.rb +298 -0
  241. data/lib/sirena/parser/grammars/gantt.rb +252 -0
  242. data/lib/sirena/parser/grammars/git_graph.rb +167 -0
  243. data/lib/sirena/parser/grammars/info.rb +58 -0
  244. data/lib/sirena/parser/grammars/kanban.rb +83 -0
  245. data/lib/sirena/parser/grammars/mindmap.rb +115 -0
  246. data/lib/sirena/parser/grammars/packet.rb +73 -0
  247. data/lib/sirena/parser/grammars/pie.rb +128 -0
  248. data/lib/sirena/parser/grammars/quadrant.rb +199 -0
  249. data/lib/sirena/parser/grammars/radar.rb +150 -0
  250. data/lib/sirena/parser/grammars/requirement.rb +188 -0
  251. data/lib/sirena/parser/grammars/sankey.rb +104 -0
  252. data/lib/sirena/parser/grammars/sequence.rb +247 -0
  253. data/lib/sirena/parser/grammars/state_diagram.rb +172 -0
  254. data/lib/sirena/parser/grammars/timeline.rb +142 -0
  255. data/lib/sirena/parser/grammars/treemap.rb +120 -0
  256. data/lib/sirena/parser/grammars/xy_chart.rb +120 -0
  257. data/lib/sirena/parser/info.rb +49 -0
  258. data/lib/sirena/parser/kanban.rb +97 -0
  259. data/lib/sirena/parser/mindmap.rb +106 -0
  260. data/lib/sirena/parser/packet.rb +76 -0
  261. data/lib/sirena/parser/pie.rb +49 -0
  262. data/lib/sirena/parser/quadrant.rb +57 -0
  263. data/lib/sirena/parser/radar.rb +104 -0
  264. data/lib/sirena/parser/requirement.rb +70 -0
  265. data/lib/sirena/parser/sankey.rb +64 -0
  266. data/lib/sirena/parser/sequence.rb +51 -0
  267. data/lib/sirena/parser/state_diagram.rb +69 -0
  268. data/lib/sirena/parser/timeline.rb +57 -0
  269. data/lib/sirena/parser/transforms/architecture.rb +97 -0
  270. data/lib/sirena/parser/transforms/block.rb +254 -0
  271. data/lib/sirena/parser/transforms/c4.rb +347 -0
  272. data/lib/sirena/parser/transforms/class_diagram.rb +352 -0
  273. data/lib/sirena/parser/transforms/er_diagram.rb +169 -0
  274. data/lib/sirena/parser/transforms/error.rb +58 -0
  275. data/lib/sirena/parser/transforms/flowchart.rb +293 -0
  276. data/lib/sirena/parser/transforms/gantt.rb +215 -0
  277. data/lib/sirena/parser/transforms/git_graph.rb +160 -0
  278. data/lib/sirena/parser/transforms/info.rb +58 -0
  279. data/lib/sirena/parser/transforms/kanban.rb +176 -0
  280. data/lib/sirena/parser/transforms/mindmap.rb +227 -0
  281. data/lib/sirena/parser/transforms/packet.rb +63 -0
  282. data/lib/sirena/parser/transforms/pie.rb +143 -0
  283. data/lib/sirena/parser/transforms/quadrant.rb +177 -0
  284. data/lib/sirena/parser/transforms/radar.rb +126 -0
  285. data/lib/sirena/parser/transforms/requirement.rb +272 -0
  286. data/lib/sirena/parser/transforms/sankey.rb +122 -0
  287. data/lib/sirena/parser/transforms/sequence.rb +342 -0
  288. data/lib/sirena/parser/transforms/state_diagram.rb +292 -0
  289. data/lib/sirena/parser/transforms/timeline.rb +177 -0
  290. data/lib/sirena/parser/transforms/treemap.rb +81 -0
  291. data/lib/sirena/parser/transforms/xy_chart.rb +132 -0
  292. data/lib/sirena/parser/treemap.rb +98 -0
  293. data/lib/sirena/parser/user_journey.rb +120 -0
  294. data/lib/sirena/parser/xy_chart.rb +114 -0
  295. data/lib/sirena/parser.rb +8 -0
  296. data/lib/sirena/renderer/architecture.rb +251 -0
  297. data/lib/sirena/renderer/base.rb +251 -0
  298. data/lib/sirena/renderer/block.rb +286 -0
  299. data/lib/sirena/renderer/c4.rb +490 -0
  300. data/lib/sirena/renderer/class_diagram.rb +499 -0
  301. data/lib/sirena/renderer/er_diagram.rb +417 -0
  302. data/lib/sirena/renderer/error.rb +131 -0
  303. data/lib/sirena/renderer/flowchart.rb +301 -0
  304. data/lib/sirena/renderer/gantt.rb +331 -0
  305. data/lib/sirena/renderer/git_graph.rb +368 -0
  306. data/lib/sirena/renderer/info.rb +93 -0
  307. data/lib/sirena/renderer/kanban.rb +295 -0
  308. data/lib/sirena/renderer/mindmap.rb +396 -0
  309. data/lib/sirena/renderer/packet.rb +239 -0
  310. data/lib/sirena/renderer/pie.rb +235 -0
  311. data/lib/sirena/renderer/quadrant.rb +292 -0
  312. data/lib/sirena/renderer/radar.rb +323 -0
  313. data/lib/sirena/renderer/requirement.rb +371 -0
  314. data/lib/sirena/renderer/sankey.rb +255 -0
  315. data/lib/sirena/renderer/sequence.rb +424 -0
  316. data/lib/sirena/renderer/state_diagram.rb +328 -0
  317. data/lib/sirena/renderer/timeline.rb +304 -0
  318. data/lib/sirena/renderer/treemap.rb +152 -0
  319. data/lib/sirena/renderer/user_journey.rb +331 -0
  320. data/lib/sirena/renderer/xy_chart.rb +452 -0
  321. data/lib/sirena/renderer.rb +8 -0
  322. data/lib/sirena/svg/circle.rb +41 -0
  323. data/lib/sirena/svg/document.rb +103 -0
  324. data/lib/sirena/svg/element.rb +65 -0
  325. data/lib/sirena/svg/ellipse.rb +33 -0
  326. data/lib/sirena/svg/group.rb +71 -0
  327. data/lib/sirena/svg/line.rb +49 -0
  328. data/lib/sirena/svg/path.rb +76 -0
  329. data/lib/sirena/svg/polygon.rb +43 -0
  330. data/lib/sirena/svg/polyline.rb +35 -0
  331. data/lib/sirena/svg/rect.rb +57 -0
  332. data/lib/sirena/svg/style.rb +44 -0
  333. data/lib/sirena/svg/text.rb +72 -0
  334. data/lib/sirena/svg.rb +19 -0
  335. data/lib/sirena/text_measurement.rb +71 -0
  336. data/lib/sirena/theme/builtin/dark.yml +70 -0
  337. data/lib/sirena/theme/builtin/default.yml +80 -0
  338. data/lib/sirena/theme/builtin/high_contrast.yml +70 -0
  339. data/lib/sirena/theme/builtin/light.yml +70 -0
  340. data/lib/sirena/theme/color_palette.rb +48 -0
  341. data/lib/sirena/theme/effect_styles.rb +28 -0
  342. data/lib/sirena/theme/registry.rb +41 -0
  343. data/lib/sirena/theme/shape_styles.rb +28 -0
  344. data/lib/sirena/theme/spacing_config.rb +24 -0
  345. data/lib/sirena/theme/typography.rb +30 -0
  346. data/lib/sirena/theme.rb +69 -0
  347. data/lib/sirena/transform/architecture.rb +273 -0
  348. data/lib/sirena/transform/base.rb +199 -0
  349. data/lib/sirena/transform/block.rb +215 -0
  350. data/lib/sirena/transform/c4.rb +288 -0
  351. data/lib/sirena/transform/class_diagram.rb +296 -0
  352. data/lib/sirena/transform/er_diagram.rb +204 -0
  353. data/lib/sirena/transform/error.rb +39 -0
  354. data/lib/sirena/transform/flowchart.rb +161 -0
  355. data/lib/sirena/transform/gantt.rb +253 -0
  356. data/lib/sirena/transform/git_graph.rb +283 -0
  357. data/lib/sirena/transform/info.rb +39 -0
  358. data/lib/sirena/transform/kanban.rb +180 -0
  359. data/lib/sirena/transform/mindmap.rb +251 -0
  360. data/lib/sirena/transform/packet.rb +185 -0
  361. data/lib/sirena/transform/pie.rb +62 -0
  362. data/lib/sirena/transform/quadrant.rb +167 -0
  363. data/lib/sirena/transform/radar.rb +227 -0
  364. data/lib/sirena/transform/requirement.rb +233 -0
  365. data/lib/sirena/transform/sankey.rb +212 -0
  366. data/lib/sirena/transform/sequence.rb +143 -0
  367. data/lib/sirena/transform/state_diagram.rb +228 -0
  368. data/lib/sirena/transform/timeline.rb +139 -0
  369. data/lib/sirena/transform/treemap.rb +120 -0
  370. data/lib/sirena/transform/user_journey.rb +207 -0
  371. data/lib/sirena/transform/xy_chart.rb +273 -0
  372. data/lib/sirena/transform.rb +8 -0
  373. data/lib/sirena/version.rb +5 -0
  374. data/lib/sirena.rb +328 -0
  375. data/lib/tasks/benchmark.rake +532 -0
  376. data/lib/tasks/examples.rake +468 -0
  377. data/lib/tasks/generate_mermaid_fixtures.rake +363 -0
  378. data/lib/tasks/mermaid_fixtures.rake +46 -0
  379. data/scripts/extract_mermaid_tests.rb +493 -0
  380. data/scripts/rename_to_sirena.rb +73 -0
  381. data/sirena.gemspec +47 -0
  382. metadata +529 -0
data/ARCHITECTURE.md ADDED
@@ -0,0 +1,744 @@
1
+ # Sirena Architecture
2
+
3
+ ## Overview
4
+
5
+ Sirena is a pure Ruby implementation of Mermaid diagram generation following
6
+ strict object-oriented principles with model-driven architecture. The system
7
+ transforms Mermaid syntax into SVG output through a pipeline of well-separated
8
+ components.
9
+
10
+ ## Core Principles
11
+
12
+ 1. **Model-Driven Design**: All data structures use `Lutaml::Model` classes
13
+ 2. **MECE Separation**: Each component has mutually exclusive, collectively
14
+ exhaustive responsibilities
15
+ 3. **Register-Based**: Diagram types and renderers registered dynamically
16
+ 4. **Open/Closed**: Extensible without modification via inheritance/composition
17
+ 5. **Single Responsibility**: Each class handles one cohesive concern
18
+
19
+ ## Processing Pipeline
20
+
21
+ ```
22
+ Mermaid Syntax Input (String)
23
+
24
+
25
+ ┌─────────┐
26
+ │ Parser │ Parslet Grammar → Transform → Parser
27
+ └────┬────┘ (3-layer architecture)
28
+
29
+
30
+ ┌───────────┐
31
+ │ Diagram │ Lutaml::Model AST
32
+ │ Model │ (Flowchart, Sequence, etc.)
33
+ └─────┬─────┘
34
+
35
+
36
+ ┌───────────┐
37
+ │Transform │ Diagram → Graph Conversion
38
+ │ Layer │
39
+ └─────┬─────┘
40
+
41
+
42
+ ┌───────────┐
43
+ │ Elkrb │ Layout Computation
44
+ │ Layout │ (Node positions, edge routes)
45
+ └─────┬─────┘
46
+
47
+
48
+ ┌───────────┐
49
+ │ SVG │ Graph → SVG Conversion
50
+ │ Renderer │
51
+ └─────┬─────┘
52
+
53
+
54
+ ┌───────────┐
55
+ │ SVG │ Lutaml::Model Structure
56
+ │ Builder │
57
+ └─────┬─────┘
58
+
59
+
60
+ SVG XML Output (String)
61
+ ```
62
+
63
+ ## Component Architecture
64
+
65
+ ```
66
+ Sirena (Root Module)
67
+
68
+ ├── Engine (Orchestrator)
69
+ │ │
70
+ │ ├── coordinates overall flow
71
+ │ ├── delegates to Parser
72
+ │ ├── delegates to Transform
73
+ │ └── delegates to Renderer
74
+
75
+ ├── Parser (Syntax Analysis - Parslet-based)
76
+ │ │
77
+ │ ├── Grammars (Parslet syntax rules)
78
+ │ ├── Transforms (Parslet tree transformations)
79
+ │ ├── Parsers (orchestrate Grammar → Transform)
80
+ │ └── produces Diagram Models
81
+
82
+ ├── Diagram (Domain Models)
83
+ │ │
84
+ │ ├── Base (Abstract)
85
+ │ ├── Flowchart
86
+ │ ├── Sequence
87
+ │ ├── ClassDiagram
88
+ │ ├── StateDiagram
89
+ │ ├── ErDiagram
90
+ │ └── UserJourney
91
+
92
+ ├── Transform (Model Conversion)
93
+ │ │
94
+ │ ├── Base (Abstract)
95
+ │ ├── FlowchartTransform
96
+ │ ├── SequenceTransform
97
+ │ ├── ClassDiagramTransform
98
+ │ ├── StateDiagramTransform
99
+ │ ├── ErDiagramTransform
100
+ │ └── UserJourneyTransform
101
+
102
+ ├── Renderer (SVG Generation)
103
+ │ │
104
+ │ ├── Base (Abstract)
105
+ │ ├── FlowchartRenderer
106
+ │ ├── SequenceRenderer
107
+ │ ├── ClassDiagramRenderer
108
+ │ ├── StateDiagramRenderer
109
+ │ ├── ErDiagramRenderer
110
+ │ └── UserJourneyRenderer
111
+
112
+ ├── Svg (SVG Models)
113
+ │ │
114
+ │ ├── Document
115
+ │ ├── Element
116
+ │ ├── Group
117
+ │ ├── Path
118
+ │ ├── Text
119
+ │ ├── Rect
120
+ │ ├── Circle
121
+ │ ├── Line
122
+ │ ├── Polygon
123
+ │ └── Style
124
+
125
+ ├── DiagramRegistry (Type Registration)
126
+ │ │
127
+ │ ├── register(type, parser, transform, renderer)
128
+ │ └── get(type) -> handler triple
129
+
130
+ └── TextMeasurement (Dimension Calculation)
131
+
132
+ ├── measure_text(text, font_size) -> {width, height}
133
+ └── character-based approximations
134
+ ```
135
+
136
+ ## Parslet-Based Parser Architecture
137
+
138
+ **All 11 implemented diagram types use Parslet exclusively** for parsing. The
139
+ legacy lexer-based approach has been fully deprecated (see
140
+ [`lib/sirena/parser/lexer.rb`](lib/sirena/parser/lexer.rb) deprecation notice).
141
+
142
+ ### 3-Layer Parslet Architecture
143
+
144
+ Every parser follows a consistent 3-layer pattern:
145
+
146
+ ```
147
+ ┌─────────────────────────────────────────────┐
148
+ │ Layer 1: Grammar (Parslet::Parser) │
149
+ │ ──────────────────────────────────── │
150
+ │ • Defines syntax rules using Parslet DSL │
151
+ │ • Parses text into intermediate tree │
152
+ │ • Returns Hash/Array structures │
153
+ │ │
154
+ │ File: lib/sirena/parser/grammars/*.rb │
155
+ └────────────┬────────────────────────────────┘
156
+
157
+ ▼ Intermediate tree (Hash/Array)
158
+ ┌─────────────────────────────────────────────┐
159
+ │ Layer 2: Transform (Parslet::Transform) │
160
+ │ ────────────────────────────────────── │
161
+ │ • Converts intermediate tree to models │
162
+ │ • Maps patterns to Diagram objects │
163
+ │ • Returns structured Diagram models │
164
+ │ │
165
+ │ File: lib/sirena/parser/transforms/*.rb │
166
+ └────────────┬────────────────────────────────┘
167
+
168
+ ▼ Diagram models (Lutaml::Model)
169
+ ┌─────────────────────────────────────────────┐
170
+ │ Layer 3: Parser (Orchestrator) │
171
+ │ ──────────────────────────────────── │
172
+ │ • Combines Grammar + Transform │
173
+ │ • Public API: parse(input) → Diagram │
174
+ │ • Handles errors and edge cases │
175
+ │ │
176
+ │ File: lib/sirena/parser/*.rb │
177
+ └─────────────────────────────────────────────┘
178
+ ```
179
+
180
+ ### Benefits of Parslet Architecture
181
+
182
+ 1. **Superior Error Messages**: Parslet provides detailed context and line
183
+ numbers for syntax errors, making debugging much easier.
184
+
185
+ 2. **Composability**: Grammar rules can be composed and reused across diagram
186
+ types through the Common grammar module.
187
+
188
+ 3. **Maintainability**: The 3-layer separation makes code easier to understand,
189
+ test, and modify. ~80% code reduction achieved vs. lexer approach.
190
+
191
+ 4. **Declarative Syntax**: Grammar rules are declarative and self-documenting,
192
+ making the parser logic clear and concise.
193
+
194
+ 5. **Type Safety**: Transform layer produces strongly-typed Lutaml::Model
195
+ objects, catching errors early.
196
+
197
+ ### Example: Flowchart Parser
198
+
199
+ ```ruby
200
+ # Layer 1: Grammar (lib/sirena/parser/grammars/flowchart.rb)
201
+ class FlowchartGrammar < Parslet::Parser
202
+ include Common # Shared rules
203
+
204
+ rule(:flowchart) do
205
+ diagram_type >> nodes >> edges >> eof
206
+ end
207
+
208
+ rule(:node) do
209
+ identifier >> (str('[') >> text >> str(']')).as(:shape)
210
+ end
211
+ end
212
+
213
+ # Layer 2: Transform (lib/sirena/parser/transforms/flowchart.rb)
214
+ class FlowchartTransform < Parslet::Transform
215
+ rule(node: simple(:id), shape: simple(:text)) do
216
+ Diagram::Flowchart::Node.new(id: id, text: text)
217
+ end
218
+ end
219
+
220
+ # Layer 3: Parser (lib/sirena/parser/flowchart.rb)
221
+ class FlowchartParser
222
+ def initialize
223
+ @grammar = Grammars::FlowchartGrammar.new
224
+ @transform = Transforms::FlowchartTransform.new
225
+ end
226
+
227
+ def parse(input)
228
+ tree = @grammar.parse(input)
229
+ @transform.apply(tree)
230
+ end
231
+ end
232
+ ```
233
+
234
+ ### Migration Summary
235
+
236
+ All 4 legacy lexer-based parsers have been successfully migrated to Parslet:
237
+
238
+ | Parser | Migration | Code Reduction | Week |
239
+ |--------|-----------|----------------|------|
240
+ | Flowchart | ✅ Complete | ~80% | Week 7 |
241
+ | Class Diagram | ✅ Complete | ~85% | Week 8 |
242
+ | ER Diagram | ✅ Complete | ~82% | Week 9 |
243
+ | State Diagram | ✅ Complete | ~78% | Week 10 |
244
+
245
+ All remaining 7 diagram types (Sequence, User Journey, Timeline, Git Graph,
246
+ Gantt, Pie, Mindmap, Quadrant) were built with Parslet from the start.
247
+
248
+ **Result**: 100% architectural consistency across all 11 diagram types.
249
+
250
+ ## Data Flow Details
251
+
252
+ ### 1. Parsing Phase (Parslet-based)
253
+
254
+ ```
255
+ Mermaid Source
256
+
257
+
258
+ Grammar.parse
259
+ (Parslet rules)
260
+
261
+
262
+ Intermediate Tree
263
+ (Hash/Array)
264
+
265
+
266
+ Transform.apply
267
+ (Pattern matching)
268
+
269
+
270
+ Diagram Model
271
+ (Lutaml::Model)
272
+ ```
273
+
274
+ The parser produces typed diagram models that fully represent the diagram
275
+ structure. Each diagram type has its own Grammar, Transform, and Parser classes
276
+ following the consistent 3-layer pattern.
277
+
278
+ ### 2. Transform Phase
279
+
280
+ ```
281
+ Diagram Model
282
+
283
+
284
+ Transform.to_graph
285
+
286
+ ├── Convert nodes to Elkrb::Graph::Node
287
+ ├── Convert edges to Elkrb::Graph::Edge
288
+ ├── Apply text measurement for dimensions
289
+ └── Set layout options
290
+
291
+
292
+ Elkrb Graph
293
+ ```
294
+
295
+ The transform layer is responsible for:
296
+ - Converting diagram-specific structures to generic graph structures
297
+ - Calculating node dimensions based on content
298
+ - Mapping diagram relationships to graph edges
299
+ - Setting appropriate layout algorithm options
300
+
301
+ ### 3. Layout Phase
302
+
303
+ ```
304
+ Elkrb Graph
305
+
306
+
307
+ Elkrb::LayoutEngine.layout
308
+
309
+ ├── Select algorithm (layered, force, etc.)
310
+ ├── Compute node positions
311
+ ├── Route edge paths with bend points
312
+ └── Position labels
313
+
314
+
315
+ Laid Out Graph
316
+ (with x, y coordinates)
317
+ ```
318
+
319
+ This phase is fully delegated to elkrb, which handles all layout computation.
320
+
321
+ ### 4. Rendering Phase
322
+
323
+ ```
324
+ Laid Out Graph
325
+
326
+
327
+ Renderer.render
328
+
329
+ ├── Create SVG::Document
330
+ ├── Add nodes as SVG shapes
331
+ ├── Add edges as SVG paths
332
+ ├── Add labels as SVG text
333
+ └── Apply styles
334
+
335
+
336
+ SVG Model
337
+ (Lutaml::Model)
338
+
339
+
340
+ SVG.to_xml
341
+
342
+
343
+ SVG String
344
+ ```
345
+
346
+ The renderer converts positioned graph elements into SVG graphic primitives.
347
+
348
+ ## Class Responsibility Matrix
349
+
350
+ | Component | Responsibility | Dependencies |
351
+ |-----------|---------------|--------------|
352
+ | `Engine` | Orchestrate entire pipeline | Parser, Transform, Renderer |
353
+ | `Parser::Grammars::*` | Define Parslet syntax rules | Parslet, Common |
354
+ | `Parser::Transforms::*` | Transform parse trees | Parslet::Transform, Diagram models |
355
+ | `Parser::*` | Orchestrate Grammar+Transform | Grammars, Transforms |
356
+ | `Diagram::Base` | Abstract diagram model | Lutaml::Model |
357
+ | `Diagram::*` | Specific diagram structures | Diagram::Base |
358
+ | `Transform::Base` | Abstract graph converter | Elkrb |
359
+ | `Transform::*` | Diagram-specific conversion | Transform::Base, TextMeasurement |
360
+ | `Renderer::Base` | Abstract SVG renderer | Svg |
361
+ | `Renderer::*` | Diagram-specific rendering | Renderer::Base, Svg |
362
+ | `Svg::*` | SVG graphic primitives | Lutaml::Model, Moxml |
363
+ | `DiagramRegistry` | Type handler registration | None |
364
+ | `TextMeasurement` | Text dimension calculation | None |
365
+
366
+ **Note**: `Parser::Lexer` is deprecated and will be removed in v2.0.0. All
367
+ parsers now use the Parslet-based 3-layer architecture.
368
+
369
+ ## Extensibility Points
370
+
371
+ ### Adding New Diagram Types
372
+
373
+ ```ruby
374
+ # 1. Define diagram model
375
+ class Diagram::NewType < Diagram::Base
376
+ attribute :elements, :array
377
+ # ... diagram-specific attributes
378
+ end
379
+
380
+ # 2. Implement transform
381
+ class Transform::NewTypeTransform < Transform::Base
382
+ def to_graph(diagram)
383
+ # Convert diagram to Elkrb::Graph
384
+ end
385
+ end
386
+
387
+ # 3. Implement renderer
388
+ class Renderer::NewTypeRenderer < Renderer::Base
389
+ def render(graph)
390
+ # Convert graph to SVG
391
+ end
392
+ end
393
+
394
+ # 4. Register
395
+ DiagramRegistry.register(
396
+ :new_type,
397
+ parser: Parser::NewTypeGrammar,
398
+ transform: Transform::NewTypeTransform,
399
+ renderer: Renderer::NewTypeRenderer
400
+ )
401
+ ```
402
+
403
+ ### Adding New SVG Shapes
404
+
405
+ ```ruby
406
+ # Define new SVG element
407
+ class Svg::NewShape < Svg::Element
408
+ attribute :custom_attr, :string
409
+
410
+ xml do
411
+ map_element "new-shape", to: :itself
412
+ # ... XML mappings
413
+ end
414
+ end
415
+ ```
416
+
417
+ ## SVG Builder Architecture
418
+
419
+ The SVG builder implements the complete SVG 1.2 graphic model using
420
+ Lutaml::Model. This enables:
421
+
422
+ 1. **Object-Oriented SVG Construction**: Build SVG programmatically
423
+ 2. **Type Safety**: Strong typing via Lutaml::Model attributes
424
+ 3. **Serialization**: Automatic XML generation via Moxml
425
+ 4. **Composability**: Nested element structures
426
+
427
+ ```
428
+ SVG::Document
429
+
430
+ ├── viewBox: String
431
+ ├── width: Numeric
432
+ ├── height: Numeric
433
+ └── children: Array<SVG::Element>
434
+
435
+ ├── SVG::Group
436
+ │ ├── transform: String
437
+ │ ├── style: SVG::Style
438
+ │ └── children: Array<SVG::Element>
439
+
440
+ ├── SVG::Path
441
+ │ ├── d: String (path data)
442
+ │ └── style: SVG::Style
443
+
444
+ ├── SVG::Text
445
+ │ ├── x, y: Numeric
446
+ │ ├── content: String
447
+ │ └── style: SVG::Style
448
+
449
+ └── SVG::Rect, Circle, Line, Polygon...
450
+ ```
451
+
452
+ ## Text Measurement Strategy
453
+
454
+ Text dimensions are calculated using character-based approximations:
455
+
456
+ ```ruby
457
+ TextMeasurement.measure("Hello", font_size: 14)
458
+ # => { width: 35.0, height: 14.0 }
459
+
460
+ # Algorithm:
461
+ # - Average character width: font_size * 0.5
462
+ # - Height: font_size * 1.0
463
+ # - Width: char_count * avg_char_width
464
+ # - Users can override with explicit dimensions
465
+ ```
466
+
467
+ This provides reasonable estimates for layout without requiring font metrics
468
+ libraries. Elkrb uses these dimensions for node sizing.
469
+
470
+ ## Error Handling
471
+
472
+ ```
473
+ Error (Base Exception)
474
+
475
+ ├── ParseError
476
+ │ ├── LexerError
477
+ │ └── GrammarError
478
+
479
+ ├── TransformError
480
+ │ └── LayoutError
481
+
482
+ └── RenderError
483
+ └── SvgBuildError
484
+ ```
485
+
486
+ Each phase has specific error types for clear debugging and error reporting.
487
+
488
+ ## Testing Strategy
489
+
490
+ ### Unit Tests (Per Class)
491
+ - Each model class has structural tests
492
+ - Each parser component tests syntax handling
493
+ - Each transform tests graph conversion
494
+ - Each renderer tests SVG generation
495
+
496
+ ### Integration Tests
497
+ - End-to-end: Mermaid syntax → SVG output
498
+ - Verify structural correctness of SVG
499
+ - Compare with expected fixture outputs
500
+ - Manual visual verification
501
+
502
+ ### Fixtures
503
+ - Located in `spec/fixtures/`
504
+ - One subdirectory per diagram type
505
+ - Input `.mmd` files and expected outputs
506
+ - Cover edge cases and complex scenarios
507
+
508
+ ## Directory Structure
509
+
510
+ ```
511
+ sirena/
512
+ ├── lib/
513
+ │ └── sirena/
514
+ │ ├── version.rb
515
+ │ ├── engine.rb
516
+ │ ├── diagram_registry.rb
517
+ │ ├── text_measurement.rb
518
+ │ ├── parser/
519
+ │ │ ├── base.rb
520
+ │ │ ├── lexer.rb (DEPRECATED - will be removed in v2.0.0)
521
+ │ │ ├── grammars/ # Parslet grammars (Layer 1)
522
+ │ │ │ ├── common.rb # Shared grammar rules
523
+ │ │ │ ├── flowchart.rb
524
+ │ │ │ ├── class_diagram.rb
525
+ │ │ │ ├── er_diagram.rb
526
+ │ │ │ ├── state_diagram.rb
527
+ │ │ │ └── (other diagram grammars)
528
+ │ │ ├── transforms/ # Parslet transforms (Layer 2)
529
+ │ │ │ ├── flowchart.rb
530
+ │ │ │ ├── class_diagram.rb
531
+ │ │ │ ├── er_diagram.rb
532
+ │ │ │ ├── state_diagram.rb
533
+ │ │ │ └── (other diagram transforms)
534
+ │ │ ├── flowchart.rb # Parser orchestrators (Layer 3)
535
+ │ │ ├── class_diagram.rb
536
+ │ │ ├── er_diagram.rb
537
+ │ │ ├── state_diagram.rb
538
+ │ │ └── (other diagram parsers)
539
+ │ ├── diagram/
540
+ │ │ ├── base.rb
541
+ │ │ ├── flowchart.rb
542
+ │ │ ├── sequence.rb
543
+ │ │ ├── class_diagram.rb
544
+ │ │ ├── state_diagram.rb
545
+ │ │ ├── er_diagram.rb
546
+ │ │ └── user_journey.rb
547
+ │ ├── transform/
548
+ │ │ ├── base.rb
549
+ │ │ └── (diagram-specific graph transforms)
550
+ │ ├── renderer/
551
+ │ │ ├── base.rb
552
+ │ │ └── (diagram-specific renderers)
553
+ │ ├── svg/
554
+ │ │ ├── document.rb
555
+ │ │ ├── element.rb
556
+ │ │ ├── group.rb
557
+ │ │ ├── path.rb
558
+ │ │ ├── text.rb
559
+ │ │ ├── rect.rb
560
+ │ │ ├── circle.rb
561
+ │ │ ├── line.rb
562
+ │ │ ├── polygon.rb
563
+ │ │ └── style.rb
564
+ │ └── cli.rb
565
+ ├── spec/
566
+ │ ├── spec_helper.rb
567
+ │ ├── fixtures/
568
+ │ │ ├── flowchart/
569
+ │ │ ├── sequence/
570
+ │ │ ├── class_diagram/
571
+ │ │ ├── state_diagram/
572
+ │ │ ├── er_diagram/
573
+ │ │ └── user_journey/
574
+ │ └── (test files mirroring lib/)
575
+ └── examples/
576
+ ├── flowchart_example.mmd
577
+ ├── sequence_example.mmd
578
+ └── (other diagram type examples)
579
+ ```
580
+
581
+ **Note**: The 3-layer Parslet architecture (grammars/ + transforms/ + parsers)
582
+ is now standard across all diagram types.
583
+
584
+ ## Component Interaction Sequence
585
+
586
+ ### Example: Rendering a Flowchart
587
+
588
+ ```
589
+ User calls: Sirena.render(mermaid_source)
590
+
591
+
592
+ Engine.render
593
+
594
+ ┌────────────┼────────────┐
595
+ │ │ │
596
+ ▼ ▼ ▼
597
+ Parse Transform Render
598
+ │ │ │
599
+ │ │ │
600
+ Diagram. Elkrb. SVG.
601
+ Flowchart Graph Document
602
+ │ │ │
603
+ └────────────┴────────────┘
604
+
605
+
606
+ SVG XML String
607
+ ```
608
+
609
+ ### Detailed Flow
610
+
611
+ 1. **Engine receives mermaid source**
612
+ - Detects diagram type from syntax prefix
613
+ - Looks up handler in DiagramRegistry
614
+
615
+ 2. **Parser processes syntax** (Parslet 3-layer architecture)
616
+ - Grammar parses input using Parslet rules
617
+ - Transform converts intermediate tree to Diagram model
618
+ - Returns typed Diagram model (Lutaml::Model)
619
+
620
+ 3. **Transform converts to graph**
621
+ - Analyzes diagram structure
622
+ - Creates Elkrb::Graph::Node for each diagram element
623
+ - Creates Elkrb::Graph::Edge for relationships
624
+ - Applies TextMeasurement for node dimensions
625
+ - Sets layout algorithm and options
626
+
627
+ 4. **Elkrb computes layout**
628
+ - Runs selected algorithm (layered, force, etc.)
629
+ - Calculates x, y coordinates for all nodes
630
+ - Routes edges with bend points
631
+ - Positions labels
632
+
633
+ 5. **Renderer generates SVG**
634
+ - Creates SVG::Document root
635
+ - Adds SVG shapes for each node
636
+ - Adds SVG paths for each edge
637
+ - Adds SVG text for labels
638
+ - Applies styling
639
+
640
+ 6. **SVG Builder serializes**
641
+ - Traverses SVG model tree
642
+ - Generates XML using Moxml
643
+ - Returns SVG 1.2 compliant string
644
+
645
+ ## Key Design Patterns
646
+
647
+ ### Registry Pattern (DiagramRegistry)
648
+
649
+ Allows dynamic registration and retrieval of diagram type handlers without
650
+ hardcoding type checks.
651
+
652
+ ```ruby
653
+ DiagramRegistry.register(:flowchart, {
654
+ parser: Parser::FlowchartGrammar,
655
+ transform: Transform::FlowchartTransform,
656
+ renderer: Renderer::FlowchartRenderer
657
+ })
658
+
659
+ handler = DiagramRegistry.get(:flowchart)
660
+ diagram = handler[:parser].parse(source)
661
+ ```
662
+
663
+ ### Strategy Pattern (Transform/Renderer)
664
+
665
+ Each diagram type has its own transformation and rendering strategy,
666
+ implementing a common interface.
667
+
668
+ ```ruby
669
+ class Transform::Base
670
+ def to_graph(diagram)
671
+ raise NotImplementedError
672
+ end
673
+ end
674
+
675
+ class Transform::FlowchartTransform < Transform::Base
676
+ def to_graph(diagram)
677
+ # Flowchart-specific conversion
678
+ end
679
+ end
680
+ ```
681
+
682
+ ### Builder Pattern (SVG)
683
+
684
+ SVG construction uses the builder pattern through Lutaml::Model to create
685
+ complex nested structures programmatically.
686
+
687
+ ```ruby
688
+ svg = Svg::Document.new(width: 800, height: 600).tap do |doc|
689
+ doc.children << Svg::Group.new.tap do |group|
690
+ group.children << Svg::Rect.new(x: 0, y: 0, width: 100, height: 50)
691
+ group.children << Svg::Text.new(x: 50, y: 25, content: "Node")
692
+ end
693
+ end
694
+ ```
695
+
696
+ ### Template Method Pattern (Base Classes)
697
+
698
+ Base classes define the algorithm structure, with subclasses providing
699
+ specific implementations.
700
+
701
+ ```ruby
702
+ class Renderer::Base
703
+ def render(graph)
704
+ svg = create_document(graph)
705
+ render_nodes(graph, svg)
706
+ render_edges(graph, svg)
707
+ render_labels(graph, svg)
708
+ svg
709
+ end
710
+
711
+ def render_nodes(graph, svg)
712
+ raise NotImplementedError
713
+ end
714
+
715
+ # ... other template methods
716
+ end
717
+ ```
718
+
719
+ ## Dependencies and Their Roles
720
+
721
+ - **parslet (~> 2.0)**: Parser construction framework for all diagram parsers
722
+ - **lutaml-model (~> 0.7)**: Serialization framework for all models
723
+ - **elkrb**: Graph layout computation engine
724
+ - **moxml**: XML/SVG serialization via Nokogiri
725
+ - **thor**: CLI framework
726
+
727
+ ## Integration with Metanorma
728
+
729
+ Sirena produces SVG 1.2 compliant output that can be directly embedded in
730
+ Metanorma documents without post-processing. The SVG includes:
731
+
732
+ - Proper XML namespace declarations
733
+ - ViewBox for scalability
734
+ - Embedded styling (no external CSS dependencies)
735
+ - Standard-compliant path data
736
+ - UTF-8 text encoding
737
+
738
+ Integration point:
739
+
740
+ ```ruby
741
+ # In Metanorma document processing
742
+ svg_output = Sirena.render(mermaid_diagram_source)
743
+ # Embed directly in document
744
+ ```