site-ui-4: live-dag.js 2.0.1, the real-data presentation options (lanes capped at 7 with the own key first, node size from the lane height with a floor, hairline merge edges within two lanes and the first parent only, the others lane smaller and dimmer, a glow and ring on proven blocks); 60 s default window; the demo-feed proof capture beside the live one

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-07 09:08:54 +00:00
parent cf0d4238e5
commit 06f1cd6e86
20 changed files with 19 additions and 12 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 MiB

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View file

@ -296,7 +296,7 @@
function caption(d){var nb=null;(d.blocks||[]).forEach(function(b){if(!nb||b.ts>nb.ts)nb=b;});if(!nb)return;var sh=nb.shards||[],w=nb.locked?'locked checkpoint':nb.proven?'proven':nb.chain?'on the selected chain':nb.color==='blue'?'included':nb.color==='red'?'excluded':'pending';
cap.classList.remove('muted');cap.textContent=(nb.number?'block '+fmtN(nb.number):nb.hash)+': mined '+hms(nb.ts)+', '+w+(sh.length?'; '+sh.length+(sh.length===1?' shard ':' shards ')+(sh.every(function(x){return x.state==='paid';})?'paid':sh.some(function(x){return x.state==='verified'||x.state==='paid';})?'proven':sh.some(function(x){return x.state==='proving';})?'proving':'planned'):'');}
var c=document.getElementById('dag'),dag=null;
if(c&&window.IgneumFeed&&window.IgneumDag){dag=IgneumDag.mount(c,{poll:false,window:90,tooltip:document.getElementById('dag-tip'),chip:document.getElementById('dag-chip')});
if(c&&window.IgneumFeed&&window.IgneumDag){dag=IgneumDag.mount(c,{poll:false,window:60,tooltip:document.getElementById('dag-tip'),chip:document.getElementById('dag-chip')});
IgneumFeed.start({window:300,subs:[dag],onState:onState,onData:function(d){onData(d);caption(d);}});}
})();
</script>

View file

