mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
🐛 Merge token activation through develop's tokens status
develop moved token activation out of the tokens lib into the document's :tokens-status (penpot/penpot#10293): ctob/get-active-theme-paths is gone, :set-active-token-themes is no longer a change type, and a set is activated by id in the status rather than through the hidden theme. The branch merge still read active theme paths off the lib, which on the JVM is a compile error in app.common.files.branch-merge and therefore stops every backend namespace that requires it from loading. The merge now reads both halves of activation off :tokens-status, as ids, and keeps the two conflict kinds and resolution keys the UI already offers (:active-themes, :active-sets). compute-changes settles each half by the rule it used before and emits one :set-tokens-status, because the change replaces the whole status. The hidden theme stays out of the theme diff, as before. The two activation tests now build the status with develop's own cfo/activate-set and cfo/activate-theme, and a third pins that a conflicting activation keeps main's status until a resolution takes the branch's. common-tests.files-branch-merge-test: 84 tests, 331 assertions, 0 failures. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
b0e01ef2ef
commit
6605968633
@ -25,6 +25,7 @@
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.types.tokens-status :as ctos]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]))
|
||||
@ -845,8 +846,10 @@
|
||||
;; existing set) carry kind :token -> :set-token. Structural changes
|
||||
;; carry their own kinds, each translated by `compute-changes`: set
|
||||
;; add/delete :token-set, rename/description :token-set-rename, set order
|
||||
;; :token-set-order, themes :token-theme, active themes
|
||||
;; :token-active-themes and active-set toggles :token-active-sets.
|
||||
;; :token-set-order, themes :token-theme, and activation, which the
|
||||
;; document keeps in `:tokens-status` rather than in the lib: active themes
|
||||
;; :token-active-themes and active sets :token-active-sets, both written
|
||||
;; back as one :set-tokens-status.
|
||||
|
||||
(defn- lib-set-ids
|
||||
[lib]
|
||||
@ -947,20 +950,19 @@
|
||||
(ctob/get-themes lib))
|
||||
{}))
|
||||
|
||||
(defn- lib-active-paths
|
||||
"Active theme paths (mergeable via :set-active-token-themes)."
|
||||
[lib]
|
||||
(if lib (set (ctob/get-active-theme-paths lib)) #{}))
|
||||
(defn- active-theme-ids
|
||||
"The active theme ids of `data`'s `:tokens-status`."
|
||||
[data]
|
||||
(if-let [status (:tokens-status data)]
|
||||
(ctos/get-active-theme-ids status)
|
||||
#{}))
|
||||
|
||||
(defn- lib-hidden-sets
|
||||
"The hidden theme's active sets (active-set toggles)."
|
||||
[lib]
|
||||
(some-> lib (ctob/get-theme ctob/hidden-theme-id) :sets set))
|
||||
|
||||
(defn- hidden-theme-map
|
||||
"The hidden theme as a plain map (for :set-token-theme)."
|
||||
[lib]
|
||||
(some->> (when lib (ctob/get-theme lib ctob/hidden-theme-id)) (into {})))
|
||||
(defn- active-set-ids
|
||||
"The active set ids of `data`'s `:tokens-status`."
|
||||
[data]
|
||||
(if-let [status (:tokens-status data)]
|
||||
(ctos/get-active-set-ids status)
|
||||
#{}))
|
||||
|
||||
(defn- set-order-by-id
|
||||
"Set ids in their stored order, filtered to `ids`."
|
||||
@ -999,15 +1001,15 @@
|
||||
;; themes, excluding hidden (mergeable: :token-theme)
|
||||
themes (three-way-entities (lib-themes bl) (lib-themes tl) (lib-themes ol)
|
||||
{:kind :token-theme})
|
||||
;; active theme paths (mergeable: :token-active-themes)
|
||||
active-paths (three-way-entities {:active-themes (lib-active-paths bl)}
|
||||
{:active-themes (lib-active-paths tl)}
|
||||
{:active-themes (lib-active-paths ol)}
|
||||
{:kind :token-active-themes})
|
||||
;; active-set toggles (hidden theme) (mergeable: :token-active-sets)
|
||||
active-sets (three-way-entities {:active-sets (lib-hidden-sets bl)}
|
||||
{:active-sets (lib-hidden-sets tl)}
|
||||
{:active-sets (lib-hidden-sets ol)}
|
||||
;; activation, read off each document's tokens status (mergeable:
|
||||
;; :token-active-themes and :token-active-sets)
|
||||
active-themes (three-way-entities {:active-themes (active-theme-ids base)}
|
||||
{:active-themes (active-theme-ids theirs)}
|
||||
{:active-themes (active-theme-ids ours)}
|
||||
{:kind :token-active-themes})
|
||||
active-sets (three-way-entities {:active-sets (active-set-ids base)}
|
||||
{:active-sets (active-set-ids theirs)}
|
||||
{:active-sets (active-set-ids ours)}
|
||||
{:kind :token-active-sets})
|
||||
;; per-token values (mergeable: :token). Sets deleted on either side
|
||||
;; relative to the base are decided wholesale at set level (delete or
|
||||
@ -1024,7 +1026,7 @@
|
||||
(lib-tokens-by-id ol sid)
|
||||
{:kind :token :set-id sid}))
|
||||
(remove deleted-on-a-side? set-ids))]
|
||||
(merge-results (concat [presence rename order themes active-paths active-sets] tokens))))
|
||||
(merge-results (concat [presence rename order themes active-themes active-sets] tokens))))
|
||||
|
||||
(defn remap-refs
|
||||
"Rewrite cross-file references in `data` through `id-map`
|
||||
@ -1951,28 +1953,24 @@
|
||||
(map (fn [sid] {:type :set-token-set :id sid :attrs (set-rename-attrs ml ol sid)})))
|
||||
common-sets)
|
||||
|
||||
;; active theme paths
|
||||
;; activation: each half settles the way `diff-tokens` classified
|
||||
;; it, and one :set-tokens-status carries both, because the change
|
||||
;; replaces the whole status
|
||||
settle (fn [b m o k]
|
||||
(cond
|
||||
(= o b) m
|
||||
(= m b) o
|
||||
(= o m) m
|
||||
(= (get resolutions k) :branch) o
|
||||
:else m))
|
||||
main-theme-ids (active-theme-ids main)
|
||||
main-set-ids (active-set-ids main)
|
||||
theme-ids (settle (active-theme-ids base) main-theme-ids (active-theme-ids branch) :active-themes)
|
||||
active-ids (settle (active-set-ids base) main-set-ids (active-set-ids branch) :active-sets)
|
||||
active-changes
|
||||
(let [bp (lib-active-paths bl) mp (lib-active-paths ml) op (lib-active-paths ol)]
|
||||
(cond
|
||||
(= op bp) []
|
||||
(= mp bp) [{:type :set-active-token-themes :theme-paths op}]
|
||||
(= op mp) []
|
||||
(= (get resolutions :active-themes) :branch) [{:type :set-active-token-themes :theme-paths op}]
|
||||
:else []))
|
||||
|
||||
;; active sets (hidden theme): bring branch's hidden theme
|
||||
active-sets-changes
|
||||
(let [bs (lib-hidden-sets bl) ms (lib-hidden-sets ml) os (lib-hidden-sets ol)
|
||||
take! (fn [] (if-let [h (hidden-theme-map ol)]
|
||||
[{:type :set-token-theme :id ctob/hidden-theme-id :attrs h}]
|
||||
[]))]
|
||||
(cond
|
||||
(= os bs) []
|
||||
(= ms bs) (take!)
|
||||
(= os ms) []
|
||||
(= (get resolutions :active-sets) :branch) (take!)
|
||||
:else []))
|
||||
(if (and (= theme-ids main-theme-ids) (= active-ids main-set-ids))
|
||||
[]
|
||||
[{:type :set-tokens-status :theme-ids theme-ids :set-ids active-ids}])
|
||||
|
||||
;; set order: reorder main's common sets to branch's order using
|
||||
;; "move nᵢ before nᵢ₊₁" right-to-left. Emitted after renames so set
|
||||
@ -2031,4 +2029,4 @@
|
||||
colors typos media
|
||||
set-presence set-rename-changes set-order-changes
|
||||
token-vals themes
|
||||
active-changes active-sets-changes))})))
|
||||
active-changes))})))
|
||||
|
||||
@ -8,10 +8,12 @@
|
||||
(:require
|
||||
[app.common.files.branch-merge :as bm]
|
||||
[app.common.files.changes :as cfc]
|
||||
[app.common.files.tokens :as cfo]
|
||||
[app.common.types.page :as ctp]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.types.tokens-status :as ctos]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
@ -472,6 +474,11 @@
|
||||
[lib]
|
||||
(assoc (mkdata {}) :tokens-lib lib))
|
||||
|
||||
(defn- with-status
|
||||
"`with-tokens`, carrying the tokens status `f` builds from an empty one."
|
||||
[lib f]
|
||||
(assoc (with-tokens lib) :tokens-status (f (ctos/make-tokens-status))))
|
||||
|
||||
(t/deftest compute-changes-token-value-roundtrip
|
||||
(let [sid (uuid/next)
|
||||
tid (uuid/next)
|
||||
@ -751,37 +758,62 @@
|
||||
(t/is (= ["c" "a" "b"] order)))))
|
||||
|
||||
(t/deftest compute-changes-token-active-sets
|
||||
(let [sid (uuid/next)
|
||||
tid (uuid/next)
|
||||
base-lib (token-lib sid tid "#ff0000")
|
||||
;; branch toggles the "core" set active in the hidden theme
|
||||
branch-lib (ctob/toggle-set-in-theme base-lib ctob/hidden-theme-id "core")
|
||||
{:keys [changes unsupported]} (bm/compute-changes (with-tokens base-lib)
|
||||
(with-tokens base-lib)
|
||||
(with-tokens branch-lib))
|
||||
ch (first (filter #(= :set-token-theme (:type %)) changes))
|
||||
hidden-sets (fn [lib] (set (:sets (ctob/get-theme lib ctob/hidden-theme-id))))]
|
||||
(let [sid (uuid/next)
|
||||
tid (uuid/next)
|
||||
lib (token-lib sid tid "#ff0000")
|
||||
base (with-status lib identity)
|
||||
;; branch activates the "core" set directly
|
||||
branch (with-status lib #(cfo/activate-set % lib sid))
|
||||
{:keys [changes unsupported]} (bm/compute-changes base base branch)
|
||||
ch (first (filter #(= :set-tokens-status (:type %)) changes))]
|
||||
(t/is (empty? unsupported))
|
||||
(t/is (= ctob/hidden-theme-id (:id ch)))
|
||||
;; round-trip: main's hidden theme active sets match the branch
|
||||
(let [data' (cfc/process-changes {:tokens-lib base-lib} changes)]
|
||||
(t/is (= (hidden-sets branch-lib) (hidden-sets (:tokens-lib data')))))))
|
||||
(t/is (= #{sid} (:set-ids ch)))
|
||||
;; round-trip: main's active sets match the branch
|
||||
(let [data' (cfc/process-changes base changes)]
|
||||
(t/is (= #{sid} (ctos/get-active-set-ids (:tokens-status data')))))))
|
||||
|
||||
(t/deftest compute-changes-token-active-themes
|
||||
(let [thid (uuid/next)
|
||||
base-lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-theme (ctob/make-token-theme {:id thid :name "Dark" :group ""})))
|
||||
branch-lib (ctob/activate-theme base-lib thid)
|
||||
{:keys [changes unsupported]} (bm/compute-changes (with-tokens base-lib)
|
||||
(with-tokens base-lib)
|
||||
(with-tokens branch-lib))
|
||||
ch (first (filter #(= :set-active-token-themes (:type %)) changes))]
|
||||
(let [sid (uuid/next)
|
||||
tid (uuid/next)
|
||||
thid (uuid/next)
|
||||
lib (-> (token-lib sid tid "#ff0000")
|
||||
(ctob/add-theme (ctob/make-token-theme {:id thid :name "Dark" :group "" :sets #{"core"}})))
|
||||
base (with-status lib identity)
|
||||
branch (with-status lib #(cfo/activate-theme % lib thid))
|
||||
{:keys [changes unsupported]} (bm/compute-changes base base branch)
|
||||
ch (first (filter #(= :set-tokens-status (:type %)) changes))]
|
||||
(t/is (empty? unsupported))
|
||||
(t/is (some? ch))
|
||||
;; round-trip: active theme paths match the branch
|
||||
(let [data' (cfc/process-changes {:tokens-lib base-lib} changes)]
|
||||
(t/is (= (set (ctob/get-active-theme-paths branch-lib))
|
||||
(set (ctob/get-active-theme-paths (:tokens-lib data'))))))))
|
||||
(t/is (= #{thid} (:theme-ids ch)))
|
||||
(t/is (= #{sid} (:set-ids ch)) "activating a theme activates its sets")
|
||||
;; round-trip: main's tokens status matches the branch
|
||||
(let [status (:tokens-status (cfc/process-changes base changes))]
|
||||
(t/is (= #{thid} (ctos/get-active-theme-ids status)))
|
||||
(t/is (= #{sid} (ctos/get-active-set-ids status))))))
|
||||
|
||||
(t/deftest token-activation-conflict-settles-by-resolution
|
||||
;; Both sides activate a different theme of one group, so both halves of
|
||||
;; the status diverge. Main's status stands until a resolution takes the
|
||||
;; branch's.
|
||||
(let [sid-a (uuid/next)
|
||||
sid-b (uuid/next)
|
||||
th-a (uuid/next)
|
||||
th-b (uuid/next)
|
||||
lib (-> (ctob/make-tokens-lib)
|
||||
(ctob/add-set (ctob/make-token-set {:id sid-a :name "a"}))
|
||||
(ctob/add-set (ctob/make-token-set {:id sid-b :name "b"}))
|
||||
(ctob/add-theme (ctob/make-token-theme {:id th-a :name "A" :group "" :sets #{"a"}}))
|
||||
(ctob/add-theme (ctob/make-token-theme {:id th-b :name "B" :group "" :sets #{"b"}})))
|
||||
base (with-status lib identity)
|
||||
main (with-status lib #(cfo/activate-theme % lib th-a))
|
||||
branch (with-status lib #(cfo/activate-theme % lib th-b))
|
||||
status-changes (fn [resolutions]
|
||||
(filterv #(= :set-tokens-status (:type %))
|
||||
(:changes (bm/compute-changes base main branch resolutions))))]
|
||||
(t/is (= #{:token-active-themes :token-active-sets}
|
||||
(set (map :kind (:conflicts (bm/compute-merge base main branch :branch->main))))))
|
||||
(t/is (= [] (status-changes {})) "an unresolved conflict keeps main's status")
|
||||
(t/is (= [{:type :set-tokens-status :theme-ids #{th-b} :set-ids #{sid-b}}]
|
||||
(status-changes {:active-themes :branch :active-sets :branch})))))
|
||||
|
||||
;;; --- Regression tests: classification on stripped shapes ---
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user