Monorepo for Aesthetic.Computer aesthetic.computer
at main 5789 lines 180 kB view raw
1// 📚 Docs, 24.01.29.21.54 2// Return up to date data for the aesthetic.computer pieces api. 3 4/* #region 🏁 TODO 5 - [-] Check ida's descriptions. 6 - [] Handle flashing screen / subtle cosmetics. 7 - [] Wire it up to the VS code extension so it fills in the prompt / opens it. 8 - [] Wire this endpoint up to a prompt auto-complete as well. 9 - [] Pals should bump upwards on hover and animate scroll to top on tap. 10 - [] And be faded / only fade up when scrolled past 0. 11 + Done 12 - [x] Fill out an initial range of identifiers. 13 - [x] Can always hide unnecessary / incomplete ones later. 14 - [x] Build a nice scrollable index page. 15#endregion */ 16 17import { respond } from "../../backend/http.mjs"; 18import { defaultTemplateStringProcessor as html } from "../../public/aesthetic.computer/lib/helpers.mjs"; 19import { getCommandDescription } from "../../public/aesthetic.computer/lib/prompt-commands.mjs"; 20const dev = process.env.CONTEXT === "dev"; 21const { keys } = Object; 22 23// GET A user's `sub` id from either their handle or email address. 24export async function handler(event, context) { 25 // Make sure this is a GET request 26 if (event.httpMethod !== "GET") { 27 return respond(405, { error: "Wrong request type." }); 28 } 29 30 const AC_ORIGIN = "https://aesthetic.computer"; 31 const LEARN_KIDLISP_ORIGIN = "https://learn.kidlisp.com"; 32 33 // TODO: Factor this out. 34 const boxBody = ` 35 <code>box()</code> <em>A random box</em></li><br> 36 <code>box(x, y, size)</code></code> <em>Square from top left corner</em><br> 37 <mark><code>box(x, y, w, h)</code> <em>Rectangle from top left corner</em></mark><br> 38 <code>box(x, y, size, mode)</code> <em>Square with <code>mode</code></em><br> 39 <code>box(x, y, w, h, mode)</code> <em>Rectangle with <code>mode</code></em><br> 40 <br> 41 <hr> 42 <code>mode</code> 43 <br> 44 <code>center</code> &nbsp;- paints a box from the center<br> 45 <code>outline</code> - paints the outline of a box<br> 46 <code>inline</code> &nbsp;- the opposite of outline<br> 47 <em>(thicken with <code>:</code> like <code>outline:4</code>)</em> 48 <br> 49 combine modes with <code>*</code> like <code>outline*center</code> or <code>inline:3*center</code> 50 `.trim(); 51 52 const l5StatusBadge = (status) => { 53 if (status === "done") return `<span class="status-badge status-done">done</span>`; 54 if (status === "in-progress") return `<span class="status-badge status-in-progress">in progress</span>`; 55 return `<span class="status-badge status-planned">planned</span>`; 56 }; 57 58 const l5ChecklistItems = [ 59 { 60 area: "Docs checklist + /l5 try page", 61 status: "done", 62 notes: "This docs section and /l5 landing page exist.", 63 }, 64 { 65 area: "Lua source detection (.lua) in loader", 66 status: "done", 67 notes: "disk + parse support .lua, including .mjs -> .lua -> .lisp fallback.", 68 }, 69 { 70 area: "Lua runtime adapter (Wasmoon)", 71 status: "done", 72 notes: "Wasmoon runtime is vendored and wired through lib/l5.mjs.", 73 }, 74 { 75 area: "L5 lifecycle bridge (setup/draw/events)", 76 status: "in-progress", 77 notes: "setup/draw + key/mouse callbacks are mapped; advanced callbacks remain.", 78 }, 79 { 80 area: "Core graphics API parity", 81 status: "in-progress", 82 notes: "background/fill/stroke/line/rect/circle/ellipse/text/triangle/quad mapped.", 83 }, 84 { 85 area: "Input globals (mouse/key/frame)", 86 status: "in-progress", 87 notes: "mouse/key/frame globals are injected each frame; parity is still incomplete.", 88 }, 89 { 90 area: "Publish .lua pieces", 91 status: "done", 92 notes: "Upload + media tracking now accept lua extension.", 93 }, 94 { 95 area: "Trust/restricted API posture for Lua", 96 status: "done", 97 notes: "Lua pieces use trustLevel=l5 and run through restricted API policy.", 98 }, 99 ]; 100 101 const l5ChecklistBody = ` 102 <p> 103 This board tracks what is actually implemented, not intended parity. 104 Update statuses as work lands. 105 </p> 106 <table> 107 <thead> 108 <tr> 109 <th>Area</th> 110 <th>Status</th> 111 <th>Notes</th> 112 </tr> 113 </thead> 114 <tbody> 115 ${l5ChecklistItems 116 .map( 117 (item) => ` 118 <tr> 119 <td>${item.area}</td> 120 <td>${l5StatusBadge(item.status)}</td> 121 <td>${item.notes}</td> 122 </tr> 123 `, 124 ) 125 .join("")} 126 </tbody> 127 </table> 128 <p><strong>Status date:</strong> 2026-02-26</p> 129 `.trim(); 130 131 const l5LifecycleBody = ` 132 <table> 133 <thead> 134 <tr> 135 <th>L5 callback</th> 136 <th>AC bridge</th> 137 <th>Status</th> 138 </tr> 139 </thead> 140 <tbody> 141 <tr><td><code>setup()</code></td><td><code>boot($)</code></td><td>${l5StatusBadge("done")}</td></tr> 142 <tr><td><code>draw()</code></td><td><code>paint($)</code></td><td>${l5StatusBadge("done")}</td></tr> 143 <tr><td><code>keyPressed()</code></td><td><code>act($)</code> keyboard events</td><td>${l5StatusBadge("done")}</td></tr> 144 <tr><td><code>mousePressed()</code></td><td><code>act($)</code> pen/touch events</td><td>${l5StatusBadge("done")}</td></tr> 145 </tbody> 146 </table> 147 `.trim(); 148 149 const l5GraphicsBody = ` 150 <table> 151 <thead> 152 <tr> 153 <th>L5 API</th> 154 <th>AC target</th> 155 <th>Status</th> 156 </tr> 157 </thead> 158 <tbody> 159 <tr><td><code>background()</code></td><td><code>$.wipe()</code></td><td>${l5StatusBadge("done")}</td></tr> 160 <tr><td><code>fill()</code>/<code>stroke()</code></td><td>state + <code>$.ink()</code></td><td>${l5StatusBadge("done")}</td></tr> 161 <tr><td><code>line()</code>/<code>point()</code></td><td><code>$.line()</code>/<code>$.plot()</code></td><td>${l5StatusBadge("done")}</td></tr> 162 <tr><td><code>rect()</code>/<code>circle()</code>/<code>ellipse()</code></td><td><code>$.box()</code>/<code>$.circle()</code>/<code>$.oval()</code></td><td>${l5StatusBadge("done")}</td></tr> 163 <tr><td><code>beginShape()</code>…</td><td><code>$.shape()</code>/<code>$.poly()</code></td><td>${l5StatusBadge("planned")}</td></tr> 164 </tbody> 165 </table> 166 `.trim(); 167 168 const l5InputBody = ` 169 <table> 170 <thead> 171 <tr> 172 <th>L5 global</th> 173 <th>Source in AC</th> 174 <th>Status</th> 175 </tr> 176 </thead> 177 <tbody> 178 <tr><td><code>mouseX</code>/<code>mouseY</code></td><td><code>$.pen.x</code>/<code>$.pen.y</code></td><td>${l5StatusBadge("done")}</td></tr> 179 <tr><td><code>mouseIsPressed</code></td><td><code>$.pen.drawing</code></td><td>${l5StatusBadge("done")}</td></tr> 180 <tr><td><code>width</code>/<code>height</code></td><td><code>$.screen.width</code>/<code>$.screen.height</code></td><td>${l5StatusBadge("done")}</td></tr> 181 <tr><td><code>frameCount</code></td><td>runtime counter</td><td>${l5StatusBadge("done")}</td></tr> 182 </tbody> 183 </table> 184 `.trim(); 185 186 const l5UnsupportedBody = ` 187 <ul> 188 <li><code>rotate()</code> and <code>scale()</code> require matrix transform support.</li> 189 <li><code>bezier()</code> and curve families are not mapped in v1 scope.</li> 190 <li>File/video APIs and full Processing IO are out of scope for initial launch.</li> 191 <li>Do not claim full L5 parity until checklist items move to <em>done</em>.</li> 192 </ul> 193 `.trim(); 194 195 const l5ExamplesBody = ` 196 <p>Use these as starter snippets. Runtime wiring exists; API coverage is still partial.</p> 197 <div class="doc-examples"> 198 <article class="doc-example"> 199 <h3>Pulse Circle</h3> 200 <pre><code class="language-lua">function setup() 201 size(256, 256) 202end 203 204function draw() 205 background(12, 12, 18) 206 local r = 40 + math.sin(frameCount * 0.05) * 20 207 fill(255, 120, 80) 208 circle(width / 2, height / 2, r * 2) 209end</code></pre> 210 </article> 211 <article class="doc-example"> 212 <h3>Mouse Dots</h3> 213 <pre><code class="language-lua">function setup() 214 background(255) 215end 216 217function draw() 218 if mouseIsPressed then 219 fill(30, 30, 30) 220 circle(mouseX, mouseY, 10) 221 end 222end</code></pre> 223 </article> 224 </div> 225 <p> 226 <a href="${AC_ORIGIN}/l5">Open the L5 try page</a> · 227 <a href="${AC_ORIGIN}/prompt">Open prompt</a> 228 </p> 229 `.trim(); 230 231 const processingChecklistItems = [ 232 { 233 area: "Docs checklist + /processing try page", 234 status: "done", 235 notes: "Processing lane docs and /processing route are live.", 236 }, 237 { 238 area: "Shared try-page architecture reuse", 239 status: "done", 240 notes: "Processing page is configured via shared try-page client module.", 241 }, 242 { 243 area: "Processing (Java) -> Lua transpile bridge", 244 status: "in-progress", 245 notes: "v0 transpiler supports callbacks, typed vars, if/while, numeric for loops, and operators.", 246 }, 247 { 248 area: "Runtime parity with Processing reference", 249 status: "planned", 250 notes: "Only a subset of Processing syntax and APIs are mapped in v0.", 251 }, 252 ]; 253 254 const processingChecklistBody = ` 255 <p> 256 This board tracks current Processing-on-AC integration scope. 257 Keep statuses synced with runtime behavior. 258 </p> 259 <table> 260 <thead> 261 <tr> 262 <th>Area</th> 263 <th>Status</th> 264 <th>Notes</th> 265 </tr> 266 </thead> 267 <tbody> 268 ${processingChecklistItems 269 .map( 270 (item) => ` 271 <tr> 272 <td>${item.area}</td> 273 <td>${l5StatusBadge(item.status)}</td> 274 <td>${item.notes}</td> 275 </tr> 276 `, 277 ) 278 .join("")} 279 </tbody> 280 </table> 281 <p><strong>Status date:</strong> 2026-02-28</p> 282 `.trim(); 283 284 const processingLifecycleBody = ` 285 <table> 286 <thead> 287 <tr> 288 <th>Processing callback</th> 289 <th>Runtime target</th> 290 <th>Status</th> 291 </tr> 292 </thead> 293 <tbody> 294 <tr><td><code>void setup()</code></td><td><code>function setup()</code> in L5 runtime</td><td>${l5StatusBadge("done")}</td></tr> 295 <tr><td><code>void draw()</code></td><td><code>function draw()</code> in L5 runtime</td><td>${l5StatusBadge("done")}</td></tr> 296 <tr><td><code>void mousePressed()</code>/<code>void keyPressed()</code></td><td>Mapped directly to L5 event callbacks</td><td>${l5StatusBadge("done")}</td></tr> 297 </tbody> 298 </table> 299 `.trim(); 300 301 const processingSyntaxBody = ` 302 <ul> 303 <li><code>void setup()</code>, <code>void draw()</code>, and common event callbacks are transpiled.</li> 304 <li>Typed declarations such as <code>int</code>, <code>float</code>, and <code>boolean</code> become Lua locals.</li> 305 <li><code>if/else</code>, <code>while</code>, and numeric <code>for</code> loops are translated to Lua block syntax.</li> 306 <li>Operators like <code>!=</code>, <code>&amp;&amp;</code>, and <code>||</code> are rewritten to Lua equivalents.</li> 307 <li>Math helpers like <code>sin()</code> and <code>cos()</code> map to <code>math.sin()</code> and <code>math.cos()</code>.</li> 308 </ul> 309 `.trim(); 310 311 const processingUnsupportedBody = ` 312 <ul> 313 <li>Class declarations, custom object types, and overloaded methods are out of scope for v0.</li> 314 <li>Complex Java generics/arrays and Processing Java-mode library imports are not supported yet.</li> 315 <li>Only syntax that can safely transpile to L5 runtime callbacks is currently targeted.</li> 316 </ul> 317 `.trim(); 318 319 const processingExamplesBody = ` 320 <p>Starter snippets for the Processing v0 transpile bridge.</p> 321 <div class="doc-examples"> 322 <article class="doc-example"> 323 <h3>Pulse Circle</h3> 324 <pre><code class="language-java">void setup() { 325 size(256, 256); 326} 327 328void draw() { 329 background(12, 12, 18); 330 float r = 40 + sin(frameCount * 0.05) * 20; 331 fill(255, 120, 80); 332 circle(width / 2, height / 2, r * 2); 333}</code></pre> 334 </article> 335 <article class="doc-example"> 336 <h3>Mouse Dots</h3> 337 <pre><code class="language-java">void setup() { 338 background(255); 339} 340 341void draw() { 342 if (mouseIsPressed) { 343 fill(20, 20, 20); 344 circle(mouseX, mouseY, 10); 345 } 346}</code></pre> 347 </article> 348 </div> 349 <p> 350 <a href="${AC_ORIGIN}/processing">Open the Processing try page</a> · 351 <a href="${AC_ORIGIN}/docs/processing:syntax">Open syntax notes</a> 352 </p> 353 `.trim(); 354 355 const mjsOverviewBody = ` 356 <p> 357 This lane documents the JavaScript piece API for <code>.mjs</code> pieces on AC. 358 It is the runtime-facing reference for functions used in <code>boot/paint/act/sim/beat</code>. 359 </p> 360 <p> 361 Start with <a href="${AC_ORIGIN}/docs/structure:paint">paint()</a>, 362 then browse <a href="${AC_ORIGIN}/docs/graphics:line">graphics</a>, 363 <a href="${AC_ORIGIN}/docs/interaction:pen">interaction</a>, 364 and <a href="${AC_ORIGIN}/docs/system:reload">system</a>. 365 </p> 366 `.trim(); 367 368 const kidlispOverviewBody = ` 369 <p> 370 KidLisp docs are part of the unified platform docs program and currently use 371 <a href="${LEARN_KIDLISP_ORIGIN}" target="_blank" rel="noopener">learn.kidlisp.com</a> 372 as the canonical public reference. 373 </p> 374 <p> 375 Use this section for cross-links between AC platform APIs and KidLisp language APIs. 376 Long-term source convergence is tracked in <code>/plans/docs-js-lua-overhaul-hitlist.md</code>. 377 </p> 378 <p> 379 <a href="${LEARN_KIDLISP_ORIGIN}/?tab=reference" target="_blank" rel="noopener">Open full KidLisp reference</a> · 380 <a href="${LEARN_KIDLISP_ORIGIN}/?tab=functions" target="_blank" rel="noopener">Open popularity/function view</a> 381 </p> 382 `.trim(); 383 384 const kidlispCoreBody = ` 385 <table> 386 <thead> 387 <tr> 388 <th>Family</th> 389 <th>Examples</th> 390 <th>Canonical source</th> 391 </tr> 392 </thead> 393 <tbody> 394 <tr> 395 <td>Drawing</td> 396 <td><code>wipe</code>, <code>ink</code>, <code>line</code>, <code>box</code>, <code>circle</code></td> 397 <td><a href="${LEARN_KIDLISP_ORIGIN}/?tab=reference" target="_blank" rel="noopener">learn.kidlisp.com reference tab</a></td> 398 </tr> 399 <tr> 400 <td>Transform</td> 401 <td><code>scroll</code>, <code>zoom</code>, <code>spin</code>, <code>blur</code>, <code>bake</code></td> 402 <td><a href="${LEARN_KIDLISP_ORIGIN}/?id=scroll" target="_blank" rel="noopener">Identifier detail pages</a></td> 403 </tr> 404 <tr> 405 <td>Control + Math</td> 406 <td><code>def</code>, <code>later</code>, <code>once</code>, <code>repeat</code>, <code>random</code></td> 407 <td><a href="${LEARN_KIDLISP_ORIGIN}/?id=def" target="_blank" rel="noopener">Learn identifiers</a></td> 408 </tr> 409 </tbody> 410 </table> 411 `.trim(); 412 413 const docs = { 414 // Commands for programming inside of pieces. 415 api: { 416 // 🏛️ Generic 417 structure: { 418 // 🧩 Top Level Piece Functions / meta-level imports. 419 boot: { 420 label: "🥾 Boot", 421 sig: "boot($)", 422 desc: "Runs once when a piece starts. Use it to initialize state and configure systems.", 423 params: [{ name: "$", type: "api", required: true, desc: "Full AC runtime API object for the piece." }], 424 returns: "void", 425 done: true, 426 }, 427 paint: { 428 label: "🎨 Paint", 429 sig: "paint($)", 430 desc: "Runs on each render frame. Draw graphics and update visual state here.", 431 params: [{ name: "$", type: "api", required: true, desc: "Frame API with graphics/input/system helpers." }], 432 returns: "void | boolean", 433 done: true, 434 }, 435 act: { 436 label: "🎪 Act", 437 sig: "act($)", 438 desc: "Runs for input and system events (keyboard, pointer, signals, lifecycle notifications).", 439 params: [{ name: "$", type: "api", required: true, desc: "Event API. Current event is available at $.event." }], 440 returns: "void", 441 done: true, 442 }, 443 sim: { 444 label: "🧮 Sim", 445 sig: "sim($)", 446 desc: "Fixed-step simulation hook for logic updates independent from render timing.", 447 params: [{ name: "$", type: "api", required: true, desc: "Simulation API for deterministic state updates." }], 448 returns: "void", 449 done: true, 450 }, 451 beat: { 452 label: "🥁 Beat", 453 sig: "beat($)", 454 desc: "Runs on system metronome ticks for rhythm-synced behavior.", 455 params: [{ name: "$", type: "api", required: true, desc: "Beat API including sound timing helpers." }], 456 returns: "void", 457 done: true, 458 }, 459 leave: { 460 label: "👋 Leave", 461 sig: "leave($)", 462 desc: "Execute code right before the piece is unloaded.", 463 params: [{ name: "$", type: "api", required: true, desc: "API snapshot available during teardown." }], 464 returns: "void", 465 done: true, 466 }, 467 meta: { 468 label: " 📰 Meta", 469 sig: "meta()", 470 desc: "Optional static metadata declaration for the piece (name, author, capabilities).", 471 returns: "object", 472 done: true, 473 }, 474 preview: { 475 label: "🖼️ Preview", 476 sig: "preview($)", 477 desc: "Render a custom preview thumbnail for galleries and listings.", 478 params: [{ name: "$", type: "api", required: true, desc: "Graphics API for drawing thumbnail output." }], 479 returns: "void", 480 done: true, 481 }, 482 icon: { 483 label: "🪷 Icon", 484 sig: "icon($)", 485 desc: "Render a small icon/fav icon representation for the piece.", 486 params: [{ name: "$", type: "api", required: true, desc: "Graphics API for icon rendering." }], 487 returns: "void", 488 done: true, 489 }, 490 brush: { 491 label: "🖌️ Brush", 492 sig: "brush($)", 493 desc: "For implementing brushes in the `nopaint` system.", 494 params: [{ name: "$", type: "api", required: true, desc: "Nopaint brush API and event context." }], 495 returns: "void", 496 done: true, 497 }, 498 filter: { 499 label: "🥤 Filter", 500 sig: "filter($)", 501 desc: "For implementing filters in the `nopaint` system.", 502 params: [{ name: "$", type: "api", required: true, desc: "Nopaint filter API and source image data." }], 503 returns: "void", 504 done: true, 505 }, 506 curtain: { 507 label: "curtain", 508 sig: "curtain($)", 509 desc: "Top-layer render hook for world-mode pieces.", 510 params: [{ name: "$", type: "api", required: true, desc: "World render API for overlay/cutaway effects." }], 511 returns: "void", 512 done: true, 513 }, 514 background: { 515 label: "🏔️ background", 516 sig: "background($)", 517 desc: "World-system backdrop hook rendered behind foreground actors.", 518 params: [{ name: "$", type: "api", required: true, desc: "World render API for backdrop painting." }], 519 returns: "void", 520 done: true, 521 }, 522 api: { 523 sig: "api", 524 desc: "References all built-in functionality for a top-level function.", 525 returns: "object", 526 done: true, 527 }, 528 DEBUG: { 529 sig: "DEBUG", 530 desc: "A global constant that determines if `AC` is in debug mode.", 531 returns: "boolean", 532 done: true, 533 }, 534 }, 535 // 🖱️ Interaction 536 interaction: { 537 pen: { 538 sig: "pen: { x, y, ... }", 539 desc: "Current primary pointer state (mouse or touch), including coordinates and press state.", 540 returns: "object", 541 examples: ["prompt~line", "prompt~plot"], 542 done: true, 543 }, 544 pens: { 545 sig: "pens(n)", 546 desc: "Read active pointer inputs. Without args returns all pens, with index returns one pointer.", 547 params: [{ name: "n", type: "number", required: false, desc: "Pointer index for a single pen." }], 548 returns: "array | object", 549 done: true, 550 }, 551 pen3d: { 552 sig: "pen3d", 553 desc: "Current XR/controller pointer state when running in immersive input contexts.", 554 returns: "object | null", 555 done: true, 556 }, 557 event: { 558 sig: "event", 559 desc: "The current input/system event being processed in the active callback.", 560 returns: "object", 561 done: true, 562 }, 563 }, 564 // 🖌️ Graphics 565 graphics: { 566 "abstract.bresenham": { 567 sig: "bresenham(x0, y0, x1, y1)", 568 desc: "Returns an array of integer points that make up an aliased line from (x0,y0) to (x1,y1). This function is abstract and does not render anything.", 569 done: true, 570 }, 571 line: { 572 sig: "line(x0, y0, x1, y1) or line({x0, y0, x1, y1}) or line(p1, p2)", 573 desc: "Draw a 1-pixel wide line. Can take 4 coordinates, an object with coordinates, or two points.", 574 params: [ 575 { name: "x0, y0, x1, y1", type: "number", required: false, desc: "Line start and end coordinates." }, 576 { name: "p1, p2", type: "point", required: false, desc: "Alternative point-object form." }, 577 ], 578 returns: "void", 579 examples: ["line", "line:2", "line:5"], 580 example: { type: "piece", entry: "line", height: 288 }, 581 done: true, 582 }, 583 point: { 584 sig: "point(...args) or point({x, y})", 585 desc: "Plot a single pixel within the panned coordinate space. Takes x,y coordinates or a point object.", 586 params: [ 587 { name: "x, y", type: "number", required: false, desc: "Pixel coordinates." }, 588 { name: "{x, y}", type: "object", required: false, desc: "Point-object form." }, 589 ], 590 returns: "void", 591 examples: ["plot", "plot 48 48", "plot 128 128"], 592 example: { type: "piece", entry: "plot", height: 288 }, 593 done: true, 594 }, 595 box: { 596 sig: "box(x, y, w, h, mode)", 597 desc: "Draw a box with optional modes: 'fill' (default), 'outline', 'inline'. Add '*center' to draw from center. Use ':N' for thickness.", 598 body: boxBody, 599 params: [ 600 { name: "x, y", type: "number", required: true, desc: "Top-left or center position (depending on mode)." }, 601 { name: "w, h", type: "number", required: true, desc: "Box width and height." }, 602 { name: "mode", type: "string", required: false, desc: "fill/outline/inline with optional center and thickness modifiers." }, 603 ], 604 returns: "void", 605 examples: ["box", "box:outline", "box:center"], 606 example: { type: "piece", entry: "box", height: 288 }, 607 done: true, 608 }, 609 wipe: { 610 sig: "wipe(color)", 611 desc: "Clear the screen with a solid color. Color can be a single number (0-255 for grayscale) or an array [r,g,b,a].", 612 params: [ 613 { name: "color", type: "number | string | array", required: false, desc: "Fill color. Defaults to black when omitted." }, 614 ], 615 returns: "void", 616 examples: ["wipe", "wipe:red", "wipe:white"], 617 example: { type: "piece", entry: "wipe", height: 288 }, 618 done: true, 619 }, 620 ink: { 621 sig: "ink(color)", 622 desc: "Set the current drawing color. Color can be a single number (0-255 for grayscale) or an array [r,g,b,a].", 623 params: [ 624 { name: "color", type: "number | string | array", required: true, desc: "Next draw color for primitives and text." }, 625 ], 626 returns: "paintApi", 627 examples: ["prompt~ink", "prompt~ink:red", "prompt~ink:black"], 628 done: true, 629 }, 630 631 circle: { 632 sig: "circle(x, y, radius)", 633 desc: "Draw a filled circle centered at (x,y) with the specified radius using the current ink color.", 634 params: [ 635 { name: "x, y", type: "number", required: true, desc: "Circle center coordinates." }, 636 { name: "radius", type: "number", required: true, desc: "Circle radius in pixels." }, 637 ], 638 returns: "void", 639 examples: ["prompt~circle", "prompt~circle:16", "prompt~circle:outline"], 640 done: true, 641 }, 642 layer: { 643 sig: "layer(index | options)", 644 desc: "Work with layered painting buffers for compositing and advanced drawing workflows.", 645 returns: "object", 646 done: true, 647 }, 648 painting: { 649 sig: "painting(width, height, callback) or painting(width, height)", 650 desc: "Create an offscreen painting buffer, optionally drawing into it via callback.", 651 params: [ 652 { name: "width, height", type: "number", required: true, desc: "Buffer dimensions." }, 653 { name: "callback", type: "function", required: false, desc: "Draw callback receiving a paint API." }, 654 ], 655 returns: "painting", 656 done: true, 657 }, 658 inkrn: { 659 sig: "inkrn()", 660 desc: "Read current ink color state as an RGBA array.", 661 returns: "[r, g, b, a]", 662 done: true, 663 }, 664 pagern: { 665 sig: "pagern()", 666 desc: "Read the current active page/buffer reference.", 667 returns: "painting", 668 done: true, 669 }, 670 notice: { 671 sig: "notice(msg, color, opts)", 672 desc: "Show a transient runtime notice/HUD message.", 673 params: [ 674 { name: "msg", type: "string", required: true, desc: "Notice text." }, 675 { name: "color", type: "array", required: false, desc: "Foreground/background color pair." }, 676 { name: "opts", type: "object", required: false, desc: "Duration/behavior options." }, 677 ], 678 returns: "void", 679 done: true, 680 }, 681 blend: { 682 sig: "blend(mode)", 683 desc: "Set blend mode behavior for subsequent draw operations.", 684 returns: "void", 685 done: true, 686 }, 687 page: { 688 sig: "page(buffer)", 689 desc: "Switch the active drawing target buffer.", 690 params: [{ name: "buffer", type: "painting", required: true, desc: "Target buffer/page." }], 691 returns: "void", 692 done: true, 693 }, 694 edit: { 695 sig: "edit(callback)", 696 desc: "Mutate the current pixel buffer with a callback.", 697 params: [{ name: "callback", type: "function", required: true, desc: "Pixel mutation callback." }], 698 returns: "void", 699 done: true, 700 }, 701 copy: { 702 sig: "copy(x, y, w, h)", 703 desc: "Copy pixel data from the current buffer region.", 704 returns: "painting", 705 done: true, 706 }, 707 paste: { 708 sig: "paste(painting, x, y)", 709 desc: "Paste a painting at the given position, anchored from the top left.", 710 params: [ 711 { name: "painting", type: "painting | image", required: true, desc: "Source bitmap, painting id, or URL." }, 712 { name: "x, y", type: "number", required: false, desc: "Target top-left position. Defaults to 0,0." }, 713 { name: "scale", type: "number", required: false, desc: "Optional scale factor." }, 714 ], 715 returns: "void", 716 examples: ["paste", "paste:camera", "paste:under"], 717 example: { type: "piece", entry: "paste", height: 288 }, 718 done: true, 719 }, 720 stamp: { 721 sig: "stamp(painting, x, y, scale)", 722 desc: "Paste a painting centered at (x,y). Useful for sprites and markers.", 723 params: [ 724 { name: "painting", type: "painting | image", required: true, desc: "Source bitmap, painting id, or URL." }, 725 { name: "x, y", type: "number", required: false, desc: "Center position." }, 726 { name: "scale", type: "number", required: false, desc: "Optional scale factor." }, 727 ], 728 returns: "void", 729 examples: ["stamp", "stamp:camera", "stamp:under"], 730 example: { type: "piece", entry: "stamp", height: 288 }, 731 done: true, 732 }, 733 pixel: { 734 sig: "pixel(x, y) -> [r, g, b, a]", 735 desc: "Read a single pixel color from the current active painting buffer.", 736 params: [ 737 { name: "x, y", type: "number", required: true, desc: "Pixel coordinates." }, 738 ], 739 returns: "[r, g, b, a]", 740 examples: ["prompt~pixel"], 741 done: true, 742 }, 743 plot: { 744 sig: "plot(x, y) or plot({x, y})", 745 desc: "Draw one pixel at the given position using current ink color.", 746 params: [ 747 { name: "x, y", type: "number", required: false, desc: "Pixel coordinates." }, 748 { name: "{x, y}", type: "object", required: false, desc: "Point-object form." }, 749 ], 750 returns: "void", 751 examples: ["plot", "plot 32 32", "plot 200 120"], 752 example: { type: "piece", entry: "plot", height: 288 }, 753 done: true, 754 }, 755 flood: { 756 sig: "flood(x, y)", 757 desc: "Flood-fill adjacent matching pixels at (x,y) using current ink color.", 758 params: [ 759 { name: "x, y", type: "number", required: true, desc: "Seed position for the fill operation." }, 760 ], 761 returns: "void", 762 examples: ["prompt~flood", "prompt~flood:blue"], 763 done: true, 764 }, 765 lineAngle: { 766 sig: "lineAngle(x, y, distance, angle)", 767 desc: "Draw a line from origin using polar coordinates.", 768 returns: "void", 769 done: true, 770 }, 771 pline: { 772 sig: "pline(points)", 773 desc: "Draw a connected polyline from a point list.", 774 returns: "void", 775 done: true, 776 }, 777 pppline: { 778 sig: "pppline(points)", 779 desc: "Draw a pixel-perfect polyline with aliased-style stepping.", 780 returns: "void", 781 done: true, 782 }, 783 oval: { 784 sig: "oval(x, y, w, h, mode)", 785 desc: "Draw an ellipse bounded by width and height dimensions.", 786 params: [ 787 { name: "x, y", type: "number", required: true, desc: "Top-left or center position depending on mode." }, 788 { name: "w, h", type: "number", required: true, desc: "Ellipse width and height." }, 789 { name: "mode", type: "string", required: false, desc: "fill/outline variants." }, 790 ], 791 returns: "void", 792 examples: ["oval", "oval:outline", "oval:center"], 793 example: { type: "piece", entry: "oval", height: 288 }, 794 done: true, 795 }, 796 poly: { 797 sig: "poly(x0, y0, x1, y1, ...)", 798 desc: "Draw a polygon from point pairs in sequence.", 799 params: [ 800 { name: "points", type: "number[]", required: true, desc: "Alternating x/y coordinate list." }, 801 ], 802 returns: "void", 803 examples: ["prompt~poly", "prompt~poly:outline"], 804 done: true, 805 }, 806 shape: { 807 sig: "shape(points, mode)", 808 desc: "Draw a higher-level shape from point arrays/objects with optional mode controls.", 809 params: [ 810 { name: "points", type: "array", required: true, desc: "Point list or packed coordinate data." }, 811 { name: "mode", type: "string", required: false, desc: "fill/outline behavior." }, 812 ], 813 returns: "void", 814 examples: ["shape", "shape:outline"], 815 example: { type: "piece", entry: "shape", height: 288 }, 816 done: true, 817 }, 818 grid: { 819 sig: "grid(x, y, w, h, scale)", 820 desc: "Create a uniform grid helper for layout and sampling.", 821 returns: "Grid", 822 done: true, 823 }, 824 draw: { 825 sig: "draw(shapeOrCommand, ...args)", 826 desc: "Execute a generic draw command helper.", 827 returns: "void", 828 done: true, 829 }, 830 printLine: { 831 sig: "printLine(text, x, y, opts)", 832 desc: "Render a single line of text using low-level type metrics.", 833 returns: "void", 834 done: true, 835 }, 836 form: { 837 sig: "form(options)", 838 desc: "Create/manage a higher-level geometric form object for 3D or batched drawing.", 839 returns: "Form", 840 done: true, 841 }, 842 pan: { 843 sig: "pan(x, y)", 844 desc: "Set or offset the current 2D camera pan.", 845 returns: "void", 846 done: true, 847 }, 848 unpan: { 849 sig: "unpan()", 850 desc: "Reset active pan transform.", 851 returns: "void", 852 done: true, 853 }, 854 savepan: { 855 sig: "savepan()", 856 desc: "Store current pan transform state.", 857 returns: "void", 858 done: true, 859 }, 860 loadpan: { 861 sig: "loadpan()", 862 desc: "Restore previously stored pan transform state.", 863 returns: "void", 864 done: true, 865 }, 866 skip: { 867 sig: "skip(n)", 868 desc: "Skip/pad drawing steps in helper-driven sequences.", 869 returns: "void", 870 done: true, 871 }, 872 glaze: { 873 sig: "glaze({ on: bool })", 874 desc: "Enable a fullscreen shader `glaze` effect.", 875 returns: "void", 876 done: true, 877 }, 878 paintCount: { 879 sig: "paintCount", 880 desc: "The number of `paint` frames that have passed.", 881 returns: "bigint", 882 done: true, 883 }, 884 screen: { 885 sig: "screen", 886 desc: "Current screen buffer object with width/height/pixels.", 887 returns: "painting", 888 done: true, 889 }, 890 display: { 891 sig: "display", 892 desc: "A reference to the current display information.", 893 returns: "object", 894 done: true, 895 }, 896 fps: { 897 sig: "fps(value)", 898 desc: "Set target frame rate for draw loops.", 899 returns: "void", 900 done: true, 901 }, 902 resolution: { 903 sig: "resolution(width, height = width, gap = 8)", 904 desc: "Adjust display resolution and optional gap/pixel spacing.", 905 params: [ 906 { name: "width", type: "number", required: true, desc: "Target render width." }, 907 { name: "height", type: "number", required: false, desc: "Target render height (defaults to width)." }, 908 { name: "gap", type: "number", required: false, desc: "Display gap spacing between pixels." }, 909 ], 910 returns: "void", 911 examples: ["prompt~resolution:128", "prompt~resolution:64"], 912 done: true, 913 }, 914 video: { 915 sig: "video(mode, options)", 916 desc: "Access camera/video capture and tracking modes.", 917 returns: "object | void", 918 done: true, 919 }, 920 rec: { 921 sig: "rec", 922 desc: "Recorder subsystem for capturing frames/media output.", 923 returns: "Recorder", 924 done: true, 925 }, 926 needsPaint: { 927 sig: "needsPaint()", 928 desc: "Mark the renderer as dirty so a frame will be painted.", 929 returns: "void", 930 done: true, 931 }, 932 noise16: { 933 sig: "noise16(opts)", 934 desc: "Apply 16-color noise texture overlay.", 935 returns: "void", 936 done: true, 937 }, 938 noise16DIGITPAIN: { 939 sig: "noise16DIGITPAIN(opts)", 940 desc: "Apply DIGITPAIN-flavored 16-color noise texture.", 941 returns: "void", 942 done: true, 943 }, 944 noise16Aesthetic: { 945 sig: "noise16Aesthetic(opts)", 946 desc: "Apply Aesthetic-themed 16-color noise texture.", 947 returns: "void", 948 done: true, 949 }, 950 noise16Sotce: { 951 sig: "noise16Sotce(opts)", 952 desc: "Apply SOTCE-themed 16-color noise texture.", 953 returns: "void", 954 done: true, 955 }, 956 noiseTinted: { 957 sig: "noiseTinted(opts)", 958 desc: "Apply tinted procedural noise to the active buffer.", 959 returns: "void", 960 done: true, 961 }, 962 write: { 963 sig: "write(text, pos, b, bounds, wordWrap)", 964 desc: "Render text into the current painting using current ink, font, and optional bounds.", 965 params: [ 966 { name: "text", type: "string", required: true, desc: "Text content to draw." }, 967 { name: "pos", type: "object | number", required: false, desc: "Position or anchor object." }, 968 { name: "bounds", type: "object", required: false, desc: "Optional clipping/wrapping bounds." }, 969 ], 970 returns: "painting | metrics", 971 examples: ["prompt~write", "prompt~word"], 972 done: true, 973 }, 974 "text.capitalize": { 975 sig: "capitalize(text)", 976 desc: "Capitalize words in a string for display labels/headings.", 977 returns: "string", 978 done: true, 979 }, 980 "text.box": { 981 sig: "box(text, pos, bounds, scale, wordWrap, fontName)", 982 desc: "Measure and layout text block metrics without drawing.", 983 returns: "object", 984 done: true, 985 }, 986 clonePixels: { 987 sig: "clonePixels(buffer)", 988 desc: "Return a cloned pixel buffer for safe mutation.", 989 returns: "Uint8ClampedArray", 990 done: true, 991 }, 992 colorsMatch: { 993 sig: "colorsMatch(color1, color2)", 994 desc: "Checks if two colors `[r, g, b, a]` are the same.", 995 returns: "boolean", 996 done: true, 997 }, 998 color: { 999 sig: "color(?)", 1000 desc: "Return a color `[r, g, b, a]` from a variety of inputs.", 1001 returns: "[r, g, b, a]", 1002 done: true, 1003 }, 1004 resize: { 1005 sig: "resize(bitmap, width, height)", 1006 desc: "Get a fresh resized bitmap with nearest neighbor scaling.", 1007 returns: "painting", 1008 done: true, 1009 }, 1010 Camera: { 1011 sig: "Camera", 1012 desc: "3D camera model/type used by form and world rendering helpers.", 1013 done: true, 1014 }, 1015 Form: { 1016 sig: "Form", 1017 desc: "3D/mesh form primitive type used in advanced rendering.", 1018 done: true, 1019 }, 1020 Dolly: { 1021 sig: "Dolly", 1022 desc: "Camera dolly helper type for 3D transforms and motion.", 1023 done: true, 1024 }, 1025 TRI: { 1026 sig: "TRI", 1027 desc: "Triangle primitive constant for form pipelines.", 1028 done: true, 1029 }, 1030 QUAD: { 1031 sig: "QUAD", 1032 desc: "Quad primitive constant for form pipelines.", 1033 done: true, 1034 }, 1035 LINE: { 1036 sig: "LINE", 1037 desc: "Line primitive constant for form pipelines.", 1038 done: true, 1039 }, 1040 CUBEL: { 1041 sig: "CUBEL", 1042 desc: "Cuboid/cube primitive constant for form pipelines.", 1043 done: true, 1044 }, 1045 ORIGIN: { 1046 sig: "ORIGIN", 1047 desc: "Origin reference constant for transform helpers.", 1048 done: true, 1049 }, 1050 "ui.Button": { 1051 sig: "new Button(box)", 1052 desc: "An interactive button model with a text label.", 1053 returns: "Button", 1054 done: true, 1055 }, 1056 "ui.TextButton": { 1057 sig: "new TextButton(text, pos)", 1058 desc: "An interactive button model with a text label.", 1059 returns: "TextButton", 1060 done: true, 1061 }, 1062 "ui.TextInput": { 1063 sig: "new TextInput($, text, processCommand, options = { palette, font, wrap })", 1064 desc: "An interactive text prompt object.", 1065 returns: "TextInput", 1066 done: true, 1067 }, 1068 "content.add": { 1069 sig: "add(content)", 1070 desc: "Make a request to add content to the DOM.", 1071 returns: "string", 1072 done: true, 1073 }, 1074 "dom.html": { 1075 sig: "html(src)", 1076 desc: "Add `html` content to the DOM.", 1077 returns: "void", 1078 done: true, 1079 }, 1080 "dom.css": { 1081 sig: "css(src)", 1082 desc: "Add `css` content to the DOM.", 1083 returns: "void", 1084 done: true, 1085 }, 1086 "dom.javascript": { 1087 sig: "javascript(src)", 1088 desc: "Add `javascript` content to the DOM.", 1089 returns: "void", 1090 done: true, 1091 }, 1092 "dom.clear": { 1093 sig: "clear()", 1094 desc: "Clear (remove) all DOM content.", 1095 returns: "void", 1096 done: true, 1097 }, 1098 typeface: { 1099 sig: "typeface", 1100 desc: "A reference to the default system typeface.", 1101 returns: "object", 1102 done: true, 1103 }, 1104 cursor: { 1105 sig: "cursor(code)", 1106 desc: "Set the system mouse cursor to a different graphic.", 1107 returns: "void", 1108 done: true, 1109 }, 1110 }, 1111 sound: { 1112 "sound.time": { 1113 sig: "sound.time", 1114 desc: "Current audio engine time in seconds.", 1115 returns: "number", 1116 done: true, 1117 }, 1118 "sound.bpm": { 1119 sig: "sound.bpm(newBPM?)", 1120 desc: "Get or set the current BPM used for beat-based durations.", 1121 params: [{ name: "newBPM", type: "number", required: false, desc: "Optional BPM override." }], 1122 returns: "number", 1123 done: true, 1124 }, 1125 "sound.freq": { 1126 sig: "sound.freq(input)", 1127 desc: "Resolve note names or numeric input to frequency in Hz.", 1128 params: [{ name: "input", type: "string | number", required: true, desc: "Examples: 440, C4, 4C#, A3." }], 1129 returns: "number | null", 1130 done: true, 1131 }, 1132 "sound.microphone": { 1133 sig: "sound.microphone", 1134 desc: "Live microphone object (connect/poll/record + analysis fields).", 1135 returns: "object", 1136 done: true, 1137 }, 1138 "sound.speaker": { 1139 sig: "sound.speaker", 1140 desc: "Live speaker output analysis object (amplitude/waveform/frequency data).", 1141 returns: "object", 1142 done: true, 1143 }, 1144 "sound.play": { 1145 sig: "sound.play(sfx, options, callbacks)", 1146 desc: "Play a registered sample/sfx by id and return a live handle.", 1147 params: [ 1148 { name: "sfx", type: "string", required: true, desc: "Sample id/path to play." }, 1149 { name: "options", type: "object", required: false, desc: "Playback options (volume, pan, loop, speed, etc)." }, 1150 { name: "callbacks", type: "object", required: false, desc: "Lifecycle callbacks (for example kill handlers)." }, 1151 ], 1152 returns: "object", 1153 done: true, 1154 }, 1155 "sound.synth": { 1156 sig: "sound.synth({ tone, type, duration, beats, attack, decay, volume, pan, generator })", 1157 desc: "Play a synthesized voice and return a handle for kill/progress/update.", 1158 params: [{ name: "options", type: "object", required: false, desc: "Synth options object with oscillator and envelope fields." }], 1159 returns: "object", 1160 done: true, 1161 }, 1162 "sound.bubble": { 1163 sig: "sound.bubble({ radius, rise, volume, pan })", 1164 desc: "Spawn a bubble-style synthesized sound voice.", 1165 params: [{ name: "options", type: "object", required: false, desc: "Bubble synth options." }], 1166 returns: "object", 1167 done: true, 1168 }, 1169 "sound.kill": { 1170 sig: "sound.kill(id, fade?)", 1171 desc: "Stop an active synth/sample by id, with optional fade time.", 1172 params: [ 1173 { name: "id", type: "number | bigint | string", required: true, desc: "Active sound identifier." }, 1174 { name: "fade", type: "number", required: false, desc: "Optional fade-out duration." }, 1175 ], 1176 returns: "void", 1177 done: true, 1178 }, 1179 }, 1180 network: { 1181 "net.signup": { 1182 sig: "signup()", 1183 desc: "Redirect a user to the signup screen.", 1184 returns: "void", 1185 done: true, 1186 }, 1187 "net.login": { 1188 sig: "login()", 1189 desc: "Redirect a user to the login screen.", 1190 returns: "void", 1191 done: true, 1192 }, 1193 "net.logout": { 1194 sig: "logout()", 1195 desc: "Log a user out and redirect them to the `prompt`.", 1196 returns: "void", 1197 done: true, 1198 }, 1199 "net.pieces": { 1200 sig: "pieces", 1201 desc: "The system path to all built-in piece code.", 1202 returns: "string", 1203 done: true, 1204 }, 1205 "net.parse": { 1206 sig: "parse(slug)", 1207 desc: "Parse a textual piece slug.", 1208 params: [{ name: "slug", type: "string", required: true, desc: "Piece slug or prompt-style path." }], 1209 returns: "object", 1210 done: true, 1211 }, 1212 "net.userRequest": { 1213 sig: "userRequest(method, endpoint, body)", 1214 desc: "Make an authorized request for a logged in user.", 1215 params: [ 1216 { name: "method", type: "string", required: true, desc: "HTTP method (GET, POST, etc)." }, 1217 { name: "endpoint", type: "string", required: true, desc: "Relative API endpoint." }, 1218 { name: "body", type: "object", required: false, desc: "JSON payload for write requests." }, 1219 ], 1220 returns: "Promise<object>", 1221 done: true, 1222 }, 1223 "net.udp": { 1224 sig: "udp(receive)", 1225 desc: "Loosely connect the UDP receiver.", 1226 params: [{ name: "receive", type: "function", required: true, desc: "Callback for incoming UDP-style messages." }], 1227 returns: "void", 1228 done: true, 1229 }, 1230 "net.lan": { 1231 sig: "lan", 1232 desc: "A reference to the local area network IP if it is available.", 1233 returns: "string | null", 1234 done: true, 1235 }, 1236 "net.iframe": { 1237 sig: "iframe", 1238 desc: "Whether or not the system is running hosted within an `iframe`.", 1239 returns: "boolean", 1240 done: true, 1241 }, 1242 back: { 1243 sig: "back()", 1244 desc: "Go back to the previous piece or prompt if there is no history.", 1245 returns: "void", 1246 done: true, 1247 }, 1248 alias: { 1249 sig: "alias(name, colon, params)", 1250 desc: "Jump to a piece without changing the corner label or url, and ignoring the history stack.", 1251 params: [ 1252 { name: "name", type: "string", required: true, desc: "Piece slug to load." }, 1253 { name: "colon", type: "array", required: false, desc: "Colon params to pass through." }, 1254 { name: "params", type: "array", required: false, desc: "Space params to pass through." }, 1255 ], 1256 returns: "void", 1257 done: true, 1258 }, 1259 load: { 1260 sig: "async load(parsed, fromHistory, alias, devReload, loadedCallback)", 1261 desc: "Load a piece after parsing a slug, with various options.", 1262 params: [ 1263 { name: "parsed", type: "object", required: true, desc: "Parsed slug payload." }, 1264 { name: "fromHistory", type: "boolean", required: false, desc: "Treat this load as history navigation." }, 1265 { name: "alias", type: "boolean", required: false, desc: "Skip URL/label rewrite when true." }, 1266 { name: "devReload", type: "boolean", required: false, desc: "Set dev-reload mode." }, 1267 { name: "loadedCallback", type: "function", required: false, desc: "Callback after load success." }, 1268 ], 1269 returns: "Promise<void>", 1270 done: true, 1271 }, 1272 slug: { 1273 sig: "slug", 1274 desc: "The full piece address containing its name, colon parameters, and space separated parameters.", 1275 returns: "object", 1276 done: true, 1277 }, 1278 piece: { 1279 sig: "piece", 1280 desc: "The name of the running piece.", 1281 returns: "string", 1282 done: true, 1283 }, 1284 query: { 1285 sig: "query", 1286 desc: "An object containing the system's URL query parameters.", 1287 returns: "object", 1288 done: true, 1289 }, 1290 params: { 1291 sig: "params", 1292 desc: "Array of space-delimited piece parameters from the current slug.", 1293 returns: "array", 1294 done: true, 1295 }, 1296 colon: { 1297 sig: "colon", 1298 desc: "Array of colon parameters for the active piece slug.", 1299 returns: "array", 1300 done: true, 1301 }, 1302 preload: { 1303 sig: "async preload(path, parseJSON = true, progressReport, options)", 1304 desc: "Preload a media asset from the network.", 1305 params: [ 1306 { name: "path", type: "string", required: true, desc: "Asset URL or path." }, 1307 { name: "parseJSON", type: "boolean", required: false, desc: "Auto-parse JSON responses." }, 1308 { name: "progressReport", type: "function", required: false, desc: "Progress callback." }, 1309 { name: "options", type: "object", required: false, desc: "Fetch options overrides." }, 1310 ], 1311 returns: "Promise<any>", 1312 done: true, 1313 }, 1314 download: { 1315 sig: "download(filename, data, modifiers)", 1316 desc: "Download a file.", 1317 params: [ 1318 { name: "filename", type: "string", required: true, desc: "Output filename." }, 1319 { name: "data", type: "Blob | string | object", required: true, desc: "Download payload." }, 1320 { name: "modifiers", type: "object", required: false, desc: "Optional mime/options." }, 1321 ], 1322 returns: "void", 1323 done: true, 1324 }, 1325 dark: { 1326 sig: "dark", 1327 desc: "If the system is in dark mode.", 1328 returns: "boolean", 1329 done: true, 1330 }, 1331 jump: { 1332 sig: "jump(to)", 1333 desc: "Navigate to a piece/url or cached code id.", 1334 params: [{ name: "to", type: "string", required: true, desc: "Target piece slug, URL, or code id." }], 1335 returns: "void", 1336 done: true, 1337 }, 1338 leaving: { 1339 sig: "leaving()", 1340 desc: "Returns true if a piece is leaving / a `jump` is in process.", 1341 returns: "boolean", 1342 done: true, 1343 }, 1344 broadcast: { 1345 sig: "broadcast(msg)", 1346 desc: "Send a message to other open `aesthetic.computer` tabs.", 1347 params: [{ name: "msg", type: "any", required: true, desc: "Broadcast payload." }], 1348 returns: "void", 1349 done: true, 1350 }, 1351 "net.socket": { 1352 sig: "socket(receive)", 1353 desc: "Hook into the piece's socket server with a receive callback.", 1354 params: [{ name: "receive", type: "function", required: true, desc: "Callback for socket events/messages." }], 1355 returns: "object | void", 1356 done: true, 1357 }, 1358 "net.devReload": { 1359 sig: "devReload", 1360 desc: "A flag that determines if the piece code was just reloaded in development.", 1361 returns: "boolean", 1362 done: true, 1363 }, 1364 "net.web": { 1365 sig: "web(url, jumpOut)", 1366 desc: "Jump the browser to a new url.", 1367 params: [ 1368 { name: "url", type: "string", required: true, desc: "Destination URL." }, 1369 { name: "jumpOut", type: "boolean", required: false, desc: "Open externally when true." }, 1370 ], 1371 returns: "void", 1372 done: true, 1373 }, 1374 "net.host": { 1375 sig: "host", 1376 desc: "The current network host.", 1377 returns: "string", 1378 done: true, 1379 }, 1380 "net.rewrite": { 1381 sig: "rewrite(path, historical = false)", 1382 desc: "Rewrite a new URL / parameter path without affecting the history.", 1383 params: [ 1384 { name: "path", type: "string", required: true, desc: "New path/query to write." }, 1385 { name: "historical", type: "boolean", required: false, desc: "Whether to push history." }, 1386 ], 1387 returns: "void", 1388 done: true, 1389 }, 1390 "net.refresh": { 1391 sig: "refresh()", 1392 desc: "Refresh the page / restart `aesthetic.computer`.", 1393 returns: "void", 1394 done: true, 1395 }, 1396 "net.waitForPreload": { 1397 sig: "waitForPreload()", 1398 desc: "Tell the system to wait until preloading is finished before painting.", 1399 returns: "void", 1400 done: true, 1401 }, 1402 "net.preloaded": { 1403 sig: "preloaded()", 1404 desc: "Tell the system that all preloading is done.", 1405 returns: "void", 1406 done: true, 1407 }, 1408 }, 1409 number: { 1410 simCount: { 1411 sig: "simCount", 1412 desc: "The number of simulation frames passed.", 1413 returns: "bigint", 1414 done: true, 1415 }, 1416 seconds: { 1417 sig: "seconds(s)", 1418 desc: "Convert seconds to `sim` frames.", 1419 params: [{ name: "s", type: "number", required: true, desc: "Seconds value." }], 1420 returns: "number", 1421 done: true, 1422 }, 1423 "num.add": { 1424 sig: "add(...numbers) | add(numbers[])", 1425 desc: "Add all numeric inputs and return the total.", 1426 returns: "number", 1427 done: true, 1428 }, 1429 "num.wrap": { 1430 sig: "wrap(n, to)", 1431 desc: "Wrap a number into the range 0..to (exclusive upper bound).", 1432 returns: "number", 1433 done: true, 1434 }, 1435 "num.even": { 1436 sig: "even(n)", 1437 desc: "Return true when n is evenly divisible by 2.", 1438 returns: "boolean", 1439 done: true, 1440 }, 1441 "num.odd": { 1442 sig: "odd(n)", 1443 desc: "Return true when n is odd.", 1444 returns: "boolean", 1445 done: true, 1446 }, 1447 "num.clamp": { 1448 sig: "clamp(value, low, high)", 1449 desc: "Clamp a value between low and high.", 1450 returns: "number", 1451 done: true, 1452 }, 1453 "num.rand": { 1454 sig: "rand()", 1455 desc: "Return a random float in the range 0..1.", 1456 returns: "number", 1457 done: true, 1458 }, 1459 "num.randInt": { 1460 sig: "randInt(n)", 1461 desc: "Gets a random integer.", 1462 done: true, 1463 }, 1464 "num.randInd": { 1465 sig: "randInd(arr)", 1466 desc: "Generates a random index from an array.", 1467 done: true, 1468 }, 1469 "num.randIntArr": { 1470 sig: "randIntArr(n, count)", 1471 desc: "Generates an array of random integers from 0-n (inclusive)", 1472 done: true, 1473 }, 1474 "num.randIntRange": { 1475 sig: "randIntRange(low, high)", 1476 desc: "Generates an integer from low-high (inclusive)", 1477 done: true, 1478 }, 1479 "num.rangedInts": { 1480 sig: "rangedInts(ints)", 1481 desc: "Converts an array of strings formatted like 1-100 into an array of random integer ranges. Useful for color ranges.", 1482 done: true, 1483 }, 1484 "num.multiply": { 1485 sig: "multiply(operands, n)", 1486 desc: "Multiplies one or more [] operands by n and returns a Number or Array.", 1487 done: true, 1488 }, 1489 "num.dist": { 1490 sig: "dist(x1, y1, x2, y2)", 1491 desc: "Compute the distance between two 2D points.", 1492 done: true, 1493 }, 1494 "num.dist3d": { 1495 sig: "dist3d(p1, p2)", 1496 desc: "Compute the distance between two 3D points as [x, y, z].", 1497 done: true, 1498 }, 1499 "num.perlin": { 1500 sig: "perlin(x, y)", 1501 desc: "Compute a 2D perlin noise value.", 1502 done: true, 1503 }, 1504 "num.radians": { 1505 sig: "radians(deg)", 1506 desc: "Convert degrees to radians.", 1507 done: true, 1508 }, 1509 "num.degrees": { 1510 sig: "degrees(rad)", 1511 desc: "Convert radians to degrees.", 1512 done: true, 1513 }, 1514 "num.lerp": { 1515 sig: "lerp(a, b, amount)", 1516 desc: "Slides a number between a and b by a normalized amount.", 1517 done: true, 1518 }, 1519 "num.map": { 1520 sig: "map(num, inMin, inMax, outMin, outMax)", 1521 desc: "Maps a number within a range to a new range.", 1522 done: true, 1523 }, 1524 "num.arrMax": { 1525 sig: "arrMax(arr)", 1526 desc: "Return the maximum number in an array.", 1527 done: true, 1528 }, 1529 "num.arrCompress": { 1530 sig: "arrCompress(arr, n)", 1531 desc: "Return a new array with every nth index missing.", 1532 done: true, 1533 }, 1534 "num.Track": { 1535 sig: "new Track(values, result)", 1536 desc: "Lerp a value using a stepping function, with optional quantization.", 1537 done: true, 1538 }, 1539 "num.p2.of": { 1540 sig: "of(x, y)", 1541 desc: "Turns two values into an {x, y} point.", 1542 done: true, 1543 }, 1544 "num.p2.len": { 1545 sig: "len(pA)", 1546 desc: "Gets the length of the point as a vector.", 1547 done: true, 1548 }, 1549 "num.p2.norm": { 1550 sig: "norm(p)", 1551 desc: "Normalizes a vector to have a length of 1.", 1552 done: true, 1553 }, 1554 "num.p2.eq": { 1555 sig: "eq(p1, p2)", 1556 desc: "Checks for the equality of two points.", 1557 done: true, 1558 }, 1559 "num.p2.inc": { 1560 sig: "inc(pout, pin)", 1561 desc: "Mutably adds P->in to P->out.", 1562 done: true, 1563 }, 1564 "num.p2.scl": { 1565 sig: "scl(pout, pin)", 1566 desc: "Mutably scales P->out by P->in.", 1567 done: true, 1568 }, 1569 "num.p2.add": { 1570 sig: "add(pA, pB)", 1571 desc: "Immutably adds pA + pB.", 1572 done: true, 1573 }, 1574 "num.p2.sub": { 1575 sig: "sub(pA, pB)", 1576 desc: "Immutably subtracts pA - pB.", 1577 done: true, 1578 }, 1579 "num.p2.rot": { 1580 sig: "rot(p, angle)", 1581 desc: "Immutably rotates p by angle in radians.", 1582 done: true, 1583 }, 1584 "num.p2.mul": { 1585 sig: "mul(pA, pB)", 1586 desc: "Immutably multiplies pA * pB.", 1587 done: true, 1588 }, 1589 "num.p2.div": { 1590 sig: "div(pA, pB)", 1591 desc: "Immutably divides pA / pB. Expands pA to an {x, y} if it is a single number.", 1592 done: true, 1593 }, 1594 "num.p2.mid": { 1595 sig: "mid(pA, pB)", 1596 desc: "Calculates the midpoint between two points.", 1597 done: true, 1598 }, 1599 "num.p2.dist": { 1600 sig: "dist(pA, pB)", 1601 desc: "Calculates the distance between two points.", 1602 done: true, 1603 }, 1604 "num.p2.angle": { 1605 sig: "angle(pA, pB)", 1606 desc: "Calculates the angle between two points.", 1607 done: true, 1608 }, 1609 "num.p2.dot": { 1610 sig: "dot(pA, pB)", 1611 desc: "Calculates the dot product of two points.", 1612 done: true, 1613 }, 1614 "num.p2.floor": { 1615 sig: "floor(p)", 1616 desc: "Applies the floor function to both x and y coordinates of a point.", 1617 done: true, 1618 }, 1619 "num.midp": { 1620 sig: "midp(a, b)", 1621 desc: "Find the midpoint between two [x, y] coordinates.", 1622 done: true, 1623 }, 1624 "num.number": { 1625 sig: "number(maybeNumber)", 1626 desc: "Determine if the value is a number or not.", 1627 done: true, 1628 }, 1629 "num.intersects": { 1630 sig: "intersects(line1, line2)", 1631 desc: "Compute whether two lines intersect. A line is: `{x0, y0, x1, y1}`", 1632 done: true, 1633 }, 1634 "num.signedCeil": { 1635 sig: "signedCeil(n)", 1636 desc: "Ceil a number away from 0.", 1637 done: true, 1638 }, 1639 "num.signedFloor": { 1640 sig: "signedFloor(val)", 1641 desc: "Floor a number towards 0.", 1642 done: true, 1643 }, 1644 "num.vec2": { 1645 sig: "vec2.?", 1646 desc: "All the `vec2` functions from the `glMatrix` library.", 1647 done: true, 1648 }, 1649 "num.vec3": { 1650 sig: "vec3.?", 1651 desc: "All the `vec3` functions from the `glMatrix` library.", 1652 done: true, 1653 }, 1654 "num.vec4": { 1655 sig: "vec4.?", 1656 desc: "All the `vec4` functions from the `glMatrix` library.", 1657 done: true, 1658 }, 1659 "num.mat3": { 1660 sig: "mat3.?", 1661 desc: "All the `mat3` functions from the `glMatrix` library.", 1662 done: true, 1663 }, 1664 "num.mat4": { 1665 sig: "mat4.?", 1666 desc: "All the `mat4` functions from the `glMatrix` library.", 1667 done: true, 1668 }, 1669 "num.quat": { 1670 sig: "quat.?", 1671 desc: "All the `quat` (quaternion) functions from the `glMatrix` library.", 1672 done: true, 1673 }, 1674 "num.parseColor": { 1675 sig: "parseColor(params)", 1676 desc: "Parses a color from piece params.", 1677 done: true, 1678 }, 1679 "num.findColor": { 1680 sig: "findColor(rgb)", 1681 desc: "Find a color inside of `cssColors` by value", 1682 done: true, 1683 }, 1684 "num.saturate": { 1685 sig: "saturate(rgb, amount = 1)", 1686 desc: "Saturate a color by `amount`.", 1687 done: true, 1688 }, 1689 "num.desaturate": { 1690 sig: "desaturate(rgb, amount = 1)", 1691 desc: "Desaturate a color by `amount`", 1692 done: true, 1693 }, 1694 "num.shiftRGB": { 1695 sig: 'shiftRGB(a, b, step, mode = "lerp", range = 255)', 1696 desc: "Lerp two RGBA arrays, skipping alpha and rounding the output.", 1697 done: true, 1698 }, 1699 "num.rgbToHexStr": { 1700 sig: "rgbToHexStr(r, g, b, prefix = \"\")", 1701 desc: "Convert separate RGB values to a hex string.", 1702 done: true, 1703 }, 1704 "num.hexToRgb": { 1705 sig: "hexToRgb(h)", 1706 desc: "Takes a string/number hex value and outputs an [r, g, b] array.", 1707 done: true, 1708 }, 1709 "num.blend": { 1710 sig: "blend(dst, src, alphaIn = 1)", 1711 desc: "Alpha blends two colors, mutating and returning `dst`.", 1712 done: true, 1713 }, 1714 "num.rgbToHsl": { 1715 sig: "rgbToHsl(r, g, b)", 1716 desc: "Convert rgb to hsl (360, 100, 100).", 1717 done: true, 1718 }, 1719 "num.hslToRgb": { 1720 sig: "hslToRgb(h, s, l)", 1721 desc: "Convert hsl (360, 100, 100) to rgb.", 1722 done: true, 1723 }, 1724 "num.rainbow": { 1725 sig: "rainbow()", 1726 desc: "Return a cycled color from the `rainbow` template.", 1727 done: true, 1728 }, 1729 delay: { 1730 sig: "delay(fun, time)", 1731 desc: "Delay a function by `time` number of sim steps.", 1732 done: true, 1733 }, 1734 blink: { 1735 sig: "blink(time, fun)", 1736 desc: "A looped `delay`.", 1737 done: true, 1738 }, 1739 "geo.Box": { 1740 sig: "new Box()", 1741 desc: "A dynamic box with helpful methods.", 1742 done: true, 1743 }, 1744 "geo.DirtyBox": { 1745 sig: "new DirtyBox()", 1746 desc: "A box model implementing dirty rectangle optimization.", 1747 done: true, 1748 }, 1749 "geo.Grid": { 1750 sig: "new Grid(x, y, w, h, s = 1)", 1751 desc: "A 2 dimensional uniform grid, using a box as the frame (with scaling).", 1752 done: true, 1753 }, 1754 "geo.Circle": { 1755 sig: "new Circle(x, y, radius = 8)", 1756 desc: "A generic circle model.", 1757 done: true, 1758 }, 1759 "geo.linePointsFromAngle": { 1760 sig: "linePointsFromAngle(x1, y1, dist, degrees)", 1761 desc: "Project outwards from an origin point at dist, and degrees to get the full line.", 1762 done: true, 1763 }, 1764 "geo.pointFrom": { 1765 sig: "pointFrom(x, y, angle, dist)", 1766 desc: "Project outwards from a point at an `angle` and `dist` and get the resulting point.", 1767 done: true, 1768 }, 1769 "geo.Race": { 1770 sig: "new Race(opts = { quantized: true })", 1771 desc: "Follows a point over time.", 1772 done: true, 1773 }, 1774 "geo.Quantizer": { 1775 sig: "new Quantizer(opts)", 1776 desc: "A simple model for lazy following of a 3D point.", 1777 done: true, 1778 }, 1779 }, 1780 help: { 1781 choose: { 1782 sig: "choose(a, b, ...)", 1783 desc: "Randomly return one of the arguments.", 1784 returns: "any", 1785 done: true, 1786 }, 1787 flip: { 1788 sig: "flip()", 1789 desc: "Flip a coin, returning true or false.", 1790 returns: "boolean", 1791 done: true, 1792 }, 1793 repeat: { 1794 sig: "repeat(n, fn)", 1795 desc: "Run a function `n` times, passing in `i` on each iteration and returning an array of the results (like map).", 1796 params: [ 1797 { name: "n", type: "number", required: true, desc: "Iteration count (floored)." }, 1798 { name: "fn", type: "function", required: true, desc: "Callback receiving index i." }, 1799 ], 1800 returns: "array", 1801 done: true, 1802 }, 1803 every: { 1804 sig: "every(obj, value)", 1805 desc: "Set every property of an object to a certain value.", 1806 params: [ 1807 { name: "obj", type: "object", required: true, desc: "Target object to mutate." }, 1808 { name: "value", type: "any", required: true, desc: "Value assigned to every key." }, 1809 ], 1810 returns: "void", 1811 done: true, 1812 }, 1813 any: { 1814 sig: "any(objOrArray)", 1815 desc: "Returns a random value from an object, or array.", 1816 returns: "any", 1817 done: true, 1818 }, 1819 anyIndex: { 1820 sig: "anyIndex(array)", 1821 desc: "Returns a random index value from an array.", 1822 returns: "number", 1823 done: true, 1824 }, 1825 anyKey: { 1826 sig: "anyKey(obj)", 1827 desc: "Returns a random key from an object.", 1828 returns: "string", 1829 done: true, 1830 }, 1831 each: { 1832 sig: "each(obj, fun)", 1833 desc: "Run a function on every value in an object.", 1834 params: [ 1835 { name: "obj", type: "object", required: true, desc: "Object to iterate." }, 1836 { name: "fun", type: "function", required: true, desc: "Callback receiving (value, key)." }, 1837 ], 1838 returns: "void", 1839 done: true, 1840 }, 1841 shuffleInPlace: { 1842 sig: "shuffleInPlace(array)", 1843 desc: "Shuffles an array, mutating it.", 1844 returns: "array", 1845 done: true, 1846 }, 1847 "gizmo.Hourglass": { 1848 sig: "new Hourglass(max, { completed, flipped, every, autoFlip = false }, startingTicks = 0)", 1849 desc: "A repeatable timer with callbacks.", 1850 returns: "Hourglass", 1851 done: true, 1852 }, 1853 "gizmo.EllipsisTicker": { 1854 sig: "new EllipsisTicker()", 1855 desc: "An animated `...` string for showing processing indicators.", 1856 returns: "EllipsisTicker", 1857 done: true, 1858 }, 1859 }, 1860 system: { 1861 signal: { 1862 sig: "signal(content)", 1863 desc: "Send a message through the `signal` system, good for communicating with added DOM content.", 1864 params: [{ name: "content", type: "any", required: true, desc: "Signal payload." }], 1865 returns: "void", 1866 done: true, 1867 }, 1868 sideload: { 1869 sig: "sideload(type)", 1870 desc: "Open a file chooser to load a file.", 1871 params: [{ name: "type", type: "string", required: false, desc: "Optional file type filter." }], 1872 returns: "Promise<File | null>", 1873 done: true, 1874 }, 1875 user: { 1876 sig: "user", 1877 desc: "A reference to the currently logged in user.", 1878 returns: "object | null", 1879 done: true, 1880 }, 1881 vscode: { 1882 sig: "vscode", 1883 desc: "A flag that's true while running the VS Code extension.", 1884 returns: "boolean", 1885 done: true, 1886 }, 1887 meta: { 1888 sig: "meta(data)", 1889 desc: "Add meta to the common api so the data can be overridden as needed.", 1890 params: [{ name: "data", type: "object", required: true, desc: "Meta fields to merge into runtime state." }], 1891 returns: "void", 1892 done: true, 1893 }, 1894 reload: { 1895 sig: "reload({ piece, name, source, codeChannel })", 1896 desc: "Reload / start a piece in various ways. Used especially in live development.", 1897 params: [{ name: "options", type: "object", required: true, desc: "Reload options including piece/name/source/codeChannel." }], 1898 returns: "void", 1899 done: true, 1900 }, 1901 pieceCount: { 1902 sig: "pieceCount", 1903 desc: "Keeps track of how many pieces have been run so far in a session.", 1904 returns: "number", 1905 done: true, 1906 }, 1907 store: { 1908 sig: "store", 1909 desc: "An object for keeping data in across piece jumps.", 1910 returns: "object", 1911 done: true, 1912 }, 1913 "store.persist": { 1914 sig: 'store.persist(key, method = "local")', 1915 desc: "Save a storage key with associated data in the user's browser.", 1916 params: [ 1917 { name: "key", type: "string", required: true, desc: "Store key to persist." }, 1918 { name: "method", type: "string", required: false, desc: "Persistence backend (for example `local` or `local:db`)." }, 1919 ], 1920 returns: "Promise<void>", 1921 done: true, 1922 }, 1923 "store.retrieve": { 1924 sig: 'store.retrieve(key, method = "local")', 1925 desc: "Load a storage key with associated data.", 1926 params: [ 1927 { name: "key", type: "string", required: true, desc: "Store key to read." }, 1928 { name: "method", type: "string", required: false, desc: "Persistence backend." }, 1929 ], 1930 returns: "Promise<any>", 1931 done: true, 1932 }, 1933 "store.delete": { 1934 sig: 'store.delete(key, method = "local")', 1935 desc: "Remove a storage key and any saved data.", 1936 params: [ 1937 { name: "key", type: "string", required: true, desc: "Store key to remove." }, 1938 { name: "method", type: "string", required: false, desc: "Persistence backend." }, 1939 ], 1940 returns: "Promise<boolean>", 1941 done: true, 1942 }, 1943 debug: { 1944 sig: "debug", 1945 desc: "Reports whether the system is in debug / development mode.", 1946 returns: "boolean", 1947 done: true, 1948 }, 1949 canShare: { 1950 sig: "canShare", 1951 desc: "Whether the current environment supports the Web Share API.", 1952 returns: "boolean", 1953 done: true, 1954 }, 1955 handle: { 1956 sig: "handle()", 1957 desc: "Returns the user's handle, if one exists.", 1958 returns: "string | null", 1959 done: true, 1960 }, 1961 ticket: { 1962 sig: "ticket(name)", 1963 desc: "Open a ticketed paywall by its name.", 1964 params: [{ name: "name", type: "string", required: true, desc: "Ticket/paywall identifier." }], 1965 returns: "void", 1966 done: true, 1967 }, 1968 mint: { 1969 sig: "mint(picture, progress, params)", 1970 desc: "Mint a picture on an external service.", 1971 params: [ 1972 { name: "picture", type: "object", required: true, desc: "Painting/pixel payload." }, 1973 { name: "progress", type: "function", required: false, desc: "Progress callback." }, 1974 { name: "params", type: "object", required: false, desc: "Mint metadata/options." }, 1975 ], 1976 returns: "Promise<any>", 1977 done: true, 1978 }, 1979 print: { 1980 sig: "print(picture, quantity, progress)", 1981 desc: "Print the `pixels` that get passed in via an external service. Stickers only right now.", 1982 params: [ 1983 { name: "picture", type: "object", required: true, desc: "Painting/pixel payload." }, 1984 { name: "quantity", type: "number", required: false, desc: "Requested print quantity." }, 1985 { name: "progress", type: "function", required: false, desc: "Progress callback." }, 1986 ], 1987 returns: "Promise<any>", 1988 done: true, 1989 }, 1990 zip: { 1991 sig: "zip(content, progress)", 1992 desc: "Create a zip file of the content. Auto-encodes paintings.", 1993 params: [ 1994 { name: "content", type: "object | array", required: true, desc: "Files/content to include." }, 1995 { name: "progress", type: "function", required: false, desc: "Progress callback." }, 1996 ], 1997 returns: "Promise<Blob>", 1998 done: true, 1999 }, 2000 "motion.start": { 2001 sig: "start()", 2002 desc: "Start tracking device motion.", 2003 returns: "Promise<void> | void", 2004 done: true, 2005 }, 2006 "motion.stop": { 2007 sig: "stop()", 2008 desc: "Stop tracking device motion.", 2009 returns: "void", 2010 done: true, 2011 }, 2012 "motion.current": { 2013 sig: "current", 2014 desc: "Populated with the device motion data upon `motion.start()`.", 2015 returns: "object | null", 2016 done: true, 2017 }, 2018 speak: { 2019 sig: "speak(utterance, voice, mode, opts)", 2020 desc: "Speak an `utterance` aloud.", 2021 params: [ 2022 { name: "utterance", type: "string", required: true, desc: "Text to speak." }, 2023 { name: "voice", type: "string", required: false, desc: "Voice id/preset." }, 2024 { name: "mode", type: "string", required: false, desc: "Speech backend mode." }, 2025 { name: "opts", type: "object", required: false, desc: "Optional speech options." }, 2026 ], 2027 returns: "Promise<void> | void", 2028 done: true, 2029 }, 2030 act: { 2031 sig: "act(event, data)", 2032 desc: "Broadcast an `act` event through the system.", 2033 params: [ 2034 { name: "event", type: "string", required: true, desc: "Event name." }, 2035 { name: "data", type: "any", required: false, desc: "Optional payload." }, 2036 ], 2037 returns: "void", 2038 done: true, 2039 }, 2040 "get.painting().by()": { 2041 sig: "get.painting(code, opts).by(handle, opts)", 2042 desc: "Retrieve a painting from network storage.", 2043 returns: "Promise<object>", 2044 done: true, 2045 }, 2046 upload: { 2047 sig: "async upload(filename, data, progress, bucket)", 2048 desc: "Upload a media file to network storage.", 2049 params: [ 2050 { name: "filename", type: "string", required: true, desc: "Destination filename." }, 2051 { name: "data", type: "Blob | Uint8Array | string", required: true, desc: "File payload." }, 2052 { name: "progress", type: "function", required: false, desc: "Progress callback." }, 2053 { name: "bucket", type: "string", required: false, desc: "Target storage bucket." }, 2054 ], 2055 returns: "Promise<object>", 2056 done: true, 2057 }, 2058 "code.channel": { 2059 sig: "channel(chan)", 2060 desc: "Set the current code channel for live development.", 2061 params: [{ name: "chan", type: "string", required: true, desc: "Channel name/id." }], 2062 returns: "void", 2063 done: true, 2064 }, 2065 encode: { 2066 sig: "async encode(file)", 2067 desc: "File should be { type, data } where type is `png`, `webp`, or `jpg`, etc.", 2068 params: [{ name: "file", type: "object", required: true, desc: "Encoder payload {type, data, ...}." }], 2069 returns: "Promise<Blob | Uint8Array>", 2070 done: true, 2071 }, 2072 file: { 2073 sig: "async file()", 2074 desc: "Request a file from the user.", 2075 returns: "Promise<File | null>", 2076 done: true, 2077 }, 2078 authorize: { 2079 sig: "async authorize()", 2080 desc: "Authorize a user.", 2081 returns: "Promise<void>", 2082 done: true, 2083 }, 2084 "hand.mediapipe": { 2085 sig: "mediapipe", 2086 desc: "A reference to the mediapipe hand tracking data. Enable through `video`.", 2087 returns: "object | null", 2088 done: true, 2089 }, 2090 "hud.label": { 2091 sig: "label(text, color, offset)", 2092 desc: "Override the piece corner label.", 2093 params: [ 2094 { name: "text", type: "string", required: true, desc: "Label text." }, 2095 { name: "color", type: "string | array", required: false, desc: "Optional label color." }, 2096 { name: "offset", type: "number", required: false, desc: "Optional offset/priority." }, 2097 ], 2098 returns: "void", 2099 done: true, 2100 }, 2101 "hud.currentStatusColor": { 2102 sig: "currentStatusColor()", 2103 desc: "Get the current connection status label color.", 2104 returns: "string | array", 2105 done: true, 2106 }, 2107 "hud.currentLabel": { 2108 sig: "currentLabel()", 2109 desc: "Get the current label content and button.", 2110 returns: "object", 2111 done: true, 2112 }, 2113 "hud.labelBack": { 2114 sig: "labelBack()", 2115 desc: "Jump to the `prompt` with the current label applied.", 2116 returns: "void", 2117 done: true, 2118 }, 2119 send: { 2120 sig: "send({type, content})", 2121 desc: "Send a message to the bios.", 2122 params: [{ name: "message", type: "object", required: true, desc: "Message payload with `type` and optional `content`." }], 2123 returns: "void", 2124 done: true, 2125 }, 2126 platform: { 2127 sig: "platform", 2128 desc: "Get the current host platform.", 2129 returns: "string", 2130 done: true, 2131 }, 2132 history: { 2133 sig: "history", 2134 desc: "An array of previously visited pieces in a session.", 2135 returns: "array", 2136 done: true, 2137 }, 2138 "bgm.set": { 2139 sig: "set(trackNumber, volume)", 2140 desc: "Start a background music track, persisting across jumps.", 2141 params: [ 2142 { name: "trackNumber", type: "number | string", required: true, desc: "Track id/index." }, 2143 { name: "volume", type: "number", required: false, desc: "Playback gain." }, 2144 ], 2145 returns: "void", 2146 done: true, 2147 }, 2148 "bgm.stop": { 2149 sig: "stop()", 2150 desc: "Stop a background music track.", 2151 returns: "void", 2152 done: true, 2153 }, 2154 "bgm.data": { 2155 sig: "data", 2156 desc: "Gets live analysis data from the current background track.", 2157 returns: "object | null", 2158 done: true, 2159 }, 2160 "system.world": { 2161 sig: "system.world", 2162 desc: "A reference to the world system state if a piece is using it.", 2163 returns: "object | null", 2164 done: true, 2165 }, 2166 "system.nopaint": { 2167 sig: "system.nopaint", 2168 desc: "A reference to the `nopaint` system state that all brushes use.", 2169 returns: "object", 2170 done: true, 2171 }, 2172 flatten: { 2173 sig: "flatten()", 2174 desc: "Paint (bake) all graphics commands immediately.", 2175 returns: "void", 2176 done: true, 2177 }, 2178 connect: { 2179 sig: "connect()", 2180 desc: "Connect with external wallet software.", 2181 returns: "Promise<void> | void", 2182 done: true, 2183 }, 2184 wiggle: { 2185 sig: "wiggle(n, level, speed)", 2186 desc: "Oscillate a value over time using a sine wave.", 2187 params: [ 2188 { name: "n", type: "number", required: true, desc: "Base value." }, 2189 { name: "level", type: "number", required: false, desc: "Amplitude." }, 2190 { name: "speed", type: "number", required: false, desc: "Oscillation speed." }, 2191 ], 2192 returns: "number", 2193 done: true, 2194 }, 2195 dark: { 2196 sig: "dark", 2197 desc: "Gets whether the system is in dark mode.", 2198 returns: "boolean", 2199 done: true, 2200 }, 2201 darkMode: { 2202 sig: "darkMode(enabled)", 2203 desc: "Toggle dark mode on or off with a boolean.", 2204 params: [{ name: "enabled", type: "boolean", required: true, desc: "Target dark mode state." }], 2205 returns: "void", 2206 done: true, 2207 }, 2208 gpuReady: { 2209 sig: "gpuReady", 2210 desc: "Whether the system GPU is ready for rendering.", 2211 returns: "boolean", 2212 done: true, 2213 }, 2214 "gpu.message": { 2215 sig: "message(content)", 2216 desc: "Send a message to the GPU driver.", 2217 params: [{ name: "content", type: "object", required: true, desc: "Driver command payload." }], 2218 returns: "void", 2219 done: true, 2220 }, 2221 }, 2222 mjs: { 2223 overview: { 2224 sig: "MJS / AC piece API overview", 2225 desc: "Entry point for JavaScript piece API docs.", 2226 body: mjsOverviewBody, 2227 done: true, 2228 }, 2229 }, 2230 kidlisp: { 2231 overview: { 2232 sig: "KidLisp API overview", 2233 desc: "How KidLisp docs connect into the unified AC docs system.", 2234 body: kidlispOverviewBody, 2235 done: true, 2236 }, 2237 core: { 2238 sig: "KidLisp core map", 2239 desc: "Core families and canonical source links for KidLisp APIs.", 2240 body: kidlispCoreBody, 2241 done: true, 2242 }, 2243 }, 2244 l5: { 2245 overview: { 2246 sig: "L5 (Lua) on Aesthetic Computer", 2247 desc: "Processing-style Lua compatibility notes and rollout status.", 2248 body: ` 2249 <p> 2250 This is the implementation board for L5 support in AC. 2251 Keep this page aligned with the actual runtime state. 2252 </p> 2253 <p> 2254 <a href="${AC_ORIGIN}/docs/l5:checklist">Open checklist</a> · 2255 <a href="${AC_ORIGIN}/docs/l5:examples">Open examples</a> · 2256 <a href="${AC_ORIGIN}/l5">Open /l5 try page</a> 2257 </p> 2258 `.trim(), 2259 done: true, 2260 }, 2261 checklist: { 2262 sig: "L5 compatibility checklist (v0)", 2263 desc: "Single source of truth for what is implemented right now.", 2264 body: l5ChecklistBody, 2265 done: true, 2266 }, 2267 lifecycle: { 2268 sig: "L5 lifecycle bridge", 2269 desc: "How setup/draw/events map onto AC piece lifecycle hooks.", 2270 body: l5LifecycleBody, 2271 done: "in-progress", 2272 }, 2273 graphics: { 2274 sig: "L5 graphics mapping", 2275 desc: "Core drawing calls and their AC equivalents.", 2276 body: l5GraphicsBody, 2277 done: "in-progress", 2278 }, 2279 input: { 2280 sig: "L5 input globals", 2281 desc: "Frame-updated globals expected by Processing-style sketches.", 2282 body: l5InputBody, 2283 done: "in-progress", 2284 }, 2285 unsupported: { 2286 sig: "Known gaps / out of scope (v1)", 2287 desc: "Features explicitly not shipped yet.", 2288 body: l5UnsupportedBody, 2289 done: true, 2290 }, 2291 examples: { 2292 sig: "L5 example snippets", 2293 desc: "Starter Lua examples for the upcoming runtime.", 2294 body: l5ExamplesBody, 2295 done: true, 2296 }, 2297 size: { 2298 sig: "size(width, height?)", 2299 desc: "Set sketch resolution by forwarding to AC `resolution()`.", 2300 params: [ 2301 { name: "width", type: "number", required: true, desc: "Target width." }, 2302 { name: "height", type: "number", required: false, desc: "Target height (defaults to width)." }, 2303 ], 2304 returns: "void", 2305 done: true, 2306 }, 2307 background: { 2308 sig: "background(r, g, b, a?)", 2309 desc: "Clear frame with a solid color.", 2310 returns: "void", 2311 done: true, 2312 }, 2313 clear: { 2314 sig: "clear()", 2315 desc: "Clear frame to transparent black.", 2316 returns: "void", 2317 done: true, 2318 }, 2319 fill: { 2320 sig: "fill(r, g, b, a?)", 2321 desc: "Set fill color for subsequent shape and text draws.", 2322 returns: "void", 2323 done: true, 2324 }, 2325 noFill: { 2326 sig: "noFill()", 2327 desc: "Disable shape fill rendering.", 2328 returns: "void", 2329 done: true, 2330 }, 2331 stroke: { 2332 sig: "stroke(r, g, b, a?)", 2333 desc: "Set stroke color for line/outline rendering.", 2334 returns: "void", 2335 done: true, 2336 }, 2337 noStroke: { 2338 sig: "noStroke()", 2339 desc: "Disable stroke rendering.", 2340 returns: "void", 2341 done: true, 2342 }, 2343 strokeWeight: { 2344 sig: "strokeWeight(weight)", 2345 desc: "Set line/outline thickness.", 2346 params: [{ name: "weight", type: "number", required: true, desc: "Stroke width in pixels." }], 2347 returns: "void", 2348 done: true, 2349 }, 2350 point: { 2351 sig: "point(x, y)", 2352 desc: "Plot a single point using stroke color if stroke is enabled.", 2353 returns: "void", 2354 done: true, 2355 }, 2356 line: { 2357 sig: "line(x1, y1, x2, y2)", 2358 desc: "Draw a line using stroke color.", 2359 returns: "void", 2360 done: true, 2361 }, 2362 rect: { 2363 sig: "rect(x, y, width, height)", 2364 desc: "Draw a rectangle with active fill/stroke state.", 2365 returns: "void", 2366 done: true, 2367 }, 2368 square: { 2369 sig: "square(x, y, size)", 2370 desc: "Draw a square with active fill/stroke state.", 2371 returns: "void", 2372 done: true, 2373 }, 2374 circle: { 2375 sig: "circle(x, y, diameter)", 2376 desc: "Draw a circle with active fill/stroke state.", 2377 returns: "void", 2378 done: true, 2379 }, 2380 ellipse: { 2381 sig: "ellipse(x, y, width, height)", 2382 desc: "Draw an ellipse with active fill/stroke state.", 2383 returns: "void", 2384 done: true, 2385 }, 2386 triangle: { 2387 sig: "triangle(x1, y1, x2, y2, x3, y3)", 2388 desc: "Draw a triangle with active fill/stroke state.", 2389 returns: "void", 2390 done: true, 2391 }, 2392 quad: { 2393 sig: "quad(x1, y1, x2, y2, x3, y3, x4, y4)", 2394 desc: "Draw a quadrilateral with active fill/stroke state.", 2395 returns: "void", 2396 done: true, 2397 }, 2398 text: { 2399 sig: "text(value, x, y)", 2400 desc: "Draw text at coordinates using fill color.", 2401 returns: "void", 2402 done: true, 2403 }, 2404 textSize: { 2405 sig: "textSize(size)", 2406 desc: "Set text size scale for subsequent `text()` draws.", 2407 params: [{ name: "size", type: "number", required: true, desc: "Text size value." }], 2408 returns: "void", 2409 done: true, 2410 }, 2411 textWidth: { 2412 sig: "textWidth(value)", 2413 desc: "Measure text width in pixels.", 2414 returns: "number", 2415 done: true, 2416 }, 2417 frameRate: { 2418 sig: "frameRate(fps)", 2419 desc: "Request a target frame rate for draw calls.", 2420 params: [{ name: "fps", type: "number", required: true, desc: "Target frames per second." }], 2421 returns: "void", 2422 done: true, 2423 }, 2424 noLoop: { 2425 sig: "noLoop()", 2426 desc: "Stop continuous draw execution.", 2427 returns: "void", 2428 done: true, 2429 }, 2430 loop: { 2431 sig: "loop()", 2432 desc: "Resume continuous draw execution.", 2433 returns: "void", 2434 done: true, 2435 }, 2436 isLooping: { 2437 sig: "isLooping()", 2438 desc: "Get whether draw loop is active.", 2439 returns: "boolean", 2440 done: true, 2441 }, 2442 redraw: { 2443 sig: "redraw()", 2444 desc: "Request a one-off draw when loop is disabled.", 2445 returns: "void", 2446 done: true, 2447 }, 2448 random: { 2449 sig: "random(max?) or random(min, max)", 2450 desc: "Generate a random number with optional range.", 2451 returns: "number", 2452 done: true, 2453 }, 2454 map: { 2455 sig: "map(value, inMin, inMax, outMin, outMax)", 2456 desc: "Remap a value from one range to another.", 2457 returns: "number", 2458 done: true, 2459 }, 2460 dist: { 2461 sig: "dist(x1, y1, x2, y2)", 2462 desc: "Calculate Euclidean distance between two points.", 2463 returns: "number", 2464 done: true, 2465 }, 2466 lerp: { 2467 sig: "lerp(start, stop, amount)", 2468 desc: "Linear interpolate between two values.", 2469 returns: "number", 2470 done: true, 2471 }, 2472 radians: { 2473 sig: "radians(degrees)", 2474 desc: "Convert degrees to radians.", 2475 returns: "number", 2476 done: true, 2477 }, 2478 degrees: { 2479 sig: "degrees(radians)", 2480 desc: "Convert radians to degrees.", 2481 returns: "number", 2482 done: true, 2483 }, 2484 constrain: { 2485 sig: "constrain(value, min, max)", 2486 desc: "Clamp a value into a minimum/maximum range.", 2487 returns: "number", 2488 done: true, 2489 }, 2490 millis: { 2491 sig: "millis()", 2492 desc: "Get elapsed runtime milliseconds.", 2493 returns: "number", 2494 done: true, 2495 }, 2496 frameCount: { 2497 sig: "frameCount", 2498 desc: "Number of draw frames processed so far.", 2499 returns: "number", 2500 done: true, 2501 }, 2502 width: { 2503 sig: "width", 2504 desc: "Current sketch width in pixels.", 2505 returns: "number", 2506 done: true, 2507 }, 2508 height: { 2509 sig: "height", 2510 desc: "Current sketch height in pixels.", 2511 returns: "number", 2512 done: true, 2513 }, 2514 mouseX: { 2515 sig: "mouseX", 2516 desc: "Current pointer X coordinate.", 2517 returns: "number", 2518 done: true, 2519 }, 2520 mouseY: { 2521 sig: "mouseY", 2522 desc: "Current pointer Y coordinate.", 2523 returns: "number", 2524 done: true, 2525 }, 2526 pmouseX: { 2527 sig: "pmouseX", 2528 desc: "Previous frame pointer X coordinate.", 2529 returns: "number", 2530 done: true, 2531 }, 2532 pmouseY: { 2533 sig: "pmouseY", 2534 desc: "Previous frame pointer Y coordinate.", 2535 returns: "number", 2536 done: true, 2537 }, 2538 mouseIsPressed: { 2539 sig: "mouseIsPressed", 2540 desc: "Whether pointer is currently pressed.", 2541 returns: "boolean", 2542 done: true, 2543 }, 2544 key: { 2545 sig: "key", 2546 desc: "Last key value from keyboard events.", 2547 returns: "string", 2548 done: true, 2549 }, 2550 keyCode: { 2551 sig: "keyCode", 2552 desc: "Last key code value from keyboard events.", 2553 returns: "number", 2554 done: true, 2555 }, 2556 keyIsPressed: { 2557 sig: "keyIsPressed", 2558 desc: "Whether a key is currently pressed.", 2559 returns: "boolean", 2560 done: true, 2561 }, 2562 }, 2563 processing: { 2564 overview: { 2565 sig: "Processing (Java) on Aesthetic Computer", 2566 desc: "Early Processing-style Java support via transpile bridge to the L5 Lua runtime.", 2567 body: ` 2568 <p> 2569 This lane tracks the Processing v0 bridge, where Java-style Processing code 2570 is transpiled into Lua for AC's L5 runtime. 2571 </p> 2572 <p> 2573 <a href="${AC_ORIGIN}/docs/processing:checklist">Open checklist</a> · 2574 <a href="${AC_ORIGIN}/docs/processing:syntax">Open syntax notes</a> · 2575 <a href="${AC_ORIGIN}/processing">Open /processing try page</a> 2576 </p> 2577 `.trim(), 2578 done: true, 2579 }, 2580 checklist: { 2581 sig: "Processing compatibility checklist (v0)", 2582 desc: "Current integration scope and readiness checkpoints.", 2583 body: processingChecklistBody, 2584 done: "in-progress", 2585 }, 2586 lifecycle: { 2587 sig: "Processing lifecycle bridge", 2588 desc: "How setup/draw/events map from Processing Java syntax to L5 callbacks.", 2589 body: processingLifecycleBody, 2590 done: "in-progress", 2591 }, 2592 syntax: { 2593 sig: "Processing syntax transpile rules (v0)", 2594 desc: "What Java-like syntax the bridge currently rewrites.", 2595 body: processingSyntaxBody, 2596 done: "in-progress", 2597 }, 2598 unsupported: { 2599 sig: "Processing v0 known gaps", 2600 desc: "Features intentionally excluded in the first bridge release.", 2601 body: processingUnsupportedBody, 2602 done: true, 2603 }, 2604 examples: { 2605 sig: "Processing v0 examples", 2606 desc: "Starter snippets for the Processing page.", 2607 body: processingExamplesBody, 2608 done: true, 2609 }, 2610 setup: { 2611 sig: "void setup()", 2612 desc: "Called once at startup. Transpiles to `function setup()`.", 2613 returns: "void", 2614 done: true, 2615 }, 2616 draw: { 2617 sig: "void draw()", 2618 desc: "Called every frame. Transpiles to `function draw()`.", 2619 returns: "void", 2620 done: true, 2621 }, 2622 size: { 2623 sig: "size(width, height)", 2624 desc: "Set sketch resolution in Processing syntax.", 2625 returns: "void", 2626 done: true, 2627 }, 2628 background: { 2629 sig: "background(r, g, b, a?)", 2630 desc: "Clear frame with a color.", 2631 returns: "void", 2632 done: true, 2633 }, 2634 fill: { 2635 sig: "fill(r, g, b, a?)", 2636 desc: "Set fill color for subsequent shapes and text.", 2637 returns: "void", 2638 done: true, 2639 }, 2640 stroke: { 2641 sig: "stroke(r, g, b, a?)", 2642 desc: "Set stroke color for lines/outlines.", 2643 returns: "void", 2644 done: true, 2645 }, 2646 line: { 2647 sig: "line(x1, y1, x2, y2)", 2648 desc: "Draw a line primitive.", 2649 returns: "void", 2650 done: true, 2651 }, 2652 rect: { 2653 sig: "rect(x, y, width, height)", 2654 desc: "Draw a rectangle primitive.", 2655 returns: "void", 2656 done: true, 2657 }, 2658 circle: { 2659 sig: "circle(x, y, diameter)", 2660 desc: "Draw a circle primitive.", 2661 returns: "void", 2662 done: true, 2663 }, 2664 mousePressed: { 2665 sig: "void mousePressed()", 2666 desc: "Pointer-down callback mapped to L5 event hook.", 2667 returns: "void", 2668 done: true, 2669 }, 2670 }, 2671 }, 2672 // 😱 Commands for entering into the prompt. 2673 prompts: { 2674 // 📦 Pack / Export 2675 pack: { 2676 sig: "pack <piece>", 2677 desc: "Download a piece as a self-contained HTML file.", 2678 params: [ 2679 { name: "piece", type: "string", required: true, desc: "Piece name or $code" } 2680 ], 2681 done: true, 2682 }, 2683 bundle: { 2684 sig: "bundle <piece>", 2685 desc: "Download a piece as a self-contained HTML file.", 2686 params: [ 2687 { name: "piece", type: "string", required: true, desc: "Piece name or $code" } 2688 ], 2689 done: true, 2690 }, 2691 m4d: { 2692 sig: "m4d <piece>", 2693 desc: "Download a piece as an offline Max for Live device (.amxd).", 2694 params: [ 2695 { name: "piece", type: "string", required: true, desc: "Piece name or $code" } 2696 ], 2697 done: true, 2698 }, 2699 "4d": { 2700 sig: "4d <piece>", 2701 desc: "Download a piece as an offline Max for Live device (.amxd).", 2702 params: [ 2703 { name: "piece", type: "string", required: true, desc: "Piece name or $code" } 2704 ], 2705 done: true, 2706 }, 2707 m4do: { 2708 sig: "m4do <piece>", 2709 desc: "Download a piece as an online Max for Live device (.amxd) that streams from aesthetic.computer.", 2710 params: [ 2711 { name: "piece", type: "string", required: true, desc: "Piece name" } 2712 ], 2713 done: true, 2714 }, 2715 "4do": { 2716 sig: "4do <piece>", 2717 desc: "Download a piece as an online Max for Live device (.amxd) that streams from aesthetic.computer.", 2718 params: [ 2719 { name: "piece", type: "string", required: true, desc: "Piece name" } 2720 ], 2721 done: true, 2722 }, 2723 // 🔷 Tezos Wallet 2724 tezos: { 2725 sig: "tezos <action> [network]", 2726 desc: "Manage Tezos wallet.", 2727 params: [ 2728 { name: "action", type: "enum", values: ["connect", "disconnect", "status"], required: true }, 2729 { name: "network", type: "enum", values: ["ghostnet", "mainnet"], required: false, default: "ghostnet" } 2730 ], 2731 done: true, 2732 }, 2733 // 🎨 KidLisp Keeping 2734 keep: { 2735 sig: "keep $code", 2736 desc: "Keep $code in your wallet.", 2737 params: [ 2738 { name: "code", type: "string", prefix: "$", required: true, desc: "KidLisp piece code" } 2739 ], 2740 done: true, 2741 }, 2742 tape: { 2743 sig: "tape [duration] [flags]", 2744 desc: "Record your screen.", 2745 params: [ 2746 { name: "duration", type: "number", required: false, default: 5, desc: "Seconds (add 'f' for frames)" }, 2747 { name: "flags", type: "flags", values: ["mic", "nomic", "baktok"], required: false } 2748 ], 2749 done: true, 2750 }, 2751 "tape:add": { 2752 sig: "tape:add", 2753 desc: "Add time to your tape.", 2754 done: false, 2755 hidden: true, 2756 }, 2757 "tape:tt": { 2758 sig: "tape:tt", 2759 desc: "", 2760 done: false, 2761 hidden: true, 2762 }, 2763 "tape:nomic": { 2764 sig: "tape:nomic", 2765 desc: "", 2766 done: false, 2767 hidden: true, 2768 }, 2769 "tape:mic": { 2770 sig: "tape:mic", 2771 desc: "", 2772 done: false, 2773 hidden: true, 2774 }, 2775 tapem: { 2776 sig: "tapem", 2777 desc: "", 2778 done: false, 2779 hidden: true, 2780 }, 2781 "tape:cut": { 2782 sig: "tape:cut", 2783 desc: "", 2784 done: false, 2785 hidden: true, 2786 }, 2787 cut: { 2788 sig: "cut", 2789 desc: "Stop the active tape recording and finalize the clip.", 2790 examples: ["cut", "tape:cut"], 2791 returns: "void", 2792 done: true, 2793 }, 2794 me: { 2795 sig: "me", 2796 desc: "Open your profile.", 2797 examples: ["me"], 2798 returns: "void", 2799 done: true, 2800 }, 2801 scream: { 2802 sig: "scream <message>", 2803 desc: "Scream at all users.", 2804 params: [ 2805 { name: "message", type: "string", required: true, desc: "Your scream text" } 2806 ], 2807 done: true, 2808 }, 2809 nonotifs: { 2810 sig: "nonotifs", 2811 desc: "Turn off notifications.", 2812 examples: ["nonotifs"], 2813 returns: "void", 2814 done: true, 2815 }, 2816 notifs: { 2817 sig: "notifs", 2818 desc: "Turn on notifications.", 2819 examples: ["notifs"], 2820 returns: "void", 2821 done: true, 2822 }, 2823 news: { 2824 sig: "news", 2825 desc: "Aesthetic.computer news.", 2826 done: true, 2827 }, 2828 papers: { 2829 sig: "papers", 2830 desc: "Open papers.aesthetic.computer.", 2831 done: true, 2832 }, 2833 nela: { 2834 sig: "nela", 2835 desc: "Open NELA Computer Club.", 2836 done: true, 2837 }, 2838 selfie: { 2839 sig: "selfie", 2840 desc: "Open the front camera.", 2841 done: false, 2842 }, 2843 cam: { 2844 sig: "cam", 2845 desc: "Take a picture.", 2846 done: false, 2847 }, 2848 camu: { 2849 sig: "camu", 2850 desc: "", 2851 done: false, 2852 hidden: true, 2853 }, 2854 sparkle: { 2855 sig: "sparkle", 2856 desc: "Paint with Maya's really fun brush.", 2857 done: false, 2858 }, 2859 "painting:start": { 2860 sig: "painting:start", 2861 desc: "", 2862 done: false, 2863 hidden: true, 2864 }, 2865 print: { 2866 sig: "print", 2867 desc: "Open the print flow for the current painting.", 2868 examples: ["print"], 2869 returns: "void", 2870 done: true, 2871 }, 2872 mint: { 2873 sig: "mint", 2874 desc: "Open mint flow for the current painting.", 2875 examples: ["mint"], 2876 returns: "void", 2877 done: true, 2878 //TODO: can this open in new tab? 2879 }, 2880 "painting:done": { 2881 sig: "painting:done", 2882 desc: "", 2883 done: false, 2884 hidden: true, 2885 }, 2886 "yes!": { 2887 sig: "yes!", 2888 desc: "Finish your painting.", 2889 examples: ["yes!"], 2890 returns: "void", 2891 done: true, 2892 }, 2893 done: { 2894 sig: "done", 2895 desc: "Finish and confirm your painting.", 2896 examples: ["done"], 2897 returns: "void", 2898 done: true, 2899 }, 2900 flower: { 2901 sig: "flower", 2902 desc: "He loves me.", 2903 done: false, 2904 hidden: true, 2905 }, 2906 petal: { 2907 sig: "petal", 2908 desc: "He loves me not.", 2909 done: false, 2910 hidden: true, 2911 }, 2912 bro: { 2913 sig: "bro", 2914 desc: "Stay out of his room.", 2915 done: false, 2916 }, 2917 sis: { 2918 sig: "sis", 2919 desc: "Don't steal her makeup.", 2920 done: false, 2921 }, 2922 gf: { 2923 sig: "gf", 2924 desc: "Caring confidant.", 2925 done: false, 2926 }, 2927 bf: { 2928 sig: "bf", 2929 desc: "He might care.", 2930 done: false, 2931 }, 2932 bb: { 2933 sig: "bb", 2934 desc: "AC fundraiser.", 2935 done: false, 2936 }, 2937 p: { 2938 sig: "p", 2939 desc: "View your current painting's steps.", 2940 done: false, 2941 }, 2942 pain: { 2943 sig: "pain", 2944 desc: "View your current painting's steps.", 2945 done: false, 2946 }, 2947 load: { 2948 sig: "load", 2949 desc: "", 2950 done: false, 2951 hidden: true, 2952 }, 2953 "mood:nuke": { 2954 sig: "mood:nuke", 2955 desc: "", 2956 done: false, 2957 hidden: true, 2958 }, 2959 "mood:denuke": { 2960 sig: "mood:denuke", 2961 desc: "", 2962 done: false, 2963 hidden: true, 2964 }, 2965 mood: { 2966 sig: "mood [emoji]", 2967 desc: "Set your mood.", 2968 params: [ 2969 { name: "emoji", type: "string", required: false, desc: "Emoji or text mood" } 2970 ], 2971 done: true, 2972 }, 2973 channel: { 2974 sig: "channel [name]", 2975 desc: "View or set a piece code channel.", 2976 params: [ 2977 { name: "name", type: "string", required: false, desc: "Channel name to join" } 2978 ], 2979 done: true, 2980 }, 2981 "code-channel": { 2982 sig: "code-channel", 2983 desc: "", 2984 done: false, 2985 hidden: true, 2986 }, 2987 run: { 2988 sig: "run", 2989 desc: "", 2990 done: false, 2991 hidden: true, 2992 }, 2993 docs: { 2994 sig: "docs", 2995 desc: "Aesthetic Computer Documentation.", 2996 examples: ["docs", "l5docs", "processingdocs", "l5", "processing"], 2997 returns: "void", 2998 done: true, 2999 }, 3000 l5docs: { 3001 sig: "l5docs", 3002 desc: "Open the L5 compatibility docs checklist.", 3003 done: true, 3004 }, 3005 processingdocs: { 3006 sig: "processingdocs", 3007 desc: "Open the Processing compatibility docs checklist.", 3008 done: true, 3009 }, 3010 l5: { 3011 sig: "l5", 3012 desc: "Open the L5 try page.", 3013 done: true, 3014 }, 3015 l5learn: { 3016 sig: "l5learn", 3017 desc: "Open the L5 try page.", 3018 done: true, 3019 }, 3020 processing: { 3021 sig: "processing", 3022 desc: "Open the Processing try page.", 3023 done: true, 3024 }, 3025 processinglearn: { 3026 sig: "processinglearn", 3027 desc: "Open the Processing try page.", 3028 done: true, 3029 }, 3030 code: { 3031 sig: "code [name]", 3032 desc: "Write a piece.", 3033 params: [ 3034 { name: "name", type: "string", required: false, desc: "Piece name (creates new)" } 3035 ], 3036 done: true, 3037 }, 3038 edit: { 3039 sig: "edit <piece>", 3040 desc: "Edit a piece.", 3041 params: [ 3042 { name: "piece", type: "string", required: true, desc: "Piece name to edit" } 3043 ], 3044 done: true, 3045 }, 3046 source: { 3047 sig: "source [piece]", 3048 desc: "Download piece code.", 3049 params: [ 3050 { name: "piece", type: "string", required: false, desc: "Piece name (or current)" } 3051 ], 3052 done: true, 3053 }, 3054 email: { 3055 sig: "email <address>", 3056 desc: "Update your email.", 3057 params: [ 3058 { name: "address", type: "email", required: true, desc: "New email address" } 3059 ], 3060 done: true, 3061 hidden: false, 3062 }, 3063 "admin:migrate-": { 3064 sig: "admin:migrate-", 3065 desc: "", 3066 done: false, 3067 hidden: true, 3068 }, 3069 handle: { 3070 sig: "handle <name>", 3071 desc: "Set your user handle.", 3072 params: [ 3073 { name: "name", type: "string", required: true, desc: "New handle (alphanumeric)" } 3074 ], 3075 done: true, 3076 }, 3077 handles: { 3078 sig: "handles", 3079 desc: "Browse all user handles.", 3080 done: true, 3081 }, 3082 ul: { 3083 sig: "ul", 3084 desc: "Alias for `upload`.", 3085 examples: ["ul"], 3086 returns: "void", 3087 done: true, 3088 }, 3089 upload: { 3090 sig: "upload", 3091 desc: "Upload your current painting/media.", 3092 examples: ["upload"], 3093 returns: "void", 3094 done: true, 3095 }, 3096 flip: { 3097 sig: "flip", 3098 desc: "Flip painting vertically.", 3099 examples: ["flip"], 3100 returns: "void", 3101 done: true, 3102 }, 3103 flop: { 3104 sig: "flop", 3105 desc: "Flop painting horizontally.", 3106 examples: ["flop"], 3107 returns: "void", 3108 done: true, 3109 }, 3110 right: { 3111 sig: "right", 3112 desc: "Rotate painting right.", 3113 examples: ["right"], 3114 returns: "void", 3115 done: true, 3116 }, 3117 left: { 3118 sig: "left", 3119 desc: "Rotate painting left.", 3120 examples: ["left"], 3121 returns: "void", 3122 done: true, 3123 }, 3124 resize: { 3125 sig: "resize <w> [h]", 3126 desc: "Resize by x and y pixel #s.", 3127 params: [ 3128 { name: "w", type: "number", required: true, desc: "Width in pixels" }, 3129 { name: "h", type: "number", required: false, desc: "Height (defaults to w)" } 3130 ], 3131 done: true, 3132 }, 3133 res: { 3134 sig: "res <w> [h]", 3135 desc: "Resize by x and y pixel #s.", 3136 params: [ 3137 { name: "w", type: "number", required: true, desc: "Width in pixels" }, 3138 { name: "h", type: "number", required: false, desc: "Height (defaults to w)" } 3139 ], 3140 done: true, 3141 }, 3142 dl: { 3143 sig: "dl [scale]", 3144 desc: "Download your painting.", 3145 params: [ 3146 { name: "scale", type: "number", required: false, default: 1, desc: "Scale multiplier" } 3147 ], 3148 done: true, 3149 }, 3150 download: { 3151 sig: "download [scale]", 3152 desc: "Download your painting.", 3153 params: [ 3154 { name: "scale", type: "number", required: false, default: 1, desc: "Scale multiplier" } 3155 ], 3156 done: true, 3157 }, 3158 gutter: { 3159 sig: "gutter", 3160 desc: "", 3161 done: false, 3162 hidden: true, 3163 }, 3164 login: { 3165 sig: "login", 3166 desc: "Log in.", 3167 examples: ["login", "hi"], 3168 returns: "void", 3169 done: true, 3170 }, 3171 hi: { 3172 sig: "hi", 3173 desc: "Log in.", 3174 done: false, 3175 }, 3176 signup: { 3177 sig: "signup", 3178 desc: "Sign up.", 3179 examples: ["signup", "imnew"], 3180 returns: "void", 3181 done: true, 3182 }, 3183 imnew: { 3184 sig: "imnew", 3185 desc: "Alias for sign up.", 3186 examples: ["imnew"], 3187 returns: "void", 3188 done: true, 3189 }, 3190 logout: { 3191 sig: "logout", 3192 desc: "Log out.", 3193 examples: ["logout"], 3194 returns: "void", 3195 done: true, 3196 }, 3197 bye: { 3198 sig: "bye", 3199 desc: "Leave a bot / character or log out.", 3200 done: false, 3201 }, 3202 no: { 3203 sig: "no", 3204 desc: "Undo painting step.", 3205 examples: ["no"], 3206 returns: "void", 3207 done: true, 3208 }, 3209 yes: { 3210 sig: "yes", 3211 desc: "Redo painting step.", 3212 examples: ["yes"], 3213 returns: "void", 3214 done: true, 3215 }, 3216 nopan: { 3217 sig: "nopan", 3218 desc: "Center your painting.", 3219 examples: ["nopan"], 3220 returns: "void", 3221 done: true, 3222 }, 3223 new: { 3224 sig: "new", 3225 desc: "Start a new painting.", 3226 examples: ["new"], 3227 returns: "void", 3228 done: true, 3229 }, 3230 "painting:reset": { 3231 sig: "painting:reset", 3232 desc: "", 3233 done: false, 3234 hidden: true, 3235 }, 3236 publish: { 3237 sig: "publish", 3238 desc: "Publish your last-run piece.", 3239 examples: ["publish"], 3240 returns: "void", 3241 done: true, 3242 }, 3243 "no!": { 3244 sig: "no!", 3245 desc: "Abandon your painting.", 3246 done: false, 3247 }, 3248 "3ine:reset": { 3249 sig: "3ine:reset", 3250 desc: "", 3251 done: false, 3252 hidden: true, 3253 }, 3254 dark: { 3255 sig: "dark", 3256 desc: "Enable dark system theme.", 3257 examples: ["dark"], 3258 returns: "void", 3259 done: true, 3260 }, 3261 light: { 3262 sig: "light", 3263 desc: "Enable light system theme.", 3264 examples: ["light"], 3265 returns: "void", 3266 done: true, 3267 }, 3268 serious: { 3269 sig: "serious", 3270 desc: "Toggle minimal black & white prompt.", 3271 done: false, 3272 }, 3273 stop: { 3274 sig: "stop", 3275 desc: "Stop a running merry pipeline.", 3276 done: false, 3277 }, 3278 mug: { 3279 sig: "mug [code] [color]", 3280 desc: "Preview & order a mug with a painting.", 3281 params: [ 3282 { name: "code", type: "string", required: false, desc: "Painting code" }, 3283 { name: "color", type: "string", required: false, desc: "Mug color (white, black, blue, pink, orange)" }, 3284 ], 3285 done: true, 3286 }, 3287 merry: { 3288 sig: "merry [duration-]piece ...", 3289 desc: "Run pieces in sequence.", 3290 params: [ 3291 { name: "pieces", type: "string", required: true, desc: "Pieces to chain, optionally with duration prefix" }, 3292 ], 3293 done: true, 3294 }, 3295 merryo: { 3296 sig: "merryo [duration-]piece ...", 3297 desc: "Run pieces in a loop.", 3298 params: [ 3299 { name: "pieces", type: "string", required: true, desc: "Pieces to chain and loop" }, 3300 ], 3301 done: true, 3302 }, 3303 mo: { 3304 sig: "mo[.duration] piece ...", 3305 desc: "Shorthand for merryo (looping merry).", 3306 done: true, 3307 }, 3308 desktop: { 3309 sig: "desktop", 3310 desc: "Download the desktop app.", 3311 done: false, 3312 }, 3313 chatgpt: { 3314 sig: "chatgpt", 3315 desc: "Open ChatGPT.", 3316 done: false, 3317 hidden: true, 3318 }, 3319 nws: { 3320 sig: "nws", 3321 desc: "Open Aesthetic News.", 3322 done: false, 3323 hidden: true, 3324 }, 3325 product: { 3326 sig: "product [key]", 3327 desc: "Switch or view active shop product.", 3328 done: false, 3329 hidden: true, 3330 }, 3331 2022: { 3332 sig: "2022", 3333 desc: "", 3334 done: false, 3335 hidden: true, 3336 }, 3337 connect: { 3338 sig: "connect", 3339 desc: "", 3340 done: false, 3341 hidden: true, 3342 }, 3343 "bgm stop": { 3344 sig: "bgm stop", 3345 desc: "", 3346 done: false, 3347 hidden: true, 3348 }, 3349 "+": { 3350 sig: "+", 3351 desc: "Make new window.", 3352 done: false, 3353 //TODO: can this open in a new tab? 3354 }, 3355 google: { 3356 sig: "google <query>", 3357 desc: "Search google.", 3358 params: [ 3359 { name: "query", type: "string", required: true, desc: "Search query" } 3360 ], 3361 done: true, 3362 }, 3363 github: { 3364 sig: "github", 3365 desc: "View AC source code.", 3366 done: false, 3367 //TODO: can this open in a new tab? 3368 }, 3369 gmail: { 3370 sig: "gmail", 3371 desc: "Go to gmail.", 3372 done: false, 3373 }, 3374 gh: { 3375 sig: "gh", 3376 desc: "View AC source code.", 3377 done: false, 3378 }, 3379 score: { 3380 sig: "score", 3381 desc: "Open the Aesthetic Computer score.", 3382 done: false, 3383 }, 3384 ucla: { 3385 sig: "ucla-syllabus", 3386 desc: "UCLA DESMA 28 - Syllabus", 3387 done: false, 3388 }, 3389 "ucla-1": { 3390 sig: "ucla-1", 3391 desc: "UCLA DESMA 28 - Piece 1", 3392 done: false, 3393 }, 3394 "ucla-2": { 3395 sig: "ucla-2", 3396 desc: "UCLA DESMA 28 - Piece 2", 3397 done: false, 3398 }, 3399 "ucla-3": { 3400 sig: "ucla-3", 3401 desc: "UCLA DESMA 28 - Piece 3", 3402 done: false, 3403 }, 3404 "ucla-4": { 3405 sig: "ucla-4", 3406 desc: "UCLA DESMA 28 - Piece 4", 3407 done: false, 3408 }, 3409 "ucla-4-box": { 3410 sig: "ucla-4-box", 3411 desc: "UCLA DESMA 28 - Piece 4 (Box)", 3412 done: false, 3413 }, 3414 "ucla-5": { 3415 sig: "ucla-5", 3416 desc: "UCLA DESMA 28 - Piece 5", 3417 done: false, 3418 }, 3419 "ucla-6": { 3420 sig: "ucla-6", 3421 desc: "UCLA DESMA 28 - Piece 6", 3422 done: false, 3423 }, 3424 "ucla-7": { 3425 sig: "ucla-7", 3426 desc: "UCLA DESMA 28 - Piece 7", 3427 done: false, 3428 }, 3429 "ucla-7-dial": { 3430 sig: "ucla-7-dial", 3431 desc: "UCLA DESMA 28 - Piece 7 (Dial)", 3432 done: false, 3433 }, 3434 "ucla-7-jump": { 3435 sig: "ucla-7-jump", 3436 desc: "UCLA DESMA 28 - Piece 7 (Jump)", 3437 done: false, 3438 }, 3439 app: { 3440 sig: "app", 3441 desc: "Get AC in the app store.", 3442 done: false, 3443 }, 3444 ios: { 3445 sig: "ios", 3446 desc: "Get AC in the app store.", 3447 done: false, 3448 }, 3449 pp: { 3450 sig: "pp", 3451 desc: "Read the privacy policy.", 3452 done: false, 3453 }, 3454 direct: { 3455 sig: "direct", 3456 desc: "Aesthetic Inc. corporate updates.", 3457 done: false, 3458 }, 3459 support: { 3460 sig: "support", 3461 desc: "Go to AC support page.", 3462 done: false, 3463 }, 3464 browserstack: { 3465 sig: "browserstack", 3466 desc: "Go to AC browserstack.", 3467 done: false, 3468 hidden: true, 3469 }, 3470 bs: { 3471 sig: "bs", 3472 desc: "Go to AC browser stack.", 3473 done: false, 3474 hidden: true, 3475 }, 3476 gpt: { 3477 sig: "gpt", 3478 desc: "", 3479 done: false, 3480 hidden: true, 3481 }, 3482 help: { 3483 sig: "help", 3484 desc: "Join a help channel.", 3485 done: false, 3486 }, 3487 shillball: { 3488 sig: "shillball", 3489 desc: "", 3490 done: false, 3491 hidden: true, 3492 }, 3493 sb: { 3494 sig: "sb", 3495 desc: "", 3496 done: false, 3497 hidden: true, 3498 }, 3499 prod: { 3500 sig: "prod", 3501 desc: "", 3502 done: false, 3503 hidden: true, 3504 }, 3505 local: { 3506 sig: "local", 3507 desc: "", 3508 done: false, 3509 hidden: true, 3510 }, 3511 of: { 3512 sig: "of", 3513 desc: "View ordfish paintings.", 3514 done: false, 3515 //TODO: fix swimming count, stuck on 56 3516 }, 3517 }, 3518 // 🧩 Pieces that can be entered into the prompt. 3519 pieces: { 3520 404: { 3521 sig: "404", 3522 desc: "", 3523 done: false, 3524 hidden: true, 3525 }, 3526 about: { 3527 sig: "about", 3528 desc: "", 3529 done: false, 3530 hidden: true, 3531 }, 3532 aframe: { 3533 sig: "aframe", 3534 desc: "", 3535 done: false, 3536 hidden: true, 3537 }, 3538 "a*": { 3539 sig: "a*", 3540 desc: "A* pathfinding animation.", 3541 done: true, 3542 }, 3543 "alex-row": { 3544 sig: "alex-row", 3545 desc: "", 3546 done: false, 3547 hidden: true, 3548 }, 3549 alphapoet: { 3550 sig: "alphapoet", 3551 desc: "Generate poems.", 3552 done: false, 3553 }, 3554 angel: { 3555 sig: "angel", 3556 desc: "Say a prayer.", 3557 done: false, 3558 }, 3559 api: { 3560 sig: "api", 3561 desc: "", 3562 done: false, 3563 hidden: true, 3564 }, 3565 baktok: { 3566 sig: "baktok", 3567 desc: "Learn to talk backwards.", 3568 done: false, 3569 }, 3570 bills: { 3571 sig: "bills", 3572 desc: "Service & billing dashboard.", 3573 url: "https://bills.aesthetic.computer", 3574 done: true, 3575 }, 3576 balls: { 3577 sig: "balls", 3578 desc: "", 3579 done: false, 3580 hidden: true, 3581 }, 3582 "basic-line-pointer": { 3583 sig: "basic-line-pointer", 3584 desc: "Drag mouse to point a line.", 3585 done: false, 3586 }, 3587 bgm: { 3588 sig: "bgm", 3589 desc: "Background music visualizer.", 3590 done: false, 3591 hidden: false, 3592 }, 3593 bits: { 3594 sig: "bits", 3595 desc: "", 3596 done: false, 3597 hidden: false, 3598 }, 3599 blank: { 3600 sig: "blank", 3601 desc: "", 3602 done: false, 3603 hidden: true, 3604 }, 3605 "blank-vello": { 3606 sig: "blank-vello", 3607 desc: "GPU test: Vello WASM renderer (purple lines)", 3608 done: true, 3609 }, 3610 "blank-webgl2": { 3611 sig: "blank-webgl2", 3612 desc: "GPU test: WebGL2 renderer (cyan lines)", 3613 done: true, 3614 }, 3615 "blank-canvas2d": { 3616 sig: "blank-canvas2d", 3617 desc: "GPU test: Canvas2D fallback (green lines)", 3618 done: true, 3619 }, 3620 "blank-thorvg": { 3621 sig: "blank-thorvg", 3622 desc: "GPU test: ThorVG WASM stub (orange lines)", 3623 done: true, 3624 }, 3625 "blank-blend2d": { 3626 sig: "blank-blend2d", 3627 desc: "GPU test: Blend2D WASM stub (magenta lines)", 3628 done: true, 3629 }, 3630 bleep: { 3631 sig: "bleep", 3632 desc: "Play notes on a grid. Try adding a #.", 3633 done: false, 3634 }, 3635 blur: { 3636 sig: "blur", 3637 desc: "Blur pixels.", 3638 done: false, 3639 }, 3640 box: { 3641 sig: "box[:color]", 3642 desc: "Draw rectangles with brush gestures.", 3643 colon: [ 3644 { name: "color", values: ["red", "green", "blue", "yellow", "white", "black", "orange", "purple", "pink", "cyan"] }, 3645 ], 3646 done: true, 3647 }, 3648 "booted-by": { 3649 sig: "booted-by", 3650 desc: "Special thanks to early patrons.", 3651 done: false, 3652 }, 3653 // botce: { 3654 // sig: "botce", 3655 // desc: "Get spiritual advice.", 3656 // done: false, 3657 // }, 3658 boxes: { 3659 sig: "boxes", 3660 desc: "", 3661 done: false, 3662 hidden: true, 3663 }, 3664 boyfriend: { 3665 sig: "boyfriend", 3666 desc: "He might care.", 3667 done: false, 3668 }, 3669 "brick-breaker": { 3670 sig: "brick-breaker", 3671 desc: "", 3672 done: false, 3673 hidden: true, 3674 }, 3675 brother: { 3676 sig: "brother", 3677 desc: "Stay out of his room.", 3678 done: false, 3679 }, 3680 brush: { 3681 sig: "brush", 3682 desc: "", 3683 done: false, 3684 hidden: true, 3685 //TODO: add new templates repository 3686 }, 3687 bubble: { 3688 sig: "bubble", 3689 desc: "Make bubble boing. Sound on.", 3690 done: false, 3691 }, 3692 butterflies: { 3693 sig: "butterflies", 3694 desc: "A 1-bit bitmap reader instrument.", 3695 done: false, 3696 }, 3697 camera: { 3698 sig: "camera[:mode]", 3699 desc: "Take a picture.", 3700 colon: [ 3701 { name: "mode", type: "enum", values: ["under", "u"], required: false, desc: "Put camera under drawing" } 3702 ], 3703 examples: ["camera", "camera:under"], 3704 done: true, 3705 }, 3706 chat: { 3707 sig: "chat", 3708 desc: "Chat with handles.", 3709 done: false, 3710 hidden: false, 3711 }, 3712 chord: { 3713 sig: "chord", 3714 desc: "", 3715 done: false, 3716 hidden: false, 3717 }, 3718 colors: { 3719 sig: "colors", 3720 desc: "An index of usable colors on AC.", 3721 done: false, 3722 hidden: false, 3723 }, 3724 colplay: { 3725 sig: "colplay", 3726 desc: "Turn colors into notes.", 3727 done: false, 3728 }, 3729 common: { 3730 sig: "common", 3731 desc: "", 3732 done: false, 3733 hidden: true, 3734 }, 3735 clock: { 3736 sig: "clock[:divisor] [melody] [sync]", 3737 desc: "Musical clock with melody, waveforms, Hz shifts, and parallel tracks.", 3738 colon: [ 3739 { name: "divisor", type: "number", required: false, default: 1, desc: "Time divisor (0.5 = faster, 2 = slower)" } 3740 ], 3741 params: [ 3742 { name: "melody", type: "string", required: false, desc: "Notes like cdefg, {square}cde, (ceg) (dfa)" }, 3743 { name: "sync", type: "enum", values: ["sync"], required: false, desc: "UTC sync mode" } 3744 ], 3745 examples: ["clock cdefg", "clock:0.5 {square}cdefgab", "clock (ceg) (dfa)", "clock ^cdefg"], 3746 done: true 3747 }, 3748 commits: { 3749 sig: "commits", 3750 desc: "Browse the live commit history.", 3751 done: true, 3752 }, 3753 crop: { 3754 sig: "crop", 3755 desc: "Crop your painting.", 3756 done: false, 3757 }, 3758 dad: { 3759 sig: "dad", 3760 desc: "A dad-icated and punny guy.", 3761 done: false, 3762 }, 3763 debug: { 3764 sig: "debug", 3765 desc: "", 3766 done: false, 3767 hidden: true, 3768 }, 3769 deck: { 3770 sig: "deck", 3771 desc: "A little slide deck!", 3772 done: false, 3773 hidden: false, 3774 }, 3775 decode: { 3776 sig: "decode", 3777 desc: "Reveal an encoded message. See encode.", 3778 done: false, 3779 }, 3780 "delete-erase-and-forget-me": { 3781 sig: "delete-erase-and-forget-me", 3782 desc: "Delete your account.", 3783 done: false, 3784 }, 3785 // 🔗 External Links 3786 github: { 3787 sig: "github", 3788 desc: "Open the AC Tangled repo (legacy alias).", 3789 done: true, 3790 }, 3791 gh: { 3792 sig: "gh", 3793 desc: "Open the AC Tangled repo (legacy alias).", 3794 done: true, 3795 }, 3796 gmail: { 3797 sig: "gmail", 3798 desc: "Open Gmail.", 3799 done: true, 3800 }, 3801 agc: { 3802 sig: "agc", 3803 desc: "Open ACG at MIT Media Lab.", 3804 done: true, 3805 }, 3806 "ucla-syllabus": { 3807 sig: "ucla-syllabus", 3808 desc: "Open the UCLA syllabus.", 3809 done: true, 3810 }, 3811 demo: { 3812 sig: "demo", 3813 desc: "Watch a demo of AC.", 3814 done: false, 3815 hidden: false, 3816 }, 3817 description: { 3818 sig: "description", 3819 desc: "", 3820 done: false, 3821 hidden: true, 3822 }, 3823 digitpain0: { 3824 sig: "digitpain0", 3825 desc: "", 3826 done: false, 3827 hidden: true, 3828 }, 3829 digitpain1: { 3830 sig: "digitpain1", 3831 desc: "", 3832 done: false, 3833 hidden: true, 3834 }, 3835 digitpain2: { 3836 sig: "digitpain2", 3837 desc: "", 3838 done: false, 3839 hidden: true, 3840 }, 3841 digitpain3: { 3842 sig: "digitpain3", 3843 desc: "", 3844 done: false, 3845 hidden: true, 3846 }, 3847 docgen: { 3848 sig: "docgen", 3849 desc: "", 3850 done: false, 3851 hidden: true, 3852 }, 3853 dolls: { 3854 sig: "dolls", 3855 desc: "", 3856 done: false, 3857 hidden: true, 3858 }, 3859 doodle: { 3860 sig: "doodle", 3861 desc: "", 3862 done: false, 3863 hidden: true, 3864 }, 3865 download: { 3866 sig: "download", 3867 desc: "Download your painting.", 3868 done: false, 3869 }, 3870 drawings: { 3871 sig: "drawings", 3872 desc: "", 3873 done: false, 3874 hidden: true, 3875 }, 3876 dync: { 3877 sig: "dync", 3878 desc: "", 3879 done: false, 3880 hidden: true, 3881 }, 3882 encode: { 3883 sig: "encode", 3884 desc: "Encrypt a secret message.", 3885 done: false, 3886 }, 3887 ff: { 3888 sig: "ff", 3889 desc: "View the Freaky Flowers collection.", 3890 done: false, 3891 }, 3892 field: { 3893 sig: "field", 3894 desc: "Play in the field with others.", 3895 done: false, 3896 }, 3897 fill: { 3898 sig: "fill", 3899 desc: "Fill with solid color.", 3900 done: false, 3901 }, 3902 fly: { 3903 sig: "fly", 3904 desc: "", 3905 done: false, 3906 hidden: true, 3907 }, 3908 "freaky-flowers": { 3909 sig: "freaky-flowers", 3910 desc: "View the Freaky Flowers collection.", 3911 done: false, 3912 }, 3913 gargoyle: { 3914 sig: "gargoyle", 3915 desc: "A steadfast guardian.", 3916 done: false, 3917 }, 3918 girlfriend: { 3919 sig: "girlfriend", 3920 desc: "Caring confidant.", 3921 done: false, 3922 }, 3923 give: { 3924 sig: "give", 3925 desc: "Support aesthetic.computer.", 3926 done: true, 3927 }, 3928 gostop: { 3929 sig: "gostop", 3930 desc: "Stop and go.", 3931 done: false, 3932 }, 3933 handprint: { 3934 sig: "handprint", 3935 desc: "Track your hand.", 3936 done: false, 3937 }, 3938 handtime: { 3939 sig: "handtime", 3940 desc: "Draw with a pinch.", 3941 done: false, 3942 }, 3943 "hell_-world": { 3944 sig: "hell_-world", 3945 desc: "View the hell_ world collection.", 3946 done: false, 3947 //TODO: doesn't seem to work? 3948 }, 3949 hha: { 3950 sig: "hha", 3951 desc: "Happy Hands Assembler", 3952 done: false, 3953 hidden: false, 3954 }, 3955 horizon: { 3956 sig: "horizon", 3957 desc: "Walk on the horizon.", 3958 done: false, 3959 }, 3960 husband: { 3961 sig: "husband", 3962 desc: "Absent-minded but well-meaning.", 3963 done: false, 3964 }, 3965 hw: { 3966 sig: "hw", 3967 desc: "View the hell_ world paintings.", 3968 done: false, 3969 }, 3970 icon: { 3971 sig: "icon", 3972 desc: "", 3973 done: false, 3974 hidden: true, 3975 }, 3976 images: { 3977 sig: "images", 3978 desc: "", 3979 done: false, 3980 hidden: true, 3981 }, 3982 imessage: { 3983 sig: "imessage", 3984 desc: "", 3985 done: false, 3986 hidden: true, 3987 }, 3988 i: { 3989 sig: "i", 3990 desc: "", 3991 done: false, 3992 hidden: true, 3993 }, 3994 kid: { 3995 sig: "kid", 3996 desc: "Maybe a unicorn.", 3997 done: false, 3998 }, 3999 lang: { 4000 sig: "lang", 4001 desc: "", 4002 done: false, 4003 hidden: true, 4004 }, 4005 learn: { 4006 sig: "learn", 4007 desc: "", 4008 done: false, 4009 hidden: true, 4010 }, 4011 'laer-klokken': { 4012 sig: "laer-klokken", 4013 desc: "Learn the 'clock'!", 4014 done: false 4015 }, 4016 "legacy-prompt": { 4017 sig: "legacy-prompt", 4018 desc: "", 4019 done: false, 4020 hidden: true, 4021 }, 4022 liar: { 4023 sig: "liar", 4024 desc: "Incredibly honest and trustworthy.", 4025 done: false, 4026 }, 4027 '3-kidlisp-tests': { 4028 sig: "3-kidlisp-tests", 4029 desc: "Tests of a new language.", 4030 done: false, 4031 }, 4032 'fia-birthday': { 4033 sig: "fia-birthday", 4034 desc: "Come to Fía's birthday!", 4035 done: false, 4036 }, 4037 "kaos-pad-template": { 4038 sig: "kaos-pad-template", 4039 desc: "A simple multi-touch XY pad template.", 4040 done: false, 4041 }, 4042 line: { 4043 sig: "line[:thickness]", 4044 desc: "Draw lines with your finger.", 4045 colon: [ 4046 { name: "thickness", type: "number", required: false, default: 1, desc: "Line width in pixels" } 4047 ], 4048 examples: ["line", "line:2", "line:5"], 4049 done: true, 4050 }, 4051 list: { 4052 sig: "list", 4053 desc: "View all commands.", 4054 done: false, 4055 }, 4056 "lmn-flower": { 4057 sig: "lmn-flower", 4058 desc: "", 4059 done: false, 4060 hidden: true, 4061 }, 4062 "lmn-petal": { 4063 sig: "lmn-petal", 4064 desc: "", 4065 done: false, 4066 hidden: true, 4067 }, 4068 "login-pattern": { 4069 sig: "login-pattern", 4070 desc: "", 4071 done: false, 4072 hidden: true, 4073 }, 4074 "login-wait": { 4075 sig: "login-wait", 4076 desc: "", 4077 done: false, 4078 hidden: true, 4079 }, 4080 m2w2: { 4081 sig: "m2w2", 4082 desc: "Music 2 Whistlegraph 2.", 4083 done: false, 4084 }, 4085 melody: { 4086 sig: "melody", 4087 desc: "Plays a sequence.", 4088 done: false, 4089 }, 4090 metronome: { 4091 sig: "metronome", 4092 desc: "Keep time.", 4093 done: false, 4094 }, 4095 microphone: { 4096 sig: "microphone", 4097 desc: "", 4098 done: false, 4099 hidden: true, 4100 //TODO: try to fix 4101 }, 4102 mom: { 4103 sig: "mom", 4104 desc: "Why does she love this way?", 4105 done: false, 4106 }, 4107 mood: { 4108 sig: "mood", 4109 desc: "Set your mood.", 4110 done: false, 4111 }, 4112 moods: { 4113 sig: "moods", 4114 desc: "Read all the moods.", 4115 done: false, 4116 }, 4117 multipen: { 4118 sig: "multipen", 4119 desc: "", 4120 done: false, 4121 hidden: true, 4122 }, 4123 nail: { 4124 sig: "nail", 4125 desc: "", 4126 done: false, 4127 hidden: true, 4128 }, 4129 noise: { 4130 sig: "noise", 4131 desc: "Some nice noise.", 4132 done: false, 4133 }, 4134 news: { 4135 sig: "news", 4136 desc: "Community news and links.", 4137 done: true, 4138 }, 4139 nopaint: { 4140 sig: "nopaint", 4141 desc: "", 4142 done: false, 4143 hidden: true, 4144 //TODO: shouldnt this go to nopaint site? nopaint.art 4145 }, 4146 notepat: { 4147 sig: "notepat[:wave][:octave] [melody...]", 4148 desc: "A melodic keyboard instrument.", 4149 // Colon params: command:opt1:opt2 → colon[0], colon[1] 4150 colon: [ 4151 { name: "wave", type: "enum", values: ["sine", "square", "triangle", "sawtooth", "noise"], required: false, default: "sine" }, 4152 { name: "octave", type: "number", values: [1, 2, 3, 4, 5, 6, 7, 8, 9], required: false, default: 4 } 4153 ], 4154 // Space params: command arg1 arg2 → params[0], params[1] 4155 params: [ 4156 { name: "melody", type: "string", required: false, desc: "Melody in note:word format (e.g. C:twin- C:-kle)" } 4157 ], 4158 examples: ["notepat", "notepat:square", "notepat:sine:5", "notepat twinkle"], 4159 done: true, 4160 }, 4161 stample: { 4162 sig: "stample", 4163 desc: "A sampling instrument.", 4164 done: false, 4165 }, 4166 old: { 4167 sig: "old", 4168 desc: "", 4169 done: false, 4170 hidden: true, 4171 }, 4172 oldpull: { 4173 sig: "oldpull", 4174 desc: "", 4175 done: false, 4176 hidden: true, 4177 }, 4178 oldwand: { 4179 sig: "oldwand", 4180 desc: "", 4181 done: false, 4182 hidden: true, 4183 }, 4184 ordfish: { 4185 sig: "ordfish", 4186 desc: "View the Ordfish painting collection.", 4187 done: false, 4188 }, 4189 ordsy: { 4190 sig: "ordsy", 4191 desc: "", 4192 done: false, 4193 hidden: true, 4194 }, 4195 oval: { 4196 sig: "oval", 4197 desc: "Draw an oval.", 4198 done: false, 4199 }, 4200 painting: { 4201 sig: "painting", 4202 desc: "View your current painting's steps.", 4203 done: false, 4204 }, 4205 paint: { 4206 sig: "paint", 4207 desc: "Generate marks by adding instructions.", 4208 done: false, 4209 }, 4210 paste: { 4211 sig: "paste", 4212 desc: "Paste an image from your library.", 4213 done: false, 4214 }, 4215 perf: { 4216 sig: "perf", 4217 desc: "", 4218 done: false, 4219 hidden: true, 4220 }, 4221 phand: { 4222 sig: "phand", 4223 desc: "", 4224 done: false, 4225 hidden: true, 4226 }, 4227 pip: { 4228 sig: "pip", 4229 desc: "", 4230 done: false, 4231 hidden: true, 4232 }, 4233 play: { 4234 sig: "play", 4235 desc: "", 4236 done: false, 4237 hidden: true, 4238 }, 4239 pline: { 4240 sig: "pline", 4241 desc: "", 4242 done: false, 4243 hidden: true, 4244 }, 4245 plot: { 4246 sig: "plot", 4247 desc: "Plot vector graphics.", 4248 done: false, 4249 }, 4250 pond: { 4251 sig: "pond", 4252 desc: "Draw ripples with others.", 4253 done: false, 4254 }, 4255 profile: { 4256 sig: "profile", 4257 desc: "Go to your profile or enter another user's.", 4258 done: false, 4259 }, 4260 prompt: { 4261 sig: "prompt", 4262 desc: "Go to the prompt.", 4263 done: false, 4264 }, 4265 prutti: { 4266 sig: "prutti", 4267 desc: "Genius old man rants.", 4268 done: false, 4269 }, 4270 ptt: { 4271 sig: "ptt", 4272 desc: "", 4273 done: false, 4274 hidden: true, 4275 }, 4276 pull: { 4277 sig: "pull", 4278 desc: "", 4279 done: false, 4280 hidden: true, 4281 }, 4282 rain: { 4283 sig: "rain", 4284 desc: "A nice rain animation.", 4285 done: false, 4286 }, 4287 rattle: { 4288 sig: "rattle", 4289 desc: "", 4290 done: false, 4291 }, 4292 rect: { 4293 sig: "rect", 4294 desc: "Draw a rectangle.", 4295 done: false, 4296 }, 4297 "run&gun": { 4298 sig: "run&gun", 4299 desc: "", 4300 done: false, 4301 hidden: true, 4302 }, 4303 sage: { 4304 sig: "sage", 4305 desc: "Paths move across the screen.", 4306 done: false, 4307 }, 4308 sb: { 4309 sig: "sb", 4310 desc: "", 4311 done: false, 4312 hidden: true, 4313 }, 4314 "scawy-snake": { 4315 sig: "scawy-snake", 4316 desc: "The classic game of snake.", 4317 done: false, 4318 //TODO: need replay button 4319 }, 4320 seashells: { 4321 sig: "seashells", 4322 desc: "A multi-touch bytebeat instrument.", 4323 done: false, 4324 }, 4325 screenshots: { 4326 sig: "screenshots", 4327 desc: "", 4328 done: false, 4329 }, 4330 screentest: { 4331 sig: "screentest", 4332 desc: "", 4333 done: false, 4334 hidden: true, 4335 }, 4336 selfie: { 4337 sig: "selfie", 4338 desc: "Open the front camera.", 4339 done: false, 4340 }, 4341 sfx: { 4342 sig: "sfx", 4343 desc: "", 4344 done: false, 4345 hidden: true, 4346 }, 4347 shape: { 4348 sig: "shape", 4349 desc: "Draw a freehand polygon.", 4350 done: false, 4351 }, 4352 shop: { 4353 sig: "shop", 4354 desc: "Order artwork and services from @jeffrey.", 4355 done: true, 4356 }, 4357 share: { 4358 sig: "share", 4359 desc: "Generate a QR code to share.", 4360 done: false, 4361 }, 4362 signature: { 4363 sig: "signature", 4364 desc: "", 4365 done: false, 4366 hidden: true, 4367 }, 4368 sign: { 4369 sig: "sign", 4370 desc: "", 4371 done: false, 4372 hidden: true, 4373 }, 4374 sing: { 4375 sig: "sing", 4376 desc: "", 4377 done: false, 4378 hidden: true, 4379 }, 4380 sister: { 4381 sig: "sister", 4382 desc: "Don't steal her makeup.", 4383 done: false, 4384 }, 4385 slip: { 4386 sig: "slip", 4387 desc: "A two octave slide instrument.", 4388 done: false, 4389 }, 4390 smear: { 4391 sig: "smear", 4392 desc: "Move pixels around.", 4393 done: false, 4394 }, 4395 sno: { 4396 sig: "sno", 4397 desc: "Walk around and fall asleep.", 4398 done: false, 4399 }, 4400 song: { 4401 sig: "song", 4402 desc: "Learn a song.", 4403 done: false, 4404 //TODO: adjust song , default octave thing 4405 }, 4406 "sotce-net": { 4407 sig: "sotce-net", 4408 desc: "diaries (work in progress)", 4409 done: false, 4410 hidden: true, 4411 }, 4412 // "sparkle-brush": { 4413 // sig: "sparkle-brush", 4414 // desc: "Paint with Maya's really fun brush.", 4415 // done: false, 4416 // }, 4417 sparkle: { 4418 sig: "sparkle", 4419 desc: "Paint with @maya's really fun brush.", 4420 done: false, 4421 }, 4422 spline: { 4423 sig: "spline", 4424 desc: "A springy wave.", 4425 done: false, 4426 }, 4427 spray: { 4428 sig: "spray", 4429 desc: "", 4430 done: false, 4431 hidden: true, 4432 }, 4433 sprinkles: { 4434 sig: "sprinkles", 4435 desc: "Watch the pretty sprinkles.", 4436 done: false, 4437 }, 4438 sprite: { 4439 sig: "sprite", 4440 desc: "", 4441 done: false, 4442 hidden: true, 4443 }, 4444 squaresong: { 4445 sig: "squaresong", 4446 desc: "Mmmm squaresong.", 4447 done: false, 4448 }, 4449 stage: { 4450 sig: "stage", 4451 desc: "", 4452 done: false, 4453 hidden: true, 4454 }, 4455 // "stage-setup.html": { 4456 // sig: "stage-setup.html", 4457 // desc: "", 4458 // done: false, 4459 // hidden: true, 4460 // }, 4461 staka: { 4462 sig: "staka", 4463 desc: "", 4464 done: false, 4465 hidden: true, 4466 }, 4467 starfield: { 4468 sig: "starfield", 4469 desc: "A celestial experience.", 4470 done: false, 4471 }, 4472 test: { 4473 sig: "test", 4474 desc: "", 4475 done: false, 4476 hidden: true, 4477 }, 4478 textfence: { 4479 sig: "textfence", 4480 desc: "A tiny play by @jeffrey and @georgica.", 4481 done: false, 4482 }, 4483 tone: { 4484 sig: "tone[:wave] [frequency]", 4485 desc: "Listen to a tone.", 4486 colon: [ 4487 { name: "wave", type: "enum", values: ["sine", "triangle", "square", "sawtooth", "cycle"], required: false, default: "sine" } 4488 ], 4489 params: [ 4490 { name: "frequency", type: "number", required: false, desc: "Tone frequency in Hz (50-4000)" } 4491 ], 4492 examples: ["tone", "tone 440", "tone:square 880", "tone:cycle"], 4493 done: true, 4494 }, 4495 toss: { 4496 sig: "toss[:wave][:tempo]", 4497 desc: "Play microtonal oscillators.", 4498 colon: [ 4499 { name: "wave", type: "enum", values: ["sine", "square", "triangle", "sawtooth"], required: false, default: "sine" }, 4500 { name: "tempo", type: "number", values: [60, 80, 100, 120, 140, 160], required: false, default: 120 } 4501 ], 4502 done: true, 4503 }, 4504 tracker: { 4505 sig: "tracker", 4506 desc: "A simple music tracker.", 4507 done: false, 4508 }, 4509 udp: { 4510 sig: "udp", 4511 desc: "", 4512 done: false, 4513 hidden: true, 4514 }, 4515 uke: { 4516 sig: "uke", 4517 desc: "Standard note meter.", 4518 done: false, 4519 }, 4520 valbear: { 4521 sig: "valbear", 4522 desc: "Make a V-Day card.", 4523 done: false, 4524 }, 4525 vary: { 4526 sig: "vary", 4527 desc: "", 4528 done: false, 4529 hidden: true, 4530 }, 4531 video: { 4532 sig: "video", 4533 desc: "", 4534 done: false, 4535 hidden: true, 4536 }, 4537 wand: { 4538 sig: "wand", 4539 desc: "Sculpt in XR.", 4540 done: false, 4541 hidden: false, 4542 }, 4543 wallet: { 4544 sig: "wallet", 4545 desc: "View your Tezos wallet.", 4546 done: true, 4547 }, 4548 wave: { 4549 sig: "wave", 4550 desc: "Wave using your hand.", 4551 done: false, 4552 hidden: false, 4553 }, 4554 wg: { 4555 sig: "wg", 4556 desc: "Watch and learn whistlegraphs.", 4557 done: false, 4558 }, 4559 wgr: { 4560 sig: "wgr", 4561 desc: "Whistlegraph recorder.", 4562 done: false, 4563 hidden: false, 4564 }, 4565 whistlegraph: { 4566 sig: "whistlegraph", 4567 desc: "Whistlegraph index.", 4568 done: false, 4569 hidden: false, 4570 }, 4571 whistle: { 4572 sig: "whistle", 4573 desc: "Convert whistles to sine waves.", 4574 done: false, 4575 }, 4576 wife: { 4577 sig: "wife", 4578 desc: "Get ready for chores.", 4579 done: false, 4580 }, 4581 wipe: { 4582 sig: "wipe", 4583 desc: "", 4584 done: false, 4585 hidden: false, 4586 //TODO: doesn't seem to clear the painting 4587 }, 4588 word: { 4589 sig: "word", 4590 desc: "Add text to your painting.", 4591 done: false, 4592 }, 4593 zoom: { 4594 sig: "zoom", 4595 desc: "", 4596 done: false, 4597 hidden: true, 4598 }, 4599 }, 4600 }; 4601 4602 const titleFromSlug = (slug = "") => 4603 String(slug) 4604 .replaceAll("-", " ") 4605 .replaceAll("_", " ") 4606 .replace(/\s+/g, " ") 4607 .trim(); 4608 4609 const fillCommandDocs = (collection, kind) => { 4610 keys(collection || {}).forEach((name) => { 4611 const entry = collection[name]; 4612 if (!entry) return; 4613 4614 if (!String(entry.sig || "").trim()) { 4615 entry.sig = name; 4616 } 4617 4618 if (!String(entry.desc || "").trim()) { 4619 const shared = getCommandDescription(name); 4620 if (shared) { 4621 entry.desc = shared; 4622 return; 4623 } 4624 4625 const pretty = titleFromSlug(name); 4626 if (kind === "piece") { 4627 entry.desc = pretty 4628 ? `Open the ${pretty} piece.` 4629 : "Open this piece."; 4630 } else { 4631 entry.desc = pretty 4632 ? `Run the ${pretty} prompt command.` 4633 : "Run this prompt command."; 4634 } 4635 } 4636 }); 4637 }; 4638 4639 fillCommandDocs(docs.prompts, "prompt"); 4640 fillCommandDocs(docs.pieces, "piece"); 4641 4642 const page = html` <html> 4643 <head> 4644 <link 4645 rel="icon" 4646 href="https://${event.headers["host"]}/icon/128x128/prompt.png" 4647 type="image/png" 4648 /> 4649 <meta charset="utf-8" /> 4650 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 4651 <title>$name · Aesthetic Computer</title> 4652 <style> 4653 ::-webkit-scrollbar { 4654 display: none; 4655 } 4656 html { 4657 cursor: 4658 url("/aesthetic.computer/cursors/precise.svg") 12 12, 4659 auto; 4660 } 4661 body { 4662 margin: 0; 4663 font-size: 22px; 4664 font-family: monospace; 4665 -webkit-text-size-adjust: none; 4666 } 4667 body.doc { 4668 overflow-x: hidden; 4669 } 4670 h1 a, h1 a:visited { 4671 color: inherit; 4672 cursor: inherit; 4673 text-decoration: none; 4674 } 4675 .links a[data-done=false] { 4676 /* text-decoration: line-through; */ 4677 } 4678 h1 { 4679 font-weight: normal; 4680 font-size: 22px; 4681 margin: 0; 4682 position: fixed; 4683 } 4684 h1:before { 4685 content: ""; 4686 height: 2.5em; 4687 width: 100vw; 4688 position: absolute; 4689 z-index: -1; 4690 top: -16px; 4691 left: -16px; 4692 } 4693 h1[data-done=false]:after { 4694 content: "wip"; 4695 color: yellow; 4696 background: maroon; 4697 position: absolute; 4698 right: -32px; 4699 top: 0px; 4700 display: block; 4701 font-size: 50%; 4702 padding: 0px 3px 2px 3px; 4703 border-radius: 2px; 4704 } 4705 h2 { 4706 font-weight: normal; 4707 font-size: 18px; 4708 } 4709 iframe { 4710 position: fixed; 4711 top: 0; 4712 left: 0; 4713 width: 100%; 4714 height: 100%; 4715 border: none; 4716 z-index: -1; 4717 opacity: 0.5; 4718 } 4719 section { 4720 margin: 12px 16px 12px 16px; 4721 } 4722 #pals { 4723 position: fixed; 4724 bottom: 5px; 4725 right: 16px; 4726 user-select: none; 4727 } 4728 p { 4729 margin-top: 0; 4730 } 4731 #command-list, #docs-welcome { 4732 margin-top: 1.5em; 4733 } 4734 #docs-welcome { 4735 padding: 0; 4736 } 4737 #title { 4738 display: inline-block; 4739 } 4740 #title.block:after { 4741 content: "_"; 4742 position: absolute; 4743 top: 6px; 4744 right: -16px; 4745 line-height: 20px; 4746 color: rgb(205, 92, 155); 4747 background-color: rgb(205, 92, 155); 4748 } 4749 a.prompt, 4750 a.prompt:visited { 4751 color: white; 4752 text-decoration: none; 4753 cursor: inherit; 4754 } 4755 a.prompt:hover { 4756 color: rgb(205, 92, 155); 4757 } 4758 small { 4759 opacity: 0.25; 4760 font-size: 100%; 4761 } 4762 .code-doc { 4763 padding-top: 2.5em; 4764 font-size: 65%; 4765 padding-left: 1px; 4766 } 4767 .doc-body { 4768 margin-top: 1em; 4769 } 4770 .doc-body table { 4771 border-collapse: collapse; 4772 width: 100%; 4773 max-width: 960px; 4774 margin-top: 0.75em; 4775 margin-bottom: 0.75em; 4776 } 4777 .doc-body th, 4778 .doc-body td { 4779 text-align: left; 4780 border: 1px solid rgba(255, 255, 255, 0.2); 4781 padding: 0.4em 0.5em; 4782 vertical-align: top; 4783 } 4784 .doc-body ul { 4785 padding-left: 1.2em; 4786 margin-top: 0.6em; 4787 margin-bottom: 0.6em; 4788 } 4789 .doc-body h3 { 4790 margin: 0.8em 0 0.4em; 4791 font-size: 1em; 4792 font-weight: normal; 4793 } 4794 .status-badge { 4795 display: inline-block; 4796 padding: 0.1em 0.45em; 4797 border-radius: 0.35em; 4798 font-size: 0.9em; 4799 text-transform: lowercase; 4800 border: 1px solid currentColor; 4801 white-space: nowrap; 4802 } 4803 .status-done { 4804 color: #4ade80; 4805 } 4806 .status-in-progress { 4807 color: #fbbf24; 4808 } 4809 .status-planned { 4810 color: #94a3b8; 4811 } 4812 .doc-examples { 4813 display: grid; 4814 gap: 0.75em; 4815 } 4816 .doc-example { 4817 border: 1px solid rgba(255, 255, 255, 0.2); 4818 padding: 0.45em 0.6em; 4819 border-radius: 0.35em; 4820 } 4821 .code-doc-welcome { 4822 padding-top: 2.75em; 4823 font-size: 65%; 4824 padding-bottom: 3em; 4825 } 4826 .lane-grid { 4827 display: grid; 4828 gap: 14px; 4829 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); 4830 } 4831 .lane-card { 4832 border: 1px solid rgba(255, 255, 255, 0.22); 4833 border-radius: 8px; 4834 padding: 10px 11px 11px 11px; 4835 } 4836 .lane-card.lane-mjs { 4837 border-color: rgba(124, 212, 255, 0.45); 4838 background: linear-gradient(180deg, rgba(26, 60, 86, 0.2), rgba(26, 60, 86, 0.05)); 4839 } 4840 .lane-card.lane-l5 { 4841 border-color: rgba(245, 213, 66, 0.5); 4842 background: linear-gradient(180deg, rgba(90, 75, 0, 0.22), rgba(90, 75, 0, 0.08)); 4843 } 4844 .lane-card.lane-processing { 4845 border-color: rgba(86, 220, 255, 0.5); 4846 background: linear-gradient(180deg, rgba(9, 71, 92, 0.22), rgba(9, 71, 92, 0.08)); 4847 } 4848 .lane-card.lane-kidlisp { 4849 border-color: rgba(102, 230, 187, 0.45); 4850 background: linear-gradient(180deg, rgba(16, 74, 56, 0.2), rgba(16, 74, 56, 0.06)); 4851 } 4852 .lane-card.lane-prompts { 4853 border-color: rgba(248, 168, 78, 0.45); 4854 background: linear-gradient(180deg, rgba(92, 41, 10, 0.2), rgba(92, 41, 10, 0.06)); 4855 } 4856 .lane-card.lane-pieces { 4857 border-color: rgba(230, 125, 175, 0.42); 4858 background: linear-gradient(180deg, rgba(78, 24, 63, 0.2), rgba(78, 24, 63, 0.06)); 4859 } 4860 .lane-head { 4861 display: flex; 4862 align-items: center; 4863 justify-content: space-between; 4864 gap: 8px; 4865 margin-bottom: 0.45em; 4866 } 4867 .lane-title { 4868 font-size: 1.1em; 4869 } 4870 .lane-mjs .lane-title { 4871 color: #9bddff; 4872 } 4873 .lane-l5 .lane-title { 4874 color: #ffe37a; 4875 } 4876 .lane-processing .lane-title { 4877 color: #93ecff; 4878 } 4879 .lane-kidlisp .lane-title { 4880 color: #9ff2d4; 4881 } 4882 .lane-prompts .lane-title { 4883 color: #ffd29a; 4884 } 4885 .lane-pieces .lane-title { 4886 color: #f3b3d3; 4887 } 4888 .lane-count { 4889 font-size: 0.92em; 4890 opacity: 0.75; 4891 white-space: nowrap; 4892 } 4893 .lane-subtitle { 4894 font-size: 0.95em; 4895 opacity: 0.9; 4896 margin-bottom: 0.65em; 4897 } 4898 .lane-section { 4899 margin-top: 0.75em; 4900 } 4901 .lane-section h3 { 4902 margin: 0 0 0.25em 0; 4903 font-size: 0.95em; 4904 font-weight: normal; 4905 opacity: 0.85; 4906 } 4907 .doc-meta { 4908 margin-top: 0.3em; 4909 font-size: 0.92em; 4910 opacity: 0.8; 4911 display: flex; 4912 flex-wrap: wrap; 4913 gap: 8px; 4914 } 4915 .doc-status { 4916 text-transform: lowercase; 4917 } 4918 .doc-grid { 4919 display: grid; 4920 gap: 11px; 4921 } 4922 .doc-block { 4923 border: 1px solid rgba(255, 255, 255, 0.2); 4924 border-radius: 8px; 4925 padding: 0.55em 0.7em; 4926 } 4927 .doc-block h2 { 4928 margin: 0 0 0.4em; 4929 font-size: 1em; 4930 text-transform: uppercase; 4931 letter-spacing: 0.04em; 4932 opacity: 0.85; 4933 } 4934 .doc-preview { 4935 border: 1px solid rgba(255, 255, 255, 0.2); 4936 border-radius: 8px; 4937 overflow: hidden; 4938 } 4939 .doc-preview iframe { 4940 position: static !important; 4941 z-index: auto !important; 4942 opacity: 1 !important; 4943 width: 100% !important; 4944 height: 100% !important; 4945 min-height: 280px; 4946 border: 0; 4947 display: block; 4948 } 4949 .doc-preview-head { 4950 padding: 0.45em 0.65em; 4951 font-size: 0.9em; 4952 border-bottom: 1px solid rgba(255, 255, 255, 0.2); 4953 } 4954 .doc-preview-body { 4955 min-height: 280px; 4956 } 4957 .doc-preview-links { 4958 display: flex; 4959 flex-wrap: wrap; 4960 gap: 7px; 4961 margin-top: 0.65em; 4962 } 4963 .doc-preview-links a { 4964 text-decoration: none; 4965 padding: 3px 6px; 4966 border: 1px solid rgba(255, 255, 255, 0.26); 4967 border-radius: 6px; 4968 } 4969 .doc-preview-links button { 4970 text-decoration: none; 4971 padding: 3px 6px; 4972 border: 1px solid rgba(255, 255, 255, 0.26); 4973 border-radius: 6px; 4974 background: transparent; 4975 color: inherit; 4976 font: inherit; 4977 cursor: pointer; 4978 } 4979 pre { 4980 margin-top: 1em; 4981 margin-bottom: 1em; 4982 } 4983 pre code.hljs { 4984 padding: 0.2em 0em; 4985 position: relative; 4986 overflow-x: visible; 4987 } 4988 pre code.hljs:after { 4989 content: ""; 4990 height: 100%; 4991 top: 0; 4992 right: -16px; 4993 width: 16; 4994 background-color: #f3f3f3; 4995 position: absolute; 4996 } 4997 pre code.hljs:before { 4998 content: ""; 4999 height: 100%; 5000 top: 0; 5001 left: -16px; 5002 width: 16px; 5003 background-color: #f3f3f3; 5004 position: absolute; 5005 } 5006 .links a { 5007 text-decoration: none; 5008 /* border: 1px solid; */ 5009 padding: 4px; 5010 } 5011 @media (prefers-color-scheme: dark) { 5012 body { 5013 background-color: rgb(64, 56, 74); 5014 color: rgba(255, 255, 255, 0.85); 5015 } 5016 h1 a:hover { 5017 color: rgb(205, 92, 155); 5018 } 5019 h1:before { 5020 background-image: linear-gradient(to bottom, rgba(64, 56, 74, 0.75) 80%, transparent); 5021 } 5022 .hljs-title.function_ { 5023 color: rgb(225, 105, 175); 5024 } 5025 .hljs { 5026 color: white; 5027 } 5028 pre code.hljs, 5029 pre code.hljs:after, 5030 pre code.hljs:before { 5031 background: rgb(25, 0, 25); 5032 } 5033 .links a { 5034 color: rgb(205, 92, 155); 5035 } 5036 .links a.top-level { 5037 color: rgb(92, 205, 155); 5038 } 5039 } 5040 @media (prefers-color-scheme: light) { 5041 body { 5042 background-color: rgba(244, 235, 250); 5043 } 5044 .doc-body th, 5045 .doc-body td, 5046 .doc-example, 5047 .lane-card, 5048 .doc-block, 5049 .doc-preview, 5050 .doc-preview-head, 5051 .doc-preview-links a, 5052 .doc-preview-links button { 5053 border-color: rgba(0, 0, 0, 0.2); 5054 } 5055 .lane-card.lane-mjs { 5056 background: linear-gradient(180deg, rgba(206, 237, 255, 0.8), rgba(238, 248, 255, 0.7)); 5057 border-color: rgba(49, 133, 173, 0.55); 5058 } 5059 .lane-card.lane-l5 { 5060 background: linear-gradient(180deg, rgba(255, 247, 185, 0.85), rgba(255, 252, 220, 0.7)); 5061 border-color: rgba(179, 149, 37, 0.55); 5062 } 5063 .lane-card.lane-processing { 5064 background: linear-gradient(180deg, rgba(205, 244, 255, 0.85), rgba(233, 251, 255, 0.72)); 5065 border-color: rgba(37, 137, 179, 0.55); 5066 } 5067 .lane-card.lane-kidlisp { 5068 background: linear-gradient(180deg, rgba(204, 245, 230, 0.8), rgba(233, 252, 245, 0.7)); 5069 border-color: rgba(41, 141, 102, 0.5); 5070 } 5071 .lane-card.lane-prompts { 5072 background: linear-gradient(180deg, rgba(255, 227, 194, 0.82), rgba(255, 244, 226, 0.72)); 5073 border-color: rgba(176, 108, 41, 0.5); 5074 } 5075 .lane-card.lane-pieces { 5076 background: linear-gradient(180deg, rgba(255, 216, 236, 0.82), rgba(255, 238, 247, 0.72)); 5077 border-color: rgba(161, 73, 115, 0.46); 5078 } 5079 .lane-mjs .lane-title { 5080 color: rgb(20, 90, 128); 5081 } 5082 .lane-l5 .lane-title { 5083 color: rgb(130, 102, 0); 5084 } 5085 .lane-processing .lane-title { 5086 color: rgb(8, 101, 138); 5087 } 5088 .lane-kidlisp .lane-title { 5089 color: rgb(24, 112, 76); 5090 } 5091 .lane-prompts .lane-title { 5092 color: rgb(132, 76, 18); 5093 } 5094 .lane-pieces .lane-title { 5095 color: rgb(125, 43, 83); 5096 } 5097 a.prompt, a.prompt:visited { 5098 color: rgb(64, 56, 74); 5099 } 5100 h1 a:hover, a.prompt:hover { 5101 color: rgb(205, 92, 155); 5102 } 5103 h1:before { 5104 background-image: linear-gradient(to bottom, rgba(244, 235, 250, 0.75) 80%, transparent); 5105 } 5106 .hljs-title.function_ { 5107 color: rgb(205, 92, 155); 5108 } 5109 .links a { 5110 color: rgb(180, 72, 135); 5111 } 5112 .links a.top-level { 5113 color: green; 5114 } 5115 } 5116 .nolink { 5117 user-select: none; 5118 pointer-events: none; 5119 } 5120 </style> 5121 <link 5122 rel="stylesheet" 5123 href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css" 5124 /> 5125 <script nonce="$nonce" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script> 5126 <script nonce="$nonce"> 5127 hljs.highlightAll(); 5128 </script> 5129 </head> 5130 <body$bodyclass> 5131 <section>$content</section> 5132 <img 5133 id="pals" 5134 width="64" 5135 src="https://${event.headers["host"]}/purple-pals.svg" 5136 /> 5137 <script nonce="$nonce"> 5138 const titleLink = document.querySelector("#title a"); 5139 if (window.self !== window.top && titleLink.innerText === "docs") { 5140 title.classList.add("nolink"); 5141 } 5142 const docPreviewFrame = document.getElementById("doc-preview-frame"); 5143 const docPreviewBase = docPreviewFrame?.dataset?.src || docPreviewFrame?.src || ""; 5144 function withFreshTimestamp(url) { 5145 if (!url) return ""; 5146 try { 5147 const next = new URL(url, window.location.origin); 5148 next.searchParams.set("t", Date.now()); 5149 return next.toString(); 5150 } catch (_err) { 5151 const joiner = url.includes("?") ? "&" : "?"; 5152 return url + joiner + "t=" + Date.now(); 5153 } 5154 } 5155 window.runDocPreview = function runDocPreview() { 5156 if (!docPreviewFrame) return; 5157 docPreviewFrame.src = withFreshTimestamp(docPreviewFrame.src || docPreviewBase); 5158 }; 5159 window.resetDocPreview = function resetDocPreview() { 5160 if (!docPreviewFrame || !docPreviewBase) return; 5161 docPreviewFrame.src = withFreshTimestamp(docPreviewBase); 5162 }; 5163 // 🌠 Live editing (while developing aesthetic locally) 5164 if (${dev}) { 5165 var socket = new WebSocket("ws://localhost:8889"); 5166 5167 socket.onopen = function (event) { 5168 console.log("🟢 Live editing enabled."); 5169 }; 5170 5171 socket.onmessage = function (event) { 5172 const msg = JSON.parse(event.data); 5173 console.log("🟡 Message from server:", msg); 5174 if (msg.type === "reload") document.location.reload(); 5175 }; 5176 5177 socket.onerror = function (event) { 5178 console.error("🔴 Error observed:", event); 5179 }; 5180 5181 socket.onclose = function (event) { 5182 console.log("🔴 Live editing disabled."); 5183 }; 5184 } 5185 </script> 5186 </body> 5187 </html>`.trim(); 5188 5189 const content = ` 5190 <h1 data-done="$done" id="title"><a href="/docs">$name</a></h1> 5191 <div class="code-doc"> 5192 <pre><code class="language-$lang">$sig</code></pre> 5193 <p>$desc</p> 5194 <div class="doc-body">$body</div> 5195 </div> 5196 `.trim(); 5197 5198 function escapeHTML(value) { 5199 return String(value || "") 5200 .replaceAll("&", "&amp;") 5201 .replaceAll("<", "&lt;") 5202 .replaceAll(">", "&gt;") 5203 .replaceAll('"', "&quot;") 5204 .replaceAll("'", "&#39;"); 5205 } 5206 5207 function normalizeStatus(done) { 5208 if (done === true || done === "done") return "done"; 5209 if (done === "in-progress") return "in-progress"; 5210 return "planned"; 5211 } 5212 5213 function statusBadge(done) { 5214 const status = normalizeStatus(done); 5215 return `<span class="status-badge status-${status}">${status.replace("-", " ")}</span>`; 5216 } 5217 5218 function familyFromCategory(category) { 5219 if (category === "l5") return "L5 / Lua API"; 5220 if (category === "processing") return "Processing / Java API"; 5221 if (category === "kidlisp") return "KidLisp / Language API"; 5222 if (category === "prompts") return "Prompt Commands"; 5223 if (category === "pieces") return "Pieces"; 5224 if (category === "mjs") return "MJS / AC Piece API"; 5225 return "MJS / AC Piece API"; 5226 } 5227 5228 function parseSigName(sig, fallback = "") { 5229 const source = String(sig || "").trim(); 5230 if (!source) return fallback; 5231 const match = source.match(/^([A-Za-z0-9_.$]+)\s*\(/); 5232 if (match?.[1]) return match[1]; 5233 return fallback || source; 5234 } 5235 5236 function playgroundUrlForCategory(category) { 5237 if (category === "l5") return `${AC_ORIGIN}/l5`; 5238 if (category === "processing") return `${AC_ORIGIN}/processing`; 5239 return `${AC_ORIGIN}/prompt`; 5240 } 5241 5242 function previewUrlForDoc(doc, category, word) { 5243 const flags = `nogap=true&nolabel=true&noauth=true&popout=true&t=${Date.now()}`; 5244 if (doc?.example?.url) return doc.example.url; 5245 if (doc?.example?.type === "piece" && doc.example.entry) { 5246 return `${AC_ORIGIN}/${encodeURIComponent(doc.example.entry)}?${flags}`; 5247 } 5248 if (category === "pieces") { 5249 return `${AC_ORIGIN}/${encodeURIComponent(word)}?${flags}`; 5250 } 5251 if (category === "prompts") { 5252 return `${AC_ORIGIN}/prompt~${encodeURIComponent(word)}?${flags}`; 5253 } 5254 if (category === "l5") { 5255 return `${AC_ORIGIN}/l5-hello.lua?${flags}`; 5256 } 5257 if (category === "processing") { 5258 return `${AC_ORIGIN}/processing?t=${Date.now()}`; 5259 } 5260 if (category === "kidlisp") { 5261 return `${AC_ORIGIN}/kidlisp?${flags}`; 5262 } 5263 if (Array.isArray(doc?.examples) && doc.examples.length) { 5264 const first = String(doc.examples[0]).replace(/^prompt~/, ""); 5265 return `${AC_ORIGIN}/prompt~${encodeURIComponent(first)}?${flags}`; 5266 } 5267 const guess = parseSigName(doc?.sig, word); 5268 return `${AC_ORIGIN}/prompt~${encodeURIComponent(guess)}?${flags}`; 5269 } 5270 5271 function renderParamsSection(doc) { 5272 if (!Array.isArray(doc?.params) || doc.params.length === 0) { 5273 return `<p>Parameters are not fully documented yet.</p>`; 5274 } 5275 return ` 5276 <table> 5277 <thead> 5278 <tr> 5279 <th>Name</th> 5280 <th>Type</th> 5281 <th>Required</th> 5282 <th>Description</th> 5283 </tr> 5284 </thead> 5285 <tbody> 5286 ${doc.params 5287 .map((param) => { 5288 const required = param.required ? "yes" : "no"; 5289 const type = Array.isArray(param.values) 5290 ? `${param.type || "enum"}: ${param.values.join(", ")}` 5291 : (param.type || ""); 5292 return ` 5293 <tr> 5294 <td><code>${escapeHTML(param.name || "")}</code></td> 5295 <td>${escapeHTML(type)}</td> 5296 <td>${required}</td> 5297 <td>${escapeHTML(param.desc || "")}</td> 5298 </tr> 5299 `; 5300 }) 5301 .join("")} 5302 </tbody> 5303 </table> 5304 `; 5305 } 5306 5307 function renderExamplesSection(doc, category, word) { 5308 const playgroundUrl = playgroundUrlForCategory(category); 5309 const examples = Array.isArray(doc?.examples) ? doc.examples : []; 5310 if (!examples.length) { 5311 return ` 5312 <p>No explicit examples yet.</p> 5313 <p><a href="${playgroundUrl}">Open playground to experiment live</a>.</p> 5314 `; 5315 } 5316 5317 return ` 5318 <ul> 5319 ${examples 5320 .map((example) => { 5321 const clean = String(example).replace(/^prompt~/, ""); 5322 return `<li><a href="${AC_ORIGIN}/prompt~${encodeURIComponent(clean)}">${escapeHTML(clean)}</a></li>`; 5323 }) 5324 .join("")} 5325 </ul> 5326 <p><a href="${playgroundUrl}">Open playground</a> · <a href="${AC_ORIGIN}/docs/${category}:${word}">Permalink</a></p> 5327 `; 5328 } 5329 5330 function renderDocContent(category, word, doc) { 5331 const lang = 5332 doc?.lang || 5333 (category === "l5" 5334 ? "lua" 5335 : category === "processing" 5336 ? "java" 5337 : category === "kidlisp" 5338 ? "lisp" 5339 : "javascript"); 5340 const previewSrc = previewUrlForDoc(doc, category, word); 5341 const family = familyFromCategory(category); 5342 const status = normalizeStatus(doc?.done); 5343 const returns = doc?.returns || "Not specified."; 5344 const notes = doc?.notes || ""; 5345 const desc = doc?.desc || "Description pending."; 5346 const sig = doc?.sig || `${word}(...)`; 5347 const playgroundUrl = playgroundUrlForCategory(category); 5348 5349 return ` 5350 <h1 data-done="${escapeHTML(doc?.done)}" id="title"><a href="/docs">${escapeHTML(word)}</a></h1> 5351 <div class="code-doc"> 5352 <div class="doc-meta"> 5353 <span><strong>${escapeHTML(family)}</strong></span> 5354 <span>/</span> 5355 <span><code>${escapeHTML(category)}</code></span> 5356 <span>/</span> 5357 <span class="doc-status">${statusBadge(status)}</span> 5358 </div> 5359 <pre><code class="language-${escapeHTML(lang)}">${escapeHTML(sig)}</code></pre> 5360 <p>${escapeHTML(desc)}</p> 5361 <div class="doc-grid"> 5362 <div class="doc-block doc-body"> 5363 <h2>Parameters</h2> 5364 ${renderParamsSection(doc)} 5365 </div> 5366 <div class="doc-block doc-body"> 5367 <h2>Returns</h2> 5368 <p><code>${escapeHTML(returns)}</code></p> 5369 </div> 5370 <div class="doc-block doc-body"> 5371 <h2>Examples</h2> 5372 ${renderExamplesSection(doc, category, word)} 5373 </div> 5374 <div class="doc-block doc-body"> 5375 <h2>Runtime Notes</h2> 5376 ${notes ? `<p>${escapeHTML(notes)}</p>` : `<p>No additional runtime notes yet.</p>`} 5377 </div> 5378 <div class="doc-block doc-body"> 5379 <h2>Details</h2> 5380 ${doc?.body || `<p>No additional details yet.</p>`} 5381 </div> 5382 <div class="doc-block"> 5383 <h2>Live Preview</h2> 5384 <div class="doc-preview"> 5385 <div class="doc-preview-head">Embedded AC preview</div> 5386 <div class="doc-preview-body"> 5387 <iframe 5388 id="doc-preview-frame" 5389 data-src="${previewSrc}" 5390 src="${previewSrc}" 5391 title="${escapeHTML(word)} preview" 5392 loading="lazy" 5393 allow="autoplay; clipboard-write" 5394 ></iframe> 5395 </div> 5396 </div> 5397 <div class="doc-preview-links"> 5398 <button type="button" onclick="runDocPreview()">Run</button> 5399 <button type="button" onclick="resetDocPreview()">Reset</button> 5400 <a href="${previewSrc}" target="_blank" rel="noopener">Open preview</a> 5401 <a href="${playgroundUrl}" target="_blank" rel="noopener">Open playground</a> 5402 <a href="${AC_ORIGIN}/docs/${category}:${word}" target="_blank" rel="noopener">Open doc</a> 5403 </div> 5404 </div> 5405 </div> 5406 </div> 5407 `; 5408 } 5409 5410 function renderCollectionIndex(category, title, subtitle) { 5411 const collection = docs[category] || {}; 5412 const count = collectionCounts(collection); 5413 const links = keys(collection) 5414 .filter((name) => !collection[name]?.hidden) 5415 .sort() 5416 .map((name) => { 5417 const status = normalizeStatus(collection[name]?.done); 5418 return `<a href="/docs/${category}:${name}" data-done="${collection[name]?.done}" title="${status}">${name}</a>`; 5419 }) 5420 .join(" "); 5421 5422 return ` 5423 <h1 id="title"><a href="/docs">${escapeHTML(title)}</a></h1> 5424 <div class="code-doc"> 5425 <div class="doc-meta"> 5426 <span><strong>${escapeHTML(title)}</strong></span> 5427 <span>/</span> 5428 <span><code>${escapeHTML(category)}</code></span> 5429 <span>/</span> 5430 <span class="doc-status">${statusBadge("in-progress")}</span> 5431 </div> 5432 <p>${escapeHTML(subtitle)}</p> 5433 <p>${count.done} done · ${count.inProgress} in progress · ${count.planned} planned</p> 5434 <div class="lane-section"> 5435 <span class="links">${links}</span> 5436 </div> 5437 </div> 5438 `; 5439 } 5440 5441 const commands = { ...docs.prompts, ...docs.pieces }; 5442 let commandList = ""; 5443 // ➿ Loop through all commands and generate HTML. 5444 keys(commands) 5445 .sort() 5446 .forEach((c) => { 5447 commandList += ` 5448 <a class="prompt" href="https://${event.headers["host"]}/prompt~${c}" onclick="send('docs:${c}')">${c}</a> 5449 <small>${commands[c].desc}</small><br> 5450 `; 5451 }); 5452 5453 // Generate doc links for a given category. 5454 function genLinks(category) { 5455 return keys(docs.api[category] || []) 5456 .map( 5457 (k) => 5458 `<a href="/docs/${category}:${k}" data-done="${docs.api[category][k].done}">${k}</a>`, 5459 ) 5460 .join(" "); 5461 } 5462 5463 function laneCounts(categories) { 5464 let total = 0; 5465 let done = 0; 5466 let inProgress = 0; 5467 categories.forEach((category) => { 5468 keys(docs.api[category] || {}).forEach((word) => { 5469 total++; 5470 const status = normalizeStatus(docs.api[category][word]?.done); 5471 if (status === "done") done++; 5472 if (status === "in-progress") inProgress++; 5473 }); 5474 }); 5475 const planned = total - done - inProgress; 5476 return { total, done, inProgress, planned }; 5477 } 5478 5479 function laneCountLabel(categories) { 5480 const count = laneCounts(categories); 5481 return `${count.done} done · ${count.inProgress} in progress · ${count.planned} planned`; 5482 } 5483 5484 function collectionCounts(collection) { 5485 let total = 0; 5486 let done = 0; 5487 let inProgress = 0; 5488 keys(collection || {}).forEach((word) => { 5489 total++; 5490 const status = normalizeStatus(collection[word]?.done); 5491 if (status === "done") done++; 5492 if (status === "in-progress") inProgress++; 5493 }); 5494 const planned = total - done - inProgress; 5495 return { total, done, inProgress, planned }; 5496 } 5497 5498 function collectionCountLabel(collection) { 5499 const count = collectionCounts(collection); 5500 return `${count.done} done · ${count.inProgress} in progress · ${count.planned} planned`; 5501 } 5502 5503 function genCollectionLinks(collectionName, limit = 32) { 5504 const collection = docs[collectionName] || {}; 5505 return keys(collection) 5506 .filter((name) => !collection[name]?.hidden) 5507 .sort() 5508 .slice(0, limit) 5509 .map((name) => `<a href="/docs/${collectionName}:${name}" data-done="${collection[name]?.done}">${name}</a>`) 5510 .join(" "); 5511 } 5512 5513 const indexContent = html` 5514 <h1 id="title"> 5515 <a 5516 href="https://${event.headers["host"]}" 5517 onclick="if (window.opener) window.close();" 5518 >docs</a 5519 > 5520 </h1> 5521 <div class="code-doc-welcome"> 5522 <div class="lane-grid"> 5523 <article class="lane-card lane-mjs"> 5524 <div class="lane-head"> 5525 <div class="lane-title">MJS / AC Piece API</div> 5526 <div class="lane-count">${laneCountLabel(["mjs", "structure", "graphics", "interaction", "sound", "number", "network", "help", "system"])}</div> 5527 </div> 5528 <div class="lane-subtitle">JavaScript runtime API for AC piece authors (<code>.mjs</code>).</div> 5529 <div class="links"> 5530 <a data-done="${docs.api.mjs.overview.done}" class="top-level" href="/docs/mjs:overview">overview</a> 5531 <a data-done="${docs.api.structure.boot.done}" class="top-level" href="/docs/structure:boot">boot</a> 5532 <a data-done="${docs.api.structure.paint.done}" class="top-level" href="/docs/structure:paint">paint</a> 5533 <a data-done="${docs.api.structure.act.done}" class="top-level" href="/docs/structure:act">act</a> 5534 <a data-done="${docs.api.structure.sim.done}" class="top-level" href="/docs/structure:sim">sim</a> 5535 <a data-done="${docs.api.structure.beat.done}" class="top-level" href="/docs/structure:beat">beat</a> 5536 </div> 5537 <div class="lane-section"> 5538 <h3>Graphics</h3> 5539 <span class="links">${genLinks("graphics")}</span> 5540 </div> 5541 <div class="lane-section"> 5542 <h3>Interaction</h3> 5543 <span class="links">${genLinks("interaction")}</span> 5544 </div> 5545 <div class="lane-section"> 5546 <h3>Sound + Number + Network</h3> 5547 <span class="links">${genLinks("sound")} ${genLinks("number")} ${genLinks("network")}</span> 5548 </div> 5549 <div class="lane-section"> 5550 <h3>Help + System</h3> 5551 <span class="links">${genLinks("help")} ${genLinks("system")}</span> 5552 </div> 5553 </article> 5554 5555 <article class="lane-card lane-l5"> 5556 <div class="lane-head"> 5557 <div class="lane-title">L5 / Lua API</div> 5558 <div class="lane-count">${laneCountLabel(["l5"])}</div> 5559 </div> 5560 <div class="lane-subtitle">Processing-style Lua compatibility layer on AC.</div> 5561 <div class="links"> 5562 <a data-done="${docs.api.l5.overview.done}" class="top-level" href="/docs/l5:overview">overview</a> 5563 ${genLinks("l5")} 5564 </div> 5565 <div class="lane-section"> 5566 <h3>Runtime Surfaces</h3> 5567 <span class="links"> 5568 <a href="${AC_ORIGIN}/l5">/l5 playground</a> 5569 <a href="${AC_ORIGIN}/prompt">prompt</a> 5570 </span> 5571 </div> 5572 </article> 5573 5574 <article class="lane-card lane-processing"> 5575 <div class="lane-head"> 5576 <div class="lane-title">Processing / Java API</div> 5577 <div class="lane-count">${laneCountLabel(["processing"])}</div> 5578 </div> 5579 <div class="lane-subtitle">Processing-style Java syntax transpiled to AC L5 runtime.</div> 5580 <div class="links"> 5581 <a data-done="${docs.api.processing.overview.done}" class="top-level" href="/docs/processing:overview">overview</a> 5582 ${genLinks("processing")} 5583 </div> 5584 <div class="lane-section"> 5585 <h3>Runtime Surfaces</h3> 5586 <span class="links"> 5587 <a href="${AC_ORIGIN}/processing">/processing playground</a> 5588 <a href="${AC_ORIGIN}/l5">/l5 runtime lane</a> 5589 </span> 5590 </div> 5591 </article> 5592 5593 <article class="lane-card lane-kidlisp"> 5594 <div class="lane-head"> 5595 <div class="lane-title">KidLisp / Language API</div> 5596 <div class="lane-count">${laneCountLabel(["kidlisp"])}</div> 5597 </div> 5598 <div class="lane-subtitle">Canonical language reference is maintained on learn.kidlisp.com.</div> 5599 <div class="links"> 5600 <a data-done="${docs.api.kidlisp.overview.done}" class="top-level" href="/docs/kidlisp:overview">overview</a> 5601 ${genLinks("kidlisp")} 5602 </div> 5603 <div class="lane-section"> 5604 <h3>Canonical Source</h3> 5605 <span class="links"> 5606 <a href="${LEARN_KIDLISP_ORIGIN}/?tab=reference" target="_blank" rel="noopener">reference</a> 5607 <a href="${LEARN_KIDLISP_ORIGIN}/?tab=functions" target="_blank" rel="noopener">function popularity</a> 5608 <a href="${LEARN_KIDLISP_ORIGIN}/?id=wipe" target="_blank" rel="noopener">identifier pages</a> 5609 </span> 5610 </div> 5611 </article> 5612 5613 <article class="lane-card lane-prompts"> 5614 <div class="lane-head"> 5615 <div class="lane-title">Prompt Commands</div> 5616 <div class="lane-count">${collectionCountLabel(docs.prompts)}</div> 5617 </div> 5618 <div class="lane-subtitle">Command docs for prompt-driven actions and workflow tools.</div> 5619 <div class="links"> 5620 <a class="top-level" href="/docs/prompts">browse all prompt docs</a> 5621 <a href="${AC_ORIGIN}/prompt">open prompt</a> 5622 </div> 5623 <div class="lane-section"> 5624 <h3>Common Commands</h3> 5625 <span class="links">${genCollectionLinks("prompts", 28)}</span> 5626 </div> 5627 </article> 5628 5629 <article class="lane-card lane-pieces"> 5630 <div class="lane-head"> 5631 <div class="lane-title">Pieces</div> 5632 <div class="lane-count">${collectionCountLabel(docs.pieces)}</div> 5633 </div> 5634 <div class="lane-subtitle">Piece-specific docs and preview entry points.</div> 5635 <div class="links"> 5636 <a class="top-level" href="/docs/pieces">browse all piece docs</a> 5637 <a href="${AC_ORIGIN}/list">open piece list</a> 5638 </div> 5639 <div class="lane-section"> 5640 <h3>Featured Piece Docs</h3> 5641 <span class="links">${genCollectionLinks("pieces", 28)}</span> 5642 </div> 5643 </article> 5644 </div> 5645 </div> 5646 `.trim(); 5647 5648 const commandsContent = html` 5649 <h1 id="title"> 5650 <a 5651 href="https://${event.headers["host"]}" 5652 onclick="if (window.opener) window.close();" 5653 >$name</a 5654 > 5655 </h1> 5656 <p id="command-list" style="white-space: nowrap; display: inline-block;"> 5657 ${commandList} 5658 </p> 5659 <script> 5660 function send(message) { 5661 if (window.opener) { 5662 window.opener.postMessage(message, "*"); 5663 window.close(); 5664 } 5665 } 5666 const title = document.querySelector("#title"); 5667 const originalHTML = title.innerHTML; 5668 document.querySelectorAll(".prompt").forEach((prompt) => { 5669 prompt.addEventListener("pointerenter", (e) => { 5670 title.innerHTML = prompt.innerHTML; 5671 title.classList.add("block"); 5672 prompt.addEventListener( 5673 "pointerleave", 5674 (e) => { 5675 title.innerHTML = originalHTML; 5676 title.classList.remove("block"); 5677 }, 5678 { once: true }, 5679 ); 5680 }); 5681 }); 5682 </script> 5683 `.trim(); 5684 5685 docs.template = page 5686 .replace("$bodyclass", " class='doc'") 5687 .replace("$content", content); 5688 5689 const splitPath = event.path.split("/"); 5690 5691 if ( 5692 splitPath.length === 4 || 5693 (splitPath.length === 3 && splitPath[1] !== "api") 5694 ) { 5695 let mergedDocs; 5696 keys(docs.api).forEach((key) => { 5697 mergedDocs = { 5698 ...mergedDocs, 5699 ...docs.api[key], 5700 }; 5701 }); 5702 5703 mergedDocs = { ...mergedDocs, ...docs.pieces, ...docs.prompts }; 5704 5705 // TODO: 5706 // Use a "colon" here for parsing and building pages from. api, pieces, 5707 // and "prompts". 5708 5709 // 🔦 What should have overlap and what should not? 5710 5711 // /docs/structure:paint 5712 // /docs/pieces:line 5713 // /docs/graphics:line 5714 5715 const [category, word] = splitPath.pop().split(":"); 5716 5717 if ((category === "prompts" || category === "pieces") && !word) { 5718 const title = category === "prompts" ? "Prompt Commands" : "Pieces"; 5719 const subtitle = 5720 category === "prompts" 5721 ? "Browse all prompt command docs and open each command page." 5722 : "Browse all piece docs and open live previews per piece."; 5723 5724 return respond( 5725 200, 5726 page 5727 .replace("$bodyclass", " class='doc'") 5728 .replace("$content", renderCollectionIndex(category, title, subtitle)) 5729 .replaceAll("$name", title), 5730 { 5731 "Content-Type": "text/html; charset=UTF-8", 5732 "Cross-Origin-Embedder-Policy": "require-corp", 5733 "Cross-Origin-Opener-Policy": "same-origin-allow-popups", 5734 "Cross-Origin-Resource-Policy": "cross-origin", 5735 }, 5736 ); 5737 } 5738 5739 let doc; 5740 if (category !== "pieces" && category !== "prompts") { 5741 doc = docs.api[category]?.[word]; 5742 } else if (category) { 5743 doc = docs[category]?.[word]; 5744 } else { 5745 return respond(404, "Not found. :(", { 5746 "Content-Type": "text/html; charset=UTF-8", 5747 }); 5748 } 5749 5750 if (!doc) { 5751 return respond(404, "Not found. :(", { 5752 "Content-Type": "text/html; charset=UTF-8", 5753 }); 5754 } 5755 5756 return respond( 5757 200, 5758 page 5759 .replace("$bodyclass", " class='doc'") 5760 .replace("$content", renderDocContent(category, word, doc)) 5761 .replaceAll("$name", word), 5762 { 5763 "Content-Type": "text/html; charset=UTF-8", 5764 "Cross-Origin-Embedder-Policy": "require-corp", 5765 "Cross-Origin-Opener-Policy": "same-origin-allow-popups", 5766 "Cross-Origin-Resource-Policy": "cross-origin", 5767 }, 5768 ); 5769 } 5770 5771 // Return json or html. 5772 if (splitPath.pop().endsWith(".json")) { 5773 return respond(200, docs); 5774 } else { 5775 return respond( 5776 200, 5777 page 5778 .replace("$bodyclass", "") 5779 .replaceAll("$content", indexContent) 5780 .replaceAll("$name", "docs"), 5781 { 5782 "Content-Type": "text/html; charset=UTF-8", 5783 "Cross-Origin-Embedder-Policy": "require-corp", 5784 "Cross-Origin-Opener-Policy": "same-origin-allow-popups", 5785 "Cross-Origin-Resource-Policy": "cross-origin", 5786 }, 5787 ); 5788 } 5789}