refactor(plugin): expose resolved TUI theme (#39536)

This commit is contained in:
James Long 2026-07-29 12:51:55 -04:00 committed by GitHub
commit c2e975c4e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 227 additions and 210 deletions

View file

@ -4,7 +4,7 @@ import { testRender } from "@opentui/solid"
import type { JSX } from "solid-js"
import { onMount, type ParentProps } from "solid-js"
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
import { ThemeProvider, useTheme, useThemes } from "../../../src/context/theme"
import { ThemeProvider, useThemes } from "../../../src/context/theme"
import type { Plugin } from "@opencode-ai/plugin/tui"
import { ConfigProvider } from "../../../src/config"
import {
@ -12,7 +12,6 @@ import {
type DiffViewerFileTreeProps,
} from "../../../src/feature-plugins/system/diff-viewer-file-tree"
import { TestTuiContexts } from "../../fixture/tui-environment"
import { createPluginTheme } from "../../../src/plugin/context"
import {
allExpandedFileTreeDirectories,
buildFileTree,
@ -130,7 +129,7 @@ describe("DiffViewerFileTree", () => {
})
function ThemedDiffViewerFileTree(props: Omit<DiffViewerFileTreeProps, "context">) {
return <DiffViewerFileTree {...props} context={{ theme: createPluginTheme(useTheme(), useThemes()) } as Plugin.Context} />
return <DiffViewerFileTree {...props} context={{ theme: useThemes().currentTokens() } as Plugin.Context} />
}
async function renderFrame(component: () => JSX.Element) {

View file

@ -11,7 +11,7 @@ import type {
Route,
Slot,
} from "@opencode-ai/plugin/tui/context"
import { ThemeProvider, useTheme, useThemes } from "../../../src/context/theme"
import { ThemeProvider, useThemes } from "../../../src/context/theme"
import { ConfigProvider } from "../../../src/config"
import { TuiKeybind } from "../../../src/config/keybind"
import { Keymap } from "../../../src/context/keymap"
@ -21,7 +21,6 @@ import { TestTuiContexts } from "../../fixture/tui-environment"
import { createApi, createEventStream, createFetch, json } from "../../fixture/tui-client"
import { DialogProvider } from "../../../src/ui/dialog"
import { ToastProvider } from "../../../src/ui/toast"
import { createPluginTheme } from "../../../src/plugin/context"
test("closing the diff viewer returns to the route it opened from", async () => {
const viewer = await renderDiffViewer([])
@ -158,7 +157,7 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
})
}, createEventStream())
function Harness() {
let theme: ReturnType<typeof createPluginTheme>
let theme: ReturnType<ReturnType<typeof useThemes>["currentTokens"]>
const context = {
options: {},
client: createApi(transport.fetch),
@ -207,7 +206,7 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?:
void diffViewerPlugin.setup(context)
function Content() {
theme = createPluginTheme(useTheme(), useThemes())
theme = useThemes().currentTokens()
const commandView = renderCommands?.({})
if (current.type !== "plugin") commands.get("diff.open")?.run()
return (

View file

@ -6,7 +6,7 @@ import { DEFAULT_THEME, selectTheme } from "@opencode-ai/theme/tui"
import { createTuiResolvedConfig } from "../../fixture/tui-runtime"
import { DEFAULT_THEMES } from "../../../src/theme"
import { ConfigProvider } from "../../../src/config"
import { ThemeContextProvider, ThemeProvider, useTheme, useThemes, type ThemeError } from "../../../src/context/theme"
import { ThemeContextProvider, ThemeProvider, type ThemeError, useTheme, useThemes } from "../../../src/context/theme"
async function wait(fn: () => boolean) {
const started = Date.now()
@ -129,9 +129,11 @@ test("contextual hooks resolve overrides and fall back to a standalone theme's b
} as const
let themes: ReturnType<typeof useThemes> | undefined
let theme: ReturnType<typeof useTheme> | undefined
let explicit: ReturnType<typeof useTheme> | undefined
function ContextProbe() {
theme = useTheme()
explicit = useTheme("elevated")
return <text>{theme.text.default.toString()}</text>
}
@ -160,9 +162,11 @@ test("contextual hooks resolve overrides and fall back to a standalone theme's b
await wait(() => themes?.ready === true)
if (!themes) throw new Error("Theme provider is not mounted")
if (!theme) throw new Error("Contextual theme is not mounted")
if (!explicit) throw new Error("Explicit contextual theme is not mounted")
expect(theme.text.default.equals(RGBA.fromHex("#abcdef"))).toBeTrue()
expect(theme.text.default).toBe(themes.contextual("elevated").text.default)
expect(themes.contextual("overlay").background.default).toBe(themes.current.background.default)
expect(theme).toBe(explicit)
expect(theme.text.default).toBe(themes.current.contextual.elevated.text.default)
expect(themes.current.contextual.overlay.background.default).toBe(themes.current.background.default)
} finally {
app.renderer.destroy()
}