/* * Copyright (C) 2026 Mateusz GruszczyƄski @linuxiarz.pl * Source-Available Code / Dual-Licensed. * * Free for non-commercial and evaluation use under terms of BSL/GPLv3. * Commercial or production use requires a valid paid license. * See LICENSE file in repository root for details. */ const STORAGE_KEY = "rustpad:theme"; const GUEST_STORAGE_KEY = "rustpad:guest-theme"; const AUTH_STATE_KEY = "rustpad:auth-state"; const THEMES = new Set(["dark", "light"]); const systemThemeQuery = window.matchMedia("(prefers-color-scheme: light)"); function systemTheme() { return systemThemeQuery.matches ? "light" : "dark"; } function normalizeTheme(value) { return THEMES.has(value) ? value : systemTheme(); } function updateBrowserChrome(theme) { document.documentElement.dataset.theme = theme; document.documentElement.style.colorScheme = theme; document.querySelector('meta[name="color-scheme"]')?.setAttribute("content", theme); } export function getTheme() { return normalizeTheme(document.documentElement.dataset.theme); } function hasAccountSession() { try { return localStorage.getItem(AUTH_STATE_KEY) === "1"; } catch { return false; } } function persistTheme(theme, scope = "auto") { try { localStorage.setItem(STORAGE_KEY, theme); const guestScope = scope === "guest" || (scope === "auto" && !hasAccountSession()); if (guestScope) localStorage.setItem(GUEST_STORAGE_KEY, theme); } catch { } } export function applyTheme(value, { persist = true, scope = "auto" } = {}) { const theme = normalizeTheme(value); const previousTheme = getTheme(); updateBrowserChrome(theme); if (persist) persistTheme(theme, scope); if (theme !== previousTheme) { window.dispatchEvent(new CustomEvent("rustpad:theme-change", { detail: { theme } })); } return theme; } export function rememberGuestTheme(value = getTheme()) { try { if (THEMES.has(localStorage.getItem(GUEST_STORAGE_KEY))) return; localStorage.setItem(GUEST_STORAGE_KEY, normalizeTheme(value)); } catch { } } export function restoreGuestTheme() { let saved = null; try { saved = localStorage.getItem(GUEST_STORAGE_KEY); } catch { } if (THEMES.has(saved)) { try { localStorage.setItem(STORAGE_KEY, saved); } catch { } return applyTheme(saved, { persist: false }); } try { localStorage.removeItem(STORAGE_KEY); } catch { } return applyTheme(systemTheme(), { persist: false }); } export function applySessionTheme(session) { if (session?.theme) applyTheme(session.theme, { scope: "account" }); } window.addEventListener("storage", event => { if (event.key !== STORAGE_KEY && event.key !== GUEST_STORAGE_KEY && event.key !== AUTH_STATE_KEY) return; if (event.key === GUEST_STORAGE_KEY && hasAccountSession()) return; let saved = null; try { saved = hasAccountSession() ? localStorage.getItem(STORAGE_KEY) : (localStorage.getItem(GUEST_STORAGE_KEY) || localStorage.getItem(STORAGE_KEY)); } catch { } applyTheme(THEMES.has(saved) ? saved : systemTheme(), { persist: false }); }); systemThemeQuery.addEventListener("change", () => { try { const saved = hasAccountSession() ? localStorage.getItem(STORAGE_KEY) : (localStorage.getItem(GUEST_STORAGE_KEY) || localStorage.getItem(STORAGE_KEY)); if (THEMES.has(saved)) return; } catch { } applyTheme(systemTheme(), { persist: false }); });