Skip to content

feat(tui): opt-in "auto" theme that follows the terminal light/dark background#3504

Merged
dgageot merged 1 commit into
mainfrom
feat/tui-auto-theme
Jul 7, 2026
Merged

feat(tui): opt-in "auto" theme that follows the terminal light/dark background#3504
dgageot merged 1 commit into
mainfrom
feat/tui-auto-theme

Conversation

@Sayt-0

@Sayt-0 Sayt-0 commented Jul 7, 2026

Copy link
Copy Markdown
Member

Closes #3500

Adds an explicit, opt-in auto theme that follows the terminal's light/dark background, at startup and live. Default behavior is unchanged: without auto, no terminal queries are emitted and escape sequences are byte-identical to today.

Behavior

Situation Result
settings.theme: auto, --theme auto, or "Auto (match terminal)" in /theme background queried at startup (OSC 11), theme follows the detected polarity
Dark background settings.theme_dark (default default)
Light background settings.theme_light (default: new bundled default-light)
Non-TTY / terminal that never answers dark fallback, bounded wait, no hang
Appearance flip while running (DEC mode 2031 terminals) theme switches live; terminals without 2031 re-sync on focus
Exit mode 2031 reset (in-TUI quit path plus a post-Run safety net)

Implementation (issue plan mapping)

Issue step Change
1. sentinel + polarity + resolver pkg/tui/styles/auto_theme.go: AutoThemeRef, SetTerminalDark/TerminalIsDark (dark default), SetAutoThemeEnabled/AutoThemeEnabled, ResolveThemeRef
2. default-light theme + contrast test pkg/tui/styles/themes/default-light.yaml, WCAG checks built on colorutil.go helpers
3. theme_dark/theme_light new Settings fields in pkg/userconfig
4. startup detection + validation/completion applyTheme() resolves auto via a TTY-gated lipgloss.HasDarkBackground; validateTheme and completeTheme accept auto; docker-agent new now applies the configured theme like run and board
5. live switching + mode reset appModel.Init emits tea.Raw(ansi.SetModeLightDark) when auto is active; uv.DarkColorSchemeEvent/uv.LightColorSchemeEvent and tea.BackgroundColorMsg apply the pair through the existing styles.ApplyTheme + ThemeChangedMsg invalidation path; tea.RequestBackgroundColor re-issued on tea.FocusMsg; mode reset on quit and after Program.Run returns
6. picker entry + persistence "Auto (match terminal)" entry with an exclusive current badge, persists settings.theme: auto, enables/resets mode 2031 when toggled mid-session
7. tests see below
8. docs docs/features/tui/index.md (new "Auto Theme" section), docs/features/cli/index.md (--theme row)

Acceptance criteria

Criterion Status
Unchanged config: behavior and emitted sequences byte-identical yes (queries, mode set/reset and focus re-queries are all gated on auto)
theme: auto picks light/dark at launch yes (synchronous OSC 11 pre-TUI, correct first frame)
Live switching on appearance flip yes (mode 2031 events, focus re-query for terminals without the mode)
--theme auto accepted, validated, shell-completed; picker offers Auto and persists it yes
Non-TTY/tmux: dark fallback, no hang; mode 2031 reset on exit yes
build, test, lint clean; docs updated yes (three pre-existing failures are unrelated network-bound SSRF tests: pkg/config, pkg/httpclient, pkg/teamloader)

Testing

Layer Coverage
Resolver, polarity, enabled state, configured pair unit tests (pkg/tui/styles)
default-light readability WCAG contrast test over the foreground/background role pairs
OSC 11 round-trip pty-based test: a fake terminal answers light, dark, or nothing; raw mode, response parsing and bounded timeout verified
DEC 2031 wire format raw CSI ?997;1|2 n bytes through the real ultraviolet decoder into appModel.Update
Picker choices, change/preview/no-op, mode toggling, quit reset, init unit tests (pkg/tui)
Flag validation, precedence, completion unit tests (cmd/root)

Manual verification in a graphical terminal (Ghostty or kitty: launch on light and dark, flip the OS appearance while running, check for stray ?997 reports after exit) remains recommended.

Notes

  • A user theme literally named auto no longer shadows the sentinel; it lists and loads as user:auto.
  • docker-agent new previously ignored settings.theme; it now applies it, aligning with run and board as the issue assumes.
  • Live 2031 handling inside the standalone lean TUI event loop stays a follow-up, per the issue.
  • github.com/creack/pty (already in the dependency graph via docker/cli) becomes a direct, test-only dependency; github.com/charmbracelet/ultraviolet moves from indirect to direct.

…background

Resolve the new "auto" theme sentinel (settings.theme, the --theme flag, or
the /theme picker's "Auto (match terminal)" entry) to a configurable pair:
settings.theme_dark (default "default") on dark backgrounds and
settings.theme_light (default: new bundled "default-light") on light ones.

The terminal background is queried synchronously (OSC 11, TTY-gated, dark
fallback) before the TUI starts so the first frame has the right polarity,
covering run, lean mode, board, the agent picker and new. In the full TUI,
DEC mode 2031 reports switch the theme live and the mode is reset on exit.

Unset or concrete theme configs keep today's behavior and emit byte-identical
escape sequences.

Closes #3500
@Sayt-0
Sayt-0 requested a review from a team as a code owner July 7, 2026 11:11
@dgageot
dgageot merged commit 2e17d81 into main Jul 7, 2026
18 checks passed
@dgageot
dgageot deleted the feat/tui-auto-theme branch July 7, 2026 11:17
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.

TUI: add opt-in "auto" theme that follows the terminal's light/dark background (incl. live switching)

2 participants