← Commit history

0.9.302: Tour sync fixes (John's run on 0.9.301): stale panel presses from the previous show are drained before a tour starts (his earlier NEXT presses made the new tour jump to step 3 at once); the narration mute is reset from the call's muted flag at thread start and pushed to the panel on the first show; STOP from the remote sets stopping on progress immediately so the dashboard can close its own pill; progress carries stopping.

John Lauer ·1aba6180dd ·1mo ago ·parent 0d3d7f6
3 files changed +36−3
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.301\ No newline at end of file+0.9.302\ No newline at end of file
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.301",+  "version": "0.9.302",   "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+34−1
@@ -986,6 +986,7 @@ def on_panel_event(ev: dict) -> dict:         # poll _EXIT and bail at their next gesture.         _EXIT["now"] = True         _caption_clear()+        _job_update(stopping=True, stepLabel="Closing the demo tour")     set_demo_control(cmd)     _job_update(lastPress={"button": b, "at": at, "via": "callback"})     return {"queued": cmd}@@ -1032,6 +1033,27 @@ def _panel_poll() -> str | None:     return None  +def _drain_stale_presses() -> None:+    """Empty the panel's press queue before a tour starts. Presses queued+    against the previous show (same id) must not steer this one."""+    try:+        from handlers import ad_client+        for _ in range(3):+            r = ad_client.call("desktop_demo_panel", {"action": "poll", "id": _PANEL_ID}, timeout=6) or {}+            out = r.get("output") if isinstance(r, dict) else None+            if isinstance(out, str):+                try:+                    import json as _j+                    out = _j.loads(out)+                except Exception:  # pylint: disable=broad-except+                    out = {}+            d = ((out or {}).get("data") if isinstance(out, dict) else None) or out or {}+            if not d.get("events"):+                break+    except Exception:  # pylint: disable=broad-except+        pass++ def _panel_hide() -> None:     if not _PANEL["ok"]:         return@@ -1126,6 +1148,15 @@ def _run_demo_background(kicad_info: dict) -> None:     done_weight = 0.0     try:         _EXIT["now"] = False+        _PANEL_SEEN.clear()+        _PANEL_SHOWN_ONCE["v"] = False+        _PANEL["scriptState"] = None+        # John, 2026-09-03: "no audio" and "jumped to step 3 instantly". The mute+        # flag outlived the previous tour, and ab keeps undrained presses for a+        # panel id across shows, so last run's NEXT presses fired at once. Reset+        # the mute from THIS call's args and swallow whatever the panel queued.+        _NARR["muted"] = bool(_JOB.get("mutedRequested"))+        _drain_stale_presses()         _panel_show(0, "playing")         _caption(_BEAT_CAPTIONS["prepare"], duration_ms=_BEAT_CAPTION_MS["prepare"])         _play_narration("prepare")@@ -1331,7 +1362,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")}}+                "panel", "muted", "muteSource", "lastPress", "paused", "stopping")}}         # 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@@ -1376,6 +1407,8 @@ def handle_demo(kicad_info: dict, args: dict) -> dict:     # ---- background start ----     if args.get("all") and args.get("background"):         _NARR["muted"] = bool(args.get("muted"))+        with _JOB_LOCK:+            _JOB["mutedRequested"] = bool(args.get("muted"))   # the thread re-applies this at start (sync rule)         snap = _job_snapshot()         if snap.get("active"):             return {"success": False, "error": "a demo job is already running",