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.171: the live menu-id resolver only understood frames[].menus[], but get_menu_ids returns a FLAT list on 10.0.5 pcbnew - so it found nothing, fell back to the stale hardcoded 20572 and posted into the void. It now walks either shape (pcbnew Footprint Editor is 20574 on 10.0.5)
5 files changed
+35−17
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.170+0.9.171
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.170",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.170/adom-bridge-kicad-v0.9.170.zip",- "sha256": "861804aba9310463e39858066b5f858639a06061c86c17f613ad30d0b23470d2",- "size": 445363,+ "version": "0.9.171",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.171/adom-bridge-kicad-v0.9.171.zip",+ "sha256": "78d4e8ba9e0cb44bd186188733229b25ba883eb67a584ea55e1b7c944beba9ae",+ "size": 445651, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-18T13:57:24Z",+ "released_at": "2026-08-18T14:40:31Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.171.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.170",+ "version": "0.9.171", "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/bridge_client.py+28−10@@ -454,17 +454,35 @@ def find_menu_id_via_plugin(exe_name: str, *label_contains: str) -> int | None: if err or not meta: return None rpc = call(meta["port"], "get_menu_ids", {}, timeout=8.0)- result = rpc.get("result")- frames = result if isinstance(result, list) else (result or {}).get("frames") or []+ # 0.9.171: the RPC's shape is not stable across KiCad versions - it can+ # be frames[].menus[] OR a FLAT list of {id,label,topMenu}. The old+ # parser only understood the nested form, so on 10.0.5 pcbnew it found+ # nothing, silently fell back to the hardcoded 20572, and that post+ # "succeeded" while opening no window (John's footprint surface, live).+ # Walk whatever comes back and collect every {id,label} pair.+ items: list[dict] = []++ def _collect(node, depth=0):+ if depth > 6 or len(items) > 5000:+ return+ if isinstance(node, dict):+ if "id" in node and "label" in node:+ items.append(node)+ for v in node.values():+ _collect(v, depth + 1)+ elif isinstance(node, list):+ for v in node:+ _collect(v, depth + 1)++ _collect(rpc.get("result")) wants = [w.lower() for w in label_contains]- for frame in frames:- for it in (frame or {}).get("menus") or []:- label = str(it.get("label") or "").lower()- if all(w in label for w in wants):- try:- return int(it.get("id"))- except (TypeError, ValueError):- continue+ for it in items:+ label = str(it.get("label") or "").lower()+ if all(w in label for w in wants):+ try:+ return int(it.get("id"))+ except (TypeError, ValueError):+ continue except Exception: pass return None