Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createMemo, createResource, createEffect, onMount, For, Show } from "so
import { createStore } from "solid-js/store"
import { useSDK } from "@tui/context/sdk"
import { useSync } from "@tui/context/sync"
import { useTheme } from "@tui/context/theme"
import { useTheme, selectedForeground } from "@tui/context/theme"
import { SplitBorder } from "@tui/component/border"
import { useCommandDialog } from "@tui/component/dialog-command"
import { Locale } from "@/util/locale"
Expand Down Expand Up @@ -455,7 +455,7 @@ export function Autocomplete(props: {
{...SplitBorder}
borderColor={theme.border}
>
<box backgroundColor={theme.backgroundElement} height={height()}>
<box backgroundColor={theme.backgroundMenu} height={height()}>
<For
each={options()}
fallback={
Expand All @@ -471,11 +471,11 @@ export function Autocomplete(props: {
backgroundColor={index() === store.selected ? theme.primary : undefined}
flexDirection="row"
>
<text fg={index() === store.selected ? theme.background : theme.text} flexShrink={0}>
<text fg={index() === store.selected ? selectedForeground(theme) : theme.text} flexShrink={0}>
{option.display}
</text>
<Show when={option.description}>
<text fg={index() === store.selected ? theme.background : theme.textMuted} wrapMode="none">
<text fg={index() === store.selected ? selectedForeground(theme) : theme.textMuted} wrapMode="none">
{option.description}
</text>
</Show>
Expand Down
15 changes: 11 additions & 4 deletions packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -791,10 +791,17 @@ export function Prompt(props: PromptProps) {
height={1}
border={["bottom"]}
borderColor={theme.backgroundElement}
customBorderChars={{
...EmptyBorder,
horizontal: "▀",
}}
customBorderChars={
theme.background.a != 0
? {
...EmptyBorder,
horizontal: "▀",
}
: {
...EmptyBorder,
horizontal: " ",
}
}
/>
</box>
<box flexDirection="row" justifyContent="space-between">
Expand Down
15 changes: 14 additions & 1 deletion packages/opencode/src/cli/cmd/tui/context/theme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Theme = {
background: RGBA
backgroundPanel: RGBA
backgroundElement: RGBA
backgroundMenu: RGBA
border: RGBA
borderActive: RGBA
borderSubtle: RGBA
Expand Down Expand Up @@ -86,6 +87,15 @@ type Theme = {
syntaxPunctuation: RGBA
}

export function selectedForeground(theme: Theme): RGBA {
if (theme.background.a === 0) {
const { r, g, b } = theme.primary
const luminance = 0.299 * r + 0.587 * g + 0.114 * b
return luminance > 0.5 ? RGBA.fromInts(0, 0, 0) : RGBA.fromInts(255, 255, 255)
}
return theme.background
}

type HexColor = `#${string}`
type RefName = string
type Variant = {
Expand Down Expand Up @@ -145,11 +155,13 @@ function resolveTheme(theme: ThemeJson, mode: "dark" | "light") {
}
return resolveColor(c[mode])
}
return Object.fromEntries(
const resolved = Object.fromEntries(
Object.entries(theme.theme).map(([key, value]) => {
return [key, resolveColor(value)]
}),
) as Theme
if (!theme.theme.backgroundMenu) resolved.backgroundMenu = resolved.backgroundElement
return resolved
}

export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
Expand Down Expand Up @@ -291,6 +303,7 @@ function generateSystem(colors: TerminalColors, mode: "dark" | "light"): ThemeJs
background: bg,
backgroundPanel: grays[2],
backgroundElement: grays[3],
backgroundMenu: grays[3],

// Border colors
borderSubtle: grays[6],
Expand Down
17 changes: 7 additions & 10 deletions packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { InputRenderable, RGBA, ScrollBoxRenderable, TextAttributes } from "@opentui/core"
import { useTheme } from "@tui/context/theme"
import { useTheme, selectedForeground } from "@tui/context/theme"
import { entries, filter, flatMap, groupBy, pipe, take } from "remeda"
import { batch, createEffect, createMemo, For, Show } from "solid-js"
import { createStore } from "solid-js/store"
Expand Down Expand Up @@ -262,32 +262,29 @@ function Option(props: {
onMouseOver?: () => void
}) {
const { theme } = useTheme()
const fg = selectedForeground(theme)

return (
<>
<Show when={props.current}>
<text
flexShrink={0}
fg={props.active ? theme.background : props.current ? theme.primary : theme.text}
marginRight={0.5}
>
<text flexShrink={0} fg={props.active ? fg : props.current ? theme.primary : theme.text} marginRight={0.5}>
</text>
</Show>
<text
flexGrow={1}
fg={props.active ? theme.background : props.current ? theme.primary : theme.text}
fg={props.active ? fg : props.current ? theme.primary : theme.text}
attributes={props.active ? TextAttributes.BOLD : undefined}
overflow="hidden"
wrapMode="none"
paddingLeft={3}
>
{Locale.truncate(props.title, 62)}
<span style={{ fg: props.active ? theme.background : theme.textMuted }}> {props.description}</span>
<span style={{ fg: props.active ? fg : theme.textMuted }}> {props.description}</span>
</text>
<Show when={props.footer}>
<box flexShrink={0}>
<text fg={props.active ? theme.background : theme.textMuted}>{props.footer}</text>
<text fg={props.active ? fg : theme.textMuted}>{props.footer}</text>
</box>
</Show>
</>
Expand Down