<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sl0ppij.git/zellij-server/src/panes/plugin_pane.rs, branch main</title>
<subtitle>a sl0p.foo repository</subtitle>
<id>https://git.sl0p.foo/sl0ppij.git/atom/zellij-server/src/panes/plugin_pane.rs?h=main</id>
<link rel='self' href='https://git.sl0p.foo/sl0ppij.git/atom/zellij-server/src/panes/plugin_pane.rs?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/'/>
<updated>2026-08-12T14:21:04Z</updated>
<entry>
<title>sl0p: hot-reload status_bar_button_style</title>
<updated>2026-08-12T14:21:04Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T14:21:04Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=78174b714461e510c6f8e703b29b796c443a35ee'/>
<id>urn:sha1:78174b714461e510c6f8e703b29b796c443a35ee</id>
<content type='text'>
The option was only read when a client connected, so changing it needed a new
session -- inconsistent with gap/padding/title_alignment, which apply live, and
the kind of inconsistency that reads as "my config change did nothing".

Threaded through update_frame_layout, the existing vehicle for the fork's
pane_frames options: Tab -&gt; tiled/floating containers -&gt; each pane's Style,
including suppressed panes so a hidden stack member is styled correctly when it
swaps back into view.

Verified by editing config.kdl against a running session and watching
1:Approve flip to [Approve] and back, then confirming a click still resolves
afterwards -- the button widths change with the style, so a stale layout would
have silently mis-targeted clicks.
</content>
</entry>
<entry>
<title>sl0p: optionally hide the 'Pane #N' placeholder title</title>
<updated>2026-08-08T20:47:16Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T20:47:16Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=9aa323fe6b9d15360ba54a1c73a216228664a289'/>
<id>urn:sha1:9aa323fe6b9d15360ba54a1c73a216228664a289</id>
<content type='text'>
`ui { pane_frames { hide_default_pane_names true } }`, default false.

A pane now shows no title at all until something actually names it: the user
renaming it (including by double-clicking the title row) or the running
program setting a title. Upstream always shows an auto-generated 'Pane #N'.

Implemented by tracking whether pane_title is still the constructor's
placeholder, NOT by matching the string: a program that legitimately titles
itself 'Pane #3' must not be mistaken for an unnamed pane. set_title clears
the flag, since something then chose that title.

current_title reads the option at render time rather than baking an empty
title in at construction, so it hot-reloads and turning the option back off
restores the placeholder on existing panes.

Display precedence is unchanged and now explicit in the code:
    user-given name &gt; title set by the running program &gt; placeholder

render_terminal_title already fell back to an empty string rather than the
placeholder, so the terminal WINDOW title is unaffected.

Empty titles were already handled by the frame renderer
(render_title_left_side returns None for an empty title), including the
sl0p centred/right alignment path.

Baselines held: zellij-server 1436/30, zellij-utils 436/5.
</content>
</entry>
<entry>
<title>sl0p: grayscale idle panes too (neovim stayed in colour)</title>
<updated>2026-08-08T20:40:53Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T20:40:53Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=e7bb75d5b76ad4ffc9541aa126a788cad92010f7'/>
<id>urn:sha1:e7bb75d5b76ad4ffc9541aa126a788cad92010f7</id>
<content type='text'>
Reported: grayscale worked for most panes but not one running neovim.

Nothing to do with neovim specifically - it was the only pane sitting IDLE.

refresh_for_focus_change called render_full_viewport(), which marks the grid's
output BUFFER so the whole viewport is emitted rather than a diff. But
Pane::render() early-returns unless grid.should_render is set, and
render_full_viewport() does not set it. So an idle pane never re-rendered at
all and kept its previous colours indefinitely.

Shell panes hid the bug: they set should_render constantly on their own
(prompt redraws, cursor), so they picked up the change almost immediately and
looked correct. A pane parked in neovim with no output produced nothing, so it
never repainted.

Both calls are now made and the reason each is needed is documented at the
call site, because 'mark the buffer' and 'schedule a render' being two
separate flags is exactly the kind of thing that reads as redundant and gets
'cleaned up' later.

Baselines held: zellij-server 1436/30.
</content>
</entry>
<entry>
<title>sl0p: grayscale the contents of unfocused panes</title>
<updated>2026-08-08T20:36:30Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T20:36:30Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=cfae103a7afae1541a46cbfe21b011ffe4d09f2d'/>
<id>urn:sha1:cfae103a7afae1541a46cbfe21b011ffe4d09f2d</id>
<content type='text'>
`ui { pane_frames { inactive_grayscale 0-100 } }`, default 0 (off).

Real colour transformation, not the SGR 'faint' attribute zellij already uses
for frames: faint is terminal-dependent, several terminals only apply it to
the 16 ANSI colours, and it does not remove colour. Each cell's colour is
resolved to RGB and blended toward its own Rec.709 luminance, so an unfocused
pane keeps its brightness but drains of colour.

Colour resolution covers the cases that actually occur: RgbCode directly,
ColorIndex via eightbit_to_rgb, and NamedColor by mapping to its 0-15 index.
Cells with NO explicit colour are deliberately left alone - they render in the
terminal default, which is already unsaturated, and rewriting them would mean
emitting an explicit colour for nearly every cell for no visible gain.

