← Commit history

Publish 1.9.176

John Lauer ·f02d0f14bd ·1mo ago ·parent af9b17c
2 files changed +106−35
dashboard/ui/index.html+99−25
@@ -83,7 +83,9 @@ input:focus-visible{outline:none;border-color:var(--accent) !important} .toast{background:var(--overlay);border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:8px;padding:10px 14px;color:var(--text);font-size:14px;box-shadow:0 8px 24px rgba(0,0,0,.4)} .toast.ok{border-left-color:var(--green)}.toast.err{border-left-color:var(--red)} dialog{margin:auto;background:var(--overlay);color:var(--text-body);border:1px solid var(--border);border-radius:12px;padding:20px;max-width:520px;width:92vw}-dialog::backdrop{background:rgba(0,0,0,.55)}+/* Backdrop law (settings-layout): dim 20-30%, NEVER blur - settings often change the very+   thing behind the modal (theme, density, the live LEDs), so the app must stay legible. */+dialog::backdrop{background:rgba(0,0,0,.25)} /* The dialog is focused programmatically on open (so it scrolls to the top instead of jumping    to the first input). Chrome then paints its DEFAULT focus ring in currentColor - a light    2px halo that reads as a white border around the modal (John spotted it, 2026-08-18). The@@ -91,13 +93,39 @@ dialog::backdrop{background:rgba(0,0,0,.55)}    rings for keyboard users. */ dialog:focus,dialog:focus-visible{outline:none} .setrow{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:12px 0;border-bottom:1px solid var(--border)}-.toggle{width:42px;height:24px;border-radius:999px;background:var(--elevated);border:1px solid var(--border);position:relative;cursor:pointer;flex:none;transition:border-color .15s ease}+/* House toggle (settings-layout): 32x18 track, WHITE thumb in BOTH states, teal-on/neutral-off,+   knob stretches under the press. Never a native checkbox (its blue is not an Adom colour). */+.toggle{width:32px;height:18px;border-radius:999px;background:var(--overlay);border:1px solid var(--border);position:relative;cursor:pointer;flex:none;transition:background .15s ease,border-color .15s ease} .toggle:hover{border-color:var(--accent)}-.toggle:active::after{width:22px}-.toggle.is-on:active::after{left:17px}-.toggle::after{content:'';position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:999px;background:var(--text-2);transition:all .15s ease}+.toggle:active::after{width:16px}+.toggle.is-on:active::after{left:10px}+.toggle::after{content:'';position:absolute;top:2px;left:2px;width:12px;height:12px;border-radius:999px;background:#fff;transition:transform .18s ease,width .12s ease,left .18s ease} .toggle.is-on{background:var(--accent);border-color:var(--accent)}-.toggle.is-on::after{left:21px;background:#05221f}+.toggle.is-on::after{left:16px;background:#fff}+/* Row default hint + breadcrumb-teal last part of a row name (settings-layout) */+.default-hint{font-size:11px;color:var(--text-2);margin-top:2px}+.ctlrow .lbl b,.ctlrow .lbl .last{color:#8fd3d4;font-weight:600}+/* Choice cards: mutually-exclusive VISUAL alternatives, one real screenshot each */+.choice-set{border:none;margin:.2rem 0 0;padding:0;display:flex;flex-wrap:wrap;gap:.6rem}+.choice{flex:1 1 150px;display:flex;flex-direction:column;gap:.3rem;padding:.55rem;cursor:pointer;background:var(--surface);border:1px solid var(--border);border-radius:8px;transition:border-color .15s ease,background .15s ease,transform .06s ease}+.choice input{position:absolute;opacity:0;pointer-events:none}+.choice .shot{display:block;width:100%;height:auto;border-radius:4px;border:1px solid var(--border);background:var(--elevated)}+.choice .shot-pending{display:flex;align-items:center;justify-content:center;height:52px;border-radius:4px;border:1px dashed var(--border);background:var(--elevated);color:var(--text-2);font-size:10.5px;text-align:center;padding:0 6px}+.choice .cname{font-family:'Familjen Grotesk',sans-serif;font-size:12.5px;font-weight:600;color:var(--text)}+.choice .cdesc{font-size:11px;color:var(--text-2);line-height:1.45}+.choice:hover{border-color:var(--accent);background:var(--elevated)}+.choice:active{transform:translateY(1px) scale(.995)}+.choice.sel{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent)}+.choice:focus-within{outline:2px solid var(--accent);outline-offset:2px}+/* House scrollbar for the settings body (never the fat OS bar) */+.dlgbody::-webkit-scrollbar{width:9px}+.dlgbody::-webkit-scrollbar-thumb{background:rgba(139,148,158,.35);border-radius:999px;border:2px solid transparent;background-clip:content-box}+.dlgbody::-webkit-scrollbar-thumb:hover{background:var(--accent);background-clip:content-box}+/* Two-click Restore defaults footer */+.settings-footer{margin-top:1.2rem;padding:1rem 18px 16px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:.5rem}+.restore-btn{width:fit-content;display:inline-flex;align-items:center;gap:.4rem;padding:.4rem .85rem;background:transparent;border:1px solid var(--border);border-radius:.375rem;color:var(--text-body);font-family:'Familjen Grotesk',sans-serif;font-size:12.5px;font-weight:600;cursor:pointer;transition:background .15s ease,border-color .15s ease,color .15s ease}+.restore-btn:hover{background:var(--elevated)}+.restore-btn.armed{color:var(--red);border-color:rgba(248,81,73,.5);background:rgba(248,81,73,.08)} @media (max-width:720px){.meta{display:none}.grid{grid-template-columns:1fr 1fr}} /* Touch targets: the responsive-ui contract is 44x44 minimum on touch. Applied for coarse    pointers AND at phone width - pointer:coarse cannot be emulated in the ralph harness, so a@@ -233,6 +261,7 @@ dialog.settings{max-width:560px;overflow:hidden;padding:0}     <div class="divider"></div>     <div class="ctlrow"><span class="lbl">Get everything ready when I open the Dashboard</span>       <div class="toggle" id="tglAutoGreen" role="switch" aria-checked="true"></div></div>+    <div class="default-hint">Default: On</div>   </div>    <div class="settings-card">@@ -241,9 +270,11 @@ dialog.settings{max-width:560px;overflow:hidden;padding:0}     <div class="divider"></div>     <div class="ctlrow"><span class="lbl">Silently install Fusion updates</span>       <div class="toggle" id="tglAutoUpdate" role="switch" aria-checked="true"></div></div>+    <div class="default-hint">Default: On</div>     <div class="divider"></div>     <div class="ctlrow"><span class="lbl">Take over the Autodesk seat when it is busy elsewhere</span>       <div class="toggle" id="tglSeatTakeover" role="switch" aria-checked="true"></div></div>+    <div class="default-hint">Default: On</div>   </div>    <div class="settings-card">@@ -252,19 +283,32 @@ dialog.settings{max-width:560px;overflow:hidden;padding:0}     <div class="divider"></div>     <div class="ctlrow"><span class="lbl">Show the "Adom is driving" badge on Fusion's taskbar button</span>       <div class="toggle" id="tglBadge" role="switch" aria-checked="true"></div></div>+    <div class="default-hint">Default: On</div>   </div>    <div class="settings-card">     <div class="card-title">Activity indicator <span class="card-status" id="stActivity"></span></div>     <div class="card-desc">How Fusion's taskbar button signals that Adom is running a verb. <b>Progress bar</b> paints a real bar paced by how long <i>this machine</i> has actually taken for that verb, and clears the moment it finishes or fails - so you can tell "nearly done" from "just started" at a glance. <b>Flash</b> is the older attention flash: it says something is happening, and nothing more. A verb with no measured history yet runs the bar indeterminate rather than inventing a percentage. Run times are recorded per verb to <code>verb-times.jsonl</code>; <code>fusion_verb_times</code> reports p50/p90 per verb.</div>     <div class="divider"></div>-    <div class="ctlrow"><span class="lbl">Indicator</span>-      <div class="segmented" id="segActivity" role="radiogroup" aria-label="Activity indicator">-        <button class="seg" data-mode="progress" role="radio">Progress bar</button>-        <button class="seg" data-mode="flash" role="radio">Flash</button>-        <button class="seg" data-mode="both" role="radio">Both</button>-        <button class="seg" data-mode="none" role="radio">None</button>-      </div></div>+    <fieldset class="choice-set" id="segActivity" role="radiogroup" aria-label="Activity indicator">+      <label class="choice" data-mode="progress"><input type="radio" name="activity" value="progress">+        <span class="shot-pending">screenshot pending<br>real capture of the taskbar bar</span>+        <span class="cname">Progress bar</span>+        <span class="cdesc">Fills over the verb's usual duration, so you can tell "nearly done" from "just started".</span></label>+      <label class="choice" data-mode="flash"><input type="radio" name="activity" value="flash">+        <span class="shot-pending">screenshot pending<br>real capture of the orange flash</span>+        <span class="cname">Orange flash</span>+        <span class="cdesc">Highlights the button until you look. Says something happened, not how long is left.</span></label>+      <label class="choice" data-mode="both"><input type="radio" name="activity" value="both">+        <span class="shot-pending">screenshot pending</span>+        <span class="cname">Both</span>+        <span class="cdesc">The bar plus the flash - the attention grab and the progress at once.</span></label>+      <label class="choice" data-mode="none"><input type="radio" name="activity" value="none">+        <span class="shot-pending">screenshot pending<br>the plain button, no highlight</span>+        <span class="cname">Off</span>+        <span class="cdesc">The taskbar button never changes while a verb runs.</span></label>+    </fieldset>+    <div class="default-hint">Default: Progress bar</div>   </div>    <div class="settings-card">@@ -273,11 +317,15 @@ dialog.settings{max-width:560px;overflow:hidden;padding:0}     <div class="divider"></div>     <div class="ctlrow"><span class="lbl">Wiki part page</span>       <input id="inpPart" style="flex:0 1 260px;background:var(--surface);border:1px solid var(--border);border-radius:8px;color:var(--text);padding:7px 10px;font:12.5px 'JetBrains Mono',monospace"></div>+    <div class="default-hint">Default: aravk/in-s63btg</div>   </div>    </div><!-- /dlgbody -->-  <div class="setfoot" style="padding:0 18px 14px">Settings are stored on this machine: the Dashboard's own choices in <code>~/.adom/fusion-dashboard/settings.json</code>, the Fusion upkeep and badge choices as sticky bridge settings on the target box.</div>-  <div class="btnrow" style="justify-content:flex-end;padding:0 18px 16px;margin-top:0"><button class="btn" id="btnCloseSettings" data-tip="Close settings - Escape and clicking outside do the same">Close</button></div>+  <div class="settings-footer">+    <button class="restore-btn" id="btnRestoreDefaults" type="button" data-tip="Reset every setting on this page to its default">Restore defaults</button>+    <div class="setfoot">Settings are stored on this machine: the Dashboard's own choices in <code>~/.adom/fusion-dashboard/settings.json</code>, the Fusion upkeep and badge choices as sticky bridge settings on the target box.</div>+    <div class="btnrow" style="justify-content:flex-end;margin-top:.2rem"><button class="btn" id="btnCloseSettings" data-tip="Close settings - Escape and clicking outside do the same">Close</button></div>+  </div> </dialog>  <dialog id="dlgSetup">@@ -405,20 +453,24 @@ $('#btnSettings').onclick=async()=>{   $('#stBadge').textContent=bd?'badge on while driving':'badge off';   $('#stBadge').className='card-status'+(bd?' ok':'');   const am=(s.activityIndicator||'progress');-  document.querySelectorAll('#segActivity .seg').forEach(b=>{-    b.classList.toggle('on', b.getAttribute('data-mode')===am);-    b.setAttribute('aria-checked', b.getAttribute('data-mode')===am ? 'true':'false');-    b.onclick=async()=>{-      const mode=b.getAttribute('data-mode');-      document.querySelectorAll('#segActivity .seg').forEach(x=>{-        x.classList.toggle('on', x===b); x.setAttribute('aria-checked', x===b?'true':'false');});-      $('#stActivity').textContent=mode==='none'?'no taskbar signal':(mode+' while a verb runs');+  const paintActivity=mode=>{+    document.querySelectorAll('#segActivity .choice').forEach(c=>{+      const on=c.getAttribute('data-mode')===mode;+      c.classList.toggle('sel',on);+      const r=c.querySelector('input');if(r)r.checked=on;+    });+    $('#stActivity').textContent=mode==='none'?'no taskbar signal':(mode+' while a verb runs');+    $('#stActivity').className='card-status'+(mode!=='none'?' ok':'');+  };+  document.querySelectorAll('#segActivity .choice').forEach(c=>{+    c.onclick=async()=>{+      const mode=c.getAttribute('data-mode');+      paintActivity(mode);       try{ await act('set_activity_indicator',{mode}); toast('activity indicator: '+mode); }       catch(e){ toast('could not set indicator: '+e,'err'); }     };   });-  $('#stActivity').textContent=am==='none'?'no taskbar signal':(am+' while a verb runs');-  $('#stActivity').className='card-status'+(am!=='none'?' ok':'');+  paintActivity(am);   $('#inpPart').value=dashSettings.demoPart||'aravk/in-s63btg';   $('#stPart').textContent=(dashSettings.demoPart||'').split('/').pop().toUpperCase();   const dlg=$('#dlgSettings');@@ -431,6 +483,28 @@ $('#btnSettings').onclick=async()=>{ }; $('#btnCloseSettings').onclick=()=>$('#dlgSettings').close(); $('#btnXSettings').onclick=()=>$('#dlgSettings').close();+// Two-click, auto-disarming Restore defaults (settings-layout): a destructive reset never fires on+// a single click. First click arms (red, "Confirm reset"), second click restores, and it disarms+// itself after 4s so a stray earlier click can't lie in wait.+(()=>{+  const btn=$('#btnRestoreDefaults');let armed=false,t=null;+  const disarm=()=>{armed=false;btn.classList.remove('armed');btn.textContent='Restore defaults';if(t){clearTimeout(t);t=null;}};+  btn.onclick=async()=>{+    if(!armed){armed=true;btn.classList.add('armed');btn.textContent='Confirm reset';t=setTimeout(disarm,4000);return;}+    disarm();+    const DEF={autoGreenOnLaunch:true,autoUpdateFusion:true,autoTakeOverSeat:true,showDrivingBadge:true,activityIndicator:'progress',demoPart:'aravk/in-s63btg'};+    try{+      await saveDashSettings({autoGreenOnLaunch:DEF.autoGreenOnLaunch,demoPart:DEF.demoPart});+      await act('set_auto_update',{enabled:DEF.autoUpdateFusion});+      await act('set_seat_takeover',{enabled:DEF.autoTakeOverSeat});+      await act('set_driving_badge',{enabled:DEF.showDrivingBadge});+      await act('set_activity_indicator',{mode:DEF.activityIndicator});+      toast('Settings restored to defaults','ok');+      renderSurfaces();+      $('#btnSettings').onclick();  // repaint the panel from the restored state+    }catch(e){ toast('could not restore defaults: '+e,'err'); }+  };+})(); // Click-OUTSIDE closes. A <dialog>'s backdrop is part of the dialog element, so a click that // lands on the dialog itself (not on its children) is a backdrop click - but only trust it when // the pointer is actually outside the dialog's box, or a click on the dialog's own padding
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]"+  }+}\ No newline at end of file