← Commit history

v1.5.10: suppress docked-panel false positives in dialog capture (gate on main-window-disabled); doc screenshot-location nuance

John Lauer ·129ddba796 ·4mo ago ·parent 910c83c
6 files changed +45−12
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.5.9\ No newline at end of file+1.5.10\ No newline at end of file
adom-bridge-fusion-manifest.json+5−5
@@ -1,13 +1,13 @@ {   "manifest_version": 1,   "name": "fusion360",-  "version": "1.5.9",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.5.9/adom-bridge-fusion-v1.5.9.zip",-  "sha256": "a3c19162a7ba58e7af9858e2949964b1bf485123c032b2df8e348f0b4837ad13",-  "size": 265348,+  "version": "1.5.10",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.5.10/adom-bridge-fusion-v1.5.10.zip",+  "sha256": "f5e15b26eba7efe1aa36eb09070aebeb1781e1aa3b257c36f1d973a4500d3626",+  "size": 266197,   "verbPrefixes": [     "fusion_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-06-29T01:15:20Z"+  "released_at": "2026-06-29T01:23:30Z" }\ No newline at end of file
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.5.9",+  "version": "1.5.10",   "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.",   "homepage": "https://github.com/adom-inc/adom-desktop-fusion-bridge",   "author": "Adom Inc.",
handlers/fusion_ui.py+11
@@ -692,10 +692,21 @@ def get_fusion_window_info() -> dict:     # Find potential dialog windows     dialogs = _find_qt_dialog_windows() +    # A real modal dialog DISABLES its owner window; docked panels (Browser,+    # Timeline) leave it ENABLED. This is the clean signal that separates an+    # actual blocking modal from a tool window that merely shares the "Fusion360"+    # title and slips past the size filter. Callers use it to suppress+    # false-positive "dialogs" when nothing is actually blocking.+    try:+        main_enabled = bool(user32.IsWindowEnabled(hwnd))+    except Exception:+        main_enabled = True+     return {         "success": True,         "hwnd": hwnd,         "title": title,+        "mainEnabled": main_enabled,         "rect": {             "left": left, "top": top,             "right": right, "bottom": bottom,
server.py+15−3
@@ -285,10 +285,18 @@ def _capture_dialog_array(settle: float = 0.6) -> dict | None:     raw = info.get("dialogs") or []     if not raw:         return None+    main_enabled = info.get("mainEnabled", True)     try:         classified = {c.get("hwnd"): c for c in classify_blocking_dialogs()}     except Exception:         classified = {}+    # Categories that mean "we could not identify it by title" - i.e. a generic+    # "Fusion360"-titled window. When the main window is still ENABLED, such a+    # window is a docked panel (Browser/Timeline), NOT a blocking modal, so we+    # suppress it to avoid crying wolf on every op. A real modal DISABLES the main+    # window (main_enabled False) and is always surfaced; known dialogs (recovery,+    # update, save prompts) are surfaced regardless of the enabled state.+    _GENERIC = {None, "generic_modal_read_screenshot", "unknown"}     out = []     for d in raw:         rect = d.get("rect", {})@@ -296,10 +304,13 @@ def _capture_dialog_array(settle: float = 0.6) -> dict | None:             continue  # skip banners / tiny docked tool windows         hwnd = d.get("hwnd")         title = d.get("title", "")+        c = classified.get(hwnd) or {}+        cat = c.get("category")+        if main_enabled and cat in _GENERIC:+            continue  # docked panel, not a modal - nothing is blocking the main window         entry = {"hwnd": hwnd, "title": title}-        c = classified.get(hwnd)-        if c:-            entry["category"] = c.get("category")+        if cat:+            entry["category"] = cat             entry["resolution"] = c.get("resolution")         try:             label = _re.sub(r'[<>:"/\\|?*]', "", title).replace(" ", "_")[:30] or "dialog"@@ -314,6 +325,7 @@ def _capture_dialog_array(settle: float = 0.6) -> dict | None:     return {         "dialogsDetected": len(out),         "dialogs": out,+        "mainWindowEnabled": main_enabled,         "_hint": (             f"⚠️ {len(out)} Fusion dialog(s)/owned popup(s) are up after this operation. "             "STOP and ANALYZE before the next call: READ each dialogs[].screenshot and use its "
skills/fusion-driving/SKILL.md+12−2
@@ -42,8 +42,14 @@ ways to get the array, in order of preference:    shots. Get the hwnd from `fusion_window_info` (its `hwnd` field) or `desktop_find_window    {"titleContains":"Autodesk Fusion"}`. -**To actually SEE a shot:** the PNG/WebP is saved on the machine running the CLI. Read the file-directly with the Read tool. Do not describe it from memory - look.+**To actually SEE a shot - mind WHERE it lands:**+- AD core `desktop_screenshot_window {hwnd}` saves the PNG **into your container** (e.g.+  `/tmp/adom-desktop-screenshots/`) - Read it directly.+- The bridge's own captures (the auto `dialogArray[].screenshot` paths and `fusion_screenshot_all`)+  are saved on the **Windows box** (`C:/tmp/conduit-screenshots/`). To see one, either re-capture that+  hwnd with `desktop_screenshot_window {hwnd}` (lands in your container) or `desktop_pull_file` it over.++Either way, LOOK - do not describe it from memory.  ## Rule 2 - NEVER fullscreen-capture. Always target the window by hwnd. @@ -101,6 +107,10 @@ commands and told the AI to read them. That same capture now fires after **all m  - cheaply enumerates dialog windows (`get_fusion_window_info`, a Win32 `EnumWindows` - no screenshot   when nothing is up),+- suppresses false positives: a docked panel (Browser/Timeline) shares the "Fusion360" title and can+  slip past the size filter, but it does NOT disable the main window. So a generic-titled candidate is+  only surfaced when the main window is actually disabled (`mainWindowEnabled:false` - a real modal is+  up) or it matches a known dialog by title. That keeps the hint from crying wolf on every op, - on detection, classifies each (`classify_blocking_dialogs`) and screenshots it by hwnd   (`screenshot_hwnd`, background `PrintWindow`), - returns `dialogsDetected` + `dialogs[]` + a top-level `_hint` that says **ANALYZE these before