<feed xmlns='http://www.w3.org/2005/Atom'>
<title>pardes.git/src/detached/client.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:16Z</updated>
<entry>
<title>Shader files compile again on save; an attached GUI runs the session's post chain</title>
<updated>2026-10-01T03:12:16Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-29T12:43:05Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=826ec1fac3c012e71ab34207328faaac5b5827b7'/>
<id>urn:sha1:826ec1fac3c012e71ab34207328faaac5b5827b7</id>
<content type='text'>
shader_build.zig takes the Shadertoy compile out of the GUI's Post: the
process that holds the core compiles each chain file behind the prefix on a
thread, watches its directory (file_watch's new shader slots), and on a
change reads and hashes each file, compiling only one whose bytes moved. A
failed compile keeps the last good SPIR-V and is said once; the same bytes
are never compiled or reported twice. A file is read with std, not fs.zig's
readFile, whose turn hand-off a worker thread does not hold.

A detached session compiles for its frontends and sends them the chain
(wire post: each pass's scene and level or the file's SPIR-V, and
ShaderAnimation) on attach and on every change, so an attached GUI runs the
same passes, levels and animation as a local one while still reading no disk
and running no program. The attached GUI describes its frames to the chain
from the session's chrome and redraws an animating chain on its own.
</content>
</entry>
<entry>
<title>Trial merge: 9P + helix + render</title>
<updated>2026-10-01T03:12:15Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T20:14:29Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=8509453c2fa78357187f1c4a7e99bfddf29027d8'/>
<id>urn:sha1:8509453c2fa78357187f1c4a7e99bfddf29027d8</id>
<content type='text'>
</content>
</entry>
<entry>
<title>The GUI draws in tiers from the regions; pane chrome moves with its pane</title>
<updated>2026-10-01T03:12:15Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T17:59:21Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=360b72ade0acfa655a3740b638a0cc8e23238a05'/>
<id>urn:sha1:360b72ade0acfa655a3740b638a0cc8e23238a05</id>
<content type='text'>
Stage 8 of docs/render-pipeline.md. The frame is drawn in groups: tier 0,
one per track in paint order (tiers 2 and 3), tier 4 (notices, then guides
and the debug box), tier 5 (bar cursors), each cells, images, decor. Decor
(rules, rails, thumbs, grip marks, spines, the workspace and column rules,
the bottom band, notice rules, bar cursors) is whole-pixel rects through
the new decor.frag over ui.vert, so it carries its track's transition and
clip. A closing pane's chrome comes from Surface.previous_regions.

Deleted: taglineBaseRgb, topbarPaneBorderHeight, bottomTaglinePresent,
frameChromeBg, cellBackgroundIs and the rail inference, paneGripCell's
scan, transient_on, PaintPlan. One cover map (coverFrame) is marked from
the layers and regions once a frame. New regions column, guide and debug;
Surface.chrome is the palette, carried in wire v8 (not shipped yet), so an
attached GUI draws the same chrome (test). A per-instance clip replaces the
vertical transition's scissor.

Goldens: 01-12 byte-identical. 13-17 differ only past the grid: the image
pass left its scissor at the grid's size, cutting rule ends, rail feet and
the bottom band in the leftover pixels whenever a picture was on screen.
16-debug now shows the debug box, which a context-row layer had hidden.
18-mid-transition is new: virtual clock (PARDES_TEST_CLOCK in the GUI),
PanelSlide Newcol with the picture, frame 6 of 12.

Also: the GUI sleeps when idle instead of polling every 16 ms, and a
minimized or occluded window sleeps through animation. Tracy 'gui frame
build' at 200x60: 992/1015 us median before, 989/987 us after.

Shared files touched: pardes.zig (one export), detached/client.zig,
detached/server.zig, detached/wire.zig (all additive), gui.zig. Not touched:
Messages.zig, mouse.zig, tagline.zig, colors.zig, tty.zig, host_io.zig,
dump.zig, exec.zig, panes.zig.
</content>
</entry>
<entry>
<title>One Layer for tags, notices, headers and bodies; a taller tag is one layer of rows; wire v8</title>
<updated>2026-10-01T03:12:15Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T17:37:29Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=d88e381793c6505bf3f2af43705bd2fa32f55ef6'/>
<id>urn:sha1:d88e381793c6505bf3f2af43705bd2fa32f55ef6</id>
<content type='text'>
src/Layer.zig merges TagLayer and BodyLayer. `rows` (0 = no layer) and a
cursor at {x, y}. A tag of N rows is ONE layer of N grid rows: tagHit answers
the row as `line`, bodyHit keeps its meaning, and the per-line layer bases
(TAG_LINE_LAYER_BASE, HEADER_LINE_LAYER_BASE) are gone, not aliased.

Wire v8, the one bump: tag layers carry rows and cursor y, and the frame
carries the placed region list. v7 and v9 peers are refused in both
directions (server test over both, a new frontend test over both).

web: tag_layer_value 11 = rows, 12 = cursor y; app.mjs lays every row.
macOS: the Zig side compiles against Layer; pardes.h still sees one row
per tag layer (accepted, the macOS shell is ignored for now).

No visual change: snapshot goldens and the 17 GUI goldens byte-identical.

Shared files touched: pardes.zig, Messages.zig, gui.zig, macos.zig,
detached/client.zig, detached/server.zig, detached/wire.zig (plus web.zig,
app.mjs, edit.zig, look.zig). Not touched: mouse.zig, tagline.zig,
colors.zig, tty.zig, dump.zig, exec.zig, host_io.zig, panes.zig.
</content>
</entry>
<entry>
<title>A Restore answers its writer before hanging up, and the log records dumps and restores</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T15:44:57Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=6fc9f416938b59b665b1fddc052402770a21cc3f'/>
<id>urn:sha1:6fc9f416938b59b665b1fddc052402770a21cc3f</id>
<content type='text'>
A client that wrote Restore saw its connection cut with no answer, and could
not tell a Restore from a crash. The listener now lets the writer's answer out
before the cut, and cuts only the old editor's connections, refusing their
requests meanwhile; a client that dials during it is the new editor's and
stays. Dump logs 'dump &lt;path&gt;' and the restored editor's log 'restore &lt;path&gt;'.
Keeping connections across a Restore was weighed and left: the fids name the
old editor's panes and opens, so it would mean carrying serials and open
records into the new one, where acme's Load only adds windows. tty's Restore
also closed its shells' ptys without reaping them; it retires them now.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Give a pane's body its own Text holding the cursor, selections, mode and undo</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T02:06:11Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=95a3a1bb822e583988bfe0f312d0ef219b27a454'/>
<id>urn:sha1:95a3a1bb822e583988bfe0f312d0ef219b27a454</id>
<content type='text'>
acme keeps what edits a text in its Text (dat.h:171-190) and the window
holds a body and a tag of that type. The cursor, the selections, the modal
state and the edit-buffer undo move off Pane into Text.zig, Pane holds
them as its body, and the edit and normal-mode operations take the Text
they edit. Nothing changes in behaviour; this is the step that lets the
tag become a second Text.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Give the pipe its own fields in Pipe</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T01:46:26Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=71a9f04342493325c10b1b9bc122a88d41143782'/>
<id>urn:sha1:71a9f04342493325c10b1b9bc122a88d41143782</id>
<content type='text'>
Not a pure move: state moves. Pardes's `pipe_seq` and `pipe_wait` become
Pipe.zig's own fields `seq` and `wait`, and Pardes embeds one as
`pipe: Pipe = .{}`. pipeRequest reads only the request in flight, so it now
takes `pipe: *const Pipe` and the four shells call
`core.pipe.pipeRequest(id)` (their Pipe imports go away again); the other
pipe functions still need the panes and keep `p: *Pardes`, writing
`p.pipe.seq/wait`. Field reads in dump.zig, the shells' tests and two test
files follow (`core.pipe_wait` becomes `core.pipe.wait`). No behaviour
change.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Move saving and loading a whole editor out of pardes.zig into dump.zig</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-28T01:34:56Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=7ba54de4bd498c2a56ea3d0d43c88356759f7b1f'/>
<id>urn:sha1:7ba54de4bd498c2a56ea3d0d43c88356759f7b1f</id>
<content type='text'>
Pure move, no behaviour change (acme keeps dump and load in rows.c):
dumpState, restore, initFromDump and initDump go verbatim to the end of
dump.zig, after the dump format they read and write. Inside dump.zig the
moved code's `dump.` prefix drops, so `Pane` there is the dump record; the
one editor pane it names is spelled `pardes.panes.Pane`, and its other
`panes.X` references become `pardes.panes.X` because dump.zig's own tests
use `panes` as a local name.

The methods become free functions taking `p: *Pardes`: `p.dumpState()`
becomes `dump.dumpState(p)`, `core.restore(bytes)` becomes
`dump.restore(core, bytes)` and `Pardes.initFromDump(..)` becomes
`dump.initFromDump(..)`, in pardes.zig, the shells, layout.zig,
Terminal.zig, builtins.zig and the tests (38 receiver rewrites plus the
initFromDump calls). The tag-tail restore helpers stay with the tag code.

Co-Authored-By: Claude Opus 5.5 &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>Separate tag text geometry from physical pane grips</title>
<updated>2026-09-15T20:24:42Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-15T19:28:57Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=1ab4c071be58033d5452f69797ca8e2c32240cff'/>
<id>urn:sha1:1ab4c071be58033d5452f69797ca8e2c32240cff</id>
<content type='text'>
</content>
</entry>
</feed>
