1pub mod builder;
19pub mod grid_protocol;
20pub mod layout;
21pub mod lint;
22pub mod palette;
23pub mod renderers;
24pub mod shapes;
25pub mod typography;
26
27#[allow(unused_imports)]
29pub use builder::{ComponentDiagramBuilder, SvgBuilder, SvgElement};
30#[allow(unused_imports)]
31pub use grid_protocol::{
32 GridError, GridProtocol, GridSpan, LayoutTemplate, PixelBounds, CANVAS_HEIGHT, CANVAS_WIDTH,
33 CELL_PADDING, CELL_SIZE, GRID_COLS, GRID_ROWS, INTERNAL_PADDING, MIN_BLOCK_GAP, TOTAL_CELLS,
34};
35#[allow(unused_imports)]
36pub use layout::{auto_layout, LayoutEngine, LayoutError, Viewport, GRID_SIZE};
37#[allow(unused_imports)]
38pub use lint::{LintConfig, LintResult, LintRule, LintSeverity, LintViolation, SvgLinter};
39#[allow(unused_imports)]
40pub use palette::{Color, MaterialPalette, SovereignPalette, VideoPalette};
41#[allow(unused_imports)]
42pub use renderers::{RenderMode, ShapeHeavyRenderer, TextHeavyRenderer};
43#[allow(unused_imports)]
44pub use shapes::{ArrowMarker, Circle, Line, Path, PathCommand, Point, Rect, Size, Text};
45#[allow(unused_imports)]
46pub use typography::{
47 FontFamily, FontWeight, MaterialTypography, TextAlign, TextStyle, VideoTypography,
48};
49
50pub fn sovereign_stack_diagram() -> String {
55 ShapeHeavyRenderer::new()
56 .template(LayoutTemplate::Diagram)
57 .title("Sovereign AI Stack Architecture")
58 .layer("compute", 50.0, 100.0, 1820.0, 200.0, "Compute Layer")
59 .horizontal_stack(
60 &[("trueno", "Trueno"), ("repartir", "Repartir"), ("trueno_db", "Trueno DB")],
61 Point::new(100.0, 150.0),
62 )
63 .layer("ml", 50.0, 350.0, 1820.0, 200.0, "ML Layer")
64 .horizontal_stack(
65 &[("aprender", "Aprender"), ("entrenar", "Entrenar"), ("realizar", "Realizar")],
66 Point::new(100.0, 400.0),
67 )
68 .layer("orch", 50.0, 600.0, 1820.0, 150.0, "Orchestration")
69 .component("batuta", 100.0, 650.0, "Batuta", "batuta")
70 .build()
71}
72
73pub fn documentation_diagram(title: &str, content: &[(&str, &str)]) -> String {
75 let mut renderer = TextHeavyRenderer::new().title(title);
76
77 for (heading, text) in content {
78 renderer = renderer.heading(heading).paragraph(text);
79 }
80
81 renderer.build()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_sovereign_stack_diagram() {
90 let svg = sovereign_stack_diagram();
91
92 assert!(svg.contains("<svg"));
93 assert!(svg.contains("Trueno"));
94 assert!(svg.contains("Aprender"));
95 assert!(svg.contains("Batuta"));
96 assert!(svg.contains("GRID PROTOCOL MANIFEST"));
98 assert!(svg.contains("viewBox=\"0 0 1920 1080\""));
99 }
100
101 #[test]
102 fn test_documentation_diagram() {
103 let svg = documentation_diagram(
104 "Test Doc",
105 &[("Section 1", "Content for section 1."), ("Section 2", "Content for section 2.")],
106 );
107
108 assert!(svg.contains("Test Doc"));
109 assert!(svg.contains("Section 1"));
110 assert!(svg.contains("Section 2"));
111 }
112
113 #[test]
114 fn test_svg_builder_quick() {
115 let svg = SvgBuilder::new()
116 .size(400.0, 300.0)
117 .title("Quick Test")
118 .rect("box", 50.0, 50.0, 100.0, 80.0)
119 .text(60.0, 90.0, "Hello")
120 .build();
121
122 assert!(svg.contains("viewBox=\"0 0 400 300\""));
123 assert!(svg.contains("<title>Quick Test</title>"));
124 }
125
126 #[test]
127 fn test_render_mode_selection() {
128 let mode: RenderMode = "shape-heavy".parse().expect("parse failed");
129 assert_eq!(mode, RenderMode::ShapeHeavy);
130
131 let mode: RenderMode = "text-heavy".parse().expect("parse failed");
132 assert_eq!(mode, RenderMode::TextHeavy);
133 }
134
135 #[test]
136 fn test_color_palette() {
137 let palette = MaterialPalette::light();
138 assert_eq!(palette.primary.to_css_hex(), "#6750A4");
139
140 let palette = MaterialPalette::dark();
141 assert_eq!(palette.primary.to_css_hex(), "#D0BCFF");
142 }
143
144 #[test]
145 fn test_typography_scale() {
146 let typo = MaterialTypography::default();
147 assert_eq!(typo.display_large.size, 57.0);
148 assert_eq!(typo.body_medium.size, 14.0);
149 assert_eq!(typo.label_small.size, 11.0);
150 }
151
152 #[test]
153 fn test_layout_grid_snap() {
154 let engine = LayoutEngine::new(Viewport::default());
155
156 assert_eq!(engine.snap_to_grid(10.0), 8.0);
158 assert_eq!(engine.snap_to_grid(15.0), 16.0);
159 assert_eq!(engine.snap_to_grid(8.0), 8.0);
160 }
161
162 #[test]
163 fn test_lint_basic() {
164 let linter = SvgLinter::new();
165
166 let palette = MaterialPalette::light();
168 assert!(linter.lint_color(&palette.primary, None).is_none());
169
170 assert!(linter.lint_color(&Color::rgb(1, 2, 3), None).is_some());
172 }
173
174 #[test]
175 fn test_shapes() {
176 let rect = Rect::new(10.0, 20.0, 100.0, 50.0);
177 assert_eq!(rect.center().x, 60.0);
178 assert_eq!(rect.center().y, 45.0);
179
180 let circle = Circle::new(50.0, 50.0, 25.0);
181 assert!(circle.contains(&Point::new(50.0, 50.0)));
182 }
183
184 #[test]
185 fn test_output_size_limit() {
186 let svg = ShapeHeavyRenderer::new()
188 .title("Large Diagram")
189 .horizontal_stack(
190 &[
191 ("c1", "Component 1"),
192 ("c2", "Component 2"),
193 ("c3", "Component 3"),
194 ("c4", "Component 4"),
195 ("c5", "Component 5"),
196 ],
197 Point::new(50.0, 200.0),
198 )
199 .build();
200
201 assert!(svg.len() < 100_000, "SVG too large: {} bytes", svg.len());
203 }
204}