TUI: set DECSET 2031 so an in-place terminal theme change updates the composer colours

Open 💬 5 comments Opened Aug 14, 2026 by everlof

What variant of Codex are you using?

CLI (codex-cli 0.147.0, macOS arm64)

What feature would you like to see?

Codex reads the terminal's foreground and background once at startup with OSC 10/11 and holds that answer for the process lifetime. The discussion in #18942 is about restoring a re-query on FocusGained. That covers a user switching macOS appearance while Codex sits in a background window, but it cannot cover a terminal that changes its own colours while its window keeps focus, because there is no focus transition to hang the re-query on.

DECSET 2031 is the mechanism for that case:

  • The application subscribes with CSI ? 2031 h, and unsubscribes with CSI ? 2031 l on shutdown.
  • The terminal then sends CSI ? 997 ; 1 n when it becomes dark and CSI ? 997 ; 2 n when it becomes light, whenever its colour scheme changes for any reason.
  • The report is a prompt to re-ask rather than the news itself, so the handler is just "re-issue the OSC 10/11 probe" and reuses whatever probe path the FocusGained fix lands on. The 1/2 payload only says which direction it moved.

This matters for every terminal whose theme is its own rather than the OS's: Ghostty and WezTerm configs, iTerm2 profiles, and any host app with its own palette. Concretely, in Threading, a macOS terminal I maintain that hosts agent CLIs and owns its own themes, a user switching the app theme from a dark one to a light one leaves a running Codex painting a #393939 composer plate with #CFCFCF ink onto a #FFFFFF background, and there is no focus change anywhere in that interaction. Quitting and resuming the session is the only recovery.

Claude Code subscribes to 2031 and re-themes live under the same theme switch. strings on the Codex 0.147.0 binary contains no 2031, so a terminal that emits the report has nothing Codex can hear.

Additional information

Measured against 0.147.0 in a bare PTY (no tmux, no SSH, no emulator), harness and results in https://github.com/openai/codex/issues/18942: exactly one OSC 10/11 query per process, at startup, and none afterwards under focus events, keyboard input, window resize or SIGWINCH.

2031 originates in Contour's terminal spec and is implemented by Ghostty, WezTerm, foot and others, so the report arrives from real terminals today.

View original on GitHub ↗

5 Comments

jdcodes1 · 9 days ago

Implementation notes from the current code, for whoever picks this up:

