← Commit history

v0.1.38: taskbar overlay auto-clears after 5 idle minutes

John Lauer ·c5da1e02dd ·1mo ago ·parent 887b23f
8 files changed +52−7
CHANGELOG.md+4
@@ -1,5 +1,9 @@ # Changelog +## 0.1.38 — 2026-09-01+- **The taskbar overlay badge auto-clears after 5 minutes without a driving verb.** John, seeing the Adom mark still parked on Altium's taskbar icon: *"do you auto-remove that overlay after 5 minutes of verb inactivity?"* The answer was no — the badge was set on the first driving verb and never cleared until the bridge died, while this module's own header PROMISED "cleared when the bridge goes idle". A permanent overlay stops meaning "an AI is driving right now" and starts meaning nothing. An idle watcher (started at boot beside the dialog reaper) now clears it at 5 idle minutes; the next driving verb re-badges instantly.+- For the record, what already worked: every driving verb with a measured estimate ≥3 s runs a REAL taskbar progress bar on Altium's button (percent paced by history, marquee when unmeasured, red flash + clear on failure) and stops it when the verb returns — progress rides the taskbar icon, never toasts, per the house rule.+ ## 0.1.37 — 2026-09-01 - **Altium's dialog text is unreadable — so the reaper stops relying on it.** Measured: the VaultExplorer warning's entire UIA subtree is `Window "Warning" -> panes -> Button "OK"`. The message is PAINTED pixels, absent from `WM_GETTEXT` and from the accessibility tree, so no amount of text classification will ever see it. - **The rule that does work: a dialog whose ONLY button is OK carries no decision.** Dismissing it can acknowledge, never choose. Everything that asks something of the user has two or more buttons — a save prompt (Save / Don't Save / Cancel), a licence chooser (Choose a license / Continue), a confirm (Yes / No). The standing reaper now clears lone-OK dialogs even when their text is unreadable, logging them as `single_ok_acknowledgement`.
adom-bridge-altium-manifest.json+4−4
@@ -1,9 +1,9 @@ {   "name": "altium",-  "version": "0.1.37",-  "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.37/adom-bridge-altium-v0.1.37.zip",-  "sha256": "fa20a659046ef5f8d7a24ff13c871cc0b54885b1ea8e1dbcfbc58f1f64f2c8d4",-  "size": 135280,+  "version": "0.1.38",+  "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.38/adom-bridge-altium-v0.1.38.zip",+  "sha256": "9468b0d69a824c6ea725a81b3cb800b1ed0e26b870a83ed13b62ac2f7ff8cb9c",+  "size": 135889,   "verbPrefixes": [     "altium_"   ],
adom-bridge-altium-v0.1.38.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "altium",   "displayName": "Altium Designer",-  "version": "0.1.37",+  "version": "0.1.38",   "description": "Drive Altium Designer from Adom: a resident DelphiScript agent (eval + the 1,100-command process launcher), the X2 -R command line, UIA over the VCL shell, dialogs by PID, screenshots of every Altium window, and Altium 365.",   "homepage": "https://wiki.adom.inc/adom/altium-bridge",   "docs": "https://wiki.adom.inc/adom/altium-bridge",
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "altium-bridge",-  "version": "0.1.37",+  "version": "0.1.38",   "type": "app",   "description": "Skills for your container so your AI knows how to drive the Altium Designer bridge (altium_* verbs). The bridge runtime itself is the release zip; Adom Bridge loads that.",   "tags": [
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.1.37+0.1.38
src/handlers/taskbar.py+38
@@ -163,3 +163,41 @@ def end_activity_progress(verb: str, failed: bool = False) -> None:             _taskbar({"hwnd": int(hwnd), "flash": "stop"})     except Exception:         pass+++# ---- idle auto-clear (John, 2026-09-01: "do you auto-remove that overlay after 5 minutes of+# verb inactivity?") ---------------------------------------------------------------------------+#+# He asked because the answer was NO: the badge was set on the first driving verb and never+# cleared until the bridge died — the header above always PROMISED "cleared when the bridge goes+# idle", but nothing implemented it. A permanent overlay is worse than none: it stops meaning+# "an AI is driving right now" and starts meaning nothing.++IDLE_CLEAR_SEC = 300.0+_LAST_DRIVING = [0.0]+_IDLE_WATCHER = [False]+++def note_driving() -> None:+    """Called by dispatch on every driving verb; feeds the idle clock."""+    _LAST_DRIVING[0] = time.time()+++def start_idle_watcher() -> bool:+    """Clear the overlay after IDLE_CLEAR_SEC without a driving verb. Idempotent.+    The next driving verb re-badges instantly (set_driving_badge re-adds after a clear)."""+    if _IDLE_WATCHER[0]:+        return False+    _IDLE_WATCHER[0] = True++    def _loop():+        while True:+            time.sleep(15.0)+            try:+                if badge_active() and _LAST_DRIVING[0] and time.time() - _LAST_DRIVING[0] > IDLE_CLEAR_SEC:+                    clear_driving_badge()+            except Exception:+                pass++    threading.Thread(target=_loop, daemon=True, name="altium-badge-idle-clear").start()+    return True
src/server.py+3
@@ -2850,6 +2850,7 @@ def dispatch_command(command: str, args: dict) -> dict:         try:             from handlers import taskbar             taskbar.set_driving_badge()+            taskbar.note_driving()  # feeds the 5-min idle clock that auto-clears the overlay             est, conf = progress.estimate(command, "warm")             if est and est >= 3:                 stopper = taskbar.start_activity_progress(command, est, conf)@@ -2946,6 +2947,8 @@ def main():     try:         dialogs.start_reaper()         print("[altium] standing dialog reaper started (known nuisances are pre-empted, never save prompts)")+        taskbar.start_idle_watcher()+        print("[altium] badge idle-watcher started (overlay auto-clears after 5 min without a driving verb)")     except Exception as e:         print(f"[altium] dialog reaper failed to start: {e}")     srv = ThreadingHTTPServer((host, port), Handler)