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
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../svg/document"
4
+ require_relative "../svg/rect"
5
+ require_relative "../svg/text"
6
+ require_relative "../svg/line"
7
+ require_relative "../svg/group"
8
+
9
+ module Sirena
10
+ module Renderer
11
+ # Renders a Packet Diagram layout to SVG.
12
+ #
13
+ # The renderer converts the positioned layout structure from
14
+ # Transform::Packet into an SVG visualization showing:
15
+ # - Packet grid with bit position markers
16
+ # - Fields as labeled cells spanning correct bit ranges
17
+ # - Row-based layout (typically 32 bits per row)
18
+ # - Field labels
19
+ #
20
+ # @example Render a packet diagram
21
+ # renderer = Renderer::Packet.new(theme: my_theme)
22
+ # svg = renderer.render(layout)
23
+ class Packet < Base
24
+ # Renders the layout structure to SVG.
25
+ #
26
+ # @param layout [Hash] layout data from Transform::Packet
27
+ # @return [Svg::Document] rendered SVG document
28
+ def render(layout)
29
+ svg = create_document_from_layout(layout)
30
+
31
+ # Store layout parameters for rendering
32
+ @layout = layout
33
+ @title_offset = layout[:title_height] + layout[:title_margin]
34
+
35
+ # Render components in order
36
+ render_title(layout, svg) if layout[:title]
37
+ render_bit_markers(layout, svg)
38
+ render_grid_lines(layout, svg)
39
+ render_fields(layout, svg)
40
+
41
+ svg
42
+ end
43
+
44
+ protected
45
+
46
+ # Creates an SVG document with dimensions from layout.
47
+ #
48
+ # @param layout [Hash] layout data
49
+ # @return [Svg::Document] new SVG document
50
+ def create_document_from_layout(layout)
51
+ Svg::Document.new.tap do |doc|
52
+ doc.width = layout[:width]
53
+ doc.height = layout[:height]
54
+ doc.view_box = "0 0 #{doc.width} #{doc.height}"
55
+ end
56
+ end
57
+
58
+ # Renders the diagram title.
59
+ #
60
+ # @param layout [Hash] layout data
61
+ # @param svg [Svg::Document] SVG document
62
+ # @return [void]
63
+ def render_title(layout, svg)
64
+ return unless layout[:title]
65
+
66
+ text = Svg::Text.new.tap do |t|
67
+ t.x = layout[:width] / 2
68
+ t.y = layout[:padding] + (layout[:title_height] / 2)
69
+ t.text_anchor = "middle"
70
+ t.dominant_baseline = "middle"
71
+ t.fill = theme_color(:title_text) || "#000000"
72
+ t.font_size = (theme_typography(:font_size_large) || 16).to_s
73
+ t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
74
+ t.font_weight = "bold"
75
+ t.content = layout[:title]
76
+ end
77
+
78
+ svg.add_element(text)
79
+ end
80
+
81
+ # Renders bit position markers at the top of each column.
82
+ #
83
+ # @param layout [Hash] layout data
84
+ # @param svg [Svg::Document] SVG document
85
+ # @return [void]
86
+ def render_bit_markers(layout, svg)
87
+ bits_per_row = layout[:bits_per_row]
88
+ cell_width = layout[:cell_width]
89
+ padding = layout[:padding]
90
+
91
+ layout[:row_count].times do |row|
92
+ bits_per_row.times do |bit_in_row|
93
+ bit_number = (row * bits_per_row) + bit_in_row
94
+
95
+ x = padding + (bit_in_row * cell_width) + (cell_width / 2)
96
+ y = padding + @title_offset + (layout[:header_height] / 2) + \
97
+ (row * layout[:cell_height])
98
+
99
+ text = Svg::Text.new.tap do |t|
100
+ t.x = x
101
+ t.y = y
102
+ t.text_anchor = "middle"
103
+ t.dominant_baseline = "middle"
104
+ t.fill = theme_color(:label_text) || "#666666"
105
+ t.font_size = (theme_typography(:font_size_small) || 10).to_s
106
+ t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
107
+ t.content = bit_number.to_s
108
+ end
109
+
110
+ svg.add_element(text)
111
+ end
112
+ end
113
+ end
114
+
115
+ # Renders the packet grid lines.
116
+ #
117
+ # @param layout [Hash] layout data
118
+ # @param svg [Svg::Document] SVG document
119
+ # @return [void]
120
+ def render_grid_lines(layout, svg)
121
+ padding = layout[:padding]
122
+ cell_width = layout[:cell_width]
123
+ cell_height = layout[:cell_height]
124
+ header_height = layout[:header_height]
125
+ bits_per_row = layout[:bits_per_row]
126
+ row_count = layout[:row_count]
127
+
128
+ grid_width = bits_per_row * cell_width
129
+ grid_height = row_count * cell_height
130
+
131
+ # Vertical lines (bit boundaries)
132
+ (bits_per_row + 1).times do |i|
133
+ x = padding + (i * cell_width)
134
+ y1 = padding + @title_offset + header_height
135
+ y2 = y1 + grid_height
136
+
137
+ line = Svg::Line.new.tap do |l|
138
+ l.x1 = x
139
+ l.y1 = y1
140
+ l.x2 = x
141
+ l.y2 = y2
142
+ l.stroke = theme_color(:grid_line) || "#cccccc"
143
+ l.stroke_width = "1"
144
+ end
145
+
146
+ svg.add_element(line)
147
+ end
148
+
149
+ # Horizontal lines (row boundaries)
150
+ (row_count + 1).times do |i|
151
+ y = padding + @title_offset + header_height + (i * cell_height)
152
+ x1 = padding
153
+ x2 = x1 + grid_width
154
+
155
+ line = Svg::Line.new.tap do |l|
156
+ l.x1 = x1
157
+ l.y1 = y
158
+ l.x2 = x2
159
+ l.y2 = y
160
+ l.stroke = theme_color(:grid_line) || "#cccccc"
161
+ l.stroke_width = "1"
162
+ end
163
+
164
+ svg.add_element(line)
165
+ end
166
+ end
167
+
168
+ # Renders all packet fields.
169
+ #
170
+ # @param layout [Hash] layout data
171
+ # @param svg [Svg::Document] SVG document
172
+ # @return [void]
173
+ def render_fields(layout, svg)
174
+ layout[:fields].each do |field|
175
+ render_field(field, layout, svg)
176
+ end
177
+ end
178
+
179
+ # Renders a single packet field.
180
+ #
181
+ # @param field [Hash] field data
182
+ # @param layout [Hash] layout data
183
+ # @param svg [Svg::Document] SVG document
184
+ # @return [void]
185
+ def render_field(field, layout, svg)
186
+ # Adjust y position for title offset
187
+ y = field[:y] + @title_offset
188
+
189
+ # Draw field background
190
+ rect = Svg::Rect.new.tap do |r|
191
+ r.x = field[:x]
192
+ r.y = y
193
+ r.width = field[:width]
194
+ r.height = field[:height]
195
+ r.fill = theme_color(:field_background) || "#e0f2fe"
196
+ r.stroke = theme_color(:field_border) || "#0284c7"
197
+ r.stroke_width = "1.5"
198
+ end
199
+
200
+ svg.add_element(rect)
201
+
202
+ # Draw field label
203
+ label_x = field[:x] + (field[:width] / 2)
204
+ label_y = y + (field[:height] / 2)
205
+
206
+ text = Svg::Text.new.tap do |t|
207
+ t.x = label_x
208
+ t.y = label_y
209
+ t.text_anchor = "middle"
210
+ t.dominant_baseline = "middle"
211
+ t.fill = theme_color(:field_text) || "#000000"
212
+ t.font_size = (theme_typography(:font_size_normal) || 12).to_s
213
+ t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
214
+ t.content = field[:label]
215
+ end
216
+
217
+ svg.add_element(text)
218
+
219
+ # Add bit range annotation if field is large enough
220
+ if field[:width] > 100
221
+ range_text = "#{field[:bit_start]}-#{field[:bit_end]}"
222
+
223
+ range = Svg::Text.new.tap do |t|
224
+ t.x = label_x
225
+ t.y = label_y + 16
226
+ t.text_anchor = "middle"
227
+ t.dominant_baseline = "middle"
228
+ t.fill = theme_color(:label_text) || "#666666"
229
+ t.font_size = (theme_typography(:font_size_small) || 9).to_s
230
+ t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
231
+ t.content = range_text
232
+ end
233
+
234
+ svg.add_element(range)
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,235 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../svg/document'
5
+ require_relative '../svg/circle'
6
+ require_relative '../svg/path'
7
+ require_relative '../svg/text'
8
+ require_relative '../svg/group'
9
+
10
+ module Sirena
11
+ module Renderer
12
+ # Pie chart renderer for converting pie diagrams to SVG.
13
+ #
14
+ # Converts a Pie diagram model into SVG with calculated slice angles,
15
+ # colors from theme, labels, and optional data values.
16
+ #
17
+ # @example Render a pie chart
18
+ # renderer = PieRenderer.new
19
+ # svg = renderer.render(pie_diagram)
20
+ class PieRenderer < Base
21
+ # Pie chart dimensions
22
+ PIE_RADIUS = 150
23
+ PIE_CENTER_X = 250
24
+ PIE_CENTER_Y = 200
25
+ LABEL_OFFSET = 180 # Distance from center for labels
26
+ TITLE_Y = 40
27
+
28
+ # Default color palette for slices
29
+ DEFAULT_COLORS = [
30
+ '#4472C4', '#ED7D31', '#A5A5A5', '#FFC000',
31
+ '#5B9BD5', '#70AD47', '#264478', '#9E480E',
32
+ '#636363', '#997300', '#255E91', '#43682B'
33
+ ].freeze
34
+
35
+ # Renders a pie chart diagram to SVG.
36
+ #
37
+ # @param graph [Hash] the pie chart graph structure from transform
38
+ # @return [Svg::Document] the rendered SVG document
39
+ def render(graph)
40
+ svg = create_document_for_pie(graph)
41
+
42
+ # Render title if present
43
+ render_title(graph, svg) if graph[:title]
44
+
45
+ # Render pie slices
46
+ render_slices(graph, svg)
47
+
48
+ # Render labels
49
+ render_labels(graph, svg)
50
+
51
+ svg
52
+ end
53
+
54
+ protected
55
+
56
+ def create_document_for_pie(graph)
57
+ width = calculate_width_for_pie(graph)
58
+ height = calculate_height_for_pie(graph)
59
+
60
+ Svg::Document.new.tap do |doc|
61
+ doc.width = width
62
+ doc.height = height
63
+ doc.view_box = "0 0 #{width} #{height}"
64
+ end
65
+ end
66
+
67
+ def calculate_width_for_pie(_graph)
68
+ 500 # Fixed width for pie chart
69
+ end
70
+
71
+ def calculate_height_for_pie(graph)
72
+ base_height = 400
73
+ base_height += 60 if graph[:title] # Add space for title
74
+ base_height
75
+ end
76
+
77
+ def render_title(graph, svg)
78
+ title_text = Svg::Text.new.tap do |t|
79
+ t.x = PIE_CENTER_X
80
+ t.y = TITLE_Y
81
+ t.content = graph[:title]
82
+ t.fill = theme_color(:label_text) || '#000000'
83
+ t.font_family = theme_typography(:font_family) ||
84
+ 'Arial, sans-serif'
85
+ t.font_size = (theme_typography(:font_size_large) || 18).to_s
86
+ t.text_anchor = 'middle'
87
+ t.font_weight = 'bold'
88
+ end
89
+ svg << title_text
90
+ end
91
+
92
+ def render_slices(graph, svg)
93
+ slices = graph[:slices] || []
94
+ return if slices.empty?
95
+
96
+ start_angle = -90.0 # Start at top (12 o'clock)
97
+
98
+ slices.each_with_index do |slice, index|
99
+ angle = slice[:angle]
100
+ end_angle = start_angle + angle
101
+
102
+ # Get color for this slice
103
+ color = get_slice_color(index)
104
+
105
+ # Render the slice
106
+ render_slice(
107
+ start_angle,
108
+ end_angle,
109
+ color,
110
+ svg,
111
+ index
112
+ )
113
+
114
+ start_angle = end_angle
115
+ end
116
+ end
117
+
118
+ def render_slice(start_angle, end_angle, color, svg, index)
119
+ # Create SVG path for pie slice
120
+ path_data = create_pie_slice_path(start_angle, end_angle)
121
+
122
+ path = Svg::Path.new.tap do |p|
123
+ p.d = path_data
124
+ p.fill = color
125
+ p.stroke = theme_color(:node_stroke) || '#ffffff'
126
+ p.stroke_width = '2'
127
+ p.id = "slice-#{index}"
128
+ end
129
+
130
+ svg << path
131
+ end
132
+
133
+ def create_pie_slice_path(start_angle, end_angle)
134
+ # Convert angles to radians
135
+ start_rad = degrees_to_radians(start_angle)
136
+ end_rad = degrees_to_radians(end_angle)
137
+
138
+ # Calculate start and end points on the circle
139
+ start_x = PIE_CENTER_X + PIE_RADIUS * Math.cos(start_rad)
140
+ start_y = PIE_CENTER_Y + PIE_RADIUS * Math.sin(start_rad)
141
+ end_x = PIE_CENTER_X + PIE_RADIUS * Math.cos(end_rad)
142
+ end_y = PIE_CENTER_Y + PIE_RADIUS * Math.sin(end_rad)
143
+
144
+ # Determine if we need a large arc
145
+ large_arc = (end_angle - start_angle) > 180 ? 1 : 0
146
+
147
+ # Create path: Move to center, line to start, arc to end, close path
148
+ [
149
+ "M #{PIE_CENTER_X} #{PIE_CENTER_Y}",
150
+ "L #{start_x} #{start_y}",
151
+ "A #{PIE_RADIUS} #{PIE_RADIUS} 0 #{large_arc} 1 #{end_x} #{end_y}",
152
+ 'Z'
153
+ ].join(' ')
154
+ end
155
+
156
+ def render_labels(graph, svg)
157
+ slices = graph[:slices] || []
158
+ return if slices.empty?
159
+
160
+ start_angle = -90.0 # Start at top
161
+
162
+ slices.each do |slice|
163
+ angle = slice[:angle]
164
+ mid_angle = start_angle + (angle / 2.0)
165
+
166
+ # Calculate label position
167
+ label_x, label_y = calculate_label_position(mid_angle)
168
+
169
+ # Render label
170
+ render_label(
171
+ slice[:label],
172
+ slice[:percentage],
173
+ label_x,
174
+ label_y,
175
+ svg,
176
+ graph[:show_data]
177
+ )
178
+
179
+ start_angle += angle
180
+ end
181
+ end
182
+
183
+ def calculate_label_position(angle)
184
+ rad = degrees_to_radians(angle)
185
+ x = PIE_CENTER_X + LABEL_OFFSET * Math.cos(rad)
186
+ y = PIE_CENTER_Y + LABEL_OFFSET * Math.sin(rad)
187
+ [x, y]
188
+ end
189
+
190
+ def render_label(label, percentage, x, y, svg, show_data)
191
+ # Format label text
192
+ label_text = label
193
+ label_text += ": #{percentage.round(1)}%" if show_data
194
+
195
+ text = Svg::Text.new.tap do |t|
196
+ t.x = x
197
+ t.y = y
198
+ t.content = label_text
199
+ t.fill = theme_color(:label_text) || '#000000'
200
+ t.font_family = theme_typography(:font_family) ||
201
+ 'Arial, sans-serif'
202
+ t.font_size = (theme_typography(:font_size_small) || 12).to_s
203
+ t.text_anchor = 'middle'
204
+ t.dominant_baseline = 'middle'
205
+ end
206
+
207
+ svg << text
208
+ end
209
+
210
+ def get_slice_color(index)
211
+ # Use theme colors if available, otherwise use default palette
212
+ if theme && theme.colors
213
+ # Try to get pie-specific colors from theme
214
+ color_key = "pie_slice_#{index}".to_sym
215
+ theme_color(color_key) || DEFAULT_COLORS[index % DEFAULT_COLORS.length]
216
+ else
217
+ DEFAULT_COLORS[index % DEFAULT_COLORS.length]
218
+ end
219
+ end
220
+
221
+ def degrees_to_radians(degrees)
222
+ degrees * Math::PI / 180.0
223
+ end
224
+
225
+ # Override base class methods for pie chart specific calculations
226
+ def calculate_width(_graph)
227
+ 500
228
+ end
229
+
230
+ def calculate_height(_graph)
231
+ 400
232
+ end
233
+ end
234
+ end
235
+ end