Skip to main content

merman_render/
model.rs

1use merman_core::ParseMetadata;
2use rustc_hash::FxHashMap;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct LayoutMeta {
9    pub diagram_type: String,
10    pub title: Option<String>,
11    pub config: Value,
12    pub effective_config: Value,
13}
14
15impl LayoutMeta {
16    pub fn from_parse_metadata(meta: &ParseMetadata) -> Self {
17        Self {
18            diagram_type: meta.diagram_type.clone(),
19            title: meta.title.clone(),
20            config: meta.config.as_value().clone(),
21            effective_config: meta.effective_config.as_value().clone(),
22        }
23    }
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct Bounds {
28    pub min_x: f64,
29    pub min_y: f64,
30    pub max_x: f64,
31    pub max_y: f64,
32}
33
34impl Bounds {
35    pub fn from_points(points: impl IntoIterator<Item = (f64, f64)>) -> Option<Self> {
36        let mut it = points.into_iter();
37        let (x0, y0) = it.next()?;
38        let mut b = Self {
39            min_x: x0,
40            min_y: y0,
41            max_x: x0,
42            max_y: y0,
43        };
44        for (x, y) in it {
45            b.min_x = b.min_x.min(x);
46            b.min_y = b.min_y.min(y);
47            b.max_x = b.max_x.max(x);
48            b.max_y = b.max_y.max(y);
49        }
50        Some(b)
51    }
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct LayoutPoint {
56    pub x: f64,
57    pub y: f64,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct LayoutLabel {
62    pub x: f64,
63    pub y: f64,
64    pub width: f64,
65    pub height: f64,
66}
67
68#[derive(Debug, Clone)]
69pub struct ClassNodeRowMetrics {
70    pub members: Vec<crate::text::TextMetrics>,
71    pub methods: Vec<crate::text::TextMetrics>,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct LayoutNode {
76    pub id: String,
77    pub x: f64,
78    pub y: f64,
79    pub width: f64,
80    pub height: f64,
81    pub is_cluster: bool,
82    #[serde(skip)]
83    pub label_width: Option<f64>,
84    #[serde(skip)]
85    pub label_height: Option<f64>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct LayoutCluster {
90    pub id: String,
91    pub x: f64,
92    pub y: f64,
93    pub width: f64,
94    pub height: f64,
95    /// Mermaid cluster "diff" value used during cluster positioning.
96    pub diff: f64,
97    /// Mermaid cluster "offsetY" value: title bbox height minus half padding.
98    pub offset_y: f64,
99    pub title: String,
100    pub title_label: LayoutLabel,
101    pub requested_dir: Option<String>,
102    pub effective_dir: String,
103    pub padding: f64,
104    pub title_margin_top: f64,
105    pub title_margin_bottom: f64,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct LayoutEdge {
110    pub id: String,
111    pub from: String,
112    pub to: String,
113    #[serde(default)]
114    pub from_cluster: Option<String>,
115    #[serde(default)]
116    pub to_cluster: Option<String>,
117    pub points: Vec<LayoutPoint>,
118    pub label: Option<LayoutLabel>,
119    #[serde(default)]
120    pub start_label_left: Option<LayoutLabel>,
121    #[serde(default)]
122    pub start_label_right: Option<LayoutLabel>,
123    #[serde(default)]
124    pub end_label_left: Option<LayoutLabel>,
125    #[serde(default)]
126    pub end_label_right: Option<LayoutLabel>,
127    /// Optional SVG marker id for marker-start (e.g. ER cardinality markers).
128    #[serde(default)]
129    pub start_marker: Option<String>,
130    /// Optional SVG marker id for marker-end (e.g. ER cardinality markers).
131    #[serde(default)]
132    pub end_marker: Option<String>,
133    /// Optional SVG dash pattern (e.g. identifying vs non-identifying ER relationships).
134    #[serde(default)]
135    pub stroke_dasharray: Option<String>,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct BlockDiagramLayout {
140    pub nodes: Vec<LayoutNode>,
141    pub edges: Vec<LayoutEdge>,
142    pub bounds: Option<Bounds>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct RequirementDiagramLayout {
147    pub nodes: Vec<LayoutNode>,
148    pub edges: Vec<LayoutEdge>,
149    pub bounds: Option<Bounds>,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct ArchitectureDiagramLayout {
154    pub nodes: Vec<LayoutNode>,
155    pub edges: Vec<LayoutEdge>,
156    /// Local Cytoscape child contribution phases for Architecture services.
157    ///
158    /// These bounds are exposed for source-backed group/content audits. SVG renderers should use
159    /// their own phase-specific logic instead of treating this as a generic root-bounds source.
160    #[serde(default, skip_serializing_if = "Vec::is_empty")]
161    pub cytoscape_service_bounds: Vec<ArchitectureCytoscapeServiceBounds>,
162    /// Final FCoSE compound rectangles exposed for source-backed Architecture audits.
163    ///
164    /// Renderers should not treat these as Mermaid SVG group rects without a phase-specific
165    /// source audit; they are layout-engine rects, not browser `node.boundingBox()` values.
166    #[serde(default, skip_serializing_if = "Vec::is_empty")]
167    pub fcose_compound_bounds: Vec<ArchitectureCompoundBounds>,
168    /// Optional local FCoSE phase trace for source-backed Architecture layout audits.
169    ///
170    /// This is populated only by explicit diagnostic runs and should stay out of normal rendering
171    /// decisions.
172    #[serde(default, skip_serializing_if = "Vec::is_empty")]
173    pub fcose_debug_stages: Vec<ArchitectureFcoseDebugStage>,
174    pub bounds: Option<Bounds>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct ArchitectureCompoundBounds {
179    pub id: String,
180    pub bounds: Bounds,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ArchitectureFcoseDebugStage {
185    pub run_index: usize,
186    pub tag: String,
187    #[serde(default, skip_serializing_if = "Option::is_none")]
188    pub iterations: Option<usize>,
189    #[serde(default, skip_serializing_if = "Option::is_none")]
190    pub bbox: Option<Bounds>,
191    #[serde(default, skip_serializing_if = "Vec::is_empty")]
192    pub nodes: Vec<ArchitectureFcoseDebugNodeBounds>,
193    #[serde(default, skip_serializing_if = "Vec::is_empty")]
194    pub compound_bounds: Vec<ArchitectureCompoundBounds>,
195    #[serde(default, skip_serializing_if = "Option::is_none")]
196    pub relocate: Option<ArchitectureFcoseRelocateDebug>,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct ArchitectureFcoseDebugNodeBounds {
201    pub id: String,
202    pub kind: String,
203    pub bounds: Bounds,
204    #[serde(default, skip_serializing_if = "Option::is_none")]
205    pub displacement: Option<LayoutPoint>,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct ArchitectureFcoseRelocateDebug {
210    pub original_center: LayoutPoint,
211    pub rect_center: LayoutPoint,
212    pub delta: LayoutPoint,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct ArchitectureCytoscapeServiceBounds {
217    pub id: String,
218    #[serde(default, skip_serializing_if = "Option::is_none")]
219    pub in_group: Option<String>,
220    pub body_bounds: Bounds,
221    #[serde(default, skip_serializing_if = "Option::is_none")]
222    pub label_bounds: Option<Bounds>,
223    #[serde(default, skip_serializing_if = "Option::is_none")]
224    pub label_metrics: Option<ArchitectureCytoscapeServiceLabelMetrics>,
225    pub union_bounds: Bounds,
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct ArchitectureCytoscapeServiceLabelMetrics {
230    pub text_width: f64,
231    pub half_width: f64,
232    pub applied_scale: f64,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct MindmapDiagramLayout {
237    pub nodes: Vec<LayoutNode>,
238    pub edges: Vec<LayoutEdge>,
239    pub bounds: Option<Bounds>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct SankeyNodeLayout {
244    pub id: String,
245    pub index: usize,
246    pub depth: usize,
247    pub height: usize,
248    pub layer: usize,
249    pub value: f64,
250    pub x0: f64,
251    pub x1: f64,
252    pub y0: f64,
253    pub y1: f64,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct SankeyLinkLayout {
258    pub index: usize,
259    pub source: String,
260    pub target: String,
261    pub value: f64,
262    pub width: f64,
263    pub y0: f64,
264    pub y1: f64,
265}
266
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct SankeyDiagramLayout {
269    pub bounds: Option<Bounds>,
270    pub width: f64,
271    pub height: f64,
272    pub node_width: f64,
273    pub node_padding: f64,
274    pub nodes: Vec<SankeyNodeLayout>,
275    pub links: Vec<SankeyLinkLayout>,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct RadarAxisLayout {
280    pub label: String,
281    pub angle: f64,
282    pub line_x2: f64,
283    pub line_y2: f64,
284    pub label_x: f64,
285    pub label_y: f64,
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct RadarGraticuleShapeLayout {
290    pub kind: String,
291    pub r: Option<f64>,
292    #[serde(default)]
293    pub points: Vec<LayoutPoint>,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct RadarCurveLayout {
298    pub label: String,
299    pub class_index: i64,
300    #[serde(default)]
301    pub points: Vec<LayoutPoint>,
302    pub path_d: String,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct RadarLegendItemLayout {
307    pub label: String,
308    pub class_index: i64,
309    pub x: f64,
310    pub y: f64,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct RadarDiagramLayout {
315    pub bounds: Option<Bounds>,
316    pub svg_width: f64,
317    pub svg_height: f64,
318    pub center_x: f64,
319    pub center_y: f64,
320    pub radius: f64,
321    pub axis_label_factor: f64,
322    pub title_y: f64,
323    #[serde(default)]
324    pub axes: Vec<RadarAxisLayout>,
325    #[serde(default)]
326    pub graticules: Vec<RadarGraticuleShapeLayout>,
327    #[serde(default)]
328    pub curves: Vec<RadarCurveLayout>,
329    #[serde(default)]
330    pub legend_items: Vec<RadarLegendItemLayout>,
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct TreemapSectionLayout {
335    pub name: String,
336    pub depth: i64,
337    pub value: f64,
338    pub x0: f64,
339    pub y0: f64,
340    pub x1: f64,
341    pub y1: f64,
342    #[serde(default)]
343    pub class_selector: Option<String>,
344    #[serde(default)]
345    pub css_compiled_styles: Option<Vec<String>>,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct TreemapLeafLayout {
350    pub name: String,
351    pub value: f64,
352    #[serde(default)]
353    pub parent_name: Option<String>,
354    pub x0: f64,
355    pub y0: f64,
356    pub x1: f64,
357    pub y1: f64,
358    #[serde(default)]
359    pub class_selector: Option<String>,
360    #[serde(default)]
361    pub css_compiled_styles: Option<Vec<String>>,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct TreemapDiagramLayout {
366    pub title_height: f64,
367    pub width: f64,
368    pub height: f64,
369    pub use_max_width: bool,
370    pub diagram_padding: f64,
371    pub show_values: bool,
372    pub value_format: String,
373    #[serde(default, rename = "accTitle")]
374    pub acc_title: Option<String>,
375    #[serde(default, rename = "accDescr")]
376    pub acc_descr: Option<String>,
377    #[serde(default)]
378    pub title: Option<String>,
379    #[serde(default)]
380    pub sections: Vec<TreemapSectionLayout>,
381    #[serde(default)]
382    pub leaves: Vec<TreemapLeafLayout>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct VennCircleLayout {
387    pub set: String,
388    pub x: f64,
389    pub y: f64,
390    pub radius: f64,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct VennAreaLayout {
395    pub sets: Vec<String>,
396    pub size: f64,
397    #[serde(default, skip_serializing_if = "Option::is_none")]
398    pub label: Option<String>,
399    pub text_x: f64,
400    pub text_y: f64,
401    pub text_disjoint: bool,
402    #[serde(default)]
403    pub circles: Vec<VennCircleLayout>,
404    pub path: String,
405    #[serde(rename = "distinctPath")]
406    pub distinct_path: String,
407}
408
409#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct VennTextDebugCellLayout {
411    pub x: f64,
412    pub y: f64,
413    pub width: f64,
414    pub height: f64,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct VennTextAreaLayout {
419    pub sets: Vec<String>,
420    pub center_x: f64,
421    pub center_y: f64,
422    pub inner_radius: f64,
423    pub font_size: f64,
424    #[serde(default)]
425    pub debug_cells: Vec<VennTextDebugCellLayout>,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
429pub struct VennTextNodeLayout {
430    pub sets: Vec<String>,
431    pub id: String,
432    #[serde(default, skip_serializing_if = "Option::is_none")]
433    pub label: Option<String>,
434    pub x: f64,
435    pub y: f64,
436    pub width: f64,
437    pub height: f64,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize)]
441pub struct VennDiagramLayout {
442    pub bounds: Option<Bounds>,
443    pub width: f64,
444    pub height: f64,
445    pub diagram_height: f64,
446    pub title_height: f64,
447    pub scale: f64,
448    pub padding: f64,
449    pub use_max_width: bool,
450    pub use_debug_layout: bool,
451    #[serde(default)]
452    pub areas: Vec<VennAreaLayout>,
453    #[serde(default)]
454    pub text_areas: Vec<VennTextAreaLayout>,
455    #[serde(default)]
456    pub text_nodes: Vec<VennTextNodeLayout>,
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct XyChartRectData {
461    pub x: f64,
462    pub y: f64,
463    pub width: f64,
464    pub height: f64,
465    pub fill: String,
466    #[serde(rename = "strokeFill")]
467    pub stroke_fill: String,
468    #[serde(rename = "strokeWidth")]
469    pub stroke_width: f64,
470}
471
472#[derive(Debug, Clone, Serialize, Deserialize)]
473pub struct XyChartTextData {
474    pub text: String,
475    pub x: f64,
476    pub y: f64,
477    pub fill: String,
478    #[serde(rename = "fontSize")]
479    pub font_size: f64,
480    #[serde(default)]
481    pub rotation: f64,
482    #[serde(rename = "verticalPos")]
483    pub vertical_pos: String,
484    #[serde(rename = "horizontalPos")]
485    pub horizontal_pos: String,
486}
487
488#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct XyChartPathData {
490    pub path: String,
491    #[serde(default)]
492    pub fill: Option<String>,
493    #[serde(rename = "strokeFill")]
494    pub stroke_fill: String,
495    #[serde(rename = "strokeWidth")]
496    pub stroke_width: f64,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
500#[serde(tag = "type")]
501pub enum XyChartDrawableElem {
502    #[serde(rename = "rect")]
503    Rect {
504        #[serde(rename = "groupTexts")]
505        group_texts: Vec<String>,
506        data: Vec<XyChartRectData>,
507    },
508    #[serde(rename = "text")]
509    Text {
510        #[serde(rename = "groupTexts")]
511        group_texts: Vec<String>,
512        data: Vec<XyChartTextData>,
513    },
514    #[serde(rename = "path")]
515    Path {
516        #[serde(rename = "groupTexts")]
517        group_texts: Vec<String>,
518        data: Vec<XyChartPathData>,
519    },
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize)]
523pub struct XyChartDiagramLayout {
524    pub width: f64,
525    pub height: f64,
526    #[serde(rename = "chartOrientation")]
527    pub chart_orientation: String,
528    #[serde(rename = "showDataLabel")]
529    pub show_data_label: bool,
530    #[serde(rename = "backgroundColor")]
531    pub background_color: String,
532    #[serde(rename = "labelData")]
533    pub label_data: Vec<String>,
534    #[serde(default)]
535    pub drawables: Vec<XyChartDrawableElem>,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct QuadrantChartTextData {
540    pub text: String,
541    pub x: f64,
542    pub y: f64,
543    pub fill: String,
544    #[serde(rename = "fontSize")]
545    pub font_size: f64,
546    #[serde(default)]
547    pub rotation: f64,
548    #[serde(rename = "verticalPos")]
549    pub vertical_pos: String,
550    #[serde(rename = "horizontalPos")]
551    pub horizontal_pos: String,
552}
553
554pub type QuadrantChartAxisLabelData = QuadrantChartTextData;
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
557pub struct QuadrantChartQuadrantData {
558    pub x: f64,
559    pub y: f64,
560    pub width: f64,
561    pub height: f64,
562    pub fill: String,
563    pub text: QuadrantChartTextData,
564}
565
566#[derive(Debug, Clone, Serialize, Deserialize)]
567pub struct QuadrantChartBorderLineData {
568    #[serde(rename = "strokeWidth")]
569    pub stroke_width: f64,
570    #[serde(rename = "strokeFill")]
571    pub stroke_fill: String,
572    pub x1: f64,
573    pub y1: f64,
574    pub x2: f64,
575    pub y2: f64,
576}
577
578#[derive(Debug, Clone, Serialize, Deserialize)]
579pub struct QuadrantChartPointData {
580    pub x: f64,
581    pub y: f64,
582    pub fill: String,
583    pub radius: f64,
584    #[serde(rename = "strokeColor")]
585    pub stroke_color: String,
586    #[serde(rename = "strokeWidth")]
587    pub stroke_width: String,
588    pub text: QuadrantChartTextData,
589}
590
591#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct QuadrantChartDiagramLayout {
593    pub width: f64,
594    pub height: f64,
595    #[serde(default)]
596    pub title: Option<QuadrantChartTextData>,
597    pub quadrants: Vec<QuadrantChartQuadrantData>,
598    #[serde(rename = "borderLines")]
599    pub border_lines: Vec<QuadrantChartBorderLineData>,
600    pub points: Vec<QuadrantChartPointData>,
601    #[serde(rename = "axisLabels")]
602    pub axis_labels: Vec<QuadrantChartAxisLabelData>,
603}
604
605#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct FlowchartV2Layout {
607    pub nodes: Vec<LayoutNode>,
608    pub edges: Vec<LayoutEdge>,
609    pub clusters: Vec<LayoutCluster>,
610    pub bounds: Option<Bounds>,
611    /// Mermaid's DOM insertion order for each extracted root graph (`""` = top-level root).
612    #[serde(skip)]
613    pub dom_node_order_by_root: std::collections::HashMap<String, Vec<String>>,
614}
615
616#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct StateDiagramV2Layout {
618    pub nodes: Vec<LayoutNode>,
619    pub edges: Vec<LayoutEdge>,
620    pub clusters: Vec<LayoutCluster>,
621    pub bounds: Option<Bounds>,
622}
623
624#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct ClassDiagramV2Layout {
626    pub nodes: Vec<LayoutNode>,
627    pub edges: Vec<LayoutEdge>,
628    pub clusters: Vec<LayoutCluster>,
629    pub bounds: Option<Bounds>,
630    #[serde(skip)]
631    pub class_row_metrics_by_id: FxHashMap<String, Arc<ClassNodeRowMetrics>>,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
635pub struct ErDiagramLayout {
636    pub nodes: Vec<LayoutNode>,
637    pub edges: Vec<LayoutEdge>,
638    pub bounds: Option<Bounds>,
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct SequenceDiagramLayout {
643    pub nodes: Vec<LayoutNode>,
644    pub edges: Vec<LayoutEdge>,
645    pub clusters: Vec<LayoutCluster>,
646    pub bounds: Option<Bounds>,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct InfoDiagramLayout {
651    pub bounds: Option<Bounds>,
652    pub version: String,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct PacketBlockLayout {
657    pub start: i64,
658    pub end: i64,
659    pub label: String,
660    pub x: f64,
661    pub y: f64,
662    pub width: f64,
663    pub height: f64,
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize)]
667pub struct PacketWordLayout {
668    pub blocks: Vec<PacketBlockLayout>,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct PacketDiagramLayout {
673    pub bounds: Option<Bounds>,
674    pub width: f64,
675    pub height: f64,
676    pub row_height: f64,
677    pub padding_x: f64,
678    pub padding_y: f64,
679    pub bit_width: f64,
680    pub bits_per_row: i64,
681    pub show_bits: bool,
682    pub words: Vec<PacketWordLayout>,
683}
684
685#[derive(Debug, Clone, Serialize, Deserialize)]
686pub struct PieSliceLayout {
687    pub label: String,
688    pub value: f64,
689    pub start_angle: f64,
690    pub end_angle: f64,
691    pub is_full_circle: bool,
692    pub percent: i64,
693    pub text_x: f64,
694    pub text_y: f64,
695    pub fill: String,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct PieLegendItemLayout {
700    pub label: String,
701    pub value: f64,
702    pub fill: String,
703    pub y: f64,
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
707pub struct PieDiagramLayout {
708    pub bounds: Option<Bounds>,
709    pub center_x: f64,
710    pub center_y: f64,
711    pub radius: f64,
712    pub outer_radius: f64,
713    pub legend_x: f64,
714    pub legend_start_y: f64,
715    pub legend_step_y: f64,
716    pub slices: Vec<PieSliceLayout>,
717    pub legend_items: Vec<PieLegendItemLayout>,
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize)]
721pub struct TimelineNodeLayout {
722    pub x: f64,
723    pub y: f64,
724    pub width: f64,
725    pub height: f64,
726    /// Width used for wrapping (excluding padding).
727    pub content_width: f64,
728    /// Padding used to compute `width` (Mermaid 11.12.2 uses 20).
729    pub padding: f64,
730    pub section_class: String,
731    pub label: String,
732    /// Wrapped lines as rendered into `<tspan>` nodes.
733    pub label_lines: Vec<String>,
734    pub kind: String,
735}
736
737#[derive(Debug, Clone, Serialize, Deserialize)]
738pub struct TimelineLineLayout {
739    pub kind: String,
740    pub x1: f64,
741    pub y1: f64,
742    pub x2: f64,
743    pub y2: f64,
744}
745
746#[derive(Debug, Clone, Serialize, Deserialize)]
747pub struct TimelineTaskLayout {
748    pub node: TimelineNodeLayout,
749    pub connector: TimelineLineLayout,
750    pub events: Vec<TimelineNodeLayout>,
751}
752
753#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct TimelineSectionLayout {
755    pub node: TimelineNodeLayout,
756    pub tasks: Vec<TimelineTaskLayout>,
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
760pub struct TimelineDiagramLayout {
761    pub bounds: Option<Bounds>,
762    pub left_margin: f64,
763    pub base_x: f64,
764    pub base_y: f64,
765    /// `svg.node().getBBox().width` computed *before* title/activity line are inserted.
766    pub pre_title_box_width: f64,
767    pub sections: Vec<TimelineSectionLayout>,
768    #[serde(default)]
769    pub orphan_tasks: Vec<TimelineTaskLayout>,
770    pub activity_line: TimelineLineLayout,
771    pub title: Option<String>,
772    pub title_x: f64,
773    pub title_y: f64,
774}
775
776#[derive(Debug, Clone, Serialize, Deserialize)]
777pub struct JourneyActorLegendLineLayout {
778    pub text: String,
779    pub x: f64,
780    pub y: f64,
781    pub tspan_x: f64,
782    pub text_margin: f64,
783}
784
785#[derive(Debug, Clone, Serialize, Deserialize)]
786pub struct JourneyActorLegendItemLayout {
787    pub actor: String,
788    pub pos: i64,
789    pub color: String,
790    pub circle_cx: f64,
791    pub circle_cy: f64,
792    pub circle_r: f64,
793    pub label_lines: Vec<JourneyActorLegendLineLayout>,
794}
795
796#[derive(Debug, Clone, Serialize, Deserialize)]
797pub enum JourneyMouthKind {
798    Smile,
799    Sad,
800    Ambivalent,
801}
802
803#[derive(Debug, Clone, Serialize, Deserialize)]
804pub struct JourneyTaskActorCircleLayout {
805    pub actor: String,
806    pub pos: i64,
807    pub color: String,
808    pub cx: f64,
809    pub cy: f64,
810    pub r: f64,
811}
812
813#[derive(Debug, Clone, Serialize, Deserialize)]
814pub struct JourneyTaskLayout {
815    pub index: i64,
816    pub section: String,
817    pub task: String,
818    pub score: i64,
819    pub x: f64,
820    pub y: f64,
821    pub width: f64,
822    pub height: f64,
823    pub fill: String,
824    pub num: i64,
825    pub people: Vec<String>,
826    pub actor_circles: Vec<JourneyTaskActorCircleLayout>,
827    pub line_id: String,
828    pub line_x1: f64,
829    pub line_y1: f64,
830    pub line_x2: f64,
831    pub line_y2: f64,
832    pub face_cx: f64,
833    pub face_cy: Option<f64>,
834    pub mouth: JourneyMouthKind,
835}
836
837#[derive(Debug, Clone, Serialize, Deserialize)]
838pub struct JourneySectionLayout {
839    pub section: String,
840    pub num: i64,
841    pub x: f64,
842    pub y: f64,
843    pub width: f64,
844    pub height: f64,
845    pub fill: String,
846    pub task_count: i64,
847}
848
849#[derive(Debug, Clone, Serialize, Deserialize)]
850pub struct JourneyLineLayout {
851    pub x1: f64,
852    pub y1: f64,
853    pub x2: f64,
854    pub y2: f64,
855}
856
857#[derive(Debug, Clone, Serialize, Deserialize)]
858pub struct JourneyDiagramLayout {
859    pub bounds: Option<Bounds>,
860    pub left_margin: f64,
861    pub max_actor_label_width: f64,
862    pub width: f64,
863    pub height: f64,
864    pub svg_height: f64,
865    pub title: Option<String>,
866    pub title_x: f64,
867    pub title_y: f64,
868    pub actor_legend: Vec<JourneyActorLegendItemLayout>,
869    pub sections: Vec<JourneySectionLayout>,
870    pub tasks: Vec<JourneyTaskLayout>,
871    pub activity_line: JourneyLineLayout,
872}
873
874#[derive(Debug, Clone, Serialize, Deserialize)]
875pub struct KanbanSectionLayout {
876    pub id: String,
877    pub label: String,
878    pub index: i64,
879    pub center_x: f64,
880    pub center_y: f64,
881    pub width: f64,
882    pub rect_y: f64,
883    pub rect_height: f64,
884    pub rx: f64,
885    pub ry: f64,
886    pub label_width: f64,
887    #[serde(skip)]
888    pub label_height: f64,
889}
890
891#[derive(Debug, Clone, Serialize, Deserialize)]
892pub struct KanbanItemLayout {
893    pub id: String,
894    pub label: String,
895    pub parent_id: String,
896    pub center_x: f64,
897    pub center_y: f64,
898    pub width: f64,
899    pub height: f64,
900    pub rx: f64,
901    pub ry: f64,
902    #[serde(default)]
903    pub ticket: Option<String>,
904    #[serde(default)]
905    pub assigned: Option<String>,
906    #[serde(default)]
907    pub priority: Option<String>,
908    #[serde(default)]
909    pub icon: Option<String>,
910}
911
912#[derive(Debug, Clone, Serialize, Deserialize)]
913pub struct KanbanDiagramLayout {
914    pub bounds: Option<Bounds>,
915    pub section_width: f64,
916    pub padding: f64,
917    pub max_label_height: f64,
918    pub viewbox_padding: f64,
919    pub sections: Vec<KanbanSectionLayout>,
920    pub items: Vec<KanbanItemLayout>,
921}
922
923#[derive(Debug, Clone, Serialize, Deserialize)]
924pub struct GitGraphBranchLayout {
925    pub name: String,
926    pub index: i64,
927    pub pos: f64,
928    pub bbox_width: f64,
929    pub bbox_height: f64,
930}
931
932#[derive(Debug, Clone, Serialize, Deserialize)]
933pub struct GitGraphCommitLayout {
934    pub id: String,
935    pub message: String,
936    pub seq: i64,
937    pub commit_type: i64,
938    #[serde(default)]
939    pub custom_type: Option<i64>,
940    #[serde(default)]
941    pub custom_id: Option<bool>,
942    #[serde(default)]
943    pub tags: Vec<String>,
944    #[serde(default)]
945    pub parents: Vec<String>,
946    pub branch: String,
947    pub pos: f64,
948    pub pos_with_offset: f64,
949    pub x: f64,
950    pub y: f64,
951}
952
953#[derive(Debug, Clone, Serialize, Deserialize)]
954pub struct GitGraphArrowLayout {
955    pub from: String,
956    pub to: String,
957    pub class_index: i64,
958    pub d: String,
959}
960
961#[derive(Debug, Clone, Serialize, Deserialize)]
962pub struct GitGraphDiagramLayout {
963    pub bounds: Option<Bounds>,
964    pub direction: String,
965    pub rotate_commit_label: bool,
966    pub show_branches: bool,
967    pub show_commit_label: bool,
968    pub parallel_commits: bool,
969    pub diagram_padding: f64,
970    pub max_pos: f64,
971    pub branches: Vec<GitGraphBranchLayout>,
972    pub commits: Vec<GitGraphCommitLayout>,
973    pub arrows: Vec<GitGraphArrowLayout>,
974}
975
976#[derive(Debug, Clone, Serialize, Deserialize)]
977pub struct TreeViewNodeLayout {
978    pub id: i64,
979    pub level: i64,
980    pub name: String,
981    pub depth: usize,
982    pub x: f64,
983    pub y: f64,
984    pub width: f64,
985    pub height: f64,
986    pub label_x: f64,
987    pub label_y: f64,
988    pub label_width: f64,
989    pub label_height: f64,
990}
991
992#[derive(Debug, Clone, Serialize, Deserialize)]
993pub struct TreeViewLineLayout {
994    pub x1: f64,
995    pub y1: f64,
996    pub x2: f64,
997    pub y2: f64,
998    pub stroke_width: f64,
999    pub kind: String,
1000}
1001
1002#[derive(Debug, Clone, Serialize, Deserialize)]
1003pub struct TreeViewDiagramLayout {
1004    pub bounds: Option<Bounds>,
1005    pub total_width: f64,
1006    pub total_height: f64,
1007    pub row_indent: f64,
1008    pub padding_x: f64,
1009    pub padding_y: f64,
1010    pub line_thickness: f64,
1011    pub use_max_width: bool,
1012    pub label_font_size: f64,
1013    pub nodes: Vec<TreeViewNodeLayout>,
1014    pub lines: Vec<TreeViewLineLayout>,
1015}
1016
1017#[derive(Debug, Clone, Serialize, Deserialize)]
1018pub struct IshikawaLineLayout {
1019    pub x1: f64,
1020    pub y1: f64,
1021    pub x2: f64,
1022    pub y2: f64,
1023    pub class_name: String,
1024    pub marker_start: bool,
1025}
1026
1027#[derive(Debug, Clone, Serialize, Deserialize)]
1028pub struct IshikawaTextLayout {
1029    pub text: String,
1030    pub lines: Vec<String>,
1031    pub class_name: String,
1032    pub x: f64,
1033    pub y: f64,
1034    pub anchor: String,
1035    pub line_height: f64,
1036    pub font_size: f64,
1037    pub bbox: Bounds,
1038}
1039
1040#[derive(Debug, Clone, Serialize, Deserialize)]
1041pub struct IshikawaLabelBoxLayout {
1042    pub x: f64,
1043    pub y: f64,
1044    pub width: f64,
1045    pub height: f64,
1046}
1047
1048#[derive(Debug, Clone, Serialize, Deserialize)]
1049pub struct IshikawaHeadLayout {
1050    pub x: f64,
1051    pub y: f64,
1052    pub width: f64,
1053    pub height: f64,
1054    pub path_d: String,
1055    pub label: IshikawaTextLayout,
1056}
1057
1058#[derive(Debug, Clone, Serialize, Deserialize)]
1059pub struct IshikawaDiagramLayout {
1060    pub bounds: Option<Bounds>,
1061    pub total_width: f64,
1062    pub total_height: f64,
1063    pub viewbox_x: f64,
1064    pub viewbox_y: f64,
1065    pub padding: f64,
1066    pub use_max_width: bool,
1067    pub font_size: f64,
1068    pub head: Option<IshikawaHeadLayout>,
1069    pub lines: Vec<IshikawaLineLayout>,
1070    pub labels: Vec<IshikawaTextLayout>,
1071    pub label_boxes: Vec<IshikawaLabelBoxLayout>,
1072}
1073
1074#[derive(Debug, Clone, Serialize, Deserialize)]
1075pub struct EventModelingSwimlaneLayout {
1076    pub index: i64,
1077    pub label: String,
1078    pub namespace: Option<String>,
1079    pub x: f64,
1080    pub y: f64,
1081    pub width: f64,
1082    pub height: f64,
1083}
1084
1085#[derive(Debug, Clone, Serialize, Deserialize)]
1086pub struct EventModelingBoxLayout {
1087    pub index: usize,
1088    pub frame_name: String,
1089    pub frame_kind: String,
1090    pub model_entity_type: String,
1091    pub entity_identifier: String,
1092    pub text: String,
1093    pub x: f64,
1094    pub y: f64,
1095    pub width: f64,
1096    pub height: f64,
1097    pub fill: String,
1098    pub stroke: String,
1099    pub swimlane_index: i64,
1100}
1101
1102#[derive(Debug, Clone, Serialize, Deserialize)]
1103pub struct EventModelingRelationLayout {
1104    pub source_frame: String,
1105    pub target_frame: String,
1106    pub x1: f64,
1107    pub y1: f64,
1108    pub x2: f64,
1109    pub y2: f64,
1110    pub stroke: String,
1111}
1112
1113#[derive(Debug, Clone, Serialize, Deserialize)]
1114pub struct EventModelingDiagramLayout {
1115    pub bounds: Option<Bounds>,
1116    pub total_width: f64,
1117    pub total_height: f64,
1118    pub viewbox_x: f64,
1119    pub viewbox_y: f64,
1120    pub padding: f64,
1121    pub use_max_width: bool,
1122    pub swimlanes: Vec<EventModelingSwimlaneLayout>,
1123    pub boxes: Vec<EventModelingBoxLayout>,
1124    pub relations: Vec<EventModelingRelationLayout>,
1125}
1126
1127#[derive(Debug, Clone, Serialize, Deserialize)]
1128pub struct GanttAxisTickLayout {
1129    pub time_ms: i64,
1130    pub x: f64,
1131    pub label: String,
1132}
1133
1134#[derive(Debug, Clone, Serialize, Deserialize)]
1135pub struct GanttExcludeRangeLayout {
1136    pub id: String,
1137    pub start_ms: i64,
1138    pub end_ms: i64,
1139    pub x: f64,
1140    pub y: f64,
1141    pub width: f64,
1142    pub height: f64,
1143}
1144
1145#[derive(Debug, Clone, Serialize, Deserialize)]
1146pub struct GanttSectionTitleLayout {
1147    pub section: String,
1148    pub index: i64,
1149    pub x: f64,
1150    pub y: f64,
1151    pub dy_em: f64,
1152    pub lines: Vec<String>,
1153    pub class: String,
1154}
1155
1156#[derive(Debug, Clone, Serialize, Deserialize)]
1157pub struct GanttRowLayout {
1158    pub index: i64,
1159    pub x: f64,
1160    pub y: f64,
1161    pub width: f64,
1162    pub height: f64,
1163    pub class: String,
1164}
1165
1166#[derive(Debug, Clone, Serialize, Deserialize)]
1167pub struct GanttTaskLabelLayout {
1168    pub id: String,
1169    pub text: String,
1170    pub font_size: f64,
1171    pub width: f64,
1172    pub x: f64,
1173    pub y: f64,
1174    pub class: String,
1175}
1176
1177#[derive(Debug, Clone, Serialize, Deserialize)]
1178pub struct GanttTaskBarLayout {
1179    pub id: String,
1180    pub x: f64,
1181    pub y: f64,
1182    pub width: f64,
1183    pub height: f64,
1184    pub rx: f64,
1185    pub ry: f64,
1186    pub class: String,
1187}
1188
1189#[derive(Debug, Clone, Serialize, Deserialize)]
1190pub struct GanttTaskLayout {
1191    pub id: String,
1192    pub task: String,
1193    pub section: String,
1194    pub task_type: String,
1195    pub order: i64,
1196    pub start_ms: i64,
1197    pub end_ms: i64,
1198    #[serde(default)]
1199    pub render_end_ms: Option<i64>,
1200    pub milestone: bool,
1201    pub vert: bool,
1202    pub bar: GanttTaskBarLayout,
1203    pub label: GanttTaskLabelLayout,
1204}
1205
1206#[derive(Debug, Clone, Serialize, Deserialize)]
1207pub struct GanttDiagramLayout {
1208    pub bounds: Option<Bounds>,
1209    pub width: f64,
1210    pub height: f64,
1211    pub left_padding: f64,
1212    pub right_padding: f64,
1213    pub top_padding: f64,
1214    pub grid_line_start_padding: f64,
1215    pub bar_height: f64,
1216    pub bar_gap: f64,
1217    pub title_top_margin: f64,
1218    pub font_size: f64,
1219    pub section_font_size: f64,
1220    pub number_section_styles: i64,
1221    pub display_mode: String,
1222    pub date_format: String,
1223    pub axis_format: String,
1224    #[serde(default)]
1225    pub tick_interval: Option<String>,
1226    pub top_axis: bool,
1227    pub today_marker: String,
1228    pub categories: Vec<String>,
1229    pub rows: Vec<GanttRowLayout>,
1230    pub section_titles: Vec<GanttSectionTitleLayout>,
1231    pub tasks: Vec<GanttTaskLayout>,
1232    pub excludes: Vec<GanttExcludeRangeLayout>,
1233    #[serde(default)]
1234    pub has_excludes_layer: bool,
1235    pub bottom_ticks: Vec<GanttAxisTickLayout>,
1236    #[serde(default)]
1237    pub top_ticks: Vec<GanttAxisTickLayout>,
1238    pub title: Option<String>,
1239    pub title_x: f64,
1240    pub title_y: f64,
1241}
1242
1243#[derive(Debug, Clone, Serialize, Deserialize)]
1244pub struct C4TextBlockLayout {
1245    pub text: String,
1246    pub y: f64,
1247    pub width: f64,
1248    pub height: f64,
1249    pub line_count: usize,
1250}
1251
1252#[derive(Debug, Clone, Serialize, Deserialize)]
1253pub struct C4ImageLayout {
1254    pub width: f64,
1255    pub height: f64,
1256    pub y: f64,
1257}
1258
1259#[derive(Debug, Clone, Serialize, Deserialize)]
1260pub struct C4ShapeLayout {
1261    pub alias: String,
1262    pub parent_boundary: String,
1263    pub type_c4_shape: String,
1264    pub x: f64,
1265    pub y: f64,
1266    pub width: f64,
1267    pub height: f64,
1268    pub margin: f64,
1269    pub image: C4ImageLayout,
1270    pub type_block: C4TextBlockLayout,
1271    pub label: C4TextBlockLayout,
1272    #[serde(default)]
1273    pub ty: Option<C4TextBlockLayout>,
1274    #[serde(default)]
1275    pub techn: Option<C4TextBlockLayout>,
1276    #[serde(default)]
1277    pub descr: Option<C4TextBlockLayout>,
1278}
1279
1280#[derive(Debug, Clone, Serialize, Deserialize)]
1281pub struct C4BoundaryLayout {
1282    pub alias: String,
1283    pub parent_boundary: String,
1284    pub x: f64,
1285    pub y: f64,
1286    pub width: f64,
1287    pub height: f64,
1288    pub image: C4ImageLayout,
1289    pub label: C4TextBlockLayout,
1290    #[serde(default)]
1291    pub ty: Option<C4TextBlockLayout>,
1292    #[serde(default)]
1293    pub descr: Option<C4TextBlockLayout>,
1294}
1295
1296#[derive(Debug, Clone, Serialize, Deserialize)]
1297pub struct C4RelLayout {
1298    pub from: String,
1299    pub to: String,
1300    pub rel_type: String,
1301    pub start_point: LayoutPoint,
1302    pub end_point: LayoutPoint,
1303    #[serde(default)]
1304    pub offset_x: Option<i64>,
1305    #[serde(default)]
1306    pub offset_y: Option<i64>,
1307    pub label: C4TextBlockLayout,
1308    #[serde(default)]
1309    pub techn: Option<C4TextBlockLayout>,
1310    #[serde(default)]
1311    pub descr: Option<C4TextBlockLayout>,
1312}
1313
1314#[derive(Debug, Clone, Serialize, Deserialize)]
1315pub struct C4DiagramLayout {
1316    pub bounds: Option<Bounds>,
1317    pub width: f64,
1318    pub height: f64,
1319    pub viewport_width: f64,
1320    pub viewport_height: f64,
1321    pub c4_type: String,
1322    pub title: Option<String>,
1323    pub boundaries: Vec<C4BoundaryLayout>,
1324    pub shapes: Vec<C4ShapeLayout>,
1325    pub rels: Vec<C4RelLayout>,
1326}
1327
1328#[derive(Debug, Clone, Serialize, Deserialize)]
1329pub struct ErrorDiagramLayout {
1330    pub viewbox_width: f64,
1331    pub viewbox_height: f64,
1332    pub max_width_px: f64,
1333}
1334
1335#[derive(Debug, Clone, Serialize, Deserialize)]
1336pub enum LayoutDiagram {
1337    BlockDiagram(Box<BlockDiagramLayout>),
1338    RequirementDiagram(Box<RequirementDiagramLayout>),
1339    ArchitectureDiagram(Box<ArchitectureDiagramLayout>),
1340    MindmapDiagram(Box<MindmapDiagramLayout>),
1341    SankeyDiagram(Box<SankeyDiagramLayout>),
1342    RadarDiagram(Box<RadarDiagramLayout>),
1343    TreemapDiagram(Box<TreemapDiagramLayout>),
1344    VennDiagram(Box<VennDiagramLayout>),
1345    XyChartDiagram(Box<XyChartDiagramLayout>),
1346    QuadrantChartDiagram(Box<QuadrantChartDiagramLayout>),
1347    FlowchartV2(Box<FlowchartV2Layout>),
1348    StateDiagramV2(Box<StateDiagramV2Layout>),
1349    ClassDiagramV2(Box<ClassDiagramV2Layout>),
1350    ErDiagram(Box<ErDiagramLayout>),
1351    SequenceDiagram(Box<SequenceDiagramLayout>),
1352    InfoDiagram(Box<InfoDiagramLayout>),
1353    PacketDiagram(Box<PacketDiagramLayout>),
1354    TimelineDiagram(Box<TimelineDiagramLayout>),
1355    PieDiagram(Box<PieDiagramLayout>),
1356    JourneyDiagram(Box<JourneyDiagramLayout>),
1357    KanbanDiagram(Box<KanbanDiagramLayout>),
1358    GitGraphDiagram(Box<GitGraphDiagramLayout>),
1359    TreeViewDiagram(Box<TreeViewDiagramLayout>),
1360    IshikawaDiagram(Box<IshikawaDiagramLayout>),
1361    EventModelingDiagram(Box<EventModelingDiagramLayout>),
1362    GanttDiagram(Box<GanttDiagramLayout>),
1363    C4Diagram(Box<C4DiagramLayout>),
1364    ErrorDiagram(Box<ErrorDiagramLayout>),
1365}
1366
1367#[derive(Debug, Clone, Serialize, Deserialize)]
1368pub struct LayoutedDiagram {
1369    pub meta: LayoutMeta,
1370    pub semantic: Value,
1371    pub layout: LayoutDiagram,
1372}