diff --git a/CHANGES.md b/CHANGES.md
index b2811d57c9..ef56d959df 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,10 +1,21 @@
# CHANGELOG
+## 2.4.2
+
+### :bug: Bugs fixed
+
+- Fix detach when top copy is dangling and nested copy is not [Taiga #9699](https://tree.taiga.io/project/penpot/issue/9699)
+- Fix problem in plugins with `replaceColor` method [#174](https://github.com/penpot/penpot-plugins/issues/174)
+- Fix issue with recursive commponents [Taiga #9903](https://tree.taiga.io/project/penpot/issue/9903)
+- Fix missing methods reference on API Docs
+- Fix memory usage issue on file-gc asynchronous task (related to snapshots feature)
+
## 2.4.1
### :bug: Bugs fixed
- Fix error when importing files with touched components [Taiga #9625](https://tree.taiga.io/project/penpot/issue/9625)
+- Fix problem when changing color libraries [Plugins #184](https://github.com/penpot/penpot-plugins/issues/184)
## 2.4.0
diff --git a/backend/scripts/repl b/backend/scripts/repl
index 4aa78f0250..1540e36016 100755
--- a/backend/scripts/repl
+++ b/backend/scripts/repl
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
+export PENPOT_SECRET_KEY=super-secret-devenv-key
export PENPOT_HOST=devenv
export PENPOT_FLAGS="\
$PENPOT_FLAGS \
diff --git a/backend/scripts/start-dev b/backend/scripts/start-dev
index 4e4c8497fb..9fe2ccb1b4 100755
--- a/backend/scripts/start-dev
+++ b/backend/scripts/start-dev
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
+export PENPOT_SECRET_KEY=super-secret-devenv-key
export PENPOT_HOST=devenv
export PENPOT_FLAGS="\
$PENPOT_FLAGS \
diff --git a/backend/src/app/auth.clj b/backend/src/app/auth.clj
index fc6d254810..271e52e024 100644
--- a/backend/src/app/auth.clj
+++ b/backend/src/app/auth.clj
@@ -8,7 +8,7 @@
(:require
[buddy.hashers :as hashers]))
-(def default-params
+(def ^:private default-options
{:alg :argon2id
:memory 32768 ;; 32 MiB
:iterations 3
@@ -16,12 +16,12 @@
(defn derive-password
[password]
- (hashers/derive password default-params))
+ (hashers/derive password default-options))
(defn verify-password
[attempt password]
(try
- (hashers/verify attempt password)
+ (hashers/verify attempt password default-options)
(catch Throwable _
{:update false
:valid false})))
diff --git a/backend/src/app/db.clj b/backend/src/app/db.clj
index d02a8ee4ed..625fe872fc 100644
--- a/backend/src/app/db.clj
+++ b/backend/src/app/db.clj
@@ -270,19 +270,17 @@
:else (throw (IllegalArgumentException. "unable to resolve connectable"))))
(def ^:private params-mapping
- {::return-keys? :return-keys
- ::return-keys :return-keys})
+ {::return-keys :return-keys})
(defn rename-opts
[opts]
(set/rename-keys opts params-mapping))
(def ^:private default-insert-opts
- {:builder-fn sql/as-kebab-maps
- :return-keys true})
+ (assoc sql/default-opts :return-keys true))
(def ^:private default-opts
- {:builder-fn sql/as-kebab-maps})
+ sql/default-opts)
(defn exec!
([ds sv] (exec! ds sv nil))
@@ -333,7 +331,7 @@
(defn update!
"A helper that build an UPDATE SQL statement and executes it.
- Given a connectable object, a table name, a hash map of columns and
+ Given a connectable object, a table name, a hash map of columns and
values to set, and either a hash map of columns and values to search
on or a vector of a SQL where clause and parameters, perform an
update on the table.
@@ -413,10 +411,20 @@
:hint "database object not found"))
row))
+(def ^:private default-plan-opts
+ (-> default-opts
+ (assoc :fetch-size 1)
+ (assoc :concurrency :read-only)
+ (assoc :cursors :close)
+ (assoc :result-type :forward-only)))
+
(defn plan
- [ds sql]
- (-> (get-connectable ds)
- (jdbc/plan sql sql/default-opts)))
+ ([ds sql]
+ (-> (get-connectable ds)
+ (jdbc/plan sql default-plan-opts)))
+ ([ds sql opts]
+ (-> (get-connectable ds)
+ (jdbc/plan sql (merge default-plan-opts opts)))))
(defn cursor
"Return a lazy seq of rows using server side cursors"
diff --git a/backend/src/app/db/sql.clj b/backend/src/app/db/sql.clj
index 42641039e3..beabb8861e 100644
--- a/backend/src/app/db/sql.clj
+++ b/backend/src/app/db/sql.clj
@@ -15,14 +15,15 @@
(defn kebab-case [s] (str/replace s #"_" "-"))
(defn snake-case [s] (str/replace s #"-" "_"))
-(def default-opts
- {:table-fn snake-case
- :column-fn snake-case})
-
(defn as-kebab-maps
[rs opts]
(jdbc-opt/as-unqualified-modified-maps rs (assoc opts :label-fn kebab-case)))
+(def default-opts
+ {:table-fn snake-case
+ :column-fn snake-case
+ :builder-fn as-kebab-maps})
+
(defn insert
([table key-map]
(insert table key-map nil))
diff --git a/backend/src/app/rpc/commands/comments.clj b/backend/src/app/rpc/commands/comments.clj
index fafecd8b87..7c44a8e671 100644
--- a/backend/src/app/rpc/commands/comments.clj
+++ b/backend/src/app/rpc/commands/comments.clj
@@ -29,10 +29,11 @@
;; --- GENERAL PURPOSE INTERNAL HELPERS
(defn- decode-row
- [{:keys [participants position] :as row}]
+ [{:keys [participants position mentions] :as row}]
(cond-> row
(db/pgpoint? position) (assoc :position (db/decode-pgpoint position))
- (db/pgobject? participants) (assoc :participants (db/decode-transit-pgobject participants))))
+ (db/pgobject? participants) (assoc :participants (db/decode-transit-pgobject participants))
+ (db/pgarray? mentions) (assoc :mentions (db/decode-pgarray mentions #{}))))
(def xf-decode-row
(map decode-row))
@@ -461,8 +462,9 @@
:thread-id thread-id
:owner-id profile-id
:content content})
- props {:file-id file-id
- :share-id nil}]
+ comment (decode-row comment)
+ props {:file-id file-id
+ :share-id nil}]
;; Update thread modified-at attribute and assoc the current
;; profile to the participant set.
diff --git a/backend/src/app/rpc/commands/management.clj b/backend/src/app/rpc/commands/management.clj
index cad94b019e..16894f4e6f 100644
--- a/backend/src/app/rpc/commands/management.clj
+++ b/backend/src/app/rpc/commands/management.clj
@@ -67,7 +67,7 @@
:is-owner true
:is-admin true
:can-edit true}
- {::db/return-keys? false}))
+ {::db/return-keys false}))
(doseq [params (sequence (comp
(map #(bfc/remap-id % :file-id))
diff --git a/backend/src/app/rpc/doc.clj b/backend/src/app/rpc/doc.clj
index 217e86332d..efd23ed44a 100644
--- a/backend/src/app/rpc/doc.clj
+++ b/backend/src/app/rpc/doc.clj
@@ -87,6 +87,7 @@
(let [params (:query-params request)
pstyle (:type params "js")
context (assoc context :param-style pstyle)]
+
{::yres/status 200
::yres/body (-> (io/resource "app/templates/api-doc.tmpl")
(tmpl/render context))}))
@@ -207,7 +208,7 @@
(assert (sm/valid? ::rpc/methods (::rpc/methods params)) "expected valid methods"))
(defmethod ig/init-key ::routes
- [_ {:keys [methods] :as cfg}]
+ [_ {:keys [::rpc/methods] :as cfg}]
[(let [context (prepare-doc-context methods)]
[["/_doc"
{:handler (doc-handler context)
diff --git a/backend/src/app/setup.clj b/backend/src/app/setup.clj
index 8e2733c6df..6df3ce657a 100644
--- a/backend/src/app/setup.clj
+++ b/backend/src/app/setup.clj
@@ -74,8 +74,7 @@
(defmethod ig/assert-key ::props
[_ params]
- (assert (db/pool? (::db/pool params)) "expected valid database pool")
- (assert (string? (::key params)) "expected valid key string"))
+ (assert (db/pool? (::db/pool params)) "expected valid database pool"))
(defmethod ig/init-key ::props
[_ {:keys [::db/pool ::key] :as cfg}]
diff --git a/backend/src/app/tasks/file_gc.clj b/backend/src/app/tasks/file_gc.clj
index 7e3c3ee27d..30f6c2f5ae 100644
--- a/backend/src/app/tasks/file_gc.clj
+++ b/backend/src/app/tasks/file_gc.clj
@@ -26,7 +26,6 @@
[app.util.pointer-map :as pmap]
[app.util.time :as dt]
[app.worker :as wrk]
- [clojure.set :as set]
[integrant.core :as ig]))
(declare ^:private get-file)
@@ -53,16 +52,21 @@
RETURNING id")
(def ^:private xf:collect-used-media
- (comp (map :data) (mapcat bfc/collect-used-media)))
+ (comp
+ (map :data)
+ (mapcat bfc/collect-used-media)))
(defn- clean-file-media!
"Performs the garbage collection of file media objects."
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
- (let [used (into #{}
- xf:collect-used-media
- (cons file
- (->> (db/cursor conn [sql:get-snapshots id])
- (map (partial decode-file cfg)))))
+ (let [xform (comp
+ (map (partial decode-file cfg))
+ xf:collect-used-media)
+
+ used (->> (db/plan conn [sql:get-snapshots id])
+ (transduce xform conj #{}))
+ used (into used xf:collect-used-media [file])
+
ids (db/create-array conn "uuid" used)
unused (->> (db/exec! conn [sql:mark-file-media-object-deleted id ids])
(into #{} (map :id)))]
@@ -145,51 +149,47 @@
AND f.deleted_at IS null
ORDER BY f.modified_at ASC")
+(def ^:private xf:map-id (map :id))
+
+(defn- get-used-components
+ "Given a file and a set of components marked for deletion, return a
+ filtered set of component ids that are still un use"
+ [components library-id {:keys [data]}]
+ (filter #(ctf/used-in? data library-id % :component) components))
+
(defn- clean-deleted-components!
"Performs the garbage collection of unreferenced deleted components."
[{:keys [::db/conn] :as cfg} {:keys [data] :as file}]
(let [file-id (:id file)
- get-used-components
- (fn [data components]
- ;; Find which of the components are used in the file.
- (into #{}
- (filter #(ctf/used-in? data file-id % :component))
- components))
+ deleted-components
+ (ctkl/deleted-components-seq data)
- get-unused-components
- (fn [components files]
- ;; Find and return a set of unused components (on all files).
- (reduce (fn [components {:keys [data]}]
- (if (seq components)
- (->> (get-used-components data components)
- (set/difference components))
- (reduced components)))
+ xform
+ (mapcat (partial get-used-components deleted-components file-id))
- components
- files))
+ used-remote
+ (->> (db/plan conn [sql:get-files-for-library file-id])
+ (transduce (comp (map (partial decode-file cfg)) xform) conj #{}))
- process-fdata
- (fn [data unused]
- (reduce (fn [data id]
- (l/trc :hint "delete component"
- :component-id (str id)
- :file-id (str file-id))
- (ctkl/delete-component data id))
- data
- unused))
+ used-local
+ (into #{} xform [file])
- deleted (into #{} (ctkl/deleted-components-seq data))
-
- unused (->> (db/cursor conn [sql:get-files-for-library file-id] {:chunk-size 1})
- (map (partial decode-file cfg))
- (cons file)
- (get-unused-components deleted)
- (mapv :id)
- (set))
-
- file (update file :data process-fdata unused)]
+ unused
+ (transduce xf:map-id disj
+ (into #{} xf:map-id deleted-components)
+ (concat used-remote used-local))
+ file
+ (update file :data
+ (fn [data]
+ (reduce (fn [data id]
+ (l/trc :hint "delete component"
+ :component-id (str id)
+ :file-id (str file-id))
+ (ctkl/delete-component data id))
+ data
+ unused)))]
(l/dbg :hint "clean" :rel "components" :file-id (str file-id) :total (count unused))
file))
diff --git a/backend/test/backend_tests/helpers.clj b/backend/test/backend_tests/helpers.clj
index 3aa7d1589c..1e1fb7abfb 100644
--- a/backend/test/backend_tests/helpers.clj
+++ b/backend/test/backend_tests/helpers.clj
@@ -62,7 +62,7 @@
(def default
{:database-uri "postgresql://postgres/penpot_test"
:redis-uri "redis://redis/1"
- :file-snapshot-every 1})
+ :auto-file-snapshot-every 1})
(def config
(cf/read-config :prefix "penpot-test"
diff --git a/backend/test/backend_tests/rpc_file_test.clj b/backend/test/backend_tests/rpc_file_test.clj
index 679d5221e0..b95358101b 100644
--- a/backend/test/backend_tests/rpc_file_test.clj
+++ b/backend/test/backend_tests/rpc_file_test.clj
@@ -383,8 +383,19 @@
;; as deleted.
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+ ;; This only clears fragments, the file media objects still referenced because
+ ;; snapshots are preserved
(let [res (th/run-task! :objects-gc {:min-age 0})]
- (t/is (= 3 (:processed res))))
+ (t/is (= 2 (:processed res))))
+
+ ;; Mark all snapshots to be a non-snapshot file change
+ (th/db-exec! ["update file_change set data = null where file_id = ?" (:id file)])
+ (th/db-exec! ["update file set has_media_trimmed = false where id = ?" (:id file)])
+
+ ;; Rerun the file-gc and objects-gc
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+ (let [res (th/run-task! :objects-gc {:min-age 0})]
+ (t/is (= 2 (:processed res))))
;; Now that file-gc have deleted the file-media-object usage,
;; lets execute the touched-gc task, we should see that two of
@@ -417,20 +428,6 @@
;; (th/print-result! out)
(t/is (nil? (:error out)))
- (:result out)))
-
- (update-file! [& {:keys [profile-id file-id changes revn] :or {revn 0}}]
- (let [params {::th/type :update-file
- ::rpc/profile-id profile-id
- :id file-id
- :session-id (uuid/random)
- :revn revn
- :vern 0
- :components-v2 true
- :changes changes}
- out (th/command! params)]
- ;; (th/print-result! out)
- (t/is (nil? (:error out)))
(:result out)))]
(let [storage (:app.storage/storage th/*system*)
@@ -550,8 +547,20 @@
;; as deleted.
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+ ;; This only removes unused fragments, file media are still
+ ;; referenced on snapshots.
(let [res (th/run-task! :objects-gc {:min-age 0})]
- (t/is (= 7 (:processed res))))
+ (t/is (= 2 (:processed res))))
+
+ ;; Mark all snapshots to be a non-snapshot file change
+ (th/db-exec! ["update file set has_media_trimmed = false where id = ?" (:id file)])
+ (th/db-exec! ["update file_change set data = null where file_id = ?" (:id file)])
+
+ ;; Rerun file-gc and objects-gc task for the same file once all snapshots are
+ ;; "expired/deleted"
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+ (let [res (th/run-task! :objects-gc {:min-age 0})]
+ (t/is (= 6 (:processed res))))
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)
:deleted-at nil})]
@@ -591,20 +600,7 @@
;; (th/print-result! out)
(t/is (nil? (:error out)))
- (:result out)))
-
- #_(update-file! [& {:keys [profile-id file-id changes revn] :or {revn 0}}]
- (let [params {::th/type :update-file
- ::rpc/profile-id profile-id
- :id file-id
- :session-id (uuid/random)
- :revn revn
- :features cfeat/supported-features
- :changes changes}
- out (th/command! params)]
- ;; (th/print-result! out)
- (t/is (nil? (:error out)))
- (:result out)))]
+ (:result out)))]
(let [storage (:app.storage/storage th/*system*)
profile (th/create-profile* 1)
@@ -1336,3 +1332,329 @@
(t/is (every? #(bytes? (:data %)) rows))
(t/is (every? #(nil? (:data-ref-id %)) rows))
(t/is (every? #(nil? (:data-backend %)) rows)))))
+
+(t/deftest file-gc-with-components-1
+ (let [storage (:app.storage/storage th/*system*)
+ profile (th/create-profile* 1)
+ file (th/create-file* 1 {:profile-id (:id profile)
+ :project-id (:default-project-id profile)
+ :is-shared false})
+
+ s-id-1 (uuid/random)
+ s-id-2 (uuid/random)
+ c-id (uuid/random)
+
+ page-id (first (get-in file [:data :pages]))]
+
+ (let [rows (th/db-query :file-data-fragment {:file-id (:id file)
+ :deleted-at nil})]
+ (t/is (= (count rows) 1)))
+
+ ;; Update file inserting new component
+ (update-file!
+ :file-id (:id file)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :add-obj
+ :page-id page-id
+ :id s-id-1
+ :parent-id uuid/zero
+ :frame-id uuid/zero
+ :components-v2 true
+ :obj (cts/setup-shape
+ {:id s-id-1
+ :name "Board"
+ :frame-id uuid/zero
+ :parent-id uuid/zero
+ :type :frame
+ :main-instance true
+ :component-root true
+ :component-file (:id file)
+ :component-id c-id})}
+
+ {:type :add-obj
+ :page-id page-id
+ :id s-id-2
+ :parent-id uuid/zero
+ :frame-id uuid/zero
+ :components-v2 true
+ :obj (cts/setup-shape
+ {:id s-id-2
+ :name "Board"
+ :frame-id uuid/zero
+ :parent-id uuid/zero
+ :type :frame
+ :main-instance false
+ :component-root true
+ :component-file (:id file)
+ :component-id c-id})}
+
+ {:type :add-component
+ :path ""
+ :name "Board"
+ :main-instance-id s-id-1
+ :main-instance-page page-id
+ :id c-id
+ :anotation nil}])
+
+ ;; Run the file-gc task immediately without forced min-age
+ (t/is (false? (th/run-task! :file-gc {:file-id (:id file)})))
+
+ ;; Run the task again
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+
+ ;; Retrieve file and check trimmed attribute
+ (let [row (th/db-get :file {:id (:id file)})]
+ (t/is (true? (:has-media-trimmed row))))
+
+ ;; Check that component exists
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ component (get-in result [:data :components c-id])]
+
+ (t/is (some? component))
+ (t/is (nil? (:objects component)))))
+
+ ;; Now proceed to delete a component
+ (update-file!
+ :file-id (:id file)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :del-component
+ :id c-id}
+ {:type :del-obj
+ :page-id page-id
+ :id s-id-1
+ :ignore-touched true}])
+
+ ;; ;; Check that component is marked as deleted
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ component (get-in result [:data :components c-id])]
+ (t/is (true? (:deleted component)))
+ (t/is (some? (not-empty (:objects component))))))
+
+ ;; Re-run the file-gc task
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+ (let [row (th/db-get :file {:id (:id file)})]
+ (t/is (true? (:has-media-trimmed row))))
+
+ ;; Check that component is still there after file-gc task
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ component (get-in result [:data :components c-id])]
+ (t/is (true? (:deleted component)))
+ (t/is (some? (not-empty (:objects component))))))
+
+ ;; Now delete the last instance using deleted component
+ (update-file!
+ :file-id (:id file)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :del-obj
+ :page-id page-id
+ :id s-id-2
+ :ignore-touched true}])
+
+ ;; Now, we have deleted the usage of component if we pass file-gc,
+ ;; that component should be deleted
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
+
+ ;; Check that component is properly removed
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ components (get-in result [:data :components])]
+ (t/is (not (contains? components c-id)))))))
+
+(t/deftest file-gc-with-components-2
+ (let [storage (:app.storage/storage th/*system*)
+ profile (th/create-profile* 1)
+ file-1 (th/create-file* 1 {:profile-id (:id profile)
+ :project-id (:default-project-id profile)
+ :is-shared true})
+
+ file-2 (th/create-file* 2 {:profile-id (:id profile)
+ :project-id (:default-project-id profile)
+ :is-shared false})
+
+ rel (th/link-file-to-library*
+ {:file-id (:id file-2)
+ :library-id (:id file-1)})
+
+ s-id-1 (uuid/random)
+ s-id-2 (uuid/random)
+ c-id (uuid/random)
+
+ f1-page-id (first (get-in file-1 [:data :pages]))
+ f2-page-id (first (get-in file-2 [:data :pages]))]
+
+ ;; Update file library inserting new component
+ (update-file!
+ :file-id (:id file-1)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :add-obj
+ :page-id f1-page-id
+ :id s-id-1
+ :parent-id uuid/zero
+ :frame-id uuid/zero
+ :components-v2 true
+ :obj (cts/setup-shape
+ {:id s-id-1
+ :name "Board"
+ :frame-id uuid/zero
+ :parent-id uuid/zero
+ :type :frame
+ :main-instance true
+ :component-root true
+ :component-file (:id file-1)
+ :component-id c-id})}
+ {:type :add-component
+ :path ""
+ :name "Board"
+ :main-instance-id s-id-1
+ :main-instance-page f1-page-id
+ :id c-id
+ :anotation nil}])
+
+ ;; Instanciate a component in a different file
+ (update-file!
+ :file-id (:id file-2)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :add-obj
+ :page-id f2-page-id
+ :id s-id-2
+ :parent-id uuid/zero
+ :frame-id uuid/zero
+ :components-v2 true
+ :obj (cts/setup-shape
+ {:id s-id-2
+ :name "Board"
+ :frame-id uuid/zero
+ :parent-id uuid/zero
+ :type :frame
+ :main-instance false
+ :component-root true
+ :component-file (:id file-1)
+ :component-id c-id})}])
+
+ ;; Run the file-gc on file and library
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file-1)})))
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file-2)})))
+
+ ;; Check that component exists
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file-1)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ component (get-in result [:data :components c-id])]
+
+ (t/is (some? component))
+ (t/is (nil? (:objects component)))))
+
+ ;; Now proceed to delete a component
+ (update-file!
+ :file-id (:id file-1)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :del-component
+ :id c-id}
+ {:type :del-obj
+ :page-id f1-page-id
+ :id s-id-1
+ :ignore-touched true}])
+
+ ;; Check that component is marked as deleted
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file-1)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ component (get-in result [:data :components c-id])]
+ (t/is (true? (:deleted component)))
+ (t/is (some? (not-empty (:objects component))))))
+
+ ;; Re-run the file-gc task
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file-1)})))
+ (t/is (false? (th/run-task! :file-gc {:min-age 0 :file-id (:id file-2)})))
+
+ ;; Check that component is still there after file-gc task
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file-1)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ component (get-in result [:data :components c-id])]
+ (t/is (true? (:deleted component)))
+ (t/is (some? (not-empty (:objects component))))))
+
+ ;; Now delete the last instance using deleted component
+ (update-file!
+ :file-id (:id file-2)
+ :profile-id (:id profile)
+ :revn 0
+ :vern 0
+ :changes
+ [{:type :del-obj
+ :page-id f2-page-id
+ :id s-id-2
+ :ignore-touched true}])
+
+ ;; Mark
+ (th/db-exec! ["update file set has_media_trimmed = false where id = ?" (:id file-1)])
+
+ ;; Now, we have deleted the usage of component if we pass file-gc,
+ ;; that component should be deleted
+ (t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file-1)})))
+
+ ;; Check that component is properly removed
+ (let [data {::th/type :get-file
+ ::rpc/profile-id (:id profile)
+ :id (:id file-1)}
+ out (th/command! data)]
+
+ (t/is (th/success? out))
+ (let [result (:result out)
+ components (get-in result [:data :components])]
+ (t/is (not (contains? components c-id)))))))
+
diff --git a/common/src/app/common/files/repair.cljc b/common/src/app/common/files/repair.cljc
index 67f90dafeb..5d80c844ff 100644
--- a/common/src/app/common/files/repair.cljc
+++ b/common/src/app/common/files/repair.cljc
@@ -320,6 +320,35 @@
(pcb/with-file-data file-data)
(pcb/update-shapes shape-ids detach-shape))))))
+
+(defmethod repair-error :shape-ref-cycle
+ [_ {:keys [shape args] :as error} file-data _]
+ (let [repair-component
+ (fn [component]
+ (let [objects (:objects component) ;; we only have encounter this on deleted components,
+ ;; so the relevant objects are inside the component
+ to-detach (->> (:cycles-ids args)
+ (map #(get objects %))
+ (map #(ctn/get-head-shape objects %))
+ (map :id)
+ distinct
+ (mapcat #(ctn/get-children-in-instance objects %))
+ (map :id)
+ set)]
+
+ (update component :objects
+ (fn [objects]
+ (reduce-kv (fn [acc k v]
+ (if (contains? to-detach k)
+ (assoc acc k (ctk/detach-shape v))
+ (assoc acc k v)))
+ {}
+ objects)))))]
+ (log/dbg :hint "repairing component :shape-ref-cycle" :id (:id shape) :name (:name shape))
+ (-> (pcb/empty-changes nil nil)
+ (pcb/with-library-data file-data)
+ (pcb/update-component (:id shape) repair-component))))
+
(defmethod repair-error :shape-ref-in-main
[_ {:keys [shape page-id] :as error} file-data _]
(let [repair-shape
diff --git a/common/src/app/common/files/validate.cljc b/common/src/app/common/files/validate.cljc
index 79e6cf3017..f1f2bdda9e 100644
--- a/common/src/app/common/files/validate.cljc
+++ b/common/src/app/common/files/validate.cljc
@@ -55,7 +55,8 @@
:component-nil-objects-not-allowed
:instance-head-not-frame
:misplaced-slot
- :missing-slot})
+ :missing-slot
+ :shape-ref-cycle})
(def ^:private schema:error
[:map {:title "ValidationError"}
@@ -482,6 +483,18 @@
"This deleted component has children with the same swap slot"
component file nil))))
+(defn check-ref-cycles
+ [component file]
+ (let [cycles-ids (->> component
+ :objects
+ vals
+ (filter #(= (:id %) (:shape-ref %)))
+ (map :id))]
+
+ (when (seq cycles-ids)
+ (report-error :shape-ref-cycle
+ "This deleted component has shapes with shape-ref pointing to self"
+ component file nil :cycles-ids cycles-ids))))
(defn- check-component
"Validate semantic coherence of a component. Report all errors found."
@@ -491,7 +504,8 @@
"Objects list cannot be nil"
component file nil))
(when (:deleted component)
- (check-component-duplicate-swap-slot component file)))
+ (check-component-duplicate-swap-slot component file)
+ (check-ref-cycles component file)))
(defn- get-orphan-shapes
[{:keys [objects] :as page}]
diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc
index bc9ab68b02..669fdbd633 100644
--- a/common/src/app/common/logic/libraries.cljc
+++ b/common/src/app/common/logic/libraries.cljc
@@ -304,7 +304,12 @@
(and (some? (ctk/get-swap-slot ref-shape))
(nil? (ctk/get-swap-slot shape))
(not= (:id shape) shape-id))
- (pcb/update-shapes [(:id shape)] #(ctk/set-swap-slot % (ctk/get-swap-slot ref-shape))))))]
+ (pcb/update-shapes [(:id shape)] #(ctk/set-swap-slot % (ctk/get-swap-slot ref-shape)))
+
+ ;; If we can't get the ref-shape (e.g. it's in an external library not linked),
+ ;: we can't do a suitable advance. So it's better to detach the shape
+ (nil? ref-shape)
+ (pcb/update-shapes [(:id shape)] ctk/detach-shape))))]
(reduce skip-near changes children)))
diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc
index 739bca6d19..9b575179aa 100644
--- a/common/src/app/common/types/file.cljc
+++ b/common/src/app/common/types/file.cljc
@@ -335,24 +335,28 @@
(true? (= (:id component) (:id ref-component)))))
(defn find-swap-slot
- [shape container file libraries]
- (if-let [swap-slot (ctk/get-swap-slot shape)]
- swap-slot
- (let [ref-shape (find-ref-shape file
- container
- libraries
- shape
- :include-deleted? true
- :with-context? true)
- shape-meta (meta ref-shape)
- ref-file (:file shape-meta)
- ref-container (:container shape-meta)]
- (when ref-shape
- (if-let [swap-slot (ctk/get-swap-slot ref-shape)]
- swap-slot
- (if (ctk/main-instance? ref-shape)
- (:id shape)
- (find-swap-slot ref-shape ref-container ref-file libraries)))))))
+ ([shape container file libraries]
+ (find-swap-slot shape container file libraries #{}))
+ ([shape container file libraries viewed-ids]
+ (if (contains? viewed-ids (:id shape)) ;; prevent cycles
+ nil
+ (if-let [swap-slot (ctk/get-swap-slot shape)]
+ swap-slot
+ (let [ref-shape (find-ref-shape file
+ container
+ libraries
+ shape
+ :include-deleted? true
+ :with-context? true)
+ shape-meta (meta ref-shape)
+ ref-file (:file shape-meta)
+ ref-container (:container shape-meta)]
+ (when ref-shape
+ (if-let [swap-slot (ctk/get-swap-slot ref-shape)]
+ swap-slot
+ (if (ctk/main-instance? ref-shape)
+ (:id shape)
+ (find-swap-slot ref-shape ref-container ref-file libraries (conj viewed-ids (:id shape)))))))))))
(defn match-swap-slot?
[shape-main shape-inst container-inst container-main file libraries]
@@ -738,16 +742,20 @@
(:component-id shape) "@"
:else "-")
- (when (and (:component-file shape) component-file)
+ (when (:component-file shape)
(str/format "<%s> "
- (if (= (:id component-file) (:id file))
- "local"
- (:name component-file))))
+ (if component-file
+ (if (= (:id component-file) (:id file))
+ "local"
+ (:name component-file))
+ (if show-ids
+ (str/format "¿%s?" (:component-file shape))
+ "?"))))
(or (:name component-shape)
- (str/format "?%s"
- (when show-ids
- (str " " (:shape-ref shape)))))
+ (if show-ids
+ (str/format "¿%s?" (:shape-ref shape))
+ "?"))
(when (and show-ids component-shape)
(str/format " %s" (:id component-shape)))
diff --git a/common/src/app/common/types/shape.cljc b/common/src/app/common/types/shape.cljc
index fa0de53597..b483c5fe4c 100644
--- a/common/src/app/common/types/shape.cljc
+++ b/common/src/app/common/types/shape.cljc
@@ -154,6 +154,7 @@
[:main-instance {:optional true} :boolean]
[:remote-synced {:optional true} :boolean]
[:shape-ref {:optional true} ::sm/uuid]
+ [:touched {:optional true} [:maybe [:set :keyword]]]
[:blocked {:optional true} :boolean]
[:collapsed {:optional true} :boolean]
[:locked {:optional true} :boolean]
@@ -191,8 +192,7 @@
[:grow-type {:optional true}
[::sm/one-of grow-types]]
[:applied-tokens {:optional true} ::cto/applied-tokens]
- [:plugin-data {:optional true} ::ctpg/plugin-data]
- [:touched {:optional true} [:maybe [:set :keyword]]]])
+ [:plugin-data {:optional true} ::ctpg/plugin-data]])
(def schema:group-attrs
[:map {:title "GroupAttrs"}
diff --git a/common/test/common_tests/logic/comp_detach_with_nested_test.cljc b/common/test/common_tests/logic/comp_detach_with_nested_test.cljc
index d7b999db31..ac7d62d116 100644
--- a/common/test/common_tests/logic/comp_detach_with_nested_test.cljc
+++ b/common/test/common_tests/logic/comp_detach_with_nested_test.cljc
@@ -103,6 +103,83 @@
(t/is (= (:shape-ref copy-nested-ellipse) (thi/id :nested-ellipse)))
(t/is (nil? (ctk/get-swap-slot copy-nested-ellipse)))))
+(t/deftest test-advance-in-library
+ (let [;; ==== Setup
+ library (setup-file)
+ file (-> (thf/sample-file :file2)
+ (thc/instantiate-component :c-big-board
+ :copy-big-board
+ :library library
+ :children-labels [:copy-h-board-with-ellipse
+ :copy-nested-h-ellipse
+ :copy-nested-ellipse]))
+ page (thf/current-page file)
+
+ ;; ==== Action
+ changes (cll/generate-detach-instance (-> (pcb/empty-changes nil)
+ (pcb/with-page page)
+ (pcb/with-objects (:objects page)))
+ page
+ {(:id file) file
+ (:id library) library}
+ (thi/id :copy-big-board))
+ file' (thf/apply-changes file changes)
+
+ ;; ==== Get
+ copy-h-board-with-ellipse (ths/get-shape file' :copy-h-board-with-ellipse)
+ copy-nested-h-ellipse (ths/get-shape file' :copy-nested-h-ellipse)
+ copy-nested-ellipse (ths/get-shape file' :copy-nested-ellipse)]
+
+ ;; ==== Check
+
+ ;; It should the same as above, but in an external library.
+ (thf/dump-file file)
+ (t/is (ctk/instance-root? copy-h-board-with-ellipse))
+ (t/is (= (:shape-ref copy-h-board-with-ellipse) (thi/id :board-with-ellipse)))
+ (t/is (nil? (ctk/get-swap-slot copy-h-board-with-ellipse)))
+
+ (t/is (ctk/instance-head? copy-nested-h-ellipse))
+ (t/is (= (:shape-ref copy-nested-h-ellipse) (thi/id :nested-h-ellipse)))
+ (t/is (nil? (ctk/get-swap-slot copy-nested-h-ellipse)))
+
+ (t/is (not (ctk/instance-head? copy-nested-ellipse)))
+ (t/is (= (:shape-ref copy-nested-ellipse) (thi/id :nested-ellipse)))
+ (t/is (nil? (ctk/get-swap-slot copy-nested-ellipse)))))
+
+(t/deftest test-advance-in-broken-library
+ (let [;; ==== Setup
+ library (setup-file)
+ file (-> (thf/sample-file :file2)
+ (thc/instantiate-component :c-big-board
+ :copy-big-board
+ :library library
+ :children-labels [:copy-h-board-with-ellipse
+ :copy-nested-h-ellipse
+ :copy-nested-ellipse]))
+ page (thf/current-page file)
+
+ ;; ==== Action
+ changes (cll/generate-detach-instance (-> (pcb/empty-changes nil)
+ (pcb/with-page page)
+ (pcb/with-objects (:objects page)))
+ page
+ {(:id file) file}
+ (thi/id :copy-big-board))
+ file' (thf/apply-changes file changes)
+
+ ;; ==== Get
+ copy-h-board-with-ellipse (ths/get-shape file' :copy-h-board-with-ellipse)
+ copy-nested-h-ellipse (ths/get-shape file' :copy-nested-h-ellipse)
+ copy-nested-ellipse (ths/get-shape file' :copy-nested-ellipse)]
+
+ ;; ==== Check
+
+ ;; If the main component cannot be found, because it's in a library that is
+ ;; not available, the nested copies should be detached too.
+ (t/is (not (ctk/in-component-copy? copy-h-board-with-ellipse)))
+ (t/is (not (ctk/in-component-copy? copy-nested-h-ellipse)))
+ (t/is (not (ctk/in-component-copy? copy-nested-ellipse)))))
+
(t/deftest test-dont-advance-when-swapped-copy
(let [;; ==== Setup
file (-> (setup-file)
diff --git a/docker/devenv/docker-compose.yaml b/docker/devenv/docker-compose.yaml
index d7b5da48a5..82a3c0ad76 100644
--- a/docker/devenv/docker-compose.yaml
+++ b/docker/devenv/docker-compose.yaml
@@ -43,7 +43,6 @@ services:
environment:
- EXTERNAL_UID=${CURRENT_USER_ID}
- - PENPOT_SECRET_KEY=super-secret-devenv-key
# SMTP setup
- PENPOT_SMTP_ENABLED=true
- PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
diff --git a/frontend/resources/images/icons/board-2.svg b/frontend/resources/images/icons/board-2.svg
deleted file mode 100644
index 70a44ea155..0000000000
--- a/frontend/resources/images/icons/board-2.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/frontend/resources/images/icons/board.svg b/frontend/resources/images/icons/board.svg
index d4e8525c2e..70a44ea155 100644
--- a/frontend/resources/images/icons/board.svg
+++ b/frontend/resources/images/icons/board.svg
@@ -1,3 +1,3 @@
\ No newline at end of file
+
+
diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs
index b809f7e2ec..13d3e8d88c 100644
--- a/frontend/src/app/config.cljs
+++ b/frontend/src/app/config.cljs
@@ -148,6 +148,11 @@
(let [f (obj/get global "externalContextInfo")]
(when (fn? f) (f))))
+(defn initialize-external-context-info
+ []
+ (let [f (obj/get global "initializeExternalConfigInfo")]
+ (when (fn? f) (f))))
+
;; --- Helper Functions
(defn ^boolean check-browser? [candidate]
diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs
index e9824671f1..57929dfe5d 100644
--- a/frontend/src/app/main.cljs
+++ b/frontend/src/app/main.cljs
@@ -30,20 +30,21 @@
[app.util.i18n :as i18n]
[app.util.theme :as theme]
[beicon.v2.core :as rx]
- [cuerdas.core :as str]
[debug]
[features]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(log/setup! {:app :info})
+(log/set-level! :debug)
(when (= :browser cf/target)
- (log/info :version (:full cf/version)
- :asserts *assert*
- :build-date cf/build-date
- :public-uri (dm/str cf/public-uri))
- (log/info :flags (str/join "," (map name cf/flags))))
+ (log/inf :version (:full cf/version)
+ :asserts *assert*
+ :build-date cf/build-date
+ :public-uri (dm/str cf/public-uri))
+ (doseq [flag cf/flags]
+ (log/dbg :hint "flag enabled" :flag (name flag))))
(declare reinit)
diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs
index 237cdde463..a8562e06cf 100644
--- a/frontend/src/app/main/data/users.cljs
+++ b/frontend/src/app/main/data/users.cljs
@@ -207,6 +207,7 @@
ptk/WatchEvent
(watch [_ _ _]
(when (is-authenticated? profile)
+ (cf/initialize-external-context-info)
(->> (rx/concat
(rx/of (profile-fetched profile)
(fetch-teams)
diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs
index fa16fb9a8f..25893d81ba 100644
--- a/frontend/src/app/main/data/workspace/libraries.cljs
+++ b/frontend/src/app/main/data/workspace/libraries.cljs
@@ -191,6 +191,25 @@
(watch [it state _]
(update-color* it state color file-id)))))
+(defn update-color-data
+ "Update color data without affecting the path location"
+ [color file-id]
+ (let [color (d/without-nils color)]
+
+ (dm/assert!
+ "expected valid color data structure"
+ (ctc/check-color! color))
+
+ (dm/assert!
+ "expected file-id"
+ (uuid? file-id))
+
+ (ptk/reify ::update-color-data
+ ptk/WatchEvent
+ (watch [it state _]
+ (let [color (assoc color :name (dm/str (:path color) "/" (:name color)))]
+ (update-color* it state color file-id))))))
+
(defn rename-color
[file-id id new-name]
(dm/assert!
diff --git a/frontend/src/app/main/ui/components/shape_icon.cljs b/frontend/src/app/main/ui/components/shape_icon.cljs
index 86ce8f3bfe..060213681f 100644
--- a/frontend/src/app/main/ui/components/shape_icon.cljs
+++ b/frontend/src/app/main/ui/components/shape_icon.cljs
@@ -9,7 +9,6 @@
[app.common.types.component :as ctk]
[app.common.types.shape :as cts]
[app.common.types.shape.layout :as ctl]
- [app.config :as cf]
[app.main.ui.icons :as i]
[rumext.v2 :as mf]))
@@ -32,7 +31,7 @@
i/flex-grid
:else
- (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board))
+ i/board)
;; TODO -> THUMBNAIL ICON
:image i/img
:line (if (cts/has-images? shape) i/img i/path)
@@ -57,7 +56,7 @@
(if main-instance?
i/component
(case type
- :frame (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)
+ :frame i/board
:image i/img
:shape i/path
:text i/text
diff --git a/frontend/src/app/main/ui/dashboard/placeholder.cljs b/frontend/src/app/main/ui/dashboard/placeholder.cljs
index 08e1b150dd..dc85232207 100644
--- a/frontend/src/app/main/ui/dashboard/placeholder.cljs
+++ b/frontend/src/app/main/ui/dashboard/placeholder.cljs
@@ -7,6 +7,7 @@
(ns app.main.ui.dashboard.placeholder
(:require-macros [app.main.style :as stl])
(:require
+ [app.config :as cf]
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
[app.main.ui.ds.product.loader :refer [loader*]]
[app.main.ui.icons :as i]
@@ -44,7 +45,7 @@
[:div {:class (stl/css :grid-empty-placeholder)}
[:button {:class (stl/css :create-new)
:on-click on-click}
- i/add]])))
+ (if (cf/external-feature-flag "add-file-01" "test") (tr "dashboard.add-file") i/add)]])))
(mf/defc loading-placeholder
[]
diff --git a/frontend/src/app/main/ui/dashboard/placeholder.scss b/frontend/src/app/main/ui/dashboard/placeholder.scss
index da06dd8635..9a4e89c1e7 100644
--- a/frontend/src/app/main/ui/dashboard/placeholder.scss
+++ b/frontend/src/app/main/ui/dashboard/placeholder.scss
@@ -48,7 +48,6 @@
cursor: pointer;
height: $s-160;
margin: $s-8;
- text-transform: uppercase;
border: $s-2 solid transparent;
width: var(--th-width, #{g.$thumbnail-default-width});
height: var(--th-height, #{g.$thumbnail-default-height});
diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs
index 29b0575572..c73c7d1bd9 100644
--- a/frontend/src/app/main/ui/icons.cljs
+++ b/frontend/src/app/main/ui/icons.cljs
@@ -63,7 +63,6 @@
(def ^:icon arrow (icon-xref :arrow))
(def ^:icon asc-sort (icon-xref :asc-sort))
(def ^:icon board (icon-xref :board))
-(def ^:icon board-2 (icon-xref :board-2))
(def ^:icon boards-thumbnail (icon-xref :boards-thumbnail))
(def ^:icon boolean-difference (icon-xref :boolean-difference))
(def ^:icon boolean-exclude (icon-xref :boolean-exclude))
diff --git a/frontend/src/app/main/ui/static.cljs b/frontend/src/app/main/ui/static.cljs
index 50a3141e81..d892d0fde4 100644
--- a/frontend/src/app/main/ui/static.cljs
+++ b/frontend/src/app/main/ui/static.cljs
@@ -476,7 +476,7 @@
request-access?
(and
- (= (:type data) :not-found)
+ (or (= (:type data) :not-found) (= (:type data) :authentication))
(or workspace? dashboard? view?)
(or (:file-id info)
(:team-id info)))]
diff --git a/frontend/src/app/main/ui/workspace/sidebar/history.cljs b/frontend/src/app/main/ui/workspace/sidebar/history.cljs
index 753c99c3c4..618e397b0d 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/history.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/history.cljs
@@ -9,7 +9,6 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
- [app.config :as cf]
[app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs]
[app.main.store :as st]
@@ -155,7 +154,7 @@
:circle i/elipse
:text i/text
:path i/path
- :frame (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)
+ :frame i/board
:group i/group
:color i/drop-icon
:typography i/text-palette
diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
index 9dd8d234a2..88f3c46010 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
@@ -12,7 +12,6 @@
[app.common.files.helpers :as cfh]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
- [app.config :as cf]
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
[app.main.store :as st]
@@ -336,7 +335,7 @@
:on-click add-filter}
[:div {:class (stl/css :filter-menu-item-name-wrapper)}
[:span {:class (stl/css :filter-menu-item-icon)}
- (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)]
+ i/board]
[:span {:class (stl/css :filter-menu-item-name)}
(tr "workspace.sidebar.layers.frames")]]
diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.cljs b/frontend/src/app/main/ui/workspace/top_toolbar.cljs
index eb19fc6f7d..fb9f3473cf 100644
--- a/frontend/src/app/main/ui/workspace/top_toolbar.cljs
+++ b/frontend/src/app/main/ui/workspace/top_toolbar.cljs
@@ -10,7 +10,6 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.media :as cm]
- [app.config :as cf]
[app.main.data.events :as ev]
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]
@@ -147,7 +146,7 @@
:on-click select-drawtool
:data-tool "frame"
:data-testid "artboard-btn"}
- (if (cf/external-feature-flag "boards-01" "test") i/board-2 i/board)]]
+ i/board]]
[:li
[:button
{:title (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect))
diff --git a/frontend/src/app/plugins/api.cljs b/frontend/src/app/plugins/api.cljs
index 7bc0fd1fc5..8516ca3c69 100644
--- a/frontend/src/app/plugins/api.cljs
+++ b/frontend/src/app/plugins/api.cljs
@@ -166,8 +166,8 @@
:replaceColor
(fn [shapes old-color new-color]
- (let [old-color (parser/parse-color old-color)
- new-color (parser/parse-color new-color)]
+ (let [old-color (parser/parse-color-data old-color)
+ new-color (parser/parse-color-data new-color)]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :replaceColor-shapes shapes)
@@ -190,7 +190,9 @@
shapes-by-color
(->> (ctc/extract-all-colors shapes file-id shared-libs)
(group-by :attrs))]
- (st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color))))))
+
+ (when-let [operations (get shapes-by-color old-color)]
+ (st/emit! (dwc/change-color-in-selected operations new-color old-color)))))))
:getRoot
(fn []
diff --git a/frontend/src/app/plugins/format.cljs b/frontend/src/app/plugins/format.cljs
index b39f422bc9..829476da0b 100644
--- a/frontend/src/app/plugins/format.cljs
+++ b/frontend/src/app/plugins/format.cljs
@@ -128,18 +128,19 @@
;; image?: ImageData;
;; }
(defn format-color
- [{:keys [id name path color opacity ref-id ref-file gradient image] :as color-data}]
+ [{:keys [id file-id name path color opacity ref-id ref-file gradient image] :as color-data}]
(when (some? color-data)
- (obj/without-empty
- #js {:id (format-id id)
- :name name
- :path path
- :color color
- :opacity opacity
- :refId (format-id ref-id)
- :refFile (format-id ref-file)
- :gradient (format-gradient gradient)
- :image (format-image image)})))
+ (let [id (or (format-id id) (format-id ref-id))
+ file-id (or (format-id file-id) (format-id ref-file))]
+ (obj/without-empty
+ #js {:id (or (format-id id) (format-id ref-id))
+ :fileId (or (format-id file-id) (format-id ref-file))
+ :name name
+ :path path
+ :color color
+ :opacity opacity
+ :gradient (format-gradient gradient)
+ :image (format-image image)}))))
;; Color & ColorShapeInfo
(defn format-color-result
diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs
index 29b8b15b0e..d1dc2096f1 100644
--- a/frontend/src/app/plugins/library.cljs
+++ b/frontend/src/app/plugins/library.cljs
@@ -48,6 +48,7 @@
:$file {:enumerable false :get (constantly file-id)}
:id {:get (fn [] (dm/str id))}
+ :fileId {:get #(dm/str file-id)}
:name
{:this true
@@ -98,7 +99,7 @@
:else
(let [color (-> (u/proxy->library-color self)
(assoc :color value))]
- (st/emit! (dwl/update-color color file-id)))))}
+ (st/emit! (dwl/update-color-data color file-id)))))}
:opacity
{:this true
@@ -115,7 +116,7 @@
:else
(let [color (-> (u/proxy->library-color self)
(assoc :opacity value))]
- (st/emit! (dwl/update-color color file-id)))))}
+ (st/emit! (dwl/update-color-data color file-id)))))}
:gradient
{:this true
@@ -133,7 +134,7 @@
:else
(let [color (-> (u/proxy->library-color self)
(assoc :gradient value))]
- (st/emit! (dwl/update-color color file-id))))))}
+ (st/emit! (dwl/update-color-data color file-id))))))}
:image
{:this true
@@ -151,7 +152,7 @@
:else
(let [color (-> (u/proxy->library-color self)
(assoc :image value))]
- (st/emit! (dwl/update-color color file-id))))))}
+ (st/emit! (dwl/update-color-data color file-id))))))}
:remove
(fn []
diff --git a/frontend/src/app/plugins/parser.cljs b/frontend/src/app/plugins/parser.cljs
index 4731528abe..8d884350dd 100644
--- a/frontend/src/app/plugins/parser.cljs
+++ b/frontend/src/app/plugins/parser.cljs
@@ -111,28 +111,36 @@
;; export interface Color {
;; id?: string;
+;; fileId?: string;
+;; refId?: string; // deprecated
+;; refFile?: string; // deprecated
;; name?: string;
;; path?: string;
;; color?: string;
;; opacity?: number;
-;; refId?: string;
-;; refFile?: string;
;; gradient?: Gradient;
;; image?: ImageData;
;; }
+(defn parse-color-data
+ [^js color]
+ (when (some? color)
+ (let [id (or (obj/get color "id") (obj/get color "refId"))
+ file-id (or (obj/get color "fileId") (obj/get color "refFile"))]
+ (d/without-nils
+ {:id (parse-id id)
+ :file-id (parse-id file-id)
+ :color (-> (obj/get color "color") parse-hex)
+ :opacity (obj/get color "opacity")
+ :gradient (-> (obj/get color "gradient") parse-gradient)
+ :image (-> (obj/get color "image") parse-image-data)}))))
+
(defn parse-color
[^js color]
(when (some? color)
(d/without-nils
- {:id (-> (obj/get color "id") parse-id)
- :name (obj/get color "name")
- :path (obj/get color "path")
- :color (-> (obj/get color "color") parse-hex)
- :opacity (obj/get color "opacity")
- :ref-id (-> (obj/get color "refId") parse-id)
- :ref-file (-> (obj/get color "refFile") parse-id)
- :gradient (-> (obj/get color "gradient") parse-gradient)
- :image (-> (obj/get color "image") parse-image-data)})))
+ (-> (parse-color-data color)
+ (assoc :name (obj/get color "name")
+ :path (obj/get color "path"))))))
;; export interface Shadow {
;; id?: string;
diff --git a/frontend/text-editor/src/editor/content/dom/Content.js b/frontend/text-editor/src/editor/content/dom/Content.js
index 2c28f269fb..1b2ca84edd 100644
--- a/frontend/text-editor/src/editor/content/dom/Content.js
+++ b/frontend/text-editor/src/editor/content/dom/Content.js
@@ -6,7 +6,7 @@
* Copyright (c) KALEIDOS INC
*/
-import { createInline } from "./Inline.js";
+import { createInline, isLikeInline } from "./Inline.js";
import {
createEmptyParagraph,
createParagraph,
@@ -14,6 +14,31 @@ import {
} from "./Paragraph.js";
import { isDisplayBlock, normalizeStyles } from "./Style.js";
+/**
+ * Returns if the content fragment should be treated as
+ * inline content and not a paragraphed one.
+ *
+ * @returns {boolean}
+ */
+function isContentFragmentFromDocumentInline(document) {
+ const nodeIterator = document.createNodeIterator(
+ document.documentElement,
+ NodeFilter.SHOW_ELEMENT,
+ );
+ let currentNode = nodeIterator.nextNode();
+ while (currentNode) {
+ if (["HTML", "HEAD", "BODY"].includes(currentNode.nodeName)) {
+ currentNode = nodeIterator.nextNode();
+ continue;
+ }
+
+ if (!isLikeInline(currentNode)) return false;
+
+ currentNode = nodeIterator.nextNode();
+ }
+ return true;
+}
+
/**
* Maps any HTML into a valid content DOM element.
*
@@ -58,6 +83,13 @@ export function mapContentFragmentFromDocument(document, root, styleDefaults) {
}
fragment.appendChild(currentParagraph);
+ if (fragment.children.length === 1) {
+ const isContentInline = isContentFragmentFromDocumentInline(document);
+ if (isContentInline) {
+ currentParagraph.dataset.inline = "force";
+ }
+ }
+
return fragment;
}
diff --git a/frontend/text-editor/src/editor/controllers/SelectionController.js b/frontend/text-editor/src/editor/controllers/SelectionController.js
index 0329de9432..4f8e49ca9d 100644
--- a/frontend/text-editor/src/editor/controllers/SelectionController.js
+++ b/frontend/text-editor/src/editor/controllers/SelectionController.js
@@ -1034,7 +1034,23 @@ export class SelectionController extends EventTarget {
* @param {DocumentFragment} fragment
*/
insertPaste(fragment) {
- const numParagraphs = fragment.children.length;
+ if (fragment.children.length === 1
+ && fragment.firstElementChild?.dataset?.inline === "force"
+ ) {
+ if (this.isInlineStart) {
+ this.focusInline.before(...fragment.firstElementChild.children)
+ } else if (this.isInlineEnd) {
+ this.focusInline.after(...fragment.firstElementChild.children);
+ } else {
+ const newInline = splitInline(
+ this.focusInline,
+ this.focusOffset
+ )
+ this.focusInline.after(...fragment.firstElementChild.children, newInline)
+ }
+ return;
+ }
+
if (this.isParagraphStart) {
this.focusParagraph.before(fragment);
} else if (this.isParagraphEnd) {
@@ -1055,7 +1071,6 @@ export class SelectionController extends EventTarget {
* @param {DocumentFragment} fragment
*/
replaceWithPaste(fragment) {
- const numParagraphs = fragment.children.length;
this.removeSelected();
this.insertPaste(fragment);
}
diff --git a/frontend/text-editor/src/editor/controllers/SelectionController.test.js b/frontend/text-editor/src/editor/controllers/SelectionController.test.js
index 786c9a18de..c44989f692 100644
--- a/frontend/text-editor/src/editor/controllers/SelectionController.test.js
+++ b/frontend/text-editor/src/editor/controllers/SelectionController.test.js
@@ -246,6 +246,251 @@ describe("SelectionController", () => {
);
});
+ test("`insertPaste` should insert a paragraph from a pasted fragment (at start)", () => {
+ const textEditorMock =
+ TextEditorMock.createTextEditorMockWithText(", World!");
+ const root = textEditorMock.root;
+ const selection = document.getSelection();
+ const selectionController = new SelectionController(
+ textEditorMock,
+ selection,
+ );
+ focus(selection, textEditorMock, root.firstChild.firstChild.firstChild, 0);
+ const paragraph = createParagraph([createInline(new Text("Hello"))]);
+ const fragment = document.createDocumentFragment();
+ fragment.append(paragraph);
+
+ selectionController.insertPaste(fragment);
+ expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.dataset.itype).toBe("root");
+ expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.firstChild.dataset.itype).toBe("paragraph");
+ expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(
+ HTMLSpanElement,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(
+ "inline",
+ );
+ expect(textEditorMock.root.textContent).toBe("Hello, World!");
+ expect(textEditorMock.root.firstChild.firstChild.firstChild).toBeInstanceOf(
+ Text,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe(
+ "Hello",
+ );
+ expect(
+ textEditorMock.root.lastChild.firstChild.firstChild.nodeValue,
+ ).toBe(", World!");
+ });
+
+ test("`insertPaste` should insert a paragraph from a pasted fragment (at middle)", () => {
+ const textEditorMock =
+ TextEditorMock.createTextEditorMockWithText("Lorem dolor");
+ const root = textEditorMock.root;
+ const selection = document.getSelection();
+ const selectionController = new SelectionController(
+ textEditorMock,
+ selection,
+ );
+ focus(selection, textEditorMock, root.firstChild.firstChild.firstChild, "Lorem ".length);
+ const paragraph = createParagraph([createInline(new Text("ipsum "))]);
+ const fragment = document.createDocumentFragment();
+ fragment.append(paragraph);
+
+ selectionController.insertPaste(fragment);
+ expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.dataset.itype).toBe("root");
+ expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.firstChild.dataset.itype).toBe("paragraph");
+ expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(
+ HTMLSpanElement,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(
+ "inline",
+ );
+ expect(textEditorMock.root.textContent).toBe("Lorem ipsum dolor");
+ expect(textEditorMock.root.firstChild.firstChild.firstChild).toBeInstanceOf(
+ Text,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe(
+ "Lorem ",
+ );
+ expect(textEditorMock.root.children.item(1).firstChild.firstChild.nodeValue).toBe(
+ "ipsum ",
+ );
+ expect(textEditorMock.root.lastChild.firstChild.firstChild.nodeValue).toBe(
+ "dolor",
+ );
+ });
+
+ test("`insertPaste` should insert a paragraph from a pasted fragment (at end)", () => {
+ const textEditorMock = TextEditorMock.createTextEditorMockWithText("Hello");
+ const root = textEditorMock.root;
+ const selection = document.getSelection();
+ const selectionController = new SelectionController(
+ textEditorMock,
+ selection,
+ );
+ focus(
+ selection,
+ textEditorMock,
+ root.firstChild.firstChild.firstChild,
+ "Hello".length,
+ );
+ const paragraph = createParagraph([createInline(new Text(", World!"))]);
+ const fragment = document.createDocumentFragment();
+ fragment.append(paragraph);
+
+ selectionController.insertPaste(fragment);
+ expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.dataset.itype).toBe("root");
+ expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.firstChild.dataset.itype).toBe("paragraph");
+ expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(
+ HTMLSpanElement,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(
+ "inline",
+ );
+ expect(textEditorMock.root.textContent).toBe("Hello, World!");
+ expect(textEditorMock.root.firstChild.firstChild.firstChild).toBeInstanceOf(
+ Text,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe(
+ "Hello",
+ );
+ expect(
+ textEditorMock.root.lastChild.firstChild.firstChild.nodeValue,
+ ).toBe(", World!");
+ });
+
+ test("`insertPaste` should insert an inline from a pasted fragment (at start)", () => {
+ const textEditorMock = TextEditorMock.createTextEditorMockWithText(", World!");
+ const root = textEditorMock.root;
+ const selection = document.getSelection();
+ const selectionController = new SelectionController(
+ textEditorMock,
+ selection,
+ );
+ focus(
+ selection,
+ textEditorMock,
+ root.firstChild.firstChild.firstChild,
+ 0,
+ );
+ const paragraph = createParagraph([createInline(new Text("Hello"))]);
+ paragraph.dataset.inline = "force";
+ const fragment = document.createDocumentFragment();
+ fragment.append(paragraph);
+
+ selectionController.insertPaste(fragment);
+ expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.dataset.itype).toBe("root");
+ expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.firstChild.dataset.itype).toBe("paragraph");
+ expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(
+ HTMLSpanElement,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(
+ "inline",
+ );
+ expect(textEditorMock.root.textContent).toBe("Hello, World!");
+ expect(textEditorMock.root.firstChild.firstChild.firstChild).toBeInstanceOf(
+ Text,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe(
+ "Hello",
+ );
+ expect(
+ textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue,
+ ).toBe(", World!");
+ });
+
+ test("`insertPaste` should insert an inline from a pasted fragment (at middle)", () => {
+ const textEditorMock =
+ TextEditorMock.createTextEditorMockWithText("Lorem dolor");
+ const root = textEditorMock.root;
+ const selection = document.getSelection();
+ const selectionController = new SelectionController(
+ textEditorMock,
+ selection,
+ );
+ focus(selection, textEditorMock, root.firstChild.firstChild.firstChild, "Lorem ".length);
+ const paragraph = createParagraph([createInline(new Text("ipsum "))]);
+ paragraph.dataset.inline = "force";
+ const fragment = document.createDocumentFragment();
+ fragment.append(paragraph);
+
+ selectionController.insertPaste(fragment);
+ expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.dataset.itype).toBe("root");
+ expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.firstChild.dataset.itype).toBe("paragraph");
+ expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(
+ HTMLSpanElement,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(
+ "inline",
+ );
+ expect(textEditorMock.root.textContent).toBe("Lorem ipsum dolor");
+ expect(textEditorMock.root.firstChild.firstChild.firstChild).toBeInstanceOf(
+ Text,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe(
+ "Lorem ",
+ );
+ expect(textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue).toBe(
+ "ipsum ",
+ );
+ expect(
+ textEditorMock.root.firstChild.children.item(2).firstChild.nodeValue,
+ ).toBe("dolor");
+ });
+
+ test("`insertPaste` should insert an inline from a pasted fragment (at end)", () => {
+ const textEditorMock = TextEditorMock.createTextEditorMockWithText("Hello");
+ const root = textEditorMock.root;
+ const selection = document.getSelection();
+ const selectionController = new SelectionController(
+ textEditorMock,
+ selection,
+ );
+ focus(
+ selection,
+ textEditorMock,
+ root.firstChild.firstChild.firstChild,
+ "Hello".length,
+ );
+ const paragraph = createParagraph([
+ createInline(new Text(", World!"))
+ ]);
+ paragraph.dataset.inline = "force";
+ const fragment = document.createDocumentFragment();
+ fragment.append(paragraph);
+
+ selectionController.insertPaste(fragment);
+ expect(textEditorMock.root).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.dataset.itype).toBe("root");
+ expect(textEditorMock.root.firstChild).toBeInstanceOf(HTMLDivElement);
+ expect(textEditorMock.root.firstChild.dataset.itype).toBe("paragraph");
+ expect(textEditorMock.root.firstChild.firstChild).toBeInstanceOf(
+ HTMLSpanElement,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.dataset.itype).toBe(
+ "inline",
+ );
+ expect(textEditorMock.root.textContent).toBe("Hello, World!");
+ expect(textEditorMock.root.firstChild.firstChild.firstChild).toBeInstanceOf(
+ Text,
+ );
+ expect(textEditorMock.root.firstChild.firstChild.firstChild.nodeValue).toBe(
+ "Hello",
+ );
+ expect(textEditorMock.root.firstChild.children.item(1).firstChild.nodeValue).toBe(
+ ", World!",
+ );
+ });
+
test("`removeBackwardText` should remove text in backward direction (backspace)", () => {
const textEditorMock =
TextEditorMock.createTextEditorMockWithText("Hello, World!");
diff --git a/frontend/translations/en.po b/frontend/translations/en.po
index 2c0b4b84a7..4029acdd2d 100644
--- a/frontend/translations/en.po
+++ b/frontend/translations/en.po
@@ -170,7 +170,7 @@ msgstr "The open-source solution for design and prototyping."
msgid "auth.terms-and-privacy-agreement"
msgstr "I agree to the [terms of service](%s) and [privacy policy](%s)."
-#: src/app/main/ui/auth/register.cljs:290, src/app/main/ui/dashboard/sidebar.cljs:1022, src/app/main/ui/workspace/main_menu.cljs:154
+#: src/app/main/ui/auth/register.cljs:290, src/app/main/ui/dashboard/sidebar.cljs:1022, src/app/main/ui/workspace/main_menu.cljs:155
msgid "auth.terms-of-service"
msgstr "Terms of service"
@@ -193,7 +193,7 @@ msgstr "Work email"
msgid "branding-illustrations-marketing-pieces"
msgstr "...branding, illustrations, marketing pieces, etc."
-#: src/app/main/ui/workspace/libraries.cljs:229
+#: src/app/main/ui/workspace/libraries.cljs:280
msgid "common.publish"
msgstr "Publish"
@@ -265,7 +265,7 @@ msgstr "Share prototypes"
msgid "common.share-link.view-all"
msgstr "Select All"
-#: src/app/main/ui/workspace/libraries.cljs:225
+#: src/app/main/ui/workspace/libraries.cljs:276
msgid "common.unpublish"
msgstr "Unpublish"
@@ -392,22 +392,19 @@ msgstr "The token will expire on %s"
msgid "dashboard.access-tokens.token-will-not-expire"
msgstr "The token has no expiration date"
-#: src/app/main/ui/dashboard/file_menu.cljs:311, src/app/main/ui/workspace/main_menu.cljs:585
+#: src/app/main/ui/dashboard/placeholder.cljs:48
+msgid "dashboard.add-file"
+msgstr "Add file"
+
+#: src/app/main/ui/dashboard/file_menu.cljs:300, src/app/main/ui/workspace/main_menu.cljs:614
msgid "dashboard.add-shared"
msgstr "Add as Shared Library"
-#: src/app/main/ui/workspace/main_menu.cljs:607
-msgid "dashboard.show-version-history"
-msgstr "Version history"
-
-msgid "dashboard.create-version-menu"
-msgstr "Pin this version"
-
#: src/app/main/ui/settings/profile.cljs:72
msgid "dashboard.change-email"
msgstr "Change email"
-#: src/app/main/data/dashboard.cljs:771, src/app/main/data/dashboard.cljs:991
+#: src/app/main/data/dashboard.cljs:773, src/app/main/data/dashboard.cljs:993
msgid "dashboard.copy-suffix"
msgstr "(copy)"
@@ -415,6 +412,10 @@ msgstr "(copy)"
msgid "dashboard.create-new-team"
msgstr "Create new team"
+#: src/app/main/ui/workspace/main_menu.cljs:625
+msgid "dashboard.create-version-menu"
+msgstr "Pin this version"
+
#: src/app/main/ui/components/context_menu_a11y.cljs:284, src/app/main/ui/dashboard/sidebar.cljs:646
msgid "dashboard.default-team-name"
msgstr "Your Penpot"
@@ -423,19 +424,19 @@ msgstr "Your Penpot"
msgid "dashboard.delete-team"
msgstr "Delete team"
-#: src/app/main/ui/dashboard/file_menu.cljs:318, src/app/main/ui/dashboard/file_menu.cljs:323, src/app/main/ui/workspace/main_menu.cljs:603, src/app/main/ui/workspace/main_menu.cljs:612
+#: src/app/main/ui/dashboard/file_menu.cljs:307, src/app/main/ui/dashboard/file_menu.cljs:312, src/app/main/ui/workspace/main_menu.cljs:652, src/app/main/ui/workspace/main_menu.cljs:661
msgid "dashboard.download-binary-file"
msgstr "Download Penpot file (.penpot)"
-#: src/app/main/ui/dashboard/file_menu.cljs:328, src/app/main/ui/workspace/main_menu.cljs:621
+#: src/app/main/ui/dashboard/file_menu.cljs:317, src/app/main/ui/workspace/main_menu.cljs:670
msgid "dashboard.download-standard-file"
msgstr "Download standard file (.svg + .json)"
-#: src/app/main/ui/dashboard/file_menu.cljs:293, src/app/main/ui/dashboard/project_menu.cljs:91
+#: src/app/main/ui/dashboard/file_menu.cljs:282, src/app/main/ui/dashboard/project_menu.cljs:91
msgid "dashboard.duplicate"
msgstr "Duplicate"
-#: src/app/main/ui/dashboard/file_menu.cljs:249
+#: src/app/main/ui/dashboard/file_menu.cljs:238
msgid "dashboard.duplicate-multi"
msgstr "Duplicate %s files"
@@ -455,7 +456,7 @@ msgstr "Once a project member creates a file, it will be displayed here."
msgid "dashboard.empty-placeholder-files-title"
msgstr "No files yet."
-#: src/app/main/ui/dashboard/placeholder.cljs:39
+#: src/app/main/ui/dashboard/placeholder.cljs:40
#, markdown
msgid "dashboard.empty-placeholder-libraries"
msgstr ""
@@ -471,23 +472,23 @@ msgstr ""
"add from our [Libraries & "
"templates](https://penpot.app/libraries-templates)."
-#: src/app/main/ui/dashboard/placeholder.cljs:35
+#: src/app/main/ui/dashboard/placeholder.cljs:36
msgid "dashboard.empty-placeholder-libraries-subtitle-viewer-role"
msgstr "Libraries added to the project will appear here."
-#: src/app/main/ui/dashboard/placeholder.cljs:32
+#: src/app/main/ui/dashboard/placeholder.cljs:33
msgid "dashboard.empty-placeholder-libraries-title"
msgstr "No libraries yet."
-#: src/app/main/ui/dashboard/file_menu.cljs:259, src/app/main/ui/dashboard/file_menu.cljs:264
+#: src/app/main/ui/dashboard/file_menu.cljs:248, src/app/main/ui/dashboard/file_menu.cljs:253
msgid "dashboard.export-binary-multi"
msgstr "Download %s Penpot files (.penpot)"
-#: src/app/main/ui/workspace/main_menu.cljs:629
+#: src/app/main/ui/workspace/main_menu.cljs:678
msgid "dashboard.export-frames"
msgstr "Export boards as PDF"
-#: src/app/main/ui/exports/assets.cljs:206
+#: src/app/main/ui/exports/assets.cljs:199
msgid "dashboard.export-frames.title"
msgstr "Export as PDF"
@@ -495,33 +496,33 @@ msgstr "Export as PDF"
msgid "dashboard.export-multi"
msgstr "Export Penpot %s files"
-#: src/app/main/ui/exports/assets.cljs:113
+#: src/app/main/ui/exports/assets.cljs:106
msgid "dashboard.export-multiple.selected"
msgstr "%s of %s elements selected"
-#: src/app/main/ui/workspace/main_menu.cljs:591
+#: src/app/main/ui/workspace/main_menu.cljs:640
msgid "dashboard.export-shapes"
msgstr "Export"
-#: src/app/main/ui/exports/assets.cljs:184
+#: src/app/main/ui/exports/assets.cljs:177
msgid "dashboard.export-shapes.how-to"
msgstr ""
"You can add export settings to elements from the design properties (at the "
"bottom of the right sidebar)."
-#: src/app/main/ui/exports/assets.cljs:188
+#: src/app/main/ui/exports/assets.cljs:181
msgid "dashboard.export-shapes.how-to-link"
msgstr "Info how to set exports at Penpot."
-#: src/app/main/ui/exports/assets.cljs:183
+#: src/app/main/ui/exports/assets.cljs:176
msgid "dashboard.export-shapes.no-elements"
msgstr "There are no elements with export settings."
-#: src/app/main/ui/exports/assets.cljs:194
+#: src/app/main/ui/exports/assets.cljs:187
msgid "dashboard.export-shapes.title"
msgstr "Export selection"
-#: src/app/main/ui/dashboard/file_menu.cljs:269
+#: src/app/main/ui/dashboard/file_menu.cljs:258
msgid "dashboard.export-standard-multi"
msgstr "Download %s standard files (.svg + .json)"
@@ -633,7 +634,7 @@ msgstr ""
msgid "dashboard.import"
msgstr "Import Penpot files"
-#: src/app/main/ui/dashboard/import.cljs:288, src/app/worker/import.cljs:843, src/app/worker/import.cljs:846
+#: src/app/main/ui/dashboard/import.cljs:288, src/app/worker/import.cljs:851, src/app/worker/import.cljs:854
msgid "dashboard.import.analyze-error"
msgstr "Oops! We couldn't import this file"
@@ -687,7 +688,7 @@ msgstr "Uploading file: %s"
msgid "dashboard.invite-profile"
msgstr "Invite people"
-#: src/app/main/ui/dashboard/sidebar.cljs:547, src/app/main/ui/dashboard/sidebar.cljs:556, src/app/main/ui/dashboard/sidebar.cljs:563, src/app/main/ui/dashboard/team.cljs:341
+#: src/app/main/ui/dashboard/sidebar.cljs:547, src/app/main/ui/dashboard/sidebar.cljs:556, src/app/main/ui/dashboard/sidebar.cljs:563, src/app/main/ui/dashboard/team.cljs:344
msgid "dashboard.leave-team"
msgstr "Leave team"
@@ -699,7 +700,7 @@ msgstr "Libraries & Templates"
msgid "dashboard.libraries-and-templates.explore"
msgstr "Explore more of them and know how to contribute"
-#: src/app/main/ui/dashboard/import.cljs:358
+#: src/app/main/ui/dashboard/import.cljs:358, src/app/main/ui/workspace/libraries.cljs:123
msgid "dashboard.libraries-and-templates.import-error"
msgstr "There was a problem importing the template. The template wasn't imported."
@@ -707,7 +708,7 @@ msgstr "There was a problem importing the template. The template wasn't imported
msgid "dashboard.libraries-title"
msgstr "Libraries"
-#: src/app/main/ui/dashboard/placeholder.cljs:54
+#: src/app/main/ui/dashboard/placeholder.cljs:55
msgid "dashboard.loading-files"
msgstr "loading your files …"
@@ -715,15 +716,15 @@ msgstr "loading your files …"
msgid "dashboard.loading-fonts"
msgstr "loading your fonts …"
-#: src/app/main/ui/dashboard/file_menu.cljs:301, src/app/main/ui/dashboard/project_menu.cljs:100
+#: src/app/main/ui/dashboard/file_menu.cljs:290, src/app/main/ui/dashboard/project_menu.cljs:100
msgid "dashboard.move-to"
msgstr "Move to"
-#: src/app/main/ui/dashboard/file_menu.cljs:254
+#: src/app/main/ui/dashboard/file_menu.cljs:243
msgid "dashboard.move-to-multi"
msgstr "Move %s files to"
-#: src/app/main/ui/dashboard/file_menu.cljs:233
+#: src/app/main/ui/dashboard/file_menu.cljs:222
msgid "dashboard.move-to-other-team"
msgstr "Move to other team"
@@ -731,7 +732,7 @@ msgstr "Move to other team"
msgid "dashboard.new-file"
msgstr "+ New File"
-#: src/app/main/data/dashboard.cljs:966, src/app/main/data/dashboard.cljs:1189
+#: src/app/main/data/dashboard.cljs:968, src/app/main/data/dashboard.cljs:1195
msgid "dashboard.new-file-prefix"
msgstr "New File"
@@ -739,7 +740,7 @@ msgstr "New File"
msgid "dashboard.new-project"
msgstr "+ New project"
-#: src/app/main/data/dashboard.cljs:735, src/app/main/data/dashboard.cljs:1192
+#: src/app/main/data/dashboard.cljs:737, src/app/main/data/dashboard.cljs:1198
msgid "dashboard.new-project-prefix"
msgstr "New Project"
@@ -763,11 +764,11 @@ msgstr "Your email address has been verified successfully"
msgid "dashboard.notifications.password-saved"
msgstr "Password saved successfully!"
-#: src/app/main/ui/dashboard/team.cljs:1119
+#: src/app/main/ui/dashboard/team.cljs:1122
msgid "dashboard.num-of-members"
msgstr "%s members"
-#: src/app/main/ui/dashboard/file_menu.cljs:284
+#: src/app/main/ui/dashboard/file_menu.cljs:273
msgid "dashboard.open-in-new-tab"
msgstr "Open file in a new tab"
@@ -779,19 +780,19 @@ msgstr "Options"
msgid "dashboard.password-change"
msgstr "Change password"
-#: src/app/main/data/common.cljs:205
+#: src/app/main/data/common.cljs:202
msgid "dashboard.permissions-change.admin"
msgstr "You are now an admin on this team."
-#: src/app/main/data/common.cljs:204
+#: src/app/main/data/common.cljs:201
msgid "dashboard.permissions-change.editor"
msgstr "You are now an editor on this team."
-#: src/app/main/data/common.cljs:206
+#: src/app/main/data/common.cljs:203
msgid "dashboard.permissions-change.owner"
msgstr "You are now owner on this team."
-#: src/app/main/data/common.cljs:203
+#: src/app/main/data/common.cljs:200
msgid "dashboard.permissions-change.viewer"
msgstr "You are now a viewer on this team."
@@ -812,7 +813,7 @@ msgstr "Want to remove your account?"
msgid "dashboard.remove-shared"
msgstr "Remove as Shared Library"
-#: src/app/main/data/common.cljs:233
+#: src/app/main/data/common.cljs:235
msgid "dashboard.removed-from-team"
msgstr "You are not part of the team “%s“ anymore."
@@ -840,7 +841,11 @@ msgstr "Select theme"
msgid "dashboard.show-all-files"
msgstr "Show all files"
-#: src/app/main/ui/dashboard/file_menu.cljs:101
+#: src/app/main/ui/workspace/main_menu.cljs:632
+msgid "dashboard.show-version-history"
+msgstr "Version history"
+
+#: src/app/main/ui/dashboard/file_menu.cljs:98
msgid "dashboard.success-delete-file"
msgid_plural "dashboard.success-delete-file"
msgstr[0] "Your file has been deleted successfully"
@@ -850,7 +855,7 @@ msgstr[1] "Your files have been deleted successfully"
msgid "dashboard.success-delete-project"
msgstr "Your project has been deleted successfully"
-#: src/app/main/ui/dashboard/file_menu.cljs:96
+#: src/app/main/ui/dashboard/file_menu.cljs:93
msgid "dashboard.success-duplicate-file"
msgid_plural "dashboard.success-delete-file"
msgstr[0] "Your file has been duplicated successfully"
@@ -860,11 +865,11 @@ msgstr[1] "Your files have been duplicated successfully"
msgid "dashboard.success-duplicate-project"
msgstr "Your project has been duplicated successfully"
-#: src/app/main/ui/dashboard/file_menu.cljs:135, src/app/main/ui/dashboard/grid.cljs:575, src/app/main/ui/dashboard/sidebar.cljs:152
+#: src/app/main/ui/dashboard/file_menu.cljs:132, src/app/main/ui/dashboard/grid.cljs:574, src/app/main/ui/dashboard/sidebar.cljs:152
msgid "dashboard.success-move-file"
msgstr "Your file has been moved successfully"
-#: src/app/main/ui/dashboard/file_menu.cljs:134
+#: src/app/main/ui/dashboard/file_menu.cljs:131
msgid "dashboard.success-move-files"
msgstr "Your files have been moved successfully"
@@ -872,15 +877,15 @@ msgstr "Your files have been moved successfully"
msgid "dashboard.success-move-project"
msgstr "Your project has been moved successfully"
-#: src/app/main/ui/dashboard/team.cljs:1090
+#: src/app/main/ui/dashboard/team.cljs:1093
msgid "dashboard.team-info"
msgstr "Team info"
-#: src/app/main/ui/dashboard/team.cljs:1108
+#: src/app/main/ui/dashboard/team.cljs:1111
msgid "dashboard.team-members"
msgstr "Team members"
-#: src/app/main/ui/dashboard/team.cljs:1123
+#: src/app/main/ui/dashboard/team.cljs:1126
msgid "dashboard.team-projects"
msgstr "Team projects"
@@ -896,7 +901,7 @@ msgstr "Search results"
msgid "dashboard.type-something"
msgstr "Type to search results"
-#: src/app/main/ui/dashboard/file_menu.cljs:308, src/app/main/ui/workspace/main_menu.cljs:578
+#: src/app/main/ui/dashboard/file_menu.cljs:297, src/app/main/ui/workspace/main_menu.cljs:606
msgid "dashboard.unpublish-shared"
msgstr "Unpublish Library"
@@ -904,42 +909,42 @@ msgstr "Unpublish Library"
msgid "dashboard.update-settings"
msgstr "Update settings"
-#: src/app/main/ui/dashboard/team.cljs:899
+#: src/app/main/ui/dashboard/team.cljs:902
msgid "dashboard.webhooks.active"
msgstr "Is active"
-#: src/app/main/ui/dashboard/team.cljs:900
+#: src/app/main/ui/dashboard/team.cljs:903
msgid "dashboard.webhooks.active.explain"
msgstr "When this hook is triggered event details will be delivered"
-#: src/app/main/ui/dashboard/team.cljs:944
+#: src/app/main/ui/dashboard/team.cljs:947
msgid "dashboard.webhooks.cant-edit"
msgstr "You only can delete or modify webhooks created by you."
-#: src/app/main/ui/dashboard/team.cljs:890
+#: src/app/main/ui/dashboard/team.cljs:893
msgid "dashboard.webhooks.content-type"
msgstr "Content type"
-#: src/app/main/ui/dashboard/team.cljs:923
+#: src/app/main/ui/dashboard/team.cljs:926
msgid "dashboard.webhooks.create"
msgstr "Create webhook"
-#: src/app/main/ui/dashboard/team.cljs:813
+#: src/app/main/ui/dashboard/team.cljs:816
msgid "dashboard.webhooks.create.success"
msgstr "Webhook created successfully."
-#: src/app/main/ui/dashboard/team.cljs:920
+#: src/app/main/ui/dashboard/team.cljs:923
msgid "dashboard.webhooks.description"
msgstr ""
"Webhooks are a simple way to allow other websites and apps to be notified "
"when certain events happen at Penpot. We’ll send a POST request to each of "
"the URLs you provide."
-#: src/app/main/ui/dashboard/team.cljs:1043
+#: src/app/main/ui/dashboard/team.cljs:1046
msgid "dashboard.webhooks.empty.add-one"
msgstr "Press the button \"Add webhook\" to add one."
-#: src/app/main/ui/dashboard/team.cljs:1042
+#: src/app/main/ui/dashboard/team.cljs:1045
msgid "dashboard.webhooks.empty.no-webhooks"
msgstr "No webhooks created so far."
@@ -959,7 +964,7 @@ msgstr "Email"
msgid "dashboard.your-name"
msgstr "Your name"
-#: src/app/main/ui/dashboard/file_menu.cljs:40, src/app/main/ui/dashboard/fonts.cljs:34, src/app/main/ui/dashboard/libraries.cljs:44, src/app/main/ui/dashboard/projects.cljs:341, src/app/main/ui/dashboard/search.cljs:31, src/app/main/ui/dashboard/sidebar.cljs:312, src/app/main/ui/dashboard/team.cljs:526, src/app/main/ui/dashboard/team.cljs:766, src/app/main/ui/dashboard/team.cljs:1029, src/app/main/ui/dashboard/team.cljs:1076
+#: src/app/main/ui/dashboard/file_menu.cljs:39, src/app/main/ui/dashboard/fonts.cljs:34, src/app/main/ui/dashboard/libraries.cljs:44, src/app/main/ui/dashboard/projects.cljs:341, src/app/main/ui/dashboard/search.cljs:31, src/app/main/ui/dashboard/sidebar.cljs:312, src/app/main/ui/dashboard/team.cljs:529, src/app/main/ui/dashboard/team.cljs:769, src/app/main/ui/dashboard/team.cljs:1032, src/app/main/ui/dashboard/team.cljs:1079
msgid "dashboard.your-penpot"
msgstr "Your Penpot"
@@ -991,7 +996,7 @@ msgstr "Ok"
msgid "ds.confirm-title"
msgstr "Are you sure?"
-#: src/app/main/data/users.cljs:733
+#: src/app/main/data/users.cljs:734
msgid "errors.auth-provider-not-allowed"
msgstr "Auth provider not allowed for this profile"
@@ -1015,7 +1020,7 @@ msgstr "The fonts %s could not be loaded"
msgid "errors.cannot-upload"
msgstr "Cannot upload the media file."
-#: src/app/main/data/workspace.cljs:1672
+#: src/app/main/data/workspace.cljs:1678
msgid "errors.clipboard-not-implemented"
msgstr "Your browser cannot do this operation"
@@ -1031,11 +1036,11 @@ msgstr "Email already validated."
msgid "errors.email-as-password"
msgstr "You can't use your email as password"
-#: src/app/main/data/users.cljs:735, src/app/main/ui/auth/register.cljs:54
+#: src/app/main/data/users.cljs:736, src/app/main/ui/auth/register.cljs:54
msgid "errors.email-domain-not-allowed"
msgstr "Domain not allowed"
-#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:57, src/app/main/ui/auth/register.cljs:60, src/app/main/ui/dashboard/team.cljs:615, src/app/main/ui/settings/change_email.cljs:37
+#: src/app/main/ui/auth/recovery_request.cljs:57, src/app/main/ui/auth/register.cljs:57, src/app/main/ui/auth/register.cljs:60, src/app/main/ui/dashboard/team.cljs:618, src/app/main/ui/settings/change_email.cljs:37
msgid "errors.email-has-permanent-bounces"
msgstr "The email «%s» has many permanent bounce reports."
@@ -1074,7 +1079,7 @@ msgstr ""
"features of the file you are trying to open. Migrations for '%s' need to be "
"applied before the file can be opened."
-#: src/app/main/data/users.cljs:741, src/app/main/ui/auth/login.cljs:80, src/app/main/ui/auth/login.cljs:121, src/app/main/ui/auth/register.cljs:66, src/app/main/ui/auth/register.cljs:207, src/app/main/ui/auth/verify_token.cljs:91, src/app/main/ui/dashboard/team.cljs:193, src/app/main/ui/onboarding/team_choice.cljs:112, src/app/main/ui/settings/access_tokens.cljs:80, src/app/main/ui/settings/feedback.cljs:49
+#: src/app/main/data/users.cljs:742, src/app/main/ui/auth/login.cljs:80, src/app/main/ui/auth/login.cljs:121, src/app/main/ui/auth/register.cljs:66, src/app/main/ui/auth/register.cljs:207, src/app/main/ui/auth/verify_token.cljs:91, src/app/main/ui/dashboard/team.cljs:193, src/app/main/ui/onboarding/team_choice.cljs:112, src/app/main/ui/settings/access_tokens.cljs:80, src/app/main/ui/settings/feedback.cljs:49
msgid "errors.generic"
msgstr "Something wrong has happened."
@@ -1130,7 +1135,7 @@ msgstr "Seems that the contents of the image does not match the file extension."
msgid "errors.media-type-not-allowed"
msgstr "Seems that this is not a valid image."
-#: src/app/main/ui/dashboard/team.cljs:610
+#: src/app/main/ui/dashboard/team.cljs:613
msgid "errors.member-is-muted"
msgstr "The profile you inviting has emails muted (spam reports or high bounces)."
@@ -1153,15 +1158,15 @@ msgstr "Password should at least be 8 characters"
msgid "errors.paste-data-validation"
msgstr "Invalid data in clipboard"
-#: src/app/main/data/users.cljs:731, src/app/main/ui/auth/login.cljs:102, src/app/main/ui/auth/login.cljs:110
+#: src/app/main/data/users.cljs:732, src/app/main/ui/auth/login.cljs:102, src/app/main/ui/auth/login.cljs:110
msgid "errors.profile-blocked"
msgstr "The profile is blocked"
-#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:176, src/app/main/ui/dashboard/team.cljs:606, src/app/main/ui/onboarding/team_choice.cljs:96, src/app/main/ui/settings/change_email.cljs:33
+#: src/app/main/ui/auth/recovery_request.cljs:53, src/app/main/ui/dashboard/team.cljs:176, src/app/main/ui/dashboard/team.cljs:609, src/app/main/ui/onboarding/team_choice.cljs:96, src/app/main/ui/settings/change_email.cljs:33
msgid "errors.profile-is-muted"
msgstr "Your profile has emails muted (spam reports or high bounces)."
-#: src/app/main/data/users.cljs:729, src/app/main/ui/auth/register.cljs:51
+#: src/app/main/data/users.cljs:730, src/app/main/ui/auth/register.cljs:51
msgid "errors.registration-disabled"
msgstr "The registration is currently disabled."
@@ -1169,15 +1174,15 @@ msgstr "The registration is currently disabled."
msgid "errors.team-feature-mismatch"
msgstr "Detected incompatible feature '%s'"
-#: src/app/main/ui/dashboard/sidebar.cljs:365, src/app/main/ui/dashboard/team.cljs:389
+#: src/app/main/ui/dashboard/sidebar.cljs:365, src/app/main/ui/dashboard/team.cljs:392
msgid "errors.team-leave.insufficient-members"
msgstr "Insufficient members to leave team, you probably want to delete it."
-#: src/app/main/ui/dashboard/sidebar.cljs:368, src/app/main/ui/dashboard/team.cljs:392
+#: src/app/main/ui/dashboard/sidebar.cljs:368, src/app/main/ui/dashboard/team.cljs:395
msgid "errors.team-leave.member-does-not-exists"
msgstr "The member you try to assign does not exist."
-#: src/app/main/ui/dashboard/sidebar.cljs:371, src/app/main/ui/dashboard/team.cljs:395
+#: src/app/main/ui/dashboard/sidebar.cljs:371, src/app/main/ui/dashboard/team.cljs:398
msgid "errors.team-leave.owner-cant-leave"
msgstr "Owner can't leave team, you must reassign the owner role."
@@ -1197,31 +1202,31 @@ msgstr "Validation Error"
msgid "errors.version-not-supported"
msgstr "File has an incompatible version number"
-#: src/app/main/ui/dashboard/team.cljs:834
+#: src/app/main/ui/dashboard/team.cljs:837
msgid "errors.webhooks.connection"
msgstr "Connection error, URL not reacheable"
-#: src/app/main/ui/dashboard/team.cljs:828
+#: src/app/main/ui/dashboard/team.cljs:831
msgid "errors.webhooks.invalid-uri"
msgstr "URL does not pass validation."
-#: src/app/main/ui/dashboard/team.cljs:986
+#: src/app/main/ui/dashboard/team.cljs:989
msgid "errors.webhooks.last-delivery"
msgstr "Last delivery was not successful."
-#: src/app/main/ui/dashboard/team.cljs:830, src/app/main/ui/dashboard/team.cljs:989
+#: src/app/main/ui/dashboard/team.cljs:833, src/app/main/ui/dashboard/team.cljs:992
msgid "errors.webhooks.ssl-validation"
msgstr "Error on SSL validation."
-#: src/app/main/ui/dashboard/team.cljs:832
+#: src/app/main/ui/dashboard/team.cljs:835
msgid "errors.webhooks.timeout"
msgstr "Timeout"
-#: src/app/main/ui/dashboard/team.cljs:826
+#: src/app/main/ui/dashboard/team.cljs:829
msgid "errors.webhooks.unexpected"
msgstr "Unexpected error on validating"
-#: src/app/main/ui/dashboard/team.cljs:836, src/app/main/ui/dashboard/team.cljs:992
+#: src/app/main/ui/dashboard/team.cljs:839, src/app/main/ui/dashboard/team.cljs:995
msgid "errors.webhooks.unexpected-status"
msgstr "Unexpected status %s"
@@ -1471,71 +1476,71 @@ msgstr "Unset"
msgid "inspect.attributes.typography.text-transform.uppercase"
msgstr "Upper Case"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:152
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:157
msgid "inspect.empty.help"
msgstr "If you want to know more about design inspect visit Penpot's help center"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:155
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:160
msgid "inspect.empty.more-info"
msgstr "More info about inspect"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:147
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:152
msgid "inspect.empty.select"
msgstr "Select a shape, board or group to inspect their properties and code"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:100
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:105
msgid "inspect.tabs.code"
msgstr "Code"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:124
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:129
msgid "inspect.tabs.code.selected.circle"
msgstr "Circle"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:125
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:130
msgid "inspect.tabs.code.selected.component"
msgstr "Component"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:126
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:131
msgid "inspect.tabs.code.selected.curve"
msgstr "Curve"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:127
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:132
msgid "inspect.tabs.code.selected.frame"
msgstr "Board"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:128
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:133
msgid "inspect.tabs.code.selected.group"
msgstr "Group"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:129
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:134
msgid "inspect.tabs.code.selected.image"
msgstr "Image"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:130
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:135
msgid "inspect.tabs.code.selected.mask"
msgstr "Mask"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:119
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:124
msgid "inspect.tabs.code.selected.multiple"
msgstr "%s Selected"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:131
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:136
msgid "inspect.tabs.code.selected.path"
msgstr "Path"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:132
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:137
msgid "inspect.tabs.code.selected.rect"
msgstr "Rectangle"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:133
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:138
msgid "inspect.tabs.code.selected.svg-raw"
msgstr "SVG"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:134
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:139
msgid "inspect.tabs.code.selected.text"
msgstr "Text"
-#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:96
+#: src/app/main/ui/viewer/inspect/right_sidebar.cljs:101
msgid "inspect.tabs.info"
msgstr "Info"
@@ -1544,11 +1549,11 @@ msgstr "Info"
msgid "intersection"
msgstr ""
-#: src/app/main/ui/workspace/main_menu.cljs:162
+#: src/app/main/ui/workspace/main_menu.cljs:163
msgid "label.shortcuts"
msgstr "Shortcuts"
-#: src/app/main/data/common.cljs:93, src/app/main/ui/dashboard/import.cljs:503
+#: src/app/main/data/common.cljs:90, src/app/main/ui/dashboard/import.cljs:503
msgid "labels.accept"
msgstr "Accept"
@@ -1556,23 +1561,23 @@ msgstr "Accept"
msgid "labels.access-tokens"
msgstr "Access tokens"
-#: src/app/main/ui/dashboard/team.cljs:1005
+#: src/app/main/ui/dashboard/team.cljs:1008
msgid "labels.active"
msgstr "Active"
-#: src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1051
-#, unused
+#: src/app/main/ui/workspace/libraries.cljs:148
msgid "labels.add"
msgstr "Add"
-msgid "labels.adding"
-msgstr "Adding..."
-
#: src/app/main/ui/dashboard/fonts.cljs:176
msgid "labels.add-custom-font"
msgstr "Add custom font"
-#: src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:310, src/app/main/ui/dashboard/team.cljs:550, src/app/main/ui/dashboard/team.cljs:580, src/app/main/ui/onboarding/team_choice.cljs:66
+#: src/app/main/ui/workspace/libraries.cljs:148
+msgid "labels.adding"
+msgstr "Adding..."
+
+#: src/app/main/ui/dashboard/team.cljs:128, src/app/main/ui/dashboard/team.cljs:313, src/app/main/ui/dashboard/team.cljs:553, src/app/main/ui/dashboard/team.cljs:583, src/app/main/ui/onboarding/team_choice.cljs:66
msgid "labels.admin"
msgstr "Admin"
@@ -1603,7 +1608,7 @@ msgstr ""
msgid "labels.bad-gateway.main-message"
msgstr "Bad Gateway"
-#: src/app/main/data/common.cljs:131, src/app/main/ui/dashboard/change_owner.cljs:68, src/app/main/ui/dashboard/import.cljs:489, src/app/main/ui/dashboard/team.cljs:906, src/app/main/ui/delete_shared.cljs:35, src/app/main/ui/exports/assets.cljs:168, src/app/main/ui/exports/files.cljs:192, src/app/main/ui/settings/access_tokens.cljs:177, src/app/main/ui/viewer/share_link.cljs:205, src/app/main/ui/workspace/sidebar/assets/groups.cljs:145, src/app/main/ui/workspace/tokens/form.cljs:429, src/app/main/ui/workspace/tokens/modals/themes.cljs:203
+#: src/app/main/data/common.cljs:128, src/app/main/ui/dashboard/change_owner.cljs:68, src/app/main/ui/dashboard/import.cljs:489, src/app/main/ui/dashboard/team.cljs:909, src/app/main/ui/delete_shared.cljs:35, src/app/main/ui/exports/assets.cljs:161, src/app/main/ui/exports/files.cljs:192, src/app/main/ui/settings/access_tokens.cljs:177, src/app/main/ui/viewer/share_link.cljs:205, src/app/main/ui/workspace/sidebar/assets/groups.cljs:145, src/app/main/ui/workspace/tokens/form.cljs:429, src/app/main/ui/workspace/tokens/modals/themes.cljs:203
msgid "labels.cancel"
msgstr "Cancel"
@@ -1611,7 +1616,7 @@ msgstr "Cancel"
msgid "labels.canva"
msgstr "Canva"
-#: src/app/main/ui/dashboard/projects.cljs:96, src/app/main/ui/exports/files.cljs:210, src/app/main/ui/settings/access_tokens.cljs:172, src/app/main/ui/viewer/login.cljs:71, src/app/main/ui/viewer/share_link.cljs:176, src/app/main/ui/workspace/comments.cljs:129, src/app/main/ui/workspace/libraries.cljs:538, src/app/main/ui/workspace/sidebar/debug.cljs:40, src/app/main/ui/workspace/sidebar/layers.cljs:299, src/app/main/ui/workspace/tokens/modals/themes.cljs:366, src/app/main/ui/workspace/tokens/modals.cljs:56
+#: src/app/main/ui/dashboard/projects.cljs:96, src/app/main/ui/exports/files.cljs:210, src/app/main/ui/settings/access_tokens.cljs:172, src/app/main/ui/viewer/login.cljs:71, src/app/main/ui/viewer/share_link.cljs:176, src/app/main/ui/workspace/comments.cljs:129, src/app/main/ui/workspace/libraries.cljs:607, src/app/main/ui/workspace/sidebar/debug.cljs:40, src/app/main/ui/workspace/sidebar/layers.cljs:300, src/app/main/ui/workspace/tokens/modals/themes.cljs:366, src/app/main/ui/workspace/tokens/modals.cljs:56
msgid "labels.close"
msgstr "Close"
@@ -1623,7 +1628,7 @@ msgstr "Collapse"
msgid "labels.comments"
msgstr "Comments"
-#: src/app/main/ui/dashboard/sidebar.cljs:985, src/app/main/ui/workspace/main_menu.cljs:114
+#: src/app/main/ui/dashboard/sidebar.cljs:985, src/app/main/ui/workspace/main_menu.cljs:115
msgid "labels.community"
msgstr "Community"
@@ -1643,7 +1648,7 @@ msgstr "Continue with"
msgid "labels.continue-with-penpot"
msgstr "You can continue with a Penpot account"
-#: src/app/main/ui/dashboard/team.cljs:678
+#: src/app/main/ui/dashboard/team.cljs:681
msgid "labels.copy-invitation-link"
msgstr "Copy link"
@@ -1655,11 +1660,11 @@ msgstr "Kaleidos @2024"
msgid "labels.create"
msgstr "Create"
-#: src/app/main/ui/dashboard/team_form.cljs:101, src/app/main/ui/dashboard/team_form.cljs:121
+#: src/app/main/ui/dashboard/team_form.cljs:103, src/app/main/ui/dashboard/team_form.cljs:123
msgid "labels.create-team"
msgstr "Create new team"
-#: src/app/main/ui/dashboard/team_form.cljs:113
+#: src/app/main/ui/dashboard/team_form.cljs:115
msgid "labels.create-team.placeholder"
msgstr "Enter new team name"
@@ -1671,7 +1676,7 @@ msgstr "Custom fonts"
msgid "labels.dashboard"
msgstr "Dashboard"
-#: src/app/main/ui/dashboard/file_menu.cljs:336, src/app/main/ui/dashboard/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:332, src/app/main/ui/dashboard/fonts.cljs:346, src/app/main/ui/dashboard/project_menu.cljs:114, src/app/main/ui/dashboard/team.cljs:942, src/app/main/ui/settings/access_tokens.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:209, src/app/main/ui/workspace/sidebar/versions.cljs:152, src/app/main/ui/workspace/tokens/form.cljs:425, src/app/main/ui/workspace/tokens/modals/themes.cljs:335, src/app/main/ui/workspace/tokens/sets_context_menu.cljs:44
+#: src/app/main/ui/dashboard/file_menu.cljs:325, src/app/main/ui/dashboard/fonts.cljs:256, src/app/main/ui/dashboard/fonts.cljs:332, src/app/main/ui/dashboard/fonts.cljs:346, src/app/main/ui/dashboard/project_menu.cljs:114, src/app/main/ui/dashboard/team.cljs:945, src/app/main/ui/settings/access_tokens.cljs:198, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:209, src/app/main/ui/workspace/sidebar/versions.cljs:155, src/app/main/ui/workspace/tokens/form.cljs:425, src/app/main/ui/workspace/tokens/modals/themes.cljs:335, src/app/main/ui/workspace/tokens/sets_context_menu.cljs:44
msgid "labels.delete"
msgstr "Delete"
@@ -1683,11 +1688,11 @@ msgstr "Delete comment"
msgid "labels.delete-comment-thread"
msgstr "Delete thread"
-#: src/app/main/ui/dashboard/team.cljs:684
+#: src/app/main/ui/dashboard/team.cljs:687
msgid "labels.delete-invitation"
msgstr "Delete invitation"
-#: src/app/main/ui/dashboard/file_menu.cljs:280
+#: src/app/main/ui/dashboard/file_menu.cljs:269
msgid "labels.delete-multi-files"
msgstr "Delete %s files"
@@ -1703,11 +1708,11 @@ msgstr "Director"
msgid "labels.discard"
msgstr "Discard"
-#: src/app/main/ui/dashboard/file_menu.cljs:30, src/app/main/ui/dashboard/files.cljs:75, src/app/main/ui/dashboard/files.cljs:168, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:229, src/app/main/ui/dashboard/sidebar.cljs:791
+#: src/app/main/ui/dashboard/file_menu.cljs:29, src/app/main/ui/dashboard/files.cljs:75, src/app/main/ui/dashboard/files.cljs:168, src/app/main/ui/dashboard/projects.cljs:225, src/app/main/ui/dashboard/projects.cljs:229, src/app/main/ui/dashboard/sidebar.cljs:791
msgid "labels.drafts"
msgstr "Drafts"
-#: src/app/main/ui/comments.cljs:356, src/app/main/ui/dashboard/fonts.cljs:253, src/app/main/ui/dashboard/team.cljs:940, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:205, src/app/main/ui/workspace/tokens/sidebar.cljs:199
+#: src/app/main/ui/comments.cljs:356, src/app/main/ui/dashboard/fonts.cljs:253, src/app/main/ui/dashboard/team.cljs:943, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:205, src/app/main/ui/workspace/tokens/sidebar.cljs:199
msgid "labels.edit"
msgstr "Edit"
@@ -1715,7 +1720,7 @@ msgstr "Edit"
msgid "labels.edit-file"
msgstr "Edit file"
-#: src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:307, src/app/main/ui/dashboard/team.cljs:551, src/app/main/ui/dashboard/team.cljs:584, src/app/main/ui/onboarding/team_choice.cljs:65
+#: src/app/main/ui/dashboard/team.cljs:126, src/app/main/ui/dashboard/team.cljs:310, src/app/main/ui/dashboard/team.cljs:554, src/app/main/ui/dashboard/team.cljs:587, src/app/main/ui/onboarding/team_choice.cljs:65
msgid "labels.editor"
msgstr "Editor"
@@ -1723,11 +1728,11 @@ msgstr "Editor"
msgid "labels.event"
msgstr "Event"
-#: src/app/main/ui/dashboard/team.cljs:698
+#: src/app/main/ui/dashboard/team.cljs:701
msgid "labels.expired-invitation"
msgstr "Expired"
-#: src/app/main/ui/exports/assets.cljs:177
+#: src/app/main/ui/exports/assets.cljs:170
msgid "labels.export"
msgstr "Export"
@@ -1767,11 +1772,11 @@ msgstr "CEO or Founder"
msgid "labels.freelancer"
msgstr "Freelancer"
-#: src/app/main/ui/dashboard/sidebar.cljs:1015, src/app/main/ui/workspace/main_menu.cljs:146
+#: src/app/main/ui/dashboard/sidebar.cljs:1015, src/app/main/ui/workspace/main_menu.cljs:147
msgid "labels.github-repo"
msgstr "Github repository"
-#: src/app/main/ui/dashboard/sidebar.cljs:1032, src/app/main/ui/settings/sidebar.cljs:113, src/app/main/ui/workspace/main_menu.cljs:175
+#: src/app/main/ui/dashboard/sidebar.cljs:1032, src/app/main/ui/settings/sidebar.cljs:113, src/app/main/ui/workspace/main_menu.cljs:176
msgid "labels.give-feedback"
msgstr "Give feedback"
@@ -1783,7 +1788,7 @@ msgstr "Go back"
msgid "labels.graphic-design"
msgstr "Graphic design"
-#: src/app/main/ui/dashboard/sidebar.cljs:978, src/app/main/ui/workspace/main_menu.cljs:106, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1079, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1104, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1290
+#: src/app/main/ui/dashboard/sidebar.cljs:978, src/app/main/ui/workspace/main_menu.cljs:107, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1079, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1104, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1290
msgid "labels.help-center"
msgstr "Help Center"
@@ -1791,7 +1796,7 @@ msgstr "Help Center"
msgid "labels.hide-resolved-comments"
msgstr "Hide resolved comments"
-#: src/app/main/ui/dashboard/team.cljs:1006
+#: src/app/main/ui/dashboard/team.cljs:1009
msgid "labels.inactive"
msgstr "Inactive"
@@ -1813,7 +1818,7 @@ msgstr "Internal Error"
msgid "labels.invision"
msgstr "InVision"
-#: src/app/main/ui/dashboard/sidebar.cljs:516, src/app/main/ui/dashboard/team.cljs:96, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:745
+#: src/app/main/ui/dashboard/sidebar.cljs:516, src/app/main/ui/dashboard/team.cljs:96, src/app/main/ui/dashboard/team.cljs:104, src/app/main/ui/dashboard/team.cljs:748
msgid "labels.invitations"
msgstr "Invitations"
@@ -1821,11 +1826,11 @@ msgstr "Invitations"
msgid "labels.language"
msgstr "Language"
-#: src/app/main/ui/dashboard/sidebar.cljs:1008, src/app/main/ui/workspace/main_menu.cljs:138
+#: src/app/main/ui/dashboard/sidebar.cljs:1008, src/app/main/ui/workspace/main_menu.cljs:139
msgid "labels.libraries-and-templates"
msgstr "Libraries & Templates"
-#: src/app/main/ui/auth/verify_token.cljs:97, src/app/main/ui/dashboard/grid.cljs:104, src/app/main/ui/dashboard/grid.cljs:124, src/app/main/ui/dashboard/import.cljs:253, src/app/main/ui/dashboard/placeholder.cljs:52, src/app/main/ui/ds/product/loader.cljs:52, src/app/main/ui/exports/files.cljs:62, src/app/main/ui/viewer.cljs:637, src/app/main/ui/workspace.cljs:129
+#: src/app/main/ui/auth/verify_token.cljs:97, src/app/main/ui/dashboard/grid.cljs:104, src/app/main/ui/dashboard/grid.cljs:124, src/app/main/ui/dashboard/import.cljs:253, src/app/main/ui/dashboard/placeholder.cljs:53, src/app/main/ui/ds/product/loader.cljs:52, src/app/main/ui/exports/files.cljs:62, src/app/main/ui/viewer.cljs:637, src/app/main/ui/workspace.cljs:129
msgid "labels.loading"
msgstr "Loading…"
@@ -1845,7 +1850,7 @@ msgstr "Logout"
msgid "labels.marketing"
msgstr "Marketing"
-#: src/app/main/ui/dashboard/team.cljs:499
+#: src/app/main/ui/dashboard/team.cljs:502
msgid "labels.member"
msgstr "Member"
@@ -1865,11 +1870,11 @@ msgstr "Next"
msgid "labels.no-comments-available"
msgstr "You're all caught up! New comment notifications will appear here."
-#: src/app/main/ui/dashboard/team.cljs:731
+#: src/app/main/ui/dashboard/team.cljs:734
msgid "labels.no-invitations"
msgstr "No pending invitations."
-#: src/app/main/ui/dashboard/team.cljs:733
+#: src/app/main/ui/dashboard/team.cljs:736
#, markdown
msgid "labels.no-invitations-hint"
msgstr "Click the **Invite people** button to invite people to this team."
@@ -1883,7 +1888,7 @@ msgstr "This page might not exist or you don’t have permissions to access to i
msgid "labels.not-found.main-message"
msgstr "Oops!"
-#: src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/team.cljs:1133
+#: src/app/main/ui/dashboard/projects.cljs:237, src/app/main/ui/dashboard/team.cljs:1136
msgid "labels.num-of-files"
msgid_plural "labels.num-of-files"
msgstr[0] "1 file"
@@ -1895,7 +1900,7 @@ msgid_plural "labels.num-of-frames"
msgstr[0] "1 board"
msgstr[1] "%s boards"
-#: src/app/main/ui/dashboard/team.cljs:1128
+#: src/app/main/ui/dashboard/team.cljs:1131
msgid "labels.num-of-projects"
msgid_plural "labels.num-of-projects"
msgstr[0] "1 project"
@@ -1930,7 +1935,7 @@ msgstr "Other (specify)"
msgid "labels.other-short"
msgstr "Other"
-#: src/app/main/ui/dashboard/team.cljs:314, src/app/main/ui/dashboard/team.cljs:549, src/app/main/ui/dashboard/team.cljs:1114
+#: src/app/main/ui/dashboard/team.cljs:317, src/app/main/ui/dashboard/team.cljs:552, src/app/main/ui/dashboard/team.cljs:1117
msgid "labels.owner"
msgstr "Owner"
@@ -1938,7 +1943,7 @@ msgstr "Owner"
msgid "labels.password"
msgstr "Password"
-#: src/app/main/ui/dashboard/team.cljs:699
+#: src/app/main/ui/dashboard/team.cljs:702
msgid "labels.pending-invitation"
msgstr "Pending"
@@ -1962,7 +1967,7 @@ msgstr "Profile"
msgid "labels.projects"
msgstr "Projects"
-#: src/app/main/ui/dashboard/sidebar.cljs:998, src/app/main/ui/settings/sidebar.cljs:106, src/app/main/ui/workspace/main_menu.cljs:130
+#: src/app/main/ui/dashboard/sidebar.cljs:998, src/app/main/ui/settings/sidebar.cljs:106, src/app/main/ui/workspace/main_menu.cljs:131
msgid "labels.release-notes"
msgstr "Release notes"
@@ -1976,23 +1981,23 @@ msgstr "Reload file"
msgid "labels.remove"
msgstr "Remove"
-#: src/app/main/ui/dashboard/team.cljs:345
+#: src/app/main/ui/dashboard/team.cljs:348
msgid "labels.remove-member"
msgstr "Remove member"
-#: src/app/main/ui/dashboard/file_menu.cljs:288, src/app/main/ui/dashboard/project_menu.cljs:87, src/app/main/ui/dashboard/sidebar.cljs:539, src/app/main/ui/workspace/sidebar/assets/groups.cljs:153, src/app/main/ui/workspace/sidebar/versions.cljs:146, src/app/main/ui/workspace/tokens/sets_context_menu.cljs:43
+#: src/app/main/ui/dashboard/file_menu.cljs:277, src/app/main/ui/dashboard/project_menu.cljs:87, src/app/main/ui/dashboard/sidebar.cljs:539, src/app/main/ui/workspace/sidebar/assets/groups.cljs:153, src/app/main/ui/workspace/sidebar/versions.cljs:149, src/app/main/ui/workspace/tokens/sets_context_menu.cljs:43
msgid "labels.rename"
msgstr "Rename"
-#: src/app/main/ui/dashboard/team_form.cljs:99
+#: src/app/main/ui/dashboard/team_form.cljs:101
msgid "labels.rename-team"
msgstr "Rename team"
-#: src/app/main/ui/dashboard/team.cljs:681
+#: src/app/main/ui/dashboard/team.cljs:684
msgid "labels.resend-invitation"
msgstr "Resend invitation"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:149, src/app/main/ui/workspace/sidebar/versions.cljs:288
+#: src/app/main/ui/workspace/sidebar/versions.cljs:152, src/app/main/ui/workspace/sidebar/versions.cljs:292
msgid "labels.restore"
msgstr "Restore"
@@ -2000,7 +2005,7 @@ msgstr "Restore"
msgid "labels.retry"
msgstr "Retry"
-#: src/app/main/ui/dashboard/team.cljs:500, src/app/main/ui/dashboard/team.cljs:746
+#: src/app/main/ui/dashboard/team.cljs:503, src/app/main/ui/dashboard/team.cljs:749
msgid "labels.role"
msgstr "Role"
@@ -2076,7 +2081,7 @@ msgstr "Sketch"
msgid "labels.start"
msgstr "Start"
-#: src/app/main/ui/dashboard/team.cljs:747
+#: src/app/main/ui/dashboard/team.cljs:750
msgid "labels.status"
msgstr "Status"
@@ -2096,11 +2101,11 @@ msgstr "Team member"
msgid "labels.themes"
msgstr "Themes"
-#: src/app/main/ui/dashboard/sidebar.cljs:992, src/app/main/ui/workspace/main_menu.cljs:122
+#: src/app/main/ui/dashboard/sidebar.cljs:992, src/app/main/ui/workspace/main_menu.cljs:123
msgid "labels.tutorials"
msgstr "Tutorials"
-#: src/app/main/ui/dashboard/file_menu.cljs:274
+#: src/app/main/ui/dashboard/file_menu.cljs:263
msgid "labels.unpublish-multi-files"
msgstr "Unpublish %s files"
@@ -2108,7 +2113,7 @@ msgstr "Unpublish %s files"
msgid "labels.update"
msgstr "Update"
-#: src/app/main/ui/dashboard/team_form.cljs:120
+#: src/app/main/ui/dashboard/team_form.cljs:122
msgid "labels.update-team"
msgstr "Update team"
@@ -2128,11 +2133,11 @@ msgstr "Uploading…"
msgid "labels.view-only"
msgstr "View only"
-#: src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:304, src/app/main/ui/dashboard/team.cljs:552, src/app/main/ui/dashboard/team.cljs:588, src/app/main/ui/onboarding/team_choice.cljs:64
+#: src/app/main/ui/dashboard/team.cljs:125, src/app/main/ui/dashboard/team.cljs:307, src/app/main/ui/dashboard/team.cljs:555, src/app/main/ui/dashboard/team.cljs:591, src/app/main/ui/onboarding/team_choice.cljs:64
msgid "labels.viewer"
msgstr "Viewer"
-#: src/app/main/ui/dashboard/sidebar.cljs:523, src/app/main/ui/dashboard/team.cljs:97, src/app/main/ui/dashboard/team.cljs:107, src/app/main/ui/dashboard/team.cljs:918
+#: src/app/main/ui/dashboard/sidebar.cljs:523, src/app/main/ui/dashboard/team.cljs:97, src/app/main/ui/dashboard/team.cljs:107, src/app/main/ui/dashboard/team.cljs:921
msgid "labels.webhooks"
msgstr "Webhooks"
@@ -2188,24 +2193,24 @@ msgstr "Radial"
msgid "media.solid"
msgstr "Solid"
-#: src/app/main/data/common.cljs:130
+#: src/app/main/data/common.cljs:127
msgid "modals.add-shared-confirm-empty.hint"
msgstr ""
"Your library is empty. Once added as Shared Library, the assets you create "
"will be available to be used among the rest of your files. Are you sure you "
"want to publish it?"
-#: src/app/main/data/common.cljs:132
+#: src/app/main/data/common.cljs:129
msgid "modals.add-shared-confirm.accept"
msgstr "Add as Shared Library"
-#: src/app/main/data/common.cljs:130
+#: src/app/main/data/common.cljs:127
msgid "modals.add-shared-confirm.hint"
msgstr ""
"Once added as Shared Library, the assets of this file library will be "
"available to be used among the rest of your files."
-#: src/app/main/data/common.cljs:129
+#: src/app/main/data/common.cljs:126
msgid "modals.add-shared-confirm.message"
msgstr "Add “%s” as Shared Library"
@@ -2262,19 +2267,19 @@ msgstr "Generate access token"
msgid "modals.create-access-token.token"
msgstr ""
-#: src/app/main/ui/dashboard/team.cljs:911
+#: src/app/main/ui/dashboard/team.cljs:914
msgid "modals.create-webhook.submit-label"
msgstr "Create webhook"
-#: src/app/main/ui/dashboard/team.cljs:876
+#: src/app/main/ui/dashboard/team.cljs:879
msgid "modals.create-webhook.title"
msgstr "Create webhook"
-#: src/app/main/ui/dashboard/team.cljs:887
+#: src/app/main/ui/dashboard/team.cljs:890
msgid "modals.create-webhook.url.label"
msgstr "Payload URL"
-#: src/app/main/ui/dashboard/team.cljs:888
+#: src/app/main/ui/dashboard/team.cljs:891
msgid "modals.create-webhook.url.placeholder"
msgstr "https://example.com/postreceive"
@@ -2328,27 +2333,27 @@ msgstr "Are you sure you want to delete this annotation?"
msgid "modals.delete-component-annotation.title"
msgstr "Delete annotation"
-#: src/app/main/ui/dashboard/file_menu.cljs:128
+#: src/app/main/ui/dashboard/file_menu.cljs:125
msgid "modals.delete-file-confirm.accept"
msgstr "Delete file"
-#: src/app/main/ui/dashboard/file_menu.cljs:127
+#: src/app/main/ui/dashboard/file_menu.cljs:124
msgid "modals.delete-file-confirm.message"
msgstr "Are you sure you want to delete this file?"
-#: src/app/main/ui/dashboard/file_menu.cljs:126
+#: src/app/main/ui/dashboard/file_menu.cljs:123
msgid "modals.delete-file-confirm.title"
msgstr "Deleting file"
-#: src/app/main/ui/dashboard/file_menu.cljs:122
+#: src/app/main/ui/dashboard/file_menu.cljs:119
msgid "modals.delete-file-multi-confirm.accept"
msgstr "Delete files"
-#: src/app/main/ui/dashboard/file_menu.cljs:121
+#: src/app/main/ui/dashboard/file_menu.cljs:118
msgid "modals.delete-file-multi-confirm.message"
msgstr "Are you sure you want to delete %s files?"
-#: src/app/main/ui/dashboard/file_menu.cljs:120
+#: src/app/main/ui/dashboard/file_menu.cljs:117
msgid "modals.delete-file-multi-confirm.title"
msgstr "Deleting %s files"
@@ -2372,11 +2377,11 @@ msgstr ""
msgid "modals.delete-font.title"
msgstr "Deleting font"
-#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/sitemap.cljs:46
+#: src/app/main/ui/workspace/context_menu.cljs:534, src/app/main/ui/workspace/sidebar/sitemap.cljs:46
msgid "modals.delete-page.body"
msgstr "Are you sure you want to delete this page?"
-#: src/app/main/ui/workspace/context_menu.cljs:532, src/app/main/ui/workspace/sidebar/sitemap.cljs:45
+#: src/app/main/ui/workspace/context_menu.cljs:533, src/app/main/ui/workspace/sidebar/sitemap.cljs:45
msgid "modals.delete-page.title"
msgstr "Delete page"
@@ -2436,15 +2441,15 @@ msgstr ""
msgid "modals.delete-team-confirm.title"
msgstr "Deleting team"
-#: src/app/main/ui/dashboard/team.cljs:457
+#: src/app/main/ui/dashboard/team.cljs:460
msgid "modals.delete-team-member-confirm.accept"
msgstr "Delete member"
-#: src/app/main/ui/dashboard/team.cljs:456
+#: src/app/main/ui/dashboard/team.cljs:459
msgid "modals.delete-team-member-confirm.message"
msgstr "Are you sure you want to delete this member from the team?"
-#: src/app/main/ui/dashboard/team.cljs:455
+#: src/app/main/ui/dashboard/team.cljs:458
msgid "modals.delete-team-member-confirm.title"
msgstr "Delete team member"
@@ -2458,23 +2463,23 @@ msgstr[1] ""
"Assets that have already been used in those files will remain there (no "
"design will be broken)."
-#: src/app/main/ui/dashboard/team.cljs:979
+#: src/app/main/ui/dashboard/team.cljs:982
msgid "modals.delete-webhook.accept"
msgstr "Delete webhook"
-#: src/app/main/ui/dashboard/team.cljs:978
+#: src/app/main/ui/dashboard/team.cljs:981
msgid "modals.delete-webhook.message"
msgstr "Are you sure you want to delete this webhook?"
-#: src/app/main/ui/dashboard/team.cljs:977
+#: src/app/main/ui/dashboard/team.cljs:980
msgid "modals.delete-webhook.title"
msgstr "Deleting webhook"
-#: src/app/main/ui/dashboard/team.cljs:910
+#: src/app/main/ui/dashboard/team.cljs:913
msgid "modals.edit-webhook.submit-label"
msgstr "Edit webhook"
-#: src/app/main/ui/dashboard/team.cljs:875
+#: src/app/main/ui/dashboard/team.cljs:878
msgid "modals.edit-webhook.title"
msgstr "Edit webhook"
@@ -2502,13 +2507,13 @@ msgstr ""
msgid "modals.invite-team-member.title"
msgstr "Invite members to the team"
-#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/dashboard/team.cljs:423
+#: src/app/main/ui/dashboard/sidebar.cljs:423, src/app/main/ui/dashboard/team.cljs:426
msgid "modals.leave-and-close-confirm.hint"
msgstr ""
"As you're the only member of this team, the team will be deleted along with "
"its projects and files."
-#: src/app/main/ui/dashboard/sidebar.cljs:422, src/app/main/ui/dashboard/team.cljs:422
+#: src/app/main/ui/dashboard/sidebar.cljs:422, src/app/main/ui/dashboard/team.cljs:425
msgid "modals.leave-and-close-confirm.message"
msgstr "Are you sure you want to leave the %s team?"
@@ -2536,15 +2541,15 @@ msgstr "Select a member to promote"
msgid "modals.leave-and-reassign.title"
msgstr "Before you leave"
-#: src/app/main/ui/dashboard/sidebar.cljs:402, src/app/main/ui/dashboard/sidebar.cljs:424, src/app/main/ui/dashboard/team.cljs:424, src/app/main/ui/dashboard/team.cljs:446
+#: src/app/main/ui/dashboard/sidebar.cljs:402, src/app/main/ui/dashboard/sidebar.cljs:424, src/app/main/ui/dashboard/team.cljs:427, src/app/main/ui/dashboard/team.cljs:449
msgid "modals.leave-confirm.accept"
msgstr "Leave team"
-#: src/app/main/ui/dashboard/sidebar.cljs:401, src/app/main/ui/dashboard/team.cljs:445
+#: src/app/main/ui/dashboard/sidebar.cljs:401, src/app/main/ui/dashboard/team.cljs:448
msgid "modals.leave-confirm.message"
msgstr "Are you sure you want to leave this team?"
-#: src/app/main/ui/dashboard/sidebar.cljs:400, src/app/main/ui/dashboard/sidebar.cljs:421, src/app/main/ui/dashboard/team.cljs:421, src/app/main/ui/dashboard/team.cljs:444
+#: src/app/main/ui/dashboard/sidebar.cljs:400, src/app/main/ui/dashboard/sidebar.cljs:421, src/app/main/ui/dashboard/team.cljs:424, src/app/main/ui/dashboard/team.cljs:447
msgid "modals.leave-confirm.title"
msgstr "Leaving team"
@@ -2566,39 +2571,39 @@ msgid_plural "modals.move-shared-confirm.title"
msgstr[0] "Move library"
msgstr[1] "Move libraries"
-#: src/app/main/ui/workspace/main_menu.cljs:271, src/app/main/ui/workspace/nudge.cljs:47
+#: src/app/main/ui/workspace/main_menu.cljs:272, src/app/main/ui/workspace/nudge.cljs:47
msgid "modals.nudge-title"
msgstr "Nudge amount"
-#: src/app/main/ui/dashboard/team.cljs:370
+#: src/app/main/ui/dashboard/team.cljs:373
msgid "modals.promote-owner-confirm.accept"
msgstr "Transfer ownership"
-#: src/app/main/ui/dashboard/team.cljs:369
+#: src/app/main/ui/dashboard/team.cljs:372
msgid "modals.promote-owner-confirm.hint"
msgstr ""
"If you transfer the ownership, you will change your role to Admin, losing "
"some permissions over this team. "
-#: src/app/main/ui/dashboard/team.cljs:368
+#: src/app/main/ui/dashboard/team.cljs:371
msgid "modals.promote-owner-confirm.message"
msgstr ""
"You are the current owner of this team. Are you sure you want to make %s "
"the new owner of the team?"
-#: src/app/main/ui/dashboard/team.cljs:367
+#: src/app/main/ui/dashboard/team.cljs:370
msgid "modals.promote-owner-confirm.title"
msgstr "New team owner"
-#: src/app/main/ui/workspace/libraries.cljs:189
+#: src/app/main/ui/workspace/libraries.cljs:240
msgid "modals.publish-empty-library.accept"
msgstr "Publish"
-#: src/app/main/ui/workspace/libraries.cljs:188
+#: src/app/main/ui/workspace/libraries.cljs:239
msgid "modals.publish-empty-library.message"
msgstr "Your library is empty. Are you sure you want to publish it?"
-#: src/app/main/ui/workspace/libraries.cljs:187
+#: src/app/main/ui/workspace/libraries.cljs:238
msgid "modals.publish-empty-library.title"
msgstr "Publish empty library"
@@ -2653,21 +2658,21 @@ msgstr ""
msgid "modals.update-remote-component-in-bulk.message"
msgstr "Update components in a shared library"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:380
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:384
msgid "modals.update-remote-component.accept"
msgstr "Update"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:379
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:383
msgid "modals.update-remote-component.cancel"
msgstr "Cancel"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:378
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:382
msgid "modals.update-remote-component.hint"
msgstr ""
"You are about to update a component in a shared library. This may affect "
"other files that use it."
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:377
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:381
msgid "modals.update-remote-component.message"
msgstr "Update a component in a shared library"
@@ -2771,19 +2776,19 @@ msgstr "To access this file, you can ask the team owner."
msgid "not-found.no-permission.you-can-ask.project"
msgstr "To access this project, you can ask the team owner."
-#: src/app/main/data/common.cljs:90
+#: src/app/main/data/common.cljs:87
msgid "notifications.by-code.maintenance"
msgstr "Maintenance break: we will be down for a short maintenance within 5 minutes."
-#: src/app/main/data/common.cljs:81
+#: src/app/main/data/common.cljs:78
msgid "notifications.by-code.upgrade-version"
msgstr "A new version is available, please refresh the page"
-#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:631
+#: src/app/main/ui/dashboard/team.cljs:164, src/app/main/ui/dashboard/team.cljs:634
msgid "notifications.invitation-email-sent"
msgstr "Invitation sent successfully"
-#: src/app/main/ui/dashboard/team.cljs:652
+#: src/app/main/ui/dashboard/team.cljs:655
msgid "notifications.invitation-link-copied"
msgstr "Invitation link copied"
@@ -3147,7 +3152,7 @@ msgstr "Go to login"
msgid "settings.detach"
msgstr "Detach"
-#: src/app/main/ui/viewer/inspect/exports.cljs:155, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:137, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:148, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:204, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:161, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:470, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:494, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:526, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:554, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:569, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:576, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:182, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:378, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:395, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:248, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:172
+#: src/app/main/ui/viewer/inspect/exports.cljs:147, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:632, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:137, src/app/main/ui/workspace/sidebar/options/menus/constraints.cljs:148, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:196, src/app/main/ui/workspace/sidebar/options/menus/fill.cljs:161, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:470, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:476, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:494, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:500, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:526, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:554, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:569, src/app/main/ui/workspace/sidebar/options/menus/measures.cljs:576, src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs:312, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:182, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:378, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:395, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:248, src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:172
msgid "settings.multiple"
msgstr "Mixed"
@@ -3160,19 +3165,19 @@ msgid "settings.select-this-color"
msgstr "Select items using this style"
# SECTIONS
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:414
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:415
msgid "shortcut-section.basics"
msgstr "Basics"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:420
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:421
msgid "shortcut-section.dashboard"
msgstr "Dashboard"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:423
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:424
msgid "shortcut-section.viewer"
msgstr "Viewer"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:417
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:418
msgid "shortcut-section.workspace"
msgstr "Workspace"
@@ -3193,7 +3198,7 @@ msgstr "Generic"
msgid "shortcut-subsection.general-viewer"
msgstr "Generic"
-#: src/app/main/ui/workspace/main_menu.cljs:777, src/app/main/ui/workspace/sidebar/shortcuts.cljs:60
+#: src/app/main/ui/workspace/main_menu.cljs:826, src/app/main/ui/workspace/sidebar/shortcuts.cljs:60
msgid "shortcut-subsection.main-menu"
msgstr "Main menu"
@@ -3537,7 +3542,7 @@ msgstr "Move up"
msgid "shortcuts.next-frame"
msgstr "Next board"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:516
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:517
msgid "shortcuts.not-found"
msgstr "No shortcuts found"
@@ -3609,7 +3614,7 @@ msgstr "Go to viewer interactions section"
msgid "shortcuts.open-workspace"
msgstr "Go to workspace"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:260
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:261
msgid "shortcuts.or"
msgstr " or "
@@ -3625,203 +3630,204 @@ msgstr "Previous board"
msgid "shortcuts.redo"
msgstr "Redo"
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:166
msgid "shortcuts.rename"
msgstr "Rename"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:166
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:167
msgid "shortcuts.reset-zoom"
msgstr "Reset zoom"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:167
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:168
msgid "shortcuts.scale"
msgstr "Scale"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:168
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:169
msgid "shortcuts.search-placeholder"
msgstr "Search shortcuts"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:169
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:170
msgid "shortcuts.select-all"
msgstr "Select all"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:170
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:171
msgid "shortcuts.select-next"
msgstr "Select next layer"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:171
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:172
msgid "shortcuts.select-parent-layer"
msgstr "Select parent layer"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:172
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:173
msgid "shortcuts.select-prev"
msgstr "Select previous layer"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:173
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:174
msgid "shortcuts.separate-nodes"
msgstr "Separate nodes"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:174
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:175
msgid "shortcuts.show-pixel-grid"
msgstr "Show / Hide pixel grid"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:175
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:176
msgid "shortcuts.show-shortcuts"
msgstr "Show / Hide shortcuts"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:176
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:177
msgid "shortcuts.snap-nodes"
msgstr "Snap to nodes"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:177
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:178
msgid "shortcuts.snap-pixel-grid"
msgstr "Snap to pixel grid"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:178
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:179
msgid "shortcuts.start-editing"
msgstr "Start editing"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:179
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:180
msgid "shortcuts.start-measure"
msgstr "Start measurement"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:180
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:181
msgid "shortcuts.stop-measure"
msgstr "Stop measurement"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:181
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182
msgid "shortcuts.text-align-center"
msgstr "Align center"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:183
msgid "shortcuts.text-align-justify"
msgstr "Align justify"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:183
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:184
msgid "shortcuts.text-align-left"
msgstr "Align left"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:184
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:185
msgid "shortcuts.text-align-right"
msgstr "Align right"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:185
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:186
msgid "shortcuts.thumbnail-set"
msgstr "Set thumbnails"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:496, src/app/main/ui/workspace/sidebar/shortcuts.cljs:505
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:497, src/app/main/ui/workspace/sidebar/shortcuts.cljs:506
msgid "shortcuts.title"
msgstr "Keyboard shortcuts"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:186
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:187
msgid "shortcuts.toggle-alignment"
msgstr "Toggle dynamic alignment"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:187
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:188
msgid "shortcuts.toggle-assets"
msgstr "Toggle assets"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:188
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:189
msgid "shortcuts.toggle-colorpalette"
msgstr "Toggle color palette"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:189
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:190
msgid "shortcuts.toggle-focus-mode"
msgstr "Toggle focus mode"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:190
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:191
msgid "shortcuts.toggle-fullscreen"
msgstr "Toggle fullscreen"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:191
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:192
msgid "shortcuts.toggle-guides"
msgstr "Show / Hide guides"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:192
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:193
msgid "shortcuts.toggle-history"
msgstr "Toggle history"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:193
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:194
msgid "shortcuts.toggle-layers"
msgstr "Toggle layers"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:194
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:195
msgid "shortcuts.toggle-layout-flex"
msgstr "Add / Remove flex layout"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:195
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:196
msgid "shortcuts.toggle-layout-grid"
msgstr "Add/remove grid layout"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:196
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:197
msgid "shortcuts.toggle-lock"
msgstr "Lock / Unlock"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:197
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:198
msgid "shortcuts.toggle-lock-size"
msgstr "Lock proportions"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:198
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:199
msgid "shortcuts.toggle-rulers"
msgstr "Show / Hide rulers"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:199
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:200
#, fuzzy
msgid "shortcuts.toggle-rules"
msgstr ""
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:200
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:201
msgid "shortcuts.toggle-snap-guides"
msgstr "Snap to guides"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:201
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:202
msgid "shortcuts.toggle-snap-ruler-guide"
msgstr "Snap to ruler guides"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:202
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:203
msgid "shortcuts.toggle-textpalette"
msgstr "Toggle text palette"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:203
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:204
msgid "shortcuts.toggle-theme"
msgstr "Change theme"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:204
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:205
msgid "shortcuts.toggle-visibility"
msgstr "Show / Hide"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:205
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:206
msgid "shortcuts.toggle-zoom-style"
msgstr "Toggle zoom style"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:206
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:207
msgid "shortcuts.underline"
msgstr "Toggle underline"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:207
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:208
msgid "shortcuts.undo"
msgstr "Undo"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:208
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:209
msgid "shortcuts.ungroup"
msgstr "Ungroup"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:209
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:210
msgid "shortcuts.unmask"
msgstr "Unmask"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:210
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:211
msgid "shortcuts.v-distribute"
msgstr "Distribute vertically"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:211
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:212
msgid "shortcuts.zoom-lense-decrease"
msgstr "Zoom lense decrease"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:212
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:213
msgid "shortcuts.zoom-lense-increase"
msgstr "Zoom lense increase"
-#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:213
+#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:214
msgid "shortcuts.zoom-selected"
msgstr "Zoom to selected"
@@ -3873,19 +3879,19 @@ msgstr "Password - Penpot"
msgid "title.settings.profile"
msgstr "Profile - Penpot"
-#: src/app/main/ui/dashboard/team.cljs:764
+#: src/app/main/ui/dashboard/team.cljs:767
msgid "title.team-invitations"
msgstr "Invitations - %s - Penpot"
-#: src/app/main/ui/dashboard/team.cljs:524
+#: src/app/main/ui/dashboard/team.cljs:527
msgid "title.team-members"
msgstr "Members - %s - Penpot"
-#: src/app/main/ui/dashboard/team.cljs:1074
+#: src/app/main/ui/dashboard/team.cljs:1077
msgid "title.team-settings"
msgstr "Settings - %s - Penpot"
-#: src/app/main/ui/dashboard/team.cljs:1027
+#: src/app/main/ui/dashboard/team.cljs:1030
msgid "title.team-webhooks"
msgstr "Webhooks - %s - Penpot"
@@ -3897,13 +3903,13 @@ msgstr "%s - View mode - Penpot"
msgid "title.workspace"
msgstr "%s - Penpot"
-#: src/app/main/ui.cljs:138
+#: src/app/main/ui.cljs:139
msgid "viewer.breaking-change.description"
msgstr ""
"This shareable link is no longer valid. Create a new one or ask the owner "
"for a new one."
-#: src/app/main/ui.cljs:137
+#: src/app/main/ui.cljs:138
msgid "viewer.breaking-change.message"
msgstr "Sorry!"
@@ -3955,7 +3961,7 @@ msgstr "Show interactions on click"
msgid "viewer.header.sitemap"
msgstr "Sitemap"
-#: src/app/main/ui/dashboard/team.cljs:985
+#: src/app/main/ui/dashboard/team.cljs:988
msgid "webhooks.last-delivery.success"
msgstr "Last delivery was successful."
@@ -3991,28 +3997,32 @@ msgstr "Distribute vertical spacing (%s)"
msgid "workspace.align.vtop"
msgstr "Align top (%s)"
+#: src/app/main/ui/workspace/sidebar/assets.cljs:171, src/app/main/ui/workspace/sidebar/assets.cljs:176
+msgid "workspace.assets.add-library"
+msgstr "Add library"
+
#: src/app/main/ui/workspace/sidebar/assets.cljs
#, unused
msgid "workspace.assets.assets"
msgstr "Assets"
-#: src/app/main/ui/workspace/sidebar/assets.cljs:135
+#: src/app/main/ui/workspace/sidebar/assets.cljs:143
msgid "workspace.assets.box-filter-all"
msgstr "All assets"
-#: src/app/main/ui/dashboard/grid.cljs:138, src/app/main/ui/dashboard/grid.cljs:170, src/app/main/ui/workspace/sidebar/assets/colors.cljs:487, src/app/main/ui/workspace/sidebar/assets.cljs:147
+#: src/app/main/ui/dashboard/grid.cljs:138, src/app/main/ui/dashboard/grid.cljs:170, src/app/main/ui/workspace/sidebar/assets/colors.cljs:492, src/app/main/ui/workspace/sidebar/assets.cljs:155
msgid "workspace.assets.colors"
msgstr "Colors"
-#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:495
+#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:500
msgid "workspace.assets.colors.add-color"
msgstr "Add color"
-#: src/app/main/ui/dashboard/grid.cljs:134, src/app/main/ui/dashboard/grid.cljs:149, src/app/main/ui/workspace/sidebar/assets/components.cljs:511, src/app/main/ui/workspace/sidebar/assets.cljs:138
+#: src/app/main/ui/dashboard/grid.cljs:134, src/app/main/ui/dashboard/grid.cljs:149, src/app/main/ui/workspace/sidebar/assets/components.cljs:502, src/app/main/ui/workspace/sidebar/assets.cljs:146
msgid "workspace.assets.components"
msgstr "Components"
-#: src/app/main/ui/workspace/sidebar/assets/components.cljs:532
+#: src/app/main/ui/workspace/sidebar/assets/components.cljs:523
msgid "workspace.assets.components.add-component"
msgstr "Add component"
@@ -4024,35 +4034,35 @@ msgstr "Create a group"
msgid "workspace.assets.create-group-hint"
msgstr "Your items are going to be named automatically as \"group name / item name\""
-#: src/app/main/ui/workspace/context_menu.cljs:540, src/app/main/ui/workspace/sidebar/assets/colors.cljs:251, src/app/main/ui/workspace/sidebar/assets/components.cljs:576, src/app/main/ui/workspace/sidebar/assets/graphics.cljs:424, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:447
+#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/assets/colors.cljs:256, src/app/main/ui/workspace/sidebar/assets/components.cljs:567, src/app/main/ui/workspace/sidebar/assets/graphics.cljs:424, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:460
msgid "workspace.assets.delete"
msgstr "Delete"
-#: src/app/main/ui/workspace/context_menu.cljs:545, src/app/main/ui/workspace/sidebar/assets/components.cljs:571
+#: src/app/main/ui/workspace/context_menu.cljs:548, src/app/main/ui/workspace/sidebar/assets/components.cljs:562
msgid "workspace.assets.duplicate"
msgstr "Duplicate"
-#: src/app/main/ui/workspace/sidebar/assets/components.cljs:570
+#: src/app/main/ui/workspace/sidebar/assets/components.cljs:561
msgid "workspace.assets.duplicate-main"
msgstr "Duplicate main"
-#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:247, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:443
+#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:252, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:456
msgid "workspace.assets.edit"
msgstr "Edit"
-#: src/app/main/ui/workspace/sidebar/assets.cljs:171
+#: src/app/main/ui/workspace/sidebar/assets.cljs:192
msgid "workspace.assets.filter"
msgstr "Filter"
-#: src/app/main/ui/workspace/sidebar/assets/graphics.cljs:384, src/app/main/ui/workspace/sidebar/assets.cljs:143
+#: src/app/main/ui/workspace/sidebar/assets/graphics.cljs:384, src/app/main/ui/workspace/sidebar/assets.cljs:151
msgid "workspace.assets.graphics"
msgstr "Graphics"
-#: src/app/main/ui/workspace/sidebar/assets/components.cljs:527
+#: src/app/main/ui/workspace/sidebar/assets/components.cljs:518
msgid "workspace.assets.grid-view"
msgstr "Grid view"
-#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:255, src/app/main/ui/workspace/sidebar/assets/components.cljs:580, src/app/main/ui/workspace/sidebar/assets/graphics.cljs:428, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:452
+#: src/app/main/ui/workspace/sidebar/assets/colors.cljs:260, src/app/main/ui/workspace/sidebar/assets/components.cljs:571, src/app/main/ui/workspace/sidebar/assets/graphics.cljs:428, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:465
msgid "workspace.assets.group"
msgstr "Group"
@@ -4060,30 +4070,27 @@ msgstr "Group"
msgid "workspace.assets.group-name"
msgstr "Group name"
-#: src/app/main/ui/workspace/sidebar/assets.cljs:163
+#: src/app/main/ui/workspace/sidebar/assets.cljs:183
msgid "workspace.assets.libraries"
msgstr "Libraries"
-msgid "workspace.assets.add-library"
-msgstr "Add library"
-
-#: src/app/main/ui/workspace/sidebar/assets/components.cljs:523
+#: src/app/main/ui/workspace/sidebar/assets/components.cljs:514
msgid "workspace.assets.list-view"
msgstr "List view"
-#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:62, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:347
+#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:72, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:347
msgid "workspace.assets.local-library"
msgstr "local library"
-#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:295
+#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:304
msgid "workspace.assets.not-found"
msgstr "No assets found"
-#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:68
+#: src/app/main/ui/workspace/sidebar/assets/file_library.cljs:78
msgid "workspace.assets.open-library"
msgstr "Open library file"
-#: src/app/main/ui/workspace/context_menu.cljs:543, src/app/main/ui/workspace/sidebar/assets/colors.cljs:243, src/app/main/ui/workspace/sidebar/assets/components.cljs:565, src/app/main/ui/workspace/sidebar/assets/graphics.cljs:421, src/app/main/ui/workspace/sidebar/assets/groups.cljs:62, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:438
+#: src/app/main/ui/workspace/context_menu.cljs:546, src/app/main/ui/workspace/sidebar/assets/colors.cljs:248, src/app/main/ui/workspace/sidebar/assets/components.cljs:556, src/app/main/ui/workspace/sidebar/assets/graphics.cljs:421, src/app/main/ui/workspace/sidebar/assets/groups.cljs:62, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:451
msgid "workspace.assets.rename"
msgstr "Rename"
@@ -4091,7 +4098,7 @@ msgstr "Rename"
msgid "workspace.assets.rename-group"
msgstr "Rename group"
-#: src/app/main/ui/workspace/sidebar/assets.cljs:168
+#: src/app/main/ui/workspace/sidebar/assets.cljs:189
msgid "workspace.assets.search"
msgstr "Search assets"
@@ -4113,15 +4120,15 @@ msgid_plural "workspace.assets.sidebar.components"
msgstr[0] "1 component"
msgstr[1] "%s components"
-#: src/app/main/ui/workspace/sidebar/assets.cljs:187
+#: src/app/main/ui/workspace/sidebar/assets.cljs:208
msgid "workspace.assets.sort"
msgstr "Sort"
-#: src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/grid.cljs:197, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:400, src/app/main/ui/workspace/sidebar/assets.cljs:151
+#: src/app/main/ui/dashboard/grid.cljs:142, src/app/main/ui/dashboard/grid.cljs:197, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:413, src/app/main/ui/workspace/sidebar/assets.cljs:159
msgid "workspace.assets.typography"
msgstr "Typographies"
-#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:408
+#: src/app/main/ui/workspace/sidebar/assets/typographies.cljs:421
msgid "workspace.assets.typography.add-typography"
msgstr "Add typography"
@@ -4150,7 +4157,7 @@ msgstr "Letter Spacing"
msgid "workspace.assets.typography.line-height"
msgstr "Line Height"
-#: src/app/main/ui/dashboard/grid.cljs:207, src/app/main/ui/workspace/libraries.cljs:469, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:474, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:499, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:625
+#: src/app/main/ui/dashboard/grid.cljs:207, src/app/main/ui/workspace/libraries.cljs:537, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:474, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:499, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:606, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:625
msgid "workspace.assets.typography.sample"
msgstr "Ag"
@@ -4166,67 +4173,67 @@ msgstr "Text Transform"
msgid "workspace.assets.ungroup"
msgstr "Ungroup"
-#: src/app/main/ui/workspace/context_menu.cljs:648
+#: src/app/main/ui/workspace/context_menu.cljs:651
msgid "workspace.context-menu.grid-cells.area"
msgstr "Create area"
-#: src/app/main/ui/workspace/context_menu.cljs:651
+#: src/app/main/ui/workspace/context_menu.cljs:654
msgid "workspace.context-menu.grid-cells.create-board"
msgstr "Create board"
-#: src/app/main/ui/workspace/context_menu.cljs:643
+#: src/app/main/ui/workspace/context_menu.cljs:646
msgid "workspace.context-menu.grid-cells.merge"
msgstr "Merge cells"
-#: src/app/main/ui/workspace/context_menu.cljs:608
+#: src/app/main/ui/workspace/context_menu.cljs:611
msgid "workspace.context-menu.grid-track.column.add-after"
msgstr "Add 1 column to the right"
-#: src/app/main/ui/workspace/context_menu.cljs:607
+#: src/app/main/ui/workspace/context_menu.cljs:610
msgid "workspace.context-menu.grid-track.column.add-before"
msgstr "Add 1 column to the left"
-#: src/app/main/ui/workspace/context_menu.cljs:609
+#: src/app/main/ui/workspace/context_menu.cljs:612
msgid "workspace.context-menu.grid-track.column.delete"
msgstr "Delete column"
-#: src/app/main/ui/workspace/context_menu.cljs:610
+#: src/app/main/ui/workspace/context_menu.cljs:613
msgid "workspace.context-menu.grid-track.column.delete-shapes"
msgstr "Delete column and shapes"
-#: src/app/main/ui/workspace/context_menu.cljs:606
+#: src/app/main/ui/workspace/context_menu.cljs:609
msgid "workspace.context-menu.grid-track.column.duplicate"
msgstr "Duplicate column"
-#: src/app/main/ui/workspace/context_menu.cljs:615
+#: src/app/main/ui/workspace/context_menu.cljs:618
msgid "workspace.context-menu.grid-track.row.add-after"
msgstr "Add 1 row below"
-#: src/app/main/ui/workspace/context_menu.cljs:614
+#: src/app/main/ui/workspace/context_menu.cljs:617
msgid "workspace.context-menu.grid-track.row.add-before"
msgstr "Add 1 row above"
-#: src/app/main/ui/workspace/context_menu.cljs:616
+#: src/app/main/ui/workspace/context_menu.cljs:619
msgid "workspace.context-menu.grid-track.row.delete"
msgstr "Delete row"
-#: src/app/main/ui/workspace/context_menu.cljs:617
+#: src/app/main/ui/workspace/context_menu.cljs:620
msgid "workspace.context-menu.grid-track.row.delete-shapes"
msgstr "Delete row and shapes"
-#: src/app/main/ui/workspace/context_menu.cljs:613
+#: src/app/main/ui/workspace/context_menu.cljs:616
msgid "workspace.context-menu.grid-track.row.duplicate"
msgstr "Duplicate row"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:527
+#: src/app/main/ui/workspace/sidebar/layers.cljs:528
msgid "workspace.focus.focus-mode"
msgstr "Focus mode"
-#: src/app/main/ui/workspace/context_menu.cljs:298, src/app/main/ui/workspace/context_menu.cljs:567
+#: src/app/main/ui/workspace/context_menu.cljs:299, src/app/main/ui/workspace/context_menu.cljs:570
msgid "workspace.focus.focus-off"
msgstr "Focus off"
-#: src/app/main/ui/workspace/context_menu.cljs:297
+#: src/app/main/ui/workspace/context_menu.cljs:298
msgid "workspace.focus.focus-on"
msgstr "Focus on"
@@ -4242,11 +4249,11 @@ msgstr "Linear gradient"
msgid "workspace.gradients.radial"
msgstr "Radial gradient"
-#: src/app/main/ui/workspace/main_menu.cljs:243
+#: src/app/main/ui/workspace/main_menu.cljs:244
msgid "workspace.header.menu.disable-dynamic-alignment"
msgstr "Disable dynamic alignment"
-#: src/app/main/ui/workspace/main_menu.cljs:197
+#: src/app/main/ui/workspace/main_menu.cljs:198
msgid "workspace.header.menu.disable-scale-content"
msgstr "Disable proportional scale"
@@ -4255,23 +4262,23 @@ msgstr "Disable proportional scale"
msgid "workspace.header.menu.disable-scale-text"
msgstr "Disable scale text"
-#: src/app/main/ui/workspace/main_menu.cljs:228
+#: src/app/main/ui/workspace/main_menu.cljs:229
msgid "workspace.header.menu.disable-snap-guides"
msgstr "Disable snap to guides"
-#: src/app/main/ui/workspace/main_menu.cljs:258
+#: src/app/main/ui/workspace/main_menu.cljs:259
msgid "workspace.header.menu.disable-snap-pixel-grid"
msgstr "Disable snap to pixel"
-#: src/app/main/ui/workspace/main_menu.cljs:212
+#: src/app/main/ui/workspace/main_menu.cljs:213
msgid "workspace.header.menu.disable-snap-ruler-guides"
msgstr "Disable snap to ruler guides"
-#: src/app/main/ui/workspace/main_menu.cljs:244
+#: src/app/main/ui/workspace/main_menu.cljs:245
msgid "workspace.header.menu.enable-dynamic-alignment"
msgstr "Enable dynamic alignment"
-#: src/app/main/ui/workspace/main_menu.cljs:198
+#: src/app/main/ui/workspace/main_menu.cljs:199
msgid "workspace.header.menu.enable-scale-content"
msgstr "Enable proportional scale"
@@ -4280,103 +4287,103 @@ msgstr "Enable proportional scale"
msgid "workspace.header.menu.enable-scale-text"
msgstr "Enable scale text"
-#: src/app/main/ui/workspace/main_menu.cljs:229
+#: src/app/main/ui/workspace/main_menu.cljs:230
msgid "workspace.header.menu.enable-snap-guides"
msgstr "Snap to guides"
-#: src/app/main/ui/workspace/main_menu.cljs:259
+#: src/app/main/ui/workspace/main_menu.cljs:260
msgid "workspace.header.menu.enable-snap-pixel-grid"
msgstr "Enable snap to pixel"
-#: src/app/main/ui/workspace/main_menu.cljs:213
+#: src/app/main/ui/workspace/main_menu.cljs:214
msgid "workspace.header.menu.enable-snap-ruler-guides"
msgstr "Snap to ruler guides"
-#: src/app/main/ui/workspace/main_menu.cljs:388
+#: src/app/main/ui/workspace/main_menu.cljs:389
msgid "workspace.header.menu.hide-artboard-names"
msgstr "Hide board names"
-#: src/app/main/ui/workspace/main_menu.cljs:342
+#: src/app/main/ui/workspace/main_menu.cljs:343
msgid "workspace.header.menu.hide-guides"
msgstr "Hide guides"
-#: src/app/main/ui/workspace/main_menu.cljs:359
+#: src/app/main/ui/workspace/main_menu.cljs:360
msgid "workspace.header.menu.hide-palette"
msgstr "Hide color palette"
-#: src/app/main/ui/workspace/main_menu.cljs:400
+#: src/app/main/ui/workspace/main_menu.cljs:401
msgid "workspace.header.menu.hide-pixel-grid"
msgstr "Hide pixel grid"
-#: src/app/main/ui/workspace/main_menu.cljs:326
+#: src/app/main/ui/workspace/main_menu.cljs:327
msgid "workspace.header.menu.hide-rules"
msgstr "Hide rulers"
-#: src/app/main/ui/workspace/main_menu.cljs:373
+#: src/app/main/ui/workspace/main_menu.cljs:374
msgid "workspace.header.menu.hide-textpalette"
msgstr "Hide fonts palette"
-#: src/app/main/ui/workspace/main_menu.cljs:803
+#: src/app/main/ui/workspace/main_menu.cljs:852
msgid "workspace.header.menu.option.edit"
msgstr "Edit"
-#: src/app/main/ui/workspace/main_menu.cljs:792
+#: src/app/main/ui/workspace/main_menu.cljs:841
msgid "workspace.header.menu.option.file"
msgstr "File"
-#: src/app/main/ui/workspace/main_menu.cljs:849
+#: src/app/main/ui/workspace/main_menu.cljs:898
msgid "workspace.header.menu.option.help-info"
msgstr "Help & info"
-#: src/app/main/ui/workspace/main_menu.cljs:825
+#: src/app/main/ui/workspace/main_menu.cljs:874
msgid "workspace.header.menu.option.preferences"
msgstr "Preferences"
-#: src/app/main/ui/workspace/main_menu.cljs:814
+#: src/app/main/ui/workspace/main_menu.cljs:863
msgid "workspace.header.menu.option.view"
msgstr "View"
-#: src/app/main/ui/workspace/main_menu.cljs:471
+#: src/app/main/ui/workspace/main_menu.cljs:472
msgid "workspace.header.menu.redo"
msgstr "Redo"
-#: src/app/main/ui/workspace/main_menu.cljs:442
+#: src/app/main/ui/workspace/main_menu.cljs:443
msgid "workspace.header.menu.select-all"
msgstr "Select all"
-#: src/app/main/ui/workspace/main_menu.cljs:389
+#: src/app/main/ui/workspace/main_menu.cljs:390
msgid "workspace.header.menu.show-artboard-names"
msgstr "Show boards names"
-#: src/app/main/ui/workspace/main_menu.cljs:343
+#: src/app/main/ui/workspace/main_menu.cljs:344
msgid "workspace.header.menu.show-guides"
msgstr "Show guides"
-#: src/app/main/ui/workspace/main_menu.cljs:360
+#: src/app/main/ui/workspace/main_menu.cljs:361
msgid "workspace.header.menu.show-palette"
msgstr "Show color palette"
-#: src/app/main/ui/workspace/main_menu.cljs:401
+#: src/app/main/ui/workspace/main_menu.cljs:402
msgid "workspace.header.menu.show-pixel-grid"
msgstr "Show pixel grid"
-#: src/app/main/ui/workspace/main_menu.cljs:327
+#: src/app/main/ui/workspace/main_menu.cljs:328
msgid "workspace.header.menu.show-rules"
msgstr "Show rulers"
-#: src/app/main/ui/workspace/main_menu.cljs:374
+#: src/app/main/ui/workspace/main_menu.cljs:375
msgid "workspace.header.menu.show-textpalette"
msgstr "Show fonts palette"
-#: src/app/main/ui/workspace/main_menu.cljs:284
+#: src/app/main/ui/workspace/main_menu.cljs:285
msgid "workspace.header.menu.toggle-dark-theme"
msgstr "Switch to dark theme"
-#: src/app/main/ui/workspace/main_menu.cljs:283
+#: src/app/main/ui/workspace/main_menu.cljs:284
msgid "workspace.header.menu.toggle-light-theme"
msgstr "Switch to light theme"
-#: src/app/main/ui/workspace/main_menu.cljs:457
+#: src/app/main/ui/workspace/main_menu.cljs:458
msgid "workspace.header.menu.undo"
msgstr "Undo"
@@ -4398,7 +4405,7 @@ msgstr "Saved"
msgid "workspace.header.saving"
msgstr "Saving"
-#: src/app/main/ui/workspace/right_header.cljs:255
+#: src/app/main/ui/workspace/right_header.cljs:257
msgid "workspace.header.share"
msgstr "Share"
@@ -4406,7 +4413,7 @@ msgstr "Share"
msgid "workspace.header.unsaved"
msgstr "Unsaved changes"
-#: src/app/main/ui/workspace/right_header.cljs:260
+#: src/app/main/ui/workspace/right_header.cljs:262
msgid "workspace.header.viewer"
msgstr "View mode (%s)"
@@ -4467,19 +4474,19 @@ msgstr "Locate grid layout"
msgid "workspace.libraries.add"
msgstr "Add"
-#: src/app/main/ui/workspace/libraries.cljs:81, src/app/main/ui/workspace/libraries.cljs:100
+#: src/app/main/ui/workspace/libraries.cljs:84, src/app/main/ui/workspace/libraries.cljs:105
msgid "workspace.libraries.colors"
msgstr "%s colors"
-#: src/app/main/ui/workspace/color_palette.cljs:129
+#: src/app/main/ui/workspace/color_palette.cljs:137
msgid "workspace.libraries.colors.empty-palette"
msgstr "There are no color styles in your library yet"
-#: src/app/main/ui/workspace/text_palette.cljs:153
+#: src/app/main/ui/workspace/text_palette.cljs:161
msgid "workspace.libraries.colors.empty-typography-palette"
msgstr "There are no typography styles in your library yet"
-#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:60, src/app/main/ui/workspace/colorpicker/libraries.cljs:73, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:50
+#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:60, src/app/main/ui/workspace/colorpicker/libraries.cljs:74, src/app/main/ui/workspace/text_palette_ctx_menu.cljs:50
msgid "workspace.libraries.colors.file-library"
msgstr "File library"
@@ -4488,7 +4495,7 @@ msgstr "File library"
msgid "workspace.libraries.colors.hsv"
msgstr "HSV"
-#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:82, src/app/main/ui/workspace/colorpicker/libraries.cljs:72
+#: src/app/main/ui/workspace/color_palette_ctx_menu.cljs:82, src/app/main/ui/workspace/colorpicker/libraries.cljs:73
msgid "workspace.libraries.colors.recent-colors"
msgstr "Recent colors"
@@ -4505,23 +4512,35 @@ msgstr "RGBA"
msgid "workspace.libraries.colors.save-color"
msgstr "Save color style"
-#: src/app/main/ui/workspace/libraries.cljs:75, src/app/main/ui/workspace/libraries.cljs:92
+#: src/app/main/ui/workspace/libraries.cljs:78, src/app/main/ui/workspace/libraries.cljs:97
msgid "workspace.libraries.components"
msgstr "%s components"
-#: src/app/main/ui/workspace/libraries.cljs:216
+#: src/app/main/ui/workspace/libraries.cljs:353
+msgid "workspace.libraries.empty.add-some"
+msgstr "Or add some of these to try:"
+
+#: src/app/main/ui/workspace/libraries.cljs:347
+msgid "workspace.libraries.empty.no-libraries"
+msgstr "There are no Shared Libraries at you team, you can look for"
+
+#: src/app/main/ui/workspace/libraries.cljs:351
+msgid "workspace.libraries.empty.some-templates"
+msgstr "some templates in here"
+
+#: src/app/main/ui/workspace/libraries.cljs:267
msgid "workspace.libraries.file-library"
msgstr "File library"
-#: src/app/main/ui/workspace/libraries.cljs:78, src/app/main/ui/workspace/libraries.cljs:96
+#: src/app/main/ui/workspace/libraries.cljs:81, src/app/main/ui/workspace/libraries.cljs:101
msgid "workspace.libraries.graphics"
msgstr "%s graphics"
-#: src/app/main/ui/workspace/libraries.cljs:210
+#: src/app/main/ui/workspace/libraries.cljs:261
msgid "workspace.libraries.in-this-file"
msgstr "LIBRARIES IN THIS FILE"
-#: src/app/main/ui/workspace/libraries.cljs:517, src/app/main/ui/workspace/libraries.cljs:542
+#: src/app/main/ui/workspace/libraries.cljs:585, src/app/main/ui/workspace/libraries.cljs:611
msgid "workspace.libraries.libraries"
msgstr "LIBRARIES"
@@ -4530,43 +4549,43 @@ msgstr "LIBRARIES"
msgid "workspace.libraries.library"
msgstr "LIBRARY"
-#: src/app/main/ui/workspace/libraries.cljs:390
+#: src/app/main/ui/workspace/libraries.cljs:458
msgid "workspace.libraries.library-updates"
msgstr "LIBRARY UPDATES"
-#: src/app/main/ui/workspace/libraries.cljs:291
+#: src/app/main/ui/workspace/libraries.cljs:342
msgid "workspace.libraries.loading"
msgstr "Loading…"
-#: src/app/main/ui/workspace/libraries.cljs:300
+#: src/app/main/ui/workspace/libraries.cljs:368
msgid "workspace.libraries.more-templates"
msgstr "You can look for "
-#: src/app/main/ui/workspace/libraries.cljs:304
+#: src/app/main/ui/workspace/libraries.cljs:372
msgid "workspace.libraries.more-templates-link"
msgstr "more templates in here"
-#: src/app/main/ui/workspace/libraries.cljs:388
+#: src/app/main/ui/workspace/libraries.cljs:456
msgid "workspace.libraries.no-libraries-need-sync"
msgstr "There are no Shared Libraries that need update"
-#: src/app/main/ui/workspace/libraries.cljs:307
+#: src/app/main/ui/workspace/libraries.cljs:375
msgid "workspace.libraries.no-matches-for"
msgstr "No matches found for “%s“"
-#: src/app/main/ui/workspace/libraries.cljs:297
+#: src/app/main/ui/workspace/libraries.cljs:365
msgid "workspace.libraries.no-shared-libraries-available"
msgstr "There are no Shared Libraries available"
-#: src/app/main/ui/workspace/libraries.cljs:261
+#: src/app/main/ui/workspace/libraries.cljs:312
msgid "workspace.libraries.search-shared-libraries"
msgstr "Search shared libraries"
-#: src/app/main/ui/workspace/libraries.cljs:257
+#: src/app/main/ui/workspace/libraries.cljs:308
msgid "workspace.libraries.shared-libraries"
msgstr "SHARED LIBRARIES"
-#: src/app/main/ui/workspace/libraries.cljs:283
+#: src/app/main/ui/workspace/libraries.cljs:334
msgid "workspace.libraries.shared-library-btn"
msgstr "Connect library"
@@ -4578,35 +4597,26 @@ msgstr "Multiple typographies"
msgid "workspace.libraries.text.multiple-typography-tooltip"
msgstr "Unlink all typographies"
-#: src/app/main/ui/workspace/libraries.cljs:84, src/app/main/ui/workspace/libraries.cljs:104
+#: src/app/main/ui/workspace/libraries.cljs:87, src/app/main/ui/workspace/libraries.cljs:109
msgid "workspace.libraries.typography"
msgstr "%s typographies"
-#: src/app/main/ui/workspace/libraries.cljs:250
+#: src/app/main/ui/workspace/libraries.cljs:301
msgid "workspace.libraries.unlink-library-btn"
msgstr "Disconnect library"
-#: src/app/main/ui/workspace/libraries.cljs:410
+#: src/app/main/ui/workspace/libraries.cljs:478
msgid "workspace.libraries.update"
msgstr "Update"
-#: src/app/main/ui/workspace/libraries.cljs:485
+#: src/app/main/ui/workspace/libraries.cljs:553
msgid "workspace.libraries.update.see-all-changes"
msgstr "see all changes"
-#: src/app/main/ui/workspace/libraries.cljs:524
+#: src/app/main/ui/workspace/libraries.cljs:593
msgid "workspace.libraries.updates"
msgstr "UPDATES"
-msgid "workspace.libraries.empty.no-libraries"
-msgstr "There are no Shared Libraries at you team, you can look for"
-
-msgid "workspace.libraries.empty.some-templates"
-msgstr "some templates in here"
-
-msgid "workspace.libraries.empty.add-some"
-msgstr "Or add some of these to try:"
-
#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs:745
msgid "workspace.options.add-interaction"
msgstr "Click the + button to add interactions."
@@ -4719,7 +4729,7 @@ msgstr "Top & Bottom"
msgid "workspace.options.design"
msgstr "Design"
-#: src/app/main/ui/viewer/inspect/exports.cljs:147
+#: src/app/main/ui/viewer/inspect/exports.cljs:139
msgid "workspace.options.export"
msgstr "Export"
@@ -4728,37 +4738,37 @@ msgstr "Export"
msgid "workspace.options.export-multiple"
msgstr "Export selection"
-#: src/app/main/ui/viewer/inspect/exports.cljs:203, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:256
+#: src/app/main/ui/viewer/inspect/exports.cljs:195, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:248
msgid "workspace.options.export-object"
msgid_plural "workspace.options.export-object"
msgstr[0] "Export 1 element"
msgstr[1] "Export %s elements"
-#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:195
+#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:187
msgid "workspace.options.export.add-export"
msgstr "Add export"
-#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:207, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:242
+#: src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:199, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:234
msgid "workspace.options.export.remove-export"
msgstr "Remove export"
-#: src/app/main/ui/viewer/inspect/exports.cljs:186, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:236
+#: src/app/main/ui/viewer/inspect/exports.cljs:178, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:228
msgid "workspace.options.export.suffix"
msgstr "Suffix"
-#: src/app/main/ui/exports/assets.cljs:246
+#: src/app/main/ui/exports/assets.cljs:239
msgid "workspace.options.exporting-complete"
msgstr "Export complete"
-#: src/app/main/ui/exports/assets.cljs:176, src/app/main/ui/exports/assets.cljs:247, src/app/main/ui/viewer/inspect/exports.cljs:202, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:255
+#: src/app/main/ui/exports/assets.cljs:169, src/app/main/ui/exports/assets.cljs:240, src/app/main/ui/viewer/inspect/exports.cljs:194, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs:247
msgid "workspace.options.exporting-object"
msgstr "Exporting…"
-#: src/app/main/ui/exports/assets.cljs:245
+#: src/app/main/ui/exports/assets.cljs:238
msgid "workspace.options.exporting-object-error"
msgstr "Export failed"
-#: src/app/main/ui/exports/assets.cljs:248
+#: src/app/main/ui/exports/assets.cljs:241
msgid "workspace.options.exporting-object-slow"
msgstr "Export unexpectedly slow"
@@ -5414,7 +5424,7 @@ msgstr "Independent corners"
msgid "workspace.options.recent-fonts"
msgstr "Recent"
-#: src/app/main/ui/exports/assets.cljs:290
+#: src/app/main/ui/exports/assets.cljs:283
msgid "workspace.options.retry"
msgstr "Retry"
@@ -5784,7 +5794,7 @@ msgstr "No plugins installed yet"
msgid "workspace.plugins.error.manifest"
msgstr "The plugin manifest is incorrect."
-#: src/app/main/data/plugins.cljs:86, src/app/main/ui/workspace/main_menu.cljs:696, src/app/main/ui/workspace/plugins.cljs:82
+#: src/app/main/data/plugins.cljs:86, src/app/main/ui/workspace/main_menu.cljs:745, src/app/main/ui/workspace/plugins.cljs:82
msgid "workspace.plugins.error.need-editor"
msgstr "You need to be an editor to use this plugin"
@@ -5800,11 +5810,11 @@ msgstr "Install"
msgid "workspace.plugins.installed-plugins"
msgstr "Installed plugins"
-#: src/app/main/ui/workspace/main_menu.cljs:651
+#: src/app/main/ui/workspace/main_menu.cljs:700
msgid "workspace.plugins.menu.plugins-manager"
msgstr "Plugins manager"
-#: src/app/main/ui/workspace/main_menu.cljs:837
+#: src/app/main/ui/workspace/main_menu.cljs:886
msgid "workspace.plugins.menu.title"
msgstr "Plugins"
@@ -5899,11 +5909,11 @@ msgstr "'%s' PLUGIN IS INSTALLED FOR YOUR USER!"
msgid "workspace.plugins.try-out.try"
msgstr "TRY PLUGIN"
-#: src/app/main/ui/workspace/context_menu.cljs:451
+#: src/app/main/ui/workspace/context_menu.cljs:452
msgid "workspace.shape.menu.add-flex"
msgstr "Add flex layout"
-#: src/app/main/ui/workspace/context_menu.cljs:455
+#: src/app/main/ui/workspace/context_menu.cljs:456
msgid "workspace.shape.menu.add-grid"
msgstr "Add grid layout"
@@ -5911,91 +5921,91 @@ msgstr "Add grid layout"
msgid "workspace.shape.menu.add-layout"
msgstr "Add layout"
-#: src/app/main/ui/workspace/context_menu.cljs:194
+#: src/app/main/ui/workspace/context_menu.cljs:195
msgid "workspace.shape.menu.back"
msgstr "Send to back"
-#: src/app/main/ui/workspace/context_menu.cljs:191
+#: src/app/main/ui/workspace/context_menu.cljs:192
msgid "workspace.shape.menu.backward"
msgstr "Send backward"
-#: src/app/main/ui/workspace/context_menu.cljs:140
+#: src/app/main/ui/workspace/context_menu.cljs:141
msgid "workspace.shape.menu.copy"
msgstr "Copy"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:427
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:431
msgid "workspace.shape.menu.create-annotation"
msgstr "Create annotation"
-#: src/app/main/ui/workspace/context_menu.cljs:286
+#: src/app/main/ui/workspace/context_menu.cljs:287
msgid "workspace.shape.menu.create-artboard-from-selection"
msgstr "Selection to board"
-#: src/app/main/ui/workspace/context_menu.cljs:475
+#: src/app/main/ui/workspace/context_menu.cljs:476
msgid "workspace.shape.menu.create-component"
msgstr "Create component"
-#: src/app/main/ui/workspace/context_menu.cljs:479
+#: src/app/main/ui/workspace/context_menu.cljs:480
msgid "workspace.shape.menu.create-multiple-components"
msgstr "Create multiple components"
-#: src/app/main/ui/workspace/context_menu.cljs:143
+#: src/app/main/ui/workspace/context_menu.cljs:144
msgid "workspace.shape.menu.cut"
msgstr "Cut"
-#: src/app/main/ui/workspace/context_menu.cljs:496, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:764, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1052
+#: src/app/main/ui/workspace/context_menu.cljs:497, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:764, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1052
msgid "workspace.shape.menu.delete"
msgstr "Delete"
-#: src/app/main/ui/workspace/context_menu.cljs:402
+#: src/app/main/ui/workspace/context_menu.cljs:403
msgid "workspace.shape.menu.delete-flow-start"
msgstr "Delete flow start"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:432
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:436
msgid "workspace.shape.menu.detach-instance"
msgstr "Detach instance"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:431
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:435
msgid "workspace.shape.menu.detach-instances-in-bulk"
msgstr "Detach instances"
-#: src/app/main/ui/workspace/context_menu.cljs:346, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:75
+#: src/app/main/ui/workspace/context_menu.cljs:347, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:75
msgid "workspace.shape.menu.difference"
msgstr "Difference"
-#: src/app/main/ui/workspace/context_menu.cljs:149
+#: src/app/main/ui/workspace/context_menu.cljs:150
msgid "workspace.shape.menu.duplicate"
msgstr "Duplicate"
-#: src/app/main/ui/workspace/context_menu.cljs:332
+#: src/app/main/ui/workspace/context_menu.cljs:333
msgid "workspace.shape.menu.edit"
msgstr "Edit"
-#: src/app/main/ui/workspace/context_menu.cljs:352
+#: src/app/main/ui/workspace/context_menu.cljs:353
msgid "workspace.shape.menu.exclude"
msgstr "Exclude"
-#: src/app/main/ui/workspace/context_menu.cljs:359, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:89
+#: src/app/main/ui/workspace/context_menu.cljs:360, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:89
msgid "workspace.shape.menu.flatten"
msgstr "Flatten"
-#: src/app/main/ui/workspace/context_menu.cljs:209
+#: src/app/main/ui/workspace/context_menu.cljs:210
msgid "workspace.shape.menu.flip-horizontal"
msgstr "Flip horizontal"
-#: src/app/main/ui/workspace/context_menu.cljs:205
+#: src/app/main/ui/workspace/context_menu.cljs:206
msgid "workspace.shape.menu.flip-vertical"
msgstr "Flip vertical"
-#: src/app/main/ui/workspace/context_menu.cljs:404
+#: src/app/main/ui/workspace/context_menu.cljs:405
msgid "workspace.shape.menu.flow-start"
msgstr "Flow start"
-#: src/app/main/ui/workspace/context_menu.cljs:185
+#: src/app/main/ui/workspace/context_menu.cljs:186
msgid "workspace.shape.menu.forward"
msgstr "Bring forward"
-#: src/app/main/ui/workspace/context_menu.cljs:188
+#: src/app/main/ui/workspace/context_menu.cljs:189
msgid "workspace.shape.menu.front"
msgstr "Bring to front"
@@ -6004,43 +6014,43 @@ msgstr "Bring to front"
msgid "workspace.shape.menu.go-main"
msgstr "Go to main component file"
-#: src/app/main/ui/workspace/context_menu.cljs:272
+#: src/app/main/ui/workspace/context_menu.cljs:273
msgid "workspace.shape.menu.group"
msgstr "Group"
-#: src/app/main/ui/workspace/context_menu.cljs:374, src/app/main/ui/workspace/sidebar/layer_item.cljs:145
+#: src/app/main/ui/workspace/context_menu.cljs:375, src/app/main/ui/workspace/sidebar/layer_item.cljs:145
msgid "workspace.shape.menu.hide"
msgstr "Hide"
-#: src/app/main/ui/workspace/context_menu.cljs:562, src/app/main/ui/workspace/main_menu.cljs:414
+#: src/app/main/ui/workspace/context_menu.cljs:565, src/app/main/ui/workspace/main_menu.cljs:415
msgid "workspace.shape.menu.hide-ui"
msgstr "Show / Hide UI"
-#: src/app/main/ui/workspace/context_menu.cljs:349
+#: src/app/main/ui/workspace/context_menu.cljs:350
msgid "workspace.shape.menu.intersection"
msgstr "Intersection"
-#: src/app/main/ui/workspace/context_menu.cljs:382, src/app/main/ui/workspace/sidebar/layer_item.cljs:153, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189
+#: src/app/main/ui/workspace/context_menu.cljs:383, src/app/main/ui/workspace/sidebar/layer_item.cljs:153, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:189
msgid "workspace.shape.menu.lock"
msgstr "Lock"
-#: src/app/main/ui/workspace/context_menu.cljs:277
+#: src/app/main/ui/workspace/context_menu.cljs:278
msgid "workspace.shape.menu.mask"
msgstr "Mask"
-#: src/app/main/ui/workspace/context_menu.cljs:146, src/app/main/ui/workspace/context_menu.cljs:559
+#: src/app/main/ui/workspace/context_menu.cljs:147, src/app/main/ui/workspace/context_menu.cljs:562
msgid "workspace.shape.menu.paste"
msgstr "Paste"
-#: src/app/main/ui/workspace/context_menu.cljs:342
+#: src/app/main/ui/workspace/context_menu.cljs:343
msgid "workspace.shape.menu.path"
msgstr "Path"
-#: src/app/main/ui/workspace/context_menu.cljs:442
+#: src/app/main/ui/workspace/context_menu.cljs:443
msgid "workspace.shape.menu.remove-flex"
msgstr "Remove flex layout"
-#: src/app/main/ui/workspace/context_menu.cljs:445
+#: src/app/main/ui/workspace/context_menu.cljs:446
msgid "workspace.shape.menu.remove-grid"
msgstr "Remove grid layout"
@@ -6048,59 +6058,59 @@ msgstr "Remove grid layout"
msgid "workspace.shape.menu.remove-layout"
msgstr "Remove layout"
-#: src/app/main/ui/workspace/context_menu.cljs:235
+#: src/app/main/ui/workspace/context_menu.cljs:236
msgid "workspace.shape.menu.rename"
msgstr "Rename"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:436
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:440
msgid "workspace.shape.menu.reset-overrides"
msgstr "Reset overrides"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:439
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:443
msgid "workspace.shape.menu.restore-main"
msgstr "Restore main component"
-#: src/app/main/ui/workspace/context_menu.cljs:175
+#: src/app/main/ui/workspace/context_menu.cljs:176
msgid "workspace.shape.menu.select-layer"
msgstr "Select layer"
-#: src/app/main/ui/workspace/context_menu.cljs:371, src/app/main/ui/workspace/sidebar/layer_item.cljs:144
+#: src/app/main/ui/workspace/context_menu.cljs:372, src/app/main/ui/workspace/sidebar/layer_item.cljs:144
msgid "workspace.shape.menu.show"
msgstr "Show"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:424
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:428
msgid "workspace.shape.menu.show-in-assets"
msgstr "Show in assets panel"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:442, src/app/main/ui/workspace/sidebar/assets/components.cljs:585
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:446, src/app/main/ui/workspace/sidebar/assets/components.cljs:576
msgid "workspace.shape.menu.show-main"
msgstr "Show main component"
-#: src/app/main/ui/workspace/context_menu.cljs:222
+#: src/app/main/ui/workspace/context_menu.cljs:223
msgid "workspace.shape.menu.thumbnail-remove"
msgstr "Remove thumbnail"
-#: src/app/main/ui/workspace/context_menu.cljs:224
+#: src/app/main/ui/workspace/context_menu.cljs:225
msgid "workspace.shape.menu.thumbnail-set"
msgstr "Set as thumbnail"
-#: src/app/main/ui/workspace/context_menu.cljs:337
+#: src/app/main/ui/workspace/context_menu.cljs:338
msgid "workspace.shape.menu.transform-to-path"
msgstr "Transform to path"
-#: src/app/main/ui/workspace/context_menu.cljs:268
+#: src/app/main/ui/workspace/context_menu.cljs:269
msgid "workspace.shape.menu.ungroup"
msgstr "Ungroup"
-#: src/app/main/ui/workspace/context_menu.cljs:343, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:70
+#: src/app/main/ui/workspace/context_menu.cljs:344, src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:70
msgid "workspace.shape.menu.union"
msgstr "Union"
-#: src/app/main/ui/workspace/context_menu.cljs:379, src/app/main/ui/workspace/sidebar/layer_item.cljs:152, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195
+#: src/app/main/ui/workspace/context_menu.cljs:380, src/app/main/ui/workspace/sidebar/layer_item.cljs:152, src/app/main/ui/workspace/sidebar/options/menus/layer.cljs:195
msgid "workspace.shape.menu.unlock"
msgstr "Unlock"
-#: src/app/main/ui/workspace/context_menu.cljs:282
+#: src/app/main/ui/workspace/context_menu.cljs:283
msgid "workspace.shape.menu.unmask"
msgstr "Unmask"
@@ -6109,7 +6119,7 @@ msgstr "Unmask"
msgid "workspace.shape.menu.update-components-in-bulk"
msgstr "Update main components"
-#: src/app/main/ui/workspace/sidebar/assets/common.cljs:445
+#: src/app/main/ui/workspace/sidebar/assets/common.cljs:449
msgid "workspace.shape.menu.update-main"
msgstr "Update main component"
@@ -6125,39 +6135,39 @@ msgstr "Expand sidebar"
msgid "workspace.sidebar.history"
msgstr "History"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:524, src/app/main/ui/workspace/sidebar.cljs:110, src/app/main/ui/workspace/sidebar.cljs:114, src/app/main/ui/workspace/sidebar.cljs:123
+#: src/app/main/ui/workspace/sidebar/layers.cljs:525, src/app/main/ui/workspace/sidebar.cljs:110, src/app/main/ui/workspace/sidebar.cljs:114, src/app/main/ui/workspace/sidebar.cljs:123
msgid "workspace.sidebar.layers"
msgstr "Layers"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:310, src/app/main/ui/workspace/sidebar/layers.cljs:382
+#: src/app/main/ui/workspace/sidebar/layers.cljs:311, src/app/main/ui/workspace/sidebar/layers.cljs:383
msgid "workspace.sidebar.layers.components"
msgstr "Components"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:307, src/app/main/ui/workspace/sidebar/layers.cljs:340
+#: src/app/main/ui/workspace/sidebar/layers.cljs:308, src/app/main/ui/workspace/sidebar/layers.cljs:341
msgid "workspace.sidebar.layers.frames"
msgstr "Boards"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:308, src/app/main/ui/workspace/sidebar/layers.cljs:354
+#: src/app/main/ui/workspace/sidebar/layers.cljs:309, src/app/main/ui/workspace/sidebar/layers.cljs:355
msgid "workspace.sidebar.layers.groups"
msgstr "Groups"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:312, src/app/main/ui/workspace/sidebar/layers.cljs:410
+#: src/app/main/ui/workspace/sidebar/layers.cljs:313, src/app/main/ui/workspace/sidebar/layers.cljs:411
msgid "workspace.sidebar.layers.images"
msgstr "Images"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:309, src/app/main/ui/workspace/sidebar/layers.cljs:368
+#: src/app/main/ui/workspace/sidebar/layers.cljs:310, src/app/main/ui/workspace/sidebar/layers.cljs:369
msgid "workspace.sidebar.layers.masks"
msgstr "Masks"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:290
+#: src/app/main/ui/workspace/sidebar/layers.cljs:291
msgid "workspace.sidebar.layers.search"
msgstr "Search layers"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:313, src/app/main/ui/workspace/sidebar/layers.cljs:424
+#: src/app/main/ui/workspace/sidebar/layers.cljs:314, src/app/main/ui/workspace/sidebar/layers.cljs:425
msgid "workspace.sidebar.layers.shapes"
msgstr "Shapes"
-#: src/app/main/ui/workspace/sidebar/layers.cljs:311, src/app/main/ui/workspace/sidebar/layers.cljs:396
+#: src/app/main/ui/workspace/sidebar/layers.cljs:312, src/app/main/ui/workspace/sidebar/layers.cljs:397
msgid "workspace.sidebar.layers.texts"
msgstr "Texts"
@@ -6304,35 +6314,35 @@ msgstr "Color Palette (%s)"
msgid "workspace.toolbar.comments"
msgstr "Comments (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:181, src/app/main/ui/workspace/top_toolbar.cljs:182
+#: src/app/main/ui/workspace/top_toolbar.cljs:182, src/app/main/ui/workspace/top_toolbar.cljs:183
msgid "workspace.toolbar.curve"
msgstr "Curve (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:161, src/app/main/ui/workspace/top_toolbar.cljs:162
+#: src/app/main/ui/workspace/top_toolbar.cljs:162, src/app/main/ui/workspace/top_toolbar.cljs:163
msgid "workspace.toolbar.ellipse"
msgstr "Ellipse (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:143, src/app/main/ui/workspace/top_toolbar.cljs:144
+#: src/app/main/ui/workspace/top_toolbar.cljs:144, src/app/main/ui/workspace/top_toolbar.cljs:145
msgid "workspace.toolbar.frame"
msgstr "Board (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:60, src/app/main/ui/workspace/top_toolbar.cljs:61
+#: src/app/main/ui/workspace/top_toolbar.cljs:61, src/app/main/ui/workspace/top_toolbar.cljs:62
msgid "workspace.toolbar.image"
msgstr "Image (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:133, src/app/main/ui/workspace/top_toolbar.cljs:134
+#: src/app/main/ui/workspace/top_toolbar.cljs:134, src/app/main/ui/workspace/top_toolbar.cljs:135
msgid "workspace.toolbar.move"
msgstr "Move (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:190, src/app/main/ui/workspace/top_toolbar.cljs:191
+#: src/app/main/ui/workspace/top_toolbar.cljs:191, src/app/main/ui/workspace/top_toolbar.cljs:192
msgid "workspace.toolbar.path"
msgstr "Path (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:201, src/app/main/ui/workspace/top_toolbar.cljs:202
+#: src/app/main/ui/workspace/top_toolbar.cljs:202, src/app/main/ui/workspace/top_toolbar.cljs:203
msgid "workspace.toolbar.plugins"
msgstr "Plugins (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:152, src/app/main/ui/workspace/top_toolbar.cljs:153
+#: src/app/main/ui/workspace/top_toolbar.cljs:153, src/app/main/ui/workspace/top_toolbar.cljs:154
msgid "workspace.toolbar.rect"
msgstr "Rectangle (%s)"
@@ -6341,7 +6351,7 @@ msgstr "Rectangle (%s)"
msgid "workspace.toolbar.shortcuts"
msgstr "Shortcuts (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:170, src/app/main/ui/workspace/top_toolbar.cljs:171
+#: src/app/main/ui/workspace/top_toolbar.cljs:171, src/app/main/ui/workspace/top_toolbar.cljs:172
msgid "workspace.toolbar.text"
msgstr "Text (%s)"
@@ -6349,7 +6359,7 @@ msgstr "Text (%s)"
msgid "workspace.toolbar.text-palette"
msgstr "Typographies (%s)"
-#: src/app/main/ui/workspace/top_toolbar.cljs:219, src/app/main/ui/workspace/top_toolbar.cljs:220
+#: src/app/main/ui/workspace/top_toolbar.cljs:220, src/app/main/ui/workspace/top_toolbar.cljs:221
msgid "workspace.toolbar.toggle-toolbar"
msgstr "Toggle toolbar"
@@ -6362,143 +6372,143 @@ msgstr "Done"
msgid "workspace.top-bar.view-only"
msgstr "**Inspecting code** (View Only)"
-#: src/app/main/ui/workspace/sidebar/history.cljs:331
+#: src/app/main/ui/workspace/sidebar/history.cljs:332
msgid "workspace.undo.empty"
msgstr "There are no history changes so far"
-#: src/app/main/ui/workspace/sidebar/history.cljs:145
+#: src/app/main/ui/workspace/sidebar/history.cljs:146
msgid "workspace.undo.entry.delete"
msgstr "Deleted %s"
-#: src/app/main/ui/workspace/sidebar/history.cljs:144
+#: src/app/main/ui/workspace/sidebar/history.cljs:145
msgid "workspace.undo.entry.modify"
msgstr "Modified %s"
-#: src/app/main/ui/workspace/sidebar/history.cljs:146
+#: src/app/main/ui/workspace/sidebar/history.cljs:147
msgid "workspace.undo.entry.move"
msgstr "Moved objects"
-#: src/app/main/ui/workspace/sidebar/history.cljs:109
+#: src/app/main/ui/workspace/sidebar/history.cljs:110
msgid "workspace.undo.entry.multiple.circle"
msgstr "circles"
-#: src/app/main/ui/workspace/sidebar/history.cljs:110
+#: src/app/main/ui/workspace/sidebar/history.cljs:111
msgid "workspace.undo.entry.multiple.color"
msgstr "color assets"
-#: src/app/main/ui/workspace/sidebar/history.cljs:111
+#: src/app/main/ui/workspace/sidebar/history.cljs:112
msgid "workspace.undo.entry.multiple.component"
msgstr "components"
-#: src/app/main/ui/workspace/sidebar/history.cljs:112
+#: src/app/main/ui/workspace/sidebar/history.cljs:113
msgid "workspace.undo.entry.multiple.curve"
msgstr "curves"
-#: src/app/main/ui/workspace/sidebar/history.cljs:113
+#: src/app/main/ui/workspace/sidebar/history.cljs:114
msgid "workspace.undo.entry.multiple.frame"
msgstr "board"
-#: src/app/main/ui/workspace/sidebar/history.cljs:114
+#: src/app/main/ui/workspace/sidebar/history.cljs:115
msgid "workspace.undo.entry.multiple.group"
msgstr "groups"
-#: src/app/main/ui/workspace/sidebar/history.cljs:115
+#: src/app/main/ui/workspace/sidebar/history.cljs:116
msgid "workspace.undo.entry.multiple.media"
msgstr "graphic assets"
-#: src/app/main/ui/workspace/sidebar/history.cljs:116
+#: src/app/main/ui/workspace/sidebar/history.cljs:117
msgid "workspace.undo.entry.multiple.multiple"
msgstr "objects"
-#: src/app/main/ui/workspace/sidebar/history.cljs:117
+#: src/app/main/ui/workspace/sidebar/history.cljs:118
msgid "workspace.undo.entry.multiple.page"
msgstr "pages"
-#: src/app/main/ui/workspace/sidebar/history.cljs:118
+#: src/app/main/ui/workspace/sidebar/history.cljs:119
msgid "workspace.undo.entry.multiple.path"
msgstr "paths"
-#: src/app/main/ui/workspace/sidebar/history.cljs:119
+#: src/app/main/ui/workspace/sidebar/history.cljs:120
msgid "workspace.undo.entry.multiple.rect"
msgstr "rectangles"
-#: src/app/main/ui/workspace/sidebar/history.cljs:120
+#: src/app/main/ui/workspace/sidebar/history.cljs:121
msgid "workspace.undo.entry.multiple.shape"
msgstr "shapes"
-#: src/app/main/ui/workspace/sidebar/history.cljs:121
+#: src/app/main/ui/workspace/sidebar/history.cljs:122
msgid "workspace.undo.entry.multiple.text"
msgstr "texts"
-#: src/app/main/ui/workspace/sidebar/history.cljs:122
+#: src/app/main/ui/workspace/sidebar/history.cljs:123
msgid "workspace.undo.entry.multiple.typography"
msgstr "typography assets"
-#: src/app/main/ui/workspace/sidebar/history.cljs:143
+#: src/app/main/ui/workspace/sidebar/history.cljs:144
msgid "workspace.undo.entry.new"
msgstr "New %s"
-#: src/app/main/ui/workspace/sidebar/history.cljs:123
+#: src/app/main/ui/workspace/sidebar/history.cljs:124
msgid "workspace.undo.entry.single.circle"
msgstr "circle"
-#: src/app/main/ui/workspace/sidebar/history.cljs:124
+#: src/app/main/ui/workspace/sidebar/history.cljs:125
msgid "workspace.undo.entry.single.color"
msgstr "color asset"
-#: src/app/main/ui/workspace/sidebar/history.cljs:125
+#: src/app/main/ui/workspace/sidebar/history.cljs:126
msgid "workspace.undo.entry.single.component"
msgstr "component"
-#: src/app/main/ui/workspace/sidebar/history.cljs:126
+#: src/app/main/ui/workspace/sidebar/history.cljs:127
msgid "workspace.undo.entry.single.curve"
msgstr "curve"
-#: src/app/main/ui/workspace/sidebar/history.cljs:127
+#: src/app/main/ui/workspace/sidebar/history.cljs:128
msgid "workspace.undo.entry.single.frame"
msgstr "board"
-#: src/app/main/ui/workspace/sidebar/history.cljs:128
+#: src/app/main/ui/workspace/sidebar/history.cljs:129
msgid "workspace.undo.entry.single.group"
msgstr "group"
-#: src/app/main/ui/workspace/sidebar/history.cljs:129
+#: src/app/main/ui/workspace/sidebar/history.cljs:130
msgid "workspace.undo.entry.single.image"
msgstr "image"
-#: src/app/main/ui/workspace/sidebar/history.cljs:130
+#: src/app/main/ui/workspace/sidebar/history.cljs:131
msgid "workspace.undo.entry.single.media"
msgstr "graphic asset"
-#: src/app/main/ui/workspace/sidebar/history.cljs:131
+#: src/app/main/ui/workspace/sidebar/history.cljs:132
msgid "workspace.undo.entry.single.multiple"
msgstr "object"
-#: src/app/main/ui/workspace/sidebar/history.cljs:132
+#: src/app/main/ui/workspace/sidebar/history.cljs:133
msgid "workspace.undo.entry.single.page"
msgstr "page"
-#: src/app/main/ui/workspace/sidebar/history.cljs:133
+#: src/app/main/ui/workspace/sidebar/history.cljs:134
msgid "workspace.undo.entry.single.path"
msgstr "path"
-#: src/app/main/ui/workspace/sidebar/history.cljs:134
+#: src/app/main/ui/workspace/sidebar/history.cljs:135
msgid "workspace.undo.entry.single.rect"
msgstr "rectangle"
-#: src/app/main/ui/workspace/sidebar/history.cljs:135
+#: src/app/main/ui/workspace/sidebar/history.cljs:136
msgid "workspace.undo.entry.single.shape"
msgstr "shape"
-#: src/app/main/ui/workspace/sidebar/history.cljs:136
+#: src/app/main/ui/workspace/sidebar/history.cljs:137
msgid "workspace.undo.entry.single.text"
msgstr "text"
-#: src/app/main/ui/workspace/sidebar/history.cljs:137
+#: src/app/main/ui/workspace/sidebar/history.cljs:138
msgid "workspace.undo.entry.single.typography"
msgstr "typography asset"
-#: src/app/main/ui/workspace/sidebar/history.cljs:147
+#: src/app/main/ui/workspace/sidebar/history.cljs:148
msgid "workspace.undo.entry.unknown"
msgstr "Operation over %s"
@@ -6507,95 +6517,101 @@ msgstr "Operation over %s"
msgid "workspace.undo.title"
msgstr "History"
-#: src/app/main/data/workspace/libraries.cljs:1147, src/app/main/ui/workspace/sidebar/versions.cljs:285
+#: src/app/main/data/workspace/libraries.cljs:1178, src/app/main/ui/workspace/sidebar/versions.cljs:289
msgid "workspace.updates.dismiss"
msgstr "Dismiss"
-#: src/app/main/data/workspace/libraries.cljs:1145
+#: src/app/main/data/workspace/libraries.cljs:1176
msgid "workspace.updates.more-info"
msgstr "More info"
-#: src/app/main/data/workspace/libraries.cljs:1143
+#: src/app/main/data/workspace/libraries.cljs:1174
msgid "workspace.updates.there-are-updates"
msgstr "There are updates in shared libraries"
-#: src/app/main/data/workspace/libraries.cljs:1150
+#: src/app/main/data/workspace/libraries.cljs:1181
msgid "workspace.updates.update"
msgstr "Update"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:193
+#: src/app/main/ui/workspace/sidebar/versions.cljs:196
msgid "workspace.versions.autosaved.entry"
msgstr "%s autosave versions"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:187
+#: src/app/main/ui/workspace/sidebar/versions.cljs:190
msgid "workspace.versions.autosaved.version"
msgstr "Autosaved %s"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:224
+#: src/app/main/ui/workspace/sidebar/versions.cljs:227
msgid "workspace.versions.button.pin"
msgstr "Pin version"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:219
+#: src/app/main/ui/workspace/sidebar/versions.cljs:222
msgid "workspace.versions.button.restore"
msgstr "Restore version"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:345, src/app/main/ui/workspace/sidebar/versions.cljs:347
+#: src/app/main/ui/workspace/sidebar/versions.cljs:361, src/app/main/ui/workspace/sidebar/versions.cljs:363
msgid "workspace.versions.button.save"
msgstr "Save version"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:354
+#: src/app/main/ui/workspace/sidebar/versions.cljs:370
msgid "workspace.versions.empty"
msgstr "There are no versions yet"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:190
+#: src/app/main/ui/workspace/sidebar/versions.cljs:193
msgid "workspace.versions.expand-snapshot"
msgstr "Expand snapshots"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:327
+#: src/app/main/ui/workspace/sidebar/versions.cljs:343
msgid "workspace.versions.filter.all"
msgstr "All versions"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:326
+#: src/app/main/ui/workspace/sidebar/versions.cljs:342
msgid "workspace.versions.filter.label"
msgstr "Versions filter"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:328
+#: src/app/main/ui/workspace/sidebar/versions.cljs:344
msgid "workspace.versions.filter.mine"
msgstr "My versions"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:334
+#: src/app/main/ui/workspace/sidebar/versions.cljs:350
msgid "workspace.versions.filter.user"
msgstr "%s's versions"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:340
+#: src/app/main/ui/workspace/sidebar/versions.cljs:356
msgid "workspace.versions.loading"
msgstr "Loading..."
-#: src/app/main/ui/workspace/sidebar/versions.cljs:283
+#: src/app/main/ui/workspace/sidebar/versions.cljs:287
msgid "workspace.versions.restore-warning"
msgstr "Do you want to restore this version?"
-msgid "workspace.versions.warning.text"
-msgstr "Autosaved versions will be kept for %s days."
-
-#, markdown
-msgid "workspace.versions.warning.subtext"
-msgstr "If you'd like to increase this limit, write to us at [support@penpot.app](%s)"
-
-#: src/app/main/ui/workspace/sidebar/versions.cljs:207
+#: src/app/main/ui/workspace/sidebar/versions.cljs:210
msgid "workspace.versions.snapshot-menu"
msgstr "Open snapshot menu"
-#: src/app/main/ui/workspace/sidebar/versions.cljs:138
+#: src/app/main/ui/workspace/sidebar.cljs:242
+msgid "workspace.versions.tab.actions"
+msgstr "Actions"
+
+#: src/app/main/ui/workspace/sidebar.cljs:241
+msgid "workspace.versions.tab.history"
+msgstr "History"
+
+#: src/app/main/ui/workspace/sidebar/versions.cljs:141
msgid "workspace.versions.version-menu"
msgstr "Open version menu"
+#: src/app/main/ui/workspace/sidebar/versions.cljs:402
+#, markdown
+msgid "workspace.versions.warning.subtext"
+msgstr ""
+"If you'd like to increase this limit, write to us at "
+"[support@penpot.app](%s)"
+
+#: src/app/main/ui/workspace/sidebar/versions.cljs:397
+msgid "workspace.versions.warning.text"
+msgstr "Autosaved versions will be kept for %s days."
+
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Click to close the path"
-
-msgid "workspace.versions.tab.history"
-msgstr "History"
-
-msgid "workspace.versions.tab.actions"
-msgstr "Actions"
diff --git a/frontend/translations/es.po b/frontend/translations/es.po
index 23691241e2..e1a05a95f9 100644
--- a/frontend/translations/es.po
+++ b/frontend/translations/es.po
@@ -394,6 +394,10 @@ msgstr "El token expirará el %s"
msgid "dashboard.access-tokens.token-will-not-expire"
msgstr "El token no tiene fecha de expiración"
+#: src/app/main/ui/dashboard/placeholder.cljs:48
+msgid "dashboard.add-file"
+msgstr "Añadir archivo"
+
#: src/app/main/ui/dashboard/file_menu.cljs:311, src/app/main/ui/workspace/main_menu.cljs:585
msgid "dashboard.add-shared"
msgstr "Añadir como Biblioteca Compartida"