memoscan
← all memos

Memo 0x16854470…fa71f3 on Ethereum

drag.y = drag.y0 = e.clientY; drag.vx = drag.vy = 0; cv.style.cursor = 'grabbing'; }); window.addEventListener('mousemove', e => { if (!drag.on) { cv.style.cursor = hitLabel(e.clientX, e.clientY) >= 0 ? 'pointer' : 'grab'; return; } dragBy(e.clientX - drag.x, e.clientY - drag.y); drag.x = e.clientX; drag.y = e.clientY; }); window.addEventListener('mouseup', e => { drag.on = false; cv.style.cursor = 'grab'; if (Math.hypot(e.clientX - drag.x0, e.clientY - drag.y0) > 5) { view.tYaw += drag.vx * 12; // a real drag: the throw carries view.tPitch = clampPitch(view.tPitch + drag.vy * 12); } else if (G && e.target === cv) { // a click: jump to that node's network let hit = hitLabel(e.clientX, e.clientY); // a name counts as its node if (hit < 0) hit = hitTest(e.clientX, e.clientY); if (hit >= 0) select(hit); else clearSel(); } }); window.addEventListener('mouseleave', () => { drag.on = false; cv.style.cursor = 'grab'; }); // zoom: wheel dollies exponentially (trackpad pinch arrives as ctrl+wheel), // two-finger pinch on touch, double-click resets — the orbit-view conventions function zoomBy(f) { view.tZoom = Math.min(10, Math.max(0.4, view.tZoom * f)); view.lastInput = performance.now(); } window.addEventListener('wheel', e => { e.preventDefault(); zoomBy(Math.exp(-e.deltaY * (e.ctrlKey ? 0.01 : 0.0015))); }, { passive: false }); window.addEventListener('dblclick', () => { view.tZoom = 1; view.lastInput = performance.now(); }); let pinchD = 0; window.addEventListener('touchstart', e => { if (e.touches.length === 2) pinchD = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY); }); window.addEventListener('touchstart', e => { if (e.touches.length === 1) { drag.x = drag.x0 = e.touches[0].clientX; drag.y = drag.y0 = e.touches[0].clientY; } }); window.addEventListener('touchmove', e => { e.preventDefault(); if (e.touches.length === 2) { const d = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY); if (pinchD) zoomBy(d / pinchD); pinchD = d; } else { const t = e.touches[0]; dragBy(t.clientX - drag.x, t.clientY - drag.y); drag.x = t.clientX; drag.y = t.clientY; } }, { passive: false }); window.addEventListener('touchend', e => { const wasPinch = pinchD !== 0; pinchD = 0; if (wasPinch || !G || !e.changedTouches.length) return; const t = e.changedTouches[0]; if (Math.hypot(t.clientX - drag.x0, t.clientY - drag.y0) <= 8 && e.target === cv) { let hit = hitLabel(t.clientX, t.clientY); // a tap on a name counts as its node if (hit < 0) hit = hitTest(t.clientX, t.clientY); if (hit >= 0) select(hit); else clearSel(); } }); window.addEventListener('keydown', e => { const el = document.activeElement; if (el && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || el.isContentEditable)) return; const k = e.key.toLowerCase(); if (k === 'a') toggleArtists(); else if (k === 'p') togglePatrons(); else if (k === 'escape') clearSel(); }); let rT = 0; window.addEventListener('resize', () => { clearTimeout(rT); rT = setTimeout(() => { if (G) { tiers(); sizeCanvas(); view.dirty = true; } }, 100); }); // ---------------- boot ---------------- async function boot() { sizeCanvas(); rafId = requestAnimationFrame(tick); status('reading the chain…'); try { await loadChain(); st.progress = ''; if (G) stats(); } catch (e) { st.progress = ''; if (G) stats(); else { status('the chain is unreachable'); ctx.fillStyle = '#000'; ctx.fillRect(window.innerWidth / 2 - 1, window.innerHeight / 2 - 1, 2, 2); rpcPrompt(); } } booted = true; // from here on, new threads flash as they land setTimeout(poll, 60000); } // last resort: let the viewer bring their own endpoint. heals in memory and // in place — on-chain contexts (data: URIs, sandboxed iframes) have no // storage and no query string, so persistence is a bonus, never a dependency function rpcPrompt() { if (document.getElementById('rp')) return; const inp = document.createElement('input'); inp.id = 'rp'; inp.placeholder = 'paste a mainnet rpc url and press enter…'; inp.style.cssText = 'position:fixed;left:12px;bottom:28px;width:300px;' + 'font:10px monospace;color:#000;background:#fff;border:1px solid #000;' + 'padding:3px 5px;outline:none'; inp.addEventListener('keydown', async e => { if (e.key !== 'Enter') return; const v = inp.value.trim(); if (!/^https?:\/\//.test(v)) return; try { localStorage.setItem(RPC_KEY, v); } catch {} // remembered where origins allow RPCS.unshift(v); // healed right now, regardless inp.disabled = true; status('reading the chain…'); try { await loadChain(); st.progress = ''; stats(); inp.remove(); } catch (err) { status('the chain is unreachable'); inp.disabled = false; inp.select(); } }); document.body.appendChild(inp); } boot(); })(); </script> </body> </html>