<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sl0ppij.git/zellij-server/src/unit/screen_tests.rs, branch main</title>
<subtitle>a sl0p.foo repository</subtitle>
<id>https://git.sl0p.foo/sl0ppij.git/atom/zellij-server/src/unit/screen_tests.rs?h=main</id>
<link rel='self' href='https://git.sl0p.foo/sl0ppij.git/atom/zellij-server/src/unit/screen_tests.rs?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/'/>
<updated>2026-08-12T16:44:24Z</updated>
<entry>
<title>sl0p: --purpose flag and tab-level purpose</title>
<updated>2026-08-12T16:44:24Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T16:44:24Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=2c67579398835d51e625085ce3b22c22bc8f0cba'/>
<id>urn:sha1:2c67579398835d51e625085ce3b22c22bc8f0cba</id>
<content type='text'>
Completes the two pieces deferred from the first workspace-hints pass.

`zellij run --purpose X` / `zellij action new-pane --purpose X` tag a pane at
spawn time, which is what an agent wants for panes it creates itself; the OSC
wrapper still works for programs that declare their own role. Not supported with
--in-place, which replaces another pane rather than being a pane of its own: clap
rejects the combination rather than accepting a flag that would do nothing.

`tab name="work" purpose="agent-work"` gives a whole tab a role, reported by
`list-tabs --json`. It rides the tab's ROOT layout node, reusing the pane field
because a tab's root is the tab container and never itself a pane -- asserted by a
test for the tightest case (a tab holding exactly one pane), since a leak there
would silently mislabel a pane.

