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.
- checksums.yaml +7 -0
- data/.github/workflows/build_deploy.yml +59 -0
- data/.github/workflows/links.yml +85 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +68 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.rubocop_todo.yml +70 -0
- data/ARCHITECTURE.md +744 -0
- data/Gemfile +12 -0
- data/LICENSE +25 -0
- data/README.adoc +357 -0
- data/Rakefile +11 -0
- data/docs/.gitignore +1 -0
- data/docs/Gemfile +13 -0
- data/docs/_config.yml +182 -0
- data/docs/_diagram_types/architecture-diagram.adoc +314 -0
- data/docs/_diagram_types/block-diagram.adoc +345 -0
- data/docs/_diagram_types/c4-diagram.adoc +559 -0
- data/docs/_diagram_types/class-diagram.adoc +816 -0
- data/docs/_diagram_types/er-diagram.adoc +719 -0
- data/docs/_diagram_types/error-diagram.adoc +114 -0
- data/docs/_diagram_types/examples/flowchart-examples.adoc +29 -0
- data/docs/_diagram_types/flowchart.adoc +488 -0
- data/docs/_diagram_types/gantt-chart.adoc +502 -0
- data/docs/_diagram_types/git-graph.adoc +600 -0
- data/docs/_diagram_types/index.adoc +192 -0
- data/docs/_diagram_types/info-diagram.adoc +103 -0
- data/docs/_diagram_types/kanban-diagram.adoc +262 -0
- data/docs/_diagram_types/mindmap.adoc +603 -0
- data/docs/_diagram_types/packet-diagram.adoc +378 -0
- data/docs/_diagram_types/pie-chart.adoc +335 -0
- data/docs/_diagram_types/quadrant-chart.adoc +406 -0
- data/docs/_diagram_types/radar-chart.adoc +528 -0
- data/docs/_diagram_types/requirement-diagram.adoc +416 -0
- data/docs/_diagram_types/sankey-diagram.adoc +357 -0
- data/docs/_diagram_types/sequence-diagram.adoc +664 -0
- data/docs/_diagram_types/state-diagram.adoc +658 -0
- data/docs/_diagram_types/timeline.adoc +352 -0
- data/docs/_diagram_types/treemap-diagram.adoc +462 -0
- data/docs/_diagram_types/user-journey.adoc +602 -0
- data/docs/_features/index.adoc +129 -0
- data/docs/_guides/cli-reference.adoc +203 -0
- data/docs/_guides/index.adoc +56 -0
- data/docs/_guides/installation.adoc +100 -0
- data/docs/_guides/quick-start.adoc +132 -0
- data/docs/_pages/comparison.adoc +441 -0
- data/docs/_pages/compatibility.adoc +300 -0
- data/docs/_pages/index.adoc +39 -0
- data/docs/_references/index.adoc +103 -0
- data/docs/_tutorials/index.adoc +57 -0
- data/docs/index.adoc +166 -0
- data/docs/lychee.toml +54 -0
- data/examples/.gitignore +10 -0
- data/examples/README.adoc +196 -0
- data/examples/README.md +64 -0
- data/examples/architecture/01-basic-services.mmd +9 -0
- data/examples/architecture/01-basic-services.svg +37 -0
- data/examples/architecture/02-service-groups.mmd +16 -0
- data/examples/architecture/02-service-groups.svg +55 -0
- data/examples/architecture/README.adoc +79 -0
- data/examples/block/01-basic-blocks.mmd +13 -0
- data/examples/block/01-basic-blocks.svg +44 -0
- data/examples/block/02-block-shapes.mmd +13 -0
- data/examples/block/02-block-shapes.svg +47 -0
- data/examples/block/README.adoc +85 -0
- data/examples/c4/01-context-diagram.mmd +10 -0
- data/examples/c4/01-context-diagram.svg +45 -0
- data/examples/c4/02-container-diagram.mmd +24 -0
- data/examples/c4/02-container-diagram.svg +105 -0
- data/examples/c4/README.adoc +92 -0
- data/examples/class_diagram/01-basic-classes.mmd +61 -0
- data/examples/class_diagram/01-basic-classes.svg +117 -0
- data/examples/class_diagram/02-relationships.mmd +61 -0
- data/examples/class_diagram/02-relationships.svg +129 -0
- data/examples/class_diagram/README.adoc +93 -0
- data/examples/er_diagram/01-basic-entities.mmd +64 -0
- data/examples/er_diagram/01-basic-entities.svg +5 -0
- data/examples/er_diagram/02-cardinality.mmd +57 -0
- data/examples/er_diagram/02-cardinality.svg +125 -0
- data/examples/er_diagram/README.adoc +88 -0
- data/examples/error/01-basic-error.mmd +1 -0
- data/examples/error/01-basic-error.svg +13 -0
- data/examples/error/02-error-display.mmd +1 -0
- data/examples/error/02-error-display.svg +13 -0
- data/examples/error/README.adoc +71 -0
- data/examples/error_message_example.svg +13 -0
- data/examples/flowchart/00-original.mmd +13 -0
- data/examples/flowchart/00-original.svg +5 -0
- data/examples/flowchart/01-basic-flow.mmd +7 -0
- data/examples/flowchart/01-basic-flow.svg +52 -0
- data/examples/flowchart/01-basic-flow.yml +13 -0
- data/examples/flowchart/02*.svg +87 -0
- data/examples/flowchart/02-node-shapes.mmd +9 -0
- data/examples/flowchart/02-node-shapes.svg +33 -0
- data/examples/flowchart/03-edge-types.mmd +7 -0
- data/examples/flowchart/03-edge-types.svg +53 -0
- data/examples/flowchart/04-subgraphs.mmd +9 -0
- data/examples/flowchart/04-subgraphs.svg +33 -0
- data/examples/flowchart/05-styling.mmd +9 -0
- data/examples/flowchart/05-styling.svg +33 -0
- data/examples/flowchart/06-complex-flow.mmd +8 -0
- data/examples/flowchart/06-complex-flow.svg +59 -0
- data/examples/flowchart/README.adoc +167 -0
- data/examples/gantt/01-simple-timeline.* +14 -0
- data/examples/gantt/01-simple-timeline.mmd +6 -0
- data/examples/gantt/01-simple-timeline.svg +26 -0
- data/examples/gantt/02-task-dependencies.mmd +6 -0
- data/examples/gantt/02-task-dependencies.svg +26 -0
- data/examples/gantt/README.adoc +86 -0
- data/examples/git_graph/01-linear-history.mmd +12 -0
- data/examples/git_graph/01-linear-history.svg +26 -0
- data/examples/git_graph/02-branching.mmd +12 -0
- data/examples/git_graph/02-branching.svg +26 -0
- data/examples/git_graph/README.adoc +73 -0
- data/examples/info/02-showinfo.mmd +1 -0
- data/examples/info/02-showinfo.svg +10 -0
- data/examples/info/README.adoc +58 -0
- data/examples/info_showinfo_example.svg +10 -0
- data/examples/kanban/01-simple-board.mmd +8 -0
- data/examples/kanban/01-simple-board.svg +43 -0
- data/examples/kanban/02-workflow.mmd +8 -0
- data/examples/kanban/02-workflow.svg +43 -0
- data/examples/kanban/README.adoc +79 -0
- data/examples/mindmap/01-simple-tree.mmd +19 -0
- data/examples/mindmap/01-simple-tree.svg +61 -0
- data/examples/mindmap/02-knowledge-map.mmd +19 -0
- data/examples/mindmap/02-knowledge-map.svg +61 -0
- data/examples/mindmap/README.adoc +77 -0
- data/examples/packet/01-basic-packet.* +17 -0
- data/examples/packet/01-basic-packet.mmd +4 -0
- data/examples/packet/01-basic-packet.svg +82 -0
- data/examples/packet/README.adoc +58 -0
- data/examples/pie/01-simple-chart.mmd +5 -0
- data/examples/pie/01-simple-chart.svg +17 -0
- data/examples/pie/02-labeled-slices.mmd +6 -0
- data/examples/pie/02-labeled-slices.svg +19 -0
- data/examples/pie/README.adoc +75 -0
- data/examples/quadrant/01-basic-quadrant.mmd +13 -0
- data/examples/quadrant/01-basic-quadrant.svg +33 -0
- data/examples/quadrant/02-positioned-items.mmd +14 -0
- data/examples/quadrant/02-positioned-items.svg +35 -0
- data/examples/quadrant/README.adoc +84 -0
- data/examples/radar/01-simple-radar.* +5 -0
- data/examples/radar/01-simple-radar.mmd +3 -0
- data/examples/radar/01-simple-radar.svg +25 -0
- data/examples/radar/02-multiple-curves.mmd +4 -0
- data/examples/radar/02-multiple-curves.svg +43 -0
- data/examples/radar/README.adoc +75 -0
- data/examples/requirement/01-basic-requirements.mmd +23 -0
- data/examples/requirement/01-basic-requirements.svg +49 -0
- data/examples/requirement/02-risk-levels.mmd +23 -0
- data/examples/requirement/02-risk-levels.svg +49 -0
- data/examples/requirement/README.adoc +85 -0
- data/examples/sankey/01-simple-flow.mmd +7 -0
- data/examples/sankey/01-simple-flow.svg +34 -0
- data/examples/sankey/02-multi-stage.mmd +11 -0
- data/examples/sankey/02-multi-stage.svg +44 -0
- data/examples/sankey/README.adoc +74 -0
- data/examples/sequence/01-basic-sequence.mmd +27 -0
- data/examples/sequence/01-basic-sequence.svg +5 -0
- data/examples/sequence/02-activations.mmd +17 -0
- data/examples/sequence/02-activations.svg +78 -0
- data/examples/sequence/README.adoc +86 -0
- data/examples/state_diagram/01-simple-states.mmd +29 -0
- data/examples/state_diagram/01-simple-states.svg +5 -0
- data/examples/state_diagram/02-composite.mmd +19 -0
- data/examples/state_diagram/02-composite.svg +81 -0
- data/examples/state_diagram/README.adoc +90 -0
- data/examples/timeline/01-simple-timeline.mmd +11 -0
- data/examples/timeline/01-simple-timeline.svg +36 -0
- data/examples/timeline/02-periods.mmd +15 -0
- data/examples/timeline/02-periods.svg +47 -0
- data/examples/timeline/README.adoc +78 -0
- data/examples/treemap/01-basic-treemap.mmd +12 -0
- data/examples/treemap/01-basic-treemap.svg +59 -0
- data/examples/treemap/README.adoc +59 -0
- data/examples/user_journey/01-simple-journey.mmd +23 -0
- data/examples/user_journey/01-simple-journey.svg +5 -0
- data/examples/user_journey/02-multi-actor.mmd +18 -0
- data/examples/user_journey/02-multi-actor.svg +129 -0
- data/examples/user_journey/README.adoc +81 -0
- data/examples/xychart/01-line-chart.mmd +5 -0
- data/examples/xychart/01-line-chart.svg +43 -0
- data/examples/xychart/02-bar-chart.mmd +7 -0
- data/examples/xychart/02-bar-chart.svg +48 -0
- data/examples/xychart/README.adoc +80 -0
- data/exe/sirena +7 -0
- data/lib/sirena/cli.rb +138 -0
- data/lib/sirena/commands/batch.rb +117 -0
- data/lib/sirena/commands/render.rb +80 -0
- data/lib/sirena/commands/types.rb +29 -0
- data/lib/sirena/commands/version.rb +24 -0
- data/lib/sirena/diagram/architecture.rb +46 -0
- data/lib/sirena/diagram/base.rb +61 -0
- data/lib/sirena/diagram/block.rb +81 -0
- data/lib/sirena/diagram/c4.rb +328 -0
- data/lib/sirena/diagram/class_diagram.rb +385 -0
- data/lib/sirena/diagram/er_diagram.rb +238 -0
- data/lib/sirena/diagram/error.rb +38 -0
- data/lib/sirena/diagram/flowchart.rb +160 -0
- data/lib/sirena/diagram/gantt.rb +71 -0
- data/lib/sirena/diagram/git_graph.rb +36 -0
- data/lib/sirena/diagram/info.rb +38 -0
- data/lib/sirena/diagram/kanban.rb +178 -0
- data/lib/sirena/diagram/mindmap.rb +54 -0
- data/lib/sirena/diagram/packet.rb +79 -0
- data/lib/sirena/diagram/pie.rb +115 -0
- data/lib/sirena/diagram/quadrant.rb +138 -0
- data/lib/sirena/diagram/radar.rb +52 -0
- data/lib/sirena/diagram/requirement.rb +133 -0
- data/lib/sirena/diagram/sankey.rb +217 -0
- data/lib/sirena/diagram/sequence.rb +242 -0
- data/lib/sirena/diagram/state_diagram.rb +237 -0
- data/lib/sirena/diagram/timeline.rb +171 -0
- data/lib/sirena/diagram/treemap.rb +84 -0
- data/lib/sirena/diagram/user_journey.rb +149 -0
- data/lib/sirena/diagram/xy_chart.rb +76 -0
- data/lib/sirena/diagram.rb +8 -0
- data/lib/sirena/diagram_registry.rb +101 -0
- data/lib/sirena/engine.rb +292 -0
- data/lib/sirena/parser/architecture.rb +41 -0
- data/lib/sirena/parser/base.rb +41 -0
- data/lib/sirena/parser/block.rb +72 -0
- data/lib/sirena/parser/c4.rb +53 -0
- data/lib/sirena/parser/class_diagram.rb +63 -0
- data/lib/sirena/parser/er_diagram.rb +40 -0
- data/lib/sirena/parser/error.rb +49 -0
- data/lib/sirena/parser/flowchart.rb +71 -0
- data/lib/sirena/parser/gantt.rb +60 -0
- data/lib/sirena/parser/git_graph.rb +95 -0
- data/lib/sirena/parser/grammars/architecture.rb +145 -0
- data/lib/sirena/parser/grammars/block.rb +190 -0
- data/lib/sirena/parser/grammars/c4.rb +226 -0
- data/lib/sirena/parser/grammars/class_diagram.rb +284 -0
- data/lib/sirena/parser/grammars/common.rb +84 -0
- data/lib/sirena/parser/grammars/er_diagram.rb +114 -0
- data/lib/sirena/parser/grammars/error.rb +40 -0
- data/lib/sirena/parser/grammars/flowchart.rb +298 -0
- data/lib/sirena/parser/grammars/gantt.rb +252 -0
- data/lib/sirena/parser/grammars/git_graph.rb +167 -0
- data/lib/sirena/parser/grammars/info.rb +58 -0
- data/lib/sirena/parser/grammars/kanban.rb +83 -0
- data/lib/sirena/parser/grammars/mindmap.rb +115 -0
- data/lib/sirena/parser/grammars/packet.rb +73 -0
- data/lib/sirena/parser/grammars/pie.rb +128 -0
- data/lib/sirena/parser/grammars/quadrant.rb +199 -0
- data/lib/sirena/parser/grammars/radar.rb +150 -0
- data/lib/sirena/parser/grammars/requirement.rb +188 -0
- data/lib/sirena/parser/grammars/sankey.rb +104 -0
- data/lib/sirena/parser/grammars/sequence.rb +247 -0
- data/lib/sirena/parser/grammars/state_diagram.rb +172 -0
- data/lib/sirena/parser/grammars/timeline.rb +142 -0
- data/lib/sirena/parser/grammars/treemap.rb +120 -0
- data/lib/sirena/parser/grammars/xy_chart.rb +120 -0
- data/lib/sirena/parser/info.rb +49 -0
- data/lib/sirena/parser/kanban.rb +97 -0
- data/lib/sirena/parser/mindmap.rb +106 -0
- data/lib/sirena/parser/packet.rb +76 -0
- data/lib/sirena/parser/pie.rb +49 -0
- data/lib/sirena/parser/quadrant.rb +57 -0
- data/lib/sirena/parser/radar.rb +104 -0
- data/lib/sirena/parser/requirement.rb +70 -0
- data/lib/sirena/parser/sankey.rb +64 -0
- data/lib/sirena/parser/sequence.rb +51 -0
- data/lib/sirena/parser/state_diagram.rb +69 -0
- data/lib/sirena/parser/timeline.rb +57 -0
- data/lib/sirena/parser/transforms/architecture.rb +97 -0
- data/lib/sirena/parser/transforms/block.rb +254 -0
- data/lib/sirena/parser/transforms/c4.rb +347 -0
- data/lib/sirena/parser/transforms/class_diagram.rb +352 -0
- data/lib/sirena/parser/transforms/er_diagram.rb +169 -0
- data/lib/sirena/parser/transforms/error.rb +58 -0
- data/lib/sirena/parser/transforms/flowchart.rb +293 -0
- data/lib/sirena/parser/transforms/gantt.rb +215 -0
- data/lib/sirena/parser/transforms/git_graph.rb +160 -0
- data/lib/sirena/parser/transforms/info.rb +58 -0
- data/lib/sirena/parser/transforms/kanban.rb +176 -0
- data/lib/sirena/parser/transforms/mindmap.rb +227 -0
- data/lib/sirena/parser/transforms/packet.rb +63 -0
- data/lib/sirena/parser/transforms/pie.rb +143 -0
- data/lib/sirena/parser/transforms/quadrant.rb +177 -0
- data/lib/sirena/parser/transforms/radar.rb +126 -0
- data/lib/sirena/parser/transforms/requirement.rb +272 -0
- data/lib/sirena/parser/transforms/sankey.rb +122 -0
- data/lib/sirena/parser/transforms/sequence.rb +342 -0
- data/lib/sirena/parser/transforms/state_diagram.rb +292 -0
- data/lib/sirena/parser/transforms/timeline.rb +177 -0
- data/lib/sirena/parser/transforms/treemap.rb +81 -0
- data/lib/sirena/parser/transforms/xy_chart.rb +132 -0
- data/lib/sirena/parser/treemap.rb +98 -0
- data/lib/sirena/parser/user_journey.rb +120 -0
- data/lib/sirena/parser/xy_chart.rb +114 -0
- data/lib/sirena/parser.rb +8 -0
- data/lib/sirena/renderer/architecture.rb +251 -0
- data/lib/sirena/renderer/base.rb +251 -0
- data/lib/sirena/renderer/block.rb +286 -0
- data/lib/sirena/renderer/c4.rb +490 -0
- data/lib/sirena/renderer/class_diagram.rb +499 -0
- data/lib/sirena/renderer/er_diagram.rb +417 -0
- data/lib/sirena/renderer/error.rb +131 -0
- data/lib/sirena/renderer/flowchart.rb +301 -0
- data/lib/sirena/renderer/gantt.rb +331 -0
- data/lib/sirena/renderer/git_graph.rb +368 -0
- data/lib/sirena/renderer/info.rb +93 -0
- data/lib/sirena/renderer/kanban.rb +295 -0
- data/lib/sirena/renderer/mindmap.rb +396 -0
- data/lib/sirena/renderer/packet.rb +239 -0
- data/lib/sirena/renderer/pie.rb +235 -0
- data/lib/sirena/renderer/quadrant.rb +292 -0
- data/lib/sirena/renderer/radar.rb +323 -0
- data/lib/sirena/renderer/requirement.rb +371 -0
- data/lib/sirena/renderer/sankey.rb +255 -0
- data/lib/sirena/renderer/sequence.rb +424 -0
- data/lib/sirena/renderer/state_diagram.rb +328 -0
- data/lib/sirena/renderer/timeline.rb +304 -0
- data/lib/sirena/renderer/treemap.rb +152 -0
- data/lib/sirena/renderer/user_journey.rb +331 -0
- data/lib/sirena/renderer/xy_chart.rb +452 -0
- data/lib/sirena/renderer.rb +8 -0
- data/lib/sirena/svg/circle.rb +41 -0
- data/lib/sirena/svg/document.rb +103 -0
- data/lib/sirena/svg/element.rb +65 -0
- data/lib/sirena/svg/ellipse.rb +33 -0
- data/lib/sirena/svg/group.rb +71 -0
- data/lib/sirena/svg/line.rb +49 -0
- data/lib/sirena/svg/path.rb +76 -0
- data/lib/sirena/svg/polygon.rb +43 -0
- data/lib/sirena/svg/polyline.rb +35 -0
- data/lib/sirena/svg/rect.rb +57 -0
- data/lib/sirena/svg/style.rb +44 -0
- data/lib/sirena/svg/text.rb +72 -0
- data/lib/sirena/svg.rb +19 -0
- data/lib/sirena/text_measurement.rb +71 -0
- data/lib/sirena/theme/builtin/dark.yml +70 -0
- data/lib/sirena/theme/builtin/default.yml +80 -0
- data/lib/sirena/theme/builtin/high_contrast.yml +70 -0
- data/lib/sirena/theme/builtin/light.yml +70 -0
- data/lib/sirena/theme/color_palette.rb +48 -0
- data/lib/sirena/theme/effect_styles.rb +28 -0
- data/lib/sirena/theme/registry.rb +41 -0
- data/lib/sirena/theme/shape_styles.rb +28 -0
- data/lib/sirena/theme/spacing_config.rb +24 -0
- data/lib/sirena/theme/typography.rb +30 -0
- data/lib/sirena/theme.rb +69 -0
- data/lib/sirena/transform/architecture.rb +273 -0
- data/lib/sirena/transform/base.rb +199 -0
- data/lib/sirena/transform/block.rb +215 -0
- data/lib/sirena/transform/c4.rb +288 -0
- data/lib/sirena/transform/class_diagram.rb +296 -0
- data/lib/sirena/transform/er_diagram.rb +204 -0
- data/lib/sirena/transform/error.rb +39 -0
- data/lib/sirena/transform/flowchart.rb +161 -0
- data/lib/sirena/transform/gantt.rb +253 -0
- data/lib/sirena/transform/git_graph.rb +283 -0
- data/lib/sirena/transform/info.rb +39 -0
- data/lib/sirena/transform/kanban.rb +180 -0
- data/lib/sirena/transform/mindmap.rb +251 -0
- data/lib/sirena/transform/packet.rb +185 -0
- data/lib/sirena/transform/pie.rb +62 -0
- data/lib/sirena/transform/quadrant.rb +167 -0
- data/lib/sirena/transform/radar.rb +227 -0
- data/lib/sirena/transform/requirement.rb +233 -0
- data/lib/sirena/transform/sankey.rb +212 -0
- data/lib/sirena/transform/sequence.rb +143 -0
- data/lib/sirena/transform/state_diagram.rb +228 -0
- data/lib/sirena/transform/timeline.rb +139 -0
- data/lib/sirena/transform/treemap.rb +120 -0
- data/lib/sirena/transform/user_journey.rb +207 -0
- data/lib/sirena/transform/xy_chart.rb +273 -0
- data/lib/sirena/transform.rb +8 -0
- data/lib/sirena/version.rb +5 -0
- data/lib/sirena.rb +328 -0
- data/lib/tasks/benchmark.rake +532 -0
- data/lib/tasks/examples.rake +468 -0
- data/lib/tasks/generate_mermaid_fixtures.rake +363 -0
- data/lib/tasks/mermaid_fixtures.rake +46 -0
- data/scripts/extract_mermaid_tests.rb +493 -0
- data/scripts/rename_to_sirena.rb +73 -0
- data/sirena.gemspec +47 -0
- metadata +529 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../svg/document"
|
|
4
|
+
require_relative "../svg/circle"
|
|
5
|
+
require_relative "../svg/line"
|
|
6
|
+
require_relative "../svg/path"
|
|
7
|
+
require_relative "../svg/text"
|
|
8
|
+
require_relative "../svg/group"
|
|
9
|
+
|
|
10
|
+
module Sirena
|
|
11
|
+
module Renderer
|
|
12
|
+
# Renders a Git Graph layout to SVG.
|
|
13
|
+
#
|
|
14
|
+
# The renderer converts the positioned layout structure from
|
|
15
|
+
# Transform::GitGraph into an SVG visualization showing:
|
|
16
|
+
# - Commit circles at calculated positions
|
|
17
|
+
# - Branch lines connecting parent-child commits
|
|
18
|
+
# - Merge arrows for merge commits
|
|
19
|
+
# - Cherry-pick indicators
|
|
20
|
+
# - Labels for commit IDs, tags, and branches
|
|
21
|
+
#
|
|
22
|
+
# @example Render a git graph
|
|
23
|
+
# renderer = Renderer::GitGraph.new(theme: my_theme)
|
|
24
|
+
# svg = renderer.render(layout)
|
|
25
|
+
class GitGraph < Base
|
|
26
|
+
# Renders the layout structure to SVG.
|
|
27
|
+
#
|
|
28
|
+
# @param layout [Hash] layout data from Transform::GitGraph
|
|
29
|
+
# @return [Svg::Document] rendered SVG document
|
|
30
|
+
def render(layout)
|
|
31
|
+
svg = create_document_from_layout(layout)
|
|
32
|
+
|
|
33
|
+
# Render in order: connections, then commits, then labels
|
|
34
|
+
render_connections(layout, svg)
|
|
35
|
+
render_commits(layout, svg)
|
|
36
|
+
render_labels(layout, svg)
|
|
37
|
+
|
|
38
|
+
svg
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
# Creates an SVG document with dimensions from layout.
|
|
44
|
+
#
|
|
45
|
+
# @param layout [Hash] layout data
|
|
46
|
+
# @return [Svg::Document] new SVG document
|
|
47
|
+
def create_document_from_layout(layout)
|
|
48
|
+
padding = 40
|
|
49
|
+
|
|
50
|
+
Svg::Document.new.tap do |doc|
|
|
51
|
+
doc.width = layout[:width] + (padding * 2)
|
|
52
|
+
doc.height = layout[:height] + (padding * 2)
|
|
53
|
+
doc.view_box = "0 0 #{doc.width} #{doc.height}"
|
|
54
|
+
|
|
55
|
+
# Add a group with padding offset
|
|
56
|
+
@offset_x = padding
|
|
57
|
+
@offset_y = padding
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Renders all connections between commits.
|
|
62
|
+
#
|
|
63
|
+
# @param layout [Hash] layout data
|
|
64
|
+
# @param svg [Svg::Document] SVG document
|
|
65
|
+
# @return [void]
|
|
66
|
+
def render_connections(layout, svg)
|
|
67
|
+
# Get branch colors for connections
|
|
68
|
+
branch_colors = layout[:branches].each_with_object({}) do |b, h|
|
|
69
|
+
h[b[:name]] = b[:color]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
layout[:connections].each do |connection|
|
|
73
|
+
case connection[:type]
|
|
74
|
+
when :merge
|
|
75
|
+
render_merge_connection(connection, branch_colors, svg)
|
|
76
|
+
when :cherry_pick
|
|
77
|
+
render_cherry_pick_connection(connection, branch_colors, svg)
|
|
78
|
+
else
|
|
79
|
+
render_normal_connection(connection, branch_colors, svg)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Renders a normal parent-child connection.
|
|
85
|
+
#
|
|
86
|
+
# @param connection [Hash] connection data
|
|
87
|
+
# @param branch_colors [Hash] branch name to color mapping
|
|
88
|
+
# @param svg [Svg::Document] SVG document
|
|
89
|
+
# @return [void]
|
|
90
|
+
def render_normal_connection(connection, branch_colors, svg)
|
|
91
|
+
color = branch_colors[connection[:from_branch]] ||
|
|
92
|
+
branch_colors[connection[:to_branch]] ||
|
|
93
|
+
theme_color(:edge_stroke) || "#666666"
|
|
94
|
+
|
|
95
|
+
from_x = connection[:from_x] + @offset_x
|
|
96
|
+
from_y = connection[:from_y] + @offset_y
|
|
97
|
+
to_x = connection[:to_x] + @offset_x
|
|
98
|
+
to_y = connection[:to_y] + @offset_y
|
|
99
|
+
|
|
100
|
+
# Draw line from parent to child
|
|
101
|
+
line = Svg::Line.new.tap do |l|
|
|
102
|
+
l.x1 = from_x
|
|
103
|
+
l.y1 = from_y
|
|
104
|
+
l.x2 = to_x
|
|
105
|
+
l.y2 = to_y
|
|
106
|
+
l.stroke = color
|
|
107
|
+
l.stroke_width = "2"
|
|
108
|
+
l.fill = "none"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
svg.add_element(line)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Renders a merge connection with curved path.
|
|
115
|
+
#
|
|
116
|
+
# @param connection [Hash] connection data
|
|
117
|
+
# @param branch_colors [Hash] branch name to color mapping
|
|
118
|
+
# @param svg [Svg::Document] SVG document
|
|
119
|
+
# @return [void]
|
|
120
|
+
def render_merge_connection(connection, branch_colors, svg)
|
|
121
|
+
color = branch_colors[connection[:from_branch]] ||
|
|
122
|
+
theme_color(:edge_stroke) || "#666666"
|
|
123
|
+
|
|
124
|
+
from_x = connection[:from_x] + @offset_x
|
|
125
|
+
from_y = connection[:from_y] + @offset_y
|
|
126
|
+
to_x = connection[:to_x] + @offset_x
|
|
127
|
+
to_y = connection[:to_y] + @offset_y
|
|
128
|
+
|
|
129
|
+
# Create curved path for merge
|
|
130
|
+
if from_y != to_y
|
|
131
|
+
# Different lanes - use bezier curve
|
|
132
|
+
control_x1 = from_x + (to_x - from_x) * 0.5
|
|
133
|
+
control_y1 = from_y
|
|
134
|
+
control_x2 = from_x + (to_x - from_x) * 0.5
|
|
135
|
+
control_y2 = to_y
|
|
136
|
+
|
|
137
|
+
path_data = "M #{from_x} #{from_y} " \
|
|
138
|
+
"C #{control_x1} #{control_y1}, " \
|
|
139
|
+
"#{control_x2} #{control_y2}, " \
|
|
140
|
+
"#{to_x} #{to_y}"
|
|
141
|
+
else
|
|
142
|
+
# Same lane - straight line
|
|
143
|
+
path_data = "M #{from_x} #{from_y} L #{to_x} #{to_y}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
path = Svg::Path.new.tap do |p|
|
|
147
|
+
p.d = path_data
|
|
148
|
+
p.stroke = color
|
|
149
|
+
p.stroke_width = "2"
|
|
150
|
+
p.fill = "none"
|
|
151
|
+
p.stroke_dasharray = "5,3" # Dashed for merge
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
svg.add_element(path)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Renders a cherry-pick connection with dotted line.
|
|
158
|
+
#
|
|
159
|
+
# @param connection [Hash] connection data
|
|
160
|
+
# @param branch_colors [Hash] branch name to color mapping
|
|
161
|
+
# @param svg [Svg::Document] SVG document
|
|
162
|
+
# @return [void]
|
|
163
|
+
def render_cherry_pick_connection(connection, branch_colors, svg)
|
|
164
|
+
color = theme_color(:edge_stroke) || "#999999"
|
|
165
|
+
|
|
166
|
+
from_x = connection[:from_x] + @offset_x
|
|
167
|
+
from_y = connection[:from_y] + @offset_y
|
|
168
|
+
to_x = connection[:to_x] + @offset_x
|
|
169
|
+
to_y = connection[:to_y] + @offset_y
|
|
170
|
+
|
|
171
|
+
path = Svg::Path.new.tap do |p|
|
|
172
|
+
p.d = "M #{from_x} #{from_y} L #{to_x} #{to_y}"
|
|
173
|
+
p.stroke = color
|
|
174
|
+
p.stroke_width = "2"
|
|
175
|
+
p.fill = "none"
|
|
176
|
+
p.stroke_dasharray = "2,4" # Dotted for cherry-pick
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
svg.add_element(path)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Renders all commits as circles.
|
|
183
|
+
#
|
|
184
|
+
# @param layout [Hash] layout data
|
|
185
|
+
# @param svg [Svg::Document] SVG document
|
|
186
|
+
# @return [void]
|
|
187
|
+
def render_commits(layout, svg)
|
|
188
|
+
# Get branch colors
|
|
189
|
+
branch_colors = layout[:branches].each_with_object({}) do |b, h|
|
|
190
|
+
h[b[:name]] = b[:color]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
layout[:commits].each do |commit|
|
|
194
|
+
render_commit_circle(commit, branch_colors, svg)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Renders a single commit as a circle.
|
|
199
|
+
#
|
|
200
|
+
# @param commit [Hash] commit data
|
|
201
|
+
# @param branch_colors [Hash] branch name to color mapping
|
|
202
|
+
# @param svg [Svg::Document] SVG document
|
|
203
|
+
# @return [void]
|
|
204
|
+
def render_commit_circle(commit, branch_colors, svg)
|
|
205
|
+
x = commit[:x] + @offset_x
|
|
206
|
+
y = commit[:y] + @offset_y
|
|
207
|
+
radius = 8
|
|
208
|
+
|
|
209
|
+
# Get color based on branch and commit type
|
|
210
|
+
fill_color = get_commit_fill_color(commit, branch_colors)
|
|
211
|
+
stroke_color = get_commit_stroke_color(commit, branch_colors)
|
|
212
|
+
|
|
213
|
+
circle = Svg::Circle.new.tap do |c|
|
|
214
|
+
c.cx = x
|
|
215
|
+
c.cy = y
|
|
216
|
+
c.r = radius
|
|
217
|
+
c.fill = fill_color
|
|
218
|
+
c.stroke = stroke_color
|
|
219
|
+
c.stroke_width = "2"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
svg.add_element(circle)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Gets the fill color for a commit based on type and branch.
|
|
226
|
+
#
|
|
227
|
+
# @param commit [Hash] commit data
|
|
228
|
+
# @param branch_colors [Hash] branch colors
|
|
229
|
+
# @return [String] color value
|
|
230
|
+
def get_commit_fill_color(commit, branch_colors)
|
|
231
|
+
case commit[:type]
|
|
232
|
+
when "HIGHLIGHT"
|
|
233
|
+
theme_color(:highlight) || "#fbbf24"
|
|
234
|
+
when "REVERSE"
|
|
235
|
+
theme_color(:background) || "#ffffff"
|
|
236
|
+
else # NORMAL
|
|
237
|
+
branch_colors[commit[:branch]] || theme_color(:primary) || "#2563eb"
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Gets the stroke color for a commit.
|
|
242
|
+
#
|
|
243
|
+
# @param commit [Hash] commit data
|
|
244
|
+
# @param branch_colors [Hash] branch colors
|
|
245
|
+
# @return [String] color value
|
|
246
|
+
def get_commit_stroke_color(commit, branch_colors)
|
|
247
|
+
case commit[:type]
|
|
248
|
+
when "REVERSE"
|
|
249
|
+
branch_colors[commit[:branch]] || theme_color(:primary) || "#2563eb"
|
|
250
|
+
else
|
|
251
|
+
branch_colors[commit[:branch]] || theme_color(:primary) || "#2563eb"
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Renders all labels (commit IDs, tags, branches).
|
|
256
|
+
#
|
|
257
|
+
# @param layout [Hash] layout data
|
|
258
|
+
# @param svg [Svg::Document] SVG document
|
|
259
|
+
# @return [void]
|
|
260
|
+
def render_labels(layout, svg)
|
|
261
|
+
layout[:commits].each do |commit|
|
|
262
|
+
render_commit_labels(commit, svg)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Render branch labels at the end of each branch
|
|
266
|
+
render_branch_labels(layout, svg)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Renders labels for a single commit.
|
|
270
|
+
#
|
|
271
|
+
# @param commit [Hash] commit data
|
|
272
|
+
# @param svg [Svg::Document] SVG document
|
|
273
|
+
# @return [void]
|
|
274
|
+
def render_commit_labels(commit, svg)
|
|
275
|
+
x = commit[:x] + @offset_x
|
|
276
|
+
y = commit[:y] + @offset_y
|
|
277
|
+
|
|
278
|
+
# Render commit ID below the circle if present
|
|
279
|
+
if commit[:id] && !commit[:id].start_with?("commit_")
|
|
280
|
+
render_commit_id_label(commit[:id], x, y, svg)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Render tag above the circle if present
|
|
284
|
+
render_tag_label(commit[:tag], x, y, svg) if commit[:tag]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Renders a commit ID label.
|
|
288
|
+
#
|
|
289
|
+
# @param id [String] commit ID
|
|
290
|
+
# @param x [Numeric] X position
|
|
291
|
+
# @param y [Numeric] Y position
|
|
292
|
+
# @param svg [Svg::Document] SVG document
|
|
293
|
+
# @return [void]
|
|
294
|
+
def render_commit_id_label(id, x, y, svg)
|
|
295
|
+
text = Svg::Text.new.tap do |t|
|
|
296
|
+
t.x = x
|
|
297
|
+
t.y = y + 20
|
|
298
|
+
t.text_anchor = "middle"
|
|
299
|
+
t.fill = theme_color(:label_text) || "#000000"
|
|
300
|
+
t.font_size = (theme_typography(:font_size_small) || 10).to_s
|
|
301
|
+
t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
|
|
302
|
+
t.content = id
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
svg.add_element(text)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Renders a tag label.
|
|
309
|
+
#
|
|
310
|
+
# @param tag [String] tag name
|
|
311
|
+
# @param x [Numeric] X position
|
|
312
|
+
# @param y [Numeric] Y position
|
|
313
|
+
# @param svg [Svg::Document] SVG document
|
|
314
|
+
# @return [void]
|
|
315
|
+
def render_tag_label(tag, x, y, svg)
|
|
316
|
+
text = Svg::Text.new.tap do |t|
|
|
317
|
+
t.x = x
|
|
318
|
+
t.y = y - 15
|
|
319
|
+
t.text_anchor = "middle"
|
|
320
|
+
t.fill = theme_color(:accent) || "#7c3aed"
|
|
321
|
+
t.font_size = (theme_typography(:font_size_small) || 10).to_s
|
|
322
|
+
t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
|
|
323
|
+
t.font_weight = "bold"
|
|
324
|
+
t.content = tag
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
svg.add_element(text)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Renders branch name labels.
|
|
331
|
+
#
|
|
332
|
+
# @param layout [Hash] layout data
|
|
333
|
+
# @param svg [Svg::Document] SVG document
|
|
334
|
+
# @return [void]
|
|
335
|
+
def render_branch_labels(layout, svg)
|
|
336
|
+
# Find the last commit for each branch
|
|
337
|
+
branch_last_commits = {}
|
|
338
|
+
|
|
339
|
+
layout[:commits].each do |commit|
|
|
340
|
+
branch = commit[:branch]
|
|
341
|
+
branch_last_commits[branch] = commit
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Render label for each branch at its last commit
|
|
345
|
+
branch_last_commits.each do |branch_name, commit|
|
|
346
|
+
x = commit[:x] + @offset_x + 15
|
|
347
|
+
y = commit[:y] + @offset_y
|
|
348
|
+
|
|
349
|
+
branch_meta = layout[:branches].find { |b| b[:name] == branch_name }
|
|
350
|
+
color = branch_meta&.dig(:color) || theme_color(:primary) || "#2563eb"
|
|
351
|
+
|
|
352
|
+
text = Svg::Text.new.tap do |t|
|
|
353
|
+
t.x = x
|
|
354
|
+
t.y = y + 4
|
|
355
|
+
t.text_anchor = "start"
|
|
356
|
+
t.fill = color
|
|
357
|
+
t.font_size = (theme_typography(:font_size_small) || 10).to_s
|
|
358
|
+
t.font_family = theme_typography(:font_family) || "Arial, sans-serif"
|
|
359
|
+
t.font_weight = "bold"
|
|
360
|
+
t.content = branch_name
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
svg.add_element(text)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative '../svg/document'
|
|
5
|
+
require_relative '../svg/rect'
|
|
6
|
+
require_relative '../svg/text'
|
|
7
|
+
|
|
8
|
+
module Sirena
|
|
9
|
+
module Renderer
|
|
10
|
+
# Info diagram renderer for converting info diagrams to SVG.
|
|
11
|
+
#
|
|
12
|
+
# Renders a simple informational message box with centered text.
|
|
13
|
+
#
|
|
14
|
+
# @example Render an info diagram
|
|
15
|
+
# renderer = InfoRenderer.new
|
|
16
|
+
# svg = renderer.render(info_diagram)
|
|
17
|
+
class InfoRenderer < Base
|
|
18
|
+
# Info box dimensions
|
|
19
|
+
BOX_WIDTH = 400
|
|
20
|
+
BOX_HEIGHT = 100
|
|
21
|
+
BOX_X = 50
|
|
22
|
+
BOX_Y = 50
|
|
23
|
+
TEXT_Y = 105
|
|
24
|
+
|
|
25
|
+
# Renders an info diagram to SVG.
|
|
26
|
+
#
|
|
27
|
+
# @param graph [Hash] the info diagram graph structure from transform
|
|
28
|
+
# @return [Svg::Document] the rendered SVG document
|
|
29
|
+
def render(graph)
|
|
30
|
+
svg = create_document_for_info(graph)
|
|
31
|
+
|
|
32
|
+
# Render info box
|
|
33
|
+
render_info_box(graph, svg)
|
|
34
|
+
|
|
35
|
+
# Render info text
|
|
36
|
+
render_info_text(graph, svg)
|
|
37
|
+
|
|
38
|
+
svg
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
def create_document_for_info(_graph)
|
|
44
|
+
width = 500
|
|
45
|
+
height = 200
|
|
46
|
+
|
|
47
|
+
Svg::Document.new.tap do |doc|
|
|
48
|
+
doc.width = width
|
|
49
|
+
doc.height = height
|
|
50
|
+
doc.view_box = "0 0 #{width} #{height}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render_info_box(_graph, svg)
|
|
55
|
+
box = Svg::Rect.new.tap do |r|
|
|
56
|
+
r.x = BOX_X
|
|
57
|
+
r.y = BOX_Y
|
|
58
|
+
r.width = BOX_WIDTH
|
|
59
|
+
r.height = BOX_HEIGHT
|
|
60
|
+
r.fill = theme_color(:node_bg) || '#E3F2FD'
|
|
61
|
+
r.stroke = theme_color(:node_stroke) || '#2196F3'
|
|
62
|
+
r.stroke_width = '2'
|
|
63
|
+
r.rx = '8'
|
|
64
|
+
r.ry = '8'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
svg << box
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def render_info_text(graph, svg)
|
|
71
|
+
message = if graph[:show_info]
|
|
72
|
+
'Info: showInfo enabled'
|
|
73
|
+
else
|
|
74
|
+
'Info'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
text = Svg::Text.new.tap do |t|
|
|
78
|
+
t.x = BOX_X + (BOX_WIDTH / 2)
|
|
79
|
+
t.y = TEXT_Y
|
|
80
|
+
t.content = message
|
|
81
|
+
t.fill = theme_color(:label_text) || '#1976D2'
|
|
82
|
+
t.font_family = theme_typography(:font_family) ||
|
|
83
|
+
'Arial, sans-serif'
|
|
84
|
+
t.font_size = (theme_typography(:font_size_base) || 16).to_s
|
|
85
|
+
t.text_anchor = 'middle'
|
|
86
|
+
t.font_weight = 'bold'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
svg << text
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|