From 50dbe6ab127e6b6ffa1ad04b869ca6651885e7c7 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Thu, 11 Dec 2025 21:34:18 +0100 Subject: [PATCH 01/18] :bug: Fix horizontal scroll on layer panel (#7956) --- CHANGES.md | 1 + .../src/app/main/ui/workspace/sidebar/layer_item.scss | 3 ++- frontend/src/app/main/ui/workspace/sidebar/layers.scss | 8 +++----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b9e4471cfa..011361113a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -94,6 +94,7 @@ example. It's still usable as before, we just removed the example. - Fix incorrect interaction betwen hower and scroll on assets sidebar [Taiga #12389](https://tree.taiga.io/project/penpot/issue/12389) - Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841) - Fix referencing typography tokens on font-family tokens [Taiga #12492](https://tree.taiga.io/project/penpot/issue/12492) +- Fix horizontal scroll on layer panel [Taiga #12843](https://tree.taiga.io/project/penpot/issue/12843) ## 2.11.1 diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss index e37644ad63..43455c1bd0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss @@ -5,6 +5,7 @@ // Copyright (c) KALEIDOS INC @use "refactor/common-refactor.scss" as deprecated; +@use "ds/_utils.scss" as *; .layer-row { --layer-indentation-size: calc(#{deprecated.$s-4} * 6); @@ -87,7 +88,7 @@ height: deprecated.$s-32; width: calc(100% - (var(--depth) * var(--layer-indentation-size))); cursor: pointer; - + min-width: px2rem(140); &.filtered { width: calc(100% - deprecated.$s-12); } diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.scss b/frontend/src/app/main/ui/workspace/sidebar/layers.scss index 89818cf9c5..353eaf009e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.scss @@ -211,9 +211,7 @@ overflow-x: auto; overflow-y: overlay; scrollbar-gutter: stable; - - .element-list { - width: var(--left-sidebar-width); - display: grid; - } +} +.element-list { + display: grid; } From 2ed39e43c30a2a11d3aa3f5a369185405f086c6e Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 11 Dec 2025 23:42:41 +0100 Subject: [PATCH 02/18] :bug: Fix issue on reading rlimit config --- backend/src/app/rpc/rlimit.clj | 64 +++++++++++++-------------- backend/src/app/rpc/rlimit/bucket.lua | 2 +- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/backend/src/app/rpc/rlimit.clj b/backend/src/app/rpc/rlimit.clj index d181b843fe..03ae35831f 100644 --- a/backend/src/app/rpc/rlimit.clj +++ b/backend/src/app/rpc/rlimit.clj @@ -104,28 +104,29 @@ (def ^:private schema:limit [:and [:map - [::name :any] + [::name :keyword] [::strategy schema:strategy] [::key :string] - [::opts :string]] - [:or - [:map - [::capacity ::sm/int] - [::rate ::sm/int] - [::internal ::ct/duration] - [::params [::sm/vec :any]]] - [:map - [::nreq ::sm/int] - [::unit [:enum :days :hours :minutes :seconds :weeks]]]]]) + [::opts :string] + [::capacity {:optional true} ::sm/int] + [::rate {:optional true} ::sm/int] + [::interval {:optional true} ::ct/duration] + [::params {:optional true} [::sm/vec :any]] + [::permits {:optional true} ::sm/int] + [::unit {:optional true} [:enum :days :hours :minutes :seconds :weeks]]] + [:fn (fn [attrs] + (let [contains-fn (partial contains? attrs)] + (or (every? contains-fn [::capacity ::rate ::interval]) + (every? contains-fn [::permits ::unit]))))]]) (def ^:private schema:limits [:map-of :keyword [::sm/vec schema:limit]]) (def ^:private valid-limit-tuple? - (sm/lazy-validator schema:limit-tuple)) + (sm/validator schema:limit-tuple)) (def ^:private valid-rlimit-instance? - (sm/lazy-validator ::rpc/rlimit)) + (sm/validator ::rpc/rlimit)) (defmethod parse-limit :window [[name strategy opts :as vlimit]] @@ -134,16 +135,16 @@ (merge {::name name ::strategy strategy} - (if-let [[_ nreq unit] (re-find window-opts-re opts)] - (let [nreq (parse-long nreq)] - {::nreq nreq + (if-let [[_ permits unit] (re-find window-opts-re opts)] + (let [permits (parse-long permits)] + {::permits permits ::unit (case unit "d" :days "h" :hours "m" :minutes "s" :seconds "w" :weeks) - ::key (str "ratelimit.window." (d/name name)) + ::key (str "penpot.rlimit." (cf/get :tenant) ".window." (d/name name)) ::opts opts}) (ex/raise :type :validation :code :invalid-window-limit-opts @@ -164,15 +165,15 @@ ::interval interval ::opts opts ::params [(->seconds interval) rate capacity] - ::key (str "ratelimit.bucket." (d/name name))}) + ::key (str "penpot.rlimit." (cf/get :tenant) ".bucket." (d/name name))}) (ex/raise :type :validation :code :invalid-bucket-limit-opts :hint (str/ffmt "looks like '%' does not have a valid format" opts)))) (defmethod process-limit :bucket - [rconn user-id now {:keys [::key ::params ::service ::capacity ::interval ::rate] :as limit}] + [rconn profile-id now {:keys [::key ::params ::service ::capacity ::interval ::rate] :as limit}] (let [script (-> bucket-rate-limit-script - (assoc ::rscript/keys [(str key "." service "." user-id)]) + (assoc ::rscript/keys [(str key "." service "." profile-id)]) (assoc ::rscript/vals (conj params (->seconds now)))) result (rds/eval rconn script) allowed? (boolean (nth result 0)) @@ -192,18 +193,18 @@ (assoc ::lresult/remaining remaining)))) (defmethod process-limit :window - [rconn user-id now {:keys [::nreq ::unit ::key ::service] :as limit}] + [rconn profile-id now {:keys [::permits ::unit ::key ::service] :as limit}] (let [ts (ct/truncate now unit) ttl (ct/diff now (ct/plus ts {unit 1})) script (-> window-rate-limit-script - (assoc ::rscript/keys [(str key "." service "." user-id "." (ct/format-inst ts))]) - (assoc ::rscript/vals [nreq (->seconds ttl)])) + (assoc ::rscript/keys [(str key "." service "." profile-id "." (ct/format-inst ts))]) + (assoc ::rscript/vals [permits (->seconds ttl)])) result (rds/eval rconn script) allowed? (boolean (nth result 0)) remaining (nth result 1)] (l/trace :hint "limit processed" :service service - :limit (name (::name limit)) + :name (name (::name limit)) :strategy (name (::strategy limit)) :opts (::opts limit) :allowed allowed? @@ -214,8 +215,8 @@ (assoc ::lresult/reset (ct/plus ts {unit 1}))))) (defn- process-limits - [rconn user-id limits now] - (let [results (into [] (map (partial process-limit rconn user-id now)) limits) + [rconn profile-id limits now] + (let [results (into [] (map (partial process-limit rconn profile-id now)) limits) remaining (->> results (d/index-by ::name ::lresult/remaining) (uri/map->query-string)) @@ -227,7 +228,7 @@ (when rejected (l/warn :hint "rejected rate limit" - :user-id (str user-id) + :profile-id (str profile-id) :limit-service (-> rejected ::service name) :limit-name (-> rejected ::name name) :limit-strategy (-> rejected ::strategy name))) @@ -371,12 +372,9 @@ (defn- on-refresh-error [_ cause] (when-not (instance? java.util.concurrent.RejectedExecutionException cause) - (if-let [explain (-> cause ex-data ex/explain)] - (l/warn ::l/raw (str "unable to refresh config, invalid format:\n" explain) - ::l/sync? true) - (l/warn :hint "unexpected exception on loading config" - :cause cause - ::l/sync? true)))) + (l/warn :hint "unexpected exception on loading config" + :cause cause + ::l/sync? true))) (defn- get-config-path [] diff --git a/backend/src/app/rpc/rlimit/bucket.lua b/backend/src/app/rpc/rlimit/bucket.lua index 4200dec4d1..32512d3506 100644 --- a/backend/src/app/rpc/rlimit/bucket.lua +++ b/backend/src/app/rpc/rlimit/bucket.lua @@ -25,9 +25,9 @@ local allowed = filled >= requested local newTokens = filled if allowed then newTokens = filled - requested + redis.call("hset", tokensKey, "tokens", newTokens, "timestamp", timestamp) end -redis.call("hset", tokensKey, "tokens", newTokens, "timestamp", timestamp) redis.call("expire", tokensKey, ttl) return { allowed, newTokens } From 974495e08f0725d9b49104c3cddbfa83e676df04 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 12 Dec 2025 08:15:54 +0100 Subject: [PATCH 03/18] :sparkles: Reduce log level for profile picture download error Because it is not blocking operation and does not provents user to proceed. --- backend/src/app/rpc/commands/auth.clj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 742077e875..fb5db45ef7 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -307,7 +307,8 @@ :content-type (:mtype input)})] (:id sobject)) (catch Throwable cause - (l/err :hint "unable to import profile picture" + (l/wrn :hint "unable to import profile picture" + :uri uri :cause cause) nil))) From 81b72c5acd526a282be11747b4a08951ce5ea4b9 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 12 Dec 2025 11:24:53 +0100 Subject: [PATCH 04/18] :bug: Fix exception on assinging gradient to shadow on multiple selection --- .../src/app/main/data/workspace/colors.cljs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index 38cfe651ac..5856cec029 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -14,7 +14,7 @@ [app.common.types.fills :as types.fills] [app.common.types.library :as ctl] [app.common.types.shape :as shp] - [app.common.types.shape.shadow :refer [check-shadow]] + [app.common.types.shape.shadow :as types.shadow] [app.common.types.text :as txt] [app.main.broadcast :as mbc] [app.main.data.helpers :as dsh] @@ -406,30 +406,30 @@ (defn change-shadow [ids attrs index] - (ptk/reify ::change-shadow - ptk/WatchEvent - (watch [_ _ _] - (rx/of (dwsh/update-shapes - ids - (fn [shape] - (let [;; If we try to set a gradient to a shadow (for - ;; example using the color selection from - ;; multiple shapes) let's use the first stop - ;; color - attrs (cond-> attrs - (:gradient attrs) - (dm/get-in [:gradient :stops 0])) + (letfn [(update-shadow [shape] + (let [;; If we try to set a gradient to a shadow (for + ;; example using the color selection from + ;; multiple shapes) let's use the first stop + ;; color + attrs (cond-> attrs + (:gradient attrs) + (-> (dm/get-in [:gradient :stops 0]) + (select-keys types.shadow/color-attrs))) - attrs' (-> (dm/get-in shape [:shadow index :color]) - (merge attrs) - (d/without-nils))] - (assoc-in shape [:shadow index :color] attrs')))))))) + attrs' (-> (dm/get-in shape [:shadow index :color]) + (merge attrs) + (d/without-nils))] + (assoc-in shape [:shadow index :color] attrs')))] + (ptk/reify ::change-shadow + ptk/WatchEvent + (watch [_ _ _] + (rx/of (dwsh/update-shapes ids update-shadow)))))) (defn add-shadow [ids shadow] (assert - (check-shadow shadow) + (types.shadow/check-shadow shadow) "expected a valid shadow struct") (assert @@ -1146,16 +1146,16 @@ (defn- shadow->color-attr "Given a stroke map enriched with :shape-id, :index, and optionally :has-token-applied / :token-name, returns a color attribute map. - + If :has-token-applied is true, adds token metadata to :attrs: {:has-token-applied true :token-name } - + Args: - stroke: map with stroke info, including :shape-id and :index - file-id: current file UUID - libraries: map of shared color libraries - + Returns: A map like: {:attrs {...color data...} @@ -1260,12 +1260,12 @@ will include extra attributes in its :attrs map: {:has-token-applied true :token-name } - + Args: - shapes: vector of shape maps - file-id: current file UUID - libraries: map of shared color libraries - + Returns: A vector of color attribute maps with metadata for each shape." [shapes file-id libraries] From 507bf7445bd6eb19ff7cef0acf9a1e1191fcbb54 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 12 Dec 2025 09:52:33 +0100 Subject: [PATCH 05/18] :bug: Fix tokens-lib encoding when value is nilable --- common/src/app/common/types/tokens_lib.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index 70e36bfc2f..90fd5a52ec 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -1410,8 +1410,8 @@ Will return a value that matches this schema: ;; NOTE: we can't assign statically at eval time the value of a ;; function that is declared but not defined; so we need to pass ;; an anonymous function and delegate the resolution to runtime - {:encode/json #(export-dtcg-json %) - :decode/json #(read-multi-set-dtcg %) + {:encode/json #(some-> % export-dtcg-json) + :decode/json #(some-> % read-multi-set-dtcg) ;; FIXME: add better, more reallistic generator :gen/gen (->> (sg/small-int) (sg/fmap (fn [_] From 94f95ca6b8f0ab8cd6c9f7853d24a7cdcbca51d0 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 12 Dec 2025 12:33:38 +0100 Subject: [PATCH 06/18] :bug: Fix incorrect redis connection error handling --- backend/src/app/worker/dispatcher.clj | 49 ++++++++++++++------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/backend/src/app/worker/dispatcher.clj b/backend/src/app/worker/dispatcher.clj index f95ade8e1c..818c50c18c 100644 --- a/backend/src/app/worker/dispatcher.clj +++ b/backend/src/app/worker/dispatcher.clj @@ -137,33 +137,34 @@ RETURNING task.id, task.queue") ::wait))) (run-batch [] - (let [rconn (rds/connect cfg)] - (try - (-> cfg - (assoc ::rds/conn rconn) - (db/tx-run! run-batch')) + (try + (let [rconn (rds/connect cfg)] + (try + (-> cfg + (assoc ::rds/conn rconn) + (db/tx-run! run-batch')) + (finally + (.close ^AutoCloseable rconn)))) - (catch InterruptedException cause - (throw cause)) - (catch Exception cause - (cond - (rds/exception? cause) - (do - (l/wrn :hint "redis exception (will retry in an instant)" :cause cause) - (px/sleep timeout)) + (catch InterruptedException cause + (throw cause)) - (db/sql-exception? cause) - (do - (l/wrn :hint "database exception (will retry in an instant)" :cause cause) - (px/sleep timeout)) + (catch Exception cause + (cond + (rds/exception? cause) + (do + (l/wrn :hint "redis exception (will retry in an instant)" :cause cause) + (px/sleep timeout)) - :else - (do - (l/err :hint "unhandled exception (will retry in an instant)" :cause cause) - (px/sleep timeout)))) + (db/sql-exception? cause) + (do + (l/wrn :hint "database exception (will retry in an instant)" :cause cause) + (px/sleep timeout)) - (finally - (.close ^AutoCloseable rconn))))) + :else + (do + (l/err :hint "unhandled exception (will retry in an instant)" :cause cause) + (px/sleep timeout)))))) (dispatcher [] (l/inf :hint "started") @@ -176,7 +177,7 @@ RETURNING task.id, task.queue") (catch InterruptedException _ (l/trc :hint "interrupted")) (catch Throwable cause - (l/err :hint " unexpected exception" :cause cause)) + (l/err :hint "unexpected exception" :cause cause)) (finally (l/inf :hint "terminated"))))] From 9aa387a4732ae9a431933edfb76c1a6e3c3e2f89 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 11 Dec 2025 18:40:13 +0100 Subject: [PATCH 07/18] :bug: Fix incorrect string truncation with abbreviate template filter --- CHANGES.md | 1 + backend/resources/app/email/invite-to-team/en.html | 2 +- backend/src/app/util/template.clj | 8 ++++++++ common/deps.edn | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 011361113a..6b941393c3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -95,6 +95,7 @@ example. It's still usable as before, we just removed the example. - Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841) - Fix referencing typography tokens on font-family tokens [Taiga #12492](https://tree.taiga.io/project/penpot/issue/12492) - Fix horizontal scroll on layer panel [Taiga #12843](https://tree.taiga.io/project/penpot/issue/12843) +- Fix unicode handling on email template abbreviation filter [Github #7966](https://github.com/penpot/penpot/pull/7966) ## 2.11.1 diff --git a/backend/resources/app/email/invite-to-team/en.html b/backend/resources/app/email/invite-to-team/en.html index 5e61f1f675..337593902d 100644 --- a/backend/resources/app/email/invite-to-team/en.html +++ b/backend/resources/app/email/invite-to-team/en.html @@ -240,4 +240,4 @@ - \ No newline at end of file + diff --git a/backend/src/app/util/template.clj b/backend/src/app/util/template.clj index 5c7a0b8c6e..2365423349 100644 --- a/backend/src/app/util/template.clj +++ b/backend/src/app/util/template.clj @@ -7,10 +7,18 @@ (ns app.util.template (:require [app.common.exceptions :as ex] + [cuerdas.core :as str] + [selmer.filters :as sf] [selmer.parser :as sp])) ;; (sp/cache-off!) +(sf/add-filter! :abbreviate + (fn [s n] + (let [n (parse-long n)] + (str/abbreviate s n)))) + + (defn render [path context] (try diff --git a/common/deps.edn b/common/deps.edn index a2d9a1b1ec..e7e73645a5 100644 --- a/common/deps.edn +++ b/common/deps.edn @@ -30,7 +30,7 @@ integrant/integrant {:mvn/version "1.0.0"} funcool/tubax {:mvn/version "2021.05.20-0"} - funcool/cuerdas {:mvn/version "2025.06.16-414"} + funcool/cuerdas {:mvn/version "2026.415"} funcool/promesa {:git/sha "46048fc0d4bf5466a2a4121f5d52aefa6337f2e8" :git/url "https://github.com/funcool/promesa"} From 83bb4bf22118289ad7ce619aafd79f184bba8832 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 19 Dec 2025 09:14:56 +0100 Subject: [PATCH 08/18] :bug: Prefill storage object bucket if it comes nil on import binfile --- backend/src/app/binfile/v3.clj | 7 ++++--- backend/src/app/storage.clj | 3 +++ backend/src/app/storage/gc_touched.clj | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/src/app/binfile/v3.clj b/backend/src/app/binfile/v3.clj index e044ef2e8b..bd6c041b0a 100644 --- a/backend/src/app/binfile/v3.clj +++ b/backend/src/app/binfile/v3.clj @@ -821,9 +821,10 @@ entries (keep (match-storage-entry-fn) entries)] (doseq [{:keys [id entry]} entries] - (let [object (->> (read-entry input entry) - (decode-storage-object) - (validate-storage-object)) + (let [object (-> (read-entry input entry) + (decode-storage-object) + (update :bucket d/nilv sto/default-bucket) + (validate-storage-object)) ext (cmedia/mtype->extension (:content-type object)) path (str "objects/" id ext) diff --git a/backend/src/app/storage.clj b/backend/src/app/storage.clj index 0d7bac7eca..0fe48c2911 100644 --- a/backend/src/app/storage.clj +++ b/backend/src/app/storage.clj @@ -35,6 +35,9 @@ :assets-s3 :s3 nil))) +(def default-bucket + "file-media-object") + (def valid-buckets #{"file-media-object" "team-font-variant" diff --git a/backend/src/app/storage/gc_touched.clj b/backend/src/app/storage/gc_touched.clj index 964765589b..fa3e144ef9 100644 --- a/backend/src/app/storage/gc_touched.clj +++ b/backend/src/app/storage/gc_touched.clj @@ -25,7 +25,7 @@ [app.common.time :as ct] [app.config :as cf] [app.db :as db] - [app.storage :as-alias sto] + [app.storage :as sto] [app.storage.impl :as impl] [integrant.core :as ig])) @@ -130,7 +130,7 @@ [{:keys [metadata]}] (or (some-> metadata :bucket) (some-> metadata :reference d/name) - "file-media-object")) + sto/default-bucket)) (defn- process-objects! [conn has-refs? bucket objects] From 336173645e13bd9fea85bab64045f3897a54bc7f Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 19 Dec 2025 09:49:35 +0100 Subject: [PATCH 09/18] :bug: Fix regression on export shape on plungins API --- frontend/src/app/plugins/shape.cljs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index c7bdbb3866..6a7827fd68 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -55,6 +55,7 @@ [app.plugins.ruler-guides :as rg] [app.plugins.text :as text] [app.plugins.utils :as u] + [app.util.http :as http] [app.util.object :as obj] [beicon.v2.core :as rx] [cuerdas.core :as str])) @@ -1196,7 +1197,12 @@ (js/Promise. (fn [resolve reject] (->> (rp/cmd! :export payload) - (rx/mapcat #(rp/cmd! :export {:cmd :get-resource :wait true :id (:id %) :blob? true})) + (rx/mapcat (fn [{:keys [uri]}] + (->> (http/send! {:method :get + :uri uri + :response-type :blob + :omit-default-headers true}) + (rx/map :body)))) (rx/mapcat #(.arrayBuffer %)) (rx/map #(js/Uint8Array. %)) (rx/subs! resolve reject)))))))) From 5cbcec3db607b211c27e65e801f4e7105c48027e Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Mon, 22 Dec 2025 10:31:07 +0100 Subject: [PATCH 10/18] :bug: Fix "maximum call stack size exceeded" crash on variant --- common/src/app/common/types/file.cljc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 5650b9bd30..ca14d0ac1c 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -362,24 +362,24 @@ component (ctkl/get-component component-file (:component-id top-instance) true) remote-shape (get-ref-shape component-file component shape) component-container (get-component-container component-file component) - [remote-shape component-container] + [remote-shape component-container component-file] (if (some? remote-shape) - [remote-shape component-container] + [remote-shape component-container component-file] ;; If not found, try the case of this being a fostered or swapped children - (let [head-instance (ctn/get-head-shape (:objects container) shape) - component-file (get-in libraries [(:component-file head-instance) :data]) - head-component (ctkl/get-component component-file (:component-id head-instance) true) - remote-shape' (get-ref-shape component-file head-component shape) - component-container (get-component-container component-file component)] - [remote-shape' component-container]))] + (let [head-instance (ctn/get-head-shape (:objects container) shape) + component-file (get-in libraries [(:component-file head-instance) :data]) + head-component (ctkl/get-component component-file (:component-id head-instance) true) + remote-shape' (get-ref-shape component-file head-component shape) + component-container' (get-component-container component-file head-component)] + [remote-shape' component-container' component-file]))] (if (nil? remote-shape) nil (if (nil? (:shape-ref remote-shape)) (cond-> remote-shape (and remote-shape with-context?) - (with-meta {:file {:id (:id file-data) - :data file-data} + (with-meta {:file {:id (:id component-file) + :data component-file} :container component-container})) (find-remote-shape component-container libraries remote-shape :with-context? with-context?))))) From bb5568e15a9b1a663956282718ba929d7e36e44e Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 22 Dec 2025 16:43:19 +0100 Subject: [PATCH 11/18] :tada: Enable hindi translations on the application --- CHANGES.md | 1 + frontend/scripts/_helpers.js | 1 + frontend/src/app/util/i18n.cljs | 1 + frontend/translations/hi.po | 850 +++++++++++++++++++++++++++++++- 4 files changed, 836 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6b941393c3..289d21c516 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -62,6 +62,7 @@ example. It's still usable as before, we just removed the example. - Ensure consistent snap behavior across all zoom levels [Github #7774](https://github.com/penpot/penpot/pull/7774) by [@Tokytome](https://github.com/Tokytome) - Fix crash in token grid view due to tooltip validation (by @dfelinto) [Github #7887](https://github.com/penpot/penpot/pull/7887) +- Enable Hindi translations on the application ### :sparkles: New features & Enhancements diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js index 21393ef464..18d29a6f5b 100644 --- a/frontend/scripts/_helpers.js +++ b/frontend/scripts/_helpers.js @@ -278,6 +278,7 @@ async function readTranslations() { "id", "ru", "tr", + "hi", "zh_CN", "zh_Hant", "hr", diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index 0bd5f39f7d..9446f6cd1d 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -48,6 +48,7 @@ {:label "Føroyskt mál (community)" :value "fo"} {:label "Korean (community)" :value "ko"} {:label "עִבְרִית (community)" :value "he"} + {:label "आधुनिक मानक हिन्दी (community)" :value "hi"} {:label "عربي/عربى (community)" :value "ar"} {:label "فارسی (community)" :value "fa"} {:label "日本語 (Community)" :value "ja_jp"} diff --git a/frontend/translations/hi.po b/frontend/translations/hi.po index c9052a05bf..68bf52dc44 100644 --- a/frontend/translations/hi.po +++ b/frontend/translations/hi.po @@ -1,15 +1,15 @@ msgid "" msgstr "" -"PO-Revision-Date: 2025-10-13 09:26+0000\n" +"PO-Revision-Date: 2025-12-22 15:34+0000\n" "Last-Translator: VKing9 \n" -"Language-Team: Hindi " -"\n" +"Language-Team: Hindi \n" "Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 5.14-dev\n" +"X-Generator: Weblate 5.15.1\n" #: src/app/main/ui/auth/register.cljs:215, src/app/main/ui/static.cljs:159, src/app/main/ui/viewer/login.cljs:100 msgid "auth.already-have-account" @@ -569,10 +569,11 @@ msgstr "" "लाइब्रेरीज़ का उपयोग कर रही हैं। आप उनके एसेट्स के साथ क्या करना चाहते हैं?" #: src/app/main/ui/exports/files.cljs:164 +#, fuzzy msgid "dashboard.export.options.all.message" msgstr "" -"साझा की गई लाइब्रेरीज़ वाली फ़ाइलें निर्यात में शामिल की जाएँगी, और उनका " -"लिंक बनाए रखा जाएगा।" +"साझा की गई लाइब्रेरीज़ वाली फ़ाइलें निर्यात में शामिल की जाएँगी, और उनका लिंक बनाए रखा " +"जाएगा।" #: src/app/main/ui/exports/files.cljs:165 msgid "dashboard.export.options.all.title" @@ -1726,10 +1727,6 @@ msgstr "" "यदि आप डिजाइन निरीक्षण के बारे में अधिक जानना चाहते हैं, तो कृपया पेनपॉट के " "हेल्प सेंटर पर जाएं" -#: src/app/main/ui/inspect/right_sidebar.cljs:240 -msgid "inspect.empty.more-info" -msgstr "निरीक्षण के बारे में अधिक जानकारी" - #: src/app/main/ui/inspect/right_sidebar.cljs:232 msgid "inspect.empty.select" msgstr "उनके गुणधर्म और कोड का निरीक्षण करने के लिए कोई आकृति, बोर्ड या समूह चुनें" @@ -4321,7 +4318,7 @@ msgstr "" #: src/app/main/ui/settings/subscription.cljs:202 msgid "subscription.settings.management.dialog.payment-explanation" -msgstr "(अभी कोई भुगतान नहीं किया जाएगा)" +msgstr "परीक्षण के बाद शुल्क लिया जाएगा। अभी क्रेडिट कार्ड की आवश्यकता नहीं है।" #: src/app/main/ui/settings/subscription.cljs:195, src/app/main/ui/settings/subscription.cljs:199 #, markdown @@ -4383,9 +4380,8 @@ msgid "subscription.settings.sucess.dialog.title" msgstr "आप %s हैं!" #: src/app/main/ui/settings/subscription.cljs:440 -#, fuzzy msgid "subscription.settings.support-us-since" -msgstr "आप इस योजना के साथ %s से हमारा समर्थन कर रहे हैं" +msgstr "आप इस योजना में हमारा समर्थन तब से कर रहे हैं: %s" #: src/app/main/ui/settings/subscription.cljs:472, src/app/main/ui/settings/subscription.cljs:488 msgid "subscription.settings.try-it-free" @@ -7169,7 +7165,6 @@ msgid "workspace.tokens.opacity-range" msgstr "अपारदर्शिता 0 और 100% या 0 और 1 (जैसे 50% या 0.5) के बीच होनी चाहिए।" #: src/app/main/ui/workspace/tokens/management/token_pill.cljs:120 -#, fuzzy msgid "workspace.tokens.original-value" msgstr "मूल मान: %s" @@ -7191,9 +7186,8 @@ msgid "workspace.tokens.reference-error" msgstr "संदर्भ त्रुटियाँ: " #: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:56, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:84, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:102, src/app/main/ui/workspace/tokens/management/create/form_input_token.cljs:109, src/app/main/ui/workspace/tokens/management/create/input_tokens_value.cljs:41, src/app/main/ui/workspace/tokens/management/create/input_tokens_value.cljs:46, src/app/main/ui/workspace/tokens/management/token_pill.cljs:121 -#, fuzzy msgid "workspace.tokens.resolved-value" -msgstr "समाधानित मान: %s" +msgstr "हल किया गया मान: %s" #: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:272 msgid "workspace.tokens.save-theme" @@ -7259,7 +7253,6 @@ msgid "workspace.tokens.themes-list" msgstr "थीम्स सूची" #: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:194, src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:195, src/app/main/ui/workspace/tokens/management/create/form.cljs:629, src/app/main/ui/workspace/tokens/management/create/form.cljs:630 -#, fuzzy msgid "workspace.tokens.token-description" msgstr "वर्णन" @@ -7628,3 +7621,826 @@ msgstr "स्वतः सहेजे गए संस्करण %s दि #, unused msgid "workspace.viewport.click-to-close-path" msgstr "पथ बंद करने के लिए क्लिक करें" + +#: src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:100, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:107 +msgid "color-row.token-color-row.deleted-token" +msgstr "यह token मौजूद नहीं है या हटा दिया गया है।" + +#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35 +msgid "color-token.empty-state" +msgstr "कोई रंग tokens उपलब्ध नहीं है। सक्रिय सेट/थीम देखें या नए टोकन जोड़ें।" + +#: src/app/main/ui/dashboard/team.cljs:765 +msgid "dashboard.invitation-modal.delete" +msgstr "आप निम्न आमंत्रणों को हटाने जा रहे हैं:" + +#: src/app/main/ui/dashboard/team.cljs:766 +msgid "dashboard.invitation-modal.resend" +msgstr "आप निम्नलिखित को पुनः निमंत्रण भेजने जा रहे हैं:" + +#: src/app/main/ui/dashboard/team.cljs:756 +msgid "dashboard.invitation-modal.title.delete-invitations" +msgstr "निमंत्रण हटाएँ" + +#: src/app/main/ui/dashboard/team.cljs:757 +msgid "dashboard.invitation-modal.title.resend-invitations" +msgstr "निमंत्रण पुनः भेजें" + +#: src/app/main/ui/dashboard/team.cljs:949 +msgid "dashboard.order-invitations-by-role" +msgstr "भूमिका के अनुसार क्रमबद्ध करें" + +#: src/app/main/ui/dashboard/team.cljs:958 +msgid "dashboard.order-invitations-by-status" +msgstr "स्थिति के अनुसार क्रमबद्ध करें" + +#: src/app/main/ui/ds/controls/numeric_input.cljs:99 +msgid "ds.inputs.numeric-input.no-applicable-tokens" +msgstr "सक्रिय सेट या थीम में कोई लागू tokens नहीं।" + +#: src/app/main/ui/ds/controls/numeric_input.cljs:100 +msgid "ds.inputs.numeric-input.no-matches" +msgstr "कोई मेल नहीं मिले।" + +#: src/app/main/ui/ds/controls/numeric_input.cljs:650, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:140 +msgid "ds.inputs.numeric-input.open-token-list-dropdown" +msgstr "token सूची खोलें" + +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:87, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:135 +msgid "ds.inputs.token-field.detach-token" +msgstr "token अलग करें" + +#: src/app/main/ui/ds/controls/utilities/token_field.cljs:41, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:98, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:105 +msgid "ds.inputs.token-field.no-active-token-option" +msgstr "यह token किसी भी सक्रिय सेट में नहीं है या इसका मान अमान्य है।" + +#: src/app/main/ui/auth/register.cljs:89 +msgid "errors.email-does-not-match-invitation" +msgstr "ईमेल आमंत्रण से मेल नहीं खाता।" + +#: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:52, src/app/main/ui/workspace/tokens/management/create/form.cljs:80 +msgid "errors.field-max-length" +msgstr "इसमें अधिकतम %s वर्ण होने चाहिए।" + +#: src/app/main/ui/dashboard/team.cljs:853 +msgid "errors.max-quote-reached" +msgstr "|" + +#: src/app/main/errors.cljs:167 +msgid "errors.only-creator-can-lock" +msgstr "केवल संस्करण निर्माता ही इसे लॉक कर सकता है" + +#: src/app/main/errors.cljs:175 +msgid "errors.only-creator-can-unlock" +msgstr "केवल संस्करण निर्माता ही इसे अनलॉक कर सकता है" + +#: src/app/main/errors.cljs:183 +msgid "errors.version-already-locked" +msgstr "यह संस्करण पहले से ही लॉक है" + +#: src/app/main/errors.cljs:159 +msgid "errors.version-locked" +msgstr "यह संस्करण लॉक है और इसे अन्य लोग हटा नहीं सकते" + +#: src/app/main/ui/settings/feedback.cljs:122 +msgid "feedback.description-placeholder" +msgstr "कृपया अपनी प्रतिक्रिया का कारण बताएँ" + +#: src/app/main/ui/settings/feedback.cljs:143 +msgid "feedback.other-ways-contact" +msgstr "हमसे संपर्क करने के अन्य तरीके" + +#: src/app/main/ui/settings/feedback.cljs:126 +msgid "feedback.penpot.link" +msgstr "" +"यदि फीडबैक किसी फ़ाइल या प्रोजेक्ट से संबंधित है, तो यहां पेनपॉट लिंक जोड़ें:" + +#: src/app/main/ui/settings/feedback.cljs:101 +msgid "feedback.title-contact-us" +msgstr "हमसे संपर्क करें" + +#: src/app/main/ui/settings/feedback.cljs:110, src/app/main/ui/settings/feedback.cljs:111 +msgid "feedback.type" +msgstr "प्रकार" + +#: src/app/main/ui/settings/feedback.cljs:115 +msgid "feedback.type.doubt" +msgstr "संदेह" + +#: src/app/main/ui/settings/feedback.cljs:113 +msgid "feedback.type.idea" +msgstr "विचार" + +#: src/app/main/ui/settings/feedback.cljs:114 +msgid "feedback.type.issue" +msgstr "मुद्दा" + +#: src/app/main/ui/inspect/styles/rows/color_properties_row.cljs:120 +msgid "inspect.attributes.image.preview" +msgstr "आकृति की भरण छवि का पूर्वावलोकन" + +#, unused +msgid "inspect.attributes.typography.text-decoration.line-through" +msgstr "स्ट्राइकथ्रू" + +#: src/app/main/ui/inspect/attributes/text.cljs:125, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:429 +msgid "inspect.attributes.typography.text-transform.capitalize" +msgstr "प्रमुख अक्षर करना" + +#: src/app/main/ui/inspect/right_sidebar.cljs:170 +msgid "inspect.color-space-label" +msgstr "रंग स्थान चुनें" + +#: src/app/main/ui/inspect/right_sidebar.cljs:166 +#, fuzzy +msgid "inspect.layer-info" +msgstr "निरीक्षण टैब चुनें" + +#: src/app/main/ui/inspect/styles/panels/tokens_panel.cljs:26 +msgid "inspect.tabs.styles.active-sets" +msgstr "सक्रिय सेट" + +#: src/app/main/ui/inspect/styles/panels/tokens_panel.cljs:21 +msgid "inspect.tabs.styles.active-themes" +msgstr "सक्रिय थीम" + +#: src/app/main/ui/inspect/styles/style_box.cljs:68 +msgid "inspect.tabs.styles.copy-shorthand" +msgstr "CSS शॉर्टहैंड को क्लिपबोर्ड पर कॉपी करें" + +#: src/app/main/ui/inspect/styles/property_detail_copiable.cljs:51 +msgid "inspect.tabs.styles.copy-to-clipboard" +msgstr "क्लिपबोर्ड पर कॉपी करें" + +#: src/app/main/ui/inspect/styles/style_box.cljs:22 +msgid "inspect.tabs.styles.geometry-panel" +msgstr "आकार & स्थिति" + +#: src/app/main/ui/inspect/styles/style_box.cljs:60, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:179 +msgid "inspect.tabs.styles.toggle-style" +msgstr "टॉगल पैनल %s" + +#: src/app/main/ui/inspect/styles/style_box.cljs:21 +msgid "inspect.tabs.styles.token-panel" +msgstr "Token सेट और थीम" + +#: src/app/main/ui/inspect/styles/rows/color_properties_row.cljs:102, src/app/main/ui/inspect/styles/rows/properties_row.cljs:60 +msgid "inspect.tabs.styles.token-resolved-value" +msgstr "हल किया गया मान:" + +#: src/app/main/ui/inspect/styles/style_box.cljs:20 +msgid "inspect.tabs.styles.variants-panel" +msgstr "भिन्न गुण" + +#: src/app/main/ui/dashboard/sidebar.cljs:1044 +msgid "labels.about-penpot" +msgstr "पेनपोट के बारे में" + +#: src/app/main/ui/inspect/styles/style_box.cljs:26 +msgid "labels.blur" +msgstr "धुंधला" + +#: src/app/main/ui/workspace/colorpicker.cljs:423 +msgid "labels.color" +msgstr "रंग" + +#: src/app/main/ui/dashboard/sidebar.cljs:1031 +msgid "labels.community-contributions" +msgstr "समुदाय & योगदान" + +#: src/app/main/ui/inspect/right_sidebar.cljs:109 +msgid "labels.computed" +msgstr "परिकलित" + +#: src/app/main/ui/static.cljs:406 +msgid "labels.contact-support" +msgstr "समर्थन से संपर्क करें" + +#: src/app/main/ui/settings/sidebar.cljs:136 +msgid "labels.contact-us" +msgstr "हमसे संपर्क करें" + +#: src/app/main/ui/static.cljs:68 +msgid "labels.copyright-period" +msgstr "कैलिडोस © 2019-वर्तमान" + +#: src/app/main/ui/settings/feedback.cljs:134, src/app/main/ui/static.cljs:400 +msgid "labels.download" +msgstr "%s डाउनलोड करें" + +#: src/app/main/ui/inspect/styles/style_box.cljs:23 +msgid "labels.fill" +msgstr "भरना" + +#: src/app/main/ui/dashboard/sidebar.cljs:1020 +msgid "labels.help-learning" +msgstr "मदद & सीखना" + +#: src/app/main/ui/static.cljs:396 +msgid "labels.internal-error.desc-message-first" +msgstr "कुछ बुरा हुआ।" + +#: src/app/main/ui/static.cljs:397 +msgid "labels.internal-error.desc-message-second" +msgstr "" +"आप ऑपरेशन पुनः प्रयास कर सकते हैं या त्रुटि की रिपोर्ट करने के लिए समर्थन से संपर्क कर सकते हैं" +"।" + +#: src/app/main/ui/inspect/styles/style_box.cljs:28 +msgid "labels.layout" +msgstr "लेआउट" + +#: src/app/main/ui/dashboard/sidebar.cljs:799 +msgid "labels.learning-center" +msgstr "अध्ययन केन्द्र" + +#: src/app/main/ui/workspace/sidebar/versions.cljs:209 +msgid "labels.lock" +msgstr "ताला" + +#: src/app/main/ui/ds/controls/numeric_input.cljs:628 +msgid "labels.mixed-values" +msgstr "मिश्रित" + +#: src/app/main/ui/dashboard/sidebar.cljs:879 +msgid "labels.penpot-changelog" +msgstr "पेनपॉट चेंजलॉग" + +#: src/app/main/ui/dashboard/sidebar.cljs:805 +msgid "labels.penpot-hub" +msgstr "पेनपॉट हब" + +#: src/app/main/ui/dashboard/sidebar.cljs:752 +msgid "labels.pinned-projects" +msgstr "पिन किए गए प्रोजेक्ट" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:667 +msgid "labels.reference" +msgstr "संदर्भ" + +#: src/app/main/ui/dashboard/team.cljs:788 +msgid "labels.resend" +msgstr "पुन: भेजें" + +#: src/app/main/ui/inspect/styles/style_box.cljs:27 +msgid "labels.shadow" +msgstr "छाया" + +#: src/app/main/ui/dashboard/sidebar.cljs:731 +msgid "labels.sources" +msgstr "स्त्रोत" + +#: src/app/main/ui/inspect/styles/style_box.cljs:24, src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs:46 +msgid "labels.stroke" +msgstr "स्ट्रोक" + +#: src/app/main/ui/inspect/right_sidebar.cljs:107, src/app/main/ui/inspect/styles.cljs:134 +msgid "labels.styles" +msgstr "शैलियों" + +#: src/app/main/ui/inspect/styles/style_box.cljs:33 +msgid "labels.svg" +msgstr "SVG" + +#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:229 +msgid "labels.switch" +msgstr "बदलना" + +#: src/app/main/ui/inspect/styles/style_box.cljs:25 +msgid "labels.text" +msgstr "मूलपाठ" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1452 +msgid "labels.typography" +msgstr "अक्षर विन्यास" + +#: src/app/main/ui/workspace/sidebar/versions.cljs:203 +msgid "labels.unlock" +msgstr "अनलॉक" + +#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028 +msgid "labels.variant" +msgstr "रूपांतर" + +#: src/app/main/ui/dashboard/sidebar.cljs:873 +msgid "labels.version-notes" +msgstr "संस्करण %s नोट्स" + +#: src/app/main/ui/inspect/styles/style_box.cljs:32 +msgid "labels.visibility" +msgstr "दृश्यता" + +#: src/app/main/ui/dashboard/team.cljs:825 +msgid "notifications.invitation-deleted" +msgstr "आमंत्रण सफलतापूर्वक हटा दिया गया" + +#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:97 +msgid "shortcuts.create-component-variant" +msgstr "घटक/संस्करण बनाएं" + +#: src/app/main/ui/dashboard/subscription.cljs:109 +msgid "subscription.dashboard.power-up.enterprise-trial.top-title" +msgstr "एंटरप्राइज़ योजना (परीक्षण)" + +#: src/app/main/ui/dashboard/subscription.cljs:84 +msgid "subscription.dashboard.power-up.professional.bottom-button" +msgstr "शक्तिप्रापक!" + +#: src/app/main/ui/dashboard/subscription.cljs:83 +msgid "subscription.dashboard.power-up.professional.bottom-description" +msgstr "" +"अपनी टीमों के लिए अतिरिक्त संग्रहण, फ़ाइल पुनर्प्राप्ति और बहुत कुछ प्राप्त करें।" + +#: src/app/main/ui/dashboard/subscription.cljs:101 +#, markdown +msgid "subscription.dashboard.power-up.unlimited.bottom-text" +msgstr "" +"अपनी सभी टीमों के लिए एक निश्चित कीमत पर असीमित स्टोरेज, विस्तारित फ़ाइल रिकवरी और " +"असीमित एडिटर प्राप्त करें। [एंटरप्राइज़ प्लान देखें।|target:self](%s)" + +#: src/app/main/ui/dashboard/subscription.cljs:194 +msgid "subscription.dashboard.professional-dashboard-cta-title" +msgstr "" +"आपकी स्वामित्व वाली टीमों में %s संपादक हैं, जबकि आपकी व्यावसायिक योजना 8 तक को कवर " +"करती है।" + +#: src/app/main/ui/dashboard/subscription.cljs:202 +#, markdown +msgid "subscription.dashboard.professional-dashboard-cta-upgrade-owner" +msgstr "" +"कृपया ज़्यादा एडिटर, स्टोरेज और फ़ाइल रिकवरी के लिए अभी अनलिमिटेड या एंटरप्राइज़ में " +"अपग्रेड करें। [अभी सब्सक्राइब करें।|target:self](%s)" + +#: src/app/main/ui/dashboard/subscription.cljs:197 +msgid "subscription.dashboard.unlimited-dashboard-cta-title" +msgstr "" +"आपकी टीम बढ़ती जा रही है! आपकी अनलिमिटेड योजना में %s संपादकों तक की सेवाएँ शामिल हैं, " +"लेकिन अब आपके पास %s हैं।" + +#: src/app/main/ui/dashboard/subscription.cljs:205 +#, markdown +msgid "subscription.dashboard.unlimited-dashboard-cta-upgrade-owner" +msgstr "" +"कृपया अपनी वर्तमान संपादक संख्या से मेल खाने के लिए अभी अपग्रेड करें। [अभी सदस्यता लें" +"।|target:self](%s)" + +#: src/app/main/ui/dashboard/subscription.cljs:182 +msgid "subscription.dashboard.unlimited-members-extra-editors-cta-text" +msgstr "" +"आपकी स्वामित्व वाली टीमों के केवल नए संपादक ही भविष्य के बिलिंग में शामिल होंगे। 25+ " +"संपादकों के लिए अभी भी $175/माह का एक समान शुल्क लागू है।" + +#: src/app/main/ui/dashboard/subscription.cljs:178 +msgid "subscription.dashboard.unlimited-members-extra-editors-cta-title" +msgstr "असीमित योजना के दौरान लोगों को आमंत्रित करना" + +#: src/app/main/ui/settings/subscription.cljs:53 +msgid "subscription.settings.editors" +msgstr "(x %s संपादक)" + +#: src/app/main/ui/settings/subscription.cljs:418, src/app/main/ui/settings/subscription.cljs:428, src/app/main/ui/settings/subscription.cljs:486 +msgid "subscription.settings.enterprise.autosave" +msgstr "90-दिन के ऑटोसेव संस्करण और फ़ाइल पुनर्प्राप्ति" + +#: src/app/main/ui/settings/subscription.cljs:419, src/app/main/ui/settings/subscription.cljs:429, src/app/main/ui/settings/subscription.cljs:487 +msgid "subscription.settings.enterprise.capped-bill" +msgstr "फ्लैट मासिक बिल" + +#: src/app/main/ui/settings/subscription.cljs:417, src/app/main/ui/settings/subscription.cljs:427, src/app/main/ui/settings/subscription.cljs:485 +msgid "subscription.settings.enterprise.unlimited-storage-benefit" +msgstr "असीमित भंडारण" + +#: src/app/main/ui/settings/subscription.cljs:154 +msgid "subscription.settings.management.dialog.currently-editors-title" +msgid_plural "subscription.settings.management.dialog.currently-editors-title" +msgstr[0] "वर्तमान में, आपकी टीम में %s व्यक्ति हैं जो संपादन कर सकते हैं।" +msgstr[1] "वर्तमान में, आपकी टीम में %s लोग हैं जो संपादन कर सकते हैं।" + +#: src/app/main/ui/inspect/attributes/text.cljs:112 +msgid "inspect.attributes.typography.text-decoration.strikethrough" +msgstr " " + +#: src/app/main/ui/inspect/right_sidebar.cljs:177 +msgid "inspect.tabs-switcher-label" +msgstr " " + +#: src/app/main/ui/settings/subscription.cljs:156 +msgid "subscription.settings.management.dialog.editors" +msgstr "संपादनकर्ता" + +#: src/app/main/ui/settings/subscription.cljs:163 +msgid "subscription.settings.management.dialog.editors-explanation" +msgstr "(स्वामी, व्यवस्थापक और संपादक। दर्शकों को संपादक नहीं माना जाएगा)" + +#: src/app/main/ui/settings/subscription.cljs:206 +msgid "subscription.settings.management.dialog.input-error" +msgstr "" +"आप मौजूदा संपादकों की संख्या से कम संपादक नहीं सेट कर सकते। टीम सेटिंग में उन लोगों की " +"भूमिका (संपादक/व्यवस्थापक से दर्शक) बदलें जो वास्तव में फ़ाइलें संपादित नहीं करते हैं।" + +msgid "subscription.settings.management-dialog.step-2-title" +msgstr "हमें आगे बढ़ने में मदद करें और अपने परीक्षण को आसान बनाएं" + +msgid "subscription.settings.management-dialog.step-2-description" +msgstr "" +"परीक्षण अवधि के बाद अपनी सदस्यता को सुचारू रूप से जारी रखने और हमारे ओपन-सोर्स प्रोजेक्ट " +"का समर्थन जारी रखने के लिए अभी अपनी भुगतान जानकारी जोड़ें। आपसे अभी कोई शुल्क नहीं लिया " +"जाएगा।" + +msgid "subscription.settings.management-dialog.step-2-skip-button" +msgstr "अभी छोड़ें और परीक्षण शुरू करें" + +msgid "subscription.settings.management-dialog.step-2-add-payment-button" +msgstr "भुगतान विवरण जोड़ें" + +#: src/app/main/ui/settings/subscription.cljs:209 +msgid "subscription.settings.management.dialog.unlimited-capped-warning" +msgstr "" +"सुझाव: आमंत्रणों से आगे रहने के लिए आप अभी अपनी सीटों की संख्या बढ़ा सकते हैं। 25+ संपादकों " +"वाली टीमों में, आपको प्रति माह ₹175 का एकमुश्त शुल्क मिलेगा।" + +#: src/app/main/ui/settings/subscription.cljs:385, src/app/main/ui/settings/subscription.cljs:456 +msgid "subscription.settings.professional.autosave-benefit" +msgstr "7-दिन का स्वतः सहेजा गया संस्करण और फ़ाइल पुनर्प्राप्ति" + +#: src/app/main/ui/settings/subscription.cljs:384, src/app/main/ui/settings/subscription.cljs:455 +msgid "subscription.settings.professional.storage-benefit" +msgstr "10GB स्टोरेज" + +#: src/app/main/ui/settings/subscription.cljs:386, src/app/main/ui/settings/subscription.cljs:457 +msgid "subscription.settings.professional.teams-editors-benefit" +msgstr "असीमित टीमें। आपकी स्वामित्व वाली टीमों में अधिकतम 8 संपादक।" + +#: src/app/main/ui/settings/subscription.cljs:50 +msgid "subscription.settings.recommended" +msgstr "अनुशंसित" + +#: src/app/main/ui/settings/subscription.cljs:263 +msgid "subscription.settings.success.dialog.thanks" +msgstr "पेनपोट %s योजना चुनने के लिए धन्यवाद!" + +#: src/app/main/ui/settings/subscription.cljs:394, src/app/main/ui/settings/subscription.cljs:406, src/app/main/ui/settings/subscription.cljs:470 +msgid "subscription.settings.unlimited.autosave-benefit" +msgstr "30-दिन का स्वतः सहेजा गया संस्करण और फ़ाइल पुनर्प्राप्ति" + +#: src/app/main/ui/settings/subscription.cljs:393, src/app/main/ui/settings/subscription.cljs:405, src/app/main/ui/settings/subscription.cljs:469 +msgid "subscription.settings.unlimited.storage-benefit" +msgstr "25GB स्टोरेज" + +#: src/app/main/ui/workspace/sidebar/versions.cljs:56 +#, markdown +msgid "subscription.workspace.versions.warning.enterprise.subtext-owner" +msgstr "यदि आप इस सीमा को बढ़ाना चाहते हैं, तो हमें [%s](mailto) पर लिखें" + +#: src/app/main/ui/workspace/sidebar/versions.cljs:58 +#, markdown +msgid "subscription.workspace.versions.warning.subtext-member" +msgstr "" +"यदि आप इस सीमा को बढ़ाना चाहते हैं, तो टीम के मालिक से संपर्क करें: [%s](mailto)" + +#: src/app/main/ui/workspace/sidebar/versions.cljs:57 +#, markdown +msgid "subscription.workspace.versions.warning.subtext-owner" +msgstr "" +"यदि आप इस सीमा को बढ़ाना चाहते हैं, तो [अपना प्लान अपग्रेड करें|target:self](%s)" + +#: src/app/main/ui/dashboard/team.cljs:933 +msgid "team.invitations-selected" +msgid_plural "team.invitations-selected" +msgstr[0] "1 आमंत्रण चयनित" +msgstr[1] "%s आमंत्रण चयनित" + +#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:81 +msgid "workspace.assets.component-group-options" +msgstr "घटक समूह विकल्प" + +#: src/app/main/ui/workspace/colorpicker.cljs:427, src/app/main/ui/workspace/colorpicker.cljs:439 +msgid "workspace.colorpicker.color-tokens" +msgstr "रंग टोकन" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:499 +msgid "workspace.component.swap.loop-error" +msgstr "घटकों को अपने अंदर नहीं रखा जा सकता।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:498 +msgid "workspace.component.switch.loop-error-multi" +msgstr "" +"कुछ प्रतियों को स्विच नहीं किया जा सका। घटकों को आपस में नेस्ट नहीं किया जा सकता।" + +#: src/app/main/ui/workspace/libraries.cljs:107, src/app/main/ui/workspace/libraries.cljs:133 +msgid "workspace.libraries.colors" +msgid_plural "workspace.libraries.colors" +msgstr[0] "1 रंग" +msgstr[1] "%s रंग" + +#: src/app/main/ui/workspace/libraries.cljs:101, src/app/main/ui/workspace/libraries.cljs:125 +msgid "workspace.libraries.components" +msgid_plural "workspace.libraries.components" +msgstr[0] "1 घटक" +msgstr[1] "%s घटक" + +#: src/app/main/ui/workspace/libraries.cljs:349 +msgid "workspace.libraries.connected-to" +msgstr "से जुड़ा" + +#: src/app/main/ui/workspace/libraries.cljs:104, src/app/main/ui/workspace/libraries.cljs:129 +msgid "workspace.libraries.graphics" +msgid_plural "workspace.libraries.graphics" +msgstr[0] "1 ग्राफ़िक" +msgstr[1] "%s ग्राफ़िक्स" + +#: src/app/main/ui/workspace/libraries.cljs:110, src/app/main/ui/workspace/libraries.cljs:137 +msgid "workspace.libraries.typography" +msgid_plural "workspace.libraries.typography" +msgstr[0] "1 अक्षर विन्यास" +msgstr[1] "%s अक्षर विन्यास" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:563 +msgid "workspace.options.component.variant.duplicated.copy.locate" +msgstr "परस्पर विरोधी वेरिएंट का पता लगाएं" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:560 +msgid "workspace.options.component.variant.duplicated.copy.title" +msgstr "" +"इस घटक में परस्पर विरोधी वैरिएंट हैं। सुनिश्चित करें कि प्रत्येक वैरिएंट में गुण मानों का एक " +"विशिष्ट सेट हो।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1330 +msgid "workspace.options.component.variant.duplicated.group.locate" +msgstr "डुप्लिकेट वेरिएंट का पता लगाएँ" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1327 +msgid "workspace.options.component.variant.duplicated.group.title" +msgstr "कुछ वेरिएंट में समान गुण और मूल्य होते हैं" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:268 +msgid "workspace.options.component.variant.duplicated.single.all" +msgstr "" +"इन वेरिएंट के गुण और मान समान हैं। मानों को समायोजित करें ताकि उन्हें पुनर्प्राप्त किया जा सके" +"।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:265 +msgid "workspace.options.component.variant.duplicated.single.one" +msgstr "" +"इस संस्करण के गुण और मान दूसरे संस्करण के समान हैं। मानों को समायोजित करें ताकि उन्हें " +"पुनर्प्राप्त किया जा सके।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:271 +msgid "workspace.options.component.variant.duplicated.single.some" +msgstr "" +"इनमें से कुछ वेरिएंट के गुण और मान समान हैं। मानों को समायोजित करें ताकि उन्हें पुनर्प्राप्त " +"किया जा सके।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:550 +msgid "workspace.options.component.variant.malformed.copy" +msgstr "" +"इस घटक के कुछ वेरिएंट अमान्य नामों वाले हैं। सुनिश्चित करें कि प्रत्येक वेरिएंट सही संरचना का " +"पालन कर रहा है।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:553 +msgid "workspace.options.component.variant.malformed.locate" +msgstr "अमान्य वेरिएंट का पता लगाएं" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:54 +msgid "workspace.options.component.variants-help-modal.intro" +msgstr "" +"वेरिएंट के बीच स्विच करते समय परिवर्तनों को बनाए रखने के लिए, पेनपॉट उन परतों को जोड़ता " +"है जो:" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:91 +msgid "workspace.options.component.variants-help-modal.outro" +msgstr "" +"इनमें से किसी भी परिवर्तन (जैसे, परत का नाम बदलना या समूह बनाना) से कनेक्शन टूट जाता है, " +"लेकिन परिवर्तन को पूर्ववत करने से यह पुनः स्थापित हो जाएगा।" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:67 +msgid "workspace.options.component.variants-help-modal.rule1" +msgstr "एक ही नाम साझा करें।" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:76 +msgid "workspace.options.component.variants-help-modal.rule2" +msgstr "एक ही प्रकार के हैं।" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:77 +msgid "workspace.options.component.variants-help-modal.rule2.detail" +msgstr "आयत, दीर्घवृत्त, पथ और बूलियन ऑपरेशन एक ही प्रकार के माने जाते हैं।" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:87 +msgid "workspace.options.component.variants-help-modal.rule3" +msgstr "समान पदानुक्रम स्तर रखें।" + +#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:88 +msgid "workspace.options.component.variants-help-modal.rule3.detail" +msgstr "समूह, बोर्ड और लेआउट को समतुल्य माना जाता है।" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1034, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1278, src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:47 +msgid "workspace.options.component.variants-help-modal.title" +msgstr "वेरिएंट कैसे जुड़े रहते हैं" + +#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:264 +msgid "workspace.options.more-token-colors" +msgstr "अधिक रंग के टोकन" + +#: src/app/main/ui/workspace/plugins.cljs:287 +msgid "workspace.plugins.permissions.allow-localstorage" +msgstr "ब्राउज़र में डेटा संग्रहीत करें।" + +#: src/app/main/ui/workspace/context_menu.cljs:617, src/app/main/ui/workspace/sidebar/assets/components.cljs:634, src/app/main/ui/workspace/sidebar/assets/groups.cljs:75, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1095 +msgid "workspace.shape.menu.combine-as-variants" +msgstr "वैरिएंट के रूप में संयोजित करें" + +#: src/app/main/ui/workspace/sidebar/assets/components.cljs:636 +msgid "workspace.shape.menu.combine-as-variants-error" +msgstr "घटकों को एक ही पृष्ठ पर होना आवश्यक है" + +#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1145 +msgid "workspace.shape.menu.remove-variant-property.last-property" +msgstr "वैरिएंट में कम से कम एक प्रॉपर्टी होनी चाहिए" + +#: src/app/main/data/workspace/tokens/errors.cljs:97 +msgid "workspace.tokens.composite-line-height-needs-font-size" +msgstr "" +"पंक्ति की ऊँचाई फ़ॉन्ट आकार पर निर्भर करती है। हल किया गया मान प्राप्त करने के लिए फ़ॉन्ट " +"आकार जोड़ें।" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:581 +msgid "workspace.tokens.edit-token" +msgstr "%s token संपादित करें" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1339 +msgid "workspace.tokens.font-size-value-enter" +msgstr "फ़ॉन्ट आकार या {उपनाम}" + +#: src/app/main/data/workspace/tokens/application.cljs:323 +msgid "workspace.tokens.font-variant-not-found" +msgstr "" +"फ़ॉन्ट वज़न/शैली सेट करते समय त्रुटि हुई। यह फ़ॉन्ट शैली वर्तमान फ़ॉन्ट में मौजूद नहीं है" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1328, src/app/main/ui/workspace/tokens/management/create/form.cljs:1343 +msgid "workspace.tokens.font-weight-value-enter" +msgstr "फ़ॉन्ट वज़न (300, बोल्ड इटैलिक...) या {उपनाम}" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:233 +msgid "workspace.tokens.import-button-prefix" +msgstr "%s आयात करें" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:273 +msgid "workspace.tokens.import-menu-folder-option" +msgstr "फ़ोल्डर" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:272 +msgid "workspace.tokens.import-menu-json-option" +msgstr "एकल JSON फ़ाइल" + +#: src/app/main/ui/workspace/tokens/import/modal.cljs:271 +msgid "workspace.tokens.import-menu-zip-option" +msgstr "ज़िप फ़ाइल" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:741 +msgid "workspace.tokens.individual-tokens" +msgstr "व्यक्तिगत टोकन का प्रयोग करें" + +#: src/app/main/data/workspace/tokens/errors.cljs:89 +msgid "workspace.tokens.invalid-font-weight-token-value" +msgstr "" +"अमान्य फ़ॉन्ट भार मान: संख्यात्मक मान (100-950) या मानक नाम (पतला, हल्का, नियमित, " +"बोल्ड, आदि) का उपयोग करें, वैकल्पिक रूप से उसके बाद 'इटैलिक' लिखें" + +#: src/app/main/data/workspace/tokens/errors.cljs:101 +msgid "workspace.tokens.invalid-shadow-type-token-value" +msgstr "" +"अमान्य छाया प्रकार: केवल 'innerShadow' या 'dropShadow' स्वीकार किए जाते हैं" + +#: src/app/main/data/workspace/tokens/errors.cljs:81 +msgid "workspace.tokens.invalid-text-case-token-value" +msgstr "" +"अमान्य token मान: केवल कोई नहीं, अपरकेस, लोअरकेस या कैपिटलाइज़ स्वीकार किए जाते हैं" + +#: src/app/main/data/workspace/tokens/errors.cljs:85 +msgid "workspace.tokens.invalid-text-decoration-token-value" +msgstr "" +"अमान्य token मान: केवल कोई नहीं, रेखांकित और स्ट्राइक-थ्रू स्वीकार किए जाते हैं" + +#: src/app/main/data/workspace/tokens/errors.cljs:93 +msgid "workspace.tokens.invalid-token-value-typography" +msgstr "अमान्य मान: एक संयुक्त टाइपोग्राफी token का संदर्भ होना चाहिए।" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1351 +msgid "workspace.tokens.letter-spacing-value-enter-composite" +msgstr "अक्षर रिक्ति या {उपनाम}" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1347 +msgid "workspace.tokens.line-height-value-enter" +msgstr "पंक्ति ऊँचाई (गुणक, पिक्सेल, %) या {उपनाम}" + +#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:123 +msgid "workspace.tokens.more-options" +msgstr "विकल्प देखने के लिए राइट क्लिक करें" + +#: src/app/main/data/workspace/tokens/errors.cljs:19 +msgid "workspace.tokens.no-token-files-found" +msgstr "इस फ़ाइल में कोई टोकन, सेट या थीम नहीं मिला।" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:775 +msgid "workspace.tokens.reference-composite" +msgstr "token टाइपोग्राफी उपनाम दर्ज करें" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1084 +msgid "workspace.tokens.shadow-add-shadow" +msgstr "छाया जोड़ें" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:981, src/app/main/ui/workspace/tokens/management/create/form.cljs:982 +msgid "workspace.tokens.shadow-blur" +msgstr "धुंधला" + +#: src/app/main/data/workspace/tokens/errors.cljs:105 +msgid "workspace.tokens.shadow-blur-range" +msgstr "छाया धुंधलापन 0 से अधिक या उसके बराबर होना चाहिए।" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:987, src/app/main/ui/workspace/tokens/management/create/form.cljs:988 +msgid "workspace.tokens.shadow-color" +msgstr "रंग" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:990, src/app/main/ui/workspace/tokens/management/create/form.cljs:991 +msgid "workspace.tokens.shadow-inset" +msgstr "अंतर्भूत" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1091 +msgid "workspace.tokens.shadow-remove-shadow" +msgstr "छाया हटाएँ" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:984, src/app/main/ui/workspace/tokens/management/create/form.cljs:985 +msgid "workspace.tokens.shadow-spread" +msgstr "फैलाना" + +#: src/app/main/data/workspace/tokens/errors.cljs:109 +msgid "workspace.tokens.shadow-spread-range" +msgstr "छाया प्रसार 0 से अधिक या उसके बराबर होना चाहिए।" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1215 +msgid "workspace.tokens.shadow-title" +msgstr "छायाएँ" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:975, src/app/main/ui/workspace/tokens/management/create/form.cljs:976 +msgid "workspace.tokens.shadow-x" +msgstr "X" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:978, src/app/main/ui/workspace/tokens/management/create/form.cljs:979 +msgid "workspace.tokens.shadow-y" +msgstr "Y" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1316, src/app/main/ui/workspace/tokens/management/create/form.cljs:1355 +msgid "workspace.tokens.text-case-value-enter" +msgstr "कोई नहीं | अपरकेस | लोअरकेस | कैपिटलाइज़ या {उपनाम}" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1322, src/app/main/ui/workspace/tokens/management/create/form.cljs:1359 +msgid "workspace.tokens.text-decoration-value-enter" +msgstr "कोई नहीं | रेखांकित | स्ट्राइक-थ्रू या {उपनाम}" + +#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:52 +msgid "workspace.tokens.theme-name-already-exists" +msgstr "इस नाम वाली थीम पहले से मौजूद है" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1277 +msgid "workspace.tokens.token-font-family-select" +msgstr "फ़ॉन्ट परिवार चुनें" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1333 +msgid "workspace.tokens.token-font-family-value" +msgstr "फॉन्ट परिवार" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1283, src/app/main/ui/workspace/tokens/management/create/form.cljs:1335 +msgid "workspace.tokens.token-font-family-value-enter" +msgstr "फ़ॉन्ट परिवार या अल्पविराम (,) द्वारा अलग किए गए फ़ॉन्ट की सूची" + +#: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:44, src/app/main/ui/workspace/tokens/management/create/form.cljs:70 +msgid "workspace.tokens.token-name-duplication-validation-error" +msgstr "पथ पर एक token पहले से मौजूद है: %s" + +#: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:42, src/app/main/ui/workspace/tokens/management/create/form.cljs:68 +msgid "workspace.tokens.token-name-length-validation-error" +msgstr "नाम कम से कम 1 अक्षर का होना चाहिए" + +#: src/app/main/data/workspace/tokens/import_export.cljs:47 +msgid "workspace.tokens.unknown-token-type-message" +msgstr "आयात सफल रहा। कुछ टोकन शामिल नहीं किए गए।" + +#: src/app/main/ui/workspace/tokens/management/create/form.cljs:745 +msgid "workspace.tokens.use-reference" +msgstr "एक संदर्भ का प्रयोग करें" + +#: src/app/main/data/workspace/tokens/errors.cljs:69 +msgid "workspace.tokens.value-with-percent" +msgstr "अमान्य मान: % की अनुमति नहीं है।" + +#, unused +msgid "workspace.versions.locked-by-other" +msgstr "यह संस्करण %s द्वारा लॉक किया गया है और इसे संशोधित नहीं किया जा सकता" + +#, unused +msgid "workspace.versions.locked-by-you" +msgstr "यह संस्करण आपके द्वारा लॉक किया गया है" + +#, unused +msgid "workspace.versions.tooltip.locked-version" +msgstr "लॉक किया गया संस्करण - केवल निर्माता ही इसे संशोधित कर सकता है" From 4000ec8762a01adac3f6e8806f9e7ecbede19c45 Mon Sep 17 00:00:00 2001 From: Alonso Torres Date: Mon, 22 Dec 2025 20:17:11 +0100 Subject: [PATCH 12/18] :bug: Fix problem resizing auto size layouts (#7995) --- frontend/src/app/main/data/workspace/transforms.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 7583f3c4da..dd427e6bd7 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -238,12 +238,12 @@ :always (ctm/resize scalev resize-origin shape-transform shape-transform-inverse) - (and (ctl/any-layout-immediate-child? objects shape) + (and (or (ctl/any-layout-immediate-child? objects shape) (ctl/any-layout? shape)) (not= (:layout-item-h-sizing shape) :fix) ^boolean change-width?) (ctm/change-property :layout-item-h-sizing :fix) - (and (ctl/any-layout-immediate-child? objects shape) + (and (or (ctl/any-layout-immediate-child? objects shape) (ctl/any-layout? shape)) (not= (:layout-item-v-sizing shape) :fix) ^boolean change-height?) (ctm/change-property :layout-item-v-sizing :fix) From 01ecde3bfa7bc9d3ba674bcdaf053077c7d60a25 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 22 Dec 2025 20:55:31 +0100 Subject: [PATCH 13/18] :sparkles: Add the ability to add relations on penpot sdk (#7987) * :sparkles: Add the ability to add relations on penpot sdk * :paperclip: Remove debug console log --- library/CHANGES.md | 6 ++++++ library/package.json | 4 ++-- library/playground/sample-relations.js | 30 ++++++++++++++++++++++++++ library/src/lib/builder.cljs | 13 ++++++++++- library/src/lib/export.cljs | 3 ++- library/test/builder.test.js | 27 +++++++++++++++++++++++ 6 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 library/playground/sample-relations.js diff --git a/library/CHANGES.md b/library/CHANGES.md index e136c7759c..ac3e4ca0bd 100644 --- a/library/CHANGES.md +++ b/library/CHANGES.md @@ -1,5 +1,11 @@ # CHANGELOG + +## 1.2.0-RC1 + +- Add the ability to add relations (with `addRelation` method) + + ## 1.1.0 - Same as 1.1.0-RC2 diff --git a/library/package.json b/library/package.json index 3eb2bac236..ab32814487 100644 --- a/library/package.json +++ b/library/package.json @@ -1,9 +1,9 @@ { "name": "@penpot/library", - "version": "1.1.0", + "version": "1.2.0-RC1", "license": "MPL-2.0", "author": "Kaleidos INC", - "packageManager": "yarn@4.11.0+sha512.4e54aeace9141df2f0177c266b05ec50dc044638157dae128c471ba65994ac802122d7ab35bcd9e81641228b7dcf24867d28e750e0bcae8a05277d600008ad54", + "packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8", "type": "module", "repository": { "type": "git", diff --git a/library/playground/sample-relations.js b/library/playground/sample-relations.js new file mode 100644 index 0000000000..ab2350c812 --- /dev/null +++ b/library/playground/sample-relations.js @@ -0,0 +1,30 @@ +import * as penpot from "#self"; +import { writeFile, readFile } from "fs/promises"; + +(async function () { + const context = penpot.createBuildContext(); + + { + const file1 = context.addFile({ name: "Test File 1" }); + const file2 = context.addFile({ name: "Test File 1" }); + + context.addRelation(file1, file2); + } + + { + let result = await penpot.exportAsBytes(context); + await writeFile("sample-relations.zip", result); + } +})() + .catch((cause) => { + console.error(cause); + + const innerCause = cause.cause; + if (innerCause) { + console.error("Inner cause:", innerCause); + } + process.exit(-1); + }) + .finally(() => { + process.exit(0); + }); diff --git a/library/src/lib/builder.cljs b/library/src/lib/builder.cljs index 316631d8d4..4cc8cc97e0 100644 --- a/library/src/lib/builder.cljs +++ b/library/src/lib/builder.cljs @@ -87,7 +87,8 @@ (try (let [params (-> params decode-params fb/decode-file)] (-> (swap! state fb/add-file params) - (get ::fb/current-file-id))) + (get ::fb/current-file-id) + (dm/str))) (catch :default cause (handle-exception cause)))) @@ -273,6 +274,16 @@ (catch :default cause (handle-exception cause)))) + :addRelation + (fn [file-id library-id] + (let [file-id (uuid/parse file-id) + library-id (uuid/parse library-id)] + (if (and file-id library-id) + (do + (swap! state update :relations assoc file-id library-id) + true) + false))) + :genId (fn [] (dm/str (uuid/next))) diff --git a/library/src/lib/export.cljs b/library/src/lib/export.cljs index 72c779c24e..1c0e883d0e 100644 --- a/library/src/lib/export.cljs +++ b/library/src/lib/export.cljs @@ -194,7 +194,8 @@ :generated-by "penpot-library/%version%" :referer (get opts :referer) :files files - :relations []} + :relations (->> (:relations state) + (mapv vec))} params (d/without-nils params)] ["manifest.json" diff --git a/library/test/builder.test.js b/library/test/builder.test.js index 207f863e35..e4c7bf8a37 100644 --- a/library/test/builder.test.js +++ b/library/test/builder.test.js @@ -54,6 +54,33 @@ test("create context with two file", () => { assert.equal(file.data.pages.length, 0) }); +test("create context with two file and relation between", () => { + const context = penpot.createBuildContext(); + + const fileId_1 = context.addFile({name: "sample 1"}); + const fileId_2 = context.addFile({name: "sample 2"}); + + context.addRelation(fileId_1, fileId_2); + + const internalState = context.getInternalState(); + + assert.ok(internalState.files[fileId_1]); + assert.ok(internalState.files[fileId_2]); + assert.equal(internalState.files[fileId_1].name, "sample 1"); + assert.equal(internalState.files[fileId_2].name, "sample 2"); + + assert.ok(internalState.relations[fileId_1]); + assert.equal(internalState.relations[fileId_1], fileId_2); + + const file = internalState.files[fileId_2]; + + assert.ok(file.data); + assert.ok(file.data.pages); + assert.ok(file.data.pagesIndex); + assert.equal(file.data.pages.length, 0) +}); + + test("create context with file and page", () => { const context = penpot.createBuildContext(); From 13fd20f76f5520fcff64f9a787b732d53de8b48d Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 19 Dec 2025 10:08:22 +0100 Subject: [PATCH 14/18] :rewind: Backport form error management improvements from develop --- .../src/app/main/ui/components/forms.cljs | 3 +- frontend/src/app/util/forms.cljs | 88 ++++++++++++------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 45419d56ca..0e6c55882a 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -50,7 +50,8 @@ touched? (and (contains? (:data @form) input-name) (get-in @form [:touched input-name])) - error (get-in @form [:errors input-name]) + error (or (get-in @form [:errors input-name]) + (get-in @form [:extra-errors input-name])) value (get-in @form [:data input-name] "") diff --git a/frontend/src/app/util/forms.cljs b/frontend/src/app/util/forms.cljs index 968c319e7f..a48fae2310 100644 --- a/frontend/src/app/util/forms.cljs +++ b/frontend/src/app/util/forms.cljs @@ -48,7 +48,11 @@ (let [props (m/properties schema) tprops (m/type-properties schema) field (or (first in) - (:error/field props))] + (:error/field props)) + + field (if (vector? field) + field + [field])] (if (contains? acc field) acc @@ -58,30 +62,30 @@ (or (= type :malli.core/missing-key) (nil? value)) - (assoc acc field {:message (tr "errors.field-missing")}) + (assoc-in acc field {:message (tr "errors.field-missing")}) ;; --- CHECK on schema props (contains? props :error/fn) - (assoc acc field (handle-error-fn props problem)) + (assoc-in acc field (handle-error-fn props problem)) (contains? props :error/message) - (assoc acc field (handle-error-message props)) + (assoc-in acc field (handle-error-message props)) (contains? props :error/code) - (assoc acc field (handle-error-code props)) + (assoc-in acc field (handle-error-code props)) ;; --- CHECK on type props (contains? tprops :error/fn) - (assoc acc field (handle-error-fn tprops problem)) + (assoc-in acc field (handle-error-fn tprops problem)) (contains? tprops :error/message) - (assoc acc field (handle-error-message tprops)) + (assoc-in acc field (handle-error-message tprops)) (contains? tprops :error/code) - (assoc acc field (handle-error-code tprops)) + (assoc-in acc field (handle-error-code tprops)) :else - (assoc acc field {:message (tr "errors.invalid-data")}))))) + (assoc-in acc field {:message (tr "errors.invalid-data")}))))) (defn- use-rerender-fn [] @@ -114,20 +118,35 @@ [f {:keys [schema validators]}] (fn [& args] (let [state (apply f args) - cleaned (sm/decode schema (:data state) sm/string-transformer) + cleaned (sm/decode schema (:data state) sm/json-transformer) valid? (sm/validate schema cleaned) - errors (when-not valid? - (collect-schema-errors schema validators state))] + + errors + (when-not valid? + (collect-schema-errors schema validators state)) + + extra-errors + (not-empty (:extra-errors state))] (assoc state :errors errors :clean-data (when valid? cleaned) - :valid (and (not errors) valid?))))) + :valid (and (not errors) + (not extra-errors) + valid?))))) + + +(defn- make-initial-state + [initial-data] + (let [initial (if (fn? initial-data) (initial-data) initial-data) + initial (d/nilv initial {})] + {:initial initial + :data initial + :errors {} + :touched {}})) (defn- create-form-mutator - [internal-state rerender-fn wrap-update-fn initial opts] - (mf/set-ref-val! internal-state initial) - + [internal-state rerender-fn wrap-update-fn opts] (reify IDeref (-deref [_] @@ -136,7 +155,10 @@ IReset (-reset! [_ new-value] (if (nil? new-value) - (mf/set-ref-val! internal-state (if (fn? initial) (initial) initial)) + (let [initial (-> (mf/ref-val internal-state) + (get :initial) + (make-initial-state))] + (mf/set-ref-val! internal-state initial)) (mf/set-ref-val! internal-state new-value)) (rerender-fn)) @@ -162,24 +184,25 @@ (rerender-fn))))) (defn use-form - [& {:keys [initial] :as opts}] + [& {:keys [initial schema validators] :as opts}] (let [rerender-fn (use-rerender-fn) initial (mf/with-memo [initial] - {:data (if (fn? initial) (initial) initial) - :errors {} - :touched {}}) + (make-initial-state initial)) internal-state - (mf/use-ref nil) + (mf/use-ref initial) form-mutator - (mf/with-memo [initial] - (create-form-mutator internal-state rerender-fn wrap-update-schema-fn initial opts))] + (mf/with-memo [schema validators] + (let [mutator (create-form-mutator internal-state rerender-fn wrap-update-schema-fn + (select-keys opts [:schema :validators]))] + (swap! mutator identity) + mutator))] ;; Initialize internal state once - (mf/with-layout-effect [] + (mf/with-effect [] (mf/set-ref-val! internal-state initial)) (mf/with-effect [initial] @@ -191,11 +214,16 @@ ([form field value] (on-input-change form field value false)) ([form field value trim?] - (swap! form (fn [state] - (-> state - (assoc-in [:touched field] true) - (assoc-in [:data field] (if trim? (str/trim value) value)) - (update :errors dissoc field)))))) + (letfn [(clean-errors [errors] + (-> errors + (dissoc field) + (not-empty)))] + (swap! form (fn [state] + (-> state + (assoc-in [:touched field] true) + (assoc-in [:data field] (if trim? (str/trim value) value)) + (update :errors clean-errors) + (update :extra-errors clean-errors))))))) (defn update-input-value! [form field value] From 8a3b33797fe7b68f334cfff136ed13a0c3295255 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 19 Dec 2025 10:08:48 +0100 Subject: [PATCH 15/18] :bug: Fix error handling on password change form Fixes https://github.com/penpot/penpot/issues/7978 --- .../src/app/main/ui/settings/password.cljs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/main/ui/settings/password.cljs b/frontend/src/app/main/ui/settings/password.cljs index 5de1e2796b..a5a2dd2b93 100644 --- a/frontend/src/app/main/ui/settings/password.cljs +++ b/frontend/src/app/main/ui/settings/password.cljs @@ -18,16 +18,18 @@ (defn- on-error [form error] - (case (:code (ex-data error)) - :old-password-not-match - (swap! form assoc-in [:errors :password-old] - {:message (tr "errors.wrong-old-password")}) - :email-as-password - (swap! form assoc-in [:errors :password-1] - {:message (tr "errors.email-as-password")}) + (let [data (ex-data error)] + (case (:code data) + :old-password-not-match + (swap! form assoc-in [:extra-errors :password-old] + {:message (tr "errors.wrong-old-password")}) - (let [msg (tr "generic.error")] - (st/emit! (ntf/error msg))))) + :email-as-password + (swap! form assoc-in [:extra-errors :password-1] + {:message (tr "errors.email-as-password")}) + + (let [msg (tr "generic.error")] + (st/emit! (ntf/error msg)))))) (defn- on-success [form] From de052b51615d141cc4a2485c27cb863fa0d1ec21 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 29 Dec 2025 11:10:04 +0100 Subject: [PATCH 16/18] :paperclip: Update changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 289d21c516..d29eeb8bfe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # CHANGELOG -## 2.12.0 (Unreleased) +## 2.12.0 ### :boom: Breaking changes & Deprecations From d3ee50daf5af6748233a5ce37d0b2f28bafe5f69 Mon Sep 17 00:00:00 2001 From: Yamila Moreno Date: Tue, 30 Dec 2025 10:57:51 +0100 Subject: [PATCH 17/18] :wrench: Add ci for branch staging-render --- .github/workflows/build-staging-render.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/build-staging-render.yml diff --git a/.github/workflows/build-staging-render.yml b/.github/workflows/build-staging-render.yml new file mode 100644 index 0000000000..8478999ebf --- /dev/null +++ b/.github/workflows/build-staging-render.yml @@ -0,0 +1,14 @@ +name: _STAGING RENDER + +on: + schedule: + - cron: '36 5-20 * * 1-5' + +jobs: + build-bundle: + uses: ./.github/workflows/build-bundle.yml + secrets: inherit + with: + gh_ref: "staging-render" + build_wasm: "yes" + build_storybook: "yes" From 48e3f35bb3d2af8af41d2bb162b4a41c8cc616af Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 30 Dec 2025 11:09:56 +0100 Subject: [PATCH 18/18] :bug: Fix setting a portion of text as bold or underline messes things up --- CHANGES.md | 6 ++++++ frontend/src/app/main/ui/shapes/text/styles.cljs | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d29eeb8bfe..e890be0fc4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.12.1 + +### :bug: Bugs fixed + +- Fix setting a portion of text as bold or underline messes things up [Github #7980](https://github.com/penpot/penpot/issues/7980) + ## 2.12.0 ### :boom: Breaking changes & Deprecations diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index 6e739ada49..33c77b3fdc 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -106,9 +106,11 @@ :overflowWrap "initial" :lineBreak "auto" :whiteSpace "break-spaces" - :textRendering "geometricPrecision" - :display "inline-block" - :verticalAlign "top"} + :textRendering "geometricPrecision"} + base (cond-> base + (= (:line-height data) "0") + (-> (obj/set! "display" "inline-block") + (obj/set! "verticalAlign" "top"))) fills (cond ;; DEPRECATED: still here for backward compatibility with