mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Default desktop startup to pure white
New desktop installs and settings payloads without an explicit theme now hydrate to the pure white workspace. The General settings selector keeps the other two themes available, but presents pure white first so the default and visible ordering agree. Constraint: Existing persisted theme choices must continue to win over the default. Rejected: Remove the warm classic theme | users still need the alternate light appearance. Confidence: high Scope-risk: narrow Directive: Keep the localStorage and user-settings theme fallbacks aligned when changing desktop theme defaults. Tested: cd desktop && bun run test src/stores/uiStore.test.ts src/stores/settingsStore.test.ts src/__tests__/generalSettings.test.tsx Tested: cd desktop && bun run lint Tested: bun run check:desktop Tested: git diff --check Not-tested: Full root bun run verify.
This commit is contained in:
parent
c75ff0bf2d
commit
5ed1081252
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="zh-CN" data-theme="white">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
@ -240,6 +240,12 @@ describe('Settings > General tab', () => {
|
||||
render(<Settings />)
|
||||
|
||||
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')
|
||||
|
||||
@ -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 }> = [
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -116,7 +116,7 @@ export const useSettingsStore = create<SettingsStore>((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,
|
||||
|
||||
@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@ -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<UIStore>((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 }
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user