refactor(plugin): expose resolved TUI theme (#39536)
This commit is contained in:
parent
6aa250ee5d
commit
c2e975c4e6
49 changed files with 227 additions and 210 deletions
3
bun.lock
3
bun.lock
|
|
@ -600,6 +600,7 @@
|
|||
"zod": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
"@opentui/keymap": "catalog:",
|
||||
"@opentui/solid": "catalog:",
|
||||
|
|
@ -611,12 +612,14 @@
|
|||
"typescript": "catalog:",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opentui/core": ">=0.4.5",
|
||||
"@opentui/keymap": ">=0.4.5",
|
||||
"@opentui/solid": ">=0.4.5",
|
||||
"solid-js": ">=1.9.0",
|
||||
},
|
||||
"optionalPeers": [
|
||||
"@opencode-ai/theme",
|
||||
"@opentui/core",
|
||||
"@opentui/keymap",
|
||||
"@opentui/solid",
|
||||
|
|
|
|||
|
|
@ -30,12 +30,16 @@
|
|||
"zod": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opentui/core": ">=0.4.5",
|
||||
"@opentui/keymap": ">=0.4.5",
|
||||
"@opentui/solid": ">=0.4.5",
|
||||
"solid-js": ">=1.9.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opencode-ai/theme": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentui/core": {
|
||||
"optional": true
|
||||
},
|
||||
|
|
@ -50,6 +54,7 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opencode-ai/theme": "workspace:*",
|
||||
"@opentui/core": "catalog:",
|
||||
"@opentui/keymap": "catalog:",
|
||||
"@opentui/solid": "catalog:",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import type {
|
|||
ShellInfo,
|
||||
SkillInfo,
|
||||
} from "@opencode-ai/client"
|
||||
import type { ResolvedTheme } from "@opencode-ai/theme/tui"
|
||||
import type { CliRenderer, KeyEvent, Renderable } from "@opentui/core"
|
||||
import type { JSX } from "@opentui/solid"
|
||||
import type { Store } from "solid-js/store"
|
||||
|
|
@ -356,7 +357,7 @@ export interface Context {
|
|||
readonly client: OpenCodeClient
|
||||
readonly data: Data
|
||||
readonly attention: Attention
|
||||
readonly theme: any
|
||||
readonly theme: ResolvedTheme
|
||||
readonly keymap: Keymap
|
||||
readonly storage: Storage
|
||||
readonly ui: UI
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
} from "@opentui/core"
|
||||
import { extend, useRenderer } from "@opentui/solid"
|
||||
import { onCleanup, onMount } from "solid-js"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme, useThemes } from "../context/theme"
|
||||
import { tint } from "../theme/color"
|
||||
import { GoUpsellArtPainter } from "./bg-pulse-render"
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ extend({ go_upsell_art: GoUpsellArtRenderable })
|
|||
|
||||
export function BgPulse() {
|
||||
const themes = useThemes()
|
||||
const theme = themes.contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const mode = themes.mode
|
||||
const renderer = useRenderer()
|
||||
let targetFps = renderer.targetFps
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export function DevToolsBar() {
|
|||
const renderer = useRenderer()
|
||||
const dimensions = useTerminalDimensions()
|
||||
const { current: theme, mode, supports, setMode } = themes
|
||||
const elevatedTheme = themes.contextual("elevated")
|
||||
const elevatedTheme = useTheme("elevated")
|
||||
const [panel, setPanel] = createSignal<Panel>()
|
||||
const [dumping, setDumping] = createSignal(false)
|
||||
const [dumpPath, setDumpPath] = createSignal<string>()
|
||||
|
|
@ -435,7 +435,7 @@ function BarItem(props: ParentProps<{ active: boolean; onClick: () => void }>) {
|
|||
}
|
||||
|
||||
function PanelBox(props: ParentProps) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const renderer = useRenderer()
|
||||
return (
|
||||
<box
|
||||
|
|
@ -461,7 +461,7 @@ function PanelBox(props: ParentProps) {
|
|||
}
|
||||
|
||||
function PanelTitle(props: ParentProps) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
return (
|
||||
<text fg={theme.text.default} attributes={TextAttributes.BOLD} marginBottom={1}>
|
||||
{props.children}
|
||||
|
|
@ -470,7 +470,7 @@ function PanelTitle(props: ParentProps) {
|
|||
}
|
||||
|
||||
function Row(props: { label: string; value: string }) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text fg={theme.text.subdued}>{props.label}</text>
|
||||
|
|
@ -481,7 +481,7 @@ function Row(props: { label: string; value: string }) {
|
|||
}
|
||||
|
||||
function Action(props: ParentProps<{ onClick: () => void; disabled?: boolean; hoverBackground?: boolean }>) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [hovered, setHovered] = createSignal(false)
|
||||
return (
|
||||
<box
|
||||
|
|
@ -506,7 +506,7 @@ function cpuPercent(microseconds: number, milliseconds: number) {
|
|||
}
|
||||
|
||||
function ProcessStat(props: { label: string; values: readonly number[]; unit: string; decimals?: number }) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const value = () => {
|
||||
const value = props.values.at(-1)
|
||||
if (value === undefined) return "--"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { useClipboard } from "../context/clipboard"
|
|||
import { useData } from "../context/data"
|
||||
import { useClient } from "../context/client"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
import { DialogPrompt } from "../ui/dialog-prompt"
|
||||
import { DialogSelect } from "../ui/dialog-select"
|
||||
|
|
@ -64,7 +64,7 @@ export function DialogIntegration(
|
|||
) {
|
||||
const data = useData()
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const options = createMemo(() => {
|
||||
const providers = data.location.websearch.list() ?? []
|
||||
const providersByID = new Map(providers.map((provider) => [provider.id, provider]))
|
||||
|
|
@ -303,8 +303,8 @@ function CommandPending(props: {
|
|||
|
||||
function CommandView(props: { title: string; output: string; message: string }) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const overlayTheme = useThemes().contextual("overlay")
|
||||
const theme = useTheme("elevated")
|
||||
const overlayTheme = useTheme("overlay")
|
||||
onMount(() => dialog.setSize("large"))
|
||||
return (
|
||||
<box gap={1} paddingBottom={1}>
|
||||
|
|
@ -341,7 +341,7 @@ function KeyMethod(props: {
|
|||
const dialog = useDialog()
|
||||
const client = useClient()
|
||||
const toast = useToast()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [error, setError] = createSignal<string>()
|
||||
|
||||
return (
|
||||
|
|
@ -516,7 +516,7 @@ function OAuthCode(props: {
|
|||
const dialog = useDialog()
|
||||
const client = useClient()
|
||||
const toast = useToast()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [error, setError] = createSignal<string>()
|
||||
let settled = false
|
||||
|
||||
|
|
@ -561,7 +561,7 @@ function OAuthCode(props: {
|
|||
|
||||
function OAuthView(props: { title: string; url?: string; instructions?: string; message: string; copy?: boolean }) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
return (
|
||||
<box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
|
||||
<box flexDirection="row" justifyContent="space-between">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Keymap } from "../context/keymap"
|
|||
import { pipe, sortBy } from "remeda"
|
||||
import { DialogSelect } from "../ui/dialog-select"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { TextAttributes, type ScrollBoxRenderable } from "@opentui/core"
|
||||
import type { McpServer } from "@opencode-ai/client"
|
||||
import { useClipboard } from "../context/clipboard"
|
||||
|
|
@ -20,7 +20,7 @@ function statusError(status: McpServer["status"]) {
|
|||
}
|
||||
|
||||
function Status(props: { enabled: boolean; loading: boolean }) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
if (props.loading) return <span style={{ fg: theme.text.subdued }}>⋯ Loading</span>
|
||||
if (props.enabled) {
|
||||
return <span style={{ fg: theme.text.feedback.success.default, attributes: TextAttributes.BOLD }}>✓ Enabled</span>
|
||||
|
|
@ -33,7 +33,7 @@ export function DialogMcp() {
|
|||
const dialog = useDialog()
|
||||
const client = useClient()
|
||||
const toast = useToast()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [focused, setFocused] = createSignal<string>()
|
||||
const [detail, setDetail] = createSignal<McpServer>()
|
||||
const [loading, setLoading] = createSignal<string | null>(null)
|
||||
|
|
@ -134,8 +134,8 @@ function DialogMcpError(props: { server: McpServer; onBack: () => void }) {
|
|||
const dialog = useDialog()
|
||||
const clipboard = useClipboard()
|
||||
const toast = useToast()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const overlayTheme = useThemes().contextual("overlay")
|
||||
const theme = useTheme("elevated")
|
||||
const overlayTheme = useTheme("overlay")
|
||||
const dimensions = useTerminalDimensions()
|
||||
const config = useConfig().data
|
||||
const [copied, setCopied] = createSignal(false)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
|
|||
import { useDialog } from "../ui/dialog"
|
||||
import { useClient } from "../context/client"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useData } from "../context/data"
|
||||
import { abbreviateHome } from "../runtime"
|
||||
import { useTuiPaths } from "../context/runtime"
|
||||
|
|
@ -38,7 +38,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) {
|
|||
const dialog = useDialog()
|
||||
const client = useClient()
|
||||
const dimensions = useTerminalDimensions()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const sessionData = useData()
|
||||
const route = useRoute()
|
||||
const toast = useToast()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useTerminalDimensions } from "@opentui/solid"
|
|||
import { createMemo, createResource, createSignal, For, Show } from "solid-js"
|
||||
import { renderUnicodeCompact } from "uqr"
|
||||
import { useClient } from "../context/client"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
import { errorMessage } from "../util/error"
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ export function DialogPair(props: { credentials?: DialogPairCredentials }) {
|
|||
const client = useClient()
|
||||
const dialog = useDialog()
|
||||
const dimensions = useTerminalDimensions()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [loadError, setLoadError] = createSignal<unknown>()
|
||||
const [showPassword, setShowPassword] = createSignal(false)
|
||||
const [passwordHover, setPasswordHover] = createSignal(false)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import { InputRenderable, TextAttributes } from "@opentui/core"
|
|||
import { Slug } from "@opencode-ai/core/util/slug"
|
||||
import { createSignal, onMount } from "solid-js"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "../ui/dialog"
|
||||
|
||||
export function DialogProjectCopyName(props: { onConfirm: (name: string) => void }) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const shortcuts = Keymap.useShortcuts()
|
||||
const [inputTarget, setInputTarget] = createSignal<InputRenderable>()
|
||||
let input: InputRenderable
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { RGBA, TextAttributes } from "@opentui/core"
|
|||
import open from "open"
|
||||
import { createSignal } from "solid-js"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "../ui/dialog"
|
||||
import { Link } from "../ui/link"
|
||||
import { BgPulse } from "./bg-pulse"
|
||||
|
|
@ -38,7 +38,7 @@ function panelOverlay(color: RGBA) {
|
|||
|
||||
export function DialogRetryAction(props: DialogRetryActionProps) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const showGoTreatment = () => props.link === GO_URL
|
||||
const textBg = () => (showGoTreatment() ? panelOverlay(theme.background.default) : undefined)
|
||||
const [selected, setSelected] = createSignal<"dismiss" | "action">("action")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { For } from "solid-js"
|
||||
|
|
@ -13,7 +13,7 @@ export function DialogSessionDeleteFailed(props: {
|
|||
onDone?: () => void
|
||||
}) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [store, setStore] = createStore({
|
||||
active: "delete" as "delete" | "restore",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useRoute } from "../context/route"
|
|||
import { useData } from "../context/data"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { Locale } from "../util/locale"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme, useThemes } from "../context/theme"
|
||||
import { useClient } from "../context/client"
|
||||
import { useLocal } from "../context/local"
|
||||
import { createDebouncedSignal } from "../util/signal"
|
||||
|
|
@ -22,7 +22,7 @@ export function DialogSessionList() {
|
|||
const route = useRoute()
|
||||
const data = useData()
|
||||
const themes = useThemes()
|
||||
const theme = themes.contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const mode = themes.mode
|
||||
const client = useClient()
|
||||
const local = useLocal()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { DialogSelect } from "../ui/dialog-select"
|
|||
import { createMemo, createSignal } from "solid-js"
|
||||
import { Locale } from "../util/locale"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { usePromptStash, type StashEntry } from "./prompt/stash"
|
||||
|
||||
function getRelativeTime(timestamp: number): string {
|
||||
|
|
@ -29,7 +29,7 @@ function getStashPreview(input: string, maxLength: number = 50): string {
|
|||
export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) {
|
||||
const dialog = useDialog()
|
||||
const stash = usePromptStash()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const shortcuts = Keymap.useShortcuts()
|
||||
|
||||
const [toDelete, setToDelete] = createSignal<number>()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog } from "../ui/dialog"
|
||||
import { useData } from "../context/data"
|
||||
import { For, Match, Switch, Show, createMemo } from "solid-js"
|
||||
|
|
@ -8,7 +8,7 @@ export type DialogStatusProps = {}
|
|||
|
||||
export function DialogStatus() {
|
||||
const data = useData()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const dialog = useDialog()
|
||||
|
||||
const mcp = createMemo(() => data.location.mcp.server.list() ?? [])
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import type { VcsFileStatus } from "@opencode-ai/client"
|
|||
import { createMemo, For } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { FilePath } from "../ui/file-path"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useConfig } from "../config"
|
||||
import { useDialog, type DialogContext } from "../ui/dialog"
|
||||
import { getScrollAcceleration } from "../util/scroll"
|
||||
|
|
@ -31,8 +31,8 @@ export function DialogWorkspaceFileChanges(props: {
|
|||
message?: string
|
||||
}) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const overlayTheme = useThemes().contextual("overlay")
|
||||
const theme = useTheme("elevated")
|
||||
const overlayTheme = useTheme("overlay")
|
||||
const config = useConfig().data
|
||||
const dimensions = useTerminalDimensions()
|
||||
const scrollAcceleration = createMemo(() => getScrollAcceleration(config))
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { getScrollAcceleration } from "../../util/scroll"
|
|||
import { useTuiPaths } from "../../context/runtime"
|
||||
import { useConfig } from "../../config"
|
||||
import { useLocation } from "../../context/location"
|
||||
import { useThemes } from "../../context/theme"
|
||||
import { useTheme } from "../../context/theme"
|
||||
import { SplitBorder } from "../../ui/border"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { Locale } from "../../util/locale"
|
||||
|
|
@ -57,7 +57,7 @@ export function Autocomplete(props: {
|
|||
const data = useData()
|
||||
const keymap = Keymap.use()
|
||||
const keymapCommands = Keymap.useCommands()
|
||||
const theme = useThemes().contextual("overlay")
|
||||
const theme = useTheme("overlay")
|
||||
const dimensions = useTerminalDimensions()
|
||||
const frecency = useFrecency()
|
||||
const config = useConfig().data
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { RGBA } from "@opentui/core"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { Spinner } from "./spinner"
|
||||
|
||||
export function Reconnecting() {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
|
||||
return (
|
||||
<box
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { createEffect, createMemo, createSignal, onCleanup, Show } from "solid-js"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { Spinner } from "./spinner"
|
||||
|
||||
export function StartupLoading(props: { ready: () => boolean }) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [show, setShow] = createSignal(false)
|
||||
const text = createMemo(() => (props.ready() ? "Finishing startup..." : "Loading plugins..."))
|
||||
let wait: NodeJS.Timeout | undefined
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import { CliRenderEvents, SyntaxStyle, type TerminalColors } from "@opentui/core"
|
||||
import { useRenderer } from "@opentui/solid"
|
||||
import { generateSyntax, resolveThemeDocument, themeModes } from "@opencode-ai/theme/tui"
|
||||
import {
|
||||
generateSyntax,
|
||||
resolveThemeDocument,
|
||||
themeModes,
|
||||
type ResolvedTheme,
|
||||
type ContextName,
|
||||
} from "@opencode-ai/theme/tui"
|
||||
import {
|
||||
DEFAULT_THEMES,
|
||||
addTheme,
|
||||
|
|
@ -95,10 +101,9 @@ type State = {
|
|||
ready: boolean
|
||||
}
|
||||
|
||||
type ContextName = "elevated" | "overlay"
|
||||
type Themes = {
|
||||
current: ComponentTheme
|
||||
contextual(context: ContextName): ComponentTheme
|
||||
currentTokens: Accessor<ResolvedTheme>
|
||||
readonly selected: string
|
||||
all: typeof allThemes
|
||||
has: typeof hasTheme
|
||||
|
|
@ -115,6 +120,12 @@ type Themes = {
|
|||
readonly ready: boolean
|
||||
}
|
||||
|
||||
type ThemeContextValue = {
|
||||
current: ComponentTheme["contextual"][ContextName]
|
||||
themes: Themes
|
||||
readonly ready: boolean
|
||||
}
|
||||
|
||||
const [store, setStore] = createStore<State>({
|
||||
themes: allThemes(),
|
||||
mode: "dark",
|
||||
|
|
@ -127,7 +138,7 @@ subscribeThemes((themes) => setStore("themes", themes))
|
|||
|
||||
const themeContext = createSimpleContext({
|
||||
name: "Theme",
|
||||
init: (props: { mode: "dark" | "light"; source?: ThemeSource }) => {
|
||||
init: (props: { mode: "dark" | "light"; source?: ThemeSource }): ThemeContextValue => {
|
||||
const renderer = useRenderer()
|
||||
const configState = useConfig()
|
||||
const config = configState.data
|
||||
|
|
@ -309,21 +320,14 @@ const themeContext = createSimpleContext({
|
|||
valuesV2()
|
||||
themePerformance.set("Init", `${(performance.now() - initStarted).toFixed(2)} ms`)
|
||||
const current = createComponentTheme(valuesV2, mode)
|
||||
const contextsV2 = {
|
||||
elevated: createComponentTheme(() => valuesV2().contexts["@context:elevated"] ?? valuesV2(), mode),
|
||||
overlay: createComponentTheme(() => valuesV2().contexts["@context:overlay"] ?? valuesV2(), mode),
|
||||
}
|
||||
|
||||
createEffect(() => renderer.setBackgroundColor(valuesV2().background.default))
|
||||
|
||||
const currentSyntax = createSyntaxStyleMemo(() => generateSyntax(valuesV2(), mode()))
|
||||
function contextual(context: ContextName) {
|
||||
return contextsV2[context]
|
||||
}
|
||||
const service: Themes = {
|
||||
current,
|
||||
currentTokens: valuesV2,
|
||||
currentSyntax,
|
||||
contextual,
|
||||
get selected() {
|
||||
return store.active
|
||||
},
|
||||
|
|
@ -368,15 +372,20 @@ const themeContext = createSimpleContext({
|
|||
export function useThemes() {
|
||||
return themeContext.use().themes
|
||||
}
|
||||
export function useTheme() {
|
||||
return themeContext.use().current
|
||||
export function useTheme(): ComponentTheme
|
||||
export function useTheme(context: ContextName): ComponentTheme["contextual"][ContextName]
|
||||
export function useTheme(context?: ContextName) {
|
||||
const value = themeContext.use()
|
||||
return context ? value.themes.current.contextual[context] : value.current
|
||||
}
|
||||
export const ThemeProvider = themeContext.provider
|
||||
|
||||
export function ThemeContextProvider(props: ParentProps<{ context: ContextName }>) {
|
||||
const themes = useThemes()
|
||||
const value = themeContext.use()
|
||||
return (
|
||||
<themeContext.context.Provider value={{ current: themes.contextual(props.context), themes, ready: themes.ready }}>
|
||||
<themeContext.context.Provider
|
||||
value={{ current: value.themes.current.contextual[props.context], themes: value.themes, ready: value.ready }}
|
||||
>
|
||||
{props.children}
|
||||
</themeContext.context.Provider>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { Panel, PanelGroup, Separator } from "./diff-viewer-ui"
|
|||
import { DialogSelect } from "../../ui/dialog-select"
|
||||
import { getScrollAcceleration } from "../../util/scroll"
|
||||
import { useConfig } from "../../config"
|
||||
import { useThemes } from "../../context/theme"
|
||||
import {
|
||||
allExpandedFileTreeDirectories,
|
||||
buildFileTree,
|
||||
|
|
@ -83,6 +84,7 @@ function DiffViewer(props: { context: Plugin.Context }) {
|
|||
const config = useConfig()
|
||||
const dialog = props.context.ui.dialog
|
||||
const theme = props.context.theme
|
||||
const currentSyntax = useThemes().currentSyntax
|
||||
const params = () => {
|
||||
const route = props.context.ui.router.current()
|
||||
return (route.type === "plugin" ? route.data : undefined) as
|
||||
|
|
@ -834,7 +836,7 @@ function DiffViewer(props: { context: Plugin.Context }) {
|
|||
diff={patch()}
|
||||
view={view()}
|
||||
filetype={reviewed() ? PLAIN_TEXT_FILETYPE : filetype(entry.file.file)}
|
||||
syntaxStyle={theme.syntaxStyle()}
|
||||
syntaxStyle={currentSyntax()}
|
||||
showLineNumbers={true}
|
||||
width="100%"
|
||||
wrapMode="char"
|
||||
|
|
@ -941,7 +943,7 @@ function DiffViewer(props: { context: Plugin.Context }) {
|
|||
}
|
||||
|
||||
function DiffViewerHelpDialog(props: { context: Plugin.Context }) {
|
||||
const theme = props.context.theme.contextual("elevated")
|
||||
const theme = props.context.theme.contextual.elevated
|
||||
const shortcut = (id: string) => () => props.context.keymap.shortcuts(id)[0]
|
||||
const rows = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function Commands(props: { context: Plugin.Context }) {
|
|||
function Scrap(props: { context: Plugin.Context }) {
|
||||
const dimensions = useTerminalDimensions()
|
||||
const theme = props.context.theme
|
||||
const elevatedTheme = props.context.theme.contextual("elevated")
|
||||
const elevatedTheme = theme.contextual.elevated
|
||||
const [tabs, setTabs] = createSignal(FIXTURE_TABS.slice(0, 6))
|
||||
const [active, setActive] = createSignal<string | undefined>("fixture-2")
|
||||
const [animations, setAnimations] = createSignal(true)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { Keymap } from "../context/keymap"
|
|||
import { useRoute } from "../context/route"
|
||||
import { useTuiApp, useTuiLifecycle, useTuiPaths } from "../context/runtime"
|
||||
import { useLocation } from "../context/location"
|
||||
import { useTheme, useThemes } from "../context/theme"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { DialogAlert } from "../ui/dialog-alert"
|
||||
import { DialogConfirm } from "../ui/dialog-confirm"
|
||||
import { DialogPrompt } from "../ui/dialog-prompt"
|
||||
|
|
@ -91,9 +91,7 @@ export function PluginProvider(props: ParentProps<{ packages: PackageResolver }>
|
|||
const app = useTuiApp()
|
||||
const paths = useTuiPaths()
|
||||
const location = useLocation()
|
||||
const theme = useTheme()
|
||||
const themes = useThemes()
|
||||
const pluginTheme = createPluginTheme(theme, themes)
|
||||
const dialog = useDialog()
|
||||
const toast = useToast()
|
||||
const attention = useAttention()
|
||||
|
|
@ -226,7 +224,9 @@ export function PluginProvider(props: ParentProps<{ packages: PackageResolver }>
|
|||
client: client.api,
|
||||
data,
|
||||
attention,
|
||||
theme: pluginTheme,
|
||||
get theme() {
|
||||
return themes.currentTokens()
|
||||
},
|
||||
keymap: {
|
||||
layer: Keymap.createLayer,
|
||||
dispatch: keymap.dispatch,
|
||||
|
|
@ -530,24 +530,6 @@ function isPlugin(value: unknown): value is Plugin.Definition {
|
|||
)
|
||||
}
|
||||
|
||||
type PluginTheme = ReturnType<typeof useTheme> & {
|
||||
contextual(context: "elevated" | "overlay"): PluginTheme
|
||||
syntaxStyle(): ReturnType<ReturnType<typeof useThemes>["currentSyntax"]>
|
||||
}
|
||||
|
||||
export function createPluginTheme(theme: ReturnType<typeof useTheme>, themes: ReturnType<typeof useThemes>): PluginTheme {
|
||||
return new Proxy(theme as PluginTheme, {
|
||||
get(target, property, receiver) {
|
||||
if (property === "contextual") {
|
||||
return (context: "elevated" | "overlay") => createPluginTheme(themes.contextual(context), themes)
|
||||
}
|
||||
if (property === "syntaxStyle") return themes.currentSyntax
|
||||
if (Reflect.has(target, property)) return Reflect.get(target, property, receiver)
|
||||
return Reflect.get(themes, property, themes)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function usePlugin() {
|
||||
const value = useContext(PluginContext)
|
||||
if (!value) throw new Error("PluginProvider is missing")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createEffect, createMemo, For, onCleanup, Show, useContext, createContext } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
import { useThemes } from "../../../context/theme"
|
||||
import { useTheme } from "../../../context/theme"
|
||||
import { SplitBorder } from "../../../ui/border"
|
||||
import { Keymap } from "../../../context/keymap"
|
||||
import { SubagentsTab } from "./subagents-tab"
|
||||
|
|
@ -39,7 +39,7 @@ export type ComposerProps = {
|
|||
}
|
||||
|
||||
export function Composer(props: ComposerProps) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
|
||||
const [store, setStore] = createStore({
|
||||
tabs: {} as Record<string, Tab>,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { createMemo, createSignal, For, onCleanup, onMount, Show } from "solid-j
|
|||
import { useRenderer, useTerminalDimensions } from "@opentui/solid"
|
||||
import type { ScrollBoxRenderable, TextareaRenderable } from "@opentui/core"
|
||||
import open from "open"
|
||||
import { useThemes } from "../../context/theme"
|
||||
import { useTheme, useThemes } from "../../context/theme"
|
||||
import type { FormField, FormValue } from "@opencode-ai/client"
|
||||
import type { FormWithLocation } from "../../context/data"
|
||||
import { useClient } from "../../context/client"
|
||||
|
|
@ -45,7 +45,7 @@ function requestOptions(form: FormWithLocation) {
|
|||
export function FormPrompt(props: { form: FormWithLocation }) {
|
||||
const client = useClient()
|
||||
const themes = useThemes()
|
||||
const theme = themes.contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const themeMode = themes.mode
|
||||
const renderer = useRenderer()
|
||||
const dimensions = useTerminalDimensions()
|
||||
|
|
|
|||
|
|
@ -1494,7 +1494,7 @@ function SessionGroupView(props: {
|
|||
function AssistantFooter(props: { message: SessionMessageAssistant }) {
|
||||
const ctx = use()
|
||||
const local = useLocal()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const model = createMemo(
|
||||
() =>
|
||||
ctx
|
||||
|
|
@ -1691,7 +1691,7 @@ function RevertMessage(props: {
|
|||
}>
|
||||
}) {
|
||||
const ctx = use()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const route = useRouteData("session")
|
||||
const client = useClient()
|
||||
const toast = useToast()
|
||||
|
|
@ -1764,7 +1764,7 @@ function RevertMessage(props: {
|
|||
}
|
||||
|
||||
function ShellMessage(props: { message: Extract<SessionMessageInfo, { type: "shell" }> }) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const output = createMemo(() => stripAnsi(props.message.output?.output.trim() ?? ""))
|
||||
|
||||
return (
|
||||
|
|
@ -1792,7 +1792,7 @@ function UserMessage(props: { message: SessionMessageUser }) {
|
|||
const local = useLocal()
|
||||
const files = createMemo(() => props.message.files ?? [])
|
||||
const themes = useThemes()
|
||||
const theme = themes.contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const mode = themes.mode
|
||||
const [hover, setHover] = createSignal(false)
|
||||
const color = createMemo(() => local.agent.color(data.session.get(ctx.sessionID)?.agent ?? "build"))
|
||||
|
|
@ -1869,7 +1869,7 @@ function UserMessage(props: { message: SessionMessageUser }) {
|
|||
function AssistantMessage(props: { message: SessionMessageAssistant; last: boolean }) {
|
||||
const ctx = use()
|
||||
const local = useLocal()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const model = createMemo(
|
||||
() =>
|
||||
ctx
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ function RejectPrompt(props: {
|
|||
onCancel: () => void
|
||||
}) {
|
||||
let input: TextareaRenderable
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const dimensions = useTerminalDimensions()
|
||||
const narrow = createMemo(() => dimensions().width < 80)
|
||||
Keymap.createLayer(() => ({
|
||||
|
|
@ -429,7 +429,7 @@ function Prompt<const T extends Record<string, string>>(props: {
|
|||
fullscreen?: boolean
|
||||
onSelect: (option: keyof T) => void
|
||||
}) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const dimensions = useTerminalDimensions()
|
||||
const keys = Object.keys(props.options) as (keyof T)[]
|
||||
const [store, setStore] = createStore({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useData } from "../../context/data"
|
||||
import { createMemo, Show } from "solid-js"
|
||||
import { useThemes } from "../../context/theme"
|
||||
import { useTheme } from "../../context/theme"
|
||||
import { useConfig } from "../../config"
|
||||
import { PluginSlot } from "../../plugin/context"
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ import { getScrollAcceleration } from "../../util/scroll"
|
|||
|
||||
export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
|
||||
const data = useData()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const config = useConfig().data
|
||||
const session = createMemo(() => data.session.get(props.sessionID))
|
||||
const scrollAcceleration = createMemo(() => getScrollAcceleration(config))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { createMemo, createSignal, Show } from "solid-js"
|
||||
import { useRouteData } from "../../context/route"
|
||||
import { useData } from "../../context/data"
|
||||
import { useThemes } from "../../context/theme"
|
||||
import { useTheme } from "../../context/theme"
|
||||
import { SplitBorder } from "../../ui/border"
|
||||
import { Locale } from "../../util/locale"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
|
|
@ -42,7 +42,7 @@ export function SubagentFooter() {
|
|||
}
|
||||
})
|
||||
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const keymap = Keymap.use()
|
||||
const shortcuts = Keymap.useShortcuts()
|
||||
const [hover, setHover] = createSignal<"parent" | "prev" | "next" | null>(null)
|
||||
|
|
|
|||
|
|
@ -1,41 +1,48 @@
|
|||
import type { RGBA } from "@opentui/core"
|
||||
import type { Accessor } from "solid-js"
|
||||
import type { Mode, ResolvedThemeView } from "@opencode-ai/theme/tui"
|
||||
import type { Mode, ResolvedTheme, ResolvedThemeTokens } from "@opencode-ai/theme/tui"
|
||||
|
||||
export function createComponentTheme(current: Accessor<ResolvedThemeView>, mode: Accessor<Mode>) {
|
||||
return {
|
||||
export function createComponentTheme(current: Accessor<ResolvedTheme>, mode: Accessor<Mode>) {
|
||||
const create = (view: Accessor<ResolvedThemeTokens>) => ({
|
||||
get hue() {
|
||||
return current().hue
|
||||
return view().hue
|
||||
},
|
||||
get categorical() {
|
||||
return current().categorical
|
||||
return view().categorical
|
||||
},
|
||||
get text() {
|
||||
return current().text
|
||||
return view().text
|
||||
},
|
||||
get background() {
|
||||
return current().background
|
||||
return view().background
|
||||
},
|
||||
get border() {
|
||||
return current().border
|
||||
return view().border
|
||||
},
|
||||
get scrollbar() {
|
||||
return current().scrollbar
|
||||
return view().scrollbar
|
||||
},
|
||||
get diff() {
|
||||
return current().diff
|
||||
return view().diff
|
||||
},
|
||||
get syntax() {
|
||||
return current().syntax
|
||||
return view().syntax
|
||||
},
|
||||
get markdown() {
|
||||
return current().markdown
|
||||
return view().markdown
|
||||
},
|
||||
source: (color: RGBA) => current().source(color),
|
||||
increase: (color: RGBA, amount = 1) => current().increase(color, amount),
|
||||
decrease: (color: RGBA, amount = 1) => current().decrease(color, amount),
|
||||
raise: (color: RGBA) => (mode() === "light" ? current().increase(color) : current().decrease(color)),
|
||||
}
|
||||
source: (color: RGBA) => view().source(color),
|
||||
increase: (color: RGBA, amount = 1) => view().increase(color, amount),
|
||||
decrease: (color: RGBA, amount = 1) => view().decrease(color, amount),
|
||||
raise: (color: RGBA) => (mode() === "light" ? view().increase(color) : view().decrease(color)),
|
||||
})
|
||||
|
||||
return Object.assign(create(current), {
|
||||
contextual: {
|
||||
elevated: create(() => current().contextual.elevated),
|
||||
overlay: create(() => current().contextual.overlay),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export type ComponentTheme = ReturnType<typeof createComponentTheme>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "./dialog"
|
||||
|
||||
export type DialogAlertProps = {
|
||||
|
|
@ -11,7 +11,7 @@ export type DialogAlertProps = {
|
|||
|
||||
export function DialogAlert(props: DialogAlertProps) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
|
||||
Keymap.createLayer(() => ({
|
||||
mode: "modal",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "./dialog"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { For } from "solid-js"
|
||||
|
|
@ -21,7 +21,7 @@ export type DialogConfirmResult = boolean | undefined
|
|||
|
||||
export function DialogConfirm(props: DialogConfirmProps) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const [store, setStore] = createStore({
|
||||
active: "confirm" as "confirm" | "cancel",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "./dialog"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { For, Show } from "solid-js"
|
||||
|
|
@ -17,8 +17,8 @@ type Active = ExportFormat | "thinking" | "copy" | "export"
|
|||
|
||||
export function DialogExportOptions(props: DialogExportOptionsProps) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const overlayTheme = useThemes().contextual("overlay")
|
||||
const theme = useTheme("elevated")
|
||||
const overlayTheme = useTheme("overlay")
|
||||
const [store, setStore] = createStore({
|
||||
format: "markdown" as ExportFormat,
|
||||
thinking: props.defaultThinking,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "./dialog"
|
||||
|
||||
export function DialogExportResult(props: { path: string; onClose?: () => void }) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
|
||||
const close = () => {
|
||||
props.onClose?.()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog } from "./dialog"
|
||||
|
||||
export function DialogHelp() {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const shortcuts = Keymap.useShortcuts()
|
||||
|
||||
Keymap.createLayer(() => ({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { TextareaRenderable, TextAttributes } from "@opentui/core"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useDialog, type DialogContext } from "./dialog"
|
||||
import { Show, createEffect, createSignal, onMount, type JSX } from "solid-js"
|
||||
import { Spinner } from "../component/spinner"
|
||||
|
|
@ -18,7 +18,7 @@ export type DialogPromptProps = {
|
|||
|
||||
export function DialogPrompt(props: DialogPromptProps) {
|
||||
const dialog = useDialog()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const shortcuts = Keymap.useShortcuts()
|
||||
const [textareaTarget, setTextareaTarget] = createSignal<TextareaRenderable>()
|
||||
let textarea: TextareaRenderable
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { InputRenderable, RGBA, ScrollBoxRenderable, TextAttributes } from "@opentui/core"
|
||||
import { Keymap, type KeymapCommand } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme, useThemes } from "../context/theme"
|
||||
import { entries, filter, flatMap, groupBy, pipe } from "remeda"
|
||||
import { batch, createEffect, createMemo, createSignal, For, Show, type JSX, on, onCleanup } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
|
|
@ -96,7 +96,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
|
|||
|
||||
const dialog = useDialog()
|
||||
const themes = useThemes()
|
||||
const theme = themes.contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const mode = themes.mode
|
||||
const config = useConfig().data
|
||||
const scrollAcceleration = createMemo(() => getScrollAcceleration(config))
|
||||
|
|
@ -773,7 +773,7 @@ function Option(props: {
|
|||
activeColor?: RGBA
|
||||
onMouseOver?: () => void
|
||||
}) {
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const text = createMemo(() => {
|
||||
if (props.active && !props.muted) return props.activeColor ?? theme.text.action.primary.focused
|
||||
if (props.muted && (props.active || props.current)) return theme.text.subdued
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useRenderer, useTerminalDimensions } from "@opentui/solid"
|
||||
import { batch, createContext, createEffect, onCleanup, Show, useContext, type JSX, type ParentProps } from "solid-js"
|
||||
import { Keymap } from "../context/keymap"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { MouseButton, Renderable, RGBA } from "@opentui/core"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useToast } from "./toast"
|
||||
|
|
@ -16,7 +16,7 @@ export function Dialog(
|
|||
}>,
|
||||
) {
|
||||
const dimensions = useTerminalDimensions()
|
||||
const theme = useThemes().contextual("elevated")
|
||||
const theme = useTheme("elevated")
|
||||
const renderer = useRenderer()
|
||||
|
||||
let dismiss = false
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createContext, useContext, type ParentProps, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useThemes } from "../context/theme"
|
||||
import { useTheme } from "../context/theme"
|
||||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { SplitBorder } from "./border"
|
||||
import { TextAttributes } from "@opentui/core"
|
||||
|
|
@ -14,7 +14,7 @@ type ToastInput = Omit<ToastOptions, "duration"> & { duration?: number }
|
|||
|
||||
export function Toast() {
|
||||
const toast = useToast()
|
||||
const theme = useThemes().contextual("overlay")
|
||||
const theme = useTheme("overlay")
|
||||
const dimensions = useTerminalDimensions()
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import { expect, test } from "bun:test"
|
||||
import { createSignal } from "solid-js"
|
||||
import { RGBA } from "@opentui/core"
|
||||
import { DEFAULT_THEME, resolveTheme, selectTheme, type ContextKey } from "@opencode-ai/theme/tui"
|
||||
import { DEFAULT_THEME, resolveTheme, selectTheme, type ContextName } from "@opencode-ai/theme/tui"
|
||||
import { createComponentTheme } from "../../../src/theme/component"
|
||||
|
||||
test("provides reactive properties, states, contexts, and color operations", () => {
|
||||
const [resolved, setResolved] = createSignal(resolveTheme(selectTheme(DEFAULT_THEME, "light")))
|
||||
const [mode, setMode] = createSignal<"light" | "dark">("light")
|
||||
const [context, setContext] = createSignal<ContextKey>()
|
||||
const theme = createComponentTheme(() => {
|
||||
const key = context()
|
||||
return key ? (resolved().contexts[key] ?? resolved()) : resolved()
|
||||
}, mode)
|
||||
const theme = createComponentTheme(resolved, mode)
|
||||
const [context, setContext] = createSignal<ContextName>()
|
||||
const current = () => {
|
||||
const name = context()
|
||||
return name ? theme.contextual[name] : theme
|
||||
}
|
||||
|
||||
expect(theme.text.default).toBe(resolved().text.default)
|
||||
expect(theme.hue.accent[500]).toBe(resolved().hue.accent[500])
|
||||
|
|
@ -50,20 +51,21 @@ test("provides reactive properties, states, contexts, and color operations", ()
|
|||
expect(theme.scrollbar.default).toBe(resolved().scrollbar.default)
|
||||
expect(theme.diff.text.added).toBe(resolved().diff.text.added)
|
||||
|
||||
setContext("@context:elevated")
|
||||
expect(theme.categorical.map((scale) => scale[500])).toEqual(resolved().categorical.map((scale) => scale[500]))
|
||||
expect(theme.text.default).toBe(resolved().contexts["@context:elevated"]!.text.default)
|
||||
expect(theme.background.action.primary.focused).toBe(
|
||||
resolved().contexts["@context:elevated"]!.background.action.primary.focused,
|
||||
setContext("elevated")
|
||||
expect("contexts" in current()).toBeFalse()
|
||||
expect(current().categorical.map((scale) => scale[500])).toEqual(resolved().categorical.map((scale) => scale[500]))
|
||||
expect(current().text.default).toBe(resolved().contextual.elevated.text.default)
|
||||
expect(current().background.action.primary.focused).toBe(
|
||||
resolved().contextual.elevated.background.action.primary.focused,
|
||||
)
|
||||
expect(theme.background.action.primary.hovered).toBe(resolved().background.surface.overlay)
|
||||
expect(theme.background.formfield.selected).toBe(
|
||||
resolved().contexts["@context:elevated"]!.background.formfield.selected,
|
||||
expect(current().background.action.primary.hovered).toBe(resolved().background.surface.overlay)
|
||||
expect(current().background.formfield.selected).toBe(
|
||||
resolved().contextual.elevated.background.formfield.selected,
|
||||
)
|
||||
|
||||
setResolved(resolveTheme(selectTheme(DEFAULT_THEME, "dark")))
|
||||
setMode("dark")
|
||||
expect(theme.text.default).toBe(resolved().contexts["@context:elevated"]!.text.default)
|
||||
expect(theme.decrease(theme.background.surface.offset, 1)).toBe(resolved().hue.neutral[600])
|
||||
expect(theme.raise(theme.background.surface.offset)).toBe(resolved().hue.neutral[600])
|
||||
expect(current().text.default).toBe(resolved().contextual.elevated.text.default)
|
||||
expect(current().decrease(current().background.surface.offset, 1)).toBe(resolved().hue.neutral[600])
|
||||
expect(current().raise(current().background.surface.offset)).toBe(resolved().hue.neutral[600])
|
||||
})
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ test("validates and resolves categorical hues in configured order", () => {
|
|||
expect(theme.categorical[0]).toBe(theme.hue.accent)
|
||||
expect(theme.categorical[1]).toBe(theme.hue.red)
|
||||
expect(theme.categorical[2]).toBe(theme.hue.interactive)
|
||||
expect(theme.contexts["@context:elevated"]?.categorical).toBe(theme.categorical)
|
||||
expect(theme.contextual.elevated.categorical).toBe(theme.categorical)
|
||||
expect(() => resolveSource({ version: 2, light: { categorical: [] } }, "light")).toThrow("Invalid theme")
|
||||
expect(() => resolveSource({ version: 2, light: { categorical: ["magenta"] } }, "light")).toThrow("Invalid theme")
|
||||
})
|
||||
|
|
@ -65,29 +65,29 @@ test("resolves independent definitions and hue aliases", () => {
|
|||
expect(lightTheme.source(lightTheme.background.surface.offset)).toEqual({ hue: "neutral", step: 300 })
|
||||
expect(lightTheme.increase(lightTheme.hue.red[100])).toBe(lightTheme.hue.red[200])
|
||||
expect(lightTheme.decrease(lightTheme.hue.red[200])).toBe(lightTheme.hue.red[100])
|
||||
expect(lightTheme.contexts["@context:elevated"]?.increase(lightTheme.hue.red[100])).toBe(lightTheme.hue.red[200])
|
||||
expect(lightTheme.contextual.elevated.increase(lightTheme.hue.red[100])).toBe(lightTheme.hue.red[200])
|
||||
expect(lightTheme.text.default).toBeInstanceOf(RGBA)
|
||||
expect(darkTheme.background.default).toBeInstanceOf(RGBA)
|
||||
expect(lightTheme.background.surface.offset).toBe(lightTheme.hue.neutral[300])
|
||||
expect(lightTheme.background.surface.overlay).toBe(lightTheme.hue.neutral[400])
|
||||
expect(lightTheme.syntax.keyword).toBeInstanceOf(RGBA)
|
||||
expect(lightTheme.text.action.primary.default).toBe(lightTheme.hue.neutral[200])
|
||||
expect(lightTheme.contexts["@context:elevated"]?.background.action.primary.default).toBe(
|
||||
expect(lightTheme.contextual.elevated.background.action.primary.default).toBe(
|
||||
lightTheme.hue.interactive[500],
|
||||
)
|
||||
expect(lightTheme.contexts["@context:elevated"]?.background.default).toBe(lightTheme.background.surface.offset)
|
||||
expect(lightTheme.contexts["@context:elevated"]?.text.action.primary.default).toBe(lightTheme.hue.neutral[100])
|
||||
expect(lightTheme.contexts["@context:overlay"]?.background.action.primary.default).toBe(
|
||||
expect(lightTheme.contextual.elevated.background.default).toBe(lightTheme.background.surface.offset)
|
||||
expect(lightTheme.contextual.elevated.text.action.primary.default).toBe(lightTheme.hue.neutral[100])
|
||||
expect(lightTheme.contextual.overlay.background.action.primary.default).toBe(
|
||||
lightTheme.hue.interactive[500],
|
||||
)
|
||||
expect(lightTheme.contexts["@context:overlay"]?.background.default).toBe(lightTheme.background.surface.overlay)
|
||||
expect(lightTheme.contexts["@context:overlay"]?.text.action.primary.default).toBe(lightTheme.hue.neutral[100])
|
||||
expect(darkTheme.contexts["@context:elevated"]?.background.action.primary.default).toBe(
|
||||
expect(lightTheme.contextual.overlay.background.default).toBe(lightTheme.background.surface.overlay)
|
||||
expect(lightTheme.contextual.overlay.text.action.primary.default).toBe(lightTheme.hue.neutral[100])
|
||||
expect(darkTheme.contextual.elevated.background.action.primary.default).toBe(
|
||||
darkTheme.hue.interactive[400],
|
||||
)
|
||||
expect(darkTheme.contexts["@context:elevated"]?.text.action.primary.default).toBe(darkTheme.hue.neutral[200])
|
||||
expect(darkTheme.contexts["@context:overlay"]?.background.action.primary.default).toBe(darkTheme.hue.interactive[400])
|
||||
expect(darkTheme.contexts["@context:overlay"]?.text.action.primary.default).toBe(darkTheme.hue.neutral[200])
|
||||
expect(darkTheme.contextual.elevated.text.action.primary.default).toBe(darkTheme.hue.neutral[200])
|
||||
expect(darkTheme.contextual.overlay.background.action.primary.default).toBe(darkTheme.hue.interactive[400])
|
||||
expect(darkTheme.contextual.overlay.text.action.primary.default).toBe(darkTheme.hue.neutral[200])
|
||||
})
|
||||
|
||||
test("resolves base hue aliases and rejects circular hue aliases", () => {
|
||||
|
|
@ -228,8 +228,8 @@ test("resolves elevated hover surfaces from direct colors", () => {
|
|||
"light",
|
||||
)
|
||||
|
||||
expect(theme.contexts["@context:elevated"]?.background.default.toInts()).toEqual([18, 52, 86, 255])
|
||||
expect(theme.contexts["@context:elevated"]?.background.action.primary.hovered.toInts()).toEqual([35, 69, 103, 255])
|
||||
expect(theme.contextual.elevated.background.default.toInts()).toEqual([18, 52, 86, 255])
|
||||
expect(theme.contextual.elevated.background.action.primary.hovered.toInts()).toEqual([35, 69, 103, 255])
|
||||
})
|
||||
|
||||
test("resolves transparent colors", () => {
|
||||
|
|
@ -271,7 +271,7 @@ test("context overrides rewire semantic references and apply state precedence",
|
|||
},
|
||||
})
|
||||
const theme = resolveTheme(definition)
|
||||
const overlay = theme.contexts["@context:elevated"]!
|
||||
const overlay = theme.contextual.elevated
|
||||
|
||||
expect(overlay.text.default.toInts()).toEqual([51, 51, 51, 255])
|
||||
expect(overlay.text.action.primary.pressed.toInts()).toEqual([68, 68, 68, 255])
|
||||
|
|
|
|||
|
|
@ -43,11 +43,11 @@ test("migrates resolved V1 modes into V2 tokens", () => {
|
|||
expect(resolved.background.action.primary.selected.toInts()).toEqual([0, 0, 0, 0])
|
||||
expect(resolved.text.action.primary.selected.toInts()).toEqual(legacy.primary.toInts())
|
||||
expect(resolved.background.feedback.error.default.toInts()).toEqual(legacy.background.toInts())
|
||||
expect(resolved.contexts["@context:elevated"]?.background.default.toInts()).toEqual(legacy.backgroundPanel.toInts())
|
||||
expect(resolved.contexts["@context:elevated"]?.background.action.primary.default.toInts()).toEqual([0, 0, 0, 0])
|
||||
expect(resolved.contexts["@context:elevated"]?.text.action.primary.default.toInts()).toEqual(legacy.text.toInts())
|
||||
expect(resolved.contexts["@context:overlay"]?.background.default.toInts()).toEqual(legacy.backgroundMenu.toInts())
|
||||
expect(resolved.contexts["@context:overlay"]?.background.action.primary.default.toInts()).toEqual([0, 0, 0, 0])
|
||||
expect(resolved.contextual.elevated.background.default.toInts()).toEqual(legacy.backgroundPanel.toInts())
|
||||
expect(resolved.contextual.elevated.background.action.primary.default.toInts()).toEqual([0, 0, 0, 0])
|
||||
expect(resolved.contextual.elevated.text.action.primary.default.toInts()).toEqual(legacy.text.toInts())
|
||||
expect(resolved.contextual.overlay.background.default.toInts()).toEqual(legacy.backgroundMenu.toInts())
|
||||
expect(resolved.contextual.overlay.background.action.primary.default.toInts()).toEqual([0, 0, 0, 0])
|
||||
})
|
||||
|
||||
test("references generated hues from matching token colors", () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue