diff --git a/site/index.html b/site/index.html index 25a833527..89614e7d4 100644 --- a/site/index.html +++ b/site/index.html @@ -260,6 +260,7 @@
+
@@ -273,7 +274,7 @@
The live page
-

One node is read every 2 s. Chain block is the EVM block number. Shards proven is every shard paid on the chain so far. Last lock is the latest checkpoint the finality rule locked. Hover or tap a block for its hash, miner, blue score, parents and proof state. Scroll or pinch to change the window.

+

One node is read every 2 s. Chain block is the EVM block number. Shards proven is every shard paid on the chain so far. Last lock is the latest checkpoint the finality rule locked. Hover or tap a block for its hash, miner, blue score, parents and proof state. Ctrl or cmd and scroll, a pinch, or a click into the scene then scroll, changes the window.

@@ -582,10 +583,10 @@ cm.textContent=fmtN(s.miners_10m!==null&&s.miners_10m!==undefined?s.miners_10m:(d.miners||[]).length); ch.textContent=fmtHash(s.hashes_per_second_estimate); if(note&&!s.stale){var lag=pv&&pv.active&&pv.median_proof_lag_s?Math.round(pv.median_proof_lag_s):null; - note.textContent='One node is read every 2 s. Chain block is the EVM block number. Shards proven is every shard paid on the chain so far'+(pv&&pv.active?' ('+fmtN(pv.shards_paid_10m||0)+' in the last 10 minutes'+(lag?', landing a median '+lag+' s behind the tip, so the newest blocks on screen are still pending':'')+')':'')+'. Last lock is the latest checkpoint the finality rule locked. A card runs several vote keys. Hover or tap a block for its details; scroll or pinch to change the window.';} + note.textContent='One node is read every 2 s. Chain block is the EVM block number. Shards proven is every shard paid on the chain so far'+(pv&&pv.active?' ('+fmtN(pv.shards_paid_10m||0)+' in the last 10 minutes'+(lag?', landing a median '+lag+' s behind the tip, so the newest blocks on screen are still pending':'')+')':'')+'. Last lock is the latest checkpoint the finality rule locked. A card runs several vote keys. Hover or tap a block for its details; ctrl or cmd and scroll, a pinch, or a click into the scene then scroll, changes the window.';} } function mountDag(){var c=document.getElementById('dag');if(!c||!window.IgneumDag)return; - IgneumDag.mount(c,{compact:window.innerWidth<700,window:90,onState:setWord,onData:onData,pauseButton:document.getElementById('dag-pause'),tooltip:document.getElementById('dag-tip')});} + IgneumDag.mount(c,{compact:window.innerWidth<700,window:90,onState:setWord,onData:onData,pauseButton:document.getElementById('dag-pause'),tooltip:document.getElementById('dag-tip'),chip:document.getElementById('dag-chip')});} if(window.IgneumDag)mountDag();else document.addEventListener('DOMContentLoaded',function(){var s=document.querySelector('script[src="/live-dag.js"]');if(window.IgneumDag)mountDag();else if(s)s.addEventListener('load',mountDag);}); // hourly program ticker diff --git a/site/live-dag.js b/site/live-dag.js index 596a95228..6bf08e11d 100644 --- a/site/live-dag.js +++ b/site/live-dag.js @@ -4,10 +4,12 @@ are drawn; the edge between two selected-chain blocks is heavier and the chain glows as one path. Colour words are the ledger's: pending, included (blue), excluded (red), proven (every shard verified or paid), locked (a checkpoint block). Checkpoints are vertical bands at their blue score with the lock weight; a locked band is solid, a pending one dashed. - The view scrolls at chain speed, new blocks arrive from the right with a brief glow; wheel and pinch zoom the window - (30 to 300 s, the API's ?window=); a pause button; hover and tap show a block's details. + The view scrolls at chain speed, new blocks arrive from the right with a brief glow. The page's scroll passes through the + scene; zoom takes ctrl or cmd with the wheel, a pinch, or a click into the scene (engaged, released by Escape or a click + outside; opts.chip is the "scroll to zoom, Esc to release" element); window 30 to 300 s (the API's ?window=); a pause button; + hover and tap show a block's details. - Usage: var dag = IgneumDag.mount(canvas, { compact, window, url, poll, mine, onState, onData, onWindow, pauseButton, tooltip }) + Usage: var dag = IgneumDag.mount(canvas, { compact, window, url, poll, mine, onState, onData, onWindow, onEngage, pauseButton, tooltip, chip }) - url: the /api/live endpoint (default '/api/live'); poll: false stops the module's own 2 s fetch, the host then calls dag.push(reply) with a reply of the same shape ({ok, state:{stale, age_s, height, ...}, blocks:[...], finality:{checkpoints:[...]}, proving}) whenever it has new data (every poll or every new block, either is fine; blocks already on screen are updated in place). @@ -196,8 +198,20 @@ } canvas.addEventListener('mousemove', function (ev) { hover = hit(ev); canvas.style.cursor = hover ? 'pointer' : 'default'; if (!pinned) showTip(hover, ev); kick(); }); canvas.addEventListener('mouseleave', function () { hover = null; if (!pinned) showTip(null); kick(); }); - canvas.addEventListener('click', function (ev) { var b = hit(ev); pinned = (pinned && b && pinned.hash === b.hash) ? null : b; showTip(pinned || hover); kick(); }); - canvas.addEventListener('wheel', function (ev) { if (!ev.ctrlKey && Math.abs(ev.deltaY) < 1) return; ev.preventDefault(); zoom(ev.deltaY > 0 ? 1.15 : 1 / 1.15); }, { passive: false }); + canvas.addEventListener('click', function (ev) { engage(true); var b = hit(ev); pinned = (pinned && b && pinned.hash === b.hash) ? null : b; showTip(pinned || hover); kick(); }); + // the wheel is never captured by default (6 Oct 2026, the project lead: scrolling past the scene zoomed it): the page scrolls through the + // scene; zoom takes ctrl or cmd with the wheel, a pinch, or a click into the scene, which engages it (opts.chip shows + // "scroll to zoom, Esc to release"); Escape, a click outside or leaving the scene releases it + var engaged = false, chip = opts.chip || null; + function engage(on) { if (on === engaged) return; engaged = on; canvas.classList.toggle('engaged', on); if (chip) chip.hidden = !on; if (opts.onEngage) opts.onEngage(on); } + canvas.addEventListener('wheel', function (ev) { + var zoomKey = ev.ctrlKey || ev.metaKey; + if (!zoomKey && !engaged) return; // a plain wheel or a two-finger scroll passes through to the page + if (Math.abs(ev.deltaY) < 1) return; + ev.preventDefault(); zoom(ev.deltaY > 0 ? 1.15 : 1 / 1.15); + }, { passive: false }); + document.addEventListener('keydown', function (ev) { if (ev.key === 'Escape' && engaged) engage(false); }); + document.addEventListener('pointerdown', function (ev) { if (engaged && !canvas.contains(ev.target) && !(chip && chip.contains(ev.target))) engage(false); }); var pinch = null; canvas.addEventListener('touchstart', function (ev) { if (ev.touches.length === 2) pinch = dist(ev.touches); }, { passive: true }); canvas.addEventListener('touchmove', function (ev) { if (ev.touches.length === 2 && pinch) { var d = dist(ev.touches); if (Math.abs(d - pinch) > 12) { zoom(d > pinch ? 1 / 1.1 : 1.1); pinch = d; } } }, { passive: true }); @@ -216,7 +230,7 @@ new MutationObserver(function () { readTheme(); kick(); }).observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); readTheme(); size(); if (opts.poll !== false) { poll(); setInterval(poll, 2000); } kick(); - return { push: push, setPaused: setPaused, zoom: zoom, getWindow: function () { return windowS; }, getState: function () { return { state: state, reason: reason }; }, redraw: kick, words: words }; + return { push: push, setPaused: setPaused, zoom: zoom, engage: engage, getWindow: function () { return windowS; }, getState: function () { return { state: state, reason: reason }; }, redraw: kick, words: words }; } root.IgneumDag = { mount: mount }; })(window); diff --git a/site/live.html b/site/live.html index 8a7873687..7de59505b 100644 --- a/site/live.html +++ b/site/live.html @@ -175,7 +175,7 @@ main{padding-bottom:var(--sec)}
connecting to the devnet observer
on screen 0chain 0identities 0last lock pendingproven pending
-
+
pending includedselected chain @@ -185,7 +185,7 @@ main{padding-bottom:var(--sec)} finality band, locked, with its weight finality band, pending
-

