diff --git a/app/igneum-app/ui/app.js b/app/igneum-app/ui/app.js index a4bf6ded2..96b73cb3e 100644 --- a/app/igneum-app/ui/app.js +++ b/app/igneum-app/ui/app.js @@ -1564,7 +1564,7 @@ if (typeof document !== 'undefined') (function () { function mountDag() { if (dag || typeof IgneumDag === 'undefined' || !$('dag-live')) return; try { - dag = IgneumDag.mount($('dag-live'), { compact: true, window: 60, poll: false, mine: mineFn, tooltip: $('dag-tip'), chip: $('dag-chip'), pauseButton: $('dag-pause'), + dag = IgneumDag.mount($('dag-live'), { compact: true, window: 60, fps: 60, narrow: window.innerWidth < 720, poll: false, mine: mineFn, tooltip: $('dag-tip'), chip: $('dag-chip'), pauseButton: $('dag-pause'), onSelect: function (b, why) { if (b && why !== 'update') openFull(b.hash); }, onState: function (st, why) { if (dagMode === 'failed') return; if (st === 'live' || st === 'stale') { setText('dag-state', st === 'live' ? 'live' : 'behind'); setText('dag-note', st === 'stale' ? (why || '') : (lastLive ? View.liveNote(lastLive) : '')); } } }); pollLive(); @@ -1594,7 +1594,7 @@ if (typeof document !== 'undefined') (function () { function mountFull() { if (dagFull || typeof IgneumDag === 'undefined') return; if (typeof IgneumProof !== 'undefined' && !proof) proof = IgneumProof.mount($('proof')); - dagFull = IgneumDag.mount($('dag-full'), { poll: false, window: 60, mine: mineFn, tooltip: $('dag-tip-full'), chip: $('dag-chip-full'), pauseButton: $('dag-pause-full'), + dagFull = IgneumDag.mount($('dag-full'), { poll: false, window: 60, fps: 60, mine: mineFn, tooltip: $('dag-tip-full'), chip: $('dag-chip-full'), pauseButton: $('dag-pause-full'), onWindow: function (w) { setText('z-pct', w + ' s'); }, onSelect: function (b) { if (b) inspect(b); else uninspect(); }, onPause: function (p) { $('dag-follow').classList.toggle('on', !p); if (proof) proof.setPaused(p || dagFull.getState().state !== 'live'); }, diff --git a/app/igneum-app/ui/live-dag.js b/app/igneum-app/ui/live-dag.js index d9d896ad2..b4e1705a9 100644 --- a/app/igneum-app/ui/live-dag.js +++ b/app/igneum-app/ui/live-dag.js @@ -1,4 +1,6 @@ -/* Igneum DAG / Ember 2.0 (the EMBER 02 renderer pack, 7 Oct 2026) with the real-data presentation options of 2.0.1: +/* Igneum DAG / Ember 2.0 (the EMBER 02 renderer pack, 7 Oct 2026) with the narrow options of 2.0.2 (narrow, narrowBreak 720, + * narrowWindow 30, narrowLanes 4, narrowMinNode 11; on a phone the window shortens until the viewer sets one, four lanes, bigger + * nodes, no hairlines but the selected chain, checkpoint labels as the percent) and 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). @@ -50,6 +52,10 @@ 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; + // narrow (phones, under narrowBreak px, or opts.narrow true/false): window narrowWindow s until the viewer sets one, at most + // narrowLanes lanes, nodes no smaller than narrowMinNode, hairlines off except the selected chain and the selected block, + // checkpoint labels as the percent alone. The owner's call of 7 Oct 2026: faster drift, more gap, on a phone. + var narrow=false,narrowBreak=finite(opts.narrowBreak)?opts.narrowBreak:720,narrowWindow=clamp(finite(opts.narrowWindow)?opts.narrowWindow:30,30,300),narrowLanes=clamp(finite(opts.narrowLanes)?opts.narrowLanes:4,2,12),narrowMinNode=finite(opts.narrowMinNode)?opts.narrowMinNode:11,baseWindow=0,windowTouched=false; 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; @@ -94,10 +100,13 @@ emit('onState',s,reason);redraw(); } function layout(){ + if(!baseWindow)baseWindow=windowS; + var wasNarrow=narrow;narrow=opts.narrow===true||(opts.narrow!==false&&!compact&&W>0&&W=start && b.ts<=end){counts.set(b.miner,(counts.get(b.miner)||0)+1);if(own(b))mineKeys.add(b.miner);}}); - var capacity=Math.min(maxLanes,Math.max(2,Math.floor((H-padT-padB)/(compact?26:46)))),ids=Array.from(counts.keys()); + var capacity=Math.min(narrow?narrowLanes:maxLanes,Math.max(2,Math.floor((H-padT-padB)/(compact?26:narrow?40: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. @@ -164,7 +173,7 @@ selectedChain:v.filter(function(b){return b.chain;}).length,proven:v.filter(function(b){return b.proven;}).length, own:v.filter(own).length,minerKeys:new Set(v.map(function(b){return b.miner;})).size, lockedCheckpoints:cps.filter(function(c){return c.state==='locked';}).length,paused:paused,following:following,queued:paused?queuedCount:0, - invalidRecords:invalid,omittedRecords:omitted,rendered:blocks.length,frames:frameCount,window:windowS,reducedMotion:reduced||!manualMotion,state:state}; + invalidRecords:invalid,omittedRecords:omitted,rendered:blocks.length,frames:frameCount,window:windowS,narrow:narrow,lanes:lanes.length,reducedMotion:reduced||!manualMotion,state:state}; } function poll(){ if(destroyed||opts.poll===false||doc.hidden)return; @@ -209,10 +218,10 @@ var band=ctx.createLinearGradient(x-20,0,x+36,0);band.addColorStop(0,rgba(col.ember,0));band.addColorStop(.38,rgba(col.ember,locked?.09:.035));band.addColorStop(1,rgba(col.ember,0));ctx.fillStyle=band;ctx.fillRect(x-20,padT-12,56,plotH+12); ctx.strokeStyle=rgba(col.ember,locked?.6:.32);ctx.lineWidth=locked?1.5:1;ctx.setLineDash(locked?[]:[3,6]);ctx.beginPath();ctx.moveTo(x,padT-14);ctx.lineTo(x,H-padB);ctx.stroke();ctx.setLineDash([]); if(burst>0){ctx.strokeStyle=rgba(col.molten,burst*.5);ctx.lineWidth=1;ctx.strokeRect(x-(1-burst)*42,padT-12,(1-burst)*84,plotH+12);} - if(!compact && W>510)cpLabels.push({x:x,cp:c,locked:locked}); + if(!compact && (narrow || W>510))cpLabels.push({x:x,cp:c,locked:locked}); }); cpLabels.sort(function(a,b){return b.x-a.x;});var cpLast=Infinity; - cpLabels.forEach(function(p){var label=(p.locked?'LOCKED':'PENDING')+(p.cp.fraction_total===null?'':' '+Math.round(p.cp.fraction_total*100)+'%'),w=label.length*5.5+22,x=clamp(p.x-w/2,padL,W-padR-w);if(x+w+8>cpLast)return;cpLast=x; + cpLabels.forEach(function(p){var pct=p.cp.fraction_total===null||p.cp.fraction_total===undefined?'':Math.round(p.cp.fraction_total*100)+'%',label=narrow?(pct||(p.locked?'LOCKED':'PENDING')):(p.locked?'LOCKED':'PENDING')+(pct?' '+pct:''),w=label.length*5.5+(narrow?14:22),x=clamp(p.x-w/2,padL,W-padR-w);if(x+w+8>cpLast)return;cpLast=x; ctx.fillStyle=col.bg;ctx.strokeStyle=rgba(col.ember,p.locked?.45:.23);rounded(ctx,x,17,w,22,4);ctx.fill();ctx.stroke();ctx.font='9px ui-monospace, monospace';ctx.fillStyle=p.locked?col.ember:col.ash;ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(label,x+w/2,28); }); // Animate lane transitions without changing their underlying key identity. @@ -220,12 +229,12 @@ ctx.save();ctx.beginPath();ctx.rect(padL-1,padT-15,plotW+2,plotH+25);ctx.clip(); 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.x0)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){if(narrow||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 S=compact?6.5:clamp(lh*.22,minNode,13); + var S=compact?6.5:narrow?clamp(lh*.24,narrowMinNode,15):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; @@ -290,7 +299,7 @@ function hit(ev){var p=eventXY(ev),best=null,dist=22*22;blocks.forEach(function(b){if(!b.on)return;var d=(b.x-p.x)**2+(b.y-p.y)**2;if(d