Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

boot: spring animation swaps flowers for flying birds + waits for full motd

Why: spring boot was all bloom; moving to a bird theme that drifts across
the screen feels more alive, and the jump out of the boot screen was
cutting off the motd mid-reveal.

- index.mjs: SPRING_BIRDS / drawTurtleBird (egg body, flapping wing,
forked tail, beak, eye+sparkle, tucked feet), horizontal drift with
edge wrap, per-bird hue/belly/beak
- index.mjs: motdComplete getter on acBootCanvas (true when all chars
have revealed at the slowest mode rate, 12 chars/s)
- boot.mjs: hideBootLog polls motdComplete every 100ms, capped at 8s
so boot can't stall

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+57 -24
+45 -22
system/netlify/functions/index.mjs
··· 1302 1302 var isNotepat=location.hostname==='notepat.com'||location.hostname==='www.notepat.com'||location.pathname==='/notepat'||location.pathname.startsWith('/notepat?')||location.pathname.startsWith('/notepat/'); 1303 1303 // Notebook: Python/Jupyter notebook with scientific aesthetic 1304 1304 var isNotebook=qs.indexOf('notebook=true')>=0; 1305 - // Boot animation mode: 'spring' (turtle-graphics flowers, default), 'serious' (clean/refined), or 'aesthetic' (VHS/glitch) 1305 + // Boot animation mode: 'spring' (turtle-graphics birds, default), 'serious' (clean/refined), or 'aesthetic' (VHS/glitch) 1306 1306 var bootTheme=params.get('boot')||'spring';var isSerious=bootTheme==='serious';var isSpring=bootTheme==='spring'; 1307 1307 // Density param for scaling (default 1, FF1 uses 8 for 4K) 1308 1308 var densityMatch=qs.match(/density=(\d+)/);var densityParam=densityMatch?parseInt(densityMatch[1]):1; ··· 1371 1371 var NP_KEYS=[];var NP_PARTICLES=[];var NP_LAST_KEY=0;var NP_KEY_INTERVAL=120; 1372 1372 var NP_NOTE_NAMES=['C','D','E','F','G','A','B']; 1373 1373 var NP_KEY_COLS=[[255,107,157],[78,205,196],[255,217,61],[149,225,211],[255,154,162],[170,150,218],[112,214,255],[255,183,77]]; 1374 - // 🌼 Spring boot animation state — procedural turtle-graphics flowers 1375 - var SPRING_FLOWERS=[],SPRING_INIT=false; 1374 + // 🐦 Spring boot animation state — procedural turtle-graphics birds 1375 + var SPRING_BIRDS=[],SPRING_INIT=false; 1376 1376 var SPRING_FONTS_LIGHT=['serif','monospace','YWFTProcessing-Bold, monospace','cursive','Georgia, serif','Courier New, monospace']; 1377 - var SPRING_PETAL_HUES=[48,52,40,330,300,200,160,20,12]; // yellows/pinks/lavenders/sky/mints/peaches 1378 - function springInit(S){SPRING_FLOWERS=[];var n=14;for(var i=0;i<n;i++){SPRING_FLOWERS.push({px:Math.random(),py:0.35+Math.random()*0.55,r:(6+Math.random()*10)*S,petals:5+Math.floor(Math.random()*4),hue:SPRING_PETAL_HUES[i%SPRING_PETAL_HUES.length],phase:Math.random()*Math.PI*2,sway:0.6+Math.random()*1.2,centerHue:40+Math.random()*30,leafSide:Math.random()>0.5?1:-1});}SPRING_INIT=true;} 1379 - function drawTurtleFlower(ctx,W,H,fl,t,S,isLightMode){var cx=fl.px*W+Math.sin(t*0.6+fl.phase)*fl.sway*4*S;var cy=fl.py*H+Math.sin(t*0.4+fl.phase*1.3)*fl.sway*2*S;var r=fl.r; 1380 - // Stem (curved line, drawn turtle-style with short segments) 1381 - ctx.save();ctx.strokeStyle=isLightMode?'rgb(70,140,80)':'rgb(140,200,150)';ctx.lineWidth=Math.max(1,1.5*S);ctx.beginPath();var sx0=cx,sy0=cy+r*0.7,segs=12;for(var s=0;s<=segs;s++){var u=s/segs;var sway=Math.sin(u*Math.PI+t*0.5+fl.phase)*r*0.5*u;ctx.lineTo(sx0+sway,sy0+u*r*3.5);}ctx.stroke(); 1382 - // Leaf 1383 - var lx=sx0+fl.leafSide*r*0.4,ly=sy0+r*1.6;ctx.beginPath();ctx.moveTo(lx,ly);ctx.quadraticCurveTo(lx+fl.leafSide*r*0.8,ly+r*0.2,lx+fl.leafSide*r*1.4,ly-r*0.1);ctx.quadraticCurveTo(lx+fl.leafSide*r*0.6,ly+r*0.4,lx,ly);ctx.fillStyle=isLightMode?'rgba(80,160,90,0.85)':'rgba(150,210,160,0.85)';ctx.fill(); 1384 - // Petals (turtle-style: parametric loop r=sin(theta)*len) 1385 - for(var p=0;p<fl.petals;p++){var ang=(p/fl.petals)*Math.PI*2+t*0.3+fl.phase*0.2;ctx.save();ctx.translate(cx,cy);ctx.rotate(ang);ctx.beginPath();var L=r*1.3,steps=14;for(var i=0;i<=steps;i++){var th=i/steps*Math.PI;var rr=Math.sin(th)*L;ctx.lineTo(rr*Math.cos(th),rr*Math.sin(th)-r*0.1);}ctx.closePath();var sat=isLightMode?75:65,lum=isLightMode?70:55;ctx.fillStyle='hsl('+fl.hue+','+sat+'%,'+lum+'%)';ctx.globalAlpha=0.9;ctx.fill();ctx.strokeStyle='hsla('+fl.hue+',80%,'+(lum-15)+'%,0.6)';ctx.lineWidth=Math.max(0.5,0.7*S);ctx.stroke();ctx.restore();} 1386 - // Center disc 1387 - ctx.beginPath();ctx.arc(cx,cy,r*0.55,0,Math.PI*2);ctx.fillStyle='hsl('+fl.centerHue+',85%,'+(isLightMode?55:65)+'%)';ctx.globalAlpha=1;ctx.fill();ctx.strokeStyle='hsla('+fl.centerHue+',90%,30%,0.6)';ctx.lineWidth=Math.max(0.5,0.6*S);ctx.stroke(); 1388 - // Pollen dots 1389 - for(var d=0;d<5;d++){var da=d/5*Math.PI*2+t*1.2+fl.phase;ctx.beginPath();ctx.arc(cx+Math.cos(da)*r*0.3,cy+Math.sin(da)*r*0.3,Math.max(0.5,0.7*S),0,Math.PI*2);ctx.fillStyle='hsl('+(fl.centerHue-15)+',95%,30%)';ctx.fill();} 1377 + var SPRING_BIRD_HUES=[205,185,50,30,340,280,160,15,100]; // sky/teal/canary/robin/rose/violet/mint/coral/moss 1378 + function springInit(S){SPRING_BIRDS=[];var n=14;for(var i=0;i<n;i++){SPRING_BIRDS.push({px:Math.random(),py:0.2+Math.random()*0.7,r:(4.5+Math.random()*6)*S,hue:SPRING_BIRD_HUES[i%SPRING_BIRD_HUES.length],phase:Math.random()*Math.PI*2,bob:0.5+Math.random()*1.2,flapSpeed:5+Math.random()*5,vx:(0.0014+Math.random()*0.0022)*(Math.random()>0.5?1:-1),bellyHue:40+Math.random()*30,beakHue:Math.random()>0.5?32:48});}SPRING_INIT=true;} 1379 + function drawTurtleBird(ctx,W,H,b,t,S,isLightMode){ 1380 + // Drift horizontally, wrap around edges 1381 + b.px+=b.vx;if(b.px>1.15)b.px=-0.15;if(b.px<-0.15)b.px=1.15; 1382 + var cx=b.px*W,cy=b.py*H+Math.sin(t*1.2+b.phase)*b.bob*3*S,r=b.r; 1383 + var dir=b.vx>=0?1:-1,flap=Math.sin(t*b.flapSpeed+b.phase); 1384 + ctx.save();ctx.translate(cx,cy);ctx.scale(dir,1); 1385 + var outline=isLightMode?'hsla('+b.hue+',70%,20%,0.7)':'hsla('+b.hue+',60%,15%,0.7)'; 1386 + ctx.strokeStyle=outline;ctx.lineWidth=Math.max(0.5,0.7*S); 1387 + // Tail — forked (turtle-style polyline) 1388 + ctx.beginPath();ctx.moveTo(-r*0.85,0);ctx.lineTo(-r*1.8,-r*0.35);ctx.lineTo(-r*1.5,0);ctx.lineTo(-r*1.8,r*0.35);ctx.closePath(); 1389 + ctx.fillStyle='hsl('+b.hue+',70%,'+(isLightMode?40:52)+'%)';ctx.fill();ctx.stroke(); 1390 + // Body (egg-shape) 1391 + ctx.beginPath();ctx.ellipse(0,0,r,r*0.65,0,0,Math.PI*2); 1392 + ctx.fillStyle='hsl('+b.hue+',65%,'+(isLightMode?55:60)+'%)';ctx.fill();ctx.stroke(); 1393 + // Belly patch 1394 + ctx.beginPath();ctx.ellipse(0,r*0.15,r*0.7,r*0.4,0,0,Math.PI*2); 1395 + ctx.fillStyle='hsl('+b.bellyHue+',85%,'+(isLightMode?78:80)+'%)';ctx.globalAlpha=0.75;ctx.fill();ctx.globalAlpha=1; 1396 + // Wing — flaps; turtle-style sin(theta) petal shape 1397 + ctx.save();ctx.translate(-r*0.1,-r*0.15);ctx.rotate(flap*0.55);ctx.beginPath(); 1398 + var wL=r*1.2,wsteps=14;for(var wi=0;wi<=wsteps;wi++){var wth=wi/wsteps*Math.PI;var wrr=Math.sin(wth)*wL;ctx.lineTo(wrr*Math.cos(wth)-r*0.3,wrr*Math.sin(wth)*0.55-r*0.2);} 1399 + ctx.closePath();ctx.fillStyle='hsl('+b.hue+',60%,'+(isLightMode?42:48)+'%)';ctx.fill();ctx.stroke();ctx.restore(); 1400 + // Head 1401 + ctx.beginPath();ctx.arc(r*0.85,-r*0.35,r*0.5,0,Math.PI*2); 1402 + ctx.fillStyle='hsl('+b.hue+',68%,'+(isLightMode?58:64)+'%)';ctx.fill();ctx.stroke(); 1403 + // Beak 1404 + ctx.beginPath();ctx.moveTo(r*1.25,-r*0.35);ctx.lineTo(r*1.75,-r*0.22);ctx.lineTo(r*1.25,-r*0.12);ctx.closePath(); 1405 + ctx.fillStyle='hsl('+b.beakHue+',95%,'+(isLightMode?48:55)+'%)';ctx.fill(); 1406 + ctx.strokeStyle='hsla('+b.beakHue+',90%,25%,0.8)';ctx.stroke(); 1407 + // Eye + sparkle 1408 + ctx.beginPath();ctx.arc(r*0.95,-r*0.5,Math.max(0.8,1.0*S),0,Math.PI*2);ctx.fillStyle='#111';ctx.fill(); 1409 + ctx.beginPath();ctx.arc(r*1.02,-r*0.56,Math.max(0.3,0.35*S),0,Math.PI*2);ctx.fillStyle='#fff';ctx.fill(); 1410 + // Tiny feet tucked under body 1411 + ctx.strokeStyle=outline;ctx.lineWidth=Math.max(0.5,0.6*S); 1412 + ctx.beginPath();ctx.moveTo(-r*0.15,r*0.6);ctx.lineTo(-r*0.1,r*0.85);ctx.moveTo(r*0.15,r*0.6);ctx.lineTo(r*0.2,r*0.85);ctx.stroke(); 1390 1413 ctx.restore();} 1391 1414 // 📊 Notebook scientific aesthetic boot animation state 1392 1415 var NB_DATA_POINTS=[];var NB_GRID_LINES=[];var NB_WAVEFORMS=[];var NB_LAST_SPAWN=0; ··· 1539 1562 var logFS=densityParam===1&&isDeviceMode?Math.max(14,Math.floor(H/60)):4*S*dS; 1540 1563 x.font=logFS+'px monospace';var logY=(densityParam===1&&isDeviceMode?Math.floor(H/20):16*S*dS)+embedPad;var logSpacing=densityParam===1&&isDeviceMode?Math.floor(logFS*1.5):7*S*dS;for(var li=0;li<lines.length&&li<10;li++){var ln=lines[li],ly=logY+li*logSpacing,la=Math.max(0.3,1-li*0.08),lc=klCols[li%klCols.length];var tw=x.measureText(ln.text).width;var logX=densityParam===1&&isDeviceMode?20:10*S*dS;var textX=densityParam===1&&isDeviceMode?30:(logX+3*S*dS);var pillH=densityParam===1&&isDeviceMode?Math.floor(logFS*1.2):6*S*dS;var pillR=densityParam===1&&isDeviceMode?6:3*S*dS;var pillW=tw+(textX-logX)*2;x.globalAlpha=la*0.15;x.fillStyle='rgb('+lc[0]+','+lc[1]+','+lc[2]+')';x.beginPath();x.roundRect(logX,ly-pillH*0.65,pillW,pillH,pillR);x.fill();x.globalAlpha=la;x.fillStyle='rgb('+lc[0]+','+lc[1]+','+lc[2]+')';x.fillText(ln.text,textX,ly);} 1541 1564 x.globalAlpha=1;requestAnimationFrame(anim);return;} 1542 - // 🌼 Spring mode — yellowish, light, with procedural turtle-graphics flowers (default) 1565 + // 🐦 Spring mode — yellowish, light, with procedural turtle-graphics birds (default) 1543 1566 if(isSpring){ 1544 1567 if(!SPRING_INIT)springInit(S); 1545 1568 // Soft sunny gradient bg (cream/butter for light, deep amber for dark) ··· 1552 1575 x.globalAlpha=1; 1553 1576 // Sunbeam streaks from top-right 1554 1577 for(var br=0;br<5;br++){x.save();x.globalAlpha=0.06+Math.sin(t*0.5+br)*0.03;x.fillStyle=isLightMode?'#fff7c0':'#ffeeaa';x.translate(W*0.85,0);x.rotate(0.6+br*0.08);x.fillRect(-2*S,0,4*S,H*1.4);x.restore();} 1555 - // Draw all flowers (sorted by y for fake depth) 1556 - var sortedFl=SPRING_FLOWERS.slice().sort(function(a,b){return a.py-b.py;}); 1557 - for(var fi=0;fi<sortedFl.length;fi++){drawTurtleFlower(x,W,H,sortedFl[fi],t,S,isLightMode);} 1578 + // Draw all birds (sorted by y for fake depth) 1579 + var sortedBd=SPRING_BIRDS.slice().sort(function(a,b){return a.py-b.py;}); 1580 + for(var fi=0;fi<sortedBd.length;fi++){drawTurtleBird(x,W,H,sortedBd[fi],t,S,isLightMode);} 1558 1581 // Logo top-left (small, soft) 1559 1582 var lS=21*S,lX=5*S,lY=5*S;var logoImg=imgFullLoaded?imgFull:img; 1560 1583 x.imageSmoothingEnabled=imgFullLoaded;x.globalAlpha=0.92;x.drawImage(logoImg,lX,lY,lS,lS);x.globalAlpha=1; ··· 1572 1595 var sec=(performance.now()-bootStart)/1000;var secT=sec.toFixed(2)+'s';x.font='bold '+(4*S)+'px monospace';x.globalAlpha=0.55;x.fillStyle=isLightMode?'#7a5a20':'#ffd870';x.fillText(secT,W-x.measureText(secT).width-5*S,lY+5*S);x.globalAlpha=1; 1573 1596 // Handle (if present) 1574 1597 if(uH){var hAge=(performance.now()-hST)/1000,hFade=Math.min(1,hAge*2);x.font='bold '+(5*S)+'px '+SPRING_FONTS_LIGHT[1];x.globalAlpha=hFade*0.85;x.fillStyle=isLightMode?'#5a3a10':'#ffe6a8';x.fillText(uH,5*S,tBaseY+tFS+6*S);x.globalAlpha=1;} 1575 - // 🌼 MOTD — chars float chaotically among the flowers, each with own font/phase 1598 + // 🐦 MOTD — chars float chaotically among the birds, each with own font/phase 1576 1599 if(motd){var mAge=(performance.now()-motdStart)/1000;var mFade=Math.min(1,mAge*0.5);var maxW=W*0.85;var motdFS=Math.min(18*S,Math.max(8*S,maxW/Math.max(8,motd.length*0.6)));x.font='bold '+motdFS+'px monospace'; 1577 1600 // Wrap motd into lines using rough char width 1578 1601 var roughChars=Math.max(8,Math.floor(maxW/(motdFS*0.6)));var mLines=wrapMotdText(motd,roughChars);var lineH=motdFS*1.4;var startY=H/2-(mLines.length*lineH)/2; ··· 2181 2204 // Touch interaction visual feedback - ripple effect 2182 2205 if(touchGlitch>0.05){var rippleR=Math.max(1,(1-touchGlitch)*100*S+10*S);x.globalAlpha=touchGlitch*0.3;x.strokeStyle=isLightMode?'rgb(100,60,140)':'rgb(200,150,255)';x.lineWidth=2*S;x.beginPath();x.arc(touchX*W,touchY*H,rippleR,0,Math.PI*2);x.stroke();x.globalAlpha=1;} 2183 2206 x.globalAlpha=1;requestAnimationFrame(anim);}anim(); 2184 - var obj={log:add,hide:function(){run=false;c.remove();},setHandle:setH,addFile:addFile,netPulse:netPulse,setSessionConnected:setConn,setErrorMode:setErrorMode};Object.defineProperty(obj,'motd',{get:function(){return motd;},set:function(v){motd=v;motdStart=performance.now();}});Object.defineProperty(obj,'motdHandle',{get:function(){return motdHandle;},set:function(v){motdHandle=v||'';}});return obj;})(); 2207 + var obj={log:add,hide:function(){run=false;c.remove();},setHandle:setH,addFile:addFile,netPulse:netPulse,setSessionConnected:setConn,setErrorMode:setErrorMode};Object.defineProperty(obj,'motd',{get:function(){return motd;},set:function(v){motd=v;motdStart=performance.now();}});Object.defineProperty(obj,'motdHandle',{get:function(){return motdHandle;},set:function(v){motdHandle=v||'';}});Object.defineProperty(obj,'motdComplete',{get:function(){if(!motd)return true;if(!motdStart)return false;var age=(performance.now()-motdStart)/1000;return age*12>=motd.length+0.5;}});return obj;})(); 2185 2208 window.acBOOT_LOG_CANVAS=function(m){if(window.acBootCanvas&&window.acBootCanvas.log)window.acBootCanvas.log(m);}; 2186 2209 window.acBOOT_ADD_FILE=function(n,s){if(window.acBootCanvas&&window.acBootCanvas.addFile)window.acBootCanvas.addFile(n,s);}; 2187 2210 window.acBOOT_NET_PULSE=function(){if(window.acBootCanvas&&window.acBootCanvas.netPulse)window.acBootCanvas.netPulse();};
+12 -2
system/public/aesthetic.computer/boot.mjs
··· 242 242 243 243 // Hide the boot log overlay (called when boot completes) 244 244 let bootLogHidden = false; 245 + let hideBootLogFirstCall = 0; 246 + const HIDE_BOOT_MAX_WAIT_MS = 8000; // cap motd wait so boot can never stall forever 245 247 function hideBootLog() { 246 248 if (bootLogHidden) return; 249 + if (!hideBootLogFirstCall) hideBootLogFirstCall = performance.now(); 250 + // Wait for the motd to finish revealing before jumping off the boot screen 251 + const waited = performance.now() - hideBootLogFirstCall; 252 + const bc = window.acBootCanvas; 253 + if (waited < HIDE_BOOT_MAX_WAIT_MS && bc?.motd && bc?.motdComplete === false) { 254 + setTimeout(hideBootLog, 100); 255 + return; 256 + } 247 257 bootLogHidden = true; 248 - if (window.acBootCanvas?.hide) { 249 - window.acBootCanvas.hide(); 258 + if (bc?.hide) { 259 + bc.hide(); 250 260 } 251 261 const elapsedTotal = Math.round(performance.now() - bootStartTime); 252 262 bootTelemetry.complete({