Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
49 kB · 1155 lines
HTML
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156<!DOCTYPE html><!-- KidLisp.com Learn — Tabbed language reference with live function popularity data --><!-- URL: kidlisp.com/learn (future: learn.kidlisp.com) --><html lang="en"><head> <meta charset="utf-8"> <title>Learn · KidLisp.com</title>
<!-- Open Graph / Social Preview --> <meta property="og:type" content="website" /> <meta property="og:url" content="https://kidlisp.com/learn" /> <meta property="og:title" content="Learn · KidLisp.com" /> <meta property="og:description" content="KidLisp language reference — 118 functions for generative art, with live popularity data from the community." /> <meta property="og:image" content="https://oven.aesthetic.computer/kidlisp-og.png" /> <meta property="og:image:width" content="1200" /> <meta property="og:image:height" content="630" />
<!-- Twitter Card --> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:title" content="Learn · KidLisp.com" /> <meta name="twitter:description" content="KidLisp language reference with live function popularity." /> <meta name="twitter:image" content="https://oven.aesthetic.computer/kidlisp-og.png" />
<link rel="icon" type="image/gif" href="https://assets.aesthetic.computer/kidlisp-favicon.gif" /> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Comic+Relief:ital,wght@0,400;0,700;1,400;1,700&family=Noto+Sans+Mono:wght@400;700&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://aesthetic.computer/type/webfonts/berkeley-mono-variable.css"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<style> @font-face { font-family: 'YWFTProcessing-Regular'; src: url('https://aesthetic.computer/type/webfonts/ywft-processing-regular.woff2') format('woff2'), url('https://aesthetic.computer/type/webfonts/ywft-processing-regular.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; } @font-face { font-family: 'YWFTProcessing-Bold'; src: url('https://aesthetic.computer/type/webfonts/ywft-processing-bold.woff2') format('woff2'), url('https://aesthetic.computer/type/webfonts/ywft-processing-bold.woff') format('woff'); font-weight: bold; font-style: normal; font-display: swap; }
:root { --bg-primary: #d4f5d8; --bg-secondary: #b8e8be; --bg-tertiary: #e2f9e5; --text-primary: #0a2e0e; --text-secondary: #1a5c24; --text-muted: #3d7a48; --accent: #22c55e; --accent-dim: rgba(34, 197, 94, 0.15); --border: #8fd49a; --code-bg: #c2edc8; --bar-color: #22c55e; --font-mono: 'Berkeley Mono Variable', 'Noto Sans Mono', monospace; --font-display: 'YWFTProcessing-Regular', monospace; --font-fun: 'Comic Relief', cursive; /* Syntax highlighting — light mode */ --kl-comment: #a0a0a0; --kl-string: #986801; --kl-number: #986801; --kl-fn: #4078f2; --kl-op: #50a14f; --kl-var: #b35000; --kl-timing: #986801; --kl-code: #50a14f; --kl-fade: #0184bc; } @media (prefers-color-scheme: dark) { :root { --bg-primary: #0a2612; --bg-secondary: #0e3318; --bg-tertiary: #133d1e; --text-primary: #d4f5d8; --text-secondary: #86efac; --text-muted: #4ade80; --border: #1a5c24; --code-bg: #0e3318; --bar-color: #4ade80; --accent-dim: rgba(34, 197, 94, 0.2); /* Syntax highlighting — dark mode */ --kl-comment: #6a6a6a; --kl-string: #e5c07b; --kl-number: #d19a66; --kl-fn: #61afef; --kl-op: #98c379; --kl-var: #e06c75; --kl-timing: #e5c07b; --kl-code: #98c379; --kl-fade: #56b6c2; } }
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: var(--font-mono); background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; font-size: 14px; display: flex; flex-direction: column; min-height: 100vh; }
.container { max-width: 900px; margin: 0 auto; padding: 0 20px; width: 100%; flex: 1; display: flex; flex-direction: column; }
/* Header */ header { display: flex; align-items: baseline; gap: 12px; padding: 24px 0 16px; flex-wrap: wrap; } header h1 { font-family: var(--font-display); font-size: 28px; font-weight: normal; letter-spacing: -0.5px; } header h1 span { color: var(--text-muted); } header nav { display: flex; gap: 16px; } header nav a { color: var(--accent); text-decoration: none; font-size: 13px; } header nav a:hover { text-decoration: underline; }
/* Tabs */ .tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 0; } .tab { padding: 10px 20px; background: none; border: none; border-bottom: 2px solid transparent; font-family: var(--font-mono); font-size: 13px; color: var(--text-muted); cursor: pointer; transition: all 0.15s; } .tab:hover { color: var(--text-primary); } .tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: bold; }
/* Tab panels */ .tab-panel { display: none; padding: 20px 0 80px; flex: 1; } .tab-panel.active { display: block; }
/* Hello book */ .hello-book { max-width: 100%; margin: 0; padding: 8px; text-align: left; } .hello-page { display: none; animation: hello-fade 0.2s ease; } .hello-page.active { display: block; } @keyframes hello-fade { from { opacity: 0; } to { opacity: 1; } } .hello-illustration { background: var(--code-bg); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; flex: 1; min-width: 0; } .hello-code { font-family: var(--font-mono); font-size: 12px; line-height: 1.5; text-align: left; color: var(--text-primary); background: none; border: none; padding: 0; margin: 0; white-space: pre-wrap; word-break: break-word; } .hello-text { font-family: var(--font-fun); font-size: 13px; line-height: 1.4; color: var(--text-secondary); margin: 6px 0 0 0; padding: 0; } .hello-nav { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 8px; } .hello-nav button { background: var(--code-bg); border: 1px solid var(--border); border-radius: 4px; padding: 4px 12px; font-family: var(--font-mono); font-size: 14px; color: var(--text-primary); cursor: pointer; transition: all 0.15s; } .hello-nav button:hover:not(:disabled) { background: var(--accent); color: white; border-color: var(--accent); } .hello-nav button:disabled { opacity: 0.3; cursor: default; } .hello-dots { display: flex; gap: 6px; } .hello-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border); cursor: pointer; transition: background 0.2s; } .hello-dot.active { background: var(--accent); } @media (max-width: 400px) { .hello-text { font-size: 12px; } .hello-code { font-size: 11px; } }
/* Subsection headings */ h3 { font-size: 14px; font-weight: bold; margin: 20px 0 8px; color: var(--text-secondary); } p, li { color: var(--text-secondary); margin-bottom: 8px; }
/* Code blocks */ code { font-family: var(--font-mono); font-size: 13px; background: var(--code-bg); padding: 2px 5px; border-radius: 3px; } pre { background: var(--code-bg); padding: 12px 16px; border-radius: 6px; overflow-x: auto; margin: 8px 0 16px; font-size: 13px; line-height: 1.5; } pre code { background: none; padding: 0; }
/* Syntax highlighting classes */ .kl-comment { color: var(--kl-comment); font-style: italic; } .kl-string { color: var(--kl-string); } .kl-number { color: var(--kl-number); } .kl-fn { color: var(--kl-fn); font-weight: bold; } .kl-op { color: var(--kl-op); font-weight: bold; } .kl-var { color: var(--kl-var); } .kl-timing { color: var(--kl-timing); font-weight: bold; } .kl-code { color: var(--kl-code); } .kl-fade { color: var(--kl-fade); }
/* Tables */ table { width: 100%; border-collapse: collapse; margin: 8px 0 16px; font-size: 13px; } th, td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border); } th { font-weight: bold; color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; } td code { font-size: 12px; }
/* Function popularity bars */ .pop-loading { color: var(--text-muted); font-style: italic; } .pop-grid { display: grid; grid-template-columns: 120px 1fr 60px; gap: 4px 12px; align-items: center; } .pop-name { font-family: var(--font-mono); font-size: 13px; font-weight: bold; } .pop-bar-wrap { height: 18px; background: var(--accent-dim); border-radius: 3px; overflow: hidden; } .pop-bar { height: 100%; background: var(--bar-color); border-radius: 3px; transition: width 0.4s ease; } .pop-count { font-size: 12px; color: var(--text-muted); text-align: right; } .pop-meta { margin-top: 12px; font-size: 12px; color: var(--text-muted); }
/* Top hits */ .hit-card { background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 8px; padding: 16px; margin-bottom: 12px; } .hit-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; } .hit-code { font-family: var(--font-mono); font-weight: bold; font-size: 15px; } .hit-code a { color: var(--accent); text-decoration: none; } .hit-code a:hover { text-decoration: underline; } .hit-views { font-size: 12px; color: var(--text-muted); } .hit-source { background: var(--code-bg); padding: 10px 12px; border-radius: 4px; font-family: var(--font-mono); font-size: 12px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
/* Responsive */ @media (max-width: 600px) { .container { padding: 0 12px; } header h1 { font-size: 22px; } .tab { padding: 8px 14px; font-size: 12px; } .pop-grid { grid-template-columns: 90px 1fr 50px; gap: 3px 8px; } .pop-name { font-size: 12px; } }
/* Embedded in parent editor iframe — hide branding */ .embedded header { display: none; } .embedded .container { padding-top: 0; } .embedded .tabs { margin-top: 0; } .embedded .tab-panel { padding-top: 8px; }
/* Identifierdex detail card */ .id-detail { background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 8px; padding: 16px; margin-bottom: 16px; animation: id-fade-in 0.2s ease; } @keyframes id-fade-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } } .id-detail-back { font-size: 12px; color: var(--accent); cursor: pointer; background: none; border: none; font-family: var(--font-mono); padding: 0; margin-bottom: 8px; } .id-detail-back:hover { text-decoration: underline; } .id-detail-name { font-family: var(--font-fun); font-size: 24px; font-weight: bold; margin-bottom: 4px; } .id-detail-category { display: inline-block; font-size: 11px; padding: 2px 8px; background: var(--accent-dim); color: var(--accent); border-radius: 10px; margin-bottom: 8px; } .id-detail-sig { font-family: var(--font-mono); font-size: 13px; background: var(--code-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 8px; } .id-detail-desc { color: var(--text-secondary); font-size: 13px; line-height: 1.5; margin-bottom: 10px; } .id-detail-stats { display: flex; gap: 16px; font-size: 12px; color: var(--text-muted); margin-bottom: 10px; } .id-detail-stats strong { color: var(--text-primary); } .id-detail-examples-title { font-size: 12px; font-weight: bold; color: var(--text-muted); margin-bottom: 4px; } .id-detail-example { font-family: var(--font-mono); font-size: 12px; background: var(--code-bg); padding: 6px 10px; border-radius: 4px; margin-bottom: 4px; cursor: pointer; transition: background 0.15s; } .id-detail-example:hover { background: var(--accent-dim); }
/* Clickable popularity rows */ .pop-row { cursor: pointer; display: contents; } .pop-row:hover .pop-name { color: var(--accent); } </style> <script>if (window.self !== window.top) document.documentElement.classList.add('embedded');</script></head><body>
<div class="container"> <header> <h1>Learn <span>· KidLisp.com</span></h1> <nav> <a href="https://kidlisp.com">Editor</a> <a href="https://kidlisp.com/keeps">Keeps</a> <a href="https://learn.kidlisp.com/decree">Decree</a> <a href="https://top.kidlisp.com">Top 100</a> <a href="sms:+15087284043">Text 1-508-728-4043</a> </nav> </header>
<div class="tabs"> <button class="tab active" data-tab="hello">Hello</button> <button class="tab" data-tab="functions">Functions</button> <button class="tab" data-tab="reference">Reference</button> <button class="tab" data-tab="hits">Hits</button> </div>
<!-- Hello — A tiny picture book intro to KidLisp --> <div class="tab-panel active" id="tab-hello"> <div class="hello-book"> <div class="hello-page active" data-page="1"> <div class="hello-illustration"> <pre class="hello-code">red</pre> </div> <p class="hello-text">Type a color. The screen fills with it.</p> </div> <div class="hello-page" data-page="2"> <div class="hello-illustration"> <pre class="hello-code">red, circle</pre> </div> <p class="hello-text">Commas chain commands. Set a color, then draw.</p> </div> <div class="hello-page" data-page="3"> <div class="hello-illustration"> <pre class="hello-code">wipe black, ink lime, box 40 40 80</pre> </div> <p class="hello-text">Wipe the screen, pick a color, draw a shape — all in one line.</p> </div> <div class="hello-page" data-page="4"> <div class="hello-illustration"> <pre class="hello-code">ink (? red blue lime), circle (wiggle w) (wiggle h) 20</pre> </div> <p class="hello-text">Randomness and motion are built in. Every frame picks a new color and wiggles.</p> </div> <div class="hello-page" data-page="5"> <div class="hello-illustration"> <pre class="hello-code">(repeat 50 i (ink (? red blue lime)) (circle (wiggle w) (wiggle h) 8))</pre> </div> <p class="hello-text">Loops make patterns. Fifty random circles in three lines.</p> </div> <div class="hello-page" data-page="6"> <div class="hello-illustration"> <pre class="hello-code">wipe navy, ink rainbow, 1s (blur 1), circle 80 60 (* (sin f) 30)</pre> </div> <p class="hello-text">Time, blur, and math — code runs every frame so things move on their own.</p> </div> <div class="hello-page" data-page="7"> <div class="hello-illustration" style="font-size: 24px; display: flex; align-items: center; justify-content: center;"> ( ) </div> <p class="hello-text">118 functions to explore. Type something and press play.</p> </div> <div class="hello-nav"> <button class="hello-prev" disabled>←</button> <span class="hello-dots"></span> <button class="hello-next">→</button> </div> </div> </div>
<!-- Function Popularity — Live data from API showing which KidLisp functions are used most across all community pieces, weighted by view count --> <div class="tab-panel" id="tab-functions"> <div id="popularity"><p class="pop-loading">Loading function popularity data...</p></div> </div>
<!-- Language Reference — Complete KidLisp API: 118 functions across 12 categories for generative art on aesthetic.computer --> <div class="tab-panel" id="tab-reference"> <h3>Syntax</h3> <pre><code class="kidlisp">; Comments start with semicolon(function-name arg1 arg2 ...)
; Shorthand comma syntaxpurple, ink, line, blur 5
; Variables & functions(def x 10)(later star x y (ink "yellow") (circle x y 20))(star 100 100)
; Loops(repeat 50 i (circle (wiggle w) (wiggle h) 10))</code></pre> <p>Shorthand: <code>w</code> = width, <code>h</code> = height, <code>f</code> = frame. <code>?</code> = random, <code>...</code> = cycle values.</p> <p>Identifiers: no dashes (parsed as subtraction). Use <code>my_var</code> not <code>my-var</code>.</p>
<h3>Drawing</h3> <table> <tr><th>Function</th><th>Usage</th><th>Description</th></tr> <tr><td><code>wipe</code></td><td><code>(wipe color)</code></td><td>Clear screen</td></tr> <tr><td><code>ink</code></td><td><code>(ink color [alpha])</code></td><td>Set draw color</td></tr> <tr><td><code>line</code></td><td><code>(line x1 y1 x2 y2)</code> or <code>(line)</code></td><td>Line (random if no args)</td></tr> <tr><td><code>box</code></td><td><code>(box x y w h)</code></td><td>Rectangle</td></tr> <tr><td><code>circle</code></td><td><code>(circle x y radius)</code></td><td>Circle</td></tr> <tr><td><code>tri</code></td><td><code>(tri x1 y1 x2 y2 x3 y3)</code></td><td>Triangle</td></tr> <tr><td><code>plot</code></td><td><code>(plot x y)</code></td><td>Single pixel</td></tr> <tr><td><code>flood</code></td><td><code>(flood x y)</code></td><td>Flood fill</td></tr> <tr><td><code>shape</code></td><td><code>(shape pts... "fill")</code></td><td>Polygon</td></tr> <tr><td><code>fill</code>/<code>outline</code></td><td><code>(fill)</code> / <code>(outline)</code></td><td>Shape mode</td></tr> </table>
<h3>Colors</h3> <p>Named: <code>red</code>, <code>blue</code>, <code>lime</code>, <code>navy</code>, etc. (all CSS colors, no quotes needed)<br> RGB: <code>(ink 255 0 0)</code> — With alpha: <code>(ink red 128)</code><br> Special: <code>rainbow</code>, <code>zebra</code>, <code>erase</code><br> Gradients: <code>fade:red-blue-black</code>, <code>fade:zebra-rainbow-zebra:direction</code></p>
<h3>Math</h3> <table> <tr><th>Function</th><th>Usage</th><th>Description</th></tr> <tr><td><code>+</code> <code>-</code> <code>*</code> <code>/</code> <code>%</code></td><td><code>(+ a b c...)</code></td><td>Arithmetic</td></tr> <tr><td><code>sin</code> <code>cos</code> <code>tan</code></td><td><code>(sin x)</code></td><td>Trigonometry</td></tr> <tr><td><code>random</code> / <code>?</code></td><td><code>(? a b c)</code></td><td>Random pick</td></tr> <tr><td><code>wiggle</code></td><td><code>(wiggle amount)</code></td><td>Random ±amount/2</td></tr> <tr><td><code>min</code> <code>max</code> <code>abs</code></td><td><code>(min a b)</code></td><td>Comparison</td></tr> <tr><td><code>sqrt</code> <code>pow</code></td><td><code>(sqrt x)</code></td><td>Power/root</td></tr> <tr><td><code>floor</code> <code>ceil</code> <code>round</code></td><td><code>(floor x)</code></td><td>Rounding</td></tr> </table>
<h3>System</h3> <p><code>width</code>/<code>w</code>, <code>height</code>/<code>h</code> — canvas size. <code>frame</code>/<code>f</code> — frame count. <code>clock</code> — UTC time. <code>pi</code> — π. <code>(fps rate)</code> — set frame rate.</p>
<h3>Control Flow</h3> <table> <tr><th>Function</th><th>Usage</th><th>Description</th></tr> <tr><td><code>def</code></td><td><code>(def x 10)</code></td><td>Variable</td></tr> <tr><td><code>later</code></td><td><code>(later fn params body)</code></td><td>Define function</td></tr> <tr><td><code>if</code></td><td><code>(if cond then else)</code></td><td>Conditional</td></tr> <tr><td><code>once</code></td><td><code>(once expr)</code></td><td>Run once only</td></tr> <tr><td><code>repeat</code>/<code>bunch</code></td><td><code>(repeat n i expr)</code></td><td>Loop</td></tr> <tr><td><code>?</code>/<code>choose</code></td><td><code>(? a b c)</code></td><td>Random pick</td></tr> <tr><td><code>...</code></td><td><code>(... a b c)</code></td><td>Cycle over time</td></tr> <tr><td><code>now</code></td><td><code>(now var value)</code></td><td>Update variable</td></tr> </table>
<h3>Timing</h3> <pre><code class="kidlisp">1s ; after 1 second2s... ; cycle every 2 seconds0.5s! ; once after 0.5s0.1s ; every 0.1 seconds (fast)
(0.25s (wipe (... red yellow blue))) ; cycle bg color(1s (blur 1)) ; periodic blur</code></pre>
<h3>Pixel Transforms</h3> <table> <tr><th>Function</th><th>Usage</th><th>Description</th></tr> <tr><td><code>scroll</code></td><td><code>(scroll dx dy)</code></td><td>Translate with wrapping</td></tr> <tr><td><code>zoom</code></td><td><code>(zoom factor)</code></td><td>Scale from center</td></tr> <tr><td><code>spin</code></td><td><code>(spin angle)</code></td><td>Rotate</td></tr> <tr><td><code>suck</code></td><td><code>(suck strength)</code></td><td>Vortex displacement</td></tr> <tr><td><code>blur</code></td><td><code>(blur amount)</code></td><td>Gaussian blur</td></tr> <tr><td><code>contrast</code></td><td><code>(contrast amount)</code></td><td>Adjust contrast</td></tr> <tr><td><code>sort</code></td><td><code>(sort)</code></td><td>Sort pixels by brightness</td></tr> <tr><td><code>bake</code></td><td><code>(bake)</code></td><td>Commit to background</td></tr> <tr><td><code>pan</code>/<code>unpan</code></td><td><code>(pan dx dy)</code></td><td>Camera movement</td></tr> </table>
<h3>Images, Text & Media</h3> <table> <tr><th>Function</th><th>Usage</th><th>Description</th></tr> <tr><td><code>paste</code></td><td><code>(paste url x y [scale])</code></td><td>Paste image (URLs can be unquoted)</td></tr> <tr><td><code>stamp</code></td><td><code>(stamp url x y)</code></td><td>Paste centered</td></tr> <tr><td><code>write</code></td><td><code>(write text x y)</code></td><td>Draw text</td></tr> <tr><td><code>tape</code></td><td><code>(tape !CODE x y w h)</code></td><td>Embed video</td></tr> </table>
<h3>Audio</h3> <p><code>mic</code>, <code>amplitude</code>, <code>melody</code>, <code>overtone</code>, <code>noise</code>, <code>speaker</code></p>
<h3>3D</h3> <p><code>cube</code>, <code>form</code>, <code>trans</code> (move, scale, spin), <code>cubespin</code>, <code>cubepos</code>, <code>cubescale</code>, <code>camrot</code>, <code>camspin</code></p>
<h3>Embedding & Navigation</h3> <pre><code class="kidlisp">($codeId) ; execute another piece($codeId x y w h alpha) ; embed with position/size/alpha(jump "piece") ; navigate to piece(jump $id) ; navigate to cached piece</code></pre> </div>
<!-- Top Hits — Real pieces with the most views, proven patterns from the community with 500+ hits --> <div class="tab-panel" id="tab-hits"> <div id="top-hits"><p class="pop-loading">Loading top hits...</p></div> </div></div>
<script>// Tab switchingconst tabs = document.querySelectorAll('.tab');const panels = document.querySelectorAll('.tab-panel');tabs.forEach(tab => { tab.addEventListener('click', () => { tabs.forEach(t => t.classList.remove('active')); panels.forEach(p => p.classList.remove('active')); tab.classList.add('active'); document.getElementById('tab-' + tab.dataset.tab).classList.add('active'); });});
// Hello book pagination(function() { const pages = document.querySelectorAll('.hello-page'); const prev = document.querySelector('.hello-prev'); const next = document.querySelector('.hello-next'); const dotsWrap = document.querySelector('.hello-dots'); if (!pages.length || !prev || !next || !dotsWrap) return; let current = 0; const total = pages.length; // Build dots for (let i = 0; i < total; i++) { const dot = document.createElement('span'); dot.className = 'hello-dot' + (i === 0 ? ' active' : ''); dotsWrap.appendChild(dot); } const dots = dotsWrap.querySelectorAll('.hello-dot'); function show(idx) { pages.forEach(p => p.classList.remove('active')); dots.forEach(d => d.classList.remove('active')); pages[idx].classList.add('active'); dots[idx].classList.add('active'); prev.disabled = idx === 0; next.disabled = idx === total - 1; current = idx; } prev.addEventListener('click', () => { if (current > 0) show(current - 1); }); next.addEventListener('click', () => { if (current < total - 1) show(current + 1); }); dots.forEach((dot, i) => dot.addEventListener('click', () => show(i)));})();
// Identifierdex global statelet popData = null;let popMeta = { totalPieces: 0, totalHits: 0 };let topHitsData = [];
// Reference data for all KidLisp identifiersconst KIDLISP_REF = { // Drawing wipe: { cat: 'Drawing', sig: '(wipe [color])', desc: 'Clear the entire canvas. With a color argument, fills with that color.' }, ink: { cat: 'Drawing', sig: '(ink color [alpha])', desc: 'Set the current drawing color. Accepts named colors (red, blue), RGB values (255 0 0), or special keywords (rainbow, zebra).' }, line: { cat: 'Drawing', sig: '(line x1 y1 x2 y2)', desc: 'Draw a line between two points. With no arguments, draws a random line.' }, lines: { cat: 'Drawing', sig: '(lines x1 y1 x2 y2 ...)', desc: 'Draw multiple connected line segments.' }, box: { cat: 'Drawing', sig: '(box x y w h)', desc: 'Draw a rectangle at position (x, y) with width w and height h.' }, circle: { cat: 'Drawing', sig: '(circle x y radius)', desc: 'Draw a circle centered at (x, y) with the given radius.' }, tri: { cat: 'Drawing', sig: '(tri x1 y1 x2 y2 x3 y3)', desc: 'Draw a triangle with three vertex points.' }, plot: { cat: 'Drawing', sig: '(plot x y)', desc: 'Draw a single pixel at position (x, y).' }, point: { cat: 'Drawing', sig: '(point x y)', desc: 'Alias for plot — draw a single pixel.' }, flood: { cat: 'Drawing', sig: '(flood x y)', desc: 'Flood fill from (x, y), replacing adjacent same-color pixels with the current ink.' }, shape: { cat: 'Drawing', sig: '(shape x1 y1 x2 y2 ... "fill")', desc: 'Draw a polygon from point pairs. Pass "fill" as last arg to fill it.' }, fill: { cat: 'Drawing', sig: '(fill)', desc: 'Set drawing mode to filled shapes.' }, outline: { cat: 'Drawing', sig: '(outline)', desc: 'Set drawing mode to outlines only.' }, stroke: { cat: 'Drawing', sig: '(stroke width)', desc: 'Set the stroke width for outlines and lines.' }, nofill: { cat: 'Drawing', sig: '(nofill)', desc: 'Disable fill for shapes.' }, nostroke: { cat: 'Drawing', sig: '(nostroke)', desc: 'Disable stroke/outline for shapes.' }, resolution: { cat: 'Drawing', sig: '(resolution w h)', desc: 'Set the canvas resolution. Smaller values = more pixelated.' }, coat: { cat: 'Drawing', sig: '(coat n)', desc: 'Set the number of paint coats per stroke.' }, mask: { cat: 'Drawing', sig: '(mask)', desc: 'Enable masking — subsequent drawing only appears where pixels already exist.' }, unmask: { cat: 'Drawing', sig: '(unmask)', desc: 'Disable masking mode.' }, draw: { cat: 'Drawing', sig: '(draw)', desc: 'Trigger a draw/render cycle.' }, label: { cat: 'Drawing', sig: '(label text x y)', desc: 'Draw a text label at position (x, y).' }, // Pixel transforms scroll: { cat: 'Transform', sig: '(scroll dx dy)', desc: 'Shift all pixels by (dx, dy) with wrapping at edges.' }, zoom: { cat: 'Transform', sig: '(zoom factor)', desc: 'Scale the canvas from the center by the given factor.' }, spin: { cat: 'Transform', sig: '(spin angle)', desc: 'Rotate the canvas by angle (degrees).' }, suck: { cat: 'Transform', sig: '(suck strength)', desc: 'Apply a vortex/swirl displacement effect.' }, blur: { cat: 'Transform', sig: '(blur amount)', desc: 'Apply Gaussian blur to the canvas.' }, contrast: { cat: 'Transform', sig: '(contrast amount)', desc: 'Adjust image contrast. Values >1 increase, <1 decrease.' }, sort: { cat: 'Transform', sig: '(sort)', desc: 'Sort all pixels by brightness — creates a pixel-sorting glitch effect.' }, pan: { cat: 'Transform', sig: '(pan dx dy)', desc: 'Move the camera/viewport by (dx, dy).' }, unpan: { cat: 'Transform', sig: '(unpan)', desc: 'Reset the camera/viewport to origin.' }, resetSpin: { cat: 'Transform', sig: '(resetSpin)', desc: 'Reset rotation to 0.' }, smoothspin: { cat: 'Transform', sig: '(smoothspin angle)', desc: 'Smooth interpolated rotation.' }, bake: { cat: 'Transform', sig: '(bake)', desc: 'Commit the current frame to the background layer permanently.' }, // Images, Text & Media paste: { cat: 'Media', sig: '(paste url x y [scale])', desc: 'Paste an image from URL at (x, y). Optional scale factor.' }, stamp: { cat: 'Media', sig: '(stamp url x y)', desc: 'Paste an image centered at (x, y).' }, painting: { cat: 'Media', sig: '(painting url)', desc: 'Load a painting/image as background.' }, steal: { cat: 'Media', sig: '(steal)', desc: 'Capture the current frame as a texture for later use.' }, putback: { cat: 'Media', sig: '(putback)', desc: 'Restore a previously stolen texture.' }, tape: { cat: 'Media', sig: '(tape !CODE x y w h)', desc: 'Embed a video clip at position with size.' }, write: { cat: 'Media', sig: '(write text x y)', desc: 'Draw text at position (x, y) using the current ink color.' }, len: { cat: 'Media', sig: '(len str)', desc: 'Get the length of a string.' }, // Audio mic: { cat: 'Audio', sig: '(mic)', desc: 'Enable microphone input for audio-reactive visuals.' }, amplitude: { cat: 'Audio', sig: '(amplitude)', desc: 'Get the current audio amplitude (0–1) from mic or speaker.' }, speaker: { cat: 'Audio', sig: '(speaker freq [vol])', desc: 'Play a tone at the given frequency.' }, melody: { cat: 'Audio', sig: '(melody notes...)', desc: 'Play a sequence of musical notes.' }, overtone: { cat: 'Audio', sig: '(overtone freq)', desc: 'Add a harmonic overtone to the audio.' }, noise: { cat: 'Audio', sig: '(noise [type])', desc: 'Generate noise — white, pink, or brown.' }, // 3D cube: { cat: '3D', sig: '(cube [size])', desc: 'Draw a 3D cube with optional size.' }, quad: { cat: '3D', sig: '(quad ...)', desc: 'Draw a 3D quad/plane.' }, form: { cat: '3D', sig: '(form type ...)', desc: 'Create a 3D form/mesh.' }, trans: { cat: '3D', sig: '(trans x y z)', desc: 'Translate 3D position.' }, cubespin: { cat: '3D', sig: '(cubespin rx ry rz)', desc: 'Set 3D cube rotation speeds.' }, cubepos: { cat: '3D', sig: '(cubepos x y z)', desc: 'Set 3D cube position.' }, cubescale: { cat: '3D', sig: '(cubescale sx sy sz)', desc: 'Set 3D cube scale.' }, camrot: { cat: '3D', sig: '(camrot rx ry rz)', desc: 'Set camera rotation angles.' }, camspin: { cat: '3D', sig: '(camspin rx ry rz)', desc: 'Set camera spin speeds.' }, // Control flow def: { cat: 'Control', sig: '(def name value)', desc: 'Define a variable with a name and value.' }, later: { cat: 'Control', sig: '(later name params body)', desc: 'Define a reusable function with parameters.' }, 'if': { cat: 'Control', sig: '(if cond then [else])', desc: 'Conditional: evaluate then-expr if cond is true, otherwise else-expr.' }, once: { cat: 'Control', sig: '(once expr)', desc: 'Execute an expression only on the first frame.' }, now: { cat: 'Control', sig: '(now var value)', desc: "Update an existing variable's value." }, not: { cat: 'Control', sig: '(not expr)', desc: 'Boolean negation.' }, die: { cat: 'Control', sig: '(die)', desc: 'Stop execution of the current piece.' }, repeat: { cat: 'Control', sig: '(repeat n i expr)', desc: 'Loop n times with iterator variable i.' }, rep: { cat: 'Control', sig: '(rep n i expr)', desc: 'Alias for repeat — loop n times.' }, bunch: { cat: 'Control', sig: '(bunch n i expr)', desc: 'Alias for repeat — loop n times.' }, choose: { cat: 'Control', sig: '(choose a b c ...)', desc: 'Randomly pick one of the arguments.' }, tap: { cat: 'Control', sig: '(tap expr)', desc: 'Execute on screen tap/click.' }, // Navigation embed: { cat: 'Navigation', sig: '($code x y w h alpha)', desc: 'Embed another piece with position, size, and opacity.' }, jump: { cat: 'Navigation', sig: '(jump "piece")', desc: 'Navigate to another piece.' }, hop: { cat: 'Navigation', sig: '(hop "piece")', desc: 'Navigate to another piece (alias for jump).' }, // Math random: { cat: 'Math', sig: '(random [max])', desc: 'Generate a random number. With max, returns 0 to max.' }, wiggle: { cat: 'Math', sig: '(wiggle amount)', desc: 'Random value in range ±amount/2 centered at 0.' }, min: { cat: 'Math', sig: '(min a b)', desc: 'Return the smaller of two values.' }, max: { cat: 'Math', sig: '(max a b)', desc: 'Return the larger of two values.' }, abs: { cat: 'Math', sig: '(abs x)', desc: 'Absolute value.' }, sqrt: { cat: 'Math', sig: '(sqrt x)', desc: 'Square root.' }, pow: { cat: 'Math', sig: '(pow base exp)', desc: 'Raise base to the power of exp.' }, floor: { cat: 'Math', sig: '(floor x)', desc: 'Round down to nearest integer.' }, ceil: { cat: 'Math', sig: '(ceil x)', desc: 'Round up to nearest integer.' }, round: { cat: 'Math', sig: '(round x)', desc: 'Round to nearest integer.' }, sin: { cat: 'Math', sig: '(sin x)', desc: 'Sine of x (in radians).' }, cos: { cat: 'Math', sig: '(cos x)', desc: 'Cosine of x (in radians).' }, tan: { cat: 'Math', sig: '(tan x)', desc: 'Tangent of x (in radians).' }, // Operators '+': { cat: 'Math', sig: '(+ a b ...)', desc: 'Addition — sum all arguments.' }, '-': { cat: 'Math', sig: '(- a b)', desc: 'Subtraction.' }, '*': { cat: 'Math', sig: '(* a b ...)', desc: 'Multiplication.' }, '/': { cat: 'Math', sig: '(/ a b)', desc: 'Division.' }, '%': { cat: 'Math', sig: '(% a b)', desc: 'Modulo (remainder).' }, '?': { cat: 'Control', sig: '(? a b c ...)', desc: 'Randomly pick one of the arguments (alias for choose/random).' }, '...': { cat: 'Control', sig: '(... a b c)', desc: 'Cycle through values over time, advancing each frame.' }, '=': { cat: 'Math', sig: '(= a b)', desc: 'Equality comparison.' }, '<': { cat: 'Math', sig: '(< a b)', desc: 'Less than comparison.' }, '>': { cat: 'Math', sig: '(> a b)', desc: 'Greater than comparison.' }, // System fps: { cat: 'System', sig: '(fps rate)', desc: 'Set the frame rate (frames per second).' }, debug: { cat: 'System', sig: '(debug)', desc: 'Enable debug mode — shows extra info.' }, width: { cat: 'System', sig: 'width (alias: w)', desc: 'Canvas width in pixels.' }, w: { cat: 'System', sig: 'w', desc: 'Shorthand for width — canvas width in pixels.' }, height: { cat: 'System', sig: 'height (alias: h)', desc: 'Canvas height in pixels.' }, h: { cat: 'System', sig: 'h', desc: 'Shorthand for height — canvas height in pixels.' }, frame: { cat: 'System', sig: 'frame (alias: f)', desc: 'Current frame number, incrementing each tick.' }, f: { cat: 'System', sig: 'f', desc: 'Shorthand for frame — current frame count.' }, clock: { cat: 'System', sig: 'clock', desc: 'Current UTC time value.' }, pi: { cat: 'System', sig: 'pi', desc: 'The constant π (3.14159...).' }, // Colors (special) rainbow: { cat: 'Color', sig: 'rainbow', desc: 'Cycles through rainbow colors automatically.' }, zebra: { cat: 'Color', sig: 'zebra', desc: 'Alternating black and white pattern.' }, erase: { cat: 'Color', sig: 'erase', desc: 'Use as ink color to erase (draw transparent).' },};
// KidLisp syntax highlighterconst KL_FNS = new Set([ 'wipe','ink','line','lines','box','circle','tri','plot','point','flood','shape', 'fill','outline','stroke','nofill','nostroke','resolution','coat','mask','unmask', 'scroll','zoom','spin','suck','blur','contrast','sort','pan','unpan','resetSpin','smoothspin','bake', 'paste','stamp','painting','steal','putback','tape','write','len', 'mic','amplitude','speaker','melody','overtone','noise', 'cube','quad','form','trans','cubespin','cubepos','cubescale','camrot','camspin', 'embed','jump','hop','def','later','if','once','now','not','die', 'repeat','rep','bunch','choose','tap','draw','label','fps','debug', 'random','wiggle','min','max','abs','sqrt','pow','floor','ceil','round', 'sin','cos','tan']);const KL_COLORS = { red:'#ff0000',blue:'#0000ff',green:'#008000',purple:'#800080',navy:'#000080', brown:'#a52a2a',salmon:'#fa8072',cyan:'#00ffff',black:'#000000',white:'#ffffff', yellow:'#ffff00',lime:'#00ff00',orange:'#ffa500',pink:'#ffc0cb',coral:'#ff7f50', magenta:'#ff00ff',teal:'#008080',crimson:'#dc143c',gold:'#ffd700',silver:'#c0c0c0', gray:'#808080',grey:'#808080',maroon:'#800000',olive:'#808000',aqua:'#00ffff', indigo:'#4b0082',violet:'#ee82ee',beige:'#f5f5dc'};const KL_SPECIAL = new Set(['rainbow','zebra','erase']);const KL_OPS = new Set(['+','-','*','/','%','=','<','>','mod']);const PAREN_COLORS = ['#FF6B6B','#4ECDC4','#FFE66D','#A8E6CF','#70D6FF','#BB8FCE'];
function esc(s) { return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
function highlightKL(src) { let out = '', i = 0, depth = 0; while (i < src.length) { const ch = src[i]; // Comment if (ch === ';') { let end = src.indexOf('\n', i); if (end === -1) end = src.length; out += '<span class="kl-comment">' + esc(src.slice(i, end)) + '</span>'; i = end; continue; } // String if (ch === '"' || ch === "'") { let end = i + 1; while (end < src.length && src[end] !== ch) { if (src[end] === '\\') end++; end++; } if (end < src.length) end++; out += '<span class="kl-string">' + esc(src.slice(i, end)) + '</span>'; i = end; continue; } // Parens if (ch === '(') { out += '<span style="color:' + PAREN_COLORS[depth % PAREN_COLORS.length] + '">(</span>'; depth++; i++; continue; } if (ch === ')') { depth = Math.max(0, depth - 1); out += '<span style="color:' + PAREN_COLORS[depth % PAREN_COLORS.length] + '">)</span>'; i++; continue; } // Whitespace if (/\s/.test(ch)) { out += ch; i++; continue; } // Token let end = i; while (end < src.length && !/[\s();"']/.test(src[end])) end++; const tok = src.slice(i, end); const tokLow = tok.toLowerCase(); if (/^\d+\.?\d*s[.!]*$/.test(tok)) { out += '<span class="kl-timing">' + esc(tok) + '</span>'; } else if (/^\d+(\.\d+)?$/.test(tok)) { out += '<span class="kl-number">' + esc(tok) + '</span>'; } else if (KL_OPS.has(tok) || tok === '?' || tok === '...') { out += '<span class="kl-op">' + esc(tok) + '</span>'; } else if (KL_FNS.has(tokLow)) { out += '<span class="kl-fn">' + esc(tok) + '</span>'; } else if (KL_COLORS[tokLow]) { const hex = KL_COLORS[tokLow]; const r = parseInt(hex.slice(1,3),16), g = parseInt(hex.slice(3,5),16), b = parseInt(hex.slice(5,7),16); const lum = 0.299*r + 0.587*g + 0.114*b; const shadow = lum < 60 ? 'text-shadow:0 0 4px rgba(255,255,255,0.6);' : lum > 200 ? 'text-shadow:0 0 4px rgba(0,0,0,0.4);' : ''; out += '<span style="color:' + hex + ';font-weight:bold;' + shadow + '">' + esc(tok) + '</span>'; } else if (KL_SPECIAL.has(tokLow)) { out += '<span class="kl-fade">' + esc(tok) + '</span>'; } else if (tok.startsWith('$')) { out += '<span class="kl-code">' + esc(tok) + '</span>'; } else if (tok.startsWith('fade:')) { out += '<span class="kl-fade">' + esc(tok) + '</span>'; } else if (tok.startsWith('!') || tok.startsWith('#')) { out += '<span class="kl-code">' + esc(tok) + '</span>'; } else { out += '<span class="kl-var">' + esc(tok) + '</span>'; } i = end; } return out;}
// Apply syntax highlighting to all code.kidlisp blocks and hello-code samplesdocument.querySelectorAll('code.kidlisp, .hello-code').forEach(el => { const raw = el.textContent; el.innerHTML = highlightKL(raw);});
// Fetch function popularity(async () => { try { const res = await fetch('/api/store-kidlisp?stats=functions'); const data = await res.json(); popData = data.functions || []; popMeta = { totalPieces: data.total_pieces || 0, totalHits: data.total_hits || 0 }; renderPopularity(); } catch (e) { document.getElementById('popularity').innerHTML = '<p style="color:var(--text-muted)">Could not load popularity data.</p>'; }})();
// Fetch top hits(async () => { const el = document.getElementById('top-hits'); try { const res = await fetch('/api/store-kidlisp?recent=true&sort=hits&limit=30'); const data = await res.json(); const hits = (data.recent || []).filter(p => p.hits >= 500 && p.source.length > 15); topHitsData = hits;
if (!hits.length) { el.innerHTML = '<p>No top hits found.</p>'; return; }
let html = ''; for (const p of hits.slice(0, 15)) { html += '<div class="hit-card" data-code="' + esc(p.code) + '">'; html += '<div class="hit-header">'; html += '<span class="hit-code" style="cursor:pointer;color:var(--accent)">$' + esc(p.code) + '</span>'; html += '<span class="hit-views">' + p.hits.toLocaleString() + ' views' + (p.handle ? ' · ' + esc(p.handle) : '') + '</span>'; html += '</div>'; html += '<div class="hit-source">' + highlightKL(p.source) + '</div>'; html += '</div>'; } el.innerHTML = html; // Wire up click handlers to send piece to parent editor el.querySelectorAll('.hit-card[data-code]').forEach(card => { card.style.cursor = 'pointer'; card.addEventListener('click', () => { const code = card.dataset.code; const piece = hits.find(h => h.code === code); if (piece && window.parent !== window) { window.parent.postMessage({ type: 'kidlisp-load-piece', code: piece.code, source: piece.source }, '*'); } else { window.open('https://aesthetic.computer/' + encodeURIComponent(code), '_blank'); } }); }); } catch (e) { el.innerHTML = '<p style="color:var(--text-muted)">Could not load top hits.</p>'; }})();
// Render the popularity grid (main view of Functions tab)function renderPopularity() { const el = document.getElementById('popularity'); const top = (popData || []).slice(0, 30); if (!top.length) { el.innerHTML = '<p>No data yet.</p>'; return; }
const maxW = top[0].weighted; let html = '<div class="pop-grid">'; for (const fn of top) { const pct = Math.round((fn.weighted / maxW) * 100); html += '<div class="pop-row" data-fn="' + esc(fn.name) + '">'; html += '<div class="pop-name">' + esc(fn.name) + '</div>'; html += '<div class="pop-bar-wrap"><div class="pop-bar" style="width:' + pct + '%"></div></div>'; html += '<div class="pop-count">' + fn.pieces.toLocaleString() + '</div>'; html += '</div>'; } html += '</div>'; html += '<p class="pop-meta">Scanned ' + popMeta.totalPieces.toLocaleString() + ' pieces · ' + popMeta.totalHits.toLocaleString() + ' total views · click a function for details</p>'; el.innerHTML = html;
// Click handlers for rows el.querySelectorAll('.pop-row').forEach(row => { row.addEventListener('click', () => selectIdentifier(row.dataset.fn)); });}
// Show identifier detail card (Pokedex entry)function selectIdentifier(word) { if (!word) return; const ref = KIDLISP_REF[word] || KIDLISP_REF[word.toLowerCase()];
// Switch to Functions tab const fnTab = document.querySelector('.tab[data-tab="functions"]'); if (fnTab && !fnTab.classList.contains('active')) fnTab.click();
const el = document.getElementById('popularity');
if (!ref) { // Unknown identifier — show a minimal card el.innerHTML = '<div class="id-detail">' + '<button class="id-detail-back" onclick="renderPopularity()">← All functions</button>' + '<div class="id-detail-name">' + esc(word) + '</div>' + '<div class="id-detail-desc" style="color:var(--text-muted)">No reference data for this identifier.</div>' + '</div>'; return; }
const w = word.toLowerCase(); const fnPop = (popData || []).find(f => f.name === w || f.name === word);
let html = '<div class="id-detail">'; html += '<button class="id-detail-back" onclick="renderPopularity()">← All functions</button>'; html += '<div class="id-detail-name">' + esc(word) + '</div>'; html += '<div class="id-detail-category">' + esc(ref.cat) + '</div>'; html += '<div class="id-detail-sig">' + esc(ref.sig) + '</div>'; html += '<div class="id-detail-desc">' + esc(ref.desc) + '</div>';
// Stats html += '<div class="id-detail-stats">'; if (fnPop) { html += '<span>Used in <strong>' + fnPop.pieces.toLocaleString() + '</strong> pieces</span>'; html += '<span>Weighted: <strong>' + fnPop.weighted.toLocaleString() + '</strong></span>'; if (popMeta.totalPieces) html += '<span><strong>' + Math.round(fnPop.pieces / popMeta.totalPieces * 100) + '%</strong> of all pieces</span>'; } else { html += '<span>No usage data yet</span>'; } html += '</div>';
// Top pieces using this function const piecesUsing = (topHitsData || []).filter(p => { const src = p.source.toLowerCase(); return src.includes('(' + w + ' ') || src.includes('(' + w + ')') || src.includes(w + ',') || src.includes(', ' + w); }).slice(0, 5);
if (piecesUsing.length) { html += '<div class="id-detail-examples-title">Top pieces using ' + esc(word) + '</div>'; for (const p of piecesUsing) { html += '<div class="id-detail-example" data-code="' + esc(p.code) + '" style="cursor:pointer">'; html += '<span style="color:var(--accent)">$' + esc(p.code) + '</span>'; html += '<span style="float:right;color:var(--text-muted);font-size:11px">' + p.hits.toLocaleString() + ' views</span>'; html += '</div>'; } }
html += '</div>'; el.innerHTML = html;
// Wire up piece example click handlers el.querySelectorAll('.id-detail-example[data-code]').forEach(row => { row.addEventListener('click', () => { const code = row.dataset.code; const piece = (topHitsData || []).find(h => h.code === code); if (piece && window.parent !== window) { window.parent.postMessage({ type: 'kidlisp-load-piece', code: piece.code, source: piece.source }, '*'); } else { window.open('https://aesthetic.computer/' + encodeURIComponent(code), '_blank'); } }); });}
// PostMessage receiver — parent editor sends identifier to highlightwindow.addEventListener('message', (event) => { if (event.data?.type === 'kidlisp-identify' && event.data.word) { selectIdentifier(event.data.word); }});
// Handle ?tab= and ?id= URL paramsconst urlParams = new URLSearchParams(window.location.search);const urlTab = urlParams.get('tab');if (urlTab) { const target = document.querySelector('.tab[data-tab="' + urlTab + '"]'); if (target) target.click();}const urlId = urlParams.get('id');if (urlId) { // Wait for data to load, then select the identifier const waitForData = setInterval(() => { if (popData) { clearInterval(waitForData); selectIdentifier(urlId); } }, 100); setTimeout(() => clearInterval(waitForData), 5000); // timeout after 5s}</script>
</body></html>