feat(v3): add 7 editor themes to multi-theme system
Generalize theme system from Catppuccin-only (4 flavors) to 11 themes across 2 groups. New editor themes: VSCode Dark+, Atom One Dark, Monokai, Dracula, Nord, Solarized Dark, GitHub Dark. All themes map to the same 26 --ctp-* CSS custom properties, so every component works unchanged. ThemeId replaces CatppuccinFlavor as primary type. Theme store uses getCurrentTheme()/setTheme() with deprecated wrappers. SettingsTab uses optgroup-based theme selector.
This commit is contained in:
parent
1ba818e7a5
commit
ff2d354219
3 changed files with 248 additions and 149 deletions
|
|
@ -14,8 +14,8 @@
|
|||
} from '../../stores/workspace.svelte';
|
||||
import { deriveIdentifier } from '../../types/groups';
|
||||
import { getSetting, setSetting } from '../../adapters/settings-bridge';
|
||||
import { getCurrentFlavor, setFlavor } from '../../stores/theme.svelte';
|
||||
import type { CatppuccinFlavor } from '../../styles/themes';
|
||||
import { getCurrentTheme, setTheme } from '../../stores/theme.svelte';
|
||||
import { THEME_LIST, type ThemeId } from '../../styles/themes';
|
||||
|
||||
let activeGroupId = $derived(getActiveGroupId());
|
||||
let activeGroup = $derived(getActiveGroup());
|
||||
|
|
@ -29,8 +29,17 @@
|
|||
// Global settings
|
||||
let defaultShell = $state('');
|
||||
let defaultCwd = $state('');
|
||||
let themeFlavor = $state<CatppuccinFlavor>(getCurrentFlavor());
|
||||
const flavors: CatppuccinFlavor[] = ['latte', 'frappe', 'macchiato', 'mocha'];
|
||||
let selectedTheme = $state<ThemeId>(getCurrentTheme());
|
||||
|
||||
// Group themes by category for <optgroup>
|
||||
const themeGroups = $derived(() => {
|
||||
const map = new Map<string, typeof THEME_LIST>();
|
||||
for (const t of THEME_LIST) {
|
||||
if (!map.has(t.group)) map.set(t.group, []);
|
||||
map.get(t.group)!.push(t);
|
||||
}
|
||||
return [...map.entries()];
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
const [shell, cwd] = await Promise.all([
|
||||
|
|
@ -39,7 +48,7 @@
|
|||
]);
|
||||
defaultShell = shell ?? '';
|
||||
defaultCwd = cwd ?? '';
|
||||
themeFlavor = getCurrentFlavor();
|
||||
selectedTheme = getCurrentTheme();
|
||||
});
|
||||
|
||||
async function saveGlobalSetting(key: string, value: string) {
|
||||
|
|
@ -50,9 +59,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
async function handleThemeChange(flavor: CatppuccinFlavor) {
|
||||
themeFlavor = flavor;
|
||||
await setFlavor(flavor);
|
||||
async function handleThemeChange(themeId: ThemeId) {
|
||||
selectedTheme = themeId;
|
||||
await setTheme(themeId);
|
||||
}
|
||||
|
||||
// New project form
|
||||
|
|
@ -92,14 +101,18 @@
|
|||
<h2>Global</h2>
|
||||
<div class="global-settings">
|
||||
<div class="setting-row">
|
||||
<label for="theme-flavor">Theme</label>
|
||||
<label for="theme-select">Theme</label>
|
||||
<select
|
||||
id="theme-flavor"
|
||||
value={themeFlavor}
|
||||
onchange={e => handleThemeChange((e.target as HTMLSelectElement).value as CatppuccinFlavor)}
|
||||
id="theme-select"
|
||||
value={selectedTheme}
|
||||
onchange={e => handleThemeChange((e.target as HTMLSelectElement).value as ThemeId)}
|
||||
>
|
||||
{#each flavors as f}
|
||||
<option value={f}>{f.charAt(0).toUpperCase() + f.slice(1)}</option>
|
||||
{#each themeGroups() as [groupName, themes]}
|
||||
<optgroup label={groupName}>
|
||||
{#each themes as t}
|
||||
<option value={t.id}>{t.label}</option>
|
||||
{/each}
|
||||
</optgroup>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
|
|
@ -154,14 +167,14 @@
|
|||
|
||||
{#each activeGroup.projects as project}
|
||||
<div class="project-settings-row">
|
||||
<label class="project-field">
|
||||
<label class="project-field project-field-grow">
|
||||
<span class="field-label">Name</span>
|
||||
<input
|
||||
value={project.name}
|
||||
onchange={e => updateProject(activeGroupId, project.id, { name: (e.target as HTMLInputElement).value })}
|
||||
/>
|
||||
</label>
|
||||
<label class="project-field">
|
||||
<label class="project-field project-field-grow">
|
||||
<span class="field-label">CWD</span>
|
||||
<input
|
||||
value={project.cwd}
|
||||
|
|
@ -237,6 +250,7 @@
|
|||
padding: 6px 10px;
|
||||
background: var(--ctp-surface0);
|
||||
border-radius: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.setting-row label {
|
||||
|
|
@ -255,12 +269,23 @@
|
|||
color: var(--ctp-text);
|
||||
font-size: 0.8rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.setting-row select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.setting-row select optgroup {
|
||||
font-weight: 600;
|
||||
color: var(--ctp-subtext0);
|
||||
}
|
||||
|
||||
.setting-row select option {
|
||||
font-weight: normal;
|
||||
color: var(--ctp-text);
|
||||
}
|
||||
|
||||
.group-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -306,12 +331,18 @@
|
|||
border-radius: 4px;
|
||||
margin-bottom: 4px;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.project-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.project-field-grow {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.field-label {
|
||||
|
|
@ -327,6 +358,8 @@
|
|||
border-radius: 3px;
|
||||
color: var(--ctp-text);
|
||||
font-size: 0.8rem;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.add-form {
|
||||
|
|
@ -334,6 +367,7 @@
|
|||
gap: 8px;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.add-form input {
|
||||
|
|
@ -344,6 +378,7 @@
|
|||
color: var(--ctp-text);
|
||||
font-size: 0.8rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
|
|
@ -370,6 +405,7 @@
|
|||
border-radius: 3px;
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.limit-notice {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
// Theme store — persists Catppuccin flavor selection via settings bridge
|
||||
// Theme store — persists theme selection via settings bridge
|
||||
|
||||
import { getSetting, setSetting } from '../adapters/settings-bridge';
|
||||
import {
|
||||
type ThemeId,
|
||||
type CatppuccinFlavor,
|
||||
ALL_THEME_IDS,
|
||||
buildXtermTheme,
|
||||
applyCssVariables,
|
||||
type XtermTheme,
|
||||
} from '../styles/themes';
|
||||
|
||||
let currentFlavor = $state<CatppuccinFlavor>('mocha');
|
||||
let currentTheme = $state<ThemeId>('mocha');
|
||||
|
||||
/** Registered theme-change listeners */
|
||||
const themeChangeCallbacks = new Set<() => void>();
|
||||
|
||||
/** Register a callback invoked after every flavor change. Returns an unsubscribe function. */
|
||||
/** Register a callback invoked after every theme change. Returns an unsubscribe function. */
|
||||
export function onThemeChange(callback: () => void): () => void {
|
||||
themeChangeCallbacks.add(callback);
|
||||
return () => {
|
||||
|
|
@ -21,18 +23,25 @@ export function onThemeChange(callback: () => void): () => void {
|
|||
};
|
||||
}
|
||||
|
||||
export function getCurrentTheme(): ThemeId {
|
||||
return currentTheme;
|
||||
}
|
||||
|
||||
/** @deprecated Use getCurrentTheme() */
|
||||
export function getCurrentFlavor(): CatppuccinFlavor {
|
||||
return currentFlavor;
|
||||
// Return valid CatppuccinFlavor or default to 'mocha'
|
||||
const catFlavors: string[] = ['latte', 'frappe', 'macchiato', 'mocha'];
|
||||
return catFlavors.includes(currentTheme) ? currentTheme as CatppuccinFlavor : 'mocha';
|
||||
}
|
||||
|
||||
export function getXtermTheme(): XtermTheme {
|
||||
return buildXtermTheme(currentFlavor);
|
||||
return buildXtermTheme(currentTheme);
|
||||
}
|
||||
|
||||
/** Change flavor, apply CSS variables, and persist to settings DB */
|
||||
export async function setFlavor(flavor: CatppuccinFlavor): Promise<void> {
|
||||
currentFlavor = flavor;
|
||||
applyCssVariables(flavor);
|
||||
/** Change theme, apply CSS variables, and persist to settings DB */
|
||||
export async function setTheme(theme: ThemeId): Promise<void> {
|
||||
currentTheme = theme;
|
||||
applyCssVariables(theme);
|
||||
// Notify all listeners (e.g. open xterm.js terminals)
|
||||
for (const cb of themeChangeCallbacks) {
|
||||
try {
|
||||
|
|
@ -43,25 +52,30 @@ export async function setFlavor(flavor: CatppuccinFlavor): Promise<void> {
|
|||
}
|
||||
|
||||
try {
|
||||
await setSetting('theme', flavor);
|
||||
await setSetting('theme', theme);
|
||||
} catch (e) {
|
||||
console.error('Failed to persist theme setting:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Load saved flavor from settings DB and apply. Call once on app startup. */
|
||||
/** @deprecated Use setTheme() */
|
||||
export async function setFlavor(flavor: CatppuccinFlavor): Promise<void> {
|
||||
return setTheme(flavor);
|
||||
}
|
||||
|
||||
/** Load saved theme from settings DB and apply. Call once on app startup. */
|
||||
export async function initTheme(): Promise<void> {
|
||||
try {
|
||||
const saved = await getSetting('theme');
|
||||
if (saved && ['latte', 'frappe', 'macchiato', 'mocha'].includes(saved)) {
|
||||
currentFlavor = saved as CatppuccinFlavor;
|
||||
if (saved && ALL_THEME_IDS.includes(saved as ThemeId)) {
|
||||
currentTheme = saved as ThemeId;
|
||||
}
|
||||
} catch {
|
||||
// Fall back to default (mocha) — catppuccin.css provides Mocha defaults
|
||||
}
|
||||
// Always apply to sync CSS vars with current flavor
|
||||
// Always apply to sync CSS vars with current theme
|
||||
// (skip if mocha — catppuccin.css already has Mocha values)
|
||||
if (currentFlavor !== 'mocha') {
|
||||
applyCssVariables(currentFlavor);
|
||||
if (currentTheme !== 'mocha') {
|
||||
applyCssVariables(currentTheme);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
// Catppuccin theme flavors — https://catppuccin.com/palette
|
||||
// Each flavor provides CSS custom properties and an xterm.js theme object.
|
||||
// Theme system — Catppuccin flavors + popular editor themes
|
||||
// All themes map to the same --ctp-* CSS custom property slots.
|
||||
|
||||
/** All available theme identifiers */
|
||||
export type ThemeId =
|
||||
| 'mocha' | 'macchiato' | 'frappe' | 'latte'
|
||||
| 'vscode-dark' | 'atom-one-dark' | 'monokai' | 'dracula'
|
||||
| 'nord' | 'solarized-dark' | 'github-dark';
|
||||
|
||||
/** Keep for backwards compat — subset of ThemeId */
|
||||
export type CatppuccinFlavor = 'latte' | 'frappe' | 'macchiato' | 'mocha';
|
||||
|
||||
export interface CatppuccinPalette {
|
||||
export interface ThemePalette {
|
||||
rosewater: string;
|
||||
flamingo: string;
|
||||
pink: string;
|
||||
|
|
@ -32,6 +39,9 @@ export interface CatppuccinPalette {
|
|||
crust: string;
|
||||
}
|
||||
|
||||
/** Keep old name as alias */
|
||||
export type CatppuccinPalette = ThemePalette;
|
||||
|
||||
export interface XtermTheme {
|
||||
background: string;
|
||||
foreground: string;
|
||||
|
|
@ -57,128 +67,162 @@ export interface XtermTheme {
|
|||
brightWhite: string;
|
||||
}
|
||||
|
||||
const palettes: Record<CatppuccinFlavor, CatppuccinPalette> = {
|
||||
export interface ThemeMeta {
|
||||
id: ThemeId;
|
||||
label: string;
|
||||
group: string; // For grouping in <optgroup>
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
export const THEME_LIST: ThemeMeta[] = [
|
||||
{ id: 'mocha', label: 'Catppuccin Mocha', group: 'Catppuccin', isDark: true },
|
||||
{ id: 'macchiato', label: 'Catppuccin Macchiato',group: 'Catppuccin', isDark: true },
|
||||
{ id: 'frappe', label: 'Catppuccin Frappé', group: 'Catppuccin', isDark: true },
|
||||
{ id: 'latte', label: 'Catppuccin Latte', group: 'Catppuccin', isDark: false },
|
||||
{ id: 'vscode-dark', label: 'VSCode Dark+', group: 'Editor', isDark: true },
|
||||
{ id: 'atom-one-dark', label: 'Atom One Dark', group: 'Editor', isDark: true },
|
||||
{ id: 'monokai', label: 'Monokai', group: 'Editor', isDark: true },
|
||||
{ id: 'dracula', label: 'Dracula', group: 'Editor', isDark: true },
|
||||
{ id: 'nord', label: 'Nord', group: 'Editor', isDark: true },
|
||||
{ id: 'solarized-dark', label: 'Solarized Dark', group: 'Editor', isDark: true },
|
||||
{ id: 'github-dark', label: 'GitHub Dark', group: 'Editor', isDark: true },
|
||||
];
|
||||
|
||||
const palettes: Record<ThemeId, ThemePalette> = {
|
||||
// --- Catppuccin ---
|
||||
latte: {
|
||||
rosewater: '#dc8a78',
|
||||
flamingo: '#dd7878',
|
||||
pink: '#ea76cb',
|
||||
mauve: '#8839ef',
|
||||
red: '#d20f39',
|
||||
maroon: '#e64553',
|
||||
peach: '#fe640b',
|
||||
yellow: '#df8e1d',
|
||||
green: '#40a02b',
|
||||
teal: '#179299',
|
||||
sky: '#04a5e5',
|
||||
sapphire: '#209fb5',
|
||||
blue: '#1e66f5',
|
||||
lavender: '#7287fd',
|
||||
text: '#4c4f69',
|
||||
subtext1: '#5c5f77',
|
||||
subtext0: '#6c6f85',
|
||||
overlay2: '#7c7f93',
|
||||
overlay1: '#8c8fa1',
|
||||
overlay0: '#9ca0b0',
|
||||
surface2: '#acb0be',
|
||||
surface1: '#bcc0cc',
|
||||
surface0: '#ccd0da',
|
||||
base: '#eff1f5',
|
||||
mantle: '#e6e9ef',
|
||||
crust: '#dce0e8',
|
||||
rosewater: '#dc8a78', flamingo: '#dd7878', pink: '#ea76cb', mauve: '#8839ef',
|
||||
red: '#d20f39', maroon: '#e64553', peach: '#fe640b', yellow: '#df8e1d',
|
||||
green: '#40a02b', teal: '#179299', sky: '#04a5e5', sapphire: '#209fb5',
|
||||
blue: '#1e66f5', lavender: '#7287fd',
|
||||
text: '#4c4f69', subtext1: '#5c5f77', subtext0: '#6c6f85',
|
||||
overlay2: '#7c7f93', overlay1: '#8c8fa1', overlay0: '#9ca0b0',
|
||||
surface2: '#acb0be', surface1: '#bcc0cc', surface0: '#ccd0da',
|
||||
base: '#eff1f5', mantle: '#e6e9ef', crust: '#dce0e8',
|
||||
},
|
||||
frappe: {
|
||||
rosewater: '#f2d5cf',
|
||||
flamingo: '#eebebe',
|
||||
pink: '#f4b8e4',
|
||||
mauve: '#ca9ee6',
|
||||
red: '#e78284',
|
||||
maroon: '#ea999c',
|
||||
peach: '#ef9f76',
|
||||
yellow: '#e5c890',
|
||||
green: '#a6d189',
|
||||
teal: '#81c8be',
|
||||
sky: '#99d1db',
|
||||
sapphire: '#85c1dc',
|
||||
blue: '#8caaee',
|
||||
lavender: '#babbf1',
|
||||
text: '#c6d0f5',
|
||||
subtext1: '#b5bfe2',
|
||||
subtext0: '#a5adce',
|
||||
overlay2: '#949cbb',
|
||||
overlay1: '#838ba7',
|
||||
overlay0: '#737994',
|
||||
surface2: '#626880',
|
||||
surface1: '#51576d',
|
||||
surface0: '#414559',
|
||||
base: '#303446',
|
||||
mantle: '#292c3c',
|
||||
crust: '#232634',
|
||||
rosewater: '#f2d5cf', flamingo: '#eebebe', pink: '#f4b8e4', mauve: '#ca9ee6',
|
||||
red: '#e78284', maroon: '#ea999c', peach: '#ef9f76', yellow: '#e5c890',
|
||||
green: '#a6d189', teal: '#81c8be', sky: '#99d1db', sapphire: '#85c1dc',
|
||||
blue: '#8caaee', lavender: '#babbf1',
|
||||
text: '#c6d0f5', subtext1: '#b5bfe2', subtext0: '#a5adce',
|
||||
overlay2: '#949cbb', overlay1: '#838ba7', overlay0: '#737994',
|
||||
surface2: '#626880', surface1: '#51576d', surface0: '#414559',
|
||||
base: '#303446', mantle: '#292c3c', crust: '#232634',
|
||||
},
|
||||
macchiato: {
|
||||
rosewater: '#f4dbd6',
|
||||
flamingo: '#f0c6c6',
|
||||
pink: '#f5bde6',
|
||||
mauve: '#c6a0f6',
|
||||
red: '#ed8796',
|
||||
maroon: '#ee99a0',
|
||||
peach: '#f5a97f',
|
||||
yellow: '#eed49f',
|
||||
green: '#a6da95',
|
||||
teal: '#8bd5ca',
|
||||
sky: '#91d7e3',
|
||||
sapphire: '#7dc4e4',
|
||||
blue: '#8aadf4',
|
||||
lavender: '#b7bdf8',
|
||||
text: '#cad3f5',
|
||||
subtext1: '#b8c0e0',
|
||||
subtext0: '#a5adcb',
|
||||
overlay2: '#939ab7',
|
||||
overlay1: '#8087a2',
|
||||
overlay0: '#6e738d',
|
||||
surface2: '#5b6078',
|
||||
surface1: '#494d64',
|
||||
surface0: '#363a4f',
|
||||
base: '#24273a',
|
||||
mantle: '#1e2030',
|
||||
crust: '#181926',
|
||||
rosewater: '#f4dbd6', flamingo: '#f0c6c6', pink: '#f5bde6', mauve: '#c6a0f6',
|
||||
red: '#ed8796', maroon: '#ee99a0', peach: '#f5a97f', yellow: '#eed49f',
|
||||
green: '#a6da95', teal: '#8bd5ca', sky: '#91d7e3', sapphire: '#7dc4e4',
|
||||
blue: '#8aadf4', lavender: '#b7bdf8',
|
||||
text: '#cad3f5', subtext1: '#b8c0e0', subtext0: '#a5adcb',
|
||||
overlay2: '#939ab7', overlay1: '#8087a2', overlay0: '#6e738d',
|
||||
surface2: '#5b6078', surface1: '#494d64', surface0: '#363a4f',
|
||||
base: '#24273a', mantle: '#1e2030', crust: '#181926',
|
||||
},
|
||||
mocha: {
|
||||
rosewater: '#f5e0dc',
|
||||
flamingo: '#f2cdcd',
|
||||
pink: '#f5c2e7',
|
||||
mauve: '#cba6f7',
|
||||
red: '#f38ba8',
|
||||
maroon: '#eba0ac',
|
||||
peach: '#fab387',
|
||||
yellow: '#f9e2af',
|
||||
green: '#a6e3a1',
|
||||
teal: '#94e2d5',
|
||||
sky: '#89dceb',
|
||||
sapphire: '#74c7ec',
|
||||
blue: '#89b4fa',
|
||||
lavender: '#b4befe',
|
||||
text: '#cdd6f4',
|
||||
subtext1: '#bac2de',
|
||||
subtext0: '#a6adc8',
|
||||
overlay2: '#9399b2',
|
||||
overlay1: '#7f849c',
|
||||
overlay0: '#6c7086',
|
||||
surface2: '#585b70',
|
||||
surface1: '#45475a',
|
||||
surface0: '#313244',
|
||||
base: '#1e1e2e',
|
||||
mantle: '#181825',
|
||||
crust: '#11111b',
|
||||
rosewater: '#f5e0dc', flamingo: '#f2cdcd', pink: '#f5c2e7', mauve: '#cba6f7',
|
||||
red: '#f38ba8', maroon: '#eba0ac', peach: '#fab387', yellow: '#f9e2af',
|
||||
green: '#a6e3a1', teal: '#94e2d5', sky: '#89dceb', sapphire: '#74c7ec',
|
||||
blue: '#89b4fa', lavender: '#b4befe',
|
||||
text: '#cdd6f4', subtext1: '#bac2de', subtext0: '#a6adc8',
|
||||
overlay2: '#9399b2', overlay1: '#7f849c', overlay0: '#6c7086',
|
||||
surface2: '#585b70', surface1: '#45475a', surface0: '#313244',
|
||||
base: '#1e1e2e', mantle: '#181825', crust: '#11111b',
|
||||
},
|
||||
|
||||
// --- VSCode Dark+ ---
|
||||
'vscode-dark': {
|
||||
rosewater: '#d4a0a0', flamingo: '#cf8686', pink: '#c586c0', mauve: '#c586c0',
|
||||
red: '#f44747', maroon: '#d16969', peach: '#ce9178', yellow: '#dcdcaa',
|
||||
green: '#6a9955', teal: '#4ec9b0', sky: '#9cdcfe', sapphire: '#4fc1ff',
|
||||
blue: '#569cd6', lavender: '#b4b4f7',
|
||||
text: '#d4d4d4', subtext1: '#cccccc', subtext0: '#b0b0b0',
|
||||
overlay2: '#858585', overlay1: '#6e6e6e', overlay0: '#5a5a5a',
|
||||
surface2: '#3e3e42', surface1: '#333338', surface0: '#2d2d30',
|
||||
base: '#1e1e1e', mantle: '#181818', crust: '#111111',
|
||||
},
|
||||
|
||||
// --- Atom One Dark ---
|
||||
'atom-one-dark': {
|
||||
rosewater: '#e5c07b', flamingo: '#e06c75', pink: '#c678dd', mauve: '#c678dd',
|
||||
red: '#e06c75', maroon: '#be5046', peach: '#d19a66', yellow: '#e5c07b',
|
||||
green: '#98c379', teal: '#56b6c2', sky: '#56b6c2', sapphire: '#61afef',
|
||||
blue: '#61afef', lavender: '#c8ccd4',
|
||||
text: '#abb2bf', subtext1: '#9da5b4', subtext0: '#8b92a0',
|
||||
overlay2: '#7f848e', overlay1: '#636d83', overlay0: '#545862',
|
||||
surface2: '#474b56', surface1: '#3b3f4c', surface0: '#333842',
|
||||
base: '#282c34', mantle: '#21252b', crust: '#181a1f',
|
||||
},
|
||||
|
||||
// --- Monokai ---
|
||||
monokai: {
|
||||
rosewater: '#f8f8f2', flamingo: '#f92672', pink: '#f92672', mauve: '#ae81ff',
|
||||
red: '#f92672', maroon: '#f92672', peach: '#fd971f', yellow: '#e6db74',
|
||||
green: '#a6e22e', teal: '#66d9ef', sky: '#66d9ef', sapphire: '#66d9ef',
|
||||
blue: '#66d9ef', lavender: '#ae81ff',
|
||||
text: '#f8f8f2', subtext1: '#e8e8e2', subtext0: '#cfcfc2',
|
||||
overlay2: '#a8a8a2', overlay1: '#90908a', overlay0: '#75715e',
|
||||
surface2: '#595950', surface1: '#49483e', surface0: '#3e3d32',
|
||||
base: '#272822', mantle: '#1e1f1c', crust: '#141411',
|
||||
},
|
||||
|
||||
// --- Dracula ---
|
||||
dracula: {
|
||||
rosewater: '#f1c4e0', flamingo: '#ff79c6', pink: '#ff79c6', mauve: '#bd93f9',
|
||||
red: '#ff5555', maroon: '#ff6e6e', peach: '#ffb86c', yellow: '#f1fa8c',
|
||||
green: '#50fa7b', teal: '#8be9fd', sky: '#8be9fd', sapphire: '#8be9fd',
|
||||
blue: '#6272a4', lavender: '#bd93f9',
|
||||
text: '#f8f8f2', subtext1: '#e8e8e2', subtext0: '#c0c0ba',
|
||||
overlay2: '#a0a0a0', overlay1: '#7f7f7f', overlay0: '#6272a4',
|
||||
surface2: '#555969', surface1: '#44475a', surface0: '#383a4a',
|
||||
base: '#282a36', mantle: '#21222c', crust: '#191a21',
|
||||
},
|
||||
|
||||
// --- Nord ---
|
||||
nord: {
|
||||
rosewater: '#d08770', flamingo: '#bf616a', pink: '#b48ead', mauve: '#b48ead',
|
||||
red: '#bf616a', maroon: '#bf616a', peach: '#d08770', yellow: '#ebcb8b',
|
||||
green: '#a3be8c', teal: '#8fbcbb', sky: '#88c0d0', sapphire: '#81a1c1',
|
||||
blue: '#5e81ac', lavender: '#b48ead',
|
||||
text: '#eceff4', subtext1: '#e5e9f0', subtext0: '#d8dee9',
|
||||
overlay2: '#a5adba', overlay1: '#8891a0', overlay0: '#6c7588',
|
||||
surface2: '#4c566a', surface1: '#434c5e', surface0: '#3b4252',
|
||||
base: '#2e3440', mantle: '#272c36', crust: '#20242c',
|
||||
},
|
||||
|
||||
// --- Solarized Dark ---
|
||||
'solarized-dark': {
|
||||
rosewater: '#d33682', flamingo: '#dc322f', pink: '#d33682', mauve: '#6c71c4',
|
||||
red: '#dc322f', maroon: '#cb4b16', peach: '#cb4b16', yellow: '#b58900',
|
||||
green: '#859900', teal: '#2aa198', sky: '#2aa198', sapphire: '#268bd2',
|
||||
blue: '#268bd2', lavender: '#6c71c4',
|
||||
text: '#839496', subtext1: '#93a1a1', subtext0: '#778a8b',
|
||||
overlay2: '#657b83', overlay1: '#586e75', overlay0: '#4a6068',
|
||||
surface2: '#1c4753', surface1: '#143845', surface0: '#073642',
|
||||
base: '#002b36', mantle: '#00222b', crust: '#001a21',
|
||||
},
|
||||
|
||||
// --- GitHub Dark ---
|
||||
'github-dark': {
|
||||
rosewater: '#ffa198', flamingo: '#ff7b72', pink: '#f778ba', mauve: '#d2a8ff',
|
||||
red: '#ff7b72', maroon: '#ffa198', peach: '#ffa657', yellow: '#e3b341',
|
||||
green: '#7ee787', teal: '#56d4dd', sky: '#79c0ff', sapphire: '#79c0ff',
|
||||
blue: '#58a6ff', lavender: '#d2a8ff',
|
||||
text: '#c9d1d9', subtext1: '#b1bac4', subtext0: '#8b949e',
|
||||
overlay2: '#6e7681', overlay1: '#565c64', overlay0: '#484f58',
|
||||
surface2: '#373e47', surface1: '#30363d', surface0: '#21262d',
|
||||
base: '#0d1117', mantle: '#090c10', crust: '#050608',
|
||||
},
|
||||
};
|
||||
|
||||
export function getPalette(flavor: CatppuccinFlavor): CatppuccinPalette {
|
||||
return palettes[flavor];
|
||||
export function getPalette(theme: ThemeId): ThemePalette {
|
||||
return palettes[theme];
|
||||
}
|
||||
|
||||
/** Build xterm.js ITheme from a Catppuccin palette */
|
||||
export function buildXtermTheme(flavor: CatppuccinFlavor): XtermTheme {
|
||||
const p = palettes[flavor];
|
||||
/** Build xterm.js ITheme from a palette */
|
||||
export function buildXtermTheme(theme: ThemeId): XtermTheme {
|
||||
const p = palettes[theme];
|
||||
return {
|
||||
background: p.base,
|
||||
foreground: p.text,
|
||||
|
|
@ -206,7 +250,7 @@ export function buildXtermTheme(flavor: CatppuccinFlavor): XtermTheme {
|
|||
}
|
||||
|
||||
/** CSS custom property names mapped to palette keys */
|
||||
const CSS_VAR_MAP: [string, keyof CatppuccinPalette][] = [
|
||||
const CSS_VAR_MAP: [string, keyof ThemePalette][] = [
|
||||
['--ctp-rosewater', 'rosewater'],
|
||||
['--ctp-flamingo', 'flamingo'],
|
||||
['--ctp-pink', 'pink'],
|
||||
|
|
@ -235,15 +279,16 @@ const CSS_VAR_MAP: [string, keyof CatppuccinPalette][] = [
|
|||
['--ctp-crust', 'crust'],
|
||||
];
|
||||
|
||||
/** Apply a Catppuccin flavor's CSS custom properties to document root */
|
||||
export function applyCssVariables(flavor: CatppuccinFlavor): void {
|
||||
const p = palettes[flavor];
|
||||
/** Apply a theme's CSS custom properties to document root */
|
||||
export function applyCssVariables(theme: ThemeId): void {
|
||||
const p = palettes[theme];
|
||||
const style = document.documentElement.style;
|
||||
for (const [varName, key] of CSS_VAR_MAP) {
|
||||
style.setProperty(varName, p[key]);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use THEME_LIST instead */
|
||||
export const FLAVOR_LABELS: Record<CatppuccinFlavor, string> = {
|
||||
latte: 'Latte (Light)',
|
||||
frappe: 'Frappe',
|
||||
|
|
@ -251,4 +296,8 @@ export const FLAVOR_LABELS: Record<CatppuccinFlavor, string> = {
|
|||
mocha: 'Mocha (Default)',
|
||||
};
|
||||
|
||||
/** @deprecated Use THEME_LIST instead */
|
||||
export const ALL_FLAVORS: CatppuccinFlavor[] = ['latte', 'frappe', 'macchiato', 'mocha'];
|
||||
|
||||
/** All valid theme IDs for validation */
|
||||
export const ALL_THEME_IDS: ThemeId[] = THEME_LIST.map(t => t.id);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue