🎉 Add binary fills integration

This commit is contained in:
Andrey Antukh
2025-07-09 17:02:23 +02:00
parent 9ee488009f
commit 8bb210e7b6
25 changed files with 210 additions and 147 deletions

View File

@@ -356,7 +356,7 @@
(first children)
(last children))
fills (if (and (contains? head :svg-attrs) (empty? (:fills head)))
types.path/default-bool-fills
(types.path/get-default-bool-fills)
(get head :fills))]
(-> bool-shape
(assoc :fills fills)

View File

@@ -83,20 +83,12 @@
:else
(ex/raise :type :internal
:code :invalid-type
:hint (str "cannot coerce " (pr-str o) "to fills"))))
:hint (str "cannot coerce " (pr-str o) " to fills"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HELPERS
;; TYPE HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; FIXME: duplicated of `assoc`
(defn assoc-fill
[fills position fill]
(if (nil? fills)
(impl/from-plain [fill])
(-> (coerce fills)
(c/assoc position fill))))
(defn get-image-ids
[fills]
(if (vector? fills)
@@ -136,7 +128,7 @@
fills (apply f fills args)]
(if (contains? flags/*current* :frontend-binary-fills)
(impl/from-plain fills)
fills)))
(vec fills))))
(defn create
[& elements]

View File

@@ -378,7 +378,26 @@
(when (< i size)
(cons (read-fill dbuffer mbuffer i)
(lazy-seq (next-seq (inc i))))))
0)))))
0)))
cljs.core/IPrintWithWriter
(-pr-writer [this writer _]
(binding [*print-dup* true]
(cljs.core/-write writer (str "#penpot/fills \"" (pr-str (vec this)) "\""))))))
#?(:clj
(defmethod print-method Fills
[o ^java.io.Writer writer]
(.write writer "#penpot/fills \"")
(print-dup (vec o) writer)
(.write writer "\"")))
#?(:clj
(defmethod print-dup Fills
[o ^java.io.Writer writer]
(.write writer "#penpot/fills \"")
(print-dup (vec o) writer)
(.write writer "\"")))
(defn from-plain
[fills]

View File

@@ -26,7 +26,9 @@
(def ^:cosnt bool-group-style-properties bool/group-style-properties)
(def ^:const bool-style-properties bool/style-properties)
(def ^:const default-bool-fills bool/default-fills)
(defn get-default-bool-fills
[]
(bool/get-default-fills))
(def schema:content impl/schema:content)
(def schema:segments impl/schema:segments)

View File

@@ -7,16 +7,22 @@
(ns app.common.types.path.bool
(:require
[app.common.data :as d]
[app.common.flags :as flags]
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.math :as mth]
[app.common.types.color :as clr]
[app.common.types.fills :as types.fills]
[app.common.types.path.helpers :as helpers]
[app.common.types.path.segment :as segment]
[app.common.types.path.subpath :as subpath]))
(def default-fills
[{:fill-color clr/black}])
(defn get-default-fills
[]
(let [fills [{:fill-color clr/black}]]
(if (contains? flags/*current* :frontend-binary-fills)
(types.fills/from-plain fills)
fills)))
(def group-style-properties
#{:shadow :blur})

View File

@@ -415,12 +415,14 @@
;; Valid attributes
(def ^:private allowed-shape-attrs #{:page-id :component-id :component-file :component-root :main-instance
:remote-synced :shape-ref :touched :blocked :collapsed :locked
:hidden :masked-group :fills :proportion :proportion-lock :constraints-h
:constraints-v :fixed-scroll :r1 :r2 :r3 :r4 :opacity :grids :exports
:strokes :blend-mode :interactions :shadow :blur :grow-type :applied-tokens
:plugin-data})
(def ^:private allowed-shape-attrs
#{:page-id :component-id :component-file :component-root :main-instance
:remote-synced :shape-ref :touched :blocked :collapsed :locked
:hidden :masked-group :fills :proportion :proportion-lock :constraints-h
:constraints-v :fixed-scroll :r1 :r2 :r3 :r4 :opacity :grids :exports
:strokes :blend-mode :interactions :shadow :blur :grow-type :applied-tokens
:plugin-data})
(def ^:private allowed-shape-geom-attrs #{:x :y :width :height})
(def ^:private allowed-shape-base-attrs #{:id :name :type :selrect :points :transform :transform-inverse :parent-id :frame-id})
(def ^:private allowed-bool-attrs #{:shapes :bool-type :content})

View File

@@ -8,7 +8,9 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.flags :as flags]
[app.common.types.color :as clr]
[app.common.types.fills :as types.fills]
[clojure.set :as set]
[clojure.walk :as walk]
[cuerdas.core :as str]))
@@ -84,10 +86,12 @@
(def default-root-attrs
{:vertical-align "top"})
(def default-text-fills
[{:fill-color clr/black
:fill-opacity 1}])
(def default-text-attrs
{:typography-ref-file nil
:typography-ref-id nil
:font-id "sourcesanspro"
{:font-id "sourcesanspro"
:font-family "sourcesanspro"
:font-variant-id "regular"
:font-size "14"
@@ -98,12 +102,22 @@
:text-transform "none"
:text-align "left"
:text-decoration "none"
:text-direction "ltr"
:fills [{:fill-color clr/black
:fill-opacity 1}]})
:text-direction "ltr"})
(def default-attrs
(merge default-root-attrs default-text-attrs))
(defn get-default-text-fills
"Return calculated default text fills"
[]
(if (contains? flags/*current* :frontend-binary-fills)
(types.fills/from-plain default-text-fills)
default-text-fills))
(defn get-default-text-attrs
"Return calculated default text attrs.
NOTE: is implemented as function because it needs resolve at runtime
the activated flag for properly encode the fills"
[]
(assoc default-text-attrs :fills (get-default-text-fills)))
(def typography-fields
[:font-id
@@ -117,9 +131,9 @@
:text-transform])
(def default-typography
(merge
{:name "Source Sans Pro Regular"}
(select-keys default-text-attrs typography-fields)))
(-> default-text-attrs
(select-keys typography-fields)
(assoc :name "Source Sans Pro Regular")))
(defn node-seq
([root] (node-seq identity root))

View File

@@ -78,6 +78,23 @@
(filter selectable?)
selected)))))
(defn split-text-shapes
"Split text shapes from non-text shapes"
[objects ids]
(loop [ids (seq ids)
text-ids []
shape-ids []]
(if-let [id (first ids)]
(let [shape (get objects id)]
(if (cfh/text-shape? shape)
(recur (rest ids)
(conj text-ids id)
shape-ids)
(recur (rest ids)
text-ids
(conj shape-ids id))))
[text-ids shape-ids])))
;; DEPRECATED
(defn lookup-selected-raw
[state]

View File

@@ -36,7 +36,7 @@
head
(cond-> head
(and (contains? head :svg-attrs) (empty? (:fills head)))
(assoc :fills path/default-bool-fills))
(assoc :fills (path/get-default-bool-fills)))
shape
{:id shape-id
@@ -62,7 +62,7 @@
head (if (= type :difference) (first shapes) (last shapes))
head (cond-> head
(and (contains? head :svg-attrs) (empty? (:fills head)))
(assoc :fills path/default-bool-fills))]
(assoc :fills (path/get-default-bool-fills)))]
(-> group
(assoc :type :bool)
(assoc :bool-type type)

View File

@@ -925,7 +925,7 @@
(let [paragraphs (->> (str/lines text)
(map str/trim)
(mapv #(hash-map :type "paragraph"
:children [(merge txt/default-text-attrs {:text %})])))]
:children [(merge (txt/get-default-text-attrs) {:text %})])))]
;; if text is composed only by line breaks paragraphs is an empty list and should be nil
(when (d/not-empty? paragraphs)
{:type "root"

View File

@@ -18,7 +18,6 @@
[app.common.types.text :as txt]
[app.config :as cfg]
[app.main.broadcast :as mbc]
[app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
[app.main.data.modal :as md]
[app.main.data.workspace.layout :as layout]
@@ -103,11 +102,7 @@
(defn assoc-shape-fill
[shape position fill]
(update shape :fills
(fn [fills]
(if (nil? fills)
[fill]
(assoc fills position fill)))))
(update shape :fills types.fills/assoc position fill))
(defn transform-fill*
"A lower-level companion function for `transform-fill`"
@@ -167,17 +162,28 @@
(assoc-in [attr index] second)
(assoc-in [attr new-index] first))))
(defn- swap-fills-index
[fills index new-index]
(let [first (get fills index)
second (get fills new-index)]
(-> fills
(assoc index second)
(assoc new-index first))))
(defn reorder-fills
[ids index new-index]
(ptk/reify ::reorder-fills
ptk/WatchEvent
(watch [_ state _]
(let [objects (dsh/lookup-page-objects state)
(let [objects
(dsh/lookup-page-objects state)
is-text? #(= :text (:type (get objects %)))
text-ids (filter is-text? ids)
shape-ids (remove is-text? ids)
transform-attrs #(swap-attrs % :fills index new-index)]
[text-ids shape-ids]
(dsh/split-text-shapes objects ids)
transform-attrs
(fn [object]
(update object :fills types.fills/update swap-fills-index index new-index))]
(rx/concat
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
@@ -207,7 +213,7 @@
(ptk/reify ::change-fill-and-clear
ptk/WatchEvent
(watch [_ state _]
(let [change-fn (fn [shape attrs] (assoc shape :fills [attrs]))
(let [change-fn (fn [shape attrs] (assoc shape :fills (types.fills/create attrs)))
undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
@@ -225,8 +231,7 @@
(watch [_ state _]
(let [change-fn
(fn [shape attrs]
(-> shape
(update :fills #(into [attrs] %))))
(update shape :fills types.fills/prepend attrs))
undo-id
(js/Symbol)]
(rx/concat
@@ -247,13 +252,13 @@
ptk/WatchEvent
(watch [_ state _]
(let [detach-fn
(fn [values index]
(update values index dissoc :fill-color-ref-id :fill-color-ref-file))
(fn [fills index]
(update fills index dissoc :fill-color-ref-id :fill-color-ref-file))
change-fn
;; The `node` can be a shape or a text content node
(fn [node]
(update node :fills detach-fn position))
(update node :fills types.fills/update detach-fn position))
undo-id
(js/Symbol)]
@@ -275,17 +280,17 @@
ptk/WatchEvent
(watch [_ state _]
(let [remove-fill-by-index
(fn [values index]
(fn [fills index]
(into []
(comp
(map-indexed (fn [i o] (when (not= i index) o)))
(filter some?))
values))
fills))
change-fn
;; The `node` can be a shape or a text content node
(fn [node]
(update node :fills remove-fill-by-index position))
(update node :fills types.fills/update remove-fill-by-index position))
undo-id
(js/Symbol)]
@@ -303,7 +308,7 @@
(ptk/reify ::remove-all-fills
ptk/WatchEvent
(watch [_ state _]
(let [change-fn (fn [node] (assoc node :fills []))
(let [change-fn (fn [node] (assoc node :fills (types.fills/create)))
undo-id (js/Symbol)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
@@ -576,16 +581,26 @@
:fill-color-ref-file (:ref-file color)
:fill-color-gradient (:gradient color)}))
(defn change-text-color
(defn- change-text-color
[old-color new-color index node]
(let [fills (map #(dissoc % :fill-color-ref-id :fill-color-ref-file) (:fills node))
parsed-color (-> (color-att->text old-color)
(dissoc :fill-color-ref-id :fill-color-ref-file))
parsed-new-color (color-att->text new-color)
has-color? (d/index-of fills parsed-color)]
(cond-> node
(some? has-color?)
(assoc-in [:fills index] parsed-new-color))))
(update node :fills types.fills/update
(fn [fills]
(let [fills'
(map #(dissoc % :fill-color-ref-id :fill-color-ref-file) fills)
parsed-color
(-> (color-att->text old-color)
(dissoc :fill-color-ref-id :fill-color-ref-file))
parsed-new-color
(color-att->text new-color)
has-color?
(d/index-of fills' parsed-color)]
(cond-> fills
(some? has-color?)
(assoc index parsed-new-color))))))
(def ^:private schema:change-color-operation
[:map
@@ -1114,39 +1129,6 @@
(assoc :type :image)
(dissoc :editing-stop :stops :gradient)))))))
(defn select-color
[position add-color]
;; FIXME: revisit
(ptk/reify ::select-color
ptk/WatchEvent
(watch [_ state _]
(let [selected (dsh/lookup-selected state)
shapes (dsh/lookup-shapes state selected)
shape (first shapes)
fills (if (cfh/text-shape? shape)
(:fills (dwt/current-text-values
{:editor-state (dm/get-in state [:workspace-editor-state (:id shape)])
:shape shape
:attrs (conj txt/text-fill-attrs :fills)}))
(:fills shape))
fill (first fills)
single? (and (= 1 (count selected))
(= 1 (count fills)))
data (if single?
(d/without-nils {:color (:fill-color fill)
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)})
{:color "#406280"
:opacity 1})]
(rx/of (md/show :colorpicker
{:x (:x position)
:y (:y position)
:on-accept add-color
:data data
:position :right})
(ptk/event ::ev/event {::ev/name "add-asset-to-library"
:asset-type "color"}))))))
(defn- stroke->color-att
[stroke file-id libraries]
(let [ref-file (:stroke-color-ref-file stroke)

View File

@@ -16,6 +16,7 @@
[app.common.media :as media]
[app.common.schema :as sm]
[app.common.types.container :as ctn]
[app.common.types.fills :as types.fills]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -63,18 +64,22 @@
ptk/WatchEvent
(watch [_ _ _]
(let [{:keys [name width height id mtype]} image
fills (types.fills/create
{:fill-opacity 1
:fill-image {:width width
:height height
:mtype mtype
:id id
:keep-aspect-ratio true}})
shape {:name name
:width width
:height height
:x (mth/round (- x (/ width 2)))
:y (mth/round (- y (/ height 2)))
:fills [{:fill-opacity 1
:fill-image {:name name
:width width
:height height
:mtype mtype
:id id
:keep-aspect-ratio true}}]}]
:fills fills}]
(rx/of (dwsh/create-and-add-shape :rect x y shape))))))
(defn svg-uploaded
@@ -299,7 +304,7 @@
:name (:name root-svg-shape)
:frame-id uuid/zero
:parent-id uuid/zero
:fills []})
:fills (types.fills/create)})
root-svg-shape
(-> root-svg-shape
@@ -335,19 +340,21 @@
:frame-id uuid/zero
:parent-id uuid/zero})
img-fills (types.fills/create
{:fill-opacity 1
:fill-image {:id id
:width width
:height height
:mtype mtype
:keep-aspect-ratio true}})
img-shape (cts/setup-shape
{:type :rect
:x (:x pos)
:y (:y pos)
:width width
:height height
:fills [{:fill-opacity 1
:fill-image {:name name
:id id
:width width
:height height
:mtype mtype
:keep-aspect-ratio true}}]
:fills img-fills
:name name
:frame-id (:id frame-shape)
:parent-id (:id frame-shape)})]

View File

@@ -196,8 +196,8 @@
ptk/UpdateEvent
(update [_ state]
(let [text-state (some->> content ted/import-content)
attrs (d/merge txt/default-text-attrs
(get-in state [:workspace-global :default-font]))
attrs (merge (txt/get-default-text-attrs)
(get-in state [:workspace-global :default-font]))
editor (cond-> (ted/create-editor-state text-state decorator)
(and (nil? content) (some? attrs))
(ted/update-editor-current-block-data attrs))]
@@ -237,7 +237,9 @@
(defn- to-new-fills
[data]
[(d/without-nils (select-keys data types.fills/fill-attrs))])
;; FIXME: maybe export this as a specific helper ?
(types.fills/create
(d/without-nils (select-keys data types.fills/fill-attrs))))
(defn- shape-current-values
[shape pred attrs]
@@ -245,7 +247,10 @@
nodes (->> (txt/node-seq pred root)
(map (fn [node]
(if (txt/is-text-node? node)
(let [fills
(let [default-text-attrs
(txt/get-default-text-attrs)
fills
(cond
(types.fills/has-valid-fill-attrs? node)
(to-new-fills node)
@@ -254,8 +259,9 @@
(:fills node)
:else
(:fills txt/default-text-attrs))]
(-> (merge txt/default-text-attrs node)
(:fills default-text-attrs))]
(-> (merge default-text-attrs node)
(assoc :fills fills)))
node))))]
(attrs/get-attrs-multi nodes attrs)))
@@ -290,7 +296,9 @@
[{:keys [editor-state attrs]}]
(let [result (-> (ted/get-editor-current-inline-styles editor-state)
(select-keys attrs))
result (if (empty? result) txt/default-text-attrs result)]
result (if (empty? result)
(txt/get-default-text-attrs)
result)]
result))
(defn current-text-values
@@ -469,21 +477,21 @@
(let [color-attrs (not-empty (select-keys node types.fills/fill-attrs))]
(cond-> node
(nil? (:fills node))
(assoc :fills [])
(assoc :fills (types.fills/create))
;; Migrate old colors and remove the old fromat
color-attrs
(-> (dissoc :fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient)
(update :fills conj color-attrs))
(update :fills types.fills/update conj color-attrs))
;; We don't have the fills attribute. It's an old text without color
;; so need to be black
(and (nil? (:fills node)) (empty? color-attrs))
(assoc :fills (:fills txt/default-text-attrs))
(assoc :fills (txt/get-default-text-fills))
;; Remove duplicates from the fills
:always
(update :fills (comp vec distinct)))))
(update :fills types.fills/update distinct))))
(defn migrate-content
[content]
@@ -905,7 +913,7 @@
(ptk/reify ::v2-update-text-editor-styles
ptk/UpdateEvent
(update [_ state]
(let [merged-styles (merge txt/default-text-attrs
(let [merged-styles (merge (txt/get-default-text-attrs)
(get-in state [:workspace-global :default-font])
new-styles)]
(update-in state [:workspace-v2-editor-state id] (fnil merge {}) merged-styles)))))

View File

@@ -295,7 +295,7 @@
(let [current-font
(if (some? font-id)
(select-keys node [:font-id :font-variant-id])
(select-keys txt/default-text-attrs [:font-id :font-variant-id]))]
(select-keys txt/default-typography [:font-id :font-variant-id]))]
(conj result current-font)))
#{})))

View File

@@ -174,7 +174,7 @@
(let [style-text-blocks (->> (:content shape)
(txt/content->text+styles)
(remove (fn [[_ text]] (str/empty? (str/trim text))))
(mapv (fn [[style text]] (vector (merge txt/default-text-attrs style) text))))]
(mapv (fn [[style text]] (vector (merge (txt/get-default-text-attrs) style) text))))]
(for [[idx [full-style text]] (map-indexed vector style-text-blocks)]
[:& typography-block {:key idx

View File

@@ -17,7 +17,7 @@
(defn- load-fonts!
[content]
(let [extract-fn (juxt :font-id :font-variant-id)
default (extract-fn txt/default-text-attrs)]
default (extract-fn txt/default-typography)]
(->> (tree-seq map? :children content)
(into #{default} (keep extract-fn))
(run! (fn [[font-id variant-id]]

View File

@@ -53,11 +53,11 @@
line-height
(if (and (some? line-height) (not= "" line-height))
line-height
(:line-height txt/default-text-attrs))
(:line-height txt/default-typography))
text-align (:text-align data "start")
base #js {;; Fix a problem when exporting HTML
:fontSize 0 ;;(str (:font-size data (:font-size txt/default-text-attrs)) "px")
:fontSize 0
:lineHeight line-height
:margin 0}]
@@ -75,7 +75,7 @@
text-transform (:text-transform data)
font-id (or (:font-id data)
(:font-id txt/default-text-attrs))
(:font-id txt/default-typography))
font-variant-id (:font-variant-id data)

View File

@@ -41,7 +41,7 @@
(defn- get-fonts
[content]
(let [extract-fn (juxt :font-id :font-variant-id)
default (extract-fn txt/default-text-attrs)]
default (extract-fn txt/default-typography)]
(->> (tree-seq map? :children content)
(into #{default} (keep extract-fn)))))
@@ -67,7 +67,7 @@
style-defaults
(styles/get-style-defaults
(merge txt/default-text-attrs txt/default-root-attrs default-font))
(merge (txt/get-default-text-attrs) txt/default-root-attrs default-font))
options
#js {:styleDefaults style-defaults

View File

@@ -217,7 +217,7 @@
(mf/use-fn
(mf/deps values)
(fn [ids attrs]
(st/emit! (dwt/save-font (-> (merge txt/default-text-attrs values attrs)
(st/emit! (dwt/save-font (-> (merge (txt/get-default-text-attrs) values attrs)
(select-keys txt/text-node-attrs)))
(dwt/update-all-attrs ids attrs))))

View File

@@ -235,9 +235,9 @@
[{:keys [values on-change on-blur show-recent full-size-selector]}]
(let [{:keys [font-id font-size font-variant-id]} values
font-id (or font-id (:font-id txt/default-text-attrs))
font-size (or font-size (:font-size txt/default-text-attrs))
font-variant-id (or font-variant-id (:font-variant-id txt/default-text-attrs))
font-id (or font-id (:font-id txt/default-typography))
font-size (or font-size (:font-size txt/default-typography))
font-variant-id (or font-variant-id (:font-variant-id txt/default-typography))
fonts (mf/deref fonts/fontsdb)
font (get fonts font-id)

View File

@@ -203,6 +203,9 @@
[shapes objects attr-group]
(let [attrs (group->attrs attr-group)
default-text-attrs
(txt/get-default-text-attrs)
merge-attrs
(fn [v1 v2]
(cond
@@ -233,7 +236,7 @@
(let [shape-attrs (select-keys shape attrs)
content-attrs
(attrs/get-text-attrs-multi shape txt/default-text-attrs attrs)
(attrs/get-text-attrs-multi shape default-text-attrs attrs)
new-values
(-> values

View File

@@ -73,12 +73,12 @@
;; it affects to the height calculation the browser does
font-size (if (some #(not= "" (:text %)) (:children paragraph))
"0"
(:font-size styles (:font-size txt/default-text-attrs)))
(:font-size styles (:font-size txt/default-typography)))
line-height (:line-height styles)
line-height (if (and (some? line-height) (not= "" line-height))
line-height
(:line-height txt/default-text-attrs))]
(:line-height txt/default-typography))]
(-> styles
(assoc :font-size font-size :line-height line-height))))

View File

@@ -12,6 +12,7 @@
[app.common.files.validate :as cfv]
[app.common.json :as json]
[app.common.logging :as l]
[app.common.pprint :as pp]
[app.common.transit :as t]
[app.common.types.file :as ctf]
[app.common.uuid :as uuid]
@@ -197,6 +198,14 @@
(logjs "selected" result)
nil))
(defn ^:export dump-selected-edn
[]
(let [objects (dsh/lookup-page-objects @st/state)
result (->> (get-selected @st/state) (map #(get objects %)))]
(pp/pprint result {:length 30 :level 30})
nil))
(defn ^:export preview-selected
[]
(st/emit! (dp/open-preview-selected)))

View File

@@ -412,7 +412,7 @@
(t/is (= (get c-frame1' :opacity) 0.9))
(t/is (= (get-in c-frame1' [:strokes 0 :stroke-width]) 8))
(t/is (= (get-in c-frame1' [:strokes 0 :stroke-color]) "#ff0000"))
(t/is (= (get-in c-frame1' [:fills 0 :fill-color]) "#ff0000"))
(t/is (= (-> c-frame1' :fills (nth 0) :fill-color) "#ff0000"))
(t/is (mth/close? (get c-frame1' :width) 200))
(t/is (mth/close? (get c-frame1' :height) 200))

View File

@@ -219,19 +219,21 @@
secondary-target (toht/get-token file' "color.secondary")
rect-1' (cths/get-shape file' :rect-1)
rect-2' (cths/get-shape file' :rect-2)]
(t/testing "regular color"
(t/is (some? (:applied-tokens rect-1')))
(t/is (= (:fill (:applied-tokens rect-1')) (:name primary-target)))
(t/is (= (get-in rect-1' [:fills 0 :fill-color]) "#ff0000"))
(t/is (= (-> rect-1' :fills (nth 0) :fill-color) "#ff0000"))
(t/is (= (:stroke-color (:applied-tokens rect-1')) (:name primary-target)))
(t/is (= (get-in rect-1' [:strokes 0 :stroke-color]) "#ff0000")))
(t/testing "color with alpha channel"
(t/is (some? (:applied-tokens rect-2')))
(t/is (= (:fill (:applied-tokens rect-2')) (:name secondary-target)))
(t/is (= (get-in rect-2' [:fills 0 :fill-color]) "#ff0000"))
(t/is (= (get-in rect-2' [:fills 0 :fill-opacity]) 0.5))
(let [fills (get rect-2' :fills)]
(t/is (= (-> fills (nth 0) :fill-color) "#ff0000"))
(t/is (= (-> fills (nth 0) :fill-opacity) 0.5)))
(t/is (= (:stroke-color (:applied-tokens rect-2')) (:name secondary-target)))
(t/is (= (get-in rect-2' [:strokes 0 :stroke-color]) "#ff0000"))
@@ -758,4 +760,4 @@
(t/is (nil? (:typography-ref-id paragraph-3)))
(t/is (nil? (:typography-ref-file paragraph-3)))
(t/is (nil? (:typography-ref-id text-node-3)))
(t/is (nil? (:typography-ref-file text-node-3))))))))))
(t/is (nil? (:typography-ref-file text-node-3))))))))))