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.289: John, 2026-09-03: no caption on a Symbol Editor launch or rescan reopen (the 0.9.285 one was large, shown too long, and wrong on his laptop: it promised 'behind your window' while KiCad raised itself in front while loading). Launch is silent. _foreground now also reports editorInFrontAtEnd, measured from AD's z-order at return time, so a response never claims background about a window the user can see on top. Dev skill records the rule: one early look, no fighting the user for the foreground, captions only on the opt-in allowFocusSteal path and short.
4 files changed
+28−23
handlers/open_symbol_editor.py+28−23@@ -375,24 +375,31 @@ _EDITOR_OPENED_AT: dict = {} # hwnd -> epoch seconds when this module saw it ap def _caption_new_frame(user_top: dict | None, rescan: bool = False) -> None:- """John's rule: when you know a window will appear, say why, on screen, first."""+ """Deliberately does nothing (John, 2026-09-03): the on-screen caption+ before an editor launch was large, ugly, shown far too long, and on his+ laptop it was WRONG (it promised "behind your window" while KiCad came up+ in front). A caption that lies is worse than none. The launch is silent;+ the response reports, measured, whether the editor ended up in front+ (editorInFrontAtEnd). Captions remain only for the opt-in allowFocusSteal+ path, and short."""+ return None+++def _editor_in_front() -> bool | None:+ """Is a Symbol Editor the top window RIGHT NOW (measured, no action)?+ John, 2026-09-03: the guard's one early look said "did not come to the+ top" while KiCad then raised itself as it finished loading, and he watched+ it come up in front. Report the end state too, so a response never says+ "background" about a window the user can see on top. None if unreadable.""" try:- from handlers import ad_client- what = ("re-opening KiCad's Symbol Editor so it re-indexes the library (a new symbol was installed)"- if rescan else "opening KiCad's Symbol Editor (it indexes every symbol library on open)")- back = " It will be placed behind your current window." if user_top and not user_top.get("isKicad") else ""- # Name the calling thread (John, 2026-09-03: a caption on his laptop- # could not tell him WHICH thread was about to open a window).- try:- import caller_identity- who = str(caller_identity.thread_name() or "").strip()- except Exception: # pylint: disable=broad-except- who = ""- prefix = f"Adom Bridge (thread {who}): " if who and who != "unknown" else "Adom Bridge: "- ad_client.call("desktop_caption", {"text": (prefix + what + back)[:180], "seconds": 6,- "reason": f"Warn the user before a KiCad window appears on their screen (caller: {who or 'unknown'})"})+ from handlers import kicad_windows+ rows = [r for r in (kicad_windows.find(all_windows=True, fresh=True) or []) if r.get("z") is not None]+ if not rows:+ return None+ top = sorted(rows, key=lambda r: r.get("z"))[0]+ return bool(kicad_windows._is_kicad(top) and "symbol editor" in str(top.get("title", "")).lower()) except Exception: # pylint: disable=broad-except- pass+ return None def _user_top_window() -> dict | None:@@ -1054,7 +1061,6 @@ def _handle_open_symbol_editor_inner(kicad_info: dict, args: dict) -> dict: _user_top = None if not sym_hwnd: _user_top = _user_top_window()- _caption_new_frame(_user_top) _step("Symbol Editor already open" if sym_hwnd else "opening the Symbol Editor (KiCad indexes every symbol library here)", 10) @@ -1068,7 +1074,6 @@ def _handle_open_symbol_editor_inner(kicad_info: dict, args: dict) -> dict: rescanned = True sym_hwnd = 0 _user_top = _user_top_window()- _caption_new_frame(_user_top, rescan=True) pathways_tried.append("rescan-closed-stale-editor") else: pathways_tried.append("rescan-close-failed")@@ -1346,7 +1351,7 @@ def _handle_open_symbol_editor_inner(kicad_info: dict, args: dict) -> dict: "output": f"Symbol '{search_text}' loaded (background win32 path): {_get_symbol_editor_title()}", "via": "win32-background", "navigation": dict(_LAST_NAV_TRACE),- "_foreground": dict(_LAST_FG_REPORT),+ "_foreground": {**_LAST_FG_REPORT, "editorInFrontAtEnd": _editor_in_front()}, "rescanned": rescanned, "editorCloseVia": _LAST_CLOSE_HOW if rescanned else None, **_resolved_fields(_get_symbol_editor_title(), library_name),@@ -1380,7 +1385,7 @@ def _handle_open_symbol_editor_inner(kicad_info: dict, args: dict) -> dict: "rescanned": rescanned, "editorCloseVia": _LAST_CLOSE_HOW if rescanned else None, "navigation": dict(_LAST_NAV_TRACE),- "_foreground": dict(_LAST_FG_REPORT),+ "_foreground": {**_LAST_FG_REPORT, "editorInFrontAtEnd": _editor_in_front()}, **_resolved_fields(_get_symbol_editor_title(), library_name), "error": (f"The Symbol Editor is open and one background navigation ran, but the title did not " f"confirm '{search_text}' before this call's time budget ended "@@ -1398,7 +1403,7 @@ def _handle_open_symbol_editor_inner(kicad_info: dict, args: dict) -> dict: f"{_get_symbol_editor_title()}", "via": "win32-background", "navigation": dict(_LAST_NAV_TRACE),- "_foreground": dict(_LAST_FG_REPORT),+ "_foreground": {**_LAST_FG_REPORT, "editorInFrontAtEnd": _editor_in_front()}, "attempts": 2, **_resolved_fields(_get_symbol_editor_title(), library_name), "rescanned": rescanned,@@ -1504,13 +1509,13 @@ def _handle_open_symbol_editor_inner(kicad_info: dict, args: dict) -> dict: # So report the paths, measured, and say only what they show. **_stale_explanation(kicad_info, search_text), "navigation": dict(_LAST_NAV_TRACE),- "_foreground": dict(_LAST_FG_REPORT),+ "_foreground": {**_LAST_FG_REPORT, "editorInFrontAtEnd": _editor_in_front()}, } return { "success": False, "editorOpened": True, "symbolLoaded": False, "attempts": 2, "navigation": dict(_LAST_NAV_TRACE),- "_foreground": dict(_LAST_FG_REPORT),+ "_foreground": {**_LAST_FG_REPORT, "editorInFrontAtEnd": _editor_in_front()}, "errorCode": "navigation_failed", "staleVeto": _stale_veto or None, **_resolved_fields(_get_symbol_editor_title(), library_name),
install.shstart.batuninstall.sh