jetstream v2 in zig stream.waow.tech
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889// Bluesky's CDN also serves a 128px avatar_thumbnail. The plain avatar is// 1000px and decodes to 4 MB; hundreds of spheres requesting it at once took// Mobile Safari past 700 MB and got pages that embed Strata killed.export function avatarFor(url,size){return size<=128?url.replace('/img/avatar/plain/','/img/avatar_thumbnail/plain/'):url;}
// Canvas field: cached surfaces, one scheduled draw, no layout reads per gesture.export function createSphereMap(canvas, { identity, label, color, select, hover }) { const ctx=canvas.getContext('2d'); const surfaces=new Map(); let nodes=[],width=1,height=1,dpr=1,transform=d3.zoomIdentity,frame=0,hovered=null,selected=null; function schedule(){if(!frame)frame=requestAnimationFrame(draw);} const imageQueue=[]; let imageWorkers=0,surfaceBytes=0; function drainImages(){ while(imageWorkers<4&&imageQueue.length){ const job=imageQueue.shift(); if(surfaces.get(job.cacheKey)!==job.result)continue; imageWorkers++; const img=new Image();img.referrerPolicy='no-referrer'; let finished=false; const finish=()=>{if(finished)return;finished=true;clearTimeout(timeout);img.onload=img.onerror=null;imageWorkers--;schedule();drainImages();}; const timeout=setTimeout(()=>{img.src='';finish();},8000); img.onload=()=>{job.paint(img);job.result.hasImage=true;finish();};img.onerror=finish;img.src=avatarFor(job.avatar,job.size); } } function surface(key,avatar,radius){ const size=avatar?Math.min(512,Math.max(32,2**Math.ceil(Math.log2(radius*2*dpr)))):128; const cacheKey=(avatar||key)+':'+size; if(surfaces.has(cacheKey)){const cached=surfaces.get(cacheKey);surfaces.delete(cacheKey);surfaces.set(cacheKey,cached);return cached;} const tile=document.createElement('canvas');tile.width=tile.height=size; const tileCtx=tile.getContext('2d'); function paint(img){ tileCtx.setTransform(size/512,0,0,size/512,0,0); tileCtx.clearRect(0,0,512,512);tileCtx.save();tileCtx.beginPath();tileCtx.arc(256,256,256,0,Math.PI*2);tileCtx.clip(); if(img){const side=Math.min(img.naturalWidth,img.naturalHeight);tileCtx.drawImage(img,(img.naturalWidth-side)/2,(img.naturalHeight-side)/2,side,side,0,0,512,512);} else {tileCtx.fillStyle=key;tileCtx.fillRect(0,0,512,512);} const light=tileCtx.createRadialGradient(160,120,0,220,200,380); light.addColorStop(0,'#ffffff55');light.addColorStop(.4,'#ffffff00');light.addColorStop(.75,'#00000025');light.addColorStop(1,'#000000b0');tileCtx.fillStyle=light;tileCtx.fillRect(0,0,512,512);tileCtx.restore(); } paint(null); const previous=avatar?[...surfaces.entries()].find(([cachedKey,value])=>cachedKey.startsWith(avatar+':')&&value.hasImage)?.[1]:null; if(previous){tileCtx.setTransform(1,0,0,1,0,0);tileCtx.clearRect(0,0,size,size);tileCtx.drawImage(previous.tile,0,0,size,size);} const result={tile,hasImage:Boolean(previous),bytes:size*size*4}; while(surfaceBytes+result.bytes>16*1024*1024&&surfaces.size){const oldest=surfaces.keys().next().value;surfaceBytes-=surfaces.get(oldest).bytes;surfaces.delete(oldest);} surfaces.set(cacheKey,result);surfaceBytes+=result.bytes; if(avatar){const job={cacheKey,result,avatar,size,paint};if(previous)imageQueue.unshift(job);else imageQueue.push(job);drainImages();} return result; } function base(){return {scale:Math.min(width,height)/900,x:(width-Math.min(width,height))/2,y:(height-Math.min(width,height))/2};} function screen(node){const b=base();return {x:(node.x*b.scale+b.x)*transform.k+transform.x,y:(node.y*b.scale+b.y)*transform.k+transform.y,r:node.r*b.scale*transform.k};} function inView({x,y,r}){return r>=.6&&x+r>0&&x-r<width&&y+r>0&&y-r<height;} function draw(){ frame=0;ctx.setTransform(dpr,0,0,dpr,0,0);ctx.clearRect(0,0,width,height);ctx.textAlign='center';ctx.textBaseline='middle'; for(const node of nodes){ const p=screen(node);if(!inView(p))continue; const avatar=identity(node.key)?.avatar;const sprite=surface(node.color,avatar,p.r); ctx.drawImage(sprite.tile,p.x-p.r,p.y-p.r,p.r*2,p.r*2); if(node.key===hovered||node.key===selected){ctx.strokeStyle='#fff';ctx.lineWidth=2;ctx.beginPath();ctx.arc(p.x,p.y,Math.max(1,p.r-1),0,Math.PI*2);ctx.stroke();} if(p.r>26&&!sprite.hasImage){const font=Math.min(14,Math.max(10,p.r/5));const max=Math.max(3,Math.floor(p.r*1.6/(font*.63)));const title=node.title.length>max?node.title.slice(0,max-1)+'…':node.title;ctx.font=`600 ${font}px ui-monospace,monospace`;ctx.fillStyle='#071119';ctx.fillText(title,p.x,p.y);} } } const zoom=d3.zoom().extent(()=>[[0,0],[width,height]]).translateExtent([[0,0],[width,height]]).scaleExtent([1,12]).clickDistance(8).tapDistance(8) .filter(event=>!event.button&&(!event.type.startsWith('touch')||Boolean(canvas.closest('dialog[open]')))&&(event.type!=='wheel'||event.ctrlKey||Boolean(canvas.closest('dialog[open]')))) .on('start',()=>{hovered=null;hover(null);}) .on('zoom',event=>{transform=event.transform;schedule();}); d3.select(canvas).call(zoom).on('dblclick.zoom',null); function hit(event){const [x,y]=d3.pointer(event,canvas);const b=base();const [wx,wy]=transform.invert([x,y]);return nodes.find(n=>Math.hypot(n.x-(wx-b.x)/b.scale,n.y-(wy-b.y)/b.scale)<=n.r);} canvas.addEventListener('click',event=>{if(event.defaultPrevented||event.detail>1)return;const node=hit(event);if(node)select(node.key);}); canvas.addEventListener('pointermove',event=>{if(event.pointerType==='touch'||event.buttons)return;const node=hit(event);if(hovered!==node?.key){hovered=node?.key;canvas.style.cursor=node?'pointer':'grab';hover(node?node.title:null);schedule();}}); canvas.addEventListener('pointerleave',()=>{hovered=null;hover(null);schedule();}); const observer=new ResizeObserver(entries=>{ const rect=entries[0].contentRect;if(!rect.width||!rect.height)return; const old=base();const center=transform.invert([width/2,height/2]); const world={x:(center[0]-old.x)/old.scale,y:(center[1]-old.y)/old.scale}; width=rect.width;height=rect.height;const next=base(); dpr=Math.min(devicePixelRatio||1,2);canvas.width=Math.round(width*dpr);canvas.height=Math.round(height*dpr); zoom.translateExtent([[0,0],[width,height]]); const t=transform.k===1?d3.zoomIdentity:d3.zoomIdentity.translate(width/2-(world.x*next.scale+next.x)*transform.k,height/2-(world.y*next.scale+next.y)*transform.k).scale(transform.k); d3.select(canvas).call(zoom.transform,t);schedule(); });observer.observe(canvas); return { setItems(items,volume){const root=d3.hierarchy({children:items.map(item=>({key:item.key,count:item.count}))}).sum(d=>d.count?(volume?d.count:Math.pow(d.count,.32)):0).sort((a,b)=>b.value-a.value);d3.pack().size([880,880]).padding(volume?0:3)(root);nodes=root.children.map(n=>({key:n.data.key,x:n.x+10,y:n.y+10,r:n.r,title:label(n.data.key),color:color(n.data.key)}));selected=null;d3.select(canvas).call(zoom.transform,d3.zoomIdentity);schedule();}, refresh(){for(const node of nodes)node.title=label(node.key);schedule();}, zoomBy(factor){d3.select(canvas).call(zoom.scaleBy,factor);}, focus(key){selected=key;const node=nodes.find(n=>n.key===key);if(!node)return;const b=base();const k=Math.min(12,Math.max(1,240/node.r));d3.select(canvas).call(zoom.transform,d3.zoomIdentity.translate(width/2-(node.x*b.scale+b.x)*k,height/2-(node.y*b.scale+b.y)*k).scale(k));}, reset(){d3.select(canvas).call(zoom.transform,d3.zoomIdentity);}, };}