adk_browser/tools/
click.rs

1//! Click tool for interacting with page elements.
2
3use crate::session::BrowserSession;
4use adk_core::{Result, Tool, ToolContext};
5use async_trait::async_trait;
6use serde_json::{json, Value};
7use std::sync::Arc;
8
9/// Tool for clicking elements on the page.
10pub struct ClickTool {
11    browser: Arc<BrowserSession>,
12}
13
14impl ClickTool {
15    /// Create a new click tool with a shared browser session.
16    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        // Wait for element to be clickable, then click
67        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        // Get element info for response
75        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
91/// Tool for double-clicking elements.
92pub 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        // Execute double-click via JS
134        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}