diff --git a/site/index.html b/site/index.html index 5a6b2a20..e9d16c4e 100644 --- a/site/index.html +++ b/site/index.html @@ -195,6 +195,7 @@ footer .wrap{padding-block:48px 32px} Economics Litepaper Log + Live Get the miner @@ -252,7 +253,7 @@ footer .wrap{padding-block:48px 32px}
-
simulated preview
+
simulated preview
blocks 0proven 0locked 0
@@ -481,14 +482,18 @@ footer .wrap{padding-block:48px 32px} function size(){W=c.clientWidth;H=c.clientHeight;c.width=W*dpr;c.height=H*dpr;x.setTransform(dpr,0,0,dpr,0,0);S=W<600?18:26;GAP=W<600?S*2.5:S*3.1;speed=GAP/1000;k=Math.max(0.22,Math.min(1,(W/speed)/22000));} size();window.addEventListener('resize',size); function laneY(l){return H*0.14+l*(H*0.72/(lanes-1));} - function spawn(px){n++;var lane=Math.floor(Math.random()*lanes);var b={id:n,x:px,y:laneY(lane),born:performance.now(),shards:[0,0,0,0],claimed:[false,false,false,false],state:'mined',cp:n%6===0,parents:[],glow:0,flash:0}; - var cands=blocks.slice(-8).filter(function(q){return q.x80)blocks.shift();cb.textContent=n;} + // live path state: when the devnet observer is fresh, real blocks (lb) replace the random spawner + var liveMode=false,liveQ=[],liveSeen={},liveLanes={},liveRate=1,byHash={}; + function spawn(px,lb){n++;var lane=lb?lb.lane:Math.floor(Math.random()*lanes);var b={id:n,x:px,y:laneY(lane),born:performance.now(),shards:[0,0,0,0],claimed:[false,false,false,false],state:'mined',cp:n%6===0,parents:[],glow:0,flash:0,hash:lb?lb.hash:null}; + if(lb){byHash[lb.hash]=b;lb.parents.forEach(function(h){var q=byHash[h];if(q&&blocks.indexOf(q)>=0&&b.parents.length<4)b.parents.push(q);});} + else{var cands=blocks.slice(-8).filter(function(q){return q.x80){var old=blocks.shift();if(old.hash)delete byHash[old.hash];}cb.textContent=n;} function rr(px,py,w,h,r){x.beginPath();x.moveTo(px+r,py);x.arcTo(px+w,py,px+w,py+h,r);x.arcTo(px+w,py+h,px,py+h,r);x.arcTo(px,py+h,px,py,r);x.arcTo(px,py,px+w,py,r);x.closePath();} var last=0,lastShard=0; function frame(t){ - if(t-last>1000){spawn(W+S);last=t;} + if(liveMode){if(liveQ.length&&t-last>Math.max(60,1000/liveRate)){spawn(W+S,liveQ.shift());last=t;}} + else if(t-last>1000){spawn(W+S);last=t;} var dt=16; blocks.forEach(function(b){b.x-=speed*dt;var age=t-b.born; if(b.state==='mined'&&age>1200*k)b.state='proving'; @@ -522,6 +527,19 @@ footer .wrap{padding-block:48px 32px} var seedN=Math.ceil(W/GAP)+1,pv=Math.min(5,Math.max(2,Math.floor(seedN*0.45)));for(var i=0;ipv){b.state='proven';b.shards=[1,1,1,1];b.claimed=[true,true,true,true];proven++;if(b.cp){b.locked=true;locked++;}}else if(i>1){b.state='proving';b.claimed=[true,true,false,false];b.shards=[1,1,0,0];}} cb.textContent=n;cp.textContent=proven;cl.textContent=locked; requestAnimationFrame(frame); + // live path: one fetch on load; if the observer updated within 30 s, the label links to /live and real blocks feed the scene + var label=document.getElementById('viz-label'),labelHTML=label?label.innerHTML:''; + function laneFor(m){if(!(m in liveLanes))liveLanes[m]=Object.keys(liveLanes).length%lanes;return liveLanes[m];} + function setLive(on){if(on===liveMode)return;liveMode=on;if(label)label.innerHTML=on?'live devnet':labelHTML;} + function pullLive(first){return fetch('/api/live',{cache:'no-store'}).then(function(r){return r.ok?r.json():null;}).then(function(d){ + if(!d||!d.ok||!d.state||d.state.stale){setLive(false);return false;} + setLive(true);liveRate=Math.max(1,(d.state.blocks_per_second_60s||0)*1.5); + var fresh=d.blocks.filter(function(b){return !liveSeen[b.hash];});if(first)fresh=fresh.slice(-12); + d.blocks.forEach(function(b){liveSeen[b.hash]=1;}); + fresh.forEach(function(b){liveQ.push({hash:b.hash,parents:b.parents,lane:laneFor(b.miner||'?')});}); + if(liveQ.length>40)liveQ=liveQ.slice(-40);return true; + }).catch(function(){setLive(false);return false;});} + pullLive(true).then(function(on){if(on)setInterval(function(){pullLive(false);},2000);}); })(); // hourly program ticker diff --git a/site/vercel.json b/site/vercel.json index 4b5cc5d2..a975ad43 100644 --- a/site/vercel.json +++ b/site/vercel.json @@ -9,6 +9,7 @@ { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" }, { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" } ] }, - { "source": "/journey.json", "headers": [ { "key": "Cache-Control", "value": "public, max-age=300" } ] } + { "source": "/journey.json", "headers": [ { "key": "Cache-Control", "value": "public, max-age=300" } ] }, + { "source": "/api/live", "headers": [ { "key": "Cache-Control", "value": "public, max-age=1" } ] } ] }