73 lines
No EOL
53 KiB
HTML
73 lines
No EOL
53 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Electromagnetic Teal Quiver — 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 = "# API Reference\n\nmotd is API-first. Every feature is an endpoint. Build clients, bots, integrations — whatever you want.\n\n## Base URL\n\n```\nhttps://motd.social/api\n```\n\nLocal dev: `http://localhost:3006/api`\n\n## Response Format\n\nEvery endpoint returns the same envelope:\n\n**Success:**\n```json\n{ \"ok\": true, \"data\": { ... } }\n```\n\n**Error:**\n```json\n{ \"ok\": false, \"error\": \"Something went wrong.\" }\n```\n\nHTTP status codes are standard: 200 success, 400 bad request, 401 unauthorized, 404 not found, 409 conflict.\n\n## Authentication\n\nMost endpoints require authentication. Get a token by registering or logging in, then send it as a Bearer token or let the browser handle it via cookies.\n\n**Header auth (for scripts and clients):**\n```\nAuthorization: Bearer <token>\n```\n\n**Cookie auth (for the web client):**\nThe login endpoints set an `motd_session` httpOnly cookie automatically.\n\nSessions expire after 7 days. Use `/login-permanently` in the web client to persist across tabs (stored in localStorage instead of sessionStorage — the token is the same).\n\n---\n\n## Auth\n\n### POST /api/auth/register\n\nCreate a new account.\n\n**Auth:** none\n\n**Body:**\n```json\n{ \"username\": \"alice\", \"password\": \"hunter2pwd\" }\n```\n\n**Rules:**\n- Username: 3-20 characters, letters, numbers, underscore only\n- Password: minimum 8 characters\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"token\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"user\": {\n \"username\": \"alice\",\n \"display_name\": \"alice\"\n }\n }\n}\n```\n\n**Errors:**\n- `400` — username or password missing, invalid format\n- `409` — username taken\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/auth/register \\\n -H \"Content-Type: application/json\" \\\n -d '{\"username\":\"alice\",\"password\":\"hunter2pwd\"}'\n```\n\n### POST /api/auth/login\n\nLog in to an existing account.\n\n**Auth:** none\n\n**Body:**\n```json\n{ \"username\": \"alice\", \"password\": \"hunter2pwd\" }\n```\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"token\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"user\": {\n \"username\": \"alice\",\n \"display_name\": \"Alice\"\n }\n }\n}\n```\n\n**Errors:**\n- `400` — missing fields\n- `401` — invalid username or password\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/auth/login \\\n -H \"Content-Type: application/json\" \\\n -d '{\"username\":\"alice\",\"password\":\"hunter2pwd\"}'\n```\n\n### GET /api/auth/me\n\nGet the current authenticated user.\n\n**Auth:** required\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"user\": {\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"bio\": \"building things\",\n \"created_at\": \"2026-03-15T10:30:00.000Z\"\n }\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/auth/me \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n### POST /api/auth/logout\n\nEnd the current session.\n\n**Auth:** required\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Logged out.\" } }\n```\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/auth/logout \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n---\n\n## Profile\n\n### GET /api/profile/:username\n\nView a user's profile.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"bio\": \"building things\",\n \"created_at\": \"2026-03-15T10:30:00.000Z\",\n \"posts\": 42,\n \"avatar\": [\n \"████ ████\",\n \" ████████ \",\n \"████████████\",\n \" ████████ \",\n \"██ ████ ██\"\n ]\n }\n}\n```\n\n**Errors:**\n- `404` — user not found\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/profile/alice\n```\n\n### PUT /api/profile\n\nUpdate your display name and/or bio.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"display_name\": \"Alice W.\", \"bio\": \"shipping code\" }\n```\n\nBoth fields are optional. Send only what you want to change.\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"username\": \"alice\",\n \"display_name\": \"Alice W.\",\n \"bio\": \"shipping code\",\n \"created_at\": \"2026-03-15T10:30:00.000Z\"\n }\n}\n```\n\n**Errors:**\n- `400` — nothing to update\n\n**Example:**\n```bash\ncurl -X PUT http://localhost:3006/api/profile \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"bio\":\"shipping code\"}'\n```\n\n### GET /api/profile/search?q=\n\nSearch users by username or display name.\n\n**Auth:** none\n\n**Params:**\n- `q` — search query (required, min 1 character)\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"users\": [\n { \"username\": \"alice\", \"display_name\": \"Alice\", \"bio\": \"building things\" },\n { \"username\": \"alice2\", \"display_name\": \"Alice Too\", \"bio\": null }\n ]\n }\n}\n```\n\n**Example:**\n```bash\ncurl \"http://localhost:3006/api/profile/search?q=alice\"\n```\n\n---\n\n## Posts\n\n### POST /api/posts\n\nPublish a post.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"content\": \"just shipped the wasm compiler [rust] [wasm]\" }\n```\n\n**Optional fields:**\n- `reply_to` — post ID to reply to\n\n**Inline commands:**\n- `/attach <media_id>` — attach previously uploaded media to the post. The `/attach` command is stripped from the displayed content.\n\n**Rules:**\n- Content: 1-500 characters\n- Tags use [brackets]: `[rust]`, `[music]`, `[coding]`\n- Tags are auto-extracted and stored\n- New tags are auto-assigned to the \"general\" category\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"id\": \"a3kf9x\",\n \"content\": \"just shipped the wasm compiler [rust] [wasm]\",\n \"reply_to\": null,\n \"tags\": [\"rust\", \"wasm\"],\n \"media_id\": null,\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"created_at\": \"2026-03-22T14:00:00.000Z\"\n }\n}\n```\n\n**Errors:**\n- `400` — empty content, too long, media not found/not yours\n- `404` — reply_to post not found\n\n**Examples:**\n\nSimple post:\n```bash\ncurl -X POST http://localhost:3006/api/posts \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"content\":\"just shipped the wasm compiler [rust] [wasm]\"}'\n```\n\nReply to a post:\n```bash\ncurl -X POST http://localhost:3006/api/posts \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"content\":\"nice work!\",\"reply_to\":\"a3kf9x\"}'\n```\n\nPost with media attachment:\n```bash\ncurl -X POST http://localhost:3006/api/posts \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"content\":\"screenshot of the new UI /attach x7km2p [showcase]\"}'\n```\n\n### GET /api/posts/feed\n\nGet the chronological feed. Public — works without auth. Authenticated users get kill-filtered results; guests get unfiltered posts (still subject to system-level filter_score suppression).\n\n**Auth:** optional\n\n**Params:**\n- `limit` — max posts to return (default 50, max 100)\n- `offset` — pagination offset (default 0)\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"posts\": [\n {\n \"id\": \"a3kf9x\",\n \"content\": \"just shipped the wasm compiler [rust] [wasm]\",\n \"reply_to\": null,\n \"tags\": [\"rust\", \"wasm\"],\n \"media_id\": null,\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"created_at\": \"2026-03-22T14:00:00.000Z\"\n }\n ]\n }\n}\n```\n\n**Examples:**\n```bash\n# Guest (unfiltered)\ncurl \"http://localhost:3006/api/posts/feed?limit=20\"\n\n# Authenticated (kill-filtered)\ncurl \"http://localhost:3006/api/posts/feed?limit=20\" \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n### GET /api/posts/:id\n\nGet a single post by ID.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"id\": \"a3kf9x\",\n \"content\": \"just shipped the wasm compiler [rust] [wasm]\",\n \"reply_to\": null,\n \"tags\": [\"rust\", \"wasm\"],\n \"media_id\": null,\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"created_at\": \"2026-03-22T14:00:00.000Z\"\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/posts/a3kf9x\n```\n\n### GET /api/posts/:id/thread\n\nGet a post and all its replies.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"post\": {\n \"id\": \"a3kf9x\",\n \"content\": \"just shipped the wasm compiler [rust] [wasm]\",\n \"reply_to\": null,\n \"tags\": [\"rust\", \"wasm\"],\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"created_at\": \"2026-03-22T14:00:00.000Z\"\n },\n \"replies\": [\n {\n \"id\": \"b7xm2k\",\n \"content\": \"nice work!\",\n \"reply_to\": \"a3kf9x\",\n \"tags\": [],\n \"username\": \"bob\",\n \"display_name\": \"Bob\",\n \"created_at\": \"2026-03-22T14:05:00.000Z\"\n }\n ]\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/posts/a3kf9x/thread\n```\n\n### GET /api/posts/search\n\nSearch posts by content. Searches active posts by default, or archived posts with the `archive` flag.\n\n**Auth:** none\n\n**Params:**\n- `q` — search query (required)\n- `archive` — set to `true` to search archived posts instead\n\n**Response (active):**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"posts\": [\n {\n \"id\": \"a3kf9x\",\n \"content\": \"just shipped the wasm compiler [rust] [wasm]\",\n \"tags\": [\"rust\", \"wasm\"],\n \"media_id\": null,\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"created_at\": \"2026-03-22T14:00:00.000Z\",\n \"archived\": false\n }\n ]\n }\n}\n```\n\n**Response (archive):**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"posts\": [\n {\n \"id\": \"b7xm2k\",\n \"content\": \"old post about [rust]\",\n \"tags\": [\"rust\"],\n \"username\": \"alice\",\n \"display_name\": \"Alice\",\n \"created_at\": \"2025-12-01T08:00:00.000Z\",\n \"archived\": true\n }\n ]\n }\n}\n```\n\n**Examples:**\n```bash\n# Search active posts\ncurl \"http://localhost:3006/api/posts/search?q=rust\"\n\n# Search archived posts\ncurl \"http://localhost:3006/api/posts/search?q=rust&archive=true\"\n```\n\n---\n\n## Filter (Kill)\n\nThe `/kill` command maps to the filter API. The word \"kill\" never appears in the database or API.\n\n### POST /api/filter\n\nAdd a filter. Hides a user or post from your feed.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"target_type\": \"user\", \"target_id\": \"bob\" }\n```\n\n- `target_type` — `\"user\"` or `\"post\"`\n- `target_id` — username (for users) or post ID (for posts)\n\nFiltering a user also increments their hidden `filter_score`. Users filtered by enough people get suppressed from everyone's feed silently (threshold: ~5 active filters, with 7-day half-life decay).\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Filtered.\" } }\n```\n\n**Errors:**\n- `400` — missing fields, invalid type, can't filter yourself\n- `404` — user not found\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/filter \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"target_type\":\"user\",\"target_id\":\"spammer\"}'\n```\n\n### GET /api/filter\n\nGet your current filter list.\n\n**Auth:** required\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"filters\": [\n { \"type\": \"user\", \"id\": \"uuid-of-filtered-user\" },\n { \"type\": \"post\", \"id\": \"b7xm2k\" }\n ]\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/filter \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n---\n\n## Media\n\nSupported formats: PNG (max 5MB), MP3 (max 10MB), MP4 (max 25MB).\n\nPNG uploads are processed into multiple variants: original (downscaled to max 1MB), thumbnail (80px wide), large (800px wide), and ASCII art.\n\n### POST /api/media/upload\n\nUpload a file.\n\n**Auth:** required\n\n**Body:** multipart/form-data with `file` field\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"media_id\": \"x7km2p\",\n \"type\": \"png\"\n }\n}\n```\n\n**Errors:**\n- `400` — no file, unsupported type, file too large\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/media/upload \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -F \"file=@screenshot.png\"\n```\n\n### GET /api/media/:id\n\nServe the original file.\n\n**Auth:** none\n\n**Response:** raw file with appropriate Content-Type header (`image/png`, `audio/mpeg`, `video/mp4`)\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/media/x7km2p -o image.png\n```\n\n### GET /api/media/:id/thumb\n\nServe the thumbnail (PNG only, max 80px wide).\n\n**Auth:** none\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/media/x7km2p/thumb -o thumb.png\n```\n\n### GET /api/media/:id/large\n\nServe the large version (PNG only, max 800px wide).\n\n**Auth:** none\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/media/x7km2p/large -o large.png\n```\n\n### GET /api/media/:id/ascii\n\nGet the ASCII art representation (PNG only).\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"ascii\": \" @@@@ \\n @ @ \\n @ @ \\n @@@@ \\n\"\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/media/x7km2p/ascii\n```\n\n---\n\n## Avatar\n\n### POST /api/avatar\n\nUpload a profile picture. PNG only, max 5MB. Processes into thumb, large, and ASCII art versions. Sets as your avatar immediately.\n\n**Auth:** required\n\n**Body:** multipart/form-data with `file` field\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"avatar_id\": \"x7km2p\" } }\n```\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/avatar \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -F \"file=@photo.png\"\n```\n\n### GET /api/avatar/:username\n\nGet a user's avatar image (large version, 800px max).\n\n**Auth:** none\n\n**Response:** raw PNG with `Content-Type: image/png`\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/avatar/alice -o avatar.png\n```\n\nReturns 404 if the user has no uploaded avatar.\n\n---\n\n## Bookmarks\n\nBookmarks are private. Nobody sees your bookmarks. No counts, no notifications. One concept for everything: users, tags, posts, docs.\n\n### POST /api/bookmarks\n\nAdd a bookmark.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"target_type\": \"user\", \"target_id\": \"alice\" }\n```\n\n- `target_type` — `\"user\"`, `\"tag\"`, `\"post\"`, or `\"doc\"`\n- `target_id` — username, tag name, post ID, or doc name\n\nBookmarking the same thing twice is a no-op, not an error.\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Bookmarked.\" } }\n```\n\n**Errors:**\n- `400` — missing fields, invalid type\n\n**Example:**\n```bash\n# Bookmark a user\ncurl -X POST http://localhost:3006/api/bookmarks \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"target_type\":\"user\",\"target_id\":\"alice\"}'\n\n# Bookmark a tag\ncurl -X POST http://localhost:3006/api/bookmarks \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"target_type\":\"tag\",\"target_id\":\"rust\"}'\n\n# Bookmark a post\ncurl -X POST http://localhost:3006/api/bookmarks \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"target_type\":\"post\",\"target_id\":\"a3kf9x\"}'\n\n# Bookmark a doc\ncurl -X POST http://localhost:3006/api/bookmarks \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"target_type\":\"doc\",\"target_id\":\"api\"}'\n```\n\n### DELETE /api/bookmarks\n\nRemove a bookmark.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"target_type\": \"user\", \"target_id\": \"alice\" }\n```\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Unbookmarked.\" } }\n```\n\n**Example:**\n```bash\ncurl -X DELETE http://localhost:3006/api/bookmarks \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"target_type\":\"user\",\"target_id\":\"alice\"}'\n```\n\n### GET /api/bookmarks\n\nList your bookmarks. Optionally filter by type.\n\n**Auth:** required\n\n**Params:**\n- `type` — optional: `user`, `tag`, `post`, or `doc`\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"bookmarks\": [\n { \"type\": \"user\", \"id\": \"alice\" },\n { \"type\": \"tag\", \"id\": \"rust\" },\n {\n \"type\": \"post\",\n \"id\": \"a3kf9x\",\n \"preview\": \"just shipped the wasm compiler...\",\n \"username\": \"alice\"\n },\n {\n \"type\": \"post\",\n \"id\": \"old123\",\n \"preview\": \"this was archived...\",\n \"archived\": true\n },\n {\n \"type\": \"post\",\n \"id\": \"gone99\",\n \"expired\": true\n },\n { \"type\": \"doc\", \"id\": \"api\" }\n ]\n }\n}\n```\n\nPost bookmarks are enriched with a content preview (60 chars), the author's username, and status flags (`archived`, `expired`) when applicable.\n\n**Examples:**\n```bash\n# All bookmarks\ncurl http://localhost:3006/api/bookmarks \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n\n# Only bookmarked tags\ncurl \"http://localhost:3006/api/bookmarks?type=tag\" \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n---\n\n## Docs\n\n### GET /api/docs\n\nList all available documentation files.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"docs\": [\n { \"name\": \"about\", \"path\": \"docs/about.md\" },\n { \"name\": \"api\", \"path\": \"docs/api.md\" },\n { \"name\": \"commands\", \"path\": \"docs/commands.md\" },\n { \"name\": \"changelog\", \"path\": \"docs/changelog.md\" }\n ]\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/docs\n```\n\n### GET /api/docs/:name\n\nGet the contents of a specific document. The `.md` extension is optional.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"name\": \"api\",\n \"content\": \"# API Reference\\n\\n...\"\n }\n}\n```\n\n**Errors:**\n- `400` — path traversal attempt\n- `404` — document not found\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/docs/api\n```\n\n---\n\n## Categories\n\n### GET /api/categories\n\nGet the category tree with tags. Used by `/tree -cat` in the client.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"categories\": [\n {\n \"id\": \"coding\",\n \"name\": \"coding\",\n \"tags\": [\"rust\", \"python\", \"wasm\", \"js\", \"frontend\", \"backend\"]\n },\n {\n \"id\": \"creative\",\n \"name\": \"creative\",\n \"tags\": [\"music\", \"art\", \"writing\", \"gamedev\"]\n },\n {\n \"id\": \"meta\",\n \"name\": \"meta\",\n \"tags\": [\"motd\", \"bugs\", \"ideas\", \"feedback\"]\n },\n {\n \"id\": \"general\",\n \"name\": \"general\",\n \"tags\": [\"offtopic\", \"introductions\", \"showcase\"]\n }\n ]\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/categories\n```\n\n---\n\n## Commands\n\n### GET /api/commands\n\nGet the full command list grouped by category. Used by `/help` and `/menu` in the client.\n\n**Auth:** none\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"categories\": [\n {\n \"name\": \"General\",\n \"commands\": [\n { \"command\": \"/help\", \"description\": \"Show all available commands\" },\n { \"command\": \"/menu\", \"description\": \"Toggle the command menu\" },\n { \"command\": \"/clear\", \"description\": \"Clear the terminal\" },\n { \"command\": \"/read [path]\", \"description\": \"Read a document\" },\n { \"command\": \"/tree -cat\", \"description\": \"Browse categories and tags\" },\n { \"command\": \"/link [target]\", \"description\": \"Navigate to user, tag, post, or doc\" }\n ]\n }\n ]\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/commands\n```\n\n---\n\n## Telegram Connect\n\n### POST /api/connect/telegram\n\nGenerate a Telegram deep link to connect your account.\n\n**Auth:** required\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"link\": \"https://t.me/motdsocial_bot?start=alice_a8f3k2\",\n \"expires_in\": 600\n }\n}\n```\n\nIf already connected:\n```json\n{\n \"ok\": true,\n \"data\": {\n \"already_connected\": true,\n \"message\": \"Telegram already connected.\"\n }\n}\n```\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/connect/telegram \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n### DELETE /api/connect/telegram\n\nDisconnect your Telegram account.\n\n**Auth:** required\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Telegram disconnected.\" } }\n```\n\n**Example:**\n```bash\ncurl -X DELETE http://localhost:3006/api/connect/telegram \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n### POST /api/connect/email\n\nSend a verification email to connect your email address.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"email\": \"you@gmail.com\" }\n```\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"message\": \"Verification email sent to you@gmail.com\",\n \"tip\": \"Tip: add noreply@motd.social to your contacts so notifications don't go to spam.\"\n }\n}\n```\n\n**Example:**\n```bash\ncurl -X POST http://localhost:3006/api/connect/email \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"email\":\"you@gmail.com\"}'\n```\n\n### GET /api/connect/email/verify\n\nVerify email address from link in verification email.\n\n**Auth:** none (link from email)\n\n**Params:**\n- `code` — verification code from the email link\n\nReturns an HTML confirmation page on success.\n\n### DELETE /api/connect/email\n\nDisconnect your email address.\n\n**Auth:** required\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Email disconnected.\" } }\n```\n\n**Example:**\n```bash\ncurl -X DELETE http://localhost:3006/api/connect/email \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n### GET /api/connect/status\n\nCheck your Telegram and email connection status and notification settings.\n\n**Auth:** required\n\n**Response:**\n```json\n{\n \"ok\": true,\n \"data\": {\n \"telegram\": {\n \"connected\": true,\n \"username\": \"alice_tg\",\n \"notify\": true,\n \"notify_bookmarks\": true,\n \"notify_mentions\": true\n },\n \"email\": {\n \"connected\": true,\n \"address\": \"alice@gmail.com\",\n \"notify\": true,\n \"notify_bookmarks\": true,\n \"notify_mentions\": true\n }\n }\n}\n```\n\n**Example:**\n```bash\ncurl http://localhost:3006/api/connect/status \\\n -H \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n---\n\n## Account\n\n### DELETE /api/account\n\nPermanently delete your account. This is irreversible. Deletes all posts, media, sessions, and user data.\n\n**Auth:** required\n\n**Body:**\n```json\n{ \"confirm_username\": \"alice\" }\n```\n\nThe `confirm_username` must match your actual username.\n\n**Response:**\n```json\n{ \"ok\": true, \"data\": { \"message\": \"Account terminated.\" } }\n```\n\n**Errors:**\n- `400` — username confirmation doesn't match\n\n**Example:**\n```bash\ncurl -X DELETE http://localhost:3006/api/account \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"confirm_username\":\"alice\"}'\n```\n";
|
|
const passes = [{"t":0,"r":933,"c":2,"a":"hold","s":0.2679057573205296,"ps":9,"e":100.79324605856424,"pr":1.1},{"t":0,"r":1068,"c":36,"a":"extend","s":0.42489483628409375,"ps":9,"e":71.43441108319092,"pr":1.1},{"t":0,"r":167,"c":0,"a":"died","s":0,"ps":8,"e":100,"pr":1},{"t":0,"r":796,"c":15,"a":"extend","s":0.37931071527731497,"ps":9,"e":71.17914000555297,"pr":1.1},{"t":0,"r":42,"c":0,"a":"extend","s":0.32302273565569745,"ps":9,"e":70.8639273196719,"pr":1.1},{"t":1,"r":933,"c":2,"a":"hold","s":0.25824367063828835,"ps":9,"e":101.50919542367055,"pr":1.1},{"t":1,"r":1068,"c":36,"a":"extend","s":0.5779786606743086,"ps":9,"e":52.29576825800977,"pr":1.1},{"t":1,"r":796,"c":15,"a":"extend","s":0.4092885881965942,"ps":8,"e":51.277414097788,"pr":1.05},{"t":1,"r":42,"c":0,"a":"hold","s":0.29633833558563366,"ps":8,"e":36.01731700217849,"pr":1.05},{"t":1,"r":796,"c":16,"a":"hold","s":0.24783871072648092,"ps":5,"e":31.738055402477404,"pr":1.2000000000000002},{"t":2,"r":933,"c":2,"a":"retracted","s":0.25824367063828835,"ps":8,"e":102.37514478877685,"pr":1.05},{"t":2,"r":1068,"c":36,"a":"extend","s":0.5779786606743086,"ps":8,"e":39.00371828038297,"pr":1.05},{"t":2,"r":796,"c":15,"a":"extend","s":0.37931071527731497,"ps":7,"e":37.28332987400456,"pr":1},{"t":2,"r":42,"c":0,"a":"hold","s":0.32302273565569745,"ps":7,"e":37.55149888742407,"pr":1},{"t":2,"r":796,"c":16,"a":"hold","s":0.2495315755412578,"ps":4,"e":33.134308006807466,"pr":1.1500000000000001},{"t":2,"r":1068,"c":37,"a":"hold","s":0.20062126283830753,"ps":5,"e":23.267442213282077,"pr":1.2000000000000002},{"t":2,"r":44,"c":4,"a":"extend","s":0.5176965445743369,"ps":9,"e":27.166222551141228,"pr":1.1500000000000001},{"t":3,"r":1068,"c":36,"a":"extend","s":0.42489483628409375,"ps":8,"e":28.842013879459003,"pr":1.05},{"t":3,"r":796,"c":15,"a":"hold","s":0.34350194395433276,"ps":6,"e":39.13134542563922,"pr":0.95},{"t":3,"r":42,"c":0,"a":"retracted","s":0.2984114068934646,"ps":7,"e":19.444395071285893,"pr":1},{"t":3,"r":796,"c":16,"a":"retracted","s":0.24783871072648092,"ps":4,"e":34.51701769261931,"pr":1.1},{"t":3,"r":1068,"c":37,"a":"hold","s":0.2152219174878237,"ps":4,"e":24.389217553184668,"pr":1.1500000000000001},{"t":3,"r":44,"c":4,"a":"extend","s":0.5241575429024936,"ps":8,"e":21.111638026052823,"pr":1.1},{"t":3,"r":1068,"c":35,"a":"hold","s":0.1824731850379719,"ps":5,"e":17.425664743325047,"pr":1.1500000000000001},{"t":3,"r":44,"c":5,"a":"hold","s":0.27996254254417446,"ps":5,"e":13.132367147985352,"pr":1.2500000000000002},{"t":4,"r":1068,"c":36,"a":"extend","s":0.42489483628409375,"ps":7,"e":21.833820798812226,"pr":1},{"t":4,"r":796,"c":15,"a":"hold","s":0.34115086789809645,"ps":5,"e":41.11055236882399,"pr":0.8999999999999999},{"t":4,"r":1068,"c":37,"a":"retracted","s":0.20062126283830753,"ps":4,"e":25.39418765589113,"pr":1.1},{"t":4,"r":44,"c":4,"a":"extend","s":0.5176965445743369,"ps":7,"e":16.94224726785326,"pr":1.05},{"t":4,"r":1068,"c":35,"a":"hold","s":0.18617985382671517,"ps":5,"e":18.16510357393877,"pr":1.1500000000000001},{"t":4,"r":44,"c":5,"a":"hold","s":0.276578224317989,"ps":4,"e":14.744992942529265,"pr":1.2000000000000002},{"t":4,"r":44,"c":3,"a":"hold","s":0.14000033913783452,"ps":8,"e":19.36439778438857,"pr":1.1},{"t":5,"r":1068,"c":36,"a":"extend","s":0.401124774909063,"ps":7,"e":16.79497329865931,"pr":1},{"t":5,"r":796,"c":15,"a":"retracted","s":0.2928320546289322,"ps":5,"e":42.703208805855446,"pr":0.8999999999999999},{"t":5,"r":44,"c":4,"a":"extend","s":0.545010766015573,"ps":7,"e":27.73171182625649,"pr":1},{"t":5,"r":1068,"c":35,"a":"retracted","s":0.18617985382671517,"ps":4,"e":19.05454240455249,"pr":1.1},{"t":5,"r":44,"c":5,"a":"retracted","s":0.268800053888311,"ps":4,"e":16.295393373635754,"pr":1.1500000000000001},{"t":6,"r":1068,"c":36,"a":"extend","s":0.401124774909063,"ps":6,"e":13.37278004855227,"pr":0.95},{"t":6,"r":44,"c":4,"a":"extend","s":0.4933595836456635,"ps":7,"e":21.44001194679526,"pr":1},{"t":7,"r":1068,"c":36,"a":"hold","s":0.3474974071802458,"ps":6,"e":15.252759305994237,"pr":0.95},{"t":7,"r":44,"c":4,"a":"extend","s":0.4933595836456635,"ps":6,"e":17.140822031172394,"pr":0.95},{"t":7,"r":1068,"c":37,"a":"hold","s":0.12326063332779774,"ps":5,"e":5.967276516001927,"pr":1.05},{"t":8,"r":1068,"c":36,"a":"hold","s":0.3474974071802458,"ps":5,"e":17.282738563436205,"pr":0.8999999999999999},{"t":8,"r":44,"c":4,"a":"extend","s":0.4815946455172386,"ps":5,"e":14.170505436717212,"pr":0.8999999999999999},{"t":8,"r":1068,"c":37,"a":"hold","s":0.1324033137432912,"ps":5,"e":6.276503025948257,"pr":1.05},{"t":8,"r":44,"c":3,"a":"hold","s":0.17573044665925386,"ps":5,"e":8.0019101580622,"pr":1.05},{"t":9,"r":1068,"c":36,"a":"retracted","s":0.3205297295326896,"ps":5,"e":19.09697639969772,"pr":0.8999999999999999},{"t":9,"r":44,"c":4,"a":"extend","s":0.48421462305897855,"ps":4,"e":12.210955694832327,"pr":0.8499999999999999},{"t":9,"r":1068,"c":37,"a":"retracted","s":0.1324033137432912,"ps":4,"e":6.735729535894587,"pr":1},{"t":9,"r":44,"c":3,"a":"hold","s":0.13886262190962728,"ps":5,"e":8.362811133339218,"pr":1.05},{"t":10,"r":44,"c":4,"a":"extend","s":0.36361700718854734,"ps":4,"e":10.163924226638493,"pr":0.8499999999999999},{"t":10,"r":44,"c":3,"a":"retracted","s":0.13886262190962728,"ps":4,"e":8.873712108616235,"pr":1},{"t":10,"r":44,"c":5,"a":"extend","s":0.4651874298084558,"ps":5,"e":5.74333631537705,"pr":0.9499999999999998},{"t":11,"r":44,"c":4,"a":"extend","s":0.36361700718854734,"ps":4,"e":8.731002198902809,"pr":0.7999999999999998},{"t":12,"r":44,"c":5,"a":"extend","s":0.4338400447879123,"ps":4,"e":6.175374237395244,"pr":0.8499999999999998},{"t":12,"r":44,"c":6,"a":"extend","s":0.5161667188150654,"ps":5,"e":5.298269503903784,"pr":1.0499999999999998},{"t":12,"r":44,"c":3,"a":"hold","s":0.15986020101913026,"ps":5,"e":4.270739693397103,"pr":0.8999999999999998},{"t":16,"r":44,"c":6,"a":"extend","s":0.45397085506207735,"ps":4,"e":6.878280695349297,"pr":0.8999999999999997},{"t":18,"r":44,"c":4,"a":"extend","s":0.36361700718854734,"ps":4,"e":5.662867812133305,"pr":0.4499999999999996},{"t":19,"r":44,"c":6,"a":"extend","s":0.4117016281550758,"ps":4,"e":6.618059929138663,"pr":0.7499999999999996},{"t":22,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.311191851566969,"pr":0.34999999999999953},{"t":24,"r":44,"c":6,"a":"hold","s":0.3739527236268281,"ps":4,"e":8.300685507483529,"pr":0.4999999999999994},{"t":26,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313268275268351,"pr":0.3},{"t":27,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313465096954377,"pr":0.3},{"t":29,"r":44,"c":6,"a":"hold","s":0.30128898761856565,"ps":5,"e":5.749029996213551,"pr":0.4},{"t":30,"r":44,"c":4,"a":"retracted","s":0.3358857476463764,"ps":4,"e":8.976956292028087,"pr":0.35000000000000003},{"t":34,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313886526293353,"pr":0.3},{"t":36,"r":44,"c":3,"a":"hold","s":0.14144583823439522,"ps":5,"e":1.7438749336792767,"pr":0.4},{"t":41,"r":44,"c":4,"a":"hold","s":0.2625407527380508,"ps":4,"e":4.946961221487669,"pr":0.3},{"t":42,"r":44,"c":3,"a":"retracted","s":0.18954172559385918,"ps":4,"e":4.041260902984975,"pr":0.30000000000000004},{"t":43,"r":44,"c":4,"a":"retracted","s":0.2625407527380508,"ps":4,"e":7.947613265296482,"pr":0.3},{"t":46,"r":44,"c":4,"a":"hold","s":0.19811660264851033,"ps":5,"e":3.8387175906369575,"pr":0.4},{"t":47,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.31392398110864,"pr":0.3},{"t":47,"r":44,"c":4,"a":"retracted","s":0.19811660264851033,"ps":4,"e":4.8236504118250405,"pr":0.35000000000000003},{"t":53,"r":44,"c":4,"a":"hold","s":0.2536312683292435,"ps":5,"e":3.9850176977624248,"pr":0.4},{"t":57,"r":44,"c":4,"a":"extend","s":0.2536312683292435,"ps":4,"e":3.517850653869356,"pr":0.3},{"t":58,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924340309258,"pr":0.3},{"t":60,"r":44,"c":3,"a":"hold","s":0.1708317790419906,"ps":4,"e":2.887768529344447,"pr":0.35000000000000003},{"t":61,"r":44,"c":4,"a":"extend","s":0.2536312683292435,"ps":4,"e":3.3784847569906846,"pr":0.3},{"t":62,"r":44,"c":4,"a":"hold","s":0.2536312683292435,"ps":4,"e":4.807534903624632,"pr":0.3},{"t":64,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924346702636,"pr":0.3},{"t":65,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924346958375,"pr":0.3},{"t":69,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347411827,"pr":0.3},{"t":72,"r":44,"c":4,"a":"hold","s":0.29849303213282913,"ps":5,"e":4.3439118345561605,"pr":0.4},{"t":74,"r":44,"c":4,"a":"extend","s":0.29849303213282913,"ps":4,"e":4.403019902031978,"pr":0.30000000000000004},{"t":77,"r":44,"c":4,"a":"extend","s":0.29849303213282913,"ps":4,"e":4.251154372473984,"pr":0.3},{"t":78,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347549321,"pr":0.3},{"t":81,"r":44,"c":6,"a":"extend","s":0.4354797615578252,"ps":5,"e":4.328743806341417,"pr":0.4},{"t":82,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347553713,"pr":0.3},{"t":82,"r":44,"c":4,"a":"extend","s":0.29849303213282913,"ps":4,"e":4.185195268863973,"pr":0.3},{"t":84,"r":44,"c":4,"a":"hold","s":0.29849303213282913,"ps":4,"e":5.9691419252112565,"pr":0.3},{"t":85,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347554625,"pr":0.3},{"t":90,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347555022,"pr":0.3},{"t":91,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347555045,"pr":0.3},{"t":92,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347555062,"pr":0.3},{"t":95,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347555088,"pr":0.3},{"t":95,"r":44,"c":6,"a":"extend","s":0.3878369704115232,"ps":4,"e":5.227006644357664,"pr":0.3},{"t":97,"r":44,"c":6,"a":"extend","s":0.3878369704115232,"ps":4,"e":5.539441214052956,"pr":0.3},{"t":97,"r":44,"c":3,"a":"hold","s":0.20898434629986598,"ps":5,"e":3.1547338010019086,"pr":0.4},{"t":98,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.313924347555097,"pr":0.3},{"t":99,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":5.7589601385966755,"pr":0.3},{"t":99,"r":44,"c":3,"a":"extend","s":0.2494047604174747,"ps":4,"e":3.1326528816652943,"pr":0.35000000000000003},{"t":103,"r":44,"c":3,"a":"retracted","s":0.20898434629986598,"ps":4,"e":6.1587936676417385,"pr":0.3},{"t":103,"r":44,"c":2,"a":"hold","s":0.18096177464703095,"ps":4,"e":3.1629130415050843,"pr":0.4000000000000001},{"t":104,"r":44,"c":6,"a":"extend","s":0.3878369704115232,"ps":4,"e":5.814902149958828,"pr":0.3},{"t":106,"r":44,"c":6,"a":"extend","s":0.3878369704115232,"ps":4,"e":5.827510011797527,"pr":0.3},{"t":107,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.029031931869076,"pr":0.3},{"t":110,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":111,"r":44,"c":3,"a":"retracted","s":0.18731997598692704,"ps":4,"e":4.602342438344109,"pr":0.35000000000000003},{"t":113,"r":44,"c":3,"a":"hold","s":0.12120417521634809,"ps":5,"e":3.3529846336224334,"pr":0.4},{"t":117,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":117,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.258262759342958,"pr":0.3},{"t":118,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.194449971618042,"pr":0.3},{"t":118,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.095576569746023,"pr":0.3},{"t":119,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":119,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.241741141102334,"pr":0.3},{"t":120,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.34405634105175,"pr":0.3},{"t":124,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.063070991807018,"pr":0.3},{"t":125,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":126,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.054137054117991,"pr":0.3},{"t":128,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.569029183037439,"pr":0.3},{"t":129,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":133,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.580478723290094,"pr":0.3},{"t":135,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.581658396288344,"pr":0.3},{"t":136,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.581998419681959,"pr":0.3},{"t":137,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":139,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.195035471986496,"pr":0.3},{"t":141,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.1187996494059345,"pr":0.3},{"t":142,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":142,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.096825794662125,"pr":0.3},{"t":145,"r":44,"c":3,"a":"retracted","s":0.22245836839748762,"ps":4,"e":5.47221536700637,"pr":0.35000000000000003},{"t":146,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582769396355408,"pr":0.3},{"t":147,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":151,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":151,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.047622490289986,"pr":0.3},{"t":152,"r":44,"c":4,"a":"hold","s":0.39886893572820925,"ps":4,"e":8.63857397611566,"pr":0.3},{"t":153,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":7.860667823358933,"pr":0.3},{"t":154,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":154,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582790515636714,"pr":0.3},{"t":154,"r":44,"c":3,"a":"retracted","s":0.1434701423290929,"ps":4,"e":4.1432174318565576,"pr":0.30000000000000004},{"t":156,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":156,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.668137691182872,"pr":0.3},{"t":157,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791364456849,"pr":0.3},{"t":158,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":158,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.350619736812159,"pr":0.3},{"t":160,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791655602156,"pr":0.3},{"t":162,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.5827917331212555,"pr":0.3},{"t":163,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":163,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.096825954906242,"pr":0.3},{"t":164,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":164,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791771105615,"pr":0.3},{"t":165,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791782054048,"pr":0.3},{"t":165,"r":44,"c":3,"a":"hold","s":0.170508980059066,"ps":5,"e":3.2204050726921025,"pr":0.4},{"t":166,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.063139930303597,"pr":0.3},{"t":166,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.58279178971795,"pr":0.3},{"t":166,"r":44,"c":3,"a":"hold","s":0.19828142241707705,"ps":5,"e":4.056656452028719,"pr":0.4},{"t":167,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":167,"r":44,"c":3,"a":"retracted","s":0.19828142241707705,"ps":4,"e":5.042907831365335,"pr":0.35000000000000003},{"t":170,"r":44,"c":3,"a":"hold","s":0.11861035562800462,"ps":5,"e":2.932193971853999,"pr":0.4},{"t":172,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":175,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":7.316133519597775,"pr":0.3},{"t":178,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.481362424992795,"pr":0.3},{"t":181,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.195035939543287,"pr":0.3},{"t":183,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":183,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807558789,"pr":0.3},{"t":183,"r":44,"c":3,"a":"retracted","s":0.15505650925369352,"ps":4,"e":3.910977613275612,"pr":0.35000000000000003},{"t":186,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.070676986099262,"pr":0.3},{"t":186,"r":44,"c":3,"a":"hold","s":0.11546976459360342,"ps":5,"e":2.7419851008042846,"pr":0.4},{"t":187,"r":44,"c":3,"a":"retracted","s":0.11546976459360342,"ps":4,"e":3.065743217553112,"pr":0.35000000000000003},{"t":188,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.05786399132119,"pr":0.3},{"t":189,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":190,"r":44,"c":3,"a":"hold","s":0.1750892288542919,"ps":5,"e":4.432247109927351,"pr":0.4},{"t":191,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":191,"r":44,"c":3,"a":"retracted","s":0.1750892288542919,"ps":4,"e":5.232960940761687,"pr":0.35000000000000003},{"t":192,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":195,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":196,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":7.316133519600303,"pr":0.3},{"t":196,"r":44,"c":3,"a":"retracted","s":0.10572849387157718,"ps":4,"e":3.051454007448694,"pr":0.35000000000000003},{"t":199,"r":44,"c":3,"a":"hold","s":0.187418769694905,"ps":5,"e":4.1218523734127634,"pr":0.4},{"t":201,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.2590998563794455,"pr":0.3},{"t":202,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.195035939543583,"pr":0.3},{"t":202,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.5827918076003415,"pr":0.3},{"t":204,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.118799878508907,"pr":0.3},{"t":206,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600379,"pr":0.3},{"t":207,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":207,"r":44,"c":3,"a":"hold","s":0.1461159349479178,"ps":4,"e":3.611215094445536,"pr":0.35000000000000003},{"t":208,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.06313993034749,"pr":0.3},{"t":209,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.057863991321215,"pr":0.3},{"t":209,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600387,"pr":0.3},{"t":210,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":211,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.051585623879946,"pr":0.3},{"t":211,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":213,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.048509223833724,"pr":0.3},{"t":213,"r":44,"c":3,"a":"retracted","s":0.25123809574264755,"ps":4,"e":6.317269514201119,"pr":0.35000000000000003},{"t":215,"r":44,"c":4,"a":"hold","s":0.39886893572820925,"ps":4,"e":8.638573982587252,"pr":0.3},{"t":217,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":219,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":219,"r":44,"c":3,"a":"retracted","s":0.21576194453134218,"ps":4,"e":6.140328508576655,"pr":0.35000000000000003},{"t":220,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":220,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.481362424993662,"pr":0.3},{"t":220,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":221,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.350619737573535,"pr":0.3},{"t":222,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":222,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.2590998563794455,"pr":0.3},{"t":223,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.195035939543583,"pr":0.3},{"t":224,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.150191197758479,"pr":0.3},{"t":224,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":226,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":226,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":226,"r":44,"c":3,"a":"extend","s":0.21410995027674695,"ps":5,"e":2.5096556851024547,"pr":0.4},{"t":227,"r":44,"c":3,"a":"extend","s":0.21740976669435638,"ps":5,"e":2.4492536730601135,"pr":0.4},{"t":229,"r":44,"c":3,"a":"hold","s":0.21410995027674695,"ps":4,"e":4.701411408828941,"pr":0.35000000000000003},{"t":230,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.057863991321215,"pr":0.3},{"t":231,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.0541708340028215,"pr":0.3},{"t":231,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":233,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":234,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":235,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":238,"r":44,"c":3,"a":"hold","s":0.1346065085695479,"ps":5,"e":3.69570970908026,"pr":0.4},{"t":239,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.934959503798185,"pr":0.3},{"t":240,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":241,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":243,"r":44,"c":3,"a":"hold","s":0.20228410200257363,"ps":5,"e":4.617258303633767,"pr":0.4},{"t":245,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":247,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.096825955034206,"pr":0.3},{"t":249,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":251,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.057863991321215,"pr":0.3},{"t":252,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":254,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.049775976793933,"pr":0.3},{"t":255,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.048509223833724,"pr":0.3},{"t":256,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.047622496761578,"pr":0.3},{"t":257,"r":44,"c":4,"a":"hold","s":0.39886893572820925,"ps":4,"e":8.638573982587252,"pr":0.3},{"t":257,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":258,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":260,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.934959503798185,"pr":0.3},{"t":260,"r":44,"c":3,"a":"hold","s":0.1488569890217938,"ps":5,"e":3.5763417062887664,"pr":0.4},{"t":261,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.668137692736701,"pr":0.3},{"t":262,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":263,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.350619737573535,"pr":0.3},{"t":266,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":266,"r":44,"c":3,"a":"extend","s":0.2532704590860597,"ps":5,"e":2.7518253527450094,"pr":0.4},{"t":267,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":267,"r":44,"c":3,"a":"extend","s":0.25492359452988533,"ps":4,"e":2.933849876288864,"pr":0.35000000000000003},{"t":268,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":268,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.096825955034206,"pr":0.3},{"t":269,"r":44,"c":3,"a":"extend","s":0.2532704590860597,"ps":4,"e":3.1347212098808317,"pr":0.3},{"t":269,"r":44,"c":2,"a":"hold","s":0.20508660607996704,"ps":4,"e":2.86849217370236,"pr":0.4},{"t":271,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.06313993034749,"pr":0.3},{"t":271,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":274,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":276,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":276,"r":44,"c":3,"a":"hold","s":0.13020940276911308,"ps":5,"e":2.890276954229808,"pr":0.4},{"t":279,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":279,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":7.860667827889047,"pr":0.3},{"t":280,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":282,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.668137692736701,"pr":0.3},{"t":283,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.481362424993662,"pr":0.3},{"t":284,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":284,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":285,"r":44,"c":3,"a":"hold","s":0.16931872262547207,"ps":5,"e":3.897660599088111,"pr":0.4},{"t":286,"r":44,"c":3,"a":"retracted","s":0.16931872262547207,"ps":4,"e":4.652210380091887,"pr":0.35000000000000003},{"t":292,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3},{"t":292,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.06313993034749,"pr":0.3},{"t":294,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":295,"r":44,"c":6,"a":"extend","s":0.42764956112144953,"ps":4,"e":6.582791807600389,"pr":0.3},{"t":296,"r":44,"c":4,"a":"extend","s":0.39886893572820925,"ps":4,"e":6.049775976793933,"pr":0.3},{"t":297,"r":44,"c":5,"a":"extend","s":0.41324594719045193,"ps":4,"e":6.3139243475551,"pr":0.3}];
|
|
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> |