<feed xmlns='http://www.w3.org/2005/Atom'>
<title>pardes.git/test/macos-snapshots/boot.golden, 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:14Z</updated>
<entry>
<title>Composite tag layers into the macOS e2e captures</title>
<updated>2026-10-01T03:12:14Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-16T14:53:11Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=0b54c599fa5e26fb1754ca0aab53da5fa0e14af1'/>
<id>urn:sha1:0b54c599fa5e26fb1754ca0aab53da5fa0e14af1</id>
<content type='text'>
Tag text stopped living in the frame grid when the core moved the topbar, the
column bar and every pane tag into tag layers of their own; the grid keeps the
chrome and the physical grips. readFrame still read the grid alone, so every
capture recorded blank header rows and each script timed out waiting for text
that was on screen but not in the dump. Flatten the layers back over the grid
at one cell per column — the tty backend's spelling, which these goldens exist
to stay comparable with — and follow pardes_font_take to its size out-param.

The goldens were recorded before the column bar and before the tag gap, so
they are rerecorded here: the captures now agree column for column with what
the tty renderer paints today.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>macos: one tagline rule for both hosts, a kqueue beside the inotify, and effects that compile</title>
<updated>2026-09-01T16:57:41Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-09-01T16:46:44Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=cce6b18a49870086982f9a0e1fda90ed170b9fba'/>
<id>urn:sha1:cce6b18a49870086982f9a0e1fda90ed170b9fba</id>
<content type='text'>
Three things this shell had its own copy of, and in each case the fix is that
it stops having one.

**The tagline band.** A pane tag draws at `gui_tagline_font_percent` of the
body face and the band it sits on shrinks with it, while the grid row stays
body-sized — so something has to decide where the shorter band sits in the
taller row. This shell decided by centring, always, which is precisely the case
`config.gui_topbar_pane_border_px` exists to prevent: the topbar's unused
half-band meets the first pane tag's unused half-band and the window background
shows through the seam. The strip is as wide as the bands are short — on a
20-pixel cell, 4 physical pixels at the default 82%, 10 at 50%, 14 at 30% — so
it grew as the tagline face shrank and read as "the tagline is wrong on the mac"
rather than as one missing rule. The rule is `pardes.taglineBandOffset` in the
core now and both pixel hosts call it: row zero bottom-aligned, the first
pane-tag row top-aligned, the two joined by `gui_topbar_pane_border_px` in the
theme's scrollbar-track colour, every row between centred, and a `Tagbottom`
band on the final row flush with the window edge — with the sub-cell strip
beneath it painted in that band's own colour, because the core grid holds only
whole cells and a window is any height it likes. `pardes_tagline_band_offset`,
`pardes_topbar_pane_border_px` and `pardes_topbar_pane_border_rgb` carry it over
the C ABI as PHYSICAL pixels: the host multiplies its points by the backing
scale going in and divides coming out, which is the snapping `Metrics` already
does for the cell, and is what keeps a one-pixel rule one pixel instead of a
two-pixel smear.

**The watch.** `file_watch.zig` was one mark/reconcile transaction over
`inotify`, so the tty shell, the SDL window and the detached daemon all watched
nothing off Linux: an edit made outside pardes never reached the pane, and a PDF
replaced on disk kept rendering the old inode. It is the same transaction over
two kernels now — `init`, `wait`, `stop`, `drain`, `markDir` and `unmarkDir` are
still the whole of it, and the hosts wait on a kqueue and poll it exactly as
they did the old descriptor. A macOS mark is TWO filters, because a kqueue
directory filter reports its entries changing and never a write to a file
already inside it: the parent mark follows rename-over saves, `markFile` catches
in-place writes, and `remarkFile` re-arms the file filter once a rename has moved
the inode. That is the same pair the AppKit host's DispatchSources already used
for the same reason. Directory marks are deduplicated here by device and inode,
because each `EVFILT_VNODE` filter needs a descriptor of its own and inotify did
that deduplication itself; `stop` and `drain` wake through the one `EVFILT_USER`
filter, since a kqueue cannot simply be read the way an inotify descriptor can.

**The effects.** The three `crt.ci.metal` entry points are
`extern "C" [[stitchable]]`. `CIKernel.kernels(withMetalString:)` compiles that
source at runtime, looks for stitchable functions, and rejects the WHOLE source
with "cannot find a valid stitchable Metal function in the source" when it finds
none — so `ScenePostprocessor.init?` returned nil and every scene effect and
panel transition silently degraded to the plain CoreText draw. The
`effect_sources.zig` test pins the exact spelling of all three, and
`draw-effect` in the e2e suite catches the degradation rather than the spelling.

