<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sl0ppij.git/zellij-server/src/panes/unit, branch main</title>
<subtitle>a sl0p.foo repository</subtitle>
<id>https://git.sl0p.foo/sl0ppij.git/atom/zellij-server/src/panes/unit?h=main</id>
<link rel='self' href='https://git.sl0p.foo/sl0ppij.git/atom/zellij-server/src/panes/unit?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/'/>
<updated>2026-08-12T15:05:34Z</updated>
<entry>
<title>sl0p: per-pane workspace hints (purpose=)</title>
<updated>2026-08-12T15:05:34Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T15:05:34Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=df052db4608f4d835fe7b4ca76dbb4059e78943b'/>
<id>urn:sha1:df052db4608f4d835fe7b4ca76dbb4059e78943b</id>
<content type='text'>
Implements the zellij half of sl0p/workspace-hints-proposal.md: a pane can carry
an opaque agent-facing hint for what it is FOR, so a project can ship a layout
that an agent is able to navigate instead of guessing.

zellij never interprets a purpose. The consuming agent owns the entire vocabulary,
which is what keeps this to one string threaded along the existing status_bar path
rather than a policy engine, and lets the vocabulary change without a rebuild.

Two ways in, because declared panes and agent-created panes are different:
  layout:  pane name="server" purpose="dev-server"
  in-band: printf '\033]5577;1;purpose;agent:build\033\\'
The second exists because tagging at spawn time would mean threading a field
through 28 SpawnTerminal call sites, and a new action variant is impossible while
protobuf enums cannot be regenerated. Wrapping the command costs one printf.

A declared purpose outranks an in-band one and cannot be overridden or cleared by
it. OSC arrives from whatever the pane happens to be showing, so otherwise a
`cat hostile.txt` in the human's editor pane could relabel it as the agent's
scratch pane and invite an agent to run commands where the human is working. A
layout is written by a human in a file they can read; in-band data only fills a
blank.

Exposed on PaneListEntry (`list-panes --json`) rather than PaneInfo, which crosses
the plugin protobuf API we cannot regenerate; resolved in the screen thread where
panes live rather than via route.rs's per-pane PTY round-trips. Values are
sanitised to a bounded [A-Za-z0-9_.:/-] token at both entry points, because a
purpose is never rendered -- it is matched against and pasted into consumers'
shell and JSON pipelines.

Verified end to end rather than by inspection: purposes from a layout appear in
list-panes --json against the right pane ids, an agent-spawned pane tags itself,
a declared purpose survives an in-band override attempt, all of it comes back
after kill + resurrect, an unknown pane property is still a parse error, and a
layout with no purpose leaves dump-layout unchanged.
</content>
</entry>
<entry>
<title>sl0p: clickable action buttons in the pane status bar</title>
<updated>2026-08-12T13:18:33Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T13:18:33Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=549f052ae5451d57b58f5de22de57b2a586e6d23'/>
<id>urn:sha1:549f052ae5451d57b58f5de22de57b2a586e6d23</id>
<content type='text'>
Second slice of the status/action frame. A program declares buttons, and a click
is reported back to it:

    printf '\033]5577;1;buttons;approve:Approve;reject:Reject\033\\'
    -&gt; click -&gt; program's stdin receives  OSC 5577 ; 1 ; click ; approve  ST

The report is a FIXED format carrying only the id, never bytes the payload chose,
and ids are restricted to [A-Za-z0-9_-]. The sequence that creates a button is
in-band and untrusted -- `cat` of a hostile file emits it as readily as a
cooperating program -- so a click that could send arbitrary bytes would let
viewing a file inject into the stdin of whatever is running, a shell included.
Labels are '%'-escaped since the wire format is ';'-separated, and both the
button count and id length are capped because the list lives as long as the pane.

Positions come from one layout_status_bar function that the renderer draws from
and the hit-test resolves against. Computing that geometry twice is how the
stacked-pane picker once selected the wrong pane; a test asserts every drawn
column resolves back to the button drawn there, across 100 widths.

Two things the drive caught that unit tests could not:

- The first delivery path pushed onto grid.pending_messages_to_pty, which is
  only drained when the PANE produces output -- so a click on an idle program sat
  in the queue forever. The write now happens at the tab level, which owns the
  senders.
- The hit-test derived the frame row from the pane's raw geometry, but this fork
  draws the frame inset by gap_offset, so with `gap 1` every click missed by one
  row. It now derives from the CONTENT rect, which is by definition the inside of
  the drawn frame and therefore the same columns the renderer lays out.

Buttons serialise with the session like the status text, so a resurrected pane
comes back with its buttons intact.
</content>
</entry>
<entry>
<title>sl0p: per-pane status bar via OSC 5577 (experimental)</title>
<updated>2026-08-12T12:39:46Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T12:39:46Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=ee50f2661a141040f7f8579efe1283a0ea5b644c'/>
<id>urn:sha1:ee50f2661a141040f7f8579efe1283a0ea5b644c</id>
<content type='text'>
First slice of the status/action frame: one row of program-supplied text in a
pane's bottom frame row.

    printf '\033]5577;1;status;building 3/7\033\\'
    printf '\033]5577;1;clear\033\\'

