etassia 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 (727) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +140 -0
  3. data/LICENSE +25 -0
  4. data/README.adoc +436 -0
  5. data/exe/etassia +7 -0
  6. data/lib/etassia/cli.rb +187 -0
  7. data/lib/etassia/commands/batch.rb +210 -0
  8. data/lib/etassia/commands/notation_loader.rb +22 -0
  9. data/lib/etassia/commands/render.rb +98 -0
  10. data/lib/etassia/commands/types.rb +33 -0
  11. data/lib/etassia/commands/version.rb +24 -0
  12. data/lib/etassia/corpus_type_names.rb +52 -0
  13. data/lib/etassia/diagram/architecture.rb +108 -0
  14. data/lib/etassia/diagram/base.rb +61 -0
  15. data/lib/etassia/diagram/block.rb +102 -0
  16. data/lib/etassia/diagram/c4.rb +337 -0
  17. data/lib/etassia/diagram/class_diagram.rb +466 -0
  18. data/lib/etassia/diagram/class_namespace.rb +17 -0
  19. data/lib/etassia/diagram/class_note.rb +17 -0
  20. data/lib/etassia/diagram/containment.rb +107 -0
  21. data/lib/etassia/diagram/er_diagram.rb +317 -0
  22. data/lib/etassia/diagram/error.rb +38 -0
  23. data/lib/etassia/diagram/flowchart.rb +419 -0
  24. data/lib/etassia/diagram/flowchart_label_text.rb +63 -0
  25. data/lib/etassia/diagram/gantt.rb +94 -0
  26. data/lib/etassia/diagram/generic_text.rb +30 -0
  27. data/lib/etassia/diagram/git_graph.rb +57 -0
  28. data/lib/etassia/diagram/info.rb +38 -0
  29. data/lib/etassia/diagram/kanban.rb +198 -0
  30. data/lib/etassia/diagram/mindmap.rb +71 -0
  31. data/lib/etassia/diagram/packet.rb +93 -0
  32. data/lib/etassia/diagram/pie.rb +115 -0
  33. data/lib/etassia/diagram/quadrant.rb +135 -0
  34. data/lib/etassia/diagram/radar.rb +65 -0
  35. data/lib/etassia/diagram/requirement.rb +168 -0
  36. data/lib/etassia/diagram/sankey.rb +217 -0
  37. data/lib/etassia/diagram/sequence.rb +299 -0
  38. data/lib/etassia/diagram/sequence_box.rb +18 -0
  39. data/lib/etassia/diagram/sequence_frame.rb +37 -0
  40. data/lib/etassia/diagram/sequence_frame_section.rb +23 -0
  41. data/lib/etassia/diagram/sequence_text.rb +69 -0
  42. data/lib/etassia/diagram/sequence_wrap.rb +23 -0
  43. data/lib/etassia/diagram/state_diagram.rb +282 -0
  44. data/lib/etassia/diagram/timeline.rb +183 -0
  45. data/lib/etassia/diagram/treemap.rb +95 -0
  46. data/lib/etassia/diagram/user_journey.rb +153 -0
  47. data/lib/etassia/diagram/xy_chart.rb +90 -0
  48. data/lib/etassia/diagram.rb +8 -0
  49. data/lib/etassia/diagram_registry.rb +34 -0
  50. data/lib/etassia/engine.rb +334 -0
  51. data/lib/etassia/error/diagram_type_error.rb +10 -0
  52. data/lib/etassia/error/layout_error.rb +12 -0
  53. data/lib/etassia/error/notation_registration_error.rb +10 -0
  54. data/lib/etassia/error/parse_error.rb +11 -0
  55. data/lib/etassia/error/pipeline_error.rb +10 -0
  56. data/lib/etassia/error/render_error.rb +12 -0
  57. data/lib/etassia/error/score_error.rb +12 -0
  58. data/lib/etassia/error.rb +10 -0
  59. data/lib/etassia/error_report.rb +69 -0
  60. data/lib/etassia/ir/data.rb +39 -0
  61. data/lib/etassia/ir/data_value.rb +35 -0
  62. data/lib/etassia/ir/dimension.rb +10 -0
  63. data/lib/etassia/ir/edge.rb +34 -0
  64. data/lib/etassia/ir/graph.rb +28 -0
  65. data/lib/etassia/ir/integrity.rb +80 -0
  66. data/lib/etassia/ir/item.rb +46 -0
  67. data/lib/etassia/ir/model.rb +56 -0
  68. data/lib/etassia/ir/node.rb +10 -0
  69. data/lib/etassia/ir/placement.rb +47 -0
  70. data/lib/etassia/ir/prepositioned.rb +25 -0
  71. data/lib/etassia/ir/prepositioned_item.rb +26 -0
  72. data/lib/etassia/ir/property_set.rb +43 -0
  73. data/lib/etassia/ir/scalar.rb +25 -0
  74. data/lib/etassia/ir/series.rb +10 -0
  75. data/lib/etassia/ir.rb +17 -0
  76. data/lib/etassia/js_math.rb +511 -0
  77. data/lib/etassia/js_number.rb +64 -0
  78. data/lib/etassia/layout/architecture.rb +1472 -0
  79. data/lib/etassia/layout/base.rb +276 -0
  80. data/lib/etassia/layout/block.rb +738 -0
  81. data/lib/etassia/layout/c4.rb +876 -0
  82. data/lib/etassia/layout/c4_boundary_metrics.rb +35 -0
  83. data/lib/etassia/layout/c4_bounds.rb +106 -0
  84. data/lib/etassia/layout/c4_intersection.rb +77 -0
  85. data/lib/etassia/layout/c4_placement.rb +135 -0
  86. data/lib/etassia/layout/c4_shape_metrics.rb +97 -0
  87. data/lib/etassia/layout/c4_shape_outline.rb +67 -0
  88. data/lib/etassia/layout/c4_stereotype.rb +30 -0
  89. data/lib/etassia/layout/c4_text.rb +54 -0
  90. data/lib/etassia/layout/class_cell_placement.rb +148 -0
  91. data/lib/etassia/layout/class_diagram.rb +1116 -0
  92. data/lib/etassia/layout/class_diagram_namespaces.rb +26 -0
  93. data/lib/etassia/layout/class_diagram_notes.rb +26 -0
  94. data/lib/etassia/layout/class_name_advances.rb +31 -0
  95. data/lib/etassia/layout/class_namespace_box.rb +21 -0
  96. data/lib/etassia/layout/class_namespace_nodes.rb +197 -0
  97. data/lib/etassia/layout/class_namespace_placement.rb +71 -0
  98. data/lib/etassia/layout/class_note.rb +44 -0
  99. data/lib/etassia/layout/class_note_nodes.rb +84 -0
  100. data/lib/etassia/layout/elk_notices.rb +48 -0
  101. data/lib/etassia/layout/elk_placement.rb +197 -0
  102. data/lib/etassia/layout/er_diagram.rb +1002 -0
  103. data/lib/etassia/layout/er_diagram_adjacency.rb +20 -0
  104. data/lib/etassia/layout/er_diagram_alignment.rb +72 -0
  105. data/lib/etassia/layout/er_diagram_compaction.rb +85 -0
  106. data/lib/etassia/layout/er_diagram_conflicts.rb +70 -0
  107. data/lib/etassia/layout/er_diagram_coordinates.rb +104 -0
  108. data/lib/etassia/layout/er_diagram_entity_size.rb +82 -0
  109. data/lib/etassia/layout/er_diagram_layering.rb +109 -0
  110. data/lib/etassia/layout/er_diagram_ordering.rb +143 -0
  111. data/lib/etassia/layout/er_diagram_placement.rb +163 -0
  112. data/lib/etassia/layout/er_diagram_placement_node.rb +22 -0
  113. data/lib/etassia/layout/error.rb +133 -0
  114. data/lib/etassia/layout/flowchart/brandes_koepf.rb +208 -0
  115. data/lib/etassia/layout/flowchart/cluster_reach.rb +70 -0
  116. data/lib/etassia/layout/flowchart/component_packing.rb +129 -0
  117. data/lib/etassia/layout/flowchart/directed_placement.rb +428 -0
  118. data/lib/etassia/layout/flowchart/edge_chains.rb +111 -0
  119. data/lib/etassia/layout/flowchart/edge_order.rb +34 -0
  120. data/lib/etassia/layout/flowchart/edge_router.rb +533 -0
  121. data/lib/etassia/layout/flowchart/label_backdrop_geometry.rb +62 -0
  122. data/lib/etassia/layout/flowchart/layer_spacing.rb +85 -0
  123. data/lib/etassia/layout/flowchart/level_edges.rb +74 -0
  124. data/lib/etassia/layout/flowchart/level_frame.rb +47 -0
  125. data/lib/etassia/layout/flowchart/minimum_rank_spacing.rb +74 -0
  126. data/lib/etassia/layout/flowchart/row_centering.rb +229 -0
  127. data/lib/etassia/layout/flowchart.rb +1290 -0
  128. data/lib/etassia/layout/gantt.rb +814 -0
  129. data/lib/etassia/layout/gantt_duration.rb +49 -0
  130. data/lib/etassia/layout/gantt_exclusions.rb +74 -0
  131. data/lib/etassia/layout/gantt_ticks.rb +165 -0
  132. data/lib/etassia/layout/git_graph.rb +1075 -0
  133. data/lib/etassia/layout/grid.rb +165 -0
  134. data/lib/etassia/layout/info.rb +113 -0
  135. data/lib/etassia/layout/kanban.rb +452 -0
  136. data/lib/etassia/layout/kanban_card_text.rb +95 -0
  137. data/lib/etassia/layout/mindmap.rb +425 -0
  138. data/lib/etassia/layout/mindmap_cose.rb +81 -0
  139. data/lib/etassia/layout/mindmap_cose_body.rb +93 -0
  140. data/lib/etassia/layout/mindmap_cose_geometry.rb +190 -0
  141. data/lib/etassia/layout/mindmap_cose_grid.rb +96 -0
  142. data/lib/etassia/layout/mindmap_cose_link.rb +42 -0
  143. data/lib/etassia/layout/mindmap_cose_relaxation.rb +166 -0
  144. data/lib/etassia/layout/mindmap_cose_seed.rb +93 -0
  145. data/lib/etassia/layout/mindmap_cose_wedge.rb +37 -0
  146. data/lib/etassia/layout/mindmap_node_size.rb +130 -0
  147. data/lib/etassia/layout/packet.rb +285 -0
  148. data/lib/etassia/layout/pie.rb +305 -0
  149. data/lib/etassia/layout/pie_legend.rb +64 -0
  150. data/lib/etassia/layout/pie_legend_entry.rb +21 -0
  151. data/lib/etassia/layout/quadrant.rb +402 -0
  152. data/lib/etassia/layout/radar.rb +442 -0
  153. data/lib/etassia/layout/requirement.rb +765 -0
  154. data/lib/etassia/layout/sankey.rb +501 -0
  155. data/lib/etassia/layout/scene.rb +18 -0
  156. data/lib/etassia/layout/sequence/actor_gaps.rb +151 -0
  157. data/lib/etassia/layout/sequence/box_shape.rb +23 -0
  158. data/lib/etassia/layout/sequence/creations.rb +73 -0
  159. data/lib/etassia/layout/sequence/frame_divider.rb +22 -0
  160. data/lib/etassia/layout/sequence/frame_placement.rb +424 -0
  161. data/lib/etassia/layout/sequence/frame_reader.rb +75 -0
  162. data/lib/etassia/layout/sequence/frame_shape.rb +45 -0
  163. data/lib/etassia/layout/sequence/geometry.rb +24 -0
  164. data/lib/etassia/layout/sequence/message_rows.rb +165 -0
  165. data/lib/etassia/layout/sequence/note.rb +16 -0
  166. data/lib/etassia/layout/sequence/note_box.rb +114 -0
  167. data/lib/etassia/layout/sequence/note_placement.rb +223 -0
  168. data/lib/etassia/layout/sequence/text_width.rb +69 -0
  169. data/lib/etassia/layout/sequence/text_wrap.rb +93 -0
  170. data/lib/etassia/layout/sequence.rb +936 -0
  171. data/lib/etassia/layout/state_diagram.rb +1277 -0
  172. data/lib/etassia/layout/timeline.rb +165 -0
  173. data/lib/etassia/layout/timeline_card.rb +142 -0
  174. data/lib/etassia/layout/timeline_flow.rb +164 -0
  175. data/lib/etassia/layout/timeline_line.rb +33 -0
  176. data/lib/etassia/layout/timeline_rounding.rb +19 -0
  177. data/lib/etassia/layout/timeline_text.rb +105 -0
  178. data/lib/etassia/layout/treemap.rb +303 -0
  179. data/lib/etassia/layout/treemap_placement.rb +52 -0
  180. data/lib/etassia/layout/treemap_squarify.rb +108 -0
  181. data/lib/etassia/layout/user_journey.rb +300 -0
  182. data/lib/etassia/layout/user_journey_face.rb +33 -0
  183. data/lib/etassia/layout/user_journey_face_geometry.rb +80 -0
  184. data/lib/etassia/layout/user_journey_geometry.rb +336 -0
  185. data/lib/etassia/layout/user_journey_legend.rb +84 -0
  186. data/lib/etassia/layout/xy_chart.rb +620 -0
  187. data/lib/etassia/layout.rb +19 -0
  188. data/lib/etassia/lint_debt_scoreboard.rb +219 -0
  189. data/lib/etassia/markdown_text/emphasis_simulator.rb +373 -0
  190. data/lib/etassia/markdown_text.rb +393 -0
  191. data/lib/etassia/notation/builtin.rb +39 -0
  192. data/lib/etassia/notation/entry.rb +15 -0
  193. data/lib/etassia/notation/mermaid/class_diagram_config.rb +42 -0
  194. data/lib/etassia/notation/mermaid/git_graph_config.rb +46 -0
  195. data/lib/etassia/notation/mermaid/ir_adapter.rb +27 -0
  196. data/lib/etassia/notation/mermaid/ir_adapters/architecture.rb +196 -0
  197. data/lib/etassia/notation/mermaid/ir_adapters/block.rb +138 -0
  198. data/lib/etassia/notation/mermaid/ir_adapters/c4.rb +258 -0
  199. data/lib/etassia/notation/mermaid/ir_adapters/class_diagram.rb +257 -0
  200. data/lib/etassia/notation/mermaid/ir_adapters/er_diagram.rb +220 -0
  201. data/lib/etassia/notation/mermaid/ir_adapters/error.rb +32 -0
  202. data/lib/etassia/notation/mermaid/ir_adapters/flowchart.rb +208 -0
  203. data/lib/etassia/notation/mermaid/ir_adapters/gantt.rb +236 -0
  204. data/lib/etassia/notation/mermaid/ir_adapters/git_graph.rb +186 -0
  205. data/lib/etassia/notation/mermaid/ir_adapters/info.rb +34 -0
  206. data/lib/etassia/notation/mermaid/ir_adapters/kanban.rb +110 -0
  207. data/lib/etassia/notation/mermaid/ir_adapters/mindmap.rb +76 -0
  208. data/lib/etassia/notation/mermaid/ir_adapters/packet.rb +51 -0
  209. data/lib/etassia/notation/mermaid/ir_adapters/pie.rb +71 -0
  210. data/lib/etassia/notation/mermaid/ir_adapters/quadrant.rb +116 -0
  211. data/lib/etassia/notation/mermaid/ir_adapters/radar.rb +142 -0
  212. data/lib/etassia/notation/mermaid/ir_adapters/requirement.rb +237 -0
  213. data/lib/etassia/notation/mermaid/ir_adapters/sankey.rb +74 -0
  214. data/lib/etassia/notation/mermaid/ir_adapters/sequence.rb +304 -0
  215. data/lib/etassia/notation/mermaid/ir_adapters/state_diagram.rb +225 -0
  216. data/lib/etassia/notation/mermaid/ir_adapters/timeline.rb +93 -0
  217. data/lib/etassia/notation/mermaid/ir_adapters/treemap.rb +143 -0
  218. data/lib/etassia/notation/mermaid/ir_adapters/user_journey.rb +127 -0
  219. data/lib/etassia/notation/mermaid/ir_adapters/xychart.rb +131 -0
  220. data/lib/etassia/notation/mermaid.rb +399 -0
  221. data/lib/etassia/notation/parsed.rb +31 -0
  222. data/lib/etassia/notation/plantuml/activity/arrow_head.rb +45 -0
  223. data/lib/etassia/notation/plantuml/activity/arrow_style.rb +53 -0
  224. data/lib/etassia/notation/plantuml/activity/bar_columns.rb +75 -0
  225. data/lib/etassia/notation/plantuml/activity/box.rb +60 -0
  226. data/lib/etassia/notation/plantuml/activity/builder.rb +77 -0
  227. data/lib/etassia/notation/plantuml/activity/canvas.rb +117 -0
  228. data/lib/etassia/notation/plantuml/activity/column_centres.rb +151 -0
  229. data/lib/etassia/notation/plantuml/activity/columns.rb +224 -0
  230. data/lib/etassia/notation/plantuml/activity/diagram.rb +35 -0
  231. data/lib/etassia/notation/plantuml/activity/edge.rb +35 -0
  232. data/lib/etassia/notation/plantuml/activity/edge_marks.rb +68 -0
  233. data/lib/etassia/notation/plantuml/activity/exits.rb +61 -0
  234. data/lib/etassia/notation/plantuml/activity/frame_marks.rb +116 -0
  235. data/lib/etassia/notation/plantuml/activity/lane_steps.rb +82 -0
  236. data/lib/etassia/notation/plantuml/activity/lane_titles.rb +46 -0
  237. data/lib/etassia/notation/plantuml/activity/layout.rb +50 -0
  238. data/lib/etassia/notation/plantuml/activity/legacy_parser.rb +181 -0
  239. data/lib/etassia/notation/plantuml/activity/legacy_row_gaps.rb +65 -0
  240. data/lib/etassia/notation/plantuml/activity/mark.rb +23 -0
  241. data/lib/etassia/notation/plantuml/activity/marks.rb +62 -0
  242. data/lib/etassia/notation/plantuml/activity/metrics.rb +169 -0
  243. data/lib/etassia/notation/plantuml/activity/new_parser/blocks.rb +166 -0
  244. data/lib/etassia/notation/plantuml/activity/new_parser.rb +177 -0
  245. data/lib/etassia/notation/plantuml/activity/node.rb +50 -0
  246. data/lib/etassia/notation/plantuml/activity/node_marks.rb +174 -0
  247. data/lib/etassia/notation/plantuml/activity/note.rb +27 -0
  248. data/lib/etassia/notation/plantuml/activity/parser.rb +51 -0
  249. data/lib/etassia/notation/plantuml/activity/placement.rb +327 -0
  250. data/lib/etassia/notation/plantuml/activity/ranking.rb +142 -0
  251. data/lib/etassia/notation/plantuml/activity/reach.rb +58 -0
  252. data/lib/etassia/notation/plantuml/activity/renderer.rb +220 -0
  253. data/lib/etassia/notation/plantuml/activity/route.rb +22 -0
  254. data/lib/etassia/notation/plantuml/activity/router.rb +308 -0
  255. data/lib/etassia/notation/plantuml/activity/row_gaps.rb +105 -0
  256. data/lib/etassia/notation/plantuml/activity/row_spread.rb +59 -0
  257. data/lib/etassia/notation/plantuml/activity/scene.rb +20 -0
  258. data/lib/etassia/notation/plantuml/activity/text.rb +36 -0
  259. data/lib/etassia/notation/plantuml/activity.rb +50 -0
  260. data/lib/etassia/notation/plantuml/arrow.rb +135 -0
  261. data/lib/etassia/notation/plantuml/caption.rb +50 -0
  262. data/lib/etassia/notation/plantuml/caption_layout.rb +97 -0
  263. data/lib/etassia/notation/plantuml/caption_rows.rb +67 -0
  264. data/lib/etassia/notation/plantuml/class_header.rb +178 -0
  265. data/lib/etassia/notation/plantuml/class_name.rb +51 -0
  266. data/lib/etassia/notation/plantuml/component.rb +37 -0
  267. data/lib/etassia/notation/plantuml/creole.rb +278 -0
  268. data/lib/etassia/notation/plantuml/creole_icons.rb +33 -0
  269. data/lib/etassia/notation/plantuml/diagram.rb +60 -0
  270. data/lib/etassia/notation/plantuml/diagram_builder.rb +442 -0
  271. data/lib/etassia/notation/plantuml/diagram_name.rb +51 -0
  272. data/lib/etassia/notation/plantuml/directives.rb +33 -0
  273. data/lib/etassia/notation/plantuml/drawing/box.rb +28 -0
  274. data/lib/etassia/notation/plantuml/drawing/face.rb +39 -0
  275. data/lib/etassia/notation/plantuml/drawing/pen.rb +105 -0
  276. data/lib/etassia/notation/plantuml/drawing/renderer.rb +36 -0
  277. data/lib/etassia/notation/plantuml/drawing/scene.rb +28 -0
  278. data/lib/etassia/notation/plantuml/drawing.rb +19 -0
  279. data/lib/etassia/notation/plantuml/ebnf/blocks.rb +272 -0
  280. data/lib/etassia/notation/plantuml/ebnf/layout.rb +117 -0
  281. data/lib/etassia/notation/plantuml/ebnf/model.rb +22 -0
  282. data/lib/etassia/notation/plantuml/ebnf/reader.rb +131 -0
  283. data/lib/etassia/notation/plantuml/ebnf.rb +34 -0
  284. data/lib/etassia/notation/plantuml/end_texts.rb +78 -0
  285. data/lib/etassia/notation/plantuml/entity.rb +54 -0
  286. data/lib/etassia/notation/plantuml/further.rb +38 -0
  287. data/lib/etassia/notation/plantuml/gantt/layout.rb +269 -0
  288. data/lib/etassia/notation/plantuml/gantt/reader.rb +210 -0
  289. data/lib/etassia/notation/plantuml/gantt/renderer.rb +81 -0
  290. data/lib/etassia/notation/plantuml/gantt.rb +34 -0
  291. data/lib/etassia/notation/plantuml/include/blocks.rb +66 -0
  292. data/lib/etassia/notation/plantuml/include/finder.rb +61 -0
  293. data/lib/etassia/notation/plantuml/include.rb +78 -0
  294. data/lib/etassia/notation/plantuml/ir_adapter/annotations.rb +40 -0
  295. data/lib/etassia/notation/plantuml/ir_adapter/links.rb +91 -0
  296. data/lib/etassia/notation/plantuml/ir_adapter/sink.rb +49 -0
  297. data/lib/etassia/notation/plantuml/ir_adapter/structure.rb +86 -0
  298. data/lib/etassia/notation/plantuml/ir_adapter.rb +32 -0
  299. data/lib/etassia/notation/plantuml/ir_reader/annotations.rb +51 -0
  300. data/lib/etassia/notation/plantuml/ir_reader/index.rb +49 -0
  301. data/lib/etassia/notation/plantuml/ir_reader/links.rb +133 -0
  302. data/lib/etassia/notation/plantuml/ir_reader/structure.rb +83 -0
  303. data/lib/etassia/notation/plantuml/ir_reader.rb +42 -0
  304. data/lib/etassia/notation/plantuml/junction.rb +20 -0
  305. data/lib/etassia/notation/plantuml/klass.rb +41 -0
  306. data/lib/etassia/notation/plantuml/label_clearance.rb +67 -0
  307. data/lib/etassia/notation/plantuml/layout.rb +2806 -0
  308. data/lib/etassia/notation/plantuml/member.rb +37 -0
  309. data/lib/etassia/notation/plantuml/member_icon.rb +87 -0
  310. data/lib/etassia/notation/plantuml/member_icon_elements.rb +91 -0
  311. data/lib/etassia/notation/plantuml/member_pitch.rb +76 -0
  312. data/lib/etassia/notation/plantuml/member_rule.rb +48 -0
  313. data/lib/etassia/notation/plantuml/network/grid.rb +170 -0
  314. data/lib/etassia/notation/plantuml/network/layout.rb +158 -0
  315. data/lib/etassia/notation/plantuml/network/model.rb +22 -0
  316. data/lib/etassia/notation/plantuml/network/placement.rb +64 -0
  317. data/lib/etassia/notation/plantuml/network/reader.rb +126 -0
  318. data/lib/etassia/notation/plantuml/network/shapes.rb +109 -0
  319. data/lib/etassia/notation/plantuml/network.rb +37 -0
  320. data/lib/etassia/notation/plantuml/node_graph/archimate.rb +49 -0
  321. data/lib/etassia/notation/plantuml/node_graph/box_reader.rb +86 -0
  322. data/lib/etassia/notation/plantuml/node_graph/cloud.rb +73 -0
  323. data/lib/etassia/notation/plantuml/node_graph/crows_foot.rb +85 -0
  324. data/lib/etassia/notation/plantuml/node_graph/curve.rb +66 -0
  325. data/lib/etassia/notation/plantuml/node_graph/data_reader.rb +77 -0
  326. data/lib/etassia/notation/plantuml/node_graph/data_shape.rb +39 -0
  327. data/lib/etassia/notation/plantuml/node_graph/detour.rb +98 -0
  328. data/lib/etassia/notation/plantuml/node_graph/edge.rb +26 -0
  329. data/lib/etassia/notation/plantuml/node_graph/entity_metrics.rb +49 -0
  330. data/lib/etassia/notation/plantuml/node_graph/entity_reader.rb +65 -0
  331. data/lib/etassia/notation/plantuml/node_graph/entity_shape.rb +111 -0
  332. data/lib/etassia/notation/plantuml/node_graph/figures.rb +92 -0
  333. data/lib/etassia/notation/plantuml/node_graph/frame_ports.rb +73 -0
  334. data/lib/etassia/notation/plantuml/node_graph/graph.rb +40 -0
  335. data/lib/etassia/notation/plantuml/node_graph/heads.rb +89 -0
  336. data/lib/etassia/notation/plantuml/node_graph/label_fan.rb +76 -0
  337. data/lib/etassia/notation/plantuml/node_graph/label_placer.rb +310 -0
  338. data/lib/etassia/notation/plantuml/node_graph/label_waypoint.rb +47 -0
  339. data/lib/etassia/notation/plantuml/node_graph/layout.rb +293 -0
  340. data/lib/etassia/notation/plantuml/node_graph/link.rb +78 -0
  341. data/lib/etassia/notation/plantuml/node_graph/macros.rb +39 -0
  342. data/lib/etassia/notation/plantuml/node_graph/metrics.rb +113 -0
  343. data/lib/etassia/notation/plantuml/node_graph/node.rb +44 -0
  344. data/lib/etassia/notation/plantuml/node_graph/note_tail.rb +58 -0
  345. data/lib/etassia/notation/plantuml/node_graph/packer.rb +126 -0
  346. data/lib/etassia/notation/plantuml/node_graph/parser.rb +309 -0
  347. data/lib/etassia/notation/plantuml/node_graph/paths.rb +48 -0
  348. data/lib/etassia/notation/plantuml/node_graph/ranking.rb +231 -0
  349. data/lib/etassia/notation/plantuml/node_graph/reader.rb +53 -0
  350. data/lib/etassia/notation/plantuml/node_graph/references.rb +38 -0
  351. data/lib/etassia/notation/plantuml/node_graph/renderer.rb +160 -0
  352. data/lib/etassia/notation/plantuml/node_graph/scene.rb +22 -0
  353. data/lib/etassia/notation/plantuml/node_graph/shapes.rb +302 -0
  354. data/lib/etassia/notation/plantuml/node_graph/skipped_ranks.rb +61 -0
  355. data/lib/etassia/notation/plantuml/node_graph/tab.rb +68 -0
  356. data/lib/etassia/notation/plantuml/node_graph/usecase_ellipse.rb +100 -0
  357. data/lib/etassia/notation/plantuml/node_graph.rb +38 -0
  358. data/lib/etassia/notation/plantuml/note.rb +27 -0
  359. data/lib/etassia/notation/plantuml/note_layout.rb +257 -0
  360. data/lib/etassia/notation/plantuml/note_outline.rb +68 -0
  361. data/lib/etassia/notation/plantuml/objects/map_rows.rb +46 -0
  362. data/lib/etassia/notation/plantuml/objects/reader.rb +208 -0
  363. data/lib/etassia/notation/plantuml/objects.rb +39 -0
  364. data/lib/etassia/notation/plantuml/package.rb +45 -0
  365. data/lib/etassia/notation/plantuml/package_color.rb +32 -0
  366. data/lib/etassia/notation/plantuml/package_frames.rb +178 -0
  367. data/lib/etassia/notation/plantuml/parser.rb +622 -0
  368. data/lib/etassia/notation/plantuml/portion_visibility.rb +91 -0
  369. data/lib/etassia/notation/plantuml/qualified_association_layout.rb +332 -0
  370. data/lib/etassia/notation/plantuml/qualifier_box.rb +78 -0
  371. data/lib/etassia/notation/plantuml/rank_chain.rb +527 -0
  372. data/lib/etassia/notation/plantuml/rank_hierarchy.rb +347 -0
  373. data/lib/etassia/notation/plantuml/relation.rb +103 -0
  374. data/lib/etassia/notation/plantuml/relation_label_separator.rb +199 -0
  375. data/lib/etassia/notation/plantuml/relation_path_points.rb +84 -0
  376. data/lib/etassia/notation/plantuml/renderer.rb +490 -0
  377. data/lib/etassia/notation/plantuml/scene.rb +134 -0
  378. data/lib/etassia/notation/plantuml/segment_box.rb +85 -0
  379. data/lib/etassia/notation/plantuml/sequence/activation.rb +28 -0
  380. data/lib/etassia/notation/plantuml/sequence/appearance.rb +71 -0
  381. data/lib/etassia/notation/plantuml/sequence/arrow_end.rb +23 -0
  382. data/lib/etassia/notation/plantuml/sequence/arrow_marks.rb +111 -0
  383. data/lib/etassia/notation/plantuml/sequence/arrow_style.rb +42 -0
  384. data/lib/etassia/notation/plantuml/sequence/arrow_syntax.rb +122 -0
  385. data/lib/etassia/notation/plantuml/sequence/bar_tracker.rb +59 -0
  386. data/lib/etassia/notation/plantuml/sequence/box.rb +23 -0
  387. data/lib/etassia/notation/plantuml/sequence/chrome.rb +60 -0
  388. data/lib/etassia/notation/plantuml/sequence/chrome_rows.rb +184 -0
  389. data/lib/etassia/notation/plantuml/sequence/create.rb +34 -0
  390. data/lib/etassia/notation/plantuml/sequence/delay.rb +19 -0
  391. data/lib/etassia/notation/plantuml/sequence/destroy.rb +20 -0
  392. data/lib/etassia/notation/plantuml/sequence/diagram.rb +86 -0
  393. data/lib/etassia/notation/plantuml/sequence/divider.rb +19 -0
  394. data/lib/etassia/notation/plantuml/sequence/edge.rb +53 -0
  395. data/lib/etassia/notation/plantuml/sequence/embedded.rb +77 -0
  396. data/lib/etassia/notation/plantuml/sequence/fill.rb +56 -0
  397. data/lib/etassia/notation/plantuml/sequence/fragment.rb +28 -0
  398. data/lib/etassia/notation/plantuml/sequence/fragment_shape.rb +138 -0
  399. data/lib/etassia/notation/plantuml/sequence/head_style.rb +43 -0
  400. data/lib/etassia/notation/plantuml/sequence/ir_adapter/blocks.rb +73 -0
  401. data/lib/etassia/notation/plantuml/sequence/ir_adapter/fills.rb +24 -0
  402. data/lib/etassia/notation/plantuml/sequence/ir_adapter/items.rb +48 -0
  403. data/lib/etassia/notation/plantuml/sequence/ir_adapter/messages.rb +62 -0
  404. data/lib/etassia/notation/plantuml/sequence/ir_adapter/participants.rb +42 -0
  405. data/lib/etassia/notation/plantuml/sequence/ir_adapter/settings.rb +54 -0
  406. data/lib/etassia/notation/plantuml/sequence/ir_adapter.rb +38 -0
  407. data/lib/etassia/notation/plantuml/sequence/ir_reader/blocks.rb +77 -0
  408. data/lib/etassia/notation/plantuml/sequence/ir_reader/fills.rb +27 -0
  409. data/lib/etassia/notation/plantuml/sequence/ir_reader/index.rb +36 -0
  410. data/lib/etassia/notation/plantuml/sequence/ir_reader/items.rb +35 -0
  411. data/lib/etassia/notation/plantuml/sequence/ir_reader/messages.rb +70 -0
  412. data/lib/etassia/notation/plantuml/sequence/ir_reader/participants.rb +42 -0
  413. data/lib/etassia/notation/plantuml/sequence/ir_reader/settings.rb +74 -0
  414. data/lib/etassia/notation/plantuml/sequence/ir_reader.rb +38 -0
  415. data/lib/etassia/notation/plantuml/sequence/keyword_defaults.rb +28 -0
  416. data/lib/etassia/notation/plantuml/sequence/layout.rb +1567 -0
  417. data/lib/etassia/notation/plantuml/sequence/message.rb +97 -0
  418. data/lib/etassia/notation/plantuml/sequence/message_wrap.rb +77 -0
  419. data/lib/etassia/notation/plantuml/sequence/note.rb +61 -0
  420. data/lib/etassia/notation/plantuml/sequence/note_fill.rb +35 -0
  421. data/lib/etassia/notation/plantuml/sequence/note_geometry.rb +126 -0
  422. data/lib/etassia/notation/plantuml/sequence/note_shape.rb +51 -0
  423. data/lib/etassia/notation/plantuml/sequence/numbered_label.rb +57 -0
  424. data/lib/etassia/notation/plantuml/sequence/numbering.rb +49 -0
  425. data/lib/etassia/notation/plantuml/sequence/outline.rb +204 -0
  426. data/lib/etassia/notation/plantuml/sequence/page_break.rb +17 -0
  427. data/lib/etassia/notation/plantuml/sequence/paint.rb +79 -0
  428. data/lib/etassia/notation/plantuml/sequence/parallel_message.rb +19 -0
  429. data/lib/etassia/notation/plantuml/sequence/parser.rb +844 -0
  430. data/lib/etassia/notation/plantuml/sequence/participant.rb +27 -0
  431. data/lib/etassia/notation/plantuml/sequence/picture.rb +29 -0
  432. data/lib/etassia/notation/plantuml/sequence/ref.rb +24 -0
  433. data/lib/etassia/notation/plantuml/sequence/ref_shape.rb +74 -0
  434. data/lib/etassia/notation/plantuml/sequence/refusals.rb +47 -0
  435. data/lib/etassia/notation/plantuml/sequence/renderer.rb +501 -0
  436. data/lib/etassia/notation/plantuml/sequence/scene.rb +200 -0
  437. data/lib/etassia/notation/plantuml/sequence/skin.rb +76 -0
  438. data/lib/etassia/notation/plantuml/sequence/spacing.rb +28 -0
  439. data/lib/etassia/notation/plantuml/sequence/style.rb +151 -0
  440. data/lib/etassia/notation/plantuml/sequence/tab_size.rb +42 -0
  441. data/lib/etassia/notation/plantuml/sequence/walker.rb +1190 -0
  442. data/lib/etassia/notation/plantuml/sequence.rb +47 -0
  443. data/lib/etassia/notation/plantuml/source_lines.rb +33 -0
  444. data/lib/etassia/notation/plantuml/state/axis_solver.rb +272 -0
  445. data/lib/etassia/notation/plantuml/state/cluster_padding.rb +75 -0
  446. data/lib/etassia/notation/plantuml/state/curve.rb +73 -0
  447. data/lib/etassia/notation/plantuml/state/dot_axis.rb +248 -0
  448. data/lib/etassia/notation/plantuml/state/extent.rb +42 -0
  449. data/lib/etassia/notation/plantuml/state/hierarchy.rb +355 -0
  450. data/lib/etassia/notation/plantuml/state/label_clearance.rb +148 -0
  451. data/lib/etassia/notation/plantuml/state/label_reach.rb +139 -0
  452. data/lib/etassia/notation/plantuml/state/layout.rb +173 -0
  453. data/lib/etassia/notation/plantuml/state/level_edges.rb +113 -0
  454. data/lib/etassia/notation/plantuml/state/look.rb +36 -0
  455. data/lib/etassia/notation/plantuml/state/note_shape.rb +73 -0
  456. data/lib/etassia/notation/plantuml/state/plane.rb +85 -0
  457. data/lib/etassia/notation/plantuml/state/ranking.rb +181 -0
  458. data/lib/etassia/notation/plantuml/state/reader.rb +310 -0
  459. data/lib/etassia/notation/plantuml/state/renderer.rb +253 -0
  460. data/lib/etassia/notation/plantuml/state/route_detour.rb +115 -0
  461. data/lib/etassia/notation/plantuml/state/route_geometry.rb +186 -0
  462. data/lib/etassia/notation/plantuml/state/routes.rb +196 -0
  463. data/lib/etassia/notation/plantuml/state/row.rb +88 -0
  464. data/lib/etassia/notation/plantuml/state/rows.rb +50 -0
  465. data/lib/etassia/notation/plantuml/state/stretch.rb +71 -0
  466. data/lib/etassia/notation/plantuml/state.rb +62 -0
  467. data/lib/etassia/notation/plantuml/stereotype_head.rb +58 -0
  468. data/lib/etassia/notation/plantuml/style/bound.rb +34 -0
  469. data/lib/etassia/notation/plantuml/style/colour.rb +41 -0
  470. data/lib/etassia/notation/plantuml/style/colour_names.rb +89 -0
  471. data/lib/etassia/notation/plantuml/style/defaults.rb +33 -0
  472. data/lib/etassia/notation/plantuml/style/inline.rb +56 -0
  473. data/lib/etassia/notation/plantuml/style/reader.rb +242 -0
  474. data/lib/etassia/notation/plantuml/style/sheet.rb +103 -0
  475. data/lib/etassia/notation/plantuml/style.rb +32 -0
  476. data/lib/etassia/notation/plantuml/style_reader.rb +160 -0
  477. data/lib/etassia/notation/plantuml/style_sheet.rb +27 -0
  478. data/lib/etassia/notation/plantuml/styled_relation_curve.rb +71 -0
  479. data/lib/etassia/notation/plantuml/timing/arrows.rb +81 -0
  480. data/lib/etassia/notation/plantuml/timing/axis.rb +100 -0
  481. data/lib/etassia/notation/plantuml/timing/layout.rb +172 -0
  482. data/lib/etassia/notation/plantuml/timing/model.rb +34 -0
  483. data/lib/etassia/notation/plantuml/timing/reader.rb +173 -0
  484. data/lib/etassia/notation/plantuml/timing/signals.rb +328 -0
  485. data/lib/etassia/notation/plantuml/timing.rb +38 -0
  486. data/lib/etassia/notation/plantuml/tree/layout.rb +276 -0
  487. data/lib/etassia/notation/plantuml/tree/mindmap_layout.rb +157 -0
  488. data/lib/etassia/notation/plantuml/tree/reader.rb +186 -0
  489. data/lib/etassia/notation/plantuml/tree/renderer.rb +67 -0
  490. data/lib/etassia/notation/plantuml/tree/role.rb +57 -0
  491. data/lib/etassia/notation/plantuml/tree/rules.rb +156 -0
  492. data/lib/etassia/notation/plantuml/tree/wbs_layout.rb +164 -0
  493. data/lib/etassia/notation/plantuml/tree.rb +41 -0
  494. data/lib/etassia/notation/plantuml/unsupported_construct_error.rb +39 -0
  495. data/lib/etassia/notation/plantuml/unsupported_constructs.rb +103 -0
  496. data/lib/etassia/notation/plantuml/usecase.rb +36 -0
  497. data/lib/etassia/notation/plantuml.rb +132 -0
  498. data/lib/etassia/notation/plantuml_data/boxes/frames.rb +101 -0
  499. data/lib/etassia/notation/plantuml_data/boxes/graph_layout.rb +348 -0
  500. data/lib/etassia/notation/plantuml_data/boxes/heads.rb +65 -0
  501. data/lib/etassia/notation/plantuml_data/boxes/highlights.rb +70 -0
  502. data/lib/etassia/notation/plantuml_data/boxes/inline_rows.rb +99 -0
  503. data/lib/etassia/notation/plantuml_data/boxes/json_reader.rb +24 -0
  504. data/lib/etassia/notation/plantuml_data/boxes/link_text.rb +94 -0
  505. data/lib/etassia/notation/plantuml_data/boxes/marks.rb +76 -0
  506. data/lib/etassia/notation/plantuml_data/boxes/ornaments.rb +130 -0
  507. data/lib/etassia/notation/plantuml_data/boxes/renderer.rb +95 -0
  508. data/lib/etassia/notation/plantuml_data/boxes/row_alignment.rb +58 -0
  509. data/lib/etassia/notation/plantuml_data/boxes/row_bend.rb +43 -0
  510. data/lib/etassia/notation/plantuml_data/boxes/scene.rb +27 -0
  511. data/lib/etassia/notation/plantuml_data/boxes/table.rb +56 -0
  512. data/lib/etassia/notation/plantuml_data/boxes/table_box.rb +355 -0
  513. data/lib/etassia/notation/plantuml_data/boxes/text.rb +101 -0
  514. data/lib/etassia/notation/plantuml_data/boxes/tree_builder.rb +82 -0
  515. data/lib/etassia/notation/plantuml_data/boxes/tree_layout.rb +82 -0
  516. data/lib/etassia/notation/plantuml_data/boxes/yaml_reader.rb +105 -0
  517. data/lib/etassia/notation/plantuml_data/openiconic.LICENSE +6 -0
  518. data/lib/etassia/notation/plantuml_data/openiconic.json +1 -0
  519. data/lib/etassia/notation/plantuml_data.rb +84 -0
  520. data/lib/etassia/notation/plantuml_salt/block.rb +80 -0
  521. data/lib/etassia/notation/plantuml_salt/box.rb +30 -0
  522. data/lib/etassia/notation/plantuml_salt/font_metrics.rb +28 -0
  523. data/lib/etassia/notation/plantuml_salt/grid_marks.rb +66 -0
  524. data/lib/etassia/notation/plantuml_salt/group_marks.rb +77 -0
  525. data/lib/etassia/notation/plantuml_salt/layout.rb +206 -0
  526. data/lib/etassia/notation/plantuml_salt/mark.rb +36 -0
  527. data/lib/etassia/notation/plantuml_salt/markup.rb +57 -0
  528. data/lib/etassia/notation/plantuml_salt/parser.rb +239 -0
  529. data/lib/etassia/notation/plantuml_salt/renderer.rb +138 -0
  530. data/lib/etassia/notation/plantuml_salt/row.rb +19 -0
  531. data/lib/etassia/notation/plantuml_salt/run.rb +34 -0
  532. data/lib/etassia/notation/plantuml_salt/scene.rb +23 -0
  533. data/lib/etassia/notation/plantuml_salt/scroll_marks.rb +94 -0
  534. data/lib/etassia/notation/plantuml_salt/tab_marks.rb +74 -0
  535. data/lib/etassia/notation/plantuml_salt/tree_marks.rb +127 -0
  536. data/lib/etassia/notation/plantuml_salt/widget.rb +40 -0
  537. data/lib/etassia/notation/plantuml_salt/widget_marks.rb +319 -0
  538. data/lib/etassia/notation/plantuml_salt.rb +66 -0
  539. data/lib/etassia/notation/plugin_failure.rb +42 -0
  540. data/lib/etassia/notation.rb +289 -0
  541. data/lib/etassia/parser/architecture.rb +25 -0
  542. data/lib/etassia/parser/atoms/delimited_run.rb +82 -0
  543. data/lib/etassia/parser/atoms/greedy_run.rb +116 -0
  544. data/lib/etassia/parser/atoms/joined.rb +47 -0
  545. data/lib/etassia/parser/atoms/trimmed_run.rb +47 -0
  546. data/lib/etassia/parser/base.rb +258 -0
  547. data/lib/etassia/parser/block.rb +32 -0
  548. data/lib/etassia/parser/builders/architecture.rb +135 -0
  549. data/lib/etassia/parser/builders/block.rb +288 -0
  550. data/lib/etassia/parser/builders/c4.rb +275 -0
  551. data/lib/etassia/parser/builders/capture_string.rb +17 -0
  552. data/lib/etassia/parser/builders/class_diagram.rb +539 -0
  553. data/lib/etassia/parser/builders/er_diagram.rb +308 -0
  554. data/lib/etassia/parser/builders/error.rb +51 -0
  555. data/lib/etassia/parser/builders/flowchart.rb +1150 -0
  556. data/lib/etassia/parser/builders/gantt.rb +267 -0
  557. data/lib/etassia/parser/builders/git_graph.rb +301 -0
  558. data/lib/etassia/parser/builders/info.rb +57 -0
  559. data/lib/etassia/parser/builders/kanban.rb +411 -0
  560. data/lib/etassia/parser/builders/mindmap.rb +271 -0
  561. data/lib/etassia/parser/builders/packet.rb +85 -0
  562. data/lib/etassia/parser/builders/pie.rb +105 -0
  563. data/lib/etassia/parser/builders/quadrant.rb +165 -0
  564. data/lib/etassia/parser/builders/radar.rb +157 -0
  565. data/lib/etassia/parser/builders/requirement.rb +230 -0
  566. data/lib/etassia/parser/builders/sankey.rb +142 -0
  567. data/lib/etassia/parser/builders/sequence.rb +431 -0
  568. data/lib/etassia/parser/builders/sequence_blocks.rb +117 -0
  569. data/lib/etassia/parser/builders/sequence_box_title.rb +57 -0
  570. data/lib/etassia/parser/builders/state_diagram.rb +364 -0
  571. data/lib/etassia/parser/builders/timeline.rb +133 -0
  572. data/lib/etassia/parser/builders/treemap.rb +138 -0
  573. data/lib/etassia/parser/builders/xy_chart.rb +148 -0
  574. data/lib/etassia/parser/c4.rb +37 -0
  575. data/lib/etassia/parser/class_diagram.rb +58 -0
  576. data/lib/etassia/parser/class_namespaces.rb +62 -0
  577. data/lib/etassia/parser/class_notes.rb +54 -0
  578. data/lib/etassia/parser/er_diagram.rb +28 -0
  579. data/lib/etassia/parser/error.rb +30 -0
  580. data/lib/etassia/parser/flowchart.rb +87 -0
  581. data/lib/etassia/parser/gantt.rb +43 -0
  582. data/lib/etassia/parser/git_graph.rb +106 -0
  583. data/lib/etassia/parser/grammars/architecture.rb +165 -0
  584. data/lib/etassia/parser/grammars/block.rb +190 -0
  585. data/lib/etassia/parser/grammars/c4.rb +229 -0
  586. data/lib/etassia/parser/grammars/class_diagram.rb +606 -0
  587. data/lib/etassia/parser/grammars/common.rb +107 -0
  588. data/lib/etassia/parser/grammars/er_diagram.rb +232 -0
  589. data/lib/etassia/parser/grammars/error.rb +32 -0
  590. data/lib/etassia/parser/grammars/flowchart.rb +1722 -0
  591. data/lib/etassia/parser/grammars/gantt.rb +198 -0
  592. data/lib/etassia/parser/grammars/git_graph.rb +234 -0
  593. data/lib/etassia/parser/grammars/info.rb +53 -0
  594. data/lib/etassia/parser/grammars/kanban.rb +417 -0
  595. data/lib/etassia/parser/grammars/mermaid_unicode_text.rb +108 -0
  596. data/lib/etassia/parser/grammars/mindmap.rb +220 -0
  597. data/lib/etassia/parser/grammars/packet.rb +71 -0
  598. data/lib/etassia/parser/grammars/pie.rb +128 -0
  599. data/lib/etassia/parser/grammars/quadrant.rb +202 -0
  600. data/lib/etassia/parser/grammars/radar.rb +158 -0
  601. data/lib/etassia/parser/grammars/requirement.rb +258 -0
  602. data/lib/etassia/parser/grammars/sankey.rb +104 -0
  603. data/lib/etassia/parser/grammars/sequence.rb +940 -0
  604. data/lib/etassia/parser/grammars/state_diagram.rb +382 -0
  605. data/lib/etassia/parser/grammars/timeline.rb +153 -0
  606. data/lib/etassia/parser/grammars/treemap.rb +122 -0
  607. data/lib/etassia/parser/grammars/xy_chart.rb +119 -0
  608. data/lib/etassia/parser/info.rb +32 -0
  609. data/lib/etassia/parser/kanban.rb +131 -0
  610. data/lib/etassia/parser/mermaid_shapes.rb +106 -0
  611. data/lib/etassia/parser/metadata_yaml.rb +452 -0
  612. data/lib/etassia/parser/mindmap.rb +92 -0
  613. data/lib/etassia/parser/native/builders/architecture.rb +103 -0
  614. data/lib/etassia/parser/native/builders/block.rb +68 -0
  615. data/lib/etassia/parser/native/builders/c4.rb +16 -0
  616. data/lib/etassia/parser/native/builders/class_diagram.rb +63 -0
  617. data/lib/etassia/parser/native/builders/er_diagram.rb +47 -0
  618. data/lib/etassia/parser/native/builders/flowchart.rb +58 -0
  619. data/lib/etassia/parser/native/builders/gantt.rb +35 -0
  620. data/lib/etassia/parser/native/builders/gitgraph.rb +35 -0
  621. data/lib/etassia/parser/native/builders/info.rb +30 -0
  622. data/lib/etassia/parser/native/builders/kanban.rb +37 -0
  623. data/lib/etassia/parser/native/builders/mindmap.rb +26 -0
  624. data/lib/etassia/parser/native/builders/packet.rb +63 -0
  625. data/lib/etassia/parser/native/builders/pie.rb +82 -0
  626. data/lib/etassia/parser/native/builders/quadrant.rb +27 -0
  627. data/lib/etassia/parser/native/builders/radar.rb +84 -0
  628. data/lib/etassia/parser/native/builders/requirement.rb +25 -0
  629. data/lib/etassia/parser/native/builders/sankey.rb +18 -0
  630. data/lib/etassia/parser/native/builders/sequence.rb +48 -0
  631. data/lib/etassia/parser/native/builders/state_diagram.rb +61 -0
  632. data/lib/etassia/parser/native/builders/timeline.rb +46 -0
  633. data/lib/etassia/parser/native/builders/treemap.rb +60 -0
  634. data/lib/etassia/parser/native/builders/user_journey.rb +83 -0
  635. data/lib/etassia/parser/native/builders/xy_chart.rb +94 -0
  636. data/lib/etassia/parser/native/declined.rb +12 -0
  637. data/lib/etassia/parser/native/mermaid.json +1 -0
  638. data/lib/etassia/parser/native/text.rb +31 -0
  639. data/lib/etassia/parser/native.rb +99 -0
  640. data/lib/etassia/parser/packet.rb +56 -0
  641. data/lib/etassia/parser/pie.rb +32 -0
  642. data/lib/etassia/parser/quadrant.rb +77 -0
  643. data/lib/etassia/parser/radar.rb +99 -0
  644. data/lib/etassia/parser/requirement.rb +32 -0
  645. data/lib/etassia/parser/sankey.rb +47 -0
  646. data/lib/etassia/parser/sequence.rb +34 -0
  647. data/lib/etassia/parser/state_diagram.rb +29 -0
  648. data/lib/etassia/parser/timeline.rb +40 -0
  649. data/lib/etassia/parser/treemap.rb +25 -0
  650. data/lib/etassia/parser/user_journey.rb +253 -0
  651. data/lib/etassia/parser/xy_chart.rb +98 -0
  652. data/lib/etassia/parser.rb +18 -0
  653. data/lib/etassia/renderer/architecture.rb +232 -0
  654. data/lib/etassia/renderer/architecture_edge_router.rb +379 -0
  655. data/lib/etassia/renderer/base.rb +295 -0
  656. data/lib/etassia/renderer/block.rb +157 -0
  657. data/lib/etassia/renderer/c4.rb +294 -0
  658. data/lib/etassia/renderer/class_diagram.rb +671 -0
  659. data/lib/etassia/renderer/er_diagram.rb +842 -0
  660. data/lib/etassia/renderer/error.rb +131 -0
  661. data/lib/etassia/renderer/flowchart.rb +315 -0
  662. data/lib/etassia/renderer/gantt.rb +177 -0
  663. data/lib/etassia/renderer/git_graph.rb +224 -0
  664. data/lib/etassia/renderer/info.rb +59 -0
  665. data/lib/etassia/renderer/kanban.rb +157 -0
  666. data/lib/etassia/renderer/line_break_text.rb +56 -0
  667. data/lib/etassia/renderer/markdown_text.rb +106 -0
  668. data/lib/etassia/renderer/mindmap.rb +290 -0
  669. data/lib/etassia/renderer/packet.rb +87 -0
  670. data/lib/etassia/renderer/pie.rb +113 -0
  671. data/lib/etassia/renderer/quadrant.rb +189 -0
  672. data/lib/etassia/renderer/radar.rb +191 -0
  673. data/lib/etassia/renderer/requirement.rb +209 -0
  674. data/lib/etassia/renderer/sankey.rb +114 -0
  675. data/lib/etassia/renderer/sequence/frame_drawing.rb +169 -0
  676. data/lib/etassia/renderer/sequence/note_drawing.rb +69 -0
  677. data/lib/etassia/renderer/sequence.rb +490 -0
  678. data/lib/etassia/renderer/state_diagram.rb +202 -0
  679. data/lib/etassia/renderer/timeline.rb +134 -0
  680. data/lib/etassia/renderer/timeline_palette.rb +18 -0
  681. data/lib/etassia/renderer/treemap/section_total.rb +33 -0
  682. data/lib/etassia/renderer/treemap.rb +128 -0
  683. data/lib/etassia/renderer/user_journey.rb +209 -0
  684. data/lib/etassia/renderer/xy_chart.rb +180 -0
  685. data/lib/etassia/renderer.rb +22 -0
  686. data/lib/etassia/runtime.rb +125 -0
  687. data/lib/etassia/source/frontmatter.rb +501 -0
  688. data/lib/etassia/source.rb +224 -0
  689. data/lib/etassia/svg/arrowhead.rb +205 -0
  690. data/lib/etassia/svg/circle.rb +19 -0
  691. data/lib/etassia/svg/document.rb +106 -0
  692. data/lib/etassia/svg/element.rb +209 -0
  693. data/lib/etassia/svg/ellipse.rb +23 -0
  694. data/lib/etassia/svg/escaping.rb +149 -0
  695. data/lib/etassia/svg/group.rb +64 -0
  696. data/lib/etassia/svg/line.rb +21 -0
  697. data/lib/etassia/svg/numbers.rb +53 -0
  698. data/lib/etassia/svg/path.rb +67 -0
  699. data/lib/etassia/svg/path_geometry.rb +378 -0
  700. data/lib/etassia/svg/polygon.rb +25 -0
  701. data/lib/etassia/svg/polyline.rb +28 -0
  702. data/lib/etassia/svg/rect.rb +34 -0
  703. data/lib/etassia/svg/style.rb +50 -0
  704. data/lib/etassia/svg/text.rb +181 -0
  705. data/lib/etassia/svg/tspan.rb +48 -0
  706. data/lib/etassia/svg.rb +38 -0
  707. data/lib/etassia/text_measurement/arial_advances.rb +122 -0
  708. data/lib/etassia/text_measurement/dejavu_advances.rb +111 -0
  709. data/lib/etassia/text_measurement/times_advances.rb +64 -0
  710. data/lib/etassia/text_measurement/trebuchet_advances.rb +53 -0
  711. data/lib/etassia/text_measurement/trebuchet_kerning.rb +29 -0
  712. data/lib/etassia/text_measurement.rb +198 -0
  713. data/lib/etassia/theme/builtin/dark.yml +229 -0
  714. data/lib/etassia/theme/builtin/default.yml +239 -0
  715. data/lib/etassia/theme/builtin/high_contrast.yml +231 -0
  716. data/lib/etassia/theme/builtin/light.yml +229 -0
  717. data/lib/etassia/theme/color_palette.rb +97 -0
  718. data/lib/etassia/theme/completion.rb +50 -0
  719. data/lib/etassia/theme/effect_styles.rb +34 -0
  720. data/lib/etassia/theme/registry.rb +45 -0
  721. data/lib/etassia/theme/shape_styles.rb +34 -0
  722. data/lib/etassia/theme/spacing_config.rb +30 -0
  723. data/lib/etassia/theme/typography.rb +40 -0
  724. data/lib/etassia/theme.rb +73 -0
  725. data/lib/etassia/version.rb +5 -0
  726. data/lib/etassia.rb +40 -0
  727. metadata +865 -0
