浏览代码

update theme handling and persist selection using Svelte store

Danny Liu 1 年之前
父节点
当前提交
6f3acb347d
共有 3 个文件被更改,包括 59 次插入41 次删除
  1. 35 37
      src/lib/components/chat/Settings/General.svelte
  2. 22 2
      src/lib/stores/index.ts
  3. 2 2
      src/routes/+layout.svelte

+ 35 - 37
src/lib/components/chat/Settings/General.svelte

@@ -1,10 +1,11 @@
 <script lang="ts">
 	import { toast } from 'svelte-sonner';
 	import { createEventDispatcher, onMount } from 'svelte';
+	import { theme, setTheme } from '../../../stores/index';
 	const dispatch = createEventDispatcher();
 
 	import { models, user } from '$lib/stores';
-
+ 
 	import AdvancedParams from './Advanced/AdvancedParams.svelte';
 
 	export let saveSettings: Function;
@@ -12,46 +13,34 @@
 
 	// General
 	let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light'];
-	let theme = 'dark';
+	let selectedTheme = 'system';
+	let actualTheme: string;
+	$: actualTheme = $theme;
 	let notificationEnabled = false;
 	let system = '';
 
 	let showAdvanced = false;
 
-	window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-		if (theme === 'system') {
-			updateSystemTheme();
-		}
-	});
-
-	function updateSystemTheme() {
-		const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
-		const systemTheme = isDarkMode ? 'dark' : 'light';
-		applyTheme(systemTheme);
-	}
-
-	function applyTheme(theme: string) {
-		localStorage.theme = theme;
+	function applyTheme(theme: string) { // only apply visually
+        let themeToApply = theme;
+        if (theme === 'system') {
+            themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+        }
 
-		if (theme === 'system') {
-			updateSystemTheme();
-			return;
-		}
-
-		themes
-			.filter((e) => e !== theme)
-			.forEach((e) => {
-				e.split(' ').forEach((e) => {
-					document.documentElement.classList.remove(e);
-				});
-			});
+        themes
+            .filter((e) => e !== themeToApply)
+            .forEach((e) => {
+                e.split(' ').forEach((e) => {
+                    document.documentElement.classList.remove(e);
+                });
+            });
 
-		theme.split(' ').forEach((e) => {
-			document.documentElement.classList.add(e);
-		});
+        themeToApply.split(' ').forEach((e) => {
+            document.documentElement.classList.add(e);
+        });
 
 		console.log(theme)
-	}
+    }
 
 	const toggleNotification = async () => {
 		const permission = await Notification.requestPermission();
@@ -98,9 +87,11 @@
 	};
 
 	onMount(async () => {
+		selectedTheme = localStorage.getItem('theme') ?? 'system';
+		applyTheme(selectedTheme);
+
 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
 
-		theme = localStorage.theme ?? 'dark';
 		notificationEnabled = settings.notificationEnabled ?? false;
 		system = settings.system ?? '';
 
@@ -116,6 +107,13 @@
 		options = { ...options, ...settings.options };
 		options.stop = (settings?.options?.stop ?? []).join(',');
 	});
+
+	function handleThemeChange(newTheme: string) {
+        selectedTheme = newTheme;
+        setTheme(newTheme); // Update the store
+        localStorage.setItem('theme', newTheme); // Persist the theme selection
+        applyTheme(newTheme); // Apply the selected theme
+    }
 </script>
 
 <div class="flex flex-col h-full justify-between text-sm">
@@ -127,7 +125,7 @@
 				<div class=" self-center text-xs font-medium">Theme</div>
 				<div class="flex items-center relative">
 					<div class=" absolute right-16">
-						{#if theme === 'dark'}
+						{#if actualTheme === 'dark'}
 							<svg
 								xmlns="http://www.w3.org/2000/svg"
 								viewBox="0 0 20 20"
@@ -140,7 +138,7 @@
 									clip-rule="evenodd"
 								/>
 							</svg>
-						{:else if theme === 'light'}
+						{:else if actualTheme === 'light'}
 							<svg
 								xmlns="http://www.w3.org/2000/svg"
 								viewBox="0 0 20 20"
@@ -156,9 +154,9 @@
 
 					<select
 						class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
-						bind:value={theme}
+						bind:value={selectedTheme}
 						placeholder="Select a theme"
-						on:change="{() => applyTheme(theme)}"
+						on:change="{() => handleThemeChange(selectedTheme)}"
 					>
 						<option value="system">System</option>
 						<option value="dark">Dark</option>

+ 22 - 2
src/lib/stores/index.ts

@@ -1,5 +1,5 @@
 import { APP_NAME } from '$lib/constants';
-import { writable } from 'svelte/store';
+import { writable, derived } from 'svelte/store';
 
 // Backend
 export const WEBUI_NAME = writable(APP_NAME);
@@ -7,7 +7,27 @@ export const config = writable(undefined);
 export const user = writable(undefined);
 
 // Frontend
-export const theme = writable('dark');
+const rawThemeSetting = writable('system');
+export const theme = derived(rawThemeSetting, ($rawThemeSetting) => {
+	if ($rawThemeSetting === 'system') {
+		return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+	}
+	return $rawThemeSetting;
+});
+
+window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
+	rawThemeSetting.update((currentTheme) => {
+		if (currentTheme === 'system') {
+			return e.matches ? 'dark' : 'light';
+		}
+		return currentTheme;
+	});
+});
+
+export function setTheme(theme){
+	rawThemeSetting.set(theme);
+	localStorage.setItem('theme', theme);
+}
 
 export const chatId = writable('');
 

+ 2 - 2
src/routes/+layout.svelte

@@ -1,6 +1,6 @@
 <script>
 	import { onMount, tick } from 'svelte';
-	import { config, user, theme, WEBUI_NAME } from '$lib/stores';
+	import { config, user, setTheme, WEBUI_NAME } from '$lib/stores';
 	import { goto } from '$app/navigation';
 	import { Toaster, toast } from 'svelte-sonner';
 
@@ -15,7 +15,7 @@
 	let loaded = false;
 
 	onMount(async () => {
-		theme.set(localStorage.theme);
+		setTheme(localStorage.theme)
 		// Check Backend Status
 		const backendConfig = await getBackendConfig();