app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
v0.9.95 (#26): FP path gets plugin-free UIA 3D-viewer tier; 3D window poll 3s->9s for slow renders
5 files changed
+41−9
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.94+0.9.95
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.94",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.94/adom-bridge-kicad-v0.9.94.zip",- "sha256": "297d99612fab0cdfac9f2fdd1b645fa4b4a2ac77844ce1671667526b2606cbd9",- "size": 387818,+ "version": "0.9.95",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.95/adom-bridge-kicad-v0.9.95.zip",+ "sha256": "526b6024d87c7748d12bc67ecae5d3f740dd45a5b47890dfdfdc803a706bdbc2",+ "size": 388116, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-14T18:56:33Z",+ "released_at": "2026-08-14T19:19:18Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.95.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.94",+ "version": "0.9.95", "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.", "homepage": "https://wiki.adom.inc/adom/adom-bridge", "author": "Adom Inc.",
handlers/open_footprint_editor.py+34−2@@ -776,6 +776,17 @@ def _find_pcb_editor() -> int | None: return None +def _poll_3d_window(rounds: int = 12):+ """Wait for a real 3D Viewer WINDOW to appear (render can lag the invoke,+ esp. on software-OpenGL). Process-verified via _find_windows_by_title."""+ for _ in range(max(1, rounds)):+ time.sleep(0.75)+ w = _find_windows_by_title("3D Viewer")+ if w:+ return w+ return []++ def handle_open_3d_viewer(kicad_info: dict, args: dict) -> dict: """Open the 3D Viewer from the Footprint Editor or the PCB Editor. @@ -873,6 +884,28 @@ def handle_open_3d_viewer(kicad_info: dict, args: dict) -> dict: except Exception as e: # pylint: disable=broad-except fp_pathways.append(f"plugin-error:{type(e).__name__}") + # Tier 1.5 (v0.9.95, #26): UIA background Invoke of the "3D Viewer" control on+ # the Footprint Editor — programmatic, no foreground/cursor, and crucially+ # PLUGIN-FREE (the FP path previously dead-ended when pcbnew's plugin hadn't+ # bound, the #21/#26 late-bind). Same primitive the open_footprint tier uses.+ try:+ from handlers import uia+ r = uia.uia_invoke(fp_hwnd, contains="3D Viewer")+ if r.get("ok"):+ fp_pathways.append("uia")+ windows = _poll_3d_window(12)+ if windows:+ return {+ "success": True,+ "output": f"3D Viewer opened from Footprint Editor via UIA: {windows[0][1]}",+ "pathway": "uia",+ "pathwaysTried": fp_pathways,+ }+ else:+ fp_pathways.append(f"uia-{r.get('reason','miss')}")+ except Exception as e: # pylint: disable=broad-except+ fp_pathways.append(f"uia-error:{type(e).__name__}")+ # Opt-in disruptive fallback: Alt+3 keystroke (needs the FP window foregrounded, # so it steals focus) — only when the caller explicitly allows it. if args.get("allowFocusSteal"):@@ -955,8 +988,7 @@ def handle_open_3d_viewer(kicad_info: dict, args: dict) -> dict: r = uia.uia_invoke(pcb_hwnd, contains="3D Viewer") if r.get("ok"): pathways_tried.append("uia")- time.sleep(3.0)- windows = _find_windows_by_title("3D Viewer")+ windows = _poll_3d_window(12) if windows: return { "success": True,