summaryrefslogtreecommitdiff
path: root/docs/web.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/web.md')
-rw-r--r--docs/web.md67
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: