Repository navigation
Description
Summary
OpenCode TUI's Adaptive Theme always starts in dark mode regardless of the terminal's actual background color. The terminal background query (OSC 11) works correctly and returns a valid response, but the luminance calculation in mode() incorrectly assumes the RGBA channels are in the 0..255 range when they are in fact normalized to 0..1. As a result, even a pure white background produces a luminance of ~0.0039, which never crosses the 0.5 threshold, so the theme is always resolved as "dark".
Environment
- OpenCode version: 1.14.20 (also reproduces on the latest dev branch)
- First reported in: issues related to v1.2.27
- OS: Ubuntu 24.04 (OS-independent; reproduces on any environment where the terminal supports OSC 11 responses)
- Terminals: Windows Terminal, kitty, Ghostty, and other terminals that respond to OSC 11
- Runtime: Bun-based TUI
Details
Expected Behavior
- When the terminal emulator's background is light → OpenCode TUI should start with the light theme.
- When the terminal emulator's background is dark → OpenCode TUI should start with the dark theme.
Actual Behavior
- TUI always starts in dark mode, even when the terminal background is clearly light.
- Manually toggling via /theme switches between light and dark correctly, but the initial adaptive detection is always biased toward dark.
- Even after the user unlocks the theme, runtime adaptive events (THEME_MODE) are not reflected correctly because the underlying mode detection is stuck on dark due to the luminance miscalculation.
Root Cause
Location
packages/opencode/src/cli/cmd/tui/util/terminal.ts
Offending code
function mode(background: RGBA | null): "dark" | "light" {
if (!background) return "dark"
const luminance = (0.299 * background.r + 0.587 * background.g + 0.114 * background.b) / 255
return luminance > 0.5 ? "light" : "dark"
}Why this is wrong
In this codebase, the .r, .g, and .b channels of an RGBA object are normalized floats in the range 0..1, not 0..255. Other theme-related code in the same repository (context/theme.tsx) treats them this way explicitly:
// RGBA stores floats in range 0-1, convert to 0-255
const bgR = bg.r * 255
const bgG = bg.g * 255
const bgB = bg.b * 255
const luminance = 0.299 * bgR + 0.587 * bgG + 0.114 * bgBHere the channels are explicitly multiplied by 255 before computing luminance.
In contrast, terminal.ts's mode() function takes the already-normalized 0..1 values and divides them by 255 again. Consequences:
- A white background
(1, 1, 1)yields a luminance of~0.0039instead of~1.0. - The
0.5threshold is essentially unreachable, so the result is always"dark"for any non-black background.
Numerical proof
| Background | r, g, b (normalized) | Current luminance | Expected luminance | Result |
|---|---|---|---|---|
| White | 1.0, 1.0, 1.0 | 0.0039 | 1.0 | dark (wrong) |
| Light gray | 0.8, 0.8, 0.8 | 0.0031 | 0.8 | dark (wrong) |
| Mid gray | 0.5, 0.5, 0.5 | 0.0020 | 0.5 | dark (wrong) |
| Black | 0.0, 0.0, 0.0 | 0.0 | 0.0 | dark (correct, but coincidental) |
Real-world OSC 11 response
Response observed from kitty when querying OSC 11:
b'\x1b]11;rgb:f4f4/f4f4/f4f4\x1b\\'
This is a very light background (~0.957 normalized), but the current code computes luminance ≈ 0.00375 → "dark".
History
The bug has existed for a while and was preserved across refactors:
e95474df0— "fix: revert parts of a824064 which caused system theme regression (fix: revert parts of a824064c4 which caused system theme regression #23714)"- earlier
3eb6508a6— "refactor: share TUI terminal background detection (refactor: share TUI terminal background detection #22297)"
Both commits contain the same erroneous / 255 expression.
Proposed Fix
Before
function mode(background: RGBA | null): "dark" | "light" {
if (!background) return "dark"
const luminance = (0.299 * background.r + 0.587 * background.g + 0.114 * background.b) / 255
return luminance > 0.5 ? "light" : "dark"
}After
function mode(background: RGBA | null): "dark" | "light" {
if (!background) return "dark"
const luminance = 0.299 * background.r + 0.587 * background.g + 0.114 * background.b
return luminance > 0.5 ? "light" : "dark"
}Impact
- Fix effect:
getTerminalBackgroundColor()now returns the correct"dark"/"light"based on the actual terminal background. - TUI propagation: The correct initial mode is propagated along
app.tsx→ThemeProvider→resolveTheme(), so the adaptive theme works as intended at startup. - Side effects: None — this is purely a fix to a single arithmetic expression; no behavioral change elsewhere.
- non-TTY / timeout fallback: Still returns
"dark"as before (intended behavior preserved).
Related Issues
- Several themes don't support light appearance - dark and light modes are identical #18397 — "Several themes don't support light appearance - dark and light modes are identical"
- That issue concerns theme JSON files where
darkandlightentries happen to be identical. - This report is about mode detection itself being wrong, which is orthogonal to Several themes don't support light appearance - dark and light modes are identical #18397, though both can be present simultaneously.
- That issue concerns theme JSON files where
Verification
- Static analysis:
RGBAusage across the codebase is consistent with0..1normalized floats. - Numerical reproduction: verified in Node.js that the current expression produces the values shown in the table above.
- OSC 11 response parsing: confirmed the real terminal response format is parsed correctly.
-
bun typecheckpasses inpackages/opencode. - Build succeeds:
bun run script/build.ts --single. - Smoke test: built binary runs and responds to
--version.
Plugins
No response
OpenCode version
1.14.20
Steps to reproduce
- Use a terminal emulator configured with a light background color.
- Run the TUI: opencode
- Observe that the TUI starts in dark mode.
Screenshot and/or share link
Theme
Terminal Theme: Light
Before Fix
After Fix
Theme
Before Fix
After Fix
Operating System
Ubuntu 24.04
Terminal
Kitty, Warp
Activity
Klassikcat commentedon Apr 22, 2026
@rekram1-node I already have the fix applied locally and builds pass — happy to open a PR if you'd like.
[FIX] terminal adaptive theme issue
Klassikcat commentedon Apr 25, 2026
Important changes: Brightness detection was moved into OpenTUI in 1.14.24. opencode pins OpenTUI 0.1.99, which only supports CSI ?997 — this breaks on terminals that don't support the sequence (kitty 0.32.2, apt/noble ghostty, etc.).
Quick hack: update opencode's OpenTUI dep to >=0.1.100 and rebuild it. Works normally as usual.
Klassikcat commentedon May 24, 2026
Resolved recent Opencode version. close the issue.