Skip to content

tui: detect terminal light/dark background and adapt the palette#402

Merged
edwin-zvs merged 1 commit into
mainfrom
tui-terminal-theme-aware
Jun 12, 2026
Merged

tui: detect terminal light/dark background and adapt the palette#402
edwin-zvs merged 1 commit into
mainfrom
tui-terminal-theme-aware

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

Problem. The Matrix palette was a single dark-tuned set of foreground colors drawn on the terminal's own background (Color::Reset, no bg of its own). On a light terminal the light-green text is low-contrast and hard to read.

Approach (option A from the discussion): be terminal-theme-aware via OSC 11.

  • The TUI now ships the palette in two variants: Theme::dark() (unchanged) and Theme::light() (dark-green functional text, darker rain heads so they read on white, pale tails).
  • theme.toml gains mode = "auto" | "light" | "dark" (default auto).
  • In auto, at startup the TUI queries the terminal background with OSC 11 (\x1b]11;?\x07), parses the RGB reply into perceived luminance, and picks light (>0.5) or dark. A terminal that doesn't answer falls back to dark.
  • [colors] overrides apply on top of whichever variant is active.

Mechanics. The query runs once, right after enable_raw_mode and before the event loop consumes stdin, with a ~120 ms timed poll/read on the tty fd (Unix; non-Unix → dark). One new dep: libc (Unix only).

Changes

  • theme.rs: ThemeMode, Theme::light(), ThemeConfig (load + resolve), detect_terminal_is_light + OSC 11 luminance parsing; parse_themeparse_theme_onto(base, …).
  • crates/cli/Cargo.toml: [target.'cfg(unix)'.dependencies] libc.
  • app.rs: parse config at startup, re-resolve the palette after raw mode (before the first render).
  • docs/configuration.md + spec 0027.

Tests

mode parsing; forced-vs-auto resolution + dark fallback when unanswered; OSC 11 luminance (white→light, black→dark, 8- and 16-bit channels, garbage→None). 7 theme tests pass; full build clean.

Verification note

The unit tests cover parsing/resolution, but the live query needs a real terminal (the test harness's stdin isn't a tty). Best confirmed by running the construct binary on a light vs dark terminal and watching the palette switch. mode = "dark"/"light" forces it if your terminal doesn't answer OSC 11.

Non-goals

Doesn't paint its own background (still relies on the terminal's); no live re-detection on theme change (resolved once at startup); no Windows OSC (→ dark).

The Matrix palette was a single dark-tuned set of foreground colors drawn on
the terminal's own background, so on a light terminal the light-green text was
hard to read.

Now the TUI ships the palette in two variants (Theme::dark / Theme::light) and
resolves which to use at startup. theme.toml gains `mode = "auto" | "light" |
"dark"` (default auto). In auto, the TUI queries the terminal background via
OSC 11 (\x1b]11;?\x07), parses the RGB reply into luminance, and picks light
(>0.5) or dark; a terminal that doesn't answer falls back to dark. `[colors]`
overrides apply on top of the active variant.

The query runs once right after enable_raw_mode (before the event loop consumes
stdin), with a ~120ms timed poll/read on the tty fd (Unix; non-unix → dark).

- theme.rs: ThemeMode, Theme::light(), ThemeConfig (load + resolve),
  detect_terminal_is_light + OSC 11 luminance parsing.
- cli Cargo.toml: libc (Unix) for the timed read.
- app.rs: load config at startup, re-resolve the palette after raw mode.
- docs/configuration.md + spec 0027.

Tests: mode parsing, forced-vs-auto resolution + dark fallback, OSC 11
luminance (light/dark, 8- and 16-bit channels). 7 theme tests pass.
@edwin-zvs
edwin-zvs merged commit 1688dbb into main Jun 12, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the tui-terminal-theme-aware branch June 12, 2026 18:58
@edwin-zvs edwin-zvs mentioned this pull request Jun 16, 2026
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