feat: add new themes, Zed fonts, and sort theme/font lists

New themes added:
- Dark: Ayu Dark, Ayu Mirage, Ember, Matcha
- Light: Ayu Light, One Light, Bluloco, Feather

Other changes:
- Bundle Zed Sans and Zed Mono fonts from zed-industries/zed-fonts
- Sort font options alphabetically (default first)
- Sort theme options alphabetically (Dark/Light first)
- Improve Ayu Dark text contrast for better readability
- Fix Matcha theme to have green undertone instead of blue
This commit is contained in:
Stefan de Vogelaere
2026-01-17 01:58:29 +01:00
parent f3b00d0f78
commit 1a7bf27ead
33 changed files with 1904 additions and 224 deletions

View File

@@ -542,9 +542,10 @@ const grayTheme: TerminalTheme = {
// Theme mapping
const terminalThemes: Record<ThemeMode, TerminalTheme> = {
light: lightTheme,
dark: darkTheme,
// Special
system: darkTheme, // Will be resolved at runtime
// Dark themes
dark: darkTheme,
retro: retroTheme,
dracula: draculaTheme,
nord: nordTheme,
@@ -556,9 +557,35 @@ const terminalThemes: Record<ThemeMode, TerminalTheme> = {
onedark: onedarkTheme,
synthwave: synthwaveTheme,
red: redTheme,
cream: creamTheme,
sunset: sunsetTheme,
gray: grayTheme,
forest: gruvboxTheme, // Green-ish theme, gruvbox is close
ocean: nordTheme, // Blue-ish theme, nord is close
ember: monokaiTheme, // Warm orange theme, monokai is close
'ayu-dark': darkTheme, // Deep dark with warm accents
'ayu-mirage': darkTheme, // Soft dark with golden accents
matcha: nordTheme, // Calming blue-gray with sage green
// Light themes
light: lightTheme,
cream: creamTheme,
solarizedlight: lightTheme, // TODO: Create dedicated solarized light terminal theme
github: lightTheme, // TODO: Create dedicated github terminal theme
paper: lightTheme,
rose: lightTheme,
mint: lightTheme,
lavender: lightTheme,
sand: creamTheme, // Warm tones like cream
sky: lightTheme,
peach: creamTheme, // Warm tones like cream
snow: lightTheme,
sepia: creamTheme, // Warm tones like cream
gruvboxlight: creamTheme, // Warm light theme
nordlight: lightTheme, // Cool light theme
blossom: lightTheme,
'ayu-light': lightTheme, // Clean light with orange accents
onelight: lightTheme, // Atom One Light - blue accent
bluloco: lightTheme, // Bluloco - cyan-blue accent
feather: lightTheme, // Feather - orange accent
};
/**

View File

@@ -47,7 +47,11 @@ export type Theme =
| 'gray'
| 'forest'
| 'ocean'
// Light themes (16)
| 'ember'
| 'ayu-dark'
| 'ayu-mirage'
| 'matcha'
// Light themes
| 'light'
| 'cream'
| 'solarizedlight'
@@ -63,7 +67,11 @@ export type Theme =
| 'sepia'
| 'gruvboxlight'
| 'nordlight'
| 'blossom';
| 'blossom'
| 'ayu-light'
| 'onelight'
| 'bluloco'
| 'feather';
export interface ThemeOption {
value: Theme;
@@ -74,9 +82,9 @@ export interface ThemeOption {
color: string; // Primary/brand color for icon display
}
// All theme options with dark/light categorization
// All theme options with dark/light categorization (alphabetically sorted, Dark/Light first)
export const themeOptions: ReadonlyArray<ThemeOption> = [
// Dark themes (16)
// Dark themes (20) - alphabetical, Dark first
{
value: 'dark',
label: 'Dark',
@@ -86,60 +94,20 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
color: '#3b82f6',
},
{
value: 'retro',
label: 'Retro',
Icon: Terminal,
testId: 'retro-mode-button',
value: 'ayu-dark',
label: 'Ayu Dark',
Icon: Moon,
testId: 'ayu-dark-mode-button',
isDark: true,
color: '#22c55e',
color: '#E6B450',
},
{
value: 'dracula',
label: 'Dracula',
Icon: Ghost,
testId: 'dracula-mode-button',
isDark: true,
color: '#bd93f9',
},
{
value: 'nord',
label: 'Nord',
Icon: Snowflake,
testId: 'nord-mode-button',
isDark: true,
color: '#88c0d0',
},
{
value: 'monokai',
label: 'Monokai',
Icon: Flame,
testId: 'monokai-mode-button',
isDark: true,
color: '#f92672',
},
{
value: 'tokyonight',
label: 'Tokyo Night',
value: 'ayu-mirage',
label: 'Ayu Mirage',
Icon: Sparkles,
testId: 'tokyonight-mode-button',
testId: 'ayu-mirage-mode-button',
isDark: true,
color: '#bb9af7',
},
{
value: 'solarized',
label: 'Solarized Dark',
Icon: Eclipse,
testId: 'solarized-mode-button',
isDark: true,
color: '#268bd2',
},
{
value: 'gruvbox',
label: 'Gruvbox',
Icon: Trees,
testId: 'gruvbox-mode-button',
isDark: true,
color: '#fe8019',
color: '#FFCC66',
},
{
value: 'catppuccin',
@@ -150,44 +118,20 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
color: '#cba6f7',
},
{
value: 'onedark',
label: 'One Dark',
Icon: Atom,
testId: 'onedark-mode-button',
value: 'dracula',
label: 'Dracula',
Icon: Ghost,
testId: 'dracula-mode-button',
isDark: true,
color: '#61afef',
color: '#bd93f9',
},
{
value: 'synthwave',
label: 'Synthwave',
Icon: Radio,
testId: 'synthwave-mode-button',
value: 'ember',
label: 'Ember',
Icon: Sunrise,
testId: 'ember-mode-button',
isDark: true,
color: '#ff7edb',
},
{
value: 'red',
label: 'Red',
Icon: Heart,
testId: 'red-mode-button',
isDark: true,
color: '#ef4444',
},
{
value: 'sunset',
label: 'Sunset',
Icon: CloudSun,
testId: 'sunset-mode-button',
isDark: true,
color: '#f97316',
},
{
value: 'gray',
label: 'Gray',
Icon: Square,
testId: 'gray-mode-button',
isDark: true,
color: '#6b7280',
color: '#fd971f',
},
{
value: 'forest',
@@ -197,6 +141,46 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
isDark: true,
color: '#22c55e',
},
{
value: 'gray',
label: 'Gray',
Icon: Square,
testId: 'gray-mode-button',
isDark: true,
color: '#6b7280',
},
{
value: 'gruvbox',
label: 'Gruvbox',
Icon: Trees,
testId: 'gruvbox-mode-button',
isDark: true,
color: '#fe8019',
},
{
value: 'matcha',
label: 'Matcha',
Icon: Leaf,
testId: 'matcha-mode-button',
isDark: true,
color: '#A4B07E',
},
{
value: 'monokai',
label: 'Monokai',
Icon: Flame,
testId: 'monokai-mode-button',
isDark: true,
color: '#f92672',
},
{
value: 'nord',
label: 'Nord',
Icon: Snowflake,
testId: 'nord-mode-button',
isDark: true,
color: '#88c0d0',
},
{
value: 'ocean',
label: 'Ocean',
@@ -205,7 +189,63 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
isDark: true,
color: '#06b6d4',
},
// Light themes (16)
{
value: 'onedark',
label: 'One Dark',
Icon: Atom,
testId: 'onedark-mode-button',
isDark: true,
color: '#61afef',
},
{
value: 'red',
label: 'Red',
Icon: Heart,
testId: 'red-mode-button',
isDark: true,
color: '#ef4444',
},
{
value: 'retro',
label: 'Retro',
Icon: Terminal,
testId: 'retro-mode-button',
isDark: true,
color: '#22c55e',
},
{
value: 'solarized',
label: 'Solarized Dark',
Icon: Eclipse,
testId: 'solarized-mode-button',
isDark: true,
color: '#268bd2',
},
{
value: 'sunset',
label: 'Sunset',
Icon: CloudSun,
testId: 'sunset-mode-button',
isDark: true,
color: '#f97316',
},
{
value: 'synthwave',
label: 'Synthwave',
Icon: Radio,
testId: 'synthwave-mode-button',
isDark: true,
color: '#ff7edb',
},
{
value: 'tokyonight',
label: 'Tokyo Night',
Icon: Sparkles,
testId: 'tokyonight-mode-button',
isDark: true,
color: '#bb9af7',
},
// Light themes (20) - alphabetical, Light first
{
value: 'light',
label: 'Light',
@@ -214,6 +254,30 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
isDark: false,
color: '#3b82f6',
},
{
value: 'ayu-light',
label: 'Ayu Light',
Icon: Sun,
testId: 'ayu-light-mode-button',
isDark: false,
color: '#F29718',
},
{
value: 'blossom',
label: 'Blossom',
Icon: Cherry,
testId: 'blossom-mode-button',
isDark: false,
color: '#ec4899',
},
{
value: 'bluloco',
label: 'Bluloco',
Icon: Waves,
testId: 'bluloco-mode-button',
isDark: false,
color: '#0099e1',
},
{
value: 'cream',
label: 'Cream',
@@ -223,12 +287,12 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
color: '#b45309',
},
{
value: 'solarizedlight',
label: 'Solarized Light',
Icon: Sunrise,
testId: 'solarizedlight-mode-button',
value: 'feather',
label: 'Feather',
Icon: Feather,
testId: 'feather-mode-button',
isDark: false,
color: '#268bd2',
color: '#FF7B2E',
},
{
value: 'github',
@@ -239,28 +303,12 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
color: '#0969da',
},
{
value: 'paper',
label: 'Paper',
Icon: Scroll,
testId: 'paper-mode-button',
value: 'gruvboxlight',
label: 'Gruvbox Light',
Icon: Trees,
testId: 'gruvboxlight-mode-button',
isDark: false,
color: '#374151',
},
{
value: 'rose',
label: 'Rose',
Icon: Flower2,
testId: 'rose-mode-button',
isDark: false,
color: '#e11d48',
},
{
value: 'mint',
label: 'Mint',
Icon: Wind,
testId: 'mint-mode-button',
isDark: false,
color: '#0d9488',
color: '#d65d0e',
},
{
value: 'lavender',
@@ -271,52 +319,12 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
color: '#8b5cf6',
},
{
value: 'sand',
label: 'Sand',
Icon: Palmtree,
testId: 'sand-mode-button',
value: 'mint',
label: 'Mint',
Icon: Wind,
testId: 'mint-mode-button',
isDark: false,
color: '#d97706',
},
{
value: 'sky',
label: 'Sky',
Icon: Sun,
testId: 'sky-mode-button',
isDark: false,
color: '#0284c7',
},
{
value: 'peach',
label: 'Peach',
Icon: Cherry,
testId: 'peach-mode-button',
isDark: false,
color: '#ea580c',
},
{
value: 'snow',
label: 'Snow',
Icon: Snowflake,
testId: 'snow-mode-button',
isDark: false,
color: '#3b82f6',
},
{
value: 'sepia',
label: 'Sepia',
Icon: Coffee,
testId: 'sepia-mode-button',
isDark: false,
color: '#92400e',
},
{
value: 'gruvboxlight',
label: 'Gruvbox Light',
Icon: Trees,
testId: 'gruvboxlight-mode-button',
isDark: false,
color: '#d65d0e',
color: '#0d9488',
},
{
value: 'nordlight',
@@ -327,12 +335,76 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
color: '#5e81ac',
},
{
value: 'blossom',
label: 'Blossom',
Icon: Cherry,
testId: 'blossom-mode-button',
value: 'onelight',
label: 'One Light',
Icon: Atom,
testId: 'onelight-mode-button',
isDark: false,
color: '#ec4899',
color: '#526FFF',
},
{
value: 'paper',
label: 'Paper',
Icon: Scroll,
testId: 'paper-mode-button',
isDark: false,
color: '#374151',
},
{
value: 'peach',
label: 'Peach',
Icon: Cherry,
testId: 'peach-mode-button',
isDark: false,
color: '#ea580c',
},
{
value: 'rose',
label: 'Rose',
Icon: Flower2,
testId: 'rose-mode-button',
isDark: false,
color: '#e11d48',
},
{
value: 'sand',
label: 'Sand',
Icon: Palmtree,
testId: 'sand-mode-button',
isDark: false,
color: '#d97706',
},
{
value: 'sepia',
label: 'Sepia',
Icon: Coffee,
testId: 'sepia-mode-button',
isDark: false,
color: '#92400e',
},
{
value: 'sky',
label: 'Sky',
Icon: Sun,
testId: 'sky-mode-button',
isDark: false,
color: '#0284c7',
},
{
value: 'snow',
label: 'Snow',
Icon: Snowflake,
testId: 'snow-mode-button',
isDark: false,
color: '#3b82f6',
},
{
value: 'solarizedlight',
label: 'Solarized Light',
Icon: Sunrise,
testId: 'solarizedlight-mode-button',
isDark: false,
color: '#268bd2',
},
];

View File

@@ -1,8 +1,9 @@
/**
* Font options for per-project font customization
*
* These are system fonts (no bundled @fontsource packages required).
* Users must have the fonts installed on their system for them to work.
* All fonts listed here are bundled with the app via @fontsource packages
* or custom font files (Zed fonts from zed-industries/zed-fonts).
* They are self-hosted and will work without any system installation.
*/
// Sentinel value for "use default font" - Radix Select doesn't allow empty strings
@@ -14,34 +15,53 @@ export interface UIFontOption {
}
/**
* Sans/UI fonts for headings, labels, and general text
* Sans/UI fonts for headings, labels, and general text (Top 10)
*
* 'default' value means "use the theme default" (Geist Sans for all themes)
*/
export const UI_SANS_FONT_OPTIONS: readonly UIFontOption[] = [
{ value: DEFAULT_FONT_VALUE, label: 'Default (Geist Sans)' },
{ value: "'Inter', system-ui, sans-serif", label: 'Inter' },
{ value: "'SF Pro', system-ui, sans-serif", label: 'SF Pro' },
// Sans fonts (alphabetical)
{ value: 'Inter, system-ui, sans-serif', label: 'Inter' },
{ value: 'Lato, system-ui, sans-serif', label: 'Lato' },
{ value: 'Montserrat, system-ui, sans-serif', label: 'Montserrat' },
{ value: "'Open Sans', system-ui, sans-serif", label: 'Open Sans' },
{ value: 'Poppins, system-ui, sans-serif', label: 'Poppins' },
{ value: 'Raleway, system-ui, sans-serif', label: 'Raleway' },
{ value: 'Roboto, system-ui, sans-serif', label: 'Roboto' },
{ value: "'Source Sans 3', system-ui, sans-serif", label: 'Source Sans' },
{ value: "'IBM Plex Sans', system-ui, sans-serif", label: 'IBM Plex Sans' },
{ value: "'Roboto', system-ui, sans-serif", label: 'Roboto' },
{ value: 'system-ui, sans-serif', label: 'System Default' },
{ value: "'Work Sans', system-ui, sans-serif", label: 'Work Sans' },
{ value: "'Zed Sans', system-ui, sans-serif", label: 'Zed Sans' },
// Monospace fonts (alphabetical, for users who prefer mono UI)
{ value: "'Cascadia Code', monospace", label: 'Cascadia Code' },
{ value: "'Fira Code', monospace", label: 'Fira Code' },
{ value: "'IBM Plex Mono', monospace", label: 'IBM Plex Mono' },
{ value: 'Inconsolata, monospace', label: 'Inconsolata' },
{ value: 'Iosevka, monospace', label: 'Iosevka' },
{ value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' },
{ value: "'Source Code Pro', monospace", label: 'Source Code Pro' },
{ value: "'Zed Mono', monospace", label: 'Zed Mono' },
] as const;
/**
* Mono/code fonts for code blocks, terminals, and monospaced text
* Mono/code fonts for code blocks, terminals, and monospaced text (Top 10)
*
* 'default' value means "use the theme default" (Geist Mono for all themes)
* Many of these support ligatures for coding symbols (-> => != etc.)
*/
export const UI_MONO_FONT_OPTIONS: readonly UIFontOption[] = [
{ value: DEFAULT_FONT_VALUE, label: 'Default (Geist Mono)' },
{ value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' },
{ value: "'Fira Code', monospace", label: 'Fira Code' },
{ value: "'SF Mono', Menlo, Monaco, monospace", label: 'SF Mono' },
{ value: "'Source Code Pro', monospace", label: 'Source Code Pro' },
{ value: "'IBM Plex Mono', monospace", label: 'IBM Plex Mono' },
{ value: "Menlo, Monaco, 'Courier New', monospace", label: 'Menlo / Monaco' },
// Bundled fonts (alphabetical)
{ value: "'Cascadia Code', monospace", label: 'Cascadia Code' },
{ value: "'Fira Code', monospace", label: 'Fira Code' },
{ value: "'IBM Plex Mono', monospace", label: 'IBM Plex Mono' },
{ value: 'Inconsolata, monospace', label: 'Inconsolata' },
{ value: 'Iosevka, monospace', label: 'Iosevka' },
{ value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' },
{ value: "'Source Code Pro', monospace", label: 'Source Code Pro' },
{ value: "'Zed Mono', monospace", label: 'Zed Mono' },
// System fonts
{ value: 'Menlo, Monaco, monospace', label: 'Menlo / Monaco (macOS)' },
] as const;
/**