adk_ui/tools/
render_modal.rs1use crate::schema::*;
2use crate::tools::{LegacyProtocolOptions, render_ui_response_with_protocol};
3use adk_core::{Result, Tool, ToolContext};
4use async_trait::async_trait;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
12pub struct RenderModalParams {
13 pub title: String,
15 pub message: String,
17 #[serde(default = "default_size")]
19 pub size: String,
20 #[serde(default = "default_true")]
22 pub closable: bool,
23 pub confirm_label: Option<String>,
25 pub cancel_label: Option<String>,
27 #[serde(default = "default_confirm_action")]
29 pub confirm_action: String,
30 #[serde(default = "default_cancel_action")]
32 pub cancel_action: String,
33 #[serde(flatten)]
35 pub protocol: LegacyProtocolOptions,
36}
37
38fn default_size() -> String {
39 "medium".to_string()
40}
41
42fn default_true() -> bool {
43 true
44}
45
46fn default_confirm_action() -> String {
47 "modal_confirm".to_string()
48}
49
50fn default_cancel_action() -> String {
51 "modal_cancel".to_string()
52}
53
54pub struct RenderModalTool;
73
74impl RenderModalTool {
75 pub fn new() -> Self {
76 Self
77 }
78}
79
80impl Default for RenderModalTool {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86#[async_trait]
87impl Tool for RenderModalTool {
88 fn name(&self) -> &str {
89 "render_modal"
90 }
91
92 fn description(&self) -> &str {
93 "Render a modal dialog overlay. Use for important messages, confirmations, or focused interactions that require user attention."
94 }
95
96 fn parameters_schema(&self) -> Option<Value> {
97 Some(super::generate_gemini_schema::<RenderModalParams>())
98 }
99
100 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
101 let params: RenderModalParams = serde_json::from_value(args)
102 .map_err(|e| adk_core::AdkError::Tool(format!("Invalid parameters: {}", e)))?;
103 let protocol_options = params.protocol.clone();
104
105 let size = match params.size.as_str() {
106 "small" => ModalSize::Small,
107 "large" => ModalSize::Large,
108 "full" => ModalSize::Full,
109 _ => ModalSize::Medium,
110 };
111
112 let content = vec![Component::Text(Text {
114 id: None,
115 content: params.message,
116 variant: TextVariant::Body,
117 })];
118
119 let footer = if params.confirm_label.is_some() || params.cancel_label.is_some() {
121 let mut buttons = Vec::new();
122 if let Some(cancel) = params.cancel_label {
123 buttons.push(Component::Button(Button {
124 id: None,
125 label: cancel,
126 action_id: params.cancel_action,
127 variant: ButtonVariant::Secondary,
128 disabled: false,
129 icon: None,
130 }));
131 }
132 if let Some(confirm) = params.confirm_label {
133 buttons.push(Component::Button(Button {
134 id: None,
135 label: confirm,
136 action_id: params.confirm_action,
137 variant: ButtonVariant::Primary,
138 disabled: false,
139 icon: None,
140 }));
141 }
142 Some(buttons)
143 } else {
144 None
145 };
146
147 let ui = UiResponse::new(vec![Component::Modal(Modal {
148 id: None,
149 title: params.title,
150 content,
151 footer,
152 size,
153 closable: params.closable,
154 })]);
155
156 render_ui_response_with_protocol(ui, &protocol_options, "modal")
157 }
158}