app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
← Commit history
v2.0.721 (insiders): The dashboard stops blinking: each live update now patches only what changed, so window and taskbar icons are no longer recreated and reloaded every 2 seconds (measured on ConfRoomROG: 0 of 30 icons recreated across 10 updates, was all 30 every update). Includes 2.0.720. Gates on arav-rog and ConfRoomROG: smoke all pass, parity 55/56, jump list 11/14 (baseline); foreground pass on arav-rog, and on ConfRoomROG 1 of 27 samples caught a blank window on top during the cold open, then two reruns passed (an intermittent cold-start flash, not this change).
3 files changed
+37−18
rust/Cargo.lock+12−12@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-pup-bridge"-version = "2.0.720"+version = "2.0.721" dependencies = [ "pup-browser", "pup-cdp",@@ -781,7 +781,7 @@ dependencies = [ [[package]] name = "pup-browser"-version = "2.0.720"+version = "2.0.721" dependencies = [ "pup-core", "pup-host",@@ -793,7 +793,7 @@ dependencies = [ [[package]] name = "pup-cdp"-version = "2.0.720"+version = "2.0.721" dependencies = [ "serde_json", "thiserror",@@ -803,7 +803,7 @@ dependencies = [ [[package]] name = "pup-core"-version = "2.0.720"+version = "2.0.721" dependencies = [ "serde", "serde_json",@@ -813,7 +813,7 @@ dependencies = [ [[package]] name = "pup-dashboard"-version = "2.0.720"+version = "2.0.721" dependencies = [ "pup-cdp", "pup-core",@@ -826,7 +826,7 @@ dependencies = [ [[package]] name = "pup-host"-version = "2.0.720"+version = "2.0.721" dependencies = [ "serde", "serde_json",@@ -835,7 +835,7 @@ dependencies = [ [[package]] name = "pup-host-mac"-version = "2.0.720"+version = "2.0.721" dependencies = [ "core-foundation", "core-graphics",@@ -851,7 +851,7 @@ dependencies = [ [[package]] name = "pup-host-win"-version = "2.0.720"+version = "2.0.721" dependencies = [ "pup-core", "pup-host",@@ -861,7 +861,7 @@ dependencies = [ [[package]] name = "pup-identity"-version = "2.0.720"+version = "2.0.721" dependencies = [ "fontdue", "image",@@ -871,7 +871,7 @@ dependencies = [ [[package]] name = "pup-session"-version = "2.0.720"+version = "2.0.721" dependencies = [ "pup-core", "serde",@@ -881,7 +881,7 @@ dependencies = [ [[package]] name = "pup-vault"-version = "2.0.720"+version = "2.0.721" dependencies = [ "pup-core", "pup-host",@@ -892,7 +892,7 @@ dependencies = [ [[package]] name = "pup-verbs"-version = "2.0.720"+version = "2.0.721" dependencies = [ "base64", "png 0.17.16",
rust/Cargo.toml+1−1@@ -4,7 +4,7 @@ members = ["core", "host", "host-win", "host-mac", "cdp", "browser", "session", # One version for the whole bridge, in lockstep with src/BRIDGE_VERSION (tools/ship.sh bumps both). [workspace.package]-version = "2.0.720"+version = "2.0.721" edition = "2021" license = "MIT"
rust/dashboard/assets/dashboard.html+24−5@@ -371,6 +371,25 @@ function favTags(f){ return '<span class="fttray" title="'+esc(tip)+'">'+bits.join('')+'</span>'; } +// 2.0.721 (John on CRR: "i keep seeing it blink like every second"): every snapshot used to replace these lists+// with innerHTML, so every icon <img> was recreated and reloaded every 2 s. morph() patches the live DOM against+// the new markup instead: unchanged nodes and images stay as they are, only changed text and attributes move.+function morph(el,html){const t=document.createElement(el.tagName);t.innerHTML=html;morphKids(el,t);}+function morphKids(a,b){+ const bk=Array.from(b.childNodes);+ bk.forEach((bn,i)=>{const an=a.childNodes[i];+ if(!an){a.appendChild(bn);return;}+ if(an.nodeType!==bn.nodeType||an.nodeName!==bn.nodeName){a.replaceChild(bn,an);return;}+ if(an.nodeType!==1){if(an.nodeValue!==bn.nodeValue)an.nodeValue=bn.nodeValue;return;}+ const img=an.tagName==='IMG', srcChanged=img&&an.getAttribute('src')!==bn.getAttribute('src');+ // a broken icon hidden by the error handler keeps its style until its src changes+ for(const at of Array.from(an.attributes)) if(!bn.hasAttribute(at.name)&&!(img&&at.name==='style'&&!srcChanged)) an.removeAttribute(at.name);+ for(const at of Array.from(bn.attributes)) if(an.getAttribute(at.name)!==at.value) an.setAttribute(at.name,at.value);+ morphKids(an,bn);+ });+ while(a.childNodes.length>bk.length) a.removeChild(a.lastChild);+}+ function render(){ if(!SNAP)return; const d=SNAP; if(d.bridge&&d.bridge.version&&d.bridge.version!==PAGE_VERSION){location.reload();return;}@@ -401,7 +420,7 @@ function render(){ // from his actual taskbar), and NO overlay at all when pup has not painted one (a bare Chrome // button must render bare here too, otherwise the mimic lies). const ghosts=(d.untrackedWindows||[]).map(h=>'<div class="tbi ghost" title="a window of the pup-managed Chrome is on the real taskbar but pup has NO window record for it (hwnd '+h+'). The mimic cannot know its icon - this is why the header LED is red.">?<span class="led red"></span></div>').join('');- _q('#taskbar').innerHTML='<span class="lbl" title="mimics ONLY the taskbar buttons of pup-managed Chrome windows. Other Adom apps (Adom Hydrogen, Adom Bridge) have their own teal-tile buttons that are out of pup scope.">TASKBAR (pup)</span>'+groups.map((g,i)=>{+ morph(_q('#taskbar'),'<span class="lbl" title="mimics ONLY the taskbar buttons of pup-managed Chrome windows. Other Adom apps (Adom Hydrogen, Adom Bridge) have their own teal-tile buttons that are out of pup scope.">TASKBAR (pup)</span>'+groups.map((g,i)=>{ const fg=g.wins.some(w=>w.foreground); return '<div class="tbi'+(fg?' fg':'')+'" data-jump="g'+i+'" title="'+esc(g.label)+': '+esc(g.ledWhy||'')+'">' +'<img class="base" src="'+esc(g.baseIcon||'')+'" data-fallback="hide">'@@ -409,10 +428,10 @@ function render(){ +'<span class="led '+esc(g.led||'red')+'" title="'+esc(g.ledWhy||'')+'"></span>' +(g.wins.length>1?'<span class="n">'+g.wins.length+'</span>':'') +(fg?'<span class="u"></span>':'')+'</div>';- }).join('')+ghosts;+ }).join('')+ghosts); // tree: taskbar icon -> windows -> tabs- _q('#m').innerHTML=groups.length?groups.map((g,gi)=>{+ morph(_q('#m'),groups.length?groups.map((g,gi)=>{ const gid='g'+gi, gcoll=COLL.has(gid); return '<div class="tbnode" id="'+gid+'">' +'<div class="row"><span class="tog" data-toggle="'+gid+'">'+(gcoll?'▸':'▾')+'</span>'@@ -461,7 +480,7 @@ function render(){ +'<button class="btn xs danger" data-act="close-tab" data-sid="'+esc(w.sessionId)+'" data-tid="'+esc(t.tabId)+'">close</button></span></div>' ).join('')+'</div></div></div>'; }).join('')+'</div></div>';- }).join(''):'<div class="empty">no pup windows open'+(th||q?' matching this filter':'')+'</div>';+ }).join(''):'<div class="empty">no pup windows open'+(th||q?' matching this filter':'')+'</div>'); _q('#ft').textContent='bridge up '+ago(d.bridge.uptimeMs)+' \u00b7 '+d.bridge.platform+' \u00b7 deps '+(d.bridge.depsOk?'ok':'MISSING')+' \u00b7 win32 '+(d.bridge.win32InProcess?'in-process':'powershell fallback'); // v2.0.315 LOOP RADAR (John): the periodic loops and in-process Win32 calls no longer show in ab's // activity log, so they are shown HERE, per minute, with the declared cadence next to each so a@@ -470,7 +489,7 @@ function render(){ const lp=d.bridge.loops||{}; const w=d.bridge.win32Calls||{}; const rows=Object.entries(lp).map(([n,v])=>{const exp=Math.max(1,Math.ceil(60000/v.cadenceMs));const hot=v.lastMin>exp*1.5;return '<span class="lp'+(hot?' hot':'')+'" title="declared cadence '+v.cadenceMs+'ms = '+exp+'/min; avg '+v.avgMs+'ms, max '+v.maxMs+'ms, '+v.ticks+' ticks total, '+v.slowTicks+' slow">'+n+' <b>'+v.lastMin+'</b>/'+exp+'</span>';}).join(''); const wc=Object.entries(w).map(([n,v])=>n+'='+v).join(' ');- _q('#loops').innerHTML='<div class="lpt">loops, ticks in the last minute (actual/declared)</div>'+rows+'<div class="lpt">in-process win32 calls since boot: '+(wc||'none')+' \u00b7 ab enum_windows last minute: '+(d.bridge.abEnumLastMin||0)+'</div>';+ morph(_q('#loops'),'<div class="lpt">loops, ticks in the last minute (actual/declared)</div>'+rows+'<div class="lpt">in-process win32 calls since boot: '+(wc||'none')+' \u00b7 ab enum_windows last minute: '+(d.bridge.abEnumLastMin||0)+'</div>'); }catch(e){} } function toast(msg,type){