🐛 Fix token display for multi-selected text layers (#11944)

* 🐛 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

* 🐛 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 <niwi@niwi.nz>
This commit is contained in:
Shreyash Agare 2026-10-01 13:43:42 +05:30 committed by GitHub
parent 8b2ec216e4
commit 970948159e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 47 additions and 3 deletions

View File

@ -283,6 +283,11 @@
(into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct)) (into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct))
(type->editable-attrs type)))) (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 merge-attrs
(fn [v1 v2] (fn [v1 v2]
(cond (cond
@ -311,8 +316,9 @@
merge-token-values merge-token-values
(fn [acc token-attrs applied-tokens] (fn [acc token-attrs applied-tokens]
"Merges token values across all token attributes derived from the shape's "Merges token values across all `token-attrs`. Callers derive them from
editable attributes." 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) (let [no-tokens? (empty? applied-tokens)
stable (deref stable-token-acc)] stable (deref stable-token-acc)]
(if (and no-tokens? (if (and no-tokens?
@ -360,7 +366,7 @@
(merge-attrs shape-attrs) (merge-attrs shape-attrs)
(merge-attrs content-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) [(conj ids id)
new-values new-values
new-token-acc]) new-token-acc])

View File

@ -44,3 +44,41 @@
(t/is (= {:p1 10 :p2 20 :p3 10 :p4 20} (t/is (= {:p1 10 :p2 20 :p3 10 :p4 20}
(:layout-padding values))) (:layout-padding values)))
(t/is (= :multiple (:layout-padding-type 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"})]))))