From 43b9d696f9e25ad7589527b91a74666e71ff0472 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 22 Sep 2026 02:21:03 -0300 Subject: Notices become tagline bands at the top of the body A message, a leader chord and a prompt used to share one row of body text at the bottom of a pane, wearing the tagline font and nothing else about a tagline. Now each one is a TagLayer of its own, emitted through the same renderHeaderLayer the pane and column tags go through, so it gets the tagline height, the small-font metrics, the band offset and the border for free -- none of which a body-grid row can have by changing its font role. The text is right aligned. The prompt stays on the canonical grid because it owns a cursor, and a cursor has to sit on a real cell. The body starts BELOW the bands rather than under them, the way tree-sitter context rows already worked. Pane.body_offset is how many rows they took and Pardes.bodyTop(pane, rect) is the one place that answers "where does the body begin" -- replacing fifteen copies of `if (tag_bottom) r.y else r.y + BOX_H` spread across the paint, hit-test, scroll, PDF and image paths, which is what let the bands and the text under them come adrift. Every notice is painted on the grid as well, because the grid is what a terminal client draws and a band it cannot see is a message it never gets; the GUI skips grid cells a tag layer covers, so nothing is drawn twice. Three bugs the bands exposed, fixed here: - a prompt band flush with the right edge put its caret one column past the pane, which the detached wire refuses -- so every frame was dropped for as long as the prompt was open. The band now reserves that column. - a click on a band mapped to Sel row 0, which is the TAG row: clicking chrome expanded a word out of the tagline and ran it as a builtin. - a watched file reloading under the editor changed the core without going through update, so needs_frame was never set and the reload was never drawn. Pardes.invalidate() is the name for that, and the file and theme reloads call it. A session can now drive its own 9P namespace instead of being refused one: ownMountSuffix answers what a path names inside this editors own tree and resolve, readLimit and write serve it from memory rather than making the syscall that never returns. The match is anchored to whole components under the registrys 9p/pardes/, because a bare /pardes/ anywhere in a string would claim ~/src/pardes//README -- and, before write learned the same trick, write the trees bytes over the real file. readFileLimit and writeFile refuse instead, having no core to answer from. A toggle setting SETS when given `on` or `off` and only flips when it is bare, so the report LocationsConfig prints can be fed back as configuration and mean what it says. Snapshots: 97/98, from 0/98. The goldens were several commits stale and 17 scripts had stopped running; `config ` is a new script command that appends to the per-script startup config, so a script that clicks body coordinates pins `Verbose off` instead of counting the rows an announcement moves. nested-optout is left failing on purpose: two levels of nesting prepend vaxis F3 codepoints to typed lines, which is a real bug and is written down in docs/divergences.md with a repro. Co-Authored-By: Claude Opus 5 (1M context) --- features.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'features.txt') diff --git a/features.txt b/features.txt index eeadba39..d035648a 100644 --- a/features.txt +++ b/features.txt @@ -167,3 +167,33 @@ Found by adversarial review and NOT fixed, because they are upstream or macOS-on frontend. Fixing it means a new header variant, i.e. a protocol change. - A shell that outlives its editor keeps PARDES_PID; if that pid is reused, `pardes ` now exits 1 instead of falling back to starting an editor. Exiting 1 only when the socket itself refused would keep the old behaviour. + +Notices are tagline bands at the TOP of the body now, not a row of body text at the bottom. Each message or leader prefix is emitted through the same +renderHeaderLayer the pane and column tags use, as a TagLayer of the new `.notice` kind, so it gets the tagline's height, its small-font metrics, its band +offset and its border for free -- none of which a body-grid row can have just by wearing a tagline font. The text is right aligned. The prompt stays on the +canonical grid because it owns a cursor, which has to sit on a real cell. + +The body starts BELOW the bands rather than under them: Pane.body_offset is the number of rows they take, and every painter and hit test derives the body's +first row from Pardes.bodyTop(pane, rect). That replaced fifteen copies of `if (tag_bottom) r.y else r.y + BOX_H` scattered across the paint and pointer paths, +which is what let the bands and the text they sit over come adrift in the first place. renderBodyLayer zeroes the offset around its own render, because it +paints into a surface of its own that carries no bands. + +Every notice is ALSO painted on the canonical grid, because the grid is what a terminal client draws and a band it cannot see is a message it never gets. The +GUI skips grid cells a tag layer covers, so nothing is drawn twice there. + +Snapshot suite: 97/98. The goldens were several commits stale (Delcol on the column tagline, the closing word moved last in every +tagline, the bare-tty boot's empty pane, stacked Locations rows, the notice bands) and 17 scripts had stopped running at all. Fixed +by making the scripts say what they mean rather than by loosening them: `config ` is a new snapshot-script command that appends +to the per-script startup config, so a script that clicks body coordinates can pin `Verbose off` instead of counting the rows an +announcement band moves; `@word` column specs replace counted columns where a tagline word has moved; and two grids grew because a +pane tag is wider now than its `Del` could fit in. + +Found by that pass and fixed: a watched file reloading under the editor changed the core WITHOUT going through `update`, so +`needs_frame` was never set and the reload was never drawn. `Pardes.invalidate()` is the name for "something changed me from +outside the event loop"; `reloadWatchedFile` and the theme reload call it. + +Also from it: a toggle setting now SETS when given `on` or `off` and only flips when it is bare, so `writeReport`'s `Verbose on` +means what it says when fed back as configuration -- the symmetry the report was asked for, on the input side too. + +Left failing, deliberately: `nested-optout`. See docs/divergences.md -- two levels of nesting prepend U+E016 (vaxis's F3) to typed +lines, which is a real bug in the key-forwarding path and not a stale expectation. -- cgit v1.3