73 lines
60 KiB
HTML
73 lines
60 KiB
HTML
|
|
<!DOCTYPE html>
|
||
|
|
<html lang="en">
|
||
|
|
<head>
|
||
|
|
<meta charset="UTF-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
|
<title>Pulsing Nodal Webwork — exploration</title>
|
||
|
|
<style>
|
||
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
|
body { background: #0a0a0f; color: #3a3a4a; font-family: ui-monospace, monospace; overflow: hidden; }
|
||
|
|
#grid { padding: 20px; font-size: 12px; line-height: 1.4; white-space: pre; }
|
||
|
|
.c { display: inline-block; transition: all 0.3s; }
|
||
|
|
.c.active { color: #fde68a; text-shadow: 0 0 6px rgba(253,230,138,0.5); }
|
||
|
|
.c.strong { color: #34d399; text-shadow: 0 0 10px rgba(52,211,153,0.6); font-weight: bold; }
|
||
|
|
.c.dead { color: #1a1a2a; }
|
||
|
|
#info { position: fixed; bottom: 10px; left: 20px; color: #3a3a4a; font-size: 11px; }
|
||
|
|
#controls { position: fixed; top: 10px; right: 20px; color: #666; font-size: 11px; }
|
||
|
|
#controls span { cursor: pointer; margin-left: 12px; color: #fde68a; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div id="grid"></div>
|
||
|
|
<div id="info">neurameba · physarum exploration</div>
|
||
|
|
<div id="controls"><span id="play-btn">play</span><span id="reset-btn">reset</span></div>
|
||
|
|
<script>
|
||
|
|
const text = "# Roadmap\n\nWhat's coming next. No dates, no promises. Just direction.\n\n## Done\n\n- Three-column UI — left panel (docs/media), center (terminal), right (menu/ask). Repositionable CLI.\n- Email bridge — `/connect email`, receive bookmarks in your inbox, email `username@motd.social` to post\n- Telegram bridge — `/connect telegram`, receive bookmarks on your phone, message users from Telegram\n- Git integration — repos, stars, Voieneur backend\n- Avatar upload — upload an image, server converts to ASCII\n- Reply threading — /reply with grey quote block, /re for quick replies, /goto\n- Feed visible to guests without login\n\n## Next\n\n## Planned\n\n- Paid # posts — sponsors (12€/year) or one-time (2€), 24h promoted placement\n- Rust CLI client — same commands, real terminal, installable binary\n- Draggable media panels — floating windows for images, audio, video\n\n## Ideas\n\n- LLM-powered /find — smarter search behind the same command\n- Self-hosting guide — run your own motd instance\n- Plugin marketplace — discover community-built integrations\n- Media bridging — forward images and audio via Telegram and email\n\n## Sponsor\n\nSponsorship directly extends archive duration for everyone.\nDetails coming in /read support.\n";
|
||
|
|
const passes = [{"t":0,"r":19,"c":1,"a":"hold","s":0.24775306869215882,"ps":9,"e":100.63202454953728,"pr":1.1},{"t":0,"r":17,"c":0,"a":"died","s":0,"ps":8,"e":100,"pr":1},{"t":0,"r":14,"c":3,"a":"hold","s":0.22581534126904965,"ps":9,"e":100.4565227301524,"pr":1.1},{"t":0,"r":26,"c":7,"a":"extend","s":0.349831148839,"ps":9,"e":71.0140544334984,"pr":1.1},{"t":0,"r":20,"c":49,"a":"hold","s":0.2479215495933637,"ps":9,"e":100.6333723967469,"pr":1.1},{"t":1,"r":19,"c":1,"a":"extend","s":0.33200579180655826,"ps":9,"e":71.35664961879282,"pr":1.1},{"t":1,"r":14,"c":3,"a":"hold","s":0.17265366154261966,"ps":9,"e":100.48775202249335,"pr":1.1},{"t":1,"r":26,"c":7,"a":"hold","s":0.3245703479397124,"ps":9,"e":72.2606172170161,"pr":1.1},{"t":1,"r":20,"c":49,"a":"hold","s":0.3291010148206439,"ps":9,"e":101.91618051531206,"pr":1.1},{"t":1,"r":26,"c":6,"a":"extend","s":0.40982954152549744,"ps":5,"e":23.0742617625923,"pr":1.2000000000000002},{"t":1,"r":25,"c":7,"a":"extend","s":0.3536382911314847,"ps":5,"e":22.759590760385834,"pr":1.2000000000000002},{"t":2,"r":19,"c":1,"a":"hold","s":0.33200579180655826,"ps":8,"e":72.81269595324528,"pr":1.05},{"t":2,"r":14,"c":3,"a":"retracted","s":0.17265366154261966,"ps":8,"e":100.66898131483431,"pr":1.05},{"t":2,"r":26,"c":7,"a":"hold","s":0.3245703479397124,"ps":8,"e":73.6571800005338,"pr":1.05},{"t":2,"r":20,"c":49,"a":"retracted","s":0.3291010148206439,"ps":8,"e":103.34898863387721,"pr":1.05},{"t":2,"r":26,"c":6,"a":"extend","s":0.4026308755746064,"ps":4,"e":17.986716137032403,"pr":1.1500000000000001},{"t":2,"r":25,"c":7,"a":"hold","s":0.3330657753867663,"ps":4,"e":24.824116963479963,"pr":1.1500000000000001},{"t":2,"r":20,"c":1,"a":"hold","s":0.2013931180862246,"ps":5,"e":31.442566209886717,"pr":1.2000000000000002},{"t":2,"r":25,"c":6,"a":"hold","s":0.3301315940372879,"ps":5,"e":11.780022079123576,"pr":1.3000000000000003},{"t":2,"r":24,"c":7,"a":"hold","s":0.14095959899385055,"ps":5,"e":10.131787117830447,"pr":1.3000000000000003},{"t":3,"r":19,"c":1,"a":"hold","s":0.24775306869215882,"ps":8,"e":73.59472050278255,"pr":1.05},{"t":3,"r":26,"c":7,"a":"retracted","s":0.349831148839,"ps":8,"e":75.2558291912458,"pr":1.05},{"t":3,"r":26,"c":6,"a":"extend","s":0.40982954152549744,"ps":4,"e":14.465746728465467,"pr":1.1},{"t":3,"r":25,"c":7,"a":"hold","s":0.3536382911314847,"ps":4,"e":27.05322329253184,"pr":1.1},{"t":3,"r":20,"c":1,"a":"hold","s":0.22933925518328818,"ps":5,"e":32.52728025135302,"pr":1.2000000000000002},{"t":3,"r":25,"c":6,"a":"hold","s":0.3303214216331569,"ps":4,"e":13.822593452188832,"pr":1.2500000000000002},{"t":3,"r":24,"c":7,"a":"hold","s":0.2084724228697507,"ps":5,"e":11.049566500788453,"pr":1.3000000000000003},{"t":3,"r":26,"c":5,"a":"hold","s":0.3600289695930307,"ps":5,"e":9.83882438690099,"pr":1.2500000000000002},{"t":4,"r":19,"c":1,"a":"retracted","s":0.24775306869215882,"ps":7,"e":74.52674505231982,"pr":1},{"t":4,"r":26,"c":6,"a":"extend","s":0.40982954152549744,"ps":4,"e":12.001068142468613,"pr":1.05},{"t":4,"r":25,"c":7,"a":"retracted","s":0.3536382911314847,"ps":4,"e":29.282329621583717,"pr":1.05},{"t":4,"r":20,"c":1,"a":"retracted","s":0.22933925518328818,"ps":4,"e":33.761994292819324,"pr":1.1500000000000001},{"t":4,"r":25,"c":6,"a":"retracted","s":0.3301315940372879,"ps":4,"e":15.863646204487136,"pr":1.2000000000000002},{"t":4,"r":24,"c":7,"a":"retracted","s":0.2084724228697507,"ps":4,"e":12.11734588374646,"pr":1.2500000000000002},{"t":4,"r":26,"c":5,"a":"extend","s":0.4673722774633745,"ps":5,"e":8.97946182462559,"pr":1.2500000000000002},{"t":4,"r":27,"c":6,"a":"hold","s":0.23993283196491946,"ps":5,"e":7.3690683964902695,"pr":1.2000000000000002},{"t":5,"r":26,"c":6,"a":"extend","s":0.40982954152549744,"ps":4,"e":10.275793132270813,"pr":1},{"t":5,"r":26,"c":5,"a":"extend","s":0.4673722774633745,"ps":4,"e":8.482908031032808,"pr":1.2000000000000002},{"t":5,"r":27,"c":6,"a":"hold","s":0.2158059356727282,"ps":5,"e":8.345515881872096,"pr":1.2000000000000002},{"t":5,"r":26,"c":7,"a":"extend","s":0.400726911722727,"ps":5,"e":5.319391148387854,"pr":1.1500000000000001},{"t":5,"r":27,"c":
|
||
|
|
const lines = text.split('\n');
|
||
|
|
const gridEl = document.getElementById('grid');
|
||
|
|
const charEls = [];
|
||
|
|
for (let r = 0; r < lines.length; r++) {
|
||
|
|
const row = [];
|
||
|
|
for (let c = 0; c < lines[r].length; c++) {
|
||
|
|
const s = document.createElement('span');
|
||
|
|
s.className = 'c';
|
||
|
|
s.textContent = lines[r][c];
|
||
|
|
row.push(s);
|
||
|
|
gridEl.appendChild(s);
|
||
|
|
}
|
||
|
|
charEls.push(row);
|
||
|
|
gridEl.appendChild(document.createTextNode('\n'));
|
||
|
|
}
|
||
|
|
let tick = -1, playing = false, iv;
|
||
|
|
function apply(t) {
|
||
|
|
for (const r of charEls) for (const e of r) e.className = 'c';
|
||
|
|
const active = new Map();
|
||
|
|
for (const p of passes) {
|
||
|
|
if (p.t > t) break;
|
||
|
|
const k = p.r+','+p.c;
|
||
|
|
if (p.a === 'died' || p.a === 'retracted') active.set(k, 'dead');
|
||
|
|
else if (p.ps > 16) active.set(k, 'strong');
|
||
|
|
else active.set(k, 'active');
|
||
|
|
}
|
||
|
|
for (const [k, cls] of active) {
|
||
|
|
const [r, c] = k.split(',').map(Number);
|
||
|
|
if (charEls[r]?.[c]) charEls[r][c].className = 'c ' + cls;
|
||
|
|
}
|
||
|
|
document.getElementById('info').textContent = 'tick ' + t + ' · ' + [...active.values()].filter(v=>v!=='dead').length + ' alive';
|
||
|
|
}
|
||
|
|
function play() {
|
||
|
|
if (playing) return;
|
||
|
|
playing = true;
|
||
|
|
document.getElementById('play-btn').textContent = 'pause';
|
||
|
|
const max = passes.length > 0 ? passes[passes.length-1].t : 0;
|
||
|
|
iv = setInterval(() => { tick++; if (tick > max) { pause(); return; } apply(tick); }, 900);
|
||
|
|
}
|
||
|
|
function pause() { playing = false; clearInterval(iv); document.getElementById('play-btn').textContent = 'play'; }
|
||
|
|
function reset() { pause(); tick = -1; for (const r of charEls) for (const e of r) e.className = 'c'; document.getElementById('info').textContent = 'neurameba'; }
|
||
|
|
document.getElementById('play-btn').addEventListener('click', () => playing ? pause() : play());
|
||
|
|
document.getElementById('reset-btn').addEventListener('click', reset);
|
||
|
|
setTimeout(play, 1000);
|
||
|
|
</script>
|
||
|
|
</body>
|
||
|
|
</html>
|