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

@ -33,6 +33,7 @@ export {
export type {
Categorical,
ContextName,
FormfieldColor,
Hue,
HueSource,
@ -40,7 +41,7 @@ export type {
ResolvedActionState,
ResolvedFormfieldState,
ResolvedTheme,
ResolvedThemeView,
ResolvedThemeTokens,
StatefulColor,
} from "./types.js"
export { DEFAULT_CATEGORICAL, DEFAULT_THEME } from "./defaults.js"

View file

@ -15,11 +15,12 @@ import {
} from "./schema.js"
import type {
ActionStateKey,
ContextName,
HueDefinition,
HueScale,
ResolvedActionState,
ResolvedTheme,
ResolvedThemeView,
ResolvedThemeTokens,
StatefulColorDefinition,
ThemeTokensDefinition,
} from "./index.js"
@ -64,16 +65,17 @@ function resolveExpandedTheme(definition: ThemeDefinition): ResolvedTheme {
const hueSteps = compileHueSteps(hue)
const base = tokens(definition)
const resolved = resolveView(base, hue, categorical, hueSteps)
const contexts = Object.fromEntries(
Object.entries(definition)
.filter(([key]) => key.startsWith("@context:"))
.map(([key, override]) => {
const contextual = contextualize(base, override as ThemeTokensDefinition)
return [key, resolveView(contextual, hue, categorical, hueSteps)]
}),
)
const context = (name: ContextName) => {
const override = definition[`@context:${name}`]
if (!override) return resolved
return resolveView(contextualize(base, override), hue, categorical, hueSteps)
}
const contextual = {
elevated: context("elevated"),
overlay: context("overlay"),
}
return { ...resolved, contexts } as ResolvedTheme
return { ...resolved, contextual } as ResolvedTheme
}
function tokens(definition: ThemeDefinition): ThemeTokensDefinition {
@ -132,15 +134,15 @@ function contextualActions(
function resolveView(
definition: ThemeTokensDefinition,
hue: ResolvedThemeView["hue"],
categorical: ResolvedThemeView["categorical"],
hueSteps: Pick<ResolvedThemeView, "source" | "increase" | "decrease">,
): ResolvedThemeView {
hue: ResolvedThemeTokens["hue"],
categorical: ResolvedThemeTokens["categorical"],
hueSteps: Pick<ResolvedThemeTokens, "source" | "increase" | "decrease">,
): ResolvedThemeTokens {
const source: Record<string, unknown> = { hue, ...definition }
return { ...(createResolver(source)(source, "theme") as ResolvedThemeView), hue, categorical, ...hueSteps }
return { ...(createResolver(source)(source, "theme") as ResolvedThemeTokens), hue, categorical, ...hueSteps }
}
function compileHueSteps(hue: ResolvedThemeView["hue"]): Pick<ResolvedThemeView, "source" | "increase" | "decrease"> {
function compileHueSteps(hue: ResolvedThemeTokens["hue"]): Pick<ResolvedThemeTokens, "source" | "increase" | "decrease"> {
const index = new WeakMap<RGBA, { hue: keyof typeof hue; step: HueStep; position: number }>()
for (const [name, scale] of Object.entries(hue) as [keyof typeof hue, HueScale][]) {
HueStep.literals.forEach((step, position) => index.set(scale[step], { hue: name, step, position }))
@ -201,7 +203,7 @@ function resolveHue(definition: HueDefinition) {
return Object.fromEntries(
[...BaseHue.literals, ...HueAlias.literals].map((name) => [name, resolve(name, [])]),
) as ResolvedThemeView["hue"]
) as ResolvedThemeTokens["hue"]
}
function createResolver(source: Record<string, unknown>) {

View file

@ -1,7 +1,7 @@
import { SyntaxStyle, type RGBA, type ThemeTokenStyle } from "@opentui/core"
import type { Mode, ResolvedThemeView } from "./index.js"
import type { Mode, ResolvedThemeTokens } from "./index.js"
export function generateSyntax(theme: ResolvedThemeView, mode: Mode) {
export function generateSyntax(theme: ResolvedThemeTokens, mode: Mode) {
const step = mode === "light" ? 800 : 200
const syntax = theme.syntax
const markdown = theme.markdown

View file

@ -7,7 +7,6 @@ import type {
HueAlias,
HueStep,
MarkdownToken,
ContextKey,
SyntaxToken,
} from "./schema.js"
@ -20,7 +19,7 @@ export type Categorical = readonly HueScale[]
export type StatefulColor = Readonly<Record<ResolvedActionState, RGBA>>
export type FormfieldColor = StatefulColor
export type ResolvedThemeView = {
export type ResolvedThemeTokens = {
readonly hue: Hue
readonly categorical: Categorical
readonly source: (color: RGBA) => HueSource | undefined
@ -63,6 +62,8 @@ export type ResolvedThemeView = {
readonly markdown: Readonly<Record<MarkdownToken, RGBA>>
}
export type ResolvedTheme = ResolvedThemeView & {
readonly contexts: Readonly<Partial<Record<ContextKey, ResolvedThemeView>>>
export type ContextName = "elevated" | "overlay"
export type ResolvedTheme = ResolvedThemeTokens & {
readonly contextual: Readonly<Record<ContextName, ResolvedThemeTokens>>
}