← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.15\ No newline at end of file+1.9.16\ No newline at end of file
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}