feat(app): add Pure black theme (#3012)
* feat(app): add AMOLED and all four Catppuccin themes Add an AMOLED theme (pure black background) and the four official Catppuccin flavors (Latte light, Frappé/Macchiato/Mocha dark) to the appearance picker. Generalize the dark-theme builder to support a light surface variant, register the new themes with Unistyles, and patch all registered theme keys when applying font/syntax appearance settings. * fix(app): localize Catppuccin + AMOLED theme labels across language packs The initial theme commit pasted the English brand/flavor labels into every locale. Localize them following each pack's convention: transliterate Catppuccin and the coffee-flavor names into Arabic/Russian scripts, use katakana/hanzi equivalents in ja/zh-CN, and standard loanwords in fr/es/pt-BR. AMOLED is a universal acronym and stays as-is except where the pack transliterates (Arabic). * fix(app): make Latte terminal ANSI colors readable * test(app): cover appearance theme picker options * refactor(app): scope theme PR to AMOLED * refactor(app): rename AMOLED theme to Pure Black * fix(app): refine Pure black appearance * test(app): wait for sidebar toggle icon * test(app): remove hover timing race --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
@@ -107,7 +107,7 @@ export const baseColors = {
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type ThemeName = "light" | "dark" | "zinc" | "midnight" | "claude" | "ghostty";
|
||||
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
|
||||
@@ -300,6 +300,8 @@ interface DarkThemeConfig {
|
||||
accentBright: string;
|
||||
accentForeground?: string;
|
||||
destructive: string;
|
||||
terminalBlack: string;
|
||||
terminalBrightBlack: string;
|
||||
}
|
||||
|
||||
const darkTerminalAnsi = {
|
||||
@@ -374,9 +376,9 @@ function buildDarkSemanticColors(tint: DarkThemeConfig) {
|
||||
cursorAccent: tint.surface0,
|
||||
selectionBackground: "rgba(255, 255, 255, 0.2)",
|
||||
selectionForeground: "#fafafa",
|
||||
black: tint.surfaceSidebar,
|
||||
black: tint.terminalBlack,
|
||||
...darkTerminalAnsi,
|
||||
brightBlack: tint.surface3,
|
||||
brightBlack: tint.terminalBrightBlack,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -403,6 +405,8 @@ const paseoDarkColors = buildDarkSemanticColors({
|
||||
accent: "#20744A",
|
||||
accentBright: "#7ccba0",
|
||||
destructive: "#c64f43", // warm red, hue ~7 — reads as red (not pink) against the green tint
|
||||
terminalBlack: "#141716",
|
||||
terminalBrightBlack: "#434645",
|
||||
});
|
||||
|
||||
// Zinc — neutral gray, no tint
|
||||
@@ -424,6 +428,8 @@ const zincDarkColors = buildDarkSemanticColors({
|
||||
accentBright: "#fafafa",
|
||||
accentForeground: "#18181b", // monochrome zinc accent is near-white — needs dark text
|
||||
destructive: "#c44a4a", // neutral red, hue 0 — clearly red without screaming
|
||||
terminalBlack: "#131316",
|
||||
terminalBrightBlack: "#3f3f46",
|
||||
});
|
||||
|
||||
// Midnight — subtle blue tint
|
||||
@@ -444,6 +450,8 @@ const midnightDarkColors = buildDarkSemanticColors({
|
||||
accent: "#3b6fcf",
|
||||
accentBright: "#7eaaeb",
|
||||
destructive: "#c44a52", // red with a hint of cool lean against the blue tint
|
||||
terminalBlack: "#121420",
|
||||
terminalBrightBlack: "#3c3e4c",
|
||||
});
|
||||
|
||||
// Claude — warm neutral with subtle orange undertone
|
||||
@@ -464,6 +472,8 @@ const claudeDarkColors = buildDarkSemanticColors({
|
||||
accent: "#d97757",
|
||||
accentBright: "#e89a7f",
|
||||
destructive: "#cf513e", // warm orange-red, hue ~10 — sits with the Claude orange accent
|
||||
terminalBlack: "#1a1918",
|
||||
terminalBrightBlack: "#4a4745",
|
||||
});
|
||||
|
||||
// Ghostty — blue-tinted dark based on Ghostty default background
|
||||
@@ -484,6 +494,8 @@ const ghosttyDarkColors = buildDarkSemanticColors({
|
||||
accent: "#89b4fa",
|
||||
accentBright: "#b4d0fc",
|
||||
destructive: "#c44a55", // red with slight cool lean against the slate-blue surfaces
|
||||
terminalBlack: "#21252d",
|
||||
terminalBrightBlack: "#4a4f5e",
|
||||
});
|
||||
|
||||
export const SPACING = {
|
||||
@@ -639,6 +651,30 @@ export const darkMidnightTheme = buildDarkTheme(midnightDarkColors);
|
||||
export const darkClaudeTheme = buildDarkTheme(claudeDarkColors);
|
||||
export const darkGhosttyTheme = buildDarkTheme(ghosttyDarkColors);
|
||||
|
||||
// Pure black — zero-luminance background with high-contrast surfaces.
|
||||
const pureBlackDarkColors = buildDarkSemanticColors({
|
||||
surface0: "#000000",
|
||||
surface1: "#0a0a0a",
|
||||
surface2: "#111111",
|
||||
surface3: "#202020",
|
||||
surface4: "#2d2d2d",
|
||||
surfaceDiffEmpty: "#0c0c0c",
|
||||
surfaceSidebar: "#000000",
|
||||
surfaceSidebarHover: "#0d0d0d",
|
||||
foregroundMuted: "#a1a1aa",
|
||||
foregroundExtraMuted: "#71717a",
|
||||
scrollbarHandle: "#71717a",
|
||||
border: "#1c1c1c",
|
||||
borderAccent: "#242424",
|
||||
accent: "#20744A",
|
||||
accentBright: "#7ccba0",
|
||||
destructive: "#c44a4a",
|
||||
terminalBlack: "#595959",
|
||||
terminalBrightBlack: "#8a8a8a",
|
||||
});
|
||||
|
||||
export const darkPureBlackTheme = buildDarkTheme(pureBlackDarkColors);
|
||||
|
||||
export const lightTheme = {
|
||||
colorScheme: "light" as const,
|
||||
colors: {
|
||||
@@ -672,8 +708,14 @@ export const lightTheme = {
|
||||
// Keep compatibility with existing code
|
||||
export const theme = darkTheme;
|
||||
|
||||
// Export a union type that works for both themes
|
||||
export type Theme = typeof darkTheme | typeof lightTheme;
|
||||
export type Theme =
|
||||
| typeof darkTheme
|
||||
| typeof darkZincTheme
|
||||
| typeof darkMidnightTheme
|
||||
| typeof darkClaudeTheme
|
||||
| typeof darkGhosttyTheme
|
||||
| typeof darkPureBlackTheme
|
||||
| typeof lightTheme;
|
||||
|
||||
type UnistylesThemeKey =
|
||||
| "light"
|
||||
@@ -681,7 +723,8 @@ type UnistylesThemeKey =
|
||||
| "darkZinc"
|
||||
| "darkMidnight"
|
||||
| "darkClaude"
|
||||
| "darkGhostty";
|
||||
| "darkGhostty"
|
||||
| "darkPureBlack";
|
||||
|
||||
export const THEME_TO_UNISTYLES: Record<ThemeName, UnistylesThemeKey> = {
|
||||
light: "light",
|
||||
@@ -690,6 +733,7 @@ export const THEME_TO_UNISTYLES: Record<ThemeName, UnistylesThemeKey> = {
|
||||
midnight: "darkMidnight",
|
||||
claude: "darkClaude",
|
||||
ghostty: "darkGhostty",
|
||||
pureBlack: "darkPureBlack",
|
||||
};
|
||||
|
||||
export const THEME_SWATCHES: Record<ThemeName, string> = {
|
||||
@@ -699,4 +743,5 @@ export const THEME_SWATCHES: Record<ThemeName, string> = {
|
||||
midnight: "#4A6BA8",
|
||||
claude: "#D97757",
|
||||
ghostty: "#8caaee",
|
||||
pureBlack: "#000000",
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user