Two non-obvious pieces were needed to make it actually work:

- The output buffer only emits CHANGED cells, so an idle pane would keep its
  old colours indefinitely after losing focus. Pane::refresh_for_focus_change
  forces a full repaint, called from ActivePanes::insert (the single place
  focus changes). It is a no-op when the feature is off, so it costs nothing
  by default.

- Terminal panes render through the SHARED multi-client path. Focus is per
  client, so once unfocused panes are desaturated two clients may need
  different output for the same pane. Both tiled and floating panes now fall
  back to per-client rendering when grayscale is enabled - the reason this is
  opt-in.

Known limitation: sixel/kitty image chunks are not colour-transformed and stay
in colour.

Hot-reloads like the other sl0p options. Tests cover the transform: full
strength yields a true neutral grey, partial sits between original and grey,
indexed and named colours resolve rather than being skipped, luminance is
perceptual (green brighter than blue) not a flat average, already-grey is
unchanged, and non-colour codes are untouched.

Baselines held: zellij-server 1436/30, zellij-utils 436/5.
</content>
</entry>
<entry>
<title>sl0p: ignore gap/padding when a pane draws no frame</title>
<updated>2026-08-08T19:35:32Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T19:35:32Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=4cc7c39fee932b75a5a1e3dce85b6bf3efe6c5d3'/>
<id>urn:sha1:4cc7c39fee932b75a5a1e3dce85b6bf3efe6c5d3</id>
<content type='text'>
Closes the frameless-mode hole documented in the README.

In frameless mode (pane_frame_style "titles" or "none") the cells around a
pane's content are owned by the inter-pane boundary renderer, not the pane.
The ghost-fix commit correctly stopped panes from BLANKING that region -
blanking it erased the boundaries and broke three tests - but nothing stopped
them from CREATING it: padding still inset the content, so the vacated cells
had no owner and kept whatever was drawn there before.

Half the job done in each direction produced exactly the ghosting the blanking
was meant to prevent, just in a mode we do not use by default.

Fix: a pane that is not drawing a frame is not inset at all - no gap, no
padding, no moat, nothing unowned. Pane::set_frame() already received the
right boolean from both the tiled and floating containers and was throwing it
away; it now records it.

Config values are preserved, they simply take effect only with full frames.
README updated to describe this as a defined limit rather than a known bug.

Baselines held: zellij-server 1421/30, zellij-utils 435/5.
</content>
</entry>
<entry>
<title>docs: prepend sl0ppij fork header to README</title>
<updated>2026-08-08T19:19:04Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T19:19:04Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=72d5201b52624a5cf3e530397b909c2f3d008dcf'/>
<id>urn:sha1:72d5201b52624a5cf3e530397b909c2f3d008dcf</id>
<content type='text'>
Anyone cloning this repo got stock Zellij's README and no indication the
binary behaves differently, with the new options discoverable only from
commit messages.

Documents the four config options, which changes apply live vs need a new
session, the fastdev build profile, and the known rough edges.

Two corrections while writing it:

- The frameless-mode caveat is stated honestly. `padding` DOES still inset
  content when pane frames are off, but the vacated cells are not repainted
  because ui/boundaries.rs owns that region, so stale glyphs are expected on
  resize. That is a real limitation, not the deliberate design the first
  draft claimed. gap/padding are documented as full-frame only for now.

- Deduplicated the borderless-pane comment in both panes, which scripted
  patching had left repeated three times.
</content>
</entry>
<entry>
<title>sl0p: CSS-style 1/2/4-value shorthand for inner padding</title>
<updated>2026-08-08T19:06:40Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T19:06:40Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=97b744a57e3ba53169781a9357d8dc86b15d387e'/>
<id>urn:sha1:97b744a57e3ba53169781a9357d8dc86b15d387e</id>
<content type='text'>
`padding` now accepts:
  padding 1          -&gt; all four sides
  padding 1 2        -&gt; up/down = 1, left/right = 2
  padding 1 2 3 4    -&gt; up=1 down=2 left=3 right=4

NOTE the 4-value order is UP DOWN LEFT RIGHT as requested, deliberately NOT
CSS's clockwise top-right-bottom-left. Documented on PanePadding and at the
parser, since the similarity to CSS invites the wrong assumption.

3 or 5 values are rejected with a message naming the accepted forms, rather
than being silently reinterpreted. Negative values clamp to 0.

ASPECT: only the 1-value form is aspect-corrected (left/right multiplied by
gap_aspect) so `padding 1` looks square on a non-square cell grid. The 2- and
4-value forms are literal: specifying axes separately means the caller is
already controlling horizontal size, and silently doubling it would surprise.

Style now carries a PanePadding {top,bottom,left,right} instead of an x/y
pair, which also collapses the reconfigure plumbing back to a single field
rather than growing another parameter per side.

to_kdl always emits the explicit 4-value form: emitting the 1-value form
would re-apply gap_aspect on the next read and silently double the padding
on every config round-trip.

