mod catalog;
mod components;
mod prompt;
pub use catalog::{Catalog, ComponentCategory};
pub use components::{ComponentDef, PropDef};
pub use prompt::{PromptOptions, generate_prompt};
use std::sync::LazyLock;
use super::{Capability, CapabilityLocalization, CapabilityStatus};
pub fn default_catalog() -> &'static Catalog {
&DEFAULT_CATALOG
}
static DEFAULT_CATALOG: LazyLock<Catalog> = LazyLock::new(|| Catalog {
root_hint: "Card",
components: components::all_components(),
categories: catalog::default_categories(),
});
pub fn default_prompt() -> &'static str {
&DEFAULT_PROMPT
}
static DEFAULT_PROMPT: LazyLock<String> =
LazyLock::new(|| generate_prompt(default_catalog(), &PromptOptions::default()));
pub const A2UI_CAPABILITY_ID: &str = "a2ui";
pub struct A2UiCapability;
impl Capability for A2UiCapability {
fn id(&self) -> &str {
A2UI_CAPABILITY_ID
}
fn name(&self) -> &str {
"A2UI"
}
fn description(&self) -> &str {
"Enables the agent to emit generative UI as Google A2UI JSON component trees, rendered by the UI with native design-system components."
}
fn localizations(&self) -> Vec<CapabilityLocalization> {
vec![CapabilityLocalization::text(
"uk",
"A2UI",
"Дає агенту змогу видавати генеративний UI як дерева компонентів Google A2UI у форматі JSON, які інтерфейс рендерить нативними компонентами дизайн-системи.",
)]
}
fn status(&self) -> CapabilityStatus {
CapabilityStatus::Available
}
fn icon(&self) -> Option<&str> {
Some("layout-grid")
}
fn category(&self) -> Option<&str> {
Some("UI")
}
fn system_prompt_addition(&self) -> Option<&str> {
Some(default_prompt())
}
fn features(&self) -> Vec<&'static str> {
vec!["a2ui"]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn capability_has_system_prompt() {
let cap = A2UiCapability;
let prompt = cap.system_prompt_addition().expect("prompt present");
assert!(prompt.contains("```a2ui"));
assert!(prompt.contains("## Schema"));
assert!(prompt.contains("## Catalog"));
assert!(prompt.contains("## Actions"));
}
#[test]
fn capability_features() {
let cap = A2UiCapability;
assert_eq!(cap.features(), vec!["a2ui"]);
}
#[test]
fn system_prompt_lists_core_components() {
let cap = A2UiCapability;
let p = cap.system_prompt_addition().unwrap();
for name in ["Card", "Stack", "Button", "Form", "List", "Table"] {
assert!(p.contains(name), "prompt missing component {name}");
}
}
}
#[cfg(test)]
mod catalog_tests {
use std::collections::HashSet;
use super::*;
#[test]
fn default_catalog_covers_components_and_categories() {
let catalog = default_catalog();
let names: Vec<&str> = catalog
.components
.iter()
.map(|component| component.name)
.collect();
for expected in ["Card", "Stack", "Button", "List", "Table", "Form"] {
assert!(names.contains(&expected), "missing component: {expected}");
}
let grouped: HashSet<&str> = catalog
.categories
.iter()
.flat_map(|category| category.components.iter().copied())
.collect();
for component in &catalog.components {
assert!(
grouped.contains(component.name),
"component '{}' is not in any category",
component.name
);
}
}
#[test]
fn default_prompt_contains_every_component_and_action_types() {
let prompt = default_prompt();
for component in &default_catalog().components {
assert!(prompt.contains(component.name));
}
assert!(prompt.contains("\"message\""));
assert!(prompt.contains("\"open_url\""));
}
}