Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948// 📚 Docs, 24.01.29.21.54// Return up to date data for the aesthetic.computer pieces api.
/* #region 🏁 TODO - [-] Check ida's descriptions. - [] Handle flashing screen / subtle cosmetics. - [] Wire it up to the VS code extension so it fills in the prompt / opens it. - [] Wire this endpoint up to a prompt auto-complete as well. - [] Pals should bump upwards on hover and animate scroll to top on tap. - [] And be faded / only fade up when scrolled past 0. + Done - [x] Fill out an initial range of identifiers. - [x] Can always hide unnecessary / incomplete ones later. - [x] Build a nice scrollable index page.#endregion */
import { respond } from "../../backend/http.mjs";import { defaultTemplateStringProcessor as html } from "../../public/aesthetic.computer/lib/helpers.mjs";import { getCommandDescription } from "../../public/aesthetic.computer/lib/prompt-commands.mjs";import fs from "node:fs";import path from "node:path";import { fileURLToPath } from "node:url";const dev = process.env.CONTEXT === "dev";const { keys } = Object;
// 🔎 Auto-piece discovery// Walks the disks/ folder and synthesizes docs entries for any .mjs or .lisp// file that isn't already in the curated `pieces` object below. A piece is// auto-listed (hidden:false) if it exports a `meta()` — the template includes// this, so intentional pieces show up; throwaway scratch files without a// meta export remain hidden but are still resolvable via /docs/pieces:name.const DISKS_DIR = path.resolve( path.dirname(fileURLToPath(import.meta.url)), "../../public/aesthetic.computer/disks",);
const META_EXPORT_PATTERNS = [ /export\s*\{[^}]*\bmeta\b[^}]*\}/, /export\s+(async\s+)?function\s+meta\b/, /export\s+(const|let|var)\s+meta\b/, /export\s+default\s+function\s+meta\b/,];
function extractHeaderDesc(src, commentStart) { const lines = src.split("\n"); const descLines = []; let sawFirstComment = false; for (const raw of lines) { const line = raw.trim(); if (!line) { if (descLines.length > 0) break; continue; } if (line.startsWith("/*")) break; if (!line.startsWith(commentStart)) break; let content = line.slice(commentStart.length).replace(/^\s+/, ""); if (!sawFirstComment) { sawFirstComment = true; continue; // skip "Name, YY.MM.DD…" header line } if (!content) break; if (/^(todo|readme|region|#region|#endregion)\b/i.test(content)) break; descLines.push(content); if (descLines.join(" ").length > 140) break; } let desc = descLines.join(" ").trim(); if (desc.length > 160) desc = desc.slice(0, 157) + "…"; return desc;}
let AUTO_PIECES_CACHE = null;function scanAutoPieces() { if (AUTO_PIECES_CACHE && !dev) return AUTO_PIECES_CACHE; const out = {}; let entries; try { entries = fs.readdirSync(DISKS_DIR, { withFileTypes: true }); } catch (e) { console.warn("docs.js auto-scan: readdir failed", e.message); AUTO_PIECES_CACHE = out; return out; } for (const entry of entries) { if (!entry.isFile()) continue; const file = entry.name; if (file.startsWith("_") || file.startsWith(".")) continue; const ext = path.extname(file); if (ext !== ".mjs" && ext !== ".lisp") continue; const name = file.slice(0, -ext.length); let src = ""; try { src = fs.readFileSync(path.join(DISKS_DIR, file), "utf8"); } catch (e) { continue; } const commentStart = ext === ".lisp" ? ";" : "//"; const desc = extractHeaderDesc(src, commentStart); const hasMeta = ext === ".lisp" || META_EXPORT_PATTERNS.some((r) => r.test(src)); out[name] = { sig: name, desc, done: false, hidden: !hasMeta, auto: true, }; } AUTO_PIECES_CACHE = out; return out;}
function mergeAutoPieces(curated) { const auto = scanAutoPieces(); for (const name in auto) { if (!curated[name]) curated[name] = auto[name]; }}
// GET A user's `sub` id from either their handle or email address.export async function handler(event, context) { // Make sure this is a GET request if (event.httpMethod !== "GET") { return respond(405, { error: "Wrong request type." }); }
const AC_ORIGIN = "https://aesthetic.computer"; const LEARN_KIDLISP_ORIGIN = "https://learn.kidlisp.com";
// TODO: Factor this out. const boxBody = ` <code>box()</code> <em>A random box</em></li><br> <code>box(x, y, size)</code></code> <em>Square from top left corner</em><br> <mark><code>box(x, y, w, h)</code> <em>Rectangle from top left corner</em></mark><br> <code>box(x, y, size, mode)</code> <em>Square with <code>mode</code></em><br> <code>box(x, y, w, h, mode)</code> <em>Rectangle with <code>mode</code></em><br> <br> <hr> <code>mode</code> <br> <code>center</code> - paints a box from the center<br> <code>outline</code> - paints the outline of a box<br> <code>inline</code> - the opposite of outline<br> <em>(thicken with <code>:</code> like <code>outline:4</code>)</em> <br> combine modes with <code>*</code> like <code>outline*center</code> or <code>inline:3*center</code> `.trim();
const l5StatusBadge = (status) => { if (status === "done") return `<span class="status-badge status-done">done</span>`; if (status === "in-progress") return `<span class="status-badge status-in-progress">in progress</span>`; return `<span class="status-badge status-planned">planned</span>`; };
const l5ChecklistItems = [ { area: "Docs checklist + /l5 try page", status: "done", notes: "This docs section and /l5 landing page exist.", }, { area: "Lua source detection (.lua) in loader", status: "done", notes: "disk + parse support .lua, including .mjs -> .lua -> .lisp fallback.", }, { area: "Lua runtime adapter (Wasmoon)", status: "done", notes: "Wasmoon runtime is vendored and wired through lib/l5.mjs.", }, { area: "L5 lifecycle bridge (setup/draw/events)", status: "in-progress", notes: "setup/draw + key/mouse callbacks are mapped; advanced callbacks remain.", }, { area: "Core graphics API parity", status: "in-progress", notes: "background/fill/stroke/line/rect/circle/ellipse/text/triangle/quad mapped.", }, { area: "Input globals (mouse/key/frame)", status: "in-progress", notes: "mouse/key/frame globals are injected each frame; parity is still incomplete.", }, { area: "Publish .lua pieces", status: "done", notes: "Upload + media tracking now accept lua extension.", }, { area: "Trust/restricted API posture for Lua", status: "done", notes: "Lua pieces use trustLevel=l5 and run through restricted API policy.", }, ];
const l5ChecklistBody = ` <p> This board tracks what is actually implemented, not intended parity. Update statuses as work lands. </p> <table> <thead> <tr> <th>Area</th> <th>Status</th> <th>Notes</th> </tr> </thead> <tbody> ${l5ChecklistItems .map( (item) => ` <tr> <td>${item.area}</td> <td>${l5StatusBadge(item.status)}</td> <td>${item.notes}</td> </tr> `, ) .join("")} </tbody> </table> <p><strong>Status date:</strong> 2026-02-26</p> `.trim();
const l5LifecycleBody = ` <table> <thead> <tr> <th>L5 callback</th> <th>AC bridge</th> <th>Status</th> </tr> </thead> <tbody> <tr><td><code>setup()</code></td><td><code>boot($)</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>draw()</code></td><td><code>paint($)</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>keyPressed()</code></td><td><code>act($)</code> keyboard events</td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>mousePressed()</code></td><td><code>act($)</code> pen/touch events</td><td>${l5StatusBadge("done")}</td></tr> </tbody> </table> `.trim();
const l5GraphicsBody = ` <table> <thead> <tr> <th>L5 API</th> <th>AC target</th> <th>Status</th> </tr> </thead> <tbody> <tr><td><code>background()</code></td><td><code>$.wipe()</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>fill()</code>/<code>stroke()</code></td><td>state + <code>$.ink()</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>line()</code>/<code>point()</code></td><td><code>$.line()</code>/<code>$.plot()</code></td><td>${l5StatusBadge("done")}</td></tr> <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> <tr><td><code>beginShape()</code>…</td><td><code>$.shape()</code>/<code>$.poly()</code></td><td>${l5StatusBadge("planned")}</td></tr> </tbody> </table> `.trim();
const l5InputBody = ` <table> <thead> <tr> <th>L5 global</th> <th>Source in AC</th> <th>Status</th> </tr> </thead> <tbody> <tr><td><code>mouseX</code>/<code>mouseY</code></td><td><code>$.pen.x</code>/<code>$.pen.y</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>mouseIsPressed</code></td><td><code>$.pen.drawing</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>width</code>/<code>height</code></td><td><code>$.screen.width</code>/<code>$.screen.height</code></td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>frameCount</code></td><td>runtime counter</td><td>${l5StatusBadge("done")}</td></tr> </tbody> </table> `.trim();
const l5UnsupportedBody = ` <ul> <li><code>rotate()</code> and <code>scale()</code> require matrix transform support.</li> <li><code>bezier()</code> and curve families are not mapped in v1 scope.</li> <li>File/video APIs and full Processing IO are out of scope for initial launch.</li> <li>Do not claim full L5 parity until checklist items move to <em>done</em>.</li> </ul> `.trim();
const l5ExamplesBody = ` <p>Use these as starter snippets. Runtime wiring exists; API coverage is still partial.</p> <div class="doc-examples"> <article class="doc-example"> <h3>Pulse Circle</h3> <pre><code class="language-lua">function setup() size(256, 256)end
function draw() background(12, 12, 18) local r = 40 + math.sin(frameCount * 0.05) * 20 fill(255, 120, 80) circle(width / 2, height / 2, r * 2)end</code></pre> </article> <article class="doc-example"> <h3>Mouse Dots</h3> <pre><code class="language-lua">function setup() background(255)end
function draw() if mouseIsPressed then fill(30, 30, 30) circle(mouseX, mouseY, 10) endend</code></pre> </article> </div> <p> <a href="${AC_ORIGIN}/l5">Open the L5 try page</a> · <a href="${AC_ORIGIN}/prompt">Open prompt</a> </p> `.trim();
const processingChecklistItems = [ { area: "Docs checklist + /processing try page", status: "done", notes: "Processing lane docs and /processing route are live.", }, { area: "Shared try-page architecture reuse", status: "done", notes: "Processing page is configured via shared try-page client module.", }, { area: "Processing (Java) -> Lua transpile bridge", status: "in-progress", notes: "v0 transpiler supports callbacks, typed vars, if/while, numeric for loops, and operators.", }, { area: "Runtime parity with Processing reference", status: "planned", notes: "Only a subset of Processing syntax and APIs are mapped in v0.", }, ];
const processingChecklistBody = ` <p> This board tracks current Processing-on-AC integration scope. Keep statuses synced with runtime behavior. </p> <table> <thead> <tr> <th>Area</th> <th>Status</th> <th>Notes</th> </tr> </thead> <tbody> ${processingChecklistItems .map( (item) => ` <tr> <td>${item.area}</td> <td>${l5StatusBadge(item.status)}</td> <td>${item.notes}</td> </tr> `, ) .join("")} </tbody> </table> <p><strong>Status date:</strong> 2026-02-28</p> `.trim();
const processingLifecycleBody = ` <table> <thead> <tr> <th>Processing callback</th> <th>Runtime target</th> <th>Status</th> </tr> </thead> <tbody> <tr><td><code>void setup()</code></td><td><code>function setup()</code> in L5 runtime</td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>void draw()</code></td><td><code>function draw()</code> in L5 runtime</td><td>${l5StatusBadge("done")}</td></tr> <tr><td><code>void mousePressed()</code>/<code>void keyPressed()</code></td><td>Mapped directly to L5 event callbacks</td><td>${l5StatusBadge("done")}</td></tr> </tbody> </table> `.trim();
const processingSyntaxBody = ` <ul> <li><code>void setup()</code>, <code>void draw()</code>, and common event callbacks are transpiled.</li> <li>Typed declarations such as <code>int</code>, <code>float</code>, and <code>boolean</code> become Lua locals.</li> <li><code>if/else</code>, <code>while</code>, and numeric <code>for</code> loops are translated to Lua block syntax.</li> <li>Operators like <code>!=</code>, <code>&&</code>, and <code>||</code> are rewritten to Lua equivalents.</li> <li>Math helpers like <code>sin()</code> and <code>cos()</code> map to <code>math.sin()</code> and <code>math.cos()</code>.</li> </ul> `.trim();
const processingUnsupportedBody = ` <ul> <li>Class declarations, custom object types, and overloaded methods are out of scope for v0.</li> <li>Complex Java generics/arrays and Processing Java-mode library imports are not supported yet.</li> <li>Only syntax that can safely transpile to L5 runtime callbacks is currently targeted.</li> </ul> `.trim();
const processingExamplesBody = ` <p>Starter snippets for the Processing v0 transpile bridge.</p> <div class="doc-examples"> <article class="doc-example"> <h3>Pulse Circle</h3> <pre><code class="language-java">void setup() { size(256, 256);}
void draw() { background(12, 12, 18); float r = 40 + sin(frameCount * 0.05) * 20; fill(255, 120, 80); circle(width / 2, height / 2, r * 2);}</code></pre> </article> <article class="doc-example"> <h3>Mouse Dots</h3> <pre><code class="language-java">void setup() { background(255);}
void draw() { if (mouseIsPressed) { fill(20, 20, 20); circle(mouseX, mouseY, 10); }}</code></pre> </article> </div> <p> <a href="${AC_ORIGIN}/processing">Open the Processing try page</a> · <a href="${AC_ORIGIN}/docs/processing:syntax">Open syntax notes</a> </p> `.trim();
const mjsOverviewBody = ` <p> This lane documents the JavaScript piece API for <code>.mjs</code> pieces on AC. It is the runtime-facing reference for functions used in <code>boot/paint/act/sim/beat</code>. </p> <p> Start with <a href="${AC_ORIGIN}/docs/structure:paint">paint()</a>, then browse <a href="${AC_ORIGIN}/docs/graphics:line">graphics</a>, <a href="${AC_ORIGIN}/docs/interaction:pen">interaction</a>, and <a href="${AC_ORIGIN}/docs/system:reload">system</a>. </p> `.trim();
const kidlispOverviewBody = ` <p> KidLisp docs are part of the unified platform docs program and currently use <a href="${LEARN_KIDLISP_ORIGIN}" target="_blank" rel="noopener">learn.kidlisp.com</a> as the canonical public reference. </p> <p> Use this section for cross-links between AC platform APIs and KidLisp language APIs. Long-term source convergence is tracked in <code>/plans/docs-js-lua-overhaul-hitlist.md</code>. </p> <p> <a href="${LEARN_KIDLISP_ORIGIN}/?tab=reference" target="_blank" rel="noopener">Open full KidLisp reference</a> · <a href="${LEARN_KIDLISP_ORIGIN}/?tab=functions" target="_blank" rel="noopener">Open popularity/function view</a> </p> `.trim();
const kidlispCoreBody = ` <table> <thead> <tr> <th>Family</th> <th>Examples</th> <th>Canonical source</th> </tr> </thead> <tbody> <tr> <td>Drawing</td> <td><code>wipe</code>, <code>ink</code>, <code>line</code>, <code>box</code>, <code>circle</code></td> <td><a href="${LEARN_KIDLISP_ORIGIN}/?tab=reference" target="_blank" rel="noopener">learn.kidlisp.com reference tab</a></td> </tr> <tr> <td>Transform</td> <td><code>scroll</code>, <code>zoom</code>, <code>spin</code>, <code>blur</code>, <code>bake</code></td> <td><a href="${LEARN_KIDLISP_ORIGIN}/?id=scroll" target="_blank" rel="noopener">Identifier detail pages</a></td> </tr> <tr> <td>Control + Math</td> <td><code>def</code>, <code>later</code>, <code>once</code>, <code>repeat</code>, <code>random</code></td> <td><a href="${LEARN_KIDLISP_ORIGIN}/?id=def" target="_blank" rel="noopener">Learn identifiers</a></td> </tr> </tbody> </table> `.trim();
const docs = { // Commands for programming inside of pieces. api: { // 🏛️ Generic structure: { // 🧩 Top Level Piece Functions / meta-level imports. boot: { label: "🥾 Boot", sig: "boot($)", desc: "Runs once when a piece starts. Use it to initialize state and configure systems.", params: [{ name: "$", type: "api", required: true, desc: "Full AC runtime API object for the piece." }], returns: "void", done: true, }, paint: { label: "🎨 Paint", sig: "paint($)", desc: "Runs on each render frame. Draw graphics and update visual state here.", params: [{ name: "$", type: "api", required: true, desc: "Frame API with graphics/input/system helpers." }], returns: "void | boolean", done: true, }, act: { label: "🎪 Act", sig: "act($)", desc: "Runs for input and system events (keyboard, pointer, signals, lifecycle notifications).", params: [{ name: "$", type: "api", required: true, desc: "Event API. Current event is available at $.event." }], returns: "void", done: true, }, sim: { label: "🧮 Sim", sig: "sim($)", desc: "Fixed-step simulation hook for logic updates independent from render timing.", params: [{ name: "$", type: "api", required: true, desc: "Simulation API for deterministic state updates." }], returns: "void", done: true, }, beat: { label: "🥁 Beat", sig: "beat($)", desc: "Runs on system metronome ticks for rhythm-synced behavior.", params: [{ name: "$", type: "api", required: true, desc: "Beat API including sound timing helpers." }], returns: "void", done: true, }, leave: { label: "👋 Leave", sig: "leave($)", desc: "Execute code right before the piece is unloaded.", params: [{ name: "$", type: "api", required: true, desc: "API snapshot available during teardown." }], returns: "void", done: true, }, meta: { label: " 📰 Meta", sig: "meta()", desc: "Optional static metadata declaration for the piece (name, author, capabilities).", returns: "object", done: true, }, preview: { label: "🖼️ Preview", sig: "preview($)", desc: "Render a custom preview thumbnail for galleries and listings.", params: [{ name: "$", type: "api", required: true, desc: "Graphics API for drawing thumbnail output." }], returns: "void", done: true, }, icon: { label: "🪷 Icon", sig: "icon($)", desc: "Render a small icon/fav icon representation for the piece.", params: [{ name: "$", type: "api", required: true, desc: "Graphics API for icon rendering." }], returns: "void", done: true, }, brush: { label: "🖌️ Brush", sig: "brush($)", desc: "For implementing brushes in the `nopaint` system.", params: [{ name: "$", type: "api", required: true, desc: "Nopaint brush API and event context." }], returns: "void", done: true, }, filter: { label: "🥤 Filter", sig: "filter($)", desc: "For implementing filters in the `nopaint` system.", params: [{ name: "$", type: "api", required: true, desc: "Nopaint filter API and source image data." }], returns: "void", done: true, }, curtain: { label: "curtain", sig: "curtain($)", desc: "Top-layer render hook for world-mode pieces.", params: [{ name: "$", type: "api", required: true, desc: "World render API for overlay/cutaway effects." }], returns: "void", done: true, }, background: { label: "🏔️ background", sig: "background($)", desc: "World-system backdrop hook rendered behind foreground actors.", params: [{ name: "$", type: "api", required: true, desc: "World render API for backdrop painting." }], returns: "void", done: true, }, api: { sig: "api", desc: "References all built-in functionality for a top-level function.", returns: "object", done: true, }, DEBUG: { sig: "DEBUG", desc: "A global constant that determines if `AC` is in debug mode.", returns: "boolean", done: true, }, }, // 🖱️ Interaction interaction: { pen: { sig: "pen: { x, y, ... }", desc: "Current primary pointer state (mouse or touch), including coordinates and press state.", returns: "object", examples: ["prompt~line", "prompt~plot"], done: true, }, pens: { sig: "pens(n)", desc: "Read active pointer inputs. Without args returns all pens, with index returns one pointer.", params: [{ name: "n", type: "number", required: false, desc: "Pointer index for a single pen." }], returns: "array | object", done: true, }, pen3d: { sig: "pen3d", desc: "Current XR/controller pointer state when running in immersive input contexts.", returns: "object | null", done: true, }, event: { sig: "event", desc: "The current input/system event being processed in the active callback.", returns: "object", done: true, }, }, // 🖌️ Graphics graphics: { "abstract.bresenham": { sig: "bresenham(x0, y0, x1, y1)", 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.", done: true, }, line: { sig: "line(x0, y0, x1, y1) or line({x0, y0, x1, y1}) or line(p1, p2)", desc: "Draw a 1-pixel wide line. Can take 4 coordinates, an object with coordinates, or two points.", params: [ { name: "x0, y0, x1, y1", type: "number", required: false, desc: "Line start and end coordinates." }, { name: "p1, p2", type: "point", required: false, desc: "Alternative point-object form." }, ], returns: "void", examples: ["line", "line:2", "line:5"], example: { type: "piece", entry: "line", height: 288 }, done: true, }, point: { sig: "point(...args) or point({x, y})", desc: "Plot a single pixel within the panned coordinate space. Takes x,y coordinates or a point object.", params: [ { name: "x, y", type: "number", required: false, desc: "Pixel coordinates." }, { name: "{x, y}", type: "object", required: false, desc: "Point-object form." }, ], returns: "void", examples: ["plot", "plot 48 48", "plot 128 128"], example: { type: "piece", entry: "plot", height: 288 }, done: true, }, box: { sig: "box(x, y, w, h, mode)", desc: "Draw a box with optional modes: 'fill' (default), 'outline', 'inline'. Add '*center' to draw from center. Use ':N' for thickness.", body: boxBody, params: [ { name: "x, y", type: "number", required: true, desc: "Top-left or center position (depending on mode)." }, { name: "w, h", type: "number", required: true, desc: "Box width and height." }, { name: "mode", type: "string", required: false, desc: "fill/outline/inline with optional center and thickness modifiers." }, ], returns: "void", examples: ["box", "box:outline", "box:center"], example: { type: "piece", entry: "box", height: 288 }, done: true, }, wipe: { sig: "wipe(color)", 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].", params: [ { name: "color", type: "number | string | array", required: false, desc: "Fill color. Defaults to black when omitted." }, ], returns: "void", examples: ["wipe", "wipe:red", "wipe:white"], example: { type: "piece", entry: "wipe", height: 288 }, done: true, }, ink: { sig: "ink(color)", desc: "Set the current drawing color. Color can be a single number (0-255 for grayscale) or an array [r,g,b,a].", params: [ { name: "color", type: "number | string | array", required: true, desc: "Next draw color for primitives and text." }, ], returns: "paintApi", examples: ["prompt~ink", "prompt~ink:red", "prompt~ink:black"], done: true, },
circle: { sig: "circle(x, y, radius)", desc: "Draw a filled circle centered at (x,y) with the specified radius using the current ink color.", params: [ { name: "x, y", type: "number", required: true, desc: "Circle center coordinates." }, { name: "radius", type: "number", required: true, desc: "Circle radius in pixels." }, ], returns: "void", examples: ["prompt~circle", "prompt~circle:16", "prompt~circle:outline"], done: true, }, layer: { sig: "layer(index | options)", desc: "Work with layered painting buffers for compositing and advanced drawing workflows.", returns: "object", done: true, }, painting: { sig: "painting(width, height, callback) or painting(width, height)", desc: "Create an offscreen painting buffer, optionally drawing into it via callback.", params: [ { name: "width, height", type: "number", required: true, desc: "Buffer dimensions." }, { name: "callback", type: "function", required: false, desc: "Draw callback receiving a paint API." }, ], returns: "painting", done: true, }, inkrn: { sig: "inkrn()", desc: "Read current ink color state as an RGBA array.", returns: "[r, g, b, a]", done: true, }, pagern: { sig: "pagern()", desc: "Read the current active page/buffer reference.", returns: "painting", done: true, }, notice: { sig: "notice(msg, color, opts)", desc: "Show a transient runtime notice/HUD message.", params: [ { name: "msg", type: "string", required: true, desc: "Notice text." }, { name: "color", type: "array", required: false, desc: "Foreground/background color pair." }, { name: "opts", type: "object", required: false, desc: "Duration/behavior options." }, ], returns: "void", done: true, }, blend: { sig: "blend(mode)", desc: "Set blend mode behavior for subsequent draw operations.", returns: "void", done: true, }, page: { sig: "page(buffer)", desc: "Switch the active drawing target buffer.", params: [{ name: "buffer", type: "painting", required: true, desc: "Target buffer/page." }], returns: "void", done: true, }, edit: { sig: "edit(callback)", desc: "Mutate the current pixel buffer with a callback.", params: [{ name: "callback", type: "function", required: true, desc: "Pixel mutation callback." }], returns: "void", done: true, }, copy: { sig: "copy(x, y, w, h)", desc: "Copy pixel data from the current buffer region.", returns: "painting", done: true, }, paste: { sig: "paste(painting, x, y)", desc: "Paste a painting at the given position, anchored from the top left.", params: [ { name: "painting", type: "painting | image", required: true, desc: "Source bitmap, painting id, or URL." }, { name: "x, y", type: "number", required: false, desc: "Target top-left position. Defaults to 0,0." }, { name: "scale", type: "number", required: false, desc: "Optional scale factor." }, ], returns: "void", examples: ["paste", "paste:camera", "paste:under"], example: { type: "piece", entry: "paste", height: 288 }, done: true, }, stamp: { sig: "stamp(painting, x, y, scale)", desc: "Paste a painting centered at (x,y). Useful for sprites and markers.", params: [ { name: "painting", type: "painting | image", required: true, desc: "Source bitmap, painting id, or URL." }, { name: "x, y", type: "number", required: false, desc: "Center position." }, { name: "scale", type: "number", required: false, desc: "Optional scale factor." }, ], returns: "void", examples: ["stamp", "stamp:camera", "stamp:under"], example: { type: "piece", entry: "stamp", height: 288 }, done: true, }, pixel: { sig: "pixel(x, y) -> [r, g, b, a]", desc: "Read a single pixel color from the current active painting buffer.", params: [ { name: "x, y", type: "number", required: true, desc: "Pixel coordinates." }, ], returns: "[r, g, b, a]", examples: ["prompt~pixel"], done: true, }, plot: { sig: "plot(x, y) or plot({x, y})", desc: "Draw one pixel at the given position using current ink color.", params: [ { name: "x, y", type: "number", required: false, desc: "Pixel coordinates." }, { name: "{x, y}", type: "object", required: false, desc: "Point-object form." }, ], returns: "void", examples: ["plot", "plot 32 32", "plot 200 120"], example: { type: "piece", entry: "plot", height: 288 }, done: true, }, flood: { sig: "flood(x, y)", desc: "Flood-fill adjacent matching pixels at (x,y) using current ink color.", params: [ { name: "x, y", type: "number", required: true, desc: "Seed position for the fill operation." }, ], returns: "void", examples: ["prompt~flood", "prompt~flood:blue"], done: true, }, lineAngle: { sig: "lineAngle(x, y, distance, angle)", desc: "Draw a line from origin using polar coordinates.", returns: "void", done: true, }, pline: { sig: "pline(points)", desc: "Draw a connected polyline from a point list.", returns: "void", done: true, }, pppline: { sig: "pppline(points)", desc: "Draw a pixel-perfect polyline with aliased-style stepping.", returns: "void", done: true, }, oval: { sig: "oval(x, y, w, h, mode)", desc: "Draw an ellipse bounded by width and height dimensions.", params: [ { name: "x, y", type: "number", required: true, desc: "Top-left or center position depending on mode." }, { name: "w, h", type: "number", required: true, desc: "Ellipse width and height." }, { name: "mode", type: "string", required: false, desc: "fill/outline variants." }, ], returns: "void", examples: ["oval", "oval:outline", "oval:center"], example: { type: "piece", entry: "oval", height: 288 }, done: true, }, poly: { sig: "poly(x0, y0, x1, y1, ...)", desc: "Draw a polygon from point pairs in sequence.", params: [ { name: "points", type: "number[]", required: true, desc: "Alternating x/y coordinate list." }, ], returns: "void", examples: ["prompt~poly", "prompt~poly:outline"], done: true, }, shape: { sig: "shape(points, mode)", desc: "Draw a higher-level shape from point arrays/objects with optional mode controls.", params: [ { name: "points", type: "array", required: true, desc: "Point list or packed coordinate data." }, { name: "mode", type: "string", required: false, desc: "fill/outline behavior." }, ], returns: "void", examples: ["shape", "shape:outline"], example: { type: "piece", entry: "shape", height: 288 }, done: true, }, grid: { sig: "grid(x, y, w, h, scale)", desc: "Create a uniform grid helper for layout and sampling.", returns: "Grid", done: true, }, draw: { sig: "draw(shapeOrCommand, ...args)", desc: "Execute a generic draw command helper.", returns: "void", done: true, }, printLine: { sig: "printLine(text, x, y, opts)", desc: "Render a single line of text using low-level type metrics.", returns: "void", done: true, }, form: { sig: "form(options)", desc: "Create/manage a higher-level geometric form object for 3D or batched drawing.", returns: "Form", done: true, }, pan: { sig: "pan(x, y)", desc: "Set or offset the current 2D camera pan.", returns: "void", done: true, }, unpan: { sig: "unpan()", desc: "Reset active pan transform.", returns: "void", done: true, }, savepan: { sig: "savepan()", desc: "Store current pan transform state.", returns: "void", done: true, }, loadpan: { sig: "loadpan()", desc: "Restore previously stored pan transform state.", returns: "void", done: true, }, skip: { sig: "skip(n)", desc: "Skip/pad drawing steps in helper-driven sequences.", returns: "void", done: true, }, glaze: { sig: "glaze({ on: bool })", desc: "Enable a fullscreen shader `glaze` effect.", returns: "void", done: true, }, paintCount: { sig: "paintCount", desc: "The number of `paint` frames that have passed.", returns: "bigint", done: true, }, screen: { sig: "screen", desc: "Current screen buffer object with width/height/pixels.", returns: "painting", done: true, }, display: { sig: "display", desc: "A reference to the current display information.", returns: "object", done: true, }, fps: { sig: "fps(value)", desc: "Set target frame rate for draw loops.", returns: "void", done: true, }, resolution: { sig: "resolution(width, height = width, gap = 8)", desc: "Adjust display resolution and optional gap/pixel spacing.", params: [ { name: "width", type: "number", required: true, desc: "Target render width." }, { name: "height", type: "number", required: false, desc: "Target render height (defaults to width)." }, { name: "gap", type: "number", required: false, desc: "Display gap spacing between pixels." }, ], returns: "void", examples: ["prompt~resolution:128", "prompt~resolution:64"], done: true, }, video: { sig: "video(mode, options)", desc: "Access camera/video capture and tracking modes.", returns: "object | void", done: true, }, rec: { sig: "rec", desc: "Recorder subsystem for capturing frames/media output.", returns: "Recorder", done: true, }, needsPaint: { sig: "needsPaint()", desc: "Mark the renderer as dirty so a frame will be painted.", returns: "void", done: true, }, noise16: { sig: "noise16(opts)", desc: "Apply 16-color noise texture overlay.", returns: "void", done: true, }, noise16DIGITPAIN: { sig: "noise16DIGITPAIN(opts)", desc: "Apply DIGITPAIN-flavored 16-color noise texture.", returns: "void", done: true, }, noise16Aesthetic: { sig: "noise16Aesthetic(opts)", desc: "Apply Aesthetic-themed 16-color noise texture.", returns: "void", done: true, }, noise16Sotce: { sig: "noise16Sotce(opts)", desc: "Apply SOTCE-themed 16-color noise texture.", returns: "void", done: true, }, noiseTinted: { sig: "noiseTinted(opts)", desc: "Apply tinted procedural noise to the active buffer.", returns: "void", done: true, }, write: { sig: "write(text, pos, b, bounds, wordWrap)", desc: "Render text into the current painting using current ink, font, and optional bounds.", params: [ { name: "text", type: "string", required: true, desc: "Text content to draw." }, { name: "pos", type: "object | number", required: false, desc: "Position or anchor object." }, { name: "bounds", type: "object", required: false, desc: "Optional clipping/wrapping bounds." }, ], returns: "painting | metrics", examples: ["prompt~write", "prompt~word"], done: true, }, "text.capitalize": { sig: "capitalize(text)", desc: "Capitalize words in a string for display labels/headings.", returns: "string", done: true, }, "text.box": { sig: "box(text, pos, bounds, scale, wordWrap, fontName)", desc: "Measure and layout text block metrics without drawing.", returns: "object", done: true, }, clonePixels: { sig: "clonePixels(buffer)", desc: "Return a cloned pixel buffer for safe mutation.", returns: "Uint8ClampedArray", done: true, }, colorsMatch: { sig: "colorsMatch(color1, color2)", desc: "Checks if two colors `[r, g, b, a]` are the same.", returns: "boolean", done: true, }, color: { sig: "color(?)", desc: "Return a color `[r, g, b, a]` from a variety of inputs.", returns: "[r, g, b, a]", done: true, }, resize: { sig: "resize(bitmap, width, height)", desc: "Get a fresh resized bitmap with nearest neighbor scaling.", returns: "painting", done: true, }, Camera: { sig: "Camera", desc: "3D camera model/type used by form and world rendering helpers.", done: true, }, Form: { sig: "Form", desc: "3D/mesh form primitive type used in advanced rendering.", done: true, }, Dolly: { sig: "Dolly", desc: "Camera dolly helper type for 3D transforms and motion.", done: true, }, TRI: { sig: "TRI", desc: "Triangle primitive constant for form pipelines.", done: true, }, QUAD: { sig: "QUAD", desc: "Quad primitive constant for form pipelines.", done: true, }, LINE: { sig: "LINE", desc: "Line primitive constant for form pipelines.", done: true, }, CUBEL: { sig: "CUBEL", desc: "Cuboid/cube primitive constant for form pipelines.", done: true, }, ORIGIN: { sig: "ORIGIN", desc: "Origin reference constant for transform helpers.", done: true, }, "ui.Button": { sig: "new Button(box)", desc: "An interactive button model with a text label.", returns: "Button", done: true, }, "ui.TextButton": { sig: "new TextButton(text, pos)", desc: "An interactive button model with a text label.", returns: "TextButton", done: true, }, "ui.TextInput": { sig: "new TextInput($, text, processCommand, options = { palette, font, wrap })", desc: "An interactive text prompt object.", returns: "TextInput", done: true, }, "content.add": { sig: "add(content)", desc: "Make a request to add content to the DOM.", returns: "string", done: true, }, "dom.html": { sig: "html(src)", desc: "Add `html` content to the DOM.", returns: "void", done: true, }, "dom.css": { sig: "css(src)", desc: "Add `css` content to the DOM.", returns: "void", done: true, }, "dom.javascript": { sig: "javascript(src)", desc: "Add `javascript` content to the DOM.", returns: "void", done: true, }, "dom.clear": { sig: "clear()", desc: "Clear (remove) all DOM content.", returns: "void", done: true, }, typeface: { sig: "typeface", desc: "A reference to the default system typeface.", returns: "object", done: true, }, cursor: { sig: "cursor(code)", desc: "Set the system mouse cursor to a different graphic.", returns: "void", done: true, }, }, sound: { "sound.time": { sig: "sound.time", desc: "Current audio engine time in seconds.", returns: "number", done: true, }, "sound.bpm": { sig: "sound.bpm(newBPM?)", desc: "Get or set the current BPM used for beat-based durations.", params: [{ name: "newBPM", type: "number", required: false, desc: "Optional BPM override." }], returns: "number", done: true, }, "sound.freq": { sig: "sound.freq(input)", desc: "Resolve note names or numeric input to frequency in Hz.", params: [{ name: "input", type: "string | number", required: true, desc: "Examples: 440, C4, 4C#, A3." }], returns: "number | null", done: true, }, "sound.microphone": { sig: "sound.microphone", desc: "Live microphone object (connect/poll/record + analysis fields).", returns: "object", done: true, }, "sound.speaker": { sig: "sound.speaker", desc: "Live speaker output analysis object (amplitude/waveform/frequency data).", returns: "object", done: true, }, "sound.play": { sig: "sound.play(sfx, options, callbacks)", desc: "Play a registered sample/sfx by id and return a live handle.", params: [ { name: "sfx", type: "string", required: true, desc: "Sample id/path to play." }, { name: "options", type: "object", required: false, desc: "Playback options (volume, pan, loop, speed, etc)." }, { name: "callbacks", type: "object", required: false, desc: "Lifecycle callbacks (for example kill handlers)." }, ], returns: "object", done: true, }, "sound.synth": { sig: "sound.synth({ tone, type, duration, beats, attack, decay, volume, pan, generator })", desc: "Play a synthesized voice and return a handle for kill/progress/update.", params: [{ name: "options", type: "object", required: false, desc: "Synth options object with oscillator and envelope fields." }], returns: "object", done: true, }, "sound.bubble": { sig: "sound.bubble({ radius, rise, volume, pan })", desc: "Spawn a bubble-style synthesized sound voice.", params: [{ name: "options", type: "object", required: false, desc: "Bubble synth options." }], returns: "object", done: true, }, "sound.kill": { sig: "sound.kill(id, fade?)", desc: "Stop an active synth/sample by id, with optional fade time.", params: [ { name: "id", type: "number | bigint | string", required: true, desc: "Active sound identifier." }, { name: "fade", type: "number", required: false, desc: "Optional fade-out duration." }, ], returns: "void", done: true, }, }, network: { "net.signup": { sig: "signup()", desc: "Redirect a user to the signup screen.", returns: "void", done: true, }, "net.login": { sig: "login()", desc: "Redirect a user to the login screen.", returns: "void", done: true, }, "net.logout": { sig: "logout()", desc: "Log a user out and redirect them to the `prompt`.", returns: "void", done: true, }, "net.pieces": { sig: "pieces", desc: "The system path to all built-in piece code.", returns: "string", done: true, }, "net.parse": { sig: "parse(slug)", desc: "Parse a textual piece slug.", params: [{ name: "slug", type: "string", required: true, desc: "Piece slug or prompt-style path." }], returns: "object", done: true, }, "net.userRequest": { sig: "userRequest(method, endpoint, body)", desc: "Make an authorized request for a logged in user.", params: [ { name: "method", type: "string", required: true, desc: "HTTP method (GET, POST, etc)." }, { name: "endpoint", type: "string", required: true, desc: "Relative API endpoint." }, { name: "body", type: "object", required: false, desc: "JSON payload for write requests." }, ], returns: "Promise<object>", done: true, }, "net.udp": { sig: "udp(receive)", desc: "Loosely connect the UDP receiver.", params: [{ name: "receive", type: "function", required: true, desc: "Callback for incoming UDP-style messages." }], returns: "void", done: true, }, "net.lan": { sig: "lan", desc: "A reference to the local area network IP if it is available.", returns: "string | null", done: true, }, "net.iframe": { sig: "iframe", desc: "Whether or not the system is running hosted within an `iframe`.", returns: "boolean", done: true, }, back: { sig: "back()", desc: "Go back to the previous piece or prompt if there is no history.", returns: "void", done: true, }, alias: { sig: "alias(name, colon, params)", desc: "Jump to a piece without changing the corner label or url, and ignoring the history stack.", params: [ { name: "name", type: "string", required: true, desc: "Piece slug to load." }, { name: "colon", type: "array", required: false, desc: "Colon params to pass through." }, { name: "params", type: "array", required: false, desc: "Space params to pass through." }, ], returns: "void", done: true, }, load: { sig: "async load(parsed, fromHistory, alias, devReload, loadedCallback)", desc: "Load a piece after parsing a slug, with various options.", params: [ { name: "parsed", type: "object", required: true, desc: "Parsed slug payload." }, { name: "fromHistory", type: "boolean", required: false, desc: "Treat this load as history navigation." }, { name: "alias", type: "boolean", required: false, desc: "Skip URL/label rewrite when true." }, { name: "devReload", type: "boolean", required: false, desc: "Set dev-reload mode." }, { name: "loadedCallback", type: "function", required: false, desc: "Callback after load success." }, ], returns: "Promise<void>", done: true, }, slug: { sig: "slug", desc: "The full piece address containing its name, colon parameters, and space separated parameters.", returns: "object", done: true, }, piece: { sig: "piece", desc: "The name of the running piece.", returns: "string", done: true, }, query: { sig: "query", desc: "An object containing the system's URL query parameters.", returns: "object", done: true, }, params: { sig: "params", desc: "Array of space-delimited piece parameters from the current slug.", returns: "array", done: true, }, colon: { sig: "colon", desc: "Array of colon parameters for the active piece slug.", returns: "array", done: true, }, preload: { sig: "async preload(path, parseJSON = true, progressReport, options)", desc: "Preload a media asset from the network.", params: [ { name: "path", type: "string", required: true, desc: "Asset URL or path." }, { name: "parseJSON", type: "boolean", required: false, desc: "Auto-parse JSON responses." }, { name: "progressReport", type: "function", required: false, desc: "Progress callback." }, { name: "options", type: "object", required: false, desc: "Fetch options overrides." }, ], returns: "Promise<any>", done: true, }, download: { sig: "download(filename, data, modifiers)", desc: "Download a file.", params: [ { name: "filename", type: "string", required: true, desc: "Output filename." }, { name: "data", type: "Blob | string | object", required: true, desc: "Download payload." }, { name: "modifiers", type: "object", required: false, desc: "Optional mime/options." }, ], returns: "void", done: true, }, dark: { sig: "dark", desc: "If the system is in dark mode.", returns: "boolean", done: true, }, jump: { sig: "jump(to)", desc: "Navigate to a piece/url or cached code id.", params: [{ name: "to", type: "string", required: true, desc: "Target piece slug, URL, or code id." }], returns: "void", done: true, }, leaving: { sig: "leaving()", desc: "Returns true if a piece is leaving / a `jump` is in process.", returns: "boolean", done: true, }, broadcast: { sig: "broadcast(msg)", desc: "Send a message to other open `aesthetic.computer` tabs.", params: [{ name: "msg", type: "any", required: true, desc: "Broadcast payload." }], returns: "void", done: true, }, "net.socket": { sig: "socket(receive)", desc: "Hook into the piece's socket server with a receive callback.", params: [{ name: "receive", type: "function", required: true, desc: "Callback for socket events/messages." }], returns: "object | void", done: true, }, "net.devReload": { sig: "devReload", desc: "A flag that determines if the piece code was just reloaded in development.", returns: "boolean", done: true, }, "net.web": { sig: "web(url, jumpOut)", desc: "Jump the browser to a new url.", params: [ { name: "url", type: "string", required: true, desc: "Destination URL." }, { name: "jumpOut", type: "boolean", required: false, desc: "Open externally when true." }, ], returns: "void", done: true, }, "net.host": { sig: "host", desc: "The current network host.", returns: "string", done: true, }, "net.rewrite": { sig: "rewrite(path, historical = false)", desc: "Rewrite a new URL / parameter path without affecting the history.", params: [ { name: "path", type: "string", required: true, desc: "New path/query to write." }, { name: "historical", type: "boolean", required: false, desc: "Whether to push history." }, ], returns: "void", done: true, }, "net.refresh": { sig: "refresh()", desc: "Refresh the page / restart `aesthetic.computer`.", returns: "void", done: true, }, "net.waitForPreload": { sig: "waitForPreload()", desc: "Tell the system to wait until preloading is finished before painting.", returns: "void", done: true, }, "net.preloaded": { sig: "preloaded()", desc: "Tell the system that all preloading is done.", returns: "void", done: true, }, }, number: { simCount: { sig: "simCount", desc: "The number of simulation frames passed.", returns: "bigint", done: true, }, seconds: { sig: "seconds(s)", desc: "Convert seconds to `sim` frames.", params: [{ name: "s", type: "number", required: true, desc: "Seconds value." }], returns: "number", done: true, }, "num.add": { sig: "add(...numbers) | add(numbers[])", desc: "Add all numeric inputs and return the total.", returns: "number", done: true, }, "num.wrap": { sig: "wrap(n, to)", desc: "Wrap a number into the range 0..to (exclusive upper bound).", returns: "number", done: true, }, "num.even": { sig: "even(n)", desc: "Return true when n is evenly divisible by 2.", returns: "boolean", done: true, }, "num.odd": { sig: "odd(n)", desc: "Return true when n is odd.", returns: "boolean", done: true, }, "num.clamp": { sig: "clamp(value, low, high)", desc: "Clamp a value between low and high.", returns: "number", done: true, }, "num.rand": { sig: "rand()", desc: "Return a random float in the range 0..1.", returns: "number", done: true, }, "num.randInt": { sig: "randInt(n)", desc: "Gets a random integer.", done: true, }, "num.randInd": { sig: "randInd(arr)", desc: "Generates a random index from an array.", done: true, }, "num.randIntArr": { sig: "randIntArr(n, count)", desc: "Generates an array of random integers from 0-n (inclusive)", done: true, }, "num.randIntRange": { sig: "randIntRange(low, high)", desc: "Generates an integer from low-high (inclusive)", done: true, }, "num.rangedInts": { sig: "rangedInts(ints)", desc: "Converts an array of strings formatted like 1-100 into an array of random integer ranges. Useful for color ranges.", done: true, }, "num.multiply": { sig: "multiply(operands, n)", desc: "Multiplies one or more [] operands by n and returns a Number or Array.", done: true, }, "num.dist": { sig: "dist(x1, y1, x2, y2)", desc: "Compute the distance between two 2D points.", done: true, }, "num.dist3d": { sig: "dist3d(p1, p2)", desc: "Compute the distance between two 3D points as [x, y, z].", done: true, }, "num.perlin": { sig: "perlin(x, y)", desc: "Compute a 2D perlin noise value.", done: true, }, "num.radians": { sig: "radians(deg)", desc: "Convert degrees to radians.", done: true, }, "num.degrees": { sig: "degrees(rad)", desc: "Convert radians to degrees.", done: true, }, "num.lerp": { sig: "lerp(a, b, amount)", desc: "Slides a number between a and b by a normalized amount.", done: true, }, "num.map": { sig: "map(num, inMin, inMax, outMin, outMax)", desc: "Maps a number within a range to a new range.", done: true, }, "num.arrMax": { sig: "arrMax(arr)", desc: "Return the maximum number in an array.", done: true, }, "num.arrCompress": { sig: "arrCompress(arr, n)", desc: "Return a new array with every nth index missing.", done: true, }, "num.Track": { sig: "new Track(values, result)", desc: "Lerp a value using a stepping function, with optional quantization.", done: true, }, "num.p2.of": { sig: "of(x, y)", desc: "Turns two values into an {x, y} point.", done: true, }, "num.p2.len": { sig: "len(pA)", desc: "Gets the length of the point as a vector.", done: true, }, "num.p2.norm": { sig: "norm(p)", desc: "Normalizes a vector to have a length of 1.", done: true, }, "num.p2.eq": { sig: "eq(p1, p2)", desc: "Checks for the equality of two points.", done: true, }, "num.p2.inc": { sig: "inc(pout, pin)", desc: "Mutably adds P->in to P->out.", done: true, }, "num.p2.scl": { sig: "scl(pout, pin)", desc: "Mutably scales P->out by P->in.", done: true, }, "num.p2.add": { sig: "add(pA, pB)", desc: "Immutably adds pA + pB.", done: true, }, "num.p2.sub": { sig: "sub(pA, pB)", desc: "Immutably subtracts pA - pB.", done: true, }, "num.p2.rot": { sig: "rot(p, angle)", desc: "Immutably rotates p by angle in radians.", done: true, }, "num.p2.mul": { sig: "mul(pA, pB)", desc: "Immutably multiplies pA * pB.", done: true, }, "num.p2.div": { sig: "div(pA, pB)", desc: "Immutably divides pA / pB. Expands pA to an {x, y} if it is a single number.", done: true, }, "num.p2.mid": { sig: "mid(pA, pB)", desc: "Calculates the midpoint between two points.", done: true, }, "num.p2.dist": { sig: "dist(pA, pB)", desc: "Calculates the distance between two points.", done: true, }, "num.p2.angle": { sig: "angle(pA, pB)", desc: "Calculates the angle between two points.", done: true, }, "num.p2.dot": { sig: "dot(pA, pB)", desc: "Calculates the dot product of two points.", done: true, }, "num.p2.floor": { sig: "floor(p)", desc: "Applies the floor function to both x and y coordinates of a point.", done: true, }, "num.midp": { sig: "midp(a, b)", desc: "Find the midpoint between two [x, y] coordinates.", done: true, }, "num.number": { sig: "number(maybeNumber)", desc: "Determine if the value is a number or not.", done: true, }, "num.intersects": { sig: "intersects(line1, line2)", desc: "Compute whether two lines intersect. A line is: `{x0, y0, x1, y1}`", done: true, }, "num.signedCeil": { sig: "signedCeil(n)", desc: "Ceil a number away from 0.", done: true, }, "num.signedFloor": { sig: "signedFloor(val)", desc: "Floor a number towards 0.", done: true, }, "num.vec2": { sig: "vec2.?", desc: "All the `vec2` functions from the `glMatrix` library.", done: true, }, "num.vec3": { sig: "vec3.?", desc: "All the `vec3` functions from the `glMatrix` library.", done: true, }, "num.vec4": { sig: "vec4.?", desc: "All the `vec4` functions from the `glMatrix` library.", done: true, }, "num.mat3": { sig: "mat3.?", desc: "All the `mat3` functions from the `glMatrix` library.", done: true, }, "num.mat4": { sig: "mat4.?", desc: "All the `mat4` functions from the `glMatrix` library.", done: true, }, "num.quat": { sig: "quat.?", desc: "All the `quat` (quaternion) functions from the `glMatrix` library.", done: true, }, "num.parseColor": { sig: "parseColor(params)", desc: "Parses a color from piece params.", done: true, }, "num.findColor": { sig: "findColor(rgb)", desc: "Find a color inside of `cssColors` by value", done: true, }, "num.saturate": { sig: "saturate(rgb, amount = 1)", desc: "Saturate a color by `amount`.", done: true, }, "num.desaturate": { sig: "desaturate(rgb, amount = 1)", desc: "Desaturate a color by `amount`", done: true, }, "num.shiftRGB": { sig: 'shiftRGB(a, b, step, mode = "lerp", range = 255)', desc: "Lerp two RGBA arrays, skipping alpha and rounding the output.", done: true, }, "num.rgbToHexStr": { sig: "rgbToHexStr(r, g, b, prefix = \"\")", desc: "Convert separate RGB values to a hex string.", done: true, }, "num.hexToRgb": { sig: "hexToRgb(h)", desc: "Takes a string/number hex value and outputs an [r, g, b] array.", done: true, }, "num.blend": { sig: "blend(dst, src, alphaIn = 1)", desc: "Alpha blends two colors, mutating and returning `dst`.", done: true, }, "num.rgbToHsl": { sig: "rgbToHsl(r, g, b)", desc: "Convert rgb to hsl (360, 100, 100).", done: true, }, "num.hslToRgb": { sig: "hslToRgb(h, s, l)", desc: "Convert hsl (360, 100, 100) to rgb.", done: true, }, "num.rainbow": { sig: "rainbow()", desc: "Return a cycled color from the `rainbow` template.", done: true, }, delay: { sig: "delay(fun, time)", desc: "Delay a function by `time` number of sim steps.", done: true, }, blink: { sig: "blink(time, fun)", desc: "A looped `delay`.", done: true, }, "geo.Box": { sig: "new Box()", desc: "A dynamic box with helpful methods.", done: true, }, "geo.DirtyBox": { sig: "new DirtyBox()", desc: "A box model implementing dirty rectangle optimization.", done: true, }, "geo.Grid": { sig: "new Grid(x, y, w, h, s = 1)", desc: "A 2 dimensional uniform grid, using a box as the frame (with scaling).", done: true, }, "geo.Circle": { sig: "new Circle(x, y, radius = 8)", desc: "A generic circle model.", done: true, }, "geo.linePointsFromAngle": { sig: "linePointsFromAngle(x1, y1, dist, degrees)", desc: "Project outwards from an origin point at dist, and degrees to get the full line.", done: true, }, "geo.pointFrom": { sig: "pointFrom(x, y, angle, dist)", desc: "Project outwards from a point at an `angle` and `dist` and get the resulting point.", done: true, }, "geo.Race": { sig: "new Race(opts = { quantized: true })", desc: "Follows a point over time.", done: true, }, "geo.Quantizer": { sig: "new Quantizer(opts)", desc: "A simple model for lazy following of a 3D point.", done: true, }, }, help: { choose: { sig: "choose(a, b, ...)", desc: "Randomly return one of the arguments.", returns: "any", done: true, }, flip: { sig: "flip()", desc: "Flip a coin, returning true or false.", returns: "boolean", done: true, }, repeat: { sig: "repeat(n, fn)", desc: "Run a function `n` times, passing in `i` on each iteration and returning an array of the results (like map).", params: [ { name: "n", type: "number", required: true, desc: "Iteration count (floored)." }, { name: "fn", type: "function", required: true, desc: "Callback receiving index i." }, ], returns: "array", done: true, }, every: { sig: "every(obj, value)", desc: "Set every property of an object to a certain value.", params: [ { name: "obj", type: "object", required: true, desc: "Target object to mutate." }, { name: "value", type: "any", required: true, desc: "Value assigned to every key." }, ], returns: "void", done: true, }, any: { sig: "any(objOrArray)", desc: "Returns a random value from an object, or array.", returns: "any", done: true, }, anyIndex: { sig: "anyIndex(array)", desc: "Returns a random index value from an array.", returns: "number", done: true, }, anyKey: { sig: "anyKey(obj)", desc: "Returns a random key from an object.", returns: "string", done: true, }, each: { sig: "each(obj, fun)", desc: "Run a function on every value in an object.", params: [ { name: "obj", type: "object", required: true, desc: "Object to iterate." }, { name: "fun", type: "function", required: true, desc: "Callback receiving (value, key)." }, ], returns: "void", done: true, }, shuffleInPlace: { sig: "shuffleInPlace(array)", desc: "Shuffles an array, mutating it.", returns: "array", done: true, }, "gizmo.Hourglass": { sig: "new Hourglass(max, { completed, flipped, every, autoFlip = false }, startingTicks = 0)", desc: "A repeatable timer with callbacks.", returns: "Hourglass", done: true, }, "gizmo.EllipsisTicker": { sig: "new EllipsisTicker()", desc: "An animated `...` string for showing processing indicators.", returns: "EllipsisTicker", done: true, }, }, system: { signal: { sig: "signal(content)", desc: "Send a message through the `signal` system, good for communicating with added DOM content.", params: [{ name: "content", type: "any", required: true, desc: "Signal payload." }], returns: "void", done: true, }, sideload: { sig: "sideload(type)", desc: "Open a file chooser to load a file.", params: [{ name: "type", type: "string", required: false, desc: "Optional file type filter." }], returns: "Promise<File | null>", done: true, }, user: { sig: "user", desc: "A reference to the currently logged in user.", returns: "object | null", done: true, }, vscode: { sig: "vscode", desc: "A flag that's true while running the VS Code extension.", returns: "boolean", done: true, }, meta: { sig: "meta(data)", desc: "Add meta to the common api so the data can be overridden as needed.", params: [{ name: "data", type: "object", required: true, desc: "Meta fields to merge into runtime state." }], returns: "void", done: true, }, reload: { sig: "reload({ piece, name, source, codeChannel })", desc: "Reload / start a piece in various ways. Used especially in live development.", params: [{ name: "options", type: "object", required: true, desc: "Reload options including piece/name/source/codeChannel." }], returns: "void", done: true, }, pieceCount: { sig: "pieceCount", desc: "Keeps track of how many pieces have been run so far in a session.", returns: "number", done: true, }, store: { sig: "store", desc: "An object for keeping data in across piece jumps.", returns: "object", done: true, }, "store.persist": { sig: 'store.persist(key, method = "local")', desc: "Save a storage key with associated data in the user's browser.", params: [ { name: "key", type: "string", required: true, desc: "Store key to persist." }, { name: "method", type: "string", required: false, desc: "Persistence backend (for example `local` or `local:db`)." }, ], returns: "Promise<void>", done: true, }, "store.retrieve": { sig: 'store.retrieve(key, method = "local")', desc: "Load a storage key with associated data.", params: [ { name: "key", type: "string", required: true, desc: "Store key to read." }, { name: "method", type: "string", required: false, desc: "Persistence backend." }, ], returns: "Promise<any>", done: true, }, "store.delete": { sig: 'store.delete(key, method = "local")', desc: "Remove a storage key and any saved data.", params: [ { name: "key", type: "string", required: true, desc: "Store key to remove." }, { name: "method", type: "string", required: false, desc: "Persistence backend." }, ], returns: "Promise<boolean>", done: true, }, debug: { sig: "debug", desc: "Reports whether the system is in debug / development mode.", returns: "boolean", done: true, }, canShare: { sig: "canShare", desc: "Whether the current environment supports the Web Share API.", returns: "boolean", done: true, }, handle: { sig: "handle()", desc: "Returns the user's handle, if one exists.", returns: "string | null", done: true, }, ticket: { sig: "ticket(name)", desc: "Open a ticketed paywall by its name.", params: [{ name: "name", type: "string", required: true, desc: "Ticket/paywall identifier." }], returns: "void", done: true, }, mint: { sig: "mint(picture, progress, params)", desc: "Mint a picture on an external service.", params: [ { name: "picture", type: "object", required: true, desc: "Painting/pixel payload." }, { name: "progress", type: "function", required: false, desc: "Progress callback." }, { name: "params", type: "object", required: false, desc: "Mint metadata/options." }, ], returns: "Promise<any>", done: true, }, print: { sig: "print(picture, quantity, progress)", desc: "Print the `pixels` that get passed in via an external service. Stickers only right now.", params: [ { name: "picture", type: "object", required: true, desc: "Painting/pixel payload." }, { name: "quantity", type: "number", required: false, desc: "Requested print quantity." }, { name: "progress", type: "function", required: false, desc: "Progress callback." }, ], returns: "Promise<any>", done: true, }, zip: { sig: "zip(content, progress)", desc: "Create a zip file of the content. Auto-encodes paintings.", params: [ { name: "content", type: "object | array", required: true, desc: "Files/content to include." }, { name: "progress", type: "function", required: false, desc: "Progress callback." }, ], returns: "Promise<Blob>", done: true, }, "motion.start": { sig: "start()", desc: "Start tracking device motion.", returns: "Promise<void> | void", done: true, }, "motion.stop": { sig: "stop()", desc: "Stop tracking device motion.", returns: "void", done: true, }, "motion.current": { sig: "current", desc: "Populated with the device motion data upon `motion.start()`.", returns: "object | null", done: true, }, speak: { sig: "speak(utterance, voice, mode, opts)", desc: "Speak an `utterance` aloud.", params: [ { name: "utterance", type: "string", required: true, desc: "Text to speak." }, { name: "voice", type: "string", required: false, desc: "Voice id/preset." }, { name: "mode", type: "string", required: false, desc: "Speech backend mode." }, { name: "opts", type: "object", required: false, desc: "Optional speech options." }, ], returns: "Promise<void> | void", done: true, }, act: { sig: "act(event, data)", desc: "Broadcast an `act` event through the system.", params: [ { name: "event", type: "string", required: true, desc: "Event name." }, { name: "data", type: "any", required: false, desc: "Optional payload." }, ], returns: "void", done: true, }, "get.painting().by()": { sig: "get.painting(code, opts).by(handle, opts)", desc: "Retrieve a painting from network storage.", returns: "Promise<object>", done: true, }, upload: { sig: "async upload(filename, data, progress, bucket)", desc: "Upload a media file to network storage.", params: [ { name: "filename", type: "string", required: true, desc: "Destination filename." }, { name: "data", type: "Blob | Uint8Array | string", required: true, desc: "File payload." }, { name: "progress", type: "function", required: false, desc: "Progress callback." }, { name: "bucket", type: "string", required: false, desc: "Target storage bucket." }, ], returns: "Promise<object>", done: true, }, "code.channel": { sig: "channel(chan)", desc: "Set the current code channel for live development.", params: [{ name: "chan", type: "string", required: true, desc: "Channel name/id." }], returns: "void", done: true, }, encode: { sig: "async encode(file)", desc: "File should be { type, data } where type is `png`, `webp`, or `jpg`, etc.", params: [{ name: "file", type: "object", required: true, desc: "Encoder payload {type, data, ...}." }], returns: "Promise<Blob | Uint8Array>", done: true, }, file: { sig: "async file()", desc: "Request a file from the user.", returns: "Promise<File | null>", done: true, }, authorize: { sig: "async authorize()", desc: "Authorize a user.", returns: "Promise<void>", done: true, }, "hand.mediapipe": { sig: "mediapipe", desc: "A reference to the mediapipe hand tracking data. Enable through `video`.", returns: "object | null", done: true, }, "hud.label": { sig: "label(text, color, offset)", desc: "Override the piece corner label.", params: [ { name: "text", type: "string", required: true, desc: "Label text." }, { name: "color", type: "string | array", required: false, desc: "Optional label color." }, { name: "offset", type: "number", required: false, desc: "Optional offset/priority." }, ], returns: "void", done: true, }, "hud.currentStatusColor": { sig: "currentStatusColor()", desc: "Get the current connection status label color.", returns: "string | array", done: true, }, "hud.currentLabel": { sig: "currentLabel()", desc: "Get the current label content and button.", returns: "object", done: true, }, "hud.labelBack": { sig: "labelBack()", desc: "Jump to the `prompt` with the current label applied.", returns: "void", done: true, }, send: { sig: "send({type, content})", desc: "Send a message to the bios.", params: [{ name: "message", type: "object", required: true, desc: "Message payload with `type` and optional `content`." }], returns: "void", done: true, }, platform: { sig: "platform", desc: "Get the current host platform.", returns: "string", done: true, }, history: { sig: "history", desc: "An array of previously visited pieces in a session.", returns: "array", done: true, }, "bgm.set": { sig: "set(trackNumber, volume)", desc: "Start a background music track, persisting across jumps.", params: [ { name: "trackNumber", type: "number | string", required: true, desc: "Track id/index." }, { name: "volume", type: "number", required: false, desc: "Playback gain." }, ], returns: "void", done: true, }, "bgm.stop": { sig: "stop()", desc: "Stop a background music track.", returns: "void", done: true, }, "bgm.data": { sig: "data", desc: "Gets live analysis data from the current background track.", returns: "object | null", done: true, }, "system.world": { sig: "system.world", desc: "A reference to the world system state if a piece is using it.", returns: "object | null", done: true, }, "system.nopaint": { sig: "system.nopaint", desc: "A reference to the `nopaint` system state that all brushes use.", returns: "object", done: true, }, flatten: { sig: "flatten()", desc: "Paint (bake) all graphics commands immediately.", returns: "void", done: true, }, connect: { sig: "connect()", desc: "Connect with external wallet software.", returns: "Promise<void> | void", done: true, }, wiggle: { sig: "wiggle(n, level, speed)", desc: "Oscillate a value over time using a sine wave.", params: [ { name: "n", type: "number", required: true, desc: "Base value." }, { name: "level", type: "number", required: false, desc: "Amplitude." }, { name: "speed", type: "number", required: false, desc: "Oscillation speed." }, ], returns: "number", done: true, }, dark: { sig: "dark", desc: "Gets whether the system is in dark mode.", returns: "boolean", done: true, }, darkMode: { sig: "darkMode(enabled)", desc: "Toggle dark mode on or off with a boolean.", params: [{ name: "enabled", type: "boolean", required: true, desc: "Target dark mode state." }], returns: "void", done: true, }, gpuReady: { sig: "gpuReady", desc: "Whether the system GPU is ready for rendering.", returns: "boolean", done: true, }, "gpu.message": { sig: "message(content)", desc: "Send a message to the GPU driver.", params: [{ name: "content", type: "object", required: true, desc: "Driver command payload." }], returns: "void", done: true, }, }, mjs: { overview: { sig: "MJS / AC piece API overview", desc: "Entry point for JavaScript piece API docs.", body: mjsOverviewBody, done: true, }, }, kidlisp: { overview: { sig: "KidLisp API overview", desc: "How KidLisp docs connect into the unified AC docs system.", body: kidlispOverviewBody, done: true, }, core: { sig: "KidLisp core map", desc: "Core families and canonical source links for KidLisp APIs.", body: kidlispCoreBody, done: true, }, }, l5: { overview: { sig: "L5 (Lua) on Aesthetic Computer", desc: "Processing-style Lua compatibility notes and rollout status.", body: ` <p> This is the implementation board for L5 support in AC. Keep this page aligned with the actual runtime state. </p> <p> <a href="${AC_ORIGIN}/docs/l5:checklist">Open checklist</a> · <a href="${AC_ORIGIN}/docs/l5:examples">Open examples</a> · <a href="${AC_ORIGIN}/l5">Open /l5 try page</a> </p> `.trim(), done: true, }, checklist: { sig: "L5 compatibility checklist (v0)", desc: "Single source of truth for what is implemented right now.", body: l5ChecklistBody, done: true, }, lifecycle: { sig: "L5 lifecycle bridge", desc: "How setup/draw/events map onto AC piece lifecycle hooks.", body: l5LifecycleBody, done: "in-progress", }, graphics: { sig: "L5 graphics mapping", desc: "Core drawing calls and their AC equivalents.", body: l5GraphicsBody, done: "in-progress", }, input: { sig: "L5 input globals", desc: "Frame-updated globals expected by Processing-style sketches.", body: l5InputBody, done: "in-progress", }, unsupported: { sig: "Known gaps / out of scope (v1)", desc: "Features explicitly not shipped yet.", body: l5UnsupportedBody, done: true, }, examples: { sig: "L5 example snippets", desc: "Starter Lua examples for the upcoming runtime.", body: l5ExamplesBody, done: true, }, size: { sig: "size(width, height?)", desc: "Set sketch resolution by forwarding to AC `resolution()`.", params: [ { name: "width", type: "number", required: true, desc: "Target width." }, { name: "height", type: "number", required: false, desc: "Target height (defaults to width)." }, ], returns: "void", done: true, }, background: { sig: "background(r, g, b, a?)", desc: "Clear frame with a solid color.", returns: "void", done: true, }, clear: { sig: "clear()", desc: "Clear frame to transparent black.", returns: "void", done: true, }, fill: { sig: "fill(r, g, b, a?)", desc: "Set fill color for subsequent shape and text draws.", returns: "void", done: true, }, noFill: { sig: "noFill()", desc: "Disable shape fill rendering.", returns: "void", done: true, }, stroke: { sig: "stroke(r, g, b, a?)", desc: "Set stroke color for line/outline rendering.", returns: "void", done: true, }, noStroke: { sig: "noStroke()", desc: "Disable stroke rendering.", returns: "void", done: true, }, strokeWeight: { sig: "strokeWeight(weight)", desc: "Set line/outline thickness.", params: [{ name: "weight", type: "number", required: true, desc: "Stroke width in pixels." }], returns: "void", done: true, }, point: { sig: "point(x, y)", desc: "Plot a single point using stroke color if stroke is enabled.", returns: "void", done: true, }, line: { sig: "line(x1, y1, x2, y2)", desc: "Draw a line using stroke color.", returns: "void", done: true, }, rect: { sig: "rect(x, y, width, height)", desc: "Draw a rectangle with active fill/stroke state.", returns: "void", done: true, }, square: { sig: "square(x, y, size)", desc: "Draw a square with active fill/stroke state.", returns: "void", done: true, }, circle: { sig: "circle(x, y, diameter)", desc: "Draw a circle with active fill/stroke state.", returns: "void", done: true, }, ellipse: { sig: "ellipse(x, y, width, height)", desc: "Draw an ellipse with active fill/stroke state.", returns: "void", done: true, }, triangle: { sig: "triangle(x1, y1, x2, y2, x3, y3)", desc: "Draw a triangle with active fill/stroke state.", returns: "void", done: true, }, quad: { sig: "quad(x1, y1, x2, y2, x3, y3, x4, y4)", desc: "Draw a quadrilateral with active fill/stroke state.", returns: "void", done: true, }, text: { sig: "text(value, x, y)", desc: "Draw text at coordinates using fill color.", returns: "void", done: true, }, textSize: { sig: "textSize(size)", desc: "Set text size scale for subsequent `text()` draws.", params: [{ name: "size", type: "number", required: true, desc: "Text size value." }], returns: "void", done: true, }, textWidth: { sig: "textWidth(value)", desc: "Measure text width in pixels.", returns: "number", done: true, }, frameRate: { sig: "frameRate(fps)", desc: "Request a target frame rate for draw calls.", params: [{ name: "fps", type: "number", required: true, desc: "Target frames per second." }], returns: "void", done: true, }, noLoop: { sig: "noLoop()", desc: "Stop continuous draw execution.", returns: "void", done: true, }, loop: { sig: "loop()", desc: "Resume continuous draw execution.", returns: "void", done: true, }, isLooping: { sig: "isLooping()", desc: "Get whether draw loop is active.", returns: "boolean", done: true, }, redraw: { sig: "redraw()", desc: "Request a one-off draw when loop is disabled.", returns: "void", done: true, }, random: { sig: "random(max?) or random(min, max)", desc: "Generate a random number with optional range.", returns: "number", done: true, }, map: { sig: "map(value, inMin, inMax, outMin, outMax)", desc: "Remap a value from one range to another.", returns: "number", done: true, }, dist: { sig: "dist(x1, y1, x2, y2)", desc: "Calculate Euclidean distance between two points.", returns: "number", done: true, }, lerp: { sig: "lerp(start, stop, amount)", desc: "Linear interpolate between two values.", returns: "number", done: true, }, radians: { sig: "radians(degrees)", desc: "Convert degrees to radians.", returns: "number", done: true, }, degrees: { sig: "degrees(radians)", desc: "Convert radians to degrees.", returns: "number", done: true, }, constrain: { sig: "constrain(value, min, max)", desc: "Clamp a value into a minimum/maximum range.", returns: "number", done: true, }, millis: { sig: "millis()", desc: "Get elapsed runtime milliseconds.", returns: "number", done: true, }, frameCount: { sig: "frameCount", desc: "Number of draw frames processed so far.", returns: "number", done: true, }, width: { sig: "width", desc: "Current sketch width in pixels.", returns: "number", done: true, }, height: { sig: "height", desc: "Current sketch height in pixels.", returns: "number", done: true, }, mouseX: { sig: "mouseX", desc: "Current pointer X coordinate.", returns: "number", done: true, }, mouseY: { sig: "mouseY", desc: "Current pointer Y coordinate.", returns: "number", done: true, }, pmouseX: { sig: "pmouseX", desc: "Previous frame pointer X coordinate.", returns: "number", done: true, }, pmouseY: { sig: "pmouseY", desc: "Previous frame pointer Y coordinate.", returns: "number", done: true, }, mouseIsPressed: { sig: "mouseIsPressed", desc: "Whether pointer is currently pressed.", returns: "boolean", done: true, }, key: { sig: "key", desc: "Last key value from keyboard events.", returns: "string", done: true, }, keyCode: { sig: "keyCode", desc: "Last key code value from keyboard events.", returns: "number", done: true, }, keyIsPressed: { sig: "keyIsPressed", desc: "Whether a key is currently pressed.", returns: "boolean", done: true, }, }, processing: { overview: { sig: "Processing (Java) on Aesthetic Computer", desc: "Early Processing-style Java support via transpile bridge to the L5 Lua runtime.", body: ` <p> This lane tracks the Processing v0 bridge, where Java-style Processing code is transpiled into Lua for AC's L5 runtime. </p> <p> <a href="${AC_ORIGIN}/docs/processing:checklist">Open checklist</a> · <a href="${AC_ORIGIN}/docs/processing:syntax">Open syntax notes</a> · <a href="${AC_ORIGIN}/processing">Open /processing try page</a> </p> `.trim(), done: true, }, checklist: { sig: "Processing compatibility checklist (v0)", desc: "Current integration scope and readiness checkpoints.", body: processingChecklistBody, done: "in-progress", }, lifecycle: { sig: "Processing lifecycle bridge", desc: "How setup/draw/events map from Processing Java syntax to L5 callbacks.", body: processingLifecycleBody, done: "in-progress", }, syntax: { sig: "Processing syntax transpile rules (v0)", desc: "What Java-like syntax the bridge currently rewrites.", body: processingSyntaxBody, done: "in-progress", }, unsupported: { sig: "Processing v0 known gaps", desc: "Features intentionally excluded in the first bridge release.", body: processingUnsupportedBody, done: true, }, examples: { sig: "Processing v0 examples", desc: "Starter snippets for the Processing page.", body: processingExamplesBody, done: true, }, setup: { sig: "void setup()", desc: "Called once at startup. Transpiles to `function setup()`.", returns: "void", done: true, }, draw: { sig: "void draw()", desc: "Called every frame. Transpiles to `function draw()`.", returns: "void", done: true, }, size: { sig: "size(width, height)", desc: "Set sketch resolution in Processing syntax.", returns: "void", done: true, }, background: { sig: "background(r, g, b, a?)", desc: "Clear frame with a color.", returns: "void", done: true, }, fill: { sig: "fill(r, g, b, a?)", desc: "Set fill color for subsequent shapes and text.", returns: "void", done: true, }, stroke: { sig: "stroke(r, g, b, a?)", desc: "Set stroke color for lines/outlines.", returns: "void", done: true, }, line: { sig: "line(x1, y1, x2, y2)", desc: "Draw a line primitive.", returns: "void", done: true, }, rect: { sig: "rect(x, y, width, height)", desc: "Draw a rectangle primitive.", returns: "void", done: true, }, circle: { sig: "circle(x, y, diameter)", desc: "Draw a circle primitive.", returns: "void", done: true, }, mousePressed: { sig: "void mousePressed()", desc: "Pointer-down callback mapped to L5 event hook.", returns: "void", done: true, }, }, }, // 😱 Commands for entering into the prompt. prompts: { // 📦 Pack / Export pack: { sig: "pack <piece>", desc: "Download a piece as a self-contained HTML file.", params: [ { name: "piece", type: "string", required: true, desc: "Piece name or $code" } ], done: true, }, bundle: { sig: "bundle <piece>", desc: "Download a piece as a self-contained HTML file.", params: [ { name: "piece", type: "string", required: true, desc: "Piece name or $code" } ], done: true, }, m4d: { sig: "m4d <piece>", desc: "Download a piece as an offline Max for Live device (.amxd).", params: [ { name: "piece", type: "string", required: true, desc: "Piece name or $code" } ], done: true, }, "4d": { sig: "4d <piece>", desc: "Download a piece as an offline Max for Live device (.amxd).", params: [ { name: "piece", type: "string", required: true, desc: "Piece name or $code" } ], done: true, }, m4do: { sig: "m4do <piece>", desc: "Download a piece as an online Max for Live device (.amxd) that streams from aesthetic.computer.", params: [ { name: "piece", type: "string", required: true, desc: "Piece name" } ], done: true, }, "4do": { sig: "4do <piece>", desc: "Download a piece as an online Max for Live device (.amxd) that streams from aesthetic.computer.", params: [ { name: "piece", type: "string", required: true, desc: "Piece name" } ], done: true, }, // 🔷 Tezos Wallet tezos: { sig: "tezos <action> [network]", desc: "Manage Tezos wallet.", params: [ { name: "action", type: "enum", values: ["connect", "disconnect", "status"], required: true }, { name: "network", type: "enum", values: ["ghostnet", "mainnet"], required: false, default: "ghostnet" } ], done: true, }, // 🎨 KidLisp Keeping keep: { sig: "keep $code", desc: "Keep $code in your wallet.", params: [ { name: "code", type: "string", prefix: "$", required: true, desc: "KidLisp piece code" } ], done: true, }, tape: { sig: "tape [duration] [flags]", desc: "Record your screen.", params: [ { name: "duration", type: "number", required: false, default: 5, desc: "Seconds (add 'f' for frames)" }, { name: "flags", type: "flags", values: ["mic", "nomic", "baktok"], required: false } ], done: true, }, "tape:add": { sig: "tape:add", desc: "Add time to your tape.", done: false, hidden: true, }, "tape:tt": { sig: "tape:tt", desc: "", done: false, hidden: true, }, "tape:nomic": { sig: "tape:nomic", desc: "", done: false, hidden: true, }, "tape:mic": { sig: "tape:mic", desc: "", done: false, hidden: true, }, tapem: { sig: "tapem", desc: "", done: false, hidden: true, }, "tape:cut": { sig: "tape:cut", desc: "", done: false, hidden: true, }, cut: { sig: "cut", desc: "Stop the active tape recording and finalize the clip.", examples: ["cut", "tape:cut"], returns: "void", done: true, }, me: { sig: "me", desc: "Open your profile.", examples: ["me"], returns: "void", done: true, }, scream: { sig: "scream <message>", desc: "Scream at all users.", params: [ { name: "message", type: "string", required: true, desc: "Your scream text" } ], done: true, }, nonotifs: { sig: "nonotifs", desc: "Turn off notifications.", examples: ["nonotifs"], returns: "void", done: true, }, notifs: { sig: "notifs", desc: "Turn on notifications.", examples: ["notifs"], returns: "void", done: true, }, news: { sig: "news", desc: "Aesthetic.computer news.", done: true, }, papers: { sig: "papers", desc: "Open papers.aesthetic.computer.", done: true, }, deadlines: { sig: "deadlines", desc: "Open papers conference & grant deadlines.", url: "https://papers.aesthetic.computer/deadlines", done: true, }, nela: { sig: "nela", desc: "Open NELA Computer Club.", done: true, }, selfie: { sig: "selfie", desc: "Open the front camera.", done: false, }, cam: { sig: "cam", desc: "Take a picture.", done: false, }, camu: { sig: "camu", desc: "", done: false, hidden: true, }, sparkle: { sig: "sparkle", desc: "Paint with Maya's really fun brush.", done: false, }, "painting:start": { sig: "painting:start", desc: "", done: false, hidden: true, }, print: { sig: "print", desc: "Open the print flow for the current painting.", examples: ["print"], returns: "void", done: true, }, mint: { sig: "mint", desc: "Open mint flow for the current painting.", examples: ["mint"], returns: "void", done: true, //TODO: can this open in new tab? }, "painting:done": { sig: "painting:done", desc: "", done: false, hidden: true, }, "yes!": { sig: "yes!", desc: "Finish your painting.", examples: ["yes!"], returns: "void", done: true, }, done: { sig: "done", desc: "Finish and confirm your painting.", examples: ["done"], returns: "void", done: true, }, flower: { sig: "flower", desc: "He loves me.", done: false, hidden: true, }, petal: { sig: "petal", desc: "He loves me not.", done: false, hidden: true, }, bro: { sig: "bro", desc: "Stay out of his room.", done: false, }, sis: { sig: "sis", desc: "Don't steal her makeup.", done: false, }, gf: { sig: "gf", desc: "Caring confidant.", done: false, }, bf: { sig: "bf", desc: "He might care.", done: false, }, bb: { sig: "bb", desc: "AC fundraiser.", done: false, }, p: { sig: "p", desc: "View your current painting's steps.", done: false, }, pain: { sig: "pain", desc: "View your current painting's steps.", done: false, }, load: { sig: "load", desc: "", done: false, hidden: true, }, "mood:nuke": { sig: "mood:nuke", desc: "", done: false, hidden: true, }, "mood:denuke": { sig: "mood:denuke", desc: "", done: false, hidden: true, }, mood: { sig: "mood [emoji]", desc: "Set your mood.", params: [ { name: "emoji", type: "string", required: false, desc: "Emoji or text mood" } ], done: true, }, channel: { sig: "channel [name]", desc: "View or set a piece code channel.", params: [ { name: "name", type: "string", required: false, desc: "Channel name to join" } ], done: true, }, "code-channel": { sig: "code-channel", desc: "", done: false, hidden: true, }, run: { sig: "run", desc: "", done: false, hidden: true, }, docs: { sig: "docs", desc: "Aesthetic Computer Documentation.", examples: ["docs", "l5docs", "processingdocs", "l5", "processing"], returns: "void", done: true, }, l5docs: { sig: "l5docs", desc: "Open the L5 compatibility docs checklist.", done: true, }, processingdocs: { sig: "processingdocs", desc: "Open the Processing compatibility docs checklist.", done: true, }, l5: { sig: "l5", desc: "Open the L5 try page.", done: true, }, l5learn: { sig: "l5learn", desc: "Open the L5 try page.", done: true, }, processing: { sig: "processing", desc: "Open the Processing try page.", done: true, }, processinglearn: { sig: "processinglearn", desc: "Open the Processing try page.", done: true, }, code: { sig: "code [name]", desc: "Write a piece.", params: [ { name: "name", type: "string", required: false, desc: "Piece name (creates new)" } ], done: true, }, edit: { sig: "edit <piece>", desc: "Edit a piece.", params: [ { name: "piece", type: "string", required: true, desc: "Piece name to edit" } ], done: true, }, source: { sig: "source [piece]", desc: "Download piece code.", params: [ { name: "piece", type: "string", required: false, desc: "Piece name (or current)" } ], done: true, }, email: { sig: "email <address>", desc: "Update your email.", params: [ { name: "address", type: "email", required: true, desc: "New email address" } ], done: true, hidden: false, }, "admin:migrate-": { sig: "admin:migrate-", desc: "", done: false, hidden: true, }, handle: { sig: "handle <name>", desc: "Set your user handle.", params: [ { name: "name", type: "string", required: true, desc: "New handle (alphanumeric)" } ], done: true, }, handles: { sig: "handles", desc: "Browse all user handles.", done: true, }, ul: { sig: "ul", desc: "Alias for `upload`.", examples: ["ul"], returns: "void", done: true, }, upload: { sig: "upload", desc: "Upload your current painting/media.", examples: ["upload"], returns: "void", done: true, }, flip: { sig: "flip", desc: "Flip painting vertically.", examples: ["flip"], returns: "void", done: true, }, flop: { sig: "flop", desc: "Flop painting horizontally.", examples: ["flop"], returns: "void", done: true, }, right: { sig: "right", desc: "Rotate painting right.", examples: ["right"], returns: "void", done: true, }, left: { sig: "left", desc: "Rotate painting left.", examples: ["left"], returns: "void", done: true, }, resize: { sig: "resize <w> [h]", desc: "Resize by x and y pixel #s.", params: [ { name: "w", type: "number", required: true, desc: "Width in pixels" }, { name: "h", type: "number", required: false, desc: "Height (defaults to w)" } ], done: true, }, res: { sig: "res <w> [h]", desc: "Resize by x and y pixel #s.", params: [ { name: "w", type: "number", required: true, desc: "Width in pixels" }, { name: "h", type: "number", required: false, desc: "Height (defaults to w)" } ], done: true, }, dl: { sig: "dl [scale]", desc: "Download your painting.", params: [ { name: "scale", type: "number", required: false, default: 1, desc: "Scale multiplier" } ], done: true, }, download: { sig: "download [scale]", desc: "Download your painting.", params: [ { name: "scale", type: "number", required: false, default: 1, desc: "Scale multiplier" } ], done: true, }, gutter: { sig: "gutter", desc: "", done: false, hidden: true, }, login: { sig: "login", desc: "Log in.", examples: ["login", "hi"], returns: "void", done: true, }, hi: { sig: "hi", desc: "Log in.", done: false, }, signup: { sig: "signup", desc: "Sign up.", examples: ["signup", "imnew"], returns: "void", done: true, }, imnew: { sig: "imnew", desc: "Alias for sign up.", examples: ["imnew"], returns: "void", done: true, }, logout: { sig: "logout", desc: "Log out.", examples: ["logout"], returns: "void", done: true, }, bye: { sig: "bye", desc: "Leave a bot / character or log out.", done: false, }, no: { sig: "no", desc: "Undo painting step.", examples: ["no"], returns: "void", done: true, }, yes: { sig: "yes", desc: "Redo painting step.", examples: ["yes"], returns: "void", done: true, }, nopan: { sig: "nopan", desc: "Center your painting.", examples: ["nopan"], returns: "void", done: true, }, new: { sig: "new", desc: "Start a new painting.", examples: ["new"], returns: "void", done: true, }, "painting:reset": { sig: "painting:reset", desc: "", done: false, hidden: true, }, publish: { sig: "publish", desc: "Publish your last-run piece.", examples: ["publish"], returns: "void", done: true, }, "no!": { sig: "no!", desc: "Abandon your painting.", done: false, }, "3ine:reset": { sig: "3ine:reset", desc: "", done: false, hidden: true, }, dark: { sig: "dark", desc: "Enable dark system theme.", examples: ["dark"], returns: "void", done: true, }, light: { sig: "light", desc: "Enable light system theme.", examples: ["light"], returns: "void", done: true, }, serious: { sig: "serious", desc: "Toggle minimal black & white prompt.", done: false, }, stop: { sig: "stop", desc: "Stop a running merry pipeline.", done: false, }, mug: { sig: "mug [code] [color]", desc: "Preview & order a mug with a painting.", params: [ { name: "code", type: "string", required: false, desc: "Painting code" }, { name: "color", type: "string", required: false, desc: "Mug color (white, black, blue, pink, orange)" }, ], done: true, }, merry: { sig: "merry [duration-]piece ...", desc: "Run pieces in sequence.", params: [ { name: "pieces", type: "string", required: true, desc: "Pieces to chain, optionally with duration prefix" }, ], done: true, }, merryo: { sig: "merryo [duration-]piece ...", desc: "Run pieces in a loop.", params: [ { name: "pieces", type: "string", required: true, desc: "Pieces to chain and loop" }, ], done: true, }, mo: { sig: "mo[.duration] piece ...", desc: "Shorthand for merryo (looping merry).", done: true, }, desktop: { sig: "desktop", desc: "Download the desktop app.", done: false, }, chatgpt: { sig: "chatgpt", desc: "Open ChatGPT.", done: false, hidden: true, }, nws: { sig: "nws", desc: "Open Aesthetic News.", done: false, hidden: true, }, product: { sig: "product [key]", desc: "Switch or view active shop product.", done: false, hidden: true, }, 2022: { sig: "2022", desc: "", done: false, hidden: true, }, connect: { sig: "connect", desc: "", done: false, hidden: true, }, "bgm stop": { sig: "bgm stop", desc: "", done: false, hidden: true, }, "+": { sig: "+", desc: "Make new window.", done: false, //TODO: can this open in a new tab? }, google: { sig: "google <query>", desc: "Search google.", params: [ { name: "query", type: "string", required: true, desc: "Search query" } ], done: true, }, github: { sig: "github", desc: "View AC source code.", done: false, //TODO: can this open in a new tab? }, gmail: { sig: "gmail", desc: "Go to gmail.", done: false, }, gh: { sig: "gh", desc: "View AC source code.", done: false, }, score: { sig: "score", desc: "Open the Aesthetic Computer score.", done: false, }, ucla: { sig: "ucla-syllabus", desc: "UCLA DESMA 28 - Syllabus", done: false, }, "ucla-1": { sig: "ucla-1", desc: "UCLA DESMA 28 - Piece 1", done: false, }, "ucla-2": { sig: "ucla-2", desc: "UCLA DESMA 28 - Piece 2", done: false, }, "ucla-3": { sig: "ucla-3", desc: "UCLA DESMA 28 - Piece 3", done: false, }, "ucla-4": { sig: "ucla-4", desc: "UCLA DESMA 28 - Piece 4", done: false, }, "ucla-4-box": { sig: "ucla-4-box", desc: "UCLA DESMA 28 - Piece 4 (Box)", done: false, }, "ucla-5": { sig: "ucla-5", desc: "UCLA DESMA 28 - Piece 5", done: false, }, "ucla-6": { sig: "ucla-6", desc: "UCLA DESMA 28 - Piece 6", done: false, }, "ucla-7": { sig: "ucla-7", desc: "UCLA DESMA 28 - Piece 7", done: false, }, "ucla-7-dial": { sig: "ucla-7-dial", desc: "UCLA DESMA 28 - Piece 7 (Dial)", done: false, }, "ucla-7-jump": { sig: "ucla-7-jump", desc: "UCLA DESMA 28 - Piece 7 (Jump)", done: false, }, app: { sig: "app", desc: "Get AC in the app store.", done: false, }, ios: { sig: "ios", desc: "Get AC in the app store.", done: false, }, pp: { sig: "pp", desc: "Read the privacy policy.", done: false, }, direct: { sig: "direct", desc: "Aesthetic Inc. corporate updates.", done: false, }, support: { sig: "support", desc: "Go to AC support page.", done: false, }, browserstack: { sig: "browserstack", desc: "Go to AC browserstack.", done: false, hidden: true, }, bs: { sig: "bs", desc: "Go to AC browser stack.", done: false, hidden: true, }, gpt: { sig: "gpt", desc: "", done: false, hidden: true, }, help: { sig: "help", desc: "Join a help channel.", done: false, }, shillball: { sig: "shillball", desc: "", done: false, hidden: true, }, sb: { sig: "sb", desc: "", done: false, hidden: true, }, prod: { sig: "prod", desc: "", done: false, hidden: true, }, local: { sig: "local", desc: "", done: false, hidden: true, }, of: { sig: "of", desc: "View ordfish paintings.", done: false, //TODO: fix swimming count, stuck on 56 }, }, // 🧩 Pieces that can be entered into the prompt. pieces: { 404: { sig: "404", desc: "", done: false, hidden: true, }, about: { sig: "about", desc: "", done: false, hidden: true, }, aframe: { sig: "aframe", desc: "", done: false, hidden: true, }, "a*": { sig: "a*", desc: "A* pathfinding animation.", done: true, }, "alex-row": { sig: "alex-row", desc: "", done: false, hidden: true, }, alphapoet: { sig: "alphapoet", desc: "Generate poems.", done: false, }, angel: { sig: "angel", desc: "Say a prayer.", done: false, }, api: { sig: "api", desc: "", done: false, hidden: true, }, baktok: { sig: "baktok", desc: "Learn to talk backwards.", done: false, }, bills: { sig: "bills", desc: "Service & billing dashboard.", url: "https://bills.aesthetic.computer", done: true, }, balls: { sig: "balls", desc: "", done: false, hidden: true, }, "basic-line-pointer": { sig: "basic-line-pointer", desc: "Drag mouse to point a line.", done: false, }, bgm: { sig: "bgm", desc: "Background music visualizer.", done: false, hidden: false, }, bits: { sig: "bits", desc: "", done: false, hidden: false, }, blank: { sig: "blank", desc: "", done: false, hidden: true, }, "blank-vello": { sig: "blank-vello", desc: "GPU test: Vello WASM renderer (purple lines)", done: true, }, "blank-webgl2": { sig: "blank-webgl2", desc: "GPU test: WebGL2 renderer (cyan lines)", done: true, }, "blank-canvas2d": { sig: "blank-canvas2d", desc: "GPU test: Canvas2D fallback (green lines)", done: true, }, "blank-thorvg": { sig: "blank-thorvg", desc: "GPU test: ThorVG WASM stub (orange lines)", done: true, }, "blank-blend2d": { sig: "blank-blend2d", desc: "GPU test: Blend2D WASM stub (magenta lines)", done: true, }, bleep: { sig: "bleep", desc: "Play notes on a grid. Try adding a #.", done: false, }, blur: { sig: "blur", desc: "Blur pixels.", done: false, }, box: { sig: "box[:color]", desc: "Draw rectangles with brush gestures.", colon: [ { name: "color", values: ["red", "green", "blue", "yellow", "white", "black", "orange", "purple", "pink", "cyan"] }, ], done: true, }, "booted-by": { sig: "booted-by", desc: "Special thanks to early patrons.", done: false, }, // botce: { // sig: "botce", // desc: "Get spiritual advice.", // done: false, // }, boxes: { sig: "boxes", desc: "", done: false, hidden: true, }, boyfriend: { sig: "boyfriend", desc: "He might care.", done: false, }, "brick-breaker": { sig: "brick-breaker", desc: "", done: false, hidden: true, }, brother: { sig: "brother", desc: "Stay out of his room.", done: false, }, brush: { sig: "brush", desc: "", done: false, hidden: true, //TODO: add new templates repository }, bubble: { sig: "bubble", desc: "Make bubble boing. Sound on.", done: false, }, butterflies: { sig: "butterflies", desc: "A 1-bit bitmap reader instrument.", done: false, }, camera: { sig: "camera[:mode]", desc: "Take a picture.", colon: [ { name: "mode", type: "enum", values: ["under", "u"], required: false, desc: "Put camera under drawing" } ], examples: ["camera", "camera:under"], done: true, }, chat: { sig: "chat", desc: "Chat with handles.", done: false, hidden: false, }, chord: { sig: "chord", desc: "", done: false, hidden: false, }, colors: { sig: "colors", desc: "An index of usable colors on AC.", done: false, hidden: false, }, colplay: { sig: "colplay", desc: "Turn colors into notes.", done: false, }, common: { sig: "common", desc: "", done: false, hidden: true, }, clock: { sig: "clock[:divisor] [melody] [sync]", desc: "Musical clock with melody, waveforms, Hz shifts, and parallel tracks.", colon: [ { name: "divisor", type: "number", required: false, default: 1, desc: "Time divisor (0.5 = faster, 2 = slower)" } ], params: [ { name: "melody", type: "string", required: false, desc: "Notes like cdefg, {square}cde, (ceg) (dfa)" }, { name: "sync", type: "enum", values: ["sync"], required: false, desc: "UTC sync mode" } ], examples: ["clock cdefg", "clock:0.5 {square}cdefgab", "clock (ceg) (dfa)", "clock ^cdefg"], done: true }, commits: { sig: "commits", desc: "Browse the live commit history.", done: true, }, crop: { sig: "crop", desc: "Crop your painting.", done: false, }, dad: { sig: "dad", desc: "A dad-icated and punny guy.", done: false, }, debug: { sig: "debug", desc: "", done: false, hidden: true, }, deck: { sig: "deck", desc: "A little slide deck!", done: false, hidden: false, }, decode: { sig: "decode", desc: "Reveal an encoded message. See encode.", done: false, }, "delete-erase-and-forget-me": { sig: "delete-erase-and-forget-me", desc: "Delete your account.", done: false, }, // 🔗 External Links github: { sig: "github", desc: "Open the AC Tangled repo (legacy alias).", done: true, }, gh: { sig: "gh", desc: "Open the AC Tangled repo (legacy alias).", done: true, }, gmail: { sig: "gmail", desc: "Open Gmail.", done: true, }, agc: { sig: "agc", desc: "Open ACG at MIT Media Lab.", done: true, }, "ucla-syllabus": { sig: "ucla-syllabus", desc: "Open the UCLA syllabus.", done: true, }, demo: { sig: "demo", desc: "Watch a demo of AC.", done: false, hidden: false, }, description: { sig: "description", desc: "", done: false, hidden: true, }, digitpain0: { sig: "digitpain0", desc: "", done: false, hidden: true, }, digitpain1: { sig: "digitpain1", desc: "", done: false, hidden: true, }, digitpain2: { sig: "digitpain2", desc: "", done: false, hidden: true, }, digitpain3: { sig: "digitpain3", desc: "", done: false, hidden: true, }, docgen: { sig: "docgen", desc: "", done: false, hidden: true, }, dolls: { sig: "dolls", desc: "", done: false, hidden: true, }, doodle: { sig: "doodle", desc: "", done: false, hidden: true, }, download: { sig: "download", desc: "Download your painting.", done: false, }, drawings: { sig: "drawings", desc: "", done: false, hidden: true, }, dync: { sig: "dync", desc: "", done: false, hidden: true, }, encode: { sig: "encode", desc: "Encrypt a secret message.", done: false, }, ff: { sig: "ff", desc: "View the Freaky Flowers collection.", done: false, }, field: { sig: "field", desc: "Play in the field with others.", done: false, }, fill: { sig: "fill", desc: "Fill with solid color.", done: false, }, fly: { sig: "fly", desc: "", done: false, hidden: true, }, "freaky-flowers": { sig: "freaky-flowers", desc: "View the Freaky Flowers collection.", done: false, }, gargoyle: { sig: "gargoyle", desc: "A steadfast guardian.", done: false, }, girlfriend: { sig: "girlfriend", desc: "Caring confidant.", done: false, }, give: { sig: "give", desc: "Support aesthetic.computer.", done: true, }, gostop: { sig: "gostop", desc: "Stop and go.", done: false, }, handprint: { sig: "handprint", desc: "Track your hand.", done: false, }, handtime: { sig: "handtime", desc: "Draw with a pinch.", done: false, }, "hell_-world": { sig: "hell_-world", desc: "View the hell_ world collection.", done: false, //TODO: doesn't seem to work? }, hha: { sig: "hha", desc: "Happy Hands Assembler", done: false, hidden: false, }, horizon: { sig: "horizon", desc: "Walk on the horizon.", done: false, }, husband: { sig: "husband", desc: "Absent-minded but well-meaning.", done: false, }, hw: { sig: "hw", desc: "View the hell_ world paintings.", done: false, }, icon: { sig: "icon", desc: "", done: false, hidden: true, }, images: { sig: "images", desc: "", done: false, hidden: true, }, imessage: { sig: "imessage", desc: "", done: false, hidden: true, }, i: { sig: "i", desc: "", done: false, hidden: true, }, kid: { sig: "kid", desc: "Maybe a unicorn.", done: false, }, lang: { sig: "lang", desc: "", done: false, hidden: true, }, learn: { sig: "learn", desc: "", done: false, hidden: true, }, 'laer-klokken': { sig: "laer-klokken", desc: "Learn the 'clock'! (alias of laklok)", done: false }, laklok: { sig: "laklok", desc: "Learn the 'clock'! (laklok.com)", done: false }, "legacy-prompt": { sig: "legacy-prompt", desc: "", done: false, hidden: true, }, liar: { sig: "liar", desc: "Incredibly honest and trustworthy.", done: false, }, '3-kidlisp-tests': { sig: "3-kidlisp-tests", desc: "Tests of a new language.", done: false, }, 'fia-birthday': { sig: "fia-birthday", desc: "Come to Fía's birthday!", done: false, }, "kaos-pad-template": { sig: "kaos-pad-template", desc: "A simple multi-touch XY pad template.", done: false, }, line: { sig: "line[:thickness]", desc: "Draw lines with your finger.", colon: [ { name: "thickness", type: "number", required: false, default: 1, desc: "Line width in pixels" } ], examples: ["line", "line:2", "line:5"], done: true, }, list: { sig: "list", desc: "View all commands.", done: false, }, "lmn-flower": { sig: "lmn-flower", desc: "", done: false, hidden: true, }, "lmn-petal": { sig: "lmn-petal", desc: "", done: false, hidden: true, }, "login-pattern": { sig: "login-pattern", desc: "", done: false, hidden: true, }, "login-wait": { sig: "login-wait", desc: "", done: false, hidden: true, }, m2w2: { sig: "m2w2", desc: "Music 2 Whistlegraph 2.", done: false, }, melody: { sig: "melody", desc: "Plays a sequence.", done: false, }, metronome: { sig: "metronome", desc: "Keep time.", done: false, }, microphone: { sig: "microphone", desc: "", done: false, hidden: true, //TODO: try to fix }, mom: { sig: "mom", desc: "Why does she love this way?", done: false, }, mood: { sig: "mood", desc: "Set your mood.", done: false, }, moods: { sig: "moods", desc: "Read all the moods.", done: false, }, multipen: { sig: "multipen", desc: "", done: false, hidden: true, }, nail: { sig: "nail", desc: "", done: false, hidden: true, }, noise: { sig: "noise", desc: "Some nice noise.", done: false, }, news: { sig: "news", desc: "Community news and links.", done: true, }, nopaint: { sig: "nopaint", desc: "", done: false, hidden: true, //TODO: shouldnt this go to nopaint site? nopaint.art }, numrank: { sig: "numrank[:seconds]", desc: "60-second mental math sprint: type the answer and it advances itself. Problems start tiny and grow as you rank up mid-run — go through the ranks to earn the complexity. Misses show the mental route; so does every solve.", colon: [ { name: "seconds", type: "number", required: false, desc: "custom run length 10-600 (non-60s runs are unranked)" }, ], examples: ["numrank", "numrank:120"], done: true, }, numbnom: { sig: "numbnom[:words|:spanish]", desc: "Number Munchers-style grid game: munch squares that match the math rule (odds, evens, primes, multiples, factors) on a beat, dodge troggles.", colon: [ { name: "mode", type: "enum", values: ["words", "spanish"], required: false, desc: "play the word edition (engnom) or spanish edition (mexinom) instead of numbers" }, ], examples: ["numbnom", "numbnom:words", "numbnom:spanish"], done: true, }, engnom: { sig: "engnom", desc: "Word Munchers-style grid game: munch squares matching the word rule (animals, colors, fruits, rhymes, doubles). English word edition of numbnom.", examples: ["engnom"], done: true, }, mexinom: { sig: "mexinom", desc: "Spanish (Mexican-flavored) word Munchers: comida, animales, colores, frutas, fiesta — speaks the English translation aloud as you munch.", examples: ["mexinom"], done: true, }, dannom: { sig: "dannom", desc: "Danish word Munchers: mad, dyr, farver, frugt, hygge — speaks the English translation aloud as you munch.", examples: ["dannom"], done: true, }, rusnom: { sig: "rusnom", desc: "Russian word Munchers: еда, животные, цвета, фрукты, природа — speaks the English translation aloud as you munch.", examples: ["rusnom"], done: true, }, notenom: { sig: "notenom", desc: "Musical Munchers-style grid game: munch the note squares that match the rule (C major, A minor, sharps, C chord, high, low) on the beat — each bite plays its note.", examples: ["notenom"], done: true, }, notepat: { sig: "notepat[:wave][:octave] [melody...]", desc: "A melodic keyboard instrument.", // Colon params: command:opt1:opt2 → colon[0], colon[1] colon: [ { name: "wave", type: "enum", values: ["sine", "square", "triangle", "sawtooth", "noise"], required: false, default: "sine" }, { name: "octave", type: "number", values: [1, 2, 3, 4, 5, 6, 7, 8, 9], required: false, default: 4 } ], // Space params: command arg1 arg2 → params[0], params[1] params: [ { name: "melody", type: "string", required: false, desc: "Melody in note:word format (e.g. C:twin- C:-kle)" } ], examples: ["notepat", "notepat:square", "notepat:sine:5", "notepat twinkle"], done: true, }, stample: { sig: "stample", desc: "A sampling instrument.", done: false, }, old: { sig: "old", desc: "", done: false, hidden: true, }, oldpull: { sig: "oldpull", desc: "", done: false, hidden: true, }, oldwand: { sig: "oldwand", desc: "", done: false, hidden: true, }, ordfish: { sig: "ordfish", desc: "View the Ordfish painting collection.", done: false, }, ordsy: { sig: "ordsy", desc: "", done: false, hidden: true, }, oval: { sig: "oval", desc: "Draw an oval.", done: false, }, painting: { sig: "painting", desc: "View your current painting's steps.", done: false, }, paint: { sig: "paint", desc: "Generate marks by adding instructions.", done: false, }, paste: { sig: "paste", desc: "Paste an image from your library.", done: false, }, perf: { sig: "perf", desc: "", done: false, hidden: true, }, phand: { sig: "phand", desc: "", done: false, hidden: true, }, pip: { sig: "pip", desc: "", done: false, hidden: true, }, play: { sig: "play", desc: "", done: false, hidden: true, }, pline: { sig: "pline", desc: "", done: false, hidden: true, }, plot: { sig: "plot", desc: "Plot vector graphics.", done: false, }, pond: { sig: "pond", desc: "Draw ripples with others.", done: false, }, profile: { sig: "profile", desc: "Go to your profile or enter another user's.", done: false, }, prompt: { sig: "prompt", desc: "Go to the prompt.", done: false, }, prutti: { sig: "prutti", desc: "Genius old man rants.", done: false, }, ptt: { sig: "ptt", desc: "", done: false, hidden: true, }, pull: { sig: "pull", desc: "", done: false, hidden: true, }, rain: { sig: "rain", desc: "A nice rain animation.", done: false, }, rattle: { sig: "rattle", desc: "", done: false, }, rect: { sig: "rect", desc: "Draw a rectangle.", done: false, }, "run&gun": { sig: "run&gun", desc: "", done: false, hidden: true, }, sage: { sig: "sage", desc: "Paths move across the screen.", done: false, }, sb: { sig: "sb", desc: "", done: false, hidden: true, }, "scawy-snake": { sig: "scawy-snake", desc: "The classic game of snake.", done: false, //TODO: need replay button }, seashells: { sig: "seashells", desc: "A multi-touch bytebeat instrument.", done: false, }, screenshots: { sig: "screenshots", desc: "", done: false, }, screentest: { sig: "screentest", desc: "", done: false, hidden: true, }, selfie: { sig: "selfie", desc: "Open the front camera.", done: false, }, sfx: { sig: "sfx", desc: "", done: false, hidden: true, }, shape: { sig: "shape", desc: "Draw a freehand polygon.", done: false, }, shop: { sig: "shop", desc: "Order artwork and services from @jeffrey.", done: true, }, share: { sig: "share", desc: "Generate a QR code to share.", done: false, }, signature: { sig: "signature", desc: "", done: false, hidden: true, }, sign: { sig: "sign", desc: "", done: false, hidden: true, }, sing: { sig: "sing", desc: "", done: false, hidden: true, }, sister: { sig: "sister", desc: "Don't steal her makeup.", done: false, }, slip: { sig: "slip", desc: "A two octave slide instrument.", done: false, }, smear: { sig: "smear", desc: "Move pixels around.", done: false, }, sno: { sig: "sno", desc: "Walk around and fall asleep.", done: false, }, song: { sig: "song", desc: "Learn a song.", done: false, //TODO: adjust song , default octave thing }, "sotce-net": { sig: "sotce-net", desc: "diaries (work in progress)", done: false, hidden: true, }, // "sparkle-brush": { // sig: "sparkle-brush", // desc: "Paint with Maya's really fun brush.", // done: false, // }, sparkle: { sig: "sparkle", desc: "Paint with @maya's really fun brush.", done: false, }, spline: { sig: "spline", desc: "A springy wave.", done: false, }, spray: { sig: "spray", desc: "", done: false, hidden: true, }, sprinkles: { sig: "sprinkles", desc: "Watch the pretty sprinkles.", done: false, }, sprite: { sig: "sprite", desc: "", done: false, hidden: true, }, squaresong: { sig: "squaresong", desc: "Mmmm squaresong.", done: false, }, stage: { sig: "stage", desc: "", done: false, hidden: true, }, // "stage-setup.html": { // sig: "stage-setup.html", // desc: "", // done: false, // hidden: true, // }, staka: { sig: "staka", desc: "", done: false, hidden: true, }, starfield: { sig: "starfield", desc: "A celestial experience.", done: false, }, test: { sig: "test", desc: "", done: false, hidden: true, }, textfence: { sig: "textfence", desc: "A tiny play by @jeffrey and @georgica.", done: false, }, tone: { sig: "tone[:wave] [frequency]", desc: "Listen to a tone.", colon: [ { name: "wave", type: "enum", values: ["sine", "triangle", "square", "sawtooth", "cycle"], required: false, default: "sine" } ], params: [ { name: "frequency", type: "number", required: false, desc: "Tone frequency in Hz (50-4000)" } ], examples: ["tone", "tone 440", "tone:square 880", "tone:cycle"], done: true, }, toss: { sig: "toss[:wave][:tempo]", desc: "Play microtonal oscillators.", colon: [ { name: "wave", type: "enum", values: ["sine", "square", "triangle", "sawtooth"], required: false, default: "sine" }, { name: "tempo", type: "number", values: [60, 80, 100, 120, 140, 160], required: false, default: 120 } ], done: true, }, tracker: { sig: "tracker", desc: "A simple music tracker.", done: false, }, udp: { sig: "udp", desc: "", done: false, hidden: true, }, uke: { sig: "uke", desc: "Standard note meter.", done: false, }, valbear: { sig: "valbear", desc: "Make a V-Day card.", done: false, }, vary: { sig: "vary", desc: "", done: false, hidden: true, }, video: { sig: "video", desc: "", done: false, hidden: true, }, wand: { sig: "wand", desc: "Sculpt in XR.", done: false, hidden: false, }, wallet: { sig: "wallet", desc: "View your Tezos wallet.", done: true, }, wave: { sig: "wave", desc: "Wave using your hand.", done: false, hidden: false, }, wg: { sig: "wg", desc: "Watch and learn whistlegraphs.", done: false, }, wgr: { sig: "wgr", desc: "Whistlegraph recorder.", done: false, hidden: false, }, whistlegraph: { sig: "whistlegraph", desc: "Whistlegraph index.", done: false, hidden: false, }, whistle: { sig: "whistle", desc: "Convert whistles to sine waves.", done: false, }, wife: { sig: "wife", desc: "Get ready for chores.", done: false, }, wipe: { sig: "wipe", desc: "", done: false, hidden: false, //TODO: doesn't seem to clear the painting }, word: { sig: "word", desc: "Add text to your painting.", done: false, }, zoom: { sig: "zoom", desc: "", done: false, hidden: true, }, }, };
const titleFromSlug = (slug = "") => String(slug) .replaceAll("-", " ") .replaceAll("_", " ") .replace(/\s+/g, " ") .trim();
const fillCommandDocs = (collection, kind) => { keys(collection || {}).forEach((name) => { const entry = collection[name]; if (!entry) return;
if (!String(entry.sig || "").trim()) { entry.sig = name; }
if (!String(entry.desc || "").trim()) { const shared = getCommandDescription(name); if (shared) { entry.desc = shared; return; }
const pretty = titleFromSlug(name); if (kind === "piece") { entry.desc = pretty ? `Open the ${pretty} piece.` : "Open this piece."; } else { entry.desc = pretty ? `Run the ${pretty} prompt command.` : "Run this prompt command."; } } }); };
// Auto-register any disk piece not already in the curated `pieces` map. mergeAutoPieces(docs.pieces);
fillCommandDocs(docs.prompts, "prompt"); fillCommandDocs(docs.pieces, "piece");
const page = html` <html> <head> <link rel="icon" href="https://${event.headers["host"]}/icon/128x128/prompt.png" type="image/png" /> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>$name · Aesthetic Computer</title> <style> ::-webkit-scrollbar { display: none; } html { cursor: url("/aesthetic.computer/cursors/precise.svg") 12 12, auto; } body { margin: 0; font-size: 22px; font-family: monospace; -webkit-text-size-adjust: none; } body.doc { overflow-x: hidden; } h1 a, h1 a:visited { color: inherit; cursor: inherit; text-decoration: none; } .links a[data-done=false] { /* text-decoration: line-through; */ } h1 { font-weight: normal; font-size: 22px; margin: 0; position: fixed; } h1:before { content: ""; height: 2.5em; width: 100vw; position: absolute; z-index: -1; top: -16px; left: -16px; } h1[data-done=false]:after { content: "wip"; color: yellow; background: maroon; position: absolute; right: -32px; top: 0px; display: block; font-size: 50%; padding: 0px 3px 2px 3px; border-radius: 2px; } h2 { font-weight: normal; font-size: 18px; } iframe { position: fixed; top: 0; left: 0; width: 100%; height: 100%; border: none; z-index: -1; opacity: 0.5; } section { margin: 12px 16px 12px 16px; } #pals { position: fixed; bottom: 5px; right: 16px; user-select: none; } p { margin-top: 0; } #command-list, #docs-welcome { margin-top: 1.5em; } #docs-welcome { padding: 0; } #title { display: inline-block; } #title.block:after { content: "_"; position: absolute; top: 6px; right: -16px; line-height: 20px; color: rgb(205, 92, 155); background-color: rgb(205, 92, 155); } a.prompt, a.prompt:visited { color: white; text-decoration: none; cursor: inherit; } a.prompt:hover { color: rgb(205, 92, 155); } small { opacity: 0.25; font-size: 100%; } .code-doc { padding-top: 2.5em; font-size: 65%; padding-left: 1px; } .doc-body { margin-top: 1em; } .doc-body table { border-collapse: collapse; width: 100%; max-width: 960px; margin-top: 0.75em; margin-bottom: 0.75em; } .doc-body th, .doc-body td { text-align: left; border: 1px solid rgba(255, 255, 255, 0.2); padding: 0.4em 0.5em; vertical-align: top; } .doc-body ul { padding-left: 1.2em; margin-top: 0.6em; margin-bottom: 0.6em; } .doc-body h3 { margin: 0.8em 0 0.4em; font-size: 1em; font-weight: normal; } .status-badge { display: inline-block; padding: 0.1em 0.45em; border-radius: 0.35em; font-size: 0.9em; text-transform: lowercase; border: 1px solid currentColor; white-space: nowrap; } .status-done { color: #4ade80; } .status-in-progress { color: #fbbf24; } .status-planned { color: #94a3b8; } .doc-examples { display: grid; gap: 0.75em; } .doc-example { border: 1px solid rgba(255, 255, 255, 0.2); padding: 0.45em 0.6em; border-radius: 0.35em; } .code-doc-welcome { padding-top: 2.75em; font-size: 65%; padding-bottom: 3em; } .lane-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); } .lane-card { border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 8px; padding: 10px 11px 11px 11px; } .lane-card.lane-mjs { border-color: rgba(124, 212, 255, 0.45); background: linear-gradient(180deg, rgba(26, 60, 86, 0.2), rgba(26, 60, 86, 0.05)); } .lane-card.lane-l5 { border-color: rgba(245, 213, 66, 0.5); background: linear-gradient(180deg, rgba(90, 75, 0, 0.22), rgba(90, 75, 0, 0.08)); } .lane-card.lane-processing { border-color: rgba(86, 220, 255, 0.5); background: linear-gradient(180deg, rgba(9, 71, 92, 0.22), rgba(9, 71, 92, 0.08)); } .lane-card.lane-kidlisp { border-color: rgba(102, 230, 187, 0.45); background: linear-gradient(180deg, rgba(16, 74, 56, 0.2), rgba(16, 74, 56, 0.06)); } .lane-card.lane-prompts { border-color: rgba(248, 168, 78, 0.45); background: linear-gradient(180deg, rgba(92, 41, 10, 0.2), rgba(92, 41, 10, 0.06)); } .lane-card.lane-pieces { border-color: rgba(230, 125, 175, 0.42); background: linear-gradient(180deg, rgba(78, 24, 63, 0.2), rgba(78, 24, 63, 0.06)); } .lane-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 0.45em; } .lane-title { font-size: 1.1em; } .lane-mjs .lane-title { color: #9bddff; } .lane-l5 .lane-title { color: #ffe37a; } .lane-processing .lane-title { color: #93ecff; } .lane-kidlisp .lane-title { color: #9ff2d4; } .lane-prompts .lane-title { color: #ffd29a; } .lane-pieces .lane-title { color: #f3b3d3; } .lane-count { font-size: 0.92em; opacity: 0.75; white-space: nowrap; } .lane-subtitle { font-size: 0.95em; opacity: 0.9; margin-bottom: 0.65em; } .lane-section { margin-top: 0.75em; } .lane-section h3 { margin: 0 0 0.25em 0; font-size: 0.95em; font-weight: normal; opacity: 0.85; } .doc-meta { margin-top: 0.3em; font-size: 0.92em; opacity: 0.8; display: flex; flex-wrap: wrap; gap: 8px; } .doc-status { text-transform: lowercase; } .doc-grid { display: grid; gap: 11px; } .doc-block { border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; padding: 0.55em 0.7em; } .doc-block h2 { margin: 0 0 0.4em; font-size: 1em; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.85; } .doc-preview { border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 8px; overflow: hidden; } .doc-preview iframe { position: static !important; z-index: auto !important; opacity: 1 !important; width: 100% !important; height: 100% !important; min-height: 280px; border: 0; display: block; } .doc-preview-head { padding: 0.45em 0.65em; font-size: 0.9em; border-bottom: 1px solid rgba(255, 255, 255, 0.2); } .doc-preview-body { min-height: 280px; } .doc-preview-links { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 0.65em; } .doc-preview-links a { text-decoration: none; padding: 3px 6px; border: 1px solid rgba(255, 255, 255, 0.26); border-radius: 6px; } .doc-preview-links button { text-decoration: none; padding: 3px 6px; border: 1px solid rgba(255, 255, 255, 0.26); border-radius: 6px; background: transparent; color: inherit; font: inherit; cursor: pointer; } pre { margin-top: 1em; margin-bottom: 1em; } pre code.hljs { padding: 0.2em 0em; position: relative; overflow-x: visible; } pre code.hljs:after { content: ""; height: 100%; top: 0; right: -16px; width: 16; background-color: #f3f3f3; position: absolute; } pre code.hljs:before { content: ""; height: 100%; top: 0; left: -16px; width: 16px; background-color: #f3f3f3; position: absolute; } .links a { text-decoration: none; /* border: 1px solid; */ padding: 4px; } @media (prefers-color-scheme: dark) { body { background-color: rgb(64, 56, 74); color: rgba(255, 255, 255, 0.85); } h1 a:hover { color: rgb(205, 92, 155); } h1:before { background-image: linear-gradient(to bottom, rgba(64, 56, 74, 0.75) 80%, transparent); } .hljs-title.function_ { color: rgb(225, 105, 175); } .hljs { color: white; } pre code.hljs, pre code.hljs:after, pre code.hljs:before { background: rgb(25, 0, 25); } .links a { color: rgb(205, 92, 155); } .links a.top-level { color: rgb(92, 205, 155); } } @media (prefers-color-scheme: light) { body { background-color: rgba(244, 235, 250); } .doc-body th, .doc-body td, .doc-example, .lane-card, .doc-block, .doc-preview, .doc-preview-head, .doc-preview-links a, .doc-preview-links button { border-color: rgba(0, 0, 0, 0.2); } .lane-card.lane-mjs { background: linear-gradient(180deg, rgba(206, 237, 255, 0.8), rgba(238, 248, 255, 0.7)); border-color: rgba(49, 133, 173, 0.55); } .lane-card.lane-l5 { background: linear-gradient(180deg, rgba(255, 247, 185, 0.85), rgba(255, 252, 220, 0.7)); border-color: rgba(179, 149, 37, 0.55); } .lane-card.lane-processing { background: linear-gradient(180deg, rgba(205, 244, 255, 0.85), rgba(233, 251, 255, 0.72)); border-color: rgba(37, 137, 179, 0.55); } .lane-card.lane-kidlisp { background: linear-gradient(180deg, rgba(204, 245, 230, 0.8), rgba(233, 252, 245, 0.7)); border-color: rgba(41, 141, 102, 0.5); } .lane-card.lane-prompts { background: linear-gradient(180deg, rgba(255, 227, 194, 0.82), rgba(255, 244, 226, 0.72)); border-color: rgba(176, 108, 41, 0.5); } .lane-card.lane-pieces { background: linear-gradient(180deg, rgba(255, 216, 236, 0.82), rgba(255, 238, 247, 0.72)); border-color: rgba(161, 73, 115, 0.46); } .lane-mjs .lane-title { color: rgb(20, 90, 128); } .lane-l5 .lane-title { color: rgb(130, 102, 0); } .lane-processing .lane-title { color: rgb(8, 101, 138); } .lane-kidlisp .lane-title { color: rgb(24, 112, 76); } .lane-prompts .lane-title { color: rgb(132, 76, 18); } .lane-pieces .lane-title { color: rgb(125, 43, 83); } a.prompt, a.prompt:visited { color: rgb(64, 56, 74); } h1 a:hover, a.prompt:hover { color: rgb(205, 92, 155); } h1:before { background-image: linear-gradient(to bottom, rgba(244, 235, 250, 0.75) 80%, transparent); } .hljs-title.function_ { color: rgb(205, 92, 155); } .links a { color: rgb(180, 72, 135); } .links a.top-level { color: green; } } .nolink { user-select: none; pointer-events: none; } </style> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css" /> <script nonce="$nonce" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script> <script nonce="$nonce"> hljs.highlightAll(); </script> </head> <body$bodyclass> <section>$content</section> <img id="pals" width="64" src="https://${event.headers["host"]}/purple-pals.svg" /> <script nonce="$nonce"> const titleLink = document.querySelector("#title a"); if (window.self !== window.top && titleLink.innerText === "docs") { title.classList.add("nolink"); } const docPreviewFrame = document.getElementById("doc-preview-frame"); const docPreviewBase = docPreviewFrame?.dataset?.src || docPreviewFrame?.src || ""; function withFreshTimestamp(url) { if (!url) return ""; try { const next = new URL(url, window.location.origin); next.searchParams.set("t", Date.now()); return next.toString(); } catch (_err) { const joiner = url.includes("?") ? "&" : "?"; return url + joiner + "t=" + Date.now(); } } window.runDocPreview = function runDocPreview() { if (!docPreviewFrame) return; docPreviewFrame.src = withFreshTimestamp(docPreviewFrame.src || docPreviewBase); }; window.resetDocPreview = function resetDocPreview() { if (!docPreviewFrame || !docPreviewBase) return; docPreviewFrame.src = withFreshTimestamp(docPreviewBase); }; // 🌠 Live editing (while developing aesthetic locally) if (${dev}) { var socket = new WebSocket("ws://localhost:8889");
socket.onopen = function (event) { console.log("🟢 Live editing enabled."); };
socket.onmessage = function (event) { const msg = JSON.parse(event.data); console.log("🟡 Message from server:", msg); if (msg.type === "reload") document.location.reload(); };
socket.onerror = function (event) { console.error("🔴 Error observed:", event); };
socket.onclose = function (event) { console.log("🔴 Live editing disabled."); }; } </script> </body> </html>`.trim();
const content = ` <h1 data-done="$done" id="title"><a href="/docs">$name</a></h1> <div class="code-doc"> <pre><code class="language-$lang">$sig</code></pre> <p>$desc</p> <div class="doc-body">$body</div> </div> `.trim();
function escapeHTML(value) { return String(value || "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); }
function normalizeStatus(done) { if (done === true || done === "done") return "done"; if (done === "in-progress") return "in-progress"; return "planned"; }
function statusBadge(done) { const status = normalizeStatus(done); return `<span class="status-badge status-${status}">${status.replace("-", " ")}</span>`; }
function familyFromCategory(category) { if (category === "l5") return "L5 / Lua API"; if (category === "processing") return "Processing / Java API"; if (category === "kidlisp") return "KidLisp / Language API"; if (category === "prompts") return "Prompt Commands"; if (category === "pieces") return "Pieces"; if (category === "mjs") return "MJS / AC Piece API"; return "MJS / AC Piece API"; }
function parseSigName(sig, fallback = "") { const source = String(sig || "").trim(); if (!source) return fallback; const match = source.match(/^([A-Za-z0-9_.$]+)\s*\(/); if (match?.[1]) return match[1]; return fallback || source; }
function playgroundUrlForCategory(category) { if (category === "l5") return `${AC_ORIGIN}/l5`; if (category === "processing") return `${AC_ORIGIN}/processing`; return `${AC_ORIGIN}/prompt`; }
function previewUrlForDoc(doc, category, word) { const flags = `nogap=true&nolabel=true&noauth=true&popout=true&t=${Date.now()}`; if (doc?.example?.url) return doc.example.url; if (doc?.example?.type === "piece" && doc.example.entry) { return `${AC_ORIGIN}/${encodeURIComponent(doc.example.entry)}?${flags}`; } if (category === "pieces") { return `${AC_ORIGIN}/${encodeURIComponent(word)}?${flags}`; } if (category === "prompts") { return `${AC_ORIGIN}/prompt~${encodeURIComponent(word)}?${flags}`; } if (category === "l5") { return `${AC_ORIGIN}/l5-hello.lua?${flags}`; } if (category === "processing") { return `${AC_ORIGIN}/processing?t=${Date.now()}`; } if (category === "kidlisp") { return `${AC_ORIGIN}/kidlisp?${flags}`; } if (Array.isArray(doc?.examples) && doc.examples.length) { const first = String(doc.examples[0]).replace(/^prompt~/, ""); return `${AC_ORIGIN}/prompt~${encodeURIComponent(first)}?${flags}`; } const guess = parseSigName(doc?.sig, word); return `${AC_ORIGIN}/prompt~${encodeURIComponent(guess)}?${flags}`; }
function renderParamsSection(doc) { if (!Array.isArray(doc?.params) || doc.params.length === 0) { return `<p>Parameters are not fully documented yet.</p>`; } return ` <table> <thead> <tr> <th>Name</th> <th>Type</th> <th>Required</th> <th>Description</th> </tr> </thead> <tbody> ${doc.params .map((param) => { const required = param.required ? "yes" : "no"; const type = Array.isArray(param.values) ? `${param.type || "enum"}: ${param.values.join(", ")}` : (param.type || ""); return ` <tr> <td><code>${escapeHTML(param.name || "")}</code></td> <td>${escapeHTML(type)}</td> <td>${required}</td> <td>${escapeHTML(param.desc || "")}</td> </tr> `; }) .join("")} </tbody> </table> `; }
function renderExamplesSection(doc, category, word) { const playgroundUrl = playgroundUrlForCategory(category); const examples = Array.isArray(doc?.examples) ? doc.examples : []; if (!examples.length) { return ` <p>No explicit examples yet.</p> <p><a href="${playgroundUrl}">Open playground to experiment live</a>.</p> `; }
return ` <ul> ${examples .map((example) => { const clean = String(example).replace(/^prompt~/, ""); return `<li><a href="${AC_ORIGIN}/prompt~${encodeURIComponent(clean)}">${escapeHTML(clean)}</a></li>`; }) .join("")} </ul> <p><a href="${playgroundUrl}">Open playground</a> · <a href="${AC_ORIGIN}/docs/${category}:${word}">Permalink</a></p> `; }
function renderDocContent(category, word, doc) { const lang = doc?.lang || (category === "l5" ? "lua" : category === "processing" ? "java" : category === "kidlisp" ? "lisp" : "javascript"); const previewSrc = previewUrlForDoc(doc, category, word); const family = familyFromCategory(category); const status = normalizeStatus(doc?.done); const returns = doc?.returns || "Not specified."; const notes = doc?.notes || ""; const desc = doc?.desc || "Description pending."; const sig = doc?.sig || `${word}(...)`; const playgroundUrl = playgroundUrlForCategory(category);
return ` <h1 data-done="${escapeHTML(doc?.done)}" id="title"><a href="/docs">${escapeHTML(word)}</a></h1> <div class="code-doc"> <div class="doc-meta"> <span><strong>${escapeHTML(family)}</strong></span> <span>/</span> <span><code>${escapeHTML(category)}</code></span> <span>/</span> <span class="doc-status">${statusBadge(status)}</span> </div> <pre><code class="language-${escapeHTML(lang)}">${escapeHTML(sig)}</code></pre> <p>${escapeHTML(desc)}</p> <div class="doc-grid"> <div class="doc-block doc-body"> <h2>Parameters</h2> ${renderParamsSection(doc)} </div> <div class="doc-block doc-body"> <h2>Returns</h2> <p><code>${escapeHTML(returns)}</code></p> </div> <div class="doc-block doc-body"> <h2>Examples</h2> ${renderExamplesSection(doc, category, word)} </div> <div class="doc-block doc-body"> <h2>Runtime Notes</h2> ${notes ? `<p>${escapeHTML(notes)}</p>` : `<p>No additional runtime notes yet.</p>`} </div> <div class="doc-block doc-body"> <h2>Details</h2> ${doc?.body || `<p>No additional details yet.</p>`} </div> <div class="doc-block"> <h2>Live Preview</h2> <div class="doc-preview"> <div class="doc-preview-head">Embedded AC preview</div> <div class="doc-preview-body"> <iframe id="doc-preview-frame" data-src="${previewSrc}" src="${previewSrc}" title="${escapeHTML(word)} preview" loading="lazy" allow="autoplay; clipboard-write" ></iframe> </div> </div> <div class="doc-preview-links"> <button type="button" onclick="runDocPreview()">Run</button> <button type="button" onclick="resetDocPreview()">Reset</button> <a href="${previewSrc}" target="_blank" rel="noopener">Open preview</a> <a href="${playgroundUrl}" target="_blank" rel="noopener">Open playground</a> <a href="${AC_ORIGIN}/docs/${category}:${word}" target="_blank" rel="noopener">Open doc</a> </div> </div> </div> </div> `; }
function renderCollectionIndex(category, title, subtitle) { const collection = docs[category] || {}; const count = collectionCounts(collection); const links = keys(collection) .filter((name) => !collection[name]?.hidden) .sort() .map((name) => { const status = normalizeStatus(collection[name]?.done); return `<a href="/docs/${category}:${name}" data-done="${collection[name]?.done}" title="${status}">${name}</a>`; }) .join(" ");
return ` <h1 id="title"><a href="/docs">${escapeHTML(title)}</a></h1> <div class="code-doc"> <div class="doc-meta"> <span><strong>${escapeHTML(title)}</strong></span> <span>/</span> <span><code>${escapeHTML(category)}</code></span> <span>/</span> <span class="doc-status">${statusBadge("in-progress")}</span> </div> <p>${escapeHTML(subtitle)}</p> <p>${count.done} done · ${count.inProgress} in progress · ${count.planned} planned</p> <div class="lane-section"> <span class="links">${links}</span> </div> </div> `; }
const commands = { ...docs.prompts, ...docs.pieces }; let commandList = ""; // ➿ Loop through all commands and generate HTML. keys(commands) .sort() .forEach((c) => { commandList += ` <a class="prompt" href="https://${event.headers["host"]}/prompt~${c}" onclick="send('docs:${c}')">${c}</a> <small>${commands[c].desc}</small><br> `; });
// Generate doc links for a given category. function genLinks(category) { return keys(docs.api[category] || []) .map( (k) => `<a href="/docs/${category}:${k}" data-done="${docs.api[category][k].done}">${k}</a>`, ) .join(" "); }
function laneCounts(categories) { let total = 0; let done = 0; let inProgress = 0; categories.forEach((category) => { keys(docs.api[category] || {}).forEach((word) => { total++; const status = normalizeStatus(docs.api[category][word]?.done); if (status === "done") done++; if (status === "in-progress") inProgress++; }); }); const planned = total - done - inProgress; return { total, done, inProgress, planned }; }
function laneCountLabel(categories) { const count = laneCounts(categories); return `${count.done} done · ${count.inProgress} in progress · ${count.planned} planned`; }
function collectionCounts(collection) { let total = 0; let done = 0; let inProgress = 0; keys(collection || {}).forEach((word) => { total++; const status = normalizeStatus(collection[word]?.done); if (status === "done") done++; if (status === "in-progress") inProgress++; }); const planned = total - done - inProgress; return { total, done, inProgress, planned }; }
function collectionCountLabel(collection) { const count = collectionCounts(collection); return `${count.done} done · ${count.inProgress} in progress · ${count.planned} planned`; }
function genCollectionLinks(collectionName, limit = 32) { const collection = docs[collectionName] || {}; return keys(collection) .filter((name) => !collection[name]?.hidden) .sort() .slice(0, limit) .map((name) => `<a href="/docs/${collectionName}:${name}" data-done="${collection[name]?.done}">${name}</a>`) .join(" "); }
const indexContent = html` <h1 id="title"> <a href="https://${event.headers["host"]}" onclick="if (window.opener) window.close();" >docs</a > </h1> <div class="code-doc-welcome"> <div class="lane-grid"> <article class="lane-card lane-mjs"> <div class="lane-head"> <div class="lane-title">MJS / AC Piece API</div> <div class="lane-count">${laneCountLabel(["mjs", "structure", "graphics", "interaction", "sound", "number", "network", "help", "system"])}</div> </div> <div class="lane-subtitle">JavaScript runtime API for AC piece authors (<code>.mjs</code>).</div> <div class="links"> <a data-done="${docs.api.mjs.overview.done}" class="top-level" href="/docs/mjs:overview">overview</a> <a data-done="${docs.api.structure.boot.done}" class="top-level" href="/docs/structure:boot">boot</a> <a data-done="${docs.api.structure.paint.done}" class="top-level" href="/docs/structure:paint">paint</a> <a data-done="${docs.api.structure.act.done}" class="top-level" href="/docs/structure:act">act</a> <a data-done="${docs.api.structure.sim.done}" class="top-level" href="/docs/structure:sim">sim</a> <a data-done="${docs.api.structure.beat.done}" class="top-level" href="/docs/structure:beat">beat</a> </div> <div class="lane-section"> <h3>Graphics</h3> <span class="links">${genLinks("graphics")}</span> </div> <div class="lane-section"> <h3>Interaction</h3> <span class="links">${genLinks("interaction")}</span> </div> <div class="lane-section"> <h3>Sound + Number + Network</h3> <span class="links">${genLinks("sound")} ${genLinks("number")} ${genLinks("network")}</span> </div> <div class="lane-section"> <h3>Help + System</h3> <span class="links">${genLinks("help")} ${genLinks("system")}</span> </div> </article>
<article class="lane-card lane-l5"> <div class="lane-head"> <div class="lane-title">L5 / Lua API</div> <div class="lane-count">${laneCountLabel(["l5"])}</div> </div> <div class="lane-subtitle">Processing-style Lua compatibility layer on AC.</div> <div class="links"> <a data-done="${docs.api.l5.overview.done}" class="top-level" href="/docs/l5:overview">overview</a> ${genLinks("l5")} </div> <div class="lane-section"> <h3>Runtime Surfaces</h3> <span class="links"> <a href="${AC_ORIGIN}/l5">/l5 playground</a> <a href="${AC_ORIGIN}/prompt">prompt</a> </span> </div> </article>
<article class="lane-card lane-processing"> <div class="lane-head"> <div class="lane-title">Processing / Java API</div> <div class="lane-count">${laneCountLabel(["processing"])}</div> </div> <div class="lane-subtitle">Processing-style Java syntax transpiled to AC L5 runtime.</div> <div class="links"> <a data-done="${docs.api.processing.overview.done}" class="top-level" href="/docs/processing:overview">overview</a> ${genLinks("processing")} </div> <div class="lane-section"> <h3>Runtime Surfaces</h3> <span class="links"> <a href="${AC_ORIGIN}/processing">/processing playground</a> <a href="${AC_ORIGIN}/l5">/l5 runtime lane</a> </span> </div> </article>
<article class="lane-card lane-kidlisp"> <div class="lane-head"> <div class="lane-title">KidLisp / Language API</div> <div class="lane-count">${laneCountLabel(["kidlisp"])}</div> </div> <div class="lane-subtitle">Canonical language reference is maintained on learn.kidlisp.com.</div> <div class="links"> <a data-done="${docs.api.kidlisp.overview.done}" class="top-level" href="/docs/kidlisp:overview">overview</a> ${genLinks("kidlisp")} </div> <div class="lane-section"> <h3>Canonical Source</h3> <span class="links"> <a href="${LEARN_KIDLISP_ORIGIN}/?tab=reference" target="_blank" rel="noopener">reference</a> <a href="${LEARN_KIDLISP_ORIGIN}/?tab=functions" target="_blank" rel="noopener">function popularity</a> <a href="${LEARN_KIDLISP_ORIGIN}/?id=wipe" target="_blank" rel="noopener">identifier pages</a> </span> </div> </article>
<article class="lane-card lane-prompts"> <div class="lane-head"> <div class="lane-title">Prompt Commands</div> <div class="lane-count">${collectionCountLabel(docs.prompts)}</div> </div> <div class="lane-subtitle">Command docs for prompt-driven actions and workflow tools.</div> <div class="links"> <a class="top-level" href="/docs/prompts">browse all prompt docs</a> <a href="${AC_ORIGIN}/prompt">open prompt</a> </div> <div class="lane-section"> <h3>Common Commands</h3> <span class="links">${genCollectionLinks("prompts", 28)}</span> </div> </article>
<article class="lane-card lane-pieces"> <div class="lane-head"> <div class="lane-title">Pieces</div> <div class="lane-count">${collectionCountLabel(docs.pieces)}</div> </div> <div class="lane-subtitle">Piece-specific docs and preview entry points.</div> <div class="links"> <a class="top-level" href="/docs/pieces">browse all piece docs</a> <a href="${AC_ORIGIN}/list">open piece list</a> </div> <div class="lane-section"> <h3>Featured Piece Docs</h3> <span class="links">${genCollectionLinks("pieces", 28)}</span> </div> </article> </div> </div> `.trim();
const commandsContent = html` <h1 id="title"> <a href="https://${event.headers["host"]}" onclick="if (window.opener) window.close();" >$name</a > </h1> <p id="command-list" style="white-space: nowrap; display: inline-block;"> ${commandList} </p> <script> function send(message) { if (window.opener) { window.opener.postMessage(message, "*"); window.close(); } } const title = document.querySelector("#title"); const originalHTML = title.innerHTML; document.querySelectorAll(".prompt").forEach((prompt) => { prompt.addEventListener("pointerenter", (e) => { title.innerHTML = prompt.innerHTML; title.classList.add("block"); prompt.addEventListener( "pointerleave", (e) => { title.innerHTML = originalHTML; title.classList.remove("block"); }, { once: true }, ); }); }); </script> `.trim();
docs.template = page .replace("$bodyclass", " class='doc'") .replace("$content", content);
const splitPath = event.path.split("/");
if ( splitPath.length === 4 || (splitPath.length === 3 && splitPath[1] !== "api") ) { let mergedDocs; keys(docs.api).forEach((key) => { mergedDocs = { ...mergedDocs, ...docs.api[key], }; });
mergedDocs = { ...mergedDocs, ...docs.pieces, ...docs.prompts };
// TODO: // Use a "colon" here for parsing and building pages from. api, pieces, // and "prompts".
// 🔦 What should have overlap and what should not?
// /docs/structure:paint // /docs/pieces:line // /docs/graphics:line
const [category, word] = splitPath.pop().split(":");
if ((category === "prompts" || category === "pieces") && !word) { const title = category === "prompts" ? "Prompt Commands" : "Pieces"; const subtitle = category === "prompts" ? "Browse all prompt command docs and open each command page." : "Browse all piece docs and open live previews per piece.";
return respond( 200, page .replace("$bodyclass", " class='doc'") .replace("$content", renderCollectionIndex(category, title, subtitle)) .replaceAll("$name", title), { "Content-Type": "text/html; charset=UTF-8", "Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Opener-Policy": "same-origin-allow-popups", "Cross-Origin-Resource-Policy": "cross-origin", }, ); }
let doc; if (category !== "pieces" && category !== "prompts") { doc = docs.api[category]?.[word]; } else if (category) { doc = docs[category]?.[word]; } else { return respond(404, "Not found. :(", { "Content-Type": "text/html; charset=UTF-8", }); }
if (!doc) { return respond(404, "Not found. :(", { "Content-Type": "text/html; charset=UTF-8", }); }
return respond( 200, page .replace("$bodyclass", " class='doc'") .replace("$content", renderDocContent(category, word, doc)) .replaceAll("$name", word), { "Content-Type": "text/html; charset=UTF-8", "Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Opener-Policy": "same-origin-allow-popups", "Cross-Origin-Resource-Policy": "cross-origin", }, ); }
// Return json or html. if (splitPath.pop().endsWith(".json")) { return respond(200, docs); } else { return respond( 200, page .replace("$bodyclass", "") .replaceAll("$content", indexContent) .replaceAll("$name", "docs"), { "Content-Type": "text/html; charset=UTF-8", "Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Opener-Policy": "same-origin-allow-popups", "Cross-Origin-Resource-Policy": "cross-origin", }, ); }}