Skip to main content

Crate smix_screen

Crate smix_screen 

Source
Expand description

§smix-screen

Crates.io docs.rs License

Accessibility tree types for iOS-Simulator automation — A11yNode, Rect, Bounds, Role, ElementSummary, ScreenDescription — with inlined visibility primitives that approach single-cycle latency on M-series machines.

Zero-allocation arithmetic, #[inline] on the hot path, criterion bench medians available below for reference. The visibility math is the same semantics maestro and Apple’s XCUITest enforce (any non-empty frame intersection with the viewport, zero-width / zero-height bounds short-circuit to false).

§Quickstart

use smix_screen::{
    A11yNode, Rect, Role, collect_visible_summaries, is_visible_enough,
    summarize_node, visible_area,
};

let node = A11yNode {
    raw_type: "button".into(),
    role: Some(Role::Button),
    identifier: Some("btn-login".into()),
    label: Some("Log in".into()),
    title: None, placeholder_value: None, value: None, text: None,
    bounds: Rect { x: 50.0, y: 100.0, w: 200.0, h: 40.0 },
    enabled: true, selected: false, has_focus: false, visible: true,
    children: vec![],
};
let viewport = Rect { x: 0.0, y: 0.0, w: 390.0, h: 844.0 };
let tree = A11yNode {
    raw_type: "application".into(),
    bounds: viewport,
    children: vec![node.clone()],
    ..node.clone()
};

assert!(is_visible_enough(&node, &tree));
assert_eq!(visible_area(&node, &tree), 200.0 * 40.0);

let summary = summarize_node(&node);
assert_eq!(summary.name.as_deref(), Some("Log in"));

let summaries = collect_visible_summaries(&tree, 100);
assert_eq!(summaries.len(), 2); // root + button

§Performance (criterion bench medians on M-series macOS)

OperationTS V8 baselinesmix-screen RustSpeedup
is_visible_enough (in-view)21.2 ns0.996 ns21×
is_visible_enough (zero-bounds early reject)20.5 ns0.449 ns46×
is_visible_enough (unknown-root conservative pass)20.6 ns0.584 ns35×
is_visible_enough (offscreen)20.7 ns0.805 ns26×
visible_area (intersection arithmetic)20.9 ns1.112 ns19×
dfs_collect 100-node composite788 ns233 ns3.4×

Numbers reproducible via cargo bench --bench visibility -p smix-screen.

§When to reach for this

Use casePick
Need typed A11yNode matching Apple’s XCUIElement attribute setsmix-screen
Need to filter “visible enough” candidates on a hot pathsmix-screen
Want the same visibility semantics as maestro / XCUITest .isHittablesmix-screen
Need a full DOM/HTML accessibility tree (browser context)use a browser-focused crate; this is iOS-shaped
Need physical device a11y treethis is sim-shaped (camelCase wire fields match xcrun simctl io outputs)

§Wire compatibility

JSON serialization uses serde(rename_all = "camelCase") matching the iOS-Simulator runner conventions: rawType, placeholderValue, hasFocus. children: Vec<A11yNode> is recursive; terminal nodes may omit the field (serde default = "...").

The Role enum mirrors XCUIElement.ElementType literals (29 variants) with a stable camelCase wire name accessor (Role::Button.as_str()"button").

§Scope

  • ✅ Pure types + visibility primitives
  • #[inline] + zero-allocation hot path
  • ✅ camelCase JSON wire matches the iOS sim runner format
  • ScreenDescription + collect_visible_summaries for DFS-pre-order visible-element projection
  • ❌ No tree fetch (use smix-runner-client for the HTTP IPC)
  • ❌ No selector parsing (use smix-selector)
  • ❌ No resolver / spatial filter logic (use smix-selector-resolver)

§License

Dual-licensed under either:

at your option. smix-screen — A11yNode + Rect + Bounds + Role types + visibility primitives (stone).

§Scope

  • Pure types (Rect, Bounds, Role, A11yNode) with serde wire compatibility (camelCase JSON, matching the existing Swift-side SmixRunnerCore /tree route shape).
  • Pure functions (is_visible_enough, visible_area) that the selector resolver consumes. No I/O — protocol parsing and types only.

§Visibility semantics

  • b.w <= 0 || b.h <= 0 → invisible (zero-bounds early reject)
  • root.w <= 0 || root.h <= 0 → conservative pass (unknown root)
  • Otherwise → any non-empty rectangle intersection with tree.bounds

Matches swift TreeRoute.isVisible (any frame ∩ appFrame intersection) and maestro ViewHierarchy.kt:40-50 isVisible(node).

Structs§

A11yNode
Accessibility tree node.
ElementSummary
Element summary — projected view of an A11yNode used in AI-readable failure prompts and driver.describe() output.
Rect
Logical-points rectangle (origin top-left, +x right, +y down — matches UIKit / XCUITest coordinate space). All fields f64 because the runner /tree route emits floating-point points (sub-pixel scale factors).
ScreenDescription
Aggregate screen description. elements is a DFS-collected ordered list of visible+enabled ElementSummary entries; screenshot is an optional base64 PNG; frontApp / summary / captured_at are caller-populated metadata.

Enums§

Role
Accessibility role enum (29 variants).

Constants§

DEFAULT_VISIBLE_LIMIT
Default visible-summary limit.

Functions§

collect_visible_summaries
Collect up to limit visible+enabled nodes (DFS pre-order), projecting each via summarize_node. Default limit = 1000.
derive_roles_recursive
Recursively fill node.role from node.raw_type whenever it is currently None. Host-set roles (test fixtures, recorder output) are left untouched.
is_visible_enough
Visibility check.
role_from_raw_type
Derive the curated Role from the raw XCUIElement type name (e.g. "button", "radioButton", "progressIndicator").
summarize_node
Project an A11yNode to an ElementSummary.
visible_area
Intersection area in logical points².

Type Aliases§

Bounds
Bounds alias — Bounds and Rect are used interchangeably. Downstream code may prefer one name or the other.