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"