1mod catalog;
22mod components;
23mod prompt;
24
25pub use catalog::{Catalog, ComponentCategory};
26pub use components::{ComponentDef, PropDef};
27pub use prompt::{PromptOptions, generate_prompt};
28
29use std::sync::LazyLock;
30
31pub fn default_catalog() -> &'static Catalog {
33 &DEFAULT_CATALOG
34}
35
36static DEFAULT_CATALOG: LazyLock<Catalog> = LazyLock::new(|| Catalog {
37 root_hint: "Card",
38 components: components::all_components(),
39 categories: catalog::default_categories(),
40});
41
42pub fn default_prompt() -> &'static str {
44 &DEFAULT_PROMPT
45}
46
47static DEFAULT_PROMPT: LazyLock<String> =
48 LazyLock::new(|| generate_prompt(default_catalog(), &PromptOptions::default()));
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn default_catalog_has_components() {
56 let cat = default_catalog();
57 assert!(!cat.components.is_empty());
58 }
59
60 #[test]
61 fn default_catalog_contains_core_components() {
62 let cat = default_catalog();
63 let names: Vec<&str> = cat.components.iter().map(|c| c.name).collect();
64 for expected in [
65 "Card",
66 "Stack",
67 "Heading",
68 "Text",
69 "Button",
70 "ButtonGroup",
71 "List",
72 "ListItem",
73 "Table",
74 "Form",
75 "TextField",
76 "Select",
77 "Checkbox",
78 "Callout",
79 "Badge",
80 ] {
81 assert!(names.contains(&expected), "missing component: {expected}");
82 }
83 }
84
85 #[test]
86 fn default_prompt_is_substantial() {
87 let prompt = default_prompt();
88 assert!(
89 prompt.len() > 800,
90 "prompt too short: {} chars",
91 prompt.len()
92 );
93 }
94
95 #[test]
96 fn default_prompt_mentions_a2ui_fence() {
97 assert!(default_prompt().contains("```a2ui"));
98 }
99
100 #[test]
101 fn default_prompt_mentions_json_shape() {
102 let prompt = default_prompt();
103 assert!(prompt.contains("\"type\""));
104 assert!(prompt.contains("\"props\""));
105 assert!(prompt.contains("\"children\""));
106 }
107
108 #[test]
109 fn default_prompt_lists_every_component() {
110 let cat = default_catalog();
111 let prompt = default_prompt();
112 for comp in &cat.components {
113 assert!(
114 prompt.contains(comp.name),
115 "prompt missing component {}",
116 comp.name
117 );
118 }
119 }
120
121 #[test]
122 fn default_prompt_includes_action_types() {
123 let prompt = default_prompt();
124 assert!(prompt.contains("\"message\""));
125 assert!(prompt.contains("\"open_url\""));
126 }
127
128 #[test]
129 fn categories_cover_all_components() {
130 let cat = default_catalog();
131 let grouped: std::collections::HashSet<&str> = cat
132 .categories
133 .iter()
134 .flat_map(|g| g.components.iter().copied())
135 .collect();
136 for comp in &cat.components {
137 assert!(
138 grouped.contains(comp.name),
139 "component '{}' not in any category",
140 comp.name
141 );
142 }
143 }
144}