Compare commits

...

47 Commits

Author SHA1 Message Date
Andrey Antukh
de052b5161 📎 Update changelog 2025-12-29 11:10:04 +01:00
Andrey Antukh
8a3b33797f 🐛 Fix error handling on password change form
Fixes https://github.com/penpot/penpot/issues/7978
2025-12-29 10:27:27 +01:00
Andrey Antukh
13fd20f76f Backport form error management improvements from develop 2025-12-29 10:27:27 +01:00
Andrey Antukh
01ecde3bfa Add the ability to add relations on penpot sdk (#7987)
*  Add the ability to add relations on penpot sdk

* 📎 Remove debug console log
2025-12-22 20:55:31 +01:00
Alonso Torres
4000ec8762 🐛 Fix problem resizing auto size layouts (#7995) 2025-12-22 20:17:11 +01:00
Andrey Antukh
bb5568e15a 🎉 Enable hindi translations on the application 2025-12-22 16:57:00 +01:00
Pablo Alba
5cbcec3db6 🐛 Fix "maximum call stack size exceeded" crash on variant 2025-12-22 16:57:00 +01:00
Alejandro Alonso
fe44c14bac Merge pull request #7982 from penpot/niwinz-staging-import-bucket
🐛 Prefill storage object bucket if it comes nil on import binfile
2025-12-22 12:17:16 +01:00
Andrey Antukh
336173645e 🐛 Fix regression on export shape on plungins API 2025-12-22 10:41:42 +01:00
Andrey Antukh
83bb4bf221 🐛 Prefill storage object bucket if it comes nil on import binfile 2025-12-19 09:32:51 +01:00
Alejandro Alonso
15ed25ca79 Merge pull request #7966 from penpot/niwinz-staging-abrreviate
🐛 Fix incorrect string truncation with abbreviate template filter
2025-12-12 13:53:33 +01:00
Andrey Antukh
9aa387a473 🐛 Fix incorrect string truncation with abbreviate template filter 2025-12-12 13:50:46 +01:00
Alejandro Alonso
67ba91b4b9 Merge pull request #7971 from penpot/niwinz-staging-bugfix-6
🐛 Fix tokens-lib encoding when value is nilable
2025-12-12 13:46:06 +01:00
Alejandro Alonso
f67f1a6a0e Merge pull request #7972 from penpot/niwinz-staging-bugfix-7
🐛 Fix exception on assinging gradient to shadow on multiple selection
2025-12-12 13:42:39 +01:00
Alejandro Alonso
82d3e2024e Merge pull request #7973 from penpot/niwinz-staging-worker-scheduler
🐛 Fix incorrect redis connection error handling
2025-12-12 13:23:49 +01:00
Alejandro Alonso
4bd846c16d Merge pull request #7969 from penpot/niwinz-staging-fix-ratelimit
🐛 Fix issue on reading rlimit config
2025-12-12 13:22:53 +01:00
Andrey Antukh
94f95ca6b8 🐛 Fix incorrect redis connection error handling 2025-12-12 12:33:38 +01:00
Andrey Antukh
507bf7445b 🐛 Fix tokens-lib encoding when value is nilable 2025-12-12 11:42:15 +01:00
Andrey Antukh
81b72c5acd 🐛 Fix exception on assinging gradient to shadow on multiple selection 2025-12-12 11:24:53 +01:00
Andrey Antukh
974495e08f Reduce log level for profile picture download error
Because it is not blocking operation and does not provents user
to proceed.
2025-12-12 08:17:13 +01:00
Andrey Antukh
2ed39e43c3 🐛 Fix issue on reading rlimit config 2025-12-11 23:50:01 +01:00
Eva Marco
50dbe6ab12 🐛 Fix horizontal scroll on layer panel (#7956) 2025-12-11 21:34:18 +01:00
Andrey Antukh
2f46cbc0d4 Make render wasm import on worker http cache aware 2025-12-11 13:27:20 +01:00
Andrey Antukh
53be6f996b 🐛 Fix issues on build processs related to render-wasm 2025-12-11 12:41:19 +01:00
Andrey Antukh
5a260294a1 🔧 Update build-tag.yml github workflow 2025-12-11 12:00:42 +01:00
Andrey Antukh
3f6e44316e 🐛 Add missing node depes install on render-wasm 2025-12-11 11:51:47 +01:00
Eva Marco
77ef8e6fe6 🐛 Fix scroll on move library modal (#7952) 2025-12-11 10:46:54 +01:00
Alejandro Alonso
916b7709dc Update Pencil Penpot Design System System template in carousel (#7948) 2025-12-10 15:09:28 +01:00
Eva Marco
443e41fea4 🐛 Fix multiple selection with color tokens (#7941) 2025-12-10 14:36:08 +01:00
Alejandro Alonso
c7c9b04095 Merge pull request #7944 from penpot/niwinz-staging-exporter-fix
🐛 Fix incorrect resource lifetime handling on exporter
2025-12-10 14:35:20 +01:00
Eva Marco
c61a0c0332 📚 Add line to changelog (#7945) 2025-12-10 13:58:18 +01:00
Eva Marco
8707ff6511 🎉 Add spanish translation 2025-12-10 13:12:30 +01:00
Florian Schroedl
3d8a251741 🐛 Disallow font-family referencing composite token 2025-12-10 13:12:30 +01:00
Andrey Antukh
34e84ee3c8 🐛 Fix incorrect resource lifetime handling on exporter 2025-12-10 13:02:31 +01:00
Alejandro Alonso
e8201402a7 Merge pull request #7938 from penpot/niwinz-staging-bugfix-5
🐛 Fix several issues
2025-12-10 12:05:42 +01:00
Aitor Moreno
8a22477b96 Merge pull request #7932 from penpot/niwinz-staging-worker-wasm-load
🐛 Fix WASM loading strategy on worker
2025-12-10 11:47:31 +01:00
Alejandro Alonso
3e684ea54f ⬆️ Update svgo dependency on frontend (#7936) 2025-12-10 10:07:02 +01:00
Andrey Antukh
98039f13d8 🐛 Fix main toolbar z-index 2025-12-10 09:47:40 +01:00
Alejandro Alonso
40c27591f6 🐛 Fix svg import (#7925) 2025-12-10 08:36:54 +01:00
Andrey Antukh
91d20a46d1 💄 Add cosmetic changes to exports assets progress component 2025-12-10 08:23:05 +01:00
Andrey Antukh
50bead7c56 🐛 Fix react warning on having p inside p on assets export progress 2025-12-10 08:22:41 +01:00
Andrey Antukh
b75b999903 📎 Fix devenv jvm warning 2025-12-10 08:22:05 +01:00
Andrey Antukh
810f1721c8 🐛 Fix recursion render on subscription modal 2025-12-10 07:54:52 +01:00
Andrey Antukh
a4646373cf ♻️ Refactor wasm loading strategy on worker 2025-12-09 19:41:19 +01:00
Andrey Antukh
f111cbb2a4 Add better cache config on devenv nginx 2025-12-09 19:38:30 +01:00
Aitor Moreno
a614207f7e 🐛 Fix exporter failing with HTTPS 2025-12-09 16:08:20 +01:00
Luis de Dios
6ce3249c6d 🐛 Fix color format does not switch in the view mode (#7923)
* 🐛 Fix color format does not switch in the inspect mode of the view mode

* ♻️ Update components
2025-12-09 14:38:15 +01:00
71 changed files with 2340 additions and 443 deletions

View File

@@ -11,7 +11,7 @@ jobs:
secrets: inherit
with:
gh_ref: ${{ github.ref_name }}
build_wasm: "no"
build_wasm: "yes"
build_storybook: "yes"
build-docker:

View File

@@ -1,6 +1,6 @@
# CHANGELOG
## 2.12.0 (Unreleased)
## 2.12.0
### :boom: Breaking changes & Deprecations
@@ -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
@@ -93,6 +94,9 @@ example. It's still usable as before, we just removed the example.
- Fix unpublish library modal not scrolling a long file list [Taiga #12285](https://tree.taiga.io/project/penpot/issue/12285)
- 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)
- Fix unicode handling on email template abbreviation filter [Github #7966](https://github.com/penpot/penpot/pull/7966)
## 2.11.1

View File

@@ -240,4 +240,4 @@
</div>
</body>
</html>
</html>

View File

@@ -3,7 +3,7 @@
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"}
{:id "penpot-design-system"
:name "Penpot Design System | Pencil"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/penpot-app.penpot"}
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Pencil-Penpot-Design-System.penpot"}
{:id "wireframing-kit"
:name "Wireframe library"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"}

View File

@@ -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)

View File

@@ -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)))

View File

@@ -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
[]

View File

@@ -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 }

View File

@@ -35,6 +35,9 @@
:assets-s3 :s3
nil)))
(def default-bucket
"file-media-object")
(def valid-buckets
#{"file-media-object"
"team-font-variant"

View File

@@ -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]

View File

@@ -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

View File

@@ -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"))))]

View File

@@ -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"}

View File

@@ -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?)))))

