summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-09 10:11:03 -0300
committerGabriel Schneider <[email protected]>2026-08-10 09:17:07 -0300
commit2b1fac6fecbf7d7cc08f9dad24687b05e9968c9a (patch)
tree7ca3eb7295549bfe50f9f3b3acf323da6e5f8ee5
parent218d8577cbffb45a6ee80cea52418864a044e452 (diff)
downloadpardes-2b1fac6fecbf7d7cc08f9dad24687b05e9968c9a.tar.gz
pardes-2b1fac6fecbf7d7cc08f9dad24687b05e9968c9a.zip
tagbottom: the message row sits just above the tagline, not at the top
-rw-r--r--src/builtins.zig6
-rw-r--r--src/pardes.zig35
-rw-r--r--test/snapshots/tagbottom.golden10
-rw-r--r--test/snapshots/tagbottom.snap13
-rw-r--r--test/snapshots/tagbottomimage.golden6
-rw-r--r--test/snapshots/tagbottomimage.snap4
-rw-r--r--test/snapshots/tagbottomshort.golden38
-rw-r--r--test/snapshots/tagbottomshort.snap49
8 files changed, 128 insertions, 33 deletions
diff --git a/src/builtins.zig b/src/builtins.zig
index 934b3996..ee542e77 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -194,9 +194,9 @@ pub const Wrap = struct {
/// Taglines at the BOTTOM of every pane instead of the top. Global like Wrap
/// and for the same reason — a layout where some panes label themselves above
/// and others below is unreadable, so it is one switch for the whole screen.
-/// The transient message row moves the other way to keep out of its way (see
-/// render), which puts the two strips of chrome at the ends they always were,
-/// just swapped.
+/// The transient message row gives that row up and takes the one above it (see
+/// render), so it is the pane's last BODY row either way and a prompt stays
+/// beside the tag it is typed into.
pub const Tagbottom = struct {
pub fn run(c: Ctx) void {
c.p.tag_bottom = !c.p.tag_bottom;
diff --git a/src/pardes.zig b/src/pardes.zig
index 592dd059..ef606f23 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -12084,17 +12084,17 @@ pub const Pardes = struct {
try p.renderPane(arena, pane, p.rects[id], id == p.active);
}
- // ---- the transient message row: the end away from the tag ----
+ // ---- the transient message row: the pane's last body row ----
//
// An OVERLAY, not geometry: no rect moves, no pane shrinks, and a pane
// with neither a message nor an armed prompt is not touched at all.
// Drawn after every pane's body so it lands OVER whatever that row was
- // showing, and painted in the tag bar's own colours — it is the
- // tagline's twin at the other end of the pane, and reading as chrome is
- // what keeps it from being mistaken for content. Out here rather than
- // at the end of renderPane because renderPane returns early for a
- // native PDF page and for an image, and a `/` on a PDF is a real search
- // whose prompt has to be visible like any other.
+ // showing, and painted across the whole row — it is the tagline's twin,
+ // and reading as one strip rather than a stamp on a body line is what
+ // keeps it from being mistaken for content. Out here rather than at the
+ // end of renderPane because renderPane returns early for a native PDF
+ // page and for an image, and a `/` on a PDF is a real search whose
+ // prompt has to be visible like any other.
//
// Exactly two things can occupy the row and an ARMED PROMPT beats a
// MESSAGE, because they are not the same kind of thing: a message is a
@@ -12104,15 +12104,19 @@ pub const Pardes = struct {
//
// ponytail: the row is draw-only. A click on it lands wherever the body
// under it says (tag clicks map to tag_col on the TAG row), so a prompt
- // that moved down here cannot be clicked into or swept the way one on
- // the tagline could — the keyboard still edits it in full. Upgrade path
+ // that moved off the tagline cannot be clicked into or swept the way it
+ // could up there — the keyboard still edits it in full. Upgrade path
// is a hit test here that maps a press on this row to tag_col + the
// marker offset, i.e. the tag row's own mapping with a constant added.
for (&p.panes, 0..) |*slot, id| {
const pane = slot.* orelse continue;
const r = p.rects[id];
// no body row (a one-row pane, or one squeezed out entirely): the
- // tag row is not ours to overwrite, so the message just waits
+ // tag row is not ours to overwrite, so the message just waits. One
+ // guard for both placements, because the row picked below is the
+ // last BODY row either way — "the pane has a body row" is the whole
+ // condition, and it is what keeps r.h - 1 - BOX_H from underflowing
+ // or landing above the pane.
if (r.w <= config.GUTTER or r.h <= BOX_H) continue;
// the same one input model renderPane cut off the tagline; only one
// of the two can ever be armed (a body key arms one, exitTagEdit
@@ -12128,10 +12132,13 @@ pub const Pardes = struct {
if (text.len == 0 and !leader_here) continue;
const tx = r.x + config.GUTTER;
const tw = r.w - config.GUTTER;
- // ...at the OTHER end from the tagline, whichever end that is:
- // Tagbottom moved the tag onto this row, so the twin moves to the
- // pane's first. They stay a pair with the body between them.
- const row = if (p.tag_bottom) r.y else r.y + r.h - 1;
+ // the pane's last BODY row: its last row outright, or one up from
+ // that when Tagbottom has taken the last for the tagline. The first
+ // cut of Tagbottom sent this row to the pane's FIRST instead — the
+ // far end, symmetric with the tag — which put the prompt you are
+ // typing as far as the pane allows from the tag you are typing
+ // into. Beside the tagline is where it is read, so it stays there.
+ const row = if (p.tag_bottom) r.y + r.h - 1 - BOX_H else r.y + r.h - 1;
// In the EDITOR's colours, not the tag bar's: this row is the one
// place the program talks back to you about the buffer you are in,
// and it reads as part of that buffer rather than as another strip
diff --git a/test/snapshots/tagbottom.golden b/test/snapshots/tagbottom.golden
index 27c6d42a..486390e2 100644
--- a/test/snapshots/tagbottom.golden
+++ b/test/snapshots/tagbottom.golden
@@ -277,7 +277,7 @@
|
|
| /tmp/pardes-snap/tagbottom/cwd/bottom.txt Save New Del
-== snap search-prompt grid=100x30 cursor=6,11
+== snap search-prompt grid=100x30 cursor=6,28
|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
| 2 bbb
| 3 ccc
@@ -289,7 +289,7 @@
| 9 iii
| 10 jjj
| /tmp/pardes-snap/tagbottom/cwd/top.txt bottom.txt Save New Del
-| /ZZ
+| 1 ZZONE
| 2 ZZTWO
|
|
@@ -306,7 +306,7 @@
|
|
|
-|
+| /ZZ
| /tmp/pardes-snap/tagbottom/cwd/bottom.txt Save New Del
== snap leader grid=100x30 cursor=9,13
|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
@@ -320,7 +320,7 @@
| 9 iii
| 10 jjj
| /tmp/pardes-snap/tagbottom/cwd/top.txt bottom.txt Save New Del
-| SPC
+| 1 ZZONE
| 2 ZZTWO
|
|
@@ -337,7 +337,7 @@
|
|
|
-|
+| SPC
| /tmp/pardes-snap/tagbottom/cwd/bottom.txt Save New Del
== snap cut-down grid=100x30 cursor=7,4
|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
diff --git a/test/snapshots/tagbottom.snap b/test/snapshots/tagbottom.snap
index 389e0c36..5d30f7c6 100644
--- a/test/snapshots/tagbottom.snap
+++ b/test/snapshots/tagbottom.snap
@@ -1,6 +1,6 @@
-# Tagbottom: every pane's tagline on its LAST row instead of its first, and
-# the transient message row (prompts, messages, the SPC indicator) at the other
-# end with it. One global bool, so the whole screen flips at once.
+# Tagbottom: every pane's tagline on its LAST row instead of its first, and the
+# transient message row (prompts, messages, the SPC indicator) stepping up to
+# the row just above it. One global bool, so the whole screen flips at once.
#
# The two things worth pinning here are not pixels. The first is that a click
# still lands where it looks like it lands: the Sel coordinate space is
@@ -96,9 +96,10 @@ release left 1 4
stable 400 8000
snap gutter-click
# `/` in the lower pane's body arms the search input. The prompt is CUT off the
-# tagline and drawn on the message row with the cursor — and with Tagbottom
-# those two are at opposite ends of the pane: the tail keeps its builtins on
-# row 29 while `/ZZ` and the cursor sit up on row 11, the lower pane's first.
+# tagline and drawn on the message row with the cursor — and with Tagbottom the
+# two are adjacent: the tail keeps its builtins on row 29 while `/ZZ` and the
+# cursor sit on row 28, the last body row. The tagline under it stays whole —
+# the prompt must not have eaten the row it was cut from.
press left 10 14
release left 10 14
stable 400 5000
diff --git a/test/snapshots/tagbottomimage.golden b/test/snapshots/tagbottomimage.golden
index 5508d01a..72b31809 100644
--- a/test/snapshots/tagbottomimage.golden
+++ b/test/snapshots/tagbottomimage.golden
@@ -91,9 +91,9 @@
|27: 0-0 d,#121212, 1-9 d,#080808, 10-11 #6c6c6c,#959595, 12-12 #6c6c6c,#352879, 13-13 #352879,#6c6c6c, 14-14 #352879,#6c5eb5, 15-65 #352879,#352879, 66-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808,
|28: 0-0 d,#121212, 1-9 d,#080808, 10-10 #6c6c6c,#352879, 11-11 #352879,#6c6c6c, 12-65 #352879,#352879, 66-74 d,#080808, 75-75 d,#444444, 76-76 d,#080808, 77-149 #bdbdbd,#080808,
|29: 0-1 #ffffff,#626262, 2-56 #949494,#121212, 57-74 d,#121212, 75-76 #ffffff,#262626, 77-129 #949494,#121212, 130-149 d,#121212,
-== snap search-prompt grid=150x30 cursor=6,1
+== snap search-prompt grid=150x30 cursor=6,28
|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
-| /ZZ
+| (f🬱]0V7R*%m🬶🬁G🬬tN#XR9q🬢tC#f/🬜_GR(🬞🬙tCRp$o*7^+[#H7s🬢YDZ🬝▟
| iU)dSotT[r27T"\*Fh▐🬅g,🬰p🬛▙🬲&*3/9xy🬛▀0kCg▛🬱C,>C}PC06m🬝▟█ pic.ppm
| "S▄;$gs,Xa▟T🬔s▖┼W▘?by🬋🬭uCR3/A3🬶lO▖▞🬃🬘╳🬟#=🬺🬕yoaJ@>4🬝🬵
| ─m!fAAD🬒jW>7JkR▄▜🬫🬞🬢FWx╲▟2ZT🬥/▖jteR?q🬶^🬅▀_@N─u4🬹🬝🬹
@@ -120,7 +120,7 @@
| eP🬗<┼A🬝🬹█
| ▀(u🬬🬝▟
| fc🬎🬹█
-| 🬝🬹
+| /ZZ
| img /tmp/pardes-snap/tagbottomimage/cwd/pic.ppm New Del /tmp/pardes-snap/tagbottomimage/cwd New Del
== snap del-image grid=150x30 cursor=5,6
|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
diff --git a/test/snapshots/tagbottomimage.snap b/test/snapshots/tagbottomimage.snap
index e46e19ba..8ac06d55 100644
--- a/test/snapshots/tagbottomimage.snap
+++ b/test/snapshots/tagbottomimage.snap
@@ -32,8 +32,8 @@ snapstyle img-bottom
# `/` on an image pane. There is no text to search, and that is the point: the
# prompt is drawn by the message-row pass, which runs OUTSIDE renderPane
# precisely so an early-returning pane still gets one. With Tagbottom it lands
-# on the pane's FIRST row (grid 1), the end away from the tag — over the top
-# row of the art, not over the tagline that is now beneath it.
+# on the last row of the art (grid 28), just above the tagline on grid 29 —
+# over the picture, never over the tag it was cut from.
key /
send ZZ
stable 600 8000
diff --git a/test/snapshots/tagbottomshort.golden b/test/snapshots/tagbottomshort.golden
new file mode 100644
index 00000000..599dd29b
--- /dev/null
+++ b/test/snapshots/tagbottomshort.golden
@@ -0,0 +1,38 @@
+== snap roomy grid=80x10 cursor=6,8
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| 1 one
+| 2 two
+| 3 three
+| 4 four
+| 5 five
+| 6 six
+|
+| /ZZ
+| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save New Del
+== snap two-body grid=80x4 cursor=6,2
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| 1 one
+| /ZZ
+| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save New Del
+== snap one-body grid=80x3 cursor=6,1
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| /ZZ
+| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save New Del
+== snap tag-only grid=80x2 cursor=64,1
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save New Del
+== snap regrown grid=80x10 cursor=6,8
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| 1 one
+| 2 two
+| 3 three
+| 4 four
+| 5 five
+| 6 six
+|
+| /ZZ
+| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save New Del
+== snap leader-one-body grid=80x3 cursor=7,1
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| SPC
+| /tmp/pardes-snap/tagbottomshort/cwd/s.txt Save New Del
diff --git a/test/snapshots/tagbottomshort.snap b/test/snapshots/tagbottomshort.snap
new file mode 100644
index 00000000..a6ca2828
--- /dev/null
+++ b/test/snapshots/tagbottomshort.snap
@@ -0,0 +1,49 @@
+# The Tagbottom message row against a pane with almost no body. That row is the
+# pane's LAST BODY row — the tagline's neighbour once Tagbottom has taken the
+# last row — so it only exists while the pane HAS a body row. Two rows is the
+# smallest pane that does. One row is a tagline and nothing else, and there the
+# message waits: not drawn over the tag, and not on the row above the pane,
+# which is what `r.y + r.h - 1 - BOX_H` gives you when the guard is off by one.
+file s.txt one\ntwo\nthree\nfour\nfive\nsix
+start 10 80 s.txt
+wait 8000 s.txt
+stable 700 20000
+key space t b
+stable 700 15000
+# `/` with room to spare: the prompt is cut off the tagline (grid 9) and drawn
+# on grid 8, the last body row, with the cursor.
+key /
+send ZZ
+stable 600 8000
+stable 400 5000
+snap roomy
+# two body rows left; the prompt takes the lower one and line 1 still shows
+resize 4 80
+stable 700 10000
+snap two-body
+# one body row, and the prompt takes it. The tagline underneath must still be
+# drawn in full — this is where an off-by-one eats it.
+resize 3 80
+stable 700 10000
+snap one-body
+# the tagline alone. No body row, so nothing of the prompt is drawn anywhere —
+# not over the tag, not on the row above the pane. The tagline itself is
+# untouched: the marker sits after the builtins, so cutting the tail there
+# leaves every column of it exactly as `tag-only` would look unarmed.
+resize 2 80
+stable 700 10000
+snap tag-only
+# ...and grown back, still armed, back on the last body row
+resize 10 80
+stable 700 10000
+snap regrown
+key esc
+settle 100
+stable 400 5000
+# the SPC leader indicator rides the same row, right-aligned, and shrinks onto
+# the single body row with it
+resize 3 80
+stable 700 10000
+key space
+stable 400 8000
+snap leader-one-body