summaryrefslogtreecommitdiff
path: root/docs/web.md
blob: 578654d33b8696efb4fa5fb39aa7c9e5cf227d25 (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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# 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.

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
tagline glyph using the build-time `gui_tagline_font_percent`; the percentage is
exported by the module rather than duplicated in JavaScript. Animation time is
also host-independent: `requestAnimationFrame` time is accumulated into 60 Hz
core ticks, so 120/144 Hz displays do not accelerate frame-count transitions
and a returning background tab has bounded catch-up work.

The `web` platform is deliberately not one of the shader-capable native GUI
shells. It exposes no `PanelSlide`/`PanelZoom`/`PanelDissolve`/`PanelAscii`/
`PanelVertical` or `Crt`/`Ripple`/`Glitch` builtins: applying those faithfully
would require a second canvas renderer and give up the DOM renderer's
selectable/accessibility contract. Theme fades and the delayed,
side-effect-free Look hover remain grid animations and continue to use the
fixed 60 Hz ticks above.

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.

Web LOOK's source archive follows Git's working-tree view: tracked `.zig` files
plus new, nonignored ones, sorted by path. The checked-in browser replay is a
real native TTY dump of that same selection; its top and bottom launcher
snapshots jointly cover the complete list before opening `build.zig`.

What is genuinely absent is narrower than "no IO". The module has no threads
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).
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.

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.