Parcourir la source

Merge pull request #1115 from dannyl1u/feat/system-wide-theme

feat: system os light/dark mode option 🔦
Timothy Jaeryang Baek il y a 1 an
Parent
commit
1f0dcb4fce
3 fichiers modifiés avec 66 ajouts et 33 suppressions
  1. 28 11
      src/app.html
  2. 37 21
      src/lib/components/chat/Settings/General.svelte
  3. 1 1
      src/lib/stores/index.ts

+ 28 - 11
src/app.html

@@ -8,18 +8,35 @@
 		<meta name="robots" content="noindex,nofollow" />
 		<script>
 			// On page load or when changing themes, best to add inline in `head` to avoid FOUC
-			if (
-				localStorage.theme === 'light' ||
-				(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
-			) {
-				document.documentElement.classList.add('light');
-			} else if (localStorage.theme) {
-				localStorage.theme.split(' ').forEach((e) => {
-					document.documentElement.classList.add(e);
+			(() => {
+				if (
+					localStorage.theme === 'light' ||
+					(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
+				) {
+					document.documentElement.classList.add('light');
+				} else if (localStorage.theme && localStorage.theme !== 'system') {
+					localStorage.theme.split(' ').forEach((e) => {
+						document.documentElement.classList.add(e);
+					});
+				} else if (localStorage.theme && localStorage.theme === 'system') {
+					systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
+					document.documentElement.classList.add(systemTheme ? 'dark' : 'light');
+				} else {
+					document.documentElement.classList.add('dark');
+				}
+
+				window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
+					if (localStorage.theme === 'system') {
+						if (e.matches) {
+							document.documentElement.classList.add('dark');
+							document.documentElement.classList.remove('light');
+						} else {
+							document.documentElement.classList.add('light');
+							document.documentElement.classList.remove('dark');
+						}
+					}
 				});
-			} else {
-				document.documentElement.classList.add('dark');
-			}
+			})();
 		</script>
 
 		%sveltekit.head%

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

@@ -4,7 +4,7 @@
 	import { getLanguages } from '$lib/i18n';
 	const dispatch = createEventDispatcher();
 
-	import { models, user } from '$lib/stores';
+	import { models, user, theme } from '$lib/stores';
 
 	const i18n = getContext('i18n');
 
@@ -15,7 +15,8 @@
 
 	// General
 	let themes = ['dark', 'light', 'rose-pine dark', 'rose-pine-dawn light'];
-	let theme = 'dark';
+	let selectedTheme = 'system';
+
 	let languages = [];
 	let lang = $i18n.language;
 	let notificationEnabled = false;
@@ -68,10 +69,11 @@
 	};
 
 	onMount(async () => {
+		selectedTheme = localStorage.theme ?? 'system';
+
 		let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
 		languages = await getLanguages();
 
-		theme = localStorage.theme ?? 'dark';
 		notificationEnabled = settings.notificationEnabled ?? false;
 		system = settings.system ?? '';
 
@@ -87,6 +89,35 @@
 		options = { ...options, ...settings.options };
 		options.stop = (settings?.options?.stop ?? []).join(',');
 	});
+
+	const applyTheme = (_theme: string) => {
+		let themeToApply = _theme;
+
+		if (_theme === 'system') {
+			themeToApply = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+		}
+
+		themes
+			.filter((e) => e !== themeToApply)
+			.forEach((e) => {
+				e.split(' ').forEach((e) => {
+					document.documentElement.classList.remove(e);
+				});
+			});
+
+		themeToApply.split(' ').forEach((e) => {
+			document.documentElement.classList.add(e);
+		});
+
+		console.log(_theme);
+	};
+
+	const themeChangeHandler = (_theme: string) => {
+		theme.set(_theme);
+		localStorage.setItem('theme', _theme);
+
+		applyTheme(_theme);
+	};
 </script>
 
 <div class="flex flex-col h-full justify-between text-sm">
@@ -99,26 +130,11 @@
 				<div class="flex items-center relative">
 					<select
 						class=" dark:bg-gray-900 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={(e) => {
-							localStorage.theme = theme;
-
-							themes
-								.filter((e) => e !== theme)
-								.forEach((e) => {
-									e.split(' ').forEach((e) => {
-										document.documentElement.classList.remove(e);
-									});
-								});
-
-							theme.split(' ').forEach((e) => {
-								document.documentElement.classList.add(e);
-							});
-
-							console.log(theme);
-						}}
+						on:change={() => themeChangeHandler(selectedTheme)}
 					>
+						<option value="system">⚙️ {$i18n.t('System')}</option>
 						<option value="dark">🌑 {$i18n.t('Dark')}</option>
 						<option value="light">☀️ {$i18n.t('Light')}</option>
 						<option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>

+ 1 - 1
src/lib/stores/index.ts

@@ -7,7 +7,7 @@ export const config = writable(undefined);
 export const user = writable(undefined);
 
 // Frontend
-export const theme = writable('dark');
+export const theme = writable('system');
 
 export const chatId = writable('');