@ -1,4 +1,7 @@
/* Igneum DAG / Ember 2.0
/* Igneum DAG / Ember 2.0 (the EMBER 02 renderer pack, 7 Oct 2026) with the real-data presentation options of 2.0.1:
* maxLanes (own key + top keys + others, default 7), minNode (node half-size floor, default 9.5), hairlineAlpha (non-chain
* edges, default .12, drawn only within laneReach lanes, default 2, and only a merged block's first parent unless selected),
* othersScale (the others lane's size and alpha, default .75), provenGlow (a glow and ring on proven blocks, default on).
* Replacement for the supplied live-dag.js. No framework, network writes or synthetic records.
* Existing mount/push options and public methods are retained; see README.md for additions.
* Every node, parent edge, proof segment and checkpoint is derived from the observer reply.
@ -47,6 +50,7 @@
var maxBlocks=clamp(finite(opts.maxBlocks)?opts.maxBlocks:6000,100,20000);
var lag=finite(opts.lagMs)?clamp(opts.lagMs,0,10000):2500;
var fps=clamp(finite(opts.fps)?opts.fps:30,10,60), minFrame=1000/fps;
var maxLanes=clamp(finite(opts.maxLanes)?opts.maxLanes:7,2,12), minNode=finite(opts.minNode)?opts.minNode:9.5, hairAlpha=finite(opts.hairlineAlpha)?opts.hairlineAlpha:.12, laneReach=finite(opts.laneReach)?opts.laneReach:2, othersScale=finite(opts.othersScale)?opts.othersScale:.75, provenGlow=opts.provenGlow!==false;
var mq=root.matchMedia?root.matchMedia('(prefers-reduced-motion: reduce)'):null;
var reduced=mq?mq.matches:false, manualMotion=true;
var mediaTheme=root.matchMedia?root.matchMedia('(prefers-color-scheme: dark)'):null;
@ -93,7 +97,7 @@
padL=compact||W<510?14:W<750?84:106;padR=compact?12:24;padT=compact?29:58;padB=compact?22:38;
var start=rightTime()-windowS*1000, end=rightTime()+lag+1000,counts=new Map(),mineKeys=new Set();
blocks.forEach(function(b){if(b.ts>=start && b.ts<=end){counts.set(b.miner,(counts.get(b.miner)||0)+1);if(own(b))mineKeys.add(b.miner);}});
var capacity=Math.max(2,Math.floor((H-padT-padB)/(compact?26:46))),ids=Array.from(counts.keys());
var capacity=Math.min(maxLanes,Math.max(2,Math.floor((H-padT-padB)/(compact?26:46)))),ids=Array.from(counts.keys());
ids.sort(function(a,b){return (mineKeys.has(b)?1:0)-(mineKeys.has(a)?1:0)||(counts.get(b)-counts.get(a))||a.localeCompare(b);});
var chosen=ids.slice(0,ids.length>capacity?capacity-1:capacity);
// Preserve existing key order instead of re-ranking every poll. Always keep the user's lane visible.
@ -214,19 +218,22 @@
// Animate lane transitions without changing their underlying key identity.
blocks.forEach(function(b){b.x=xOf(b.ts,rt);var progress=animate?clamp((clock-b.laneAt)/450,0,1):1;progress=1-Math.pow(1-progress,3);b.y=b.fromY+(b.targetY-b.fromY)*progress;b.on=b.ts>=left&&b.ts<=rt&&passes(b);if(b.on&&b.newArrival&&b.visibleAt===null){b.visibleAt=t;b.born=t;}});
ctx.save();ctx.beginPath();ctx.rect(padL-1,padT-15,plotW+2,plotH+25);ctx.clip();
blocks.forEach(function(b){if(!b.on)return;b.parents.forEach(function(hash){var p=view.get(hash);if(!p||!passes(p)||p.x>W-padR||p.x<padL-plotW*.2)return;
blocks.forEach(function(b){if(!b.on)return;var bSel=selected&&b.hash===selected;b.parents.forEach(function(hash,pi){var p=view.get(hash);if(!p||!passes(p)||p.x>W-padR||p.x<padL-plotW*.2)return;
var path=b.chain&&p.chain,selectedEdge=selected&&(b.hash===selected||p.hash===selected);
ctx.strokeStyle=rgba(path?col.ember:selectedEdge?col.bone:col.ash,path?.1:selectedEdge?.10:.04);ctx.lineWidth=path?8:selectedEdge?4:2;curve(b,p);ctx.stroke();
ctx.strokeStyle=rgba(path?col.ember:selectedEdge?col.bone:b.color==='red'?col.red:col.ash,path?.68:selectedEdge?.7:b.color==='red'?.3:.23);ctx.lineWidth=path?1.65:selectedEdge?1.4:1;curve(b,p);ctx.stroke();
if(!path&&!selectedEdge){if(pi>0)return;var li=laneOf.has(b.miner)?laneOf.get(b.miner):lanes.length-1,lp=laneOf.has(p.miner)?laneOf.get(p.miner):lanes.length-1;if(Math.abs(li-lp)>laneReach)return;}
if(path||selectedEdge){ctx.strokeStyle=rgba(path?col.ember:col.bone,path?.1:.10);ctx.lineWidth=path?8:4;curve(b,p);ctx.stroke();}
ctx.strokeStyle=rgba(path?col.ember:selectedEdge?col.bone:b.color==='red'?col.red:col.ash,path?.68:selectedEdge?.7:hairAlpha);ctx.lineWidth=path?1.65:selectedEdge?1.4:1;curve(b,p);ctx.stroke();
var age=t-b.born;if(animate&&age>=0&&age<1400){var f=clamp(age/1150,0,1),pt=pointOn(b,p,f);ctx.shadowBlur=10;ctx.shadowColor=path?col.ember:col.blue;ctx.fillStyle=path?col.molten:col.blue;ctx.beginPath();ctx.arc(pt.x,pt.y,path?2.2:1.65,0,TAU);ctx.fill();ctx.shadowBlur=0;}
});});
var density=blocks.filter(function(b){return b.on;}).length/Math.max(plotW,1), S=compact?6.5:density>.16?7:9.5;
var S=compact?6.5:clamp(lh*.22,minNode,13);
blocks.forEach(function(b){if(!b.on)return;
var age=t-b.born,newness=animate?Math.max(clamp(1-age/1700,0,1),clamp(1-(t-b.changed)/900,0,1)*.65):0;
var ink=b.color==='red'?col.red:b.chain?col.ember:b.color==='blue'?col.blue:col.ash;
var isOwn=own(b),isSelected=b.hash===selected||b.hash===hover,sz=S*(1+(newness*.15));
var isOwn=own(b),isSelected=b.hash===selected||b.hash===hover,inOthers=!laneOf.has(b.miner),sz=S*(1+(newness*.15))*(inOthers?othersScale:1);
if(newness>0||isOwn||isSelected){var glow=ctx.createRadialGradient(b.x,b.y,0,b.x,b.y,sz*3.8);glow.addColorStop(0,rgba(isOwn?col.molten:ink,newness*.26+(isSelected?.15:isOwn?.06:0)));glow.addColorStop(1,rgba(ink,0));ctx.fillStyle=glow;ctx.fillRect(b.x-sz*4,b.y-sz*4,sz*8,sz*8);}
ctx.globalAlpha=b.color==='red'?.78:1;
ctx.globalAlpha=(b.color==='red'?.78:1)*(inOthers&&!isSelected&&!isOwn?othersScale:1);
// a proven block glows and carries a ring, so the proof reads from across the room
if(provenGlow&&b.proven){var pg=ctx.createRadialGradient(b.x,b.y,0,b.x,b.y,sz*3.2);pg.addColorStop(0,rgba(ink,.26));pg.addColorStop(1,rgba(ink,0));ctx.fillStyle=pg;ctx.fillRect(b.x-sz*3.4,b.y-sz*3.4,sz*6.8,sz*6.8);ctx.strokeStyle=rgba(ink,.6);ctx.lineWidth=1;ctx.beginPath();ctx.arc(b.x,b.y,sz+5,0,TAU);ctx.stroke();}
// Inset tiles: inclusion is the outline; proof is the core, never the same status.
ctx.fillStyle=col.bg;ctx.strokeStyle=rgba(ink,b.chain?.95:.8);ctx.lineWidth=b.chain?1.75:1.25;rounded(ctx,b.x-sz,b.y-sz,2*sz,2*sz,3);ctx.fill();ctx.stroke();
var tile=ctx.createLinearGradient(b.x,b.y-sz,b.x,b.y+sz);tile.addColorStop(0,rgba(ink,b.proven?.5:.16));tile.addColorStop(1,rgba(ink,b.proven?.22:.025));ctx.fillStyle=tile;rounded(ctx,b.x-sz+2,b.y-sz+2,2*sz-4,2*sz-4,2);ctx.fill();
@ -338,5 +345,5 @@
getSnapshot:function(){return {ok:true,state:{stale:state!=='live'},blocks:blocks.map(cloneBlock),finality:{checkpoints:cps.map(function(c){return Object.assign({},c);})}};}};
instances.set(canvas,api);if(chip)chip.hidden=true;theme();size();emit('onState',state,reason);if(opts.poll!==false)poll();redraw();return api;
}
root.IgneumDag={mount:mount,version:'2.0.0'};
root.IgneumDag={mount:mount,version:'2.0.1'};
})(window);

View file

@ -296,7 +296,7 @@ details.tablebar summary{display:flex;align-items:center}
<div class="scene"><canvas id="dag" aria-label="The devnet's block graph, live: time-aligned miner lanes, every parent connection, the selected chain as one path, checkpoint bands, your blocks framed"></canvas><div class="tip" id="dag-tip" hidden></div><div class="chip-zoom" id="dag-chip" hidden>scroll to zoom · Esc to release</div></div>
<div class="gfoot">
<span>Click to inspect &nbsp;&middot;&nbsp; Drag to explore</span>
<span class="zoom"><button type="button" id="z-out" aria-label="Wider window">&minus;</button><span id="z-pct">90 s</span><button type="button" id="z-in" aria-label="Narrower window">+</button></span>
<span class="zoom"><button type="button" id="z-out" aria-label="Wider window">&minus;</button><span id="z-pct">60 s</span><button type="button" id="z-in" aria-label="Narrower window">+</button></span>
</div>
</div>
<aside class="icol" id="inspector" aria-live="polite">
@ -507,7 +507,7 @@ details.tablebar summary{display:flex;align-items:center}
$('m-cp').textContent=cp?(cp.state==='locked'?'Locked':'Pending'):'None';$('m-cp-w').textContent=cp&&cp.fraction_total!==null&&cp.fraction_total!==undefined?Math.round(cp.fraction_total*100)+'% weight':'';$('m-cp-note').textContent=cp?'#'+fmtN(cp.index)+' · blue score '+fmtN(cp.blue_score):'no checkpoint in the reply';
$('c-count').textContent=st.blocks+' blocks';var r=dag.getViewRange(),lo=null,hi=null;dag.getBlocks().forEach(function(b){if(b.ts>=r.start&&b.ts<=r.end){if(lo===null||b.blue_score<lo)lo=b.blue_score;if(hi===null||b.blue_score>hi)hi=b.blue_score;}});$('blue-range').textContent=lo===null?'blue score pending':'blue score '+fmtN(lo)+' to '+fmtN(hi);}
function mountDag(){if(dag||!window.IgneumDag)return;
dag=IgneumDag.mount($('dag'),{poll:false,window:90,mine:mine||undefined,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'),
dag=IgneumDag.mount($('dag'),{poll:false,window:60,mine:mine||undefined,tooltip:$('dag-tip'),chip:$('dag-chip'),pauseButton:$('dag-pause'),
onWindow:function(w){$('z-pct').textContent=w+' s';if(lastD)render(lastD);},
onState:function(st,reason){var el=$('dag-state');el.className='state '+(st==='live'?'live':st==='failed'?'bad':'');el.innerHTML='<span class="dot '+(st==='live'?'live':st==='failed'?'bad':'off')+'"></span>'+(st==='live'?'live devnet':esc(reason||st));if(proof)proof.setPaused(st!=='live'||dag.getStats().paused);},
onPause:function(p){$('dag-follow').classList.toggle('on',!p);if(proof)proof.setPaused(p||dag.getState().state!=='live');},