Skip to content

Commit 34065cf

Browse files
committed
Make ColorSchemeStore state based
1 parent 74a1f68 commit 34065cf

6 files changed

Lines changed: 125 additions & 125 deletions

File tree

src/lib/components/LightSwitch.svelte

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44
import { buttonVariants } from '$lib/components/ui/button';
55
import * as Dialog from '$lib/components/ui/dialog';
66
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
7-
import { ColorSchemeStore, LightMode, getDarkReaderState } from '$lib/stores/ColorSchemeStore';
7+
import {
8+
ColorScheme,
9+
colorScheme,
10+
getDarkReaderState,
11+
} from '$lib/stores/ColorSchemeStore.svelte';
812
import { cn } from '$lib/utils';
913
import { toast } from 'svelte-sonner';
1014
11-
let pendingScheme = $state<LightMode | null>(null);
15+
let pendingScheme = $state<ColorScheme | null>(null);
1216
function handleOpenChanged(open: boolean) {
1317
if (open) return;
1418
pendingScheme = null;
1519
}
1620
function confirm() {
1721
if (!pendingScheme) return;
18-
ColorSchemeStore.set(pendingScheme);
22+
colorScheme.Value = pendingScheme;
1923
pendingScheme = null;
2024
}
2125
@@ -25,13 +29,13 @@
2529
// dark true false check
2630
// system check false false
2731
// current
28-
function isGoingNuclear(current: LightMode, pending: LightMode) {
32+
function isGoingNuclear(current: ColorScheme, pending: ColorScheme) {
2933
// There will definetly be no transition from dark -> light
30-
if (current === pending || current === LightMode.Light || pending === LightMode.Dark)
34+
if (current === pending || current === ColorScheme.Light || pending === ColorScheme.Dark)
3135
return false;
3236
33-
const currentDark = current === LightMode.Dark;
34-
const pendingLight = pending === LightMode.Light;
37+
const currentDark = current === ColorScheme.Dark;
38+
const pendingLight = pending === ColorScheme.Light;
3539
if (currentDark && pendingLight) return true; // Change will definetly go from dark to light
3640
3741
// Now either current or pending is system, so we need to query the browser preference
@@ -43,8 +47,8 @@
4347
// system prefers darkmode, if pending is light then the transition will happen
4448
return pendingLight;
4549
}
46-
function evaluateLightSwitch(scheme: LightMode) {
47-
if (isGoingNuclear($ColorSchemeStore, scheme)) {
50+
function evaluateLightSwitch(scheme: ColorScheme) {
51+
if (isGoingNuclear(colorScheme.Value, scheme)) {
4852
const darkreader = getDarkReaderState();
4953
if (darkreader.isActive) {
5054
toast.warning('DarkReader is enabled, activating light mode will have no effect!');
@@ -53,7 +57,7 @@
5357
pendingScheme = scheme;
5458
return;
5559
}
56-
ColorSchemeStore.set(scheme);
60+
colorScheme.Value = scheme;
5761
}
5862
</script>
5963

@@ -82,7 +86,7 @@
8286
<span class="sr-only">Toggle theme</span>
8387
</DropdownMenu.Trigger>
8488
<DropdownMenu.Content align="end">
85-
{#each Object.values(LightMode) as value}
89+
{#each Object.values(ColorScheme) as value}
8690
<DropdownMenu.Item
8791
class="cursor-pointer capitalize"
8892
onclick={() => evaluateLightSwitch(value)}>{value}</DropdownMenu.Item

src/lib/components/Turnstile.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { PUBLIC_TURNSTILE_DEV_BYPASS_VALUE, PUBLIC_TURNSTILE_SITE_KEY } from '$env/static/public';
55
import CloudflareLogo from '$lib/components/svg/CloudflareLogo.svelte';
66
import LoadingCircle from '$lib/components/svg/LoadingCircle.svelte';
7-
import { ColorSchemeStore, LightMode } from '$lib/stores/ColorSchemeStore';
7+
import { ColorScheme, colorScheme } from '$lib/stores/ColorSchemeStore.svelte';
88
import { onMount } from 'svelte';
99
import { toast } from 'svelte-sonner';
1010
@@ -28,7 +28,7 @@
2828
function renderTurnstile() {
2929
mounted = true;
3030
31-
const theme = $ColorSchemeStore === LightMode.System ? 'auto' : $ColorSchemeStore;
31+
const theme = colorScheme.Value === ColorScheme.System ? 'auto' : colorScheme.Value;
3232
3333
widgetId = window.turnstile!.render(element, {
3434
sitekey: PUBLIC_TURNSTILE_SITE_KEY,

src/lib/components/ui/sonner/sonner.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<script lang="ts">
22
import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
3-
import { ColorSchemeStore } from '$lib/stores/ColorSchemeStore';
3+
import { colorScheme } from '$lib/stores/ColorSchemeStore.svelte';
44
55
let { ...restProps }: SonnerProps = $props();
66
</script>
77

88
<Sonner
9-
theme={$ColorSchemeStore}
9+
theme={colorScheme.Value}
1010
class="toaster group"
1111
style="--normal-bg: var(--color-popover); --normal-text: var(--color-popover-foreground); --normal-border: var(--color-border);"
1212
{...restProps}

src/lib/init.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { browser } from '$app/environment';
2-
import { initializeDarkModeStore } from '$lib/stores/ColorSchemeStore';
2+
import { initializeDarkModeStore } from '$lib/stores/ColorSchemeStore.svelte';
33
import { initializeSerialPortsStore } from '$lib/stores/SerialPortsStore';
44
import { UserStore } from '$lib/stores/UserStore';
55
import { get, writable } from 'svelte/store';
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { browser } from '$app/environment';
2+
import { isString } from '$lib/typeguards';
3+
4+
export enum ColorScheme {
5+
Dark = 'dark',
6+
Light = 'light',
7+
System = 'system',
8+
}
9+
10+
function isColorSchemeEnum(value: unknown): value is ColorScheme {
11+
if (!isString(value)) return false;
12+
return Object.values(ColorScheme).includes(value as ColorScheme);
13+
}
14+
15+
function getLocalStoreState(): ColorScheme {
16+
// If we are not in a browser environment, return default
17+
if (!browser) {
18+
return ColorScheme.System;
19+
}
20+
21+
// If the stored value is invalid, reset it and return default
22+
const scheme = localStorage.getItem('theme');
23+
if (!isColorSchemeEnum(scheme)) {
24+
localStorage.setItem('theme', ColorScheme.System);
25+
return ColorScheme.System;
26+
}
27+
28+
return scheme;
29+
}
30+
31+
export function getDarkReaderState() {
32+
const rootHtml = document.documentElement;
33+
34+
const proxyInjected = rootHtml.getAttribute('data-darkreader-proxy-injected');
35+
const metaElement = rootHtml.querySelector('head meta[name="darkreader"]');
36+
const scheme = rootHtml.getAttribute('data-darkreader-scheme');
37+
38+
return {
39+
isInjected: proxyInjected === 'true',
40+
isActive: metaElement !== null,
41+
scheme,
42+
};
43+
}
44+
45+
function resolveDarkMode(userPreference: ColorScheme): boolean {
46+
if (userPreference !== ColorScheme.System) {
47+
return userPreference !== ColorScheme.Light;
48+
}
49+
50+
// If a user has Dark Reader extension installed, assume they prefer dark mode
51+
const darkReaderState = getDarkReaderState();
52+
if (darkReaderState.isInjected) {
53+
return true;
54+
}
55+
56+
// Check if the user has a system theme preference for light mode
57+
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
58+
return false;
59+
}
60+
61+
// Default to dark mode
62+
return true;
63+
}
64+
65+
function setDarkMode(preference: ColorScheme) {
66+
document.documentElement.classList.toggle('dark', resolveDarkMode(preference));
67+
}
68+
69+
class ColorSchemeState {
70+
#value;
71+
72+
constructor() {
73+
this.#value = $state<ColorScheme>(getLocalStoreState());
74+
}
75+
76+
get Value() {
77+
return this.#value;
78+
}
79+
set Value(value: ColorScheme) {
80+
localStorage.setItem('theme', value);
81+
setDarkMode(value);
82+
}
83+
}
84+
85+
export const colorScheme = new ColorSchemeState();
86+
87+
function handleMediaQueryChange() {
88+
setDarkMode(getLocalStoreState());
89+
}
90+
export function initializeDarkModeStore() {
91+
handleMediaQueryChange();
92+
93+
window
94+
.matchMedia('(prefers-color-scheme: light)')
95+
.addEventListener('change', handleMediaQueryChange);
96+
window
97+
.matchMedia('(prefers-color-scheme: dark)')
98+
.addEventListener('change', handleMediaQueryChange);
99+
100+
window.addEventListener('storage', (event) => {
101+
if (event.key !== 'theme') return;
102+
103+
setDarkMode(isColorSchemeEnum(event.newValue) ? event.newValue : ColorScheme.System);
104+
});
105+
}

src/lib/stores/ColorSchemeStore.ts

Lines changed: 0 additions & 109 deletions
This file was deleted.

0 commit comments

Comments
 (0)