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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
# pardes
A text environment in the acme tradition: columns of panes, each pane a tag line
plus a body, where the body is a live terminal, a file, an image, or a PDF. The
mouse carries meaning — left selects, middle executes, right looks — and
everything on screen is text that is equally alive, whether a shell printed it or
you typed it.
One core, five frontends. The core owns editing, layout, rendering, and the
filesystem namespace. Frontends translate native input into `pardes.Event`,
present `pardes.Surface`, and perform host effects such as spawning processes.
Fifteen [native Pardes themes](docs/themes.md) coordinate the editor, search,
diagnostics and embedded terminal: `orchard` (the near-black default), `dusk`,
`ink`, `paper`, `daybreak`, and the Acme-inspired `atelier`. `ink` and
`daybreak` provide high contrast dark and light choices. Six classic-inspired
adaptations add `forge`, `lagoon`, `solarium`, `spectrum`, `harvest`, and `clay`.
`forge_black` and `orchard_black` offer pure-black variations; `forge_soft`
offers a deliberately softer contrast. Execute `ThemeSel` to choose native
themes first, followed by the existing legacy/imported collection. `FocusTint` toggles
the active pane and column tag tints (on by default), and `SyntaxBold` toggles bold
syntax keywords (off by default), in both GUI and TTY.
SDL also supports `Font <name>:<size>` and optional pixel companions in the
unused workspace tag: `Pet cat`, `Pet frog`, or `Pet off`.
`Mini path` opens a braille minimap with syntax colors: two text columns by four
lines per cell. It reads through the normal filesystem namespace; run it again
to refresh. Mini snapshots survive Dump/Restore without rereading the source.
On Linux, `Tty9p` (`SPC n 9`) opens a terminal with the session's 9P tree
mounted through kernel v9fs. It asks sudo in that pane, then starts your shell
as your normal user. Access the tree through `$PARDES_MOUNT`.
See [mounted terminals](docs/v9fs.md).
## Requirements
Zig **0.16.0** (`build.zig.zon` pins `minimum_zig_version`). Dependencies are
fetched and pinned by the manifest; no system package is required for the
terminal build. The SDL shell builds SDL3 and FreeType from source. Native PDF
support builds MuPDF and is on by default (`-Dmupdf=false` to drop it).
Optional 9P-over-QUIC support (`-Dquic=true`) uses system OpenSSL 3.6+ and
pkg-config. The default Unix socket and optional TCP transport do not need it.
## Build
```
zig build
```
That is the whole of it, and it is an *install*: it builds both native shells and
puts them in `~/.local/bin`.
```
~/.local/bin/pardes the terminal shell (libvaxis)
~/.local/bin/pardes-gui the SDL3 window
```
Override with `--prefix <dir>`. Test, benchmark and run steps build what they
need without installing development binaries.
```
pardes --version e.g. pardes 0.0.1 (e61bbb2e86bd)
pardes --help every flag
```
The version comes from `build.zig.zon`'s `.version`; the commit is read from
`git` at configure time and is simply absent when there is no repository to ask.
## The five platforms
| build | what it is |
|---|---|
| `zig build` | the terminal shell and the SDL window, together |
| `zig build -Dplatform=tty` | the terminal shell alone |
| `zig build -Dplatform=gui` | the SDL3 window alone |
| `zig build web -Dplatform=web -Dtarget=wasm32-freestanding -Ddump=<dump.zon>` | a freestanding wasm core plus vanilla JavaScript, rendered as HTML/CSS |
| `zig build -Dplatform=macos` | an AppKit and CoreText app over a static `libpardes.a` |
| `zig build -Dplatform=esp32p4` | a freestanding riscv32 editor object for ESP32-P4, using a 384 KiB heap |
Firmware images are built in the sibling `../05-zig-p4` toolchain, which needs
ESP-IDF register headers. After building the editor object here, `zig build
-Dpardes` there links `src/esp32p4/app.zig`. The separate GPIO 9P image uses
`zig build -Dapp=../02-pardes-code/src/esp32p4_9p.zig` there; it does not link the
editor. Its fixed GPIO namespace is in `src/esp32p4_gpio.zig`.
## Detached sessions
A shell need not be in the same process as the core.
```
pardes --detach=work a core with no terminal of its own
pardes --attach=work become a frontend of it
pardes-gui --attach=work ...the SDL window can attach too
```
Several frontends may be attached at once and all see the same screen. The
detached core performs every effect that needs a disk or a process table, so its
pane shells outlive every frontend; a frontend keeps only what needs the human's
own display. From inside the editor, `Attach` and `Detach` do the same thing as
words. See `docs/detached.md`.
## Tests
```
zig build unit-test module and shell unit tests
zig build test-build compile the unit-test programs without running them
zig build unit-profile test request time and process memory as JSONL
zig build unit-profile-test profiler timing, failure and timeout checks
zig build core-test core tests without native shell tests
zig build config-test configuration tests without compiling the editor
zig build pane-test pane and namespace integration tests
zig build syntax-test tree-sitter tests without building the editor
zig build syntax deterministic per-byte highlighting snapshots
zig build syntax-bench highlighting latency and allocation counts
zig build perf-test benchmark validation without compiling the editor
zig build lspbench-check require every configured language probe to be correct
zig build lspbench-test language benchmark omission and expectation gates
zig build history-test historical measurement and comparison tests
zig build fs-test real sessions and mounts over 9P
zig build agent-session-test interactive session driver checks over 9P
zig build fs-bench-test filesystem benchmark option checks without the editor
zig build 9p-test freestanding protocol tests
zig build quic-test -Dquic=true optional QUIC transport tests
zig build snap scripted input traces against frozen golden grids
zig build snap-driver-test retry evidence, strict failures and fixture isolation
zig build hxdiff differential suite against helix's own behaviour
zig build hxdiff-test comparator, allocation and CLI regression checks
zig build hxdiff-live compare against a freshly run hx-harness
zig build hxdiff-update update reference results only after comparison passes
zig build hxparity file-pane vs pty-pane editing parity
zig build mupdf-check compile, link, render and search docs/design.pdf
zig build web-snap browser highlighting and touch interactions
zig build web-driver-test browser driver timeouts and cleanup
zig build web-e2e Chrome-driven DOM end-to-end suite
```
`snap -- --record=/tmp/captures` writes independent snapshots for comparing
binaries without changing goldens. `snap -- --update` replaces goldens;
`snap -- --no-retry` makes the first failure decisive. Retried runs retain
each attempt's report and mismatching grid in a fresh printed directory.
For custom differential cases, use
`zig build hxdiff -- --strict cases.jsonl reference.jsonl [waivers.jsonl]`.
Without a reference, the driver only emits results; exit zero does not mean a
comparison passed. Custom arguments must include `--strict` to check coverage.
The benchmark steps — `perf`, `pdf-bench`, `pdf-scroll-bench`, `pdf-sections-bench`,
`lspbench`, `fs-bench` — all accept `-- --json`.
Snapshot scripts can use `snap9p` to capture core cells and styles through the
default socket alongside the terminal emulator's independent captures.
`-Dtest-filter=<text>` applies to every unit-test binary. Use
`-Doptimize=ReleaseFast` for performance measurements. To record output and
first/warm command runtimes across revisions, run:
```
jj status
zig build history -- run 'ancestors(@, 2)' /tmp/pardes-history -- zig build unit-test
zig build history -- compare /tmp/pardes-history/<before>.json /tmp/pardes-history/<after>.json 1.20
```
The history runner uses `jj run` serially, with recordings outside its isolated
checkouts without integrating history changes. Run `jj status` before measuring
`@`: the runner deliberately does not snapshot pending edits. `history record`
measures current files directly. Add `run --allow-immutable`
to measure immutable revisions. Each command runs four times; the first run is reported separately
from the warm median. Standard output and errors are preserved for every run.
Commands must exist in the selected revisions. The optional comparison ratio
fails the command when runtime regresses beyond that limit. Add `--snapshots`
to require stable, identical stdout and stderr, or `--benchmarks` to compare
individual `syntax-bench` cases, including allocation counts; the ratio limit
also applies to each case's cold and median time, using medians across the last
three processes. First-process cold times are reported separately, not gated.
All four captures must contain the same cases and input sizes. Old revisions
need the same harness and uncached test execution for meaningful comparisons.
The recorded Zig version identifies the recorder's compiler; record the child
toolchain separately when comparing different compilers.
Recordings are exclusive: use a fresh output directory to repeat a measurement.
Existing results and partial runs are never overwritten.
`perf -- --base old.json` requires matching build metadata, harness, viewport,
repetition count, fixtures, and `--only` selection. Reports identify the compiler,
resolved target/CPU, driver/core/dependency optimization modes, and feature flags.
Missing or different metadata is rejected; old reports must be regenerated.
JSON reports omit unmeasured cells. Gesture checks run
outside the timed interval, and each process owns and removes its fixture directory.
Hardware, machine load and runtime library versions must be matched separately.
`zig build perf -Dplatform=tty -Doptimize=ReleaseFast -Dtree-sitter=zig -- --mini --json`
measures braille generation, full highlighting plus generation, and cached Mini
redraws separately, checking output hashes and allocation balance.
`zig build perf -Doptimize=ReleaseFast -- --terminal-mib 64 --json` streams
colored, wrapped agent-like output past the terminal history limit. It checks
the newest text, colors and input, and measures raw redraws, modal movement,
edit-overlay redraws and memory. Linux RSS comes from the current process image's
`VmHWM`; allocator counts exclude the terminal library's private mappings.
`python3 -B test/agent_session.py zig-out/bin/pardes --ready 'ready text' --min-rows 20000 -- command args`
runs a caller-selected interactive command in a private POSIX shell and checks
its history and an unsubmitted input probe through 9P. Use an owned copy of any
saved conversation.
Reports contain counts, hashes and 9P observation times, not conversation text
or physical keyboard latency. GUI builds need `--gui-grid`.
`test-build -Dtest-rebuild` forces fresh Zig test compilation while retaining
cached C dependencies. Use a disposable local cache for repeated cold-build
experiments. Ordinary `unit-test` always runs its tests, even with cached builds.
`unit-profile` measures the request-to-result interval, including test-runner
communication, and reports whole-process time and memory separately.
Filtered test steps fail if no named test matches, even when import guards pass.
The default differential suites require exact case coverage and reject stale
waivers. Each waiver pins both the reference and editor result. Live Helix
steps use `-Dhelix-harness=<path>`, `HX_HARNESS`, or `hx-harness` on PATH.
## Documentation
Start with `src/panes.zig`, `src/layout.zig`, and `src/fs.zig` for ownership and
operations, and `src/pardes.zig` for input. `docs/design.typ` is the architecture
sketch; `docs/design.pdf` is a retained rendering/search fixture and may lag it.
| file | subject |
|---|---|
| `docs/design.typ` | architecture: the seams, the data model, the build graph |
| `docs/detached.md` | one core, many frontends, over a unix socket |
| `docs/config.md` | build options and runtime configuration |
| `docs/tags.md` | editable workspace, column and pane tags; filename drafts |
| `docs/fs.md` | default 9P service, Look resolution, and named mounts |
| `docs/lsp.md` | in-process ZLS and external language servers |
| `docs/lsp-evaluation.md` | why that backend, measured against the alternatives |
| `docs/helix-keys.md` | the helix-compatible key model and its differential suite |
| `docs/macos.md` | the native macOS shell, its bundle and its signing |
| `docs/web.md` | the browser shell |
| `docs/ghostty-macos-notes.md` | notes on the ghostty dependency |
| `docs/ideas.typ` | scratch notes; nothing compiles it, and it says so |
`next-steps.txt` is a wishlist with a status header saying which items have
shipped; `transactions.txt` records one open structural gap against helix, and
says which waiver proves it is still open.
## Layout
```
src/ core events (pardes.zig), panes, layout, fs, syntax
src/detached/ the wire, the detached core, the frontend client
src/lsp/ ZLS and external language-server backends
test/ harnesses, snapshot goldens, helix cases
build/ build-time helpers (the snapshot suite)
docs/ see above
```
|