From af00860c134f07f1f96d2217ca24a910db4626f3 Mon Sep 17 00:00:00 2001 From: "Dr. Dominik Jain" Date: Tue, 28 Jul 2026 11:09:17 +0200 Subject: [PATCH] :bug: Report the effective UI theme to plugins (#10677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Penpot itself and plugins used different default theme selection strategies when no theme is explicitly selected in Penpot by the user: - Penpot itself defaults to dark. - Plugins defaulted to whatever is configured in the user's system/browser, which could be light. So with no explicit theme chosen in the profile — the common state — every plugin could be told the theme was light while Penpot itself showed dark, both on open (penpot.theme) and on themechange. For every explicit choice (light, dark, system, and the legacy default) the two resolutions already agreed; only the unset case diverged. Extract the app's resolution into app.util.theme/resolve-theme (system follows the system theme; default and unset mean dark) as the single source of truth, and use it in the app's own use-initialize as well as in the plugin runtime's getTheme and themechange handling. The themechange handler's now-dead default-to-dark remapping is removed. Fixes #10676 AI-assisted-by: claude-fable-5 --- frontend/src/app/plugins/api.cljs | 10 +--------- frontend/src/app/plugins/events.cljs | 16 +++------------- frontend/src/app/util/theme.cljs | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs index 2b65475c22..fc7d67968a 100644 --- a/frontend/src/app/plugins/api.cljs +++ b/frontend/src/app/plugins/api.cljs @@ -237,15 +237,7 @@ :getTheme (fn [] (let [theme (get-in @st/state [:profile :theme])] - (cond - (or (not theme) (= theme "system")) - (theme/get-system-theme) - - (= theme "default") - "dark" - - :else - theme))) + (theme/resolve-theme theme (theme/get-system-theme)))) :getCurrentUser (fn [] diff --git a/frontend/src/app/plugins/events.cljs b/frontend/src/app/plugins/events.cljs index a6078b2b2b..17e50140a1 100644 --- a/frontend/src/app/plugins/events.cljs +++ b/frontend/src/app/plugins/events.cljs @@ -53,16 +53,8 @@ (defn- get-theme [state] - (let [theme (get-in state [:profile :theme])] - (cond - (or (not theme) (= theme "system")) - (theme/get-system-theme) - - (= theme "default") - "dark" - - :else - theme))) + (theme/resolve-theme (get-in state [:profile :theme]) + (theme/get-system-theme))) (defmethod handle-state-change "themechange" [_ _ old-val new-val _] @@ -70,9 +62,7 @@ new-theme (get-theme new-val)] (if (identical? old-theme new-theme) ::not-changed - (if (= new-theme "default") - "dark" - new-theme)))) + new-theme))) (defmethod handle-state-change "shapechange" [_ plugin-id old-val new-val props] diff --git a/frontend/src/app/util/theme.cljs b/frontend/src/app/util/theme.cljs index 7291fe043e..07b21ec6cb 100644 --- a/frontend/src/app/util/theme.cljs +++ b/frontend/src/app/util/theme.cljs @@ -45,6 +45,17 @@ (.removeAttribute node "class") (.add ^js (.-classList ^js node) class))) +(defn resolve-theme + "Resolves the profile's theme setting to the effective UI theme ('dark' or + 'light'): 'system' follows the given system theme, 'default' and an unset + theme mean dark, and any other value is taken as-is. Single source of truth + for the app's own theme and the theme reported to plugins." + [profile-theme system-theme] + (cond + (= profile-theme "system") system-theme + (= profile-theme "default") "dark" + :else (d/nilv profile-theme "dark"))) + (defn use-initialize [{profile-theme :theme}] (let [system-theme* (mf/use-state get-system-theme) @@ -58,9 +69,5 @@ (rx/dispose! s)))) (mf/with-effect [system-theme profile-theme] - (set-color-scheme - (cond - (= profile-theme "system") system-theme - (= profile-theme "default") "dark" - :else (d/nilv profile-theme "dark"))) + (set-color-scheme (resolve-theme profile-theme system-theme)) (notify-color-scheme-listeners!))))