← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.9.215\ No newline at end of file+1.9.216\ 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.215",-  "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.215/adom-bridge-fusion-v1.9.215.zip",-  "sha256": "85a4fb31b22106da4bba7120da1312a8c87da3b967694a77216763219637f1e7",-  "size": 505688,+  "version": "1.9.216",+  "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.216/adom-bridge-fusion-v1.9.216.zip",+  "sha256": "2503c3bc281c832b597237d64b8cf10ef8b8729b7aa372afedf4e49ebd15c7f9",+  "size": 506828,   "verbPrefixes": [     "fusion_"   ],
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.9.215",+  "version": "1.9.216",   "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.",
dashboard/Cargo.lock+1−1
@@ -4,7 +4,7 @@ version = 4  [[package]] name = "fusion-dashboard"-version = "1.9.215"+version = "1.9.216" dependencies = [  "serde_json", ]
dashboard/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.9.215"+version = "1.9.216" edition = "2021"  [dependencies]
install.sh+1−1
@@ -21,7 +21,7 @@ echo "Installed fusion-bridge skills: $(ls "$PKG_DIR/skills" 2>/dev/null | tr '\ # binary (the three-artifact pattern: bridge zip -> ab, THIS pkg -> container skills + # this download, page media -> wiki). Per the wiki's PKG_VS_RELEASE policy, binaries are # platform-tagged RELEASE assets, never tarball content - the installer fetches them.-DASH_VERSION="1.9.215"+DASH_VERSION="1.9.216" DASH_URL="https://wiki.adom.inc/download/adom/fusion-bridge/${DASH_VERSION}/fusion-dashboard" DEST="" if [ -w /usr/local/bin ]; then DEST=/usr/local/bin
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "fusion-bridge",-  "version": "1.9.215",+  "version": "1.9.216",   "type": "app",   "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.",   "dependencies": {
page.json+11−8
@@ -1,6 +1,6 @@ {   "slug": "fusion-bridge",-  "version": "1.9.215",+  "version": "1.9.216",   "type": "app",   "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.",   "dependencies": {@@ -11,6 +11,10 @@     "uninstall": "./uninstall.sh"   },   "tags": [+    "ab",+    "bridge",+    "fusion",+    "desktop-app",     "fusion360",     "autodesk",     "cad",@@ -75,7 +79,10 @@     "build a basic parts library",     "add a 3d model to a component",     "make a fusion 360 library with 3d",-    "set up the fusion bridge"+    "set up the fusion bridge",+    "fusion dashboard",+    "fusion status page",+    "open the fusion launch page"   ],   "discovery_pitch": "Use the Adom Bridge Fusion 360 bridge when the user wants to DRIVE Autodesk Fusion 360 from the cloud: open/search their team's Fusion cloud designs, lay out a PCB or do electronics, export STEP/IGES/STL/Gerbers/BOM, or build a component library with real rendering 3D. It runs Fusion on the user's own machine via Adom Bridge. NOT for web/browser/login/form-filling (that's the native-browser extension) and NOT for KiCad (that's the kicad bridge).",   "brief": "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.",@@ -104,9 +111,5 @@       "label": "Part volume",       "prompt": "How much material does each printed part in this assembly use?"     }-  ],-  "author": {-    "name": "John Lauer",-    "email": "[email protected]"-  }-}\ No newline at end of file+  ]+}
server.py+69−4
@@ -4220,6 +4220,61 @@ def _handle_dismiss_blocking_dialogs(fusion_info: dict, args: dict) -> dict: COMMAND_HANDLERS["dismiss_blocking_dialogs"] = _handle_dismiss_blocking_dialogs  +def _capture_3d_view(pkg: str, lbr_path: str, settle: float) -> dict:+    """Capture the DEDICATED 3D-package render as its own artifact (page issue #63).++    The 3D chip previously existed only as a thumbnail inside the ganged component view.+    Getting it standalone meant a separate fusion_show_3d_package call that re-attaches the+    STEP and re-renders - measured at a 180s timeout during the import campaign, and it+    took Fusion down with it under concurrent load. The 3D body is already built and saved+    by the time captures run, so entering the Package3D environment and photographing it is+    cheap: no re-attach, no re-render.+    """+    import time as _t+    entry = {"package": pkg, "view": "3d", "path": None, "ok": False}+    # A PLACEHOLDER IS NOT A RESULT (page issue #42). With no <package3d> bound, Fusion+    # renders a grey slab that photographs exactly like a real chip. Knowable offline.+    if lbr_path:+        try:+            if not _library_artifact_names(lbr_path).get("p3d"):+                entry.update({+                    "errorCode": "no_3d_package_bound",+                    "error": "No <package3d> is bound in that library, so the only thing to "+                             "photograph is Fusion's grey Placeholder - refusing to return "+                             "that as the 3D artifact.",+                    "_hint": "Run fusion_build_library_3d (or fusion_bind_library_3d) first, "+                             "then re-capture. A Placeholder looks like a successful 3D view "+                             "in a screenshot, which is why this refuses instead."})+                return entry+        except Exception:+            pass+    cp = _create_3d_package(lbr_path, timeout=60) if lbr_path else {"success": False,+                                                                    "error": "no libraryPath"}+    if not cp.get("success"):+        entry.update({"errorCode": "create_3d_package_failed",+                      "error": "Could not enter the Package3D environment: %s"+                               % str(cp.get("error"))[:200]})+        return entry+    try:+        if settle:+            _t.sleep(settle)+        entry["path"] = _capture_labeled("%s_3d" % pkg)+        entry["ok"] = bool(entry["path"])+    finally:+        # ALWAYS leave the Package3D environment, even if the capture threw. Staying in it+        # would make every LATER view capture the 3D editor - exactly the mislabelled-frame+        # failure reported in page issue #62.+        try:+            _proxy_to_addin("execute_text_command", {"command": "Package3DStop"}, timeout=20)+            _t.sleep(0.6)+            _d = _dismiss_dialogs_bg()+            if _d:+                print("[Fusion Bridge] Package3DStop raised a dialog (%s) - answered in the "+                      "background; see skills/fusion-no-modal-dialogs" % _d)+        except Exception as _e:+            print("[Fusion Bridge] Package3DStop failed: %s" % _e)+    return entry+ def _orchestrate_capture_library_views(args: dict) -> dict:     """Capture the LIBRARY-EDITOR views that make EEs trust a part: the schematic SYMBOL, the     FOOTPRINT (pads + layer stack), and the COMPONENT/device view (Content Manager: the symbol +@@ -4244,10 +4299,15 @@ def _orchestrate_capture_library_views(args: dict) -> dict:         return {"success": False,                 "error": "packages: [<deviceset name>] (or package: <name>) required.",                 "_hint": "Open the .lbr first (fusion_open_lbr). Names are the deviceset names, e.g. ESP32-S3FN8."}-    view_ext = {"component": "dev", "symbol": "sym", "footprint": "pac",-                "dev": "dev", "sym": "sym", "pac": "pac"}-    label_of = {"dev": "component", "sym": "symbol", "pac": "footprint"}-    views = args.get("views") or ["component", "symbol", "footprint"]+    # FOUR artifacts, in ASSEMBLY ORDER (page issues #60 and #63): symbol, footprint,+    # the dedicated 3D chip, then the ganged component view LAST as the closing proof.+    # The 3D chip used to exist only as a thumbnail inside the ganged view, so a caller+    # could not show it as its own artifact without a separate fusion_show_3d_package call+    # that re-attaches the STEP - slow (180s timeout observed) and crash-prone under load.+    view_ext = {"component": "dev", "symbol": "sym", "footprint": "pac", "3d": "3d",+                "dev": "dev", "sym": "sym", "pac": "pac", "p3d": "3d", "package3d": "3d"}+    label_of = {"dev": "component", "sym": "symbol", "pac": "footprint", "3d": "3d"}+    views = args.get("views") or ["symbol", "footprint", "3d", "component"]     # EDIT is async - the editor takes a beat to actually SWITCH the view. Capturing too soon grabs     # the PREVIOUS view (a mislabeled shot). Settle AFTER the EDIT (before fit/capture); tunable.     settle = float(args.get("settle", 1.5))@@ -4287,6 +4347,8 @@ def _orchestrate_capture_library_views(args: dict) -> dict:         for pkg in pkgs:             for v in views:                 ext = view_ext.get(v, v)+                if ext == "3d":+                    continue          # not an EDIT target; guarded by its own p3d check                 r, err = _resolve_artifact_name(pkg, ext, lbr_path)                 if err:                     name_errors.append({"package": pkg, "view": label_of.get(ext, ext),@@ -4299,6 +4361,9 @@ def _orchestrate_capture_library_views(args: dict) -> dict:     for pkg in pkgs:         for v in views:             ext = view_ext.get(v, v)+            if ext == "3d":+                out.append(_capture_3d_view(pkg, lbr_path, settle))+                continue             if lbr_path and (pkg, ext) not in resolved_names:                 # Would have prompted. Report it as a skip; do NOT hand it to Fusion.                 _bad = next((e for e in name_errors