Monorepo for Aesthetic.Computer
aesthetic.computer
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> - paints a box from the center<br>
45 <code>outline</code> - paints the outline of a box<br>
46 <code>inline</code> - 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>&&</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("&", "&")
5201 .replaceAll("<", "<")
5202 .replaceAll(">", ">")
5203 .replaceAll('"', """)
5204 .replaceAll("'", "'");
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}