diff --git a/cli/src/native/snapshot.rs b/cli/src/native/snapshot.rs index 2196eb5..8aec758 100644 --- a/cli/src/native/snapshot.rs +++ b/cli/src/native/snapshot.rs @@ -148,12 +148,38 @@ impl TreeNode { } } +/// The type of a hidden form input found inside a cursor-interactive element. +#[derive(Clone, Copy)] +enum HiddenInputKind { + Radio, + Checkbox, +} + +impl HiddenInputKind { + fn parse(s: &str) -> Option { + match s { + "radio" => Some(Self::Radio), + "checkbox" => Some(Self::Checkbox), + _ => None, + } + } + + fn as_role(&self) -> &str { + match self { + Self::Radio => "radio", + Self::Checkbox => "checkbox", + } + } +} + /// Information about a cursor-interactive element (elements with cursor:pointer, onclick, tabindex, etc.) #[derive(Clone)] struct CursorElementInfo { kind: String, // "clickable", "focusable", "editable" hints: Vec, text: String, // textContent from the DOM element (fallback when ARIA name is empty) + hidden_input_kind: Option, + hidden_input_checked: Option, // "true", "false", or "mixed" (tristate) } struct RoleNameTracker { @@ -277,7 +303,7 @@ pub async fn take_snapshot( ) .await?; - let (tree_nodes, root_indices) = build_tree(&ax_tree.nodes); + let (mut tree_nodes, root_indices) = build_tree(&ax_tree.nodes); // When a selector is given, find AX nodes whose backendDOMNodeId falls // within the target DOM subtree and pick the top-level ones as roots. @@ -323,6 +349,8 @@ pub async fn take_snapshot( .await .unwrap_or_default(); + promote_hidden_inputs(&mut tree_nodes, &cursor_elements); + for (idx, node) in tree_nodes.iter().enumerate() { let role = node.role.as_str(); let mut should_ref = if INTERACTIVE_ROLES.contains(&role) { @@ -349,7 +377,6 @@ pub async fn take_snapshot( let duplicates = tracker.get_duplicates(); - let mut tree_nodes = tree_nodes; for (idx, nth) in &nodes_with_refs { let node = &tree_nodes[*idx]; let key = format!("{}:{}", node.role, node.name); @@ -639,6 +666,23 @@ async fn find_cursor_interactive_elements( var rect = el.getBoundingClientRect(); if (rect.width === 0 || rect.height === 0) continue; + // Detect hidden radio/checkbox inputs inside this element (common pattern: + //