lumbda/wasm/app/style.css
russell@unturf.com 346b873247
wasm: three-tier Lumbda to WebAssembly + browser playground
Adds a parallel build of all three Lumbda implementations to WASM, a
single-page playground at www/playground/, and a verified test suite.

Tiers
  - Python: Pyodide (CPython-in-WASM) hosting lumbda.py
  - C:      Emscripten build of c/ (tree-walker + bytecode VM; jit.c
            stubbed, gc.c uses its existing no-Boehm fallback)
  - Asm:    hand-written asm/lumbda.wat — parallel impl to asm/lumbda.s.
            Reader, eval (lambda/define/if/cond/let/and/or/quote/set!),
            recursion across mutated top-level env, bump allocator with
            memory.grow, 24 primitives. ~1200 lines of raw WAT.

SPA (wasm/app/, deployed to www/playground/)
  - CodeMirror 6 editor (Scheme highlighting) on left, output on right
  - Radios: 4 demos (Mandelbrot, Fib+Ack, Sieve, self-interp meta-eval)
            x 4 tiers (Python | C | Asm | All three)
  - All-three mode renders the three tier outputs side by side with
    per-tier elapsed timing

Tests (38 verified assertions)
  - 20 unit (Node): per-tier module loads, eval smoke
  - 8 integration (Node): each demo on c+asm WASM byte-matches the
                          canonical native Python run
  - 10 functional (Playwright headless Chromium): page mounts, every
                          demo runs on every tier, all-three renders

Makefile
  - Root targets: wasm-build, wasm-test, wasm-test-fn, wasm-serve,
                  wasm-deploy, wasm-clean
  - wasm/Makefile orchestrates the three tier builds; deploy copies
    dist/ into www/playground/

Asm tier notes
  - WAT linear symbol intern + linear env lookup is MOAD-0001 at scale;
    documented in the asm/lumbda.wat header and in the SPA footer. The
    demos hit ~30 globals so the linear walks are cheap enough.
  - Bump allocator never frees (matches asm/lumbda.s heap discipline);
    memory.grow expands by 1 MB chunks. Browser tab tears down at unload.

Toolchain (developer prerequisites)
  - Emscripten 6.0.0 via emsdk at ~/git/emsdk
  - wabt 1.0.36 at ~/git/wabt
  - Playwright for functional tests (symlinked from ~/git/agnt)
2026-06-14 11:40:34 -04:00

183 lines
3.3 KiB
CSS

/* Lumbda playground SPA — vanilla CSS, mono terminal aesthetic */
:root {
--bg: #0f1115;
--fg: #d6dadf;
--dim: #8b9099;
--accent: #6ee7b7;
--warn: #fbbf24;
--err: #fb7185;
--pane: #15181f;
--border: #262a33;
--mono: ui-monospace, "SF Mono", Menlo, Consolas, "Courier New", monospace;
}
* { box-sizing: border-box; }
html, body {
margin: 0; padding: 0;
background: var(--bg);
color: var(--fg);
font-family: var(--mono);
font-size: 13px;
height: 100%;
}
header {
padding: 16px 24px 8px;
border-bottom: 1px solid var(--border);
}
header h1 {
margin: 0 0 4px;
font-size: 18px;
font-weight: 600;
color: var(--accent);
}
header h1 .sub {
color: var(--dim);
font-weight: 400;
font-size: 13px;
}
header .tag {
margin: 0;
color: var(--dim);
font-size: 12px;
line-height: 1.5;
}
header code {
color: var(--warn);
font-size: 11px;
}
header strong {
color: var(--fg);
font-weight: 600;
}
.controls {
padding: 12px 24px;
border-bottom: 1px solid var(--border);
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
}
.controls fieldset {
border: 1px solid var(--border);
border-radius: 4px;
padding: 4px 10px 6px;
margin: 0;
}
.controls fieldset legend {
color: var(--dim);
font-size: 11px;
padding: 0 4px;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.controls label {
margin-right: 10px;
cursor: pointer;
user-select: none;
}
.controls label input { margin-right: 4px; }
.controls button {
background: var(--accent);
color: #0a0c0f;
border: none;
padding: 6px 16px;
border-radius: 4px;
font-family: var(--mono);
font-weight: 600;
font-size: 13px;
cursor: pointer;
}
.controls button:hover { filter: brightness(1.1); }
.controls button:disabled { opacity: 0.4; cursor: wait; }
.controls .status {
color: var(--dim);
font-size: 12px;
margin-left: auto;
}
.controls .status.busy { color: var(--warn); }
.controls .status.err { color: var(--err); }
.controls .status.ok { color: var(--accent); }
.panes {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1px;
background: var(--border);
height: calc(100vh - 220px);
min-height: 360px;
}
.pane {
background: var(--pane);
padding: 8px 12px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.pane h2 {
margin: 0 0 8px;
font-size: 11px;
font-weight: 500;
color: var(--dim);
text-transform: uppercase;
letter-spacing: 0.08em;
}
#editor {
flex: 1;
overflow: hidden;
}
.cm-editor { height: 100%; font-size: 13px; }
.cm-editor.cm-focused { outline: none; }
#output {
flex: 1;
white-space: pre;
overflow: auto;
background: #0a0c10;
border: 1px solid var(--border);
padding: 8px 10px;
font-size: 13px;
line-height: 1.35;
border-radius: 2px;
}
#output .tier-block {
margin-bottom: 14px;
}
#output .tier-block h3 {
margin: 0 0 4px;
font-size: 11px;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 0.08em;
font-weight: 600;
}
#output .tier-block .time {
color: var(--dim);
font-size: 11px;
font-weight: 400;
}
#output .tier-block pre {
margin: 0;
white-space: pre;
}
#output .err { color: var(--err); }
footer {
padding: 8px 24px;
border-top: 1px solid var(--border);
color: var(--dim);
font-size: 11px;
}
footer code {
color: var(--warn);
}
footer a {
color: var(--accent);
text-decoration: none;
}