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.125: PM menu command ids resolved by menu walk, not hardcoded (10.0.5 renumbered 20011/20013 - beats 1-3 fired WM_COMMAND into nothing)
6 files changed
+37−14
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.124+0.9.125
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.124",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.124/adom-bridge-kicad-v0.9.124.zip",- "sha256": "dfd7cdda6701c8e4af4419a887f614e1f19ae431f941950f0d6226774415c538",- "size": 412323,+ "version": "0.9.125",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.125/adom-bridge-kicad-v0.9.125.zip",+ "sha256": "0a449a7ea1bea4fc5d6e443e9e74c64e8bb73c0040a405ce035ca7320f040d87",+ "size": 412630, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-16T17:59:58Z",+ "released_at": "2026-08-16T18:05:23Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.125.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.124",+ "version": "0.9.125", "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+14−1@@ -517,11 +517,24 @@ def handle_open_footprint_editor(kicad_info: dict, args: dict) -> dict: # the flaky icon-click fallback documented at the top of this file # (KiCad 9 launcher icons use dynamic WM_COMMAND IDs we can't rely on). KICAD_FOOTPRINT_EDITOR_CMD_ID = 20013 # noqa: N806+ # 10.0.5 renumbered PM menu ids (the symbol editor's 20011 died the same+ # day) — resolve from the real menu bar first, hardcoded id as fallback.+ _pm_fp_id = None+ try:+ from handlers import win_menu+ from handlers.kicad_ui import _find_all_kicad_windows+ _pm = next((w for w in _find_all_kicad_windows()+ if (w.get("title") or "").startswith("KiCad")+ or "KiCad" == (w.get("title") or "").strip()[:5]), None)+ if _pm:+ _pm_fp_id = win_menu.find_menu_command(_pm["hwnd"], "footprint editor")+ except Exception:+ _pm_fp_id = None if not fp_hwnd: try: from handlers.bridge_client import try_wm_command_via_bridge tier1 = try_wm_command_via_bridge(- exe_name="kicad", menu_id=KICAD_FOOTPRINT_EDITOR_CMD_ID, frame_index=0,+ exe_name="kicad", menu_id=_pm_fp_id or KICAD_FOOTPRINT_EDITOR_CMD_ID, frame_index=0, ) pathways_tried.append("plugin" if tier1.get("success") else f"plugin-failed:{tier1.get('errorCode','')}") if tier1.get("success"):
handlers/open_symbol_editor.py+16−6@@ -506,11 +506,20 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict: } time.sleep(2.0) - # Open Symbol Editor via WM_COMMAND. A cold KiCad 10.0.5 project manager- # can take 15s+ to act on the command (caught by the demo job racing it,- # 2026-08-16) — wait up to 30s and re-post once at the 10s mark in case- # the first message landed before the menu was wired.- user32.PostMessageW(kicad_hwnd, WM_COMMAND, KICAD_SYMBOL_EDITOR_CMD_ID, 0)+ # Open Symbol Editor via WM_COMMAND — resolving the command id from the+ # PM's REAL menu bar (win32 menu walk), never trusting a hardcoded id+ # across KiCad versions: 10.0.5 renumbered the old 20011 and the command+ # fired into nothing (demo beats 1-3 dead, 2026-08-16). Hardcoded id+ # stays as the last-resort fallback only.+ cmd_id = None+ try:+ from handlers import win_menu+ cmd_id = win_menu.find_menu_command(kicad_hwnd, "symbol editor")+ except Exception:+ cmd_id = None+ if cmd_id:+ pathways_tried.append(f"menu-walk-id-{cmd_id}")+ user32.PostMessageW(kicad_hwnd, WM_COMMAND, cmd_id or KICAD_SYMBOL_EDITOR_CMD_ID, 0) pathways_tried.append("wm_command") for i in range(60):@@ -519,7 +528,8 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict: if sym_hwnd: break if i == 20:- user32.PostMessageW(kicad_hwnd, WM_COMMAND, KICAD_SYMBOL_EDITOR_CMD_ID, 0)+ user32.PostMessageW(kicad_hwnd, WM_COMMAND,+ cmd_id or KICAD_SYMBOL_EDITOR_CMD_ID, 0) pathways_tried.append("wm_command-repost") if not sym_hwnd: