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
Update 4 file(s)
4 files changed
+52−13
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.209+0.9.210
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.209",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.209/adom-bridge-kicad-v0.9.209.zip",- "sha256": "4386290016fec8694f8d2f384c1698ce0e4fae92c1249a499b6349599ce82aed",- "size": 477661,+ "version": "0.9.210",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.210/adom-bridge-kicad-v0.9.210.zip",+ "sha256": "c06dc627507b8fe6d94afa33475f36af0602e319682ebf2e79cf5e1e3501228a",+ "size": 478408, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-20T14:05:00Z",+ "released_at": "2026-08-20T14:25:00Z", "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.209",+ "version": "0.9.210", "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/kicad_ui.py+45−6@@ -101,6 +101,21 @@ _SHOT_MAX_FILES = 300 # and a hard ceiling for a heavy test day # stepShots are simply bigger, so the same count now costs far more disk. What a # user actually cares about is megabytes, so cap those too. _SHOT_MAX_BYTES = 40 * 1024 * 1024++# 0.9.210 (John, 2026-08-20: "step files could be up to 200mb"). Two hazards the+# size cap introduced, both worth naming because a janitor that deletes the+# wrong file is far worse than a directory that grows:+#+# 1. It deleted ANY file it found. Nothing but this bridge's own captures should+# ever be removed by it. STEP/WRL/GLB exports live under+# Documents/adom-part-exports today, so they are not in the blast radius -+# but "not today" is not a safety property. Only files this bridge NAMED are+# eligible now, matched on prefix AND extension.+# 2. A single file larger than the whole cap made the trim loop delete every+# other file and still finish over budget - maximum destruction, zero+# benefit. An oversized file is now left alone and REPORTED instead.+_SHOT_PREFIX = "kicad"+_SHOT_EXTS = (".png", ".bmp", ".webp") _last_prune = 0.0 @@ -118,12 +133,15 @@ def prune_screenshots(force: bool = False) -> dict: _last_prune = now removed = 0 freed = 0+ report_foreign: dict = {}+ report_oversized: list = [] for d in (SCREENSHOT_DIR,) + _LEGACY_SCREENSHOT_DIRS: try: if not os.path.isdir(d): continue legacy = d != SCREENSHOT_DIR entries = []+ skipped_foreign = 0 for name in os.listdir(d): fp = os.path.join(d, name) try:@@ -132,7 +150,14 @@ def prune_screenshots(force: bool = False) -> dict: continue if not os.path.isfile(fp): continue+ # OURS ONLY. A file we did not write is never ours to delete.+ if not (name.lower().startswith(_SHOT_PREFIX)+ and name.lower().endswith(_SHOT_EXTS)):+ skipped_foreign += 1+ continue entries.append((st.st_mtime, st.st_size, fp))+ if skipped_foreign:+ report_foreign[d] = skipped_foreign entries.sort() # A retired directory is drained entirely; the live one keeps a window. doomed = list(entries) if legacy else [@@ -144,11 +169,18 @@ def prune_screenshots(force: bool = False) -> dict: keep = keep[len(keep) - _SHOT_MAX_FILES:] # Then trim by SIZE, oldest first, so one heavy run cannot leave # tens of megabytes behind just because it stayed under the count.- total = sum(sz for _mt, sz, _fp in keep)+ # Leave any single file that is bigger than the entire cap: it+ # can never be trimmed INTO budget, and chasing it would delete+ # everything else for nothing.+ oversized = [e for e in keep if e[1] > _SHOT_MAX_BYTES]+ trimmable = [e for e in keep if e[1] <= _SHOT_MAX_BYTES]+ if oversized:+ report_oversized.extend(os.path.basename(e[2]) for e in oversized)+ total = sum(sz for _mt, sz, _fp in trimmable) i = 0- while total > _SHOT_MAX_BYTES and i < len(keep):- doomed.append(keep[i])- total -= keep[i][1]+ while total > _SHOT_MAX_BYTES and i < len(trimmable):+ doomed.append(trimmable[i])+ total -= trimmable[i][1] i += 1 for _mt, size, fp in doomed: try:@@ -164,8 +196,15 @@ def prune_screenshots(force: bool = False) -> dict: pass except Exception: pass- return {"removed": removed, "freedMB": round(freed / 1048576.0, 1),- "dir": SCREENSHOT_DIR}+ out = {"removed": removed, "freedMB": round(freed / 1048576.0, 1),+ "dir": SCREENSHOT_DIR}+ if report_foreign:+ out["leftAlone"] = report_foreign+ out["_hint"] = ("Files this bridge did not write were found here and NOT touched. "+ "The janitor only removes its own captures (kicad*.png/.bmp/.webp).")+ if report_oversized:+ out["oversizedKept"] = report_oversized+ return out # KiCad window title keywords (wxWidgets windows use class wxWindowNR) KICAD_TITLE_KEYWORDS = [