fix(app): keep theme selection consistent (#3214)
Theme registration, the appearance picker, persistence, and shortcut cycling drifted as new choices were added. Make one ordered catalog own those paths so Auto and Pure Black participate consistently, while lifting the Pure Black selected-workspace surface above its sidebar.
This commit is contained in:
@@ -107,8 +107,6 @@ export const baseColors = {
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type ThemeName = "light" | "dark" | "zinc" | "midnight" | "claude" | "ghostty" | "pureBlack";
|
||||
|
||||
// Diff colors — the +/- inside a diff view, where the color *is* the signal and has to
|
||||
// survive being scanned line by line, so it stays saturated. Light uses muted tones, dark
|
||||
// uses the brighter palette values.
|
||||
@@ -669,7 +667,8 @@ const pureBlackDarkColors = buildDarkSemanticColors({
|
||||
surface4: "#2d2d2d",
|
||||
surfaceDiffEmpty: "#0c0c0c",
|
||||
surfaceSidebar: "#000000",
|
||||
surfaceSidebarHover: "#0d0d0d",
|
||||
// Selected sidebar rows share this surface with hover, so it must remain visible at rest.
|
||||
surfaceSidebarHover: "#161616",
|
||||
foregroundMuted: "#a1a1aa",
|
||||
foregroundExtraMuted: "#71717a",
|
||||
scrollbarHandle: "#71717a",
|
||||
@@ -717,40 +716,94 @@ export const lightTheme = {
|
||||
// Keep compatibility with existing code
|
||||
export const theme = darkTheme;
|
||||
|
||||
export type Theme =
|
||||
| typeof darkTheme
|
||||
| typeof darkZincTheme
|
||||
| typeof darkMidnightTheme
|
||||
| typeof darkClaudeTheme
|
||||
| typeof darkGhosttyTheme
|
||||
| typeof darkPureBlackTheme
|
||||
| typeof lightTheme;
|
||||
export const THEME_OPTIONS = [
|
||||
{
|
||||
name: "light",
|
||||
group: "primary",
|
||||
unistylesName: "light",
|
||||
theme: lightTheme,
|
||||
swatch: "#ffffff",
|
||||
},
|
||||
{
|
||||
name: "dark",
|
||||
group: "primary",
|
||||
unistylesName: "dark",
|
||||
theme: darkTheme,
|
||||
swatch: "#2D8B62",
|
||||
},
|
||||
{ name: "auto", group: "primary" },
|
||||
{
|
||||
name: "zinc",
|
||||
group: "variant",
|
||||
unistylesName: "darkZinc",
|
||||
theme: darkZincTheme,
|
||||
swatch: "#808080",
|
||||
},
|
||||
{
|
||||
name: "midnight",
|
||||
group: "variant",
|
||||
unistylesName: "darkMidnight",
|
||||
theme: darkMidnightTheme,
|
||||
swatch: "#4A6BA8",
|
||||
},
|
||||
{
|
||||
name: "claude",
|
||||
group: "variant",
|
||||
unistylesName: "darkClaude",
|
||||
theme: darkClaudeTheme,
|
||||
swatch: "#D97757",
|
||||
},
|
||||
{
|
||||
name: "ghostty",
|
||||
group: "variant",
|
||||
unistylesName: "darkGhostty",
|
||||
theme: darkGhosttyTheme,
|
||||
swatch: "#8caaee",
|
||||
},
|
||||
{
|
||||
name: "pureBlack",
|
||||
group: "variant",
|
||||
unistylesName: "darkPureBlack",
|
||||
theme: darkPureBlackTheme,
|
||||
swatch: "#000000",
|
||||
},
|
||||
] as const;
|
||||
|
||||
type UnistylesThemeKey =
|
||||
| "light"
|
||||
| "dark"
|
||||
| "darkZinc"
|
||||
| "darkMidnight"
|
||||
| "darkClaude"
|
||||
| "darkGhostty"
|
||||
| "darkPureBlack";
|
||||
export type ThemePreference = (typeof THEME_OPTIONS)[number]["name"];
|
||||
export type ThemeName = Exclude<ThemePreference, "auto">;
|
||||
type ConcreteThemeOption = Exclude<(typeof THEME_OPTIONS)[number], { name: "auto" }>;
|
||||
export type Theme = ConcreteThemeOption["theme"];
|
||||
|
||||
export const THEME_TO_UNISTYLES: Record<ThemeName, UnistylesThemeKey> = {
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
zinc: "darkZinc",
|
||||
midnight: "darkMidnight",
|
||||
claude: "darkClaude",
|
||||
ghostty: "darkGhostty",
|
||||
pureBlack: "darkPureBlack",
|
||||
const CONCRETE_THEME_OPTIONS = THEME_OPTIONS.filter(
|
||||
(option): option is ConcreteThemeOption => option.name !== "auto",
|
||||
);
|
||||
|
||||
type ThemeToUnistyles = {
|
||||
[Name in ThemeName]: Extract<ConcreteThemeOption, { name: Name }>["unistylesName"];
|
||||
};
|
||||
|
||||
export const THEME_SWATCHES: Record<ThemeName, string> = {
|
||||
light: "#ffffff",
|
||||
dark: "#2D8B62",
|
||||
zinc: "#808080",
|
||||
midnight: "#4A6BA8",
|
||||
claude: "#D97757",
|
||||
ghostty: "#8caaee",
|
||||
pureBlack: "#000000",
|
||||
type ThemeSwatches = {
|
||||
[Name in ThemeName]: Extract<ConcreteThemeOption, { name: Name }>["swatch"];
|
||||
};
|
||||
|
||||
type RegisteredThemes = {
|
||||
[Option in ConcreteThemeOption as Option["unistylesName"]]: Option["theme"];
|
||||
};
|
||||
|
||||
export const THEME_TO_UNISTYLES = Object.fromEntries(
|
||||
CONCRETE_THEME_OPTIONS.map((option) => [option.name, option.unistylesName]),
|
||||
) as ThemeToUnistyles;
|
||||
|
||||
export const THEME_SWATCHES = Object.fromEntries(
|
||||
CONCRETE_THEME_OPTIONS.map((option) => [option.name, option.swatch]),
|
||||
) as ThemeSwatches;
|
||||
|
||||
export const REGISTERED_THEMES = Object.fromEntries(
|
||||
CONCRETE_THEME_OPTIONS.map((option) => [option.unistylesName, option.theme]),
|
||||
) as RegisteredThemes;
|
||||
|
||||
export function getNextThemePreference(current: ThemePreference): ThemePreference {
|
||||
const currentIndex = THEME_OPTIONS.findIndex((option) => option.name === current);
|
||||
const nextIndex = (currentIndex + 1) % THEME_OPTIONS.length;
|
||||
return THEME_OPTIONS[nextIndex]?.name ?? THEME_OPTIONS[0].name;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user