feat(themes): add follow-system mode to the theme scheduler (#1163)

* feat(themes): add follow-system mode to the theme scheduler

The theme scheduler can now switch the day/night theme pair by the OS
light/dark preference instead of a clock schedule. A segmented control
picks the trigger; in system mode the time inputs are hidden and the two
theme pickers read as Light/Dark.

The OS theme is read via the native Tauri window theme API (theme() +
onThemeChanged) rather than the Web prefers-color-scheme media query,
which is unreliable through WebKitGTK on Linux. Live updates land
instantly where the platform forwards them; on Linux setups that don't,
a hint notes the change applies after an app restart.

* docs(changelog): add follow-system theme mode entry (#1163)

CHANGELOG Added entry, contributor credit and What's New highlight for the
theme scheduler's new system-theme mode.
This commit is contained in:
Psychotoxical
2026-06-23 12:13:26 +02:00
committed by GitHub
parent 78a177b1bc
commit c7d76af790
20 changed files with 356 additions and 51 deletions
+17 -1
View File
@@ -18,11 +18,17 @@ export type BuiltinTheme =
*/
export type Theme = BuiltinTheme | (string & {});
/** Trigger for the day/night theme switch. */
export type ThemeSchedulerMode = 'time' | 'system';
interface ThemeState {
theme: Theme;
setTheme: (theme: Theme) => void;
enableThemeScheduler: boolean;
setEnableThemeScheduler: (v: boolean) => void;
/** What drives the day/night switch: a clock schedule or the OS theme. */
schedulerMode: ThemeSchedulerMode;
setSchedulerMode: (v: ThemeSchedulerMode) => void;
themeDay: string;
setThemeDay: (v: string) => void;
themeNight: string;
@@ -51,8 +57,16 @@ interface ThemeState {
setExternalArtworkByok: (v: string) => void;
}
export function getScheduledTheme(state: Pick<ThemeState, 'enableThemeScheduler' | 'theme' | 'themeDay' | 'themeNight' | 'timeDayStart' | 'timeNightStart'>): string {
export function getScheduledTheme(
state: Pick<
ThemeState,
'enableThemeScheduler' | 'schedulerMode' | 'theme' | 'themeDay' | 'themeNight' | 'timeDayStart' | 'timeNightStart'
>,
systemPrefersDark = false,
): string {
if (!state.enableThemeScheduler) return state.theme;
// Follow the OS theme: dark → night theme, light → day theme.
if (state.schedulerMode === 'system') return systemPrefersDark ? state.themeNight : state.themeDay;
const now = new Date();
const nowMins = now.getHours() * 60 + now.getMinutes();
const [dh, dm] = state.timeDayStart.split(':').map(Number);
@@ -72,6 +86,8 @@ export const useThemeStore = create<ThemeState>()(
setTheme: (theme) => set({ theme }),
enableThemeScheduler: false,
setEnableThemeScheduler: (v) => set({ enableThemeScheduler: v }),
schedulerMode: 'time',
setSchedulerMode: (v) => set({ schedulerMode: v }),
themeDay: 'latte',
setThemeDay: (v) => set({ themeDay: v }),
themeNight: 'mocha',