mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
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:
@@ -32,27 +32,56 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) {
|
||||
setProjectFontSans,
|
||||
setProjectFontMono,
|
||||
} = useAppStore();
|
||||
const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark');
|
||||
|
||||
// Font local state - tracks what's selected when using custom fonts
|
||||
const [fontSansLocal, setFontSansLocal] = useState<string>(
|
||||
project.fontFamilySans || DEFAULT_FONT_VALUE
|
||||
);
|
||||
const [fontMonoLocal, setFontMonoLocal] = useState<string>(
|
||||
project.fontFamilyMono || DEFAULT_FONT_VALUE
|
||||
);
|
||||
|
||||
// Sync font state when project changes
|
||||
useEffect(() => {
|
||||
setFontSansLocal(project.fontFamilySans || DEFAULT_FONT_VALUE);
|
||||
setFontMonoLocal(project.fontFamilyMono || DEFAULT_FONT_VALUE);
|
||||
}, [project]);
|
||||
|
||||
// Theme state
|
||||
const projectTheme = project.theme as Theme | undefined;
|
||||
const hasCustomTheme = projectTheme !== undefined;
|
||||
const effectiveTheme = projectTheme || globalTheme;
|
||||
|
||||
// Determine if current theme is light or dark
|
||||
const isLightTheme = lightThemes.some((t) => t.value === effectiveTheme);
|
||||
const [activeTab, setActiveTab] = useState<'dark' | 'light'>(isLightTheme ? 'light' : 'dark');
|
||||
|
||||
// Helper to validate fonts against available options
|
||||
const isValidSansFont = (font: string | undefined): boolean => {
|
||||
if (!font) return false;
|
||||
return UI_SANS_FONT_OPTIONS.some((opt) => opt.value === font);
|
||||
};
|
||||
const isValidMonoFont = (font: string | undefined): boolean => {
|
||||
if (!font) return false;
|
||||
return UI_MONO_FONT_OPTIONS.some((opt) => opt.value === font);
|
||||
};
|
||||
|
||||
// Font local state - tracks what's selected when using custom fonts
|
||||
// Falls back to default if stored font is not in available options
|
||||
const [fontSansLocal, setFontSansLocal] = useState<string>(
|
||||
project.fontFamilySans && isValidSansFont(project.fontFamilySans)
|
||||
? project.fontFamilySans
|
||||
: DEFAULT_FONT_VALUE
|
||||
);
|
||||
const [fontMonoLocal, setFontMonoLocal] = useState<string>(
|
||||
project.fontFamilyMono && isValidMonoFont(project.fontFamilyMono)
|
||||
? project.fontFamilyMono
|
||||
: DEFAULT_FONT_VALUE
|
||||
);
|
||||
|
||||
// Sync state when project changes
|
||||
useEffect(() => {
|
||||
setFontSansLocal(
|
||||
project.fontFamilySans && isValidSansFont(project.fontFamilySans)
|
||||
? project.fontFamilySans
|
||||
: DEFAULT_FONT_VALUE
|
||||
);
|
||||
setFontMonoLocal(
|
||||
project.fontFamilyMono && isValidMonoFont(project.fontFamilyMono)
|
||||
? project.fontFamilyMono
|
||||
: DEFAULT_FONT_VALUE
|
||||
);
|
||||
// Also sync the active tab based on current theme
|
||||
const currentIsLight = lightThemes.some((t) => t.value === (project.theme || globalTheme));
|
||||
setActiveTab(currentIsLight ? 'light' : 'dark');
|
||||
}, [project, globalTheme]);
|
||||
|
||||
// Font state - check if project has custom fonts set
|
||||
const hasCustomFontSans = project.fontFamilySans !== undefined;
|
||||
const hasCustomFontMono = project.fontFamilyMono !== undefined;
|
||||
@@ -79,10 +108,11 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) {
|
||||
setProjectFontSans(project.id, null);
|
||||
setFontSansLocal(DEFAULT_FONT_VALUE);
|
||||
} else {
|
||||
// Set to current global font or default
|
||||
// Set explicit project override - use 'default' value to indicate explicit default choice
|
||||
const fontToSet = globalFontSans || DEFAULT_FONT_VALUE;
|
||||
setFontSansLocal(fontToSet);
|
||||
setProjectFontSans(project.id, fontToSet === DEFAULT_FONT_VALUE ? null : fontToSet);
|
||||
// Store the actual value (including 'default') so hasCustomFontSans stays true
|
||||
setProjectFontSans(project.id, fontToSet);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -92,21 +122,24 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) {
|
||||
setProjectFontMono(project.id, null);
|
||||
setFontMonoLocal(DEFAULT_FONT_VALUE);
|
||||
} else {
|
||||
// Set to current global font or default
|
||||
// Set explicit project override - use 'default' value to indicate explicit default choice
|
||||
const fontToSet = globalFontMono || DEFAULT_FONT_VALUE;
|
||||
setFontMonoLocal(fontToSet);
|
||||
setProjectFontMono(project.id, fontToSet === DEFAULT_FONT_VALUE ? null : fontToSet);
|
||||
// Store the actual value (including 'default') so hasCustomFontMono stays true
|
||||
setProjectFontMono(project.id, fontToSet);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFontSansChange = (value: string) => {
|
||||
setFontSansLocal(value);
|
||||
setProjectFontSans(project.id, value === DEFAULT_FONT_VALUE ? null : value);
|
||||
// Store the actual value (including 'default') - only null clears to use global
|
||||
setProjectFontSans(project.id, value);
|
||||
};
|
||||
|
||||
const handleFontMonoChange = (value: string) => {
|
||||
setFontMonoLocal(value);
|
||||
setProjectFontMono(project.id, value === DEFAULT_FONT_VALUE ? null : value);
|
||||
// Store the actual value (including 'default') - only null clears to use global
|
||||
setProjectFontMono(project.id, value);
|
||||
};
|
||||
|
||||
// Get display label for global font
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
@@ -24,14 +24,34 @@ interface AppearanceSectionProps {
|
||||
}
|
||||
|
||||
export function AppearanceSection({ effectiveTheme, onThemeChange }: AppearanceSectionProps) {
|
||||
const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark');
|
||||
const { fontFamilySans, fontFamilyMono, setFontSans, setFontMono } = useAppStore();
|
||||
|
||||
// Determine if current theme is light or dark
|
||||
const isLightTheme = lightThemes.some((t) => t.value === effectiveTheme);
|
||||
const [activeTab, setActiveTab] = useState<'dark' | 'light'>(isLightTheme ? 'light' : 'dark');
|
||||
|
||||
// Sync active tab when theme changes
|
||||
useEffect(() => {
|
||||
const currentIsLight = lightThemes.some((t) => t.value === effectiveTheme);
|
||||
setActiveTab(currentIsLight ? 'light' : 'dark');
|
||||
}, [effectiveTheme]);
|
||||
|
||||
const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes;
|
||||
|
||||
// Convert null to 'default' for Select component
|
||||
const fontSansValue = fontFamilySans || DEFAULT_FONT_VALUE;
|
||||
const fontMonoValue = fontFamilyMono || DEFAULT_FONT_VALUE;
|
||||
// Also fallback to default if the stored font is not in the available options
|
||||
const isValidSansFont = (font: string | null): boolean => {
|
||||
if (!font) return false;
|
||||
return UI_SANS_FONT_OPTIONS.some((opt) => opt.value === font);
|
||||
};
|
||||
const isValidMonoFont = (font: string | null): boolean => {
|
||||
if (!font) return false;
|
||||
return UI_MONO_FONT_OPTIONS.some((opt) => opt.value === font);
|
||||
};
|
||||
const fontSansValue =
|
||||
fontFamilySans && isValidSansFont(fontFamilySans) ? fontFamilySans : DEFAULT_FONT_VALUE;
|
||||
const fontMonoValue =
|
||||
fontFamilyMono && isValidMonoFont(fontFamilyMono) ? fontFamilyMono : DEFAULT_FONT_VALUE;
|
||||
|
||||
const handleFontSansChange = (value: string) => {
|
||||
setFontSans(value === DEFAULT_FONT_VALUE ? null : value);
|
||||
|
||||
Reference in New Issue
Block a user