app
Altium - the Altium Bridge
Public Made by Adomby adom
Adom Bridge for Altium Designer: a resident DelphiScript agent gives the AI eval, the 1,100-command process launcher, all 8 EDA surfaces with screenshots, compile/ERC, DRC, OutJob outputs, native SchLib/PcbLib authoring with STEP 3D bodies, and dialog handling by PID.
main
John Lauer
0.2.21 source: script-error auto-recovery + errorShot, symbol_prims/set_models fix for AD 26.9.1, ENGINE_HALTING releas…
a577bb1
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
"""UI Automation helpers — the QUIET way to drive Altium.
Measured 2026-09-01 on AD26.9.1 (test machine), after John asked "you keep turning on the cursor,
but you could maybe use UIA to do those clicks":
Altium exposes a real UIA tree, and far more of it than this bridge was using:
* **Dialogs** (Error / Warning / Confirm / "Connect to Workspace") are plain VCL forms — their
buttons are reachable by accessible NAME (`OK`, `Choose a license`, `Continue`).
* **The main menus and title bar** are MenuItems: `File`, `View`, `Project`, `Window`, `Help`,
`Preferences...`, `Share`, `Buy Now`, `Open Home page`, and the workspace item (`Adom`).
* **Every open document is a button** in the document bar — automationId
`GUIBinderView.TGUIDocBarButton`, accessible name = the tab caption
("Bluetooth_Sentinel.PcbDoc", "(2) License Management"). Invoking one makes that document
CURRENT — no cursor, no foreground. This is the quiet way to satisfy the board verbs'
"needs a .PcbDoc current" rule.
* **The WebView2 pages** (License Management) expose their DOM through the a11y tree: the
"Use License" button is `automationId=UseLicenseButton`, `invokable=true`. The bridge used to
click it at hardcoded pixel coordinates measured on a maximised window.
Why it matters (John's rule): `desktop_click` is SendInput — it needs the target FOREGROUNDED and
it moves the user's pointer. UIA Invoke is programmatic: no foreground, no focus steal, no cursor
movement. So: **UIA first, image-space click only for what UIA genuinely cannot reach.**
Two costs to know:
* A UIA search over Altium's tree is SLOW (seconds, occasionally >60 s under load). Scope every
call with `hwnd`, keep timeouts generous, and never loop over speculative names.
* Synthetic input (and UIA) is refused outright while a higher-integrity window owns the
foreground — a screensaver counts. The error names UIPI and says retrying will not help.
"""
from __future__ import annotations
import caller_identity
# Known automationIds, so the common paths skip a name search entirely.
IDS = {
"use_license": "UseLicenseButton",
}
DOC_TAB_ID = "GUIBinderView.TGUIDocBarButton"
def click(hwnd: int, name: str | None = None, automation_id: str | None = None,
role: str | None = None, timeout: int = 30) -> dict:
"""Invoke a control programmatically. Returns ab's reply plus `quiet: True` when it worked
(quiet = the user's cursor never moved and their foreground window was not stolen)."""
args: dict = {"hwnd": hwnd}
if automation_id:
args["automationId"] = automation_id
if name:
args["name"] = name
if role:
args["role"] = role
r = caller_identity.ad_callback("desktop_ui_click", args, timeout=timeout) or {}
if r.get("success"):
r["quiet"] = True
return r
def find(hwnd: int, contains: str, role: str | None = None, timeout: int = 60) -> dict:
args: dict = {"hwnd": hwnd, "contains": contains}
if role:
args["role"] = role
return caller_identity.ad_callback("desktop_find_control", args, timeout=timeout) or {}
def click_by_name_then_pixels(hwnd: int, name: str, pixel_click, automation_id: str | None = None,
role: str = "button") -> dict:
"""UIA first (quiet), image-space click only if UIA cannot reach the control.
`pixel_click` is a zero-arg callable performing the legacy click; it is invoked ONLY on a UIA
miss, and the result is marked `windowEtiquette: "foreground"` so callers can tell the user
their screen was touched.
"""
r = click(hwnd, name=name, automation_id=automation_id or IDS.get(name.lower().replace(" ", "_")), role=role)
if r.get("success"):
r["windowEtiquette"] = "background"
r["lane"] = "uia"
return r
out = pixel_click() or {}
if isinstance(out, dict):
out["lane"] = "image_click"
out["windowEtiquette"] = "foreground"
out["_uiaError"] = r.get("error") or r.get("_hint")
return out
def activate_document(hwnd: int, caption: str) -> dict:
"""Make an OPEN document current by invoking its tab in the document bar — no cursor, no
foreground. `caption` is matched against the tab's accessible name; the exact caption carries
Altium's "(2) " prefixes, so a substring search runs first."""
hit = find(hwnd, caption)
best = hit.get("best") or {}
exact = best.get("name")
r = click(hwnd, name=exact or caption, role="button")
if r.get("success"):
return {"success": True, "activated": exact or caption, "lane": "uia",
"windowEtiquette": "background",
"_hint": "The document bar tab was invoked programmatically — the user's cursor never moved."}
return {"success": False, "error": r.get("error") or "no document tab matched",
"candidate": exact, "_hint": f"altium_window_info lists open documents; the tab caption may carry a '(n) ' prefix."}