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