diff options
Diffstat (limited to 'docs/web.md')
| -rw-r--r-- | docs/web.md | 67 |
1 files changed, 46 insertions, 21 deletions
diff --git a/docs/web.md b/docs/web.md index 793c79bc..0f8c068f 100644 --- a/docs/web.md +++ b/docs/web.md @@ -1,10 +1,11 @@ # DOM web backend The web backend keeps the existing Pardes core and replaces the old -Emscripten/SDL/WebGL shell. Zig produces a `wasm32-freestanding` module with no -imports. Vanilla JavaScript owns `requestAnimationFrame`, browser input, window -sizing, and effect/IO dispatch; HTML and CSS render selectable, accessible text. -There is no canvas. +Emscripten/SDL/WebGL shell. Zig produces a `wasm32-freestanding` module whose +only imports are the four browser capabilities listed below — no libc, no WASI, +no runtime. JavaScript owns `requestAnimationFrame`, browser input and window +sizing; the CORE owns the loop and JavaScript calls one iteration of it per +frame. HTML and CSS render selectable, accessible text. There is no canvas. The default web build links Tree-sitter's C runtime and the Zig grammar inside that same import-free module. A small freestanding compatibility layer supplies @@ -12,11 +13,20 @@ the C ABI locally, so source panes retain the core's Tree-sitter highlighting without Emscripten, WASI, or browser libc imports. Other grammar tiers remain selectable with `-Dtree-sitter=minimal` or `-Dtree-sitter=full`. -`src/web.zig` is the narrow WASM ABI. Events enter through exported functions, -and `pardes_frame` writes the canonical `Surface` into a packed 20-byte cell -array. `src/web/app.mjs` reads that array in one linear pass and patches stable -DOM nodes. Core effects become browser operations (links, downloads, clipboard) -or `pardes-io` custom events for a process-capable embedding host. +`src/web.zig` is the narrow WASM ABI. Events enter through exported functions +that hand them straight to `Pardes.update`. `pardes_tick` is one +`Pardes.pump` — queued input, effects, render, present — and the host's +`present` packs the canonical `Surface` into a 20-byte cell array that +`pardes_frame`/`_ptr`/`_cols`/`_rows` describe. `src/web/app.mjs` reads that +array in one linear pass and patches stable DOM nodes. + +Animated time is JavaScript's alone. `pardes_animation_tick` spends one fixed +60 Hz step and is the only entry that advances a transition, exactly as the +AppKit shell keeps its display clock apart from its pumps: if every input pump +also advanced a fade, a burst of keys would collapse ten frames into one. +`pardes_tick` never advances animated time itself, and no longer has to undo +anything to avoid it: `Pardes.pump` does not post a tick for its caller, since +only the host knows when a real frame interval has passed. Cell attribute bit 7 carries the core's tagline font role. The DOM keeps every cell at the body grid's fixed width and height, but scales and centres the @@ -65,22 +75,37 @@ and no host filesystem, so there is no startup config directory, init file, ThemeFile, or DumpThemes; LOOK resolves against the build-generated source archive rather than disk, and no language BACKEND is compiled in (`zls_backend` is off for wasm — which also means -`lsp.supports` is empty, so the core never even raises a language query there; -`web.zig`'s prong for it is waiting for a host that links one). +`lsp.supports` is empty, so the core never even raises a language query there, +and the host leaves `lsp` null). Replay terminals retain the same core shape but use Zig's failing IO value; this keeps the module freestanding without instantiating POSIX threaded IO that the browser can never call. -The EFFECTS themselves are all still emitted, each as a numbered code across -the ABI: `spawn` 1, `write` 2, `resize_pty` 3, `open_link` 4, `save_file` 5, -`write_dump` 6, `set_clipboard` 7, `lsp` 8, `watch` 9, `quit` 10, `new_file` -11, `pipe` 12, `read_clipboard` 13. Inbound, `pardes_output`, -`pardes_set_cwd`, `pardes_eof`, `pardes_paste` and `pardes_lsp_response` -exist to answer them. A plain page ignores most of those and gets a replay -viewer; a -process-capable host (the `pardes-io` events above) answers them and gets -terminals. The core does not know the difference — that is the point of the -seam. +`src/web.zig` fills in six methods of `Host.VTable` and leaves the rest null, +and a null method is answered by the core itself rather than forwarded to a +page that could not honour it. The six are `present`, `write_file`, +`write_dump`, `set_clipboard`, `read_clipboard` and `open_link`; the last five +are backed by four imports from the module `pardes` that JavaScript supplies at +instantiation, because a written file and a written dump are both a download: + +| import | effect behind it | +|---|---| +| `host_open_link(url, len)` | `open_link` — `window.open` | +| `host_set_clipboard(text, len)` | `set_clipboard` — `navigator.clipboard.writeText` | +| `host_read_clipboard()` | `read_clipboard` — answered later, or never, by `pardes_paste` | +| `host_download(path, path_len, bytes, len)` | `save_file`, `save_text` and `write_dump`: a file a page writes is a download | + +What the core answers instead of asking: `spawn`, `pty_write` and `pty_resize` +land in the per-instance fallback, so a pane with no child is SILENT rather +than synthetic; `tty_taken` is never taken; any save the page declines resolves +against the core's virtual filesystem, which is also where every read comes +from — the core reads files itself and never asks a host; `watch_file` records +the request; `lsp` answers an empty row set at once (`zls_backend` is off for +wasm, so the core never raises a query there anyway) and `pipe` answers +failure; `wait_input` is null because wasm must never block, and `theme_file` +and `dump_themes` are not reachable without a config directory. `quit` sets the +core's flag, which `pardes_should_quit` reports so the page can stop its frame +loop. Two dependency-free test layers cover the boundary: |
