mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-29 04:54:49 +02:00
[eric] theme: default to light + fix light/dark switch flicker
This commit is contained in:
@@ -39,7 +39,7 @@ class AppSettings(BaseModel):
|
||||
default_max_turns: Optional[int] = None
|
||||
default_thinking_level: Literal["off", "low", "medium", "high", "auto"] = "auto"
|
||||
zoom_sensitivity: float = 50.0
|
||||
theme: str = "dark"
|
||||
theme: str = "light"
|
||||
# Shared across App Builder workspaces (each runs its own vite port / localStorage origin); null = follow system.
|
||||
app_template_theme_override: Optional[Literal["light", "dark"]] = None
|
||||
new_agent_shortcut: str = "Meta+l"
|
||||
|
||||
@@ -123,7 +123,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
|
||||
default_max_turns: null,
|
||||
default_thinking_level: 'auto',
|
||||
zoom_sensitivity: 50,
|
||||
theme: 'dark',
|
||||
theme: 'light',
|
||||
new_agent_shortcut: 'Meta+l',
|
||||
anthropic_api_key: null,
|
||||
browser_homepage: 'https://duckduckgo.com',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo } from 'react';
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import { ClaudeTokens, lightTokens, darkTokens } from './claudeTokens';
|
||||
|
||||
type ThemeMode = 'light' | 'dark';
|
||||
@@ -17,10 +17,20 @@ function getInitialMode(): ThemeMode {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored === 'light' || stored === 'dark') return stored;
|
||||
} catch {}
|
||||
if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) return 'dark';
|
||||
// Default light regardless of OS; the real choice arrives from settings a beat later, and matching that default here keeps the pre-load frame from flashing to the opposite theme.
|
||||
return 'light';
|
||||
}
|
||||
|
||||
// A theme swap repaints every element, and the app's hover transitions (transition: background-color / all) would each crossfade the new color at their own duration: that staggered animation IS the flicker. Kill all transitions for the single swap frame so the change is one instant cut; hovers animate again next tick.
|
||||
function p_suppressTransitionsForSwap(): () => void {
|
||||
const killer = document.createElement('style');
|
||||
killer.appendChild(document.createTextNode('*,*::before,*::after{transition:none!important}'));
|
||||
document.head.appendChild(killer);
|
||||
void document.body.offsetHeight; // force a synchronous restyle so the rule applies to this frame
|
||||
const id = window.setTimeout(() => killer.remove(), 0);
|
||||
return () => { window.clearTimeout(id); killer.remove(); };
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextValue>({
|
||||
mode: 'light',
|
||||
tokens: lightTokens,
|
||||
@@ -30,17 +40,21 @@ const ThemeContext = createContext<ThemeContextValue>({
|
||||
|
||||
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [mode, setModeState] = useState<ThemeMode>(getInitialMode);
|
||||
const firstMount = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
try { localStorage.setItem(STORAGE_KEY, mode); } catch {}
|
||||
if (firstMount.current) { firstMount.current = false; return; }
|
||||
return p_suppressTransitionsForSwap();
|
||||
}, [mode]);
|
||||
|
||||
const tokens = useMemo(() => (mode === 'dark' ? darkTokens : lightTokens), [mode]);
|
||||
|
||||
const toggleMode = () => setModeState((m) => (m === 'light' ? 'dark' : 'light'));
|
||||
const setMode = (m: ThemeMode) => setModeState(m);
|
||||
// Stable identities: SettingsLoader's "apply settings.theme" effect lists setMode in its deps, so a setter that changed every render made that effect re-fire on each toggle and re-assert the OLD persisted theme until the debounced save caught up: live theme snapped back for ~900ms = the switch flicker.
|
||||
const toggleMode = useCallback(() => setModeState((m) => (m === 'light' ? 'dark' : 'light')), []);
|
||||
const setMode = useCallback((m: ThemeMode) => setModeState(m), []);
|
||||
|
||||
const value = useMemo(() => ({ mode, tokens, toggleMode, setMode }), [mode, tokens]);
|
||||
const value = useMemo(() => ({ mode, tokens, toggleMode, setMode }), [mode, tokens, toggleMode, setMode]);
|
||||
|
||||
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user