Composer text becomes unreadable after live terminal theme switch
## Repro
- Start Codex TUI in a terminal with a dark theme.
- Type in the composer.
- Switch the terminal to a light theme without restarting Codex.
- Return focus to Codex and keep typing.
## Actual
Composer input becomes nearly invisible, effectively white/light text on a white/light background.
## Expected
Composer text should stay readable after a terminal theme change.
## Suspected cause
The composer background is recomputed from terminal colors, but textarea text is rendered with default/inherited cell styling. After a dark -> light theme switch, stale
foreground styling can remain while the background updates, leaving the input white-on-white.
## Relevant files
codex-rs/tui/src/bottom_pane/chat_composer.rscodex-rs/tui/src/bottom_pane/textarea.rscodex-rs/tui/src/tui/event_stream.rs
Before switch:
<img width="2557" height="702" alt="Image" src="https://github.com/user-attachments/assets/4cdae374-f963-41c5-8b6c-8dac98093eb1" />
After:
<img width="2557" height="702" alt="Image" src="https://github.com/user-attachments/assets/39a368a8-48b0-49f5-bce7-ecd55b05a1c7" />
Same after dark to light switches
# Evidence:
- Composer background uses
user_message_style():
<https://github.com/openai/codex/blob/main/codex-rs/tui/src/bottom_pane/chat_composer.rs#L4380>
<https://github.com/openai/codex/blob/main/codex-rs/tui/src/style.rs#L8>
- Typed text uses plain
Style::default():
<https://github.com/openai/codex/blob/main/codex-rs/tui/src/bottom_pane/textarea.rs#L1373>
- Palette refresh only happens on
FocusGained:
<https://github.com/openai/codex/blob/main/codex-rs/tui/src/tui/event_stream.rs#L249>
- Style guide warning about white/black assumptions:
<https://github.com/openai/codex/blob/main/codex-rs/tui/styles.md#L17>
3 Comments
Analysis & Root Cause
I traced the rendering path through the TUI code.
Root cause:
terminal_palette.rscaptures the terminal's foreground/background colors at startup (or palette construction time). When the terminal theme changes externally (dark -> light), ratatui widgets that useColor::Resetor no explicit color inherit the new terminal defaults correctly. But the composer input widget inpublic_widgets/composer_input.rsappears to use explicit or cached foreground colors derived from the original palette, creating a stale-color-on-new-background mismatch.Relevant files:
codex-rs/tui/src/terminal_palette.rs- initial color capturecodex-rs/tui/src/public_widgets/composer_input.rs- text renderingcodex-rs/tui/src/color.rsandcodex-rs/tui/src/style.rs- color/style helperscodex-rs/tui/src/bottom_pane/list_selection_view.rs- theme picker (syntax highlighting themes, separate concern)The pattern: Any widget that caches a resolved foreground color from the palette at init time will break on theme switch. The fix needs to address the root (palette staleness), not just the symptom (composer text).
Proposed Fix
Simplest approach: In the composer input widget, use
Color::Resetfor text foreground instead of a cached color value. This delegates color choice to the terminal, which always reflects the current theme. This is the lowest-risk fix and matches how most ratatui apps handle default text colors.More robust approach: Detect terminal theme changes via OSC 10/11 escape sequences (query terminal fg/bg colors) on each draw cycle or on SIGWINCH. If the resolved colors differ from the cached palette, rebuild the palette. This would fix the issue for all widgets, not just the composer.
The simpler
Color::Resetapproach is probably right for a first fix - it solves the reported problem with minimal blast radius. Happy to submit a PR if invited.I can confirm this is still extant. It's particularly a problem if you have long running tasks and use auto-light mode.
Working automatic reference implementation covering this repro is posted here: https://github.com/openai/codex/issues/38575#issuecomment-5425261944
It includes both live DEC mode 2031 refresh and the stale composer foreground fix. Validated on Ghostty 1.2.3 through tmux 3.6a. No command or config surface.
~ codex & piotr