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}