@@ -0,0 +1,419 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "base"
5
+ require_relative "containment"
6
+
7
+ module Etassia
8
+ module Diagram
9
+ # Represents a flowchart node.
10
+ #
11
+ # A node has an identifier, label text, and a shape type that
12
+ # determines how it should be rendered visually.
13
+ class FlowchartNode < Lutaml::Model::Serializable
14
+ # Unique identifier for the node
15
+ attribute :id, :string
16
+
17
+ # Display text/label for the node
18
+ attribute :label, :string
19
+
20
+ # Shape type: :rect, :rounded, :stadium, :subroutine, :cylindrical,
21
+ # :circle, :asymmetric, :rhombus, :hexagon, :parallelogram,
22
+ # :parallelogram_alt, :trapezoid, :trapezoid_alt, :double_circle
23
+ attribute :shape, :string
24
+
25
+ # Optional classes/styles
26
+ attribute :classes, :string
27
+ attribute :fill, :string
28
+ attribute :stroke, :string
29
+
30
+ PAINT_KEYWORDS = %w[
31
+ none currentcolor transparent aliceblue antiquewhite aqua aquamarine
32
+ azure beige bisque black blanchedalmond blue blueviolet brown burlywood
33
+ cadetblue chartreuse chocolate coral cornflowerblue cornsilk crimson
34
+ cyan darkblue darkcyan darkgoldenrod darkgray darkgrey darkgreen
35
+ darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred
36
+ darksalmon darkseagreen darkslateblue darkslategray darkslategrey
37
+ darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue
38
+ firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold
39
+ goldenrod gray grey green greenyellow honeydew hotpink indianred indigo
40
+ ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue
41
+ lightcoral lightcyan lightgoldenrodyellow lightgray lightgrey lightgreen
42
+ lightpink lightsalmon lightseagreen lightskyblue lightslategray
43
+ lightslategrey lightsteelblue lightyellow lime limegreen linen magenta
44
+ maroon mediumaquamarine mediumblue mediumorchid mediumpurple
45
+ mediumseagreen mediumslateblue mediumspringgreen mediumturquoise
46
+ mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite
47
+ navy oldlace olive olivedrab orange orangered orchid palegoldenrod
48
+ palegreen paleturquoise palevioletred papayawhip peachpuff peru pink
49
+ plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown
50
+ salmon sandybrown seagreen seashell sienna silver skyblue slateblue
51
+ slategray slategrey snow springgreen steelblue tan teal thistle tomato
52
+ turquoise violet wheat white whitesmoke yellow yellowgreen
53
+ ].freeze
54
+ HEX_PAINT = /\A#(?:[0-9a-f]{3}|[0-9a-f]{6})\z/i
55
+ RGB_COMPONENT = /[+-]?(?:\d+(?:\.\d*)?|\.\d+)%?/
56
+ RGB_PAINT = /\Argb\(\s*#{RGB_COMPONENT}\s*,
57
+ \s*#{RGB_COMPONENT}\s*,\s*#{RGB_COMPONENT}\s*\)\z/ix
58
+ private_constant :PAINT_KEYWORDS, :HEX_PAINT, :RGB_COMPONENT, :RGB_PAINT
59
+
60
+ # Invalid CSS declarations leave the earlier paint in force. Keep the
61
+ # supported opaque color forms separate from permissive parser tails.
62
+ def self.paint_value(value)
63
+ paint = value.to_s.strip
64
+ return paint if PAINT_KEYWORDS.include?(paint.downcase)
65
+ return paint if HEX_PAINT.match?(paint) || RGB_PAINT.match?(paint)
66
+
67
+ nil
68
+ end
69
+
70
+ # Initialize with default shape
71
+ def initialize(*args)
72
+ super
73
+ self.shape ||= "rect"
74
+ end
75
+
76
+ # Validates the node has required attributes.
77
+ #
78
+ # @return [Boolean] true if node is valid
79
+ def valid?
80
+ !id.nil? && !id.empty? && !label.nil?
81
+ end
82
+ end
83
+
84
+ # Represents a flowchart edge (connection between nodes).
85
+ #
86
+ # An edge connects a source node to a target node, with optional
87
+ # label text and arrow styling.
88
+ class FlowchartEdge < Lutaml::Model::Serializable
89
+ # Source node identifier
90
+ attribute :source_id, :string
91
+
92
+ # Target node identifier
93
+ attribute :target_id, :string
94
+
95
+ # Optional edge label
96
+ attribute :label, :string
97
+
98
+ # Arrow type: the head part, optionally prefixed by the line's
99
+ # weight. The head part is `line` for none, or `arrow`, `cross` or
100
+ # `circle`; a `_both` suffix marks the head as drawn at both ends.
101
+ # Weights are `thick_` and `dotted_`. `invisible` carries neither.
102
+ attribute :arrow_type, :string
103
+
104
+ # Minimum number of layout ranks crossed by an explicitly long link.
105
+ # nil is the ordinary one-rank Mermaid default.
106
+ attribute :minimum_rank_span, :integer
107
+
108
+ # Initialize with default arrow type
109
+ def initialize(*args)
110
+ super
111
+ self.arrow_type ||= "arrow"
112
+ end
113
+
114
+ # Validates the edge has required attributes.
115
+ #
116
+ # @return [Boolean] true if edge is valid
117
+ def valid?
118
+ !source_id.nil? && !source_id.empty? &&
119
+ !target_id.nil? && !target_id.empty?
120
+ end
121
+ end
122
+
123
+ # A subgraph groups nodes under a titled box. mermaid draws it as a
124
+ # cluster painted behind the edges and nodes it contains.
125
+ #
126
+ # Nodes and edges stay in the diagram's own flat collections — a
127
+ # subgraph names its members rather than owning them, so an edge
128
+ # crossing a boundary needs no special case.
129
+ class FlowchartSubgraph < Lutaml::Model::Serializable
130
+ # Identifier, as written or generated by mermaid
131
+ attribute :id, :string
132
+
133
+ # The label mermaid draws, resolved by the transform because the
134
+ # rule depends on how the title was written. Often absent: read
135
+ # `title`, which falls back to the id, so `subgraph s` reads "s".
136
+ attribute :declared_title, :string
137
+
138
+ # Enclosing subgraph's id, nil at the top level
139
+ attribute :parent_id, :string
140
+
141
+ # Ids of the nodes declared directly inside this subgraph
142
+ attribute :node_ids, :string, collection: true, default: -> { [] }
143
+
144
+ # Ids of the subgraphs declared directly inside this subgraph
145
+ attribute :child_ids, :string, collection: true, default: -> { [] }
146
+
147
+ # `direction` written inside the box, which turns its contents
148
+ # independently of the diagram's own direction. nil when unwritten.
149
+ #
150
+ # Read by nothing yet, so it changes nothing on the page. The
151
+ # fallback grid ignores layout options entirely, and the diagram's
152
+ # OWN `direction` is dropped the same way: `flowchart LR` puts its
153
+ # nodes exactly where `flowchart TD` does. Both start working
154
+ # together when the real layout engine lands. Keeping the value
155
+ # here means the source is not thrown away in the meantime.
156
+ attribute :direction, :string
157
+
158
+ # A method rather than a constructor default: the id is usually
159
+ # assigned after construction, so a fallback baked into initialize
160
+ # would have already seen nil and stuck with it.
161
+ #
162
+ # @return [String] the label to draw on the cluster
163
+ def title
164
+ declared_title.nil? || declared_title.empty? ? id : declared_title
165
+ end
166
+
167
+ # mermaid draws no cluster for a subgraph holding nothing, even
168
+ # though it accepts the source.
169
+ #
170
+ # @return [Boolean] true when there is a box worth drawing
171
+ def drawable?
172
+ !node_ids.empty? || !child_ids.empty?
173
+ end
174
+
175
+ # Validates the subgraph names itself.
176
+ #
177
+ # Everything downstream keys off the id: the layout hangs children
178
+ # on it, the renderer writes it into the cluster's group id, and an
179
+ # edge end that names a box finds it by id. A box with no id draws
180
+ # a cluster nobody can reach, so it is refused here instead.
181
+ #
182
+ # @return [Boolean] true if the subgraph is valid
183
+ def valid?
184
+ !id.nil? && !id.empty?
185
+ end
186
+ end
187
+
188
+ # Flowchart diagram model.
189
+ #
190
+ # Represents a complete flowchart with nodes and edges. Flowcharts
191
+ # support various node shapes and edge types to create flow diagrams,
192
+ # decision trees, and process flows.
193
+ #
194
+ # @example Creating a simple flowchart
195
+ # flowchart = Flowchart.new(direction: 'TD')
196
+ # flowchart.nodes << FlowchartNode.new(
197
+ # id: 'A',
198
+ # label: 'Start',
199
+ # shape: 'stadium'
200
+ # )
201
+ # flowchart.nodes << FlowchartNode.new(
202
+ # id: 'B',
203
+ # label: 'Process',
204
+ # shape: 'rect'
205
+ # )
206
+ # flowchart.edges << FlowchartEdge.new(
207
+ # source_id: 'A',
208
+ # target_id: 'B',
209
+ # arrow_type: 'arrow'
210
+ # )
211
+ class Flowchart < Base
212
+ # Collection of nodes in the flowchart
213
+ attribute :nodes, FlowchartNode, collection: true, default: -> { [] }
214
+
215
+ # Collection of edges connecting nodes
216
+ attribute :edges, FlowchartEdge, collection: true, default: -> { [] }
217
+
218
+ # Subgraphs, flat and in declaration order. Nesting is expressed by
219
+ # `parent_id` rather than by containment, so a lookup never has to
220
+ # walk a tree.
221
+ attribute :subgraphs, FlowchartSubgraph, collection: true,
222
+ default: -> { [] }
223
+
224
+ # Returns the diagram type identifier.
225
+ #
226
+ # @return [Symbol] :flowchart
227
+ def diagram_type
228
+ :flowchart
229
+ end
230
+
231
+ # Validates the flowchart structure.
232
+ #
233
+ # A flowchart is valid if:
234
+ # - `nodes` is set (mermaid draws a source with none at all, but an
235
+ # explicit `nil` here is a caller bug, not a real diagram)
236
+ # - No box is its own ancestor
237
+ # - A flowchart with no nodes is valid only if it also declares no
238
+ # subgraph: mermaid draws a bare `graph TD` as an empty canvas, but
239
+ # draws a declared-and-empty subgraph (`subgraph s\nend`) as a
240
+ # fallback node this model has no way to represent, so that shape
241
+ # is refused instead of silently dropped
242
+ # - All nodes are valid
243
+ # - All edges are valid
244
+ # - All subgraphs are valid
245
+ # - Every member a box names is in the diagram
246
+ # - `parent_id` and `child_ids` tell the same story
247
+ # - Every edge end names a node, or a subgraph that gets drawn
248
+ #
249
+ # @return [Boolean] true if flowchart is valid
250
+ def valid?
251
+ return false if nodes.nil?
252
+
253
+ boxes = subgraphs || []
254
+ structure_valid?(boxes) && references_resolve?(boxes)
255
+ end
256
+
257
+ # Finds a node by its identifier.
258
+ #
259
+ # @param id [String] the node identifier to find
260
+ # @return [FlowchartNode, nil] the node or nil if not found
261
+ def find_node(id)
262
+ nodes.find { |n| n.id == id }
263
+ end
264
+
265
+ # Finds all edges originating from a specific node.
266
+ #
267
+ # @param node_id [String] the source node identifier
268
+ # @return [Array<FlowchartEdge>] edges from the node
269
+ def edges_from(node_id)
270
+ edges.select { |e| e.source_id == node_id }
271
+ end
272
+
273
+ # Finds all edges targeting a specific node.
274
+ #
275
+ # @param node_id [String] the target node identifier
276
+ # @return [Array<FlowchartEdge>] edges to the node
277
+ def edges_to(node_id)
278
+ edges.select { |e| e.target_id == node_id }
279
+ end
280
+
281
+ private
282
+
283
+ def structure_valid?(boxes)
284
+ !parent_cycle?(boxes) && collections_valid?(boxes) &&
285
+ containment_valid?(boxes)
286
+ end
287
+
288
+ def collections_valid?(boxes)
289
+ nodes.all?(&:valid?) && valid_collection?(edges) &&
290
+ boxes.all?(&:valid?)
291
+ end
292
+
293
+ def containment_valid?(boxes)
294
+ members_named?(boxes, nodes) && nesting_agrees?(boxes)
295
+ end
296
+
297
+ def valid_collection?(items)
298
+ items.nil? || items.all?(&:valid?)
299
+ end
300
+
301
+ # An edge may name a subgraph rather than a node: mermaid joins
302
+ # the cluster boxes and draws no node for either end. Only a box
303
+ # that gets drawn is carried into the layout and can be reached.
304
+ def references_resolve?(boxes)
305
+ drawn_box_ids = drawable_box_ids(boxes)
306
+ return false if only_undrawable_boxes?(boxes, drawn_box_ids)
307
+
308
+ edge_references_resolve?(nodes.map(&:id) + drawn_box_ids)
309
+ end
310
+
311
+ def drawable_box_ids(boxes)
312
+ boxes.filter_map { |box| box.id if box.drawable? }
313
+ end
314
+
315
+ def only_undrawable_boxes?(boxes, drawn_box_ids)
316
+ boxes.any? && nodes.empty? && drawn_box_ids.empty?
317
+ end
318
+
319
+ def edge_references_resolve?(known)
320
+ edges.nil? || edges.all? do |edge|
321
+ known.include?(edge.source_id) && known.include?(edge.target_id)
322
+ end
323
+ end
324
+
325
+ # A box names its members rather than owning them, so a name that
326
+ # matches nothing leaves the box holding an empty seat: the box
327
+ # counts as drawable, the layout sizes it around no contents, and
328
+ # the picture shows a cluster with nothing in it.
329
+ #
330
+ # A member may name a node or another box. An empty subgraph stays
331
+ # a plain node in mermaid, so both spellings come out of a parse.
332
+ #
333
+ # @param boxes [Array<FlowchartSubgraph>] the diagram's subgraphs
334
+ # @param drawn_nodes [Array<FlowchartNode>] the diagram's nodes
335
+ # @return [Boolean] true when every named member is present
336
+ def members_named?(boxes, drawn_nodes)
337
+ # A hash rather than an array scan: one box can hold thousands of
338
+ # members, and asking an array for each of them is quadratic.
339
+ known = {}
340
+ drawn_nodes.each { |node| known[node.id] = true }
341
+ boxes.each { |box| known[box.id] = true }
342
+
343
+ boxes.all? do |box|
344
+ box.node_ids.all? { |member_id| known.key?(member_id) }
345
+ end
346
+ end
347
+
348
+ # `parent_id` and `child_ids` say the same thing from two ends, and
349
+ # the drawing believes both. The transform hangs a box off its
350
+ # `parent_id`, while `drawable?` counts `child_ids` — so a box
351
+ # listed as a child but pointing nowhere is drawn twice, and one
352
+ # pointing at a parent that does not list it stops the parent from
353
+ # being drawn at all. Nothing keeps the two in step on a model
354
+ # somebody builds by hand, so a model that argues with itself is
355
+ # refused here rather than drawn wrong.
356
+ #
357
+ # A parent nobody declared is not a disagreement: the transform
358
+ # puts such a box at the top level on purpose.
359
+ #
360
+ # Ids are looked up as groups, not one apiece, because a source may
361
+ # declare the same box twice. Only the first object carries the
362
+ # nesting; the second is an empty repeat of the same name.
363
+ #
364
+ # @param boxes [Array<FlowchartSubgraph>] the diagram's subgraphs
365
+ # @return [Boolean] true when both directions agree
366
+ def nesting_agrees?(boxes)
367
+ holders = boxes.group_by(&:id)
368
+
369
+ boxes.all? do |box|
370
+ named_by_parent?(holders, box) && children_name_back?(holders, box)
371
+ end
372
+ end
373
+
374
+ def named_by_parent?(holders, box)
375
+ parents = holders[box.parent_id]
376
+
377
+ parents.nil? ||
378
+ parents.any? { |parent| parent.child_ids.include?(box.id) }
379
+ end
380
+
381
+ def children_name_back?(holders, box)
382
+ box.child_ids.all? do |child_id|
383
+ holders.fetch(child_id, []).any? { |child| child.parent_id == box.id }
384
+ end
385
+ end
386
+
387
+ # A box cannot be its own ancestor. The layout hangs each box off
388
+ # its parent, so a ring of them hangs off itself: not one of the
389
+ # boxes in the ring is ever placed, while their nodes still count
390
+ # as spoken for and so are left out of the loose pile too. Two
391
+ # boxes parented to each other lose everything they hold, and so
392
+ # does a box naming itself. Boxes outside the ring still draw,
393
+ # which is why the model has to refuse this rather than let a
394
+ # half-empty picture through.
395
+ #
396
+ # The parser cannot build one, so this only ever fires on a model
397
+ # somebody assembled by hand. Every parent edge the transform can
398
+ # set — a reference it claimed, or a nested declaration — is also
399
+ # an edge in the containment graph it walks BEFORE any of this,
400
+ # so the parent graph is a subset of one already proven acyclic.
401
+ #
402
+ # Every box contributes its parent, rather than the last one for a
403
+ # given id winning, because a duplicated id means the layout can
404
+ # follow either edge and a loop through either one is real.
405
+ #
406
+ # @param boxes [Array<FlowchartSubgraph>] the subgraphs to test
407
+ # @return [Boolean] true when some box is its own ancestor
408
+ def parent_cycle?(boxes)
409
+ graph = boxes.each_with_object({}) do |box, acc|
410
+ next if box.parent_id.nil?
411
+
412
+ acc[box.id] = acc.fetch(box.id, []) | [box.parent_id]
413
+ end
414
+
415
+ !Containment.looping_pair(graph).nil?
416
+ end
417
+ end
418
+ end
419
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../markdown_text"
4
+
5
+ module Etassia
6
+ module Diagram
7
+ # Mermaid draws a flowchart label without the syntax that wrote it: the
8
+ # quote or markdown-string delimiters around it, a leading `fa:fa-x`
9
+ # icon, HTML tags, and the doubling of a backslash.
10
+ module FlowchartLabelText
11
+ QUOTED = /\A"(.*)"\z/m
12
+ MARKDOWN = /\A`(.*)`\z/m
13
+ ICON = /\bfa[bklrs]?:fa-[\w-]+\s*/
14
+ BREAK = %r{<br\s*/?>}i
15
+ LINE_BREAK = Regexp.union(BREAK, /\n/)
16
+ TAG = %r{</?[a-zA-Z][^>]*>}
17
+ DOUBLED_BACKSLASH = "\\\\"
18
+ ESCAPED_LINE_BREAK = "\\n"
19
+
20
+ # @param text [String, nil] label text as written in the source
21
+ # @return [String, nil] the text as mermaid draws it
22
+ def self.display(text)
23
+ return text if text.nil?
24
+
25
+ lines(text).join(" ").strip
26
+ end
27
+
28
+ # @param text [String, nil] label text as written in the source
29
+ # @return [Array<String>] the lines mermaid draws, one for each `<br>`
30
+ # or newline; one empty line for no text
31
+ def self.lines(text)
32
+ shown = visible_source(text.to_s)
33
+ markdown = shown[MARKDOWN, 1]
34
+ return markdown_lines(markdown) if markdown
35
+
36
+ parts = shown.split(LINE_BREAK, -1)
37
+ lines = parts.map { |line| tidy(line) }
38
+ lines.empty? ? [""] : lines
39
+ end
40
+
41
+ def self.visible_source(text)
42
+ shown = text.strip
43
+ quoted = shown.match(QUOTED)
44
+ return shown unless quoted
45
+
46
+ quoted[1].gsub(ESCAPED_LINE_BREAK, "\n")
47
+ end
48
+
49
+ def self.markdown_lines(text)
50
+ MarkdownText.parse_lines(text.gsub(BREAK, "\n")).map do |runs|
51
+ tidy(runs.map(&:text).join)
52
+ end
53
+ end
54
+
55
+ def self.tidy(text)
56
+ shown = text.gsub(ICON, "").gsub(TAG, "")
57
+ shown.gsub(DOUBLED_BACKSLASH, "\\").gsub(/[[:space:]]+/, " ").strip
58
+ end
59
+
60
+ private_class_method :visible_source, :markdown_lines, :tidy
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "base"
5
+
6
+ module Etassia
7
+ module Diagram
8
+ # Gantt chart task
9
+ class GanttTask < Lutaml::Model::Serializable
10
+ # Filled in by the transform once `after_task`/`until_task` resolve. Not
11
+ # attributes: they are derived, and reopening the class from the
12
+ # transform to add them put a second definition in that file.
13
+ attr_accessor :calculated_start, :calculated_end
14
+
15
+ attribute :description, :string
16
+ attribute :id, :string
17
+ attribute :start_date, :string
18
+ attribute :end_date, :string
19
+ attribute :duration, :string
20
+ attribute :after_task, :string
21
+ attribute :until_task, :string
22
+ attribute :tags, :string, collection: true
23
+ attribute :click_href, :string
24
+ attribute :click_callback, :string
25
+
26
+ def initialize
27
+ super
28
+ @tags ||= []
29
+ end
30
+
31
+ def done?
32
+ tags.include?("done")
33
+ end
34
+
35
+ def active?
36
+ tags.include?("active")
37
+ end
38
+
39
+ def critical?
40
+ tags.include?("crit")
41
+ end
42
+
43
+ def milestone?
44
+ tags.include?("milestone")
45
+ end
46
+ end
47
+
48
+ # Gantt chart section grouping
49
+ class GanttSection < Lutaml::Model::Serializable
50
+ attribute :name, :string
51
+ attribute :tasks, GanttTask, collection: true
52
+
53
+ def initialize(name = nil)
54
+ super()
55
+ @name = name
56
+ @tasks ||= []
57
+ end
58
+ end
59
+
60
+ # Gantt chart diagram model
61
+ class Gantt < Base
62
+ attribute :title, :string
63
+ attribute :date_format, :string, default: -> { "YYYY-MM-DD" }
64
+ attribute :axis_format, :string
65
+ attribute :tick_interval, :string
66
+ attribute :excludes, :string, collection: true
67
+ attribute :weekend, :string
68
+ attribute :inclusive_end_dates, :boolean, default: -> { false }
69
+ attribute :today_marker, :string
70
+ attribute :sections, GanttSection, collection: true
71
+
72
+ def initialize
73
+ super
74
+ @sections ||= []
75
+ @excludes ||= []
76
+ end
77
+
78
+ # Returns the diagram type identifier.
79
+ #
80
+ # @return [Symbol] :gantt
81
+ def diagram_type
82
+ :gantt
83
+ end
84
+
85
+ # Gantt charts have no validation rules yet. See TODO.foundation's
86
+ # corpus burndown for real validation; this is deliberately trivial.
87
+ #
88
+ # @return [Boolean] true
89
+ def valid?
90
+ true
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+
5
+ module Etassia
6
+ module Diagram
7
+ # Mermaid writes a generic as `List~T~` and draws it as `List<T>`.
8
+ module GenericText
9
+ PAIR = /~([^~]+)~/
10
+
11
+ # @param text [String] source text holding `~T~` pairs
12
+ # @return [String] the text with each pair drawn as `<T>`
13
+ def self.display(text)
14
+ shown = text
15
+ shown = shown.gsub(PAIR, '<\1>') while shown.match?(PAIR)
16
+ shown
17
+ end
18
+
19
+ # Like {display}, for member text: a leading `~` is the package
20
+ # visibility mark and not the start of a pair, and an HTML entity such
21
+ # as `&lt;` is drawn as the character it names.
22
+ def self.display_member(text)
23
+ text = CGI.unescapeHTML(text)
24
+ return display(text) unless text.start_with?("~")
25
+
26
+ "~#{display(text[1..])}"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "base"
5
+
6
+ module Etassia
7
+ module Diagram
8
+ # Represents a Git Graph diagram
9
+ class GitGraph < Base
10
+ # Represents a commit in the git graph
11
+ class Commit < Lutaml::Model::Serializable
12
+ attribute :id, :string
13
+ attribute :message, :string
14
+ attribute :type, :string, default: -> { "NORMAL" }
15
+ attribute :tag, :string
16
+ attribute :tags, :string, collection: true, default: -> { [] }
17
+ attribute :branch_name, :string
18
+ attribute :parent_ids, :string, collection: true, default: -> { [] }
19
+ attribute :is_merge, :boolean, default: -> { false }
20
+ attribute :merge_branch, :string
21
+ attribute :is_cherry_pick, :boolean, default: -> { false }
22
+ attribute :cherry_pick_parent, :string
23
+ end
24
+
25
+ # Represents a branch in the git graph
26
+ class Branch < Lutaml::Model::Serializable
27
+ attribute :name, :string
28
+ attribute :order, :integer
29
+ attribute :parent_branch, :string
30
+ attribute :created_at_commit, :string
31
+ end
32
+
33
+ # "LR" (default), "TB" or "BT": the direction time runs in.
34
+ attribute :orientation, :string, default: -> { "LR" }
35
+ attribute :main_branch_name, :string, default: -> { "main" }
36
+ attribute :acc_title, :string
37
+ attribute :acc_description, :string
38
+ attribute :commits, Commit, collection: true, default: -> { [] }
39
+ attribute :branches, Branch, collection: true, default: -> { [] }
40
+
41
+ # Returns the diagram type identifier.
42
+ #
43
+ # @return [Symbol] :git_graph
44
+ def diagram_type
45
+ :git_graph
46
+ end
47
+
48
+ # Git graphs have no validation rules yet. See TODO.foundation's
49
+ # corpus burndown for real validation; this is deliberately trivial.
50
+ #
51
+ # @return [Boolean] true
52
+ def valid?
53
+ true
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+ require_relative "base"
5
+
6
+ module Etassia
7
+ module Diagram
8
+ # Info diagram model.
9
+ #
10
+ # Represents a simple informational diagram that displays a message
11
+ # or status information. Info diagrams are typically used to show
12
+ # system information, help text, or status messages.
13
+ #
14
+ # @example Creating an info diagram
15
+ # info = Info.new
16
+ # info.show_info = true
17
+ class Info < Base
18
+ # Whether to show additional info
19
+ attribute :show_info, :boolean, default: -> { false }
20
+
21
+ # Returns the diagram type identifier.
22
+ #
23
+ # @return [Symbol] :info
24
+ def diagram_type
25
+ :info
26
+ end
27
+
28
+ # Validates the info diagram structure.
29
+ #
30
+ # Info diagrams are always valid as they have no required content.
31
+ #
32
+ # @return [Boolean] true
33
+ def valid?
34
+ true
35
+ end
36
+ end
37
+ end
38
+ end