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