summaryrefslogtreecommitdiff
path: root/build.zig
Commit message (Collapse)AuthorAge
* Builtins are chips that link to a glossary generated from the registry, and ↵Gabriel Schneider15 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | the virtual filesystem is drawn as a tree #word draws a builtin as a chip on the tag's ground over a rule (Look and Exec in acme's look green and exec red), a <code class="word"> in HTML, and links it to its entry in a new Builtins chapter wherever that chapter is in the same output (the book, the site). The chapter is drawn from docs/typ/builtins-tty.json and builtins-gui.json, which `zig build builtins-json -Dplatform=tty|gui` writes from the registry and the Help rows (src/builtins_json.zig, tools/gen_builtins_json.zig; summaryOf in ninep/ctl.zig is now pub); style.typ merges them, marking the window-only words. The doc steps refresh their build's file first, and each build's unit-test fails when its file is stale. Everyday words first (`everyday` in style.typ), then groups by leader prefix as Help has them, then settings, the words with no path, debugging and internals, and last an A-Z index of every word. `zig build book` joins the doc steps. #fstree draws a directory tree with its connectors, a description column and links to the sections that document each file; reference.typ draws the served tree with it, each pane, pty and column file on its own line, and a test in config.zig checks it against ninep/tree.zig's files both ways. The cheatsheet has a two-level version. On the site the tree reads as `tree` text in w3m and wraps descriptions under the names on a phone. The site's tutor draws its B1, B2, B3 and 1-2 chords as mouse glyphs; the landing credits Plan 9 and acme, as does the book's title page; install.typ no longer lists the mirrors (the footer does). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The docs become a website: a page per chapter, a landing page with ↵Gabriel Schneider16 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | recordings, one Typst bundle run docs/site/site.typ writes every page in one bundle compile: the landing page (the tagline, the recordings listed in media/clips.toml, links to start from), a page per entry of `chapters` in style.typ, and the tutor. Every page shares a tag-line header with the chapter words; #doc links resolve to the right page and anchor; a chapter with four or more sections gets an "On this page" list. Links are relative and pages flat, so it reads from file:// or any static server. `zig build site` installs it to <prefix>/share/doc/pardes/site and fails plainly without typst. The look is 0x4200.cafe's lapis layout (framed panels, hard shadows, a tag-line trail, Departure Mono headings over Crimson Pro) in orchard's greens and amber, forestbones_light when the reader prefers light; both fonts are OFL and shipped. In HTML a key is one pill with its + and ›, and a mouse button is an inline SVG in currentColor. The tagline lives once in style.typ for the sheet, the book and the site, and the single page html.typ gives way to the site. The landing leads with one plain sentence (columns of panes, click to run or open, helix keys, scripts through files), then the tagline and a line on where it runs; a hero image is the first ready clip's poster; "Start: the guide" leads the links, "Hands on: the tutor" next, the rest a plain row; the captions use no undefined words. docs/typ/install.typ holds the install lines once: setup.typ has an Install section that includes it, and the landing includes it too. Building says up front it is for contributors and points at setup to install. In HTML the guide's glossary is a closed <details> (#glossary, the same heading and table on paper), each drawn mouse carries hidden text (left-click, hold left, click middle) for screen readers, w3m and copying, and the footer takes a source link and a licence from `project` in style.typ once decided. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Release Pardes 0.16Gabriel Schneider28 hours
|\ | | | | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | -Dplatform=esp32p4 with another -Dtarget is refused, as build.zig meant: the ↵Gabriel Schneider29 hours
|/ | | | | | | | | | | check reads the target asked for, not the board's pinned one The refusal compared the resolved target, which build.zig pins to the board for esp32p4, so it could never fire and -Dtarget=x86_64-linux built firmware anyway, silently. It now reads -Dtarget as given and refuses any target but riscv32-freestanding. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A one-page A4 cheatsheet, the first file of a Typst docs corpus in docs/typGabriel Schneider29 hours
| | | | | | | | | | | | docs/typ/cheatsheet.typ holds the facts as semantic calls (#key, #btn, #chord, #word, #tag, #addr, #file, #cmd, #doc); docs/typ/style.typ defines those once, drawing them for paged output and emitting HTML elements under --features html; docs/typ/cheatsheet-a4.typ is the page. `zig build cheatsheet` renders it when typst is on PATH. A config.zig test reads every .typ in docs/typ and fails when one names a builtin, key binding or default tag pardes no longer has. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The PDF benches share their clock, fatal exit, hash, count parser and effect ↵Gabriel Schneider29 hours
| | | | | | | | drain, and lspprobe is lspbench's probe subcommand B4: test/bench_util.zig holds what pdf_bench, pdf_pointer_bench, pdf_scroll_bench and pdf_sections_bench each copied (nowNs x4, fatal x3 named per bench, mix x3, parsePositive/parseCount x3, drainEffects x2). They stay four entry points: pdf_bench links its own mupdf module (with its C source and c_heap) where the others link the core's, which one executable cannot import under one name, and their reports and identities are each bench's own. pdf_sections_bench had rotted since the pane's cursor and selections moved to its body and the render revision became next_raster_revision; it builds and runs again. C6: tools/lspprobe.zig is lspbench's `probe` subcommand (zig build lspbench -- probe <kind> <file> <line>:<col>), and the lspprobe build step goes; docs/lsp.md names the new command. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Zig files go to zls as a child process through the protocol client, as every ↵Gabriel Schneider29 hours
| | | | | | | | other language does: the in-process ZLS backend goes 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 <[email protected]>
* The GUI embeds its shaders' GLSL from shaders/ always: the 14 prebuilt ↵Gabriel Schneider29 hours
| | | | | | | | copies, identical to their sources, go, and so does the label saying which was used shaders/prebuilt/*.glsl were byte-identical to shaders/X.glsl or to prefix.glsl + post/X.glsl (checked with cmp, all 14), kept only so a -Dprebuilt-shaders build could name them; EffectCode and Config then labelled the GUI's shader source as live or prebuilt. The GUI embeds shaderSource (a concatenation, no glslc) either way now; the prebuilt SPIR-V stays, and zig build shaders refreshes only it. Config's report and EffectCode lose their GUI shader source line. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* shader_build.zig is ShaderBuild.zig: a file that is a struct (its fields are ↵Gabriel Schneider29 hours
| | | | | | | | the post chain's builds) takes a type's name The naming the split agreed on: a file with fields is a type, TitleCase, as Messages.zig and Layer.zig are. Its importers bind it as ShaderBuild; comments and docs/render-pipeline.md name the new file. No behaviour changes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Merge: loose end + msize 64K + docs slimming + docs-audit fixes + monkey-gui ↵Gabriel Schneider29 hours
|\ \ | | | | | | | | | + tty ssh acked frames
| | * vaxis sends a frame with nothing changed as nothing: its secondary-cursor ↵Gabriel Schneider29 hours
| | | | | | | | | | | | test read equal as changed
| | * A blank whose ink alone changed is skipped in vaxis's own frame diff, not ↵Gabriel Schneider29 hours
| | | | | | | | | | | | weighed up per cell in the tty
| * | The GUI monkey lives in test/ and runs as zig build monkey-guiGabriel Schneider29 hours
|/ / | | | | | | | | | | It was a scratch tool (.scratch/render/tools/gui_monkey.py) that the gates rely on and that imported fs.py from a workspace since removed. It is test/gui_monkey.py now, importing from its own directory, and zig build monkey-gui -Dplatform=gui runs it on the built window, the same hidden test windows; it exits 1 when a combination fails, and its outdir is optional. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* / A 9P monkey drives random requests at a throwaway session and checks the ↵Gabriel Schneider29 hours
|/ | | | | | | | | | | | | | | | | | | documented rules test/monkey9p.py starts pardes --detach with its own HOME, XDG dirs and runtime dir, speaks raw 9P (test/ninep.py) with a timeout on every reply and Tflush for held reads, and after each seeded operation checks that pardes is alive, the one-failure rule over a marker-delimited log window (with and without a follower), /index against /pane, and chunked against whole reads. Every run writes a replayable ops log; a failure is saved with its seed and step, shrunk by delta debugging over replays, and the run goes on in a fresh session. Teardown makes the fuzzer a subreaper and kills every process carrying the session's token. zig build monkey-9p -- --seed N --steps M | --replay F | --shrink F; a 150-step fixed-seed smoke run is part of fs-test. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Merge: 9P round 23 + perf fixes + input garbage fixes + Crt docGabriel Schneider29 hours
|\ \
| | * Terminal replies and malformed escape sequences never become typed textGabriel Schneider29 hours
| |/
* / Lspinfo lists pardes's own dependency imports only for a file in pardes's treeGabriel Schneider29 hours
|/ | | | | | The in-process ZLS's report listed the module map of the build pardes came from (vaxis, ghostty and the rest) for any .zig file, a project that has none of them included. It now lists them only for a file in that tree (project_root, a build option), and says there are none for any other. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The theme's chrome is worked out once, not per grapheme; still post passes ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | let the GUI rest; a perf gate somtrmsz's contrast floors made ChromeTheme.fromTheme run its focus-tint and separator searches (pow calls each), and Output's RowDecoration.styleAt called it for every grapheme of every highlighted row: the 50k-line file's render went from 0.11 ms to 13 ms. Pardes.bodyChrome now keeps the theme's chrome, worked out again only when the theme differs; recolorSyntax asks for it once a decorated row, and a plain row (nearly every row of a file) never asks. ReleaseFast, medium fixture, median us, before -> after (main): render 13150 -> 76 (111), key-down 14658 -> 79 (110), wheel 13589 -> 76 (109), open 14891 -> 2095 (2055), edit-char 16703 -> 3006 (1251; the rest of that gap is editing and tree-sitter, not this). Post.animating asked for frames whenever the window had focus and any pass was ready, so a still pass kept the GUI drawing at the display's rate (Bloom: 49% of a core idle, in a hidden test window). A pass now says whether it moves on its own: the CRT (its hum and dither) and a Shadertoy file whose source reads iTime, iFrame or iDate (shader_build.readsTime; the flag rides the wire's post message); Bloom, Vignette and Grain are still. Bloom idle: 49% -> 1.2%, as with no pass. zig build perf-gate: the 50k-line file's gestures, each's fastest sample within 3x of the recorded baseline's (test/perf-baseline-<platform>- <optimize>.json, recorded from this build), run with every unit-test. On the regressed code it fails at 180x for render.
* Shader files compile again on save; an attached GUI runs the session's post ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | chain 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.
* Merge: 9P rounds 9-17 + render DPI, dark tints, G4-G10Gabriel Schneider29 hours
|\
| * G4: bloom, vignette and grain in the post chainGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three bundled Shadertoy passes beside Crt, each `Bloom|Vignette|Grain 0..3` (off by default, nothing drawn while off), in shaders/post/: - Bloom: what is brighter than the page (a channel's linear peak over 0.75/0.7/0.6 and over the page's own + 0.15, so a light page does not bloom) glows, 2/3.5/5% (§8.2's budget). The glow is a dual Kawase blur in RGBA16F textures at half size and down, 3/4/5 steps by level (shaders/bloom-down, bloom-up; Post.bloomChain), composited in linear light and dithered. - Vignette: round, from halfway to a corner, 10/18/28% at the far corners, in linear light, dithered. - Grain: a still, triangular-spread grain of 1.5/3/5 of 255 on page-coloured pixels only, a logical pixel big; still, so it asks for no frames. Focus rule: the shell passes every bundled pass the rectangles of the tags with their grips, the column and workspace tags, the notices and the cursor (pardesSpare, at most 32, neighbours on one row merged), and a bundled pass leaves them as they are; and a per-theme ceiling (pardesCap, Post.capsOf) keeps the page's text and the selection's text at min(their contrast, 4.5) under each pass at its strongest. User files get no ceiling and no spared rectangles. Fix found on the way: SDL GPU binds at most 4 KiB of a uniform block, so every Shadertoy colour after iPalette (iBackgroundColor, iForegroundColor, the cursor and selection colours, iPalette[>=4]) read zero. The prefix's block is cut in three (Globals, Palette, Colors) with ghostty's names and order, each pushed as a slice of the one struct; pardes's own block moves to binding 3. Tests: the caps hold over every native theme; each uniform slice fits 4 KiB; the prefix's members in ghostty's order across its blocks. Gates: tty/gui unit-test and core-test pass; snap fails only nested-optout; GUI goldens 01-08 byte-identical (09-18 wait on wkzlsqvy, as in somtrmsz); web builds. Feel review material: .scratch/render/{bloom,vignette,grain}/ (levels on forge, dusk, acme, crops, a levels mp4).
* | $ may end any alternative in a pattern, not only the lastGabriel Schneider29 hours
|/ | | | | | | | | | mvzr refuses $ anywhere but at the very end, so /h$|zz/ and Edit ,x/r$|pi/ were bad patterns while /zz|h$/ worked. Its matcher already ends an alternative at .end, so a build-time patch, anchor-checked like the group one, lets $ stand before | and ). The upstream report is in docs/mvzr-group-backtrack.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Merge: 9P rounds 7-8 + layout files + loose ends + G3 cursorGabriel Schneider29 hours
|\ \
| * | The board image links again: sources are no longer embedded by default on ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | esp32p4 uzksuumxruzr turned -Dembed-sources on for esp32p4 (before it, the board was the one build without them). The sources are ~1.8 MB against a 1.5 MiB app partition, so the object built but the firmware did not link: .flash.rodata overflowed by 786392 bytes and .flash.text by 1732312. Default off everywhere; -Dembed-sources=true still embeds them. Without them the image is 1459760 B, 113104 B under 1.5 MiB. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | config-test goes: config.zig is the core now, and core-test runs its testsGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | config.zig imports pardes.zig and builtins.zig, so it cannot be compiled "without the editor" any more; the step failed with 40 missing-module errors (zls, uucode, cloud9, mupdf, ...). Its two tests (Space-k unbound, wheel drift guard) already run in core-test, checked with -Dtest-filter. Wiring every core import into it would only rebuild core-test for two tests. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | Only kept builds carry the commit, so a jj new rebuilds nothingGabriel Schneider29 hours
| |/ | | | | | | | | | | | | | | | | | | | | The git commit sat in the options module every binary imports, so each HEAD move (jj new, jj commit) recompiled every test and snapshot binary: measured 24.4 s and +0.6 GB of .zig-cache for test-build -Dplatform=tty after one no-op HEAD move. Now -Dstamp-commit decides, defaulting to on for release builds and the bare zig build that installs into ~/.local; every other Debug build prints `pardes <version>` alone. After: 0.21 s, +0 bytes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* / A repeat inside a regexp group gives back to what follows the groupGabriel Schneider29 hours
|/ | | | | | | | | | | | | | On aab, /a+ab/ matched but /(a+)ab/ and /(a*)ab/ missed, and so did (.+)_area and ([a-z_]+)_area, in addr, Edit and look alike. mvzr's hasAlt counts a group's own ) as an alternative, so every group with pattern after it takes matchGroup's alternatives branch, which never tries a repeat's shorter matches. No newer mvzr fixes it (trunk is the pinned commit), so the build patches the fetched source as it does the step budget, anchor-checked: when the rest fails after a group, the group's shorter matches are tried, longest first. docs/mvzr-group-backtrack.md is a report for upstream. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Trial merge: 9P + helix + renderGabriel Schneider29 hours
|\ \
| | * Ripple and Glitch are gone; Crt staysGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The user tried them live and wants them removed entirely: the builtins, their levels, config keys and leader paths (tR, tg), their slots in the built-in chain, their shaders (source and prebuilt), EffectCode paths, and on macOS their scene flags (pardes.h), the Metal kernel's coordinate warps and the Swift pointer mapping that mirrored them. Docs say so (decision 8 in docs/render-pipeline.md); config.md documents Crt's levels, Shader and ShaderAnimation instead. Shared files touched: macos.zig (flags). Not touched: pardes.zig, Messages.zig, mouse.zig, gui.zig, detached/*.
| | * Post chain: Shadertoy passes compiled by glslc; Crt, Ripple and Glitch ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | rewritten as Shadertoy files Stage 9 of docs/render-pipeline.md. src/gui/Post.zig runs the chain over the finished frame: each pass reads the one before, ping-pong textures between, the last writes the window; an empty chain is the direct path. The prefix (shaders/post/prefix.glsl) is ghostty's uniform block, name for name and offset for offset (tested against a copy of its Uniforms, the selection colours by their GLSL names), at SDL GPU's bindings, y down. A user's file (Shader <path>) is compiled by glslc with compileGlsl's own flags on a thread of its own, taken in at the next loop step; errors name the file's own lines; a failed compile keeps the last good pipeline; glslc missing is said once. The bundled passes compile with the build, through the same prefix. Crt, Ripple and Glitch are Shadertoy files, rewritten: no barrel (input is identity, tested), everything in linear light and dithered, keyed to iTime. scene_effects, crt.zig and crt.frag.glsl are gone; the core keeps no clock for the chain. ShaderAnimation off|on|always drives redraw level A: the chain alone over the retained frame (34 us CPU a redraw, measured). Shared files touched: pardes.zig (nextWake loses the scene line, disableSceneEffects empties the chain, two tests), builtins.zig (one switch arm), ninep/ctl.zig (two switch arms), macos.zig (flags from the chain), gui.zig. Not touched: Messages.zig, mouse.zig, detached/*, host_io, tty.
| | * The GUI draws in tiers from the regions; pane chrome moves with its paneGabriel Schneider29 hours
| |/ |/| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| * ]f, mif and the other textobject keys use helix's own queriesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | helix's textobjects.scm for pardes's grammars are vendored in vendor/queries under MPL-2.0, each with a header naming its source and what it inherits, inlined; a README says what is there and what is not. syntax.objectAt and objectNext run them over the file's kept parse, with their #eq? and #match? predicates, for ]f [f ]t [t ]a [a ]c [c ]T [T ]e [e ]x [x and mi/ma with f t a c T e x. erlang's and zig's queries do not compile against the grammar versions pardes builds and are left out; fortran, markdown and powershell have none in helix. A test replays sixteen Python and JSON cases whose results were taken from the installed hx, and another holds that every vendored query compiles. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * The helix-golf steps that walk search hits are waived, so hxgolf can passGabriel Schneider29 hours
| | | | | | | | | | | | | | | | reverse_golf_example from ""N on needs *, N and n to walk the search register; in pardes those keys are the acme look ring, by decision. The seven steps are pinned in golf-waivers.jsonl, so a new mismatch stands out. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * Every helix-golf example is a differential case, one per step of its walkthroughGabriel Schneider29 hours
|/ | | | | | | | | | | The ten examples on helix-golf are imported as 118 cases: each numbered step of an example's walkthrough is the case whose keys are the command up to and including that step, so the first mismatch names the step where pardes and helix part. Six more restart at a step that begins with %, from helix's own text there, so a later step is tested even when an earlier one misses. zig build hxgolf compares all 124 with goldens from hx-harness. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A regular expression search is bounded by a step budget patched into mvzr, ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | not windows and a repeat cap The windows returned wrong matches: a candidate reaching a window's edge was left to the next window, half a window on, which could answer a match starting mid-token rather than the leftmost, and addr then pointed data's next write at the wrong bytes. The repeat cap missed mvzr's own worst case, a chain of a?, and alternation under a repeat, each exponential inside one mvzr call the deadline could not interrupt; and it refused ordinary s/S patterns. build.zig now patches the fetched mvzr at build time with a step counter on its backtracking recursion (matchPattern), so a fresh fetch keeps it and a moved anchor stops the build; regexp.zig gives each compiled pattern a budget, about 300 ms here, and a search that spends it fails as taking too long. Windows, the cap and their special cases are gone, and matches are exact again. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Allocate tree-sitter's external scanners from the same Zig allocator as its ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | runtime syntax.zig points the tree-sitter runtime at a Zig allocator, but the grammars' external scanners did not follow it. A grammar's tree_sitter/alloc.h maps ts_malloc to the runtime's current allocator only when TREE_SITTER_REUSE_ALLOCATOR is defined, and plain malloc otherwise, so every scanner's state and its arrays came from libc. Five scanners (bash, markdown, markdown_inline, python, typst) also call malloc, calloc, realloc and free by name. build.zig now compiles every grammar with TREE_SITTER_REUSE_ALLOCATOR and forces in src/tree_sitter_heap.h, which includes stdlib.h and then defines the four names as calls through ts_current_malloc and friends. A header forced in from the build rather than a patch to the grammars keeps zig-pkg pristine. No grammar archive refers to libc's allocator any more; only the runtime does, for its defaults. A scanner frees only what it allocated itself, and the syntax tests that create and destroy a parser for every grammar under a leak-checking allocator pass. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Inflate MuPDF's streams with the zlib FreeType linksGabriel Schneider29 hours
| | | | | | | | | | | | | | | | MuPDF compiled its bundled zlib 1.3.1 (ZLIB_SRC) into libmupdf.a, and the freetype package links the zlib package, 1.3.2, for its gzip module: 68 symbols defined twice, with the linker keeping one of each. MuPDF now links that same zlib artifact and stops compiling its own. build.zig asks FreeType's build for zlib with the options FreeType asks with, which the build's dependency cache answers with the very artifact FreeType links, so the tty and the gui each carry exactly one zlib. MuPDF hands zlib its own allocation functions on every stream, so nothing about where the memory comes from changes. Every page of the three PDFs in docs/ still renders bit for bit as before, at 144 and at 300 dpi. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Build MuPDF against the FreeType the gui links, not the slim one it bundlesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | MuPDF compiled its own FreeType 2.13.3 (FREETYPE_SRC, trimmed by scripts/freetype's slimftmodules.h and slimftoptions.h) into libmupdf.a, and the gui also links the freetype package, 2.14.3 in its full configuration. The two define the same 312 symbols, and a linker handed both keeps one of each: the gui's MuPDF ran the package's FreeType compiled against the slim headers, while the tty ran the slim one. Now there is one. mupdf.zig takes the build's freetype artifact, links it (its headers come with it) and no longer compiles FREETYPE_SRC or puts the slim headers on the include path. The shared artifact is built at c_optimize, as MuPDF is, so a Debug gui's glyph atlas now uses a ReleaseFast FreeType too; one artifact cannot be both. The tty gains the full FreeType's extra modules, which FT_Add_Default_Modules registers whether or not a PDF needs them. MuPDF asks nothing of FreeType the full configuration lacks: every page of docs/design.pdf, docs/9p.pdf and docs/registry.pdf renders bit for bit as before, at 144 and at 300 dpi. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Hand every C library's allocator hook the same C heapGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | C's malloc, calloc, realloc and free over a Zig allocator were written three times: syntax.zig's for tree-sitter, pdf.zig's for MuPDF, and gui.zig's for FreeType and HarfBuzz. They are now one, src/c_heap.zig's Heap. Each hook names the allocator variable its heap reads; gui.zig exports its heap under the ui_malloc names font.c and HarfBuzz call. It is a module of its own because pdf.zig is the MuPDF module, and a file cannot belong to that module and the core's at once. A block starts with a 16-byte header holding its size and the allocator that made it, so it goes back where it came from even after the heap is repointed, as the fonts' copy did. The fonts' copy carried the 16-byte Allocator itself in a 32-byte header; here the header holds the allocator's index in a table of every allocator a heap has used (entries are published once and never change, and a heap remembers where its current one was last found). The difference is measurable: with the 32-byte header, pardes-pdf-bench's filtered page render at 96 dpi took 1.1% longer than before this change, slower in 15 of 16 pinned rounds in two separate runs, and highlighting all of src/pardes.zig (305k tree-sitter blocks) up to 0.6% longer, against an A/A pair within 0.7%. With the 16-byte header both are back within the A/A pair's noise. Two other things change. For tree-sitter and MuPDF, a block made before the allocator was repointed now goes back to the one that made it rather than through the current one. For the fonts, realloc(block, 0) now frees and returns null, as glibc's does and as the other two copies already did; neither FreeType nor HarfBuzz asks for it. The heap's test replaces the tests of the old copies. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Allocate FreeType's and HarfBuzz's memory from the gui's Zig allocatorGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | FreeType and HarfBuzz allocated with libc malloc, out of sight of the Zig allocator the rest of the gui uses, so its Debug leak report and a test's std.testing.allocator never saw them. Now every allocation the font code makes goes through ui_malloc, ui_calloc, ui_realloc and ui_free, exported from gui.zig over `font_allocator`: - HarfBuzz is compiled with hb_malloc_impl and friends pointing at them. - Each UIFont gives its FreeType library its own memory manager (FT_New_Library with an FT_MemoryRec_ over the same functions, then FT_Add_Default_Modules and FT_Set_Default_Properties: exactly what FT_Init_FreeType does over its malloc manager), torn down with FT_Done_Library. - The shim's own UIFont and scratch arrays use them too. C's free and realloc pass no size, so a block starts with a 32-byte header (the caller's pointer stays 16-byte, max_align_t, aligned) holding its size and the allocator that made it, so a block goes back where it came from even after font_allocator is repointed. runNative points font_allocator at the gui's gpa. HarfBuzz makes a few process-wide objects on first use and never frees them (the default Unicode functions, the locale's language, hb-ft's font functions); ui_font_prime makes them from the default heap before that, so a Debug build's leak report stays about fonts. Tests do the same: the shaping tests, and a new test that creates, rasterizes and shapes fonts under std.testing.allocator, report any leak. Only the render thread calls into fonts, and every allocator used is thread-safe. It costs nothing measurable: against the previous change, 10 interleaved rounds with an A/A pair show every scenario as fast or faster with Adwaita Mono and Maple Mono alike, and first paint over 30 launches each matches main. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Shape text with HarfBuzz, so a font's programming ligatures draw across ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | their cells The SDL shell rasterized every cell by its own codepoint, so a font's `->`, `!=` or `<=` ligature never appeared. Text is now shaped with HarfBuzz 11.0.0, built from source like FreeType: a lazy build.zig.zon dependency (the tarball Ghostty pins) compiled only for the gui shell, its FreeType integration linked against the freetype package, so the binary carries no system HarfBuzz or FreeType and a tty build never compiles it. Shaping turns on only the font's ligature features (liga, calt, clig, rlig, rclt); composition, local forms and fractions stay off, so a cell draws either its nominal glyph, as it always did, or part of a ligature. A glyph no lookup of those features covers cannot change, so its cell is resolved alone, and that resolution is cached by codepoint: in a font without ligatures (Adwaita Mono) no cell is ever shaped, and the pipeline draws pixel for pixel as before. A cell whose glyph a ligature could replace is shaped with its word: the cells between spaces that share a decoration and a role. A word ends either side of the cursor, block or bar, so the cell being edited shows its own character (Ghostty's rule), and inside f|i, f|l and s|t, whose typographic ligatures do not belong on a grid (Ghostty again). Colours never end a word: a ligature spans a syntax colour change or a selection edge. A codepoint the primary font lacks shapes in the fallback face that has it. Taglines are not shaped: they are drawn at two pitches, and a strip would not line up in one. HarfBuzz only chooses glyphs; the grid places them. A substituted glyph whose ink reaches over neighbouring cells that draw nothing of their own (the spacer glyphs of Fira Code-style fonts such as Maple Mono, or the cells a merged ligature cluster swallowed) claims them: it is rasterized once as a strip that many cells wide, and each cell samples its own slice, so selection, the cursor and per-cell colours work unchanged. Anything the grid cannot place (several glyphs in one cell, a positioned mark) keeps its nominal glyphs. A frame must not pay for this. A shaped word's atlas slots are cached by its text (checked against the stored text, not just its hash), so a frame costs one lookup per word and a compare for the rest of the word being walked. The atlas is keyed by (face, glyph, role, decoration, lead, span), packed into 64 bits so a lookup hashes one word; ASCII glyph ids are a table, and a face's HarfBuzz state is made when it is first asked about a glyph. Measured with the latency trace, whose F line now also carries the submit time (CPU = submit - present entry), in 10 interleaved rounds against main with an A/A pair, at 160x50 cells: with Adwaita Mono every scenario (idle, scrolling, typing, terminal output) is as fast or 1-3% faster, and the first frame and time to first paint are unchanged. With Maple Mono, ligatures cost the first frame about 0.8 ms (HarfBuzz setup and shaping the screen) and scrolling new text about 1-2%. With it the codepoint raster path is gone: grips look up their glyph and stay centered, since a grip is not text. ui_font_scale_for_height, an identity kept as a "size token", is removed (px is the size everywhere), ui_font_has_glyph becomes ui_font_glyph, and the space slot is simply the first, cleared cell of the atlas rather than a rasterized space. CellStyle.ul gets an explicit u3 tag so a decoration fits in the packed keys. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
* Repaint PDF highlights by row, send rasters by shared memory, and animate ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | messages 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) <[email protected]>
* Merge the macOS first-class-host workGabriel Schneider29 hours
|\
| * Make the macOS shell a first-class hostGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pty children are exec'd with their own TERM/COLORTERM/TERM_PROGRAM instead of inheriting a .app launch's empty environment, and ttyTaken finally answers on darwin — libproc walks the tty's foreground process group — so Escape reaches the child and Exec stops believing every pane sits at its prompt. The occupancy suite runs on both platforms now. The workspace tag row moves into the native menu bar as a Builtins menu. -Dworkspace-tag (default off for -Dplatform=macos, on everywhere else) drives it, and Pardes.topBarHeight replaces the TOPBAR_H constant so the core stops reserving the row. The view pins every variable-font axis to the file's own default (Maple Mono came up Thin otherwise), shapes ligatures, carries per-shape pointer cursors, and draws the look-hover affordance as refracted glass. Tag rows fill edge to edge, with the anchor box painted back on top of that fill and its mode glyph centred on the same square. Theme accents re-saturated across the set. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* | Advertise the editor in the posted-9P registryGabriel Schneider29 hours
|/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | pardes spoke 9P and could be mounted, but only by naming its socket: $XDG_RUNTIME_DIR/pardes-9p-<name>.sock sits one directory above the registry and nothing could find it. Now a listening editor advertises itself at $XDG_RUNTIME_DIR/9p/pardes/<name>, a symlink to the socket it already binds. One directory for the program, one entry per editor — the layout zmx posts its sessions under — so several editors group rather than crowd the registry root. The socket does not move: adopting the registry only advertises. Only the runtime-directory socket posts, so an instance on the ~/.local/state fallback stays out of the user's registry, the way a private ZMX_DIR does for zmx. Stopping unposts, and only while the entry is still ours, so a name another editor has since claimed is never unlinked. The cloud9 pin moves to 9c4d668c for cloud9.post's path helpers. Serving is the whole of it. Consuming the registry is 9ns's job: it mounts the lot at /mnt/9p and an interactive fish already self-wraps in one, so a pardes started from a terminal reads /mnt/9p/harness/... with the same code that reads any other path. Two drafts that taught `resolve` to dial the registry itself were reverted — one duplicated 9ns for no gain, the other reinterpreted relative dials, which are a feature. `resolve` is byte-identical to what it was, and no dial that worked changes meaning. What pardes still does not do, and why, is in docs/cloud9.md: it binds its own socket rather than posting through cloud9.post, because post claims flat names only — legalName rejects '/', and claimName derives its lock directory by stripping "/9p" — so a name inside a subdirectory cannot go through it. zmx hand-rolls the same symlink for the same reason. Unifying them means teaching post a group, which is a change to adversarially-hardened code rather than a rename. docs/divergences.md records what this bookmark move leaves beside it: the editor line rruwvuzm (~1300 lines, forked at 01104e7c, still on the old cloud9 pin), the other bookmarks, and two failures that are not this change — fs-test's syntax-highlighting assertion, which fails identically on a clean main, and pardes not starting headless, which is why this is covered by 9p-io-test rather than by running the editor. Tests: 11/11 9p-io-test, including a listener that posts on start and unposts on stop; 31/31 unit-test.
* Flatten the 9P control tree and move it out of fs.zigGabriel Schneider29 hours
| | | | | | | | | | | | | | | The served tree loses the self/ level: /index /ctl /new /log /screen /listeners /pane/<n>/... /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 <[email protected]>
* Reuse bounded source analysis and speed up result context traversalGabriel Schneider29 hours
|
* Release Pardes 0.3 and stamp the macOS package versionGabriel Schneider29 hours
|
* Add Linux Tty9p mounted terminals and forward raw TTY keysGabriel Schneider2026-09-15
|
* 9p: use cloud9 protocol sessions and transportsGabriel Schneider2026-09-15
|