adk_browser/tools/
windows.rs

1//! Window and tab management tools.
2
3use crate::session::BrowserSession;
4use adk_core::{AdkError, Result, Tool, ToolContext};
5use async_trait::async_trait;
6use serde_json::{json, Value};
7use std::sync::Arc;
8
9/// Tool for listing all windows/tabs.
10pub struct ListWindowsTool {
11    browser: Arc<BrowserSession>,
12}
13
14impl ListWindowsTool {
15    pub fn new(browser: Arc<BrowserSession>) -> Self {
16        Self { browser }
17    }
18}
19
20#[async_trait]
21impl Tool for ListWindowsTool {
22    fn name(&self) -> &str {
23        "browser_list_windows"
24    }
25
26    fn description(&self) -> &str {
27        "List all open browser windows/tabs."
28    }
29
30    fn parameters_schema(&self) -> Option<Value> {
31        Some(json!({
32            "type": "object",
33            "properties": {}
34        }))
35    }
36
37    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
38        let (windows, current) = self.browser.list_windows().await?;
39
40        Ok(json!({
41            "success": true,
42            "windows": windows,
43            "current_window": current,
44            "count": windows.len()
45        }))
46    }
47}
48
49/// Tool for opening a new tab.
50pub struct NewTabTool {
51    browser: Arc<BrowserSession>,
52}
53
54impl NewTabTool {
55    pub fn new(browser: Arc<BrowserSession>) -> Self {
56        Self { browser }
57    }
58}
59
60#[async_trait]
61impl Tool for NewTabTool {
62    fn name(&self) -> &str {
63        "browser_new_tab"
64    }
65
66    fn description(&self) -> &str {
67        "Open a new browser tab and switch to it."
68    }
69
70    fn parameters_schema(&self) -> Option<Value> {
71        Some(json!({
72            "type": "object",
73            "properties": {
74                "url": {
75                    "type": "string",
76                    "description": "Optional URL to navigate to in the new tab"
77                }
78            }
79        }))
80    }
81
82    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
83        let url = args.get("url").and_then(|v| v.as_str());
84
85        let handle = self.browser.new_tab().await?;
86
87        if let Some(url) = url {
88            self.browser.navigate(url).await?;
89        }
90
91        let current_url = self.browser.current_url().await.unwrap_or_default();
92
93        Ok(json!({
94            "success": true,
95            "window_handle": handle,
96            "url": current_url
97        }))
98    }
99}
100
101/// Tool for opening a new window.
102pub struct NewWindowTool {
103    browser: Arc<BrowserSession>,
104}
105
106impl NewWindowTool {
107    pub fn new(browser: Arc<BrowserSession>) -> Self {
108        Self { browser }
109    }
110}
111
112#[async_trait]
113impl Tool for NewWindowTool {
114    fn name(&self) -> &str {
115        "browser_new_window"
116    }
117
118    fn description(&self) -> &str {
119        "Open a new browser window and switch to it."
120    }
121
122    fn parameters_schema(&self) -> Option<Value> {
123        Some(json!({
124            "type": "object",
125            "properties": {
126                "url": {
127                    "type": "string",
128                    "description": "Optional URL to navigate to in the new window"
129                }
130            }
131        }))
132    }
133
134    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
135        let url = args.get("url").and_then(|v| v.as_str());
136
137        let handle = self.browser.new_window().await?;
138
139        if let Some(url) = url {
140            self.browser.navigate(url).await?;
141        }
142
143        let current_url = self.browser.current_url().await.unwrap_or_default();
144
145        Ok(json!({
146            "success": true,
147            "window_handle": handle,
148            "url": current_url
149        }))
150    }
151}
152
153/// Tool for switching to a window/tab.
154pub struct SwitchWindowTool {
155    browser: Arc<BrowserSession>,
156}
157
158impl SwitchWindowTool {
159    pub fn new(browser: Arc<BrowserSession>) -> Self {
160        Self { browser }
161    }
162}
163
164#[async_trait]
165impl Tool for SwitchWindowTool {
166    fn name(&self) -> &str {
167        "browser_switch_window"
168    }
169
170    fn description(&self) -> &str {
171        "Switch to a different browser window/tab by its handle."
172    }
173
174    fn parameters_schema(&self) -> Option<Value> {
175        Some(json!({
176            "type": "object",
177            "properties": {
178                "handle": {
179                    "type": "string",
180                    "description": "The window handle to switch to"
181                }
182            },
183            "required": ["handle"]
184        }))
185    }
186
187    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
188        let handle = args
189            .get("handle")
190            .and_then(|v| v.as_str())
191            .ok_or_else(|| AdkError::Tool("Missing 'handle' parameter".to_string()))?;
192
193        self.browser.switch_to_window(handle).await?;
194
195        let url = self.browser.current_url().await.unwrap_or_default();
196        let title = self.browser.title().await.unwrap_or_default();
197
198        Ok(json!({
199            "success": true,
200            "switched_to": handle,
201            "url": url,
202            "title": title
203        }))
204    }
205}
206
207/// Tool for closing the current window/tab.
208pub struct CloseWindowTool {
209    browser: Arc<BrowserSession>,
210}
211
212impl CloseWindowTool {
213    pub fn new(browser: Arc<BrowserSession>) -> Self {
214        Self { browser }
215    }
216}
217
218#[async_trait]
219impl Tool for CloseWindowTool {
220    fn name(&self) -> &str {
221        "browser_close_window"
222    }
223
224    fn description(&self) -> &str {
225        "Close the current browser window/tab."
226    }
227
228    fn parameters_schema(&self) -> Option<Value> {
229        Some(json!({
230            "type": "object",
231            "properties": {}
232        }))
233    }
234
235    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
236        self.browser.close_window().await?;
237
238        Ok(json!({
239            "success": true,
240            "message": "Window closed"
241        }))
242    }
243}
244
245/// Tool for maximizing the window.
246pub struct MaximizeWindowTool {
247    browser: Arc<BrowserSession>,
248}
249
250impl MaximizeWindowTool {
251    pub fn new(browser: Arc<BrowserSession>) -> Self {
252        Self { browser }
253    }
254}
255
256#[async_trait]
257impl Tool for MaximizeWindowTool {
258    fn name(&self) -> &str {
259        "browser_maximize_window"
260    }
261
262    fn description(&self) -> &str {
263        "Maximize the browser window."
264    }
265
266    fn parameters_schema(&self) -> Option<Value> {
267        Some(json!({
268            "type": "object",
269            "properties": {}
270        }))
271    }
272
273    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
274        self.browser.maximize_window().await?;
275
276        Ok(json!({
277            "success": true,
278            "message": "Window maximized"
279        }))
280    }
281}
282
283/// Tool for minimizing the window.
284pub struct MinimizeWindowTool {
285    browser: Arc<BrowserSession>,
286}
287
288impl MinimizeWindowTool {
289    pub fn new(browser: Arc<BrowserSession>) -> Self {
290        Self { browser }
291    }
292}
293
294#[async_trait]
295impl Tool for MinimizeWindowTool {
296    fn name(&self) -> &str {
297        "browser_minimize_window"
298    }
299
300    fn description(&self) -> &str {
301        "Minimize the browser window."
302    }
303
304    fn parameters_schema(&self) -> Option<Value> {
305        Some(json!({
306            "type": "object",
307            "properties": {}
308        }))
309    }
310
311    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
312        self.browser.minimize_window().await?;
313
314        Ok(json!({
315            "success": true,
316            "message": "Window minimized"
317        }))
318    }
319}
320
321/// Tool for setting window size.
322pub struct SetWindowSizeTool {
323    browser: Arc<BrowserSession>,
324}
325
326impl SetWindowSizeTool {
327    pub fn new(browser: Arc<BrowserSession>) -> Self {
328        Self { browser }
329    }
330}
331
332#[async_trait]
333impl Tool for SetWindowSizeTool {
334    fn name(&self) -> &str {
335        "browser_set_window_size"
336    }
337
338    fn description(&self) -> &str {
339        "Set the browser window size and position."
340    }
341
342    fn parameters_schema(&self) -> Option<Value> {
343        Some(json!({
344            "type": "object",
345            "properties": {
346                "width": {
347                    "type": "integer",
348                    "description": "Window width in pixels"
349                },
350                "height": {
351                    "type": "integer",
352                    "description": "Window height in pixels"
353                },
354                "x": {
355                    "type": "integer",
356                    "description": "Window X position (default: 0)"
357                },
358                "y": {
359                    "type": "integer",
360                    "description": "Window Y position (default: 0)"
361                }
362            },
363            "required": ["width", "height"]
364        }))
365    }
366
367    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
368        let width = args
369            .get("width")
370            .and_then(|v| v.as_u64())
371            .ok_or_else(|| AdkError::Tool("Missing 'width' parameter".to_string()))?
372            as u32;
373
374        let height = args
375            .get("height")
376            .and_then(|v| v.as_u64())
377            .ok_or_else(|| AdkError::Tool("Missing 'height' parameter".to_string()))?
378            as u32;
379
380        let x = args.get("x").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
381        let y = args.get("y").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
382
383        self.browser.set_window_rect(x, y, width, height).await?;
384
385        Ok(json!({
386            "success": true,
387            "width": width,
388            "height": height,
389            "x": x,
390            "y": y
391        }))
392    }
393}