🎉 Allow the user to choose the tokens-source of a file

This commit is contained in:
Andrés Moya 2026-07-23 11:51:13 +02:00
parent 76e60b177c
commit 209330a5c1
12 changed files with 391 additions and 42 deletions

View File

@ -1091,10 +1091,10 @@
(defn set-tokens-source
[changes library-id]
(assert-file-data! changes)
(let [file-data (::file-data (meta changes))
file-id (:id file-data)
prev-val (:tokens-source file-data)]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
file-id (:id library-data)
prev-val (:tokens-source library-data)]
(-> changes
(update :redo-changes conj {:type :set-tokens-source
:file-id file-id

View File

@ -463,13 +463,43 @@
(update :tokens-status #(or % (ctos/make-tokens-status)))))
(defn get-tokens-source
"Return the current tokens source of the file. When there is no explicit :tokens-source attribute,
the source is the file itself."
[file-data]
(:tokens-source file-data))
(or (:tokens-source file-data) (:id file-data)))
(defn set-tokens-source
[file-data tokens-source]
(assoc file-data :tokens-source tokens-source))
(defn tokens-source?
"Returns true if the given id is the current tokens source of the file-data.
When no tokens-source is set, the file's own id is considered the source if there are any tokens in it."
[file-data id]
(assert (uuid? id) "expected valid uuid")
(let [source (:tokens-source file-data)
tokens-lib (:tokens-lib file-data)
has-tokens? (and (some? tokens-lib)
(not (ctob/empty-lib? tokens-lib)))]
(if (= id (:id file-data))
(and has-tokens?
(or (nil? source) (= source id)))
(= source id))))
(defn tokens-provider?
"Returns true if the file MAY become a tokens source. This is if the file has tokens and has
not configured another tokens source."
[file-data]
(and (some? (:tokens-lib file-data))
(or (nil? (:tokens-source file-data))
(= (:tokens-source file-data) (:id file-data)))))
(defn editable-tokens?
"Returns true if the file-data owns its tokens (no external source or source is itself)."
[file-data]
(let [source (:tokens-source file-data)]
(or (nil? source) (= source (:id file-data)))))
(defn get-tokens-lib
[file-data]
(:tokens-lib file-data))
@ -656,7 +686,9 @@
(conj force-set-id))]
(reduce (fn [tokens set-id]
(let [set (ctob/get-set tokens-lib set-id)]
(merge tokens (ctob/get-tokens- set))))
(if set
(merge tokens (ctob/get-tokens- set))
tokens)))
(d/ordered-map)
ordered-active)))

View File

