//! DOM interaction tools (`tool.click`, `tool.fill`, `tool.press`, //! `tool.select`). //! //! Element-targeted tools accept either a snapshot `ref` (`@e` form, //! normalised against the session's RefStore) **or** a CSS selector //! resolved at call time. Modifiers / mouse buttons are encoded as //! lowercase JSON strings so the same wire shape works for CLI flags and //! the extension's CDP bridge. use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use super::JavaScriptDialogInfo; /// Keyboard modifier flags. Multiple flags may be combined; the /// extension folds them into CDP's bitfield (`alt=1, ctrl=2, meta=4, /// shift=8`). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum KeyModifier { Alt, Ctrl, Meta, Shift, } /// Mouse button selector for `tool.click`. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum MouseButton { #[default] Left, Middle, Right, } // --------------------------------------------------------------------------- // click // --------------------------------------------------------------------------- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct ClickParams { pub session_id: String, /// Optional `@e` ref allocated by the last `tool.snapshot`. /// Mutually exclusive with `selector` (caller must supply exactly /// one). Accepts both `"e3"` and `"@e3"`. #[serde( rename = "ref", alias = "ref_", default, skip_serializing_if = "Option::is_none" )] pub ref_: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub selector: Option, /// Target tab. Defaults to the Agent Window's active tab. #[serde(default, skip_serializing_if = "Option::is_none")] pub tab_id: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub button: Option, /// Number of consecutive mouse presses (double-click = 2). #[serde(default, skip_serializing_if = "Option::is_none")] #[schemars(range(min = 1))] pub click_count: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub modifiers: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] #[schemars(range(min = 1))] pub timeout_ms: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct ClickResult { pub tab_id: i64, #[serde(default, skip_serializing_if = "Option::is_none")] pub used_ref: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub used_selector: Option, /// Viewport-relative click coordinates (CSS pixels). Reported so /// agents can correlate with a follow-up `tool.screenshot`. pub x: f64, pub y: f64, /// Native JS dialogs observed and auto-handled during this call. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub dialogs: Vec, } // --------------------------------------------------------------------------- // fill // --------------------------------------------------------------------------- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct FillParams { pub session_id: String, pub value: String, #[serde( rename = "ref", alias = "ref_", default, skip_serializing_if = "Option::is_none" )] pub ref_: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub selector: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub tab_id: Option, /// Clear the field before typing. Defaults to `true`; pass `false` /// to append instead of replacing. #[serde(default, skip_serializing_if = "Option::is_none")] pub clear_before: Option, #[serde(default, skip_serializing_if = "Option::is_none")] #[schemars(range(min = 1))] pub timeout_ms: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct FillResult { pub tab_id: i64, #[serde(default, skip_serializing_if = "Option::is_none")] pub used_ref: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub used_selector: Option, /// UTF-16 code-unit length of the value that was finally typed /// (matches what `input.value.length` would report in the page). pub value_length: u32, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub dialogs: Vec, } // --------------------------------------------------------------------------- // press // --------------------------------------------------------------------------- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct PressParams { pub session_id: String, /// Logical key name. Accepts CDP `key` strings (`Enter`, `Escape`, /// `ArrowDown`, single characters like `a`), or a compound /// expression such as `Ctrl+A` / `Meta+Shift+P`. Modifiers in the /// compound form combine with anything supplied via `modifiers`. pub key: String, #[serde(default, skip_serializing_if = "Option::is_none")] pub modifiers: Option>, /// Optional target to focus before dispatching the key. #[serde( rename = "ref", alias = "ref_", default, skip_serializing_if = "Option::is_none" )] pub ref_: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub selector: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub tab_id: Option, /// Hold the key down for this many milliseconds between `keyDown` /// and `keyUp`. Useful for testing long-press handlers. #[serde(default, skip_serializing_if = "Option::is_none")] pub hold_ms: Option, #[serde(default, skip_serializing_if = "Option::is_none")] #[schemars(range(min = 1))] pub timeout_ms: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct PressResult { pub tab_id: i64, pub key: String, pub code: String, #[serde(default)] pub modifiers: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub dialogs: Vec, } // --------------------------------------------------------------------------- // select // --------------------------------------------------------------------------- #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct SelectParams { pub session_id: String, /// Option `value` strings to set as the final selection. For a /// single-select `` the list replaces the current selection /// (an empty list clears all selections). pub values: Vec, /// Optional `@e` ref allocated by the last `tool.snapshot`. /// Mutually exclusive with `selector`. Accepts both `"e3"` and /// `"@e3"`. #[serde( rename = "ref", alias = "ref_", default, skip_serializing_if = "Option::is_none" )] pub ref_: Option, /// CSS selector resolved against the live DOM. Mutually exclusive /// with `ref_`. #[serde(default, skip_serializing_if = "Option::is_none")] pub selector: Option, /// Target tab. Defaults to the Agent Window's active tab. #[serde(default, skip_serializing_if = "Option::is_none")] pub tab_id: Option, /// Maximum time the daemon waits for the tool call before timing out. #[serde(default, skip_serializing_if = "Option::is_none")] #[schemars(range(min = 1))] pub timeout_ms: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct SelectResult { pub tab_id: i64, #[serde(default, skip_serializing_if = "Option::is_none")] pub used_ref: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub used_selector: Option, /// Whether the target `