Baselines held: zellij-utils 435/5, zellij-server 1421/30.
</content>
</entry>
<entry>
<title>sl0p: uniform pane gap + aspect-corrected horizontal gap</title>
<updated>2026-08-08T18:58:17Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T18:58:17Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=c21f7324abdb6d83300a62b95e2af27bfcc75a1e'/>
<id>urn:sha1:c21f7324abdb6d83300a62b95e2af27bfcc75a1e</id>
<content type='text'>
Two changes to how the gap is defined.

1. UNIFORM GAP. Previously every pane inset every side by `gap`, so two
   neighbours each gave up a cell and the space between them was 2*gap while
   the screen edge got only gap. Now a pane insets RIGHT and BOTTOM always,
   and LEFT/TOP only where that edge is the viewport boundary. Two neighbours
   contribute exactly one gap between them, and the outer screen edges get
   the same gap. `gap 1` now means 1 everywhere.

   The per-side offset is computed in tiled_panes, which is the layer that
   knows the viewport, and pushed to panes via Pane::set_gap_offset before the
   content-offset sweep. Floating panes keep a zero gap: they are positioned
   explicitly by the user, so an inter-pane gap does not apply to them (they
   still get padding).

2. ASPECT CORRECTION. Terminal cells are roughly twice as tall as they are
   wide, so an equal number of rows and columns does not look like an equal
   distance. `gap` and `padding` are now expressed in ROWS, and columns are
   multiplied by `gap_aspect` (default 2, minimum 1). Style carries the
   resolved pane_gap_x/y and pane_padding_x/y.

FrameConfig::Default is now hand-written rather than derived: gap_aspect must
default to 2 to match the KDL parser, otherwise a parsed config disagrees with
a default-constructed one and it surfaces as config round-trip diffs (this
broke can_define_ui_configuration_in_configfile and both
bare_config_from_default_assets_to_string tests).

Tests assert the actual invariants: stacked and side-by-side neighbours get
exactly one gap between them, screen edges get the same gap as between panes,
and inset_by refuses to collapse a pane too small to survive it.

Baselines held: zellij-server 1421/30, zellij-utils 433/5.
</content>
</entry>
<entry>
<title>sl0p: blank the padding ring too, fixing remaining resize ghosting</title>
<updated>2026-08-08T18:40:12Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T18:40:12Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=93b86ce0e0d2cec9750fb27075932f6bacd3055b'/>
<id>urn:sha1:93b86ce0e0d2cec9750fb27075932f6bacd3055b</id>
<content type='text'>
The previous ghosting fix only covered ONE of the two regions the insets
carve out. With `padding N` there is a second unowned region: the cells
between the frame border and the content rect. Nothing wrote them, so when
a pane grew, cells that had previously held another pane's border or title
kept those glyphs - the reported 'old pane title/border repeated in the gap
space'.

Both rings are now blanked at every frame-render site:
  1. allocation   \ drawn frame   (outer gap)
  2. frame inner  \ content rect  (inner padding)

Frameless mode is explicitly excluded from ring 2. With pane frames off the
cells around the content belong to ui/boundaries.rs, which draws the
inter-pane boundaries there, and content_offset deliberately leaves room for
them. Blanking that region erased the boundaries and broke three tests
(render_stacks_without_pane_frames,
swap_tiled_layout_with_stacked_children_and_no_pane_frames,
send_cli_toggle_pane_frames_action). Collapsing the interior onto the
content rect emits no ring at all in that mode.

gap_ring_chunks is now robust to any rect relationship: spans are clamped to
`outer`, so a mis-clamped caller cannot make it write outside the pane's own
allocation. Tested with inner rects that cover, miss, straddle and degenerate
against outer.

Suite back to baseline: 1421 passed / 30 pre-existing failures.
</content>
</entry>
<entry>
<title>sl0p: fix ghosting/artifacts when resizing panes with a gap</title>
<updated>2026-08-08T18:26:04Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-08T18:26:04Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppij.git/commit/?id=ccc77067e2ef230f3bc84155644b09476728b4ae'/>
<id>urn:sha1:ccc77067e2ef230f3bc84155644b09476728b4ae</id>
<content type='text'>
With a gap configured, PaneFrame::render() emits CharacterChunks only for
the INSET frame. The gap ring - the cells between a pane's allocated
geometry and the smaller frame drawn inside it - was written by nobody.

On a fresh paint that looks correct, but only because the screen had just
been cleared. As soon as panes are moved or resized, whatever was
previously drawn in those cells survives, which is the ghosting/artifacts
seen while dragging pane borders.

gap_ring_chunks() emits explicit blanks for that ring, restoring the
renderer's existing invariant that every cell of a pane's allocation is
written by exactly one pane on every render. It is appended at all four
frame-render sites (tiled + plugin panes, cached and uncached paths), and
is a no-op when gap is 0 so the default path is untouched.

Tests assert the ring never overlaps itself, never escapes the allocation,
never overdraws the frame, has exactly (outer area - inner area) cells, and
that ring + frame together tile the allocation with no holes - a hole being
exactly what produces the artifacts.
</content>
</entry>
</feed>
