<feed xmlns='http://www.w3.org/2005/Atom'>
<title>pardes.git/test/snapshot.zig, branch main</title>
<subtitle>Pardes</subtitle>
<id>https://git.0x4200.cafe/pardes.git/atom?h=main</id>
<link rel='self' href='https://git.0x4200.cafe/pardes.git/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/'/>
<updated>2026-10-01T03:12:17Z</updated>
<entry>
<title>A program that tracks the mouse gets its clicks, drags and moves over its grid, in the format it chose; Shift keeps pardes's select, execute and look, and chrome is never the program's</title>
<updated>2026-10-01T03:12:17Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-30T17:12:51Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=b546b578baadbd8fdd15b42988ad4d66c9c8ff21'/>
<id>urn:sha1:b546b578baadbd8fdd15b42988ad4d66c9c8ff21</id>
<content type='text'>
Presses and releases under 1000, drags with a button down under 1002,
every move once per cell under 1003, X10 presses under 9, in raw terminal
input only. A press the program took keeps its drag and release, reported
clamped to the grid's edge wherever the pointer goes. The press also makes
the pane the active one. docs/tags.md says so.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Zig files go to zls as a child process through the protocol client, as every other language does: the in-process ZLS backend goes</title>
<updated>2026-10-01T03:12:17Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-30T01:31:27Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=7e8e2655567cab1c28597f44d0aec79e46b0deb8'/>
<id>urn:sha1:7e8e2655567cab1c28597f44d0aec79e46b0deb8</id>
<content type='text'>
lsp_zls.zig (1,522 lines), the zls dependency, build.zig's zls wiring and the import graph it baked into the options only for it, and main.zig's reference go. lsp_client's table gains a zls row (PARDES_LSP_ZIG overrides it); Hover, Rename, Diagnostics, WsSymbols, Lspinfo and gd answer through it (checked with lspprobe and the snapshots against zls 0.16.1-dev). What changes for a user: zls must be on PATH; a completion's rows are zls's candidates at the cursor, where the linked analyser gave each one's declaration; document symbols carry no signature. The Zig snapshots (lsp, lspcomplete, lspcompletemoved, lspdebug, lsprelpath) run the real zls now: lspdebug reads the client's explain and report, lsprelpath spells gr's rows where it spelled a completion's, and their goldens are recorded again (stable over three runs). Lspinfo still opens with its backend line. The ReleaseFast tty binary is 105.35 MB to 96.28 MB, the Debug one 296.5 MB to 245.9 MB. docs/lsp.md, web.md, design.typ and the README say so.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Snapshots leave out a blank cell's ink, which shows nowhere and the tty now keeps from the frame before</title>
<updated>2026-10-01T03:12:17Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-29T21:14:31Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=b8b9e87972f87abd28c5bebf7275e9bdd2754eac'/>
<id>urn:sha1:b8b9e87972f87abd28c5bebf7275e9bdd2754eac</id>
<content type='text'>
</content>
</entry>
<entry>
<title>The default Shell is $SHELL, else /bin/sh</title>
<updated>2026-10-01T03:12:15Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-29T05:23:59Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=45a66bd1c062a5cf4d63b1df68e050b059cdfa1d'/>
<id>urn:sha1:45a66bd1c062a5cf4d63b1df68e050b059cdfa1d</id>
<content type='text'>
With no Shell set, pardes ran fish, which a box may not have and a user
may not use. The default is now the user's login shell ($SHELL), else
/bin/sh; an explicit Shell still overrides it. The tests pin their own
shell (snapshots bash, the python suites /bin/sh or bash) and pass with
SHELL unset; the Config report and docs say where the default comes from.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Shell prompt files are one per content under $XDG_RUNTIME_DIR, so a killed pardes leaves none behind; tests remove their /tmp dirs</title>
<updated>2026-10-01T03:12:15Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T21:13:42Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=4354669aa560c5504b046413f771d02c4216dd45'/>
<id>urn:sha1:4354669aa560c5504b046413f771d02c4216dd45</id>
<content type='text'>
Every pardes host wrote its own pardes-osc133-bash-* and -fish-* files to /tmp and removed them only at a clean teardown, so each killed session, test and crash left two: 72K of them had piled up. They are now written once per content, named by its hash, in the user's private runtime directory, renamed into place whole and shared by every pardes; without that directory the old private /tmp files remain. fish's -C source is quoted. The 9p_io tests remove their runtime dirs with what the listener left in them, and the snapshot runner removes its retry captures when every retry passed.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Step core animation by the shell's clock and sleep to the next wake</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T16:15:41Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=e0b5564f04aa0ae6666f29ef29c768d800fbabce'/>
<id>urn:sha1:e0b5564f04aa0ae6666f29ef29c768d800fbabce</id>
<content type='text'>
A shell now answers Host.now, its monotonic clock in ns, and the pump
advances core animation to it: one .tick per whole 16 ms frame since core
time last stood still. The same animation therefore takes the same time at
60, 120 and 144 Hz, over ssh and after a slow frame (the GUI's re-armed
clock ran them ~25% slow at 144 Hz; the tty's post-frame sleep drifted).
nextWake() lists every core animation in one place: a frame from now while
anything moves, the end of the wait while something only waits (a message
lingering, the look-hover delay), null when idle. Shells sleep exactly that
long, and a wait is jumped to its end in one step, so an 800 ms linger
costs one frame instead of fifty. An overshoot under 1.5 ms after a step is
let go: at 60 and 120 Hz every display frame takes exactly one step (a 16 ms
frame against a 16.67 ms vsync would otherwise double-step every 24th).

The six tick drivers are gone: tty's timer thread only times the wait,
interruptibly (a newer, shorter request cuts short a sleep still timing a
longer one); the GUI's
AnimationClock, web's JS tick bank (pardes_tick now takes rAF's timestamp),
the detached server's and the board's ticks; grid mode steps a virtual
clock straight to each wake. PARDES_TEST_CLOCK, set by the snapshot
harness, gives tty and the detached server the same virtual clock.

