mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-15 04:27:20 +03:00
fix(theme): give migrated V1 dialogs an opaque background (#60016)
This commit is contained in:
committed by
GitHub
Unverified
parent
77a0fd6f3b
commit
f43563b2f4
@@ -9,6 +9,7 @@ import {
|
||||
categoricalTokens,
|
||||
chromaticHues,
|
||||
detectMode,
|
||||
dialogBackground,
|
||||
hex,
|
||||
inferHues,
|
||||
referenceHues,
|
||||
@@ -28,7 +29,7 @@ export function migrateV1NewDesigns(theme: ThemeV1Json): ThemeDocument {
|
||||
const darkMode = detectMode(dark)
|
||||
if (lightMode === darkMode) {
|
||||
const definition = migrateMode(lightMode === "light" ? light : dark, lightMode)
|
||||
if (lightMode === "light") return { base: base(definition), light: lightOverrides(definition) }
|
||||
if (lightMode === "light") return { base: base(definition), light: lightOverrides(definition, light) }
|
||||
return { base: base(definition), dark: { hue: definition.hue } }
|
||||
}
|
||||
}
|
||||
@@ -36,7 +37,7 @@ export function migrateV1NewDesigns(theme: ThemeV1Json): ThemeDocument {
|
||||
const darkDefinition = migrateMode(dark, "dark")
|
||||
return {
|
||||
base: base(lightDefinition),
|
||||
light: lightOverrides(lightDefinition),
|
||||
light: lightOverrides(lightDefinition, light),
|
||||
dark: darkDefinition,
|
||||
}
|
||||
}
|
||||
@@ -53,8 +54,12 @@ const lightDialog = {
|
||||
},
|
||||
} satisfies ModeDefinition["@dialog"]
|
||||
|
||||
function lightOverrides(definition: ThemeDefinition): ModeDefinition {
|
||||
return { hue: definition.hue, "@dialog": lightDialog }
|
||||
function lightOverrides(definition: ThemeDefinition, theme: Theme): ModeDefinition {
|
||||
if (theme.backgroundPanel.a === 1) return { hue: definition.hue, "@dialog": lightDialog }
|
||||
return {
|
||||
hue: definition.hue,
|
||||
"@dialog": { ...lightDialog, background: { ...lightDialog.background, base: dialogBackground(theme) } },
|
||||
}
|
||||
}
|
||||
|
||||
// Mirrors the opencode2 token layout: text at 100/200/300, the page at 900, raised surfaces at 700/600/500,
|
||||
@@ -125,7 +130,7 @@ function migrateMode(theme: Theme, mode: Mode): ThemeDefinition {
|
||||
...paletteTokens(theme),
|
||||
"@dialog": {
|
||||
background: {
|
||||
base: "$background.raised.base",
|
||||
base: dialogBackground(theme),
|
||||
action: {
|
||||
primary: { $hovered: "$background.raised.max" },
|
||||
secondary: { $hovered: "$background.raised.max" },
|
||||
|
||||
@@ -217,13 +217,25 @@ function migrateMode(theme: Theme, mode: Mode): ThemeDefinition {
|
||||
},
|
||||
"@dialog": {
|
||||
background: {
|
||||
base: "$background.raised.base",
|
||||
base: dialogBackground(theme),
|
||||
action: { primary: { $hovered: "$background.raised.high" } },
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Dialogs float over the session, so a see-through panel would let the content underneath show through them.
|
||||
// Fall back to the first visible surface color, made opaque.
|
||||
export function dialogBackground(theme: Theme) {
|
||||
if (theme.backgroundPanel.a === 1) return "$background.raised.base"
|
||||
const visible = [theme.backgroundPanel, theme.backgroundMenu, theme.backgroundElement, theme.background].find(
|
||||
(color) => color.a > 0,
|
||||
)
|
||||
if (!visible) return luminance(theme.text) > 0.5 ? "#000000" : "#ffffff"
|
||||
const [red, green, blue] = visible.toInts()
|
||||
return hexInts(red, green, blue, 255)
|
||||
}
|
||||
|
||||
export function referenceHues(theme: ThemeDefinition): ThemeDefinition {
|
||||
const definitions = theme.hue as Record<string, string | Partial<Record<HueStep, string>>> | undefined
|
||||
if (!definitions) return theme
|
||||
|
||||
@@ -279,6 +279,42 @@ test("collapses identical V1 backgrounds when both variants infer one mode", ()
|
||||
expect(selectThemeMode(migratedLight, "dark").mode).toBe("light")
|
||||
})
|
||||
|
||||
test("gives dialogs an opaque background when V1 panels are see-through", () => {
|
||||
const lucent = DEFAULT_THEMES["lucent-orng"]!
|
||||
for (const mode of ["dark", "light"] as const) {
|
||||
const legacy = resolveV1(lucent, mode)
|
||||
const dialog = resolveThemeDocument(migrateV1(lucent), mode).surface("dialog")
|
||||
expect(legacy.backgroundPanel.a).toBe(0)
|
||||
const [red, green, blue] = legacy.backgroundMenu.toInts()
|
||||
expect(legacy.backgroundMenu.a).toBeLessThan(1)
|
||||
expect(dialog.background.base.toInts()).toEqual([red, green, blue, 255])
|
||||
}
|
||||
|
||||
const source = structuredClone(opencodeV1)
|
||||
source.theme.background = "transparent"
|
||||
source.theme.backgroundPanel = "transparent"
|
||||
source.theme.backgroundElement = "transparent"
|
||||
source.theme.backgroundMenu = "transparent"
|
||||
expect(hex(resolveThemeDocument(migrateV1(source), "dark").surface("dialog").background.base)).toBe("#000000")
|
||||
expect(hex(resolveThemeDocument(migrateV1(source), "light").surface("dialog").background.base)).toBe("#ffffff")
|
||||
|
||||
const opaque = resolveThemeDocument(migrateV1(opencodeV1), "dark")
|
||||
expect(hex(opaque.surface("dialog").background.base)).toBe(hex(opaque.background.raised.base))
|
||||
})
|
||||
|
||||
test("gives new designs dialogs an opaque background when V1 panels are see-through", () => {
|
||||
const lucent = DEFAULT_THEMES["lucent-orng"]!
|
||||
for (const mode of ["dark", "light"] as const) {
|
||||
const [red, green, blue] = resolveV1(lucent, mode).backgroundMenu.toInts()
|
||||
const dialog = resolveThemeDocument(migrateV1NewDesigns(lucent), mode).surface("dialog")
|
||||
expect(dialog.background.base.toInts()).toEqual([red, green, blue, 255])
|
||||
}
|
||||
|
||||
const opaque = migrateV1NewDesigns(DEFAULT_THEMES.tokyonight!)
|
||||
expect(opaque.base["@dialog"]?.background?.base).toBe("$background.raised.base")
|
||||
expect(opaque.light?.["@dialog"]?.background?.base).toBe("$hue.neutral.800")
|
||||
})
|
||||
|
||||
test("keeps both modes when a shared background has different contrast", () => {
|
||||
const source = structuredClone(opencodeV1)
|
||||
source.theme.background = "#808080"
|
||||
|
||||
Reference in New Issue
Block a user