73 lines
No EOL
16 KiB
HTML
73 lines
No EOL
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>silent fractures in motion — 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 = "# Changelog\n\n## v0.11.3 — Neurameba v3: Rust/WASM Physarum Organism\n\n- **Rust WASM organism** — replaced JS neural net with a Rust Physarum simulation compiled to WASM via wasm-pack. Dynamic architecture — not a fixed-size network.\n- **Variable neuron pools** — 4-64 neurons per node. Grow on surprise (novel patterns), shrink on familiarity.\n- **Energy economics** — thick tubes cost more to maintain, high activation feeds energy back. Nodes die at zero energy.\n- **Shuttle streaming** — sinusoidal oscillation on extension threshold. Tendrils pulse forward and retract like real Physarum.\n- **Cloning and merging** — organism splits at structural boundaries (headings, rules), merges when different-origin tendrils meet.\n- **Chemical trails** — activation values persisted in SQLite between explorations. The organism remembers where it's been.\n- **Habituation** — repeated text patterns dampen activation over time. Boilerplate gets boring.\n- **Topology metrics** — surviving nodes, branches, loops, max depth, thickness ratio, fractal dimension (box-counting), final energy. All sent to the builder LLM.\n- **Structural family diversity** — builder reports which animation family it chose; last 5 tracked in DB to avoid repetition.\n- **`/neurameba view`** — unified visualization with network-terrain slider. Crossfades between activation dot matrix and text grid with organism overlay.\n\n## v0.11.2 — Module UX1: Folders, Live Feed, Configurable Prefixes\n\n- **Folders** — posts belong to folders (replaces categories). `@username` folder auto-created per user. `random` is the default.\n- **Live feed** — SSE-powered real-time feed updates. New posts appear without refreshing.\n- **Configurable prefixes** — users can set custom command prefix (default `/`) and post prefix via `/settings`. Enables different input modes.\n- **`@username` folders** — auto-created on registration and backfilled for existing users.\n\n## v0.11.0 — Module 11: Neurameba / Voieneur\n\n- **Voieneur rebrand** — Forgejo renamed to Voieneur (voie + voyeur). Clone URLs now `git@voieneur.motd.social:user/repo.git`. Same codebase, new identity.\n- **Neurameba** — autonomous creative organism. Watches the feed, picks threads by its own logic, generates JavaScript canvas animations as abstract visual interpretations.\n- **Three-layer engine** — Layer 1: neural net organism explores text as 2D terrain. Layer 2: LLM tone analyzer extracts 6 emotional dimensions. Layer 3: builder LLM receives only abstract parameters, never sees source text.\n- **Docs as bootstrap** — when no threads with 2+ replies exist, neurameba reads platform docs as fallback input.\n- **Zero-gate publishing** — alive animations ship, dead output disappears silently.\n- **Voieneur repos** — each mushroom published with index.html, exploration.html (slime mold replay), network.json, LICENSE (MIT + origin), README (SEO-optimized).\n- **GitHub mirroring** — repos auto-mirrored to a GitHub org when configured.\n- **Feed announcements** — neurameba posts about each birth, linking back to the source conversation.\n- **GitHub issues** — reads open issues on its repos as secondary creative input.\n\n## v0.10.0 — Module 10: Voie Integration\n\n- **`/voie search`** — search European infrastructure providers via voie.fi API\n- **`/voie topics`**, **`/voie browse`**, **`/voie providers`** — browse the catalog by category\n- **`/voie compare`** — side-by-side comparison of two searches\n- **`/voie stats`** — catalog statistics (1,500+ providers, 36 countries)\n\n## v0.9.0 — Module 9: Three-Column UI\n\n- **Three-column layout** — left panel (content), center (terminal), right (sidebar). Inspired by VS Code. Panels take space from margins, center stays fixed.\n- **Left panel** — `/read` opens docs in side panel, `/view` opens media, `/expand` or `[read more]` opens full posts with replies. Close with `[x]`, `/close`, or Escape.\n- **Right panel** — sidebar becomes a proper column with `/ask` input field, connection status (Telegram/email), commands, and docs. /ask answers show in the panel, not the terminal.\n- **Prompt change** — `{username}@motd ~ $` → `{username}@motd >`. Cleaner, matches the m> logo.\n- **`/close`** — close the left panel\n- **`/settings cli [position]`** — move the input line: bottom (default), top, right, left, middle. Stored in localStorage.\n- **Keyboard shortcuts** — `Ctrl+B` toggles left panel, `Ctrl+J` toggles right panel\n- **Responsive** — wide (>1400px) all three columns, medium (1000-1400px) one side at a time, mobile (<800px) full-screen overlays\n- **Smooth transitions** — CSS transitions for panel open/close, no layout shifts in center column\n\n## v0.8.0 — Module 8: Email Bridge\n\n- **`/connect email`** — verify and link your email address\n- **`/disconnect email`** — unlink email\n- **Inbound email** — anyone can email `username@motd.social` to create a public post with `[email]` tag\n- **Email notifications** — connected users receive emails for bookmarked users, tags, mentions, and replies\n- **Reply to notifications** — reply to a notification email to create a threaded reply on motd\n- **189-char excerpt** — posts longer than 189 characters show a truncated preview with clickable `[read more]` in the feed. Applies to ALL posts globally.\n- **Email parsing** — strips signatures, reply chains, HTML. Plain text only. Max 500 chars stored.\n- **Postfix integration** — inbound mail handled via pipe to `email-handler.js`. Outbound via sendmail.\n- **Rate limiting** — unknown senders: 5 emails/hour per address\n- **Notification settings** — toggle email notifications independently in `/settings`\n- **DKIM/SPF/DMARC** — documented DNS setup for email deliverability\n\n## v0.7.0 — Module 7: Telegram Bridge\n\n- **`/connect telegram`** — one-click account linking via Telegram deep link\n- **`/disconnect telegram`** — unlink your Telegram account\n- **Telegram bot** — `@motdsocial_bot` runs inside the motd process via webhooks\n- **`/msg` command** — anyone on Telegram can message a motd user publicly. Posts appear in the feed with `[telegram]` tag\n- **Notifications** — connected users receive Telegram messages for bookmarked users, bookmarked tags, mentions, and replies\n- **Reply from Telegram** — reply to a forwarded message in Telegram to create a threaded reply on motd\n- **`/feed` in Telegram** — last 5 posts matching your bookmarks\n- **Rate limiting** — 5 msg/hr for non-motd users, 30 msg/hr for connected users\n- **Notification settings** — toggle notifications in `/settings`: bookmarked users/tags, mentions/replies\n- **Kill list integration** — filtered users don't appear in Telegram notifications\n- **Non-motd users** — Telegram users without an account post as `telegram:theirname`\n\n## v0.6.0 — Module 6: Voieneur Git Integration\n\n- **`/repo create`**, **`/repo list`**, **`/repo info`**, **`/repo delete`** — git repository management via Voieneur\n- **`/star`**, **`/unstar`**, **`/stars`** — star repositories\n- **Voieneur admin API** — all operations use admin API for simplified integration\n\n## v0.5.0 — Module 5: UI Redesign\n\n- **Centered terminal layout** — 800px default width, drag-to-resize from left/right edges (invisible 8px hit zones, min 400px). Width stored in localStorage.\n- **`/terminal --width`** — set terminal width in px, %, or reset to default\n- **Mobile** — 100% width, no resize\n- **Full sidebar** — replaces the old popup menu. motd branding, commands, doc links, footer.\n- **Client-side config** — localStorage overrides merged with server defaults\n- **`/config export`** — download your config as JSON\n- **`/config import`** — load config from a JSON file\n- **`/config reset`** — restore server defaults\n- **Guest landing** — last 5 posts shown on load for logged-out visitors\n\n## v0.4.0 — Module 4: Bookmarks, Docs & Tag Syntax\n\n- **Bookmarks** — `/bookmark`, `/unbookmark`, `/bookmarks`. One concept for users, tags, posts, docs. Private, no counts, no notifications.\n- **Bookmarks by type** — `/bookmarks --users`, `--tags`, `--posts`, `--docs`\n- **Right-click context menu** — terminal-styled context menu on usernames, tags, posts, doc links, and media. Shows relevant actions (bookmark, kill, reply, find, etc).\n- **Tag syntax update** — tags now use [brackets] instead of the reserved symbol. All existing posts migrated. `/find rust` and `/find [rust]` both work.\n- **Full documentation** — about, api, commands, milestone, roadmap, plugins, changelog, terms, support\n\n## v0.3.0 — Module 3: Post Lifecycle & Media\n\n- **Post lifecycle** — active (30d), archived (90d), then permanently purged. Daily cron job handles transitions.\n- **Media uploads** — PNG (5MB), MP3 (10MB), MP4 (25MB)\n- **Image pipeline** — original (max 1MB), thumbnail (80px), large (800px), ASCII art preview\n- **`/attach`** — inline media attachment in posts\n- **`/view`**, **`/play`**, **`/pause`** — media playback commands\n- **`/read`** — inline markdown document reader\n- **`/tree -cat`** — category and tag browser\n- **`/link`** — universal navigation command\n- **`/find --archive`** — search archived posts\n- **`/terminate-account`** — permanent account deletion with cascading wipe\n- **Short IDs** — 6-8 character alphanumeric IDs for posts and media\n- **Categories** — coding, creative, meta, general with seeded tag mappings\n- Removed following system — feed shows all posts, chronological, kill-filtered\n\n## v0.2.0 — Module 2: Posts & Feed\n\n- **Posts** — `/post` with 500 character limit\n- **Chronological feed** — no algorithm, kill-filtered\n- **User search** — `/find` by username or display name\n- **ASCII avatars** — deterministic 5x5 symmetric block pattern from username\n- **Filter score threshold** — community-filtered users suppressed from everyone's feed\n\n## v0.1.0 — Module 1: Terminal Shell & Auth\n\n- **Terminal UI** — dark background, monospace, command input\n- **Registration and login** — `/register`, `/login`, `/login-permanently`, `/logout`\n- **Profiles** — display name, bio, `/profile`, `/settings`\n- **`/kill`** — hidden moderation system with filter_score and time decay\n- **Command menu** — hamburger menu, tab completion, command history\n- **`/help`**, **`/menu`**, **`/clear`**\n- **Config-driven styling** — colors, font, prompt format via config.json\n- **API-first architecture** — all features accessible via REST endpoints\n";
|
|
const passes = [{"t":0,"r":75,"c":55,"a":"hold","s":0.22938325088512956,"ps":9,"e":100.48506600708103,"pr":1.1},{"t":0,"r":36,"c":67,"a":"extend","s":0.421189054857224,"ps":9,"e":71.41365870720044,"pr":1.1},{"t":0,"r":76,"c":14,"a":"hold","s":0.2464628311165622,"ps":9,"e":100.6217026489325,"pr":1.1},{"t":0,"r":6,"c":19,"a":"hold","s":0.28179018776859743,"ps":9,"e":100.90432150214878,"pr":1.1},{"t":1,"r":75,"c":55,"a":"hold","s":0.25142665213547816,"ps":9,"e":101.14647922416485,"pr":1.1},{"t":1,"r":36,"c":67,"a":"extend","s":0.4155969074685256,"ps":8,"e":51.47690377686405,"pr":1.05},{"t":1,"r":76,"c":14,"a":"hold","s":0.2569824476305214,"ps":9,"e":101.32756222997666,"pr":1.1},{"t":1,"r":6,"c":19,"a":"hold","s":0.2897486042013739,"ps":8,"e":102.02231033575977,"pr":1.05},{"t":1,"r":36,"c":66,"a":"hold","s":0.2852646412999839,"ps":5,"e":32.1379708620572,"pr":1.2000000000000002},{"t":2,"r":75,"c":55,"a":"retracted","s":0.25142665213547816,"ps":8,"e":101.95789244124867,"pr":1.05},{"t":2,"r":36,"c":67,"a":"extend","s":0.421189054857224,"ps":7,"e":37.657491351005284,"pr":1},{"t":2,"r":76,"c":14,"a":"retracted","s":0.2569824476305214,"ps":8,"e":102.18342181102084,"pr":1.05},{"t":2,"r":6,"c":19,"a":"retracted","s":0.27052919771469824,"ps":7,"e":103.13654391747735,"pr":1},{"t":2,"r":36,"c":66,"a":"extend","s":0.3761230278445338,"ps":5,"e":24.077868559369428,"pr":1.2000000000000002},{"t":2,"r":37,"c":67,"a":"hold","s":0.18627674706468011,"ps":5,"e":22.801744166602035,"pr":1.1500000000000001},{"t":3,"r":36,"c":67,"a":"hold","s":0.36248714781998903,"ps":6,"e":39.6573885335652,"pr":0.95},{"t":3,"r":36,"c":66,"a":"hold","s":0.3761230278445338,"ps":4,"e":26.4868527821257,"pr":1.1500000000000001},{"t":3,"r":37,"c":67,"a":"hold","s":0.1288196055962546,"ps":5,"e":23.08230101137207,"pr":1.1500000000000001},{"t":3,"r":37,"c":66,"a":"hold","s":0.19250150207578032,"ps":5,"e":11.109098542050283,"pr":1.3000000000000003},{"t":3,"r":36,"c":65,"a":"hold","s":0.13621000161079774,"ps":5,"e":10.658766538330422,"pr":1.3000000000000003},{"t":4,"r":36,"c":67,"a":"hold","s":0.35240782119313885,"ps":6,"e":41.57665110311031,"pr":0.95},{"t":4,"r":36,"c":66,"a":"hold","s":0.2852646412999839,"ps":4,"e":28.16896991252557,"pr":1.1500000000000001},{"t":4,"r":37,"c":67,"a":"retracted","s":0.1288196055962546,"ps":4,"e":23.51285785614211,"pr":1.1},{"t":4,"r":37,"c":66,"a":"hold","s":0.3051609893408715,"ps":5,"e":12.800386456777256,"pr":1.3000000000000003},{"t":4,"r":36,"c":65,"a":"hold","s":0.19725424511569392,"ps":5,"e":11.486800499255974,"pr":1.3000000000000003},{"t":5,"r":36,"c":67,"a":"retracted","s":0.35240782119313885,"ps":5,"e":43.64591367265542,"pr":0.8999999999999999},{"t":5,"r":36,"c":66,"a":"retracted","s":0.2852646412999839,"ps":4,"e":29.85108704292544,"pr":1.1},{"t":5,"r":37,"c":66,"a":"retracted","s":0.3051609893408715,"ps":4,"e":14.641674371504227,"pr":1.2500000000000002},{"t":5,"r":36,"c":65,"a":"retracted","s":0.19725424511569392,"ps":4,"e":12.464834460181525,"pr":1.2500000000000002}];
|
|
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> |