summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAge
...
* | | A pane tag wraps onto rows and can collapse to one, as acme's doesGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | acme's frame wraps a tag at its right edge and the window grows to show every row (wind.c wintaglines); Tagup/Tagdown shrink it to one row and back. Pane tags used to be one row, scrolled sideways to the caret. Now a tag's lines wrap at its width (one cell kept for the caret past a full row) and it starts expanded; insert-mode Up on the first row collapses it, Down on the last row expands it, and Alt-Up/Alt-Down do so in either mode. A word the wrap breaks across rows is still one word to a click and a selection. Column and workspace tags keep scrolling sideways. Tests read body rows relative to the body's first row; the 41 snapshot goldens that moved were re-recorded by name after a programmatic check that each differs only by body rows shifting under the taller tags, and twelve scripts whose clicks targeted fixed rows now click the same text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A look with a blank in its pattern addresses, file:N selects the line, and a ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | miss is said, logged and reads back empty Round 6: look calc.py:/return a/ was a word search, since a blank ended the address; calc.py:99 past the end focused the file and said nothing; and /look still read back the pane before a miss. A pattern after the colon may now hold blanks; file:N selects the line, newline included, as acme's does; a line past the end opens and focuses nothing; and every miss (no match, a bad address, a line past the end) says so, logs err <serial> look: why, keeps the selection, and leaves look reading back empty. Documented, with file:0/re/ for the first match, since acme evaluates the address from the file's dot. Five goldens change: a look at file:N now selects the line. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A key between two clicks makes them two clicks, not a double-clickGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | A double-click is two clicks with nothing between them; a click, some typing and a click at the same cell within half a second selected a word, and snapshot scripts that did that came out by timing. A key now forgets the last click. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A tag no longer marks an unsaved file with *: the grip shows itGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | The grip button now shows a pane's unsaved text in every state, as acme's modbutton does, and acme's tag has no asterisk. The computed prefix is the name alone, so tag reads, event offsets that count the path and the tag's text lose the " *". dirty, index's flag and the refuse-once guards are unchanged; a dump from before still restores its tags past the old mark. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A B1 sweep held past a pane's edge scrolls the body, as acme's frselect doesGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | Dragging a selection beyond the top or bottom of a pane stopped at the last visible row. Now, while B1 is held past the body's top or bottom (at a pane that reaches the screen's bottom, its last row, since the pointer cannot leave the screen), the body scrolls a line a tick; the sweep's start keeps its place in the text, and a sweep that scrolled ends as the text's own selection. Also here: a ctrl-click is not half a double-click (lspdebug's golden stays as it was), and a test that clicks many points in a row resets the double-click between them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Everything a B1 chord does while B1 is down is one undo stepGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | B1-B2 then B1-B3 cut and pasted as two undo steps. acme marks the file once per B1 hold (text.c:881, textselect), so the pair is a copy. The chord now takes one undo point when it begins and its edits share it until B1 comes up. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A double-click selects as acme's does: a word, a line, a bracketed or quoted ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | text A second B1 click at the same cell selected nothing more. Now, as acme's textdoubleclick (plan9port text.c:1407), it selects up to the match of a bracket just before or after the point, the whole line at a line's start or end, the quoted text inside ' " `, or else the word, in bodies, tags and terminals in normal mode. A plain click still puts the caret on its cell. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Column and workspace tags are one line again, as acme'sGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | acme gives a column tag one font height (cols.c:244) and its row tag one line; pardes let them grow a row per line. A newline typed, pasted, written or restored into a column or workspace tag now becomes a space, the caret and selection kept on the same characters, and the bars are one row each. A dump holding a multi-line header tag comes back with its lines joined by spaces. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A write of an Edit block reaches Edit whole, on a pane's ctl, the root's and ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | exec Every write was split into lines, so a { group or an a/c/i text block written to ctl or exec became lines of their own: i/.../ and } ran as shell command panes that exited 127. An Edit line now takes the lines after it while its group is open or its text block waits for its . line (sam_edit.needsMore, asking sam's parser), on all three; the root ctl takes Edit at the active pane. s/re/text/N counts as sN/re/text/ does. The docs and the skill show a block written by heredoc or env printf, since bash's printf writes line by line, and take round 5's doc list: get refusing once, the (xN) on a repeated err, limit bounding only a forward search's end, line:col's clamping, and $NINE_MOUNT under 9ns --unix. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | An Edit with an unclosed { is refused, not runGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | Edit ,x/area_of/{ answered ok and did nothing: the group ran out of text and ran what it had. sam reads on for the }; an Edit has nothing more to read, so it now fails with unmatched `{', in the words sam uses for the other side. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A replace through data or body, truncate then write, is one undo stepGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | A shell's printf x > data truncates the addressed range and then writes, and each pushed its own undo point, so one replace took two Undos. A truncation of data, xdata or body now lets the write that follows it at once join its step, as acme's replace is one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Writes made under mark 0 are one undo step, and no history is lostGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | mark 0 skipped the undo point of each write, and mark 1 then pushed the text as it had become, so the text from before the batch was in no step: with no history before it, no Undo brought it back. Turning mark off now keeps the text as it is, and the writes after it are one step back to it, as acme's nomark joins them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | ^ in a pattern with \n matches at every line start, and a ^ or $ that cannot ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | work is refused A pattern naming a newline runs over the whole text, where mvzr's ^ is only the text's start: /^def .*\n/ from #0 failed, and Edit ,x/^def .*\n/.../ silently did nothing. A leading ^ is now tried at each line start from the search's own; $ just before \n is dropped, as it changes nothing; any other ^ or $ in such a pattern is refused with why, since mvzr would read it as the text's ends. ^ inside an alternation holds only where a search starts, an mvzr limit, documented. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A mouse sweep selects a stream, as acme's does, not a rectangleGabriel Schneider3 days
|/ / | | | | | | | | | | A B1, B2 or B3 drag over several lines took the same columns from every row, a block. acme's sweep is a stream (libframe/frselect.c:115-140, frselectpaint; acme text.c:881, textselect): the first row from the press to its end, the rows between whole, the last from its start to the release. edit.sweepCols says which columns of a row a sweep takes; the captured spans, the text a sweep yields (copy, chords, B2 and B3 operands, a terminal's automatic yank) and the highlight all follow it, in bodies, multi-row tags, terminals and text-rendered PDFs; a row the sweep runs through is highlighted to the edge, and rows after the first leave out the line numbers. Native PDF selection was already MuPDF's stream. No pane keeps a rectangle. exec-bigdrag's golden changes: its middle sweep now runs whole lines. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Edit runs sam's command language on a pane's body, as acme's Edit does, in ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | one undo step Over 9P a rename everywhere was a loop of addr and data writes. Edit, from a tag, a pane ctl or exec, parses sam's commands as acme's edit.c does and runs them as ecmd.c does against the text as it was, collecting the changes in order as elog.c logs them and applying them at once: addresses (the addr file's, line:col included), x y g v c a i d s p = m t, u alone, and { }. One undo step; any error, in acme's words, changes nothing, fails a ctl write and is logged. p and = print to the directory's +Errors. Left out, and said: the file commands, the pipes, \1-\9 (mvzr keeps no submatches), and changes out of sequence, which acme applies with a warning. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | addr takes line:col, as Look's file:12:5 doesGabriel Schneider3 days
| | | | | | | | | | | | A script that knows a line and column had to count bytes to write #n. `12:5` is now the point at line 12, column 5 (bytes from 1, clamped to the line's end and snapped to a character's start, as Look's cursor is), and it composes like any simple address. A pardes extension, documented as one; sam reads it as a syntax error. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The docs say how a --detach=NAME session is posted, what > does to tag, ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | where #n inside a character lands, and what tag click offsets count; a refusal says which ctl takes the word, and a lock refusal who holds it Dogfood round 4's doc list: the recipe assumed pardes/<pid>; the README said tag writes append, though > replaces the editable text; #n inside a multibyte rune or grapheme snaps to its start, unsaid; a tag click's offsets count the path. A word written to the other ctl now says write it to pane/<n>/ctl or /ctl, and a held lock says another open of that ctl holds it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A followed log, event or pty/data answers a read inside its last answer from ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | it again, so bash's read loses nothing bash's read on a seekable fd takes a chunk, keeps one line and lseeks back to just past it; a stream had already moved on, so the rest of the chunk was lost and a while-read loop over the log dropped records. Each stream open keeps its last answer and the offset it was read at, and a read strictly inside it is answered from it; a read at the same offset, as a client that ignores offsets makes, still gets the next record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A shell's > on addr no longer resets it, so echo /re/ > addr searches onGabriel Schneider3 days
| | | | | | | | | | | | Truncating addr reset it to #0, so a shell's find-and-replace loop matched the first hit for ever, .+#1 read 1 1 each time, and foo x foo y foo became foobarbarbar... acme resets addr on the first open (xfid.c:105-108), for clients that hold the fid; a shell cannot. Here neither an open nor a truncation resets it: the address written is evaluated from the last one, and 0 or , starts over. Documented as a departure from acme. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Shell prompt files are one per content under $XDG_RUNTIME_DIR, so a killed ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | pardes leaves none behind; tests remove their /tmp dirs 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 <[email protected]>
* | get on a pane with unsaved edits refuses once, as acme's doesGabriel Schneider3 days
| | | | | | | | | | | | A ctl get threw unsaved text away at once. acme's get asks winclean first (exec.c:513): warned once, the same get again discards. A pane dirty only because it was renamed has no text to lose, and is not asked about. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Text brought back to what was saved is clean againGabriel Schneider3 days
| | | | | | | | | | | | dirty compared revisions only, so undoing to the save point, or typing the text back, left the pane dirty. The saved text's hash is kept from the first edit after a save or load, and an edit that makes the text equal to it marks the pane saved again, as undo to the save point does in acme. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | What a root ctl write says is the session's in the log, msg -, not the ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | active pane's A session builtin written to /ctl runs at the active pane, so its messages were logged under that pane's serial, as if the pane had said them. They are now `msg -`, and still shown on the active pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A malformed flag write reads as EINVAL through a mount againGabriel Schneider3 days
| | | | | | | | | | | | The refusal `takes 1 or 0` matched none of 9ns's words, so a mount turned it into EIO. It now starts `bad flag:`, which 9ns maps to EINVAL, as it did the bare errno before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Sixteen 9P connections, and the next one is told there is no room instead of ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | being reset Four slots ran out under scripts plus a mount, and the fifth client was closed without a word, which through a mount looks exactly like a dead session. pardes now serves sixteen, and builds against cloud9 09b77cf, whose runner answers the Tversion of a client it cannot seat with an Rerror "too many connections" and tells pardes, which logs `err - 9p: too many connections`. QUIC keeps its own sixteen and still just closes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | An Exit or Restore refused over a ctl write names every unsaved pane, not ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | just the first Each pane got its own message, but a ctl write fails with its first error only, so a script was told about one pane, and the next Exit discarded both. The refusal now lists them all, as acme's rowclean does, and says the next one discards them all. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A look takes acme's file:addr and :addr, and one that finds nothing changes ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | nothing A look at `file:/re/` or `:/re/` searched for the words instead of addressing, and a miss left an empty +Search pane and a cleared selection, while a hit answered the +Search pane on /look. Now an address after a colon (acme look.c:450) is evaluated in the file from its dot; a miss says no match and leaves the panes and the selection alone; a hit reads back as the pane the text is selected in. Both live in lookAt's one flow, so they are one change. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A Save the host cannot do fails the 9P write that asked for it, and the log ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | names the path and why The write answered ok and the pane stayed dirty, with `save: AccessDenied` on its message row: a script saw success. The failure now says `Save <path>: <why>`, and a 9P write that waited on the save fails with EIO and that text, logged as an err record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Undo and Redo are builtins, as in acme's tags, so a pane's ctl and exec can ↵Gabriel Schneider3 days
|/ | | | | | | | step its body Over 9P there was no way to take back an edit: acme's Undo and Redo were keys only. They are now pane builtins, reached from a tag, a pane ctl and exec, and listed in /commands. They are not added to the default tags, which would change every tag. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Trial merge: 9P + helix + renderGabriel Schneider3 days
|\ \
| | * No panic from a notice wider than its band, or from the rest of its classGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The showcase GUI died in joinNotice on a long shader-error notice: with the tagline narrower than the body, the band hides more columns (skip) than a long message has padding (pad), and `skip..@min(pad, ..)` ran backwards. An audit of draw.zig, Layer.zig, Presentation.zig and animation.zig found the same class elsewhere, all fixed: - a multi-row pane tag or header scrolled for its caret's line sliced a shorter row past its end (paintPaneTag, renderHeaderLayer); - a zero-width column tag kept an earlier frame's viewport, and a column move's clip took it below zero (now saturating, and the early return resets the viewport and caret); - a tag line past 65535 columns overflowed tag_scroll's u16; - a hover left over from a bigger window dashed past the grid; - a border drag over a column that went away, a move preview past a shrunken grid, a column-move rail at zero columns; - Layer.pointAt cast a host's unchecked float metrics (web) to u16; - Text.insertVerticalCursor read past its lines for a cursor whose text changed under it. Regressions: a test of a notice wider than its band through Msg and through setMessage (the Post.zig compile-error path), which panics without the fix; and a monkey over the core at a gui's metrics (random resizes to a cell, long notices, prompts, multi-row tags, clicks, keys), 400 steps in the suite, PARDES_FUZZ_SEED/STEPS for longer runs; 60 seeds x 3000 steps clean. Shared files touched: none of the list (draw.zig, Layer.zig, Text.zig).
| | * Ripple and Glitch are gone; Crt staysGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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/*.
| | * Crt, Ripple and Glitch take a level, 0 to 3, tunable at run timeGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Coordinator's request: `Crt 0..3` (likewise Ripple, Glitch), 0 off, `on` the default level 2 (the polished rewrite), `off` and a bare word as before. The level is one uniform (pardesLevel, beside pardesOpaque in the prefix's own block); each pass picks its constants by it. Level 3 is at least as strong as the old effects, measured as pixels changed by more than 8 in a channel against a plain frame: Crt 18.3% (old 17.4%), Ripple 11.1% (9.5%), Glitch 9.3% a frame (8.1%); Glitch's level raises how often it bursts as well as how hard. Config and ctl report `Crt 2` / `Crt off`. Shared files touched: ninep/ctl.zig (the report line). Not touched: pardes.zig, Messages.zig, mouse.zig, gui.zig, detached/*.
| | * Post chain review fixes: glslc on stdin, level A at the display's refreshGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Stage 9 review: a user's Shadertoy file goes to glslc on its stdin, so no source file is written anywhere (the old predictable name under XDG_RUNTIME_DIR or /tmp was a symlink-clobber hazard). std's spawn does all its allocation before fork and searches PATH on the stack after it, said at the call. Level A redraws are paced by the display's refresh rate (SDL_GetCurrentDisplayMode), not a fixed 16 ms. Shared files touched: gui.zig.
| | * Post chain: Shadertoy passes compiled by glslc; Crt, Ripple and Glitch ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| | * While anything moves, a notice stays in its own pane's groupGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | Generalises vowtymmr (review): a pane sliding, zooming or opening over a neighbour covers that neighbour's notice as a closing one does. Tier 4 holds notices only while nothing moves. Shared files touched: gui.zig.
| | * A notice's word under the pointer is lit, in its band and the grid; ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | mark_hover is gone Open point 6 of docs/render-pipeline.md: notice words are Look and Exec targets, so the hover affordance headers have is back on notices, painted once in the notice's layer and joined into the grid from it. Deleted: Surface.mark_hover, Cell.hover and render's per-frame reset of it, none of which had been set since stage 5 (macOS loses its glass hover rect; its build is ignored for now). Snapshot goldens unchanged. Shared files touched: macos.zig (the hover bit's removal). Not touched: pardes.zig, Messages.zig, mouse.zig, gui.zig, detached/*.
| | * A still pane's notice stays under a closing pane's tombstoneGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | Tier 4 draws notices over every group; while a pane closes, a notice goes back to its own pane's group instead, so the tombstone sliding over it covers it as it did before stage 8. Review follow-up to stage 8. Shared files touched: gui.zig.
| | * The GUI draws in tiers from the regions; pane chrome moves with its paneGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| | * One Layer for tags, notices, headers and bodies; a taller tag is one layer ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | of rows; wire v8 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.
| | * Paint a body with context rows once, into its layer, and join itGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | joinBody replaces the second paint of a file body with compact tree-sitter context rows: the body is painted once into its body layer (more rows than the grid has) and the grid's body rows are copies of the layer's first ones; the rail is the grid's alone, from its region. Which rows are context rows is still decided by a text build before the paint, and a body without them reuses that text and paints straight onto the grid: every other body has no row the grid cannot show, so it gets no layer and no copy. Each part now paints only its own rows. The tag paints the tag rows of every pointer sweep that crosses them (the select sweep in the tag's colours, the look-hover affordance over it, a middle or right sweep over both in its button's tint), which the body used to paint over the tag on the grid alone; the body paints only body rows, and a select sweep begun on the tag marks the body rows it crossed before the text is laid down, as it did. Grid and layers agree, the snapshot goldens are byte-identical, and a test checks the joined grid rows and caret against a plain paint of the same body with the tag on top and at the bottom. The GUI goldens gain a terminal pane scene.
| | * Paint tags, notices and headers once, and join them into the gridGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A pane's tag rows, each notice chip and the workspace and column tags are painted once, as their tag layers, and the grid's cells for them are copies of those layers (joinCells): the second paint of every tag, the grid's own notice pass and paintHeaders' grid text, hover, selection and caret are gone, and so is renderTagLayers. The grid's caret for a tag, a header or a prompt is the layer's, projected to grid columns. A wide glyph cut by the edge of the copy is left as the band's blank, as the grid's print never drew half of one (a test proves it, and fails without the re-clip). Where the two copies disagreed, the grid's behaviour is kept, so every snapshot golden is byte-identical: - a notice highlighted the hovered word only as a layer; now it never does; - a column tag's hover was suppressed during a column move only on the grid; now in both; - a header under edit showed the hovered word only on the grid; now in both, under the selection; - a notice's leading padding carried the band's ink only as a layer; the grid copy keeps it blank; - a notice whose band is wider in tagline pitch than in grid cells (a GUI core) now shows the band's right end on the grid, where its text is, and its prompt caret there. A sweep begun on a tag and dragged into the body still shows on the body rows it crossed, on the grid only (paintPointerSelection's from_row).
| | * Place every part of the frame once, before painting itGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | draw.place builds the Surface's region list at the head of render: the page; each pane's grip, tag rows, body, rail with its thumb, and notice chips; each column's grip and tag; the workspace tag. The painters read their geometry from it instead of working it out again: the grid's notice chips and the notice layers share one chip rectangle, the rails (file, terminal, image, PDF page) paint the placed thumb (a native PDF measures its own into the region as it places its pages), and the body, tag, notice and column layers take their viewports from their regions. A collapsed pane places only its grip and tag. The column-move rail's one-cell clip of the layers under it is now a region field set in place, not a mutation of the built layers. No behaviour change; a test checks every region against the old geometry, the painted rail and the layer viewports across tag bottom, column tags, notices, a collapsed pane and a multi-line tag.
| | * Paint into a target Surface instead of swapping the frame's outGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | The painters of a pane's body and tag (renderBody and its selection, gutter, syntax, ANSI and wrap-marker passes; paintPaneTag; paintHeaders; renderPane; the image and PDF grid paths) take the Surface they paint as a parameter. The body layer and the tag layers paint straight into their own temporary surfaces, and the std.mem.swap of p.surface around those calls is gone. No behaviour change.
| | * Step core animation by the shell's clock and sleep to the next wakeGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| | * Move the frame into draw.zig and the transitions out of layout.zigGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Pure moves, no behaviour change. draw.zig holds the whole core frame in order: render, the pane, tag, header and notice painters it calls, and the character-effect composition (Pardes.render stays a declaration alias). Presentation.zig is the panel presentation state as a file struct, and animation.zig the easing curves, transition kinds, tracks and boxes, the character effects' sources and the generic displayed-value transition, all of which lived in layout.zig. layout.zig keeps only layout.
| * | Zig has textobjects: helix's query, ported to the zig grammar pardes buildsGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | pardes builds maxxnino/tree-sitter-zig, whose node names are not those of the grammar helix's query targets, so the query is ported by hand (vendor/queries/zig, its MPL header saying it is modified and how): a function is a Decl holding an FnProto and a Block with pub beside it, struct/enum/union one ContainerDecl, parameters and arguments ParamDeclList and FnCallArguments, and comments come in three kinds. That grammar's comments take their line's newline, and a node ending in a newline is now taken without it, as helix's grammars have it. The replay test gains fifteen Zig cases from the installed hx; none walks call arguments, which the reference helix counts as parameters and 25.07.1's query does not. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | ]f, mif and the other textobject keys use helix's own queriesGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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]>
| * | A selection's other ranges live out of line, made at the second range and ↵Gabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | freed at one The ranges beside the primary were an inline array in every Text, which at 1024 grew each pane by about 60 KiB. They are now allocated when a second range appears (Text.reserveSels) and given back when the selection is one range again (Text.dropSels); a column's or the workspace's tag allocates from Text.gpa, any other text from its pane's. A refused tag edit restores them by copy. The Pane size budget is back to 128 KiB, and a test holds that a single cursor allocates nothing for them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | gw labels the words in view and jumps to the one whose label is typedGabriel Schneider3 days
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | helix goto_word: the words of two or more word characters between the view's first and last lines get two-letter labels, nearest the cursor first, one forward and one back in turn (modal.jumpWords). The next two keys choose one and select it, stretching the selection to it in select mode; any other key takes the labels away. The words live on Pane.jump, and renderBody paints their labels over the body's cells in the selection colours, the one change on the render side. Co-Authored-By: Claude Opus 5.5 <[email protected]>