From 5e4faf191c0a5365231fa0a8f257f38fa68760dd Mon Sep 17 00:00:00 2001 From: Niels Mokkenstorm Date: Mon, 10 Aug 2026 13:45:58 +0200 Subject: [PATCH] fix: track the app theme in theme-color and drop the unreachable theme migration --- web/priv/static/index.html | 27 ++++++++++++++++----------- web/src/crate_web.gleam | 2 +- web/src/crate_web/ffi.ts | 25 +++++++++++++++++-------- web/src/crate_web/prefs.gleam | 25 ------------------------- 4 files changed, 34 insertions(+), 45 deletions(-) diff --git a/web/priv/static/index.html b/web/priv/static/index.html index 5748841..a29fd2c 100644 --- a/web/priv/static/index.html +++ b/web/priv/static/index.html @@ -3,23 +3,19 @@ - - + + crate diff --git a/web/src/crate_web.gleam b/web/src/crate_web.gleam index 493fe9b..a95e1d7 100644 --- a/web/src/crate_web.gleam +++ b/web/src/crate_web.gleam @@ -141,7 +141,7 @@ fn initial_display() -> model.Display { } fn initial_theme() -> model.Theme { - prefs.get_theme() + prefs.get(prefs.theme_key) |> result.map(model.theme_from_string) |> result.unwrap(model.System) } diff --git a/web/src/crate_web/ffi.ts b/web/src/crate_web/ffi.ts index 4f22d4c..1ac100c 100644 --- a/web/src/crate_web/ffi.ts +++ b/web/src/crate_web/ffi.ts @@ -19,9 +19,26 @@ export function applyTheme(theme: string): void { } if (theme === "light" || theme === "dark") { root.setAttribute("data-theme", theme); + syncThemeColor(theme); return; } root.removeAttribute("data-theme"); + syncThemeColor(theme); +} + +// The browser chrome follows the app's own theme, not the OS: an explicit +// light/dark choice has to beat prefers-color-scheme, which a media-keyed +// pair cannot express. +function syncThemeColor(theme: string): void { + const meta = globalThis.document?.getElementById("theme-color"); + if (!meta) { + return; + } + const prefersDark = + !!globalThis.matchMedia && + globalThis.matchMedia("(prefers-color-scheme: dark)").matches; + const dark = theme === "dark" || (theme !== "light" && prefersDark); + meta.setAttribute("content", dark ? "#191008" : "#f1e7d2"); } // A real full-page navigation, since modem would otherwise intercept a same-origin click. @@ -157,14 +174,6 @@ export function prefsSet(key: string, value: string): void { } } -export function prefsRemove(key: string): void { - try { - globalThis.localStorage?.removeItem(key); - } catch { - // best-effort - } -} - // The page origin, so a relative BFF path can be made absolute for the // gleam_http request the async xrpc transport builds (fetch resolves relative // urls itself, but request.to() needs an absolute one). diff --git a/web/src/crate_web/prefs.gleam b/web/src/crate_web/prefs.gleam index 925b4f1..ea983e3 100644 --- a/web/src/crate_web/prefs.gleam +++ b/web/src/crate_web/prefs.gleam @@ -16,18 +16,11 @@ fn ffi_get(key: String) -> Dynamic @external(javascript, "./ffi.ts", "prefsSet") fn ffi_set(key: String, value: String) -> Nil -@external(javascript, "./ffi.ts", "prefsRemove") -fn ffi_remove(key: String) -> Nil - /// The crate page's grid/rows toggle. pub const display_key = "display" pub const theme_key = "theme" -/// Every key not-theme started fresh under "crate:"; theme is the only one -/// that shipped real user data under the pre-rename "at-record:" namespace. -const legacy_theme_key = "at-record:theme" - /// Locally-ignored `catalog.edit` proposal uri+cid pairs, capped and JSON-encoded. pub const ignored_proposals_key = "ignored-proposals" @@ -51,24 +44,6 @@ pub fn set(key: String, value: String) -> Nil { ffi_set(namespaced(key), value) } -/// Reads the theme, migrating a value left under the pre-rename key on first -/// read: adopt it under the new key, then drop the old one so this only -/// costs a lookup once per user. -pub fn get_theme() -> Result(String, Nil) { - case get(theme_key) { - Ok(value) -> Ok(value) - Error(Nil) -> - ffi_get(legacy_theme_key) - |> decode.run(decode.string) - |> result.map(fn(value) { - set(theme_key, value) - ffi_remove(legacy_theme_key) - value - }) - |> result.replace_error(Nil) - } -} - pub fn encode_ignored_proposals(ignored: List(#(String, String))) -> String { ignored |> json.array(fn(pair) { -- 2.51.2