adk_browser/tools/
click.rs1use crate::session::BrowserSession;
4use adk_core::{Result, Tool, ToolContext};
5use async_trait::async_trait;
6use serde_json::{json, Value};
7use std::sync::Arc;
8
9pub struct ClickTool {
11 browser: Arc<BrowserSession>,
12}
13
14impl ClickTool {
15 pub fn new(browser: Arc<BrowserSession>) -> Self {
17 Self { browser }
18 }
19}
20
21#[async_trait]
22impl Tool for ClickTool {
23 fn name(&self) -> &str {
24 "browser_click"
25 }
26
27 fn description(&self) -> &str {
28 "Click on an element on the page. Use CSS selectors to identify the element."
29 }
30
31 fn parameters_schema(&self) -> Option<Value> {
32 Some(json!({
33 "type": "object",
34 "properties": {
35 "selector": {
36 "type": "string",
37 "description": "CSS selector for the element to click (e.g., '#submit-btn', '.nav-link', 'button[type=submit]')"
38 },
39 "wait_timeout": {
40 "type": "integer",
41 "description": "Optional timeout in seconds to wait for element to be clickable (default: 10)"
42 }
43 },
44 "required": ["selector"]
45 }))
46 }
47
48 fn response_schema(&self) -> Option<Value> {
49 Some(json!({
50 "type": "object",
51 "properties": {
52 "success": { "type": "boolean" },
53 "clicked_element": { "type": "string" }
54 }
55 }))
56 }
57
58 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
59 let selector = args
60 .get("selector")
61 .and_then(|v| v.as_str())
62 .ok_or_else(|| adk_core::AdkError::Tool("Missing 'selector' parameter".to_string()))?;
63
64 let wait_timeout = args.get("wait_timeout").and_then(|v| v.as_u64()).unwrap_or(10);
65
66 let element = self.browser.wait_for_clickable(selector, wait_timeout).await?;
68
69 element
70 .click()
71 .await
72 .map_err(|e| adk_core::AdkError::Tool(format!("Click failed: {}", e)))?;
73
74 let tag_name = element.tag_name().await.unwrap_or_else(|_| "unknown".to_string());
76
77 let text = element.text().await.unwrap_or_default();
78 let element_info = if text.is_empty() {
79 tag_name
80 } else {
81 format!("{}: {}", tag_name, text.chars().take(50).collect::<String>())
82 };
83
84 Ok(json!({
85 "success": true,
86 "clicked_element": element_info
87 }))
88 }
89}
90
91pub struct DoubleClickTool {
93 browser: Arc<BrowserSession>,
94}
95
96impl DoubleClickTool {
97 pub fn new(browser: Arc<BrowserSession>) -> Self {
98 Self { browser }
99 }
100}
101
102#[async_trait]
103impl Tool for DoubleClickTool {
104 fn name(&self) -> &str {
105 "browser_double_click"
106 }
107
108 fn description(&self) -> &str {
109 "Double-click on an element on the page."
110 }
111
112 fn parameters_schema(&self) -> Option<Value> {
113 Some(json!({
114 "type": "object",
115 "properties": {
116 "selector": {
117 "type": "string",
118 "description": "CSS selector for the element to double-click"
119 }
120 },
121 "required": ["selector"]
122 }))
123 }
124
125 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
126 let selector = args
127 .get("selector")
128 .and_then(|v| v.as_str())
129 .ok_or_else(|| adk_core::AdkError::Tool("Missing 'selector' parameter".to_string()))?;
130
131 let element = self.browser.find_element(selector).await?;
132
133 self.browser
135 .execute_script(&format!(
136 "document.querySelector('{}').dispatchEvent(new MouseEvent('dblclick', {{'view': window, 'bubbles': true, 'cancelable': true}}))",
137 selector.replace('\'', "\\'")
138 ))
139 .await?;
140
141 let tag_name = element.tag_name().await.unwrap_or_else(|_| "unknown".to_string());
142
143 Ok(json!({
144 "success": true,
145 "double_clicked_element": tag_name
146 }))
147 }
148}