mirror of
https://github.com/penpot/penpot.git
synced 2026-08-31 17:19:11 +00:00
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
124 lines
4.0 KiB
Clojure
124 lines
4.0 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
|
|
|
(ns app.plugins.events
|
|
(:require
|
|
[app.common.data.macros :as dm]
|
|
[app.main.data.helpers :as dsh]
|
|
[app.main.store :as st]
|
|
[app.plugins.file :as file]
|
|
[app.plugins.page :as page]
|
|
[app.plugins.parser :as parser]
|
|
[app.plugins.shape :as shape]
|
|
[app.util.object :as obj]
|
|
[app.util.theme :as theme]
|
|
[goog.functions :as gf]))
|
|
|
|
(defmulti handle-state-change (fn [type _] type))
|
|
|
|
(defmethod handle-state-change "finish"
|
|
[_ _ old-val new-val _]
|
|
(let [old-file-id (:current-file-id old-val)
|
|
new-file-id (:current-file-id new-val)]
|
|
(if (and (some? old-file-id) (nil? new-file-id))
|
|
(str old-file-id)
|
|
::not-changed)))
|
|
|
|
(defmethod handle-state-change "filechange"
|
|
[_ plugin-id old-val new-val _]
|
|
(let [old-file-id (:current-file-id old-val)
|
|
new-file-id (:current-file-id new-val)]
|
|
(if (identical? old-file-id new-file-id)
|
|
::not-changed
|
|
(file/file-proxy plugin-id new-file-id))))
|
|
|
|
(defmethod handle-state-change "pagechange"
|
|
[_ plugin-id old-val new-val _]
|
|
(let [old-page-id (:current-page-id old-val)
|
|
new-page-id (:current-page-id new-val)]
|
|
(if (identical? old-page-id new-page-id)
|
|
::not-changed
|
|
(page/page-proxy plugin-id (:current-file-id new-val) new-page-id))))
|
|
|
|
(defmethod handle-state-change "selectionchange"
|
|
[_ _ old-val new-val _]
|
|
(let [old-selection (get-in old-val [:workspace-local :selected])
|
|
new-selection (get-in new-val [:workspace-local :selected])]
|
|
(if (identical? old-selection new-selection)
|
|
::not-changed
|
|
(apply array (map str new-selection)))))
|
|
|
|
(defn- get-theme
|
|
[state]
|
|
(theme/resolve-theme (get-in state [:profile :theme])
|
|
(theme/get-system-theme)))
|
|
|
|
(defmethod handle-state-change "themechange"
|
|
[_ _ old-val new-val _]
|
|
(let [old-theme (get-theme old-val)
|
|
new-theme (get-theme new-val)]
|
|
(if (identical? old-theme new-theme)
|
|
::not-changed
|
|
new-theme)))
|
|
|
|
(defmethod handle-state-change "shapechange"
|
|
[_ plugin-id old-val new-val props]
|
|
(if-let [shape-id (-> (obj/get props "shapeId") parser/parse-id)]
|
|
(let [old-shape (dsh/lookup-shape old-val shape-id)
|
|
new-shape (dsh/lookup-shape new-val shape-id)
|
|
|
|
file-id (:current-file-id new-val)
|
|
page-id (:current-page-id new-val)]
|
|
(if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id))
|
|
::not-changed
|
|
(shape/shape-proxy plugin-id file-id page-id shape-id)))
|
|
::not-changed))
|
|
|
|
(defmethod handle-state-change "contentsave"
|
|
[_ _ old-val new-val _]
|
|
(let [old-status (dm/get-in old-val [:persistence :status])
|
|
new-status (dm/get-in new-val [:persistence :status])]
|
|
(if (and (= :saved new-status) (not= new-status old-status))
|
|
::void ;; Changed but void
|
|
::not-changed)))
|
|
|
|
(defmethod handle-state-change :default
|
|
[_ _ _ _]
|
|
::not-changed)
|
|
|
|
(defn add-listener
|
|
[type plugin-id callback props]
|
|
(let [plugin-id (parser/parse-id plugin-id)
|
|
key (js/Symbol)
|
|
|
|
;; We wrap the callback in an exception handler so the plugins
|
|
;; don't crash the application
|
|
safe-callback
|
|
(fn [value]
|
|
(try
|
|
(if (= ::void value)
|
|
(callback)
|
|
(callback value))
|
|
(catch :default cause
|
|
(.error js/console cause))))
|
|
|
|
;; We also debounce the callbacks so we don't get too many at the same time
|
|
debounced-callback (gf/debounce safe-callback 10)]
|
|
|
|
(add-watch
|
|
st/state key
|
|
(fn [_ _ old-val new-val]
|
|
(let [result (handle-state-change type plugin-id old-val new-val props)]
|
|
(when (not= ::not-changed result)
|
|
(debounced-callback result)))))
|
|
|
|
;; return the generated key
|
|
key))
|
|
|
|
(defn remove-listener
|
|
[key]
|
|
(remove-watch st/state key))
|