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.316: Tour remote: FIFO press queue (no press lost), poll-drained presses recorded, presses on progress
3 files changed
+26−11
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.315+0.9.316
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "kicad", "displayName": "KiCad EDA",- "version": "0.9.315",+ "version": "0.9.316", "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+24−9@@ -1080,13 +1080,20 @@ def _stop_narration() -> None: # interrupted mid-verb) and while paused; exit also silences narration and # clears the caption. _CTL_TOAST_ID = "kicad-demo-ctl"-_CTRL: dict = {"cmd": None, "seq": 0}+_CTRL: dict = {"cmd": None, "seq": 0, "queue": []} # queue: FIFO of presses not yet honoured _CTRL_LOCK = _threading.Lock() def set_demo_control(cmd: str) -> None:+ """Queue a transport command. FIFO (measured on ConfRoomROG 2026-09-04: a+ single slot lost a 'next' when 'back' arrived during the same beat's+ un-interruptible window open). 'exit' jumps the queue.""" with _CTRL_LOCK:- _CTRL["cmd"] = cmd+ if cmd == "exit":+ _CTRL["queue"] = ["exit"]+ elif len(_CTRL["queue"]) < 8:+ _CTRL["queue"].append(cmd)+ _CTRL["cmd"] = _CTRL["queue"][0] if _CTRL["queue"] else None def _post_transport(body: str, paused: bool = False) -> None:@@ -1299,13 +1306,21 @@ def _panel_poll() -> str | None: _apply_mute(bool(d["muted"]), "panel-poll") events = [e for e in (d.get("events") or []) if str((e or {}).get("at")) not in _PANEL_SEEN] mapping = {"back": "prev", "play": "play", "pause": "pause", "next": "next", "stop": "exit"}- for ev in reversed(events):+ for ev in events: b = str((ev or {}).get("button") or "").lower()+ _PANEL_SEEN.add(str((ev or {}).get("at")))+ with _JOB_LOCK:+ _JOB.setdefault("presses", []).append({"button": b, "at": (ev or {}).get("at"), "via": "poll",+ "beat": _JOB.get("beatIndex"), "step": _JOB.get("step")}) if b in ("mute", "unmute"): _apply_mute(b == "mute", "panel-poll") continue if b in mapping:- return mapping[b]+ set_demo_control(mapping[b])+ with _CTRL_LOCK:+ if _CTRL["queue"]:+ cmd = _CTRL["queue"].pop(0); _CTRL["cmd"] = _CTRL["queue"][0] if _CTRL["queue"] else None+ return cmd if d.get("state") == "paused": return "pause-held" except Exception: # pylint: disable=broad-except@@ -1347,8 +1362,8 @@ def _panel_hide() -> None: def _drain_ctrl() -> str | None: """One pending control command, from the verb channel or a toast click.""" with _CTRL_LOCK:- cmd = _CTRL["cmd"]- _CTRL["cmd"] = None+ cmd = _CTRL["queue"].pop(0) if _CTRL["queue"] else None+ _CTRL["cmd"] = _CTRL["queue"][0] if _CTRL["queue"] else None if cmd: return cmd pc = _panel_poll()@@ -1503,7 +1518,7 @@ def _run_demo_background(kicad_info: dict) -> None: # the mute from THIS call's args and swallow whatever the panel queued. _NARR["muted"] = bool(_JOB.get("mutedRequested")) with _CTRL_LOCK:- _CTRL["cmd"] = None # a control queued against the previous tour must not steer this one+ _CTRL["cmd"] = None; _CTRL["queue"] = [] # a control queued against the previous tour must not steer this one _drain_stale_presses() _restore_bounds("leftovers") # a crashed earlier tour never leaves windows moved _close_stale_tour_windows()@@ -1637,7 +1652,7 @@ def _run_demo_background(kicad_info: dict) -> None: break if cmd: with _CTRL_LOCK: # keep it for the between-beat check- _CTRL["cmd"] = cmd+ _CTRL["queue"].insert(0, cmd); _CTRL["cmd"] = cmd break _t.sleep(0.5) if exited:@@ -1733,7 +1748,7 @@ def handle_demo(kicad_info: dict, args: dict) -> dict: out = {"success": True, **{k: snap.get(k) for k in ("done", "step", "stepLabel", "beatIndex", "beatCount", "percent", "elapsedSec", "estimatedSec", "error",- "panel", "muted", "muteSource", "lastPress", "paused", "stopping", "verified", "captions", "windowsRestored")}}+ "panel", "muted", "muteSource", "lastPress", "presses", "paused", "stopping", "verified", "captions", "windowsRestored")}} # 0.9.199 (wiki issue #39, John): `active` used to be a BOOLEAN here and # an ARRAY in kicad_progress - the same key, two types, from one bridge. # A renderer written as `if (resp.active)` then treats a finished job as