@ -14,7 +14,6 @@
[app.common.types.tokens-lib :as ctob]
[app.common.types.tokens-status :as ctos]
[app.common.uuid :as uuid]
[clojure.datafy :refer [datafy]]
[clojure.test :as t]))
;; Helper functions
@ -235,25 +234,25 @@
(t/is (ctos/tokens-status? (:tokens-status file-data')))))
(t/testing "ensure-tokens-lib should not add a tokens-lib if there is a tokens-source"
(let [tokens-source (uuid/next)
(let [source-id (uuid/next)
tokens-status (ctos/make-tokens-status)
file (-> (thf/sample-file :file1)
(assoc-in [:data :tokens-source] tokens-source)
(assoc-in [:data :tokens-source] source-id)
(assoc-in [:data :tokens-status] tokens-status))
file-data (ctf/file-data file)
file-data' (cfo/ensure-tokens-lib file-data)]
(t/is (not (contains? file-data' :tokens-lib)))
(t/is (= tokens-source (:tokens-source file-data')))
(t/is (= source-id (:tokens-source file-data')))
(t/is (= tokens-status (:tokens-status file-data')))))
(t/testing "ensure-tokens-lib should not add a tokens-lib if there is a tokens-source, but should add a tokens-status if it's missing"
(let [tokens-source (uuid/next)
(let [source-id (uuid/next)
file (-> (thf/sample-file :file1)
(assoc-in [:data :tokens-source] tokens-source))
(assoc-in [:data :tokens-source] source-id))
file-data (ctf/file-data file)
file-data' (cfo/ensure-tokens-lib file-data)]
(t/is (not (contains? file-data' :tokens-lib)))
(t/is (= tokens-source (:tokens-source file-data')))
(t/is (= source-id (:tokens-source file-data')))
(t/is (contains? file-data' :tokens-status))
(t/is (ctos/tokens-status? (:tokens-status file-data'))))))
@ -268,20 +267,224 @@
(t/deftest test-get-tokens-source
(t/testing "returns tokens-source from file data"
(let [file-id (thi/new-id! :tokens-source)
file-data {:tokens-source file-id}]
(t/is (= file-id (cfo/get-tokens-source file-data)))))
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(assoc-in [:data :tokens-source] source-id))]
(t/is (= source-id (cfo/get-tokens-source (:data file))))))
(t/testing "returns nil when no tokens-source"
(t/is (nil? (cfo/get-tokens-source {})))))
(t/testing "returns file id when no tokens-source"
(let [file (thf/sample-file :file1)]
(t/is (= (:id file) (cfo/get-tokens-source file))))))
(t/deftest test-set-tokens-source
(t/testing "sets tokens-source on file data"
(let [file-id (thi/new-id! :tokens-source)
file-data {:other :data}
file-data' (cfo/set-tokens-source file-data file-id)]
(t/is (= file-id (:tokens-source file-data')))
(t/is (= :data (:other file-data'))))))
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (:data file)
file-data' (cfo/set-tokens-source file-data source-id)]
(t/is (= source-id (:tokens-source file-data'))))))
(t/deftest test-tokens-source?
(t/testing "no tokens, no tokens-source, ask for file id -> false"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "empty tokens-lib, no tokens-source, ask for file id -> false"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "tokens, no tokens-source, ask for file id -> true"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (:data file)]
(t/is (true? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "no tokens, no tokens source, ask for other id -> false"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/testing "empty tokens-lib, no tokens source, ask for other id -> false"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/testing "tokens, no tokens source, ask for other id -> false"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (:data file)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
;; ---
(t/testing "no tokens, tokens source = file id, ask for file id -> false" ;; This case should not occur
(let [file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "empty tokens-lib, tokens source = file id, ask for file id -> false" ;; This case should not occur
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "tokens, tokens source = file id, ask for file id -> true"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (true? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "no tokens, tokens source = file id, ask for other id -> false" ;; This case should not occur
(let [file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/testing "empty tokens-lib, tokens source = file id, ask for other id -> false" ;; This case should not occur
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/testing "tokens, tokens source = file id, ask for other id -> false"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
;; ---
(t/testing "no tokens, tokens source = other id, ask for file id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "empty tokens-lib, tokens source = other id, ask for file id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "tokens, tokens source = other id, ask for file id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (:id file-data))))))
(t/testing "no tokens, tokens source = other id, ask for this same id -> true"
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (true? (cfo/tokens-source? file-data source-id)))))
(t/testing "empty tokens-lib, tokens source = other id, ask for this same id -> true"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (true? (cfo/tokens-source? file-data source-id)))))
(t/testing "tokens, tokens source = other id, ask for this same id -> true"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (true? (cfo/tokens-source? file-data source-id)))))
(t/testing "no tokens, tokens source = other id, ask for a different id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/testing "empty tokens-lib, tokens source = other id, ask for a different id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next))))))
(t/testing "tokens, tokens source = other id, ask for a different id -> false"
(let [source-id (thi/new-id! :tokens-source)
file (-> (thf/sample-file :file1)
(tht/add-tokens-lib)
(tht/update-tokens-lib
#(-> %
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a) :name "set-a"))
(ctob/add-token (thi/id :set-a) (ctob/make-token :id (thi/new-id! :tok1) :name "spacing" :type :spacing :value "8px")))))
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/tokens-source? file-data (uuid/next)))))))
(t/deftest test-tokens-provider?
(t/testing "returns false if it has no tokens"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (false? (cfo/tokens-provider? file-data)))))
(t/testing "returns true if it has tokens and no tokens-source"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (:data file)]
(t/is (true? (cfo/tokens-provider? file-data)))))
(t/testing "returns true if it has tokens and tokens-source is its own id"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (:id file))]
(t/is (true? (cfo/tokens-provider? file-data)))))
(t/testing "returns false if it has tokens and tokens-source is other id"
(let [file (-> (thf/sample-file :file1)
(tht/add-tokens-lib))
file-data (cfo/set-tokens-source (:data file) (uuid/next))]
(t/is (false? (cfo/tokens-provider? file-data))))))
(t/deftest test-editable-tokens?
(t/testing "returns true when file-data has no tokens-source"
(let [file (thf/sample-file :file1)
file-data (:data file)]
(t/is (true? (cfo/editable-tokens? file-data)))))
(t/testing "returns true when tokens-source is the file's own id"
(let [file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) (:id (:data file)))]
(t/is (true? (cfo/editable-tokens? file-data)))))
(t/testing "returns false when tokens-source is a different uuid"
(let [source-id (thi/new-id! :tokens-source)
file (thf/sample-file :file1)
file-data (cfo/set-tokens-source (:data file) source-id)]
(t/is (false? (cfo/editable-tokens? file-data))))))
(t/deftest test-get-tokens-status
(t/testing "returns tokens-status from file data"

View File

@ -38,10 +38,11 @@
([state file-id]
(dm/get-in state [:files file-id :data])))
;; TODO perhaps this is not necessary, lookup-tokens-lib should be enough
(defn lookup-tokens-source-data
[state]
(let [current-file-data (lookup-file-data state)
tokens-source-id (or (cfo/get-tokens-source current-file-data) (:id current-file-data))]
tokens-source-id (cfo/get-tokens-source current-file-data)]
(lookup-file-data state tokens-source-id)))
(defn lookup-tokens-lib

View File

@ -1628,10 +1628,6 @@
(map first)
set)]
(rx/concat
(when (contains? cf/flags :token-lib-sync)
(rx/of (dch/commit-changes (-> (pcb/empty-changes nil)
(pcb/with-file-data file-data)
(pcb/set-tokens-source library-id)))))
(->> (rp/cmd! :link-file-to-library {:file-id file-id :library-id library-id})
(rx/merge-map (fn [libraries-to-load]
(as-> libraries-to-load $

View File

@ -420,6 +420,17 @@
(rx/of (dch/commit-changes changes)
(dwtp/propagate-workspace-tokens))))))
(defn set-tokens-source
[library-id]
(ptk/reify ::set-tokens-source
ptk/WatchEvent
(watch [it state _]
(let [data (dsh/lookup-file-data state)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-tokens-source library-id))]
(rx/of (dch/commit-changes changes))))))
(defn delete-token-set
[id]
(ptk/reify ::delete-token-set

View File

@ -9,6 +9,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.tokens :as cfo]
[app.common.files.variant :as cfv]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
@ -24,6 +25,7 @@
[app.main.data.team :as dtm]
[app.main.data.workspace.colors :as mdc]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.refs :as refs]
[app.main.render :refer [component-svg]]
[app.main.store :as st]
@ -262,7 +264,7 @@
unlink-library
(mf/use-fn
(mf/deps file-id)
(mf/deps file-id local-library)
(fn [event]
(let [library-id (some-> (dom/get-current-target event)
(dom/get-data "library-id")
@ -270,7 +272,11 @@
(when (= library-id @selected)
(reset! selected :file))
(st/emit! (dwl/unlink-file-from-library file-id library-id)
(dwl/sync-file file-id library-id)))))
(dwl/sync-file file-id library-id))
;; When the unlinked library is the current tokens source,
;; we must reset it to the local library.
(when (cfo/tokens-source? local-library library-id)
(st/emit! (dwtl/set-tokens-source (:id local-library)))))))
import-tokens
(mf/use-fn
@ -283,6 +289,14 @@
:tokens/import-from-library {:file-id file-id
:library-id library-id})))))
set-as-tokens-source
(mf/use-fn
(fn [event]
(let [library-id (some-> (dom/get-current-target event)
(dom/get-data "library-id")
(uuid/parse))]
(st/emit! (dwtl/set-tokens-source library-id)))))
on-delete-accept
(mf/use-fn
(mf/deps file-id)
@ -335,7 +349,16 @@
[:div {:class (stl/css :item-content)}
[:div {:class (stl/css :item-title)} (tr "workspace.libraries.file-library")]
[:ul {:class (stl/css :item-contents)}
[:> library-description* {:summary summary}]]]
[:> library-description* {:summary summary}]]
(when (contains? cf/flags :token-lib-sync)
(if (cfo/tokens-source? local-library (:id local-library))
[:div (tr "workspace.libraries.tokens-source")]
(when (not= (cfo/get-tokens-source local-library) (:id local-library))
[:> button* {:variant "secondary"
:type "button"
:data-library-id (dm/str (:id local-library))
:on-click set-as-tokens-source}
(tr "workspace.libraries.set-as-tokens-source")])))]
(if ^boolean is-shared
[:> button* {:variant "secondary"
@ -366,16 +389,28 @@
[:div {:class (stl/css :connected-to-wrapper)}
[:span "(" (tr "workspace.libraries.connected-to") " "]
[:span {:class (stl/css :connected-to-values)} (str/join ", " connected-to-names)]
[:span ")"]])])]]
[:span ")"]])])]
(when (contains? cf/flags :token-lib-sync)
(when (cfo/tokens-source? local-library id)
[:div (tr "workspace.libraries.tokens-source")]))]
[:div {:class (stl/css :library-actions)}
(when ^boolean has-tokens?
[:> icon-button*
{:type "button"
:aria-label (tr "workspace.tokens.import-tokens")
:icon i/import-export
:data-library-id (dm/str id)
:variant "secondary"
:on-click import-tokens}])
(if (contains? cf/flags :token-lib-sync)
(when (and (cfo/tokens-provider? (:data library))
(not (cfo/tokens-source? local-library id)))
[:> button* {:variant "secondary"
:type "button"
:data-library-id (dm/str id)
:on-click set-as-tokens-source}
(tr "workspace.libraries.set-as-tokens-source")])
(when ^boolean has-tokens?
[:> icon-button*
{:type "button"
:aria-label (tr "workspace.tokens.import-tokens")
:icon i/import-export
:data-library-id (dm/str id)
:variant "secondary"
:on-click import-tokens}]))
[:> icon-button* {:type "button"
:aria-label (tr "workspace.libraries.unlink-library-btn")

View File

@ -8,8 +8,10 @@
(:require
[app.common.files.tokens :as cfo]
[app.common.types.tokens-lib :as ctob]
[app.main.data.changes :as dch]
[app.main.data.helpers :as dsh]
[app.main.data.style-dictionary :as sd]
[app.main.data.workspace.undo :as dwu]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
@ -53,6 +55,31 @@
"Stops on `send-update-indices` function being called, which should be the last function of an event chain."
(stop-on ::end))
(defn watch-undo-stack
"Maintain the workspace undo stack by watching for commit events.
Must be emitted before the events under test so undo entries are populated."
[]
(ptk/reify ::watch-undo-stack
ptk/WatchEvent
(watch [_ _ stream]
(let [stopper-s (->> stream (rx/filter (ptk/type? ::watch-undo-stack)))]
(->> stream
(rx/filter dch/commit?)
(rx/map deref)
(rx/filter #(= :local (:source %)))
(rx/mapcat
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo? selected-before]}]
(if (and save-undo? (seq undo-changes))
(rx/of (dwu/append-undo
{:undo-changes undo-changes
:redo-changes redo-changes
:undo-group undo-group
:tags tags
:selected-before selected-before}
stack-undo?))
(rx/empty))))
(rx/take-until stopper-s))))))
;; Support for async events in tests
;; https://chat.kaleidos.net/penpot-partners/pl/tz1yoes3w3fr9qanxqpuhoz3ch
(defn run-store

View File

@ -6,7 +6,6 @@
(ns frontend-tests.tokens.logic.token-actions-test
(:require
[app.common.files.tokens :as cfo]
[app.common.test-helpers.compositions :as ctho]
[app.common.test-helpers.files :as cthf]
[app.common.test-helpers.ids-map :as cthi]

View File

@ -13,6 +13,7 @@
[app.common.types.tokens-status :as ctos]
[app.common.uuid :as uuid]
[app.main.data.workspace.tokens.library-edit :as dwtl]
[app.main.data.workspace.undo :as dwu]
[cljs.test :as t :include-macros true]
[frontend-tests.helpers.pages :as thp]
[frontend-tests.helpers.state :as ths]
@ -123,4 +124,32 @@
sets' (ctob/get-sets tokens-lib')]
(t/testing "Set has been deleted"
(t/is (= (count sets') 0)))))))))
(t/is (= (count sets') 0)))))))))
(t/deftest set-tokens-source
(t/async
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
library-id (uuid/next)]
;; Phase 1: set tokens-source with undo watcher active
(tohs/run-store
store identity
[(tohs/watch-undo-stack)
(dwtl/set-tokens-source library-id)]
(fn [new-state]
(let [file-data' (-> (ths/get-file-from-state new-state) :data)]
(t/testing "tokens-source is set to the library id"
(t/is (= library-id (:tokens-source file-data'))))))
(tohs/stop-on ::dwtl/set-tokens-source))
;; Phase 2: undo and verify restoration
(tohs/run-store
store done
[dwu/undo]
(fn [undone-state]
(let [file-data'' (-> (ths/get-file-from-state undone-state) :data)]
(t/testing "tokens-source is restored to nil"
(t/is (nil? (:tokens-source file-data''))))))
(tohs/stop-on ::dwu/undo)))))

View File

@ -7239,6 +7239,14 @@ msgstr "some templates in here"
msgid "workspace.libraries.file-library"
msgstr "File library"
#: src/app/main/ui/workspace/libraries.cljs:336
msgid "workspace.libraries.tokens-source"
msgstr "Tokens source"
#: src/app/main/ui/workspace/libraries.cljs:336
msgid "workspace.libraries.set-as-tokens-source"
msgstr "Set as tokens source"
#: src/app/main/ui/workspace/libraries.cljs:100, src/app/main/ui/workspace/libraries.cljs:124
msgid "workspace.libraries.graphics"
msgid_plural "workspace.libraries.graphics"

View File

@ -7072,6 +7072,14 @@ msgstr "algunas plantillas aquí"
msgid "workspace.libraries.file-library"
msgstr "Biblioteca del archivo"
#: src/app/main/ui/workspace/libraries.cljs:336
msgid "workspace.libraries.tokens-source"
msgstr "Fuente de tokens"
#: src/app/main/ui/workspace/libraries.cljs:336
msgid "workspace.libraries.set-as-tokens-source"
msgstr "Activar fuente de tokens"
#: src/app/main/ui/workspace/libraries.cljs:100, src/app/main/ui/workspace/libraries.cljs:124
msgid "workspace.libraries.graphics"
msgid_plural "workspace.libraries.graphics"