← Commit history

import_step board form routing; tool_update check budget

Colby Knox ·8adc956b09 ·12d ago ·parent 655610f
3 files changed +23−5
CHANGELOG.md+10
@@ -3,6 +3,16 @@ All notable changes to the Adom Bridge Fusion 360 bridge, newest first. Canonical: https://wiki.adom.inc/adom/fusion-bridge ยท GitHub backup: `adom-inc/fusion-bridge` +## 1.13.33 (2026-10-30)++- `fusion_import_step` reaches the exe's board form: 1.13.32 still routed it to the add-in's import, which opened the+  STEP as a new design (seen live). `into:'new'` still does that on purpose.+- `fusion_tool_update {action:'check'}` stops its update-button search at 40 s (`complete:false` then) so it answers+  within the relay's budget, and reads Fusion's version from app.version.+- Checked live on 1.13.32: fusion_panel lists Fusion's 14 panels and opens one, verified EXPANDED;+  fusion_export_3d wrote the board as STL (18.8 MB) and refused vrml by name; fusion_edit_pours set a pour's thermals+  and rank, read them back, and set them back to the board's starting revision.+ ## 1.13.32 (2026-10-30)  More of the standard answered exactly (eda-bridge-standard#29, "we need the verbs"):
rust/bridge/src/main.rs+2−1
@@ -535,7 +535,8 @@ fn main() {                             "data": {"disabled": true, "use": "fusion_aps_search", "apsConfigured": configured, "apsSignedIn": signed},                             "_hint": next})                     },-                    v if addin_owner_is_exe(&**h) && addin::owns(v.strip_prefix("fusion_").unwrap_or(v)) => {+                    // import_step is the exe's (it places the STEP with the board; into:"new" passes to the add-in)+                    v if addin_owner_is_exe(&**h) && addin::owns(v.strip_prefix("fusion_").unwrap_or(v)) && !matches!(v, "fusion_import_step" | "import_step") => {                         let cmd = v.strip_prefix("fusion_").unwrap_or(v).to_string();                         let (sc, ca) = (sidecar.clone(), caller.clone());                         let sd = h.bridge_state_dir();
rust/bridge/src/maintenance.rs+11−4
@@ -23,7 +23,12 @@ fn find_controls_any(v: &Value) -> Value { }  /// `_find_update_offer`: is Fusion offering an update (a button on any family window)?-pub fn find_update_offer(ctx: &Ctx) -> Value {+pub fn find_update_offer(ctx: &Ctx) -> Value { find_update_offer_within(ctx, None) }++/// The same search, stopped once `budget` has passed (`complete:false` then): every label on every+/// family window is a UI Automation search, which can outlast a relay's budget.+pub fn find_update_offer_within(ctx: &Ctx, budget: Option<Duration>) -> Value {+    let t0 = std::time::Instant::now();     let Some(ab) = Loopback::from_env() else { return json!({"offered": false}) };     let facts = fusion_core::windows_facts::facts_from_ab(&ab, &ctx.caller);     let Some(hw) = facts.main_hwnd else { return json!({"offered": false}) };@@ -31,6 +36,7 @@ pub fn find_update_offer(ctx: &Ctx) -> Value {     for w in &facts.family { if let Some(h) = w["hwnd"].as_i64() { if h != hw { candidates.push(h); } } }     for h in candidates {         for label in UPDATE_BUTTONS {+            if budget.map(|b| t0.elapsed() > b).unwrap_or(false) { return json!({"offered": false, "hwnd": hw, "complete": false}); }             let r = ab.call("desktop_find_control", json!({"hwnd": h, "name": label}), &ctx.caller, 20).map(|v| find_controls_any(&v)).unwrap_or(json!({}));             let best = &r["best"];             if best["name"].as_str().map(|n| !n.is_empty()).unwrap_or(false) && best["invokable"].as_bool().unwrap_or(false) {@@ -192,12 +198,13 @@ pub fn prefs_close(ctx: &Ctx, args: &Value) -> Value { /// while Fusion is busy), auto reads or sets automatic updating. pub fn tool_update(ctx: &Ctx, args: &Value, pass: &dyn Fn(&str, Value) -> Value) -> Value {     let auto = crate::small_verbs::get_pref(&ctx.state_dir, "autoUpdateFusion").map(|v| v != json!(false)).unwrap_or(true);-    let current = pass("get_app_state", json!({}))["data"]["version"].clone();+    let v = pass("run_modeling_script", json!({"script": "result = {'version': app.version}"}));+    let current = v["data"]["result"]["version"].clone();     match args["action"].as_str().unwrap_or("check") {         "check" => {-            let offer = find_update_offer(ctx);+            let offer = find_update_offer_within(ctx, Some(Duration::from_secs(40)));             let offered = offer["offered"].as_bool() == Some(true);-            json!({"success": true, "mutated": false, "action": "check", "current": current, "offered": if offered { offer["button"].clone() } else { Value::Null },+            json!({"success": true, "mutated": false, "action": "check", "current": current, "offered": if offered { offer["button"].clone() } else { Value::Null }, "complete": offer["complete"].as_bool().unwrap_or(true),                    "updateOffered": offered, "auto": auto, "applied": false, "needsRestart": false,                    "_hint": if offered { "Fusion offers an update: tool_update {action:'apply'} takes it in the background (deferred while Fusion is busy)." } else { "No update offered: Fusion is current, or Autodesk has not offered one in this session yet." }})         }