← Commit history

Update 40 file(s)

John Lauer ·4022071595 ·1mo ago ·parent cef39b9
4 files changed +66−12
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.263\ No newline at end of file+0.9.264\ No newline at end of file
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.263",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.263/adom-bridge-kicad-v0.9.263.zip",-  "sha256": "a02a7bab619328c8118a010a11a734c73f740a7c7139f0718cb9efe75b43b37c",-  "size": 1029604,+  "version": "0.9.264",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.264/adom-bridge-kicad-v0.9.264.zip",+  "sha256": "6f7179349c1454ad103f2de90b8ad4f87492207b03d960b854e9de7007b8f39b",+  "size": 1030598,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-09-01T01:36:06Z",+  "released_at": "2026-09-01T01:48:44Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.263",+  "version": "0.9.264",   "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/pcm.py+59−5
@@ -46,14 +46,66 @@ _MAX_ZIP = 512 * 1024 * 1024 _UA = "adom-kicad-bridge pcm"  +def _shell_documents_dir() -> Path | None:+    """The Windows *shell* Documents folder (FOLDERID_Documents), which is what+    KiCad's PATHS::GetDefault3rdPartyPath() uses via wxStandardPaths. This is+    NOT always %USERPROFILE%/Documents: with OneDrive Known Folder Redirection+    it is %USERPROFILE%/OneDrive/Documents. Caught live 2026-09-01: the bridge+    installed a package under Documents/KiCad/10.0/3rdparty while KiCad was+    scanning OneDrive/Documents/KiCad/10.0/3rdparty, so PCM showed the package+    as Installed and no library ever appeared."""+    try:+        import ctypes+        from ctypes import wintypes+        FOLDERID_Documents = ctypes.c_char_p(  # {FDD39AD0-238F-46AF-ADB4-6C85480369C7}+            b"\xd0\x9a\xd3\xfd\x8f\x23\xaf\x46\xad\xb4\x6c\x85\x48\x03\x69\xc7")+        class GUID(ctypes.Structure):+            _fields_ = [("Data1", wintypes.DWORD), ("Data2", wintypes.WORD),+                        ("Data3", wintypes.WORD), ("Data4", wintypes.BYTE * 8)]+        guid = GUID(0xFDD39AD0, 0x238F, 0x46AF, (wintypes.BYTE * 8)(0xAD, 0xB4, 0x6C, 0x85, 0x48, 0x03, 0x69, 0xC7))+        out = ctypes.c_wchar_p()+        if ctypes.windll.shell32.SHGetKnownFolderPath(ctypes.byref(guid), 0, None, ctypes.byref(out)) == 0:+            p = Path(out.value)+            ctypes.windll.ole32.CoTaskMemFree(out)+            return p+    except Exception:  # pylint: disable=broad-except+        pass+    return None+++def _third_party_dir(kicad_info: dict) -> tuple[Path | None, str]:+    """Resolve KICAD<n>_3RD_PARTY exactly as KiCad does, in priority order:+    1. an explicit override in kicad_common.json environment.vars+    2. <shell Documents>/KiCad/<major.minor>/3rdparty (OneDrive-aware)+    3. the bridge's user_dir guess (last resort, and it was wrong once)+    Returns (path, how)."""+    ver = str(kicad_info.get("version") or "")+    major = ver.split(".")[0] if ver else ""+    config_dir = kicad_info.get("config_dir")+    if config_dir and major:+        try:+            common = json.loads((Path(config_dir) / "kicad_common.json").read_text(encoding="utf-8"))+            v = ((common.get("environment") or {}).get("vars") or {}).get(f"KICAD{major}_3RD_PARTY")+            if v:+                return Path(v), "kicad_common.json override"+        except (OSError, ValueError):+            pass+    docs = _shell_documents_dir()+    if docs and ver:+        return docs / "KiCad" / ver / "3rdparty", "shell Documents (OneDrive-aware)"+    if kicad_info.get("user_dir"):+        return Path(kicad_info["user_dir"]) / "3rdparty", "user_dir fallback"+    return None, "unresolved"++ def _paths(kicad_info: dict) -> tuple[Path | None, Path | None, str | None]:-    """(3rdparty dir, installed_packages.json, KiCad major version) or Nones."""-    user_dir = kicad_info.get("user_dir")+    """(3rdparty dir, installed_packages.json, KiCad version) or Nones."""     config_dir = kicad_info.get("config_dir")     ver = str(kicad_info.get("version") or "")-    if not user_dir or not config_dir:+    third, _how = _third_party_dir(kicad_info)+    if not third or not config_dir:         return None, None, None-    return Path(user_dir) / "3rdparty", Path(config_dir) / "installed_packages.json", ver+    return third, Path(config_dir) / "installed_packages.json", ver   def _fetch(url: str, timeout: int = 120) -> bytes:@@ -207,6 +259,7 @@ def handle_pcm_install(kicad_info: dict, args: dict) -> dict:     nick = lambda names: [f"{prefix}{n}" for n in names]     return {"success": True, "identifier": ident, "version": vsel["version"],             "filesInstalled": ext["files"], "installedTo": str(third / "*" / ext["packageDir"]),+            "thirdPartyDir": str(third), "thirdPartyResolvedBy": _third_party_dir(kicad_info)[1],             "symbolLibraries": nick(ext["libraries"]["symbols"]),             "footprintLibraries": nick(ext["libraries"]["footprints"]),             "modelSets": ext["libraries"]["3dmodels"],@@ -231,7 +284,8 @@ def handle_pcm_list(kicad_info: dict, args: dict) -> dict:                     "repository": e.get("repository_name"),                     "onDisk": any((third / f / _pkgdir(p.get("identifier", ""))).exists() for f in _FOLDERS)})     return {"success": True, "count": len(out), "packages": out,-            "thirdPartyDir": str(third), "kicadVersion": ver}+            "thirdPartyDir": str(third), "thirdPartyResolvedBy": _third_party_dir(kicad_info)[1],+            "kicadVersion": ver}   def handle_pcm_uninstall(kicad_info: dict, args: dict) -> dict: