Merge pull request #6561 from mdbenito/feature/5030-use-system-theme

 Use system theme
This commit is contained in:
Andrey Antukh 2025-05-30 10:34:46 +02:00 committed by GitHub
commit cc76a42088
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 94 additions and 25 deletions

View file

@ -97,7 +97,8 @@
(cur/init-styles)
(thr/init!)
(init-ui)
(st/emit! (plugins/initialize)
(st/emit! (theme/initialize)
(plugins/initialize)
(initialize)))
(defn ^:export reinit

View file

@ -160,8 +160,10 @@
(update [_ state]
(update-in state [:profile :theme]
(fn [current]
(if (= current "default")
"light"
(case current
"dark" "light"
"light" "system"
"system" "dark"
"default"))))
ptk/WatchEvent

View file

@ -30,6 +30,7 @@
[app.main.ui.static :as static]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.theme :as theme]
[beicon.v2.core :as rx]
[rumext.v2 :as mf]))
@ -358,10 +359,12 @@
(let [route (mf/deref refs/route)
edata (mf/deref refs/exception)
profile (mf/deref refs/profile)
theme (or (:theme profile) "default")]
profile-theme (:theme profile)
system-theme (mf/deref theme/preferred-color-scheme)]
(mf/with-effect [theme]
(dom/set-html-theme-color theme))
(mf/with-effect [profile-theme system-theme]
(dom/set-html-theme-color
(if (= profile-theme "system") system-theme profile-theme)))
[:& (mf/provider ctx/current-route) {:value route}
[:& (mf/provider ctx/current-profile) {:value profile}

View file

@ -59,8 +59,9 @@
[:& fm/select {:label (tr "dashboard.select-ui-theme")
:name :theme
:default "default"
:options [{:label "Penpot Dark (default)" :value "default"}
{:label "Penpot Light" :value "light"}]
:options [{:label (tr "dashboard.select-ui-theme.dark") :value "dark"}
{:label (tr "dashboard.select-ui-theme.light") :value "light"}
{:label (tr "dashboard.select-ui-theme.system") :value "system"}]
:data-testid "setting-theme"}]]
[:> fm/submit-button*

View file

@ -280,9 +280,11 @@
:data-testid "toggle-theme"
:id "file-menu-toggle-theme"}
[:span {:class (stl/css :item-name)}
(if (= (:theme profile) "default")
(tr "workspace.header.menu.toggle-light-theme")
(tr "workspace.header.menu.toggle-dark-theme"))]
(case (:theme profile) ;; default = dark -> light -> system -> dark and so on
"default" (tr "workspace.header.menu.toggle-light-theme")
"light" (tr "workspace.header.menu.toggle-system-theme")
"system" (tr "workspace.header.menu.toggle-dark-theme")
(tr "workspace.header.menu.toggle-light-theme"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-theme))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))

View file

@ -76,9 +76,10 @@
(defn set-html-theme-color
[^string color]
(let [node (.querySelector js/document "body")]
(let [node (.querySelector js/document "body")
class (if (= color "dark") "default" "light")]
(.removeAttribute node "class")
(.add ^js (.-classList ^js node) color)))
(.add ^js (.-classList ^js node) class)))
(defn set-page-style!
[styles]

View file

@ -17,7 +17,7 @@
goog.provide("app.util.globals");
goog.scope(function() {
goog.scope(function () {
app.util.globals.global = goog.global;
function createGlobalEventEmitter(k) {
@ -25,22 +25,27 @@ goog.scope(function() {
* may subscribe to them.
*/
return {
addListener(...args) {
},
removeListener(...args) {
},
addEventListener(...args) {
},
removeEventListener(...args) {
}
}
addListener(...args) {},
removeListener(...args) {},
addEventListener(...args) {},
removeEventListener(...args) {},
dispatchEvent(...args) { return true; },
};
}
app.util.globals.window = (function() {
app.util.globals.window = (function () {
if (typeof goog.global.window !== "undefined") {
return goog.global.window;
} else {
return createGlobalEventEmitter();
const mockWindow = createGlobalEventEmitter();
mockWindow.matchMedia = function (query) {
const mediaObj = createGlobalEventEmitter();
mediaObj.matches = false;
mediaObj.media = query;
mediaObj.onchange = null;
return mediaObj;
};
return mockWindow;
}
})();

View file

@ -10,8 +10,10 @@
(:require
[app.config :as cfg]
[app.util.dom :as dom]
[app.util.globals :as globals]
[app.util.storage :as storage]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(defonce theme (get storage/global ::theme cfg/default-theme))
@ -44,3 +46,30 @@
#js [])
theme))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Set the preferred color scheme based on the user's system settings.
;; TODO: this is unrelated to the theme support above, which seems unused as
;; of v2.7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce ^:private color-scheme-mq
(.matchMedia globals/window "(prefers-color-scheme: dark)"))
;; This atom is referenced in app.main.ui.app
(defonce preferred-color-scheme
(atom (if (.-matches color-scheme-mq) "dark" "light")))
(defonce prefers-color-scheme-sub
(let [sub (rx/behavior-subject "dark")
ob (->> (rx/from-event color-scheme-mq "change")
(rx/map #(if (.-matches %) "dark" "light")))]
(rx/sub! ob sub)
sub))
(defn initialize
[]
(ptk/reify ::initialize
ptk/WatchEvent
(watch [_ _ _]
(->> prefers-color-scheme-sub
(rx/map #(reset! preferred-color-scheme %))))))