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.128: tier 1 falls through on NO-WINDOW, not just on post-failure - a stale eeschema menu id posts 'successfully' while opening nothing, and the working PM in-process route (verified live: 20011 opens the editor on project-loaded 10.0.5) was never tried
5 files changed
+49−50
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.127+0.9.128
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.127",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.127/adom-bridge-kicad-v0.9.127.zip",- "sha256": "3613508335e8493502a98b023bf9406dbe2e2fdb4e1ef4b759c608cc66b49987",- "size": 412891,+ "version": "0.9.128",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.128/adom-bridge-kicad-v0.9.128.zip",+ "sha256": "b9b67d49d99e4610de41af67a85a6b3c376359735e957e12790a6b0656251293",+ "size": 413017, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-16T18:13:00Z",+ "released_at": "2026-08-16T18:24:36Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.128.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.127",+ "version": "0.9.128", "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_symbol_editor.py+42−43@@ -418,53 +418,52 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict: try: from handlers.bridge_client import try_wm_command_via_bridge - # 1a: prefer eeschema if alive- tier1 = try_wm_command_via_bridge(- exe_name="eeschema", menu_id=20390, frame_index=0,- )+ def _post_and_wait(exe, mid, label, wait_iters):+ """Post the menu command in-process, then wait for the editor.+ A post can 'succeed' while opening NOTHING (a stale menu id+ lands harmlessly) — only the WINDOW appearing counts."""+ t = try_wm_command_via_bridge(exe_name=exe, menu_id=mid, frame_index=0)+ pathways_tried.append(+ f"plugin-{label}" if t.get("success")+ else f"plugin-{label}-failed:{t.get('errorCode', '')}")+ if t.get("success"):+ for _ in range(wait_iters):+ time.sleep(0.5)+ h = _find_symbol_editor()+ if h:+ return h, t+ return None, t++ # 1a: eeschema's own menu, if eeschema is alive.+ sym_hwnd, tier1 = _post_and_wait("eeschema", 20390, "eeschema-20390", 12) tier1_source = "eeschema-20390"- if not tier1.get("success"):- # 1b: the kicad project manager. On 10.0.5 the EXTERNAL- # PostMessage tier below is dead (cross-process WM_COMMAND no- # longer lands, and the PM menu is not a native Win32 menu), so- # this in-process plugin post is the ONLY route from a running- # PM — and the plugin takes a few seconds to bind after PM- # spawn. Wait for it (up to ~15s) instead of failing once- # (demo job beats died racing a fresh PM, 2026-08-16).- tier1_pm = {}+ if not sym_hwnd:+ # 1b: the kicad project manager, IN-PROCESS. On 10.0.5 this is+ # the ONLY reliable route from a running PM: the external+ # PostMessage tier below is ignored by a project-loaded PM, and+ # 1a's eeschema id can post 'successfully' while opening+ # nothing — 0.9.127 and earlier treated that as terminal and+ # never tried 1b (the demo's beats 1-3 died exactly there).+ # The plugin can still be binding on a fresh PM: retry ~15s. for _attempt in range(5):- tier1_pm = try_wm_command_via_bridge(- exe_name="kicad", menu_id=KICAD_SYMBOL_EDITOR_CMD_ID, frame_index=0,- )- if tier1_pm.get("success"):- break- time.sleep(3)- if tier1_pm.get("success"):- tier1 = tier1_pm- tier1_source = "kicad-20011"- pathways_tried.append(- f"plugin-{tier1_source}" if tier1.get("success")- else f"plugin-failed:{tier1.get('errorCode','')}"- )- if tier1.get("success"):- for _ in range(12):- time.sleep(0.5)- sym_hwnd = _find_symbol_editor()+ sym_hwnd, tier1 = _post_and_wait(+ "kicad", KICAD_SYMBOL_EDITOR_CMD_ID, "kicad-20011", 24) if sym_hwnd:+ tier1_source = "kicad-20011" break- if sym_hwnd:- _force_foreground(sym_hwnd)- if not symbol_name and not library_name:- return {- "success": True,- "output": f"Symbol Editor opened successfully via bridge ({tier1_source}).",- "pathway": "plugin",- "pathwaysTried": pathways_tried,- "bridgePid": tier1.get("pid"),- }- # If symbol_name/library_name was requested, fall- # through to the navigate step below (uses legacy- # PowerShell SendKeys path).+ time.sleep(3)+ if sym_hwnd:+ _force_foreground(sym_hwnd)+ if not symbol_name and not library_name:+ return {+ "success": True,+ "output": f"Symbol Editor opened successfully via bridge ({tier1_source}).",+ "pathway": "plugin",+ "pathwaysTried": pathways_tried,+ "bridgePid": tier1.get("pid"),+ }+ # If symbol_name/library_name was requested, fall+ # through to the navigate step below. except Exception as e: # pylint: disable=broad-except # Bridge import failed (unlikely) — record and fall through. pathways_tried.append(f"plugin-import-error:{type(e).__name__}")