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.114: sentinel exemption for sanctioned foregrounds (bring_to_user / announce_foreground set expect_foreground so the sentinel never fights the bridge's own user-requested fg paths)
5 files changed
+23−8
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.113+0.9.114
adom-bridge-kicad-manifest.json+5−5@@ -1,15 +1,15 @@ { "manifest_version": 1, "name": "kicad",- "version": "0.9.113",- "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.113/adom-bridge-kicad-v0.9.113.zip",- "sha256": "4afd53258aa6a175bd6b2a585473bb88c7e4ca111e1708a376aca3127d6af45e",- "size": 408755,+ "version": "0.9.114",+ "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.114/adom-bridge-kicad-v0.9.114.zip",+ "sha256": "4aafd591a26ff321f3ba3242a03d7a0db35664971552b96104db0e8899a297a0",+ "size": 408981, "verbPrefixes": [ "kicad_" ], "healthEndpoint": "/status",- "released_at": "2026-08-16T13:20:09Z",+ "released_at": "2026-08-16T13:25:01Z", "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png", "languages": [ "Python",
adom-bridge-kicad-v0.9.114.zipadded⋯ 1 unchanged line ⋯
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.113",+ "version": "0.9.114", "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/win_focus.py+16−1@@ -298,6 +298,16 @@ def _seconds_since_user_input() -> float: _USER_INTENT_WINDOW_S = 1.2 _sentinel_started = False _focus_events = None # collections.deque, lazy+_expected_fg_until = 0.0 # sanctioned bridge-initiated foreground window+++def expect_foreground(seconds: float = 20.0) -> None:+ """The bridge is ABOUT to foreground a KiCad window legitimately (user asked:+ foreground:true / allowForeground / bring_to_user — always announced first).+ The sentinel must not fight it: exempt bounces for `seconds`."""+ global _expected_fg_until+ import time as _t+ _expected_fg_until = max(_expected_fg_until, _t.monotonic() + seconds) def get_focus_events() -> list:@@ -337,7 +347,10 @@ def start_focus_sentinel() -> None: if fg != last_fg: if fg and is_kicad_window(fg): ago = _seconds_since_user_input()- if ago <= _USER_INTENT_WINDOW_S:+ if _t.monotonic() < _expected_fg_until:+ blessed.add(fg)+ _log("sanctioned-foreground (allowed)", fg, ago)+ elif ago <= _USER_INTENT_WINDOW_S: blessed.add(fg) _log("user-foregrounded (left alone)", fg, ago) elif _guard_count > 0:@@ -442,6 +455,7 @@ def bring_to_user(hwnd) -> bool: if not IS_WINDOWS or not hwnd: return False try:+ expect_foreground(30.0) # tell the sentinel this foreground is sanctioned allow_activation(hwnd) # a suppressed window can't be activated _user32.ShowWindow(hwnd, 9) # SW_RESTORE (in case minimized) return _set_foreground_robust(hwnd)@@ -456,6 +470,7 @@ def announce_foreground(action: str, seconds: int = 5) -> None: the calling AI thread via the forwarded caller headers — so the user sees WHY their screen is about to be taken, BEFORE it happens. Fire-and-forget in a daemon thread: the toast failing must never fail (or delay) the verb."""+ expect_foreground(max(20.0, float(seconds) + 15.0)) # sentinel: sanctioned fg incoming def _fire(): try: from caller_identity import ad_callback