diff --git a/src/app.d.ts b/src/app.d.ts
index 367926a..d8b4864 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -4,6 +4,12 @@ declare global {
namespace App {
// interface Error {}
// interface Locals {}
+ interface Locals {
+ theme: Theme
+ }
+ interface PageData {
+ theme: Theme
+ }
// interface PageData {}
// interface PageState {}
// interface Platform {}
diff --git a/src/app.html b/src/app.html
index 84ffad1..98dfe47 100644
--- a/src/app.html
+++ b/src/app.html
@@ -1,5 +1,5 @@
-
+
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
new file mode 100644
index 0000000..8a60dd8
--- /dev/null
+++ b/src/hooks.server.ts
@@ -0,0 +1,24 @@
+import type { Handle } from "@sveltejs/kit"
+
+export type Theme = "light" | "dark" | "auto"
+
+export const isValidTheme = (
+ theme: FormDataEntryValue | null
+): theme is Theme =>
+ !!theme && (theme === "light" || theme === "dark" || theme === "auto")
+
+export const handle: Handle = async ({ event, resolve }) => {
+ const theme = event.cookies.get("theme") ?? "auto"
+ if (isValidTheme(theme)) {
+ event.locals.theme = theme
+ }
+
+ event.setHeaders({
+ "cache-control": `private, max-age=${5 * 60}`,
+ })
+ const response = await resolve(event, {
+ transformPageChunk: ({ html }) => html.replace("%THEME%", theme),
+ })
+
+ return response
+}
diff --git a/src/lib/components/NavBar.svelte b/src/lib/components/NavBar.svelte
new file mode 100644
index 0000000..67a982c
--- /dev/null
+++ b/src/lib/components/NavBar.svelte
@@ -0,0 +1,74 @@
+
+
+
+
+
diff --git a/src/lib/components/ThemePicker.svelte b/src/lib/components/ThemePicker.svelte
index c69aed6..6cb2e2a 100644
--- a/src/lib/components/ThemePicker.svelte
+++ b/src/lib/components/ThemePicker.svelte
@@ -10,6 +10,9 @@
function toggleTheme() {
const currentIndex = themes.indexOf(theme)
theme = themes[(currentIndex + 1) % themes.length]
+ // if (typeof window !== "undefined" && typeof localStorage !== "undefined") {
+ // localStorage.setItem("theme", theme)
+ // }
}
let icon = $derived.by(() => {
diff --git a/src/routes/+layout.js b/src/routes/+layout.js
deleted file mode 100644
index e69de29..0000000
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts
new file mode 100644
index 0000000..134cc66
--- /dev/null
+++ b/src/routes/+layout.server.ts
@@ -0,0 +1,7 @@
+import type { LayoutServerLoad } from "./$types"
+
+export const load: LayoutServerLoad = async ({ locals }) => {
+ const { theme } = locals
+
+ return { theme }
+}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 3fd29cc..1d255e3 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,14 +1,22 @@
-
+
+
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
new file mode 100644
index 0000000..15ff7c0
--- /dev/null
+++ b/src/routes/+page.server.ts
@@ -0,0 +1,19 @@
+import { fail, type Actions } from "@sveltejs/kit"
+import { isValidTheme } from "../hooks.server"
+
+const TEN_YEARS_IN_SECONDS = 10 * 365 * 24 * 60 * 60
+
+export const actions: Actions = {
+ theme: async ({ cookies, request }) => {
+ const data = await request.formData()
+ const theme = data.get("theme")
+
+ if (!isValidTheme(theme)) {
+ return fail(400, { theme, missing: true })
+ }
+
+ cookies.set("theme", theme, { path: "/", maxAge: TEN_YEARS_IN_SECONDS })
+
+ return { success: true }
+ },
+}
diff --git a/src/service-worker.js b/src/service-worker.js
new file mode 100644
index 0000000..3a3cdd5
--- /dev/null
+++ b/src/service-worker.js
@@ -0,0 +1,67 @@
+///
+import { build, files, version } from "$service-worker"
+
+const CACHE = `cache-${version}`
+
+const ASSETS = [...build, ...files]
+
+self.addEventListener("install", (event) => {
+ async function addFilesToCache() {
+ const cache = await caches.open(CACHE)
+ await cache.addAll(ASSETS)
+ }
+
+ event.waitUntil(addFilesToCache())
+})
+
+self.addEventListener("activate", (event) => {
+ async function deleteOldCaches() {
+ for (const key of await caches.keys()) {
+ if (key !== CACHE) await caches.delete(key)
+ }
+ }
+
+ event.waitUntil(deleteOldCaches())
+})
+
+self.addEventListener("fetch", (event) => {
+ if (event.request.method !== "GET") return
+
+ async function respond() {
+ const url = new URL(event.request.url)
+ const cache = await caches.open(CACHE)
+
+ if (ASSETS.includes(url.pathname)) {
+ return cache.match(event.request)
+ }
+
+ try {
+ const response = await fetch(event.request)
+
+ if (response.status === 200) {
+ cache.put(event.request, response.clone())
+ }
+
+ return response
+ } catch {
+ return cache.match(event.request)
+ }
+ }
+
+ event.respondWith(respond())
+})
+
+// self.addEventListener('storage', event => {
+// if (event.key === 'theme') {
+// const newCacheName = event.newValue === 'dark' ? 'my-cache-dark' : 'my-cache';
+
+// caches.keys().then(cacheNames => {
+// return Promise.all(
+// cacheNames.filter(cacheName => cacheName === CACHE)
+// .map(cacheName => caches.delete(cacheName))
+// );
+// }).then(() => {
+// return caches.open(newCacheName);
+// })
+// }
+// })