← Commit history

v0.9.71: live-verify fixes — demo/export_molecule join the dispatcher focus sets (windows were landing ON TOP of the user), _capture gets real kicad_info (screenshots were silently empty), symbol beat verifies the part actually LOADED (was narrating over a blank editor)

John Lauer ·1f97723202 ·2mo ago ·parent 2fc63d5
6 files changed +36−14
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.70+0.9.71
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.70",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.70/adom-bridge-kicad-v0.9.70.zip",-  "sha256": "91f626909c22c745703250b86630182d254c752c2e4369e2aa8ccbbcbe11a702",-  "size": 365778,+  "version": "0.9.71",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.71/adom-bridge-kicad-v0.9.71.zip",+  "sha256": "1565375de642c42a59e622da75d954de3321801ba5e35700ec061a2389d6c706",+  "size": 366198,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-08T23:07:12Z",+  "released_at": "2026-08-08T23:50:06Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.71.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.70",+  "version": "0.9.71",   "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+21−4
@@ -49,7 +49,7 @@ STEP_LABEL = {"symbol": "symbol", "footprint": "footprint", "footprint3d": "part               "schematic": "schematic", "board": "board_2d", "board3d": "board_3d"}  -def _capture(step: str, window_title: str) -> dict | None:+def _capture(kicad_info: dict, step: str, window_title: str) -> dict | None:     """Capture the window this beat just opened and return {label, path}.      fusion_demo hands back `screenshots:[{label,path}]` rather than making the@@ -57,7 +57,7 @@ def _capture(step: str, window_title: str) -> dict | None:     way for both bridges.     """     try:-        shots = handle_screenshot_all({}, {}) or {}+        shots = handle_screenshot_all(kicad_info, {}) or {}         for sh in (shots.get("data") or {}).get("screenshots") or shots.get("screenshots") or []:             if window_title.lower() in (sh.get("title") or "").lower():                 p = sh.get("fullPath") or sh.get("localPath") or sh.get("savedTo")@@ -324,7 +324,24 @@ def _run_step(kicad_info: dict, step: str, prep: dict) -> dict:     has3d = bool((prep.get("models3d") or {}).get("soic8"))     if step == "symbol":         r = handle_open_symbol_editor(kicad_info, {"libraryName": DEMO_LIB, "symbolName": IC_SYM})-        return {"ok": bool(r.get("success")), "window": "Symbol Editor",+        # open() succeeding is NOT the same as the part being on the canvas —+        # a "[no symbol loaded]" editor shows an empty sheet and the demo would+        # narrate over a blank window (caught live on AdomLapper 2026-08-08).+        loaded = False+        if r.get("success"):+            try:+                import time as _t+                from .kicad_ui import _find_all_kicad_windows+                for _ in range(3):+                    w = next((x for x in _find_all_kicad_windows()+                              if "symbol editor" in (x.get("title") or "").lower()), None)+                    if w and "no symbol loaded" not in (w.get("title") or "").lower():+                        loaded = True+                        break+                    _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",                 "title": "1/6 · Schematic symbol",                 "say": (f"This is the {IC_SYM} symbol, in the user's own KiCad symbol editor. "                         "The bridge installed it into their Adom library a second ago — no file "@@ -477,7 +494,7 @@ def handle_demo(kicad_info: dict, args: dict) -> dict:     shots = []     for r in results:         if r.get("ok") and r.get("window"):-            c = _capture(r["step"], r["window"])+            c = _capture(kicad_info, r["step"], r["window"])             if c:                 shots.append(c)                 r["screenshot"] = c["path"]
server.py+8−3
@@ -657,10 +657,15 @@ def dispatch_command(command: str, args: dict) -> dict:     _SPAWNS_WINDOW = {"open_board", "open_schematic", "open_symbol_editor",                       "open_footprint_editor", "open_3d_viewer",                       "place_footprint", "install_symbol", "install_footprint",-                      "install_plugin", "upgrade"}+                      "install_plugin", "upgrade",+                      # demo warms eeschema+pcbnew and opens editors/3D — without+                      # membership here the capture/restore machinery skips it and+                      # every window it spawns lands ON TOP of the user (verified+                      # live on AdomLapper 2026-08-08: PCB Editor at z0).+                      "demo", "export_molecule"}     _SPAWNS_WINDOW_FOCUS = {"open_board", "open_schematic", "open_symbol_editor",                             "open_footprint_editor", "open_3d_viewer",-                            "place_footprint", "install_symbol", "install_footprint"}+                            "place_footprint", "install_symbol", "install_footprint", "demo", "export_molecule"}      # BACKGROUND-OPEN RULE: capture the USER's foreground window BEFORE any verb     # that spawns/raises a KiCad window, so we can hand focus back afterward. The@@ -729,7 +734,7 @@ def dispatch_command(command: str, args: dict) -> dict:                 # seconds) — STARTUPINFO can't touch it and a 6s restore expires                 # mid-render, so it needs a longer guard. Plain launched editors                 # only self-activate once, right after launch, so 6s covers them.-                _restore_secs = 16.0 if command in ("open_3d_viewer", "place_footprint") else 6.0+                _restore_secs = 16.0 if command in ("open_3d_viewer", "place_footprint", "demo") else 6.0                 win_focus.restore_foreground_persistent(_user_fg, seconds=_restore_secs)             if isinstance(result, dict):                 result["_userFocusRestored"] = bool(restored)