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
(() => {
  // 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);
})()