app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
← Commit history
v1.5.10: suppress docked-panel false positives in dialog capture (gate on main-window-disabled); doc screenshot-location nuance
6 files changed
+45−12
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.5.9+1.5.10
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" }
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