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}