adk_ui/tools/
render_modal.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
11pub struct RenderModalParams {
12 pub title: String,
14 pub message: String,
16 #[serde(default = "default_size")]
18 pub size: String,
19 #[serde(default = "default_true")]
21 pub closable: bool,
22 pub confirm_label: Option<String>,
24 pub cancel_label: Option<String>,
26 #[serde(default = "default_confirm_action")]
28 pub confirm_action: String,
29 #[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
50pub 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 let content = vec![Component::Text(Text {
109 id: None,
110 content: params.message,
111 variant: TextVariant::Body,
112 })];
113
114 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}