Skip to content

fix(nvim): judge the real rendering chain, not $TERM (completes #53) - #55

Merged
YASoftwareDev merged 4 commits into
masterfrom
fix/nvim-colour-through-tmux
Sep 18, 2026
Merged

YASoftwareDev merged 4 commits into
masterfrom
fix/nvim-colour-through-tmux

Conversation

@YASoftwareDev

Copy link
Copy Markdown
Owner

What

v1.11.8 fixed only half of #53, and the half it fixed was the half that was not
happening. This fixes the real chain and reverts the part of 1.11.8 that was
measured actively harmful.

Why 1.11.8 missed it

1.11.8 gated termguicolors on $TERM. Inside tmux $TERM is always tmux's
own (tmux-256color), regardless of what the client is - so the gate saw "256",
concluded the chain was capable, and never fired. Meanwhile tmux quantizes
whatever nvim emits down to the attached client's palette.

Measured inside tmux with an 8-colour client (TERM=xterm), scoring the final
screen through a real terminal emulator (pyte), counting only nvim's rows:

config unreadable cells dominant pair
v1.11.8 87/886 = 9.8% blue/black x72
this branch 5/1120 = 0.4% -
256-colour client, either version 1/886 = 0.1% unchanged

The second finding: 1.11.8's colorscheme switch was worse than the bug

1.11.8 also swapped the scheme to habamax on the fallback path. habamax sets
ctermfg=251/ctermbg=234, and both collapse to black in tmux's 256->8
conversion. Same harness, same screen:

fallback unreadable
keep nightfly, termguicolors off 5 (0.5%)
default, off 5 (0.5%)
industry, off 5 (0.6%)
nightfly, termguicolors on (the bug) 25 (2.8%)
habamax, off (what 1.11.8 shipped) 587 (67.4%)
retrobox, off 587 (67.4%)

So the gate now touches termguicolors and nothing else. Keeping nightfly with
truecolor off costs its colours - it defines no cterm values - but renders in the
terminal's own fg/bg, which is readable by construction.

How the chain is judged now

_chain_colors() asks tmux for #{client_termname} when $TMUX is set, then
counts that terminal's colours with tput -T <name> colors. Counting beats
matching the name: alacritty and xterm-kitty are truecolor terminals whose
names carry no 256, and downgrading them would be a regression. Confirmed
positively on all four chains:

xterm            -> termguicolors=0  nightfly
screen           -> termguicolors=0  nightfly
xterm-256color   -> termguicolors=1  nightfly
alacritty        -> termguicolors=1  nightfly

Verified

tests/nvim-colour-fallback.sh rewritten to four arms: direct low-colour, direct
256-colour, inside tmux with an 8-colour client, and a guard against adopting a
232-255 grey scheme. Each arm red-proofed, and each mutation reddens only the arms
it should:

mutation reddens
force termguicolors = true arms 1 and 3
reinstate 1.11.8's tmux blind spot (ignore $TMUX) only arm 3
reinstate the habamax fallback only arm 4, naming ctermfg=251/ctermbg=234

Restored tree: RESULT: PASSED (4 checks ran, none skipped).

Also: bash test.sh workstation 61/62, 0 failures, 1 skip (unchanged);
tests/lint-workflows.sh PASSED; tests/ci-gate-covers-all.py passes;
shellcheck -S warning clean.

Measurement notes, because three harnesses were wrong first

Recorded so the numbers above can be trusted and the traps are not repeated:

  • Attaching a client only AFTER nvim starts makes #{client_termname} empty at
    the moment the config reads it, so the fix cannot fire and the arm proves
    nothing. A client must be attached first.
  • Scoring the raw byte stream counts every redraw over time, so the shell prompt
    and tmux status bar dominate the number. Only the final screen, reconstructed by
    an emulator, is meaningful.
  • Killing the tmux server before scoring means the emulator's final screen is the
    teardown clear - that scored 73 cells and looked like a clean pass.
  • A backgrounded script inherits the loop's stdin and eats the remaining input,
    so only the first candidate ran.
  • xterm-kitty has no terminfo on the dev box, so that row was a failed attach,
    not a result; it is excluded rather than reported as "no colour".

1.11.8 gated termguicolors on $TERM, which inside tmux is always tmux's
own (tmux-256color). The gate therefore never fired in tmux - the case
the report actually came from - while tmux quantized nvim's truecolor
down to the client's palette. Measured through an 8-colour client:
blue-on-black across 9.8% of the screen.

_chain_colors() now asks tmux for #{client_termname} and counts that
terminal's colours with tput -T. Counting beats matching the name:
alacritty and xterm-kitty are truecolor terminals with no '256' in them.

Also drop the colorscheme switch 1.11.8 added. habamax and retrobox set
ctermfg=251/ctermbg=234, and BOTH collapse to black at 8 colours - 67%
of the screen black-on-black, far worse than the bug. Keeping nightfly
with termguicolors off renders in the terminal's own fg/bg: 0.4%.

Refs #53
The arm typed :call writefile(...) after a fixed sleep, which raced a cold
nvim start in CI and reported 'produced no result - cannot tell'. nvim now
writes the answer from -c, and the arm polls for the file instead of
sleeping a guessed amount. Re-red-proofed: reinstating the tmux blind spot
still reddens this arm alone.
The arm still produced no result in CI while passing locally under a clean
environment, so the cause is the container, not timing. Two changes: it now
writes through the same lua io.open form probe.lua already uses successfully
on nvim 0.10.4 in CI (writefile() wants a List of strings and was being handed
a number - unproven as the cause, both forms work on 0.12 here), and on failure
it prints the pane state and the pane's last output instead of only saying it
could not tell.
CI diagnostics showed the real cause: the pane's shell is the login shell,
which after this repo's install is zsh, and with no ~/.p10k.zsh it opens
powerlevel10k's configuration wizard - so send-keys typed into the wizard.
It could not reproduce here because this box already has ~/.p10k.zsh.

The pane now runs a script that waits for a flag file and then starts nvim,
so nothing depends on the login shell or on keystroke timing. The client is
held attached through a fifo: with stdin on /dev/null, script hit EOF at once
and the session was destroyed about two seconds in, which is why pinning a
bare bash alone made the arm skip.

Re-red-proofed after the rewrite: the tmux blind-spot mutation still reddens
this arm alone.
@YASoftwareDev
YASoftwareDev merged commit 59d50d6 into master Sep 18, 2026
30 checks passed
@YASoftwareDev
YASoftwareDev deleted the fix/nvim-colour-through-tmux branch September 18, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants