Skip to main content

browsr_types/
commands.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
5pub struct CommandPoint {
6    pub x: f64,
7    pub y: f64,
8}
9
10#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
11#[serde(rename_all = "snake_case")]
12pub enum CommandType {
13    Action,
14    Extraction,
15}
16
17#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
18#[serde(rename_all = "snake_case")]
19pub enum PageContentKind {
20    Markdown,
21    Html,
22    Json,
23}
24
25impl Default for PageContentKind {
26    fn default() -> Self {
27        PageContentKind::Markdown
28    }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
32#[serde(tag = "command", rename_all = "snake_case", content = "data")]
33pub enum Commands {
34    ToggleClickOverlay {
35        enabled: bool,
36    },
37    ToggleBoundingBoxes {
38        enabled: bool,
39        selector: Option<String>,
40        limit: Option<usize>,
41        include_html: Option<bool>,
42    },
43    // Navigation commands
44    NavigateTo {
45        url: String,
46    },
47    Refresh,
48    WaitForNavigation {
49        timeout_ms: Option<u64>,
50    },
51    WaitForElement {
52        selector: String,
53        timeout_ms: Option<u64>,
54        visible_only: Option<bool>,
55    },
56
57    // Element interaction commands
58    Click {
59        selector: String,
60    },
61    ClickAt {
62        x: f64,
63        y: f64,
64    },
65    Clear {
66        selector: String,
67    },
68    PressKey {
69        selector: String,
70        key: String,
71    },
72
73    // Content extraction
74    GetContent {
75        #[serde(default, skip_serializing_if = "Option::is_none")]
76        selector: Option<String>,
77        #[serde(default)]
78        kind: Option<PageContentKind>,
79    },
80    GetText {
81        selector: String,
82    },
83    GetAttribute {
84        selector: String,
85        attribute: String,
86    },
87
88    // Page information
89    GetTitle,
90    ExtractStructuredContent {
91        query: String,
92        /// JSON schema for structured extraction - can be a string or a JSON object
93        #[serde(default, skip_serializing_if = "Option::is_none")]
94        schema: Option<serde_json::Value>,
95        #[serde(default, skip_serializing_if = "Option::is_none")]
96        max_chars: Option<usize>,
97    },
98
99    // JavaScript execution
100    Evaluate {
101        expression: String,
102    },
103    GetBoundingBoxes {
104        selector: String,
105        limit: Option<usize>,
106        include_html: Option<bool>,
107    },
108    InspectElement {
109        selector: String,
110    },
111
112    // Mouse and keyboard
113    /// Scroll to absolute coordinates. Both x and y are optional.
114    /// If x is omitted, horizontal scroll position is unchanged.
115    /// If y is omitted, vertical scroll position is unchanged.
116    ScrollTo {
117        #[serde(default)]
118        x: Option<f64>,
119        #[serde(default)]
120        y: Option<f64>,
121    },
122    /// Move mouse to coordinates with optional human-like interpolation.
123    /// If `steps` is provided, the mouse will move through intermediate points
124    /// with small delays to simulate human movement.
125    MoveMouseTo {
126        x: f64,
127        y: f64,
128        /// Number of intermediate steps for human-like movement. Default is 1 (instant).
129        /// Higher values (10-25) create smoother, more human-like paths.
130        steps: Option<u32>,
131    },
132    Drag {
133        from: CommandPoint,
134        to: CommandPoint,
135        modifiers: Option<i64>,
136    },
137    ScrollIntoView {
138        selector: String,
139    },
140
141    // Screenshot and capture
142    Screenshot {
143        full_page: Option<bool>,
144        path: Option<String>,
145    },
146
147    // Element-focused interactions
148    ClickAdvanced {
149        selector: String,
150        button: Option<String>,
151        click_count: Option<u8>,
152        modifiers: Option<i64>,
153    },
154    TypeText {
155        selector: String,
156        text: String,
157        clear: Option<bool>,
158    },
159    Hover {
160        selector: String,
161    },
162    Focus {
163        selector: String,
164    },
165    Check {
166        selector: String,
167    },
168    SelectOption {
169        selector: String,
170        values: Vec<String>,
171    },
172    DragTo {
173        selector: String,
174        target_selector: Option<String>,
175        source_position: Option<CommandPoint>,
176        target_position: Option<CommandPoint>,
177        modifiers: Option<i64>,
178    },
179    EvaluateOnElement {
180        selector: String,
181        expression: String,
182    },
183    GetElementBoundingBox {
184        selector: String,
185    },
186    ElementScreenshot {
187        selector: String,
188        format: Option<String>,
189        quality: Option<u8>,
190    },
191    GetBasicInfo {
192        selector: String,
193    },
194
195    // Cookie commands
196    /// Set a single cookie. For cookies with a domain, you may need to navigate to a matching URL first.
197    SetCookie {
198        name: String,
199        value: String,
200        #[serde(default, skip_serializing_if = "Option::is_none")]
201        domain: Option<String>,
202        #[serde(default, skip_serializing_if = "Option::is_none")]
203        path: Option<String>,
204        #[serde(default, skip_serializing_if = "Option::is_none")]
205        url: Option<String>,
206        #[serde(default, skip_serializing_if = "Option::is_none")]
207        secure: Option<bool>,
208        #[serde(default, skip_serializing_if = "Option::is_none")]
209        http_only: Option<bool>,
210        #[serde(default, skip_serializing_if = "Option::is_none")]
211        same_site: Option<String>,
212        #[serde(default, skip_serializing_if = "Option::is_none")]
213        expires: Option<f64>,
214    },
215    /// Set multiple cookies at once from a semicolon-separated string (e.g., "name1=value1; name2=value2")
216    SetCookies {
217        /// Semicolon-separated cookie string
218        cookies: String,
219        /// Domain to apply to all cookies
220        domain: String,
221        #[serde(default, skip_serializing_if = "Option::is_none")]
222        path: Option<String>,
223        #[serde(default, skip_serializing_if = "Option::is_none")]
224        secure: Option<bool>,
225        #[serde(default, skip_serializing_if = "Option::is_none")]
226        http_only: Option<bool>,
227    },
228    /// Get all cookies for the current page
229    GetCookies,
230    /// Delete a cookie by name
231    DeleteCookie {
232        name: String,
233        #[serde(default, skip_serializing_if = "Option::is_none")]
234        domain: Option<String>,
235    },
236
237    // LocalStorage commands (operates on current page's domain)
238    /// Get all localStorage items for the current page
239    GetLocalStorage,
240    /// Set localStorage items on the current page
241    SetLocalStorage {
242        /// Key-value pairs to set
243        items: Vec<LocalStorageItem>,
244    },
245    /// Clear all localStorage for the current page
246    ClearLocalStorage,
247
248    // SessionStorage commands (operates on current page's domain)
249    /// Get all sessionStorage items for the current page
250    GetSessionStorage,
251    /// Set sessionStorage items on the current page
252    SetSessionStorage {
253        /// Key-value pairs to set
254        items: Vec<LocalStorageItem>,
255    },
256    /// Clear all sessionStorage for the current page
257    ClearSessionStorage,
258
259    /// Capture complete browser state (cookies, localStorage, sessionStorage, current URL)
260    CaptureState,
261}
262
263/// A single localStorage or sessionStorage item
264#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
265pub struct LocalStorageItem {
266    pub key: String,
267    pub value: String,
268}
269
270#[derive(serde::Deserialize)]
271pub struct StepPayload {
272    pub commands: Vec<Commands>,
273    pub thinking: Option<String>,
274    pub evaluation_previous_goal: Option<String>,
275    pub memory: Option<String>,
276    pub next_goal: Option<String>,
277}
278
279impl Commands {
280    pub fn command_type(&self) -> CommandType {
281        match self {
282            Commands::GetContent { .. }
283            | Commands::GetText { .. }
284            | Commands::GetAttribute { .. }
285            | Commands::GetTitle
286            | Commands::ExtractStructuredContent { .. }
287            | Commands::Screenshot { .. }
288            | Commands::ElementScreenshot { .. }
289            | Commands::GetElementBoundingBox { .. }
290            | Commands::Evaluate { .. }
291            | Commands::EvaluateOnElement { .. }
292            | Commands::GetBoundingBoxes { .. }
293            | Commands::InspectElement { .. }
294            | Commands::GetBasicInfo { .. }
295            | Commands::GetCookies
296            | Commands::GetLocalStorage
297            | Commands::GetSessionStorage
298            | Commands::CaptureState => CommandType::Extraction,
299            _ => CommandType::Action,
300        }
301    }
302}