Rendered in the EXISTING frame row rather than in reserved rows. Reserving would
shrink the pane's content area and resize its PTY whenever the bar appeared or
disappeared, firing SIGWINCH at the program that asked for it -- the same churn
that made the stack-list picker use a fixed cap. The frame row is already a text
surface with a precedence chain, so this cost no geometry work at all.

State lives on Grid like the window title, so it survives re-renders, resizes
and detach/reattach with no draining. Session resurrection is NOT covered yet.

Versioned from the start, and unknown versions or verbs are ignored, so the
planned button verbs cannot be half-interpreted by an older build. Text is the
remaining params rejoined with ';', which means a single text field needs no
escaping -- the trick OSC 0/2 already uses for titles.

Treated as untrusted, because it is: an in-band sequence is emitted just as
readily by `cat` of a hostile file as by a cooperating program. Control
characters are stripped -- a raw ESC could otherwise repaint anything on screen,
a newline would corrupt the frame row -- and the length is capped, since the
string is retained for the life of the pane.
</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>fix(grid): properly pad rows in CUF (#5377)</title>
<updated>2026-07-17T10:31:19Z</updated>
<author>
<name>Aram Drevekenin</name>
<email>aram@poor.dev</email>
</author>
<published>2026-07-17T10:31:19Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=1889819b52b3821e4639fdffdd69e978016ca0f9'/>
<id>urn:sha1:1889819b52b3821e4639fdffdd69e978016ca0f9</id>
<content type='text'>
* fix(grid): properly pad rows in CUF

* add pr</content>
</entry>
<entry>
<title>fix(grid): scroll the region when a line wraps at its bottom margin (#5357)</title>
<updated>2026-07-17T09:06:57Z</updated>
<author>
<name>Mofan552</name>
<email>71218481+mofan552@users.noreply.github.com</email>
</author>
<published>2026-07-17T09:06:57Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=6b967baacc86cb24b3fd55e445c833b28f733c0c'/>
<id>urn:sha1:6b967baacc86cb24b3fd55e445c833b28f733c0c</id>
<content type='text'>
* fix(grid): scroll the region when a line wraps at its bottom margin

Wrapping at the bottom margin of a DECSTBM scroll region used to move
the cursor below the region instead of scrolling it, after which no
subsequent output could scroll through the region (or reach scrollback
for top-anchored regions) anymore.

line_wrap() now mirrors add_canonical_line(): the region scrolls up one
line, the cursor stays on its bottom row, and the new row is a wrapped
(non-canonical) row. Top-anchored partial regions preserve the
scrolled-off line in scrollback (#4941), other regions discard it, and
the alternate screen never transfers.

* docs(changelog): line wrap scroll region fix

* fix(grid): keep hyperlink tracking aligned when wrap scrolls an inner region

When a line wraps at the bottom of a scroll region that does not start
at the top of the screen, the region's rows shift up but the hyperlink
tracker's recorded positions did not, so a URL wrapping there lost its
clickable anchors. Offset the tracked positions for the rows inside the
region, the way the full-screen and top-anchored paths already do via
offset_cursor_lines.

* refactor shared logic and adjust changelog

---------

Co-authored-by: Aram Drevekenin &lt;aram@poor.dev&gt;</content>
</entry>
<entry>
<title>Implement DECQRM for CSI2031 (#5111)</title>
<updated>2026-04-29T12:12:05Z</updated>
<author>
<name>Aram Drevekenin</name>
<email>aram@poor.dev</email>
</author>
<published>2026-04-29T12:12:05Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=79c0e2088786fe72050eb4dadf41da1c1ac4afad'/>
<id>urn:sha1:79c0e2088786fe72050eb4dadf41da1c1ac4afad</id>
<content type='text'>
* implement DECQRM for CSI2031

* add pr</content>
</entry>
<entry>
<title>Support CSI2031 (dark/light theme switching) + remove STDIN cache to avoid stale color queries (#5105)</title>
<updated>2026-04-28T15:14:59Z</updated>
<author>
<name>Aram Drevekenin</name>
<email>aram@poor.dev</email>
</author>
<published>2026-04-28T15:14:59Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=227142dd567ee72bcc137c116bc1ce2b818975e7'/>
<id>urn:sha1:227142dd567ee72bcc137c116bc1ce2b818975e7</id>
<content type='text'>
* initial phase 1 implementation

* various fies

* some fixes

* fix fragmented stdin cases

* some fixes

* renaming

* guard against old clients

* implement csi 2031

* allow toggling theme with actions

* rustfmt

* fix: finalize trailing characters in ansi parser as well

* add pr</content>
</entry>
</feed>
