From 7aeb3d5dd4d53ce6a6fed03957ee6f5d9eee0f39 Mon Sep 17 00:00:00 2001 From: Elysia Date: Fri, 13 Feb 2026 14:49:41 +0800 Subject: [PATCH] feat(theme): add system theme support close #13 --- electron/main/index.ts | 14 ++++++++- src/App.vue | 56 ++++++++++++++++++++++++++++------ src/components/AppSidebar.vue | 4 +-- src/components/ThemeToggle.vue | 35 +++++++++++++++------ 4 files changed, 87 insertions(+), 22 deletions(-) diff --git a/electron/main/index.ts b/electron/main/index.ts index a141dcae..631fa40c 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -1,4 +1,12 @@ -import { app, BrowserWindow, ipcMain, Menu, shell, Tray } from "electron"; +import { + app, + BrowserWindow, + ipcMain, + Menu, + shell, + Tray, + nativeTheme, +} from "electron"; import { fileURLToPath } from "node:url"; import path from "node:path"; import os from "node:os"; @@ -116,6 +124,10 @@ ipcMain.on("renderer-ready", (event, args) => { logger.info(`isLoaded set to: ${isLoaded.value}`); }); +ipcMain.on("set-theme-source", (event, theme: "system" | "light" | "dark") => { + nativeTheme.themeSource = theme; +}); + app.whenReady().then(() => { createWindow(); handleCommandLine(process.argv); diff --git a/src/App.vue b/src/App.vue index 3c2967e6..95f4b8de 100644 --- a/src/App.vue +++ b/src/App.vue @@ -9,7 +9,7 @@ :categories="categories" :active-category="activeCategory" :category-counts="categoryCounts" - :is-dark-theme="isDarkTheme" + :theme-mode="themeMode" @toggle-theme="toggleTheme" @select-category="selectCategory" /> @@ -153,7 +153,15 @@ const axiosInstance = axios.create({ }); // 响应式状态 -const isDarkTheme = ref(false); +const themeMode = ref<"light" | "dark" | "auto">("auto"); +const systemIsDark = ref( + window.matchMedia("(prefers-color-scheme: dark)").matches, +); +const isDarkTheme = computed(() => { + if (themeMode.value === "auto") return systemIsDark.value; + return themeMode.value === "dark"; +}); + const categories: Ref> = ref({}); const apps: Ref = ref([]); const activeCategory = ref("all"); @@ -218,18 +226,39 @@ const hasSelectedUpgrades = computed(() => { }); // 方法 -const syncThemePreference = (enabled: boolean) => { - document.documentElement.classList.toggle("dark", enabled); +const syncThemePreference = () => { + document.documentElement.classList.toggle("dark", isDarkTheme.value); }; const initTheme = () => { const savedTheme = localStorage.getItem("theme"); - isDarkTheme.value = savedTheme === "dark"; - syncThemePreference(isDarkTheme.value); + if ( + savedTheme === "dark" || + savedTheme === "light" || + savedTheme === "auto" + ) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + themeMode.value = savedTheme as any; + } else { + themeMode.value = "auto"; + } + window.ipcRenderer.send( + "set-theme-source", + themeMode.value === "auto" ? "system" : themeMode.value, + ); + syncThemePreference(); + + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", (e) => { + systemIsDark.value = e.matches; + }); }; const toggleTheme = () => { - isDarkTheme.value = !isDarkTheme.value; + if (themeMode.value === "auto") themeMode.value = "light"; + else if (themeMode.value === "light") themeMode.value = "dark"; + else themeMode.value = "auto"; }; const selectCategory = (category: string) => { @@ -736,8 +765,15 @@ onMounted(async () => { }); // 观察器 -watch(isDarkTheme, (newVal) => { - localStorage.setItem("theme", newVal ? "dark" : "light"); - syncThemePreference(newVal); +watch(themeMode, (newVal) => { + localStorage.setItem("theme", newVal); + window.ipcRenderer.send( + "set-theme-source", + newVal === "auto" ? "system" : newVal, + ); +}); + +watch(isDarkTheme, () => { + syncThemePreference(); }); diff --git a/src/components/AppSidebar.vue b/src/components/AppSidebar.vue index a6a840d5..5b2b6a5e 100644 --- a/src/components/AppSidebar.vue +++ b/src/components/AppSidebar.vue @@ -17,7 +17,7 @@ - +