adk_ui/tools/
render_modal.rs

1use crate::schema::*;
2use adk_core::{Result, Tool, ToolContext};
3use async_trait::async_trait;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::sync::Arc;
8
9/// Parameters for the render_modal tool
10#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
11pub struct RenderModalParams {
12    /// Modal title shown in the header
13    pub title: String,
14    /// Main message or content to display
15    pub message: String,
16    /// Modal size: small, medium, large, full
17    #[serde(default = "default_size")]
18    pub size: String,
19    /// Whether to show the close button
20    #[serde(default = "default_true")]
21    pub closable: bool,
22    /// Optional confirm button label
23    pub confirm_label: Option<String>,
24    /// Optional cancel button label
25    pub cancel_label: Option<String>,
26    /// Action ID for the confirm button
27    #[serde(default = "default_confirm_action")]
28    pub confirm_action: String,
29    /// Action ID for the cancel button
30    #[serde(default = "default_cancel_action")]
31    pub cancel_action: String,
32}
33
34fn default_size() -> String {
35    "medium".to_string()
36}
37
38fn default_true() -> bool {
39    true
40}
41
42fn default_confirm_action() -> String {
43    "modal_confirm".to_string()
44}
45
46fn default_cancel_action() -> String {
47    "modal_cancel".to_string()
48}
49
50/// Tool for rendering modal dialogs.
51///
52/// Creates overlay modal dialogs for focused interactions, confirmations,
53/// or important messages that require user attention.
54///
55/// # Example JSON Parameters
56///
57/// ```json
58/// {
59///   "title": "Confirm Action",
60///   "message": "Are you sure you want to proceed with this action?",
61///   "size": "medium",
62///   "confirm_label": "Yes, Proceed",
63///   "cancel_label": "Cancel",
64///   "confirm_action": "confirm_action",
65///   "cancel_action": "cancel_action"
66/// }
67/// ```
68pub struct RenderModalTool;
69
70impl RenderModalTool {
71    pub fn new() -> Self {
72        Self
73    }
74}
75
76impl Default for RenderModalTool {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82#[async_trait]
83impl Tool for RenderModalTool {
84    fn name(&self) -> &str {
85        "render_modal"
86    }
87
88    fn description(&self) -> &str {
89        "Render a modal dialog overlay. Use for important messages, confirmations, or focused interactions that require user attention."
90    }
91
92    fn parameters_schema(&self) -> Option<Value> {
93        Some(super::generate_gemini_schema::<RenderModalParams>())
94    }
95
96    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
97        let params: RenderModalParams = serde_json::from_value(args)
98            .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
99
100        let size = match params.size.as_str() {
101            "small" => ModalSize::Small,
102            "large" => ModalSize::Large,
103            "full" => ModalSize::Full,
104            _ => ModalSize::Medium,
105        };
106
107        // Build modal content
108        let content = vec![Component::Text(Text {
109            id: None,
110            content: params.message,
111            variant: TextVariant::Body,
112        })];
113
114        // Build footer with buttons if provided
115        let footer = if params.confirm_label.is_some() || params.cancel_label.is_some() {
116            let mut buttons = Vec::new();
117            if let Some(cancel) = params.cancel_label {
118                buttons.push(Component::Button(Button {
119                    id: None,
120                    label: cancel,
121                    action_id: params.cancel_action,
122                    variant: ButtonVariant::Secondary,
123                    disabled: false,
124                    icon: None,
125                }));
126            }
127            if let Some(confirm) = params.confirm_label {
128                buttons.push(Component::Button(Button {
129                    id: None,
130                    label: confirm,
131                    action_id: params.confirm_action,
132                    variant: ButtonVariant::Primary,
133                    disabled: false,
134                    icon: None,
135                }));
136            }
137            Some(buttons)
138        } else {
139            None
140        };
141
142        let ui = UiResponse::new(vec![Component::Modal(Modal {
143            id: None,
144            title: params.title,
145            content,
146            footer,
147            size,
148            closable: params.closable,
149        })]);
150
151        serde_json::to_value(ui)
152            .map_err(|e| adk_core::AdkError::Tool(format!("Failed to serialize UI: {}", e)))
153    }
154}