Merge remote-tracking branch 'origin/scene-parity-21-sizing' into release-0.3.21

This commit is contained in:
igneum-labs 2026-10-07 14:04:32 +00:00
commit fedf00ef73
4 changed files with 66 additions and 13 deletions

View file

@ -1,5 +1,10 @@
/* Igneum DAG / Ember 2.0 (the EMBER 02 renderer pack, 7 Oct 2026). THE SHARED SOURCE: scene/live-dag.js; site/live-dag.js and
* app/igneum-app/ui/live-dag.js are byte-equal copies written by tools/scene/sync.mjs (the gate refuses a drifted copy). Edit here.
* 2.0.4 (7 Oct 2026): the box's height follows the lanes. laneHeight (default 46 px, 40 on a phone, 26 compact) times the lanes
* shown (at most maxLanes, narrowLanes on a phone, never under 2) plus the axis and padding is the scene's wanted height;
* onSize(heightPx, {lanes, laneHeight, narrow, compact}) reports it whenever it changes, so a page can size the box, and with
* autoHeight (true by default when the host set no CSS height on the canvas, forced either way by the option; never in
* compact mode, the app's card keeps its own height) the renderer sets canvas.style.height itself and lets every lane through.
* 2.0.3 (scene parity, 7 Oct 2026): (1) every push, size and theme change paints the current picture at once; only the motion
* loop waits for a visible document and an intersecting canvas (a page that loads with document.hidden true, or whose embedder
* never fires visibilitychange, showed a blank canvas while reporting live). (2) The narrow (phone) rule keys on the viewport
@ -61,6 +66,10 @@
// 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;
// autoHeight: the host set no height on the canvas (its computed height is still the attribute's, 150 by default) and this
// is not the compact card; read before size() touches the attribute
var hostHeightSet=(function(){try{var cs=getComputedStyle(canvas),attr=canvas.getAttribute('height'),h=parseFloat(cs.height);return canvas.style.height!==''||!(finite(h)&&Math.round(h)===(attr===null?150:Number(attr)));}catch(e){return true;}})();
var autoHeight=opts.autoHeight===true||(opts.autoHeight!==false&&!compact&&!hostHeightSet),laneHeightOpt=finite(opts.laneHeight)?opts.laneHeight:0,wantH=0,laneHNow=46;
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;
@ -112,7 +121,8 @@
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.min(narrow?narrowLanes:maxLanes,Math.max(2,Math.floor((H-padT-padB)/(compact?26:narrow?40:46)))),ids=Array.from(counts.keys());
var laneH=laneHeightOpt||(compact?26:narrow?40:46),cap=narrow?narrowLanes:maxLanes;laneHNow=laneH;
var capacity=autoHeight?cap:Math.min(cap,Math.max(2,Math.floor((H-padT-padB)/laneH))),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.
@ -121,6 +131,9 @@
keep.sort(function(a,b){return (mineKeys.has(b)?1:0)-(mineKeys.has(a)?1:0);});
lanes=keep;if(ids.length>chosen.length)lanes.push('__others__');
laneOf=new Map();lanes.forEach(function(id,i){laneOf.set(id,i);});
// the wanted box height for these lanes (2.0.4): reported on change; applied here only under autoHeight
var nextH=padT+padB+Math.max(2,lanes.length)*laneH;
if(nextH!==wantH){wantH=nextH;emit('onSize',wantH,{lanes:lanes.length,laneHeight:laneH,narrow:narrow,compact:compact});if(autoHeight&&canvas.style.height!==wantH+'px')canvas.style.height=wantH+'px';}
var now=performance.now();
blocks.forEach(function(b){var i=laneOf.has(b.miner)?laneOf.get(b.miner):Math.max(0,lanes.length-1);
var y=padT+(i+.5)*(H-padT-padB)/Math.max(1,lanes.length);
@ -179,7 +192,9 @@
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,narrow:narrow,lanes:lanes.length,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,
// the lane geometry (2.0.4), so a page that sizes its own box reads no constants
laneHeight:laneHNow,padT:padT,padB:padB,capacity:narrow?narrowLanes:maxLanes,wantedHeight:wantH,autoHeight:autoHeight};
}
function poll(){
if(destroyed||opts.poll===false||doc.hidden)return;
@ -356,11 +371,11 @@
function destroy(){if(destroyed)return;destroyed=true;stopFrame();clearTimeout(pollTimer);clearTimeout(timeout);clearInterval(healthTimer);if(controller)controller.abort();remove.forEach(function(fn){fn();});if(ro)ro.disconnect();if(io)io.disconnect();if(mo)mo.disconnect();if(tip)tip.hidden=true;canvas.classList.remove('engaged');
[['tabindex',initialAttributes.tabindex],['role',initialAttributes.role],['aria-label',initialAttributes.label]].forEach(function(pair){if(pair[1]===null)canvas.removeAttribute(pair[0]);else canvas.setAttribute(pair[0],pair[1]);});canvas.style.touchAction=initialAttributes.touch;canvas.style.cursor=initialAttributes.cursor;instances.delete(canvas);model.clear();view.clear();blocks=[];
}
var api={push:push,setPaused:setPaused,zoom:zoom,engage:engage,getWindow:function(){return windowS;},getState:function(){return {state:state,reason:reason};},redraw:redraw,words:words,
var api={push:push,setPaused:setPaused,zoom:zoom,engage:engage,getWindow:function(){return windowS;},getWantedHeight:function(){return wantH;},getState:function(){return {state:state,reason:reason};},redraw:redraw,words:words,
destroy:destroy,follow:follow,setWindow:setWindow,setFilter:setFilter,setMotion:setMotion,select:select,getStats:stats,
getViewRange:function(){return {start:rightTime()-windowS*1000,end:rightTime(),filter:filter};},getBlocks:function(){return blocks.map(cloneBlock);},getCheckpoints:function(){return cps.map(function(c){return Object.assign({},c);});},
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.3'};
root.IgneumDag={mount:mount,version:'2.0.4'};
})(window);

View file

@ -5,7 +5,7 @@ are ONE renderer fed ONE JSON shape. This folder is the source; everything else
| File | What it is | Copies |
|---|---|---|
| `live-dag.js` | EMBER 02 `IgneumDag` 2.0.3: lanes per vote key, parent curves, the selected chain, checkpoint bands, proof glow | `site/live-dag.js`, `app/igneum-app/ui/live-dag.js` |
| `live-dag.js` | EMBER 02 `IgneumDag` 2.0.4: lanes per vote key, parent curves, the selected chain, checkpoint bands, proof glow; the box height follows the lanes (`onSize`, `autoHeight`) | `site/live-dag.js`, `app/igneum-app/ui/live-dag.js` |
| `proof-core.js` | `IgneumProof` 2.0.0: the selected block's shards | `site/proof-core.js`, `app/igneum-app/ui/proof-core.js` |
| `tokens.css` | The fourteen palette tokens the two scripts read from `:root`, dark and light, the brand package's values | the `scene-tokens` block in `site/site.css` and `app/igneum-app/ui/app.css` |
| `feed-contract.md`, `feed-contract.json` | The feed shape, in words and as key lists | read by `tools/scene/feed-contract.mjs` and the app's `src/live.rs` test |
@ -25,6 +25,14 @@ option names this machine's vote keys, so its own blocks glow and its lane reads
picture, never a second renderer, never a rewritten feed. The phone rule (30 s window, four lanes, bigger nodes) keys on the
viewport width under 720 px, not on the canvas width: a 640 px hero on a laptop is not a phone.
The box's height follows the lanes (2.0.4): wanted height = top padding + axis padding + lanes shown times `laneHeight`
(46 px, 40 on a phone, 26 compact; at most `maxLanes` 7, `narrowLanes` 4 on a phone, never under 2). The renderer calls
`onSize(heightPx, {lanes, laneHeight, narrow, compact})` whenever that changes and exposes `getWantedHeight()`; a page that
lays out its own box sets its height from the callback. With `autoHeight` (on by default when the host set no CSS height on
the canvas, forced either way by the option, never in compact mode) the renderer sets `canvas.style.height` itself and shows
every lane up to the cap. Every current host sets a height (/live 420 px, the hero 500 px, the app's card 220 px and its
Inspect view 420 px), so until a page opts in the frames are unchanged.
## The tests (all in `tools/ci/pre-push.sh`)
| Line | What it proves |

View file

@ -1,5 +1,10 @@
/* Igneum DAG / Ember 2.0 (the EMBER 02 renderer pack, 7 Oct 2026). THE SHARED SOURCE: scene/live-dag.js; site/live-dag.js and
* app/igneum-app/ui/live-dag.js are byte-equal copies written by tools/scene/sync.mjs (the gate refuses a drifted copy). Edit here.
* 2.0.4 (7 Oct 2026): the box's height follows the lanes. laneHeight (default 46 px, 40 on a phone, 26 compact) times the lanes
* shown (at most maxLanes, narrowLanes on a phone, never under 2) plus the axis and padding is the scene's wanted height;
* onSize(heightPx, {lanes, laneHeight, narrow, compact}) reports it whenever it changes, so a page can size the box, and with
* autoHeight (true by default when the host set no CSS height on the canvas, forced either way by the option; never in
* compact mode, the app's card keeps its own height) the renderer sets canvas.style.height itself and lets every lane through.
* 2.0.3 (scene parity, 7 Oct 2026): (1) every push, size and theme change paints the current picture at once; only the motion
* loop waits for a visible document and an intersecting canvas (a page that loads with document.hidden true, or whose embedder
* never fires visibilitychange, showed a blank canvas while reporting live). (2) The narrow (phone) rule keys on the viewport
@ -61,6 +66,10 @@
// 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;
// autoHeight: the host set no height on the canvas (its computed height is still the attribute's, 150 by default) and this
// is not the compact card; read before size() touches the attribute
var hostHeightSet=(function(){try{var cs=getComputedStyle(canvas),attr=canvas.getAttribute('height'),h=parseFloat(cs.height);return canvas.style.height!==''||!(finite(h)&&Math.round(h)===(attr===null?150:Number(attr)));}catch(e){return true;}})();
var autoHeight=opts.autoHeight===true||(opts.autoHeight!==false&&!compact&&!hostHeightSet),laneHeightOpt=finite(opts.laneHeight)?opts.laneHeight:0,wantH=0,laneHNow=46;
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;
@ -112,7 +121,8 @@
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.min(narrow?narrowLanes:maxLanes,Math.max(2,Math.floor((H-padT-padB)/(compact?26:narrow?40:46)))),ids=Array.from(counts.keys());
var laneH=laneHeightOpt||(compact?26:narrow?40:46),cap=narrow?narrowLanes:maxLanes;laneHNow=laneH;
var capacity=autoHeight?cap:Math.min(cap,Math.max(2,Math.floor((H-padT-padB)/laneH))),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.
@ -121,6 +131,9 @@
keep.sort(function(a,b){return (mineKeys.has(b)?1:0)-(mineKeys.has(a)?1:0);});
lanes=keep;if(ids.length>chosen.length)lanes.push('__others__');
laneOf=new Map();lanes.forEach(function(id,i){laneOf.set(id,i);});
// the wanted box height for these lanes (2.0.4): reported on change; applied here only under autoHeight
var nextH=padT+padB+Math.max(2,lanes.length)*laneH;
if(nextH!==wantH){wantH=nextH;emit('onSize',wantH,{lanes:lanes.length,laneHeight:laneH,narrow:narrow,compact:compact});if(autoHeight&&canvas.style.height!==wantH+'px')canvas.style.height=wantH+'px';}
var now=performance.now();
blocks.forEach(function(b){var i=laneOf.has(b.miner)?laneOf.get(b.miner):Math.max(0,lanes.length-1);
var y=padT+(i+.5)*(H-padT-padB)/Math.max(1,lanes.length);
@ -179,7 +192,9 @@
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,narrow:narrow,lanes:lanes.length,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,
// the lane geometry (2.0.4), so a page that sizes its own box reads no constants
laneHeight:laneHNow,padT:padT,padB:padB,capacity:narrow?narrowLanes:maxLanes,wantedHeight:wantH,autoHeight:autoHeight};
}
function poll(){
if(destroyed||opts.poll===false||doc.hidden)return;
@ -356,11 +371,11 @@
function destroy(){if(destroyed)return;destroyed=true;stopFrame();clearTimeout(pollTimer);clearTimeout(timeout);clearInterval(healthTimer);if(controller)controller.abort();remove.forEach(function(fn){fn();});if(ro)ro.disconnect();if(io)io.disconnect();if(mo)mo.disconnect();if(tip)tip.hidden=true;canvas.classList.remove('engaged');
[['tabindex',initialAttributes.tabindex],['role',initialAttributes.role],['aria-label',initialAttributes.label]].forEach(function(pair){if(pair[1]===null)canvas.removeAttribute(pair[0]);else canvas.setAttribute(pair[0],pair[1]);});canvas.style.touchAction=initialAttributes.touch;canvas.style.cursor=initialAttributes.cursor;instances.delete(canvas);model.clear();view.clear();blocks=[];
}
var api={push:push,setPaused:setPaused,zoom:zoom,engage:engage,getWindow:function(){return windowS;},getState:function(){return {state:state,reason:reason};},redraw:redraw,words:words,
var api={push:push,setPaused:setPaused,zoom:zoom,engage:engage,getWindow:function(){return windowS;},getWantedHeight:function(){return wantH;},getState:function(){return {state:state,reason:reason};},redraw:redraw,words:words,
destroy:destroy,follow:follow,setWindow:setWindow,setFilter:setFilter,setMotion:setMotion,select:select,getStats:stats,
getViewRange:function(){return {start:rightTime()-windowS*1000,end:rightTime(),filter:filter};},getBlocks:function(){return blocks.map(cloneBlock);},getCheckpoints:function(){return cps.map(function(c){return Object.assign({},c);});},
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.3'};
root.IgneumDag={mount:mount,version:'2.0.4'};
})(window);

View file

@ -1,5 +1,10 @@
/* Igneum DAG / Ember 2.0 (the EMBER 02 renderer pack, 7 Oct 2026). THE SHARED SOURCE: scene/live-dag.js; site/live-dag.js and
* app/igneum-app/ui/live-dag.js are byte-equal copies written by tools/scene/sync.mjs (the gate refuses a drifted copy). Edit here.
* 2.0.4 (7 Oct 2026): the box's height follows the lanes. laneHeight (default 46 px, 40 on a phone, 26 compact) times the lanes
* shown (at most maxLanes, narrowLanes on a phone, never under 2) plus the axis and padding is the scene's wanted height;
* onSize(heightPx, {lanes, laneHeight, narrow, compact}) reports it whenever it changes, so a page can size the box, and with
* autoHeight (true by default when the host set no CSS height on the canvas, forced either way by the option; never in
* compact mode, the app's card keeps its own height) the renderer sets canvas.style.height itself and lets every lane through.
* 2.0.3 (scene parity, 7 Oct 2026): (1) every push, size and theme change paints the current picture at once; only the motion
* loop waits for a visible document and an intersecting canvas (a page that loads with document.hidden true, or whose embedder
* never fires visibilitychange, showed a blank canvas while reporting live). (2) The narrow (phone) rule keys on the viewport
@ -61,6 +66,10 @@
// 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;
// autoHeight: the host set no height on the canvas (its computed height is still the attribute's, 150 by default) and this
// is not the compact card; read before size() touches the attribute
var hostHeightSet=(function(){try{var cs=getComputedStyle(canvas),attr=canvas.getAttribute('height'),h=parseFloat(cs.height);return canvas.style.height!==''||!(finite(h)&&Math.round(h)===(attr===null?150:Number(attr)));}catch(e){return true;}})();
var autoHeight=opts.autoHeight===true||(opts.autoHeight!==false&&!compact&&!hostHeightSet),laneHeightOpt=finite(opts.laneHeight)?opts.laneHeight:0,wantH=0,laneHNow=46;
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;
@ -112,7 +121,8 @@
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.min(narrow?narrowLanes:maxLanes,Math.max(2,Math.floor((H-padT-padB)/(compact?26:narrow?40:46)))),ids=Array.from(counts.keys());
var laneH=laneHeightOpt||(compact?26:narrow?40:46),cap=narrow?narrowLanes:maxLanes;laneHNow=laneH;
var capacity=autoHeight?cap:Math.min(cap,Math.max(2,Math.floor((H-padT-padB)/laneH))),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.
@ -121,6 +131,9 @@
keep.sort(function(a,b){return (mineKeys.has(b)?1:0)-(mineKeys.has(a)?1:0);});
lanes=keep;if(ids.length>chosen.length)lanes.push('__others__');
laneOf=new Map();lanes.forEach(function(id,i){laneOf.set(id,i);});
// the wanted box height for these lanes (2.0.4): reported on change; applied here only under autoHeight
var nextH=padT+padB+Math.max(2,lanes.length)*laneH;
if(nextH!==wantH){wantH=nextH;emit('onSize',wantH,{lanes:lanes.length,laneHeight:laneH,narrow:narrow,compact:compact});if(autoHeight&&canvas.style.height!==wantH+'px')canvas.style.height=wantH+'px';}
var now=performance.now();
blocks.forEach(function(b){var i=laneOf.has(b.miner)?laneOf.get(b.miner):Math.max(0,lanes.length-1);
var y=padT+(i+.5)*(H-padT-padB)/Math.max(1,lanes.length);
@ -179,7 +192,9 @@
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,narrow:narrow,lanes:lanes.length,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,
// the lane geometry (2.0.4), so a page that sizes its own box reads no constants
laneHeight:laneHNow,padT:padT,padB:padB,capacity:narrow?narrowLanes:maxLanes,wantedHeight:wantH,autoHeight:autoHeight};
}
function poll(){
if(destroyed||opts.poll===false||doc.hidden)return;
@ -356,11 +371,11 @@
function destroy(){if(destroyed)return;destroyed=true;stopFrame();clearTimeout(pollTimer);clearTimeout(timeout);clearInterval(healthTimer);if(controller)controller.abort();remove.forEach(function(fn){fn();});if(ro)ro.disconnect();if(io)io.disconnect();if(mo)mo.disconnect();if(tip)tip.hidden=true;canvas.classList.remove('engaged');
[['tabindex',initialAttributes.tabindex],['role',initialAttributes.role],['aria-label',initialAttributes.label]].forEach(function(pair){if(pair[1]===null)canvas.removeAttribute(pair[0]);else canvas.setAttribute(pair[0],pair[1]);});canvas.style.touchAction=initialAttributes.touch;canvas.style.cursor=initialAttributes.cursor;instances.delete(canvas);model.clear();view.clear();blocks=[];
}
var api={push:push,setPaused:setPaused,zoom:zoom,engage:engage,getWindow:function(){return windowS;},getState:function(){return {state:state,reason:reason};},redraw:redraw,words:words,
var api={push:push,setPaused:setPaused,zoom:zoom,engage:engage,getWindow:function(){return windowS;},getWantedHeight:function(){return wantH;},getState:function(){return {state:state,reason:reason};},redraw:redraw,words:words,
destroy:destroy,follow:follow,setWindow:setWindow,setFilter:setFilter,setMotion:setMotion,select:select,getStats:stats,
getViewRange:function(){return {start:rightTime()-windowS*1000,end:rightTime(),filter:filter};},getBlocks:function(){return blocks.map(cloneBlock);},getCheckpoints:function(){return cps.map(function(c){return Object.assign({},c);});},
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.3'};
root.IgneumDag={mount:mount,version:'2.0.4'};
})(window);