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
Publish 1.9.253
3 files changed
+23−14
.release-page-head+1−1@@ -1 +1 @@-f1a484758e69126618c69841ffd97a18e31f8135+184f99300016957818f9aadbd959be9923526f72
dashboard/ui/quick-actions.html+15−3@@ -68,6 +68,10 @@ input[type=text]{font:inherit;color:var(--text);background:var(--surface);border .live-led{width:8px;height:8px;border-radius:50%;background:var(--text-2)}.live-led.on{background:var(--red);box-shadow:0 0 6px var(--red);animation:blink 1s steps(2) infinite} @keyframes blink{50%{opacity:.35}} .live img{display:block;width:100%;height:auto;border-radius:6px;min-height:120px;background:#111}+.chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}+.chip{font:12px var(--mono);color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:999px;padding:5px 11px;cursor:pointer;min-height:32px;transition:border-color .15s ease,transform .06s ease}+.chip:hover{border-color:var(--accent)}.chip:active{transform:translateY(1px)}.chip.active{border-color:var(--accent);color:var(--accent)}+.chip small{color:var(--text-2);margin-left:6px} .gallery{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;margin-top:10px} .shot{background:var(--surface);border:1px solid var(--border);border-radius:8px;overflow:hidden} .shot img{display:block;width:100%;height:auto;background:#000}@@ -132,6 +136,8 @@ input[type=text]{font:inherit;color:var(--text);background:var(--surface);border <div class="card-title">Part to import <span class="card-status" id="partStatus"></span></div> <div class="card-desc">A wiki component page publishes its Fusion files (<code>.lbr</code> plus a <code>.step</code>). Resolve them here; the import below zips them in the browser and stages the zip on this machine in one call.</div> <div class="row"><input type="text" id="partRef" value="ray/dac80504" aria-label="Wiki page reference" placeholder="owner/slug"><button class="btn" id="btnResolve" data-action="resolve">Resolve files</button></div>+ <div class="row" style="align-items:baseline"><span class="mini">or pick one of Ray's recent parts:</span><button class="btn" id="btnShuffle" data-action="shuffle" style="min-height:32px;padding:4px 10px">Shuffle</button></div>+ <div class="chips" id="chips"></div> <div class="kv" id="partKv"></div> </section> @@ -217,8 +223,14 @@ function cmd(command,args,opts){opts=opts||{};if(!bridge){return Promise.reject( function connect(){status($('connStatus'),'probing 127.0.0.1:24680-24689...');$('connNote').textContent='allow local network access if your browser asks';$('led').className='led';return probe().then(function(b){bridge=b;if(!b){status($('connStatus'),'Adom Bridge not detected','bad');$('led').className='led bad';$('conn').textContent='not connected';$('meta').textContent='is Adom Bridge running?';toast('Adom Bridge not found on 24680-24689. Is ab running on this machine?','err');return null}var wc=b.raw.webControl||{};var m=b.raw.machine;var name=(m&&typeof m==='object')?(m.clientName||m.hostname||'this machine'):(m||'this machine');$('led').className='led '+(wc.enabled?'on':'warn');$('conn').textContent=name+' :'+b.port;$('meta').textContent='ab '+(b.raw.version||'?')+' · web control '+(wc.enabled?'on':'OFF')+(wc.grantedForYou?' · this page is granted':(wc.promptExpected?' · ab will ask you per command':''));$('bpAb').textContent=(b.raw.version||'?')+' on '+name;status($('connStatus'),wc.enabled?'connected, web control enabled':'connected, web control is OFF in ab Settings',wc.enabled?'ok':'warn');$('connNote').textContent='';kv($('connKv'),{machine:name,'ab version':b.raw.version||'?',endpoint:'http://127.0.0.1:'+b.port,'web control':wc.enabled?'enabled':'disabled','this page':wc.grantedForYou?'granted (no prompts)':(wc.promptExpected?'ab asks you once per command (Allow once / Allow 1 hr / Deny)':'unknown'),'handshake required':wc.requireHandshake?'yes':'no'});toast('Connected to Adom Bridge on '+name,'ok');$('btnConnect').textContent='Reconnect';return b})} // ---- part files -----------------------------------------------------------------------function resolvePart(){var ref=$('partRef').value.trim().replace(/^\/+|\/+$/g,'');if(!ref)return;status($('partStatus'),'resolving...');part=null;$('btnImport').disabled=true;return fetch('https://wiki.adom.inc/api/v1/pages/'+ref+'/files',{signal:timeout(15000)}).then(function(r){if(!r.ok)throw new Error('wiki answered '+r.status);return r.json()}).then(function(j){var files=j.files||[];var lbr=files.filter(function(f){return /\.lbr$/i.test(f.name)});var steps=files.filter(function(f){return /\.(step|stp)$/i.test(f.name)});if(!lbr.length)throw new Error('this page publishes no .lbr');var base='https://wiki.adom.inc/api/v1/pages/'+ref+'/files/';part={ref:ref,name:ref.split('/').pop(),lbr:{name:lbr[0].name,url:base+lbr[0].name,size:lbr[0].size},steps:steps.map(function(f){return{name:f.name,url:base+f.name,size:f.size}})};kv($('partKv'),{page:'https://wiki.adom.inc/'+ref,library:part.lbr.name+' ('+part.lbr.size+' B)',models:part.steps.map(function(s){return s.name+' ('+s.size+' B)'}).join(', ')||'none (the import will be an unbound library)'});status($('partStatus'),'ready','ok');$('btnImport').disabled=false;$('importNote').textContent=part.steps.length?'one call, then the page polls':'no STEP on this page: nothing to bind';toast('Resolved '+part.lbr.name+(part.steps.length?' + '+part.steps.length+' model(s)':''),'ok')}).catch(function(e){status($('partStatus'),e.message,'bad');toast('Could not resolve the part: '+e.message,'err')})}+function resolvePart(rec){var ref=$('partRef').value.trim().replace(/^\/+|\/+$/g,'');if(!ref)return;rec=rec&&rec.slug?rec:(catalogPages[ref]||null);status($('partStatus'),'resolving...');part=null;$('btnImport').disabled=true;return fetch('https://wiki.adom.inc/api/v1/pages/'+ref+'/files',{signal:timeout(15000)}).then(function(r){if(!r.ok)throw new Error('wiki answered '+r.status);return r.json()}).then(function(j){var files=j.files||[];var lbr=files.filter(function(f){return /\.lbr$/i.test(f.name)});var steps=files.filter(function(f){return /\.(step|stp)$/i.test(f.name)});if(!lbr.length)throw new Error('this page publishes no .lbr');var base='https://wiki.adom.inc/api/v1/pages/'+ref+'/files/';part={ref:ref,name:ref.split('/').pop(),lbr:{name:lbr[0].name,url:base+lbr[0].name,size:lbr[0].size},steps:steps.map(function(f){return{name:f.name,url:base+f.name,size:f.size}})};var lc=(j.files||[]).map(function(f){return (f.lastCommit||{}).timestamp||0}).reduce(function(a,b){return Math.max(a,b)},0);var info={page:'https://wiki.adom.inc/'+ref};if(rec){info.title=(rec.title||'')+(rec.brief?' - '+String(rec.brief).slice(0,90):'');info.version=rec.version||'';info.published=fmtDate(rec.last_commit_at||rec.updated_at)+(rec.created_at?' (page created '+fmtDate(rec.created_at)+')':'')}else if(lc){info['files published']=fmtDate(lc*1000)}info.library=part.lbr.name+' ('+part.lbr.size+' B)';info.models=part.steps.map(function(s){return s.name+' ('+s.size+' B)'}).join(', ')||'none (the import will be an unbound library)';kv($('partKv'),info);status($('partStatus'),'ready','ok');$('btnImport').disabled=false;$('importNote').textContent=part.steps.length?'one call, then the page polls':'no STEP on this page: nothing to bind';toast('Resolved '+part.lbr.name+(part.steps.length?' + '+part.steps.length+' model(s)':''),'ok')}).catch(function(e){status($('partStatus'),e.message,'bad');toast('Could not resolve the part: '+e.message,'err')})} ++// ---- Ray's recent parts: random picks with their publish date --------------------------+var catalogPages={},rayPages=null;+function fmtDate(v){if(!v)return '';var d=new Date(typeof v==='number'?v:String(v).replace(' ','T'));if(isNaN(d))return String(v);return d.toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'})}+function loadRay(){if(rayPages)return Promise.resolve(rayPages);return fetch('https://wiki.adom.inc/api/v1/pages?owner=ray&limit=500',{signal:timeout(15000)}).then(function(r){return r.json()}).then(function(j){rayPages=(j.pages||[]).filter(function(p){return p.kind==='component'});rayPages.forEach(function(p){catalogPages[p.owner+'/'+p.slug]=p});rayPages.sort(function(a,b){return String(b.last_commit_at||'').localeCompare(String(a.last_commit_at||''))});return rayPages})}+function shuffle(){$('chips').innerHTML='<span class="mini">loading Ray\'s parts...</span>';loadRay().then(function(ps){var recent=ps.slice(0,120),pick=[];while(pick.length<6&&recent.length){var i=Math.floor(Math.random()*recent.length);pick.push(recent.splice(i,1)[0])}$('chips').innerHTML='';pick.forEach(function(p){var b=document.createElement('button');b.className='chip';b.setAttribute('data-action','pick');b.setAttribute('data-ref',p.owner+'/'+p.slug);b.innerHTML=esc(p.slug)+'<small>'+esc(fmtDate(p.last_commit_at||p.updated_at).replace(/,? \d{4}/,''))+'</small>';b.title=(p.title||'')+' - '+(p.brief||'');b.onclick=function(){[].forEach.call(document.querySelectorAll('.chip'),function(c){c.className='chip'});b.className='chip active';$('partRef').value=p.owner+'/'+p.slug;resolvePart(p)};$('chips').appendChild(b)});var newest=ps[0];if(newest)$('chips').insertAdjacentHTML('beforeend','<span class="mini" style="align-self:center">'+ps.length+' parts by Ray, newest '+esc(fmtDate(newest.last_commit_at))+'</span>')}).catch(function(e){$('chips').innerHTML='<span class="mini">could not list Ray\'s pages: '+esc(e.message)+'</span>'})} // ---- zip (STORE only) built in the browser ------------------------------------------ var CRC=(function(){var t=[];for(var n=0;n<256;n++){var c=n;for(var k=0;k<8;k++)c=c&1?0xEDB88320^(c>>>1):c>>>1;t[n]=c>>>0}return t})(); function crc32(u8){var c=0xFFFFFFFF;for(var i=0;i<u8.length;i++)c=CRC[(c^u8[i])&0xFF]^(c>>>8);return (c^0xFFFFFFFF)>>>0}@@ -257,14 +269,14 @@ function describe(){status($('catStatus'),'loading...');return cmd('fusion_descr function renderCatalog(){var f=$('catFilter').value.toLowerCase();$('catalog').innerHTML=catalog.filter(function(v){return !f||(v.name+' '+v.desc).toLowerCase().indexOf(f)>=0}).slice(0,200).map(function(v){return '<div><b>'+esc(v.name)+'</b> <span>'+esc(String(v.desc).slice(0,160))+'</span></div>'}).join('')||'<div><span>nothing matches</span></div>'} // ---- wire up ----------------------------------------------------------------------------------$('btnConnect').onclick=connect;$('btnResolve').onclick=resolvePart;$('partRef').addEventListener('keydown',function(e){if(e.key==='Enter')resolvePart()});+$('btnConnect').onclick=connect;$('btnResolve').onclick=function(){resolvePart()};$('btnShuffle').onclick=shuffle;$('partRef').addEventListener('keydown',function(e){if(e.key==='Enter')resolvePart()}); $('btnFusion').onclick=fusionState;$('btnPlan').onclick=plan;$('btnImport').onclick=importBundle;$('btnAttach').onclick=attachRun; $('btnFeed').onclick=function(){feed('status')};$('btnFeedStart').onclick=function(){feed('start')};$('btnFeedStop').onclick=function(){feed('stop')}; $('btnBannerShow').onclick=function(){banner(true)};$('btnBannerHide').onclick=function(){banner(false)}; $('btnDescribe').onclick=describe;$('catFilter').addEventListener('input',renderCatalog); $('btnClear').onclick=function(){$('log').innerHTML='';calls=0;$('logStatus').textContent='0 calls'}; window.QuickActions={connect:connect,resolvePart:resolvePart,importBundle:importBundle,poll:poll,cmd:cmd,state:function(){return{bridge:bridge,part:part,staged:staged,runId:runId,calls:calls}}};-resolvePart();+loadRay().then(function(){resolvePart();shuffle()},function(){resolvePart()}); })(); </script> </body>
page.json+7−10@@ -11,10 +11,6 @@ "uninstall": "./uninstall.sh" }, "tags": [- "ab",- "bridge",- "fusion",- "desktop-app", "fusion360", "autodesk", "cad",@@ -79,10 +75,7 @@ "build a basic parts library", "add a 3d model to a component", "make a fusion 360 library with 3d",- "set up the fusion bridge",- "fusion dashboard",- "fusion status page",- "open the fusion launch page"+ "set up the fusion bridge" ], "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.",@@ -111,5 +104,9 @@ "label": "Part volume", "prompt": "How much material does each printed part in this assembly use?" }- ]-}+ ],+ "author": {+ "name": "John Lauer",+ "email": "[email protected]"+ }+}