Compare commits

..

92 Commits

Author SHA1 Message Date
Andrey Antukh
69c880d00e 🐛 Fix importmap usage on firefox 2025-12-23 13:10:58 +01:00
Andrey Antukh
9eebc467ef Preload default translations 2025-12-23 13:10:58 +01:00
Andrey Antukh
b77712ce73 Move frontend/vendor to frontend/packages 2025-12-23 13:10:58 +01:00
Andrey Antukh
3d3e81f314 Replace tubax with more modern tooling 2025-12-23 13:10:58 +01:00
Andrey Antukh
fe6441bb24 Replace hightlight.js internal bundle with direct npm use 2025-12-23 13:10:58 +01:00
Andrey Antukh
e15f0baf30 Replace direct draft-js usage with internal module 2025-12-23 13:10:58 +01:00
Andrey Antukh
c040cbb784 🔥 Remove old gulp related dependencies 2025-12-23 13:10:58 +01:00
Andrey Antukh
7f674b78a9 📎 Move all deps to dev-dependencies on frontend package.json
All they only needed for build process.
2025-12-23 13:10:58 +01:00
Andrey Antukh
099b78affd 📎 Update frontend yarn.lock file 2025-12-23 13:10:58 +01:00
Andrey Antukh
78cc3f0aa4 📎 Add immutable dependency to vendor/draft-js 2025-12-23 13:10:58 +01:00
Andrey Antukh
76f5f12808 ⬆️ Update dependencies on exporter 2025-12-23 13:10:58 +01:00
Andrey Antukh
047483a70a 🐛 Fix deleted files thumbnails generation 2025-12-22 20:20:43 +01:00
Andrey Antukh
8cb2f27de8 ♻️ Move file permissions to binfile common ns 2025-12-22 20:16:41 +01:00
Andrey Antukh
0433336fc9 📎 Use correct criterium version on frontend deps 2025-12-22 20:16:41 +01:00
Andrey Antukh
ce234fbeda Allow get thumbnails for deleted files 2025-12-22 20:16:41 +01:00
Andrey Antukh
fc4d31eed7 Add minor efficiency improvements to deleted dashboard page 2025-12-22 20:16:41 +01:00
María Valderrama
c670aac339 🎉 Added deleted files to dashboard 2025-12-22 20:16:41 +01:00
Andrés Moya
1d3fb5434f Enable shadow tokens by default (#7996) 2025-12-22 18:17:29 +01:00
Andrey Antukh
f478399ae0 Merge remote-tracking branch 'origin/staging-render' into develop 2025-12-22 17:28:18 +01:00
Andrey Antukh
6a1854f180 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-22 17:28:01 +01:00
Andrés Moya
0858e297e5 🎉 Add composite tokens to plugins API (#7992) 2025-12-22 17:14:54 +01:00
Alejandro Alonso
5780a43fe0 🐛 Fix object added in different page (#7988) 2025-12-22 16:59:47 +01:00
Alejandro Alonso
737eceda3a 🐛 Fix unmasking shapes (#7989) 2025-12-22 16:59:04 +01:00
Alonso Torres
923c3c2dbd 🐛 Fix font weight token (#7991) 2025-12-22 16:58:26 +01:00
Alejandro Alonso
a14b4561e7 🐛 Fix comment bubbles (#7990) 2025-12-22 16:57:45 +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
Anonymous
105e1fe86c 🌐 Add translations for: Spanish
Currently translated at 97.1% (1940 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-12-22 16:34:43 +01:00
Yaron Shahrabani
3e0a916883 🌐 Add translations for: Hebrew
Currently translated at 99.3% (1985 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-12-22 16:34:42 +01:00
Ahmad HosseinBor
4f80238bc2 🌐 Add translations for: Persian
Currently translated at 39.2% (783 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fa/
2025-12-22 16:34:42 +01:00
Alejandro Alonso
5156cc5d9a 🌐 Add translations for: Yoruba
Currently translated at 58.8% (1176 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-12-22 16:34:42 +01:00
Yessenia Villarte Vaca
42c46b6cfc 🌐 Add translations for: Spanish (Latin America)
Currently translated at 6.5% (131 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es_419/
2025-12-22 16:34:41 +01:00
VKing9
8b3c40b35e 🌐 Add translations for: Hindi
Currently translated at 99.6% (1991 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-12-22 16:34:41 +01:00
Andy Li
d3996e5fb1 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 80.1% (1601 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-12-22 16:34:40 +01:00
Anonymous
0c42bca866 🌐 Add translations for: German
Currently translated at 95.5% (1908 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-22 16:34:40 +01:00
Marius
e5685c1f1c 🌐 Add translations for: German
Currently translated at 95.5% (1908 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-22 16:34:40 +01:00
Anonymous
2784209bde 🌐 Add translations for: Turkish
Currently translated at 99.5% (1989 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-12-22 16:34:40 +01:00
Alejandro Alonso
024f460e99 🌐 Add translations for: Igbo
Currently translated at 25.6% (512 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ig/
2025-12-22 16:34:39 +01:00
Anonymous
1d9b76b62a 🌐 Add translations for: Romanian
Currently translated at 97.2% (1942 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-12-22 16:34:39 +01:00
Shuaib Zahda
7e17a75b7d 🌐 Add translations for: Arabic
Currently translated at 56.4% (1127 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ar/
2025-12-22 16:34:39 +01:00
Anonymous
ca093d6fae 🌐 Add translations for: French
Currently translated at 96.8% (1934 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-22 16:34:38 +01:00
Alexandre Pawlak
0f0b7562b5 🌐 Add translations for: French
Currently translated at 96.8% (1934 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-22 16:34:38 +01:00
Anonymous
9cdc694697 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 90.3% (1804 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-12-22 16:34:37 +01:00
Dário
b972a4033b 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 90.3% (1804 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-12-22 16:34:37 +01:00
Anonymous
cbe9f4da51 🌐 Add translations for: Swedish
Currently translated at 99.3% (1985 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-12-22 16:34:37 +01:00
Anonymous
c583bde9e3 🌐 Add translations for: Russian
Currently translated at 76.8% (1534 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-12-22 16:34:37 +01:00
Vint Prox
3911ebdc4e 🌐 Add translations for: Russian
Currently translated at 76.8% (1534 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-12-22 16:34:36 +01:00
Anonymous
3e3b18667b 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 69.9% (1396 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-12-22 16:34:36 +01:00
Anonymous
ed81c9b8df 🌐 Add translations for: Dutch
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-22 16:34:36 +01:00
Sebastiaan Pasma
fbdf98d29c 🌐 Add translations for: Dutch
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-22 16:34:36 +01:00
Anonymous
e603825a55 🌐 Add translations for: Italian
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-22 16:34:35 +01:00
Valentina Chapellu
1d724783e6 🌐 Add translations for: Italian
Currently translated at 99.5% (1988 of 1997 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-22 16:34:35 +01:00
Hosted Weblate
e0abe7dcb5 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-12-22 16:33:05 +01:00
Andrey Antukh
5c1bbf5be8 Merge remote-tracking branch 'weblate/develop' into develop 2025-12-22 16:32:30 +01:00
Pablo Alba
bbb0d58190 🐛 Fix "maximum call stack size exceeded" crash on variant 2025-12-22 16:27:10 +01:00
Andrey Antukh
88dcf9d1fe 🐛 Mark rpc calls as authenticated when shared key is used (#7901) 2025-12-22 12:18:36 +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
Belén Albeza
20061067ad 🐛 Fix text editor not getting focus back after font variant change 2025-12-22 11:18:25 +01:00
Andrey Antukh
336173645e 🐛 Fix regression on export shape on plungins API 2025-12-22 10:41:42 +01:00
Andrey Antukh
2acf15958b Merge branch 'staging-render' into develop 2025-12-22 09:24:04 +01:00
Andrey Antukh
08267de242 Merge remote-tracking branch 'origin/staging' into staging-render 2025-12-22 09:23:48 +01:00
Pablo Alba
35fb376a78 Add proxypass to caddyfile on devenv (#7985) 2025-12-22 09:21:22 +01:00
Dalai Felinto
13fcf3a9bb 💄 Set import Tokens default option to be Single JSON value (#7918)
This patches makes the default Tokens importing option to match the
current default Tokens exporting option (single JSON value). This way it
is more obvious and quick to export the tokens from a file and import
in new one,

---

While testing our design system we are often re-exporting and
re-importing the Tokens to the files using the design system components.

I'm aware that this may be addressed in the future so the Tokens are
brought in together with the library. Meanwhile (and even in the future)
I think it is sensible to have a symmetry between the export and import
defeault options.

Co-authored-by: Dalai Felinto <dalai@blender.org>
2025-12-19 10:44:05 +01:00
Andrey Antukh
83bb4bf221 🐛 Prefill storage object bucket if it comes nil on import binfile 2025-12-19 09:32:51 +01:00
Henrik Steffens
dba6ae2820 🌐 Add translations for: German
Currently translated at 95.8% (1911 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-17 13:00:25 +01:00
Marius
ada101c236 🌐 Add translations for: German
Currently translated at 95.8% (1911 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-17 13:00:24 +01:00
Marius
ea48fb5825 🌐 Add translations for: German
Currently translated at 90.4% (1804 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-12-16 11:00:25 +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
alonso.torres
8fde6b28ed 🐛 Fix problems with alignments and margins 2025-12-12 13:21:04 +01:00
alonso.torres
63325ec796 🐛 Fix problem with flex fill size distribution 2025-12-12 13:21:04 +01:00
alonso.torres
84415476d0 🐛 Fix problem with reflow layout 2025-12-12 13:21:04 +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
Stephan Paternotte
b45bdd723f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-12-09 22:00:21 +00:00
Ingrid Pigueron
8696044620 🌐 Add translations for: French
Currently translated at 97.1% (1937 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-09 22:00:19 +00:00
Ingrid Pigueron
4f3ca6422c 🌐 Add translations for: French
Currently translated at 96.8% (1931 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-12-08 19:40:14 +01:00
Nicola Bortoletto
1c03457fda 🌐 Add translations for: Italian
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-12-06 07:00:19 +00:00
VKing9
74d4b9b045 🌐 Add translations for: Hindi
Currently translated at 100.0% (1994 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hi/
2025-12-04 11:00:32 +01:00
Yaron Shahrabani
60df56caa3 🌐 Add translations for: Hebrew
Currently translated at 99.6% (1988 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-12-02 21:00:32 +00:00
Nicola Bortoletto
34da754357 🌐 Add translations for: Italian
Currently translated at 98.9% (1973 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-11-28 05:00:28 +00:00
Oğuz Ersen
39eafae251 🌐 Add translations for: Turkish
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2025-11-25 16:51:25 +00:00
Edgars Andersons
e1e09b7f96 🌐 Add translations for: Latvian
Currently translated at 94.0% (1876 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-11-25 16:51:24 +00:00
Stephan Paternotte
3b39980f2f 🌐 Add translations for: Dutch
Currently translated at 99.8% (1991 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-11-25 16:51:23 +00:00
Anton Palmqvist
223b12d2c7 🌐 Add translations for: Swedish
Currently translated at 99.6% (1987 of 1994 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-11-25 16:51:21 +00:00
115 changed files with 4984 additions and 2730 deletions

View File

@@ -12,7 +12,9 @@
### :sparkles: New features & Enhancements
- Add new Box Shadow Tokens [Taiga #10201](https://tree.taiga.io/project/penpot/us/10201)
- Make i18n translation files load on-demand [Taiga #11474](https://tree.taiga.io/project/penpot/us/11474)
- Add deleted files to dashboard [Taiga #8149](https://tree.taiga.io/project/penpot/us/8149)
### :bug: Bugs fixed
@@ -83,6 +85,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
@@ -116,6 +119,7 @@ example. It's still usable as before, we just removed the example.
- Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841)
- Fix referencing typography tokens on font-family tokens [Taiga #12492](https://tree.taiga.io/project/penpot/issue/12492)
- Fix horizontal scroll on layer panel [Taiga #12843](https://tree.taiga.io/project/penpot/issue/12843)
- Fix unicode handling on email template abbreviation filter [Github #7966](https://github.com/penpot/penpot/pull/7966)
## 2.11.1

View File

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

View File

@@ -331,6 +331,81 @@
(set/difference cfeat/backend-only-features))
#{}))))
(defn check-file-exists
[cfg id & {:keys [include-deleted?]
:or {include-deleted? false}
:as options}]
(db/get-with-sql cfg [sql:get-minimal-file id]
{:db/remove-deleted (not include-deleted?)}))
(def ^:private sql:file-permissions
"select fpr.is_owner,
fpr.is_admin,
fpr.can_edit
from file_profile_rel as fpr
inner join file as f on (f.id = fpr.file_id)
where fpr.file_id = ?
and fpr.profile_id = ?
union all
select tpr.is_owner,
tpr.is_admin,
tpr.can_edit
from team_profile_rel as tpr
inner join project as p on (p.team_id = tpr.team_id)
inner join file as f on (p.id = f.project_id)
where f.id = ?
and tpr.profile_id = ?
union all
select ppr.is_owner,
ppr.is_admin,
ppr.can_edit
from project_profile_rel as ppr
inner join file as f on (f.project_id = ppr.project_id)
where f.id = ?
and ppr.profile_id = ?")
(defn- get-file-permissions*
[conn profile-id file-id]
(when (and profile-id file-id)
(db/exec! conn [sql:file-permissions
file-id profile-id
file-id profile-id
file-id profile-id])))
(defn get-file-permissions
([conn profile-id file-id]
(let [rows (get-file-permissions* conn profile-id file-id)
is-owner (boolean (some :is-owner rows))
is-admin (boolean (some :is-admin rows))
can-edit (boolean (some :can-edit rows))]
(when (seq rows)
{:type :membership
:is-owner is-owner
:is-admin (or is-owner is-admin)
:can-edit (or is-owner is-admin can-edit)
:can-read true
:is-logged (some? profile-id)})))
([conn profile-id file-id share-id]
(let [perms (get-file-permissions conn profile-id file-id)
ldata (some-> (db/get* conn :share-link {:id share-id :file-id file-id})
(dissoc :flags)
(update :pages db/decode-pgarray #{}))]
;; NOTE: in a future when share-link becomes more powerful and
;; will allow us specify which parts of the app is available, we
;; will probably need to tweak this function in order to expose
;; this flags to the frontend.
(cond
(some? perms) perms
(some? ldata) {:type :share-link
:can-read true
:pages (:pages ldata)
:is-logged (some? profile-id)
:who-comment (:who-comment ldata)
:who-inspect (:who-inspect ldata)}))))
(defn get-project
[cfg project-id]
(db/get cfg :project {:id project-id}))

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

@@ -30,7 +30,7 @@
(defn- get-file-media-object
[pool id]
(db/get pool :file-media-object {:id id}))
(db/get pool :file-media-object {:id id} {::db/remove-deleted false}))
(defn- serve-object-from-s3
[{:keys [::sto/storage] :as cfg} obj]

View File

@@ -309,7 +309,7 @@
(fn [request]
(let [key (yreq/get-header request "x-shared-key")]
(if (= key shared-key)
(handler request)
(handler (assoc request ::http/auth-with-shared-key true))
{::yres/status 403}))))
(fn [_ _]
{::yres/status 403})))

View File

@@ -14,6 +14,7 @@
[app.common.spec :as us]
[app.common.time :as ct]
[app.common.uri :as u]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.http :as-alias http]
@@ -92,7 +93,11 @@
(let [handler-name (:type path-params)
etag (yreq/get-header request "if-none-match")
profile-id (or (::session/profile-id request)
(::actoken/profile-id request))
(::actoken/profile-id request)
(if (::http/auth-with-shared-key request)
uuid/zero
nil))
ip-addr (inet/parse-request request)
data (-> params

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

@@ -79,85 +79,14 @@
;; --- FILE PERMISSIONS
(def ^:private sql:file-permissions
"select fpr.is_owner,
fpr.is_admin,
fpr.can_edit
from file_profile_rel as fpr
inner join file as f on (f.id = fpr.file_id)
where fpr.file_id = ?
and fpr.profile_id = ?
and f.deleted_at is null
union all
select tpr.is_owner,
tpr.is_admin,
tpr.can_edit
from team_profile_rel as tpr
inner join project as p on (p.team_id = tpr.team_id)
inner join file as f on (p.id = f.project_id)
where f.id = ?
and tpr.profile_id = ?
and f.deleted_at is null
union all
select ppr.is_owner,
ppr.is_admin,
ppr.can_edit
from project_profile_rel as ppr
inner join file as f on (f.project_id = ppr.project_id)
where f.id = ?
and ppr.profile_id = ?
and f.deleted_at is null")
(defn get-file-permissions
[conn profile-id file-id]
(when (and profile-id file-id)
(db/exec! conn [sql:file-permissions
file-id profile-id
file-id profile-id
file-id profile-id])))
(defn get-permissions
([conn profile-id file-id]
(let [rows (get-file-permissions conn profile-id file-id)
is-owner (boolean (some :is-owner rows))
is-admin (boolean (some :is-admin rows))
can-edit (boolean (some :can-edit rows))]
(when (seq rows)
{:type :membership
:is-owner is-owner
:is-admin (or is-owner is-admin)
:can-edit (or is-owner is-admin can-edit)
:can-read true
:is-logged (some? profile-id)})))
([conn profile-id file-id share-id]
(let [perms (get-permissions conn profile-id file-id)
ldata (some-> (db/get* conn :share-link {:id share-id :file-id file-id})
(dissoc :flags)
(update :pages db/decode-pgarray #{}))]
;; NOTE: in a future when share-link becomes more powerful and
;; will allow us specify which parts of the app is available, we
;; will probably need to tweak this function in order to expose
;; this flags to the frontend.
(cond
(some? perms) perms
(some? ldata) {:type :share-link
:can-read true
:pages (:pages ldata)
:is-logged (some? profile-id)
:who-comment (:who-comment ldata)
:who-inspect (:who-inspect ldata)}))))
(def has-edit-permissions?
(perms/make-edition-predicate-fn get-permissions))
(perms/make-edition-predicate-fn bfc/get-file-permissions))
(def has-read-permissions?
(perms/make-read-predicate-fn get-permissions))
(perms/make-read-predicate-fn bfc/get-file-permissions))
(def has-comment-permissions?
(perms/make-comment-predicate-fn get-permissions))
(perms/make-comment-predicate-fn bfc/get-file-permissions))
(def check-edition-permissions!
(perms/make-check-fn has-edit-permissions?))
@@ -170,7 +99,7 @@
(defn check-comment-permissions!
[conn profile-id file-id share-id]
(let [perms (get-permissions conn profile-id file-id share-id)
(let [perms (bfc/get-file-permissions conn profile-id file-id share-id)
can-read (has-read-permissions? perms)
can-comment (has-comment-permissions? perms)]
(when-not (or can-read can-comment)
@@ -222,7 +151,7 @@
(defn- get-minimal-file-with-perms
[cfg {:keys [:id ::rpc/profile-id]}]
(let [mfile (get-minimal-file cfg id)
perms (get-permissions cfg profile-id id)]
perms (bfc/get-file-permissions cfg profile-id id)]
(assoc mfile :permissions perms)))
(defn get-file-etag
@@ -248,7 +177,7 @@
;; will be already prefetched and we just reuse them instead
;; of making an additional database queries.
(let [perms (or (:permissions (::cond/object params))
(get-permissions conn profile-id id))]
(bfc/get-file-permissions conn profile-id id))]
(check-read-permissions! perms)
(let [team (teams/get-team conn
@@ -311,7 +240,7 @@
::sm/result schema:file-fragment}
[cfg {:keys [::rpc/profile-id file-id fragment-id share-id]}]
(db/run! cfg (fn [cfg]
(let [perms (get-permissions cfg profile-id file-id share-id)]
(let [perms (bfc/get-file-permissions cfg profile-id file-id share-id)]
(check-read-permissions! perms)
(-> (get-file-fragment cfg file-id fragment-id)
(rph/with-http-cache long-cache-duration))))))
@@ -456,8 +385,7 @@
:code :params-validation
:hint "page-id is required when object-id is provided"))
(let [perms (get-permissions conn profile-id file-id share-id)
(let [perms (bfc/get-file-permissions conn profile-id file-id share-id)
file (bfc/get-file cfg file-id :read-only? true)
proj (db/get conn :project {:id (:project-id file)})
@@ -688,11 +616,10 @@
"Get libraries used by the specified file."
{::doc/added "1.17"
::sm/params schema:get-file-libraries}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id]}]
(dm/with-open [conn (db/open pool)]
(check-read-permissions! conn profile-id file-id)
(bfc/get-file-libraries conn file-id)))
[cfg {:keys [::rpc/profile-id file-id]}]
(bfc/check-file-exists cfg file-id)
(check-read-permissions! cfg profile-id file-id)
(bfc/get-file-libraries cfg file-id))
;; --- COMMAND QUERY: Files that use this File library
@@ -785,8 +712,7 @@
FROM file AS f
INNER JOIN project AS p ON (p.id = f.project_id)
LEFT JOIN file_thumbnail AS ft on (ft.file_id = f.id
AND ft.revn = f.revn
AND ft.deleted_at is null)
AND ft.revn = f.revn)
WHERE p.team_id = ?
AND (p.deleted_at > ?::timestamptz OR
f.deleted_at > ?::timestamptz)

View File

@@ -199,15 +199,13 @@
[cfg {:keys [::rpc/profile-id file-id strip-frames-with-thumbnails] :as params}]
(db/run! cfg (fn [{:keys [::db/conn] :as cfg}]
(files/check-read-permissions! conn profile-id file-id)
(let [team (teams/get-team conn
:profile-id profile-id
:file-id file-id)
file (bfc/get-file cfg file-id
:include-deleted? true
:realize? true
:read-only? true)
strip-frames-with-thumbnails
(or (nil? strip-frames-with-thumbnails) ;; if not present, default to true
(true? strip-frames-with-thumbnails))]
@@ -333,12 +331,16 @@
;; --- MUTATION COMMAND: create-file-thumbnail
(defn- create-file-thumbnail!
[{:keys [::db/conn ::sto/storage]} {:keys [file-id revn props media] :as params}]
(defn- create-file-thumbnail
[{:keys [::db/conn ::sto/storage] :as cfg} {:keys [file-id revn props media] :as params}]
(media/validate-media-type! media)
(media/validate-media-size! media)
(let [props (db/tjson (or props {}))
(let [file (bfc/get-file cfg file-id
:include-deleted? true
:load-data? false)
props (db/tjson (or props {}))
path (:path media)
mtype (:mtype media)
hash (sto/calculate-hash path)
@@ -367,7 +369,7 @@
(db/update! conn :file-thumbnail
{:media-id (:id media)
:deleted-at nil
:deleted-at (:deleted-at file)
:updated-at tnow
:props props}
{:file-id file-id
@@ -378,6 +380,7 @@
:revn revn
:created-at tnow
:updated-at tnow
:deleted-at (:deleted-at file)
:props props
:media-id (:id media)}))
@@ -402,6 +405,8 @@
::rtry/when rtry/conflict-exception?
::sm/params schema:create-file-thumbnail}
;; FIXME: do not run the thumbnail upload inside a transaction
[cfg {:keys [::rpc/profile-id file-id] :as params}]
(db/tx-run! cfg (fn [{:keys [::db/conn] :as cfg}]
;; TODO For now we check read permissions instead of write,
@@ -409,6 +414,6 @@
;; review this approach on the future.
(files/check-read-permissions! conn profile-id file-id)
(when-not (db/read-only? conn)
(let [media (create-file-thumbnail! cfg params)]
(let [media (create-file-thumbnail cfg params)]
{:uri (files/resolve-public-uri (:id media))
:id (:id media)})))))

View File

@@ -6,6 +6,7 @@
(ns app.rpc.commands.fonts
(:require
[app.binfile.common :as bfc]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
@@ -66,7 +67,7 @@
(uuid? file-id)
(let [file (db/get-by-id conn :file file-id {:columns [:id :project-id]})
project (db/get-by-id conn :project (:project-id file) {:columns [:id :team-id]})
perms (files/get-permissions conn profile-id file-id share-id)]
perms (bfc/get-file-permissions conn profile-id file-id share-id)]
(files/check-read-permissions! perms)
(db/query conn :team-font-variant
{:team-id (:team-id project)

View File

@@ -13,7 +13,6 @@
[app.config :as cf]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.commands.files :as files]
[app.rpc.commands.teams :as teams]
[app.rpc.cond :as-alias cond]
[app.rpc.doc :as-alias doc]
@@ -121,7 +120,7 @@
[system {:keys [::rpc/profile-id file-id share-id] :as params}]
(db/run! system
(fn [{:keys [::db/conn] :as system}]
(let [perms (files/get-permissions conn profile-id file-id share-id)
(let [perms (bfc/get-file-permissions conn profile-id file-id share-id)
params (-> params
(assoc ::perms perms)
(assoc :profile-id profile-id))]

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

@@ -29,8 +29,7 @@
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
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

@@ -169,6 +169,7 @@
:enable-component-thumbnails
:enable-render-wasm-dpr
:enable-token-color
:enable-token-shadow
:enable-inspect-styles
:enable-feature-fdata-objects-map])

View File

@@ -340,7 +340,7 @@
(dfn-diff t2 t1)))
#?(:cljs
(defn set-default-locale!
(defn set-default-locale
[locale]
(when-let [locale (unchecked-get locales locale)]
(dfn-set-default-options #js {:locale locale}))))

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

@@ -59,6 +59,7 @@
:dimensions "dimension"
:font-family "fontFamilies"
:font-size "fontSizes"
:font-weight "fontWeights"
:letter-spacing "letterSpacing"
:number "number"
:opacity "opacity"
@@ -70,7 +71,6 @@
:stroke-width "borderWidth"
:text-case "textCase"
:text-decoration "textDecoration"
:font-weight "fontWeights"
:typography "typography"})
(def dtcg-token-type->token-type

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 [_]
@@ -1545,7 +1545,7 @@ Will return a value that matches this schema:
(and (not (contains? decoded-json "$metadata"))
(not (contains? decoded-json "$themes"))))
(defn- convert-dtcg-font-family
(defn convert-dtcg-font-family
"Convert font-family token value from DTCG format to internal format.
- If value is a string, split it into a collection of font families
- If value is already an array, keep it as is
@@ -1556,7 +1556,7 @@ Will return a value that matches this schema:
(sequential? value) value
:else value))
(defn- convert-dtcg-typography-composite
(defn convert-dtcg-typography-composite
"Convert typography token value keys from DTCG format to internal format."
[value]
(if (map? value)
@@ -1568,7 +1568,7 @@ Will return a value that matches this schema:
;; Reference value
value))
(defn- convert-dtcg-shadow-composite
(defn convert-dtcg-shadow-composite
"Convert shadow token value from DTCG format to internal format."
[value]
(let [process-shadow (fn [shadow]

View File

@@ -10,3 +10,7 @@ localhost:3449 {
http://localhost:3450 {
reverse_proxy localhost:4449
}
http://penpot-devenv-main:3450 {
reverse_proxy localhost:4449
}

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
"packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
@@ -16,9 +16,9 @@
"date-fns": "^4.1.0",
"generic-pool": "^3.9.0",
"inflation": "^2.1.0",
"ioredis": "^5.8.1",
"playwright": "^1.55.1",
"raw-body": "^3.0.1",
"ioredis": "^5.8.2",
"playwright": "^1.57.0",
"raw-body": "^3.0.2",
"source-map-support": "^0.5.21",
"svgo": "penpot/svgo#v3.1",
"undici": "^7.16.0",

View File

@@ -243,7 +243,7 @@ __metadata:
languageName: node
linkType: hard
"bytes@npm:3.1.2":
"bytes@npm:~3.1.2":
version: 3.1.2
resolution: "bytes@npm:3.1.2"
checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e
@@ -442,7 +442,7 @@ __metadata:
languageName: node
linkType: hard
"depd@npm:2.0.0, depd@npm:~2.0.0":
"depd@npm:~2.0.0":
version: 2.0.0
resolution: "depd@npm:2.0.0"
checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c
@@ -577,9 +577,9 @@ __metadata:
date-fns: "npm:^4.1.0"
generic-pool: "npm:^3.9.0"
inflation: "npm:^2.1.0"
ioredis: "npm:^5.8.1"
playwright: "npm:^1.55.1"
raw-body: "npm:^3.0.1"
ioredis: "npm:^5.8.2"
playwright: "npm:^1.57.0"
raw-body: "npm:^3.0.2"
source-map-support: "npm:^0.5.21"
svgo: "penpot/svgo#v3.1"
undici: "npm:^7.16.0"
@@ -683,16 +683,16 @@ __metadata:
languageName: node
linkType: hard
"http-errors@npm:2.0.0":
version: 2.0.0
resolution: "http-errors@npm:2.0.0"
"http-errors@npm:~2.0.1":
version: 2.0.1
resolution: "http-errors@npm:2.0.1"
dependencies:
depd: "npm:2.0.0"
inherits: "npm:2.0.4"
setprototypeof: "npm:1.2.0"
statuses: "npm:2.0.1"
toidentifier: "npm:1.0.1"
checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19
depd: "npm:~2.0.0"
inherits: "npm:~2.0.4"
setprototypeof: "npm:~1.2.0"
statuses: "npm:~2.0.2"
toidentifier: "npm:~1.0.1"
checksum: 10c0/fb38906cef4f5c83952d97661fe14dc156cb59fe54812a42cd448fa57b5c5dfcb38a40a916957737bd6b87aab257c0648d63eb5b6a9ca9f548e105b6072712d4
languageName: node
linkType: hard
@@ -716,15 +716,6 @@ __metadata:
languageName: node
linkType: hard
"iconv-lite@npm:0.7.0":
version: 0.7.0
resolution: "iconv-lite@npm:0.7.0"
dependencies:
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
checksum: 10c0/2382400469071c55b6746c531eed5fa4d033e5db6690b7331fb2a5f59a30d7a9782932e92253db26df33c1cf46fa200a3fbe524a2a7c62037c762283f188ec2f
languageName: node
linkType: hard
"iconv-lite@npm:^0.6.2":
version: 0.6.3
resolution: "iconv-lite@npm:0.6.3"
@@ -734,6 +725,15 @@ __metadata:
languageName: node
linkType: hard
"iconv-lite@npm:~0.7.0":
version: 0.7.0
resolution: "iconv-lite@npm:0.7.0"
dependencies:
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
checksum: 10c0/2382400469071c55b6746c531eed5fa4d033e5db6690b7331fb2a5f59a30d7a9782932e92253db26df33c1cf46fa200a3fbe524a2a7c62037c762283f188ec2f
languageName: node
linkType: hard
"ieee754@npm:^1.2.1":
version: 1.2.1
resolution: "ieee754@npm:1.2.1"
@@ -755,16 +755,16 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2.0.4, inherits@npm:~2.0.3":
"inherits@npm:~2.0.3, inherits@npm:~2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
languageName: node
linkType: hard
"ioredis@npm:^5.8.1":
version: 5.8.1
resolution: "ioredis@npm:5.8.1"
"ioredis@npm:^5.8.2":
version: 5.8.2
resolution: "ioredis@npm:5.8.2"
dependencies:
"@ioredis/commands": "npm:1.4.0"
cluster-key-slot: "npm:^1.1.0"
@@ -775,7 +775,7 @@ __metadata:
redis-errors: "npm:^1.2.0"
redis-parser: "npm:^3.0.0"
standard-as-callback: "npm:^2.1.0"
checksum: 10c0/4ed66444017150da027bce940a24bf726994691e2a7b3aa11d52f8aeb37f258068cc171af4d9c61247acafc28eb086fa8a7c79420b8e8d2907d2f74f39584465
checksum: 10c0/305e385f811d49908899e32c2de69616cd059f909afd9e0a53e54f596b1a5835ee3449bfc6a3c49afbc5a2fd27990059e316cc78f449c94024957bd34c826d88
languageName: node
linkType: hard
@@ -1106,27 +1106,27 @@ __metadata:
languageName: node
linkType: hard
"playwright-core@npm:1.55.1":
version: 1.55.1
resolution: "playwright-core@npm:1.55.1"
"playwright-core@npm:1.57.0":
version: 1.57.0
resolution: "playwright-core@npm:1.57.0"
bin:
playwright-core: cli.js
checksum: 10c0/39837a8c1232ec27486eac8c3fcacc0b090acc64310f7f9004b06715370fc426f944e3610fe8c29f17cd3d68280ed72c75f660c02aa5b5cf0eb34bab0031308f
checksum: 10c0/798e35d83bf48419a8c73de20bb94d68be5dde68de23f95d80a0ebe401e3b83e29e3e84aea7894d67fa6c79d2d3d40cc5bcde3e166f657ce50987aaa2421b6a9
languageName: node
linkType: hard
"playwright@npm:^1.55.1":
version: 1.55.1
resolution: "playwright@npm:1.55.1"
"playwright@npm:^1.57.0":
version: 1.57.0
resolution: "playwright@npm:1.57.0"
dependencies:
fsevents: "npm:2.3.2"
playwright-core: "npm:1.55.1"
playwright-core: "npm:1.57.0"
dependenciesMeta:
fsevents:
optional: true
bin:
playwright: cli.js
checksum: 10c0/b84a97b0d764403df512f5bbb10c7343974e151a28202cc06f90883a13e8a45f4491a0597f0ae5fb03a026746cbc0d200f0f32195bfaa381aee5ca5770626771
checksum: 10c0/ab03c99a67b835bdea9059f516ad3b6e42c21025f9adaa161a4ef6bc7ca716dcba476d287140bb240d06126eb23f889a8933b8f5f1f1a56b80659d92d1358899
languageName: node
linkType: hard
@@ -1161,15 +1161,15 @@ __metadata:
languageName: node
linkType: hard
"raw-body@npm:^3.0.1":
version: 3.0.1
resolution: "raw-body@npm:3.0.1"
"raw-body@npm:^3.0.2":
version: 3.0.2
resolution: "raw-body@npm:3.0.2"
dependencies:
bytes: "npm:3.1.2"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.7.0"
unpipe: "npm:1.0.0"
checksum: 10c0/892f4fbd21ecab7e2fed0f045f7af9e16df7e8050879639d4e482784a2f4640aaaa33d916a0e98013f23acb82e09c2e3c57f84ab97104449f728d22f65a7d79a
bytes: "npm:~3.1.2"
http-errors: "npm:~2.0.1"
iconv-lite: "npm:~0.7.0"
unpipe: "npm:~1.0.0"
checksum: 10c0/d266678d08e1e7abea62c0ce5864344e980fa81c64f6b481e9842c5beaed2cdcf975f658a3ccd67ad35fc919c1f6664ccc106067801850286a6cbe101de89f29
languageName: node
linkType: hard
@@ -1270,7 +1270,7 @@ __metadata:
languageName: node
linkType: hard
"setprototypeof@npm:1.2.0":
"setprototypeof@npm:~1.2.0":
version: 1.2.0
resolution: "setprototypeof@npm:1.2.0"
checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc
@@ -1368,10 +1368,10 @@ __metadata:
languageName: node
linkType: hard
"statuses@npm:2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0
"statuses@npm:~2.0.2":
version: 2.0.2
resolution: "statuses@npm:2.0.2"
checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f
languageName: node
linkType: hard
@@ -1500,7 +1500,7 @@ __metadata:
languageName: node
linkType: hard
"toidentifier@npm:1.0.1":
"toidentifier@npm:~1.0.1":
version: 1.0.1
resolution: "toidentifier@npm:1.0.1"
checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1
@@ -1539,7 +1539,7 @@ __metadata:
languageName: node
linkType: hard
"unpipe@npm:1.0.0":
"unpipe@npm:~1.0.0":
version: 1.0.0
resolution: "unpipe@npm:1.0.0"
checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c

View File

@@ -8,6 +8,11 @@
metosin/reitit-core {:mvn/version "0.9.1"}
funcool/okulary {:mvn/version "2022.04.11-16"}
funcool/tubax
{:git/tag "v2025.11.28"
:git/sha "2d9a986"
:git/url "https://github.com/funcool/tubax.git"}
funcool/potok2
{:git/tag "v2.2"
:git/sha "0f7e15a"
@@ -45,7 +50,7 @@
{thheller/shadow-cljs {:mvn/version "3.2.2"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
org.clojure/tools.namespace {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "0.4.6"}
cider/cider-nrepl {:mvn/version "0.57.0"}}}
:shadow-cljs

View File

@@ -53,83 +53,74 @@
"watch:storybook:assets": "node ./scripts/watch-storybook.js"
},
"devDependencies": {
"@penpot/draft-js": "portal:./packages/draft-js",
"@penpot/mousetrap": "portal:./packages/mousetrap",
"@penpot/plugins-runtime": "1.3.2",
"@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "portal:./text-editor",
"@playwright/test": "1.52.0",
"@storybook/addon-docs": "10.0.4",
"@storybook/addon-themes": "10.0.4",
"@storybook/addon-vitest": "10.0.4",
"@storybook/react-vite": "10.0.4",
"@tokens-studio/sd-transforms": "1.2.11",
"@types/node": "^22.15.21",
"@vitest/browser": "3.2.4",
"@vitest/coverage-v8": "3.2.4",
"@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",
"autoprefixer": "^10.4.21",
"compression": "^1.8.1",
"concurrently": "^9.2.1",
"date-fns": "^4.1.0",
"esbuild": "^0.25.9",
"eventsource-parser": "^3.0.6",
"express": "^5.1.0",
"fancy-log": "^2.0.0",
"getopts": "^2.3.0",
"gettext-parser": "^8.0.0",
"gulp-concat": "^2.6.1",
"gulp-gzip": "^1.4.2",
"gulp-mustache": "^5.0.0",
"gulp-postcss": "^10.0.0",
"gulp-rename": "^2.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-svg-sprite": "^2.0.3",
"highlight.js": "^11.10.0",
"js-beautify": "^1.15.4",
"jsdom": "^27.0.0",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"map-stream": "0.0.7",
"marked": "^15.0.12",
"mkdirp": "^3.0.1",
"mustache": "^4.2.0",
"nodemon": "^3.1.10",
"npm-run-all": "^4.1.5",
"opentype.js": "^1.3.4",
"p-limit": "^6.2.0",
"playwright": "1.56.1",
"postcss": "^8.5.4",
"postcss-clean": "^1.2.2",
"postcss-modules": "^6.0.1",
"prettier": "3.5.3",
"pretty-time": "^1.1.0",
"prop-types": "^15.8.1",
"rimraf": "^6.0.1",
"sass": "^1.89.0",
"sass-embedded": "^1.89.0",
"storybook": "10.0.4",
"svg-sprite": "^2.0.4",
"typescript": "^5.9.2",
"vite": "^6.3.5",
"vitest": "^3.2.0",
"wasm-pack": "^0.13.1",
"watcher": "^2.3.1",
"workerpool": "^9.3.2"
},
"dependencies": {
"@penpot/draft-js": "portal:./vendor/draft-js",
"@penpot/hljs": "portal:./vendor/hljs",
"@penpot/mousetrap": "portal:./vendor/mousetrap",
"@penpot/plugins-runtime": "1.3.2",
"@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",
"compression": "^1.8.1",
"date-fns": "^4.1.0",
"eventsource-parser": "^3.0.6",
"js-beautify": "^1.15.4",
"lodash": "^4.17.21",
"lodash.debounce": "^4.0.8",
"opentype.js": "^1.3.4",
"postcss-modules": "^6.0.1",
"randomcolor": "^0.6.2",
"react": "19.1.1",
"react-dom": "19.1.1",
"react-error-boundary": "^6.0.0",
"react-virtualized": "^9.22.6",
"rimraf": "^6.0.1",
"rxjs": "8.0.0-alpha.14",
"sass": "^1.89.0",
"sass-embedded": "^1.89.0",
"sax": "^1.4.1",
"source-map-support": "^0.5.21",
"storybook": "10.0.4",
"style-dictionary": "5.0.0-rc.1",
"svg-sprite": "^2.0.4",
"tdigest": "^0.1.2",
"tinycolor2": "^1.6.0",
"typescript": "^5.9.2",
"ua-parser-js": "2.0.5",
"vite": "^6.3.5",
"vitest": "^3.2.0",
"wasm-pack": "^0.13.1",
"watcher": "^2.3.1",
"workerpool": "^9.3.2",
"xregexp": "^5.1.2"
}
}

View File

@@ -16,7 +16,9 @@ export const {
RichTextEditorUtil,
SelectionState,
convertFromRaw,
convertToRaw
convertToRaw,
EditorBlock,
Editor
} = pkg;
import DraftPasteProcessor from 'draft-js/lib/DraftPasteProcessor.js';

View File

@@ -8,7 +8,8 @@
"author": "Andrey Antukh",
"license": "MPL-2.0",
"dependencies": {
"draft-js": "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0"
"draft-js": "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0",
"immutable": "^5.1.4"
},
"peerDependencies": {
"react": ">=0.17.0",

View File

@@ -173,12 +173,13 @@ __metadata:
languageName: node
linkType: hard
"@penpot/draft-js-wrapper@workspace:.":
"@penpot/draft-js@workspace:.":
version: 0.0.0-use.local
resolution: "@penpot/draft-js-wrapper@workspace:."
resolution: "@penpot/draft-js@workspace:."
dependencies:
draft-js: "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0"
esbuild: "npm:^0.24.0"
immutable: "npm:^5.1.4"
peerDependencies:
react: ">=0.17.0"
react-dom: ">=0.17.0"
@@ -320,6 +321,13 @@ __metadata:
languageName: node
linkType: hard
"immutable@npm:^5.1.4":
version: 5.1.4
resolution: "immutable@npm:5.1.4"
checksum: 10c0/f1c98382e4cde14a0b218be3b9b2f8441888da8df3b8c064aa756071da55fbed6ad696e5959982508456332419be9fdeaf29b2e58d0eadc45483cc16963c0446
languageName: node
linkType: hard
"immutable@npm:~3.7.4":
version: 3.7.6
resolution: "immutable@npm:3.7.6"

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,47 @@
[
{
"~:id": "~uc7ce0794-0992-8105-8004-38e630f41234",
"~:revn": 1,
"~:vern": 1,
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1705307400000",
"~:modified-at": "~m1732111500000",
"~:deleted-at": "~m1732111500000",
"~:name": "Deleted Design File 1",
"~:is-shared": false,
"~:will-be-deleted-at": "~m1732716300000",
"~:thumbnail-id": null,
"~:row-num": 1,
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d"
},
{
"~:id": "~uc7ce0794-0992-8105-8004-38e630f41235",
"~:revn": 2,
"~:vern": 2,
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
"~:created-at": "~m1704875700000",
"~:modified-at": "~m1732025400000",
"~:deleted-at": "~m1732025400000",
"~:name": "Deleted Design File 2",
"~:is-shared": true,
"~:will-be-deleted-at": "~m1732630200000",
"~:thumbnail-id": null,
"~:row-num": 2,
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d"
},
{
"~:id": "~uc7ce0794-0992-8105-8004-38e630f41236",
"~:revn": 3,
"~:vern": 3,
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920c",
"~:created-at": "~m1706792400000",
"~:modified-at": "~m1731939600000",
"~:deleted-at": "~m1731939600000",
"~:name": "Old Project Design",
"~:is-shared": false,
"~:will-be-deleted-at": "~m1732544400000",
"~:thumbnail-id": null,
"~:row-num": 3,
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d"
}
]

View File

@@ -106,6 +106,13 @@ export class DashboardPage extends BaseWebSocketPage {
);
}
async setupDeletedFiles() {
await this.mockRPC(
"get-team-deleted-files?team-id=*",
"dashboard/get-team-deleted-files.json",
);
}
async setupDrafts() {
await this.mockRPC(
"get-project-files?project-id=*",
@@ -160,6 +167,10 @@ export class DashboardPage extends BaseWebSocketPage {
});
await this.mockRPC("search-files", "dashboard/search-files.json");
await this.mockRPC("get-teams", "logged-in-user/get-teams-complete.json");
await this.mockRPC(
"get-team-deleted-files?team-id=*",
"dashboard/get-team-deleted-files.json",
);
}
async setupAccessTokensEmpty() {
@@ -289,6 +300,13 @@ export class DashboardPage extends BaseWebSocketPage {
await expect(this.mainHeading).toHaveText("Libraries");
}
async goToDeleted() {
await this.page.goto(
`#/dashboard/deleted?team-id=${DashboardPage.anyTeamId}`,
);
await expect(this.mainHeading).toHaveText("Projects");
}
async openProfileMenu() {
await this.userAccount.click();
}

View File

@@ -0,0 +1,31 @@
import { test, expect } from "@playwright/test";
import DashboardPage from "../pages/DashboardPage";
test.beforeEach(async ({ page }) => {
await DashboardPage.init(page);
await DashboardPage.mockRPC(
page,
"get-profile",
"logged-in-user/get-profile-logged-in-no-onboarding.json",
);
});
test.describe("Dashboard Deleted Page", () => {
test("User can navigate to deleted page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
// Setup mock for deleted files API
await dashboardPage.setupDeletedFiles();
// Navigate directly to deleted page
await dashboardPage.goToDeleted();
// Check for the restore all and clear trash buttons
await expect(
page.getByRole("button", { name: "Restore All" }),
).toBeVisible();
await expect(
page.getByRole("button", { name: "Clear trash" }),
).toBeVisible();
});
});

View File

@@ -24,6 +24,8 @@
<link rel="icon" href="images/favicon.png" />
<script type="importmap">{{& manifest.importmap }}</script>
<script type="module">
globalThis.penpotVersion = "{{& version}}";
globalThis.penpotBuildDate = "{{& build_date}}";
@@ -33,7 +35,6 @@
{{# manifest}}
<script src="{{& config}}"></script>
<script src="{{& polyfills}}"></script>
<script type="importmap">{{& importmap }}</script>
{{/manifest}}
<!--cookie-consent-->
@@ -49,7 +50,9 @@
<script type="module" src="{{& libs}}"></script>
<script type="module">
import { init } from "{{& app_main}}";
init();
import defaultTranslations from "{{& default_translations}}";
init({defaultTranslations});
</script>
{{/manifest}}
</body>

View File

@@ -187,7 +187,7 @@ async function readManifestFile(resource) {
return JSON.parse(content);
}
async function readShadowManifest() {
async function generateManifest() {
const index = {
app_main: "./js/main.js",
render_main: "./js/render.js",
@@ -197,6 +197,7 @@ async function readShadowManifest() {
polyfills: "./js/polyfills.js?version=" + CURRENT_VERSION,
libs: "./js/libs.js?version=" + CURRENT_VERSION,
worker_main: "./js/worker/main.js?version=" + CURRENT_VERSION,
default_translations: "./js/translation.en.js?version=" + CURRENT_VERSION,
importmap: JSON.stringify({
"imports": {
@@ -276,6 +277,7 @@ export async function compileTranslations() {
"id",
"ru",
"tr",
"hi",
"zh_CN",
"zh_Hant",
"hr",
@@ -391,7 +393,7 @@ async function generateTemplates() {
const isDebug = process.env.NODE_ENV !== "production";
await fs.mkdir("./resources/public/", { recursive: true });
const manifest = await readShadowManifest();
const manifest = await generateManifest();
let content;
const iconsSprite = await fs.readFile(

View File

@@ -90,7 +90,10 @@
(rx/map #(ws/initialize)))))))
(defn ^:export init
[]
[options]
(some-> (unchecked-get options "defaultTranslations")
(i18n/set-default-translations))
(mw/init!)
(i18n/init)
(cur/init-styles)

View File

@@ -386,3 +386,21 @@
(rx/of ::dps/force-persist
(rt/nav :viewer params options))))))
(defn go-to-dashboard-deleted
[& {:keys [team-id] :as options}]
(ptk/reify ::go-to-dashboard-deleted
ptk/WatchEvent
(watch [_ state _]
(let [profile (get state :profile)
team-id (cond
(= :default team-id)
(:default-team-id profile)
(uuid? team-id)
team-id
:else
(:current-team-id state))
params {:team-id team-id}]
(rx/of (modal/hide)
(rt/nav :dashboard-deleted params options))))))

View File

@@ -21,6 +21,7 @@
[app.main.data.modal :as modal]
[app.main.data.websocket :as dws]
[app.main.repo :as rp]
[app.main.store :as st]
[app.util.i18n :as i18n :refer [tr]]
[app.util.sse :as sse]
[beicon.v2.core :as rx]
@@ -76,7 +77,8 @@
ptk/UpdateEvent
(update [_ state]
(reduce (fn [state {:keys [id] :as project}]
(update-in state [:projects id] merge project))
;; Replace completely instead of merge to ensure deleted-at is removed
(assoc-in state [:projects id] project))
state
projects))))
@@ -152,6 +154,34 @@
(->> (rp/cmd! :get-builtin-templates)
(rx/map builtin-templates-fetched)))))
;; --- EVENT: deleted-files
(defn- deleted-files-fetched
[files]
(ptk/reify ::deleted-files-fetched
ptk/UpdateEvent
(update [_ state]
(let [now (ct/now)
filtered-files (filterv (fn [file]
(let [will-be-deleted-at (:will-be-deleted-at file)]
(or (nil? will-be-deleted-at)
(ct/is-after? will-be-deleted-at now))))
files)
files (d/index-by :id filtered-files)]
(-> state
(assoc :deleted-files files)
(update :files d/merge files))))))
(defn fetch-deleted-files
([] (fetch-deleted-files nil))
([team-id]
(ptk/reify ::fetch-deleted-files
ptk/WatchEvent
(watch [_ state _]
(when-let [team-id (or team-id (:current-team-id state))]
(->> (rp/cmd! :get-team-deleted-files {:team-id team-id})
(rx/map deleted-files-fetched)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Selection
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -460,6 +490,7 @@
(-> state
(d/update-in-when [:files file-id] assoc :thumbnail-id thumbnail-id)
(d/update-in-when [:recent-files file-id] assoc :thumbnail-id thumbnail-id)
(d/update-in-when [:deleted-files file-id] assoc :thumbnail-id thumbnail-id)
(d/update-when :dashboard-search-result update-search-files))))))
;; --- EVENT: create-file
@@ -656,3 +687,156 @@
:team-role-change (handle-change-team-role msg)
:team-membership-change (dcm/team-membership-change msg)
nil))
;; --- Delete files immediately
(defn delete-files-immediately
[{:keys [team-id ids] :as params}]
(assert (uuid? team-id))
(assert (set? ids))
(assert (every? uuid? ids))
(ptk/reify ::delete-files-immediately
ev/Event
(-data [_]
{:team-id team-id
:num-files (count ids)})
ptk/WatchEvent
(watch [_ _ _]
(let [{:keys [on-success on-error]
:or {on-success identity
on-error rx/throw}} (meta params)]
(->> (rp/cmd! :permanently-delete-team-files {:team-id team-id :ids ids})
(rx/tap on-success)
(rx/catch on-error))))))
;; --- Restore deleted files immediately
(defn- initialize-restore-status
[files]
(ptk/reify ::init-restore-status
ptk/UpdateEvent
(update [_ state]
(let [restore-state {:in-progress true
:healthy? true
:error false
:progress 0
:widget-visible true
:detail-visible true
:files files
:last-update (ct/now)
:cmd :restore-files}]
(assoc state :restore restore-state)))))
(defn- update-restore-status
[{:keys [index total] :as data}]
(ptk/reify ::upd-restore-status
ptk/UpdateEvent
(update [_ state]
(let [time-diff (ct/diff-ms (get-in state [:restore :last-update]) (ct/now))
healthy? (< time-diff 6000)]
(update state :restore assoc
:progress index
:total total
:last-update (ct/now)
:healthy? healthy?)))))
(defn- complete-restore-status
[]
(ptk/reify ::comp-restore-status
ptk/UpdateEvent
(update [_ state]
(let [total (get-in state [:restore :total])]
(update state :restore assoc
:in-progress false
:progress total ; Ensure progress equals total on completion
:last-update (ct/now))))))
(defn- error-restore-status
[error]
(ptk/reify ::err-restore-status
ptk/UpdateEvent
(update [_ state]
(update state :restore assoc
:in-progress false
:error error
:last-update (ct/now)
:healthy? false))))
(defn toggle-restore-detail-visibility
[]
(ptk/reify ::toggle-restore-detail
ptk/UpdateEvent
(update [_ state]
(update-in state [:restore :detail-visible] not))))
(defn retry-last-restore
[]
(ptk/reify ::retry-restore
ptk/UpdateEvent
(update [_ state]
;; Reset restore state for retry - actual retry will be handled by UI
(if (get state :restore)
(update state :restore assoc :error false :in-progress false)
state))))
(defn clear-restore-state
[]
(ptk/reify ::clear-restore
ptk/UpdateEvent
(update [_ state]
(dissoc state :restore))))
(defn- projects-restored
[team-id]
(ptk/reify ::projects-restored
ptk/WatchEvent
(watch [_ _ _]
;; Refetch projects to get the updated state without deleted-at
(rx/of (fetch-projects team-id)))))
(defn restore-files-immediately
[{:keys [team-id ids] :as params}]
(dm/assert! (uuid? team-id))
(dm/assert! (set? ids))
(dm/assert! (every? uuid? ids))
(ptk/reify ::restore-files-immediately
ev/Event
(-data [_]
{:team-id team-id
:num-files (count ids)})
ptk/WatchEvent
(watch [_ _ _]
(let [{:keys [on-success on-error]
:or {on-success identity
on-error rx/throw}} (meta params)
files (mapv #(hash-map :id %) ids)]
(rx/merge
(rx/of (initialize-restore-status files))
(->> (rp/cmd! ::sse/restore-deleted-team-files {:team-id team-id :ids ids})
(rx/tap (fn [event]
(let [payload (sse/get-payload event)
type (sse/get-type event)]
(when (and payload (= type "progress"))
(let [{:keys [index total]} payload]
(when (and index total)
;; Dispatch progress update
(st/emit! (update-restore-status {:index index :total total}))))))))
(rx/filter sse/end-of-stream?)
(rx/map sse/get-payload)
(rx/tap on-success)
(rx/mapcat (fn [_]
(rx/of (complete-restore-status)
(projects-restored team-id))))
(rx/catch (fn [error]
(rx/concat
(rx/of (error-restore-status (ex-message error)))
(on-error error)))))
(rx/of (ptk/data-event ::restore-start {:total (count ids)})))))))

View File

@@ -98,9 +98,7 @@
(def context
(atom (d/without-nils (collect-context))))
(add-watch i18n/state "events"
(fn [_ _ _ v]
(swap! context assoc :locale (get v :locale))))
(add-watch i18n/locale "events" #(swap! context assoc :locale %4))
;; --- EVENT TRANSLATION

View File

@@ -270,8 +270,12 @@
(ptk/reify ::process-wasm-object
ptk/EffectEvent
(effect [_ state _]
(let [objects (dsh/lookup-page-objects state)]
(wasm.api/process-object (get objects id))))))
(let [objects (dsh/lookup-page-objects state)
shape (get objects id)]
;; Only process objects that exist in the current page
;; This prevents errors when processing changes from other pages
(when shape
(wasm.api/process-object shape))))))
(defn initialize-workspace
[team-id file-id]

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

@@ -636,3 +636,6 @@
(def persistence-state
(l/derived (comp :status :persistence) st/state))
(def restore
(l/derived :restore st/state))

View File

@@ -87,6 +87,9 @@
{:stream? true
:form-data? true}
::sse/restore-deleted-team-files
{:stream? true}
:export-binfile {:response-type :blob}
:retrieve-list-of-builtin-templates {:query-params :all}})

View File

@@ -224,7 +224,8 @@
:dashboard-members
:dashboard-invitations
:dashboard-webhooks
:dashboard-settings)
:dashboard-settings
:dashboard-deleted)
(let [params (get params :query)
team-id (some-> params :team-id uuid/parse*)
project-id (some-> params :project-id uuid/parse*)

View File

@@ -20,6 +20,7 @@
[app.main.router :as rt]
[app.main.store :as st]
[app.main.ui.context :as ctx]
[app.main.ui.dashboard.deleted :refer [deleted-section*]]
[app.main.ui.dashboard.files :refer [files-section*]]
[app.main.ui.dashboard.fonts :refer [fonts-page* font-providers-page*]]
[app.main.ui.dashboard.import]
@@ -29,6 +30,7 @@
[app.main.ui.dashboard.sidebar :refer [sidebar*]]
[app.main.ui.dashboard.team :refer [team-settings-page* team-members-page* team-invitations-page* webhooks-page*]]
[app.main.ui.dashboard.templates :refer [templates-section*]]
[app.main.ui.exports.assets :refer [progress-widget]]
[app.main.ui.hooks :as hooks]
[app.main.ui.modal :refer [modal-container*]]
[app.main.ui.workspace.plugins]
@@ -84,6 +86,9 @@
[:div {:class (stl/css :dashboard-content)
:on-click clear-selected-fn
:ref container}
[:& progress-widget {:operation :restore}]
(case section
:dashboard-recent
(when (seq projects)
@@ -140,6 +145,11 @@
:dashboard-settings
[:> team-settings-page* {:team team :profile profile}]
:dashboard-deleted
[:> deleted-section* {:team team
:projects projects
:profile profile}]
nil)]))
(def ref:dashboard-initialized

View File

@@ -0,0 +1,331 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.dashboard.deleted
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.main.data.common :as dcm]
[app.main.data.dashboard :as dd]
[app.main.data.modal :as modal]
[app.main.data.notifications :as ntf]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
[app.main.ui.dashboard.grid :refer [grid*]]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as deprecated-icon]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def ^:private menu-icon
(deprecated-icon/icon-xref :menu (stl/css :menu-icon)))
(mf/defc header*
{::mf/props :obj
::mf/private true}
[]
[:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"}
[:div#dashboard-deleted-title {:class (stl/css :dashboard-title)}
[:h1 (tr "dashboard.projects-title")]]])
(mf/defc deleted-project-menu*
[{:keys [project files team-id show on-close top left]}]
(let [top (d/nilv top 0)
left (d/nilv left 0)
file-ids
(mf/with-memo [files]
(into #{} d/xf:map-id files))
restore-fn
(fn [_]
(st/emit! (dd/restore-files-immediately
(with-meta {:team-id team-id :ids file-ids}
{:on-success #(st/emit! (ntf/success (tr "restore-modal.success-restore-immediately" (:name project)))
(dd/fetch-projects team-id)
(dd/fetch-deleted-files team-id))
:on-error #(st/emit! (ntf/error (tr "restore-modal.error-restore-project" (:name project))))}))))
on-restore-project
(fn []
(st/emit!
(modal/show {:type :confirm
:title (tr "restore-modal.restore-project.title")
:message (tr "restore-modal.restore-project.description" (:name project))
:accept-style :primary
:accept-label (tr "labels.continue")
:on-accept restore-fn})))
delete-fn
(fn [_]
(st/emit! (ntf/success (tr "delete-forever-modal.success-delete-immediately" (:name project)))
(dd/delete-files-immediately
{:team-id team-id
:ids file-ids})
(dd/fetch-projects team-id)
(dd/fetch-deleted-files team-id)))
on-delete-project
(fn []
(st/emit!
(modal/show {:type :confirm
:title (tr "delete-forever-modal.title")
:message (tr "delete-forever-modal.delete-project.description" (:name project))
:accept-label (tr "dashboard.deleted.delete-forever")
:on-accept delete-fn})))
options
[{:name (tr "dashboard.deleted.restore-project")
:id "project-restore"
:handler on-restore-project}
{:name (tr "dashboard.deleted.delete-project")
:id "project-delete"
:handler on-delete-project}]]
[:> context-menu*
{:on-close on-close
:show show
:fixed (or (not= top 0) (not= left 0))
:min-width true
:top top
:left left
:options options}]))
(mf/defc deleted-project-item*
{::mf/props :obj
::mf/private true}
[{:keys [project team files]}]
(let [project-files (filterv #(= (:project-id %) (:id project)) files)
empty? (empty? project-files)
selected-files (mf/deref refs/selected-files)
dstate (mf/deref refs/dashboard-local)
edit-id (:project-for-edit dstate)
local (mf/use-state
#(do {:menu-open false
:menu-pos nil
:edition (= (:id project) edit-id)}))
[rowref limit] (hooks/use-dynamic-grid-item-width)
on-menu-click
(mf/use-fn
(fn [event]
(dom/prevent-default event)
(let [client-position (dom/get-client-position event)
position (if (and (nil? (:y client-position)) (nil? (:x client-position)))
(let [target-element (dom/get-target event)
points (dom/get-bounding-rect target-element)
y (:top points)
x (:left points)]
(gpt/point x y))
client-position)]
(swap! local assoc
:menu-open true
:menu-pos position))))
on-menu-close
(mf/use-fn #(swap! local assoc :menu-open false))
handle-menu-click
(mf/use-callback
(mf/deps on-menu-click)
(fn [event]
(when (kbd/enter? event)
(dom/stop-propagation event)
(on-menu-click event))))]
[:article {:class (stl/css-case :dashboard-project-row true)}
[:header {:class (stl/css :project)}
[:div {:class (stl/css :project-name-wrapper)}
[:h2 {:class (stl/css :project-name)
:title (:name project)}
(:name project)]
(when (:deleted-at project)
[:div {:class (stl/css :info-wrapper)}
[:div {:class (stl/css-case :project-actions true)}
[:button {:class (stl/css :options-btn)
:on-click on-menu-click
:title (tr "dashboard.options")
:aria-label (tr "dashboard.options")
:data-testid "project-options"
:on-key-down handle-menu-click}
menu-icon]]
(when (:menu-open @local)
[:> deleted-project-menu*
{:project project
:files project-files
:team-id (:id team)
:show (:menu-open @local)
:left (+ 24 (:x (:menu-pos @local)))
:top (:y (:menu-pos @local))
:on-close on-menu-close}])])]]
[:div {:class (stl/css :grid-container) :ref rowref}
(if ^boolean empty?
[:> empty-placeholder* {:title (tr "dashboard.empty-placeholder-files-title")
:class (stl/css :placeholder-placement)
:type 1
:subtitle (tr "dashboard.empty-placeholder-files-subtitle")}]
[:> grid*
{:project project
:files project-files
:origin :deleted
:can-edit false
:can-restore true
:limit limit
:selected-files selected-files}])]]))
(def ^:private ref:deleted-files
(l/derived :deleted-files st/state))
(mf/defc deleted-section*
[{:keys [team projects]}]
(let [deleted-map
(mf/deref ref:deleted-files)
projects
(mf/with-memo [projects deleted-map]
(->> projects
(filter (fn [project]
(or (:deleted-at project)
(when deleted-map
(some #(= (:id project) (:project-id %))
(vals deleted-map))))))
(filter (fn [project]
(when deleted-map
(some #(= (:id project) (:project-id %))
(vals deleted-map)))))
(sort-by :modified-at)
(reverse)))
team-id
(get team :id)
;; Calculate deletion days based on team subscription
deletion-days
(let [subscription (get team :subscription)
sub-type (get subscription :type)
sub-status (get subscription :status)
canceled? (contains? #{"canceled" "unpaid"} sub-status)]
(cond
(and (= "unlimited" sub-type) (not canceled?)) 30
(and (= "enterprise" sub-type) (not canceled?)) 90
:else 7))
on-clear
(mf/use-fn
(mf/deps team-id deleted-map)
(fn []
(when deleted-map
(let [file-ids (into #{} (keys deleted-map))]
(when (seq file-ids)
(st/emit!
(modal/show {:type :confirm
:title (tr "delete-forever-modal.title")
:message (tr "delete-forever-modal.delete-all.description" (count file-ids))
:accept-label (tr "dashboard.deleted.delete-forever")
:on-accept #(st/emit!
(dd/delete-files-immediately
{:team-id team-id
:ids file-ids})
(dd/fetch-projects team-id)
(dd/fetch-deleted-files team-id))})))))))
restore-fn
(fn [file-ids]
(st/emit! (dd/restore-files-immediately
(with-meta {:team-id team-id :ids file-ids}
{:on-success #(st/emit! (dd/fetch-projects team-id)
(dd/fetch-deleted-files team-id))
:on-error #(st/emit! (ntf/error (tr "restore-modal.error-restore-files")))}))))
on-restore-all
(mf/use-fn
(mf/deps team-id deleted-map)
(fn []
(when deleted-map
(let [file-ids (into #{} (keys deleted-map))]
(when (seq file-ids)
(st/emit!
(modal/show {:type :confirm
:title (tr "restore-modal.restore-all.title")
:message (tr "restore-modal.restore-all.description" (count file-ids))
:accept-label (tr "labels.continue")
:accept-style :primary
:on-accept #(restore-fn file-ids)})))))))
on-recent-click
(mf/use-fn
(mf/deps team-id)
(fn []
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id))))]
(mf/with-effect [team-id]
(st/emit! (dd/fetch-projects team-id)
(dd/fetch-deleted-files team-id)
(dd/clear-selected-files)))
[:*
[:> header* {:team team}]
[:section {:class (stl/css :dashboard-container :no-bg)}
[:*
[:div {:class (stl/css :no-bg)}
[:div {:class (stl/css :nav-options)}
[:> button* {:variant "ghost"
:data-testid "recent-tab"
:type "button"
:on-click on-recent-click}
(tr "dashboard.labels.recent")]
[:div {:class (stl/css :selected)
:data-testid "deleted-tab"}
(tr "dashboard.labels.deleted")]]
[:div {:class (stl/css :deleted-content)}
[:div {:class (stl/css :deleted-info)}
[:div
(tr "dashboard.deleted.info-text")
[:span {:class (stl/css :info-text-highlight)}
(tr "dashboard.deleted.info-days" deletion-days)]
(tr "dashboard.deleted.info-text2")]
[:div
(tr "dashboard.deleted.restore-text")]]
[:div {:class (stl/css :deleted-options)}
[:> button* {:variant "ghost"
:type "button"
:on-click on-restore-all}
(tr "dashboard.deleted.restore-all")]
[:> button* {:variant "destructive"
:type "button"
:icon "delete"
:on-click on-clear}
(tr "dashboard.deleted.clear")]]]
(when (seq projects)
(for [{:keys [id] :as project} projects]
(let [files (when deleted-map
(->> (vals deleted-map)
(filterv #(= id (:project-id %)))
(sort-by :modified-at #(compare %2 %1))))]
[:> deleted-project-item* {:project project
:team team
:files files
:key id}])))]]]]))

View File

@@ -0,0 +1,125 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-dashboard";
@use "../ds/typography.scss" as t;
@use "../ds/_borders.scss" as *;
@use "../ds/spacing.scss" as *;
@use "../ds/_sizes.scss" as *;
@use "../ds/z-index.scss" as *;
.dashboard-container {
flex: 1 0 0;
width: 100%;
margin-inline-end: var(--sp-l);
border-top: $b-1 solid var(--panel-border-color);
overflow-y: auto;
padding-block-end: var(--sp-xxxl);
}
.deleted-content {
display: flex;
gap: var(--sp-l);
justify-content: space-between;
margin-inline-start: var(--sp-l);
margin-block-start: var(--sp-xxl);
}
.deleted-info {
@include t.use-typography("body-medium");
color: var(--color-foreground-secondary);
}
.info-text-highlight {
color: var(--color-accent-primary);
}
.deleted-options {
display: flex;
gap: 5px;
flex-shrink: 0;
}
.nav-options {
display: flex;
gap: var(--sp-l);
justify-content: space-between;
border-bottom: $b-1 solid var(--panel-border-color);
padding-inline-start: var(--sp-l);
background: var(--color-background-default);
position: sticky;
top: 0;
z-index: var(--z-index-panels);
}
.selected {
@include t.use-typography("headline-small");
display: flex;
align-items: center;
justify-content: center;
color: var(--color-foreground-primary);
border: $b-1 solid transparent;
border-bottom: $b-1 solid var(--color-foreground-primary);
padding: 0 var(--sp-m);
}
.project {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: var(--sp-s);
width: 99%;
max-height: $sz-40;
padding: var(--sp-s) var(--sp-s) var(--sp-s) var(--sp-l);
margin-block-start: var(--sp-l);
border-radius: $br-4;
}
.project-name-wrapper {
display: flex;
align-items: center;
justify-content: flex-start;
width: 100%;
min-height: var(--sp-xxxl);
margin-inline-start: var(--sp-s);
}
.project-name {
@include t.use-typography("body-large");
width: fit-content;
margin-inline-end: var(--sp-m);
line-height: 0.8;
color: var(--title-foreground-color-hover);
height: var(--sp-l);
}
.project-actions {
display: flex;
opacity: var(--actions-opacity);
margin-inline-start: var(--sp-xxxl);
}
.add-file-btn,
.options-btn {
@extend .button-tertiary;
height: var(--sp-xxxl);
width: var(--sp-xxxl);
margin: 0 var(--sp-s);
padding: var(--sp-s);
}
.info-wrapper {
display: flex;
align-items: center;
gap: var(--sp-s);
}
.add-icon,
.menu-icon {
@extend .button-icon;
stroke: var(--icon-foreground);
}

View File

@@ -55,7 +55,7 @@
projects))
(mf/defc file-menu*
[{:keys [files on-edit on-close top left navigate origin parent-id can-edit]}]
[{:keys [files on-edit on-close top left navigate origin parent-id can-edit can-restore]}]
(assert (seq files) "missing `files` prop")
(assert (fn? on-edit) "missing `on-edit` prop")
@@ -187,7 +187,46 @@
on-export-binary-files
(fn []
(st/emit! (-> (fexp/open-export-dialog files)
(with-meta {::ev/origin "dashboard"}))))]
(with-meta {::ev/origin "dashboard"}))))
restore-fn
(fn [_]
(st/emit! (dd/restore-files-immediately
(with-meta {:team-id (:id current-team)
:ids #{(:id file)}}
{:on-success #(st/emit! (ntf/success (tr "restore-modal.success-restore-immediately" (:name file)))
(dd/fetch-projects (:id current-team))
(dd/fetch-deleted-files (:id current-team)))
:on-error #(st/emit! (ntf/error (tr "restore-modal.error-restore-file" (:name file))))}))))
on-restore-immediately
(fn []
(st/emit!
(modal/show {:type :confirm
:title (tr "restore-modal.restore-file.title")
:message (tr "restore-modal.restore-file.description" (:name file))
:accept-label (tr "labels.continue")
:accept-style :primary
:on-accept restore-fn})))
delete-fn
(fn [_]
(st/emit! (ntf/success (tr "delete-forever-modal.success-delete-immediately" (:name file)))
(dd/delete-files-immediately
{:team-id (:id current-team)
:ids #{(:id file)}})
(dd/fetch-projects (:id current-team))
(dd/fetch-deleted-files (:id current-team))))
on-delete-immediately
(fn []
(st/emit!
(modal/show {:type :confirm
:title (tr "delete-forever-modal.title")
:message (tr "delete-forever-modal.delete-file.description" (:name file))
:accept-label (tr "delete-forever-modal.title")
:on-accept delete-fn})))]
(mf/with-effect []
(->> (rp/cmd! :get-all-projects)
@@ -227,76 +266,85 @@
(:id sub-project))})})}]))
options
(if multi?
[(when can-edit
{:name (tr "dashboard.duplicate-multi" file-count)
:id "duplicate-multi"
:handler on-duplicate})
(if can-restore
[(when can-restore
{:name (tr "dashboard.restore-file")
:id "restore-file"
:handler on-restore-immediately})
(when can-restore
{:name (tr "dashboard.delete-file")
:id "delete-file"
:handler on-delete-immediately})]
(if multi?
[(when can-edit
{:name (tr "dashboard.duplicate-multi" file-count)
:id "duplicate-multi"
:handler on-duplicate})
(when (and (or (seq current-projects) (seq other-teams)) can-edit)
{:name (tr "dashboard.move-to-multi" file-count)
:id "file-move-multi"
:options sub-options})
(when (and (or (seq current-projects) (seq other-teams)) can-edit)
{:name (tr "dashboard.move-to-multi" file-count)
:id "file-move-multi"
:options sub-options})
{:name (tr "dashboard.export-binary-multi" file-count)
:id "file-binary-export-multi"
:handler on-export-binary-files}
{:name (tr "dashboard.export-binary-multi" file-count)
:id "file-binary-export-multi"
:handler on-export-binary-files}
(when (and (:is-shared file) can-edit)
{:name (tr "labels.unpublish-multi-files" file-count)
:id "file-unpublish-multi"
:handler on-del-shared})
(when (and (:is-shared file) can-edit)
{:name (tr "labels.unpublish-multi-files" file-count)
:id "file-unpublish-multi"
:handler on-del-shared})
(when (and (not is-lib-page?) can-edit)
{:name :separator}
{:name (tr "labels.delete-multi-files" file-count)
:id "file-delete-multi"
:handler on-delete})]
(when (and (not is-lib-page?) can-edit)
{:name :separator}
{:name (tr "labels.delete-multi-files" file-count)
:id "file-delete-multi"
:handler on-delete})]
[{:name (tr "dashboard.open-in-new-tab")
:id "file-open-new-tab"
:handler on-new-tab}
(when (and (not is-search-page?) can-edit)
{:name (tr "labels.rename")
:id "file-rename"
:handler on-edit})
[{:name (tr "dashboard.open-in-new-tab")
:id "file-open-new-tab"
:handler on-new-tab}
(when (and (not is-search-page?) can-edit)
{:name (tr "labels.rename")
:id "file-rename"
:handler on-edit})
(when (and (not is-search-page?) can-edit)
{:name (tr "dashboard.duplicate")
:id "file-duplicate"
:handler on-duplicate})
(when (and (not is-search-page?) can-edit)
{:name (tr "dashboard.duplicate")
:id "file-duplicate"
:handler on-duplicate})
(when (and (not is-lib-page?)
(not is-search-page?)
(or (seq current-projects) (seq other-teams))
can-edit)
{:name (tr "dashboard.move-to")
:id "file-move-to"
:options sub-options})
(when (and (not is-lib-page?)
(not is-search-page?)
(or (seq current-projects) (seq other-teams))
can-edit)
{:name (tr "dashboard.move-to")
:id "file-move-to"
:options sub-options})
(when (and (not is-search-page?)
can-edit)
(if (:is-shared file)
{:name (tr "dashboard.unpublish-shared")
:id "file-del-shared"
:handler on-del-shared}
{:name (tr "dashboard.add-shared")
:id "file-add-shared"
:handler on-add-shared}))
(when (and (not is-search-page?)
can-edit)
(if (:is-shared file)
{:name (tr "dashboard.unpublish-shared")
:id "file-del-shared"
:handler on-del-shared}
{:name (tr "dashboard.add-shared")
:id "file-add-shared"
:handler on-add-shared}))
{:name :separator}
{:name :separator}
{:name (tr "dashboard.download-binary-file")
:id "download-binary-file"
:handler on-export-binary-files}
{:name (tr "dashboard.download-binary-file")
:id "download-binary-file"
:handler on-export-binary-files}
(when (and (not is-lib-page?) (not is-search-page?) can-edit)
{:name :separator})
(when (and (not is-lib-page?) (not is-search-page?) can-edit)
{:name :separator})
(when (and (not is-lib-page?) (not is-search-page?) can-edit)
{:name (tr "labels.delete")
:id "file-delete"
:handler on-delete})])]
(when (and (not is-lib-page?) (not is-search-page?) can-edit)
{:name (tr "labels.delete")
:id "file-delete"
:handler on-delete})]))]
[:> context-menu*
{:on-close on-close

View File

@@ -86,7 +86,7 @@
(mf/defc grid-item-thumbnail*
{::mf/props :obj
::mf/private true}
[{:keys [can-edit file]}]
[{:keys [can-edit file can-restore]}]
(let [file-id (get file :id)
revn (get file :revn)
thumbnail-id (get file :thumbnail-id)
@@ -109,7 +109,8 @@
:message (ex-message cause)))))]
(partial rx/dispose! subscription))))
[:div {:class (stl/css :grid-item-th)
[:div {:class (stl/css-case :grid-item-th true
:deleted-item can-restore)
:style {:background-color bg-color}
:ref container}
(when visible?
@@ -131,13 +132,15 @@
(mf/defc grid-item-library*
{::mf/props :obj}
[{:keys [file]}]
[{:keys [file can-restore]}]
(mf/with-effect [file]
(when file
(let [font-ids (map :font-id (get-in file [:library-summary :typographies :sample] []))]
(run! fonts/ensure-loaded! font-ids))))
[:div {:class (stl/css :grid-item-th :library)}
[:div {:class (stl/css-case :grid-item-th true
:library true
:deleted-item can-restore)}
(if (nil? file)
[:> loader* {:class (stl/css :grid-loader)
:overlay true
@@ -250,7 +253,7 @@
counter-el))
(mf/defc grid-item*
[{:keys [file origin can-edit selected-files]}]
[{:keys [file origin can-edit selected-files can-restore]}]
(let [file-id (get file :id)
state (mf/deref refs/dashboard-local)
@@ -289,12 +292,13 @@
on-navigate
(mf/use-fn
(mf/deps file-id)
(mf/deps file-id can-restore)
(fn [event]
(let [menu-icon (mf/ref-val menu-ref)
target (dom/get-target event)]
(when-not (dom/child? target menu-icon)
(st/emit! (dcm/go-to-workspace :file-id file-id))))))
(when-not can-restore
(let [menu-icon (mf/ref-val menu-ref)
target (dom/get-target event)]
(when-not (dom/child? target menu-icon)
(st/emit! (dcm/go-to-workspace :file-id file-id)))))))
on-drag-start
(mf/use-fn
@@ -412,8 +416,8 @@
[:div {:class (stl/css :overlay)}]
(if ^boolean is-library-view?
[:> grid-item-library* {:file file}]
[:> grid-item-thumbnail* {:file file :can-edit can-edit}])
[:> grid-item-library* {:file file :can-restore can-restore}]
[:> grid-item-thumbnail* {:file file :can-edit can-edit :can-restore can-restore}])
(when (and (:is-shared file) (not is-library-view?))
[:div {:class (stl/css :item-badge)} deprecated-icon/library])
@@ -451,11 +455,12 @@
:on-edit on-edit
:on-close on-menu-close
:origin origin
:parent-id (dm/str file-id "-action-menu")}]])]]]]]))
:parent-id (dm/str file-id "-action-menu")
:can-restore can-restore}]])]]]]]))
(mf/defc grid*
{::mf/props :obj}
[{:keys [files project origin limit create-fn can-edit selected-files]}]
[{:keys [files project origin limit create-fn can-edit selected-files can-restore]}]
(let [dragging? (mf/use-state false)
project-id (get project :id)
team-id (get project :team-id)
@@ -535,7 +540,8 @@
:key (dm/str (:id item))
:origin origin
:selected-files selected-files
:can-edit can-edit}])])
:can-edit can-edit
:can-restore can-restore}])])
:else
[:> empty-grid-placeholder*
@@ -548,7 +554,7 @@
:on-finish-import on-finish-import}])]))
(mf/defc line-grid-row
[{:keys [files selected-files dragging? limit can-edit] :as props}]
[{:keys [files selected-files dragging? limit can-edit can-restore] :as props}]
(let [elements limit
limit (if dragging? (dec limit) limit)]
[:ul {:class (stl/css :grid-row :no-wrap)
@@ -563,10 +569,11 @@
:file item
:selected-files selected-files
:can-edit can-edit
:key (dm/str (:id item))}])]))
:key (dm/str (:id item))
:can-restore can-restore}])]))
(mf/defc line-grid
[{:keys [project team files limit create-fn can-edit] :as props}]
[{:keys [project team files limit create-fn can-edit can-restore] :as props}]
(let [dragging? (mf/use-state false)
project-id (:id project)
team-id (:id team)
@@ -664,7 +671,8 @@
:selected-files selected-files
:dragging? @dragging?
:can-edit can-edit
:limit limit}]
:limit limit
:can-restore can-restore}]
:else
[:> empty-grid-placeholder*

View File

@@ -375,3 +375,7 @@ $thumbnail-default-height: deprecated.$s-168; // Default width
.grid-loader {
--icon-width: calc(var(--th-width, #{$thumbnail-default-width}) * 0.25);
}
.deleted-item {
opacity: 0.5;
}

View File

@@ -21,6 +21,7 @@
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
[app.main.ui.dashboard.pin-button :refer [pin-button*]]
[app.main.ui.dashboard.project-menu :refer [project-menu*]]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as deprecated-icon]
@@ -342,7 +343,13 @@
(fn []
(reset! show-team-hero* false)
(st/emit! (ptk/data-event ::ev/event {::ev/name "dont-show-team-up-hero"
::ev/origin "dashboard"}))))]
::ev/origin "dashboard"}))))
on-deleted-click
(mf/use-fn
(mf/deps team-id)
(fn []
(st/emit! (dcm/go-to-dashboard-deleted :team-id team-id))))]
(mf/with-effect [show-team-hero?]
(swap! storage/global assoc ::show-team-hero show-team-hero?))
@@ -376,6 +383,15 @@
(not is-defalt-team?)
show-team-hero?
can-invite))}
[:div {:class (stl/css :nav-options)}
[:div {:class (stl/css :selected)
:data-testid "recent-tab"}
(tr "dashboard.labels.recent")]
[:> button* {:variant "ghost"
:type "button"
:data-testid "deleted-tab"
:on-click on-deleted-click}
(tr "dashboard.labels.deleted")]]
(for [{:keys [id] :as project} projects]
;; FIXME: refactor this, looks inneficient
(let [files (when recent-map

View File

@@ -4,16 +4,21 @@
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
@use "common/refactor/common-refactor.scss" as deprecated;
@use "common/refactor/common-dashboard";
@use "../ds/typography.scss" as t;
@use "../ds/_borders.scss" as *;
@use "../ds/spacing.scss" as *;
@use "../ds/_sizes.scss" as *;
@use "../ds/z-index.scss" as *;
.dashboard-container {
flex: 1 0 0;
width: 100%;
margin-right: deprecated.$s-16;
border-top: deprecated.$s-1 solid var(--panel-border-color);
margin-inline-end: var(--sp-l);
border-top: $b-1 solid var(--panel-border-color);
overflow-y: auto;
padding-bottom: deprecated.$s-32;
padding-bottom: var(--sp-xxxl);
}
.dashboard-projects {
@@ -27,16 +32,16 @@
.dashboard-shared {
width: calc(100vw - deprecated.$s-320);
margin-right: deprecated.$s-52;
margin-inline-end: deprecated.$s-52;
}
.search {
margin-top: deprecated.$s-12;
margin-block-start: var(--sp-m);
}
.dashboard-project-row {
--actions-opacity: 0;
margin-bottom: deprecated.$s-24;
margin-block-end: var(--sp-xxl);
position: relative;
&:hover,
@@ -60,12 +65,12 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: deprecated.$s-8;
gap: var(--sp-s);
width: 99%;
max-height: deprecated.$s-40;
padding: deprecated.$s-8 deprecated.$s-8 deprecated.$s-8 deprecated.$s-16;
margin-top: deprecated.$s-16;
border-radius: deprecated.$br-4;
max-height: $sz-40;
padding: var(--sp-s) var(--sp-s) var(--sp-s) var(--sp-l);
margin-block-start: var(--sp-l);
border-radius: $br-4;
}
.project-name-wrapper {
@@ -73,30 +78,29 @@
align-items: center;
justify-content: flex-start;
width: 100%;
min-height: deprecated.$s-32;
margin-left: deprecated.$s-8;
min-height: var(--sp-xxxl);
margin-inline-start: var(--sp-s);
}
.project-name {
@include deprecated.bodyLargeTypography;
@include deprecated.textEllipsis;
@include t.use-typography("body-large");
width: fit-content;
margin-right: deprecated.$s-12;
margin-inline-end: var(--sp-m);
line-height: 0.8;
color: var(--title-foreground-color-hover);
cursor: pointer;
height: deprecated.$s-16;
height: var(--sp-l);
}
.info-wrapper {
display: flex;
align-items: center;
gap: deprecated.$s-8;
gap: var(--sp-s);
}
.info,
.recent-files-row-title-info {
@include deprecated.bodyMediumTypography;
@include t.use-typography("body-medium");
color: var(--title-foreground-color);
@media (max-width: 760px) {
display: none;
@@ -106,16 +110,16 @@
.project-actions {
display: flex;
opacity: var(--actions-opacity);
margin-left: deprecated.$s-32;
margin-inline-start: var(--sp-xxxl);
}
.add-file-btn,
.options-btn {
@extend .button-tertiary;
height: deprecated.$s-32;
width: deprecated.$s-32;
margin: 0 deprecated.$s-8;
padding: deprecated.$s-8;
height: var(--sp-xxxl);
width: var(--sp-xxxl);
margin: 0 var(--sp-s);
padding: var(--sp-s);
}
.add-icon,
@@ -126,24 +130,24 @@
.grid-container {
width: 100%;
padding: 0 deprecated.$s-4;
padding: 0 var(--sp-xs);
}
.placeholder-placement {
margin: deprecated.$s-16 deprecated.$s-32;
margin: var(--sp-l) var(--sp-xxxl);
}
.show-more {
--show-more-color: var(--button-secondary-foreground-color-rest);
@include deprecated.buttonStyle;
@include deprecated.bodyMediumTypography;
@include t.use-typography("body-medium");
position: absolute;
top: deprecated.$s-8;
top: var(--sp-s);
right: deprecated.$s-52;
display: flex;
align-items: center;
justify-content: space-between;
column-gap: deprecated.$s-12;
column-gap: var(--sp-m);
color: var(--show-more-color);
&:hover {
@@ -152,8 +156,8 @@
}
.show-more-icon {
height: deprecated.$s-16;
width: deprecated.$s-16;
height: var(--sp-l);
width: var(--sp-l);
fill: none;
stroke: var(--show-more-color);
}
@@ -164,13 +168,13 @@
border-radius: deprecated.$br-8;
border: none;
display: flex;
margin: deprecated.$s-16;
padding: deprecated.$s-8;
margin: var(--sp-l);
padding: var(--sp-s);
position: relative;
img {
border-radius: deprecated.$br-4;
height: deprecated.$s-200;
border-radius: $br-4;
height: var(--sp-xl) 0;
width: auto;
@media (max-width: 1200px) {
@@ -185,18 +189,18 @@
flex-direction: column;
align-items: flex-start;
flex-grow: 1;
padding: deprecated.$s-20 deprecated.$s-20;
padding: var(--sp-xl) var(--sp-xl);
}
.title {
font-size: deprecated.$fs-24;
font-size: $sz-24;
color: var(--color-foreground-primary);
font-weight: deprecated.$fw400;
}
.info {
flex: 1;
font-size: deprecated.$fs-16;
font-size: $sz-16;
span {
color: var(--color-foreground-secondary);
display: block;
@@ -204,15 +208,15 @@
a {
color: var(--color-accent-primary);
}
padding: deprecated.$s-8 0;
padding: var(--sp-s) 0;
}
.close {
--close-icon-foreground-color: var(--icon-foreground);
position: absolute;
top: deprecated.$s-20;
right: deprecated.$s-24;
width: deprecated.$s-24;
top: var(--sp-xl);
right: var(--sp-xxl);
width: var(--sp-xxl);
background-color: transparent;
border: none;
cursor: pointer;
@@ -227,7 +231,7 @@
}
.invite {
height: deprecated.$s-32;
height: var(--sp-xxxl);
width: deprecated.$s-180;
}
@@ -235,8 +239,8 @@
display: flex;
align-items: center;
justify-content: center;
width: deprecated.$s-200;
height: deprecated.$s-200;
width: var(--sp-xl) 0;
height: var(--sp-xl) 0;
overflow: hidden;
border-radius: deprecated.$br-4;
@media (max-width: 1200px) {
@@ -244,3 +248,26 @@
width: 0;
}
}
.nav-options {
display: flex;
gap: var(--sp-l);
justify-content: space-between;
border-bottom: $b-1 solid var(--panel-border-color);
padding-inline-start: var(--sp-l);
background: var(--color-background-default);
position: sticky;
top: 0;
z-index: var(--z-index-panels);
}
.selected {
@include t.use-typography("headline-small");
display: flex;
align-items: center;
justify-content: center;
color: var(--color-foreground-primary);
border: $b-1 solid transparent;
border-bottom: $b-1 solid var(--color-foreground-primary);
padding: 0 var(--sp-m);
}

View File

@@ -27,11 +27,11 @@
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
[app.main.ui.dashboard.project-menu :refer [project-menu*]]
[app.main.ui.dashboard.subscription :refer [subscription-sidebar*
[app.main.ui.dashboard.subscription :refer [dashboard-cta*
get-subscription-type
menu-team-icon*
dashboard-cta*
show-subscription-dashboard-banner?
get-subscription-type]]
subscription-sidebar*]]
[app.main.ui.dashboard.team-form]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.icons :as deprecated-icon]

View File

@@ -12,6 +12,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.color :as clr]
[app.main.data.dashboard :as dd]
[app.main.data.exports.assets :as de]
[app.main.data.modal :as modal]
[app.main.refs :as refs]
@@ -205,10 +206,13 @@
:cmd :export-frames
:origin origin}]))
(mf/defc export-progress-widget
(mf/defc progress-widget
{::mf/wrap [mf/memo]}
[]
(let [state (mf/deref refs/export)
[{:keys [operation] :or {operation :export}}]
(let [state (mf/deref (case operation
:export refs/export
:restore refs/restore
refs/export))
profile (mf/deref refs/profile)
theme (or (:theme profile) theme/default)
is-default-theme? (= theme/default theme)
@@ -217,11 +221,14 @@
detail-visible? (:detail-visible state)
widget-visible? (:widget-visible state)
progress (:progress state)
exports (:exports state)
total (count exports)
items (case operation
:export (:exports state)
:restore (:files state)
[])
total (or (:total state) (count items))
complete? (= progress total)
circ (* 2 Math/PI 12)
pct (- circ (* circ (/ progress total)))
pct (if (zero? total) circ (- circ (* circ (/ progress total))))
pwidth
(if error?
@@ -243,19 +250,43 @@
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"))
error? (case operation
:export (tr "workspace.options.exporting-object-error")
:restore (tr "workspace.options.restoring-object-error")
(tr "workspace.options.processing-object-error"))
complete? (case operation
:export (tr "workspace.options.exporting-complete")
:restore (tr "workspace.options.restoring-complete")
(tr "workspace.options.processing-complete"))
healthy? (case operation
:export (tr "workspace.options.exporting-object")
:restore (tr "workspace.options.restoring-object")
(tr "workspace.options.processing-object"))
(not healthy?) (case operation
:export (tr "workspace.options.exporting-object-slow")
:restore (tr "workspace.options.restoring-object-slow")
(tr "workspace.options.processing-object-slow")))
retry-last-export
(mf/use-fn #(st/emit! (de/retry-last-export)))
retry-last-operation
(mf/use-fn
(mf/deps operation)
(fn []
(case operation
:export (st/emit! (de/retry-last-export))
:restore (st/emit! (dd/retry-last-restore))
nil)))
toggle-detail-visibility
(mf/use-fn #(st/emit! (de/toggle-detail-visibililty)))]
(mf/use-fn
(mf/deps operation)
(fn []
(case operation
:export (st/emit! (de/toggle-detail-visibililty))
:restore (st/emit! (dd/toggle-restore-detail-visibility))
nil)))]
[:*
(when widget-visible?
(when (and widget-visible? (= operation :export))
[:div {:class (stl/css :export-progress-widget)
:on-click toggle-detail-visibility}
[:svg {:width "24" :height "24"}
@@ -283,11 +314,11 @@
error-icon
neutral-icon)
[:p {:class (stl/css :export-progress-title)}
title
[:div {:class (stl/css :export-progress-title)}
[:div {:class (stl/css :title-text)} title]
(if error?
[:button {:class (stl/css :retry-btn)
:on-click retry-last-export}
:on-click retry-last-operation}
(tr "workspace.options.retry")]
[:span {:class (stl/css :progress)}

View File

@@ -64,7 +64,8 @@
["/fonts" :dashboard-fonts]
["/fonts/providers" :dashboard-font-providers]
["/libraries" :dashboard-libraries]
["/files" :dashboard-files]]
["/files" :dashboard-files]
["/deleted" :dashboard-deleted]]
["/dashboard/team/:team-id"
["/members" :dashboard-legacy-team-members]

View File

@@ -14,7 +14,7 @@
[app.main.data.viewer.shortcuts :as sc]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.exports.assets :refer [export-progress-widget]]
[app.main.ui.exports.assets :refer [progress-widget]]
[app.main.ui.formats :as fmt]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.viewer.comments :refer [comments-menu]]
@@ -167,7 +167,7 @@
(open-share-dialog)))
[:div {:class (stl/css :options-zone)}
[:& export-progress-widget]
[:& progress-widget {:operation :export}]
(case section
:interactions [:*

View File

@@ -22,7 +22,7 @@
[app.main.ui.dashboard.team]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.exports.assets :refer [export-progress-widget]]
[app.main.ui.exports.assets :refer [progress-widget]]
[app.main.ui.formats :as fmt]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.workspace.presence :refer [active-sessions]]
@@ -200,7 +200,7 @@
[:div {:class (stl/css :users-section)}
[:& active-sessions]]
[:& export-progress-widget]
[:& progress-widget {:operation :export}]
[:div {:class (stl/css :separator)}]

View File

@@ -6,7 +6,7 @@
(ns app.main.ui.workspace.shapes.text.editor
(:require
["draft-js" :as draft]
["@penpot/draft-js" :as draft]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]

View File

@@ -271,8 +271,10 @@
:font-variant-id new-variant-id
:font-weight (:weight variant)
:font-style (:style variant)}))
(dom/blur! (dom/get-target new-variant-id)))))
;; NOTE: the select component we are using does not fire on-blur event
;; so we need to call on-blur manually
(when (some? on-blur)
(on-blur)))))
on-font-select
(mf/use-fn

View File

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

View File

@@ -223,7 +223,6 @@
(cond
(= existing ::not-found) (assoc acc t-attr new-val)
(= existing new-val) acc
(nil? new-val) acc
:else (assoc acc t-attr :multiple))))
merge-shape-attr

View File

@@ -268,8 +268,8 @@
:on-click modal/hide!}
(tr "labels.cancel")]
[:> import-type-dropdown*
{:options [{:label (tr "workspace.tokens.import-menu-zip-option") :value :zip}
{:label (tr "workspace.tokens.import-menu-json-option") :value :file}
{:options [{:label (tr "workspace.tokens.import-menu-json-option") :value :file}
{:label (tr "workspace.tokens.import-menu-zip-option") :value :zip}
{:label (tr "workspace.tokens.import-menu-folder-option") :value :folder}]
:on-click handle-import-action
:text-render render-button-text

View File

@@ -19,5 +19,5 @@
}
.threads {
position: fixed;
position: absolute;
}

View File

@@ -54,6 +54,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]))
@@ -1195,7 +1196,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

@@ -194,7 +194,12 @@
:addToken
(fn [type-str name value]
(let [type (cto/dtcg-token-type->token-type type-str)]
(let [type (cto/dtcg-token-type->token-type type-str)
value (case type
:font-family (ctob/convert-dtcg-font-family (js->clj value))
:typography (ctob/convert-dtcg-typography-composite (js->clj value))
:shadow (ctob/convert-dtcg-shadow-composite (js->clj value))
(js->clj value))]
(cond
(nil? type)
(u/display-not-valid :addTokenType type-str)

View File

@@ -18,6 +18,7 @@
[app.common.types.path :as path]
[app.common.types.path.impl :as path.impl]
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as txt]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.main.refs :as refs]
@@ -964,8 +965,8 @@
(set-shape-children children)
(set-shape-corners corners)
(set-shape-blur blur)
(when (and (= type :group) masked)
(set-masked masked))
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)
(set-shape-bool-type bool-type))
(when (and (some? content)
@@ -1411,20 +1412,23 @@
(get span))
text (subs (:text element) start-pos end-pos)]
{:x x
:y (+ y height)
:width width
:height height
:direction (dr/translate-direction direction)
:font-family (get element :font-family)
:font-size (get element :font-size)
:font-weight (get element :font-weight)
:text-transform (get element :text-transform)
:text-decoration (get element :text-decoration)
:letter-spacing (get element :letter-spacing)
:font-style (get element :font-style)
:fills (get element :fills)
:text text}))))]
(d/patch-object
txt/default-text-attrs
(d/without-nils
{:x x
:y (+ y height)
:width width
:height height
:direction (dr/translate-direction direction)
:font-family (get element :font-family)
:font-size (get element :font-size)
:font-weight (get element :font-weight)
:text-transform (get element :text-transform)
:text-decoration (get element :text-decoration)
:letter-spacing (get element :letter-spacing)
:font-style (get element :font-style)
:fills (get element :fills)
:text text}))))))]
(mem/free)
result)))

View File

@@ -233,8 +233,8 @@
(api/set-shape-shadows (:shadow shape)))
:masked-group
(when (cfh/mask-shape? shape)
(api/set-masked (:masked-group shape)))
(when (cfh/group-shape? shape)
(api/set-masked (boolean (:masked-group shape))))
:content
(cond

View File

@@ -6,11 +6,11 @@
(ns app.util.code-highlight
(:require
["@penpot/hljs" :as hljs]
["highlight.js" :as hljs]
[app.util.dom :as dom]))
(defn highlight!
{:lazy-loadable true}
[node]
(dom/set-data! node "highlighted" nil)
(hljs/highlightElement node))
(.highlightElement hljs/default node))

View File

@@ -50,6 +50,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"}
@@ -69,7 +70,6 @@
(-> (.-language globals/navigator)
(parse-locale))))
;; Set initial translation loading state as globaly stored variable;
;; this facilitates hot reloading
(when-not (exists? (unchecked-get globals/global "penpotTranslations"))
@@ -93,14 +93,8 @@
(def ^:dynamic *current-locale*
(get-current))
(defonce state
(l/atom {:render 0 :locale *current-locale*}))
(defn- assign-current-locale
[state locale]
(-> state
(update :render inc)
(assoc :locale locale)))
(defonce locale
(l/atom *current-locale*))
(defn- get-translations
"Get globaly stored mutable object with all loaded translations"
@@ -114,6 +108,10 @@
(unchecked-set translations locale data)
nil))
(defn set-default-translations
[data]
(set-translations cf/default-language data))
(defn- load
[locale]
(let [path (str "./translation." locale ".js?version=" (:full cf/version))]
@@ -122,15 +120,14 @@
(p/fnly (fn [data cause]
(if cause
(js/console.error "unexpected error on fetching locale" cause)
(do
(set! *current-locale* locale)
(set-translations locale data)
(swap! state assign-current-locale locale))))))))
(set-translations locale data)))))))
(defn init
"Initialize the i18n module"
[]
(load *current-locale*))
(load *current-locale*)
(when-not (= *current-locale* cf/default-language)
(load cf/default-language)))
(defn set-locale
[lname]
@@ -145,7 +142,10 @@
(recur (rest locales)))
cf/default-language))))]
(load lname)))
(->> (load lname)
(p/fnly (fn [_r _c]
(set! *current-locale* lname)
(reset! locale lname))))))
(deftype C [val]
IDeref
@@ -206,9 +206,7 @@
:className class
:on-click on-click}]))
(add-watch state "common.time"
(add-watch locale "common.time"
(fn [_ _ pv cv]
(let [pv (get pv :locale)
cv (get cv :locale)]
(when (not= pv cv)
(ct/set-default-locale! cv)))))
(when (not= pv cv)
(ct/set-default-locale cv))))

View File

@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-22 10:51+0000\n"
"Last-Translator: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Shuaib Zahda <shuaib.zahda@gmail.com>\n"
"Language-Team: Arabic <https://hosted.weblate.org/projects/penpot/frontend/"
"ar/>\n"
"Language: ar\n"
@@ -10,7 +10,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"X-Generator: Weblate 5.15-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"
@@ -402,8 +402,10 @@ msgstr ""
"أصولهم*؟"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr "سيتم ادراج الملفات التي لها مكتبات مشتركة في التصدير، مع الحفاظ على روابطهم."
msgstr ""
"سيتم ادراج الملفات التي لها مكتبات مشتركة في التصدير، مع الحفاظ على روابطهم."
#: src/app/main/ui/exports/files.cljs:165
msgid "dashboard.export.options.all.title"

View File

@@ -1575,10 +1575,6 @@ msgstr ""
"Pokud se chcete dozvědět více o inspektorovi designu, navštivte centrum "
"nápovědy společnosti Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Více informací o inspektorovi"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""

View File

@@ -1,19 +1,19 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-03 20:51+0000\n"
"Last-Translator: Stas Haas <stas@girafic.de>\n"
"Language-Team: German "
"<https://hosted.weblate.org/projects/penpot/frontend/de/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: German <https://hosted.weblate.org/projects/penpot/frontend/"
"de/>\n"
"Language: de\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.1-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"
msgstr "Sie haben schon ein Konto?"
msgstr "Sie haben bereits ein Konto?"
#: src/app/main/ui/auth/recovery_request.cljs:113, src/app/main/ui/auth/register.cljs:238
msgid "auth.check-mail"
@@ -584,10 +584,11 @@ msgstr ""
"machen?"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr ""
"Dateien mit geteilten Bibliotheken werden exportiert, und ihre "
"Verknüpfungen bleiben erhalten."
"Dateien mit geteilten Bibliotheken werden exportiert, und ihre Verknüpfungen "
"bleiben erhalten."
#: src/app/main/ui/exports/files.cljs:165
msgid "dashboard.export.options.all.title"
@@ -1799,10 +1800,6 @@ msgstr ""
"Für weitere Informationen zum Thema \"Auswerten von "
"Design-Spezifikationen\", besuchen Sie bitte das Penpot-Hilfezentrum"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Weitere Informationen zur Inspektion"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""
@@ -1810,6 +1807,7 @@ msgstr ""
"Eigenschaften und Code zu überprüfen"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Info zur Ebene"
@@ -2630,7 +2628,7 @@ msgstr "Eigene Schriftarten hochladen"
#: src/app/main/ui/dashboard/fonts.cljs:252
msgid "labels.uploading"
msgstr "Hochladen…"
msgstr "Lädt hoch…"
#: src/app/main/ui/inspect/right_sidebar.cljs:65, src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1028
msgid "labels.variant"
@@ -3907,7 +3905,7 @@ msgstr "Kopieren"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:96
msgid "shortcuts.copy-link"
msgstr "Link in die Zwischenablage kopieren"
msgstr "Link kopieren"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:106
#, unused
@@ -6705,7 +6703,7 @@ msgstr "Als CSS kopieren (verschachtelte Ebenen)"
#: src/app/main/ui/workspace/context_menu.cljs:202
msgid "workspace.shape.menu.copy-link"
msgstr "Link in die Zwischenablage kopieren"
msgstr "Link kopieren"
#: src/app/main/ui/workspace/context_menu.cljs:215
msgid "workspace.shape.menu.copy-paste-as"
@@ -7504,3 +7502,662 @@ msgstr "Automatisch gespeicherte Versionen werden für %s Tage aufbewahrt."
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Klicken Sie, um den Pfad zu schließen"
#: src/app/main/ui/dashboard/team.cljs:765
msgid "dashboard.invitation-modal.delete"
msgstr "Diese Einladungen werden gelöscht:"
#: src/app/main/ui/dashboard/team.cljs:766
msgid "dashboard.invitation-modal.resend"
msgstr "Diese Einladungen werden erneut gesendet:"
#: src/app/main/ui/dashboard/team.cljs:933
msgid "team.invitations-selected"
msgid_plural "team.invitations-selected"
msgstr[0] "Eine Einladung ausgewählt"
msgstr[1] "%s Einladungen ausgewählt"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:121
msgid "workspace.tokens.themes-description"
msgstr ""
"Hier können Sie ihre Themes verwalten, ein- oder ausschalten und aktive Sets "
"auswählen."
#: src/app/main/ui/workspace/tokens/management.cljs:143
msgid "workspace.tokens.inactive-set"
msgstr "Inaktiv"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:174
msgid "workspace.tokens.no-active-sets"
msgstr "Keine Sets aktiviert"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:173
msgid "workspace.tokens.num-active-sets"
msgstr "%s aktivierte Sets"
#: src/app/main/ui/workspace/colorpicker/color_tokens.cljs:35
msgid "color-token.empty-state"
msgstr ""
"Keine Farb-Tokens vorhanden. Aktivieren sie Sets und/oder Themes oder fügen "
"Sie neue Tokens hinzu."
#: src/app/main/ui/ds/controls/numeric_input.cljs:99
msgid "ds.inputs.numeric-input.no-applicable-tokens"
msgstr ""
"Es sind keine passenden Tokens in aktivierten Sets oder Themes vorhanden."
#: 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 "Dieser Token ist nicht Teil eines aktiven Sets oder ungültig."
#: src/app/main/errors.cljs:307
msgid "errors.deprecated"
msgstr ""
"Tut uns leid! Diese Datei beinhaltet veraltete Penpot-Assets und kann "
"deshalb nicht geöffnet werden."
#: src/app/main/errors.cljs:310
msgid "errors.deprecated.contact.after"
msgstr "damit wir Ihnen helfen können."
#: 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 "Darf maximal %s Zeichen enthalten."
#: src/app/main/ui/settings/feedback.cljs:143
msgid "feedback.other-ways-contact"
msgstr "Weitere Möglichkeiten uns zu kontaktieren"
#: src/app/main/ui/settings/feedback.cljs:122
msgid "feedback.description-placeholder"
msgstr "Bitte beschreiben Sie ihr Feedback"
#: src/app/main/ui/settings/feedback.cljs:126
msgid "feedback.penpot.link"
msgstr ""
"Wenn sich Ihr Feedback auf eine Datei oder Projekt bezieht, können Sie einen "
"Link hinzufügen:"
#: src/app/main/ui/settings/feedback.cljs:101
msgid "feedback.title-contact-us"
msgstr "Kontakt"
#: src/app/main/ui/settings/feedback.cljs:110, src/app/main/ui/settings/feedback.cljs:111
msgid "feedback.type"
msgstr "Art"
#: src/app/main/ui/settings/feedback.cljs:115
msgid "feedback.type.doubt"
msgstr "Bedenken"
#: src/app/main/ui/settings/feedback.cljs:113
msgid "feedback.type.idea"
msgstr "Idee"
#: src/app/main/ui/settings/feedback.cljs:114
msgid "feedback.type.issue"
msgstr "Problem"
#: src/app/main/ui/inspect/styles/rows/color_properties_row.cljs:120
msgid "inspect.attributes.image.preview"
msgstr "Bildvorschau"
#: src/app/main/ui/inspect/right_sidebar.cljs:170
msgid "inspect.color-space-label"
msgstr "Farbraum auswählen"
#: src/app/main/ui/inspect/styles/style_box.cljs:68
msgid "inspect.tabs.styles.copy-shorthand"
msgstr "CSS-Stile kopieren"
#: 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-Schaltfläche ein-/ausblenden"
#: src/app/main/ui/inspect/styles/style_box.cljs:21
msgid "inspect.tabs.styles.token-panel"
msgstr "Token Sets und Themes"
#: 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 "Festgelegter Wert:"
#: src/app/main/ui/inspect/styles/style_box.cljs:26
msgid "labels.blur"
msgstr "Weichzeichnen"
#: src/app/main/ui/dashboard/sidebar.cljs:1031
msgid "labels.community-contributions"
msgstr "Community & Beiträge"
#: src/app/main/ui/static.cljs:406
msgid "labels.contact-support"
msgstr "Support kontaktieren"
#: src/app/main/ui/settings/sidebar.cljs:136
msgid "labels.contact-us"
msgstr "Kontakt"
#: src/app/main/ui/static.cljs:68
msgid "labels.copyright-period"
msgstr "Kaleidos © 2019-heute"
#: src/app/main/ui/settings/feedback.cljs:134, src/app/main/ui/static.cljs:400
msgid "labels.download"
msgstr "%s herunterladen"
#: src/app/main/ui/inspect/styles/style_box.cljs:23
msgid "labels.fill"
msgstr "Fläche"
#: src/app/main/ui/dashboard/sidebar.cljs:1020
msgid "labels.help-learning"
msgstr "Hilfe & Lernen"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:667
msgid "labels.reference"
msgstr "Referenz"
#: src/app/main/ui/dashboard/team.cljs:788
msgid "labels.resend"
msgstr "Erneut senden"
#: src/app/main/ui/dashboard/sidebar.cljs:873
msgid "labels.version-notes"
msgstr "Hinweise zu Version %s"
#: src/app/main/ui/inspect/styles/style_box.cljs:32
msgid "labels.visibility"
msgstr "Sichtbarkeit"
#: src/app/main/ui/static.cljs:397
msgid "labels.internal-error.desc-message-second"
msgstr "Probieren Sie es erneut oder kontaktieren Sie unseren Support."
#: src/app/main/ui/ds/product/loader.cljs:26
msgid "loader.tips.04.title"
msgstr ""
#: src/app/main/ui/dashboard/team.cljs:825
msgid "notifications.invitation-deleted"
msgstr "Einladung erfolgreich widerrufen"
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:97
msgid "shortcuts.create-component-variant"
msgstr "Komponente / Variante erstellen"
#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:81
msgid "workspace.assets.component-group-options"
msgstr "Optionen für diese Gruppe"
#: src/app/main/ui/workspace/colorpicker.cljs:427, src/app/main/ui/workspace/colorpicker.cljs:439
msgid "workspace.colorpicker.color-tokens"
msgstr "Farb-Tokens"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:499
msgid "workspace.component.swap.loop-error"
msgstr "Komponenten können nicht in sich selbst verschachtelt werden."
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:498
msgid "workspace.component.switch.loop-error-multi"
msgstr ""
"Einige Elemente konnten nicht ausgetauscht werden. Komponenten können nicht "
"in sich selbst verschachtelt werden."
#: 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 Farbe"
msgstr[1] "%s Farben"
#: 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 Komponente"
msgstr[1] "%s Komponenten"
#: 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] "%s Grafik"
msgstr[1] "%s Grafiken"
#: 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 Textstil"
msgstr[1] "%s Textstile"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1055
msgid "workspace.options.component.unlinked"
msgstr "Getrennt"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1330
msgid "workspace.options.component.variant.duplicated.group.locate"
msgstr "Doppelte Varianten anzeigen"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:1327
msgid "workspace.options.component.variant.duplicated.group.title"
msgstr "Einige Varianten haben identische Eigenschaften und Werte"
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:268
msgid "workspace.options.component.variant.duplicated.single.all"
msgstr ""
"Diese Varianten haben identische Eigenschaften und Werte. Geben Sie jeder "
"Variante einen eindeutigen Wert."
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:265
msgid "workspace.options.component.variant.duplicated.single.one"
msgstr ""
"Diese Variante hat die selben Eigenschaften order Werte wie eine andere "
"Variante. Geben Sie jeder Variante einen eindeutigen Wert."
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:271
msgid "workspace.options.component.variant.duplicated.single.some"
msgstr ""
"Einige dieser Varianten haben identische Eigenschaften und Werte. Geben Sie "
"jeder Variante einen eindeutigen Wert."
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:550
msgid "workspace.options.component.variant.malformed.copy"
msgstr ""
"Diese Komponente hat Varianten mit ungültigen Namen. Stellen Sie sicher, das "
"jede Variante korrekt benannt ist."
#: src/app/main/ui/workspace/sidebar/options/menus/component.cljs:424
msgid "workspace.options.component.variant.malformed.structure.title"
msgstr "Versuchen Sie diesem Schema zu folgen:"
#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:54
msgid "workspace.options.component.variants-help-modal.intro"
msgstr ""
"Um Änderungen beizubehalten wenn Sie Varianten wechseln, verbindet Penpot "
"Ebenen die:"
#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:91
msgid "workspace.options.component.variants-help-modal.outro"
msgstr ""
"Jede Änderung (z.B. Ebenen umbenennen oder gruppieren) trennt die "
"Verbindung. Änderungen können rückgängig gemacht werden, um die Verbindung "
"wiederherzustellen."
#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:67
msgid "workspace.options.component.variants-help-modal.rule1"
msgstr "Den gleichen Namen haben."
#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:76
msgid "workspace.options.component.variants-help-modal.rule2"
msgstr "Vom gleichen Typ sind."
#: src/app/main/ui/workspace/sidebar/options/menus/variants_help_modal.cljs:77
msgid "workspace.options.component.variants-help-modal.rule2.detail"
msgstr ""
"Rechtecke, Ellipsen, Pfade und boolesche Operationen gelten als der gleiche "
"Typ."
#: src/app/main/data/workspace/tokens/library_edit.cljs:209, src/app/main/data/workspace/tokens/library_edit.cljs:452
msgid "workspace.tokens.duplicate-suffix"
msgstr "Kopie"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:581
msgid "workspace.tokens.edit-token"
msgstr "%s Token bearbeiten"
#: src/app/main/data/workspace/tokens/errors.cljs:41
msgid "workspace.tokens.empty-input"
msgstr "Der Token-Wert darf nicht leer sein"
#: src/app/main/data/workspace/tokens/errors.cljs:15
msgid "workspace.tokens.error-parse"
msgstr "Fehler beim Importieren. JSON konnte nicht verarbeitet werden."
#: src/app/main/ui/workspace/tokens/export/modal.cljs:49
msgid "workspace.tokens.export"
msgstr "Export"
#: src/app/main/ui/workspace/tokens/export/modal.cljs:125
msgid "workspace.tokens.export-tokens"
msgstr "Tokens Exportieren"
#: src/app/main/ui/workspace/tokens/export/modal.cljs:118
msgid "workspace.tokens.export.multiple-files"
msgstr "Mehrere Dateien"
#: src/app/main/ui/workspace/tokens/export/modal.cljs:38
msgid "workspace.tokens.export.no-tokens-themes-sets"
msgstr "Es sind keine Tokens, Themes oder Sets zum Exportieren vorhanden."
#: src/app/main/ui/workspace/tokens/export/modal.cljs:35
msgid "workspace.tokens.export.preview"
msgstr "Vorschau:"
#: src/app/main/ui/workspace/tokens/export/modal.cljs:116
msgid "workspace.tokens.export.single-file"
msgstr "Einzeln"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1339
msgid "workspace.tokens.font-size-value-enter"
msgstr "Schriftgröße oder {alias}"
#: src/app/main/data/workspace/tokens/application.cljs:323
msgid "workspace.tokens.font-variant-not-found"
msgstr ""
"Fehler beim Festlegen der Schriftstärke/des Schriftstils. Dieser Schriftstil "
"ist in der aktuellen Schriftart nicht vorhanden"
#: 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 "Schriftstärke (300, Fett Kursiv) oder {alias}"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:225
msgid "workspace.tokens.gaps"
msgstr "Zwischenräume"
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs
#, unused
msgid "workspace.tokens.generic-error"
msgstr "Fehler: "
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:129
msgid "workspace.tokens.group-name"
msgstr "Gruppenname"
#: src/app/main/ui/workspace/tokens/import/modal.cljs:233
msgid "workspace.tokens.import-button-prefix"
msgstr "%s importieren"
#: src/app/main/data/workspace/tokens/errors.cljs:32, src/app/main/data/workspace/tokens/errors.cljs:37
msgid "workspace.tokens.import-error"
msgstr "Fehler beim Import:"
#: src/app/main/ui/workspace/tokens/import/modal.cljs:273
msgid "workspace.tokens.import-menu-folder-option"
msgstr "Ordner"
#: src/app/main/ui/workspace/tokens/import/modal.cljs:272
msgid "workspace.tokens.import-menu-json-option"
msgstr "Einzelne JSON-Datei"
#: src/app/main/ui/workspace/tokens/import/modal.cljs:271
msgid "workspace.tokens.import-menu-zip-option"
msgstr "ZIP-Datei"
#: src/app/main/ui/workspace/tokens/import/modal.cljs:241
msgid "workspace.tokens.import-multiple-files"
msgstr ""
"Bei mehreren Dateien werden die Datei- oder Pfadnamen zum Benennen der Sets "
"verwendet."
#: src/app/main/ui/workspace/tokens/import/modal.cljs:240
msgid "workspace.tokens.import-single-file"
msgstr ""
"Bei einer einzelnen JSON-Datei sollten die Keys der ersten Ebene die Namen "
"der Token-Sets sein."
#: src/app/main/ui/workspace/tokens/import/modal.cljs:237
msgid "workspace.tokens.import-tokens"
msgstr "Token importieren"
#: src/app/main/ui/workspace/tokens/sidebar.cljs:414, src/app/main/ui/workspace/tokens/sidebar.cljs:415
#, unused
msgid "workspace.tokens.import-tooltip"
msgstr ""
"Beim Importieren einer JSON-Datei werden alle bestehenden Tokens, Sets und "
"Themes überschrieben"
#: src/app/main/ui/workspace/tokens/import/modal.cljs:247
msgid "workspace.tokens.import-warning"
msgstr ""
"Beim Import werden alle bestehenden Tokens, Sets und Themes überschrieben."
#: src/app/main/ui/workspace/tokens/management.cljs:134
msgid "workspace.tokens.inactive-set-description"
msgstr ""
"Dieses Set ist nicht aktiv. Ändern Sie das Theme oder aktivieren Sie dieses "
"Set, um Änderungen im Anzeigebereich sehen zu können"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:741
msgid "workspace.tokens.individual-tokens"
msgstr "Einzelne Token verwenden"
#: src/app/main/data/workspace/tokens/errors.cljs:49
msgid "workspace.tokens.invalid-color"
msgstr "Ungültiger Farbwert: %s"
#: src/app/main/data/workspace/tokens/errors.cljs:89
msgid "workspace.tokens.invalid-font-weight-token-value"
msgstr ""
"Ungültiger Wert für die Schriftstärke: Verwenden Sie numerische Werte (100"
"950) oder Standardbezeichnungen (dünn, leicht, normal, fett usw.), optional "
"gefolgt von „kursiv”"
#: src/app/main/data/workspace/tokens/errors.cljs:23
msgid "workspace.tokens.invalid-json"
msgstr "Fehler beim Importieren: Ihre JSON-Datei enthält ungültige Token-Daten."
#: src/app/main/data/workspace/tokens/errors.cljs:27
msgid "workspace.tokens.invalid-json-token-name"
msgstr "Fehler beim Importieren: Ihre JSON-Datei enthält ungültige Token-Namen."
#: src/app/main/data/workspace/tokens/errors.cljs:28
msgid "workspace.tokens.invalid-json-token-name-detail"
msgstr ""
"„%s“ ist kein gültiger Namen für ein Token.\n"
"Token-Namen dürfen nur Buchstaben und Ziffern enthalten, die durch Punkte "
"getrennt sind und dürfen nicht mit einem Dollarzeichen beginnen."
#: src/app/main/data/workspace/tokens/errors.cljs:101
msgid "workspace.tokens.invalid-shadow-type-token-value"
msgstr ""
"Ungültiger Schattentyp: Es werden nur „innerShadow” oder „dropShadow” "
"akzeptiert"
#: src/app/main/data/workspace/tokens/errors.cljs:81
msgid "workspace.tokens.invalid-text-case-token-value"
msgstr ""
"Ungültiger Token-Wert: Es werden nur „none“, „Uppercase“, „Lowercase“ oder „"
"Capitalize“ akzeptiert"
#: src/app/main/data/workspace/tokens/errors.cljs:85
msgid "workspace.tokens.invalid-text-decoration-token-value"
msgstr ""
"Ungültiger Token-Wert: Es werden nur „none“, „underline“ oder „strike-through"
"“ akzeptiert"
#: src/app/main/data/workspace/tokens/errors.cljs:93
msgid "workspace.tokens.invalid-token-value-typography"
msgstr ""
"Ungültiger Wert: Der Wert muss auf ein zusammengesetztes Typografie-Token "
"verweisen."
#: src/app/main/data/workspace/tokens/errors.cljs:61, src/app/main/data/workspace/tokens/errors.cljs:73, src/app/main/data/workspace/tokens/errors.cljs:77
msgid "workspace.tokens.invalid-value"
msgstr "Ungültiger Token-Wert: %s"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:229
msgid "workspace.tokens.label.group-placeholder"
msgstr "Gruppe hinzufügen (z. B. „Modus“)"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:236
msgid "workspace.tokens.label.theme-placeholder"
msgstr "Theme hinzufügen (z. B. „Hell“)"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1351
msgid "workspace.tokens.letter-spacing-value-enter-composite"
msgstr "Zeichenabstand oder {alias}"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1347
msgid "workspace.tokens.line-height-value-enter"
msgstr "Zeilenabstand (Multiplikator, px, %) oder {alias}"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:217
msgid "workspace.tokens.margins"
msgstr "Abstände"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:253
msgid "workspace.tokens.max-size"
msgstr "Maximale Größe"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:247
msgid "workspace.tokens.min-size"
msgstr "Mindestgröße"
#: src/app/main/data/workspace/tokens/errors.cljs:57
msgid "workspace.tokens.missing-references"
msgstr "Fehlende Token-Referenzen: "
#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:123
msgid "workspace.tokens.more-options"
msgstr "Rechtsklick, um Optionen anzuzeigen"
#: src/app/main/data/workspace/tokens/errors.cljs:19
msgid "workspace.tokens.no-token-files-found"
msgstr "In dieser Datei wurden keine Tokens, Sets oder Themen gefunden."
#: src/app/main/data/workspace/tokens/errors.cljs:53
msgid "workspace.tokens.number-too-large"
msgstr "Ungültiger Tokenwert. Der ermittelte Wert ist zu hoch: %s"
#: src/app/main/data/workspace/tokens/errors.cljs:73, src/app/main/data/workspace/tokens/warnings.cljs:15
msgid "workspace.tokens.opacity-range"
msgstr ""
"Die Deckkraft muss zwischen 0 und 100 % oder zwischen 0 und 1 liegen (z. B. "
"50 % oder 0,5)."
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:201
msgid "workspace.tokens.paddings"
msgstr "Innenabstände"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:277
msgid "workspace.tokens.radius"
msgstr "Radius"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:775
msgid "workspace.tokens.reference-composite"
msgstr "Geben Sie einen Typografie-Alias für diesen Token ein"
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs
#, unused
msgid "workspace.tokens.reference-error"
msgstr "Referenzfehler: "
#: src/app/main/data/workspace/tokens/errors.cljs:45, src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:55
msgid "workspace.tokens.self-reference"
msgstr "Der Token referenziert sich selbst"
#: src/app/main/ui/workspace/tokens/sets/lists.cljs:60
msgid "workspace.tokens.set-edit-placeholder"
msgstr "Namen eingeben (für Gruppen „/“ verwenden)"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:168
msgid "workspace.tokens.sets-hint"
msgstr "Theme bearbeiten und Sets verwalten"
#: src/app/main/ui/workspace/tokens/settings/menu.cljs:91
msgid "workspace.tokens.setting-description"
msgstr ""
"Hier können Sie die grundlegende Schriftgröße festlegen, die als Wert für "
"1rem verwendet wird:"
#: src/app/main/ui/workspace/tokens/settings/menu.cljs:84
msgid "workspace.tokens.settings"
msgstr "Token-Einstellungen"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1084
msgid "workspace.tokens.shadow-add-shadow"
msgstr "Schatten hinzufügen"
#: 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 "Weichzeichnen"
#: src/app/main/data/workspace/tokens/errors.cljs:105
msgid "workspace.tokens.shadow-blur-range"
msgstr "Wert muss größer oder gleich 0 sein."
#: 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 "Farbe"
#: 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 "Innerer Schatten"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1091
msgid "workspace.tokens.shadow-remove-shadow"
msgstr "Schatten entfernen"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1215
msgid "workspace.tokens.shadow-title"
msgstr "Schatten"
#: 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/context_menu.cljs:241
msgid "workspace.tokens.size"
msgstr "Größe"
#: src/app/main/data/workspace/tokens/errors.cljs:77, src/app/main/data/workspace/tokens/warnings.cljs:19
msgid "workspace.tokens.stroke-width-range"
msgstr "Die Rahmenbreite muss größer oder gleich 0 sein."
#: 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 "none | underline | strike-through oder {alias}"
#: 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 "none | uppercase | lowercase | capitalize oder {alias}"
#: src/app/main/ui/workspace/tokens/themes/create_modal.cljs:52
msgid "workspace.tokens.theme-name-already-exists"
msgstr "Ein Theme mit diesem Namen existiert bereits"
#: 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 "Schriftfamilie oder eine durch Kommas (,) getrennte Liste von Schriften"
#: 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 "Unter diesem Speicherort existiert bereits ein 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 "Der Name muss mindestens 1 Zeichen lang sein"
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs:259
#, unused
msgid "workspace.tokens.token-not-resolved"
msgstr "Das Token mit dem Namen „%s” konnte nicht gefunden werden"
#: src/app/main/ui/workspace/tokens/management/create/border_radius.cljs:185, src/app/main/ui/workspace/tokens/management/create/form.cljs:605
msgid "workspace.tokens.token-value-enter"
msgstr "Geben Sie einen Wert oder einen Alias mittels {alias} ein"
#: src/app/main/data/workspace/tokens/import_export.cljs:49
msgid "workspace.tokens.unknown-token-type-section"
msgstr "„%s“ wird nicht als Datentyp unterstützt (%s)\n"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:745
msgid "workspace.tokens.use-reference"
msgstr "Referenz verwenden"
#: src/app/main/data/workspace/tokens/errors.cljs:65
msgid "workspace.tokens.value-with-units"
msgstr "Ungültiger Wert: Einheiten sind hier nicht zulässig."
#: src/app/main/ui/workspace/top_toolbar.cljs:129
msgid "workspace.toolbar.frame-first-time"
msgstr ""
"Zeichenfläche erstellen. Zum Festlegen der Größe anklicken und ziehen. (%s)"
#, unused
msgid "workspace.versions.locked-by-other"
msgstr ""
"Diese Version ist durch %s gesperrt und kann gerade nicht bearbeitet werden"

View File

@@ -8421,3 +8421,111 @@ msgstr "Autosaved versions will be kept for %s days."
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Click to close the path"
msgid "dashboard.labels.recent"
msgstr "Recent"
msgid "dashboard.labels.deleted"
msgstr "Deleted"
msgid "dashboard.deleted.restore-all"
msgstr "Restore All"
msgid "dashboard.deleted.clear"
msgstr "Clear trash"
msgid "dashboard.restore-file"
msgstr "Restore file"
msgid "dashboard.delete-file"
msgstr "Delete file"
msgid "dashboard.deleted.restore-project"
msgstr "Restore project"
msgid "dashboard.deleted.delete-project"
msgstr "Delete project"
msgid "dashboard.deleted.info-text"
msgstr "Deleted files will remain in the trash for"
msgid "dashboard.deleted.info-days"
msgstr " %s days. "
msgid "dashboard.deleted.info-text2"
msgstr "After that, they will be permanently deleted."
msgid "dashboard.deleted.restore-text"
msgstr "If you change your mind, you can restore them or delete them permanently from each file's menu."
msgid "dashboard.deleted.delete-forever"
msgstr "Delete forever"
msgid "restore-modal.restore-all.title"
msgstr "Restore all projects and files"
msgid "restore-modal.restore-all.description"
msgstr "You're going to restore all your projects and files. This may take a while."
msgid "restore-modal.restore-file.title"
msgstr "Restore file"
msgid "restore-modal.restore-file.description"
msgstr "You're going to restore %s."
msgid "restore-modal.restore-project.title"
msgstr "Restore Project"
msgid "restore-modal.restore-project.description"
msgstr "You're going to restore %s project and all the files contained in it."
msgid "delete-forever-modal.title"
msgstr "Delete forever"
msgid "delete-forever-modal.delete-all.description"
msgstr "Are you sure you want to delete forever all your deleted projects and files? This is a non reversible action."
msgid "delete-forever-modal.delete-file.description"
msgstr "Are you sure you want to delete forever %s? This is a non reversible action."
msgid "delete-forever-modal.delete-project.description"
msgstr "Are you sure you want to delete forever %s project? You're going to delete it forever an all of the files contained in it. This is a non reeversible action."
msgid "restore-modal.success-restore-immediately"
msgstr "%s has been successfully restored."
msgid "delete-forever-modal.success-delete-immediately"
msgstr "%s has been successfully deleted."
msgid "restore-modal.error-restore-files"
msgstr "There was an error while restoring the files."
msgid "restore-modal.error-restore-file"
msgstr "There was an error while restoring the file %s."
msgid "restore-modal.error-restore-project"
msgstr "There was an error while restoring the project %s and its files."
msgid "restore-modal.normal-progress-label"
msgstr "Restoring files…"
msgid "restore-modal.failed-progress-label"
msgstr "Restore failed"
msgid "restore-modal.slow-progress-label"
msgstr "Restore unexpectedly slow"
msgid "restore-modal.complete-process-label"
msgstr "Restore completed"
msgid "progress-widget.default-normal-progress-label"
msgstr "Processing…"
msgid "progress-widget.default-failed-progress-label"
msgstr "Process failed"
msgid "progress-widget.default-slow-progress-label"
msgstr "Process unexpectedly slow"
msgid "progress-widget.default-complete-progress-label"
msgstr "Process completed"

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-10-07 16:35+0000\n"
"Last-Translator: Deleted User <noreply+94857@weblate.org>\n"
"Language-Team: Spanish "
"<https://hosted.weblate.org/projects/penpot/frontend/es/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/penpot/frontend/"
"es/>\n"
"Language: es\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"
@@ -1843,6 +1843,7 @@ msgid "inspect.empty.select"
msgstr "Elige una forma, tablero o grupo para inspeccionar sus propiedades y código"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Info. de capa"
@@ -8276,3 +8277,111 @@ msgstr "Los autoguardados duran %s días."
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Pulsar para cerrar la ruta"
msgid "dashboard.labels.recent"
msgstr "Recientes"
msgid "dashboard.labels.deleted"
msgstr "Eliminados"
msgid "dashboard.deleted.restore-all"
msgstr "Restaurar todo"
msgid "dashboard.deleted.clear"
msgstr "Vaciar papelera"
msgid "dashboard.restore-file"
msgstr "Restaurar archivo"
msgid "dashboard.delete-file"
msgstr "Eliminar archivo"
msgid "dashboard.deleted.restore-project"
msgstr "Restaurar proyecto"
msgid "dashboard.deleted.delete-project"
msgstr "Eliminar proyecto"
msgid "dashboard.deleted.info-text"
msgstr "Los archivos eliminados permanecerán en la papelera durante"
msgid "dashboard.deleted.info-days"
msgstr " %s días. "
msgid "dashboard.deleted.info-text2"
msgstr "Después de eso, serán eliminados permanentemente."
msgid "dashboard.deleted.restore-text"
msgstr "Si cambias de opinión, puedes restaurarlos o eliminarlos permanentemente desde el menú de cada archivo."
msgid "dashboard.deleted.delete-forever"
msgstr "Eliminar para siempre"
msgid "restore-modal.restore-all.title"
msgstr "Restaurar todos los proyectos y archivos"
msgid "restore-modal.restore-all.description"
msgstr "Vas a restaurar todos tus proyectos y archivos. Esto puede tardar un poco."
msgid "restore-modal.restore-file.title"
msgstr "Restaurar archivo"
msgid "restore-modal.restore-file.description"
msgstr "Vas a restaurar %s."
msgid "restore-modal.restore-project.title"
msgstr "Restaurar proyecto"
msgid "restore-modal.restore-project.description"
msgstr "Vas a restaurar el proyecto %s y todos los archivos que contiene."
msgid "delete-forever-modal.title"
msgstr "Eliminar para siempre"
msgid "delete-forever-modal.delete-all.description"
msgstr "¿Estás seguro de que quieres eliminar para siempre todos tus proyectos y archivos eliminados? Esta es una acción irreversible."
msgid "delete-forever-modal.delete-file.description"
msgstr "¿Estás seguro de que quieres eliminar para siempre %s? Esta es una acción irreversible."
msgid "delete-forever-modal.delete-project.description"
msgstr "¿Estás seguro de que quieres eliminar para siempre el proyecto %s? Vas a eliminarlo para siempre junto con todos los archivos que contiene. Esta es una acción irreversible."
msgid "restore-modal.success-restore-immediately"
msgstr "%s ha sido restaurado correctamente."
msgid "delete-forever-modal.success-delete-immediately"
msgstr "%s ha sido eliminado correctamente."
msgid "restore-modal.error-restore-files"
msgstr "Hubo un error al restaurar los archivos."
msgid "restore-modal.error-restore-file"
msgstr "Hubo un error al restaurar el archivo %s."
msgid "restore-modal.error-restore-project"
msgstr "Hubo un error al restaurar el proyecto %s y sus archivos."
msgid "restore-modal.normal-progress-label"
msgstr "Restaurando archivos…"
msgid "restore-modal.failed-progress-label"
msgstr "Falló la restauración"
msgid "restore-modal.slow-progress-label"
msgstr "Restauración lenta"
msgid "restore-modal.complete-process-label"
msgstr "Restauración completada"
msgid "progress-widget.default-normal-progress-label"
msgstr "Procesando…"
msgid "progress-widget.default-failed-progress-label"
msgstr "Falló el procesamiento"
msgid "progress-widget.default-slow-progress-label"
msgstr "Procesamiento lento"
msgid "progress-widget.default-complete-progress-label"
msgstr "Procesamiento completado"

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2024-06-17 08:07+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Spanish (Latin America) "
"<https://hosted.weblate.org/projects/penpot/frontend/es_419/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Yessenia Villarte Vaca <yesseniavillarte@gmail.com>\n"
"Language-Team: Spanish (Latin America) <https://hosted.weblate.org/projects/"
"penpot/frontend/es_419/>\n"
"Language: es_419\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.6-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"
@@ -454,6 +454,7 @@ msgstr ""
"¿Qué quiere hacer con sus activos*?"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr ""
"Los archivos con bibliotecas compartidas se incluirán en la exportación, "

View File

@@ -1188,10 +1188,6 @@ msgstr ""
"Diseinua ikuskatzeari buruz gehiago jakin nahi baduzu zoaz Penpoten "
"laguntza zentrora"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Informazio gehiago ikuskatzeari buruz"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-10 07:51+0000\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Ahmad HosseinBor <123hozeifeh@gmail.com>\n"
"Language-Team: Persian "
"<https://hosted.weblate.org/projects/penpot/frontend/fa/>\n"
"Language-Team: Persian <https://hosted.weblate.org/projects/penpot/frontend/"
"fa/>\n"
"Language: fa\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.15-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"
@@ -501,10 +501,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"

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-10-27 12:02+0000\n"
"Last-Translator: Ingrid Pigueron <ingridp.uxr@gmail.com>\n"
"Language-Team: French "
"<https://hosted.weblate.org/projects/penpot/frontend/fr/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: French <https://hosted.weblate.org/projects/penpot/frontend/"
"fr/>\n"
"Language: fr\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.1-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"
@@ -585,6 +585,7 @@ msgstr ""
"bibliothèques partagées. Que voulez-vous faire avec leurs ressources?"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr ""
"Les fichiers avec des bibliothèques partagées seront inclus dans "
@@ -1840,10 +1841,6 @@ msgstr "Propriétés des variantes"
msgid "inspect.empty.help"
msgstr "Pour en savoir plus sur l'inspection, visitez le centre d'aide de Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Plus d'informations sur l'inspection"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""
@@ -1851,6 +1848,7 @@ msgstr ""
"leurs propriétés et le code"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Info sur la couche"
@@ -8228,3 +8226,86 @@ msgstr "Les versions auto-enregistrées seront gardées %s jours."
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Cliquez pour fermer le chemin"
#: src/app/main/ui/settings/feedback.cljs:122
msgid "feedback.description-placeholder"
msgstr "Décrivez la raison de votre commentaire"
#: src/app/main/ui/settings/feedback.cljs:143
msgid "feedback.other-ways-contact"
msgstr "Autres méthodes de contact"
#: src/app/main/ui/settings/feedback.cljs:126
msgid "feedback.penpot.link"
msgstr ""
"Si le commentaire concerne un fichier ou un projet, ajoutez le lien penpot "
"ici :"
#: src/app/main/ui/settings/feedback.cljs:110, src/app/main/ui/settings/feedback.cljs:111
#, fuzzy
msgid "feedback.type"
msgstr "Type"
#: src/app/main/ui/settings/feedback.cljs:113
msgid "feedback.type.idea"
msgstr "Idée"
#: src/app/main/ui/inspect/styles/rows/color_properties_row.cljs:120
msgid "inspect.attributes.image.preview"
msgstr "Aperçu de l'image de remplissage de la forme"
#: src/app/main/ui/static.cljs:68
msgid "labels.copyright-period"
msgstr "Kaleidos © depuis 2019"
#: src/app/main/ui/settings/feedback.cljs:134, src/app/main/ui/static.cljs:400
msgid "labels.download"
msgstr "Télécharger %s"
#: src/app/main/ui/dashboard/sidebar.cljs:1020
msgid "labels.help-learning"
msgstr "Aide et formation"
#: src/app/main/ui/static.cljs:396
msgid "labels.internal-error.desc-message-first"
msgstr "Un incident est survenu."
#: src/app/main/ui/static.cljs:397
msgid "labels.internal-error.desc-message-second"
msgstr ""
"Vous pouvez réessayer d'effectuer l'opération ou contacter l'assistance "
"technique pour signaler l'erreur."
#: src/app/main/ui/dashboard/sidebar.cljs:799
msgid "labels.learning-center"
msgstr "Centre de formation"
#: src/app/main/ui/settings/feedback.cljs:101
msgid "feedback.title-contact-us"
msgstr "Contact"
#: src/app/main/ui/inspect/right_sidebar.cljs:170
msgid "inspect.color-space-label"
msgstr "Sélectionner un espace de couleur"
#: src/app/main/ui/static.cljs:406
msgid "labels.contact-support"
msgstr "Contacter l'assistance technique"
#: src/app/main/ui/settings/sidebar.cljs:136
msgid "labels.contact-us"
msgstr "Contact"
#: src/app/main/ui/dashboard/subscription.cljs:83
msgid "subscription.dashboard.power-up.professional.bottom-description"
msgstr ""
"Bénéficiez de stockage supplémentaire, de la récupération de fichiers et de "
"bien plus encore pour vos équipes."
#: src/app/main/ui/dashboard/subscription.cljs:101
#, markdown
msgid "subscription.dashboard.power-up.unlimited.bottom-text"
msgstr ""
"Bénéficiez d'un stockage illimité, de la récupération de fichier étendue et "
"d'un nombre illimité d'éditeurs pour toutes vos équipes à un tarif fixe. "
"[Consultez le forfait Entreprise.|target:self](%s)"

View File

@@ -1244,10 +1244,6 @@ msgstr "manyan baqaqe"
msgid "inspect.empty.help"
msgstr "domin neman qarin bayani game da fenfot a tuntubi sashen agaji"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "qarin bayani a fagen lura"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr "zabar zubi, hukumar masu sa ido akan bangarorinsu da lambobinsu"

View File

@@ -1,16 +1,16 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-10-14 17:07+0000\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
"Language-Team: Hebrew "
"<https://hosted.weblate.org/projects/penpot/frontend/he/>\n"
"Language-Team: Hebrew <https://hosted.weblate.org/projects/penpot/frontend/"
"he/>\n"
"Language: he\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && "
"n % 10 == 0) ? 2 : 3));\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"
@@ -564,6 +564,7 @@ msgstr ""
"המשאבים שלהן*?"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr "קבצים עם ספריות משותפות יצורפו לייצוא, תוך שימור הקישוריות שלהם."
@@ -1769,17 +1770,14 @@ msgstr "מאפייני הגוונים"
msgid "inspect.empty.help"
msgstr "למידע נוסף על חקירת עיצוב אפשר לבקר במרכז העזרה של Penpot"
#: 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 "ניתן לבחור צורה, לוח או קבוצה ולראות את המאפיינים והקוד שלהם"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "פרטי שכבה"
msgstr "בחירת לשונית חקירה"
#: src/app/main/ui/inspect/right_sidebar.cljs:137
msgid "inspect.multiple-selected"
@@ -2194,7 +2192,7 @@ msgstr "בלתי פעיל"
#: src/app/main/ui/inspect/right_sidebar.cljs:114
msgid "labels.info"
msgstr "מידע"
msgstr "פירוט"
#: src/app/main/ui/dashboard/fonts.cljs:428
msgid "labels.installed-fonts"
@@ -4663,7 +4661,6 @@ 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"
@@ -7583,7 +7580,7 @@ msgstr "הוספת ערכת עיצוב (למשל: בהירה)"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1351
msgid "workspace.tokens.letter-spacing-value-enter-composite"
msgstr "הוספת ריווח תווים או {alias}"
msgstr "ריווח תווים או {alias}"
#: src/app/main/ui/workspace/tokens/management/context_menu.cljs:217
msgid "workspace.tokens.margins"
@@ -7654,7 +7651,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"
@@ -7680,7 +7676,6 @@ 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"
@@ -7744,7 +7739,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 "תיאור"
@@ -8147,3 +8141,214 @@ msgstr "גרסאות שנשמרו אוטומטית תישמרנה למשך %s י
#, unused
msgid "workspace.viewport.click-to-close-path"
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/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 ""
"אם המשוב הוא משהו שקשור לקובץ או למיזם, יש להוסיף את קישור ה־penpot לכאן:"
#: 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 "תצוגה מקדימה של תמונת מילוי הצורה"
#: src/app/main/ui/inspect/right_sidebar.cljs:170
msgid "inspect.color-space-label"
msgstr "בחירת מרחב צבע"
#: src/app/main/ui/inspect/styles/style_box.cljs:68
msgid "inspect.tabs.styles.copy-shorthand"
msgstr "העתקת קיצור CSS ללוח הגזירים"
#: 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 "Kaleidos ‏© 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/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/workspace/sidebar/options/rows/stroke_row.cljs:229
msgid "labels.switch"
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 "קבלת אחסון נוסף, שחזור קבצים ועוד למען הצוותים שלך."
msgid "subscription.settings.management-dialog.step-2-title"
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:50
msgid "subscription.settings.recommended"
msgstr "מומלץ"
#: src/app/main/ui/dashboard/team.cljs:933
msgid "team.invitations-selected"
msgid_plural "team.invitations-selected"
msgstr[0] "הזמנה נבחרה"
msgstr[1] "שתי הזמנות נבחרו"
msgstr[2] "%s הזמנות נבחרו"
msgstr[3] "%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] "טיפוגרפיה אחת"
msgstr[1] "שתי טיפוגרפיות"
msgstr[2] "%s טיפוגרפיות"
msgstr[3] "%s טיפוגרפיות"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:264
msgid "workspace.options.more-token-colors"
msgstr "עוד אסימוני צבע"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1339
msgid "workspace.tokens.font-size-value-enter"
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/data/workspace/tokens/errors.cljs:89
msgid "workspace.tokens.invalid-font-weight-token-value"
msgstr ""
"ערך משקל גופן שגוי: יש להשתמש בערכים מספריים (100-950) או שמות תקניים (thin, "
"light, regular, bold ועוד), אפשר גם לצרף בסוף Italic (נטוי) במקרה הצורך"
#: 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 "ערך אסימון שגוי: רק none, Uppercase, Lowercase או Capitalize מורשים"
#: 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/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/themes/create_modal.cljs:52
msgid "workspace.tokens.theme-name-already-exists"
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 "כבר קיים אסימון בנתיב: %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 "אורך השם חייב להיות תו אחד לפחות"
#: src/app/main/data/workspace/tokens/errors.cljs:85
msgid "workspace.tokens.invalid-text-decoration-token-value"
msgstr "ערך אסימון שגוי: מותר רק none, underline ו־strike-through"

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

@@ -1569,10 +1569,6 @@ msgstr ""
"Ako želite saznati više o pregledu dizajna, posjetite Penpotov centar za "
"pomoć"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Više informacija o inspekciji"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr "Odaberite oblik, ploču ili grupu da provjerite njihova svojstva i kod"

View File

@@ -1653,10 +1653,6 @@ msgstr ""
"Jika Anda ingin mengetahui lebih lanjut tentang inspeksi desain kunjungi "
"pusat bantuan Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Lebih banyak info tentang inspeksi"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr "Pilih bentuk, papan, atau grup untuk menginskpeksi properti dan kodenya"

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-10-07 16:35+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Igbo "
"<https://hosted.weblate.org/projects/penpot/frontend/ig/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Alejandro Alonso <alejandro.alonso@kaleidos.net>\n"
"Language-Team: Igbo <https://hosted.weblate.org/projects/penpot/frontend/ig/>"
"\n"
"Language: ig\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 5.14-dev\n"
"X-Generator: Weblate 5.15.1\n"
#: src/app/main/ui/auth/register.cljs:277
#, unused
@@ -402,6 +402,7 @@ msgid "dashboard.export.detail"
msgstr "* Nwere Ike ịgụnye ngwa , esereese gasị, agwụgwara na/ma ọ bụ akara nkụpụta."
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr ""
"A ga-atinye ederede nwere ọba ederede nkekọrịta ma gụnyere mbupu , ma "

View File

@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-23 12:51+0000\n"
"Last-Translator: Nicola Bortoletto <nicola.bortoletto@live.com>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Italian <https://hosted.weblate.org/projects/penpot/frontend/"
"it/>\n"
"Language: it\n"
@@ -9,7 +9,7 @@ msgstr ""
"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.15-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"
@@ -582,10 +582,11 @@ msgstr ""
"cosa desideri fare con le loro risorse*?"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr ""
"I file con librerie condivise verranno inclusi nell'esportazione, "
"mantenendo il loro collegamento."
"I file con librerie condivise verranno inclusi nell'esportazione, mantenendo "
"il loro collegamento."
#: src/app/main/ui/exports/files.cljs:165
msgid "dashboard.export.options.all.title"
@@ -1824,10 +1825,6 @@ msgstr ""
"Per ulteriori informazioni su l'ispezione, visita il centro di supporto di "
"Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Maggiori informazioni sull'ispezione"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""
@@ -1835,6 +1832,7 @@ msgstr ""
"loro proprietà e il loro codice"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Informazioni livello"
@@ -4743,9 +4741,8 @@ msgid "subscription.settings.sucess.dialog.title"
msgstr "Sei %s!"
#: src/app/main/ui/settings/subscription.cljs:440
#, fuzzy
msgid "subscription.settings.support-us-since"
msgstr "Ci supporti con questo piano dal %s"
msgstr "Ci stai supportando con questo piano dal %s"
#: src/app/main/ui/settings/subscription.cljs:472, src/app/main/ui/settings/subscription.cljs:488
msgid "subscription.settings.try-it-free"
@@ -7772,7 +7769,6 @@ msgstr ""
"0.5)."
#: src/app/main/ui/workspace/tokens/management/token_pill.cljs:120
#, fuzzy
msgid "workspace.tokens.original-value"
msgstr "Valore originale: %s"
@@ -7798,7 +7794,6 @@ msgid "workspace.tokens.reference-error"
msgstr "Errori di riferimento: "
#: 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 "Valore risolto: %"
@@ -7872,7 +7867,6 @@ msgid "workspace.tokens.themes-list"
msgstr "Elenco temi"
#: 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 "Descrizione"
@@ -8419,3 +8413,96 @@ msgstr "Esiste già un tema con questo nome"
#: 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 "Un token con questo nome esiste già nel percorso: %s"
#: src/app/main/ui/static.cljs:396
msgid "labels.internal-error.desc-message-first"
msgstr "Qualcosa è andato storto."
#: src/app/main/ui/static.cljs:397
msgid "labels.internal-error.desc-message-second"
msgstr ""
"Puoi riprovare loperazione oppure contattare il supporto per segnalare "
"lerrore."
#: src/app/main/ui/dashboard/subscription.cljs:84
msgid "subscription.dashboard.power-up.professional.bottom-button"
msgstr "Potenzia!"
#: src/app/main/ui/dashboard/subscription.cljs:83
msgid "subscription.dashboard.power-up.professional.bottom-description"
msgstr ""
"Ottieni spazio aggiuntivo, recupero dei file e altro ancora per i tuoi team."
msgid "subscription.settings.management-dialog.step-2-title"
msgstr "Aiutaci a crescere e rendi la tua prova ancora più semplice"
msgid "subscription.settings.management-dialog.step-2-description"
msgstr ""
"Aggiungi subito i tuoi dati di pagamento per mantenere attiva la "
"sottoscrizione dopo il periodo di prova e continuare a supportare il nostro "
"progetto open-source. Non ti verrà ancora addebitato nulla."
#: src/app/main/ui/inspect/styles/style_box.cljs:68
msgid "inspect.tabs.styles.copy-shorthand"
msgstr "Copia l'abbreviazione CSS negli appunti"
#: src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs:229
msgid "labels.switch"
msgstr "Cambia"
msgid "subscription.settings.management-dialog.step-2-add-payment-button"
msgstr "Aggiungi dettagli di pagamento"
#: src/app/main/ui/settings/subscription.cljs:50
msgid "subscription.settings.recommended"
msgstr "Consigliato"
#: src/app/main/ui/dashboard/team.cljs:933
msgid "team.invitations-selected"
msgid_plural "team.invitations-selected"
msgstr[0] "1 invito selezionato"
msgstr[1] "%s inviti selezionati"
#: 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 colore"
msgstr[1] "%s colori"
#: 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 componente"
msgstr[1] "%s componenti"
#: 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 grafica"
msgstr[1] "%s grafiche"
#: 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 tipografia"
msgstr[1] "%s tipografie"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs:264
msgid "workspace.options.more-token-colors"
msgstr "Altri token colore"
#: src/app/main/ui/workspace/tokens/management/create/form.cljs:1339
msgid "workspace.tokens.font-size-value-enter"
msgstr "Dimensione carattere o {alias}"
#: src/app/main/data/workspace/tokens/errors.cljs:101
msgid "workspace.tokens.invalid-shadow-type-token-value"
msgstr ""
"Tipologia ombra non valida: sono consentite solo 'innerShadow' o 'dropShadow'"
#: 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 "Il nome deve essere di almeno 1 carattere"
msgid "subscription.settings.management-dialog.step-2-skip-button"
msgstr "Salta per ora e inizia la prova"

View File

@@ -1,6 +1,6 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-23 12:51+0000\n"
"PO-Revision-Date: 2025-11-25 16:51+0000\n"
"Last-Translator: Edgars Andersons <Edgars+Weblate@gaitenis.id.lv>\n"
"Language-Team: Latvian <https://hosted.weblate.org/projects/penpot/frontend/"
"lv/>\n"
@@ -1809,10 +1809,6 @@ msgstr ""
"Ja ir vēlme uzzināt vairāk par dizaina apskati, jāapmeklē Penpot palīdzības "
"centrs"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Vairāk informācijas par apskatīšanu"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr "Jāatlasa apveids, plātne vai kopa, lai apskatītu to īpašības un kodu"
@@ -7966,3 +7962,12 @@ msgstr "Ierosinājums"
#: src/app/main/ui/settings/feedback.cljs:114
msgid "feedback.type.issue"
msgstr "Sarežģījums"
msgid "subscription.settings.management-dialog.step-2-title"
msgstr "Palīdzi mums attīstīties un padarīt Tavu izmēģinājumu vienkāršāku"
msgid "subscription.settings.management-dialog.step-2-skip-button"
msgstr "Pagaidām izlaist un uzsākt izmēģinājumu"
msgid "subscription.settings.management-dialog.step-2-add-payment-button"
msgstr "Pievienot maksājumu informāciju"

View File

@@ -1297,10 +1297,6 @@ msgstr ""
"Jika anda ingin mengetahui lebih lanjut tentang pemeriksaan reka bentuk, "
"lawati pusat bantuan Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Maklumat lanjut tentang inspect"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr "Pilih bentuk, papan atau kumpulan untuk memeriksa sifat dan kod mereka"

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-22 10:51+0000\n"
"Last-Translator: Keunes <keunes@mailbox.org>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/penpot/frontend/nl/"
">\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/penpot/frontend/"
"nl/>\n"
"Language: nl\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.15-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"
@@ -580,6 +580,7 @@ msgstr ""
"bibliotheken. Wat wil je doen met hun assets*?"
#: src/app/main/ui/exports/files.cljs:164
#, fuzzy
msgid "dashboard.export.options.all.message"
msgstr ""
"Bestanden met gedeelde bibliotheken worden opgenomen in de export en hun "
@@ -1825,10 +1826,6 @@ msgstr ""
"Als je meer wilt weten over ontwerpinspectie, ga dan naar het helpcentrum "
"van Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Meer info over inspecteren"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""
@@ -1836,6 +1833,7 @@ msgstr ""
"inspecteren"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Laaginfo"
@@ -8384,7 +8382,7 @@ msgstr "Schakelaar"
#: src/app/main/ui/dashboard/subscription.cljs:84
msgid "subscription.dashboard.power-up.professional.bottom-button"
msgstr "Inschakelen!"
msgstr "Gas geven!"
#: src/app/main/ui/dashboard/subscription.cljs:83
#, markdown
@@ -8474,3 +8472,18 @@ msgstr "Naam moet minimaal 1 teken zijn"
#: src/app/main/ui/inspect/styles/style_box.cljs:68
msgid "inspect.tabs.styles.copy-shorthand"
msgstr "CSS-code kopiëren naar klembord"
msgid "subscription.settings.management-dialog.step-2-title"
msgstr "Help ons groeien en maak je proefperiode eenvoudiger"
msgid "subscription.settings.management-dialog.step-2-description"
msgstr ""
"Voeg nu je betalingsgegevens toe om je abonnement na de proefperiode soepel "
"te laten verlopen en ons open-sourceproject te blijven ondersteunen. Er "
"worden nog geen kosten in rekening gebracht."
msgid "subscription.settings.management-dialog.step-2-skip-button"
msgstr "Nu overslaan en proefperiode starten"
msgid "subscription.settings.management-dialog.step-2-add-payment-button"
msgstr "Betalingsgegevens toevoegen"

View File

@@ -1170,10 +1170,6 @@ msgstr ""
"Jeśli chcesz dowiedzieć się więcej o inspekcji projektu, odwiedź centrum "
"pomocy Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Więcej informacji o inspekcji"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr "Wybierz kształt, tablicę lub grupę, aby sprawdzić ich właściwości i kod"

View File

@@ -1,15 +1,15 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-11-18 13:51+0000\n"
"Last-Translator: Tiago José <soutiagojose@outlook.com>\n"
"Language-Team: Portuguese (Brazil) "
"<https://hosted.weblate.org/projects/penpot/frontend/pt_BR/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"penpot/frontend/pt_BR/>\n"
"Language: pt_BR\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.15-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"
@@ -1812,10 +1812,6 @@ msgstr ""
"Se quiser saber mais sobre inspecionar o design, visite a central de ajuda "
"do Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Mais informações sobre inspecionar"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""
@@ -1823,6 +1819,7 @@ msgstr ""
"código"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Informações da camada"

View File

@@ -1576,10 +1576,6 @@ msgstr "Maiúsculas"
msgid "inspect.empty.help"
msgstr "Se quiseres saber mais sobre a inspeção, visita o centro de ajuda do Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Mais informações sobre a inspeção"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""

View File

@@ -1,16 +1,16 @@
msgid ""
msgstr ""
"PO-Revision-Date: 2025-10-17 11:07+0000\n"
"Last-Translator: AlexTECPlayz <alextecplayz@vantainteractive.com>\n"
"Language-Team: Romanian "
"<https://hosted.weblate.org/projects/penpot/frontend/ro/>\n"
"PO-Revision-Date: 2025-12-22 15:34+0000\n"
"Last-Translator: Anonymous <noreply@weblate.org>\n"
"Language-Team: Romanian <https://hosted.weblate.org/projects/penpot/frontend/"
"ro/>\n"
"Language: ro\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
"20)) ? 1 : 2;\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"
@@ -1821,10 +1821,6 @@ msgstr ""
"Dacă dorești să afli mai multe despre inspectorul de design, vizitează "
"centrul de ajutor Penpot"
#: src/app/main/ui/inspect/right_sidebar.cljs:240
msgid "inspect.empty.more-info"
msgstr "Mai multe informații despre inspector"
#: src/app/main/ui/inspect/right_sidebar.cljs:232
msgid "inspect.empty.select"
msgstr ""
@@ -1832,6 +1828,7 @@ msgstr ""
"și codul"
#: src/app/main/ui/inspect/right_sidebar.cljs:166
#, fuzzy
msgid "inspect.layer-info"
msgstr "Informație strat"

Some files were not shown because too many files have changed in this diff Show More