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    /// Source-backed ELK carries Mermaid DOM `getBoundingClientRect()` edge-label bounds.
615    #[serde(skip)]
616    pub source_backed_edge_label_bboxes: bool,
617    /// Source-ported ELK should follow Mermaid ELK renderer DOM quirks.
618    #[serde(skip)]
619    pub source_ported_elk_rendering: bool,
620}
621
622#[derive(Debug, Clone, Serialize, Deserialize)]
623pub struct StateDiagramV2Layout {
624    pub nodes: Vec<LayoutNode>,
625    pub edges: Vec<LayoutEdge>,
626    pub clusters: Vec<LayoutCluster>,
627    pub bounds: Option<Bounds>,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize)]
631pub struct ClassDiagramV2Layout {
632    pub nodes: Vec<LayoutNode>,
633    pub edges: Vec<LayoutEdge>,
634    pub clusters: Vec<LayoutCluster>,
635    pub bounds: Option<Bounds>,
636    #[serde(skip)]
637    pub class_row_metrics_by_id: FxHashMap<String, Arc<ClassNodeRowMetrics>>,
638}
639
640#[derive(Debug, Clone, Serialize, Deserialize)]
641pub struct ErDiagramLayout {
642    pub nodes: Vec<LayoutNode>,
643    pub edges: Vec<LayoutEdge>,
644    pub bounds: Option<Bounds>,
645}
646
647#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct SequenceDiagramLayout {
649    pub nodes: Vec<LayoutNode>,
650    pub edges: Vec<LayoutEdge>,
651    pub clusters: Vec<LayoutCluster>,
652    pub bounds: Option<Bounds>,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct InfoDiagramLayout {
657    pub bounds: Option<Bounds>,
658    pub version: String,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct PacketBlockLayout {
663    pub start: i64,
664    pub end: i64,
665    pub label: String,
666    pub x: f64,
667    pub y: f64,
668    pub width: f64,
669    pub height: f64,
670}
671
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct PacketWordLayout {
674    pub blocks: Vec<PacketBlockLayout>,
675}
676
677#[derive(Debug, Clone, Serialize, Deserialize)]
678pub struct PacketDiagramLayout {
679    pub bounds: Option<Bounds>,
680    pub width: f64,
681    pub height: f64,
682    pub row_height: f64,
683    pub padding_x: f64,
684    pub padding_y: f64,
685    pub bit_width: f64,
686    pub bits_per_row: i64,
687    pub show_bits: bool,
688    pub words: Vec<PacketWordLayout>,
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize)]
692pub struct PieSliceLayout {
693    pub label: String,
694    pub value: f64,
695    pub start_angle: f64,
696    pub end_angle: f64,
697    pub is_full_circle: bool,
698    pub percent: i64,
699    pub text_x: f64,
700    pub text_y: f64,
701    pub fill: String,
702}
703
704#[derive(Debug, Clone, Serialize, Deserialize)]
705pub struct PieLegendItemLayout {
706    pub label: String,
707    pub value: f64,
708    pub fill: String,
709    pub y: f64,
710}
711
712#[derive(Debug, Clone, Serialize, Deserialize)]
713pub struct PieDiagramLayout {
714    pub bounds: Option<Bounds>,
715    pub center_x: f64,
716    pub center_y: f64,
717    pub radius: f64,
718    pub outer_radius: f64,
719    pub legend_x: f64,
720    pub legend_start_y: f64,
721    pub legend_step_y: f64,
722    pub slices: Vec<PieSliceLayout>,
723    pub legend_items: Vec<PieLegendItemLayout>,
724}
725
726#[derive(Debug, Clone, Serialize, Deserialize)]
727pub struct TimelineNodeLayout {
728    pub x: f64,
729    pub y: f64,
730    pub width: f64,
731    pub height: f64,
732    /// Width used for wrapping (excluding padding).
733    pub content_width: f64,
734    /// Padding used to compute `width` (Mermaid 11.12.2 uses 20).
735    pub padding: f64,
736    pub section_class: String,
737    pub label: String,
738    /// Wrapped lines as rendered into `<tspan>` nodes.
739    pub label_lines: Vec<String>,
740    pub kind: String,
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize)]
744pub struct TimelineLineLayout {
745    pub kind: String,
746    pub x1: f64,
747    pub y1: f64,
748    pub x2: f64,
749    pub y2: f64,
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize)]
753pub struct TimelineTaskLayout {
754    pub node: TimelineNodeLayout,
755    pub connector: TimelineLineLayout,
756    pub events: Vec<TimelineNodeLayout>,
757}
758
759#[derive(Debug, Clone, Serialize, Deserialize)]
760pub struct TimelineSectionLayout {
761    pub node: TimelineNodeLayout,
762    pub tasks: Vec<TimelineTaskLayout>,
763}
764
765#[derive(Debug, Clone, Serialize, Deserialize)]
766pub struct TimelineDiagramLayout {
767    pub bounds: Option<Bounds>,
768    pub left_margin: f64,
769    pub base_x: f64,
770    pub base_y: f64,
771    /// `svg.node().getBBox().width` computed *before* title/activity line are inserted.
772    pub pre_title_box_width: f64,
773    pub sections: Vec<TimelineSectionLayout>,
774    #[serde(default)]
775    pub orphan_tasks: Vec<TimelineTaskLayout>,
776    pub activity_line: TimelineLineLayout,
777    pub title: Option<String>,
778    pub title_x: f64,
779    pub title_y: f64,
780    #[serde(default = "crate::timeline::default_use_max_width")]
781    pub use_max_width: bool,
782}
783
784#[derive(Debug, Clone, Serialize, Deserialize)]
785pub struct JourneyActorLegendLineLayout {
786    pub text: String,
787    pub x: f64,
788    pub y: f64,
789    pub tspan_x: f64,
790    pub text_margin: f64,
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct JourneyActorLegendItemLayout {
795    pub actor: String,
796    pub pos: i64,
797    pub color: String,
798    pub circle_cx: f64,
799    pub circle_cy: f64,
800    pub circle_r: f64,
801    pub label_lines: Vec<JourneyActorLegendLineLayout>,
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize)]
805pub enum JourneyMouthKind {
806    Smile,
807    Sad,
808    Ambivalent,
809}
810
811#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct JourneyTaskActorCircleLayout {
813    pub actor: String,
814    pub pos: i64,
815    pub color: String,
816    pub cx: f64,
817    pub cy: f64,
818    pub r: f64,
819}
820
821#[derive(Debug, Clone, Serialize, Deserialize)]
822pub struct JourneyTaskLayout {
823    pub index: i64,
824    pub section: String,
825    pub task: String,
826    pub score: i64,
827    pub x: f64,
828    pub y: f64,
829    pub width: f64,
830    pub height: f64,
831    pub fill: String,
832    pub num: i64,
833    pub people: Vec<String>,
834    pub actor_circles: Vec<JourneyTaskActorCircleLayout>,
835    pub line_id: String,
836    pub line_x1: f64,
837    pub line_y1: f64,
838    pub line_x2: f64,
839    pub line_y2: f64,
840    pub face_cx: f64,
841    pub face_cy: Option<f64>,
842    pub mouth: JourneyMouthKind,
843}
844
845#[derive(Debug, Clone, Serialize, Deserialize)]
846pub struct JourneySectionLayout {
847    pub section: String,
848    pub num: i64,
849    pub x: f64,
850    pub y: f64,
851    pub width: f64,
852    pub height: f64,
853    pub fill: String,
854    pub task_count: i64,
855}
856
857#[derive(Debug, Clone, Serialize, Deserialize)]
858pub struct JourneyLineLayout {
859    pub x1: f64,
860    pub y1: f64,
861    pub x2: f64,
862    pub y2: f64,
863}
864
865#[derive(Debug, Clone, Serialize, Deserialize)]
866pub struct JourneyDiagramLayout {
867    pub bounds: Option<Bounds>,
868    pub left_margin: f64,
869    pub max_actor_label_width: f64,
870    pub width: f64,
871    pub height: f64,
872    pub svg_height: f64,
873    #[serde(default = "crate::journey::default_use_max_width")]
874    pub use_max_width: bool,
875    pub title: Option<String>,
876    pub title_x: f64,
877    pub title_y: f64,
878    pub actor_legend: Vec<JourneyActorLegendItemLayout>,
879    pub sections: Vec<JourneySectionLayout>,
880    pub tasks: Vec<JourneyTaskLayout>,
881    pub activity_line: JourneyLineLayout,
882}
883
884#[derive(Debug, Clone, Serialize, Deserialize)]
885pub struct KanbanSectionLayout {
886    pub id: String,
887    pub label: String,
888    pub index: i64,
889    pub center_x: f64,
890    pub center_y: f64,
891    pub width: f64,
892    pub rect_y: f64,
893    pub rect_height: f64,
894    pub rx: f64,
895    pub ry: f64,
896    pub label_width: f64,
897    #[serde(skip)]
898    pub label_height: f64,
899}
900
901#[derive(Debug, Clone, Serialize, Deserialize)]
902pub struct KanbanItemLayout {
903    pub id: String,
904    pub label: String,
905    pub parent_id: String,
906    pub center_x: f64,
907    pub center_y: f64,
908    pub width: f64,
909    pub height: f64,
910    pub rx: f64,
911    pub ry: f64,
912    #[serde(default)]
913    pub ticket: Option<String>,
914    #[serde(default)]
915    pub assigned: Option<String>,
916    #[serde(default)]
917    pub priority: Option<String>,
918    #[serde(default)]
919    pub icon: Option<String>,
920}
921
922#[derive(Debug, Clone, Serialize, Deserialize)]
923pub struct KanbanDiagramLayout {
924    pub bounds: Option<Bounds>,
925    pub section_width: f64,
926    pub padding: f64,
927    pub max_label_height: f64,
928    pub viewbox_padding: f64,
929    #[serde(default = "crate::kanban::default_use_max_width")]
930    pub use_max_width: bool,
931    pub sections: Vec<KanbanSectionLayout>,
932    pub items: Vec<KanbanItemLayout>,
933}
934
935#[derive(Debug, Clone, Serialize, Deserialize)]
936pub struct GitGraphBranchLayout {
937    pub name: String,
938    pub index: i64,
939    pub pos: f64,
940    pub bbox_width: f64,
941    pub bbox_height: f64,
942}
943
944#[derive(Debug, Clone, Serialize, Deserialize)]
945pub struct GitGraphCommitLayout {
946    pub id: String,
947    pub message: String,
948    pub seq: i64,
949    pub commit_type: i64,
950    #[serde(default)]
951    pub custom_type: Option<i64>,
952    #[serde(default)]
953    pub custom_id: Option<bool>,
954    #[serde(default)]
955    pub tags: Vec<String>,
956    #[serde(default)]
957    pub parents: Vec<String>,
958    pub branch: String,
959    pub pos: f64,
960    pub pos_with_offset: f64,
961    pub x: f64,
962    pub y: f64,
963}
964
965#[derive(Debug, Clone, Serialize, Deserialize)]
966pub struct GitGraphArrowLayout {
967    pub from: String,
968    pub to: String,
969    pub class_index: i64,
970    pub d: String,
971}
972
973#[derive(Debug, Clone, Serialize, Deserialize)]
974pub struct GitGraphDiagramLayout {
975    pub bounds: Option<Bounds>,
976    pub direction: String,
977    pub rotate_commit_label: bool,
978    pub show_branches: bool,
979    pub show_commit_label: bool,
980    pub parallel_commits: bool,
981    pub diagram_padding: f64,
982    pub max_pos: f64,
983    pub branches: Vec<GitGraphBranchLayout>,
984    pub commits: Vec<GitGraphCommitLayout>,
985    pub arrows: Vec<GitGraphArrowLayout>,
986}
987
988#[derive(Debug, Clone, Serialize, Deserialize)]
989pub struct TreeViewNodeLayout {
990    pub id: i64,
991    pub level: i64,
992    pub name: String,
993    pub depth: usize,
994    pub x: f64,
995    pub y: f64,
996    pub width: f64,
997    pub height: f64,
998    pub label_x: f64,
999    pub label_y: f64,
1000    pub label_width: f64,
1001    pub label_height: f64,
1002}
1003
1004#[derive(Debug, Clone, Serialize, Deserialize)]
1005pub struct TreeViewLineLayout {
1006    pub x1: f64,
1007    pub y1: f64,
1008    pub x2: f64,
1009    pub y2: f64,
1010    pub stroke_width: f64,
1011    pub kind: String,
1012}
1013
1014#[derive(Debug, Clone, Serialize, Deserialize)]
1015pub struct TreeViewDiagramLayout {
1016    pub bounds: Option<Bounds>,
1017    pub total_width: f64,
1018    pub total_height: f64,
1019    pub row_indent: f64,
1020    pub padding_x: f64,
1021    pub padding_y: f64,
1022    pub line_thickness: f64,
1023    pub use_max_width: bool,
1024    pub label_font_size: f64,
1025    pub nodes: Vec<TreeViewNodeLayout>,
1026    pub lines: Vec<TreeViewLineLayout>,
1027}
1028
1029#[derive(Debug, Clone, Serialize, Deserialize)]
1030pub struct IshikawaLineLayout {
1031    pub x1: f64,
1032    pub y1: f64,
1033    pub x2: f64,
1034    pub y2: f64,
1035    pub class_name: String,
1036    pub marker_start: bool,
1037}
1038
1039#[derive(Debug, Clone, Serialize, Deserialize)]
1040pub struct IshikawaTextLayout {
1041    pub text: String,
1042    pub lines: Vec<String>,
1043    pub class_name: String,
1044    pub x: f64,
1045    pub y: f64,
1046    pub anchor: String,
1047    pub line_height: f64,
1048    pub font_size: f64,
1049    pub bbox: Bounds,
1050}
1051
1052#[derive(Debug, Clone, Serialize, Deserialize)]
1053pub struct IshikawaLabelBoxLayout {
1054    pub x: f64,
1055    pub y: f64,
1056    pub width: f64,
1057    pub height: f64,
1058}
1059
1060#[derive(Debug, Clone, Serialize, Deserialize)]
1061pub struct IshikawaHeadLayout {
1062    pub x: f64,
1063    pub y: f64,
1064    pub width: f64,
1065    pub height: f64,
1066    pub path_d: String,
1067    pub label: IshikawaTextLayout,
1068}
1069
1070#[derive(Debug, Clone, Serialize, Deserialize)]
1071pub struct IshikawaDiagramLayout {
1072    pub bounds: Option<Bounds>,
1073    pub total_width: f64,
1074    pub total_height: f64,
1075    pub viewbox_x: f64,
1076    pub viewbox_y: f64,
1077    pub padding: f64,
1078    pub use_max_width: bool,
1079    pub font_size: f64,
1080    pub head: Option<IshikawaHeadLayout>,
1081    pub lines: Vec<IshikawaLineLayout>,
1082    pub labels: Vec<IshikawaTextLayout>,
1083    pub label_boxes: Vec<IshikawaLabelBoxLayout>,
1084}
1085
1086#[derive(Debug, Clone, Serialize, Deserialize)]
1087pub struct EventModelingSwimlaneLayout {
1088    pub index: i64,
1089    pub label: String,
1090    pub namespace: Option<String>,
1091    pub x: f64,
1092    pub y: f64,
1093    pub width: f64,
1094    pub height: f64,
1095}
1096
1097#[derive(Debug, Clone, Serialize, Deserialize)]
1098pub struct EventModelingBoxLayout {
1099    pub index: usize,
1100    pub frame_name: String,
1101    pub frame_kind: String,
1102    pub model_entity_type: String,
1103    pub entity_identifier: String,
1104    pub text: String,
1105    pub x: f64,
1106    pub y: f64,
1107    pub width: f64,
1108    pub height: f64,
1109    pub fill: String,
1110    pub stroke: String,
1111    pub swimlane_index: i64,
1112}
1113
1114#[derive(Debug, Clone, Serialize, Deserialize)]
1115pub struct EventModelingRelationLayout {
1116    pub source_frame: String,
1117    pub target_frame: String,
1118    pub x1: f64,
1119    pub y1: f64,
1120    pub x2: f64,
1121    pub y2: f64,
1122    pub stroke: String,
1123}
1124
1125#[derive(Debug, Clone, Serialize, Deserialize)]
1126pub struct EventModelingDiagramLayout {
1127    pub bounds: Option<Bounds>,
1128    pub total_width: f64,
1129    pub total_height: f64,
1130    pub viewbox_x: f64,
1131    pub viewbox_y: f64,
1132    pub padding: f64,
1133    pub use_max_width: bool,
1134    pub swimlanes: Vec<EventModelingSwimlaneLayout>,
1135    pub boxes: Vec<EventModelingBoxLayout>,
1136    pub relations: Vec<EventModelingRelationLayout>,
1137}
1138
1139#[derive(Debug, Clone, Serialize, Deserialize)]
1140pub struct GanttAxisTickLayout {
1141    pub time_ms: i64,
1142    pub x: f64,
1143    pub label: String,
1144}
1145
1146#[derive(Debug, Clone, Serialize, Deserialize)]
1147pub struct GanttExcludeRangeLayout {
1148    pub id: String,
1149    pub start_ms: i64,
1150    pub end_ms: i64,
1151    pub x: f64,
1152    pub y: f64,
1153    pub width: f64,
1154    pub height: f64,
1155}
1156
1157#[derive(Debug, Clone, Serialize, Deserialize)]
1158pub struct GanttSectionTitleLayout {
1159    pub section: String,
1160    pub index: i64,
1161    pub x: f64,
1162    pub y: f64,
1163    pub dy_em: f64,
1164    pub lines: Vec<String>,
1165    pub class: String,
1166}
1167
1168#[derive(Debug, Clone, Serialize, Deserialize)]
1169pub struct GanttRowLayout {
1170    pub index: i64,
1171    pub x: f64,
1172    pub y: f64,
1173    pub width: f64,
1174    pub height: f64,
1175    pub class: String,
1176}
1177
1178#[derive(Debug, Clone, Serialize, Deserialize)]
1179pub struct GanttTaskLabelLayout {
1180    pub id: String,
1181    pub text: String,
1182    pub font_size: f64,
1183    pub width: f64,
1184    pub x: f64,
1185    pub y: f64,
1186    pub class: String,
1187}
1188
1189#[derive(Debug, Clone, Serialize, Deserialize)]
1190pub struct GanttTaskBarLayout {
1191    pub id: String,
1192    pub x: f64,
1193    pub y: f64,
1194    pub width: f64,
1195    pub height: f64,
1196    pub rx: f64,
1197    pub ry: f64,
1198    pub class: String,
1199}
1200
1201#[derive(Debug, Clone, Serialize, Deserialize)]
1202pub struct GanttTaskLayout {
1203    pub id: String,
1204    pub task: String,
1205    pub section: String,
1206    pub task_type: String,
1207    pub order: i64,
1208    pub start_ms: i64,
1209    pub end_ms: i64,
1210    #[serde(default)]
1211    pub render_end_ms: Option<i64>,
1212    pub milestone: bool,
1213    pub vert: bool,
1214    pub bar: GanttTaskBarLayout,
1215    pub label: GanttTaskLabelLayout,
1216}
1217
1218#[derive(Debug, Clone, Serialize, Deserialize)]
1219pub struct GanttDiagramLayout {
1220    pub bounds: Option<Bounds>,
1221    pub width: f64,
1222    pub height: f64,
1223    pub left_padding: f64,
1224    pub right_padding: f64,
1225    pub top_padding: f64,
1226    pub grid_line_start_padding: f64,
1227    pub bar_height: f64,
1228    pub bar_gap: f64,
1229    pub title_top_margin: f64,
1230    pub font_size: f64,
1231    pub section_font_size: f64,
1232    pub number_section_styles: i64,
1233    pub display_mode: String,
1234    pub date_format: String,
1235    pub axis_format: String,
1236    #[serde(default)]
1237    pub tick_interval: Option<String>,
1238    pub top_axis: bool,
1239    pub today_marker: String,
1240    pub categories: Vec<String>,
1241    pub rows: Vec<GanttRowLayout>,
1242    pub section_titles: Vec<GanttSectionTitleLayout>,
1243    pub tasks: Vec<GanttTaskLayout>,
1244    pub excludes: Vec<GanttExcludeRangeLayout>,
1245    #[serde(default)]
1246    pub has_excludes_layer: bool,
1247    pub bottom_ticks: Vec<GanttAxisTickLayout>,
1248    #[serde(default)]
1249    pub top_ticks: Vec<GanttAxisTickLayout>,
1250    pub title: Option<String>,
1251    pub title_x: f64,
1252    pub title_y: f64,
1253}
1254
1255#[derive(Debug, Clone, Serialize, Deserialize)]
1256pub struct C4TextBlockLayout {
1257    pub text: String,
1258    pub y: f64,
1259    pub width: f64,
1260    pub height: f64,
1261    pub line_count: usize,
1262}
1263
1264#[derive(Debug, Clone, Serialize, Deserialize)]
1265pub struct C4ImageLayout {
1266    pub width: f64,
1267    pub height: f64,
1268    pub y: f64,
1269}
1270
1271#[derive(Debug, Clone, Serialize, Deserialize)]
1272pub struct C4ShapeLayout {
1273    pub alias: String,
1274    pub parent_boundary: String,
1275    pub type_c4_shape: String,
1276    pub x: f64,
1277    pub y: f64,
1278    pub width: f64,
1279    pub height: f64,
1280    pub margin: f64,
1281    pub image: C4ImageLayout,
1282    pub type_block: C4TextBlockLayout,
1283    pub label: C4TextBlockLayout,
1284    #[serde(default)]
1285    pub ty: Option<C4TextBlockLayout>,
1286    #[serde(default)]
1287    pub techn: Option<C4TextBlockLayout>,
1288    #[serde(default)]
1289    pub descr: Option<C4TextBlockLayout>,
1290}
1291
1292#[derive(Debug, Clone, Serialize, Deserialize)]
1293pub struct C4BoundaryLayout {
1294    pub alias: String,
1295    pub parent_boundary: String,
1296    pub x: f64,
1297    pub y: f64,
1298    pub width: f64,
1299    pub height: f64,
1300    pub image: C4ImageLayout,
1301    pub label: C4TextBlockLayout,
1302    #[serde(default)]
1303    pub ty: Option<C4TextBlockLayout>,
1304    #[serde(default)]
1305    pub descr: Option<C4TextBlockLayout>,
1306}
1307
1308#[derive(Debug, Clone, Serialize, Deserialize)]
1309pub struct C4RelLayout {
1310    pub from: String,
1311    pub to: String,
1312    pub rel_type: String,
1313    pub start_point: LayoutPoint,
1314    pub end_point: LayoutPoint,
1315    #[serde(default)]
1316    pub offset_x: Option<i64>,
1317    #[serde(default)]
1318    pub offset_y: Option<i64>,
1319    pub label: C4TextBlockLayout,
1320    #[serde(default)]
1321    pub techn: Option<C4TextBlockLayout>,
1322    #[serde(default)]
1323    pub descr: Option<C4TextBlockLayout>,
1324}
1325
1326#[derive(Debug, Clone, Serialize, Deserialize)]
1327pub struct C4DiagramLayout {
1328    pub bounds: Option<Bounds>,
1329    pub width: f64,
1330    pub height: f64,
1331    #[serde(default = "crate::c4::default_use_max_width")]
1332    pub use_max_width: bool,
1333    pub viewport_width: f64,
1334    pub viewport_height: f64,
1335    pub c4_type: String,
1336    pub title: Option<String>,
1337    pub boundaries: Vec<C4BoundaryLayout>,
1338    pub shapes: Vec<C4ShapeLayout>,
1339    pub rels: Vec<C4RelLayout>,
1340}
1341
1342#[derive(Debug, Clone, Serialize, Deserialize)]
1343pub struct ErrorDiagramLayout {
1344    pub viewbox_width: f64,
1345    pub viewbox_height: f64,
1346    pub max_width_px: f64,
1347}
1348
1349#[derive(Debug, Clone, Serialize, Deserialize)]
1350pub enum LayoutDiagram {
1351    BlockDiagram(Box<BlockDiagramLayout>),
1352    RequirementDiagram(Box<RequirementDiagramLayout>),
1353    ArchitectureDiagram(Box<ArchitectureDiagramLayout>),
1354    MindmapDiagram(Box<MindmapDiagramLayout>),
1355    SankeyDiagram(Box<SankeyDiagramLayout>),
1356    RadarDiagram(Box<RadarDiagramLayout>),
1357    TreemapDiagram(Box<TreemapDiagramLayout>),
1358    VennDiagram(Box<VennDiagramLayout>),
1359    XyChartDiagram(Box<XyChartDiagramLayout>),
1360    QuadrantChartDiagram(Box<QuadrantChartDiagramLayout>),
1361    FlowchartV2(Box<FlowchartV2Layout>),
1362    StateDiagramV2(Box<StateDiagramV2Layout>),
1363    ClassDiagramV2(Box<ClassDiagramV2Layout>),
1364    ErDiagram(Box<ErDiagramLayout>),
1365    SequenceDiagram(Box<SequenceDiagramLayout>),
1366    InfoDiagram(Box<InfoDiagramLayout>),
1367    PacketDiagram(Box<PacketDiagramLayout>),
1368    TimelineDiagram(Box<TimelineDiagramLayout>),
1369    PieDiagram(Box<PieDiagramLayout>),
1370    JourneyDiagram(Box<JourneyDiagramLayout>),
1371    KanbanDiagram(Box<KanbanDiagramLayout>),
1372    GitGraphDiagram(Box<GitGraphDiagramLayout>),
1373    TreeViewDiagram(Box<TreeViewDiagramLayout>),
1374    IshikawaDiagram(Box<IshikawaDiagramLayout>),
1375    EventModelingDiagram(Box<EventModelingDiagramLayout>),
1376    GanttDiagram(Box<GanttDiagramLayout>),
1377    C4Diagram(Box<C4DiagramLayout>),
1378    ErrorDiagram(Box<ErrorDiagramLayout>),
1379}
1380
1381#[derive(Debug, Clone, Serialize, Deserialize)]
1382pub struct LayoutedDiagram {
1383    pub meta: LayoutMeta,
1384    pub semantic: Value,
1385    pub layout: LayoutDiagram,
1386}