Skip to main content

adk_ui/kit/
generator.rs

1use serde_json::{Value, json};
2
3use super::spec::KitSpec;
4
5#[derive(Debug, Clone)]
6pub struct KitArtifacts {
7    pub catalog: Value,
8    pub tokens: Value,
9    pub templates: Value,
10    pub theme_css: String,
11}
12
13#[derive(Debug, Default)]
14pub struct KitGenerator;
15
16impl KitGenerator {
17    pub fn new() -> Self {
18        Self
19    }
20
21    pub fn generate(&self, spec: &KitSpec) -> KitArtifacts {
22        let catalog_id = format!(
23            "zavora.ai:adk-ui/kit/{}@{}",
24            slugify(&spec.name),
25            spec.version
26        );
27
28        let catalog = json!({
29            "$schema": "https://json-schema.org/draft/2020-12/schema",
30            "$id": catalog_id,
31            "title": format!("ADK-UI Kit: {}", spec.name),
32            "description": format!("Generated UI kit for {}", spec.name),
33            "catalogId": catalog_id,
34            "components": {},
35            "theme": {
36                "primaryColor": spec.colors.primary,
37                "agentDisplayName": spec.name
38            }
39        });
40
41        let tokens = json!({
42            "colors": {
43                "primary": spec.colors.primary,
44                "accent": spec.colors.accent,
45                "surface": spec.colors.surface,
46                "background": spec.colors.background,
47                "text": spec.colors.text
48            },
49            "typography": {
50                "family": spec.typography.family,
51                "scale": spec.typography.scale
52            },
53            "density": format!("{:?}", spec.density).to_lowercase(),
54            "radius": format!("{:?}", spec.radius).to_lowercase()
55        });
56
57        let templates = json!({
58            "templates": spec.templates
59        });
60
61        let theme_css = format!(
62            ":root {{\n  --adk-primary: {};\n  --adk-font-family: \"{}\";\n}}\n",
63            spec.colors.primary, spec.typography.family
64        );
65
66        KitArtifacts {
67            catalog,
68            tokens,
69            templates,
70            theme_css,
71        }
72    }
73}
74
75fn slugify(input: &str) -> String {
76    let mut out = String::new();
77    for ch in input.chars() {
78        if ch.is_ascii_alphanumeric() {
79            out.push(ch.to_ascii_lowercase());
80        } else if (ch.is_whitespace() || ch == '-' || ch == '_') && !out.ends_with('-') {
81            out.push('-');
82        }
83    }
84    out.trim_matches('-').to_string()
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90    use crate::kit::spec::{KitBrand, KitColors, KitSpec, KitTypography};
91
92    #[test]
93    fn generates_catalog_with_expected_id() {
94        let spec = KitSpec {
95            name: "Fintech Pro".to_string(),
96            version: "0.1.0".to_string(),
97            brand: KitBrand {
98                vibe: "trustworthy".to_string(),
99                industry: None,
100            },
101            colors: KitColors {
102                primary: "#2F6BFF".to_string(),
103                accent: None,
104                surface: None,
105                background: None,
106                text: None,
107            },
108            typography: KitTypography {
109                family: "Source Sans 3".to_string(),
110                scale: None,
111            },
112            density: Default::default(),
113            radius: Default::default(),
114            components: Default::default(),
115            templates: vec!["auth_login".to_string()],
116        };
117
118        let artifacts = KitGenerator::new().generate(&spec);
119        assert_eq!(
120            artifacts.catalog["catalogId"],
121            "zavora.ai:adk-ui/kit/fintech-pro@0.1.0"
122        );
123        assert!(artifacts.tokens["colors"]["primary"].is_string());
124    }
125}