The interesting part is why the flag first shipped broken. There are TWO protobuf
boundaries -- the plugin API and the client-&gt;server IPC contract -- and a CLI flag
must cross the second one. Defaulting the field in the conversion (what "no
protoc" usually forces) compiles cleanly and then silently drops the value in
transit, so --purpose parsed, printed a pane id, and did nothing. Fixed by adding
the field to both contracts by hand, in the .proto AND the committed generated
prost, kept in lockstep so a future regeneration reproduces it: protoc is only
needed to regenerate, not to build. Both boundaries now have a round-trip test,
and the IPC one was confirmed to fail when the wire read is removed.

Sanitising moved to Grid::set_authoritative_purpose, the single choke point every
out-of-band source passes through, after the CLI path was observed delivering
`bad purpose; rm -rf /` intact into list-panes --json.
</content>
</entry>
<entry>
<title>sl0p: separate the hover resize hint from the hover highlight</title>
<updated>2026-08-12T12:09:21Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T12:09:21Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=ea500ea70aa77da57870e8272bab593887819b0b'/>
<id>urn:sha1:ea500ea70aa77da57870e8272bab593887819b0b</id>
<content type='text'>
Upstream's mouse_hover_effects governs two unrelated things: the frame highlight
on hover, and the "Ctrl &lt;MouseScroll&gt; or &lt;drag borders&gt; to resize" hint. Its own
comment in default.kdl admits as much. So silencing a beginner reminder you have
outgrown also costs you a highlight that stays useful indefinitely.

mouse_hover_help_text (default true) gates only the hint. One line in
execute_send_to_terminal; the highlight is set in execute_update_hover and is
untouched.

Config-file only, deliberately. The option has no protobuf slot, so it does not
cross the client/server IPC contract -- the roundtrip test asserts exactly that,
with the fixture set to None and a comment explaining why. It still works from
the config file because the server parses the file itself and Options::merge
keeps the file value when the client sends None; a CLI flag would be silently
ignored.
</content>
</entry>
<entry>
<title>sl0p: add a minimal style for the stacked-pane picker</title>
<updated>2026-08-11T18:08:23Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-11T18:08:23Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=3418e8218d91d916b3b2a45ac81d3032c27575c6'/>
<id>urn:sha1:3418e8218d91d916b3b2a45ac81d3032c27575c6</id>
<content type='text'>
The boxed entries read as clutter: every entry bracketed in vertical bars, and
the active one marked by a "&gt; " that sits outside its own cell.

`stack_list_style "minimal"` drops both. The active entry is shown by
inverting it instead, which needs no glyph and no column, and the padding is
moved inside the styled run so the inverted block covers the whole cell rather
than leaving unstyled gaps at its edges. Four fewer columns of decoration per
entry, so more fit per row in grid mode.

Uses ribbon_selected rather than text_selected. Themes give ribbon_selected
real contrast because it marks the selected item elsewhere in zellij's UI --
gruber yellow here, white-on-blue in the TempleOS theme -- whereas
text_selected's background is 40,40,40 against a 24,24,24 terminal and read as
essentially no highlight when tried.

Decoration width comes from the style itself, so Tab's column arithmetic and
the renderer cannot disagree about how wide an entry is. Default stays boxed,
so nothing changes for anyone who does not ask.
</content>
</entry>
<entry>
<title>sl0p: optionally render the stacked-pane picker as a grid</title>
<updated>2026-08-11T14:33:46Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-11T14:33:46Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=4e001669f5a9dcc883f58bf0c3710c7460ae93cc'/>
<id>urn:sha1:4e001669f5a9dcc883f58bf0c3710c7460ae93cc</id>
<content type='text'>
`stack_list_layout "grid"` packs picker entries several per row, which turns
stack_list_max_rows from a count into a space budget: eight panes in a wide
terminal now fit on one row instead of spending three and hiding five.

The entry renderer already drew each entry as a single chunk at its own x/y,
sized by its geom and centring its own label, so a grid needed no rendering
changes at all -- only the loop assigning every entry the same x and the full
width. Column count is derived from the widest label rather than configured, so
it adapts to the titles; one column is the floor and reproduces the previous
full-width rows byte for byte.

The row -&gt; member mapping now has a column axis, and it had already caused one
wrong-pane bug when two consumers disagreed. So the layout is computed once,
by one function, into explicit cells; the renderer, the reserved-row budget and
the mouse hit-test all read those same cells rather than each doing their own
arithmetic. Windowing is by row, so a grid scrolls a whole row at a time and
degrades to the old per-member window when there is one column.

Cells are separated by a two-column gap: without it the focused entry's "&gt; "
marker sits flush against the previous cell and reads as belonging to it.
</content>
</entry>
<entry>
<title>sl0p: cap how many rows the stacked-pane picker takes</title>
<updated>2026-08-11T12:14:53Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-11T12:14:53Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=9441f1b177c1e08f4ceae76ef70d444103d15efa'/>
<id>urn:sha1:9441f1b177c1e08f4ceae76ef70d444103d15efa</id>
<content type='text'>
A stack reserves one row per member plus a padding row, so a deep stack spends
most of the screen listing itself. `stack_list_max_rows` caps that; the list
windows around the focused member and reports the rest as "+N more".

The indicator goes in the padding row the list already reserves, so it costs
no extra row and never displaces a real entry. Entries keep their real rank
rather than renumbering from 1, otherwise the numbering stops meaning anything
once the window has scrolled. The window slides on ordinary focus movement,
which is also why this needs no new keybinding -- Action is a protobuf enum
and the prost assets cannot be regenerated here (no protoc).

A fixed cap rather than hover-to-expand on purpose: reserved_top_rows feeds
pane geometry, so growing the list on hover would resize the pane's PTY and
reflow whatever is running in it on every mouse move. A real expand/collapse
has to be an overlay that reserves nothing, which is a much larger change to
the render path.

reserved_top_rows and the renderer both go through stack_list_window(); if
they disagreed the list would draw into rows the geometry never reserved, i.e.
over the pane beneath it.
</content>
</entry>
<entry>
<title>sl0p: collapse to a swap layout when the terminal gets narrow</title>
<updated>2026-08-10T22:12:10Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-10T22:12:10Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=393748bb2fb0ad741f2d51cfe0f8db75cf2ad7c3'/>
<id>urn:sha1:393748bb2fb0ad741f2d51cfe0f8db75cf2ad7c3</id>
<content type='text'>
Splitting a window into thirds is great until the window is 70 columns wide
and each pane gets 23. `responsive_min_cols` collapses the tab into the swap
layout named by `responsive_layout` when it gets narrower than that, and
restores the previously active layout when it grows back.

Selects a swap layout BY NAME rather than adding a size-aware
LayoutConstraint: that type crosses both the plugin protobuf API and the
client/server contract, and the committed prost assets cannot be regenerated
here (no protoc).

Restoring uses a 10-column hysteresis band. Without a dead band, dragging a
window edge that lands exactly on the threshold flips the layout on every
motion event, and every flip resizes every PTY in the tab.

"Did the user rearrange panes by hand" is judged from the damaged flag as
sampled BEFORE each resize, not the live one: resize_whole_tab's own
auto-layout pass sets that flag itself and clears it again only if it found a
layout that fits, so on a terminal too narrow for any of them it latches true
although nothing was rearranged. And once collapsed, restore stays reachable
regardless of the flag, so damage occurring while collapsed cannot strand the
tab in the narrow layout.
</content>
</entry>
<entry>
<title>Support the Kitty Image Protocol (#5428)</title>
<updated>2026-07-31T17:19:49Z</updated>
<author>
<name>Aram Drevekenin</name>
<email>aram@poor.dev</email>
</author>
<published>2026-07-31T17:19:49Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=0e6e4404027a187f1399a43bf91bdbd13d9636e1'/>
<id>urn:sha1:0e6e4404027a187f1399a43bf91bdbd13d9636e1</id>
<content type='text'>
* initial implementation - kinda working

* tests and fixes

* fix bounds

* tests and compatibility fixes

* fix: cursor scroll, clears, deletes, key tolerance, and host id range

* fix: keep images alive across resize, relayout and history scroll

* anchor to lines, not pixels

* fix image/text alignments

* config to allow disabling

* fix pinned pane issue

* never let host graphics replies reach the keyboard parser

* fix nested session

* properly handle fullscreening a pane

* rustfmt

* cleanup of ignored test

* add pr</content>
</entry>
<entry>
<title>Dep upgrades for debian (#5420)</title>
<updated>2026-07-28T10:05:38Z</updated>
<author>
<name>Aram Drevekenin</name>
<email>aram@poor.dev</email>
</author>
<published>2026-07-28T10:05:38Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=9969ed10b2cd95cb84ddaf8a783949483e2b038b'/>
<id>urn:sha1:9969ed10b2cd95cb84ddaf8a783949483e2b038b</id>
<content type='text'>
* chore(deps): upgrade base64 0.22, colored 3.1, crossterm 0.29, dialoguer 0.11, directories 6.0, highway 1.2, prost 0.12.6, strip-ansi-escapes 0.2.1, suggest 0.5.1

* chore(deps): upgrade miette 7.6, nix 0.30, notify 8.2, notify-debouncer-full 0.5

* vte 0.15

* clap 4.6

* workspaceify plugin deps

* don't send empty fs watch events</content>
</entry>
<entry>
<title>Nested session handling (#5417)</title>
<updated>2026-07-27T16:24:37Z</updated>
<author>
<name>Aram Drevekenin</name>
<email>aram@poor.dev</email>
</author>
<published>2026-07-27T16:24:37Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=ea07e2d5b6bbc9ea6f9c765b0838b3e91156a58d'/>
<id>urn:sha1:ea07e2d5b6bbc9ea6f9c765b0838b3e91156a58d</id>
<content type='text'>
* fix: theme query and updated pixel dimensions

* add no-ui-fullscreen key/action/capability

* protocol core

* integration test harness

* fix integration tests

* fix flaky tests

* nested ui (dimming) and directional focus initial implementation

* integration tests for nested ui dimming and directional focus

* initial full screen nesteed implementation

* fix: keep hidden floating panes hidden across nested no-ui fullscreen

* fix integration tests

* no ui fullscreen integration tests

* hardening behavior/tests

* fix(nested): re-derive descend/dim on host tab switch and new-tab so clicking dimmed chrome ascends

* initial modal implementation

* initial ui improvements

* fix integration tests

* improve nested selection ui

* nested session modal refactoring and tests

* improve integration test speed somewhat

* some ui adjustments

* simplify full/auto nested session handling

* hints in compact-bar

* guest stdin leak fix

* allow floating panes to be fullscreen

* make nested session handling configurable

* protocol reannounce

* refactor: naming confusion

* improve ux somewhat

* rustfmt

* merge fixes

* improve integration test rate limiting (fix ci)

* add pr</content>
</entry>
<entry>
<title>feat(config): add mouse_scroll_resize option (#5283)</title>
<updated>2026-07-17T07:20:38Z</updated>
<author>
<name>WeZZard</name>
<email>960509+WeZZard@users.noreply.github.com</email>
</author>
<published>2026-07-17T07:20:38Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=cd50d723b54c3b63e33b4292ccca5797a4d378da'/>
<id>urn:sha1:cd50d723b54c3b63e33b4292ccca5797a4d378da</id>
<content type='text'>
</content>
</entry>
</feed>