View File

@@ -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 [_]

View File

@@ -223,16 +223,19 @@ http {
add_header X-Cache-Status $upstream_cache_status;
}
location ~ ^/(/|css|fonts|images|js|wasm|mjs|map) {
location ~* \.(jpg|png|svg|ttf|woff|woff2)$ {
add_header Cache-Control "public, max-age=604800" always; # 7 days
}
location ~* \.(js|css|wasm)$ {
add_header Cache-Control "no-store" always;
}
location ~ ^/[^/]+/(.*)$ {
return 301 " /404";
}
add_header Cache-Control "no-store";
# This header is what we need to use on prod
# add_header Cache-Control "public, must-revalidate, max-age=0";
add_header Cache-Control "no-store" always;
try_files $uri /index.html$is_args$args /index.html =404;
}
}

View File

@@ -21,6 +21,7 @@
"raw-body": "^3.0.1",
"source-map-support": "^0.5.21",
"svgo": "penpot/svgo#v3.1",
"undici": "^7.16.0",
"xml-js": "^1.6.11",
"xregexp": "^5.1.2"
},

View File

@@ -100,7 +100,7 @@
(def browser-pool-factory
(letfn [(create []
(p/let [opts #js {:args #js ["--font-render-hinting=none"]}
(p/let [opts #js {:args #js ["--allow-insecure-localhost" "--font-render-hinting=none"]}
browser (.launch pw/chromium opts)
id (swap! pool-browser-id inc)]
(l/info :origin "factory" :action "create" :browser-id id)

View File

@@ -74,7 +74,7 @@
(p/fmap (fn [resource]
(assoc exchange :response/body resource)))
(p/merr (fn [cause]
(l/error :hint "unexpected error on export multiple"
(l/error :hint "unexpected error on single export"
:cause cause)
(p/rejected cause))))))
@@ -94,7 +94,7 @@
(redis/pub! topic data))))
on-error (fn [cause]
(l/error :hint "unexpected error on multiple exportation" :cause cause)
(l/error :hint "unexpected error on multiple export" :cause cause)
(if wait
(p/rejected cause)
(redis/pub! topic {:type :export-update
@@ -107,12 +107,12 @@
:on-progress on-progress)
append (fn [{:keys [filename path] :as resource}]
(rsc/add-to-zip! zip path (str/replace filename sanitize-file-regex "_")))
(rsc/add-to-zip zip path (str/replace filename sanitize-file-regex "_")))
proc (->> exports
(map (fn [export] (rd/render export append)))
(p/all)
(p/fnly (fn [_] (.finalize zip)))
(p/mcat (fn [_] (rsc/close-zip zip)))
(p/fmap (constantly resource))
(p/mcat (partial rsc/upload-resource auth-token))
(p/fmap (fn [resource]

View File

@@ -11,6 +11,7 @@
["node:fs" :as fs]
["node:fs/promises" :as fsp]
["node:path" :as path]
["undici" :as http]
[app.common.exceptions :as ex]
[app.common.transit :as t]
[app.common.uri :as u]
@@ -53,30 +54,40 @@
(.pipe zip out)
zip))
(defn add-to-zip!
(defn add-to-zip
[zip path name]
(.file ^js zip path #js {:name name}))
(defn close-zip!
(defn close-zip
[zip]
(.finalize ^js zip))
(p/create (fn [resolve]
(.on ^js zip "close" resolve)
(.finalize ^js zip))))
(defn upload-resource
[auth-token resource]
(->> (fsp/readFile (:path resource))
(p/fmap (fn [buffer]
(js/console.log buffer)
(new js/Blob #js [buffer] #js {:type (:mtype resource)})))
(p/mcat (fn [blob]
(let [fdata (new js/FormData)
uri (-> (cf/get :public-uri)
(u/ensure-path-slash)
(u/join "api/management/methods/upload-tempfile")
(str))]
(let [fdata (new http/FormData)
agent (new http/Agent #js {:connect #js {:rejectUnauthorized false}})
headers #js {"X-Shared-Key" cf/management-key
"Authorization" (str "Bearer " auth-token)}
request #js {:headers headers
:method "POST"
:body fdata
:dispatcher agent}
uri (-> (cf/get :public-uri)
(u/ensure-path-slash)
(u/join "api/management/methods/upload-tempfile")
(str))]
(.append fdata "content" blob (:filename resource))
(js/fetch uri #js {:headers #js {"X-Shared-Key" cf/management-key
"Authorization" (str "Bearer " auth-token)}
:method "POST"
:body fdata}))))
(http/fetch uri request))))
(p/mcat (fn [response]
(if (not= (.-status response) 200)
(ex/raise :type :internal

View File

@@ -75,7 +75,8 @@
[path]
(->> (.stat fs/promises path)
(p/fmap (fn [data]
{:created-at (inst-ms (.-ctime ^js data))
{:path path
:created-at (inst-ms (.-ctime ^js data))
:size (.-size data)}))
(p/merr (fn [_cause]
(p/resolved nil)))))

View File

@@ -582,6 +582,7 @@ __metadata:
raw-body: "npm:^3.0.1"
source-map-support: "npm:^0.5.21"
svgo: "penpot/svgo#v3.1"
undici: "npm:^7.16.0"
ws: "npm:^8.18.3"
xml-js: "npm:^1.6.11"
xregexp: "npm:^5.1.2"
@@ -1513,6 +1514,13 @@ __metadata:
languageName: node
linkType: hard
"undici@npm:^7.16.0":
version: 7.16.0
resolution: "undici@npm:7.16.0"
checksum: 10c0/efd867792e9f233facf9efa0a087e2d9c3e4415c0b234061b9b40307ca4fa01d945fee4d43c7b564e1b80e0d519bcc682f9f6e0de13c717146c00a80e2f1fb0f
languageName: node
linkType: hard
"unique-filename@npm:^4.0.0":
version: 4.0.0
resolution: "unique-filename@npm:4.0.0"

View File

@@ -50,5 +50,8 @@
:shadow-cljs
{:main-opts ["-m" "shadow.cljs.devtools.cli"]
:jvm-opts ["--sun-misc-unsafe-memory-access=allow" "-Dpenpot.wasm.profile-marks=true"]}
:jvm-opts ["--sun-misc-unsafe-memory-access=allow"
"-Dpenpot.wasm.profile-marks=true"
"-XX:+UnlockExperimentalVMOptions"
"-XX:CompileCommand=blackhole,criterium.blackhole.Blackhole::consume"]}
}}

View File

@@ -45,9 +45,9 @@
"translations": "node ./scripts/translations.js",
"watch:app:assets": "node ./scripts/watch.js",
"watch:app:libs": "node ./scripts/build-libs.js --watch",
"watch:app:main": "clojure -M:dev:shadow-cljs watch main storybook",
"watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook",
"clear:shadow-cache": "rm -rf .shadow-cljs",
"watch:app": "yarn run clear:shadow-cache && yarn run build:app:worker && concurrently \"yarn run watch:app:main\" \"yarn run watch:app:libs\"",
"watch:app": "yarn run clear:shadow-cache && concurrently \"yarn run watch:app:main\" \"yarn run watch:app:libs\"",
"watch": "yarn run watch:app:assets",
"watch:storybook": "yarn run build:storybook:assets && concurrently \"storybook dev -p 6006 --no-open\" \"yarn run watch:storybook:assets\"",
"watch:storybook:assets": "node ./scripts/watch-storybook.js"
@@ -106,7 +106,7 @@
"@penpot/hljs": "portal:./vendor/hljs",
"@penpot/mousetrap": "portal:./vendor/mousetrap",
"@penpot/plugins-runtime": "1.3.2",
"@penpot/svgo": "penpot/svgo#v3.1",
"@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "portal:./text-editor",
"@tokens-studio/sd-transforms": "1.2.11",
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",

View File

@@ -180,8 +180,8 @@ export async function watch(baseDir, predicate, callback) {
});
}
async function readManifestFile() {
const manifestPath = "resources/public/js/manifest.json";
async function readManifestFile(resource) {
const manifestPath = "resources/public/" + resource;
let content = await fs.readFile(manifestPath, { encoding: "utf8" });
return JSON.parse(content);
}
@@ -189,19 +189,23 @@ async function readManifestFile() {
async function readShadowManifest() {
const ts = Date.now();
try {
const content = await readManifestFile();
const content = await readManifestFile("js/manifest.json");
const index = {
ts: ts,
config: "js/config.js?ts=" + ts,
polyfills: "js/polyfills.js?ts=" + ts,
worker_main: "js/worker/main.js?ts=" + ts,
};
for (let item of content) {
index[item.name] = "js/" + item["output-name"];
}
const content2 = await readManifestFile("js/worker/manifest.json");
for (let item of content2) {
index["worker_" + item.name] = "js/worker/" + item["output-name"];
}
return index;
} catch (cause) {
return {
@@ -274,6 +278,7 @@ async function readTranslations() {
"id",
"ru",
"tr",
"hi",
"zh_CN",
"zh_Hant",
"hr",

View File

@@ -20,21 +20,24 @@ echo $PATH
set -ex
corepack enable;
corepack install || exit 1;
corepack install;
yarn install || exit 1;
rm -rf resources/public;
rm -rf target/dist;
rm -rf resources/public;
yarn run build:app:main --config-merge "{:release-version \"${CURRENT_HASH}-${TS}\"}" $EXTRA_PARAMS || exit 1
mkdir -p resources/public;
if [ "$INCLUDE_WASM" = "yes" ]; then
yarn run build:wasm || exit 1;
fi
pushd ../render-wasm;
./build
popd
yarn run build:app:main --config-merge "{:release-version \"${CURRENT_HASH}-${TS}\"}" $EXTRA_PARAMS;
yarn run build:app:libs || exit 1;
yarn run build:app:assets || exit 1;
sed -i "s/\.\/render.js/.\/render.js?version=$CURRENT_VERSION/g" resources/public/js/worker/main*.js
mkdir -p target/dist;
rsync -avr resources/public/ target/dist/
@@ -44,10 +47,6 @@ sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/rasterizer.html;
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/index.html;
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/rasterizer.html;
if [ "$INCLUDE_WASM" = "yes" ]; then
sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js;
fi
if [ "$INCLUDE_STORYBOOK" = "yes" ]; then
# build storybook
yarn run build:storybook || exit 1;

View File

@@ -83,7 +83,7 @@
:source-map-detail-level :all}}}
:worker
{:target :esm
{:target :browser
:output-dir "resources/public/js/worker/"
:asset-path "/js/worker"
:devtools {:browser-inject :main
@@ -94,6 +94,7 @@
{:main
{:entries [app.worker]
:web-worker true
:prepend-js "importScripts('./render.js');"
:depends-on #{}}}
:js-options

View File

@@ -127,7 +127,7 @@
public-uri))
(def worker-uri
(obj/get global "penpotWorkerURI" "/js/worker.js"))
(obj/get global "penpotWorkerURI" "/js/worker/main.js"))
(defn external-feature-flag
[flag value]
@@ -189,7 +189,11 @@
(true? thumbnail?) (u/join (dm/str id "/thumbnail"))
(false? thumbnail?) (u/join (dm/str id)))))))
(defn resolve-static-asset
[path]
(let [uri (u/join public-uri path)]
(assoc uri :query (dm/str "version=" (:full version)))))
(defn resolve-href
[resource]
(let [version (get version :full)
href (-> public-uri
(u/ensure-path-slash)
(u/join resource)
(get :path))]
(str href "?version=" version)))

View File

@@ -255,14 +255,19 @@
(defn- parse-sd-token-font-family-value
[value]
(let [missing-references (seq (some cto/find-token-value-references value))]
(let [value (-> (js->clj value) (flatten))
valid-font-family (or (string? value) (every? string? value))
missing-references (seq (some cto/find-token-value-references value))]
(cond
(not valid-font-family)
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-font-family value)]}
missing-references
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference missing-references)]
:references missing-references}
:else
{:value (-> (js->clj value) (flatten))})))
{:value value})))
(defn parse-atomic-typography-value [token-type token-value]
(case token-type

View File

@@ -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 <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 <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]

View File

@@ -88,6 +88,10 @@
{:error/code :error.style-dictionary/invalid-token-value-font-weight
:error/fn #(tr "workspace.tokens.invalid-font-weight-token-value" %)}
:error.style-dictionary/invalid-token-value-font-family
{:error/code :error.style-dictionary/invalid-token-value-font-family
:error/fn #(tr "workspace.tokens.invalid-font-family-token-value" %)}
:error.style-dictionary/invalid-token-value-typography
{:error/code :error.style-dictionary/invalid-token-value-typography
:error/fn #(tr "workspace.tokens.invalid-token-value-typography" %)}

View File

@@ -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)

View File

@@ -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] "")

View File

@@ -45,7 +45,7 @@
.element-list {
@include t.use-typography("body-large");
color: var(--modal-text-foreground-color);
overflow-y: scroll;
overflow-y: auto;
margin-block: 0;
}

View File

@@ -223,24 +223,30 @@
circ (* 2 Math/PI 12)
pct (- circ (* circ (/ progress total)))
pwidth (if error?
280
(/ (* progress 280) total))
color (cond
error? clr/new-danger
healthy? (if is-default-theme?
clr/new-primary
clr/new-primary-light)
(not healthy?) clr/new-warning)
pwidth
(if error?
280
(/ (* progress 280) total))
background-clr (if is-default-theme?
clr/background-quaternary
clr/background-quaternary-light)
title (cond
error? (tr "workspace.options.exporting-object-error")
complete? (tr "workspace.options.exporting-complete")
healthy? (tr "workspace.options.exporting-object")
(not healthy?) (tr "workspace.options.exporting-object-slow"))
color
(cond
error? clr/new-danger
healthy? (if is-default-theme?
clr/new-primary
clr/new-primary-light)
(not healthy?) clr/new-warning)
background-clr
(if is-default-theme?
clr/background-quaternary
clr/background-quaternary-light)
title
(cond
error? (tr "workspace.options.exporting-object-error")
complete? (tr "workspace.options.exporting-complete")
healthy? (tr "workspace.options.exporting-object")
(not healthy?) (tr "workspace.options.exporting-object-slow"))
retry-last-export
(mf/use-fn #(st/emit! (de/retry-last-export)))
@@ -284,7 +290,7 @@
:on-click retry-last-export}
(tr "workspace.options.retry")]
[:p {:class (stl/css :progress)}
[:span {:class (stl/css :progress)}
(dm/str progress " / " total)])]
[:button {:class (stl/css :progress-close-button)

View File

@@ -36,7 +36,7 @@
:text [:visibility :geometry :text :shadow :blur :stroke :layout-element]
:variant [:variant :geometry :fill :stroke :shadow :blur :layout :layout-element]})
(mf/defc attributes
(mf/defc attributes*
[{:keys [page-id file-id shapes frame from libraries share-id objects color-space]}]
(let [shapes (hooks/use-equal-memo shapes)
first-shape (first shapes)

View File

@@ -96,7 +96,7 @@
embed-images? (replace-map images-data))]
(str/format page-template style-code markup-code)))
(mf/defc code
(mf/defc code*
[{:keys [shapes frame on-expand from]}]
(let [style-type* (mf/use-state "css")
markup-type* (mf/use-state "html")

View File

@@ -16,8 +16,8 @@
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.inspect.attributes :refer [attributes]]
[app.main.ui.inspect.code :refer [code]]
[app.main.ui.inspect.attributes :refer [attributes*]]
[app.main.ui.inspect.code :refer [code*]]
[app.main.ui.inspect.selection-feedback :refer [resolve-shapes]]
[app.main.ui.inspect.styles :refer [styles-tab*]]
[app.util.dom :as dom]
@@ -122,8 +122,7 @@
(fn []
(if (seq shapes)
(st/emit! (ptk/event ::ev/event {::ev/name "inspect-mode-click-element"}))
(handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info)))
(reset! color-space* "hex")))
(handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info)))))
[:aside {:class (stl/css-case :settings-bar-right true
:viewer-code (= from :viewer))}
@@ -189,41 +188,41 @@
:libraries libraries
:file-id file-id}]
:computed
[:& attributes {:color-space color-space
:page-id page-id
:objects objects
:file-id file-id
:frame frame
:shapes shapes
:from from
:libraries libraries
:share-id share-id}]
[:> attributes* {:color-space color-space
:page-id page-id
:objects objects
:file-id file-id
:frame frame
:shapes shapes
:from from
:libraries libraries
:share-id share-id}]
:code
[:& code {:frame frame
:shapes shapes
:on-expand handle-expand
:from from}])]
[:> code* {:frame frame
:shapes shapes
:on-expand handle-expand
:from from}])]
[:> tab-switcher* {:tabs tabs
:selected (name @section)
:on-change handle-change-tab
:class (stl/css :viewer-tab-switcher)}
(case @section
:info
[:& attributes {:page-id page-id
:objects objects
:file-id file-id
:frame frame
:shapes shapes
:from from
:libraries libraries
:share-id share-id}]
[:> attributes* {:page-id page-id
:objects objects
:file-id file-id
:frame frame
:shapes shapes
:from from
:libraries libraries
:share-id share-id}]
:code
[:& code {:frame frame
:shapes shapes
:on-expand handle-expand
:from from}])])]]
[:> code* {:frame frame
:shapes shapes
:on-expand handle-expand
:from from}])])]]
[:div {:class (stl/css :empty)}
[:div {:class (stl/css :code-info)}
[:span {:class (stl/css :placeholder-icon)}

View File

@@ -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]

View File

@@ -77,7 +77,7 @@
[:button {:class (stl/css :cta-button :bottom-link)
:on-click cta-link-trial} cta-text-trial])])
(defn schema:seats-form [min-editors]
(defn- make-management-form-schema [min-editors]
[:map {:title "SeatsForm"}
[:min-members [::sm/number {:min min-editors
:max 9999}]]
@@ -87,7 +87,6 @@
{::mf/register modal/components
::mf/register-as :management-dialog}
[{:keys [subscription-type current-subscription editors subscribe-to-trial]}]
(let [unlimited-modal-step*
(mf/use-state 1)
@@ -112,9 +111,12 @@
{:min-members min-editors
:redirect-to-payment-details false})
schema
(mf/with-memo [min-editors]
(make-management-form-schema min-editors))
form
(fm/use-form :schema (schema:seats-form min-editors)
:initial initial)
(fm/use-form :schema schema :initial initial)
submit-in-progress
(mf/use-ref false)
@@ -334,11 +336,15 @@
[:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription")}]]
[:div {:class (stl/css :modal-end)}
[:div {:class (stl/css :modal-title)} (tr "subscription.settings.sucess.dialog.title" subscription-name)]
[:div {:class (stl/css :modal-title)}
(tr "subscription.settings.sucess.dialog.title" subscription-name)]
(when (not= subscription-name "professional")
[:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.success.dialog.thanks" subscription-name)])
[:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.success.dialog.description")]
[:p {:class (stl/css :modal-text-large)} (tr "subscription.settings.sucess.dialog.footer")]
[:p {:class (stl/css :modal-text-large)}
(tr "subscription.settings.success.dialog.thanks" subscription-name)])
[:p {:class (stl/css :modal-text-large)}
(tr "subscription.settings.success.dialog.description")]
[:p {:class (stl/css :modal-text-large)}
(tr "subscription.settings.sucess.dialog.footer")]
[:div {:class (stl/css :success-action-buttons)}
[:input
@@ -418,7 +424,11 @@
(mf/with-effect []
(dom/set-html-title (tr "subscription.labels")))
(mf/with-effect [authenticated? show-subscription-success-modal? show-trial-subscription-modal? success-modal-is-trial? subscription]
(mf/with-effect [authenticated?
show-subscription-success-modal?
show-trial-subscription-modal?
success-modal-is-trial?
subscription]
(when ^boolean authenticated?
(cond
^boolean show-trial-subscription-modal?

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -68,7 +68,7 @@
(mf/defc color-token-row*
{::mf/private true}
[{:keys [active-tokens color-token color on-swatch-click-token detach-token open-modal-from-token]}]
[{:keys [active-tokens applied-token-name color on-swatch-click-token detach-token open-modal-from-token]}]
(let [;; `active-tokens` may be provided as a `delay` (lazy computation).
;; In that case we must deref it (`@active-tokens`) to force evaluation
;; and obtain the actual value. If its already realized (not a delay),
@@ -77,21 +77,22 @@
@active-tokens
active-tokens)
color-tokens (:color active-tokens)
active-color-tokens (:color active-tokens)
token (some #(when (= (:name %) color-token) %) color-tokens)
token (some #(when (= (:name %) applied-token-name) %) active-color-tokens)
on-detach-token
(mf/use-fn
(mf/deps detach-token token color-token)
(mf/deps detach-token token applied-token-name)
(fn []
(let [token (or token color-token)]
(let [token (or token applied-token-name)]
(detach-token token))))
has-errors (some? (:errors token))
token-name (:name token)
resolved (:resolved-value token)
not-active (and (some? active-tokens) (nil? token))
not-active (and (empty? active-tokens)
(nil? token))
id (dm/str (:id token) "-name")
swatch-tooltip-content (cond
not-active
@@ -109,7 +110,7 @@
#(mf/html
[:div
[:span (dm/str (tr "workspace.tokens.token-name") ": ")]
[:span {:class (stl/css :token-name-tooltip)} color-token]]))]
[:span {:class (stl/css :token-name-tooltip)} applied-token-name]]))]
[:div {:class (stl/css :color-info)}
[:div {:class (stl/css-case :token-color-wrapper true
@@ -128,7 +129,7 @@
:class (stl/css :token-tooltip)}
[:div {:class (stl/css :token-name)
:aria-labelledby id}
(or token-name color-token)]]
(or token-name applied-token-name)]]
[:div {:class (stl/css :token-actions)}
[:> icon-button*
{:variant "action"
@@ -146,7 +147,11 @@
on-change on-reorder on-detach on-open on-close on-remove origin on-detach-token
disable-drag on-focus on-blur select-only select-on-focus on-token-change applied-token]}]
(let [token-color (contains? cfg/flags :token-color)
(let [;; TODO: Remove this workaround fixing `get-attrs*` fn on sidebar/options/shapes/multiple.cljs
applied-token (if (= :multiple applied-token)
nil
applied-token)
token-color (contains? cfg/flags :token-color)
libraries (mf/deref refs/files)
color-without-hash (mf/use-memo
@@ -177,7 +182,6 @@
(-> (deref active-tokens*)
(select-keys (get tk/tokens-by-input origin))
(not-empty)))))
on-focus'
(mf/use-fn
(mf/deps on-focus)
@@ -352,7 +356,7 @@
(cond
(and token-color applied-token)
[:> color-token-row* {:active-tokens tokens
:color-token applied-token
:applied-token-name applied-token
:color (dissoc color :ref-id :ref-file)
:on-swatch-click-token on-swatch-click-token
:detach-token detach-token

View File

@@ -63,7 +63,8 @@
:data {:index index})
[nil nil])
stroke-color-token (:stroke-color applied-tokens)
stroke-color-token
(:stroke-color applied-tokens)
on-color-change-refactor
(mf/use-fn

View File

@@ -18,7 +18,7 @@
padding: deprecated.$s-8 deprecated.$s-16;
border-radius: deprecated.$s-8;
border: deprecated.$s-2 solid var(--panel-border-color);
z-index: deprecated.$z-index-3;
z-index: deprecated.$z-index-1;
background-color: var(--color-background-primary);
transition:
top 0.3s,

View File

@@ -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))))))))

View File

@@ -1316,51 +1316,16 @@
(mem/free)
content)))
(defn init-wasm-module
[module]
(let [default-fn (unchecked-get module "default")
serializers
#js
{:blur-type (unchecked-get module "RawBlurType")
:blend-mode (unchecked-get module "RawBlendMode")
:bool-type (unchecked-get module "RawBoolType")
:font-style (unchecked-get module "RawFontStyle")
:flex-direction (unchecked-get module "RawFlexDirection")
:grid-direction (unchecked-get module "RawGridDirection")
:grow-type (unchecked-get module "RawGrowType")
:align-items (unchecked-get module "RawAlignItems")
:align-self (unchecked-get module "RawAlignSelf")
:align-content (unchecked-get module "RawAlignContent")
:justify-items (unchecked-get module "RawJustifyItems")
:justify-content (unchecked-get module "RawJustifyContent")
:justify-self (unchecked-get module "RawJustifySelf")
:wrap-type (unchecked-get module "RawWrapType")
:grid-track-type (unchecked-get module "RawGridTrackType")
:shadow-style (unchecked-get module "RawShadowStyle")
:stroke-style (unchecked-get module "RawStrokeStyle")
:stroke-cap (unchecked-get module "RawStrokeCap")
:shape-type (unchecked-get module "RawShapeType")
:constraint-h (unchecked-get module "RawConstraintH")
:constraint-v (unchecked-get module "RawConstraintV")
:sizing (unchecked-get module "RawSizing")
:vertical-align (unchecked-get module "RawVerticalAlign")
:fill-data (unchecked-get module "RawFillData")
:text-align (unchecked-get module "RawTextAlign")
:text-direction (unchecked-get module "RawTextDirection")
:text-decoration (unchecked-get module "RawTextDecoration")
:text-transform (unchecked-get module "RawTextTransform")
:segment-data (unchecked-get module "RawSegmentData")
:stroke-linecap (unchecked-get module "RawStrokeLineCap")
:stroke-linejoin (unchecked-get module "RawStrokeLineJoin")
:fill-rule (unchecked-get module "RawFillRule")}]
(set! wasm/serializers serializers)
(default-fn)))
href (cf/resolve-href "js/render-wasm.wasm")]
(default-fn #js {:locateFile (constantly href)})))
(defonce module
(delay
(if (exists? js/dynamicImport)
(let [uri (cf/resolve-static-asset "js/render_wasm.js")]
(let [uri (cf/resolve-href "js/render-wasm.js")]
(->> (js/dynamicImport (str uri))
(p/mcat init-wasm-module)
(p/fmap

View File

@@ -0,0 +1,242 @@
export const GrowType = {
"fixed": 0,
"auto-width": 1,
"auto-height": 2,
};
export const RawBlendMode = {
"normal": 3,
"screen": 14,
"overlay": 15,
"darken": 16,
"lighten": 17,
"color-dodge": 18,
"color-burn": 19,
"hard-light": 20,
"soft-light": 21,
"difference": 22,
"exclusion": 23,
"multiply": 24,
"hue": 25,
"saturation": 26,
"color": 27,
"luminosity": 28,
};
export const RawBlurType = {
"layer-blur": 0,
};
export const RawFillData = {
"solid": 0,
"linear": 1,
"radial": 2,
"image": 3,
};
export const RawFontStyle = {
"normal": 0,
"italic": 1,
};
export const RawAlignItems = {
"start": 0,
"end": 1,
"center": 2,
"stretch": 3,
};
export const RawAlignContent = {
"start": 0,
"end": 1,
"center": 2,
"space-between": 3,
"space-around": 4,
"space-evenly": 5,
"stretch": 6,
};
export const RawJustifyItems = {
"start": 0,
"end": 1,
"center": 2,
"stretch": 3,
};
export const RawJustifyContent = {
"start": 0,
"end": 1,
"center": 2,
"space-between": 3,
"space-around": 4,
"space-evenly": 5,
"stretch": 6,
};
export const RawJustifySelf = {
"none": 0,
"auto": 1,
"start": 2,
"end": 3,
"center": 4,
"stretch": 5,
};
export const RawAlignSelf = {
"none": 0,
"auto": 1,
"start": 2,
"end": 3,
"center": 4,
"stretch": 5,
};
export const RawVerticalAlign = {
"top": 0,
"center": 1,
"bottom": 2,
};
export const RawConstraintH = {
"left": 0,
"right": 1,
"leftright": 2,
"center": 3,
"scale": 4,
};
export const RawConstraintV = {
"top": 0,
"bottom": 1,
"topbottom": 2,
"center": 3,
"scale": 4,
};
export const RawFlexDirection = {
"row": 0,
"row-reverse": 1,
"column": 2,
"column-reverse": 3,
};
export const RawWrapType = {
"wrap": 0,
"nowrap": 1,
};
export const RawGridDirection = {
"row": 0,
"column": 1,
};
export const RawGridTrackType = {
"percent": 0,
"flex": 1,
"auto": 2,
"fixed": 3,
};
export const RawSizing = {
"fill": 0,
"fix": 1,
"auto": 2,
};
export const RawBoolType = {
"union": 0,
"difference": 1,
"intersection": 2,
"exclusion": 3,
};
export const RawSegmentData = {
"move-to": 1,
"line-to": 2,
"curve-to": 3,
"close": 4,
};
export const RawShadowStyle = {
"drop-shadow": 0,
"inner-shadow": 1,
};
export const RawShapeType = {
"frame": 0,
"group": 1,
"bool": 2,
"rect": 3,
"path": 4,
"text": 5,
"circle": 6,
"svg-raw": 7,
};
export const RawStrokeStyle = {
"solid": 0,
"dotted": 1,
"dashed": 2,
"mixed": 3,
};
export const RawStrokeCap = {
"none": 0,
"line-arrow": 1,
"triangle-arrow": 2,
"square-marker": 3,
"circle-marker": 4,
"diamond-marker": 5,
"round": 6,
"square": 7,
};
export const RawFillRule = {
"nonzero": 0,
"evenodd": 1,
};
export const RawStrokeLineCap = {
"butt": 0,
"round": 1,
"square": 2,
};
export const RawStrokeLineJoin = {
"miter": 0,
"round": 1,
"bevel": 2,
};
export const RawTextAlign = {
"left": 0,
"center": 1,
"right": 2,
"justify": 3,
};
export const RawTextDirection = {
"ltr": 0,
"rtl": 1,
};
export const RawTextDecoration = {
"none": 0,
"underline": 1,
"line-through": 2,
"overline": 3,
};
export const RawTextTransform = {
"none": 0,
"uppercase": 1,
"lowercase": 2,
"capitalize": 3,
};
export const RawGrowType = {
"fixed": 0,
"auto-width": 1,
"auto-height": 2,
};

View File

@@ -4,9 +4,43 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.render-wasm.wasm)
(ns app.render-wasm.wasm
(:require ["./api/shared.js" :as shared]))
(defonce internal-frame-id nil)
(defonce internal-module #js {})
(defonce serializers #js {})
(defonce serializers
#js {:blur-type shared/RawBlurType
:blend-mode shared/RawBlendMode
:bool-type shared/RawBoolType
:font-style shared/RawFontStyle
:flex-direction shared/RawFlexDirection
:grid-direction shared/RawGridDirection
:grow-type shared/RawGrowType
:align-items shared/RawAlignItems
:align-self shared/RawAlignSelf
:align-content shared/RawAlignContent
:justify-items shared/RawJustifyItems
:justify-content shared/RawJustifyContent
:justify-self shared/RawJustifySelf
:wrap-type shared/RawWrapType
:grid-track-type shared/RawGridTrackType
:shadow-style shared/RawShadowStyle
:stroke-style shared/RawStrokeStyle
:stroke-cap shared/RawStrokeCap
:shape-type shared/RawShapeType
:constraint-h shared/RawConstraintH
:constraint-v shared/RawConstraintV
:sizing shared/RawSizing
:vertical-align shared/RawVerticalAlign
:fill-data shared/RawFillData
:text-align shared/RawTextAlign
:text-direction shared/RawTextDirection
:text-decoration shared/RawTextDecoration
:text-transform shared/RawTextTransform
:segment-data shared/RawSegmentData
:stroke-linecap shared/RawStrokeLineCap
:stroke-linejoin shared/RawStrokeLineJoin
:fill-rule shared/RawFillRule})
(defonce context-initialized? false)

View File

@@ -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]

View File

@@ -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"}

View File

@@ -89,7 +89,7 @@
(defn init
"Return a initialized webworker instance."
[path on-error]
(let [instance (js/Worker. path #js {:type "module"})
(let [instance (js/Worker. path)
bus (rx/subject)
worker (Worker. instance (rx/to-observable bus))

View File

@@ -24,11 +24,12 @@
[beicon.v2.core :as rx]
[okulary.core :as l]
[promesa.core :as p]
[rumext.v2 :as mf]
[shadow.esm :refer (dynamic-import)]))
[rumext.v2 :as mf]))
(log/set-level! :trace)
(def ^:private ^:const thumbnail-aspect-ratio (/ 2 3))
(defn- handle-response
[{:keys [body status] :as response}]
(cond
@@ -64,6 +65,10 @@
(rx/map http/conditional-decode-transit)
(rx/mapcat handle-response))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SVG RENDERING (LEGACY RENDER)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- render-thumbnail
[{:keys [page file-id revn] :as params}]
(try
@@ -98,15 +103,13 @@
(->> (request-data-for-thumbnail file-id revn true)
(rx/map render-thumbnail)))
(def init-wasm
(delay
(let [uri (cf/resolve-static-asset "js/render_wasm.js")]
(-> (dynamic-import (str uri))
(p/then #(wasm.api/init-wasm-module %))
(p/then #(set! wasm/internal-module %))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WASM RENDERING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(mf/defc svg-wrapper
[{:keys [data-uri background width height]}]
(mf/defc svg-wrapper*
{::mf/private true}
[{:keys [uri background width height]}]
[:svg {:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
@@ -116,85 +119,97 @@
:background background}
:fill "none"
:viewBox (dm/str "0 0 " width " " height)}
[:image {:xlinkHref data-uri
[:image {:xlinkHref uri
:width width
:height height}]])
(defn blob->uri
(defn- blob->uri
[blob]
(.readAsDataURL (js/FileReaderSync.) blob))
(def thumbnail-aspect-ratio (/ 2 3))
(defn- render-canvas-blob
[canvas width height background]
(->> (.convertToBlob ^js canvas)
(p/fmap (fn [blob]
(rds/renderToStaticMarkup
(mf/element svg-wrapper*
#js {:uri (blob->uri blob)
:width width
:height height
:background background}))))))
(defn render-canvas-blob
[canvas width height background-color]
(-> (.convertToBlob canvas)
(p/then
(fn [blob]
(rds/renderToStaticMarkup
(mf/element
svg-wrapper
#js {:data-uri (blob->uri blob)
:width width
:height height
:background background-color}))))))
(defonce ^:private wasm-module
(delay
(let [module (unchecked-get js/globalThis "WasmModule")
init-fn (unchecked-get module "default")
href (cf/resolve-href "js/render-wasm.wasm")]
(->> (init-fn #js {:locateFile (constantly href)})
(p/fnly (fn [module cause]
(if cause
(js/console.error cause)
(set! wasm/internal-module module))))))))
(defn process-wasm-thumbnail
(defn- render-thumbnail-with-wasm
[{:keys [id file-id revn width] :as message}]
(->> (rx/from @init-wasm)
(->> (rx/from @wasm-module)
(rx/mapcat #(request-data-for-thumbnail file-id revn false))
(rx/mapcat
(fn [{:keys [page] :as file}]
(rx/create
(fn [subs]
(let [background-color (or (:background page) cc/canvas)
height (* width thumbnail-aspect-ratio)
canvas (js/OffscreenCanvas. width height)
init? (wasm.api/init-canvas-context canvas)]
(let [bgcolor (or (:background page) cc/canvas)
height (* width thumbnail-aspect-ratio)
canvas (js/OffscreenCanvas. width height)
init? (wasm.api/init-canvas-context canvas)]
(if init?
(let [objects (:objects page)
frame (some->> page :thumbnail-frame-id (get objects))
vbox (if frame
(-> (gsb/get-object-bounds objects frame)
(grc/fix-aspect-ratio thumbnail-aspect-ratio))
(render/calculate-dimensions objects thumbnail-aspect-ratio))
zoom (/ width (:width vbox))]
frame (some->> page :thumbnail-frame-id (get objects))
vbox (if frame
(-> (gsb/get-object-bounds objects frame)
(grc/fix-aspect-ratio thumbnail-aspect-ratio))
(render/calculate-dimensions objects thumbnail-aspect-ratio))
zoom (/ width (:width vbox))]
(wasm.api/initialize-viewport
objects zoom vbox background-color
objects zoom vbox bgcolor
(fn []
(if frame
(wasm.api/render-sync-shape (:id frame))
(wasm.api/render-sync))
(-> (render-canvas-blob canvas width height background-color)
(p/then #(rx/push! subs {:id id :data % :file-id file-id :revn revn}))
(p/catch #(rx/error! subs %))
(p/finally #(rx/end! subs))))))
(->> (render-canvas-blob canvas width height bgcolor)
(p/fnly (fn [data cause]
(if cause
(rx/error! subs cause)
(rx/push! subs
{:id id
:data data
:file-id file-id
:revn revn}))
(rx/end! subs)))))))
(rx/end! subs))
nil)))))))
(defonce thumbs-subject (rx/subject))
(defonce ^:private
thumbnails-queue
(rx/subject))
(defonce thumbs-stream
(->> thumbs-subject
(rx/mapcat process-wasm-thumbnail)
(defonce ^:private
thumbnails-stream
(->> thumbnails-queue
(rx/mapcat render-thumbnail-with-wasm)
(rx/share)))
(defmethod impl/handler :thumbnails/generate-for-file-wasm
[message _]
(rx/create
(fn [subs]
(let [id (uuid/next)
sid
(->> thumbs-stream
(rx/filter #(= id (:id %)))
(rx/subs!
#(do
(rx/push! subs %)
(rx/end! subs))))]
(rx/push! thumbs-subject (assoc message :id id))
(let [id (uuid/next)
sid (->> thumbnails-stream
(rx/filter #(= id (:id %)))
(rx/subs!
(fn [result]
(rx/push! subs result)
(rx/end! subs))))]
(rx/push! thumbnails-queue (assoc message :id id))
#(rx/dispose! sid)))))

View File

@@ -7745,6 +7745,10 @@ msgstr ""
"Invalid token value: only none, Uppercase, Lowercase or Capitalize are "
"accepted"
#: src/app/main/data/workspace/tokens/errors.cljs:93
msgid "workspace.tokens.invalid-font-family-token-value"
msgstr "Invalid token value: you can only reference a font-family token"
#: src/app/main/data/workspace/tokens/errors.cljs:85
msgid "workspace.tokens.invalid-text-decoration-token-value"
msgstr "Invalid token value: only none, underline and strike-through are accepted"

View File

@@ -7670,6 +7670,14 @@ msgstr ""
msgid "workspace.tokens.invalid-shadow-type-token-value"
msgstr "Tipo de sombra no válida: solo se aceptan 'innerShadow' o 'dropShadow'"
#: src/app/main/data/workspace/tokens/errors.cljs:93
msgid "workspace.tokens.invalid-font-family-token-value"
msgstr "Valor de token no válido: solo puedes referenciar tokens tipo font-family"
#: src/app/main/data/workspace/tokens/errors.cljs:85
msgid "workspace.tokens.invalid-text-decoration-token-value"
msgstr "Valor de token no válido: solo none, underline y strike-through son aceptados"
#: src/app/main/data/workspace/tokens/errors.cljs:93
msgid "workspace.tokens.invalid-token-value-typography"
msgstr "Valor no válido: debe hacer referencia a un token tipográfico compuesto."

View File

@@ -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 <vaibhavrathod2282@gmail.com>\n"
"Language-Team: Hindi "
"<https://hosted.weblate.org/projects/penpot/frontend/hi/>\n"
"Language-Team: Hindi <https://hosted.weblate.org/projects/penpot/frontend/"
"hi/>\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 "लॉक किया गया संस्करण - केवल निर्माता ही इसे संशोधित कर सकता है"

View File

@@ -1226,16 +1226,16 @@ __metadata:
languageName: node
linkType: hard
"@penpot/svgo@penpot/svgo#v3.1":
"@penpot/svgo@penpot/svgo#v3.2":
version: 4.0.0
resolution: "@penpot/svgo@https://github.com/penpot/svgo.git#commit=a46262c12c0d967708395972c374eb2adead4180"
resolution: "@penpot/svgo@https://github.com/penpot/svgo.git#commit=8c9b0e32e9cb5f106085260bd9375f3c91a5010b"
dependencies:
"@trysound/sax": "npm:0.2.0"
css-select: "npm:^5.1.0"
css-tree: "npm:^3.1.0"
csso: "npm:^5.0.5"
lodash: "npm:^4.17.21"
checksum: 10c0/db5f81c99dec2765721d73b69bb30594869ebf657380dfb46709c79775b6c0dc1af678fe9fe51bbe2272a2c78d19c2694a12ec6578bcc41235fa4aff475c9416
checksum: 10c0/d7af2801451b97f8ffb17664147c609456f5bcc786c6d03b222546125260c0f268e750748311d61598e31f66610b00038d2b969635b1a15e5694647e19c6b63a
languageName: node
linkType: hard
@@ -4311,7 +4311,7 @@ __metadata:
"@penpot/hljs": "portal:./vendor/hljs"
"@penpot/mousetrap": "portal:./vendor/mousetrap"
"@penpot/plugins-runtime": "npm:1.3.2"
"@penpot/svgo": "penpot/svgo#v3.1"
"@penpot/svgo": "penpot/svgo#v3.2"
"@penpot/text-editor": "portal:./text-editor"
"@playwright/test": "npm:1.52.0"
"@storybook/addon-docs": "npm:10.0.4"

View File

@@ -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

View File

@@ -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",

View File

@@ -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);
});

View File

@@ -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)))

View File

@@ -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"

View File

@@ -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();

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
"packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
@@ -15,6 +15,7 @@
"fmt": "./scripts/fmt"
},
"devDependencies": {
"@types/node": "^20.12.7"
"@types/node": "^20.12.7",
"esbuild": "^0.25.9"
}
}

View File

@@ -1,17 +1,23 @@
#!/usr/bin/env bash
export CURRENT_VERSION=${CURRENT_VERSION:-develop};
if [ "$NODE_ENV" = "production" ]; then
export _BUILD_MODE="release";
export BUILD_MODE="release";
else
export _BUILD_MODE=${1:-debug};
export BUILD_MODE=${1:-debug};
fi
export BUILD_NAME="${BUILD_NAME:-render-wasm}"
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"};
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"}
# 256 MB of initial heap to perform less
# initial calls to memory grow.
EM_INITIAL_HEAP=$((256 * 1024 * 1024))
export EM_INITIAL_HEAP=$((256 * 1024 * 1024))
# 1.0 doubles the heap on every growth.
EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8"
export EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8"
# Malloc implementation to use.
# - dlmalloc: a powerful general-purpose malloc.
@@ -21,38 +27,71 @@ EM_MEMORY_GROWTH_GEOMETRIC_STEP="0.8"
# - emmalloc-verbose: use emmalloc with assertions + verbose logging.
# - emmalloc-memvalidate-verbose: use emmalloc with assertions + heap consistency checking + verbose logging.
# Default: dlmalloc
EM_MALLOC="dlmalloc"
export EM_MALLOC="dlmalloc"
EMCC_CFLAGS="--no-entry \
export EMCC_CFLAGS="--no-entry \
--js-library src/js/wapi.js \
-sASSERTIONS=1 \
-sALLOW_TABLE_GROWTH=1 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_HEAP=$EM_INITIAL_HEAP \
-sMEMORY_GROWTH_GEOMETRIC_STEP=$EM_MEMORY_GROWTH_GEOMETRIC_STEP \
-sENVIRONMENT=web \
-sERROR_ON_UNDEFINED_SYMBOLS=0 \
-sMAX_WEBGL_VERSION=2 \
-sMODULARIZE=1 \
-sEXPORT_NAME=createRustSkiaModule \
-sEXPORTED_RUNTIME_METHODS=GL,stringToUTF8,HEAPU8,HEAP32,HEAPU32,HEAPF32 \
-sEXPORT_ES6=1"
-sENVIRONMENT=web \
-sMODULARIZE=1 \
-sEXPORT_ES6=1";
export EM_CACHE="/tmp/emsdk_cache";
_CARGO_PARAMS="${@:2}";
export CARGO_PARAMS="${@:2}";
if [ "$_BUILD_MODE" = "release" ]; then
_CARGO_PARAMS="--release $_CARGO_PARAMS"
EMCC_CFLAGS="-Os $EMCC_CFLAGS"
if [ "$BUILD_MODE" = "release" ]; then
export CARGO_PARAMS="--release $CARGO_PARAMS"
export EMCC_CFLAGS="-Os $EMCC_CFLAGS"
else
# TODO: Extra parameters that could be good to look into:
# -gseparate-dwarf
# -gsplit-dwarf
# -gsource-map
EMCC_CFLAGS="-g $EMCC_CFLAGS -sVERBOSE=1 -sMALLOC=$EM_MALLOC"
export EMCC_CFLAGS="-g $EMCC_CFLAGS -sVERBOSE=1 -sMALLOC=$EM_MALLOC"
fi
export EMCC_CFLAGS;
export _CARGO_PARAMS;
function clean {
cargo clean;
}
function setup {
corepack enable;
corepack install;
yarn install;
}
function build {
cargo build $CARGO_PARAMS;
}
function copy_artifacts {
DEST=$1;
mkdir -p $DEST;
cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js $DEST/$BUILD_NAME.js;
cp target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.wasm $DEST/$BUILD_NAME.wasm;
sed -i "s/render_wasm.wasm/$BUILD_NAME.wasm?version=$CURRENT_VERSION/g" $DEST/$BUILD_NAME.js;
yarn esbuild target/wasm32-unknown-emscripten/$BUILD_MODE/render_wasm.js \
--log-level=error \
--outfile=$DEST/worker/render.js \
--platform=neutral \
--format=iife \
--global-name=WasmModule;
}
function copy_shared_artifact {
SHARED_FILE=$(find target/wasm32-unknown-emscripten -name render_wasm_shared.js | head -n 1);
cp $SHARED_FILE ../frontend/src/app/render_wasm/api/shared.js;
}

View File

@@ -2,8 +2,6 @@
EMSDK_QUIET=1 . /opt/emsdk/emsdk_env.sh
set -x
_BUILD_NAME="${_BUILD_NAME:-render_wasm}"
_SCRIPT_DIR=$(dirname $0);
@@ -11,18 +9,13 @@ pushd $_SCRIPT_DIR;
. ./_build_env
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"};
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"}
set -ex;
cargo build $_CARGO_PARAMS
setup;
build;
copy_artifacts "../frontend/resources/public/js";
copy_shared_artifact;
_SHARED_FILE=$(find target/wasm32-unknown-emscripten -name render_wasm_shared.js | head -n 1);
cat target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.js "$_SHARED_FILE" > ../frontend/resources/public/js/$_BUILD_NAME.js
cp target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/$_BUILD_NAME.wasm
sed -i "s/render_wasm.wasm/$_BUILD_NAME.wasm?version=develop/g" ../frontend/resources/public/js/$_BUILD_NAME.js;
exit $?
exit $?;
popd

17
render-wasm/package.json Normal file
View File

@@ -0,0 +1,17 @@
{
"name": "penpot-render-wasm",
"version": "1.20.0",
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
},
"type": "module",
"devDependencies": {
"@types/node": "^20.12.7",
"esbuild": "^0.25.9"
}
}

View File

@@ -1,14 +1,14 @@
#!/usr/bin/env bash
set -x
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"}
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"x86_64-unknown-linux-gnu"};
_SCRIPT_DIR=$(dirname $0);
pushd $_SCRIPT_DIR;
. ./_build_env
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"}
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"x86_64-unknown-linux-gnu"};
cargo test --bin render_wasm -- --show-output
# Exit with the same status code as cargo test

View File

@@ -1,25 +1,21 @@
#!/usr/bin/env bash
set -x
_SCRIPT_DIR=$(dirname $0);
pushd $_SCRIPT_DIR;
. ./_build_env
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"};
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.87.0/skia-binaries-e551f334ad5cbdf43abf-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp.tar.gz"}
set -x
_SHARED_FILE=$(find target/wasm32-unknown-emscripten -name render_wasm_shared.js | head -n 1);
cat target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.js "$_SHARED_FILE" > ../frontend/resources/public/js/$_BUILD_NAME.js
cp target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/$_BUILD_NAME.wasm
build;
copy_artifacts "../frontend/resources/public/js";
copy_shared_artifact;
pushd $_SCRIPT_DIR;
cargo watch \
-x "build $_CARGO_PARAMS" \
-s "cat target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.js \"$_SHARED_FILE\" > ../frontend/resources/public/js/$_BUILD_NAME.js" \
-s "cp target/wasm32-unknown-emscripten/$_BUILD_MODE/render_wasm.wasm ../frontend/resources/public/js/" \
-s "sed -i 's/render_wasm.wasm/render_wasm.wasm?version=develop/g' ../frontend/resources/public/js/render_wasm.js" \
-x "build $CARGO_PARAMS" \
-s "./build" \
-s "echo 'DONE\n'";
popd

302
render-wasm/yarn.lock Normal file
View File

@@ -0,0 +1,302 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"@esbuild/aix-ppc64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/aix-ppc64@npm:0.25.12"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/android-arm64@npm:0.25.12"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/android-arm@npm:0.25.12"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/android-x64@npm:0.25.12"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/darwin-arm64@npm:0.25.12"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/darwin-x64@npm:0.25.12"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/freebsd-arm64@npm:0.25.12"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/freebsd-x64@npm:0.25.12"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-arm64@npm:0.25.12"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-arm@npm:0.25.12"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-ia32@npm:0.25.12"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-loong64@npm:0.25.12"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-mips64el@npm:0.25.12"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-ppc64@npm:0.25.12"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-riscv64@npm:0.25.12"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-s390x@npm:0.25.12"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-x64@npm:0.25.12"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/netbsd-arm64@npm:0.25.12"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/netbsd-x64@npm:0.25.12"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/openbsd-arm64@npm:0.25.12"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/openbsd-x64@npm:0.25.12"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openharmony-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/openharmony-arm64@npm:0.25.12"
conditions: os=openharmony & cpu=arm64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/sunos-x64@npm:0.25.12"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/win32-arm64@npm:0.25.12"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/win32-ia32@npm:0.25.12"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/win32-x64@npm:0.25.12"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@types/node@npm:^20.12.7":
version: 20.19.26
resolution: "@types/node@npm:20.19.26"
dependencies:
undici-types: "npm:~6.21.0"
checksum: 10c0/68e7d92dd2b7bddff9dffabb9c740e655906ceac428dcf070915cdcae720579e4d72261c55ed7eccbfa907a75cbb1ff3a9148ea49878a07a72d5dd6c9e06d9d7
languageName: node
linkType: hard
"esbuild@npm:^0.25.9":
version: 0.25.12
resolution: "esbuild@npm:0.25.12"
dependencies:
"@esbuild/aix-ppc64": "npm:0.25.12"
"@esbuild/android-arm": "npm:0.25.12"
"@esbuild/android-arm64": "npm:0.25.12"
"@esbuild/android-x64": "npm:0.25.12"
"@esbuild/darwin-arm64": "npm:0.25.12"
"@esbuild/darwin-x64": "npm:0.25.12"
"@esbuild/freebsd-arm64": "npm:0.25.12"
"@esbuild/freebsd-x64": "npm:0.25.12"
"@esbuild/linux-arm": "npm:0.25.12"
"@esbuild/linux-arm64": "npm:0.25.12"
"@esbuild/linux-ia32": "npm:0.25.12"
"@esbuild/linux-loong64": "npm:0.25.12"
"@esbuild/linux-mips64el": "npm:0.25.12"
"@esbuild/linux-ppc64": "npm:0.25.12"
"@esbuild/linux-riscv64": "npm:0.25.12"
"@esbuild/linux-s390x": "npm:0.25.12"
"@esbuild/linux-x64": "npm:0.25.12"
"@esbuild/netbsd-arm64": "npm:0.25.12"
"@esbuild/netbsd-x64": "npm:0.25.12"
"@esbuild/openbsd-arm64": "npm:0.25.12"
"@esbuild/openbsd-x64": "npm:0.25.12"
"@esbuild/openharmony-arm64": "npm:0.25.12"
"@esbuild/sunos-x64": "npm:0.25.12"
"@esbuild/win32-arm64": "npm:0.25.12"
"@esbuild/win32-ia32": "npm:0.25.12"
"@esbuild/win32-x64": "npm:0.25.12"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
"@esbuild/android-arm":
optional: true
"@esbuild/android-arm64":
optional: true
"@esbuild/android-x64":
optional: true
"@esbuild/darwin-arm64":
optional: true
"@esbuild/darwin-x64":
optional: true
"@esbuild/freebsd-arm64":
optional: true
"@esbuild/freebsd-x64":
optional: true
"@esbuild/linux-arm":
optional: true
"@esbuild/linux-arm64":
optional: true
"@esbuild/linux-ia32":
optional: true
"@esbuild/linux-loong64":
optional: true
"@esbuild/linux-mips64el":
optional: true
"@esbuild/linux-ppc64":
optional: true
"@esbuild/linux-riscv64":
optional: true
"@esbuild/linux-s390x":
optional: true
"@esbuild/linux-x64":
optional: true
"@esbuild/netbsd-arm64":
optional: true
"@esbuild/netbsd-x64":
optional: true
"@esbuild/openbsd-arm64":
optional: true
"@esbuild/openbsd-x64":
optional: true
"@esbuild/openharmony-arm64":
optional: true
"@esbuild/sunos-x64":
optional: true
"@esbuild/win32-arm64":
optional: true
"@esbuild/win32-ia32":
optional: true
"@esbuild/win32-x64":
optional: true
bin:
esbuild: bin/esbuild
checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b
languageName: node
linkType: hard
"penpot-render-wasm@workspace:.":
version: 0.0.0-use.local
resolution: "penpot-render-wasm@workspace:."
dependencies:
"@types/node": "npm:^20.12.7"
esbuild: "npm:^0.25.9"
languageName: unknown
linkType: soft
"undici-types@npm:~6.21.0":
version: 6.21.0
resolution: "undici-types@npm:6.21.0"
checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04
languageName: node
linkType: hard

272
yarn.lock
View File

@@ -5,6 +5,188 @@ __metadata:
version: 8
cacheKey: 10c0
"@esbuild/aix-ppc64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/aix-ppc64@npm:0.25.12"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/android-arm64@npm:0.25.12"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/android-arm@npm:0.25.12"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/android-x64@npm:0.25.12"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/darwin-arm64@npm:0.25.12"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/darwin-x64@npm:0.25.12"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/freebsd-arm64@npm:0.25.12"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/freebsd-x64@npm:0.25.12"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-arm64@npm:0.25.12"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-arm@npm:0.25.12"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-ia32@npm:0.25.12"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-loong64@npm:0.25.12"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-mips64el@npm:0.25.12"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-ppc64@npm:0.25.12"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-riscv64@npm:0.25.12"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-s390x@npm:0.25.12"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/linux-x64@npm:0.25.12"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/netbsd-arm64@npm:0.25.12"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/netbsd-x64@npm:0.25.12"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/openbsd-arm64@npm:0.25.12"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/openbsd-x64@npm:0.25.12"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openharmony-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/openharmony-arm64@npm:0.25.12"
conditions: os=openharmony & cpu=arm64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/sunos-x64@npm:0.25.12"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/win32-arm64@npm:0.25.12"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/win32-ia32@npm:0.25.12"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.25.12":
version: 0.25.12
resolution: "@esbuild/win32-x64@npm:0.25.12"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@types/node@npm:^20.12.7":
version: 20.12.12
resolution: "@types/node@npm:20.12.12"
@@ -14,11 +196,101 @@ __metadata:
languageName: node
linkType: hard
"esbuild@npm:^0.25.9":
version: 0.25.12
resolution: "esbuild@npm:0.25.12"
dependencies:
"@esbuild/aix-ppc64": "npm:0.25.12"
"@esbuild/android-arm": "npm:0.25.12"
"@esbuild/android-arm64": "npm:0.25.12"
"@esbuild/android-x64": "npm:0.25.12"
"@esbuild/darwin-arm64": "npm:0.25.12"
"@esbuild/darwin-x64": "npm:0.25.12"
"@esbuild/freebsd-arm64": "npm:0.25.12"
"@esbuild/freebsd-x64": "npm:0.25.12"
"@esbuild/linux-arm": "npm:0.25.12"
"@esbuild/linux-arm64": "npm:0.25.12"
"@esbuild/linux-ia32": "npm:0.25.12"
"@esbuild/linux-loong64": "npm:0.25.12"
"@esbuild/linux-mips64el": "npm:0.25.12"
"@esbuild/linux-ppc64": "npm:0.25.12"
"@esbuild/linux-riscv64": "npm:0.25.12"
"@esbuild/linux-s390x": "npm:0.25.12"
"@esbuild/linux-x64": "npm:0.25.12"
"@esbuild/netbsd-arm64": "npm:0.25.12"
"@esbuild/netbsd-x64": "npm:0.25.12"
"@esbuild/openbsd-arm64": "npm:0.25.12"
"@esbuild/openbsd-x64": "npm:0.25.12"
"@esbuild/openharmony-arm64": "npm:0.25.12"
"@esbuild/sunos-x64": "npm:0.25.12"
"@esbuild/win32-arm64": "npm:0.25.12"
"@esbuild/win32-ia32": "npm:0.25.12"
"@esbuild/win32-x64": "npm:0.25.12"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
"@esbuild/android-arm":
optional: true
"@esbuild/android-arm64":
optional: true
"@esbuild/android-x64":
optional: true
"@esbuild/darwin-arm64":
optional: true
"@esbuild/darwin-x64":
optional: true
"@esbuild/freebsd-arm64":
optional: true
"@esbuild/freebsd-x64":
optional: true
"@esbuild/linux-arm":
optional: true
"@esbuild/linux-arm64":
optional: true
"@esbuild/linux-ia32":
optional: true
"@esbuild/linux-loong64":
optional: true
"@esbuild/linux-mips64el":
optional: true
"@esbuild/linux-ppc64":
optional: true
"@esbuild/linux-riscv64":
optional: true
"@esbuild/linux-s390x":
optional: true
"@esbuild/linux-x64":
optional: true
"@esbuild/netbsd-arm64":
optional: true
"@esbuild/netbsd-x64":
optional: true
"@esbuild/openbsd-arm64":
optional: true
"@esbuild/openbsd-x64":
optional: true
"@esbuild/openharmony-arm64":
optional: true
"@esbuild/sunos-x64":
optional: true
"@esbuild/win32-arm64":
optional: true
"@esbuild/win32-ia32":
optional: true
"@esbuild/win32-x64":
optional: true
bin:
esbuild: bin/esbuild
checksum: 10c0/c205357531423220a9de8e1e6c6514242bc9b1666e762cd67ccdf8fdfdc3f1d0bd76f8d9383958b97ad4c953efdb7b6e8c1f9ca5951cd2b7c5235e8755b34a6b
languageName: node
linkType: hard
"penpot@workspace:.":
version: 0.0.0-use.local
resolution: "penpot@workspace:."
dependencies:
"@types/node": "npm:^20.12.7"
esbuild: "npm:^0.25.9"
languageName: unknown
linkType: soft