Expand description
§smix-screen
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)
| Operation | TS V8 baseline | smix-screen Rust | Speedup |
|---|---|---|---|
is_visible_enough (in-view) | 21.2 ns | 0.996 ns | 21× |
is_visible_enough (zero-bounds early reject) | 20.5 ns | 0.449 ns | 46× |
is_visible_enough (unknown-root conservative pass) | 20.6 ns | 0.584 ns | 35× |
is_visible_enough (offscreen) | 20.7 ns | 0.805 ns | 26× |
visible_area (intersection arithmetic) | 20.9 ns | 1.112 ns | 19× |
dfs_collect 100-node composite | 788 ns | 233 ns | 3.4× |
Numbers reproducible via cargo bench --bench visibility -p smix-screen.
§When to reach for this
| Use case | Pick |
|---|---|
Need typed A11yNode matching Apple’s XCUIElement attribute set | smix-screen |
| Need to filter “visible enough” candidates on a hot path | smix-screen |
Want the same visibility semantics as maestro / XCUITest .isHittable | smix-screen |
| Need a full DOM/HTML accessibility tree (browser context) | use a browser-focused crate; this is iOS-shaped |
| Need physical device a11y tree | this 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_summariesfor DFS-pre-order visible-element projection - ❌ No tree fetch (use
smix-runner-clientfor 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/treeroute 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§
- A11y
Node - Accessibility tree node.
- Element
Summary - Element summary — projected view of an
A11yNodeused in AI-readable failure prompts anddriver.describe()output. - Rect
- Logical-points rectangle (origin top-left, +x right, +y down — matches
UIKit / XCUITest coordinate space). All fields
f64because the runner/treeroute emits floating-point points (sub-pixel scale factors). - Screen
Description - Aggregate screen description.
elementsis a DFS-collected ordered list of visible+enabledElementSummaryentries;screenshotis an optional base64 PNG;frontApp/summary/captured_atare 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
limitvisible+enabled nodes (DFS pre-order), projecting each viasummarize_node. Default limit = 1000. - derive_
roles_ recursive - Recursively fill
node.rolefromnode.raw_typewhenever it is currentlyNone. Host-set roles (test fixtures, recorder output) are left untouched. - is_
visible_ enough - Visibility check.
- role_
from_ raw_ type - Derive the curated
Rolefrom the raw XCUIElement type name (e.g."button","radioButton","progressIndicator"). - summarize_
node - Project an
A11yNodeto anElementSummary. - visible_
area - Intersection area in logical points².
Type Aliases§
- Bounds
- Bounds alias —
BoundsandRectare used interchangeably. Downstream code may prefer one name or the other.