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.9.16: foreground handback
5 files changed
+40−9
BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.15+1.9.16
adom-bridge-fusion-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "fusion360",- "version": "1.9.15",- "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.15/adom-bridge-fusion-v1.9.15.zip",- "sha256": "45c8cc80b1a6a7b81a7c3a4dcab130d2e4e08f3220d31b8d1db3e7defe529a87",- "size": 359588,+ "version": "1.9.16",+ "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.16/adom-bridge-fusion-v1.9.16.zip",+ "sha256": "926d7262909ec6e1a6d46702829bf30e6c6cc535c3402a8d083bb69876952ad1",+ "size": 359890, "verbPrefixes": [ "fusion_" ],
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "fusion360", "displayName": "Autodesk Fusion 360",- "version": "1.9.15",+ "version": "1.9.16", "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://wiki.adom.inc/adom/fusion-bridge", "author": "Adom Inc.",
dev-skills/fusion-background-etiquette/SKILL.md+5−1@@ -37,7 +37,11 @@ foreground mid-keystroke is the single most user-hostile thing this bridge can d 4. **Foreground, the documented last resort** - ONLY when input genuinely requires focus (real keystrokes into a focused field that UIA cannot set). Every such verb: (a) is a conscious, documented exception here, (b) announces itself with an- on-screen caption first, (c) restores the user's foreground after.+ on-screen caption first, (c) HANDS THE FOREGROUND BACK after. Rule (c) is not+ optional plumbing: `_snapshot_foreground()` before the take,+ `_handback_foreground(prev, taken_hwnd)` after the input lands (handlers/+ fusion_ui.py) - taking focus for an ESC and never restoring it is as rude as the+ steal (John caught send_key doing exactly this live, 2026-08-15). **The user can ALWAYS choose foreground:** every window-raising verb accepts `foreground: true` - the dispatcher then skips the envelope entirely so the user can
handlers/fusion_ui.py+29−2@@ -409,6 +409,7 @@ def screenshot_fusion_window(use_bitblt: bool = False) -> dict: if use_bitblt: # Screen DC capture — needs foreground SW_RESTORE = 9+ _prev_fg = _snapshot_foreground() user32.ShowWindow(hwnd, SW_RESTORE) user32.SetForegroundWindow(hwnd) time.sleep(1.0)@@ -418,6 +419,7 @@ def screenshot_fusion_window(use_bitblt: bool = False) -> dict: hbmp = gdi32.CreateCompatibleBitmap(hdc_screen, width, height) old_bmp = gdi32.SelectObject(hdc_mem, hbmp) gdi32.BitBlt(hdc_mem, 0, 0, width, height, hdc_screen, left, top, SRCCOPY)+ _handback_foreground(_prev_fg, hwnd) else: # PrintWindow capture — works without foreground hdc_screen = user32.GetDC(hwnd)@@ -504,6 +506,26 @@ def screenshot_fusion_window(use_bitblt: bool = False) -> dict: # --- Click --- +def _snapshot_foreground():+ """The USER'S current foreground window, so a focus-requiring action can hand it back."""+ try:+ return user32.GetForegroundWindow()+ except Exception:+ return 0+++def _handback_foreground(prev, taken_hwnd):+ """Give the foreground BACK after a focus-requiring action (John, 2026-08-15: taking+ fg for an ESC and never restoring is as rude as the steal). No-op if the user was+ already in the window we took."""+ try:+ if prev and prev != taken_hwnd and user32.IsWindow(prev):+ time.sleep(0.15)+ user32.SetForegroundWindow(prev)+ except Exception:+ pass++ def click_fusion(x, y, relative=True, hwnd=None) -> dict: """Click at coordinates in a window (Fusion main window or any dialog). @@ -539,7 +561,8 @@ def click_fusion(x, y, relative=True, hwnd=None) -> dict: width = right - left height = bottom - top - # Bring to front+ # Bring to front - snapshot the user's window first, hand it back after the click+ _prev_fg = _snapshot_foreground() user32.SetForegroundWindow(hwnd) time.sleep(0.1) @@ -579,6 +602,7 @@ def click_fusion(x, y, relative=True, hwnd=None) -> dict: up.union.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTUP _send_input(move, down, up)+ _handback_foreground(_prev_fg, hwnd) return { "success": True,@@ -619,7 +643,8 @@ def send_key_to_fusion(key: str, hwnd=None) -> dict: "_hint": "Call fusion_start to start Fusion 360, then retry.", } - # Bring to front+ # Bring to front - snapshot the user's window first, hand it back after the key lands+ _prev_fg = _snapshot_foreground() user32.SetForegroundWindow(hwnd) time.sleep(0.1) @@ -665,6 +690,7 @@ def send_key_to_fusion(key: str, hwnd=None) -> dict: inputs.append(s_up) _send_input(*inputs)+ _handback_foreground(_prev_fg, hwnd) return {"success": True, "key": key, "vk": vk, "shift": bool(shift)} else: return {"success": False, "error": f"Unknown key: '{key}'. Use a named key or single character."}@@ -680,6 +706,7 @@ def send_key_to_fusion(key: str, hwnd=None) -> dict: ku.union.ki.dwFlags = KEYEVENTF_KEYUP _send_input(kd, ku)+ _handback_foreground(_prev_fg, hwnd) return {"success": True, "key": key_lower, "vk": vk}