diff --git a/CHANGES.md b/CHANGES.md index c9681623c4..f47c0905a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -81,6 +81,7 @@ A non exhaustive list of changes: - Copy to SVG from contextual menu [Github #838](https://github.com/penpot/penpot/issues/838) - Add styles for Inkeep Chat at workspace [Taiga #10708](https://tree.taiga.io/project/penpot/us/10708) - Add configuration for air gapped installations with Docker +- Support system color scheme [Github #5030](https://github.com/penpot/penpot/issues/5030) ### :bug: Bugs fixed - Fix getCurrentUser for plugins api [Taiga #11057](https://tree.taiga.io/project/penpot/issue/11057) diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index b048df8842..9de03be41f 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -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 diff --git a/frontend/src/app/main/data/profile.cljs b/frontend/src/app/main/data/profile.cljs index b43ef9f55c..c0d1e29f53 100644 --- a/frontend/src/app/main/data/profile.cljs +++ b/frontend/src/app/main/data/profile.cljs @@ -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 diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index ddf43430e2..8a55c93573 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -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} diff --git a/frontend/src/app/main/ui/settings/options.cljs b/frontend/src/app/main/ui/settings/options.cljs index f3ab76e288..5efb157d9b 100644 --- a/frontend/src/app/main/ui/settings/options.cljs +++ b/frontend/src/app/main/ui/settings/options.cljs @@ -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* diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index 7bc717faf6..e92cfbb4c6 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -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])]]])) diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 026986ce95..e150c44d6c 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -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] diff --git a/frontend/src/app/util/globals.js b/frontend/src/app/util/globals.js index b9e6b4b2f8..44b18e8044 100644 --- a/frontend/src/app/util/globals.js +++ b/frontend/src/app/util/globals.js @@ -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; } })(); diff --git a/frontend/src/app/util/theme.cljs b/frontend/src/app/util/theme.cljs index 28c17efe58..c6a4e1dec0 100644 --- a/frontend/src/app/util/theme.cljs +++ b/frontend/src/app/util/theme.cljs @@ -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 %)))))) \ No newline at end of file diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 1ff7c784e6..abbfbcd593 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -923,6 +923,15 @@ msgstr "Select UI language" msgid "dashboard.select-ui-theme" msgstr "Select theme" +msgid "dashboard.select-ui-theme.dark" +msgstr "Penpot Dark (default)" + +msgid "dashboard.select-ui-theme.light" +msgstr "Penpot Light" + +msgid "dashboard.select-ui-theme.system" +msgstr "System theme" + #: src/app/main/ui/settings/notifications.cljs:57 msgid "dashboard.settings.notifications.dashboard-comments.all" msgstr "All comments, mentions and replies" @@ -5166,6 +5175,9 @@ msgstr "Switch to dark theme" msgid "workspace.header.menu.toggle-light-theme" msgstr "Switch to light theme" +msgid "workspace.header.menu.toggle-system-theme" +msgstr "Switch to system theme" + #: src/app/main/ui/workspace/main_menu.cljs:457 msgid "workspace.header.menu.undo" msgstr "Undo" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 6c96f3fb61..27b42d3802 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -935,6 +935,15 @@ msgstr "Cambiar el idioma de la interfaz" msgid "dashboard.select-ui-theme" msgstr "Selecciona un tema" +msgid "dashboard.select-ui-theme.dark" +msgstr "Penpot Dark (por defecto)" + +msgid "dashboard.select-ui-theme.light" +msgstr "Penpot Light" + +msgid "dashboard.select-ui-theme.system" +msgstr "Sistema" + #: src/app/main/ui/settings/notifications.cljs:57 msgid "dashboard.settings.notifications.dashboard-comments.all" msgstr "Todos los comentarios, menciones y respuestas" @@ -5195,6 +5204,9 @@ msgstr "Cambiar a tema oscuro" msgid "workspace.header.menu.toggle-light-theme" msgstr "Cambiar a tema claro" +msgid "workspace.header.menu.toggle-system-theme" +msgstr "Cambiar a tema del sistema" + #: src/app/main/ui/workspace/main_menu.cljs:457 msgid "workspace.header.menu.undo" msgstr "Deshacer"