From 970948159ec96a41faf6a0c48ca2cc4612e4c152 Mon Sep 17 00:00:00 2001 From: Shreyash Agare Date: Thu, 1 Oct 2026 13:43:42 +0530 Subject: [PATCH] :bug: Fix token display for multi-selected text layers (#11944) * :bug: Fix token display for multi-selected text layers When multiple text layers share the same fill token, the design panel showed the hex value instead of the token name. type->token-attrs derived token keys from type->editable-attrs, which returns empty for text shapes in the fill group. Fall back to the group's own attrs when editable-attrs is empty. Closes #11924 AI-assisted-by: claude-opus-4-6 * :bug: Take text token attrs from the group attrs The :text read mode reads values from the group attrs, so its token attrs now come from those attrs too, instead of falling back when the editable attrs are empty. type->token-attrs is restored to its original form. Add regression tests for fill tokens on multiple selections of text shapes, and of a rect mixed with a text. AI-assisted-by: claude-opus-5-5 --------- Co-authored-by: Shreyash Agare <264953665+ShreyashAgare26@users.noreply.github.com> Co-authored-by: Andrey Antukh --- .../sidebar/options/shapes/multiple.cljs | 12 ++++-- .../ui/layout_container_multiple_test.cljs | 38 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index a5ed1ce76b..8c4cad66b7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -283,6 +283,11 @@ (into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct)) (type->editable-attrs type)))) + ;; The `:text` read mode reads values from `attrs`, not from the + ;; editable attrs, so its token attrs must come from `attrs` too. + text-token-attrs + (into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct)) attrs) + merge-attrs (fn [v1 v2] (cond @@ -311,8 +316,9 @@ merge-token-values (fn [acc token-attrs applied-tokens] - "Merges token values across all token attributes derived from the shape's - editable attributes." + "Merges token values across all `token-attrs`. Callers derive them from + the attributes the shape's read mode reads: the editable attributes for + `:shape`, the whole group attributes for `:text`." (let [no-tokens? (empty? applied-tokens) stable (deref stable-token-acc)] (if (and no-tokens? @@ -360,7 +366,7 @@ (merge-attrs shape-attrs) (merge-attrs content-attrs)) - new-token-acc (merge-token-values token-acc (type->token-attrs type) applied-tokens)] + new-token-acc (merge-token-values token-acc text-token-attrs applied-tokens)] [(conj ids id) new-values new-token-acc]) diff --git a/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs b/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs index 10b64d95b1..77bc263fa3 100644 --- a/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs +++ b/frontend/test/frontend_tests/ui/layout_container_multiple_test.cljs @@ -44,3 +44,41 @@ (t/is (= {:p1 10 :p2 20 :p3 10 :p4 20} (:layout-padding values))) (t/is (= :multiple (:layout-padding-type values))))) + +(defn- fill-tokens + [shapes] + (let [[_ids _values tokens] (multiple/get-attrs* shapes {} :fill)] + tokens)) + +(defn- text-shape + [id applied-tokens] + (cond-> {:id id :type :text :fills []} + applied-tokens (assoc :applied-tokens applied-tokens))) + +(defn- rect-shape + [id applied-tokens] + (cond-> {:id id :type :rect :fills []} + applied-tokens (assoc :applied-tokens applied-tokens))) + +(t/deftest multiple-text-selection-same-fill-token + (t/is (= {:fill "color.primary"} + (fill-tokens [(text-shape :text-1 {:fill "color.primary"}) + (text-shape :text-2 {:fill "color.primary"})])))) + +(t/deftest multiple-text-selection-different-fill-tokens + (t/is (= {:fill :multiple} + (fill-tokens [(text-shape :text-1 {:fill "color.primary"}) + (text-shape :text-2 {:fill "color.secondary"})])))) + +(t/deftest multiple-text-selection-token-and-no-token + (t/is (= {:fill :multiple} + (fill-tokens [(text-shape :text-1 {:fill "color.primary"}) + (text-shape :text-2 nil)])))) + +(t/deftest multiple-selection-rect-and-text-fill-tokens + (t/is (= {:fill "color.primary"} + (fill-tokens [(rect-shape :rect-1 {:fill "color.primary"}) + (text-shape :text-1 {:fill "color.primary"})]))) + (t/is (= {:fill :multiple} + (fill-tokens [(rect-shape :rect-1 {:fill "color.primary"}) + (text-shape :text-1 {:fill "color.secondary"})]))))