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
0.9.305: Tour window targeting (John's laptop: fourteen KiCad windows from days of demos; the 2D beat showed an empty untitled PCB Editor and the 3D beat the old sample cube): every beat now targets the tour's own document titles (esc-g431 schematic and board, the Adom STM32 symbol, the LQFP footprint), reuses an existing window instead of opening a duplicate, and opens each 3D view from that exact editor (open_3d_viewer accepts pcbHwnd/fpHwnd). The clean slate at start also closes old adom-demo documents and an empty untitled PCB Editor.
4 files changed
+52−16
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.304+0.9.305
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.304",+ "version": "0.9.305", "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/demo.py+39−13@@ -436,8 +436,8 @@ def _warm_kicad(kicad_info: dict, prep: dict) -> dict: import time try: from .kicad_ui import _find_all_kicad_windows- titles = [(w.get("title") or "") for w in _find_all_kicad_windows()]- if any("Schematic Editor" in t for t in titles) and any("PCB Editor" in t for t in titles):+ tt = _tour_titles(prep)+ if _hwnd_for_title(tt["schematic"]) and _hwnd_for_title(tt["board"]): return {"warmed": True, "eeschema": True, "pcbnew": True, "reused": True} except Exception: pass@@ -463,6 +463,22 @@ def _warm_kicad(kicad_info: dict, prep: dict) -> dict: "raw": {"schematic": sch, "board": brd}} +def _doc_stem(prep: dict) -> str:+ try:+ return Path(prep.get("board") or prep.get("schematic") or "esc-g431").stem+ except Exception: # pylint: disable=broad-except+ return "esc-g431"+++def _tour_titles(prep: dict) -> dict:+ """The exact window titles this tour drives. Generic titles ('PCB Editor',+ '3D Viewer') matched the wrong window on John's laptop (an empty untitled+ PCB Editor, an old demo board); every beat targets these instead."""+ stem = _doc_stem(prep)+ return {"schematic": f"{stem} — Schematic Editor", "board": f"{stem} — PCB Editor",+ "symbol": f"Adom:{IC_SYM} — Symbol Editor", "footprint": f"Adom:{IC_FP} — Footprint Editor"}++ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: """Run one tour step. Returns narration-ready info, never raises.""" has3d = bool((prep.get("models3d") or {}).get("soic8"))@@ -485,7 +501,8 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: _t.sleep(2) except Exception: loaded = True # can't verify — don't fail the beat on the checker- return {"ok": bool(r.get("success")) and loaded, "window": "Symbol Editor",+ return {"ok": bool(r.get("success")) and loaded, "window": _tour_titles(prep)["symbol"],+ "hwnd": _hwnd_for_title(_tour_titles(prep)["symbol"]), "title": "1/6 · The MCU symbol", "say": ("This is the STM32G431, the microcontroller on the board you are about to see, " "in your own KiCad Symbol Editor. The bridge lifted it from the board's schematic "@@ -495,7 +512,8 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: "raw": r} if step == "footprint": r = handle_open_footprint_editor(kicad_info, {"libraryName": DEMO_LIB, "footprintName": IC_FP})- return {"ok": bool(r.get("success")), "window": "Footprint Editor",+ _fh = _hwnd_for_title(_tour_titles(prep)["footprint"])+ return {"ok": bool(r.get("success")) and bool(_fh), "window": _tour_titles(prep)["footprint"], "hwnd": _fh, "title": "2/6 · Its footprint", "say": ("Same chip, now its land pattern: a 48 pin LQFP on half millimetre pitch, " "taken from the board itself. The symbol is the idea, the footprint is the copper."),@@ -509,7 +527,8 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: # The viewer that APPEARS after this open is the chip's; remember it so # the board beat can close it before opening its own. before = set(_viewers_3d())- r = handle_open_3d_viewer(kicad_info, {"editor": "footprint"})+ _fh = _hwnd_for_title(_tour_titles(prep)["footprint"])+ r = handle_open_3d_viewer(kicad_info, {"editor": "footprint", **({"fpHwnd": _fh} if _fh else {})}) v = _new_window(before, _viewers_3d, 8.0) with _JOB_LOCK: _JOB["chipViewer"] = v@@ -523,8 +542,11 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: if has3d else ["the bare pads (KiCad's 3D library is not installed here)"]), "raw": r} if step == "schematic":- r = handle_open_schematic(kicad_info, {"filePath": prep["schematic"]})- return {"ok": bool(r.get("success")), "window": "Schematic Editor",+ _t = _tour_titles(prep)["schematic"]+ _sh = _hwnd_for_title(_t)+ r = {"success": True, "reused": True} if _sh else handle_open_schematic(kicad_info, {"filePath": prep["schematic"]})+ _sh = _sh or _new_window(set(), lambda: [h for h in [_hwnd_for_title(_t)] if h], 20.0)+ return {"ok": bool(r.get("success")) and bool(_sh), "window": _t, "hwnd": _sh, "title": "4/6 · The ESC schematic", "say": ("Now the real schematic: the Adom ESC G431, a brushless motor controller, " "straight from its wiki page. Over two hundred symbols: the STM32 you just saw, "@@ -533,8 +555,11 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: "the whole sheet came from wiki.adom.inc/adom/esc-g431"], "raw": r} if step == "board":- r = handle_open_board(kicad_info, {"filePath": prep["board"]})- return {"ok": bool(r.get("success")), "window": "PCB Editor",+ _t = _tour_titles(prep)["board"]+ _bh = _hwnd_for_title(_t)+ r = {"success": True, "reused": True} if _bh else handle_open_board(kicad_info, {"filePath": prep["board"]})+ _bh = _bh or _new_window(set(), lambda: [h for h in [_hwnd_for_title(_t)] if h], 25.0)+ return {"ok": bool(r.get("success")) and bool(_bh), "window": _t, "hwnd": _bh, "title": "5/6 · The board in 2D", "say": ("The layout of that board: one hundred and forty nine footprints, the MOSFETs " "around the edge, the MCU in the middle, copper, silkscreen and courtyards all real."),@@ -549,8 +574,7 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: try: from .kicad_ui import _find_all_kicad_windows for _ in range(15):- if any("pcb editor" in (w.get("title") or "").lower()- for w in _find_all_kicad_windows()):+ if _hwnd_for_title(_tour_titles(prep)["board"]): break time.sleep(1) except Exception:@@ -561,7 +585,8 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict: _close_hwnd(chip) # the chip's viewer must not win the title match time.sleep(1.5) before = set(_viewers_3d())- r = handle_open_3d_viewer(kicad_info, {"editor": "pcb"})+ _bh = _hwnd_for_title(_tour_titles(prep)["board"])+ r = handle_open_3d_viewer(kicad_info, {"editor": "pcb", **({"pcbHwnd": _bh} if _bh else {})}) v = _new_window(before, _viewers_3d, 12.0) if not v: vs = _viewers_3d()@@ -1168,7 +1193,8 @@ def _close_stale_tour_windows() -> None: from handlers.kicad_ui import _find_all_kicad_windows for w in _find_all_kicad_windows(): t = (w.get("title") or "").lower()- if "3d viewer" in t or "footprint editor" in t:+ if ("3d viewer" in t or "footprint editor" in t or t.startswith("adom-demo")+ or t.strip() == "pcb editor"): _close_hwnd(int(w["hwnd"])) import time as _t _t.sleep(1.5)
handlers/open_footprint_editor.py+11−1@@ -1270,6 +1270,16 @@ def handle_open_3d_viewer(kicad_info: dict, args: dict) -> dict: fp_hwnd = _find_footprint_editor() pcb_hwnd = _find_pcb_editor()+ # Explicit source editors (tour, 2026-09-03): on a laptop with several PCB+ # Editors (an empty untitled one, an old demo board, the real board) the+ # first title match opened the wrong board's 3D view. A caller that knows+ # its editor's hwnd passes pcbHwnd / fpHwnd and we never guess.+ _pcb_override = args.get("pcbHwnd") or args.get("pcb_hwnd")+ _fp_override = args.get("fpHwnd") or args.get("fp_hwnd")+ if _pcb_override:+ pcb_hwnd = int(_pcb_override)+ if _fp_override:+ fp_hwnd = int(_fp_override) if editor_choice in {"fp", "footprint"}: if not fp_hwnd: return {@@ -1386,7 +1396,7 @@ def handle_open_3d_viewer(kicad_info: dict, args: dict) -> dict: # over the legacy Alt+3 keystroke. The bridge posts WM_COMMAND 20563 # (3D Viewer menu id from PCB Editor's View menu) which doesn't # require foregrounding the window or stealing keyboard focus.- pcb_hwnd = _find_pcb_editor()+ pcb_hwnd = int(_pcb_override) if _pcb_override else _find_pcb_editor() if pcb_hwnd: # Tier 0 (v0.9.99, #26): native Win32 menu-bar resolve + PostMessage. try: