Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions packages/opencode/src/cli/cmd/tui/context/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ type ColorValue = HexColor | RefName | Variant | RGBA
export type ThemeJson = {
$schema?: string
defs?: Record<string, HexColor | RefName>
theme: Omit<Record<ThemeColor, ColorValue>, "selectedListItemText" | "backgroundMenu"> & {
theme: Omit<Record<ThemeColor, ColorValue>, "selectedListItemText" | "backgroundMenu" | "backgroundDialogOverlay" | "backgroundSidebarOverlay"> & {
selectedListItemText?: ColorValue
backgroundMenu?: ColorValue
backgroundDialogOverlay?: ColorValue
backgroundSidebarOverlay?: ColorValue
thinkingOpacity?: number
}
}
Expand Down Expand Up @@ -222,7 +224,7 @@ export function resolveTheme(theme: ThemeJson, mode: "dark" | "light") {

const resolved = Object.fromEntries(
Object.entries(theme.theme)
.filter(([key]) => key !== "selectedListItemText" && key !== "backgroundMenu" && key !== "thinkingOpacity")
.filter(([key]) => key !== "selectedListItemText" && key !== "backgroundMenu" && key !== "backgroundDialogOverlay" && key !== "backgroundSidebarOverlay" && key !== "thinkingOpacity")
.map(([key, value]) => {
return [key, resolveColor(value as ColorValue)]
}),
Expand All @@ -245,6 +247,20 @@ export function resolveTheme(theme: ThemeJson, mode: "dark" | "light") {
resolved.backgroundMenu = resolved.backgroundElement
}

// Handle backgroundDialogOverlay - optional with fallback to semi-transparent black
if (theme.theme.backgroundDialogOverlay !== undefined) {
resolved.backgroundDialogOverlay = resolveColor(theme.theme.backgroundDialogOverlay)
} else {
resolved.backgroundDialogOverlay = RGBA.fromInts(0, 0, 0, 150)
}

// Handle backgroundSidebarOverlay - optional with fallback to semi-transparent black
if (theme.theme.backgroundSidebarOverlay !== undefined) {
resolved.backgroundSidebarOverlay = resolveColor(theme.theme.backgroundSidebarOverlay)
} else {
resolved.backgroundSidebarOverlay = RGBA.fromInts(0, 0, 0, 70)
}

// Handle thinkingOpacity - optional with default of 0.6
const thinkingOpacity = theme.theme.thinkingOpacity ?? 0.6

Expand Down Expand Up @@ -575,6 +591,8 @@ function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJs
backgroundPanel: grays[2],
backgroundElement: grays[3],
backgroundMenu: grays[3],
backgroundDialogOverlay: transparent,
backgroundSidebarOverlay: transparent,

// Border colors
borderSubtle: grays[6],
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1221,7 +1221,7 @@ export function Session() {
right={0}
bottom={0}
alignItems="flex-end"
backgroundColor={RGBA.fromInts(0, 0, 0, 70)}
backgroundColor={theme.backgroundSidebarOverlay}
>
<Sidebar sessionID={route.sessionID} />
</box>
Expand Down
4 changes: 2 additions & 2 deletions packages/opencode/src/cli/cmd/tui/ui/dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/solid"
import { batch, createContext, Show, useContext, type JSX, type ParentProps } from "solid-js"
import { useTheme } from "@tui/context/theme"
import { MouseButton, Renderable, RGBA } from "@opentui/core"
import { MouseButton, Renderable } from "@opentui/core"
import { createStore } from "solid-js/store"
import { useToast } from "./toast"
import { Flag } from "@opencode-ai/core/flag/flag"
Expand Down Expand Up @@ -44,7 +44,7 @@ export function Dialog(
paddingTop={dimensions().height / 4}
left={0}
top={0}
backgroundColor={RGBA.fromInts(0, 0, 0, 150)}
backgroundColor={theme.backgroundDialogOverlay}
>
<box
onMouseUp={(e) => {
Expand Down
16 changes: 16 additions & 0 deletions packages/opencode/test/cli/tui/theme-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,19 @@ test("resolveTheme rejects circular color refs", () => {

expect(() => resolveTheme(item, "dark")).toThrow("Circular color reference")
})

test("resolveTheme uses default for overlay backgrounds when not specified", () => {
const item = structuredClone(DEFAULT_THEMES.opencode)
const resolved = resolveTheme(item, "dark")
expect(resolved.backgroundDialogOverlay.a).toBeCloseTo(150 / 255, 2)
expect(resolved.backgroundSidebarOverlay.a).toBeCloseTo(70 / 255, 2)
})

test("resolveTheme uses custom overlay background values", () => {
const item = structuredClone(DEFAULT_THEMES.opencode)
item.theme.backgroundDialogOverlay = "#ff0000"
item.theme.backgroundSidebarOverlay = "#0000ff"
const resolved = resolveTheme(item, "dark")
expect(resolved.backgroundDialogOverlay.r).toEqual(1)
expect(resolved.backgroundSidebarOverlay.b).toEqual(1)
})
2 changes: 2 additions & 0 deletions packages/opencode/test/fixture/tui-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ function themeCurrent(): HostPluginApi["theme"]["current"] {
backgroundPanel: h,
backgroundElement: i,
backgroundMenu: i,
backgroundDialogOverlay: RGBA.fromInts(0, 0, 0, 150),
backgroundSidebarOverlay: RGBA.fromInts(0, 0, 0, 70),
border: j,
borderActive: c,
borderSubtle: i,
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin/src/tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ export type TuiThemeCurrent = {
readonly backgroundPanel: RGBA
readonly backgroundElement: RGBA
readonly backgroundMenu: RGBA
readonly backgroundDialogOverlay: RGBA
readonly backgroundSidebarOverlay: RGBA
readonly border: RGBA
readonly borderActive: RGBA
readonly borderSubtle: RGBA
Expand Down
Loading