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
v0.9.185: background the project manager too (a spawned window that appears after the verb returned is still ours, for 60s) + wiki #35 warm-editor reuse: if the editor already shows the part, return in about a second instead of a ~56s cold path
6 files changed
+81−9
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.184+0.9.185
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.184",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.184/adom-bridge-kicad-v0.9.184.zip",- "sha256": "7778eb8b5ce1f04aa2bff108d478b2d41f7fd1fff6f1dfb28b8685639df987d5",- "size": 453470,+ "version": "0.9.185",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.185/adom-bridge-kicad-v0.9.185.zip",+ "sha256": "289572d55a9f54487fff3cecba20ad9920f3d72f0b32ba77a15f93b1022a2a54",+ "size": 454450, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-19T18:39:05Z",+ "released_at": "2026-08-19T19:00:04Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.185.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.184",+ "version": "0.9.185", "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/show.py+47−2@@ -42,10 +42,45 @@ def handle_show_project(kicad_info: dict, args: dict) -> dict: return r +def _open_editor_showing(kind: str, name: str, lib: str):+ """Is an editor of `kind` ALREADY open on exactly this part?++ Wiki issue #35 (Colby Knox): kicad_show_symbol cost ~56s on EVERY call, even+ when the Symbol Editor was already open on that exact symbol, because the+ verb always ran the cold path - and KiCad re-indexes every symbol library+ when that editor opens, which is where the time goes. The desired end state+ was already on screen; we just were not looking."""+ try:+ from handlers.kicad_ui import _find_all_kicad_windows+ want_editor = kind.lower()+ for w in _find_all_kicad_windows() or []:+ title = (w.get("title") or "")+ low = title.lower()+ if want_editor not in low:+ continue+ if name and name.lower() in low and (not lib or lib.lower() in low):+ return w+ except Exception:+ pass+ return None++ def handle_show_symbol(kicad_info: dict, args: dict) -> dict: """Show a schematic SYMBOL in the Symbol Editor. Args: symbolName (+ libraryName), or libraryName alone to browse a library."""- r = handle_open_symbol_editor(kicad_info, args or {})+ a = args or {}+ already = _open_editor_showing("symbol editor", a.get("symbolName") or "",+ a.get("libraryName") or a.get("library") or "")+ if already:+ return {"success": True, "surface": "symbol", "reused": True,+ "window": already.get("title"),+ "output": f"Symbol Editor is already showing {already.get('title')} - "+ f"reused it (no relaunch, no library rescan).",+ "_hint": "Returned the existing editor instead of running the cold path. "+ "KiCad re-indexes every symbol library when the Symbol Editor "+ "opens, so reuse is the difference between about a second and "+ "about a minute (wiki issue #35)."}+ r = handle_open_symbol_editor(kicad_info, a) r = _verify_window(r, _find_symbol_editor, "Symbol Editor") r.setdefault("surface", "symbol") return r@@ -143,8 +178,18 @@ def ensure_plugin_ready(kicad_info: dict, waits: int = 14) -> bool: def handle_show_footprint(kicad_info: dict, args: dict) -> dict: """Show a FOOTPRINT in the Footprint Editor. Args: footprintName (+ library), or library alone to browse."""+ a2 = args or {}+ already_fp = _open_editor_showing("footprint editor", a2.get("footprintName") or "",+ a2.get("library") or a2.get("libraryName") or "")+ if already_fp:+ return {"success": True, "surface": "footprint", "reused": True,+ "window": already_fp.get("title"),+ "output": f"Footprint Editor is already showing {already_fp.get('title')} - "+ f"reused it (no relaunch, no library rescan).",+ "_hint": "Returned the existing editor instead of running the cold path "+ "(wiki issue #35)."} ensure_plugin_ready(kicad_info)- r = handle_open_footprint_editor(kicad_info, args or {})+ r = handle_open_footprint_editor(kicad_info, a2) r = _verify_window(r, _find_footprint_editor, "Footprint Editor") r.setdefault("surface", "footprint") return r
handlers/win_focus.py+27@@ -579,12 +579,33 @@ def load_owned_ledger() -> None: pass +_spawn_watch_until = 0.0 # we are responsible for windows appearing before this+++def spawn_watch_active() -> bool:+ try:+ import time as _t+ return _t.monotonic() < _spawn_watch_until+ except Exception:+ return False++ def register_owned_pid(pid) -> None: """Spawn sites call this for every kicad-family process the bridge starts."""+ global _spawn_watch_until try: if pid: _owned_pids.add(int(pid)) _save_ledger()+ # 0.9.185 (John, live: "you opened the kicad main menu and it came to+ # the foreground - you did nothing to even attempt once to put it in+ # the background"). A spawned app can take many seconds to show its+ # first window, by which time the verb has returned and the idle rule+ # correctly says "hands off". That rule is for windows the USER+ # opened; for 60s after WE spawn something, a window that appears in+ # front is ours to put behind - exactly once, as always.+ import time as _t+ _spawn_watch_until = _t.monotonic() + 60.0 except Exception: pass @@ -1317,6 +1338,12 @@ def start_focus_sentinel() -> None: _log("user-owned window (left alone)", fg, ago) elif _guard_count > 0: _log("steal-during-guard (guardian handles)", fg, ago)+ elif (_verb_inflight <= 0 and _guard_count <= 0+ and spawn_watch_active() and _is_owned_window(fg)):+ # We spawned this within the last minute: it is ours+ # to background, once, even though the verb returned.+ _log("our freshly spawned window took the foreground", fg, ago)+ _push_kicad_to_background(fg) elif _verb_inflight <= 0 and _guard_count <= 0: # 0.9.172 - THE IDLE RULE (John, 2026-08-19: "when I # click the taskbar icon to bring the app to the fg,