The palette is a process-lifetime cache with an attempted flag, filled once by the bounded startup probe (https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/tui/src/terminal_palette.rs#L140-L175). set_default_colors_from_startup_probe already overwrites it, so the "re-probe on 2031 report" handler mostly exists — reset the cache and re-run the OSC 10/11 probe, then trigger a full redraw.

The real blocker is plumbing: after startup the TUI reads input through crossterm's event stream, and crossterm's parser silently discards CSI sequences it doesn't recognize — CSI ? 997 ; Ps n is one of them. So the report never reaches application code without either a crossterm patch/fork hook or moving the subscription parsing into the raw-input layer the startup probe already uses. That plumbing choice is shared with the FocusGained re-query in #18942, so it's worth solving once for both: any mechanism that can re-run the probe post-startup serves 2031, focus, and a manual /theme refresh fallback alike.

me-on-gh · 3 days ago

Environment

  • Codex CLI 0.149.1
  • macOS Terminal.app
  • tmux 3.7b
  • TERM=tmux-256color

Problem

When Terminal.app switches between light and dark profiles while Codex remains
running, Codex keeps the foreground/background palette cached at startup.

The terminal and tmux repaint correctly, but the Codex composer retains colors
from the previous theme and can become effectively unreadable.

Previously I worked around this by sending CSI I (FocusGained) to marked
Codex panes. That stopped working after #35649 / commit 976129f, because
FocusGained now requests a redraw without re-querying OSC 10/11 colors.

tmux refresh-client, SIGWINCH, resize events, and synthetic focus events
therefore only redraw the stale cached palette. Restarting Codex corrects it.

Reproduction

  1. Start Codex using a light Terminal.app profile.
  2. Change Terminal.app to a dark profile without restarting Codex.
  3. Focus away and back, or send CSI I through tmux.
  4. Observe that the composer keeps its light-theme colors.
  5. Restart Codex and observe that the colors become correct.

Requirement

A long-running Codex TUI should be able to refresh its cached terminal
foreground/background colors without restarting and without losing or delaying
keyboard input.

Suggested acceptance criteria

  • Palette refresh is bounded and non-blocking.
  • Queued keyboard input is preserved.
  • Palette-derived composer, transcript and syntax colors are recalculated.
  • It works through tmux.
  • When automatic theme-change detection is unavailable, provide an explicit

trigger such as /refresh-theme, a signal, or a documented control sequence.

DECSET 2031 may cover supporting terminals, but an explicit refresh mechanism is
still needed for terminals or multiplexers that do not support or forward it.

cblp0k · 2 days ago

Confirming this issue on Linux with Foot, including the current Codex release.

Environment

  • Codex CLI: 0.149.0, standalone Linux x86_64 build
  • Terminal: Foot 1.27.0
  • TERM=foot
  • COLORTERM=truecolor
  • Session: Hyprland / Wayland
  • OS: Manjaro Linux, kernel 7.2.0--1-MANJARO
  • Direct Foot PTY, without tmux or SSH

Reproduction

  1. Start Foot with its light color theme active.
  2. Start codex.
  3. Keep the Foot window and Codex TUI focused.
  4. Switch the existing Foot instance to its dark theme at runtime using Foot native two-theme switching (SIGUSR1/SIGUSR2).

Actual behavior

Foot changes its background and palette immediately, but the running Codex TUI keeps the old light RGB background in the composer/input area.

The stale white composer remains after keyboard input, normal redraws, and an explicit SIGWINCH. Restarting only Codex — without restarting Foot — fixes the colors immediately.

Expected behavior

Codex should subscribe to Foot private mode 2031 notifications. After receiving a theme-change report, it should:

  1. re-query the terminal defaults through OSC 10/11;
  2. update the cached terminal foreground/background;
  3. invalidate cached TUI styles;
  4. perform a full redraw.

Foot 1.27 implements private mode 2031, so this appears to be the same missing subscription described in this issue. This reproduction also confirms that the problem is still present in Codex CLI 0.149.0 and affects the light-to-dark direction.

piotrostr · 1 day ago

Reference implementation is ready:

It subscribes to DEC mode 2031, handles color-scheme reports automatically, re-queries OSC 10/11 through an input-preserving probe, replaces the palette cache, re-resolves the adaptive syntax theme, fixes stale composer foreground cells, and redraws. No command or config surface.

Validated on Ghostty 1.2.3 through tmux 3.6a on macOS. just test -p codex-tui: 3976 passed, 6 skipped, including a PTY regression test for automatic refresh and interleaved typeahead preservation.

The repository blocks external PR creation, so the Codex change is hosted as a reviewable fork PR for maintainers to cherry-pick or port.

~ codex & piotr

fbaltor · 1 day ago

Confirming on Linux with a direct Ghostty PTY, without tmux or SSH.

Environment

  • Codex CLI 0.149.1
  • Ghostty 1.3.1 (GTK, TERM=xterm-ghostty, COLORTERM=truecolor)
  • NixOS 26.05, GNOME/Wayland

Reproduction

  1. Start Codex while Ghostty uses a light theme.
  2. From another Ghostty tab/session, switch the theme globally. My theme helper writes the selected theme's OSC 10/11/12 and OSC 4 palette sequences directly to every live Ghostty PTY, then reloads Ghostty.
  3. The affected terminal surface changes to the dark palette immediately, while remaining focused.
  4. The running Codex composer/input field remains a bright white block against the dark terminal.
  5. Exit and resume Codex; the composer is immediately correct.

This is the same stale startup palette described here, but confirms the in-place/no-focus-transition path on Ghostty 1.3.1 under Linux. Normal redraws do not repair it because the terminal defaults have changed while Codex retains palette-derived RGB values.