From fd05f475476320d33f82dd00c2ec108d11a656b0 Mon Sep 17 00:00:00 2001
From: igneum-labs <337424239+igneum-labs@users.noreply.github.com>
Date: Sun, 4 Oct 2026 10:24:49 +0000
Subject: [PATCH] Revert "Live DAG: chain rail layout, time ruler, slide-in,
red as ember tint, pending count"
This reverts commit 3e03e9bff7f9fae6df4309580843b185ea66d985.
---
site/live.html | 120 ++++++++++++++++++++++++-------------------------
1 file changed, 58 insertions(+), 62 deletions(-)
diff --git a/site/live.html b/site/live.html
index 39a7efe90..b007f1625 100644
--- a/site/live.html
+++ b/site/live.html
@@ -176,20 +176,19 @@ footer{border-top:1px solid var(--line);padding-block:48px 32px}
block dag
-
on screen 0chain 0side 0pending 0newest blue 0locked none
+
on screen 0chain 0side 0newest blue 0locked none
chain block
included (blue): paid
pending
- excluded (red)
+ excluded (red)
locked checkpoint, final
just arrived
selected chain
first parent
- 12:34ruler, a tick every 5 s
- 5d3fd79cring colour = miner; the gutter counts each miner's blocks on screen
+ 5d3fd79cone lane per miner, colour from its id
hover or tap a block for its id, scores and parents
@@ -264,29 +263,29 @@ footer{border-top:1px solid var(--line);padding-block:48px 32px}
function esc(s){return String(s).replace(/&/g,'&').replace(//g,'>');}
// ---------- DAG scene ----------
- // One canvas, one requestAnimationFrame loop. The selected chain runs along one horizontal rail; side blocks hang
- // above and below it in time order, packed into rows so none overlap. The miner is the ring colour; the left gutter
- // lists the active miners with their block counts. A time ruler sits under the blocks. Edges draw under blocks.
- var c=$('dag'),x=c.getContext('2d'),W=0,H=0,S=22,G=104,narrow=false,level=0;
- var blocks=[],byHash={},miners={},minerList=[],rail=0,maxRows=1,rowGap=33;
- var LV=[{S:22,s:14,pps:40,npps:14},{S:16,s:12,pps:26,npps:11},{S:12,s:10,pps:18,npps:9}];
+ // One canvas, one requestAnimationFrame loop. Lanes: one per miner in the visible window, most blocks on top,
+ // the least active share an "others" lane. Labels live in a left gutter, never on blocks. Edges go under blocks.
+ var c=$('dag'),x=c.getContext('2d'),W=0,H=0,S=22,G=78,narrow=false,level=0,maxLanes=8;
+ var blocks=[],byHash={},miners={},lanes=[],laneN=1,othersN=0;
+ var LV=[{S:22,s:14,pps:40,npps:14},{S:16,s:12,pps:26,npps:10},{S:12,s:10,pps:18,npps:8}];
var pxPerSec=40,targetPx=40,bps=0,live=false,serverNow=0,serverAt=0,frozenAt=null,note='';
- var running=false,needSize=true,inView=true,ptrX=-1,ptrY=-1,pin=null,above=[],below=[],crow=[],CO=[0,-1.1,1.1],tickLabels={},tickCount=0;
- var TOP=18,BOT=4,RUL=20,SLIDE=450;
+ var running=false,needSize=true,inView=true,ptrX=-1,ptrY=-1,ptrTouch=false,pin=null,laneLast=[],laneRun=[];
+ var TOP=20,BOT=12;
// 12 hues that sit beside ember on obsidian; the index comes from the short id, so a miner keeps its colour across reloads
var PAL=['#5CB8FF','#3FD39A','#F5C542','#C49BFF','#FF7AA8','#8FD15F','#FFB08A','#6F8CFF','#D9E25A','#FF8FD6','#4FD8D8','#E0A96D'];
function colour(id){var v=0;for(var i=0;i>>0;return PAL[v%PAL.length];}
function jitter(h){var v=0;for(var i=0;i<8;i++)v=(v*31+h.charCodeAt(i))%1000;return v/1000-0.5;}
function vnow(){if(frozenAt!==null)return frozenAt;return serverNow+(performance.now()-serverAt);}
+ function laneY(l){var u=H-TOP-BOT,p=u/Math.max(1,laneN);return TOP+(l+0.5)*p;}
function size(){
needSize=false;W=c.clientWidth;H=c.clientHeight;if(!W||!H)return;
c.width=Math.round(W*dpr);c.height=Math.round(H*dpr);x.setTransform(dpr,0,0,dpr,0,0);
- narrow=W<560;G=narrow?54:104;applyLevel();
+ narrow=W<560;G=narrow?36:80;applyLevel();
}
function applyLevel(){
var L=LV[level];S=narrow?L.s:L.S;targetPx=narrow?L.npps:L.pps;
- rowGap=S*1.5;rail=TOP+(H-TOP-RUL-BOT)/2;
- maxRows=Math.max(1,Math.floor((rail-TOP-S*2.8)/rowGap)+1);
+ maxLanes=Math.max(2,Math.min(14,Math.floor((H-TOP-BOT)/(S*1.7))));
+ layoutLanes();
}
function pickLevel(){
// projected blocks on screen at the base scale drives the step, with hysteresis so it never flickers
@@ -294,6 +293,16 @@ footer{border-top:1px solid var(--line);padding-block:48px 32px}
if(level<2&&proj>up[level])next=level+1;else if(level>0&&projlaneN?ids.length-(laneN-1):0;
+ lanes=[];
+ ids.forEach(function(id,i){var m=miners[id],l=othersN&&i>=laneN-1?laneN-1:i;m.lane=l;if(m.y===undefined)m.y=laneY(l);if(!othersN||lcut;if(!keep)delete byHash[b.hash];return keep;});
blocks.sort(function(a,b){return a.ts-b.ts;});
for(var id in miners)miners[id].n=0;
- blocks.forEach(function(b){var m=miners[b.miner];if(!m){m=miners[b.miner]={id:b.miner,n:0,col:colour(b.miner),tag:b.miner.slice(0,3)};}m.n++;});
- minerList=[];for(id in miners){if(!miners[id].n)delete miners[id];else minerList.push(miners[id]);}
- minerList.sort(function(a,b){return b.n-a.n||(a.idvis){on++;if(b.chain)chain++;else if(b.color==='pending')pend++;}});
- $('c-screen').textContent=on;$('c-chain').textContent=chain;$('c-side').textContent=on-chain;$('c-pending').textContent=pend;
+ pickLevel();layoutLanes();
+ var vis=vnow()-((W-G)/Math.max(8,targetPx))*1000,on=0,chain=0;
+ blocks.forEach(function(b){if(b.ts>vis){on++;if(b.chain)chain++;}});
+ $('c-screen').textContent=on;$('c-chain').textContent=chain;$('c-side').textContent=on-chain;
$('c-newest').textContent=blocks.length?compact(blocks[blocks.length-1].blue_score):'0';
var f=d.finality;$('c-locked').textContent=f&&f.latest_locked_index?('#'+f.latest_locked_index+' at blue '+compact(f.latest_locked_blue_score)):(f&&f.supported?'none yet':'n/a');
if(!running&&!document.hidden)kick();
}
function rrect(px,py,w,h,r){x.beginPath();x.moveTo(px+r,py);x.lineTo(px+w-r,py);x.quadraticCurveTo(px+w,py,px+w,py+r);x.lineTo(px+w,py+h-r);x.quadraticCurveTo(px+w,py+h,px+w-r,py+h);x.lineTo(px+r,py+h);x.quadraticCurveTo(px,py+h,px,py+h-r);x.lineTo(px,py+r);x.quadraticCurveTo(px,py,px+r,py);x.closePath();}
- // a cubic with horizontal tangents; the control offset is clamped so a link across many rows reads as a gentle S, never a loop
+ // a cubic with horizontal tangents; the control offset is clamped so a link across many lanes reads as a gentle S, never a loop
function link(a,b){var d=Math.max(S*0.6,Math.min(48,Math.abs(b.px-a.px)*0.5));x.moveTo(a.px,a.py);x.bezierCurveTo(a.px+d,a.py,b.px-d,b.py,b.px,b.py);}
function hit(){
var best=null,bd=S*0.9,qx=ptrX,qy=ptrY;
@@ -326,40 +334,30 @@ footer{border-top:1px solid var(--line);padding-block:48px 32px}
for(var i=0;i240){tickLabels={};tickCount=0;}var d=new Date(tt);s=(d.getMinutes()<10?'0':'')+d.getMinutes()+':'+(d.getSeconds()<10?'0':'')+d.getSeconds();tickLabels[tt]=s;tickCount++;return s;}
function draw(now){
if(needSize)size();if(!W||!H)return;
pxPerSec+=(targetPx-pxPerSec)*0.06;
var t=vnow(),xr=W-S,i,b,m,n=blocks.length,id;
- // positions: time on x, the chain on the rail; side blocks hang above or below it in time order, packed into rows
- above.length=0;below.length=0;crow.length=0;
- for(i=0;iG-S;}
+ for(id in miners){m=miners[id];var ty=laneY(m.lane);m.y+=(ty-m.y)*0.12;if(Math.abs(ty-m.y)<0.05)m.y=ty;}
+ for(i=0;iG-S;}
x.clearRect(0,0,W,H);
- var bottom=H-RUL-BOT;
// final region: everything left of the newest locked checkpoint
var lk=null,fx=-1;
for(i=0;ilk.ts))lk=b;if(b.final&&b.px>fx)fx=b.px;}
if(lk)fx=lk.px;
- if(fx>G){x.fillStyle='rgba(242,84,27,0.06)';x.fillRect(G,0,fx-G,bottom);x.strokeStyle='rgba(255,179,92,0.55)';x.lineWidth=1;x.setLineDash([4,5]);x.beginPath();x.moveTo(fx,4);x.lineTo(fx,bottom);x.stroke();x.setLineDash([]);}
- // the rail
- x.strokeStyle='rgba(58,58,66,0.9)';x.lineWidth=1;x.beginPath();x.moveTo(G,Math.round(rail)+0.5);x.lineTo(W,Math.round(rail)+0.5);x.stroke();
- // time ruler: a tick every 5 s, labelled with the clock, "now" at the right edge
- var ry=H-RUL+0.5;x.strokeStyle='rgba(58,58,66,0.9)';x.beginPath();x.moveTo(G,ry);x.lineTo(W,ry);x.stroke();
- x.font='500 9px IBM Plex Mono, monospace';x.textAlign='center';x.fillStyle='rgba(154,154,158,0.75)';x.strokeStyle='rgba(90,90,98,0.9)';x.beginPath();
- var every=narrow?10000:5000,tt=Math.floor(t/5000)*5000,labelFrom=W-34;
- for(;;tt-=5000){var xx=xr-(t-tt)*pxPerSec/1000;if(xxG+16)x.fillText(tickLabel(tt),xx,H-2);}
- x.stroke();x.textAlign='right';x.fillStyle='rgba(255,179,92,0.85)';x.fillText('now',W-3,H-2);
- x.save();x.beginPath();x.rect(G,0,W-G,bottom);x.clip();
+ if(fx>G){x.fillStyle='rgba(242,84,27,0.06)';x.fillRect(G,0,fx-G,H);x.strokeStyle='rgba(255,179,92,0.55)';x.lineWidth=1;x.setLineDash([4,5]);x.beginPath();x.moveTo(fx,4);x.lineTo(fx,H-4);x.stroke();x.setLineDash([]);}
+ // lane rails
+ x.strokeStyle='rgba(42,42,48,0.75)';x.lineWidth=1;x.beginPath();for(i=0;iG-S*2)link(q,b);}
x.stroke();
- // the selected chain as one path along the rail
+ // the selected chain as one path through the chain blocks
x.strokeStyle=level?'rgba(242,84,27,0.6)':'rgba(242,84,27,0.85)';x.lineWidth=narrow||level?1.5:2;x.beginPath();var prev=null;
for(i=0;iG-S*2)link(prev,b);prev=b;}
x.stroke();
@@ -368,36 +366,34 @@ footer{border-top:1px solid var(--line);padding-block:48px 32px}
if(hv){x.strokeStyle='rgba(255,179,92,0.9)';x.lineWidth=1.5;x.beginPath();for(i=0;i0){var g=x.createRadialGradient(b.px,b.py,0,b.px,b.py,S*1.6);g.addColorStop(0,'rgba(255,179,92,'+(0.5*fl)+')');g.addColorStop(1,'rgba(255,179,92,0)');x.fillStyle=g;x.beginPath();x.arc(b.px,b.py,S*1.6,0,Math.PI*2);x.fill();}
- if(b.chain){x.fillStyle='#F2541B';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=mc;x.lineWidth=1.5;rrect(b.px-h-1,b.py-h-1,S+2,S+2,r+1);x.stroke();tip=b;}
- else if(b.color==='blue'){x.fillStyle=mc+'B3';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=fl>0?'rgba(255,179,92,0.95)':mc;x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();}
- else if(b.color==='red'){x.fillStyle='rgba(242,84,27,0.22)';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle='rgba(242,84,27,0.5)';x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();}
- else{x.fillStyle='#0C0C0E';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=fl>0?'rgba(255,179,92,0.95)':'rgba(154,154,158,0.85)';x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();}
+ if(b.chain){x.fillStyle='#F2541B';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=m?m.col:'#9A9A9E';x.lineWidth=1.5;rrect(b.px-h-1,b.py-h-1,S+2,S+2,r+1);x.stroke();tip=b;}
+ else if(b.color==='blue'){var mc=m?m.col:'#9A9A9E';x.fillStyle=mc+'B3';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=fl>0?'rgba(255,179,92,0.95)':mc;x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();}
+ else{x.fillStyle='#0C0C0E';rrect(b.px-h,b.py-h,S,S,r);x.fill();x.strokeStyle=fl>0?'rgba(255,179,92,0.95)':(b.color==='red'?'rgba(154,154,158,0.6)':'rgba(154,154,158,0.85)');x.lineWidth=1.5;rrect(b.px-h,b.py-h,S,S,r);x.stroke();
+ if(b.color==='red'){x.strokeStyle='rgba(154,154,158,0.9)';x.lineWidth=1.5;x.beginPath();x.moveTo(b.px-h*0.5,b.py+h*0.5);x.lineTo(b.px+h*0.5,b.py-h*0.5);x.stroke();}}
if(fl>0&&b.chain){x.strokeStyle='rgba(255,179,92,'+fl+')';x.lineWidth=2;rrect(b.px-h-3,b.py-h-3,S+6,S+6,r+2);x.stroke();}
if(b.locked){x.strokeStyle='#FFB35C';x.lineWidth=2.5;x.beginPath();x.arc(b.px,b.py,S*0.95,0,Math.PI*2);x.stroke();}
if(hv===b){x.strokeStyle='#F4F1EC';x.lineWidth=1.5;rrect(b.px-h-3,b.py-h-3,S+6,S+6,r+2);x.stroke();}}
x.font='500 10px IBM Plex Mono, monospace';
// the chain tip carries its short id; nothing else is labelled unless hovered, pinned or locked
- if(tip){x.fillStyle='rgba(244,241,236,0.92)';x.textAlign='right';x.fillText(tip.hash.slice(0,8),Math.min(W-4,tip.px+h+2),tip.py-h-6);}
+ if(tip){x.fillStyle='rgba(244,241,236,0.92)';x.textAlign='right';var ly=tip.py-h-6;if(ly<10){ly=tip.py+h+12;}x.fillText(tip.hash.slice(0,8),Math.min(W-4,tip.px+h+2),ly);}
if(fx>G){var ft=lk?'final #'+lk.hash.slice(0,8):'final',fw=x.measureText(ft).width;x.fillStyle='#FFB35C';
if(fx-G>fw+14){x.textAlign='right';x.fillText(ft,fx-6,12);}else{x.textAlign='left';x.fillText(ft,fx+6,12);}}
// tooltip on the canvas, no DOM
if(hv){var l1=hv.hash.slice(0,8)+' '+(hv.locked?'locked':hv.chain?'chain':hv.color==='blue'?'included, paid':hv.color==='red'?'excluded':'pending'),l2='blue '+compact(hv.blue_score)+' daa '+compact(hv.daa),l3=hv.parents.length+(hv.parents.length===1?' parent':' parents')+' '+hv.miner;
- var tw=Math.max(x.measureText(l1).width,x.measureText(l2).width,x.measureText(l3).width)+20,th=52,tx2=hv.px+h+10,tty=hv.py-th/2;
- if(tx2+tw>W-4)tx2=hv.px-h-10-tw;if(tx2W-4)tx=hv.px-h-10-tw;if(txfit?fit-1:minerList.length;
- for(i=0;i