Every stepped frame is drawn. The old pump never drew the last frame of a
fade (a .tick asks for no frame, and the fade was over by the check), so a
theme switch stopped at 9/10 of the way until the next input: theme.golden,
themesel.golden and the GUI's acme-light scene move to the theme's true
colours, and nothing else changes. The frozen previous grid is captured only
while a panel transition is chosen, not on every idle frame.
</content>
</entry>
<entry>
<title>Repaint PDF highlights by row, send rasters by shared memory, and animate messages</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-22T20:43:57Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=9695cb098c784792af79c22d46a17f7bc7247d33'/>
<id>urn:sha1:9695cb098c784792af79c22d46a17f7bc7247d33</id>
<content type='text'>
PDF highlights (hover preview, search, selection) are baked into page rasters,
and any change re-rendered the whole page with MuPDF; the TTY then re-sent it
as base64 (4.7 MB a page), the GUI as a new texture. Worse, a pointer motion
over a PDF invalidated the page even when no preview was shown, so every
motion paid that. Now:

- A raster whose baked highlight set equals the wanted one is left alone.
- A highlighted page keeps its clean rows (before highlights and tint); a
  change repaints only the rows of quads that differ, running MuPDF's
  highlight pass (pardes_pdf_paint_highlights) over those clean rows and
  tinting them: the operations a full render performs, so the pixels are
  identical. MuPDF band renders are NOT bit-identical to a whole page (edge
  rows, resampled images), so they are never used to patch; the comment
  claiming otherwise is corrected.
- ImagePlace.patch hands shells the changed rows; the GUI uploads just those
  rows into the texture it holds.
- The TTY probes kitty shared memory (t=s) with an id vaxis never reaches and
  sends rasters as a /dev/shm name when the terminal reads it; direct base64
  otherwise (ssh).
- Shells that take row patches (GUI, TTY with shm) repaint a selection while
  it is dragged instead of only on release.

Latency elsewhere:
- TTY: an animating frame no longer sleeps 16 ms blind; a tick thread posts
  into the input queue, so input inside the frame is handled at once.
- TTY and GUI: queued pointer motions coalesce to the last.
- GUI: a skipped swapchain image re-arms the frame (3 retries); animations
  still tick while nothing presents.
- Editing: the line index is carried across an edit instead of rebuilt from
  a scan of the whole file per keystroke.

Messages fall into their row (ease-in; the GUI slides the band out from under
the tagline, a terminal fades it), stay until the next input as before, linger
MessageLinger ms (default 800), and dissolve (ease-out). MessageAnimation
toggles it; both are settings, in Config and startup files. The snapshot
harness pins the old behaviour. The detached server now ticks animations.

A restored terminal comes back live: the old screen and scrollback (dumped
as clean VT by ghostty's formatter, replayed at the new size; older dumps
fall back to their rendered text), a dim
"restored history" marker, then a new shell in the directory it was in.