120 s of blocks on screen. One lane per miner, the busiest first; the rest share the last lane. Hover or tap a block for its hash, miner, blue score, parents and proof state. Scroll or pinch to change the window; the page's ?window= fixes it.

+

120 s of blocks on screen. One lane per miner, the busiest first; the rest share the last lane. Hover or tap a block for its hash, miner, blue score, parents and proof state. Ctrl or cmd and scroll, a pinch, or a click into the scene then scroll, changes the window; the page's ?window= fixes it.

@@ -293,7 +293,7 @@ main{padding-bottom:var(--sec)} var dagCanvas=$('dag'),dag=null,lastD=null; function windowWord(w){return w+' s of blocks on screen';} function mountDag(){if(dag||!dagCanvas||!window.IgneumDag)return; - dag=IgneumDag.mount(dagCanvas,{poll:false,window:fixedWindow||120,tooltip:$('dag-tip'),pauseButton:$('dag-pause'), + dag=IgneumDag.mount(dagCanvas,{poll:false,window:fixedWindow||120,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'), onWindow:function(w){$('c-window').textContent=windowWord(w);}, onState:function(st,reason){var el=$('dag-state');if(!el)return;el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML=''+(st==='live'?'live devnet':st==='connecting'?'connecting to the devnet observer':esc(reason||'live feed unavailable'));}}); $('c-window').textContent=windowWord(dag.getWindow());if(lastD)dag.push(lastD);} diff --git a/site/site.css b/site/site.css index e39ee5d34..3500f2c5e 100644 --- a/site/site.css +++ b/site/site.css @@ -210,6 +210,11 @@ input:focus,select:focus,textarea:focus{border-color:var(--line-2)} .legend span{display:inline-flex;align-items:center;gap:8px} .sw{width:12px;height:12px;border-radius:3px;display:inline-block;flex:none} +/* the engaged scene: a chip in the corner while the wheel zooms */ +.scene canvas.engaged{outline:2px solid var(--ember);outline-offset:-2px} +.chip-zoom{position:absolute;right:12px;top:12px;display:inline-flex;align-items:center;gap:8px;background:var(--graphite);border:1px solid var(--line-2);border-radius:var(--r-pill);padding:5px 12px;font:500 var(--t-sm)/1.4 var(--f-mono);color:var(--ink-2);pointer-events:none} +.chip-zoom[hidden]{display:none} + /* ---------- 8. print ---------- */ @media print{ :root{--obsidian:#fff;--graphite:#fff;--row:#f3f3f3;--line:#bbb;--line-2:#999;--bone:#000;--ink-2:#111;--ash:#444;--ember:#B8390C;--ember-text:#B8390C;--molten-text:#8F5810}