Beside them, the offscreen harness owes the core a PRESENTATION. Its window is
borderless and never ordered front, so AppKit runs no display cycle and
`pardes_frame_presented` — whose only caller is `draw(_:)` — never fired. The
core holds pointer gestures inert while a layout mutation has not reached a
backend, which for an unpresenting harness is the rest of the script: the first
pane a script opened silently killed every later click, drag and Look. So
`readFrame` presents what it just rendered, into a bitmap nobody reads.
`PARDES_CHROME` also looks under `/Applications`, where a browser's executable
lives inside an application bundle and never on `PATH`. The macOS goldens are
regenerated; docs/macos.md, config.md, detached.md, web.md and the design PDF
follow.
</content>
</entry>
<entry>
<title>term_pane + builtins: terminal pane work, builtins/config additions, snapshots</title>
<updated>2026-08-19T02:45:58Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-08-18T20:53:18Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=dba1ab69e5f38d8daf9312ab2d343b36fa078162'/>
<id>urn:sha1:dba1ab69e5f38d8daf9312ab2d343b36fa078162</id>
<content type='text'>
</content>
</entry>
<entry>
<title>big slow change: prebuilt shaders (SPIR-V/Metal), core gui reflow, docs, web + snapshot refresh</title>
<updated>2026-08-19T02:44:42Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-08-16T18:49:12Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=1551e409c31992437cb2fa864f576d45c8433801'/>
<id>urn:sha1:1551e409c31992437cb2fa864f576d45c8433801</id>
<content type='text'>
</content>
</entry>
<entry>
<title>macos: pixel attachments, live theming, and a signed app</title>
<updated>2026-08-11T12:58:59Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-08-10T20:23:18Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=a797a1ab2f648e773f7a1b28d12bf9441b9f13f4'/>
<id>urn:sha1:a797a1ab2f648e773f7a1b28d12bf9441b9f13f4</id>
<content type='text'>
The AppKit shell now draws what the core renders, follows the theme without a
relaunch, and builds into something you can hand to someone.

- Pixel attachments. Surface.images was dropped on the floor here, so a PDF
  pane showed nothing at all: native_images is now set, pardes_image_s carries
  the geometry the core already clipped, and PardesView keeps one CGImage per
  (serial, page, revision) so scrolling costs a draw and not a decode. Image
  panes get real pixels instead of the petscii fallback.

- Themes take hold live. pardes_tick never advanced the chrome animation, so
  every tagline kept the previous theme's colours until the next launch and
  the 16 ms re-pump spun for the rest of the session. pardes_theme_bg retires
  the hand-agreed #121212 and drives the window background and the titlebar
  appearance; a theme with no background of its own now gets a transparent
  window over an NSVisualEffectView.

- The cell snaps to whole DEVICE pixels rather than whole points. Monaco
  advances 8.4014pt at 14, so ceiling to 9 spaced every column 7.1% wider than
  the face was drawn for.

- The dial is one notch per 10 degrees instead of 20, and a release keeps
  turning in proportion to how hard it was thrown -- ramping up from zero at
  the floor, so a slow twist coasts not a little but not at all.

- A file dropped on the grid is a click plus Look, so it opens beside the pane
  it was dropped on. No drop concept was added to the core.

- The titlebar follows the focused pane: proxy icon, filename, and the dirty
  dot. File.saved_revision is the watermark that last one needed.

- Config (SPC f c) prints the resolved startup config path.

- build.zig assembles, signs and packages the bundle itself; build-app.sh is
  gone. -Dmacos-identity= takes a Developer ID, macos-dmg makes the image, and
  the icon is Glenda.
</content>
</entry>
<entry>
<title>macos: the AppKit shell, its icon, and the offscreen e2e harness</title>
<updated>2026-08-11T12:58:59Z</updated>
<author>
<name>Gabriel Schneider</name>
<email>gbrls@0x4200.cafe</email>
</author>
<published>2026-08-10T12:58:31Z</published>
<link rel='alternate' type='text/html' href='https://git.0x4200.cafe/pardes.git/commit/?id=eb11ab331b4e13e2b9e5a673a4c012d22fdd1d9c'/>
<id>urn:sha1:eb11ab331b4e13e2b9e5a673a4c012d22fdd1d9c</id>
<content type='text'>
</content>
</entry>
</feed>
