app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Drive your own KiCad from an AI, in the background: open every editor, read the whole board as data (the EDA Bridge Standard board model), stack-up, part heights, netlists, place, route, pour, DRC and exports.
master
John Lauer
0.9.274: Wiki #71 root cause: on a OneDrive-redirected Documents the bridge used %USERPROFILE%/Documents while KiCad re…
d9e363d
2mo ago
"""Windows shell folders, resolved the way KiCad resolves them.
WHY (wiki #71, Ray, 2026-09-03; and the PCM bug the day before). KiCad finds the
user's KiCad folder through the SHELL Documents known folder
(wxStandardPaths::GetDocumentsDir -> FOLDERID_Documents). With OneDrive Known
Folder Redirection that is %USERPROFILE%\\OneDrive\\Documents, NOT
%USERPROFILE%\\Documents. This bridge used the naive path, so on a redirected
box it created and wrote a SECOND, empty Adom library that KiCad never reads,
while KiCad's real one sat under OneDrive with every symbol in it. Every
"installed but invisible" report on such a box traces back to this.
"""
from __future__ import annotations
import os
from pathlib import Path
def shell_documents_dir() -> Path | None:
"""FOLDERID_Documents via SHGetKnownFolderPath, or None off-Windows."""
try:
import ctypes
from ctypes import wintypes
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 documents_candidates() -> list[Path]:
"""Shell Documents FIRST (what KiCad uses), naive %USERPROFILE%\\Documents
second (what this bridge used to assume). Both, de-duplicated, in that order,
so a caller can prefer KiCad's answer and still find a legacy tree."""
out: list[Path] = []
s = shell_documents_dir()
if s:
out.append(s)
up = os.environ.get("USERPROFILE")
if up:
n = Path(up) / "Documents"
if not out or n.resolve() != out[0].resolve():
out.append(n)
return out