diff options
| author | user <user@clank> | 2026-08-08 22:36:30 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-08 22:36:30 +0200 |
| commit | cfae103a7afae1541a46cbfe21b011ffe4d09f2d (patch) | |
| tree | 35db8eac79e73d6cf2a9cb9ccdbf885b815072cc /zellij-utils/src/input/theme.rs | |
| parent | chore: stop tracking transient bgrun job artifacts (diff) | |
| download | sl0ppij-cfae103a7afae1541a46cbfe21b011ffe4d09f2d.tar.gz sl0ppij-cfae103a7afae1541a46cbfe21b011ffe4d09f2d.tar.xz sl0ppij-cfae103a7afae1541a46cbfe21b011ffe4d09f2d.zip | |
sl0p: grayscale the contents of unfocused panes
`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.
Diffstat (limited to 'zellij-utils/src/input/theme.rs')
| -rw-r--r-- | zellij-utils/src/input/theme.rs | 5 |
1 files changed, 5 insertions, 0 deletions
diff --git a/zellij-utils/src/input/theme.rs b/zellij-utils/src/input/theme.rs index f1e0adf1..e1371f85 100644 --- a/zellij-utils/src/input/theme.rs +++ b/zellij-utils/src/input/theme.rs @@ -37,6 +37,9 @@ pub struct FrameConfig { /// wide, so 1 row of gap needs ~2 columns to look like the same distance. /// 0 is treated as 1 (no correction). pub gap_aspect: usize, + /// How strongly to desaturate the CONTENT of unfocused panes, 0-100. + /// 0 disables it entirely (the default). + pub inactive_grayscale: u8, } /// Hand-written rather than derived: `gap_aspect` must default to 2 to match @@ -52,6 +55,7 @@ impl Default for FrameConfig { gap: 0, padding: Default::default(), gap_aspect: 2, + inactive_grayscale: 0, } } } @@ -78,6 +82,7 @@ impl FrameConfig { merged.gap = other.gap; merged.padding = other.padding; merged.gap_aspect = other.gap_aspect; + merged.inactive_grayscale = other.inactive_grayscale; merged } } |
