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.
master
John Lauer
v2.0.709 (insiders): Alt+Shift+A starts Annotate in pup windows (the page catches it; Chrome keeps the key for itself a…
bf97c04
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
(() => {
// v2.0.89: VERSIONED guard. evaluateOnNewDocument scripts from every prior bridge process stay
// registered on long-lived pages and run oldest-first, so a plain boolean guard let the OLDEST
// floaty win forever (John kept seeing the big hover zone after reloads). The newest module now
// tears down any elder and rebuilds.
if (window !== window.top) return;
if (window.__pupFloatyV === '${BRIDGE_VERSION}:__HITPX__:__FLOATY_ON__:__HOTKEY_ON__') return;
window.__pupFloatyV = '${BRIDGE_VERSION}:__HITPX__:__FLOATY_ON__:__HOTKEY_ON__'; window.__pupFloaty = true;
var _oldHost = document.getElementById('__pup_floaty'); if (_oldHost) _oldHost.remove();
function add() {
// v2.0.95 (John: the corner hover zone fights buttons in app headers — off by default now
// that the toolbar pen button covers discovery; the elder-host removal above still runs so
// flipping the setting OFF tears the pencil out of every open page).
if (!'__FLOATY_ON__') return;
if (!document.body) { setTimeout(add, 400); return; }
var host = document.createElement('div');
host.id = '__pup_floaty';
// v2.0.84 (John): TUCKED AWAY by default so it never interferes with page UI — a corner
// hover zone reveals it (the taskbar-tuck pattern), tracked via mousemove (zero hit-testing
// interference). Once dragged (grip, floating-windows skill), it pins visible where placed.
host.style.cssText = 'position:fixed;top:14px;right:14px;z-index:2147483645;pointer-events:auto;display:flex;align-items:center;gap:4px;opacity:0;transform:translateY(-6px);transition:opacity .2s ease,transform .2s ease;';
var root = host.attachShadow({ mode: 'closed' });
var row = document.createElement('div');
row.style.cssText = 'display:flex;align-items:center;gap:4px;';
var grip = document.createElement('div');
grip.setAttribute('aria-label', 'Move the annotate button');
grip.style.cssText = 'width:12px;height:22px;cursor:grab;display:none;grid-template-columns:1fr 1fr;gap:2px;align-content:center;justify-items:center;';
for (var gi = 0; gi < 6; gi++) { var gd = document.createElement('div'); gd.style.cssText = 'width:2.5px;height:2.5px;border-radius:50%;background:#8b949e;'; grip.appendChild(gd); }
var btn = document.createElement('button');
btn.setAttribute('aria-label', 'Annotate this page');
btn.style.cssText = 'width:30px;height:30px;border-radius:8px;display:grid;place-items:center;background:rgba(13,17,23,.72);border:1px solid rgba(0,184,177,.45);cursor:pointer;padding:0;';
// 2.0.704: nodes, never innerHTML (Trusted Types pages such as Gmail refuse HTML strings and the throw kills the script).
var NS = 'http://www.w3.org/2000/svg', svg = document.createElementNS(NS, 'svg');
[['viewBox','0 0 24 24'],['width','15'],['height','15'],['fill','none'],['stroke','#e6edf3'],['stroke-width','1.8'],['stroke-linecap','round'],['stroke-linejoin','round']].forEach(function (a) { svg.setAttribute(a[0], a[1]); });
['M4 20l1.5-4.5L16 5a2.1 2.1 0 0 1 3 3L8.5 18.5 4 20z', 'M13.5 7.5l3 3'].forEach(function (d) { var p = document.createElementNS(NS, 'path'); p.setAttribute('d', d); svg.appendChild(p); });
btn.appendChild(svg);
var tip = document.createElement('div');
var t1 = document.createElement('div'); t1.style.cssText = 'font-weight:700;color:#e6edf3;margin-bottom:4px;font-family:Familjen Grotesk,Satoshi,sans-serif;'; t1.textContent = 'Annotate this page';
var t2 = document.createElement('div'); t2.style.cssText = 'color:#c9d1d9;'; t2.textContent = 'Draw on the page to show your AI exactly what you mean; a red circle says it best. Press Enter to copy the marked-up area to your clipboard, ready to paste into any AI chat.';
var t3 = document.createElement('div'); t3.style.cssText = 'margin-top:7px;color:#8b949e;font-size:11.5px;'; t3.textContent = 'Shortcut\u00a0\u00a0';
var kb = document.createElement('span'); kb.style.cssText = 'font-family:JetBrains Mono,monospace;font-size:10.5px;border:1px solid #30363d;border-radius:4px;padding:1px 6px;color:#c9d1d9;'; kb.textContent = 'Alt\u00a0Shift\u00a0A'; t3.appendChild(kb);
tip.appendChild(t1); tip.appendChild(t2); tip.appendChild(t3);
tip.style.cssText = 'position:fixed;z-index:2147483647;max-width:min(320px,calc(100vw - 40px));background:rgba(22,27,34,.96);color:#e6edf3;border:1px solid rgba(0,184,177,.28);border-radius:10px;padding:9px 13px;font:400 13px/1.4 Satoshi,system-ui,sans-serif;box-shadow:0 12px 40px rgba(0,0,0,.55);backdrop-filter:blur(16px);pointer-events:none;opacity:0;transform:translateY(4px);transition:opacity .18s ease,transform .18s ease;';
row.appendChild(grip); row.appendChild(btn); root.appendChild(row); root.appendChild(tip);
var timer = null, pinned = false, shown = false, hideTimer = null, idleTimer = null, moves = 0;
// v2.0.93 (John: "why is the pen showing in the window you just opened?"): a window that
// opens maximized UNDER a parked cursor gets ONE synthetic hover mousemove from Chromium at
// load; if that lands in the corner zone the pencil revealed and stayed stuck (tuck only
// runs on real movement). A reveal that saw almost no movement is that synthetic case:
// auto-tuck it. Real hover-in produces dozens of mousemoves and is untouched.
function reveal() {
clearTimeout(hideTimer); clearTimeout(idleTimer); moves = 0;
idleTimer = setTimeout(function () { if (!pinned && moves < 3) { shown = false; host.style.opacity = '0'; host.style.transform = 'translateY(-6px)'; grip.style.display = 'none'; tipHide(); } }, 3500);
if (shown) return; shown = true; host.style.opacity = '1'; host.style.transform = 'translateY(0)'; grip.style.display = 'grid';
}
function tuck() { if (pinned) return; hideTimer = setTimeout(function () { shown = false; host.style.opacity = '0'; host.style.transform = 'translateY(-6px)'; grip.style.display = 'none'; tipHide(); }, 1200); }
// corner hover zone via mousemove: NOTHING intercepts page clicks
addEventListener('mousemove', function (e) {
moves++;
if (pinned) return;
var hr = host.getBoundingClientRect();
var inCorner = e.clientX > innerWidth - __HITPX__ && e.clientY < __HITPX__; // v2.0.90: user-tunable corner target (floatyHitPx slider in Settings)
var onHost = shown && e.clientX >= hr.left - 8 && e.clientX <= hr.right + 8 && e.clientY >= hr.top - 8 && e.clientY <= hr.bottom + 8;
if (inCorner || onHost) reveal(); else if (shown) tuck();
}, { passive: true });
function tipShow() { clearTimeout(timer); timer = setTimeout(function () {
var a = btn.getBoundingClientRect(), M = 8, w = tip.offsetWidth, h = tip.offsetHeight;
var x = Math.min(Math.max(M, a.right - w), innerWidth - w - M);
var y = a.bottom + 10; if (y + h > innerHeight - M) y = a.top - h - 10;
y = Math.min(Math.max(M, y), innerHeight - h - M);
tip.style.left = x + 'px'; tip.style.top = y + 'px'; tip.style.opacity = '1'; tip.style.transform = 'translateY(0)';
}, 600); }
function tipHide() { clearTimeout(timer); tip.style.opacity = '0'; tip.style.transform = 'translateY(4px)'; }
btn.onmouseenter = tipShow; btn.onmouseleave = tipHide;
btn.onclick = function () { tipHide(); btn.style.borderColor = '#00d4cb'; if (window.__pupStartAnnotate) { window.__pupStartAnnotate('pen'); } window.__pupFloatyClick = Date.now(); if (typeof window.__pupFloatyPush === 'function') { try { window.__pupFloatyPush('btn'); window.__pupFloatyClick = null; } catch (e) {} } setTimeout(function () { btn.style.borderColor = 'rgba(0,184,177,.45)'; }, 1500); };
// drag by grip (floating-windows skill: visible grip, viewport-clamped); a drag PINS it
(function () {
var drag = null;
grip.addEventListener('pointerdown', function (e) {
var r = host.getBoundingClientRect();
host.style.right = ''; host.style.left = r.left + 'px'; host.style.top = r.top + 'px';
drag = { dx: e.clientX - r.left, dy: e.clientY - r.top };
grip.setPointerCapture(e.pointerId); grip.style.cursor = 'grabbing'; e.preventDefault();
});
grip.addEventListener('pointermove', function (e) {
if (!drag) return;
var w = host.offsetWidth, h = host.offsetHeight, M = 8;
host.style.left = Math.min(Math.max(M, e.clientX - drag.dx), innerWidth - w - M) + 'px';
host.style.top = Math.min(Math.max(M, e.clientY - drag.dy), innerHeight - h - M) + 'px';
});
grip.addEventListener('pointerup', function () { drag = null; pinned = true; grip.style.cursor = 'grab'; });
})();
document.body.appendChild(host);
}
add();
if ('__HOTKEY_ON__') addEventListener('keydown', function (e) {
// v2.0.372 (John): Alt+Shift+A = annotate in Auto; Alt+Shift+M = annotate straight into
// MULTI (compose shapes, auto-advancing colors, capture once on Done). If the overlay is
// already up, the hotkey switches its mode instead of being ignored.
if (e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && (e.key === 'A' || e.key === 'a' || e.key === 'M' || e.key === 'm')) {
e.preventDefault(); e.stopPropagation();
var mm = (e.key === 'M' || e.key === 'm') ? 'multi' : null; // 2.0.707: A opens in the default mode (Multi), like every other entry point
if (window.__pupStartAnnotate) { window.__pupStartAnnotate('pen', mm); }
window.__pupFloatyClick = Date.now(); if (typeof window.__pupFloatyPush === 'function') { try { window.__pupFloatyPush('hotkey'); window.__pupFloatyClick = null; } catch (e) {} }
}
}, true);
})()