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