Skip to content

Theme-adaptive ANSI color rewrite for terminal readability#644

Merged
h0x91b merged 7 commits into
mainfrom
fix/dev3-claude-light-theme-colors
Jun 10, 2026
Merged

Theme-adaptive ANSI color rewrite for terminal readability#644
h0x91b merged 7 commits into
mainfrom
fix/dev3-claude-light-theme-colors

Conversation

@h0x91b

@h0x91b h0x91b commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • AI agents emit colors tuned for the opposite background: Claude Code sends pale 256-color codes and SGR dim (washed out in light theme), Codex sends near-black truecolors (invisible in dark theme). The PTY stream is now rewritten on the fly by a stateful filter (src/mainview/utils/ansi-theme-adapt.ts) applied in the TerminalView write batcher.
  • Light mode: dim is dropped, pale foregrounds are darkened, white backgrounds (Claude Code message bars) become light gray; the light 16-color palette gets darker white/brightBlack/yellows.
  • Dark mode: too-dark foregrounds are brightened toward gray, white backgrounds (message bars, history-select highlight) become dark gray with dark ANSI fg on them flipped to light.
  • Foreground fixes are gated by the luminance of the active explicit background: opposite-polarity or unknown (named ANSI, reverse video) backgrounds pass through untouched, same-polarity ones stay fixed (Codex paints pure-black text on its own dark truecolor background).

See decisions/066-light-theme-ansi-stream-rewrite.md for the full investigation.

h0x91b added 7 commits June 10, 2026 10:30
AI agents emit pale 256-color SGR codes and dim attributes tuned for
dark backgrounds. ghostty-web resolves 256-color indexes inside WASM
(theme palette cannot remap them) and renders dim as 50% alpha, which
washes text out on white.

Add a light-mode stream filter that drops SGR dim and darkens pale
indexed/truecolor foregrounds by luminance before term.write(), and
darken the light theme ANSI palette (white, brightBlack, yellows).
Claude Code's light-ansi theme paints message bars with ansi:white as a
background (SGR 47) plus dark 30/90 foregrounds. Our light palette maps
white to a dark gray so 37 stays legible as text, which turned those
bars dark-on-dark. Split the roles: 37/97 text stays dark, 47/107 and
48;5;7 / 48;5;15 backgrounds become light gray truecolor.
Codex emits GitHub-light ink truecolors (#333333, #183691, ...) regardless
of the terminal background, making its output unreadable on the dark theme.
Extend the PTY stream filter (renamed ansi-light-adapt -> ansi-theme-adapt)
with a dark mode that blends foregrounds below 0.25 luminance toward white.
Foreground adjustment in both modes is now skipped while an explicit SGR
background or reverse video is active, preserving intentional contrast in
vim themes and highlight bars; the gate state persists across chunks.
Claude Code paints message bars and the history-select highlight with
ansi:white/whiteBright (SGR 47/107); the dark palette resolves those to
pale lavender, leaving default-fg text unreadable. Rewrite them to Claude
Code's own dark theme bar colors (55/70 gray) and flip explicit dark ANSI
fg (30/90) on those bars to light grays, tracking the last dark fg across
sequences since Claude emits fg before bg. Remapped white bars no longer
gate fg adjustment — they sit near the theme background after remapping.
Pure black brightened to the flat 0.38 luminance target lands on a
chroma-less #616161 gray that still reads as nearly invisible on a dark
background (e.g. Codex paints its model name with #000000). Ramp the
blend target up as input luminance approaches zero (0.38 at the
threshold, 0.50 for pure black), so near-black ink gets a stronger lift
while already-colored dark foregrounds barely shift.
The 0.50 target still rendered pure black as a gray too dark to read
comfortably on the dark background (Codex model name in its status bar).
Pure black now maps to #999999; colors near the threshold barely shift.
A binary gate skipped fg fixes under any explicit background, so Codex's
pure-black model name on its own dark truecolor bg (48;2;30;30;46) was
never brightened. Explicit truecolor/indexed backgrounds are now
classified by luminance: same-polarity bgs keep fg adjustment on,
opposite-polarity and unknown (named ANSI, reverse video) still gate.
@h0x91b h0x91b enabled auto-merge (squash) June 10, 2026 10:18
@h0x91b h0x91b merged commit 3ae0431 into main Jun 10, 2026
4 checks passed
@h0x91b h0x91b deleted the fix/dev3-claude-light-theme-colors branch June 10, 2026 10:19
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.

1 participant