summaryrefslogtreecommitdiff
path: root/docs/web.md
blob: ddc09d35d1cf4bf98f8082a837ec494f3fcb60f3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 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.

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
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.

Build a replay from a dump:

```sh
zig build web \
  -Dplatform=web \
  -Dtarget=wasm32-freestanding \
  -Dtree-sitter=zig \
  -Ddump=test/web-snapshots/source-list.dump.zon
```

The output is in `zig-out/web`. Serve that directory over HTTP; browsers do not
allow a useful WASM module load from `file:` URLs.

The browser shell has no argv: every command-line flag `src/main.zig` parses —
`--tty`, `--tty-toggle`, `-n`, `-l`, `--nested`, `-h`, and the positional
file-or-directory — is native-only, because the wasm module roots at
`src/web.zig` and never links `main.zig`. State comes from the embedded dump
instead.

What is genuinely absent is narrower than "no IO". The module has no threads
and no host filesystem, so there is no startup config file, 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).

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.

Two dependency-free test layers cover the boundary:

```sh
# Real WASM in Node plus a small DOM double.
zig build web-harness \
  -Dplatform=web \
  -Dtarget=wasm32-freestanding \
  -Dtree-sitter=zig \
  -Ddump=test/web-snapshots/source-list.dump.zon

# Real headless Chrome, DOM rendering, and browser touch input.
zig build web-e2e
```

Set `PARDES_CHROME` when Chrome/Chromium is not on `PATH` or in a normal
Playwright/Puppeteer cache.