Right-click on a line number in a file pane looks at that line (a sticky
context header's number included).

Measured with an external pty driver (TTY), an in-process fence trace
(GUI, PARDES_TEST_LATENCY), and test/pdf_pointer_bench.zig (pixel identity
against the baseline and a whole-page oracle); balanced A/A/B rounds, paired
per-round statistics.

Messages stack: each event gets its own row and its own fall, linger and
dissolve; a line keeps its row until it leaves and a new one fills the first
free row. Announcements and statuses are replaced in place, not stacked.
MessageFall, MessageDissolve and DumpDir are settings Config reports.

Co-Authored-By: Claude Opus 5.5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Notices become tagline bands at the top of the body</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-22T05:21:03Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=43b9d696f9e25ad7589527b91a74666e71ff0472'/>
<id>urn:sha1:43b9d696f9e25ad7589527b91a74666e71ff0472</id>
<content type='text'>
A message, a leader chord and a prompt used to share one row of body text at
the bottom of a pane, wearing the tagline font and nothing else about a
tagline. Now each one is a TagLayer of its own, emitted through the same
renderHeaderLayer the pane and column tags go through, so it gets the tagline
height, the small-font metrics, the band offset and the border for free --
none of which a body-grid row can have by changing its font role. The text is
right aligned. The prompt stays on the canonical grid because it owns a
cursor, and a cursor has to sit on a real cell.

The body starts BELOW the bands rather than under them, the way tree-sitter
context rows already worked. Pane.body_offset is how many rows they took and
Pardes.bodyTop(pane, rect) is the one place that answers "where does the body
begin" -- replacing fifteen copies of `if (tag_bottom) r.y else r.y + BOX_H`
spread across the paint, hit-test, scroll, PDF and image paths, which is what
let the bands and the text under them come adrift. Every notice is painted on
the grid as well, because the grid is what a terminal client draws and a band
it cannot see is a message it never gets; the GUI skips grid cells a tag layer
covers, so nothing is drawn twice.

Three bugs the bands exposed, fixed here:
  - a prompt band flush with the right edge put its caret one column past the
    pane, which the detached wire refuses -- so every frame was dropped for as
    long as the prompt was open. The band now reserves that column.
  - a click on a band mapped to Sel row 0, which is the TAG row: clicking
    chrome expanded a word out of the tagline and ran it as a builtin.
  - a watched file reloading under the editor changed the core without going
    through update, so needs_frame was never set and the reload was never
    drawn. Pardes.invalidate() is the name for that, and the file and theme
    reloads call it.

A session can now drive its own 9P namespace instead of being refused one:
ownMountSuffix answers what a path names inside this editors own tree and
resolve, readLimit and write serve it from memory rather than making the
syscall that never returns. The match is anchored to whole components under
the registrys 9p/pardes/&lt;name&gt;, because a bare /pardes/&lt;name&gt; anywhere in a
string would claim ~/src/pardes/&lt;name&gt;/README -- and, before write learned the
same trick, write the trees bytes over the real file. readFileLimit and
writeFile refuse instead, having no core to answer from.

A toggle setting SETS when given `on` or `off` and only flips when it is bare,
so the report LocationsConfig prints can be fed back as configuration and mean
what it says.

Snapshots: 97/98, from 0/98. The goldens were several commits stale and 17
scripts had stopped running; `config &lt;line&gt;` is a new script command that
appends to the per-script startup config, so a script that clicks body
coordinates pins `Verbose off` instead of counting the rows an announcement
moves. nested-optout is left failing on purpose: two levels of nesting prepend
vaxis F3 codepoints to typed lines, which is a real bug and is written down in
docs/divergences.md with a repro.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Flatten the 9P control tree and move it out of fs.zig</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-20T03:36:50Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=c1990b3e6e196ad41379aa432bf3ccca8a65d9f0'/>
<id>urn:sha1:c1990b3e6e196ad41379aa432bf3ccca8a65d9f0</id>
<content type='text'>
The served tree loses the self/ level: /index /ctl /new /log /screen
/listeners /pane/&lt;n&gt;/... /os, with /src only in -Dembed-sources=true builds
(default off, on for esp32p4). ctl speaks the editor's own language with two
lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/
factory directory becomes one clone file; cons is gone (exec Msg); name and
sel are files; stats report real lengths, modes and mtimes; /log streams
pane new/del/rename/save events. The tree code lives in src/ninep/
(tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host
access, mounts, resolution and find/grep. Same engine and transports.
README (fs-help.txt) and docs rewritten; tests updated and extended.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Refactor panes and filesystem; replace FUSE with 9P</title>
<updated>2026-09-07T16:59:12Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-06T21:11:36Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=60367d8fe23f6af98ec28e3cf6c2094dfe332df0'/>
<id>urn:sha1:60367d8fe23f6af98ec28e3cf6c2094dfe332df0</id>
<content type='text'>
Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples.

Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
</content>
</entry>
</feed>
