← Commit history

0.9.343: Footprint loads with two pcbnew processes; list_footprints reads the fp-lib-table (#81)

John Lauer ·f7430ff68a ·1mo ago ·parent 54e5da4
5 files changed +63−19
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.342\ No newline at end of file+0.9.343\ No newline at end of file
SKILL.md+1
@@ -383,3 +383,4 @@ Rules live on the wiki page adom/pcb-design-rules and are fetched at call time, - **Adom boards with a schematic and a PCB**: `adom/drone-motor-carrier`, `adom/esc-g431`, `adom/carrier-fc-rp2350a-mini`, `adom/rp2350a-ads1220` (each carries `<slug>.kicad_sch`, `<slug>.kicad_pcb`, `.step`, `.glb`). Pull the `.kicad_sch` + `.kicad_pcb`, push them to the desktop, then `kicad_open_board` / `kicad_show_2d_board` / `kicad_show_3d_board`, `kicad_lint_board`, `kicad_set_design_rules {"fab":"adom-instapcb"}`, `kicad_export_gerbers`. - **The Adom PCM libraries** (symbols, footprints and 3D for every part on the Adom line): `kicad_pcm_install {"repositoryUrl":"https://wiki.adom.inc/api/v1/pages/kicad-pcm/files/repository.json","identifier":"inc.adom.kicad.basic-parts"}` (also `inc.adom.kicad.rp2040`). KiCad registers the libraries on its next start. - Never quote a wiki page you have not fetched: `aravk/in-s42ata`, an older example, no longer exists.+
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.342",+  "version": "0.9.343",   "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/install_footprint.py+37−13
@@ -306,25 +306,49 @@ def handle_list_footprints(kicad_info: dict, args: dict) -> dict:         return {"success": False, "error": "KiCad user directory not found",                 "_hint": "Launch KiCad once so it creates Documents/KiCad/<ver>/."} -    roots = [_P(user_dir) / "footprints"]     libraries, total = {}, 0-    for root in roots:-        if not root.is_dir():-            continue++    def _add(name: str, pretty) -> None:+        nonlocal total+        if name in libraries or not pretty.is_dir():+            return+        if want and name.lower() != want.lower():+            return+        names = sorted(p.stem for p in pretty.glob("*.kicad_mod"))+        libraries[name] = {"path": str(pretty).replace("\\", "/"),+                           "count": len(names), "footprints": names}+        total += len(names)++    # 1. Every library KiCad itself knows: the user's fp-lib-table, URIs expanded+    #    the way KiCad expands them (stock ${KICAD<n>_FOOTPRINT_DIR} libraries+    #    included). Issue #81: "No library named 'Resistor_SMD' on this machine"+    #    was this verb only looking at Documents/KiCad/<ver>/footprints.+    try:+        from lib_table import LibTable+        from adom_library import _expand_kicad_uri+        config_dir = kicad_info.get("config_dir")+        if not config_dir:+            import kicad_detect+            config_dir = kicad_detect._win_find_config_dir(kicad_info.get("version") or "")+        table = _P(config_dir) / "fp-lib-table" if config_dir else None+        if table and table.is_file():+            for e in LibTable.parse_file(str(table)).entries:+                resolved = _expand_kicad_uri(e.uri, kicad_info)+                if resolved:+                    _add(e.name, _P(resolved))+    except Exception:  # pylint: disable=broad-except+        pass+    # 2. Unregistered .pretty folders in the user dir (installed but not yet in the table).+    root = _P(user_dir) / "footprints"+    if root.is_dir():         for pretty in sorted(root.glob("*.pretty")):-            name = pretty.stem-            if want and name.lower() != want.lower():-                continue-            names = sorted(p.stem for p in pretty.glob("*.kicad_mod"))-            libraries[name] = {"path": str(pretty).replace("\\", "/"),-                               "count": len(names), "footprints": names}-            total += len(names)+            _add(pretty.stem, pretty)      out = {"success": True, "libraryCount": len(libraries), "footprintCount": total,            "libraries": libraries}     if want and not libraries:-        out["_hint"] = (f"No library named '{want}' on this machine. Libraries present: "-                        f"{', '.join(sorted(libraries)) or 'none'}. Install one with "+        out["_hint"] = (f"No library named '{want}' in this KiCad's fp-lib-table or user footprints dir. "+                        f"Install one with "                         f"kicad_install_footprint, which creates Adom.pretty on first use.")     else:         out["_hint"] = ("The footprint NAME is the .kicad_mod file stem, which is what "
handlers/open_footprint_editor.py+23−4
@@ -606,7 +606,7 @@ def _ensure_tree_search(fp_hwnd: int):         return edit     import time as _t     now = _t.monotonic()-    if now - _TREE_TOGGLED.get(int(fp_hwnd), 0) > 60 and _show_library_tree_pane():+    if now - _TREE_TOGGLED.get(int(fp_hwnd), 0) > 60 and _show_library_tree_pane(fp_hwnd):         _TREE_TOGGLED[int(fp_hwnd)] = now     for _ in range(16):         edit = _find_child_by_class_and_parent(fp_hwnd, "Edit", "searchCtrl")@@ -616,12 +616,31 @@ def _ensure_tree_search(fp_hwnd: int):     return edit if _visible(edit) else None  -def _show_library_tree_pane() -> bool:+def _window_pid(hwnd: int) -> int | None:+    try:+        pid = ctypes.wintypes.DWORD(0)+        user32.GetWindowThreadProcessId(int(hwnd), ctypes.byref(pid))+        return int(pid.value) or None+    except Exception:  # pylint: disable=broad-except+        return None+++def _show_library_tree_pane(fp_hwnd: int = 0) -> bool:     """Tick View > Panels > Library Tree on the Footprint Editor frame via the-    pcbnew plugin (menu id resolved live; the FP editor is not frame 0)."""+    pcbnew plugin (menu id resolved live; the FP editor is not frame 0).++    Ask the pcbnew that OWNS the Footprint Editor window (#81, winvm 2026-09-07):+    with two pcbnew processes alive the resolver's default "newest instance" was+    a stray PCB Editor with no Footprint Editor frame, the toggle went nowhere,+    the tree stayed hidden and every background load typed into nothing."""     try:         from handlers.bridge_client import _resolve_plugin, call as _rpc-        meta, err = _resolve_plugin("pcbnew")+        meta, err = None, None+        owner = _window_pid(fp_hwnd) if fp_hwnd else None+        if owner:+            meta, err = _resolve_plugin("pcbnew", owner)+        if err or not meta:+            meta, err = _resolve_plugin("pcbnew")         if err or not meta:             return False         rpc = _rpc(meta["port"], "get_menu_ids", {}, timeout=8.0)