← Commit history

v0.9.134: menu ids resolved LIVE via the plugin's get_menu_ids (10.0.5 renumbered eeschema 20390->20391); demo warm launches the PM so the part beats' in-process route has a bound target

John Lauer ·254dfa4199 ·1mo ago ·parent c137a21
8 files changed +63−11
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.133+0.9.134
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.133",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.133/adom-bridge-kicad-v0.9.133.zip",-  "sha256": "b89504a941f9c10bd884af64e9e66819290ed3751cca4f5b8750f156e1b3aadf",-  "size": 414617,+  "version": "0.9.134",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.134/adom-bridge-kicad-v0.9.134.zip",+  "sha256": "2e2586a98ab1fc48e47ac01cf507dd0a28de2e76777df1eca02d9e15cb216b23",+  "size": 415193,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-16T20:19:46Z",+  "released_at": "2026-08-16T20:27:28Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.134.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.133",+  "version": "0.9.134",   "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+26
@@ -444,6 +444,32 @@ def handle_open_editors(kicad_info: dict, args: dict) -> dict:  # ── Phase 3 — three-tier fallback for menu-driven actions ────────────── +def find_menu_id_via_plugin(exe_name: str, *label_contains: str) -> int | None:+    """Resolve a menu command id from the LIVE process's own menu bar via the+    plugin's get_menu_ids RPC — never trust hardcoded ids across KiCad versions+    (10.0.5 renumbered eeschema's Symbol Editor 20390 -> 20391, found live+    2026-08-16). Returns the id or None when no plugin/match."""+    try:+        meta, err = _resolve_plugin(exe_name)+        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 []+        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+    except Exception:+        pass+    return None++ def try_wm_command_via_bridge(exe_name: str, menu_id: int, frame_index: int = 0,                                pid: int | None = None, timeout: float = 10.0) -> dict:     """Tier 1 of Phase 3's 3-tier fallback chain.
handlers/demo.py+9
@@ -299,6 +299,7 @@ def _prepare(kicad_info: dict) -> dict:     (d / "adom-demo.kicad_pcb").write_text(_board(kicad_info), encoding="utf-8")     (d / "adom-demo.kicad_pro").write_text('{"board":{},"meta":{"filename":"adom-demo.kicad_pro","version":1},"schematic":{},"sheets":[]}', encoding="utf-8")     out["projectDir"] = str(d)+    out["project"] = str(d / "adom-demo.kicad_pro")     out["schematic"] = str(d / "adom-demo.kicad_sch")     out["board"] = str(d / "adom-demo.kicad_pcb")     return out@@ -331,6 +332,14 @@ def _warm_kicad(kicad_info: dict, prep: dict) -> dict:     # "Could not open the Footprint Editor via the background (plugin) path".     # So open BOTH documents up front; every later beat then takes the fast     # in-process plugin pathway.+    # Launch the PROJECT MANAGER first: the part beats' most reliable route is+    # the PM's in-process plugin post, so kicad.exe must exist and its plugin+    # must have bind time — the design beats provide that time for free.+    try:+        from handlers.launch import handle_launch+        handle_launch(kicad_info, {"project": prep.get("project") or "", "waitSeconds": 15})+    except Exception:+        pass     sch = handle_open_schematic(kicad_info, {"filePath": prep["schematic"]})     time.sleep(6)     brd = handle_open_board(kicad_info, {"filePath": prep["board"]})
handlers/open_footprint_editor.py+12
@@ -484,6 +484,13 @@ def handle_open_footprint_editor(kicad_info: dict, args: dict) -> dict:     # running, and whose first-run "KiCad Setup" wizard otherwise blocks the icon     # click). Mirrors how open_symbol_editor drives a live eeschema. (ADOMBASELINE 2026-07.)     PCBNEW_FOOTPRINT_EDITOR_MENU_ID = 20572  # noqa: N806+    try:+        from handlers.bridge_client import find_menu_id_via_plugin+        _live = find_menu_id_via_plugin("pcbnew", "footprint editor")+        if _live:+            PCBNEW_FOOTPRINT_EDITOR_MENU_ID = _live  # noqa: N806+    except Exception:+        pass     if not fp_hwnd:         try:             from handlers.bridge_client import try_wm_command_via_bridge@@ -534,6 +541,11 @@ def handle_open_footprint_editor(kicad_info: dict, args: dict) -> dict:     if not fp_hwnd:         try:             from handlers.bridge_client import try_wm_command_via_bridge+            try:+                from handlers.bridge_client import find_menu_id_via_plugin+                _pm_fp_id = find_menu_id_via_plugin("kicad", "footprint editor") or _pm_fp_id+            except Exception:+                pass             tier1 = try_wm_command_via_bridge(                 exe_name="kicad", menu_id=_pm_fp_id or KICAD_FOOTPRINT_EDITOR_CMD_ID, frame_index=0,             )
handlers/open_symbol_editor.py+9−4
@@ -434,9 +434,12 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:                             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"+            # 1a: eeschema's own menu, if eeschema is alive — id resolved from+            # the LIVE menu bar (10.0.5 renumbered 20390 -> 20391).+            from handlers.bridge_client import find_menu_id_via_plugin+            _ee_id = find_menu_id_via_plugin("eeschema", "symbol editor") or 20390+            sym_hwnd, tier1 = _post_and_wait("eeschema", _ee_id, f"eeschema-{_ee_id}", 12)+            tier1_source = f"eeschema-{_ee_id}"             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@@ -449,9 +452,11 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:                 # (measured live on 10.0.5, 2026-08-16; 15s of retries all hit                 # plugin_not_running while a minutes-old PM answered                 # instantly). Retry for up to ~45s.+                _pm_id = None                 for _attempt in range(15):+                    _pm_id = _pm_id or find_menu_id_via_plugin("kicad", "symbol editor")                     sym_hwnd, tier1 = _post_and_wait(-                        "kicad", KICAD_SYMBOL_EDITOR_CMD_ID, "kicad-20011", 24)+                        "kicad", _pm_id or KICAD_SYMBOL_EDITOR_CMD_ID, "kicad-pm", 24)                     if sym_hwnd:                         tier1_source = "kicad-20011"                         break