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 {