diff --git a/desktop/index.html b/desktop/index.html index a4064a84..4cf2a5cd 100644 --- a/desktop/index.html +++ b/desktop/index.html @@ -1,5 +1,5 @@ - + diff --git a/desktop/src/__tests__/generalSettings.test.tsx b/desktop/src/__tests__/generalSettings.test.tsx index 7a2d5e1d..3cde1548 100644 --- a/desktop/src/__tests__/generalSettings.test.tsx +++ b/desktop/src/__tests__/generalSettings.test.tsx @@ -240,6 +240,12 @@ describe('Settings > General tab', () => { render() fireEvent.click(screen.getByText('General')) + const pureWhite = screen.getByRole('button', { name: 'Pure White' }) + const warmClassic = screen.getByRole('button', { name: 'Warm Classic' }) + const dark = screen.getByRole('button', { name: 'Dark' }) + + expect((pureWhite.compareDocumentPosition(warmClassic) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true) + expect((warmClassic.compareDocumentPosition(dark) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0).toBe(true) fireEvent.click(screen.getByRole('button', { name: 'Pure White' })) expect(useSettingsStore.getState().setTheme).toHaveBeenCalledWith('white') diff --git a/desktop/src/pages/Settings.tsx b/desktop/src/pages/Settings.tsx index ddbaaf44..1b94b5c8 100644 --- a/desktop/src/pages/Settings.tsx +++ b/desktop/src/pages/Settings.tsx @@ -1459,9 +1459,9 @@ function GeneralSettings() { RESPONSE_LANGUAGES.find(({ value }) => value === responseLanguage)?.label ?? RESPONSE_LANGUAGES[0]!.label const THEMES: Array<{ value: ThemeMode; label: string }> = [ + { value: 'white', label: t('settings.general.appearance.white') }, { value: 'light', label: t('settings.general.appearance.light') }, { value: 'dark', label: t('settings.general.appearance.dark') }, - { value: 'white', label: t('settings.general.appearance.white') }, ] const WEB_SEARCH_MODES: Array<{ value: WebSearchMode; label: string }> = [ diff --git a/desktop/src/stores/settingsStore.test.ts b/desktop/src/stores/settingsStore.test.ts index 1e15d18f..8c45f172 100644 --- a/desktop/src/stores/settingsStore.test.ts +++ b/desktop/src/stores/settingsStore.test.ts @@ -314,6 +314,53 @@ describe('settingsStore theme persistence', () => { document.documentElement.style.colorScheme = '' }) + it('falls back to the pure white theme when user settings have no theme', async () => { + vi.doMock('../api/settings', () => ({ + settingsApi: { + getUser: vi.fn().mockResolvedValue({}), + updateUser: vi.fn(), + getPermissionMode: vi.fn().mockResolvedValue({ mode: 'default' }), + setPermissionMode: vi.fn(), + getCliLauncherStatus: vi.fn(), + }, + })) + vi.doMock('../api/models', () => ({ + modelsApi: { + list: vi.fn().mockResolvedValue({ models: [] }), + getCurrent: vi.fn().mockResolvedValue({ model: null }), + setCurrent: vi.fn(), + getEffort: vi.fn().mockResolvedValue({ level: 'medium' }), + setEffort: vi.fn(), + }, + })) + vi.doMock('../api/h5Access', () => ({ + h5AccessApi: { + get: vi.fn().mockResolvedValue({ + settings: { + enabled: false, + tokenPreview: null, + allowedOrigins: [], + publicBaseUrl: null, + }, + }), + enable: vi.fn(), + disable: vi.fn(), + regenerate: vi.fn(), + update: vi.fn(), + }, + })) + + const { useSettingsStore } = await import('./settingsStore') + const { useUIStore } = await import('./uiStore') + + await useSettingsStore.getState().fetchAll() + + expect(useSettingsStore.getState().theme).toBe('white') + expect(useUIStore.getState().theme).toBe('white') + expect(document.documentElement.getAttribute('data-theme')).toBe('white') + expect(document.documentElement.style.colorScheme).toBe('light') + }) + it('hydrates the pure white theme from user settings', async () => { vi.doMock('../api/settings', () => ({ settingsApi: { diff --git a/desktop/src/stores/settingsStore.ts b/desktop/src/stores/settingsStore.ts index f87d4a64..51e628ab 100644 --- a/desktop/src/stores/settingsStore.ts +++ b/desktop/src/stores/settingsStore.ts @@ -116,7 +116,7 @@ export const useSettingsStore = create((set, get) => ({ settingsApi.getUser(), loadH5AccessSettings(previousH5Access), ]) - const theme = isThemeMode(userSettings.theme) ? userSettings.theme : 'light' + const theme = isThemeMode(userSettings.theme) ? userSettings.theme : 'white' useUIStore.getState().setTheme(theme) set({ permissionMode: mode, diff --git a/desktop/src/stores/uiStore.test.ts b/desktop/src/stores/uiStore.test.ts index 6f0dfd45..ba719d6f 100644 --- a/desktop/src/stores/uiStore.test.ts +++ b/desktop/src/stores/uiStore.test.ts @@ -8,6 +8,15 @@ describe('uiStore theme handling', () => { document.documentElement.style.colorScheme = '' }) + it('defaults new installs to the pure white theme', async () => { + const { initializeTheme, useUIStore } = await import('./uiStore') + + expect(useUIStore.getState().theme).toBe('white') + initializeTheme() + expect(document.documentElement.getAttribute('data-theme')).toBe('white') + expect(document.documentElement.style.colorScheme).toBe('light') + }) + it('hydrates and applies the pure white theme as a light color scheme', async () => { window.localStorage.setItem('cc-haha-theme', 'white') @@ -19,9 +28,13 @@ describe('uiStore theme handling', () => { expect(document.documentElement.style.colorScheme).toBe('light') }) - it('cycles through light, dark, and pure white themes', async () => { + it('cycles through pure white, warm classic, and dark themes', async () => { const { useUIStore } = await import('./uiStore') + useUIStore.getState().toggleTheme() + expect(useUIStore.getState().theme).toBe('light') + expect(document.documentElement.style.colorScheme).toBe('light') + useUIStore.getState().toggleTheme() expect(useUIStore.getState().theme).toBe('dark') expect(document.documentElement.style.colorScheme).toBe('dark') @@ -29,9 +42,5 @@ describe('uiStore theme handling', () => { useUIStore.getState().toggleTheme() expect(useUIStore.getState().theme).toBe('white') expect(document.documentElement.style.colorScheme).toBe('light') - - useUIStore.getState().toggleTheme() - expect(useUIStore.getState().theme).toBe('light') - expect(document.documentElement.style.colorScheme).toBe('light') }) }) diff --git a/desktop/src/stores/uiStore.ts b/desktop/src/stores/uiStore.ts index 5ecc96ca..686f0898 100644 --- a/desktop/src/stores/uiStore.ts +++ b/desktop/src/stores/uiStore.ts @@ -8,7 +8,7 @@ function getStoredTheme(): ThemeMode { const stored = localStorage.getItem(THEME_STORAGE_KEY) if (isThemeMode(stored)) return stored } catch { /* localStorage unavailable */ } - return 'light' + return 'white' } export function applyTheme(theme: ThemeMode) { @@ -89,7 +89,7 @@ export const useUIStore = create((set) => ({ toggleTheme: () => { set((state) => { const currentIndex = THEME_MODES.indexOf(state.theme) - const next = THEME_MODES[(currentIndex + 1) % THEME_MODES.length] ?? 'light' + const next = THEME_MODES[(currentIndex + 1) % THEME_MODES.length] ?? 'white' applyTheme(next) try { localStorage.setItem(THEME_STORAGE_KEY, next) } catch { /* noop */ } return { theme: next } diff --git a/desktop/src/types/settings.ts b/desktop/src/types/settings.ts index 4a5b247a..d5ea3aa8 100644 --- a/desktop/src/types/settings.ts +++ b/desktop/src/types/settings.ts @@ -3,7 +3,7 @@ export type PermissionMode = 'default' | 'acceptEdits' | 'plan' | 'bypassPermissions' | 'dontAsk' export type EffortLevel = 'low' | 'medium' | 'high' | 'max' -export const THEME_MODES = ['light', 'dark', 'white'] as const +export const THEME_MODES = ['white', 'light', 'dark'] as const export type ThemeMode = (typeof THEME_MODES)[number] export function isThemeMode(value: unknown): value is ThemeMode {