Compare commits

...

1303 Commits

Author SHA1 Message Date
Andrey Antukh
e2c5a1378e Merge pull request #6724 from penpot/elenatorro-improve-create-profile-command
🔧 Add option to skip tutorial/walkthrough when creating a profile from the script
2025-07-11 14:15:49 +02:00
Xavier Julian
81f99458e5 📎 Add new font-size token type to CHANGELOG 2025-07-11 14:09:39 +02:00
Florian Schroedl
9c5a13c4ac Enable font-size token 2025-07-11 10:37:17 +02:00
Xavier Julian
02ae934e25 📎 Add import tokens from zip file to CHANGELOG 2025-07-10 13:40:42 +02:00
Andrey Antukh
95cfb26b38 Merge pull request #6882 from penpot/xaviju-11283-info-tab-visibility-attrs-review
♻️ Fix tab info not updating and suggested code refactor
2025-07-10 11:56:38 +02:00
Andrey Antukh
935c22d124 Merge pull request #6885 from penpot/marina-change-text-capitalize
🐛 Fix title button from Title case to Capitalize
2025-07-10 11:55:50 +02:00
Marina López
ba6a02d1d9 🐛 Add fixes from subscription design review (#6870)
* 🐛 Fixes from subscription design review

* 🐛 Fix to consider professional plan the unpaid and canceled status

* 📎 Fixes PR feedback
2025-07-10 11:55:16 +02:00
Xavier Julian
0b681effe7 ♻️ Fix tab info not updating and suggested code refactor 2025-07-10 11:38:53 +02:00
Marina López
6826db8498 🐛 Fix title button from Title case to Capitalize 2025-07-10 11:29:48 +02:00
Andrey Antukh
66c5841d48 Merge pull request #6886 from penpot/alotor-fix-create-layout
🐛 Fix problem when creating a layout from an existing layout
2025-07-10 11:28:19 +02:00
Xavier Julian
af10705b4c ♻️ Review import message text 2025-07-10 10:18:59 +02:00
Pablo Alba
41146ef71d 🐛 Fix text overrides when there are structure changes 2025-07-09 21:58:01 +02:00
Pablo Alba
abb6aee57d 🐛 On texts overrides, keep also vertical-align property 2025-07-09 21:58:01 +02:00
alonso.torres
aa01d3b707 🐛 Fix problem when creating a layout from an existing layout 2025-07-09 15:44:15 +02:00
alonso.torres
a003687256 🐛 Fix problem with grid assignments 2025-07-09 14:55:14 +02:00
Andrey Antukh
51a6d61be6 Merge pull request #6865 from penpot/xaviju-11283-info-tab-visibility-attrs
 Add visibility group and attributes to info tab
2025-07-09 12:18:10 +02:00
Xavier Julian
0daa8be0b5 Add visibility group and attributes to info tab 2025-07-09 11:19:30 +02:00
Andrey Antukh
00599f76d0 Merge pull request #6875 from penpot/ladybenko-fix-devenv-mac-ubuntu
🔧 Fix building and running devenv (Mac / Linux)
2025-07-09 08:28:49 +02:00
Belén Albeza
cb8aae4d5f 🔧 Drop the -R in chown (dockerfile mac) 2025-07-08 15:45:34 +02:00
Belén Albeza
927228fc8f 🔧 Remove COPY of apt.sources (linux issue) 2025-07-08 15:44:42 +02:00
Andrés Moya
88bb9bfe52 🐛 Detach styles from assets when applying tokens 2025-07-08 13:15:45 +02:00
Andrey Antukh
e554b9fcb7 Merge remote-tracking branch 'origin/staging' into develop 2025-07-08 11:04:29 +02:00
Aitor Moreno
4548310235 Merge pull request #6867 from penpot/azazeln28-fix-missing-solid-color
🐛 Fix missing required SolidColor
2025-07-08 09:11:12 +02:00
Aitor Moreno
ea9261b0b2 🐛 Fix missing required SolidColor 2025-07-08 08:45:03 +02:00
Aitor Moreno
6ffcd58368 Merge pull request #6846 from penpot/alotor-wasm-refactor-mut-2
♻️ Refactor wasm shapes state management
2025-07-08 08:31:15 +02:00
alonso.torres
69135ef8c7 ♻️ Refactor wasm shapes state management 2025-07-08 08:30:40 +02:00
Aitor Moreno
747427daa4 Merge pull request #6841 from penpot/superalex-fix-frame-clipping
🐛 Fix frame clipping
2025-07-08 08:26:48 +02:00
Alejandro Alonso
5b704faf79 Merge pull request #6862 from penpot/niwinz-develop-minor-fixes
 Add several improvements
2025-07-07 15:40:20 +02:00
Andrey Antukh
d24eab7241 📎 Update changelog 2025-07-07 15:07:07 +02:00
Andrey Antukh
c8fef97598 Add missing field on get-teams rpc method 2025-07-07 13:36:00 +02:00
Andrey Antukh
44e3e4a641 🔥 Remove image shape inspect pannel
Image shapes are long ago deprecated not used
on penpot
2025-07-07 13:36:00 +02:00
Andrey Antukh
f3616c68a0 Improve decode empty string for path content 2025-07-07 13:36:00 +02:00
Andrey Antukh
9ea3f81bc4 🐛 Clean invalid library colors 2025-07-07 13:35:59 +02:00
Pablo Alba
cfec023585 ♻️ Rename flag :component-swap to :allow-altering-copies 2025-07-07 12:07:36 +02:00
Pablo Alba
469d47eaf3 🐛 Fix variants combobox and select to new options format 2025-07-07 11:46:50 +02:00
Alejandro Alonso
51bb6583d2 🐛 Fix frame clipping 2025-07-07 11:09:29 +02:00
Pablo Alba
a44c70ef69 Keep the swapped childs if the copies when doing a variant switch 2025-07-07 10:50:49 +02:00
Andrés Moya
4fddf34a73 🐛 Fix error when there exists a tokens lib with no sets 2025-07-07 10:02:49 +02:00
Xavier Julian
8f840daa91 Improve token import error copy 2025-07-07 09:59:57 +02:00
Juanfran
0a7d6d98e1 Integrate plugin runtime as npm library (#6852) 2025-07-07 09:46:07 +02:00
Álvaro Tejero-Cantero
bcb69b6227 🐛 Restore viewport and selection when exiting focus mode (#6827)
* 📚 Provide guidance on how to exit focus mode

* 🐛 Restore viewport & selection post focus mode

* 📎 Update changelog
2025-07-07 09:44:06 +02:00
Andrey Antukh
92d708d52c Merge remote-tracking branch 'origin/staging' into develop 2025-07-07 09:37:55 +02:00
Alonso Torres
accd5226d7 🐛 Fix sidebar width in localhost (#6732) 2025-07-07 09:28:52 +02:00
Andrey Antukh
16a1fd14e5 🐛 Fix media translation on text nodes on paste (#6845)
Fix incorrect media translation on paste text with fill images
2025-07-07 09:03:35 +02:00
Aitor Moreno
824bb19c7e Merge pull request #6848 from penpot/niwinz-staging-library-referer
 Add referer field to binfile v3
2025-07-07 09:02:13 +02:00
Aitor Moreno
d0f3e0f0b0 Merge pull request #6853 from penpot/niwinz-staging-path-bool-fixes
🐛 Fix exception on a corner case of creating bool shape
2025-07-07 09:00:57 +02:00
Andrey Antukh
43ba2b05e8 📎 Change current config values for error report explain 2025-07-04 14:51:08 +02:00
Andrey Antukh
d5ccb704b2 🐛 Fix unexpected exception on creating bool shapes 2025-07-04 14:42:09 +02:00
Andrey Antukh
0374e4f3eb Merge remote-tracking branch 'origin/staging' into develop 2025-07-04 12:02:12 +02:00
David Barragán Merino
6d21fcc9de 🔧 Fix condition for automatic events (#6849) 2025-07-04 11:57:57 +02:00
Andrey Antukh
77741b49a7 Add tracking for referer on the import-binfile 2025-07-04 11:02:36 +02:00
Andrey Antukh
a7e0cfc609 🎉 Bump 1.0.7 release of the penpot library
Includes the ability to pass referer
2025-07-04 11:02:36 +02:00
Andrey Antukh
50a6355537 🎉 Add options for creating library build context
With the ability to pass referer.
2025-07-04 11:02:36 +02:00
Alejandro Alonso
264aef277d Merge pull request #6847 from penpot/niwinz-staging-error-reports-2
 Add minor improvements to error report on calc bool content
2025-07-04 10:53:02 +02:00
Andrey Antukh
78d0e6d059 Add minor improvements to error report on calc bool content 2025-07-04 10:13:24 +02:00
Alonso Torres
6d41d36b3a 🐛 Fix problem when double click on hidden shapes (#6833) 2025-07-04 09:01:20 +02:00
David Barragán Merino
bb97df373e 🔧 Add Github Action to build and upload artifact (#6840)
Co-authored-by: Francis Santiago <francis.santiago@kaleidos.net>
2025-07-04 08:41:46 +02:00
David Barragán Merino
528c819323 🔧 Add Github Action to build and upload artifact (#6840)
Co-authored-by: Francis Santiago <francis.santiago@kaleidos.net>
2025-07-04 08:25:23 +02:00
Florian Schrödl
21746144b7 Add letter spacing token (#6814)
* 🐛 Fix merge schema not working with key generation

*  Add letter-spacing token

* ♻️ Remove comments

* ♻️ Inline line-height for now
2025-07-03 16:00:58 +02:00
Andrey Antukh
3165761bac Merge remote-tracking branch 'origin/staging' into develop 2025-07-03 15:32:30 +02:00
Andrés Moya
c09f72c3d5 🐛 Sanitize wrong ids in token themes (#6843) 2025-07-03 15:31:45 +02:00
Alejandro Alonso
a41af032cd Merge pull request #6844 from penpot/niwinz-staging-enhancements
 Improve error reporting
2025-07-03 15:31:20 +02:00
Andrey Antukh
86ee4f55c5 📚 Update docstring 2025-07-03 14:54:00 +02:00
Andrey Antukh
63cd3ae025 Add better error handling for bool creation 2025-07-03 14:54:00 +02:00
Andrey Antukh
cafb7abb53 🎉 Add better syntax facility for ex/try! macro 2025-07-03 14:54:00 +02:00
Andrey Antukh
e5b6c4a9e0 Add minor improvement to error reporter logger 2025-07-03 14:54:00 +02:00
Andrey Antukh
1d5bad5523 💄 Report file-id on file changes exception 2025-07-03 14:54:00 +02:00
Andrey Antukh
96d6868b45 🐛 Add missing fields on get-team-shared-files query 2025-07-03 14:54:00 +02:00
Andrey Antukh
b739d8bd0c 💄 Change default depth on params for error reports 2025-07-03 14:54:00 +02:00
Alejandro Alonso
dd803dc1de Merge pull request #6839 from penpot/niwinz-staging-fix-broken-path
🐛 Add migration for fix undecoded path content
2025-07-03 13:43:59 +02:00
Florian Schrödl
7dd61968b5 Implement object type specific tokens (#6816)
*  Allow token applying for supported shape types only

* 🐛 Remove x/y attribute keys from spacing token

*  Shape specific context-menu

*  Only apply tokens to supported shapes when doing multi selection apply

*  Handle groups not supported by tokens yet

* 🐛 Fix outdated tests

* ♻️ Commentary

*  Add helper functions for attribute applicability checks

* ♻️ Groups don't have own attributes

* ♻️ Remove unused function

* ♻️ Move attribute logic to common.types.token
2025-07-03 12:22:04 +02:00
Juanfran
669d6d9ae2 Merge pull request #6837 from penpot/juanfran-us-11186-rules-help
 Add in-app help to guide users about variant rules
2025-07-03 11:30:16 +02:00
Andrey Antukh
b627c10737 🔥 Remove duplicated check-fn 2025-07-03 10:50:09 +02:00
María Valderrama
95f4a9bd29 Add missing start-plugin event (#6809)
*  Add missing start-plugin event

* 📎 Correct event origin
2025-07-03 10:29:43 +02:00
Miguel de Benito Delgado
b931547300 🐳 Add "postgres" network alias to default docker network in devenv (#6823) 2025-07-03 10:28:53 +02:00
Andrey Antukh
a2b8f19ff3 🐛 Add migration for fix undecoded path content 2025-07-03 08:40:23 +02:00
Belén Albeza
30274c4f5c 🔧 Restore arm64 build of devenv (#6826) 2025-07-03 08:28:07 +02:00
Andrés Moya
0a71134652 🔧 Sanitize and check tokens when deserializing from db (#6838) 2025-07-02 17:01:10 +02:00
Juanfran
72b1919e29 Add in-app help to guide users about variant rules 2025-07-02 14:46:36 +02:00
Andrey Antukh
898182e3d5 Add minor events props normalization (#6836) 2025-07-02 14:41:48 +02:00
Xavier Julian
be43365909 🐛 Fix broken import file type drodown options 2025-07-02 14:35:01 +02:00
Andrey Antukh
41994703a9 ♻️ Refactor tab-switcher* component (#6815)
* 💄 Add minor style adjustments to workspace sidebar

* 💄 Add style enhacement to sitemap component

* ♻️ Refactor tab-switcher* component
2025-07-02 14:08:47 +02:00
Marina López
3d45080e3c 🐛 Fixes from subscription design review (#6812) 2025-07-02 10:49:16 +02:00
Miguel de Benito Delgado
28c055e3f9 📚 Fix and extend backend repl doc (#6819) 2025-07-02 10:38:35 +02:00
Prithvi Tharun
4f993bf4ae 💄 Replace 'Verify new email' label with 'Confirm new email' (#6831)
Improves clarity by using more accurate and familiar terminology.

Signed-off-by: Prithvi Tharun <ptrithu8@gmail.com>
2025-07-02 10:32:09 +02:00
Alejandro Alonso
3cb0e1b6ee 🐛 Fix exif rotation detection when auto-rotation isn't supported (#6818) 2025-07-02 10:31:05 +02:00
Miguel de Benito Delgado
e03c822b51 🐛 Fix internal error on missing theme setting in profile (#6822) 2025-07-02 10:17:22 +02:00
Andrey Antukh
1432b211a6 Merge remote-tracking branch 'origin/staging' into develop 2025-07-02 10:13:30 +02:00
Miguel de Benito Delgado
3e45e4fb25 🐛 Fix internal error on missing theme setting in profile (#6822) 2025-07-02 09:57:56 +02:00
Eva Marco
a3aabf3b7d 🐛 Fix tooltip position after click (#6830) 2025-07-02 09:56:14 +02:00
Andrés Moya
953287ea33 🐛 Avoid crash in combobox with empty options 2025-07-02 08:57:25 +02:00
Elena Torró
493831f110 Merge pull request #6821 from penpot/alotor-refactor-mutability
♻️ Refactor mutability modifiers in wasm
2025-07-01 13:52:39 +02:00
alonso.torres
3d374e8e97 ♻️ Refactor mutability modifiers in wasm 2025-07-01 12:47:31 +02:00
Andrés Moya
f0f01af55c 🔧 Make TokenSet an abstract data type 2025-06-30 16:59:00 +02:00
Xavier Julian
6de9de9e38 Add new metric for token update and provide token type 2025-06-30 13:21:49 +02:00
Kelp
b893a62e40 Add new typography icon to the DS (#6808)
Signed-off-by: Kelp <5446186+NatachaMenjibar@users.noreply.github.com>
2025-06-30 11:06:54 +02:00
alonso.torres
8dcb376b18 Add drop grid cells in wasm 2025-06-30 10:28:59 +02:00
alonso.torres
52a4fc6030 🐛 Fix drop index on flex layout wasm 2025-06-30 10:28:59 +02:00
Andrey Antukh
403d92838a ♻️ Add minor refactor to options dropdown options handling and validation (#6739)
* ♻️ Refactor options-dropdown* and related components

* 🐛 Fix props error

* 🐛 Fix test

* 📎 Update rumext

---------

Co-authored-by: Eva Marco <evamarcod@gmail.com>
2025-06-29 11:52:29 +02:00
Xavier Julian
6bd3253e5e ♻️ Restructure UI files for tokens editor 2025-06-27 13:23:42 +02:00
Pablo Alba
20b5b7f6e4 🐛 Fix variant switch in another page (#6802) 2025-06-27 12:23:54 +02:00
Andrés Moya
5c4fd97541 🐛 Allow importing file without any token but with themes or sets (#6796) 2025-06-27 11:32:14 +02:00
Pablo Alba
804146ae9a 🐛 Fix text partial change doesn't show up on another page (#6799) 2025-06-27 10:21:21 +02:00
Juanfran
24e78e6a10 🐛 Display error message on register form (#6797) 2025-06-27 10:01:54 +02:00
Pablo Alba
daca26e54f 🐛 On variants override use the component name instead of the copy name 2025-06-26 17:37:21 +02:00
Aitor Moreno
29016cef49 Merge pull request #6794 from penpot/alotor-wasm-fix-grid-fr
🐛 Fix problem with fr allocation
2025-06-26 14:39:42 +02:00
alonso.torres
fb07788e8f 🐛 Fix problem with fr allocation 2025-06-26 13:17:26 +02:00
Alejandro Alonso
3010abbf64 Merge pull request #6793 from penpot/alotor-fix-plugins-system-theme
🐛 Fix problem with plugins on system theme
2025-06-26 12:30:51 +02:00
alonso.torres
e6a7eed7a9 🐛 Fix problem with plugins on system theme 2025-06-26 12:15:06 +02:00
Andrey Antukh
c75a617d26 Merge remote-tracking branch 'origin/staging' into develop 2025-06-26 11:19:29 +02:00
Andrey Antukh
f2c4a1eb1f Merge pull request #6674 from penpot/niwinz-develop-enhacements-3
 Refactor fills-menu and related components
2025-06-26 11:09:30 +02:00
Marina López
62371fded0 🐛 Fix libraries position in dashboard sidebar (#6791) 2025-06-26 11:08:18 +02:00
Alejandro Alonso
daf3b5caa8 🐛 Fix slow color picker (#6780) 2025-06-26 11:07:35 +02:00
Andrey Antukh
e72d31a082 🔥 Remove unused and commented code 2025-06-26 10:50:38 +02:00
Andrey Antukh
6b4a85cd15 🐛 Fix issue on changing from gradient to solid color on colorpicker 2025-06-26 10:50:38 +02:00
Andrey Antukh
027a7a457d Add minor style improvements for reorder-handler component 2025-06-26 10:50:38 +02:00
Andrey Antukh
20d2d22f39 Add performance oriented refactor to fill-menu component 2025-06-26 10:50:36 +02:00
Andrey Antukh
34d65ed1c8 Merge pull request #6775 from penpot/superalex-fix-entering-long-project-name
🐛 Fix entering long project name
2025-06-26 10:48:54 +02:00
Andrey Antukh
a191fe63a1 Merge remote-tracking branch 'origin/staging' into develop 2025-06-26 09:18:23 +02:00
luisδμ
2de0c90fc7 🐛 Remove empty properties starting with the last one (#6757)
* 🐛 Remove empty properties starting with the last one

*  MR changes

---------

Co-authored-by: Pablo Alba <pablo.alba@kaleidos.net>
2025-06-26 09:16:59 +02:00
Alejandro Alonso
27c624ae0f Merge pull request #6787 from penpot/niwinz-staging-hotfix-4
🐛 Several fixes
2025-06-26 09:13:49 +02:00
Andrey Antukh
3831b3034e 🐛 Fix boolean shape migration that causes issues on import 2025-06-26 08:55:09 +02:00
Luis de Dios
7cd0e28c3b Allow variants with no properties 2025-06-26 08:42:34 +02:00
Andrey Antukh
00390a1349 🐛 Add correct is-text-node? predicate to text processing methods 2025-06-26 08:32:11 +02:00
Andrey Antukh
17bfed137c 📎 Add better defaults for text processing on old migrations 2025-06-26 08:32:11 +02:00
Andrey Antukh
77ef26b207 📎 Add srepl script for validate file schema 2025-06-26 08:32:11 +02:00
Andrey Antukh
26239a15f2 📎 Add missing changes on lost-colors fix script 2025-06-25 20:13:35 +02:00
Andrey Antukh
25ef1800d0 Merge remote-tracking branch 'origin/staging' into develop 2025-06-25 19:30:57 +02:00
Andrey Antukh
207974fe6c Add minor improvement to color cleaning migration 2025-06-25 19:26:43 +02:00
Andrey Antukh
b52e2fa681 🐛 Add missing version field on get-team-shared-files internal query 2025-06-25 19:24:18 +02:00
Andrey Antukh
bf719b587f Add better shadow cleaning migration 2025-06-25 19:17:58 +02:00
Alejandro Alonso
61109c91e3 Merge pull request #6784 from penpot/niwinz-staging-hotfix-3
🐛 Fix incorrect library color cleaning mechanism
2025-06-25 16:21:58 +02:00
Andrey Antukh
4915a97c2c 📎 Add script for restoring lost colors 2025-06-25 16:10:35 +02:00
Andrey Antukh
903aba5642 🐛 Fix incorrect library color cleaning mechanism 2025-06-25 14:36:33 +02:00
Andrey Antukh
9760911fce Merge remote-tracking branch 'origin/staging' into develop 2025-06-25 14:24:26 +02:00
Alejandro Alonso
82583f5079 🐛 Fix entering long project name 2025-06-25 14:21:52 +02:00
Alejandro Alonso
4561392791 🐛 Fix shortcut error pressing G+W from the View Mode (#6772) 2025-06-25 14:14:44 +02:00
Marina López
f81a973a4d 🐛 Fix text decoration line through value in inspect tab (#6778) 2025-06-25 14:11:58 +02:00
Alejandro Alonso
ca99671d3c 📚 Update CHANGES with support for exif rotated images (#6782) 2025-06-25 14:10:13 +02:00
Alejandro Alonso
8e8b2acddd Merge pull request #6781 from penpot/niwinz-staging-hotfix-2
🐛 Hot fixes
2025-06-25 13:53:58 +02:00
Marina López
1f42f032fc 🐛 Add fixes for subscription design review (#6751)
* 🐛 Fix from subscription design review

* 📎 Fixes PR feedback
2025-06-25 13:41:45 +02:00
Andrey Antukh
93cbd99932 🐛 Clear invalid keys from color libraries 2025-06-25 13:38:35 +02:00
Andrey Antukh
15c91a5de5 Make the bool-content normalize migration idempotent 2025-06-25 13:26:32 +02:00
Andrey Antukh
7f2e819789 🐛 Fix migration persistence ordering issue
When migrations are applied to old files
2025-06-25 13:26:25 +02:00
Alejandro Alonso
4947bf480b Merge pull request #6779 from penpot/niwinz-staging-hotfix-2
🐛 Fix regression on changing color type on fills
2025-06-25 12:27:34 +02:00
Marina López
67ca8ccb22 🐛 Fix copy font-size doesn't copy the unit (#6776) 2025-06-25 12:14:33 +02:00
Alejandro Alonso
c51ae35fc5 Merge pull request #6777 from penpot/niwinz-staging-hotfix-1
🐛 Remove qualified keyword keys from colors
2025-06-25 12:13:53 +02:00
Andrey Antukh
81564dbfa9 🐛 Fix regression on changing color type on fills 2025-06-25 12:12:04 +02:00
Andrey Antukh
56472a95de 🐛 Add missing file migration to get-team-shared-files rpc method 2025-06-25 11:56:11 +02:00
Andrey Antukh
9e5bc3675c 🐛 Remove cider nrepl handler from default nrepl server 2025-06-25 11:41:24 +02:00
Xavier Julian
ce59070fd1 ♻️ Restructure UI files for token sets 2025-06-25 11:27:13 +02:00
Andrey Antukh
787c066357 🐛 Remove qualified keyword keys from colors 2025-06-25 11:23:39 +02:00
Marina López
82bedda604 Add tokens library to dashboard carousel (#6769) 2025-06-25 10:22:19 +02:00
Marina López
e258030bc0 💄 Change 'save color' button (#6774) 2025-06-25 10:21:22 +02:00
Alejandro Alonso
8f00292f8f 🎉 Support for exim rotated images (#6767) 2025-06-25 10:20:37 +02:00
Alejandro Alonso
1b67be2f36 Merge pull request #6773 from penpot/niwinz-staging-paste-regression
🐛 Fix minor regression on paste shapes with fill-image
2025-06-25 10:02:36 +02:00
Andrey Antukh
1a8a9df2b7 🐛 Fix minor regression on paste shapes with fill-image 2025-06-25 09:40:17 +02:00
Alejandro Alonso
e1ce7ec787 Merge pull request #6752 from penpot/niwinz-staging-path-editor-fixes
🐛 Fix incorrect path tool handling on shapes not coerced to path
2025-06-25 07:25:13 +02:00
Andrey Antukh
546b7d5f60 🐛 Fix incorrect path tool handling on shapes not coerced to path 2025-06-24 19:09:38 +02:00
Florian Schroedl
fe91201431 Keep warning for unsupported token types when FF is disabled 2025-06-24 15:41:24 +02:00
Florian Schroedl
00c7411f92 🐛 Fix dtcg token type name 2025-06-24 15:41:24 +02:00
Xavier Julian
e585cbd673 ♻️ Restructure UI files for import/export and common files 2025-06-24 13:58:54 +02:00
Alejandro Alonso
bdc10ac173 Merge pull request #6754 from penpot/azazeln28-issue-11401-fix-wrong-aspect-ratio
🐛 Fix image aspect ratio rendering on oriented images
2025-06-24 13:23:35 +02:00
Elena Torró
9f5cb61a19 Merge pull request #6766 from penpot/elenatorro-fix-text-auto-height
🐛 Fix text auto height
2025-06-24 13:18:28 +02:00
Alejandro Alonso
fb6121bf92 Merge pull request #6765 from penpot/alotor-update-plugins-runtime
⬆️ Update plugins runtime
2025-06-24 13:14:53 +02:00
Alejandro Alonso
e442d8adad Add tests for exif rotated images 2025-06-24 13:08:18 +02:00
Elena Torro
925af4e1e9 🐛 Fix text auto height 2025-06-24 12:36:12 +02:00
alonso.torres
a45886c86c Small cosmetic change 2025-06-24 10:26:37 +02:00
alonso.torres
36b6f6323a ♻️ Refactor modifiers methods 2025-06-24 10:26:37 +02:00
alonso.torres
afec3b9bc1 🐛 Fix problem with margin in flex layout 2025-06-24 10:26:37 +02:00
alonso.torres
ac6a814026 🐛 Fix problem with flex layout in wasm 2025-06-24 10:26:37 +02:00
Alejandro Alonso
89fb802362 Merge pull request #6764 from penpot/alotor-fix-problem-lines
🐛 Fix wasm problem with horizontal/vertical lines
2025-06-24 09:43:11 +02:00
alonso.torres
8cdcfb70e2 ⬆️ Update plugins runtime 2025-06-24 09:25:12 +02:00
alonso.torres
b0d858df2b 🐛 Fix wasm problem with horizontal/vertical lines 2025-06-24 09:24:00 +02:00
Aitor Moreno
f54497194a Merge pull request #6762 from penpot/elenatorro-10901-add-text-vertical-alignment
🔧 Add vertical alignment for text shapes
2025-06-23 17:05:47 +02:00
Elena Torro
134fb1ab4c 🔧 Add vertical alignment for text shapes 2025-06-23 16:45:25 +02:00
andrés gonzález
8c2dc1f22d 📚 Add DT import/export options to the docs (#6753) 2025-06-23 15:37:57 +02:00
Aitor Moreno
833546d754 🐛 Fix wrong aspect ratio on oriented image 2025-06-23 15:30:01 +02:00
Elena Torró
0010d61ae2 Merge pull request #6758 from penpot/elenatorro-text-rendering-fixes-and-tests
🔧 Add tests to cover text styles
2025-06-23 14:06:19 +02:00
Elena Torro
747f72be47 🔧 Add tests to cover text styles 2025-06-23 13:43:09 +02:00
Alejandro Alonso
1882efe3f7 🐛 Fix paths rendered initially ony in tile 0 0 2025-06-23 12:23:49 +02:00
Florian Schrödl
580bb46a05 Implement font-size token type (#6708)
*  Implement font-size token type

*  Hide typography tokens behind FF

* 💄 Update icon

* 🔧 Add font-size to unapply check

* ♻️ Generalize status-icon logic and remove icon for font-size
2025-06-23 12:12:40 +02:00
Alejandro Alonso
9ea0875e65 Merge pull request #6742 from penpot/ladybenko-fix-wasm-debug-text-hi-dpr
 Fix size of 'wasm renderer' debug text on dpr > 1
2025-06-23 11:47:20 +02:00
Alejandro Alonso
43b19ba33e Merge pull request #6738 from penpot/ladybenko-11247-enable-dpr-when-render-wasm
🔧 Enable render-wasm-dpr by default
2025-06-23 11:46:24 +02:00
Andrey Fedorov
130cd52f79 Notify user if imported file or directory doesn't contain token files 2025-06-23 11:44:00 +02:00
Aitor Moreno
21fd56076c Merge pull request #6756 from penpot/superalex-fix-empty-fills
🐛 Fix empty fills
2025-06-23 11:31:34 +02:00
Alejandro Alonso
c97314ddb5 🐛 Fix empty fills 2025-06-23 11:14:58 +02:00
Andrey Antukh
34bbce5089 Merge remote-tracking branch 'origin/staging' into develop 2025-06-23 10:06:05 +02:00
ºelhombretecla
9a0538e5e3 Add visual indicator for new comments in the workspace (#6728)
* 🎉 Add comment notification to workspace

* 💄 Add info to changelog

*  Add minor efficiency improvements

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-06-23 09:51:41 +02:00
ºelhombretecla
1b041d949c 💄 Add new content for 2.8 release slides (#6729)
* 💄 Add new content for 2.8 release slides

* 📎 Fix linter issues

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-06-23 09:43:55 +02:00
Belén Albeza
56d96aaf07 🔧 Enable render-wasm-dpr by default 2025-06-20 12:48:49 +02:00
Eva Marco
1a8f36562b 🐛 Fix export button width on design tab (#6744) 2025-06-20 12:04:49 +02:00
luisδμ
70f3988046 Show warning when selecting a copy of conflicted variant (#6743) 2025-06-20 11:52:07 +02:00
Andrey Antukh
ec021d944d Merge remote-tracking branch 'origin/staging' into develop 2025-06-20 11:37:50 +02:00
Alejandro Alonso
3d0c3013e5 Merge pull request #6746 from penpot/niwinz-staging-path-editor-fixes
 Add missing fixes to the path edition mode
2025-06-20 11:31:17 +02:00
Elena Torró
f1a6b46165 Merge pull request #6745 from penpot/superalex-bug-fix-text-image-strokes
🐛 Fix image fill strokes are not rendered correctly for texts
2025-06-20 11:14:24 +02:00
Andrey Antukh
3274a74611 Add missing fixes to the path edition mode 2025-06-20 11:06:48 +02:00
Belén Albeza
2412402a23 Fix size of 'wasm renderer' debug text on dpr > 1 2025-06-20 10:55:33 +02:00
Alejandro Alonso
5375029497 🐛 Fix image fill strokes are not rendered correctly for texts 2025-06-20 10:52:26 +02:00
Alejandro Alonso
3a0ac577f8 Merge pull request #6720 from penpot/niwinz-staging-path-editor-fixes
 Fix path edition/drawing state on workspace
2025-06-20 10:34:19 +02:00
Andrés Moya
cf78e68787 🐛 Fix token unapply for text shapes 2025-06-20 10:15:49 +02:00
Aitor Moreno
8bfc314b17 Merge pull request #6700 from penpot/superalex-fix-async-content-rendering
🐛 Fix asynchronous content rendering
2025-06-20 10:11:28 +02:00
Elena Torró
38112e287a Merge pull request #6734 from penpot/ladybenko-11392-no-ui-in-tests
🔧 Make visual regression tests to hide the UI when taking a screenshot (render-wasm tests only)
2025-06-20 09:05:30 +02:00
Alejandro Alonso
43ba30c694 Merge pull request #6741 from penpot/niwinz-staging-join-nodes
🔥 Remove incorrect assert on path segment helper
2025-06-20 08:22:27 +02:00
Andrey Antukh
520ea0e094 🔥 Remove incorrect assert on path segment helper 2025-06-20 08:01:59 +02:00
Alejandro Alonso
5c7a4ce5b7 🐛 Fix fill images for text 2025-06-20 07:45:28 +02:00
Andrey Antukh
0c260c626b Make the path creation flow consistent 2025-06-20 07:41:06 +02:00
Andrey Antukh
db4721f692 Forward path edition state from vieweport to editor 2025-06-20 07:41:06 +02:00
Andrey Antukh
f8d63f5d9d 🐛 Fix incorrect path drawing/edition state management 2025-06-20 07:41:06 +02:00
Andrey Antukh
7e909dfbe8 Merge remote-tracking branch 'origin/staging' into develop 2025-06-19 15:35:19 +02:00
Alonso Torres
7f7f0893d0 🐛 Fix sidebar width in localhost (#6732) 2025-06-19 15:31:27 +02:00
Andrey Antukh
0f46efc117 Merge pull request #6733 from penpot/eva-bugfixing
🐛 Fix some bugs
2025-06-19 15:29:34 +02:00
María Valderrama
22fbc3fa5f 💄 Improve dashboard's sidebar (#6736) 2025-06-19 15:28:32 +02:00
Alejandro Alonso
d71fa659d5 🐛 Fix asynchronous content rendering 2025-06-19 14:03:40 +02:00
Alejandro Alonso
d0425cabda Merge pull request #6721 from penpot/ladybenko-11276-fix-modifiers-dpr
🐛 Fix panning and scroll when dpr > 1 (render wasm)
2025-06-19 14:01:47 +02:00
Eva Marco
909838c8c4 🐛 Fix snap nodes shortcut 2025-06-19 14:01:15 +02:00
Eva Marco
86e36061fb 🐛 Fix multiple values on text 2025-06-19 13:41:35 +02:00
Belén Albeza
9852d24b83 🔧 Make visual regression tests to hide the UI when taking a screenshot (render-wasm tests only) 2025-06-19 13:37:52 +02:00
Alejandro Alonso
2239482049 Merge pull request #6717 from penpot/alotor-grid-editor
 Support grid editor with wasm modifiers
2025-06-19 13:32:27 +02:00
Xavier Julian
4ea4a1e130 ♻️ Restructure UI files for token settings 2025-06-19 13:10:09 +02:00
alonso.torres
11467e26a2 🐛 Fix problem with flex wrap in wasm 2025-06-19 13:03:25 +02:00
alonso.torres
b997d5a320 🐛 Fix problem with grid layout wasm 2025-06-19 13:03:25 +02:00
alonso.torres
5b4cd9f4f1 🐛 Fix problem when moving masks, bools, groups with wasm 2025-06-19 13:03:25 +02:00
alonso.torres
58e5748b4f 🐛 Fix wasm layout problems 2025-06-19 13:03:25 +02:00
alonso.torres
b2647f30c2 Support grid editor with wasm modifiers 2025-06-19 13:03:25 +02:00
Eva Marco
fbdabcd913 🐛 Fix button width 2025-06-19 12:51:53 +02:00
luisδμ
72f2a409f9 Show warning when duplicated variant prop name and value (#6639)
*  Show warning when duplicated variant prop name and value

* 📎 PR changes
2025-06-19 12:34:28 +02:00
Alejandro Alonso
191ea3d02a Merge pull request #6637 from penpot/mavalroot-missing-events
 Add missing user experience events
2025-06-19 12:11:07 +02:00
Xavier Julian
62a6f2c2f1 ♻️ Restructure UI files for theme creation modal 2025-06-19 11:59:25 +02:00
Andrey Antukh
b747ccc382 🎉 Add shortcut helper for creating events 2025-06-19 11:38:08 +02:00
Andrey Antukh
892c9ab12c Add minor code consistency fixes 2025-06-19 11:38:08 +02:00
Eva Marco
b595d5abf8 🐛 Fix radio buttons gap 2025-06-19 11:23:32 +02:00
María Valderrama
cb46d643ac Add missing user experience events 2025-06-19 11:22:58 +02:00
Xavier Julian
105e0ba75f ♻️ Create themes folder and themes root file 2025-06-19 10:53:31 +02:00
Belén Albeza
4a9f6ea04e 🐛 Fix panning and scroll when dpr > 1 (render wasm) 2025-06-19 10:42:19 +02:00
Andrey Antukh
03a82c18cb Merge pull request #6726 from penpot/eva-fix-comment-icon
🐛 Fix comment icon fill
2025-06-19 09:43:02 +02:00
Andrey Antukh
a559547f97 Merge pull request #6727 from penpot/bameda-fix-typo
🐛 Fix a typo
2025-06-19 09:42:35 +02:00
David Barragán Merino
b91d703060 🐛 Fix a typo 2025-06-19 09:34:53 +02:00
luisδμ
e7e39a5521 Avoid duplicated property names adding a number (#6681)
*  Avoid repeated property names appending a number

* 📎 PR changes

* 🐛 Adjust rules for incrementing numbers in prop names
2025-06-19 09:11:41 +02:00
Belén Albeza
70a29c43ec 🔧 Fix race condition in colorpicket gradient tests (#6722) 2025-06-19 08:57:38 +02:00
Eva Marco
7bde3d0ec1 🐛 Fix comment icon fill 2025-06-19 08:51:29 +02:00
Andrey Antukh
386c729507 Merge remote-tracking branch 'origin/staging' into develop 2025-06-19 08:49:11 +02:00
Alejandro Alonso
219dca3ab8 Merge pull request #6723 from penpot/elenatorro-11385-fix-text-gradients
🐛 Fix text fill gradients and add visual regression test for text…
2025-06-19 07:03:19 +02:00
Elena Torro
5c120b601c 🐛 Fix text fill gradients and add visual regression test for text styles 2025-06-18 18:02:28 +02:00
Andrey Antukh
54643b79f6 🐛 Fix exception on changing hex color on colorpicker gradient editor (#6718) 2025-06-18 17:59:14 +02:00
Elena Torro
cf8006ce9c 🔧 Add option to skip tutorial/walkthrough when creating profiles for dev purposes 2025-06-18 17:00:46 +02:00
Kelp
71afccbeb5 Adds new font-size icon to the DS
Signed-off-by: Kelp <5446186+NatachaMenjibar@users.noreply.github.com>
2025-06-18 15:55:13 +02:00
Andrey Antukh
f0b82864dd Merge pull request #6719 from penpot/andy-changelog-update
📚 Update changelog
2025-06-18 15:04:28 +02:00
Andres Gonzalez
29244776f0 📚 Update changelog 2025-06-18 14:11:35 +02:00
Andrey Antukh
bbb9713f97 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 14:09:49 +02:00
Andrey Antukh
40c33c7dcc Merge pull request #6715 from penpot/alotor-bug-fix-workaround
🐛 Fix problem fetching images
2025-06-18 14:09:29 +02:00
Andrey Antukh
063c6e7771 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 13:34:47 +02:00
alonso.torres
1a6fcb5daf 🐛 Fix problem fetching images 2025-06-18 13:26:15 +02:00
Andrey Antukh
34febfc833 📎 Update indirect dependencies 2025-06-18 13:20:45 +02:00
Andrey Antukh
2c0abea254 📎 Add missing deps for build wasm tests 2025-06-18 12:45:25 +02:00
Andrey Antukh
4524782282 📎 Adapt backend test to devenv changes 2025-06-18 12:35:58 +02:00
Andrey Antukh
b8b56d5aa4 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 10:54:17 +02:00
Alejandro Alonso
2a5b087aa4 Merge pull request #6714 from penpot/niwinz-staging-fix-path-edition
🐛 Fix unexpected exception on selectiong node on non path shape
2025-06-18 10:45:36 +02:00
Andrey Antukh
402508a710 Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 10:41:30 +02:00
Alejandro Alonso
1f034654a0 Merge pull request #6705 from penpot/niwinz-staging-docker-improvements
 Major improvement and update to docker images and devenv
2025-06-18 10:39:51 +02:00
Andrey Antukh
88ed08916e Merge remote-tracking branch 'origin/staging' into develop 2025-06-18 10:39:23 +02:00
Andrey Antukh
a9a0970001 Merge pull request #6679 from penpot/niwinz-develop-enhacements-4
 Add editors count to get-owned-teams rpc method response
2025-06-18 10:38:48 +02:00
Andrey Antukh
1576016999 Merge pull request #6711 from penpot/elenatorro-11212-fix-right-sidebar-overflow
🐛 Fix right-sidebar width overflow
2025-06-18 10:38:18 +02:00
Andrey Antukh
5ea515cc9f Merge pull request #6713 from mdbenito/doc/undo-ns
📚 Document app.main.data.workspace.undo
2025-06-18 10:37:42 +02:00
Andrey Antukh
e3cce104e1 🐛 Fix unexpected exception on selectiong node on non path shape 2025-06-18 10:12:24 +02:00
Alejandro Alonso
a24631ac66 Merge pull request #6710 from penpot/niwinz-staging-path-make-curve-point
🐛 Fix incorrect behavior of `make-curve-point` fn
2025-06-18 10:06:28 +02:00
Miguel de Benito Delgado
c0df527b3d 📚 Document app.main.data.workspace.undo 2025-06-18 09:52:15 +02:00
Andrey Antukh
fd81ea6a84 🔥 Remove commented code 2025-06-18 09:46:15 +02:00
Andrey Antukh
a3c7151157 ⬆️ Update indirect exporter module dependencies 2025-06-18 09:46:15 +02:00
Andrey Antukh
2d4fc3e05f ♻️ Refactor devenv build mechanism
This introduces multistage build process for devenv making
different dependencies build depend on its own (per example, when
jvm version is changed, only the jvm stage is rebuild)

This commit also introduces imagemagick 7.x custom build
in the same way as we have on public docker images, so on
devenv we use the same version.
2025-06-18 09:46:15 +02:00
Andrey Antukh
b01dea20d6 Add imagemagick custom build to backend docker image
This allows us be in control of the imagemagick version and not
depend on the version available on the distro repository, which
right now only ships the legacy 6.x version
2025-06-18 09:46:15 +02:00
Andrey Antukh
3f40a830fd Revisit exporter docker image dependencies
And remove cache for not include unnecessary files on
the docker image.
2025-06-18 09:46:15 +02:00
Andrey Antukh
50e9816526 Use custom jre for backend image
Mainly for reduce the size of the image and not include
not necessary modules. It also starts using multistage
dockerfiles for fetch and setup jdk and node and then
only copy the required files.
2025-06-18 09:46:15 +02:00
Andrey Antukh
4c0165da62 ⬆️ Update node dependency on devenv 2025-06-18 09:46:15 +02:00
Andrey Antukh
42d36bae0a Update several node based dependencies 2025-06-18 09:46:15 +02:00
Andrey Antukh
6dd0f4f164 🔥 Remove unused jvm options from backend start-dev script 2025-06-18 09:46:15 +02:00
Elena Torro
f7c4bd77be 🐛 Fix right-sidebar width overflow 2025-06-18 09:10:01 +02:00
Alejandro Alonso
5d72954611 Merge pull request #6707 from penpot/niwinz-staging-fix-path-join-nodes
🐛 Fix incorrect type handling on path join nodes operation
2025-06-18 07:37:58 +02:00
Alejandro Alonso
9930f54558 Merge pull request #6706 from penpot/niwinz-staging-opacity-fix
🐛 Fix incorrect handling of opacity change on gradient stop
2025-06-18 07:32:34 +02:00
Alejandro Alonso
667c9ddbb9 Merge pull request #6698 from penpot/niwinz-staging-fix-exporter
🐛 Fix incorrect params handling on exporter
2025-06-18 07:29:36 +02:00
Alejandro Alonso
4f09005586 Merge pull request #6694 from penpot/niwinz-staging-fix-profile-lang
🐛 Fix bad initial state on profile settings
2025-06-18 07:26:42 +02:00
Alejandro Alonso
cbc98a761f Merge pull request #6693 from penpot/niwinz-staging-register-regression
🐛 Fix regression on singup flow
2025-06-18 07:23:37 +02:00
Andrey Antukh
c3b306201d 🐛 Fix incorrect behavior of make-curve-point fn 2025-06-17 23:18:34 +02:00
Andrey Antukh
a772b442c8 🐛 Fix incorrect type handling on path join nodes operation 2025-06-17 17:34:37 +02:00
Alejandro Alonso
6a46110f80 Merge pull request #6672 from penpot/superalex-fix-focus-mode-wasm
🐛 Fix focus mode for wasm render
2025-06-17 16:55:47 +02:00
Alejandro Alonso
1c7aea4b84 🐛 Fix focus mode for wasm render 2025-06-17 16:42:45 +02:00
Andrey Antukh
90116c207f Merge remote-tracking branch 'origin/staging' into develop 2025-06-17 16:23:35 +02:00
Pablo Alba
46fe3a6239 📚 Add comments on convoluted variants code (#6704) 2025-06-17 16:17:56 +02:00
Andrey Antukh
332bbc71c3 🐛 Fix incorrect handling of opacity change on gradient stop 2025-06-17 16:14:10 +02:00
Andrey Antukh
c5b0206bf0 🐛 Add workaround for webkit issue on fileReader (#6697)
On reading blob as data uri
2025-06-17 15:25:58 +02:00
Elena Torró
01311225c7 Merge pull request #6695 from penpot/superalex-fix-allocate-0-bytes-path-attrs
🐛 Fix wasm render path issues
2025-06-17 14:44:15 +02:00
Elena Torró
717f3e1b32 Merge pull request #6703 from penpot/elenatorro-update-render-wasm-docs
📚 Add schemas and links to render-wasm README
2025-06-17 14:24:47 +02:00
Elena Torro
9a44bd0967 📚 Add schemas and links to render-wasm README 2025-06-17 14:06:46 +02:00
Belén Albeza
b92e108205 Are more visual regression tests for wasm (#6702) 2025-06-17 12:39:38 +02:00
Alejandro Alonso
8c6a80829f Merge pull request #6671 from penpot/azazeln28-refactor-minor-perf-issues
♻️ Refactor some minor perf issues
2025-06-17 11:30:43 +02:00
Elena Torró
039a544990 Merge pull request #6675 from penpot/alotor-grid-helpers
 Add grid helpers to wasm
2025-06-17 11:14:18 +02:00
Alejandro Alonso
60dbf02896 Merge pull request #6701 from penpot/elenatorro-fix-custom-font-load
🐛 Fix storing custom fonts
2025-06-17 10:06:02 +02:00
Elena Torro
d248dd09bc 🐛 Fix storing custom fonts 2025-06-17 09:38:17 +02:00
Alejandro Alonso
81d2b9a82e 🐛 Fix group fills propagation when fill is none 2025-06-17 09:17:54 +02:00
Alejandro Alonso
1bb6f2754c 🐛 Fix allocate 0 bytes for path attrs 2025-06-17 08:43:00 +02:00
Andrey Antukh
22ca1ab5f9 🐛 Fix incorrect params handling on exporter
that caused unexpected exception on multiple exports
2025-06-16 16:58:03 +02:00
Andrey Antukh
df84396fea Merge remote-tracking branch 'origin/staging' into develop 2025-06-16 16:51:14 +02:00
Andrey Antukh
acf0d02c5d Merge pull request #6696 from penpot/eva-add-export-metrics
🎉 Add token export event tag
2025-06-16 16:50:49 +02:00
Eva Marco
5ccf34fdae 🐛 Fix reposition on tooltip 2025-06-16 16:34:53 +02:00
Eva Marco
41c8bba1df ♻️ Remove anonym fn 2025-06-16 16:23:53 +02:00
Eva Marco
325a78a967 🎉 Add token export event tag 2025-06-16 16:20:14 +02:00
mirakernel
409ff31c30 🐛 Avoid crash on empty string in interaction prototype (#6687)
Fixes #6469

Using `uuid/parse` caused a crash when "Relative to" field was set to "auto",
producing an empty string. This change uses `uuid/parse*` instead, which safely
returns nil for invalid or empty inputs, preventing the crash.

Signed-off-by: Dmitriy Mikheev <mirakernel.disroot.org>
Co-authored-by: kira <kira@kira.kira>
2025-06-16 15:19:45 +02:00
mirakernel
a56822ad99 🐛 Avoid crash on empty string in interaction prototype (#6687)
Fixes #6469

Using `uuid/parse` caused a crash when "Relative to" field was set to "auto",
producing an empty string. This change uses `uuid/parse*` instead, which safely
returns nil for invalid or empty inputs, preventing the crash.

Signed-off-by: Dmitriy Mikheev <mirakernel.disroot.org>
Co-authored-by: kira <kira@kira.kira>
2025-06-16 15:18:59 +02:00
Andrey Antukh
8cb42a63e5 🐛 Fix bad initial state on profile settings 2025-06-16 15:05:29 +02:00
Andrey Antukh
2af1feafb6 Merge pull request #6667 from penpot/niwinz-develop-enhacements-2
 Add internal changes to tooltip* ds component
2025-06-16 15:03:42 +02:00
Eva Marco
691a67b595 🐛 Fix tooltip height on safari 2025-06-16 14:45:12 +02:00
Andrey Antukh
f7e94accc3 Add internal performance oriented changes to tooltip* 2025-06-16 14:45:12 +02:00
Andrey Antukh
fc655224af Add memorization to icon-button* ds component 2025-06-16 14:45:12 +02:00
Andrey Antukh
8b0ead6832 🐛 Fix regression on singup flow
Move all params handling to prepare-register, just for consistency
2025-06-16 14:31:30 +02:00
Alejandro Alonso
10ae4dd3f7 Merge pull request #6689 from penpot/niwinz-staging-add-sr-lang
 Add several fixes & enhancements
2025-06-16 13:43:24 +02:00
Andrey Antukh
34d6e86e42 🐛 Fix incorrect theem selection on export progress bar color 2025-06-16 13:42:43 +02:00
Andrey Antukh
481d1ec53a Normalize layout schemas 2025-06-16 13:42:43 +02:00
Andrey Antukh
c6f4ee1974 ⬆️ Update cuerdas dependency
Fixes a corner case with camel->kebab casing
2025-06-16 13:42:41 +02:00
Andrey Antukh
f20032199a 🎉 Add Serbian lang 2025-06-16 13:42:22 +02:00
Elena Torró
4869373a43 🔧 Add methods to render text as path (#6624)
* 🔧 Refactor text strokes drawing

* 🔧 Add text to path methods for future usage

* 📚 Add text as paths internal documentation
2025-06-16 13:37:29 +02:00
María Valderrama
d0aac65c76 🐛 Fix misalignments at create account (#6692) 2025-06-16 13:26:33 +02:00
Pablo Alba
2d36a1f3e0 🐛 Fix when retrieving a variant from several with same props, it get the last one 2025-06-16 12:23:40 +02:00
Alejandro Alonso
38941d4811 Merge pull request #6676 from penpot/elenatorro-fix-load-pending-single-attr
🐛 Fix parsing pending callback on setting single shape attr
2025-06-16 11:50:57 +02:00
María Valderrama
f21e546bc1 Add import error event (#6690) 2025-06-16 10:39:56 +02:00
alonso.torres
0be8a6e0e6 Add grid helpers to wasm 2025-06-16 09:55:35 +02:00
Marina López
3624a14141 Subscription tests (#6669)
*  Subscription tests

*  Subscription tests
2025-06-16 09:31:50 +02:00
Peter Kahoun
141431bb9e Update cs.po - inflection fixes (#6677)
Signed-off-by: Peter Kahoun <kahi.cz@gmail.com>
2025-06-16 09:29:47 +02:00
Alonso Torres
874a658369 Adds generateFontFaces method to the plugins api (#6682)
*  Adds generateFontFaces method to the plugins api

* ⬆️ Update plugin runtime
2025-06-16 09:28:04 +02:00
Alonso Torres
c254ebd7c3 🐛 Fix problem with import modal style (#6683) 2025-06-16 09:26:35 +02:00
Elena Torro
f58ee2c89f 🔧 Add visual regression tests for font load 2025-06-11 13:22:23 +02:00
Pablo Alba
925b6c02d6 🎉 Separate the content of the text of the rest of properties on variants 2025-06-11 11:22:43 +02:00
Pablo Alba
9761cba337 ♻️ Restore separate the content of the text of the rest of properties on components updates
This reverts commit b2aaa5f0df.
2025-06-11 11:21:54 +02:00
Florian Schrödl
267a3af1e5 💄 Fixes export dialog styling issues (#6673)
* 🐛 Fix text overflow in file list
* 💄 Set monospace font for code-block component
* 💄 Reduce padding
2025-06-11 10:43:02 +02:00
Andrey Antukh
71f5806e23 Add editors count to get-owned-teams rpc method response 2025-06-11 08:40:58 +02:00
Elena Torro
330bee7839 🐛 Fix parsing pending callback on setting single shape attr 2025-06-10 21:34:41 +02:00
Andrey Fedorov
d44e4e5275 Add zip file format import for tokens 2025-06-10 17:32:06 +02:00
Aitor Moreno
369e134bed ♻️ Refactor some minor perf issues 2025-06-10 16:00:10 +02:00
Andrey Antukh
b8ee7cad26 Merge pull request #6663 from penpot/alotor-plugins-bugfixes
 Plugins fixes and enhancements
2025-06-10 15:30:32 +02:00
alonso.torres
26efc9f0c8 📚 Add documentation for new plugin permission 2025-06-10 15:09:22 +02:00
alonso.torres
1d593e1287 ⬆️ Updates plugins runtime 2025-06-10 15:09:22 +02:00
alonso.torres
b34c161fc3 Adds local store proxy in plugins 2025-06-10 15:09:22 +02:00
alonso.torres
ed0c84a069 Exposes board clipContent and showInViewMode properties in plugins 2025-06-10 15:06:31 +02:00
alonso.torres
29466b47fe Adds skipChildren to export parameters 2025-06-10 15:06:30 +02:00
Elena Torró
f02dd9f8dc Merge pull request #6651 from penpot/superalex-path-fixes
🐛 Path fixes
2025-06-10 12:11:25 +02:00
David Barragán Merino
b385f055e0 🐳 Migrate from Redis to Valkey (#6666) 2025-06-10 10:08:10 +02:00
Alejandro Alonso
e91550cd9d Merge pull request #6646 from penpot/ladybenko-10904-playwright-wasm
🔧 Set up visual regression tests for wasm renderer
2025-06-10 09:24:59 +02:00
Alejandro Alonso
5faa619bc4 Merge pull request #6664 from penpot/niwinz-hotfix-1
🐛 Add better fix for path transformation
2025-06-10 08:51:51 +02:00
Alejandro Alonso
ed76b1b1ee 🎉 Support for webp images (#6665) 2025-06-10 08:40:30 +02:00
Belén Albeza
afdbb5cf2f 📚 Add documentation specific for wasm visual regression tests 2025-06-09 17:46:18 +02:00
Belén Albeza
971b92a75b 🔧 Make mockAsset to accept an array of asset ids too 2025-06-09 17:46:18 +02:00
Belén Albeza
479406b884 🔧 Add initial snapshots 2025-06-09 17:46:18 +02:00
Belén Albeza
1a10b7ebfd 🔧 Wait for first render using a custom event (visual regression tests) 2025-06-09 17:46:18 +02:00
Belén Albeza
59a4b51d2c 🔧 Set up playwright project for render wasm 2025-06-09 17:01:29 +02:00
Andrey Antukh
74430aad21 📎 Add missing emsd loading to devenv bashrc 2025-06-09 15:57:31 +02:00
Alejandro Alonso
78d6166bac 🐛 Fix caps for rounded paths 2025-06-09 14:40:54 +02:00
Alejandro Alonso
8db910baee 🐛 Fix rendering vertical and horizontal paths 2025-06-09 13:04:18 +02:00
Alejandro Alonso
a9702f104d 🐛 Fix shapes without fills contained in a group with fills 2025-06-09 13:04:18 +02:00
Andrey Antukh
01dda6dd6b 🐛 Add better fix for path transformation 2025-06-09 12:46:53 +02:00
Andrey Antukh
b2bc481672 Merge remote-tracking branch 'origin/develop' into staging 2025-06-09 12:21:09 +02:00
Andrey Antukh
8c337f508b Merge remote-tracking branch 'origin/staging' into develop 2025-06-09 12:20:55 +02:00
Andrey Antukh
f9f45dc612 🐛 Fix path rotation issue (#6662) 2025-06-09 12:14:09 +02:00
Anonymous
93542854c1 🌐 Add translations for: Swedish
Currently translated at 86.6% (1610 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-06-09 11:13:39 +02:00
Anonymous
858f94c6d2 🌐 Add translations for: Dutch
Currently translated at 96.5% (1793 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-06-09 11:13:38 +02:00
Anonymous
f932d663b0 🌐 Add translations for: Latvian
Currently translated at 96.5% (1793 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-06-09 11:13:38 +02:00
Anonymous
63631e60cd 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 96.5% (1793 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-06-09 11:13:38 +02:00
Anonymous
6018cd67c5 🌐 Add translations for: Croatian
Currently translated at 87.1% (1619 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-06-09 11:13:37 +02:00
Anonymous
4856d0e957 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 85.7% (1592 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-06-09 11:13:37 +02:00
Anonymous
995ca4d7d9 🌐 Add translations for: Czech
Currently translated at 87.1% (1618 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-06-09 11:13:37 +02:00
Anonymous
d5d254a7f3 🌐 Add translations for: Italian
Currently translated at 96.5% (1793 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-06-09 11:13:36 +02:00
Anonymous
69f45dc811 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 87.3% (1623 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-06-09 11:13:35 +02:00
Anonymous
7534ad283c 🌐 Add translations for: Hebrew
Currently translated at 96.5% (1793 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-06-09 11:13:34 +02:00
Anonymous
3d6695171d 🌐 Add translations for: Indonesian
Currently translated at 92.7% (1722 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-06-09 11:13:34 +02:00
Anonymous
8fa28ce176 🌐 Add translations for: German
Currently translated at 89.9% (1670 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-06-09 11:13:34 +02:00
Anonymous
f03a0de665 🌐 Add translations for: French
Currently translated at 95.7% (1778 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-06-09 11:13:34 +02:00
Anonymous
555af2fa52 🌐 Add translations for: English
Currently translated at 99.7% (1852 of 1857 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/en/
2025-06-09 11:13:33 +02:00
Andrey Antukh
0683b20b25 🌐 Rehash and validate translation files 2025-06-09 11:01:08 +02:00
Hosted Weblate
163ae639ff 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-06-09 10:57:51 +02:00
Andrey Antukh
354ba91aa6 Merge remote-tracking branch 'weblate/develop' into develop 2025-06-09 10:57:13 +02:00
Andrey Antukh
38d9a9d2d5 📎 Update changelog 2025-06-09 10:29:18 +02:00
Yamila Moreno
beb3d16693 📎 Fix minor typo (#6661) 2025-06-09 10:25:16 +02:00
Andrey Antukh
5770c0cb02 Merge pull request #6625 from penpot/niwinz-develop-enhacements-1
 Several enhacements
2025-06-09 10:24:44 +02:00
Eva Marco
46ce9500fc 🐛 Fix absolute position tooltip message (#6660) 2025-06-09 09:52:35 +02:00
Alejandro Alonso
d008ea9edd Merge pull request #6652 from penpot/elenatorro-fix-load-fonts
🐛 Fix fonts initialization
2025-06-09 09:31:00 +02:00
Eva Marco
eba8d02c18 📚 Update changelog 2025-06-06 15:48:39 +02:00
Florian Schrödl
bb9daf7c03 Add export tokens modal with multi-file export (#6649) 2025-06-06 15:37:10 +02:00
Andrey Antukh
f1232fc461 🐛 Fix issues with old profile theme data 2025-06-06 15:25:49 +02:00
Andrey Antukh
9584e1b02d 📎 Add missing playwright chromium install to devenv 2025-06-06 15:10:52 +02:00
Edgars Andersons
96ccac5085 🌐 Add translations for: Latvian
Currently translated at 99.9% (1807 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-06-06 13:03:07 +00:00
Ingrid Pigueron
2540d58096 🌐 Add translations for: French
Currently translated at 98.7% (1786 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-06-06 13:03:05 +00:00
Andrey Antukh
de2695682d Allow encode mailto and self target link in markdown
On translation files
2025-06-06 14:03:52 +02:00
Andrey Antukh
e464d8cf9c 📎 Add library tests to circle-ci config 2025-06-06 14:03:52 +02:00
Andrey Antukh
a8433bcef3 🐛 Fix incorrect decoding of library color 2025-06-06 14:03:52 +02:00
Andrey Antukh
644dd9ff44 🎉 Add support for progress reporting to library export method 2025-06-06 14:03:52 +02:00
Andrey Antukh
27d2724153 🐛 Fix incorrect shape filtering on bool creation on library 2025-06-06 14:03:52 +02:00
Andrey Antukh
c647d122d8 ⬆️ Update frontend dependencies 2025-06-06 14:03:52 +02:00
Andrey Antukh
e4a65f3a04 ⬆️ Update backend dependencies 2025-06-06 14:03:52 +02:00
Andrey Antukh
389f1d6502 ⬆️ Update common dependencies 2025-06-06 14:03:52 +02:00
Andrey Antukh
b91a670198 Replace redis with valkey on devenv compose file 2025-06-06 14:03:52 +02:00
Andrey Antukh
5650629b73 Add minimal wait to run-devenv when no compose is started 2025-06-06 14:01:10 +02:00
Andrey Antukh
a4310b4213 🔥 Remove usage of dm/assert on data.profile ns file 2025-06-06 14:01:10 +02:00
Elena Torro
a8d4b293dc 🐛 Fix loading same resources multiple times 2025-06-06 12:26:35 +02:00
Alejandro Alonso
fec7d5cff2 🐛 Fix detach stroke (#6655) 2025-06-06 12:24:18 +02:00
Alejandro Alonso
5e254ff3f6 Merge pull request #6643 from penpot/azazeln28-refactor-performance-improvements
♻️ Minor performance improvements
2025-06-06 11:38:53 +02:00
Elena Torro
8c20159fb0 🐛 Cache emoji font correctly 2025-06-06 11:28:50 +02:00
Elena Torro
79e1c29306 🐛 Fix emoji detection and load 2025-06-06 11:28:50 +02:00
Aitor Moreno
fcd3e5c34c ♻️ Refactor surface iteration 2025-06-06 10:21:31 +02:00
Eva Marco
8b9f15f414 🐛 Fix select color tooltip error (#6648) 2025-06-05 21:15:00 +02:00
Pablo Alba
b2aaa5f0df ♻️ Remove separate the content of the text of the rest of properties on components updates 2025-06-05 16:43:23 +02:00
Aitor Moreno
8922e7454f ♻️ Refactor some allocations 2025-06-05 15:37:03 +02:00
Alejandro Alonso
4f7d97a31e Merge pull request #6641 from penpot/ladybenko-fix-fills-wasm
🐛 Fix set-shape-fills serialization (wasm renderer)
2025-06-05 14:24:27 +02:00
Stas Haas
e32af5e71e 🌐 Add translations for: German
Currently translated at 92.4% (1672 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-06-05 14:09:36 +02:00
Ingrid Pigueron
6cb5b812da 🌐 Add translations for: French
Currently translated at 98.0% (1772 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-06-05 14:09:36 +02:00
Belén Albeza
ca24e23775 🐛 Fix set-shape-fills serialization (wasm renderer) 2025-06-05 14:04:41 +02:00
Alejandro Alonso
28ed62fb2c Merge pull request #6640 from penpot/niwinz-develop-hotfix-1
🐛 Remove fills from paragraph-set nodes on text shapes
2025-06-05 14:03:18 +02:00
Andrey Antukh
9c4b60e95f 🐛 Fix incorrect opacity assignation on changing fill from solid to gradient 2025-06-05 13:34:13 +02:00
Andrey Antukh
6719902647 Add color checks and test of event creation for fills 2025-06-05 13:34:13 +02:00
Andrey Antukh
cf8307af8f 📎 Update changelog 2025-06-05 13:29:51 +02:00
Andrey Antukh
8c54cb764f 🐛 Remove fills from paragraph-set nodes on text shapes 2025-06-05 13:11:39 +02:00
Eva Marco
4a9fef12eb 🐛 Fix url params on invitation link (#6638) 2025-06-05 12:28:53 +02:00
Alejandro Alonso
ea4e69f381 Merge pull request #6633 from penpot/niwinz-develop-hotfix-1
🐛 Fix library color detach operation
2025-06-05 11:04:57 +02:00
Anderson Paulo
5fed12d807 🌐 Add translations for: Portuguese
Currently translated at 5.1% (93 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt/
2025-06-05 11:01:50 +02:00
Edgars Andersons
a2f41a7a40 🌐 Add translations for: Latvian
Currently translated at 99.3% (1797 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-06-05 11:01:47 +02:00
Stas Haas
f4fd9fa13d 🌐 Add translations for: German
Currently translated at 92.3% (1669 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-06-05 11:01:47 +02:00
Andrés Moya
8a0aa20789 🔧 Renames, privacy adjustments and other small enhancements 2025-06-05 10:58:23 +02:00
Andrés Moya
1b3a200010 🔧 Reorder code for more legibility 2025-06-05 10:58:23 +02:00
Andrés Moya
70263ba901 🔥 Remove unused code 2025-06-05 10:58:23 +02:00
Andrés Moya
5c8f6dd498 🔧 Add internal id to all token entities 2025-06-05 10:58:23 +02:00
Eva Marco
a2abaea637 ♻️ Update button-icon with tooltip component (#6539)
* 🐛 Add tooltip to base icon button

* 🎉 Update id prop

* 🐛 Fix test
2025-06-05 10:18:56 +02:00
Alejandro Alonso
55997a3d4a Merge pull request #6634 from penpot/ladybenko-fit-set-shape-fills
🐛 Fix wasm crash in set_shape_fills (wasm) when fill list is empty
2025-06-05 10:07:34 +02:00
Belén Albeza
a30ab17605 🐛 Fix wasm crash in set_shape_fills (wasm) when fill list is empty 2025-06-05 09:52:51 +02:00
Eva Marco
ab0219876e Add numeric token type (#6575)
*  Add numeric type token

* 🐛 Fix comments
2025-06-05 09:33:54 +02:00
Andrey Antukh
19961f440a 🐛 Fix library color detach operation 2025-06-05 09:33:06 +02:00
Alejandro Alonso
db84eb365b 🐛 Fix strokes migration (#6632) 2025-06-05 08:42:57 +02:00
Marina López
b7f97dbeea 🐛 Subscription review fixes (#6628) 2025-06-04 21:01:48 +02:00
Elenzakaleidos
4cbaef1451 💄 New video in the README.md (#6629)
I added a new video in the readme page cause the previous one had the wrong name

Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2025-06-04 21:00:49 +02:00
andrés gonzález
e1adbdfd9f 📚 Update changelog (#6616) 2025-06-04 15:12:33 +02:00
Alejandro Alonso
687e1e7b0b Merge pull request #6569 from penpot/niwinz-fills-cleanup
♻️ Clean and sanitize color types
2025-06-04 14:26:12 +02:00
Alejandro Alonso
055ee27be0 🐛 Add migrations to fix colors 2025-06-04 14:01:48 +02:00
Andrés Moya
47af278f5e 🐛 Remove unneeded check 2025-06-04 13:11:16 +02:00
Andrey Antukh
29ad99d685 🐛 Fix incorrect declaration of tokens lib schema decoder 2025-06-04 13:03:51 +02:00
Andrey Antukh
3a8b312f6d 🐛 Fix calculate hash on empty path data instance 2025-06-04 13:03:51 +02:00
Andrey Antukh
8f55269522 🐛 Remove unknown types from stroke style schema 2025-06-04 13:03:51 +02:00
Andrey Antukh
f86ce38f04 🎉 Add Fills binary data type 2025-06-04 13:03:51 +02:00
Andrey Antukh
b97a3f9783 Add tests for common buffers namespace to the cljs test runner 2025-06-04 12:46:49 +02:00
Andrey Antukh
91807151ba Add stricter validation for gradient stop offset
It should be from a number from 0 to 1 inclusive
2025-06-04 12:46:49 +02:00
Andrey Antukh
5c225a51ce Add the ability to retrieve a set of keys from map schemas 2025-06-04 12:46:49 +02:00
Andrey Antukh
3d61924162 ♻️ Change internal naming for PathData cljs impl
For make it more consistent with CLJ impl and move
the equals helper to buffers for make it reusable by
other binary types.
2025-06-04 12:46:49 +02:00
Andrey Antukh
580013bc3f ⬆️ Update shadow-cljs to 3.1.5 2025-06-04 12:46:49 +02:00
Andrey Antukh
9ec5467e2a 🐛 Fix incorrect neested data structure validation on components
This commit upgrades rumext where the issue is fixed. It also
adapts the swatch component for correct data passing.
2025-06-04 12:46:49 +02:00
Andrey Antukh
b113736321 📎 Disable shadow-cljs reload on storybook build 2025-06-04 12:46:49 +02:00
Andrey Antukh
22db773b2e 🔥 Remove unused code from worker import ns 2025-06-04 12:46:49 +02:00
Andrey Antukh
91636ffc41 🐛 Adapt inspect code to color type changes
Still contains broken code because it was already broken
2025-06-04 12:46:49 +02:00
Andrey Antukh
7160334cb9 ♻️ Add stricter validation for colors, fills and strokes 2025-06-04 12:46:49 +02:00
Andrey Antukh
d08d2f49ac Add better defaults for several number schema generators 2025-06-04 12:46:49 +02:00
Andrey Antukh
8f774a3611 Simplify media mime types structs 2025-06-04 12:46:49 +02:00
Andrey Antukh
d9d2cc7b4e Split byte buffer helpers from types path impl 2025-06-04 12:46:49 +02:00
Andrey Antukh
2e0fd6ec1b Merge pull request #6626 from penpot/niwinz-develop-fixes-4
🐛 Several bugfixes
2025-06-04 12:39:39 +02:00
Andrey Antukh
d2d39aad30 Make theme toggle skip "system" theme 2025-06-04 12:24:01 +02:00
Andrey Antukh
d6e0001ac4 🐛 Fix encoding and decoding issue with tokens lib type 2025-06-04 12:12:31 +02:00
Elena Torró
793c01a0a1 Merge pull request #6611 from penpot/elenatorro-add-new-render-texts-docs
📚 Add texts documentation
2025-06-04 10:11:31 +02:00
Elena Torró
bbac5d050e Merge pull request #6623 from penpot/ladybenko-11106-send-all-fills
 Send all fills of a shape in a single wasm call
2025-06-04 09:18:39 +02:00
Alejandro Alonso
5a7d9e3f18 Merge pull request #6609 from penpot/elenatorro-11213-fix-language-font-fallback
🎉 Implement font fallback to support multiple languages
2025-06-03 17:19:39 +02:00
Edgars Andersons
1a921c2750 🌐 Add translations for: Latvian
Currently translated at 99.1% (1792 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-06-03 14:04:12 +00:00
Yaron Shahrabani
39f145b8b1 🌐 Add translations for: Hebrew
Currently translated at 99.9% (1807 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-06-03 14:04:10 +00:00
Stas Haas
6611769dc9 🌐 Add translations for: German
Currently translated at 92.2% (1667 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-06-03 14:04:08 +00:00
Eva Marco
b3d230ba16 📚 Add copyright to docs files (#6618) 2025-06-03 15:49:24 +02:00
Eva Marco
ddae0026fe 🎉 Add metrics to new multi file import (#6610) 2025-06-03 15:48:22 +02:00
Belén Albeza
9fc3f4858a Send all fills of a shape in a single wasm call 2025-06-03 15:37:36 +02:00
andrés gonzález
1120c7c7fb 🐛 Fix copy in error message (#6615) 2025-06-03 12:28:49 +02:00
Andrey Antukh
e9bd44b819 Merge remote-tracking branch 'origin/staging' into develop 2025-06-03 10:44:11 +02:00
Andrey Antukh
2244bf6aa7 Merge remote-tracking branch 'origin/main' into staging 2025-06-03 10:43:39 +02:00
Andrey Antukh
f4ef4a705c Merge tag '2.7.2-RC1' 2025-06-03 10:43:14 +02:00
Elena Torro
c40de5fb87 🎉 Implement font fallback to support multiple languages 2025-06-03 08:21:59 +02:00
Elena Torro
9733c41ae4 🐛 Fix blend mode on merge fills 2025-06-03 08:21:59 +02:00
Alejandro Alonso
fe8d9fdd76 Merge pull request #6614 from penpot/niwinz-staging-backport-1
 Make the hash optional on binfile-v3
2025-06-03 08:13:42 +02:00
Alejandro Alonso
401fa823a0 Merge pull request #6612 from penpot/niwinz-develop-devenv
🐛 Fix build issues on devenv
2025-06-03 07:49:34 +02:00
Andrey Antukh
3da3281a56 🐛 Fix library compatibility issue on media encoding with penpot 2.7 (#6613) 2025-06-02 23:25:39 +02:00
Andrey Antukh
3131eec271 Make the hash optional on binfile-v3
Backport the change from develop
2025-06-02 23:24:35 +02:00
Andrey Antukh
1909189ce0 Use different approach for setup cargo home 2025-06-02 22:29:39 +02:00
Andrey Antukh
0ec0917b6d Add isolated-shell to manage.sh
Instead of attaching to an existing devenv, starts a new one.
2025-06-02 19:13:20 +02:00
Andrey Antukh
0e4c535edc 📎 Print current path on frontend scripts build script 2025-06-02 19:11:55 +02:00
Andrey Antukh
46f330fef3 Move several logic from init to entrypoint on devenv
For make commands consistent independently if they are executed
inside devenv or from manage.sh
2025-06-02 19:10:48 +02:00
Andrey Antukh
f067c86b02 🔥 Remove unnecesary env vars from bashrc (devenv) 2025-06-02 19:10:20 +02:00
Andrey Antukh
2b6a91819b Reduce verbosity of frontend build script 2025-06-02 18:05:11 +02:00
Andrey Antukh
1f652fe364 Remove arm64 build of devenv
Looks unused right now
2025-06-02 17:44:22 +02:00
Elena Torro
4c10aeefe6 📚 Add texts documentation 2025-06-02 16:36:25 +02:00
Andrey Antukh
e70da78a77 Merge remote-tracking branch 'origin/staging' into develop 2025-06-02 12:55:22 +02:00
Andrey Antukh
27ab910a64 📚 Update changelog 2025-06-02 12:36:47 +02:00
Alejandro Alonso
c1fa6be7c4 Merge pull request #6591 from penpot/azazeln28-refactor-render-iteration
♻️ Refactor render iteration
2025-06-02 12:33:19 +02:00
Andrey Antukh
2398c1fc2b Merge pull request #6604 from penpot/alotor-fix-sandbox-runtime
🐛 Add sandbox runtime
2025-06-02 12:30:51 +02:00
Alejandro Alonso
13859f90b9 Merge pull request #6601 from penpot/alotor-fix-move-guides
 Move guides and comments for wasm modifiers
2025-06-02 12:28:01 +02:00
Yamila Moreno
e2724d180b Merge pull request #6497 from penpot/yms-update-coc
📚 Update Code of conduct
2025-06-02 12:20:57 +02:00
Andrey Antukh
c6bccafd98 Merge pull request #6607 from penpot/andy-update-changelog
📚 Update changelog
2025-06-02 12:17:53 +02:00
Andrey Antukh
1357ab34eb 📚 Move library rework changes to its own changelog 2025-06-02 12:16:27 +02:00
Andres Gonzalez
6e9ee3d310 📚 Update changelog 2025-06-02 12:10:32 +02:00
Yamila Moreno
5816695246 📚 Update Code of Conduct 2025-06-02 12:09:20 +02:00
Yamila Moreno
0d9160506b 📚 Add direct link to the CoC 2025-06-02 12:09:20 +02:00
Yamila Moreno
c3c6628bf1 📚 Minor improvement in README / Getting started 2025-06-02 12:09:20 +02:00
Alejandro Alonso
8642ffba46 🐛 Fix frontend build (#6608) 2025-06-02 12:03:08 +02:00
Andrey Antukh
25372c3edf Persist ruler layout flag to local storage 2025-06-02 11:43:13 +02:00
Andrey Antukh
e13d1743da Merge pull request #6598 from penpot/superalex-deprecate-old-image-type
♻️ Migrations for deprecated types and attributes
2025-06-02 11:29:44 +02:00
luisδμ
02d1a1f0b1 Delete variant property when it has no value anywhere after editing a formula (#6586) 2025-06-02 09:50:27 +02:00
Alejandro Alonso
08aeb93710 Merge pull request #6606 from penpot/niwinz-develop-fixes-2
 Fix several issues on penpot library
2025-06-02 07:04:22 +02:00
Alejandro Alonso
04f0f77cd8 Merge pull request #6605 from penpot/niwinz-develop-fixes-1
🐛 Fix default theme setup
2025-06-02 07:02:59 +02:00
Andrey Antukh
15adf1bd06 📎 Set penpot library version to 1.0.2 2025-06-01 11:29:31 +02:00
Andrey Antukh
1080ffc6b8 Add correct library version on the metadata 2025-06-01 11:28:42 +02:00
Andrey Antukh
1450672341 Remove obsolete props from bool style attrs 2025-06-01 11:20:26 +02:00
Andrey Antukh
483e88d6a3 Add more descriptive names for playground samples 2025-06-01 11:20:26 +02:00
Andrey Antukh
9fee16f4a9 🐛 Fix compatibility issue with penpot 2.7 2025-06-01 11:20:26 +02:00
Andrey Antukh
89a09346a5 🐛 Fix incorrect boolean shapes generation on builder 2025-06-01 11:06:00 +02:00
Andrey Antukh
77fa235965 🐛 Fix incorrect boolean shape generation on file builder 2025-06-01 10:25:11 +02:00
Andrey Antukh
03e4ca12be ♻️ Move update-bool from common geom to types path 2025-06-01 10:24:09 +02:00
Andrey Antukh
229c9b8385 📎 Add minor changes to circleci cache management 2025-06-01 09:34:05 +02:00
Andrey Antukh
a4fab5c5bd 🐛 Fix default theme setup
broken on previous commits
2025-06-01 09:30:57 +02:00
Andrey Antukh
d8913ab18b Add minor changes to devenv for avoid repeated deps download (#6600)
*  Add minor changes to devenv for avoid repeated dependency download

*  Add minor changes to devenv for integrate payments service

*  Remove playwright deps install from circleci config

*  Move cargo_home to userspace on devenv start

*  Improve cache management on CI

*  Improve cargo installation

*  Add missing playwright install cmd on CI

*  Install cargo-watch on devenv

---------

Co-authored-by: David Barragán Merino <david.barragan@kaleidos.net>
2025-06-01 09:16:28 +02:00
Edgars Andersons
7ef2f4e67e 🌐 Add translations for: Latvian
Currently translated at 98.8% (1787 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-05-31 12:03:08 +02:00
Alejandro Alonso
1d065e68f4 🎉 Allow force render mode from get param (#6594) 2025-05-30 20:05:58 +02:00
Miguel de Benito Delgado
c9ceceb7e9 🔥 Remove old code for theme support (#6597) 2025-05-30 16:54:23 +02:00
luisδμ
ad26efaa5d Limit the length of property names and values to 60 chars (#6587) 2025-05-30 16:15:18 +02:00
alonso.torres
a3e17047a4 🐛 Add sandbox runtime 2025-05-30 15:40:36 +02:00
Alejandro Alonso
0552ef55cf Merge pull request #6603 from penpot/alotor-fix-duplicate-shapes
🐛 Fix problem in wasm when duplicate objects
2025-05-30 14:08:01 +02:00
Belén Albeza
d4c6063378 Avoid intercepting get-file-fragment in the playwright test 2025-05-30 13:53:00 +02:00
Belén Albeza
f23e460b2a Fix broken tokens test 2025-05-30 13:53:00 +02:00
Belén Albeza
35b29bb203 🐛 Fix font size input not displaying 'mixed' when needed 2025-05-30 13:53:00 +02:00
Alejandro Alonso
cd02905d1f ♻️ Migrate old fill text attributes 2025-05-30 13:51:05 +02:00
Alejandro Alonso
87d917bc2e ♻️ Deprecate old image type 2025-05-30 13:51:05 +02:00
alonso.torres
e8d1ea24d1 🐛 Fix problem in wasm when duplicate objects 2025-05-30 13:49:56 +02:00
Andrey Antukh
ad842872fb 🐛 Fix unexpected exception on get-team internal method
Because of invalid SQL
2025-05-30 13:49:05 +02:00
Alejandro Alonso
90744c182e Merge pull request #6602 from penpot/elenatorro-11214-use-text-decoration-from-leaf
🐛 Fix reading text-decoration and text-transform from leaf, and fallback to paragraph values
2025-05-30 13:33:58 +02:00
Alejandro Alonso
78aaf28532 Merge pull request #6498 from penpot/niwinz-develop-update-fonts
⬆️ Update google fonts
2025-05-30 13:28:08 +02:00
Elena Torro
4e2f905a26 🐛 Fix reading text-decoration and text-transform from leaf, and fallback to paragraph values 2025-05-30 13:22:39 +02:00
Andrey Antukh
d2cd99ed44 ⬆️ Update google fonts 2025-05-30 13:08:57 +02:00
Alejandro Alonso
885231e9a1 Merge pull request #6512 from penpot/niwinz-develop-custom-deletion-rules
♻️ Normalize logical deletion delay handling
2025-05-30 12:53:37 +02:00
Pablo Alba
baabfe2de8 🎉 Split text and its properties on components updates 2025-05-30 12:36:10 +02:00
alonso.torres
facb0227a0 Move guides and comments for wasm modifiers 2025-05-30 12:15:21 +02:00
Elena Torró
f6fe41af96 🔧 Add texts playground (#6599) 2025-05-30 12:10:34 +02:00
Edgars Andersons
f225fce9a1 🌐 Add translations for: Latvian
Currently translated at 98.4% (1780 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-05-30 11:01:47 +02:00
Yaron Shahrabani
3570d29575 🌐 Add translations for: Hebrew
Currently translated at 97.1% (1756 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-05-30 11:01:45 +02:00
Andrey Antukh
f8489a521f Merge pull request #6590 from penpot/niwinz-develop-library-fixes
 Add minor enhancements to penpot library
2025-05-30 10:35:41 +02:00
Andrey Antukh
cc76a42088 Merge pull request #6561 from mdbenito/feature/5030-use-system-theme
 Use system theme
2025-05-30 10:34:46 +02:00
Andrey Antukh
50cc70201d Merge pull request #6578 from penpot/ladybenko-11105-cap-fills
🎉 Disable adding fills in UI when limit has been reached
2025-05-30 10:11:05 +02:00
Belén Albeza
e88b3bae5a 🔥 Remove gulp (#6592) 2025-05-30 10:03:22 +02:00
Aitor Moreno
2b2939b4b7 ♻️ Remove unnecesary sort operation 2025-05-30 09:56:58 +02:00
Miguel de Benito Delgado
6b25720155 🌐 Add missing translation 2025-05-29 20:11:11 +00:00
Miguel de Benito Delgado
96d099b71e Mock .matchMedia in global/window 2025-05-29 20:11:11 +00:00
Miguel de Benito Delgado
fab9e842e8 Support following system color scheme 2025-05-29 22:10:00 +02:00
Miguel de Benito Delgado
ee022e225c 🚧 UI to support switching to system theme 2025-05-29 22:10:00 +02:00
Andrey Antukh
1b3fcb0432 📎 Update circleci workflow 2025-05-29 13:50:39 +02:00
Andrey Antukh
37f88067b9 🔥 Remove library method addComponentInstance 2025-05-29 13:07:44 +02:00
Alejandro Alonso
2650eccd09 🐛 Fix set path attrs (#6589) 2025-05-29 12:27:08 +02:00
Andrey Antukh
969b171510 📎 Prepare for release 1.0.1 of the penpot library 2025-05-29 12:15:17 +02:00
Andrey Antukh
4b22a0ebfb 🐛 Make the library generate output compatible with penpot 2.7.x 2025-05-29 12:08:50 +02:00
Andrey Antukh
eafea7aec9 Merge pull request #6588 from penpot/niwinz-develop-fix-boolean
🐛 Fix incorrect bool shape creation
2025-05-29 11:38:47 +02:00
Belén Albeza
ce23fee292 Limit the amount of fills shown in the UI 2025-05-29 11:26:18 +02:00
Alejandro Alonso
f3d734357a Merge pull request #6409 from penpot/azazeln28-feat-compare-wasm
🎉 Add comparison tool to WASM playground
2025-05-29 11:20:54 +02:00
Andrey Antukh
d31f64796f 🐛 Fix incorrect bool shape creation issue 2025-05-29 11:16:12 +02:00
Andrey Antukh
3a27a5a542 Add minor naming change on process selected fn 2025-05-29 11:15:46 +02:00
Andrey Antukh
2a04f78337 Add common transducers section to common data ns 2025-05-29 11:14:53 +02:00
Aitor Moreno
aeee05c90d 🎉 Add comparison tool to WASM playground 2025-05-29 10:46:38 +02:00
Yamila Moreno
6fc63f14a0 Add configuration for air gapped installations (#6567) 2025-05-29 10:34:47 +02:00
Belén Albeza
f33c1fb530 Update binary fills flag name and add it to supported flags 2025-05-29 10:32:49 +02:00
Andrey Antukh
75170bb043 Merge pull request #6537 from penpot/niwinz-library-publish
 Add minor enhancements to penpot-library for publish it on npm
2025-05-29 09:49:51 +02:00
Belén Albeza
c0a98288d0 🔧 Gate cap with config flag 2025-05-29 09:45:31 +02:00
Belén Albeza
7d5739b663 Add playwright test for disabling adding fills 2025-05-29 09:45:31 +02:00
Elena Torró
fe60016124 Merge pull request #6573 from penpot/elenatorro-11021-text-fixes
🔧 Fix text parsing and transformation
2025-05-29 09:33:05 +02:00
Alejandro Alonso
5c58a04fc2 🐛 Fix inner strokes black background effect 2025-05-29 09:05:30 +02:00
Alejandro Alonso
04a1f8475d Merge pull request #6585 from penpot/alotor-scale-content
 Add scale content to render wasm
2025-05-29 07:32:47 +02:00
Belén Albeza
3c05f09fd1 🔧 Fix unnecessary playwright dependency in root dir (#6577) 2025-05-28 17:09:05 +02:00
María Valderrama
5eaea63ca8 🐛 Fix palette is over sidebar (#6581) 2025-05-28 17:08:23 +02:00
alonso.torres
bcfa9a82ea Add scale content to render wasm 2025-05-28 16:40:57 +02:00
Denys Kisil
d9649eaedd 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.7% (1803 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-05-28 16:01:45 +02:00
Belén Albeza
170d35dde2 Disable new fills in UI when the cap is reached 2025-05-28 14:01:26 +02:00
andrés gonzález
2943f80db5 📚 Change help links at the Help Center (#6582) 2025-05-28 13:22:42 +02:00
luisδμ
46b0e4f0e7 Manage empty property values in the combobox in design tab (#6574)
*  Manage empty property values in the combobox in design tab

* 📎 PR changes
2025-05-28 12:41:04 +02:00
Marina López
878952f7b5 Add subscription events (#6563)
*  Add subscription events

* 📎 Fixes PR feedback

* 📎 Fixes PR feedback
2025-05-28 10:50:36 +02:00
Marina López
f84ffc3562 Add history version days for subscriptions (#6571) 2025-05-28 10:49:53 +02:00
Aitor Moreno
e9edebbbb5 📚 Add tile rendering documentation (#6568) 2025-05-28 09:41:07 +02:00
Xavier Julian
14afd58eac 🐛 Display color swatch only on color type tokens 2025-05-28 09:29:26 +02:00
Belén Albeza
827d39a406 💄 Remove ununsed prop on fill-menu component 2025-05-27 15:30:16 +02:00
Belén Albeza
e4a1c373bb Only take N amount of fills 2025-05-27 15:30:11 +02:00
Pablo Alba
be13704934 🐛 Fix access to libs on migration during an import (#6572) 2025-05-27 14:54:17 +02:00
Elena Torro
88e77e3218 🔧 Fix text parsing and transformation 2025-05-27 14:04:27 +02:00
Pablo Alba
443cabe94e Add the ability to access libraries from file migrations 2025-05-27 13:12:34 +02:00
Alejandro Alonso
c7c8e91183 🐛 Fix keep aspect ratio support 2025-05-27 12:20:40 +02:00
Alejandro Alonso
327db5a1a3 🐛 Fix render of paths with empty selrects 2025-05-27 12:20:05 +02:00
Edgars Andersons
bcb74822d2 🌐 Add translations for: Latvian
Currently translated at 97.9% (1771 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-05-27 09:01:55 +00:00
Yaron Shahrabani
790d422100 🌐 Add translations for: Hebrew
Currently translated at 96.4% (1743 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-05-27 09:01:54 +00:00
laurentiucotet
419a949816 🌐 Add translations for: Romanian
Currently translated at 71.7% (1298 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2025-05-27 09:01:53 +00:00
Ingrid Pigueron
1ad6ee6e38 🌐 Add translations for: French
Currently translated at 97.4% (1761 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-05-27 09:01:51 +00:00
Andrey Antukh
da10425800 📚 Add readme for library directory 2025-05-27 10:55:49 +02:00
Andrey Antukh
3e4c80fa27 Prepare library to be published on npm 2025-05-27 10:55:49 +02:00
Marina López
179a5654e7 🐛 Fix get current user for plugins api 2025-05-27 10:50:01 +02:00
Marina López
bc38bd6a9c 🐛 Fix team name dropdown menu from dashboard (#6562) 2025-05-27 10:05:19 +02:00
Alejandro Alonso
1c5d182a90 Merge pull request #6559 from penpot/alotor-perf-fixes
🐛 Fix some problems with modifiers
2025-05-27 09:46:37 +02:00
alonso.torres
a85a42d367 🐛 Fix some problems with modifiers 2025-05-27 09:33:33 +02:00
Alejandro Alonso
1a705cee24 Merge pull request #6555 from penpot/niwinz-develop-fix-path-bug-1
🐛 Fix path node type change operation
2025-05-27 09:04:11 +02:00
Eva Marco
a771ca91ab 🐛 Add token units flag to common/flags file (#6557) 2025-05-26 13:53:56 +02:00
Andrey Antukh
4326e2c5a4 Merge remote-tracking branch 'origin/staging' into develop 2025-05-26 13:25:05 +02:00
Andrés Moya
050ffa235c ⬆️ Update cuerdas library (#6556) 2025-05-26 13:22:30 +02:00
Eva Marco
3dfccdaf9b ♻️ Put token settings under config flag (#6551) 2025-05-26 12:57:32 +02:00
Marina López
e5bc369e56 Visual indicators subscription for teams and project settings (#6546)
*  Visual indicators subscription for teams and project settings

* 📎 Fixes PR feedback

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-05-26 12:56:40 +02:00
Andrey Antukh
fdd6502671 📚 Update changelog 2025-05-26 12:41:34 +02:00
Andrey Antukh
e698fd7d35 🐛 Fix path node type change operation 2025-05-26 12:13:52 +02:00
Andrés Moya
5e8929e504 🔧 Refactor token json file import/export 2025-05-26 12:02:26 +02:00
Alejandro Alonso
3ee3ee2059 Merge pull request #6553 from penpot/alotor-bug-fix-grid-editor-problem
🐛 Fix problem with grid editing
2025-05-26 11:40:24 +02:00
alonso.torres
9eacde567d 🐛 Fix problem with grid editing 2025-05-26 11:20:09 +02:00
Alejandro Alonso
ac0b74e11a Merge pull request #6549 from penpot/niwinz-staging-hotfix-1
🐛 Fix incorrect relink operation for stroke image
2025-05-26 09:50:52 +02:00
Stephan Paternotte
24d4871b23 🌐 Add translations for: Dutch
Currently translated at 99.9% (1807 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-05-25 04:01:45 +00:00
Andrey Antukh
9638fd274f Merge pull request #6547 from penpot/eva-remove-deprecated-props
♻️ Update docs and remove deprecated props
2025-05-24 11:18:53 +02:00
Andrey Antukh
b5d96d312a 🐛 Fix incorrect relink operation for stroke image 2025-05-24 09:16:10 +02:00
Edgars Andersons
f88420efb5 🌐 Add translations for: Latvian
Currently translated at 96.7% (1750 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-05-23 20:01:44 +02:00
Nicola Bortoletto
c301d95f20 🌐 Add translations for: Italian
Currently translated at 99.7% (1804 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-05-23 20:01:44 +02:00
Eva Marco
7c072abe28 📚 Update docs without props obj 2025-05-23 12:57:24 +02:00
Eva Marco
603e41bbfd ♻️ Remove mf/props obj from DS components 2025-05-23 12:57:02 +02:00
Pablo Alba
b561ad033c 🐛 Fix restore component when its original parent with layout is deleted 2025-05-23 12:11:35 +02:00
luisδμ
7373056037 Improve formula validating and parsing (#6527) 2025-05-23 12:08:50 +02:00
luisδμ
a9173f672d 🐛 Sanitize inputs for variant property names and values (#6532) 2025-05-23 12:08:39 +02:00
luisδμ
44829ff1ae Use different copies for different variant selection cases (#6544)
*  Use different copies for different variant selection cases

* 📎 PR changes
2025-05-23 12:08:24 +02:00
Andrey Antukh
927ee9e55e Add get-owned-teams rpc method 2025-05-23 11:20:35 +02:00
Xavier Julian
066b252522 Add composition and slots documentation to storybook 2025-05-23 10:12:20 +02:00
luisδμ
556a68a78f Select all variants with errors (#6533) 2025-05-23 09:21:57 +02:00
Belén Albeza
f9bbf2d524 Improve paths deserialization (wasm) (#6501)
* ♻️ Refactor path wasm code to its own wasm submodule

* ♻️ Use unified enum for RawSegmentData and transmute to deserialize

* ♻️ Move set_shape_path_attrs to wasm::paths module

* 💄 Unify repr declarations
2025-05-23 08:48:55 +02:00
alonso.torres
eaaca5629e 🐛 Fix problem with sidebar layout 2025-05-22 18:35:08 +02:00
Andrey Antukh
0df2a12814 Merge remote-tracking branch 'origin/staging' into develop 2025-05-22 13:34:46 +02:00
Andrey Antukh
df27db1996 Merge pull request #6531 from mdbenito/fix/choppy-closest-point
 Fix choppy behaviour of new node on path
2025-05-22 13:24:04 +02:00
Miguel de Benito Delgado
7fc0d15418 🐛 Fix cursor overlap query (#6530)
* 🐛 Fix cursor overlap query. Closes #4472

* 📎 Update CHANGES.md

---------

Signed-off-by: Miguel de Benito Delgado <m.debenito.d@gmail.com>
2025-05-22 13:22:52 +02:00
Eva Marco
99fb905070 🐛 Fix at icon (#6540) 2025-05-22 13:15:09 +02:00
Stephan Paternotte
e74cf1836f 🌐 Add translations for: Dutch
Currently translated at 99.9% (1807 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-05-22 13:06:02 +02:00
Edgars Andersons
dfdc1ac35b 🌐 Add translations for: Latvian
Currently translated at 96.4% (1743 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-05-22 13:06:01 +02:00
Yaron Shahrabani
97a5a93694 🌐 Add translations for: Hebrew
Currently translated at 96.2% (1740 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-05-22 13:06:00 +02:00
Ingrid Pigueron
dd0b8f8f6e 🌐 Add translations for: French
Currently translated at 97.3% (1760 of 1808 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-05-22 13:05:58 +02:00
Alejandro Alonso
413fc6de16 Merge pull request #6536 from penpot/niwinz-update-promesa
⬆️ Update dependencies
2025-05-22 12:49:00 +02:00
Aitor Moreno
d54a7d0401 Merge pull request #6526 from penpot/superalex-improve-zoom-performance-and-behaviour
🐛 Fix zoom performance and behaviour
2025-05-22 12:15:38 +02:00
Alejandro Alonso
ed53793d9d 🐛 Fix render shapes in multiple tiles with high dprs (#6538) 2025-05-22 12:10:51 +02:00
María Valderrama
faa68784af 💄 Add styles for external widgets on workspace (#6509)
* 💄 Add styles for Inkeep Chat at workspace

* 📎 Styles review
2025-05-22 11:56:45 +02:00
Aitor Moreno
58b1cf6b0c Merge pull request #6491 from penpot/alotor-perf-pixel-precision
 Pixel precision for new renderer
2025-05-22 11:37:11 +02:00
Andrey Antukh
f9c9e865b5 🐛 Remove unexpected modified-at on binfile-v3 import 2025-05-22 10:53:23 +02:00
Andrey Antukh
ebe321d9d3 ⬆️ Update dependencies on exporter 2025-05-22 10:53:23 +02:00
Andrey Antukh
0683fbd17c ⬆️ Update backend dependencies 2025-05-22 10:53:23 +02:00
Andrey Antukh
09a7ef3e45 ⬆️ Upgrade frontend dependendencies 2025-05-22 10:53:23 +02:00
Andrey Antukh
172b70d8a7 ⬆️ Upgrade promesa library to latest version
That fixes a workaround introduced in previous commits
2025-05-22 10:53:23 +02:00
Alejandro Alonso
3597e5bb54 🐛 Fix zoom performance and behaviour 2025-05-22 10:29:43 +02:00
Eva Marco
949b6d1205 🎉 Add missing translation (#6534) 2025-05-22 10:24:41 +02:00
Eva Marco
c0af77faf7 📚 Add css rules to the UI guide (#6521)
* 📚 Add css rules to UI guide

* 🐛 Solve comments on PR

* 🐛 Add missing class

* 🐛 Improve css modules improvement

* 🐛 Fix width

* 🐛 Fix note
2025-05-22 10:06:03 +02:00
Miguel de Benito Delgado
8f7a674000 🔥 Remove unused fn types.path.segment.path-closest-point 2025-05-21 21:32:18 +02:00
Miguel de Benito Delgado
e4f2dfaa11 Make precision closest point computation depend on zoom 2025-05-21 21:29:40 +02:00
Andrey Antukh
ec29c4f5fe Merge pull request #6528 from penpot/ladybenko-11076-fix-xywh-inputs
🐛 Fix misalignment in measure section (design tab)
2025-05-21 21:05:38 +02:00
Elena Torró
c21f5221bb Merge pull request #6453 from penpot/elenatorro-10900-add-text-fills
🎉 Add text fills
2025-05-21 18:46:04 +02:00
Elena Torro
42ef2f929a 🎉 Add text fills 2025-05-21 18:32:50 +02:00
Belén Albeza
2b21401368 🐛 Fix clip buttons size 2025-05-21 17:08:56 +02:00
Belén Albeza
a5c8063b2c 🐛 Fix presets menu alignment 2025-05-21 17:01:23 +02:00
Belén Albeza
2ad2af2aea 🐛 Fix measures inputs' alignment 2025-05-21 16:58:49 +02:00
Eva Marco
c2ce7c6cf6 🐛 Remove unnecesary icon (#6524) 2025-05-21 15:44:25 +02:00
María Valderrama
47490db4be 💄 Add styles for external widgets on workspace (#6509)
* 💄 Add styles for Inkeep Chat at workspace

* 📎 Styles review
2025-05-21 14:17:48 +02:00
andrés gonzález
a2ac2bc6c6 Change copy as SVG menu order (#6523) 2025-05-21 13:12:33 +02:00
Elena Torró
e80ca7e332 Merge pull request #6439 from penpot/elenatorro-11035-fix-overflow-x-scroll-on-sidebar
🐛 Fix default scroll visibility on layers sidebar
2025-05-21 11:51:32 +02:00
Elena Torro
e4644ff506 🔧 Use scroll only on layers and refactor layer element name 2025-05-21 11:36:24 +02:00
Andrey Antukh
662b926b4b 🌐 Rehash all translations 2025-05-21 11:20:36 +02:00
Miguel de Benito Delgado
6319ed78f9 🌐 Add missing translation strings for error messages (#6519)
* 🌐 Add i18n strings for some error messages

* 🌐 Add fr, de, es translations for some error messages
2025-05-21 11:17:53 +02:00
Eva Marco
3abc8774f6 ♻️ Change translation string from workspace.token to workspace.tokens (#6508)
* ♻️ Change string translation for tokens

* ♻️ Apply find-and-replace on translation files

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-05-21 11:17:05 +02:00
Anonymous
af1c90c252 🌐 Add translations for: Swedish
Currently translated at 89.4% (1612 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-05-21 10:44:23 +02:00
Anonymous
8019ae7840 🌐 Add translations for: Dutch
Currently translated at 95.7% (1726 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-05-21 10:44:22 +02:00
Anonymous
6bd615ff8b 🌐 Add translations for: Latvian
Currently translated at 95.7% (1726 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-05-21 10:44:22 +02:00
Anonymous
c4a793d306 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 95.7% (1726 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-05-21 10:44:22 +02:00
Anonymous
631b3ac062 🌐 Add translations for: Croatian
Currently translated at 89.9% (1621 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-05-21 10:44:22 +02:00
Anonymous
48995850fa 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 88.4% (1594 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-05-21 10:44:21 +02:00
Anonymous
a5c7a2c97b 🌐 Add translations for: Czech
Currently translated at 89.8% (1620 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-05-21 10:44:20 +02:00
Anonymous
3a8285bc69 🌐 Add translations for: Italian
Currently translated at 95.6% (1724 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-05-21 10:44:20 +02:00
Anonymous
02e3cc089e 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 90.1% (1625 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-05-21 10:44:20 +02:00
Anonymous
17e19afcbd 🌐 Add translations for: Hebrew
Currently translated at 95.7% (1726 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-05-21 10:44:19 +02:00
Anonymous
a2b52a6408 🌐 Add translations for: Indonesian
Currently translated at 95.7% (1726 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-05-21 10:44:19 +02:00
Anonymous
8cc4b69291 🌐 Add translations for: German
Currently translated at 92.1% (1661 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-05-21 10:44:19 +02:00
Anonymous
045ddf5829 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 70.9% (1279 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-05-21 10:44:18 +02:00
Anonymous
1d0335aba6 🌐 Add translations for: French
Currently translated at 95.7% (1727 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-05-21 10:44:18 +02:00
Anonymous
5412d72236 🌐 Add translations for: Spanish
Currently translated at 98.8% (1782 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/es/
2025-05-21 10:44:17 +02:00
Anonymous
896ee43212 🌐 Add translations for: English
Currently translated at 99.8% (1800 of 1803 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/en/
2025-05-21 10:44:17 +02:00
alonso.torres
5d42b9793b 🐛 Fix some problems with layouts 2025-05-21 10:42:03 +02:00
alonso.torres
6cd2c712ab Pixel precision for new renderer 2025-05-21 10:42:03 +02:00
Elena Torro
a575410a29 🐛 Fix default scroll visibility on layers sidebar 2025-05-21 10:38:27 +02:00
Hosted Weblate
6b5703c1fe 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-05-21 10:38:18 +02:00
Andrey Antukh
22c3d4d807 Merge remote-tracking branch 'weblate/develop' into develop 2025-05-21 10:37:42 +02:00
luisδμ
b0701f6bb4 Control malformed variant formulas (#6473)
*  Control malformed variant strings

* 📎 PR changes

* 📎 PR changes
2025-05-21 10:18:11 +02:00
Alejandro Alonso
0748ef7267 Merge pull request #6518 from penpot/niwinz-staging-tokenslib-json-encoding
🐛 Add json encoding for tokenslib type
2025-05-21 10:17:37 +02:00
Pablo Alba
9bad9b8e91 🐛 Fix restore component when its original parent is deleted (#6517) 2025-05-21 10:05:22 +02:00
Andrey Antukh
9ca4fa752c 🐛 Add json encoding for tokenslib type 2025-05-21 09:59:28 +02:00
Andrey Antukh
b6563f620b 📎 Allow merge commits on commit linter 2025-05-21 09:34:05 +02:00
Andrey Antukh
a63fa2944d Merge remote-tracking branch 'origin/staging' into develop 2025-05-21 09:23:15 +02:00
Miguel de Benito Delgado
fd89c9d82c Avoid double id lookup when calling lookup-page-objects (#6513) 2025-05-20 22:31:40 +02:00
Andrey Antukh
a706907b26 ♻️ Normalize logical deletion to future dates
Instead of managing the ...
2025-05-20 22:25:57 +02:00
Andrey Antukh
a3b4fc9545 🔥 Remove unused function from workspace.media ns 2025-05-20 22:25:57 +02:00
Miguel de Benito Delgado
71bb2556f9 ♻️ Move page setup out of the data.workspace ns (#6502)
* ♻️ Split history workspace.cljs to workspace/pages.cljs - rename file to target-name

* ♻️ Split history workspace.cljs to workspace/pages.cljs - rename source-file to git-split-temp

* ♻️ Split history workspace.cljs to workspace/pages.cljs - restore name of source-file

* ♻️ Cleanup after adding new ns, add exports

* ♻️ Move set-plugin-data to main.data.plugins

* 🐛 Possible bugfix, cherry-picked from commit 8f7c63d6e2 (conflict during refactor)

---------

Co-authored-by: Eva Marco <eva.marco@kaleidos.net>
2025-05-20 22:11:05 +02:00
Miguel de Benito Delgado
f36aa30525 Add copy-as-svg to contextual menu (#6510)
*  Add "copy as svg" to contextual menu

* 🌐 Add a few translations of the new string

* 📚 Document commit message format for translations

* 📎 Log SVG import errors to the console

* 📎 Update CHANGES.md (two PRs)

---------

Signed-off-by: Miguel de Benito Delgado <m.debenito.d@gmail.com>
2025-05-20 22:06:36 +02:00
Eva Marco
8f7c63d6e2 Add base font fallback (#6468)
*  Add base font fallback

* ♻️ Add asserts to change-builder

* 🐛 Change fn name
2025-05-20 17:27:04 +02:00
Andrey Antukh
965b22718f 📚 Update changelog 2025-05-20 15:46:56 +02:00
Miguel de Benito Delgado
48a3d38d82 Add the Shift+ctrl+drag to deselect (#6494)
*  Allow shape deselection using Ctrl+Shift+Drag

*  Allow point deselection using Ctrl+Shift+Drag

*  Properly remember previous selection during addition/removal of shapes

*  Preload point selection in path handle-area-selection

Also: prefer dm/get-in over get-in

*  Highlight path nodes in selection rectangle incrementally
2025-05-20 15:23:05 +02:00
Florian Schroedl
31f642ed25 ♻️ Use rx streams for style dictionary interface 2025-05-20 14:55:07 +02:00
andrés gonzález
9f414b6ecd 📚 Update changelog (#6511) 2025-05-20 14:14:17 +02:00
Alejandro Alonso
334d7833d5 Merge pull request #6490 from penpot/azazeln28-refactor-iteration-performance
♻️ Refactor tile iteration
2025-05-20 13:56:37 +02:00
Alejandro Alonso
ff9c8f5929 Merge pull request #6483 from penpot/niwinz-staging-bugfixes-error-report
🐛 Several bugfixes
2025-05-20 13:54:54 +02:00
Xavier Julian
f7311cbb6b ♻️ Ensure tokens feature integrates design system 2025-05-20 13:52:38 +02:00
Alejandro Alonso
e4c563f917 Merge pull request #6479 from penpot/niwinz-develop-json-encoding-fix
🐛 Fix exception on rendering openapi.json
2025-05-20 13:46:16 +02:00
Andrey Antukh
2d3ad5a88f 📎 Update changelog 2025-05-20 13:30:04 +02:00
Andrey Antukh
1334d733cd 🐛 Fix openapi json generation for :re schemas 2025-05-20 13:29:44 +02:00
Andrey Antukh
004a9f17d3 Add minor js-like type schema formatting improvements 2025-05-20 13:29:44 +02:00
Andrey Antukh
c87fa4f723 Make the rpc doc generation lazy 2025-05-20 13:29:44 +02:00
Andrey Antukh
9378a5786f Replace json library used for generate openapi json 2025-05-20 13:29:44 +02:00
Andrey Antukh
3224ba26f1 ♻️ Replace :any schema with own ::sm/any
That a more specific, json friendly generator
2025-05-20 13:29:44 +02:00
Andrey Antukh
d33a5e6df1 Backport from develop partial improvements to sm/register! helper 2025-05-20 13:29:44 +02:00
Alejandro Alonso
0d60e3d997 Merge pull request #6486 from penpot/niwinz-library-export
 Add .penpot export support for penpot library
2025-05-20 13:27:11 +02:00
Andrey Antukh
645c4a57db Add playground file with example of how to create a component
This also fixes several internal issues related to component
creaton.
2025-05-20 13:06:07 +02:00
Andrey Antukh
778de6adaf Add minimal testing structure 2025-05-20 13:06:07 +02:00
Andrey Antukh
29d23577d2 🎉 Add .penpot (binfile-v3) support for library 2025-05-20 13:06:07 +02:00
Andrey Antukh
1fea1e8f5b 🔥 Remove unused svg parsing code from common 2025-05-20 13:06:07 +02:00
Andrey Antukh
c8a211742a 🔥 Remove already broken and unused internal components-v2 migration code 2025-05-20 13:06:06 +02:00
Andrey Antukh
2da8747485 ♻️ Move library to its own directory 2025-05-20 13:05:52 +02:00
Andrey Antukh
6803c78e80 Change naming and schema registation on tokens lib 2025-05-20 13:05:52 +02:00
Andrey Antukh
d8daea72de ⬆️ Update promesa library 2025-05-20 13:05:52 +02:00
Andrey Antukh
36b162b4fa ♻️ Replace jszip usage with zip.js library 2025-05-20 13:05:52 +02:00
Andrey Antukh
4c487834f0 Add the ability to deref internal state on library file instance 2025-05-20 13:05:52 +02:00
Andrey Antukh
dc7e53881a 🔥 Remove legacy-zip exportation support 2025-05-20 13:05:52 +02:00
Alejandro Alonso
1a01c9ee4a Merge pull request #6500 from penpot/niwinz-develop-svg-fixes
🐛 Fix svg path parsing on uploading svg image
2025-05-20 12:58:48 +02:00
Yamila Moreno
b6be416c7b 📎 Add wasm envvar to manage script 2025-05-20 12:15:14 +02:00
Florian Schroedl
4a27e8d1dd 🐛 Prevent unkown tokens hint always showing 2025-05-20 10:53:04 +02:00
Aitor Moreno
1bc97f9ad0 Merge pull request #6505 from penpot/supearlex-fix-avoid-unncesary-clone-for-new-render
🐛 Avoid unnecesary clone call
2025-05-20 10:07:23 +02:00
Aitor Moreno
aaa57cb17f 🐛 Fix inline styles in code tab (#6428) 2025-05-20 10:05:35 +02:00
Alejandro Alonso
b2d6342422 🐛 Avoid unnecesary clone call 2025-05-20 09:45:19 +02:00
Andrés Moya
ba1e16b55b 🐛 Fix token directory import 2025-05-20 09:42:38 +02:00
Aitor Moreno
ef95e3ecb0 ♻️ Refactor tile iteration 2025-05-19 16:24:52 +02:00
Eva Marco
55d21761fc Add multi file import on tokens (#6444)
*  Implement token multi-file import

* ♻️ Refactor import modal UI

* 🐛 Fix comments

---------

Co-authored-by: Florian Schroedl <flo.schroedl@gmail.com>
2025-05-19 16:12:46 +02:00
Andrey Antukh
0b4a367e9e 🐛 Fix svg path parsing on uploading svg image 2025-05-19 15:35:58 +02:00
Andrey Antukh
8f2ca15ec0 Merge pull request #6495 from mdbenito/refactor/frontend-app-main-data-workspace-clipboard
♻️ Factor clipboard code out of frontend/src/app/main/data/workspace.cljs
2025-05-19 15:20:52 +02:00
Aitor Moreno
21041eb925 Merge pull request #6496 from penpot/superalex-fix-path-performance
🐛 Fix paths performance in new render
2025-05-19 13:57:20 +02:00
Pablo Alba
53cfc29a1f Merge pull request #6425 from penpot/palba-variants-overrides-same-name
 Manage layers with the same name on variants overrides
2025-05-19 13:51:16 +02:00
Alejandro Alonso
96d44e0631 🐛 Fix paths performance in new render 2025-05-19 12:22:42 +02:00
Belén Albeza
8afd217a80 🔧 Enable back clippy rules (#6492)
* 🔧 Fix lint script (rust)

* 🔧 Temporarily add clippy rules to ignore so lint script passes

* 💄 Fix clippy rule crate_in_macro_def

* 💄 Fix clippy rule redundant-static-lifetimes

* 💄 Fix clippy rule unnecessary_cast

* 💄 Fix clippy rule nonminimal_bool

* 💄 Fix clippy rule redundant_pattern_matching

* 💄 Fix clippy rule assign_op_pattern

* 💄 Fix clippy rule needless_lifetimes

* 💄 Fix clippy rule for_kv_map

* 💄 Fix clippy rule ptr_arg

* 💄 Fix clippy rule match_like_matches_macro

* 💄 Fix clippy rule macro_metavars_in_unsafe

* 💄 Fix clippy rule map_clone

* 💄 Fix clippy rule wrong_self_convention

* 💄 Fix clippy rule vec_box

* 💄 Fix clippy rule useless_format

* 💄 Fix clippy rule unwrap_or_default

* 💄 Fix clippy rule unused_unit

* 💄 Fix clippy rule unnecessary_to_owned

* 💄 Fix clippy rule too_many_arguments

* 💄 Fix clippy rule slow_vector_initialization

* 💄 Fix clippy rule single_match

* 💄 Fix clippy rule redundant_field_names

* 💄 Fix clippy rule rendudant_closure

* 💄 Fix clippy rule needless_return

* 💄 Fix clippy rule needless_range_loop

* 💄 Fix clippy rule needless_borrows_for_generic_args

* 💄 Fix clippy rule needless-borrow

* 💄 Fix clippy rule missing_transmute_annotations

* 💄 Fix clippy rule map_entry

* 💄 Fix clippy rule manual_map

* 💄 Fix clippy rule len_zero

* 💄 Fix clippy rule from_over_into

* 💄 Fix clippy rule field_reassign_with_default

* 💄 Fix clippy rule enum_variant_names

* 💄 Fix clippy rule derivable_impls

* 💄 Fix clippy rule clone_on_copy

* 💄 Fix clippy rule box_collection

* 🔧 Make lint script also check test config target

* 🔧 Remove cargo-watch as a lib dependency

* 💄 Fix clippy rule for join_bounds

* 🔧 Fix lint script return code

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2025-05-19 11:14:55 +02:00
Miguel de Benito Delgado
330e49db56 ♻️ Cleanup after adding new ns, add exports 2025-05-18 18:49:59 +02:00
Miguel de Benito Delgado
aa39170d47 ♻️ Split history workspace.cljs to workspace/clipboard.cljs - restore name of source-file 2025-05-18 18:49:07 +02:00
Miguel de Benito Delgado
8fa7a69094 ♻️ Split history workspace.cljs to workspace/clipboard.cljs - resolve conflict and keep both files 2025-05-18 18:49:07 +02:00
Miguel de Benito Delgado
33d989feb2 ♻️ Split history workspace.cljs to workspace/clipboard.cljs - rename source-file to git-split-temp 2025-05-18 18:49:07 +02:00
Miguel de Benito Delgado
e309a57223 ♻️ Split history workspace.cljs to workspace/clipboard.cljs - rename file to target-name 2025-05-18 18:49:07 +02:00
Andrey Antukh
0b289153cb Add the ability to disable wasm on build script 2025-05-18 17:30:41 +02:00
Andrey Antukh
cf274099c4 Improve events/sse internal API
For make code cleaner and more evident for a quick view
2025-05-18 17:30:41 +02:00
Andrey Antukh
6524e75770 💄 Fix check-fn naming on types.container 2025-05-18 17:30:41 +02:00
Andrey Antukh
9b80f7c9b3 💄 Don't return unnecesary object from db query
the return value is already ignored
2025-05-18 17:30:41 +02:00
Andrey Antukh
bf76f328c8 Remove duplicate error logging on sse response 2025-05-18 17:30:41 +02:00
Xavier Julian
051c2a7e99 🐛 Fix sloppy behaviour on tokens value inputs 2025-05-16 15:42:25 +02:00
Xavier Julian
887fa6b77b Add slots feature to DS input component 2025-05-16 15:42:25 +02:00
Andrey Fedorov
d9f98008f4 Add unknown token type reporting 2025-05-16 15:09:36 +02:00
Alejandro Alonso
0cb6e0dee2 🐛 Fix new render zoom (#6488)
* 🐛 Fix new render zoom

* 🐛 Use scale instead of just zoom in get_tiles_for_viewbox

---------

Co-authored-by: Belén Albeza <belen@hey.com>
2025-05-16 10:49:03 +02:00
Andrey Antukh
ad87e9842d Merge pull request #6429 from penpot/yms-update-ubuntu-in-docker-images
🐳 Update docker images and dependencies
2025-05-16 10:38:56 +02:00
Miguel de Benito Delgado
e22a55334e 💄 Rename some namespace aliases for consistency (#6485) 2025-05-15 17:43:02 +02:00
Elena Torró
f5e81debbc Merge pull request #6478 from penpot/ladybenko-11030-fix-dpr-fills
🐛 Fix fills & strokes when dpr > 1
2025-05-15 16:04:30 +02:00
andrés gonzález
ddfd55261d :Books: Update design tokens doc (#6487) 2025-05-15 14:44:51 +02:00
Belén Albeza
300e24b403 🐛 Fix drawing shapes when dpr > 1 2025-05-15 11:01:14 +02:00
Andrey Antukh
a00e7c1061 Merge remote-tracking branch 'origin/staging' into develop 2025-05-15 09:52:31 +02:00
Alonso Torres
ba25ce3098 🐛 Fix share button being displayed with no permissions (#6476)
* 🐛 Fix share button being displayed with no permissions

*  Simplify impl by accessing perms from teams directly

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-05-15 09:49:29 +02:00
Miguel de Benito Delgado
968ea56197 ♻️ Reorganize index management on worker code (#6477)
* ♻️ Factor index management out of app.worker.impl

* 💄 Fix silly spacing

* 💄 Lint
2025-05-15 09:46:49 +02:00
Miguel de Benito Delgado
2635873b9a 📚 Update CONTRIBUTING.md with formatting and linting (#6480) 2025-05-15 09:41:33 +02:00
Alejandro Alonso
f5f1316f0b Merge pull request #6474 from penpot/superalex-develop-paste-html-fix
🐛 Fix exception on paste invalid html
2025-05-14 16:16:53 +02:00
Andrey Antukh
79a164be6d 🐛 Fix exception on paste invalid html 2025-05-14 16:07:01 +02:00
alonso.torres
ecb85778bc 🐛 Fix problem with path edition of shapes 2025-05-14 14:45:29 +02:00
Elena Torró
676c4d2dfe Merge pull request #6472 from penpot/alotor-perf-selrect-modifiers
 Set selrect for new render modifiers
2025-05-14 14:19:38 +02:00
Andrés Moya
5b8d1c1ca6 Merge branch 'hiru-update-tech-guide' 2025-05-14 13:23:38 +02:00
Andrés Moya
24e2948407 📚 Update code samples 2025-05-14 13:22:49 +02:00
Andrés Moya
c569c71306 📚 Update Tech Guide about abstraction levels 2025-05-14 13:22:38 +02:00
Andrés Moya
2cdc241e68 Merge branch 'hiru-update-tech-guide' into staging 2025-05-14 12:00:03 +02:00
Andrés Moya
057bf9bf25 📚 Update code samples 2025-05-14 11:38:55 +02:00
Andrés Moya
2ddcd0ce15 📚 Update Tech Guide about abstraction levels 2025-05-14 11:37:28 +02:00
alonso.torres
fef08dfa18 Set selrect for new render modifiers 2025-05-14 11:21:43 +02:00
Andrey Antukh
831422feaf ⬆️ Update several npm dependencies on frontend module 2025-05-14 10:39:34 +02:00
Andrey Antukh
d01e3085f4 ⬆️ Update yarn to 4.9.1 2025-05-14 10:39:34 +02:00
Andrey Antukh
d9ca82dc15 ⬆️ Update dependencies 2025-05-14 10:39:34 +02:00
Yamila Moreno
1e7127d98a 🐳 Update frontend image to nginx:1.28.0 2025-05-14 10:39:34 +02:00
Yamila Moreno
002ae8b91a 🐳 Update docker images to ubuntu 24.04 2025-05-14 10:39:34 +02:00
Aitor Moreno
6831acb71d Merge pull request #6465 from penpot/superalex-fix-render-wasm-maks
🐛 Fix new render masks
2025-05-14 10:33:52 +02:00
Alejandro Alonso
1f44d53f6b 🐛 Fix new render masks 2025-05-13 15:41:41 +02:00
Alonso Torres
ca2891d441 🐛 Fix problem syncing library colors and typographies (#6467) 2025-05-13 13:28:16 +02:00
Belén Albeza
91fbe8f8ef 🎉 Cap stop amount in UI for wasm (#6438)
* 🎉 Cap in the colorpicker the amount of stops a gradient can have

* 🎉 Cap the stops amount in gradient handlers

* 🎉 Disable add stop in gradient handlers (viewport + colorpicker)

*  Add integration test for gradient limits

* 💄 Address PR suggestion
2025-05-13 10:37:05 +02:00
Miguel de Benito Delgado
69cc4fb4c2 📚 Add missing command to open a repl on frontend process (#6458)
* 📚 Add missing command to open a repl on frontend process

* 📚 Add further information on starting a REPL on the frontend process
2025-05-13 08:10:52 +02:00
Elenzakaleidos
37abb7b237 💄 Update video in readme page (#6461)
Signed-off-by: Elenzakaleidos <elena.scilinguo@kaleidos.net>
2025-05-13 08:10:19 +02:00
Ramiro Andres Sanchez Balo
5fc2208c16 📚 Improve metadata descriptions (#6457) 2025-05-13 08:09:59 +02:00
Alejandro Alonso
c2b67d7c67 Merge pull request #6459 from penpot/superalex-fix-wasm-playground-fills-size
🐛 Fix wasm playground fills size
2025-05-13 06:23:48 +02:00
Pablo Alba
294ce7bb1b 🐛 Fix variants override for nested components (#6421) 2025-05-12 15:50:06 +02:00
Andrey Antukh
a558bfdb2f Merge remote-tracking branch 'origin/staging' into develop 2025-05-12 15:16:19 +02:00
Andrey Antukh
86bcd1b681 🐛 Fix issue on shortcuts restore operation (#6462)
* 🐛 Fix issue on shortcuts restore operation

Happens when the order of shortcuts pop events is inconsistent with
push events. Using less strictly order policy for pop operations
allows relax this and make it eventually consistent.

* 💄 Add cosmetic changes on shortcuts hooks on colorpicker and    wport

* 📎 Update changelog

* 📎 Add PR feedback changes
2025-05-12 15:08:14 +02:00
Elena Torró
33c260c35b Merge pull request #6456 from penpot/alotor-perf-text-grow-2
 Reflow flex on grow text height
2025-05-12 14:16:29 +02:00
Andrey Antukh
94312bb35c Merge remote-tracking branch 'origin/staging' into develop 2025-05-12 13:44:24 +02:00
Andrey Antukh
70b1989f10 Merge tag '2.7.0-RC2' 2025-05-12 13:11:40 +02:00
Alejandro Alonso
eb76d16b3b 🐛 Fix wasm playground fills size 2025-05-12 12:05:10 +02:00
Xavier Julian
c0eaa75232 💄 Fix errors UI on input token for value 2025-05-12 12:03:23 +02:00
Alejandro Alonso
dbb9971482 Merge pull request #6351 from penpot/niwinz-develop-improve-cleaner
 Add cleaner to file-gc
2025-05-12 11:52:01 +02:00
Alejandro Alonso
0828994840 Merge pull request #6419 from penpot/niwinz-refactor-library
♻️ Refactor penpot library
2025-05-12 11:47:00 +02:00
ºelhombretecla
e6b5618bd3 🎉 Add 2.7 release slides (#6440) 2025-05-12 11:38:00 +02:00
Aitor Moreno
9c24d3a521 Merge pull request #6370 from penpot/superalex-improve-zoom-in-zoom-out-performance-2
🎉 Improve zoom in/out performance
2025-05-12 11:22:57 +02:00
Alejandro Alonso
480e0887e3 🎉 Improve zoom in/out performance 2025-05-12 11:10:21 +02:00
Alejandro Alonso
e0e381bdfc Merge pull request #6451 from penpot/niwinz-develop-features-binfile-v1
🐛 Apply migrations in correct order for binfile-v1
2025-05-12 11:08:10 +02:00
Alejandro Alonso
5199b306aa Merge pull request #6446 from penpot/niwinz-staging-bugfixes-2
🐛 Properly propagate export errors from worker to main thread
2025-05-12 10:47:46 +02:00
Alejandro Alonso
8febfaa21e Merge pull request #6447 from penpot/niwinz-staging-bugfixes
 Ensure read-only mode on non-workspace access to file
2025-05-12 10:43:02 +02:00
Aitor Moreno
69062f03ee Merge pull request #6449 from penpot/superalex-add-shapes-buffer
🎉 Add shapes buffer to improve memory allocation
2025-05-12 10:23:34 +02:00
alonso.torres
eb04fa19e1 Reflow flex on grow text height 2025-05-12 09:48:57 +02:00
Alejandro Alonso
03b4fe3558 🎉 Add shapes buffer to improve memory allocation 2025-05-09 15:00:02 +02:00
Andrey Antukh
b349d08155 🐛 Apply migrations in correct order for binfile-v1
The patch was already existed but only applied to binfile-v3,
with this commit, the fix is properly applied to all binfile
formats and for duplicate file operation.
2025-05-09 13:38:13 +02:00
Elena Torró
15e9d92094 Merge pull request #6445 from penpot/elenatorro-11044-fix-parsing-text-spaces
🐛 Fix parsing text spaces
2025-05-09 12:31:17 +02:00
andrés gonzález
5e675dbf0b 📚 Update changelog for 2.7 (#6448) 2025-05-09 12:20:39 +02:00
Elena Torro
a5660819de 🐛 Fix stroke paragraphs 2025-05-09 11:54:51 +02:00
luisδμ
d277fefc87 Improve combobox component (#6424) 2025-05-09 11:33:57 +02:00
Elena Torro
1383010826 🔧 Remove log 2025-05-09 11:23:06 +02:00
Elena Torro
59982c9056 🐛 Fix parsing text spaces 2025-05-09 11:23:00 +02:00
Alejandro Alonso
afcff84e38 Merge pull request #6443 from penpot/niwinz-develop-feaures-bugfix
🐛 Fix incorrect features asignation after file migration
2025-05-09 11:17:27 +02:00
Andrey Antukh
fc5d9659d6 🐛 Properly propagate export errors from worker to main thread 2025-05-09 11:15:04 +02:00
Andrey Antukh
8fa7fa8c4b 🐛 Fix incorrect features asignation after file migration 2025-05-09 10:53:16 +02:00
Elena Torró
23bde76192 Merge pull request #6437 from penpot/elenatorro-add-fill-text-strokes
🎉 Add text stroke fills
2025-05-09 10:41:12 +02:00
BDVGitHub
ca7a80fb83 📚 Update framework version
Fix Svelte version number
2025-05-09 08:46:33 +02:00
BDVGitHub
cf0d9a433d 📚 Chore: Update create-a-plugin.md
Add Svelte and change version to the updated version of in the examples on https://github.com/penpot/plugin-examples
2025-05-09 08:46:33 +02:00
Andrey Antukh
bc20598b3d Don't persist file on several read operations
after applying migrations
2025-05-08 19:15:28 +02:00
Andrey Antukh
9de8ebb52c Add read-only option for files/get-file 2025-05-08 19:12:50 +02:00
alonso.torres
568af52ebc Text grow width/height 2025-05-08 17:59:18 +02:00
Elena Torro
eddabc0d68 🎉 Add text stroke fills 2025-05-08 15:49:58 +02:00
Pablo Alba
6b300d516b 🐛 Fix restore totally deleted variant should add props as name 2025-05-08 15:01:29 +02:00
Andrey Antukh
e271caa32b Merge remote-tracking branch 'origin/staging' into develop 2025-05-08 13:41:11 +02:00
Alonso Torres
9be569c54c 🐛 Fix problem when duplicating grid layout (#6426) 2025-05-08 13:39:47 +02:00
Alonso Torres
7e6a621484 🐛 Restore component fix order inside flex (#6432) 2025-05-08 13:37:59 +02:00
Eva Marco
66b47f9444 🐛 Fix resize bar background (#6435) 2025-05-08 13:37:00 +02:00
Andrey Antukh
694a2084e2 Add file cleaner to file-gc process 2025-05-08 13:35:25 +02:00
Andrey Antukh
fef19a3c80 Add legacy flex dir cleaner 2025-05-08 13:35:25 +02:00
Andrey Antukh
3da8b945ca 📎 Don't send unnecesary features to worker 2025-05-08 13:35:24 +02:00
Andrey Antukh
8f27b82edd Extend cleaner to fix invalid root shapes 2025-05-08 13:34:48 +02:00
Andrés Moya
8b529d308c Merge pull request #6338 from penpot/hiru-rework-abstraction-levels
📚 Update Tech Guide about abstraction levels
2025-05-08 13:32:23 +02:00
Alejandro Alonso
71aa8e5a86 Merge pull request #6434 from penpot/niwinz-staging-webhook-audit
 Add webhook trigger to the audit events
2025-05-08 13:00:04 +02:00
Andrey Antukh
ab01f0b274 Merge remote-tracking branch 'origin/staging' into develop 2025-05-08 12:22:50 +02:00
Andrey Antukh
e203536506 Add webhook trigger to audit events 2025-05-08 11:56:06 +02:00
Eva Marco
b71b9edee7 🐛 Tooltip positioning tunning (#6418) 2025-05-08 11:09:58 +02:00
Elena Torró
bd514c0594 🔧 Fix linting warnings and errors (#6431) 2025-05-08 11:07:36 +02:00
Xavier Julian
36e1ad287c 💄 Fix design review for input component 2025-05-08 10:55:07 +02:00
Florian Schrödl
92f5b5f92b Allow importing token files with reference errors (#6374)
*  Allow importing token files with reference errors

*  Add test for missing references
2025-05-08 10:11:02 +02:00
Andrey Antukh
0b7b6e2c23 ♻️ Refactor penpot library 2025-05-08 09:51:25 +02:00
Elena Torró
46709fb02e Merge pull request #6379 from penpot/ladybenko-10753-fills-serialization
🎉 Serialize as bytes all fill kinds
2025-05-07 18:03:42 +02:00
Elena Torró
61eb2f4a19 🎉 Add text solid strokes (#6384)
* 🎉 Add text strokes

* 🔧 Minor refactor
2025-05-07 17:28:36 +02:00
Belén Albeza
3fe16bd8f9 🐛 Fix history panel shortcut (#6420)
*  Remove duplicate ID in file menu

* 🐛 Fix shortcut for Show Version History

*  Add regression test
2025-05-07 16:49:54 +02:00
Alejandro Alonso
a9725a1aac Merge pull request #6422 from penpot/superalex-add-even-to-power-up-link-from-workspace-menu
🐛 Add event to power up link from workspace menu
2025-05-07 15:13:31 +02:00
Belén Albeza
8f9298fac8 ♻️ Remove redundant calls to add_shape_fill 2025-05-07 14:55:54 +02:00
Alejandro Alonso
c3e76817cd 🐛 Add event to power up link from workspace menu 2025-05-07 13:45:57 +02:00
Andrey Antukh
8bdec66927 Remove the ILazySchema internal abstraction from schema ns 2025-05-07 12:17:24 +02:00
Andrey Antukh
66ee9edaf8 Add minor enhacements and naming fixes on schemas 2025-05-07 12:17:24 +02:00
Andrey Antukh
ffd7bc883d ⬆️ Update shadow-cljs to 3.0.3 on common and frontend 2025-05-07 12:17:23 +02:00
Andrey Antukh
1bcfa4b8dc 🎉 Add facility to define custom js class 2025-05-07 12:17:23 +02:00
Andrey Antukh
99e325acaf 🔥 Remove support from legacy-zip format 2025-05-07 12:14:52 +02:00
Andrey Antukh
8badd1f2eb 💄 Add cosmetic improvements to common scripts/repl
Make it consistent with backend scripts/repl
2025-05-07 12:14:51 +02:00
alonso.torres
44bf276c49 🐛 Remove print 2025-05-07 12:13:47 +02:00
Eva Marco
0f3a4db71e ♻️ Refactor modal/hide! function calls (#6415) 2025-05-07 09:53:07 +02:00
Pablo Alba
751bed4117 Manage overrides on variants switch 2025-05-07 09:29:41 +02:00
Alejandro Alonso
ea095a98ba Merge pull request #6367 from penpot/azazeln28-refactor-flush-and-submit
♻️ Flush and submit
2025-05-07 07:03:22 +02:00
Eva Marco
348a9c82bf Merge pull request #6413 from penpot/eva-fix-tooltip-display-prop
🐛 Fix tooltip display on hide
2025-05-06 19:36:29 +02:00
Eva Marco
e2918f4148 🎉 Create tooltip DS component (#6340)
*  Add new tooltip DS component

* 🎉 Add delay

* 🎉 Update docs and stories

* 🎉 Add configurable delay

* ♻️ Fix comments

* ♻️ Fix comments
2025-05-06 17:15:22 +02:00
Aitor Moreno
c45187eedd Merge pull request #6381 from penpot/alotor-perf-modifiers-refactor
 Apply modifiers changes into data
2025-05-06 15:52:57 +02:00
Elena Torró
eeea5f2cc8 Merge pull request #6411 from penpot/alotor-perf-fix-text-editor-v2-error
🐛 Fix problem with editor v2
2025-05-06 15:03:15 +02:00
alonso.torres
05b6aeef3e 🐛 Fix problem with editor v2 2025-05-06 14:50:10 +02:00
Belén Albeza
6323031b40 📚 Add serialization docs for fills 2025-05-06 14:41:40 +02:00
Andrey Antukh
6ccb6cafaa Merge pull request #6263 from penpot/niwinz-develop-path-data-optimizations-1
 Performance optimizations to path related functions
2025-05-06 13:53:56 +02:00
Andrey Antukh
be26985ca5 Make the fdata/path-data feature no-team-inheritable
And also add helpers for revert it to plain format
2025-05-06 13:39:17 +02:00
Andrey Antukh
2aa2525d0e Add db conn dynamic binding for srepl helpers 2025-05-06 13:39:17 +02:00
Andrey Antukh
7cb2f307d8 Move path-editor from selection handlers 2025-05-06 13:39:17 +02:00
Andrey Antukh
f1a557c372 Add minor performance enhacements to viewport top-bar 2025-05-06 13:39:17 +02:00
Andrey Antukh
202337b135 💄 Add cosmetic improvements for start-editing-selected event fn 2025-05-06 13:39:16 +02:00
Andrey Antukh
4e3abcbd45 🐛 Prevent NPE on get-points 2025-05-06 13:39:16 +02:00
Andrey Antukh
122e5a4b57 🐛 Fix path content json decoding mechanism 2025-05-06 13:39:16 +02:00
Andrey Antukh
1981946480 🐛 Fix incorrect path content handling on converting from shape 2025-05-06 13:39:16 +02:00
Andrey Antukh
7d327d23a2 Make consistent use of .toString with path content 2025-05-06 13:39:16 +02:00
Andrey Antukh
500c27859b 🐛 Fix geom/point zero? predicate to work correctly with mixed numeric types
Using numeric indpendent equality check: `==`
2025-05-06 13:39:16 +02:00
Andrey Antukh
c6f68e6ed1 ♻️ Use LITTLE_ENDIAN instead of BIG_ENDIAND for path encoding 2025-05-06 13:39:15 +02:00
Andrey Antukh
b48faf8fe0 Simplify impl with sharing more code
and use macros for abstract platform differences
2025-05-06 13:39:15 +02:00
Andrey Antukh
fa24ced3a3 🐛 Don't render path editor on editing grid on frame 2025-05-06 13:39:15 +02:00
Andrey Antukh
b9ea2425b9 🔥 Remove legacy path formating code 2025-05-06 13:39:15 +02:00
Andrey Antukh
1abaff9c52 Add minor improvements to curve drawing internal impl 2025-05-06 13:39:15 +02:00
Andrey Antukh
6f2ccabaa2 Coerce PathData float values to double
For avoid equality issues on JVM
2025-05-06 13:39:14 +02:00
Andrey Antukh
1c77126fe6 Implement get-handlers in term of internal reduce
That has an average performance improvement of 64% over
original impl and reduction of generation of object garbage
2025-05-06 13:39:14 +02:00
Andrey Antukh
7196be2a23 🎉 Add support for internal reduce on PathData type 2025-05-06 13:39:14 +02:00
Andrey Antukh
d509b840dc 🔥 Remove unused get-commands fn 2025-05-06 13:39:14 +02:00
Andrey Antukh
61c23877c1 Rename handler->point to get-handler-point 2025-05-06 13:39:14 +02:00
Andrey Antukh
0e61398d67 Optimize handler->point path segment helper fn
More or les x2 speed improvement and reduced the generation
of objects garbage.
2025-05-06 13:39:13 +02:00
Andrey Antukh
f12656463d Add a helper for perform internal lookup on path content 2025-05-06 13:39:13 +02:00
Andrey Antukh
ba9fc37226 🔥 Remove unused fn content->points
Replaced by get-points
2025-05-06 13:39:13 +02:00
Andrey Antukh
60f754f172 Add minor improvements to get-segments-with-points
And rename it from `get-segments`
2025-05-06 13:39:13 +02:00
Andrey Antukh
3a22545158 Replace cmd name usage with segment name
For fix naming inconsistency
2025-05-06 13:39:13 +02:00
Andrey Antukh
1d0020f6e6 Replace duplicate fn get-point with segment->point 2025-05-06 13:39:13 +02:00
Andrey Antukh
f3c3f3e2d8 🔥 Remove legacy-parser1
Unused
2025-05-06 13:39:12 +02:00
Andrey Antukh
9ba0ae5532 Replace command->point with segment->point helper 2025-05-06 13:39:12 +02:00
Andrey Antukh
db73c2eea0 Fix segment param naming on path type helpers 2025-05-06 13:39:12 +02:00
Andrey Antukh
753823c0b3 Reorganize path toString impl 2025-05-06 13:39:12 +02:00
Andrey Antukh
44e8eacb8d Add the ability to provide initial value on path -walk 2025-05-06 13:39:12 +02:00
Andrey Antukh
33bcbd89f1 Optimize calculate-extremities path helper
Heavily used on path edition
2025-05-06 13:39:11 +02:00
Andrey Antukh
b0cbe3cec8 Replace content->points with faster get-points 2025-05-06 13:39:11 +02:00
Andrey Antukh
3ca76c9ef7 ♻️ Refactor path-editor component 2025-05-06 13:39:11 +02:00
Andrey Antukh
93199e1a70 ♻️ Refactor path editor component: path-snap 2025-05-06 13:39:11 +02:00
Andrey Antukh
93a601a1e7 ♻️ Refactor path editor component: path-preview 2025-05-06 13:39:11 +02:00
Andrey Antukh
3d864c4ff1 ♻️ Refactor path editor components: path-handler and path-point 2025-05-06 13:39:11 +02:00
Andrey Antukh
da2f519805 Add get-points helper, a faster alternative to content->points 2025-05-06 13:39:10 +02:00
Andrey Antukh
230e330eb2 Add cache and faster way to iterate over PathData 2025-05-06 13:39:10 +02:00
Andrey Antukh
4f6dffabb4 ♻️ Use new call convention for path drawing components 2025-05-06 13:39:10 +02:00
Andrey Antukh
09c3490cae Add naming improvement to bool content update fn 2025-05-06 13:39:10 +02:00
Andrey Antukh
1fc0203c38 🎉 Add full integration with path data type feature 2025-05-06 13:39:10 +02:00
Andrey Antukh
f545d7b3ea ♻️ Refactor bool shape creation and modification events 2025-05-06 13:39:09 +02:00
Andrey Antukh
b242eb5b32 🔥 Remove unused components-v2 binding on fdata creation 2025-05-06 13:39:09 +02:00
Andrey Antukh
be9e3fa355 Add better error reporting for test check tests 2025-05-06 13:39:09 +02:00
Andrey Antukh
fac93e4ff8 Add serialization support for PathData
For transit and fressian
2025-05-06 13:39:09 +02:00
Belén Albeza
8609db2182 ♻️ Remove unused deserialization code 2025-05-06 13:00:25 +02:00
Belén Albeza
ec73bd640c Use mem::transmute to deserialize raw fill data 2025-05-06 12:38:30 +02:00
Belén Albeza
cba65972dd Use same wasm function to add all types of fills 2025-05-06 12:33:14 +02:00
luisδμ
e62231cfed ♻️ Rename, move and refactor the input-with-values component (#6387)
* 💄 Adapt behaviour when hovering

* ♻️ Rename, refactor and move component

* 📎 PR changes
2025-05-06 11:19:18 +02:00
Aitor Moreno
3249fb43c3 Merge pull request #6378 from penpot/elenatorro-10914-fix-children-render-order
🐛 Render children in the correct order
2025-05-06 09:59:59 +02:00
Pablo Alba
ee0ba15f9e ♻️ Refactor update attrs
* Extract token update from update-attrs

* Split update-attrs in smaller functions for legibility and reusability
2025-05-05 18:14:04 +02:00
Belén Albeza
784aecd1a1 🎉 Add a DTO that handles all fill types 2025-05-05 16:55:00 +02:00
Belén Albeza
173d6c23b0 Serialize image fills in binary 2025-05-05 15:51:21 +02:00
Aitor Moreno
abc1241402 ♻️ Refactor flush and submit 2025-05-05 15:10:20 +02:00
Belén Albeza
f30441626e ♻️ Refactor fills DTOs into separate submodules 2025-05-05 12:33:40 +02:00
Belén Albeza
5ae125db94 Serialize stroke solid fills as bytes (wasm) 2025-05-05 12:33:40 +02:00
Belén Albeza
093fa18839 Serialize solid fills as bytes (wasm) 2025-05-05 12:33:40 +02:00
Belén Albeza
81f18ad7f4 ♻️ Normalize opacity in fills to u8 2025-05-05 12:33:40 +02:00
Belén Albeza
875e019d4f ♻️ Refactor raw gradient data into wasm module 2025-05-05 12:33:40 +02:00
Belén Albeza
8e18a0880e ♻️ Use a single byte to store gradient stop count (wasm) 2025-05-05 12:33:39 +02:00
alonso.torres
36b78e5e21 🐛 Fix restore component restore layout 2025-05-05 11:50:08 +02:00
María Valderrama
86a498fc29 Optimize profile setup flow for better user experience (#6223)
*  Optimize profile setup flow for better user experience

* 📎 Remove extra onboarding step

* 📎 Code review

* 📎 Update changelog

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-05-05 10:42:08 +02:00
Alejandro Alonso
aae81b8a04 🎉 Add wasm playground environment 2025-05-05 09:45:59 +02:00
Xaviju
486f036a11 ♻️ Redesign form input tokens (#6294)
* ♻️ Redesign form input tokens

* ♻️ Redesign form input tokens

---------

Co-authored-by: Xavier Julian <xaviju@proton.me>
2025-05-05 09:05:14 +02:00
Alonso Torres
a2c9d307df 🐛 Fix problem in viewer with the back button (#6385) 2025-05-05 08:54:05 +02:00
Andrés Moya
e52fd90963 💄 Add copyright header to all tokens source files (#6389) 2025-05-01 10:06:17 +02:00
Elena Torro
f8602810eb 🐛 Fix font id serialization 2025-04-30 11:48:07 +02:00
Pablo Alba
219ddfabaf Restore a deleted variant 2025-04-30 11:40:00 +02:00
alonso.torres
d8b3b216e9 🐛 Fix import modal style problem 2025-04-30 10:48:48 +02:00
alonso.torres
88e5209856 Apply modifiers changes into data 2025-04-30 09:34:13 +02:00
Andrey Antukh
c2b13a6d5d 📚 Update changelog 2025-04-29 14:46:15 +02:00
Elena Torro
9eefe13e8b 🐛 Render children in the correct order 2025-04-29 13:39:57 +02:00
Elena Torró
7eab6a2f1d Merge pull request #6372 from penpot/elenatorro-10892-load-emoji-font-lazily
 Load emoji font dynamically when initializing
2025-04-29 13:18:09 +02:00
Elena Torro
2306df5fb7 Load emoji font dynamically when initializing 2025-04-29 13:07:06 +02:00
Andrey Antukh
56ecacee21 Merge remote-tracking branch 'origin/staging' into develop 2025-04-29 11:30:16 +02:00
Andrey Antukh
a60b3d4b08 Merge pull request #6375 from penpot/hiru-move-tokens-staging
🔧 Reorganize tokens data functions
2025-04-29 10:43:13 +02:00
Andrés Moya
b14798b405 🔧 Make private function 2025-04-29 10:28:00 +02:00
Andrés Moya
8382a88efe 🔧 Move errors and warnings to workspace.data 2025-04-29 10:27:56 +02:00
Andrés Moya
53057c4bf2 🔧 Move token update to workspace.data and rename to propagation 2025-04-29 10:27:50 +02:00
Andrés Moya
3e0f38e8c3 🔧 Move token helpers to common.files 2025-04-29 10:27:46 +02:00
Andrés Moya
a5bbe765b9 🔧 Move style-dictionary and tinycolor to main.data 2025-04-29 10:27:42 +02:00
Andrés Moya
4455adc813 🔧 Move token lib edit to workspace.data and remove unused code 2025-04-29 10:27:38 +02:00
Andrés Moya
abca763aa5 🔧 Move token application to workspace.data 2025-04-29 10:27:20 +02:00
Andrés Moya
5c74349de0 🔧 Make private function 2025-04-29 10:11:40 +02:00
Andrés Moya
4a7b72dae1 🔧 Move errors and warnings to workspace.data 2025-04-29 10:11:40 +02:00
Andrés Moya
23e17d7f30 🔧 Move token update to workspace.data and rename to propagation 2025-04-29 10:11:40 +02:00
Andrés Moya
37cf829188 🔧 Move token helpers to common.files 2025-04-29 10:11:40 +02:00
Andrés Moya
f213ffabe1 🔧 Move style-dictionary and tinycolor to main.data 2025-04-29 10:11:40 +02:00
Andrés Moya
a1921bb767 🔧 Move token lib edit to workspace.data and remove unused code 2025-04-29 10:11:40 +02:00
Andrés Moya
213c04bc8a 🔧 Move token application to workspace.data 2025-04-29 10:11:40 +02:00
Pablo Alba
916eb530a0 Close swap panel after doing a swap 2025-04-28 11:23:42 +02:00
Andrey Antukh
1f0644ea91 Merge pull request #6314 from penpot/niwinz-subscriptions-internal-api
 Add prepl api for subscriptions
2025-04-28 10:34:29 +02:00
Andrey Antukh
b20147255a Add better approach for returning subscription on teams response 2025-04-28 10:23:02 +02:00
Andrey Antukh
38728eb342 Add the ability to known the subscription status on teams list 2025-04-28 10:23:02 +02:00
Andrey Antukh
18c7890f65 Add proper impl for retrieving num of editors 2025-04-28 10:23:02 +02:00
Andrey Antukh
1c224609b9 Add prototype for returning number of used slots on customer 2025-04-28 10:23:02 +02:00
Andrey Antukh
4b81468c9c Allow subscription to be nil 2025-04-28 10:23:02 +02:00
Andrey Antukh
cffac2a56a Change schema for subscription 2025-04-28 10:23:02 +02:00
Andrey Antukh
05c0f8d69f 🎉 Add update-customer-subscription prepl method 2025-04-28 10:23:02 +02:00
Andrey Antukh
5db5bc65de 🎉 Add get-customer-prfile prepl rpc method 2025-04-28 10:23:02 +02:00
Andrey Antukh
952ab032f9 🎉 Add authenticate prepl rpc method 2025-04-28 10:23:02 +02:00
Andrey Antukh
2df6f2b8b1 ♻️ Refactor prepl interface
Make prepl to be json message based protocol
instead of clojure expression. This facilitates
implementing internal RPC over socket server.
2025-04-28 10:23:02 +02:00
Andrey Antukh
62a12a64a3 Merge branch 'staging' into develop 2025-04-28 08:44:05 +02:00
Andrey Antukh
6935d54870 Merge branch 'main' into staging 2025-04-28 08:43:54 +02:00
Andrey Antukh
049427c6ca Merge branch 'main' into develop 2025-04-28 08:43:39 +02:00
Andrey Antukh
65e8526ee2 Merge tag '2.6.2-RC4' 2025-04-28 08:43:04 +02:00
alonso.torres
8ce71e792e Add dynamic properties modifiers to WASM 2025-04-25 15:03:24 +02:00
Andrés Moya
202762027f 🐛 Handle swapped nested instances when detaching 2025-04-25 10:00:14 +02:00
Andrés Moya
d95551e651 🔧 Add debug traces to detach copy operation 2025-04-25 10:00:14 +02:00
Elena Torro
44d68ad723 🐛 Calculate text-length in bytes correctly 2025-04-25 09:48:12 +02:00
Elena Torró
9e4c9d3101 Merge pull request #6353 from penpot/ladybenko-10867-comment-crash
🐛 Fix comment loading crash (new renderer viewport)
2025-04-25 08:41:51 +02:00
Andrey Antukh
050692952e Merge remote-tracking branch 'origin/staging' into develop 2025-04-24 15:11:12 +02:00
Xaviju
c96fbfdcd6 📚 Update tokens changelog for 2.6.2 (#6364)
Co-authored-by: Xavier Julian <xaviju@proton.me>
2025-04-24 13:29:56 +02:00
Belén Albeza
ab90d9d01c 🔧 Increase the cap for gradient stops 2025-04-24 12:20:34 +02:00
Belén Albeza
281c0068d9 Embed stop data into RawGradientData 2025-04-24 12:20:34 +02:00
Belén Albeza
e7b74939cb 💄 Change to more idiomatic code in main.rs functions 2025-04-24 12:20:34 +02:00
Elena Torró
c2ae58bf08 🎉 Add text shadows (#6335) 2025-04-24 12:19:41 +02:00
Elena Torró
14e8026e30 🐛 Fix take new lines into account when rendering text (#6337) 2025-04-24 12:16:21 +02:00
Xaviju
eb29a42209 📚 Update tokens changelog for 2.6.2 and 2.7 (#6363)
Co-authored-by: Xavier Julian <xaviju@proton.me>
2025-04-24 11:40:06 +02:00
Pablo Alba
6fdaad1bf4 🐛 Fix corner case when selecting component to switch (#6350) 2025-04-24 10:38:32 +02:00
Elena Torró
dfa6c502dc Improve left sidebar horizontal layers scroll (#6317) 2025-04-24 10:00:15 +02:00
Belén Albeza
b958dcb891 🐛 Fix positioning of comment thread draft (#6357)
* 🐛 Fix positioning of comment thread draft

* 📚 Update changelog
2025-04-24 09:53:07 +02:00
Alejandro Alonso
6e5d64d403 Merge pull request #6362 from penpot/niwinz-staging-bugfixes-2
🐛 Add migration for fix old broken root shapes (file migration)
2025-04-24 09:28:04 +02:00
Andrey Antukh
3e0c2bf1a1 🐛 Add migration for fix root shape 2025-04-24 09:17:33 +02:00
Andrey Antukh
9c4896d72b Merge remote-tracking branch 'origin/staging' into develop 2025-04-24 09:00:13 +02:00
Andrey Antukh
283cdee5d6 Ensure consistency on using d/update-vals on file migrations 2025-04-24 08:55:54 +02:00
Andrey Antukh
ab5e01e54a Ensure we don't leave :components with nil on file data
after aplying migrations
2025-04-24 08:53:30 +02:00
Alejandro Alonso
01fec1a0cf Merge pull request #6339 from penpot/azazeln28-refactor-rebuild-tiles
♻️ Refactor how rebuild_tiles works
2025-04-24 07:38:56 +02:00
Aitor Moreno
caf13eb774 ♻️ Refactor how rebuild_tiles works 2025-04-24 07:34:35 +02:00
Alejandro Alonso
373248e304 Merge pull request #6360 from penpot/niwinz-staging-bugfixes-2
🐛 Fix issues on file data migration handling
2025-04-24 07:30:50 +02:00
Marina López
fef342b489 🐛 Fixed team info settings alignment (#6354) 2025-04-23 22:59:07 +02:00
Pablo Alba
6e9adece1f 🐛 Fix problems cutting-paste a variant to another page or file (#6359) 2025-04-23 22:58:12 +02:00
Andrey Antukh
80308ceafa 🐛 Make http cache aware of missing file data migrations 2025-04-23 18:15:33 +02:00
Andrey Antukh
f65518f865 🐛 Fix incorrect migration application after binfile import 2025-04-23 18:10:52 +02:00
Andrés Moya
c0315e2c30 🔥 Remove redundant schemas (and add some tooling) 2025-04-23 12:27:01 +02:00
Andrés Moya
2f20ccf289 🔥 Remove unused functions 2025-04-23 12:27:01 +02:00
Belén Albeza
1a7d60bb88 🐛 Fix comment loading crash 2025-04-23 12:15:16 +02:00
Alejandro Alonso
90b1895f19 Merge pull request #6352 from penpot/alotor-update-scripts
🐛 Fix wasm scripts
2025-04-23 11:39:15 +02:00
alonso.torres
7945a95522 🐛 Fix wasm scripts 2025-04-23 11:21:24 +02:00
Andrey Antukh
40fe6369cb Merge remote-tracking branch 'origin/staging' into develop 2025-04-23 08:59:33 +02:00
Alejandro Alonso
55da3ee275 Merge pull request #6349 from penpot/azazeln28-fix-rendering-order
🐛 Fix rendering order
2025-04-23 07:07:27 +02:00
Aitor Moreno
38a708e12b 🐛 Fix rendering order 2025-04-22 18:49:57 +02:00
Alonso Torres
53dcd94f0d Add support for self mentions (#6341) 2025-04-22 18:49:10 +02:00
Pablo Alba
a3ccc3dfef Select correct component to switch when changing variant property (#6346) 2025-04-22 18:48:18 +02:00
luisδμ
77d8504baf Selection of several components of a variant at the same time 2025-04-22 15:56:38 +02:00
Aitor Moreno
484772e3b2 Merge pull request #6313 from penpot/superalex-improve-images-performance-wasm
🎉 Improve images performance
2025-04-22 11:36:24 +02:00
Alejandro Alonso
5c7a1fb407 🎉 Improve tile caching 2025-04-22 11:03:21 +02:00
Pablo Alba
064981ff3d Merge remote-tracking branch 'origin/staging' into develop 2025-04-22 10:44:29 +02:00
Pablo Alba
fe003d7496 Add removal of variant container when it becomes empty (#6311) 2025-04-22 09:22:18 +02:00
Pablo Alba
fae1df7f4b 🐛 Fix extract component variant from variant with path (#6303) 2025-04-22 09:18:31 +02:00
Florian Schrödl
0bff76e5f1 Don't override user provided color format (#6231) 2025-04-22 09:10:07 +02:00
Elena Torró
c7b062f483 Merge pull request #6278 from penpot/elenatorro-send-leaf-attrs-uint-arr
🔧 Parse text leaves all at once
2025-04-21 14:27:26 +02:00
Elena Torro
83f72f3e41 🔧 Parse text leaves all at once 2025-04-21 13:24:39 +02:00
Aitor Moreno
d8b71d76dd Merge pull request #6225 from penpot/elenatorro-10710-render-text-while-typing
🎉 Render text while typing
2025-04-21 12:33:08 +02:00
Aitor Moreno
a6d2f385af Merge pull request #6266 from penpot/superalex-fix-path-unions
🐛 Fix unions for new render
2025-04-21 11:23:14 +02:00
Pablo Alba
db9e397531 🐛 Fix variant from a shared lib doesnt't show props 2025-04-21 09:25:00 +02:00
Elena Torró
18dea6c3a3 Merge pull request #6298 from penpot/ladybenko-10752-gradient-stops
 Send fill + Stops in a single wasm call
2025-04-21 09:17:26 +02:00
Alejandro Alonso
8ebaecc472 🐛 Fix unions for new render 2025-04-21 08:10:15 +02:00
Unreal Vision
58e0b26493 🌐 Add translations for: French
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-04-17 15:01:43 +02:00
Corentin Noël
c75380e063 🌐 Add translations for: French
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-04-17 15:01:42 +02:00
Andrey Antukh
43a75b64b4 Merge pull request #6318 from penpot/ladybenko-10781-rust-devenv
🔧 Improve Rust dev env
2025-04-17 08:48:50 +02:00
Xaviju
3a741d1c14 🐛 Hide inactive warning if sets are empty (#6295)
* 🐛 Hide inactive warning if sets are empty

* 📎 Add simplified impl with proper comment

---------

Co-authored-by: Xavier Julian <xaviju@proton.me>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-04-16 17:17:13 +02:00
Pablo Alba
ba442e1549 Allow copy-paste variants into another file (#6319) 2025-04-16 17:12:12 +02:00
Andrés Moya
8c15296d07 Merge branch 'tokens-studio-hyma/99-improve-token-error-messages' into develop 2025-04-16 16:31:53 +02:00
Florian Schroedl
d26464c810 Catch invalid name errors in json 2025-04-16 16:30:47 +02:00
Belén Albeza
8ee202e5a1 🔧 Fix emscripten version in devenv 2025-04-16 15:30:29 +02:00
Belén Albeza
689cddfd0c 🔧 Use cargo env variable for target and allow to override it as well as SKIA_BINARIES_URL 2025-04-16 15:05:31 +02:00
Elena Torró
25950bb5a5 Merge pull request #6312 from penpot/elenatorro-fix-children-removal-regression
🐛 Fix last shape removal
2025-04-16 15:03:26 +02:00
Belén Albeza
1da623e63f 💄 Change naming and args order of write-gradient-fill! 2025-04-16 14:09:24 +02:00
Belén Albeza
4bf9e24d43 ♻️ Avoid using get-prop when unneeded in serializers 2025-04-16 14:09:24 +02:00
Belén Albeza
b41a7b8547 💄 Remove no longer used functions in wasm serialization 2025-04-16 14:09:23 +02:00
Belén Albeza
f500a00d04 ♻️ Extract wasm-functions for fills and strokes out of main.rs 2025-04-16 14:09:19 +02:00
Belén Albeza
64a2a08d24 ♻️ Refactor gradient parsing from bytes 2025-04-16 14:08:38 +02:00
Belén Albeza
1f58f96e88 ♻️ Refactor serializing gradient fill 2025-04-16 14:07:35 +02:00
Belén Albeza
dc3d802d3d 🎉 Serialize radial fills in one go 2025-04-16 14:07:35 +02:00
Belén Albeza
5765d1c56c ♻️ Switch to a f32 offset for gradient stops 2025-04-16 14:07:35 +02:00
Belén Albeza
abcd050c69 ♻️ Adapt linear gradient type so it can be used for radial too (wasm) 2025-04-16 14:07:33 +02:00
Belén Albeza
f40ef26c69 ♻️ Move color and fill serializers to their own sub-namespaces 2025-04-16 14:06:22 +02:00
Belén Albeza
fccd1a5bd7 Send fill + stops data in one call for linear fills 2025-04-16 14:05:39 +02:00
Alejandro Alonso
16012a3881 Merge remote-tracking branch 'origin/staging' into develop 2025-04-16 13:15:18 +02:00
Marina López
7ada3692cf Design improvements to the Invitations page with an empty state 2025-04-16 12:38:47 +02:00
Elena Torro
1ab5d5027f 🐛 Fix last shape removal 2025-04-16 12:37:29 +02:00
Alejandro Alonso
1f16816fe4 Merge pull request #6310 from penpot/alotor-perf-reparent-modifiers
 Reparent modifiers
2025-04-16 12:21:20 +02:00
alonso.torres
daf048e258 Reparent modifiers 2025-04-16 11:27:51 +02:00
Alejandro Alonso
f3d13005b2 🐛 Fix avoid uncacheable tiles (#6281) 2025-04-16 10:59:24 +02:00
Elena Torró
25a44e1387 Merge pull request #6306 from penpot/ladybenko-fix-raf-macros
🐛 Fix broken test build for rust wasm
2025-04-16 07:15:27 +02:00
Belén Albeza
9e9612cf1f 🐛 Fix broken test build for rust wasm 2025-04-15 16:58:49 +02:00
Aitor Moreno
304c44048f ♻️ Refactor how rAF/cAF is handled (#6241) 2025-04-15 15:45:28 +02:00
Alejandro Alonso
99e64ad387 Merge pull request #6274 from penpot/alotor-perf-grid-layout-modifiers-3
 Modifiers grid multi-span
2025-04-15 13:39:45 +02:00
Marina López
0f0c45af8e Propagate sharing a prototype to editors and viewers (#6297) 2025-04-15 12:50:01 +02:00
Marina López
f2977cf938 Visual indicators for unlimited tier users (#6270)
*  Visual indicators for unlimited tier users

* ♻️ Refactor to organize properly subscription

* ♻️ Refactor with PR feedback

* 💄 Add minor cosmetic changes

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2025-04-15 12:47:37 +02:00
Andrey Antukh
bb80da137d Merge remote-tracking branch 'origin/staging' into develop 2025-04-15 12:10:53 +02:00
Pablo Alba
f4b16a255c Copy values of same named properties moving a variant into another (#6288)
*  Copy values of same named properties moving a variant into another

*  Add MR changes
2025-04-15 12:06:58 +02:00
Marina López
ec8c30f060 Update board presets with newer devices (#6296) 2025-04-15 10:42:46 +02:00
Andrey Antukh
7990400c7a 🐛 Fix srepl helper for restore file snapshots (#6300) 2025-04-15 10:39:51 +02:00
Eva Marco
eee5cf5fb4 Add duplicate sets feature (#6240)
*  Add duplicate sets feature

*  Add test to each module

* 🎉 Fix comments

* 🎉 Remove duplicate from groups

* 🎉 Remove create theme from test

* 🎉 Remove ' from names
2025-04-14 16:22:40 +02:00
Xavier Julian
f5c699ab7a Improve copy on inactive set warning title 2025-04-14 10:30:48 +02:00
TheScientistPT
3d67c7930c 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 92.3% (1598 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-04-13 20:18:26 +02:00
TheScientistPT
b55ec38c35 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 92.2% (1596 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-04-11 23:02:00 +02:00
Stas Haas
02a1cfb457 🌐 Add translations for: German
Currently translated at 96.1% (1663 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-04-11 23:01:59 +02:00
Corentin Noël
b2ba38b5de 🌐 Add translations for: French
Currently translated at 98.7% (1708 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-04-11 23:01:57 +02:00
Pablo Alba
dcf1a593f7 Add playwright tests for cut-copy-paste variants 2025-04-11 15:17:33 +02:00
Pablo Alba
785b61be2f Fix corner cases of duplicate/copy-paste/cut-paste variants 2025-04-11 15:17:33 +02:00
Elena Torró
39c7782019 Merge pull request #6264 from penpot/elenatorro-10681-update-component-color-preview
🐛 Update the component thumbnail on component change
2025-04-11 14:33:07 +02:00
Alejandro Alonso
1c5c51907a Merge remote-tracking branch 'origin/staging' into develop 2025-04-11 14:05:00 +02:00
andrés gonzález
e004671346 📚 Update Recommended storage info (#6275) 2025-04-11 14:02:35 +02:00
alonso.torres
a59014cad0 Modifiers grid multi-span 2025-04-11 13:35:16 +02:00
Aitor Moreno
87650de9bc 🐛 Fix color gradient on text positioned incorrectly (#6253) 2025-04-11 11:36:31 +02:00
Florian Schrödl
ee5596067e ⬆️ Upgrade style_dictionary dependency to v5-rc1 (#6283) 2025-04-11 11:31:28 +02:00
Eva Marco
870fec6bbd 🐛 Fix modal hint as context notification (#6276) 2025-04-11 11:27:30 +02:00
Alejandro Alonso
686ab14b43 Merge pull request #6282 from penpot/alotor-fix-bool-wasm
🐛 Fix problem with booleans in wasm
2025-04-11 11:06:31 +02:00
alonso.torres
fced0cf3b1 🐛 Fix problem with booleans in wasm 2025-04-11 10:56:08 +02:00
Andrey Antukh
25dd53290c 🐛 Fix component thumbnail refresh on change
Happens when a component is nested in an other top level frame
2025-04-11 10:40:56 +02:00
Elena Torró
de8e27feb8 Merge pull request #6242 from penpot/ladybenko-10666-builtin-fonts
🎉 Load built-in font and its variants (wasm)
2025-04-11 08:48:31 +02:00
luisδμ
e7144142e5 Add, edit and delete variant properties from layer panel (#6257) 2025-04-10 21:15:23 +02:00
Xavier Julian
b6f2a434cf Display inactive set warning 2025-04-10 15:02:30 +02:00
Alonso Torres
caf558f8dd 🐛 Fix import error messages (#6265) 2025-04-10 14:46:50 +02:00
Florian Schrödl
6f2f1b7a76 ⬆️ Update style dictionary to 4.3.3 (#6260)
Co-authored-by: Andrey Fedorov <oran9e.red@gmail.com>
2025-04-10 14:42:24 +02:00
Elena Torró
744ef1958b Merge pull request #6269 from penpot/elenatorro-fix-render-wasm-tests
🔧 Return the test output
2025-04-10 14:19:02 +02:00
Elena Torro
08b44e1857 🔧 Return the test output 2025-04-10 14:10:05 +02:00
Eva Marco
580b60550c 🐛 Fix notification vertical alignment (#6272) 2025-04-10 13:53:20 +02:00
Elena Torró
20f695e8d7 Merge pull request #6271 from penpot/ladybenko-fix-test-run
🔧 Update test script to use the right architecture
2025-04-10 13:04:22 +02:00
Belén Albeza
1d7ff1f9e4 🔧 Fix requiring an emscripten macro in non-wasm architectures 2025-04-10 12:52:56 +02:00
Belén Albeza
5b18f1d76d 🔧 Update test script to use the right architecture 2025-04-10 12:50:22 +02:00
Aitor Moreno
d880307a9b 🎉 Add performance measuring functions (#6229) 2025-04-10 11:33:22 +02:00
Alejandro
97c24c8b9c 🐛 Fix merge path nodes with only one node selected (#6248) 2025-04-10 09:08:44 +02:00
Alejandro Alonso
43535ae573 Merge remote-tracking branch 'origin/staging' into develop 2025-04-10 08:40:29 +02:00
Alejandro
61643f676c Merge pull request #6249 from penpot/elenatorro-10750-fix-path-nil-svg-attrs
🔧 Do not try to set svg path attrs if none
2025-04-10 06:51:50 +02:00
Alejandro
9b8c8c4971 Merge pull request #6258 from penpot/elenatorro-10756-delete-last-shape
🐛 Remove children correctly
2025-04-10 06:49:41 +02:00
Alejandro
033ca0d56b 🐛 Fix error when an external user tries to export a shape on viewer (#6252) 2025-04-09 16:48:49 +02:00
Alonso Torres
28a6797595 🐛 Fix problem with error detail in toast (#6259) 2025-04-09 16:36:11 +02:00
Elena Torro
953db56a0d 🐛 Remove children correctly 2025-04-09 14:39:21 +02:00
Denys Kisil
68ce13368e 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-04-09 14:01:41 +02:00
Alejandro Alonso
82cf474863 Merge remote-tracking branch 'origin/staging' into develop 2025-04-09 13:52:54 +02:00
Andrey Antukh
edfcac3d5c 🐛 Restore uid assignation on devenv entrypoint 2025-04-09 13:11:12 +02:00
Alejandro Alonso
774e11c827 Merge remote-tracking branch 'origin/staging' into develop 2025-04-09 12:53:18 +02:00
Elena Torro
43f77376b6 🔧 Do not try to set svg path attrs if none 2025-04-09 10:13:39 +02:00
Belén Albeza
c0ba92f503 🎉 Add support for default font variants (wasm) 2025-04-08 16:45:27 +02:00
Belén Albeza
90cb0357c6 🎉 Swap default font for source sans (wasm) 2025-04-08 14:48:28 +02:00
Alejandro
d55e55ebcc Merge pull request #6237 from penpot/niwinz-upgrade-devenv
⬆️ Upgrade devenv (ubuntu, jvm, node)
2025-04-08 10:50:50 +02:00
Aitor Moreno
c2522329fd Merge pull request #6235 from penpot/alotor-perf-grid-layout-modifiers-2
 Grid layout modifiers
2025-04-08 09:47:42 +02:00
Alejandro
2470c1788e 🐛 Fix render wasm build (#6239) 2025-04-08 09:06:32 +02:00
Pablo Alba
230d259551 Merge pull request #6238 from penpot/luis-improve-input-with-values-doc
📚 Update documentation for input-with-values component
2025-04-07 23:31:40 +02:00
Denys Kisil
a55db1d52b 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-04-07 16:01:42 +00:00
Luis de Dios
cb533335c4 📚 Update documentation for input-with-values component 2025-04-07 17:18:21 +02:00
Andrés Moya
a8890e4b13 Merge branch 'tokens-studio-florian-70-sets-in-theme-create' into develop 2025-04-07 17:08:33 +02:00
Florian Schroedl
0281e0dba4 🚧 Explicit state handling 2025-04-07 16:42:43 +02:00
Florian Schroedl
1c209f49fc 🚧 Don't expose state 2025-04-07 16:36:02 +02:00
Andrey Antukh
a4701866a4 ⬆️ Upgrade devenv (ubuntu, jvm, node) 2025-04-07 16:26:04 +02:00
Florian Schroedl
12f72c8ca9 🚧 Show back button only when coming from a modal view 2025-04-07 16:04:18 +02:00
Florian Schroedl
c1165bd12d 🚧 Convert rumext 2025-04-07 16:04:18 +02:00
Florian Schroedl
215fb53c52 🚧 Use use-fn 2025-04-07 16:04:18 +02:00
Florian Schroedl
8df780b237 Allow editing of sets in create theme dialog
♻️ theme-state -> current-theme
2025-04-07 16:04:18 +02:00
Andrés Moya
79679cbb16 Merge branch 'tokens-studio-andrei/91-single-set-import-refactoring' into develop 2025-04-07 15:56:22 +02:00
Andrey Fedorov
fb2db4b918 ♻️ Refactor single set import functionality 2025-04-07 15:48:27 +02:00
Andrey Antukh
05b66f1dcf Merge remote-tracking branch 'origin/develop' into develop 2025-04-07 14:45:29 +02:00
Andrey Antukh
0f1b2003be Merge branch 'staging' into develop 2025-04-07 14:45:08 +02:00
Belén Albeza
6f91da9461 🔧 Fix Rust tests (#6208)
* 🔧 Fix test script (rust wasm)

* 🔧 Make code compile in test mode + using aarch64 as a target for tests
2025-04-07 14:08:41 +02:00
alonso.torres
63666fca48 Grid layout modifiers 2025-04-07 11:51:28 +02:00
Andrey Antukh
d279b6c232 📎 Fix linter issues 2025-04-07 11:40:19 +02:00
Andrey Antukh
17f7f920c4 Merge branch 'staging' into develop 2025-04-07 11:32:41 +02:00
Andrey Antukh
b55c86544b 📎 Fix linter issues 2025-04-07 10:52:07 +02:00
Pablo Alba
af1d5b86e1 Merge pull request #6214 from penpot/palba-cleanup-components-v2
♻️ Cleanup of componentsv2
 Add check to avoid open files with components v1
2025-04-07 10:18:53 +02:00
Alonso Torres
137e8d042f 🐛 Fix problem with boolean edges (#6218) 2025-04-07 09:19:43 +02:00
Rick Benetti
ee96c5599c 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 74.0% (1281 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-04-05 14:07:11 +00:00
Elena Torro
4ceaedcbe8 🎉 Render text while typing 2025-04-04 14:02:02 +02:00
Pablo Alba
f375cc9a82 Add check to avoid open files with components v1 2025-04-04 11:08:30 +02:00
Elena Torró
5937ed57ce 🎉 Update font and variant on change (#6220) 2025-04-04 10:17:39 +02:00
Aitor Moreno
2e3ed0c23f Merge pull request #6230 from penpot/azazeln28-fix-mem-bytes-alloc-32
🐛 Fix bytes-alloc-32
2025-04-04 09:55:34 +02:00
Aitor Moreno
5d1d2ef289 🐛 Fix bytes-alloc-32 2025-04-04 09:43:48 +02:00
Elena Torró
480c224250 🐛 Fix long file names (#6216)
* 🔧 Add truncate str macro

* 🐛 Use truncate instead of prune to prevent title override

* 💄 Use text-overflow ellipsis on file names

* 📎 Add #10662 to 2.7 bugfixes
2025-04-03 16:06:02 +02:00
Aitor Moreno
cd731c3ad2 ♻️ Refactor heap usage (#6204) 2025-04-03 16:04:51 +02:00
Andrey Antukh
9bc49e3381 Merge pull request #6209 from penpot/niwinz-features-enhancement
 Make feature resolved on team load
2025-04-03 15:43:12 +02:00
Pablo Alba
f961b75bba ♻️ Cleanup of componentsv2 2025-04-03 11:38:35 +02:00
Marina López
1e16fb8ca2 🐛 Fix horizontal scroll from design tab (#6213) 2025-04-02 20:00:22 +02:00
Elena Torró
c332528185 🐛 Fix render object with pending status (#6215) 2025-04-02 19:58:03 +02:00
Pablo Alba
387c5e67f3 🐛 Fix flick on create variant (#6217) 2025-04-02 19:54:48 +02:00
Elena Torró
2ed780e14d Merge pull request #6205 from penpot/elenatorro-10528-fix-google-font-variant-styles
🐛 Fix Google Fonts load by parsing italic variant ids correctly
2025-04-02 17:16:51 +02:00
Pablo Alba
1b8714fe7f 🐛 Fix position problems cutting-pasting a component 2025-04-02 12:10:18 +02:00
Andrey Antukh
e28f8cae74 Merge remote-tracking branch 'origin/staging' into develop 2025-04-02 10:34:31 +02:00
Andrey Antukh
87ef98dad5 Consolidate layout/grid feature 2025-04-01 21:15:38 +02:00
Andrey Antukh
e6e71e9278 Add minor enhacement for error reporting 2025-04-01 20:24:07 +02:00
Andrey Antukh
02220d02ed ⬆️ Update svgo 2025-04-01 20:01:21 +02:00
Andrey Antukh
ff7b77bda7 ⬆️ Update yarn 2025-04-01 20:01:21 +02:00
Andrey Antukh
f8ffae75c4 Make feature resolved on team load
That simplifies features retrieval to simple get
2025-04-01 20:01:21 +02:00
Andrey Antukh
cb350b26a1 Merge remote-tracking branch 'origin/develop' into develop 2025-04-01 18:27:02 +02:00
Andrey Antukh
dccebb0bea Merge remote-tracking branch 'origin/staging' into develop 2025-04-01 18:26:37 +02:00
Elena Torró
4cefbb52e1 Merge pull request #6206 from penpot/azazeln28-fix-not-enough-surfaces
🐛 Fix not enough surfaces
2025-04-01 15:47:59 +02:00
Elena Torro
d757009b48 🐛 Fix Google Fonts load by parsing italic variant ids correctly 2025-04-01 14:01:54 +02:00
Aitor Moreno
ca202711e1 🐛 Fix not enough surfaces 2025-04-01 13:41:33 +02:00
Pablo Alba
f04229d8cb 🎉 Allow duplicate/copy-paste/cut-paste variants 2025-04-01 11:07:22 +02:00
Andrey Antukh
076d64df8f Merge remote-tracking branch 'origin/staging' into develop 2025-04-01 11:02:32 +02:00
Elena Torró
3d7479f9aa 🐛 Fix stroke image rendering (#6189) 2025-04-01 09:55:19 +02:00
Elena Torró
76ffc2d268 🔧 Log error on process animation frame (#6182) 2025-04-01 09:01:49 +02:00
Elena Scilinguo
d0d118b31e 📚 Update README.md
Signed-off-by: Elena Scilinguo <elena.scilinguo@kaleidos.net >
2025-04-01 08:51:23 +02:00
Elena Torró
895b5b2ee1 🐛 Fix new line parsing (#6180) 2025-03-31 14:59:34 +02:00
Rick Benetti
21702c090d 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 73.9% (1280 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-03-31 11:06:36 +00:00
Elena Torró
9fd0e9af66 Merge pull request #6178 from penpot/ladybenko-10528-google-fonts
🎉 Render text that uses a google font (wasm)
2025-03-31 11:55:57 +02:00
luisδμ
648a8f9237 Allow modifying property name when a variation is selected (#6174)
*  Change property name when a variation is selected

* 📎 PR changes
2025-03-31 09:12:43 +02:00
Edgars Andersons
c4254106e8 🌐 Add translations for: Latvian
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-03-29 13:01:53 +01:00
Aitor Moreno
4e1ae1bc1a Merge pull request #6181 from penpot/superalex-tada-avoid-full-tiles-rebuild-on-set-modifiers
🎉 Avoid full tiles rebuild on set modifiers
2025-03-28 16:27:30 +01:00
Aitor Moreno
b6ac1dea4d 🐛 Fix TileSurfaceCache not deref surfaces 2025-03-28 16:14:37 +01:00
Belén Albeza
219d9af885 ♻️ Simplify font ids 2025-03-28 15:21:42 +01:00
Belén Albeza
c6bba54573 ♻️ Refactor font-related wasm code into a sub-namespace 2025-03-28 15:17:13 +01:00
Belén Albeza
f53cae0faa 🎉 Render text that uses a Google Font 2025-03-28 15:17:12 +01:00
Edgars Andersons
981336ed5e 🌐 Add translations for: Latvian
Currently translated at 98.4% (1704 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-03-28 11:01:58 +00:00
Linerly
3864ce6855 🌐 Add translations for: Indonesian
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-03-28 11:01:57 +00:00
Alejandro Alonso
6953a57333 🎉 Avoid full tiles rebuild on set modifiers 2025-03-28 11:55:54 +01:00
Alejandro
a109f11926 Merge pull request #6187 from penpot/azazeln28-fix-surface-pool-missing-deallocation
🐛 Fix SurfacePool missing deallocation
2025-03-28 11:55:45 +01:00
Alejandro
45c9904e05 Merge pull request #6155 from penpot/azazeln28-feat-cache-extra-tiles
🎉 Cache extra tiles
2025-03-28 11:55:31 +01:00
Aitor Moreno
08fc32cdc6 🎉 Cache extra tiles 2025-03-28 11:34:18 +01:00
Aitor Moreno
6c10f1e364 🐛 Fix SurfacePool missing deallocation 2025-03-28 11:34:04 +01:00
Alejandro
e8549ffb79 Merge pull request #6177 from penpot/azazeln28-feat-sort-viewport-tiles
🎉 Sort viewport tiles by distance to center
2025-03-28 11:32:08 +01:00
Aitor Moreno
8a8d89dfc0 🎉 Sort viewport tiles by distance to center 2025-03-28 11:22:10 +01:00
Pablo Alba
b6c4376217 🐛 Fix bugs from varaints design review 2025-03-28 11:20:10 +01:00
Alejandro
bd5e47f5fc Merge pull request #6126 from penpot/elenatorro-10516-fix-stroke-shadows
🐛 Fix stroke shadows
2025-03-28 09:54:39 +01:00
Alejandro
2aa756af38 Merge pull request #6175 from penpot/niwinz-develop-binfile-path-fix
🐛 Fix binfile-v3 importation related to bool shape normalization
2025-03-28 09:46:16 +01:00
Andrey Antukh
78c2840b22 Merge remote-tracking branch 'origin/staging' into develop 2025-03-28 09:45:34 +01:00
Aitor Moreno
af0a516a79 🐛 Fix path editing with wrong selrect (#6168) 2025-03-28 09:43:46 +01:00
Andrey Antukh
651beb4b9c 🐛 Fix binfile-v3 importation related to bool shape normalization 2025-03-28 08:57:47 +01:00
Elena Torro
f4d04a3dcb 🐛 Fix Stroke Shadows
- Move shadows surface responsibility
- Draw shadows directly into DropShadows and InnerShadows surfaces
- Draw stroke shadows directly into Strokes in order
- Clean up old shadow surfaces (Shadow & Overlay)
2025-03-28 08:56:37 +01:00
Elena Torró
d573da55b0 Merge pull request #6179 from penpot/elenatorro-10530-fix-paragraph
🐛 Revert offset change to fix paragraph rendering
2025-03-27 17:29:50 +01:00
Elena Torro
3c4be537d9 🐛 Revert offset change to fix paragraph rendering 2025-03-27 17:17:36 +01:00
Elena Torró
9800331505 🎉 Improve performance reducing unnecessary calls to set-objects
🎉 Improve performance reducing unnecessary calls to set-objects
2025-03-27 16:19:36 +01:00
Alejandro Alonso
7728d5b317 🎉 Improve performance reducing unnecessary calls to set-objects 2025-03-27 15:47:03 +01:00
Aitor Moreno
8f47ed8b0a Merge pull request #6136 from penpot/superalex-disable-svg-viewport-texts-for-wasm-render
🐛 Disable svg viewport texts for wasm render
2025-03-27 14:46:38 +01:00
Alejandro
c137e682dc Merge pull request #6172 from penpot/elenatorro-10530-fix-text-drag-and-drop
 Render text properly while dragging and resizing the text Shape
2025-03-27 14:18:11 +01:00
Andrey Antukh
14c639a425 🐛 Fix bool type shape normalize migration 2025-03-27 14:15:13 +01:00
Elena Torró
06bfb1ad26 🎉 Add PathData data type
🎉 Add PathData data type
2025-03-27 13:39:54 +01:00
Elena Torro
33c3611345 Render text properly while dragging and resizing the text Shape 2025-03-27 13:04:45 +01:00
Alejandro Alonso
e012046f62 Merge remote-tracking branch 'origin/staging' into develop 2025-03-27 12:12:57 +01:00
Andrey Antukh
ebf3730454 Normalize the content prop from bool type (#6162)
Make it the same as path shape, because they are essentially the
same data type
2025-03-27 11:15:48 +01:00
Edgars Andersons
ec0183ce94 🌐 Add translations for: Latvian
Currently translated at 97.6% (1690 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-03-27 11:01:53 +01:00
Aitor Moreno
3cf823ffb3 Merge pull request #6137 from penpot/superalex-rendering-wasm-performance
🎉 Avoid rendering too small shapes
2025-03-27 10:22:02 +01:00
Aitor Moreno
6231a9f931 Merge pull request #6130 from penpot/azazeln28-fix-tile-cache-exhaustion
🐛 Fix tile cache exhaustion
2025-03-27 09:51:16 +01:00
Elena Torró
dd30e939ae 📎 Add #9489 bug fix to CHANGELOG
📎 Add #9489 bug fix to CHANGELOG
2025-03-26 14:14:34 +01:00
Elena Torro
6f2e1d3794 📎 Add #9489 bug fix to CHANGELOG 2025-03-26 13:55:42 +01:00
Elena Torró
2e41bd7607 🐛 Fix linecap SVG path property (#6163) 2025-03-26 13:46:35 +01:00
Andrey Antukh
b9907ec401 🎉 Add PathData data type
That replaces the ad-hoc helpers for convert path content from
plain with encapsulated type that implements the appropriate
clojure protocols and interfaces that helps interacting with
binary encoded path data
2025-03-26 13:17:25 +01:00
Andrey Antukh
416e9e8e1d Merge remote-tracking branch 'origin/staging' into develop 2025-03-26 13:15:58 +01:00
Alonso Torres
83d41dba6f Serialization of grid layout data (#6148)
*  Add serializators for grid layout properties

*  Extract serializers for wasm api module
2025-03-26 12:10:31 +01:00
Pablo Alba
7284fb539f 🐛 Fix when editing a property name, the wrong property changes name (#6156) 2025-03-26 11:17:48 +01:00
luisδμ
f932f3efb1 Filter variant by name on layers panel 2025-03-25 21:50:29 +01:00
Aitor Moreno
60bc88a075 🐛 Fix tile cache exhaustion 2025-03-25 16:35:49 +01:00
Aitor Moreno
6eb686c06b ♻️ Refactor Custom Fonts allocations (#6146)
* ♻️ Refactor Custom Fonts allocations

* 💄 Remove commented code

---------

Co-authored-by: Belén Albeza <belen@hey.com>
2025-03-25 15:34:11 +01:00
Edgars Andersons
f587ed4ade 🌐 Add translations for: Latvian
Currently translated at 97.1% (1680 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-03-25 11:01:54 +00:00
Alejandro
065b50f5a2 🐛 Fix asynchronous content dependant rendering (#6142)
* 🐛 Fix custom fonts rendering

* 🐛 Fix asynchronous content dependant rendering

* 🎉 Renaming clear_cache to clear_drawing_cache
2025-03-25 09:49:47 +01:00
Alejandro Alonso
85b24e1e8d 🎉 Disable antialias for small shapes 2025-03-24 16:27:43 +01:00
Pablo Alba
9653e72e47 ♻️ Refactor variants
* ♻️ Refactor variants

*  Add MR changes
2025-03-24 13:15:02 +01:00
Alejandro Alonso
a80f114d66 🎉 Avoid rendering too small shapes 2025-03-24 11:49:38 +01:00
Alejandro Alonso
74ae4743d8 🐛 Disable svg viewport texts for wasm render 2025-03-24 11:06:14 +01:00
Andrey Antukh
d1d30e7eb5 Revert "📎 Add workaround for wasm production build failure"
This reverts commit f8a2291a55.
2025-03-24 10:48:22 +01:00
Andrey Antukh
e83be01475 Merge remote-tracking branch 'origin/staging' into develop 2025-03-24 10:42:14 +01:00
Aitor Moreno
22efd6574d Merge pull request #6125 from penpot/alotor-perf-flex-layout-2
 Improvements on flex layout positioning
2025-03-24 09:53:30 +01:00
Nicola Bortoletto
bb5a103944 🌐 Add translations for: Italian
Currently translated at 99.8% (1728 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-03-23 19:01:53 +00:00
Rick Benetti
34b3520fb2 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 70.9% (1228 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-03-23 19:01:52 +00:00
Stephan Paternotte
3217ba5a77 🌐 Add translations for: Dutch
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-03-22 18:01:53 +01:00
Nicola Bortoletto
a91caded9e 🌐 Add translations for: Italian
Currently translated at 96.4% (1669 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-03-22 18:01:52 +01:00
Stephan Paternotte
05ba1c3e64 🌐 Add translations for: Dutch
Currently translated at 99.0% (1714 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-03-21 15:02:01 +00:00
Edgars Andersons
77f025eb8d 🌐 Add translations for: Latvian
Currently translated at 96.0% (1662 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-03-21 15:02:00 +00:00
Yaron Shahrabani
aacec1809b 🌐 Add translations for: Hebrew
Currently translated at 100.0% (1730 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-03-21 15:01:59 +00:00
Linerly
0435f560a4 🌐 Add translations for: Indonesian
Currently translated at 95.4% (1652 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-03-21 15:01:58 +00:00
Stas Haas
766f034e5e 🌐 Add translations for: German
Currently translated at 94.1% (1628 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-03-21 15:01:56 +00:00
Ally Tiago
8502d9d21b 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 70.4% (1218 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-03-21 15:01:55 +00:00
Rick Benetti
6c874b2bb7 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 70.4% (1218 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-03-21 15:01:55 +00:00
Andrey Antukh
f8a2291a55 📎 Add workaround for wasm production build failure 2025-03-21 14:07:02 +01:00
Andrey Antukh
8c302e314f Merge remote-tracking branch 'origin/staging' into develop 2025-03-21 11:17:26 +01:00
alonso.torres
a830c27ceb Improvements on flex layout positioning 2025-03-21 10:18:34 +01:00
Alejandro
4c12af957c Merge pull request #6121 from penpot/superalex-tada-improve-tile-shapes-iteration-2
🎉 Improve tile shapes iteration
2025-03-21 07:30:52 +01:00
Alejandro Alonso
9ea3c54b92 🎉 Improve tile shapes iteration 2025-03-21 07:20:45 +01:00
Aitor Moreno
4620764111 Merge pull request #6127 from penpot/superalex-fix-wasm-state-warning
🐛 Fix wasm state warning
2025-03-20 18:47:49 +01:00
Alejandro Alonso
ca86137d0f 🐛 Fix wasm state warning 2025-03-20 18:18:50 +01:00
Alejandro Alonso
b299a732c0 Merge remote-tracking branch 'origin/staging' into develop 2025-03-20 18:08:06 +01:00
Alejandro
7a4c9d9933 Merge pull request #6122 from penpot/elenatorro-10516-fix-inner-shadow-rendering
🐛 Fix Fill Inner Shadow rendering
2025-03-20 15:46:15 +01:00
Elena Torro
91d15ea221 🐛 Fix Fill Inner Shadows 2025-03-20 15:20:15 +01:00
Andrey Antukh
b043fec0d5 🌐 Validate and rehash translation files 2025-03-20 14:46:46 +01:00
Andrey Antukh
0bab46eb5c Merge remote-tracking branch 'weblate/develop' into develop 2025-03-20 14:45:42 +01:00
Andrey Antukh
82bff09373 🐛 Fix incorrect syntax on spanish translation file 2025-03-20 14:45:20 +01:00
Andrey Antukh
329b2d30d0 Revert "🌐 Sync, validate and rehash translations"
This reverts commit 37a8bf7bfc.
2025-03-20 14:44:57 +01:00
Anonymous
0d65b652d4 🌐 Add translations for: Swedish
Currently translated at 93.1% (1612 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/sv/
2025-03-20 14:42:44 +01:00
Anonymous
9d3c19e86a 🌐 Add translations for: Yoruba
Currently translated at 69.1% (1196 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/yo/
2025-03-20 14:42:43 +01:00
Anonymous
56a7800519 🌐 Add translations for: Hausa
Currently translated at 72.9% (1262 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ha/
2025-03-20 14:42:43 +01:00
Anonymous
ba0cebd713 🌐 Add translations for: Dutch
Currently translated at 94.0% (1627 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-03-20 14:42:43 +01:00
Anonymous
e28628d148 🌐 Add translations for: Latvian
Currently translated at 94.0% (1627 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-03-20 14:42:42 +01:00
Anonymous
40bc860dc6 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 93.9% (1626 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-03-20 14:42:42 +01:00
Anonymous
decf32fdd5 🌐 Add translations for: Croatian
Currently translated at 93.8% (1623 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-03-20 14:42:42 +01:00
Anonymous
d9d6ee9922 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 88.9% (1539 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-03-20 14:42:42 +01:00
Anonymous
903609a38f 🌐 Add translations for: Czech
Currently translated at 93.6% (1621 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-03-20 14:42:41 +01:00
Anonymous
4504903b4c 🌐 Add translations for: Italian
Currently translated at 93.9% (1626 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-03-20 14:42:40 +01:00
Anonymous
4bf4972b6e 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 93.9% (1626 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-03-20 14:42:40 +01:00
Anonymous
47e4b41dd2 🌐 Add translations for: Hebrew
Currently translated at 94.0% (1627 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-03-20 14:42:39 +01:00
Anonymous
af413ff1c0 🌐 Add translations for: Indonesian
Currently translated at 94.0% (1627 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-03-20 14:42:39 +01:00
Anonymous
5fcf0808c6 🌐 Add translations for: German
Currently translated at 94.0% (1627 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-03-20 14:42:39 +01:00
Anonymous
fb956b3aa1 🌐 Add translations for: Chinese (Simplified Han script)
Currently translated at 79.3% (1373 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hans/
2025-03-20 14:42:38 +01:00
Anonymous
93986af181 🌐 Add translations for: Russian
Currently translated at 83.5% (1446 of 1730 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-03-20 14:42:38 +01:00
Andrey Antukh
37a8bf7bfc 🌐 Sync, validate and rehash translations 2025-03-20 14:41:50 +01:00
Elena Torro
e60e36a0e2 🔧 Refactor RenderState scale calculation 2025-03-20 14:36:45 +01:00
Hosted Weblate
199e182399 🌐 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2025-03-20 14:31:52 +01:00
Andrey Antukh
a9f4b29f32 Merge remote-tracking branch 'weblate/develop' into develop 2025-03-20 14:31:09 +01:00
Andrey Antukh
22cd43b8a2 Merge remote-tracking branch 'origin/staging' into develop 2025-03-20 14:27:37 +01:00
Alejandro
2d61644b05 Merge pull request #5914 from penpot/azazeln28-feat-tile-rendering
🎉 Tile rendering
2025-03-20 11:25:58 +01:00
Aitor Moreno
084816fb9f 🎉 Tile rendering system 2025-03-20 11:14:14 +01:00
william chen
b5ea90f740 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 100.0% (1646 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-03-20 10:02:52 +01:00
Andrey Antukh
e6839e4983 Merge pull request #6106 from penpot/palba--variants-inspect-header
 Add subtitle to inspect tab for components
2025-03-19 13:42:57 +01:00
Pablo Alba
82d3e466be Add subtitle to inspect tab for components 2025-03-19 10:34:19 +01:00
Alejandro
b0dacf6b11 Merge pull request #6105 from penpot/superalex-add-changelog-for-2.7.0
📎 Update changelog (add entry for 2.7.0)
2025-03-19 10:02:49 +01:00
Alejandro Alonso
64d090839d 📎 Update changelog (add entry for 2.7.0) 2025-03-19 09:51:22 +01:00
al0cam
12d3994f45 🌐 Add translations for: Croatian
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-03-18 10:01:54 +00:00
Tummas Jóhan Sigvardsen
e3b6b24c5f 🌐 Add translations for: Faroese
Currently translated at 10.0% (165 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fo/
2025-03-17 00:01:53 +01:00
Louis Chance
1eb7205c12 🌐 Add translations for: French
Currently translated at 85.3% (1405 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2025-03-17 00:01:52 +01:00
al0cam
92f4bdae03 🌐 Add translations for: Croatian
Currently translated at 87.7% (1445 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-03-14 22:02:57 +01:00
Vin
bd63a460eb 🌐 Add translations for: Russian
Currently translated at 88.0% (1450 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ru/
2025-03-14 22:02:56 +01:00
al0cam
02e975f594 🌐 Add translations for: Croatian
Currently translated at 77.8% (1282 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/hr/
2025-03-13 17:02:00 +00:00
Denys Kisil
fdeabc15ab 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-03-11 09:02:04 +01:00
Denys Kisil
8363cb7449 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-03-09 19:02:05 +01:00
Nicola Bortoletto
da43a4d3b4 🌐 Add translations for: Italian
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-03-03 16:04:07 +01:00
william chen
69d21e20c9 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 100.0% (1646 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-02-27 04:02:34 +00:00
william chen
a925d6710a 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 88.2% (1453 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-02-26 03:05:58 +00:00
Andy Li
f5218e207b 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 66.1% (1089 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-02-25 03:04:17 +01:00
william chen
7fb6a095c6 🌐 Add translations for: Chinese (Traditional Han script)
Currently translated at 66.1% (1089 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/zh_Hant/
2025-02-25 03:04:16 +01:00
Edgars Andersons
6a0bb2f452 🌐 Add translations for: Latvian
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-02-14 16:01:57 +01:00
Amerey.eu
d02f85315a 🌐 Add translations for: Czech
Currently translated at 99.3% (1635 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/cs/
2025-02-14 16:01:56 +01:00
Edgars Andersons
fbd5c404d2 🌐 Add translations for: Latvian
Currently translated at 98.1% (1616 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-02-13 11:02:06 +01:00
Nicola Bortoletto
32b65e8dbc 🌐 Add translations for: Italian
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-02-13 11:02:04 +01:00
Stas Haas
bf153eb96b 🌐 Add translations for: German
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2025-02-11 14:22:12 +01:00
TheScientistPT
f16fcf25e2 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 93.9% (1546 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-02-09 21:02:46 +01:00
Linerly
0048f9725d 🌐 Add translations for: Indonesian
Currently translated at 100.0% (1646 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-02-07 15:01:51 +01:00
Robson Cardoso dos Santos
4bb5592e75 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 72.7% (1198 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-02-07 15:01:51 +01:00
Renan Mayrinck
d7fbd3c3bc 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 72.7% (1198 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-02-06 14:41:03 +01:00
Robson Cardoso dos Santos
0b62bee90d 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 72.7% (1198 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-02-06 14:41:03 +01:00
Edgars Andersons
a4a88769af 🌐 Add translations for: Latvian
Currently translated at 97.5% (1605 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/lv/
2025-02-06 12:02:04 +01:00
Denys Kisil
2dce8d09b8 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-02-06 12:02:03 +01:00
TheScientistPT
e6e6401702 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 93.6% (1542 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-02-06 12:02:01 +01:00
Robson Cardoso dos Santos
e83ece392c 🌐 Add translations for: Portuguese (Brazil)
Currently translated at 70.7% (1164 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_BR/
2025-02-06 12:02:00 +01:00
Late Night Defender
bb161f9da8 🌐 Add translations for: Thai
Currently translated at 12.5% (206 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/th/
2025-02-04 22:02:01 +01:00
Denys Kisil
07360efd17 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-02-04 22:01:59 +01:00
TheScientistPT
7c8eaaa4f9 🌐 Add translations for: Portuguese (Portugal)
Currently translated at 93.3% (1537 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2025-02-04 22:01:58 +01:00
Denys Kisil
01e04843bf 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-02-02 14:01:52 +01:00
Linerly
3907f39c29 🌐 Add translations for: Indonesian
Currently translated at 98.9% (1628 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2025-02-01 02:01:53 +01:00
Stephan Paternotte
c570c0929f 🌐 Add translations for: Dutch
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2025-01-31 00:57:21 +01:00
Denys Kisil
f53473f9e9 🌐 Add translations for: Ukrainian (ukr_UA)
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ukr_UA/
2025-01-31 00:57:20 +01:00
Nicola Bortoletto
39e6d28826 🌐 Add translations for: Italian
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/it/
2025-01-31 00:57:18 +01:00
Yaron Shahrabani
0b56d07a67 🌐 Add translations for: Hebrew
Currently translated at 99.6% (1641 of 1646 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2025-01-31 00:57:17 +01:00
1004 changed files with 163970 additions and 107213 deletions

View File

@@ -1,5 +1,53 @@
version: 2.1
jobs:
lint:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
steps:
- checkout
- run:
name: "fmt check"
working_directory: "."
command: |
yarn install
yarn run fmt:clj:check
- run:
name: "lint clj common"
working_directory: "."
command: |
yarn run lint:clj:common
- run:
name: "lint clj frontend"
working_directory: "."
command: |
yarn run lint:clj:frontend
- run:
name: "lint clj backend"
working_directory: "."
command: |
yarn run lint:clj:backend
- run:
name: "lint clj exporter"
working_directory: "."
command: |
yarn run lint:clj:exporter
- run:
name: "lint clj library"
working_directory: "."
command: |
yarn run lint:clj:library
test-common:
docker:
- image: penpotapp/devenv:latest
@@ -17,15 +65,7 @@ jobs:
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "common/deps.edn"}}
- run:
name: "fmt check & linter"
working_directory: "./common"
command: |
yarn install
yarn run fmt:clj:check
yarn run lint:clj
- v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
- run:
name: "JVM tests"
@@ -37,12 +77,16 @@ jobs:
name: "NODE tests"
working_directory: "./common"
command: |
yarn install
yarn run test
- save_cache:
paths:
- ~/.m2
key: v1-dependencies-{{ checksum "common/deps.edn"}}
- ~/.yarn
- ~/.gitlibs
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
test-frontend:
docker:
@@ -61,36 +105,68 @@ jobs:
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: "prepopulate linter cache"
working_directory: "./common"
name: "install dependencies"
working_directory: "./frontend"
# We install playwright here because the dependent tasks
# uses the same cache as this task so we prepopulate it
command: |
yarn install
yarn run lint:clj
yarn run playwright install chromium
- run:
name: "fmt check & linter"
name: "lint scss on frontend"
working_directory: "./frontend"
command: |
yarn install
yarn run fmt:clj:check
yarn run fmt:js:check
yarn run lint:scss
yarn run lint:clj
- run:
name: "unit tests"
working_directory: "./frontend"
command: |
yarn install
yarn run test
- save_cache:
paths:
- ~/.m2
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}
- ~/.yarn
- ~/.gitlibs
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
test-library:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx6g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies and build
working_directory: "./library"
command: |
yarn install
- run:
name: Build and Test
working_directory: "./library"
command: |
./scripts/build
yarn run test
test-components:
docker:
@@ -109,14 +185,14 @@ jobs:
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies
working_directory: "./frontend"
command: |
yarn
npx playwright install --with-deps
yarn install
yarn run playwright install chromium
- run:
name: Build Storybook
@@ -148,7 +224,7 @@ jobs:
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: "integration tests"
@@ -158,7 +234,7 @@ jobs:
yarn run build:app:assets
yarn run build:app
yarn run build:app:libs
yarn run playwright install --with-deps chromium
yarn run playwright install chromium
yarn run test:e2e -x --workers=4
test-backend:
@@ -185,21 +261,6 @@ jobs:
keys:
- v1-dependencies-{{ checksum "backend/deps.edn" }}
- run:
name: "prepopulate linter cache"
working_directory: "./common"
command: |
yarn install
yarn run lint:clj
- run:
name: "fmt check & linter"
working_directory: "./backend"
command: |
yarn install
yarn run fmt:clj:check
yarn run lint:clj
- run:
name: "tests"
working_directory: "./backend"
@@ -215,37 +276,9 @@ jobs:
- save_cache:
paths:
- ~/.m2
- ~/.gitlibs
key: v1-dependencies-{{ checksum "backend/deps.edn" }}
test-exporter:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
- run:
name: "prepopulate linter cache"
working_directory: "./common"
command: |
yarn install
yarn run lint:clj
- run:
name: "fmt check & linter"
working_directory: "./exporter"
command: |
yarn install
yarn run fmt:clj:check
yarn run lint:clj
test-render-wasm:
docker:
- image: penpotapp/devenv:latest
@@ -278,10 +311,32 @@ jobs:
workflows:
penpot:
jobs:
- test-frontend
- test-components
- test-integration
- test-backend
- test-common
- test-exporter
- lint
- test-frontend:
requires:
- lint: success
- test-library:
requires:
- test-frontend: success
- lint: success
- test-components:
requires:
- test-frontend: success
- lint: success
- test-integration:
requires:
- test-frontend: success
- lint: success
- test-backend:
requires:
- lint: success
- test-common:
requires:
- lint: success
- test-render-wasm

129
.github/workflows/build-bundles.yml vendored Normal file
View File

@@ -0,0 +1,129 @@
name: Build and Upload Penpot Bundles non-prod
on:
# Create bundler for every tag
push:
tags:
- '**' # Pattern matched against refs/tags
# Create bundler every hour between 5:00 and 20:00 on working days
schedule:
- cron: '0 5-20 * * 1-5'
# Create bundler from manual action
workflow_dispatch:
inputs:
zip_mode:
# zip_mode defines how the build artifacts are packaged:
# - 'individual': creates one ZIP file per component (frontend, backend, exporter)
# - 'all': creates a single ZIP containing all components
# - null: for the rest of cases (non-manual events)
description: 'Bundle packaging mode'
required: false
default: 'individual'
type: choice
options:
- individual
- all
jobs:
build-bundles:
name: Build and Upload Penpot Bundles
runs-on: ubuntu-24.04
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract somer useful variables
id: vars
run: |
echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "gh_branch=${{ github.base_ref || github.ref_name }}" >> $GITHUB_OUTPUT
# Set up Docker Buildx for multi-arch build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Run manage.sh build-bundle from host
run: ./manage.sh build-bundle
- name: Prepare directories for zipping
run: |
mkdir zips
mv bundles penpot
- name: Create zip bundles for zip_mode == 'all'
if: ${{ github.event.inputs.zip_mode == 'all' }}
run: |
echo "📦 Packaging Penpot 'all' bundles..."
zip -r zips/penpot-all-bundles.zip penpot
- name: Create zip bundles for zip_mode != 'all'
if: ${{ github.event.inputs.zip_mode != 'all' }}
run: |
echo "📦 Packaging Penpot 'individual' bundles..."
zip -r zips/penpot-frontend.zip penpot/frontend
zip -r zips/penpot-backend.zip penpot/backend
zip -r zips/penpot-exporter.zip penpot/exporter
- name: Upload unified 'all' bundle
if: ${{ github.event.inputs.zip_mode == 'all' }}
uses: actions/upload-artifact@v4
with:
name: penpot-all-bundles
path: zips/penpot-all-bundles.zip
- name: Upload individual bundles
if: ${{ github.event.inputs.zip_mode != 'all' }}
uses: actions/upload-artifact@v4
with:
name: penpot-individual-bundles
path: |
zips/penpot-frontend.zip
zips/penpot-backend.zip
zips/penpot-exporter.zip
- name: Upload unified 'all' bundle to S3
if: ${{ github.event.inputs.zip_mode == 'all' }}
run: |
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.gh_branch}}.zip
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.commit_hash }}.zip
- name: Upload 'individual' bundles to S3
if: ${{ github.event.inputs.zip_mode != 'all' }}
run: |
for name in penpot-frontend penpot-backend penpot-exporter; do
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.gh_branch }}-latest.zip
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.commit_hash }}.zip
done
- name: Notify Mattermost about automatic bundles
if: github.event_name == 'pull_request'
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
TEXT: |
📦 *Penpot bundle automatically generated*
📄 PR: ${{ github.event.pull_request.title }}
🔁 From: \`${{ github.head_ref }}\` to \`{{ github.base_ref }}\`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Notify Mattermost about manual bundles
if: github.event_name == 'workflow_dispatch'
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
TEXT: |
📦 *Penpot bundle manually generated*
📄 Triggered from branch: `${{ github.ref_name}}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Print artifact summary URL
run: |
echo "📦 Artifacts available at:"
echo "🔗 https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check Commit Type
uses: gsactions/commit-message-checker@v2
with:
pattern: '^:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle):\s[A-Z].*[^.]$'
pattern: '^(Merge|:(lipstick|globe_with_meridians|wrench|books|arrow_up|arrow_down|zap|ambulance|construction|boom|fire|whale|bug|sparkles|paperclip|tada|recycle):)\s[A-Z].*[^.]$'
flags: 'gm'
error: 'Commit should match CONTRIBUTING.md guideline'
checkAllCommitMessages: 'true' # optional: this checks all commits associated with a pull request

3
.gitignore vendored
View File

@@ -30,6 +30,7 @@
/*.zip
/.clj-kondo/.cache
/_dump
/notes
/backend/*.md
/backend/*.sql
/backend/*.txt
@@ -68,6 +69,8 @@
/vendor/**/target
/vendor/svgclean/bundle*.js
/web
/library/target/
clj-profiler/
node_modules
/test-results/

View File

@@ -1,11 +1,178 @@
# CHANGELOG
## 2.6.2 (Unreleased)
## 2.9.0 (Unreleased)
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
### :sparkles: New features & Enhancements
- Add visual indicator for new comments in the workspace [Taiga #11328](https://tree.taiga.io/project/penpot/issue/11328)
- On components overrides, separate the content of the text from the rest of properties [Taiga #7434](https://tree.taiga.io/project/penpot/us/7434)
- Improve dashboard's sidebar [Taiga #10700](https://tree.taiga.io/project/penpot/us/10700)
- Change "Save color" button to primary button [Taiga #9410](https://tree.taiga.io/project/penpot/issue/9410)
- Support for exif rotated images [GitHub #6767](https://github.com/penpot/penpot/issues/6767)
- Display Blend Mode and Layer Opacity properties in the Inspect tab [Taiga #11283](https://tree.taiga.io/project/penpot/issue/11283)
- Add the option to import tokens in a .zip file. [Taiga #11378](https://tree.taiga.io/project/penpot/us/11378)
- New typography token type - font size token [Taiga #10938](https://tree.taiga.io/project/penpot/us/10938)
### :bug: Bugs fixed
- Copying font size does not copy the unit [Taiga #11143](https://tree.taiga.io/project/penpot/issue/11143)
- Fix text-decoration line-through that displays a wrong property value [Taiga #11145](https://tree.taiga.io/project/penpot/issue/11145)
- Fix display error message on register form [Taiga #11444](https://tree.taiga.io/project/penpot/issue/11444)
- Fix toggle focus mode did not restore viewport and selection upon exit [GitHub #6280](https://github.com/penpot/penpot/issues/6820)
- Fix problem when creating a layout from an existing layout [Taiga #11554](https://tree.taiga.io/project/penpot/issue/11554)
- Fix title button from Title Case to Capitalize [Taiga #11476](https://tree.taiga.io/project/penpot/issue/11476)
## 2.8.0
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
**Penpot Library**
The initial prototype is completly reworked for provide a more consistent API
and to have proper validation and params decoding. All the details can be found
on [its own changelog](library/CHANGES.md)
**Penpot migrate from Redis to Valkey**
As [Valkey](https://valkey.io/) is an opne-souce fork of [Redis](https://redis.io/)
version 7.2.4, this version of Penpot will be compatible with Redis but may diverge
in future versions. Therefore, **migration from Redis to ValKey is recommended for all
on-premises instances** that want to keep up to date.
### :heart: Community contributions (Thank you!)
- Add Serbian language [GitHub #5002](https://github.com/penpot/penpot/issues/5002) by [crnobog69](https://github.com/crnobog69)
### :sparkles: New features & Enhancements
- Optimize profile setup flow for better user experience [Taiga #10028](https://tree.taiga.io/project/penpot/us/10028)
- Rewrite path shape data PathData encoding [Taiga #8542](https://tree.taiga.io/project/penpot/us/8542?milestone=441308)
- Update base image for Docker Backend and Exporter to Ubuntu 24.04
- Update base image for Docker Frontend to Nginx 1.28.0
- Allow multi file token import [Github #27](https://github.com/tokens-studio/penpot/issues/27)
- Create `input*` wrapper component, and `label*`, `input-field*` and `hint-message*` components [Taiga #10713](https://tree.taiga.io/project/penpot/us/10713)
- Deselect layers (and path nodes) with Ctrl+Shift+Drag [Github #2509](https://github.com/penpot/penpot/issues/2509)
- Copy to SVG from contextual menu [Github #838](https://github.com/penpot/penpot/issues/838)
- Add styles for Inkeep Chat at workspace [Taiga #10708](https://tree.taiga.io/project/penpot/us/10708)
- Add configuration for air gapped installations with Docker
- Support system color scheme [Github #5030](https://github.com/penpot/penpot/issues/5030)
- Persist ruler visibility across files and reloads [GitHub #4586](https://github.com/penpot/penpot/issues/4586)
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
- Add tooltip component to DS [Taiga 9220](https://tree.taiga.io/project/penpot/us/9220)
- Allow multi file token export [Taiga #10144](https://tree.taiga.io/project/penpot/us/10144)
- Fix problem when double click on hidden shapes [Taiga #11314](https://tree.taiga.io/project/penpot/issue/11314)
### :bug: Bugs fixed
- Fix getCurrentUser for plugins api [Taiga #11057](https://tree.taiga.io/project/penpot/issue/11057)
- Fix spacing / sizes of different elements in the measurements section of the design tab [Taiga #11076](https://tree.taiga.io/project/penpot/issue/11076)
- Fix selection of short paths [Github #4472](https://github.com/penpot/penpot/issues/4472)
- Fix element positioning on the right side to adjust to grid [#11073](https://tree.taiga.io/project/penpot/issue/11073)
- Fix palette is over sidebar [#11160](https://tree.taiga.io/project/penpot/issue/11160)
- Fix font size input not displaying "mixed" when multiple texts are selected [Taiga #11177](https://tree.taiga.io/project/penpot/issue/11177)
- Misalignments at Create account [Taiga #11315](https://tree.taiga.io/project/penpot/issue/11315)
- Fix issue with importing files where flex/grid is used [Taiga #11334](https://tree.taiga.io/project/penpot/issue/11334)
- Fix wrong color in the export progress bar [Taiga #11299](https://tree.taiga.io/project/penpot/issue/11299)
- Fix right sidebar width overflow on long layer names [Taiga #11212](https://tree.taiga.io/project/penpot/issue/11212)
- Fix comment icon fill [Taiga #11388](https://tree.taiga.io/project/penpot/issue/11388)
- Fix gap on radio-buttons component [Taiga #11360](https://tree.taiga.io/project/penpot/issue/11360)
- Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
- Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337](https://tree.taiga.io/project/penpot/issue/11337)
- Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061)
- Fix entering long project name [Taiga #11417](https://tree.taiga.io/project/penpot/issue/11417)
- Fix slow color picker [Taiga #11019](https://tree.taiga.io/project/penpot/issue/11019)
- Fix tooltip position after click [Taiga #11405](https://tree.taiga.io/project/penpot/issue/11405)
- Fix incorrect media translation on paste text with fill images [Github #6845](https://github.com/penpot/penpot/pull/6845)
## 2.7.2
### :bug: Bugs fixed
- Update plugins runtime [Github #6604](https://github.com/penpot/penpot/pull/6604)
- Backport from develop a minor fix that enables import of files
generated by penpot library [Github #6614](https://github.com/penpot/penpot/pull/6614)
- Fix copy in error message [GitHub #6615](https://github.com/penpot/penpot/pull/6615)
- Fix url on invitation link [Taiga #11284](https://tree.taiga.io/project/penpot/issue/11284)
## 2.7.1
### :bug: Bugs fixed
- Fix incorrect handling of strokes with images on importing files
- Fix tokens disappearing after manual additions [Taiga #11063](https://tree.taiga.io/project/penpot/issue/11063)
## 2.7.0
### :rocket: Epics and highlights
### :boom: Breaking changes & Deprecations
### :heart: Community contributions (Thank you!)
- Design improvements to the Invitations page with an empty state [GitHub #2608](https://github.com/penpot/penpot/issues/2608) by [@iprithvitharun](https://github.com/iprithvitharun)
### :sparkles: New features
- Update board presets with a newer devices [Taiga #10610](https://tree.taiga.io/project/penpot/us/10610)
- Propagate "sharing a prototype" to editors and viewers [Taiga #8853](https://tree.taiga.io/project/penpot/us/8853)
- Design improvements to the Invitations page with an empty state [Taiga #4554](https://tree.taiga.io/project/penpot/us/4554)
- Duplicate token sets [Taiga #10694](https://tree.taiga.io/project/penpot/issue/10694)
- Add set selection in create Token themes flow [Taiga #10746](https://tree.taiga.io/project/penpot/issue/10746)
- Display indicator on not active sets [Taiga #10668](https://tree.taiga.io/project/penpot/issue/10668)
### :bug: Bugs fixed
- Fix "at" icon to match all icons on app [Taiga #11136](https://tree.taiga.io/project/penpot/issue/11136)
- Fix problem in viewer with the back button [Taiga #10907](https://tree.taiga.io/project/penpot/issue/10907)
- Fix resize bar background on tokens panel [Taiga #10811](https://tree.taiga.io/project/penpot/issue/10811)
- Fix shortcut for history version panel [Taiga #11006](https://tree.taiga.io/project/penpot/issue/11006)
- Fix positioning of comment drafts when near the right / bottom edges of viewport [Taiga #10534](https://tree.taiga.io/project/penpot/issue/10534)
- Fix path having a wrong selrect [Taiga #10257](https://tree.taiga.io/project/penpot/issue/10257)
- Fix SVG `stroke-linecap` property when importing SVGs [Taiga #9489](https://tree.taiga.io/project/penpot/issue/9489)
- Fix position problems cutting-pasting a component [Taiga #10677](https://tree.taiga.io/project/penpot/issue/10677)
- Fix design tab has a horizontal scroll [Taiga #10660](https://tree.taiga.io/project/penpot/issue/10660)
- Fix long file names being clipped when longer than allowed length [Taiga #10662](https://tree.taiga.io/project/penpot/issue/10662)
- Fix problem with error detail in toast [Taiga #10519](https://tree.taiga.io/project/penpot/issue/10519)
- Fix view mode error when an external user tries to export something from a prototype using a shared link [Taiga #10251](https://tree.taiga.io/project/penpot/issue/10251)
- Fix merge path nodes with only one node selected [Taiga #9626](https://tree.taiga.io/project/penpot/issue/9626)
- Fix problem with import errors [Taiga #10040](https://tree.taiga.io/project/penpot/issue/10040)
- Fix color gradient on texts [Taiga Issue #7488](https://tree.taiga.io/project/penpot/issue/7488)
- Add support for self mentions [Taiga #10809](https://tree.taiga.io/project/penpot/issue/10809)
- Fix team info settings alignment [Taiga #10869](https://tree.taiga.io/project/penpot/issue/10869)
- Fix left sidebar horizontal scroll on nested layers [Taiga #10791](https://tree.taiga.io/project/penpot/issue/10791)
- Improve error message details importing tokens [Taiga Issue #10772](https://tree.taiga.io/project/penpot/issue/10772)
- Fix no selected set after Drag & Drop [Github #71](https://github.com/tokens-studio/penpot/issues/71)
- Styledictionary v5 Update [Github #6283](https://github.com/penpot/penpot/pull/6283)
- Fix Rename a set throws an internal error [Github #78](https://github.com/tokens-studio/penpot/issues/78)
- Fix Out of Sync Token Value & Color Picker [Github #102](https://github.com/tokens-studio/penpot/issues/102)
- Fix Color should preserve color space [Github #69](https://github.com/tokens-studio/penpot/issues/69)
- Fix cannot rename Design Token Sets when group of same name exists [Taiga Issue #10773](https://tree.taiga.io/project/penpot/issue/10773)
- Fix problem when duplicating grid layout [Github #6391](https://github.com/penpot/penpot/issues/6391)
- Fix issue that makes workspace shortcuts stop working [Taiga #11062](https://tree.taiga.io/project/penpot/issue/11062)
- Fix problem while syncing library colors and typographies [Taiga #11068](https://tree.taiga.io/project/penpot/issue/11068)
- Fix problem with path edition of shapes [Taiga #9496](https://tree.taiga.io/project/penpot/issue/9496)
- Fix exception on paste invalid html [Taiga #11047](https://tree.taiga.io/project/penpot/issue/11047)
- Fix share button being displayed with no permissions [Taiga #11086](https://tree.taiga.io/project/penpot/issue/11086)
- Fix inline styles in code tab [Taiga Issue #7583](https://tree.taiga.io/project/penpot/issue/7583)
- Fix exception on returning openapi.json
- Fix json encoding of TokensLib [Taiga #10994](https://tree.taiga.io/project/penpot/issue/10994)
## 2.6.2
### :bug: Bugs fixed
- Increase the height of the right sidebar dropdowns [Taiga #10615](https://tree.taiga.io/project/penpot/issue/10615)
- Fix scroll on token themes modal [Taiga #10745](https://tree.taiga.io/project/penpot/issue/10745)
- Fix collapsing grouped sets in "edit Theme" closes the dialog [Taiga #10771](https://tree.taiga.io/project/penpot/issue/10771)
- Fix unexpected exception on path editor on merge segments when undo stack is empty
- Fix pricing CTA to be under a config flag [Taiga #10808](https://tree.taiga.io/project/penpot/issue/10808)
- Fix allow moving a main component into another [Taiga #10818](https://tree.taiga.io/project/penpot/issue/10818)
@@ -14,6 +181,7 @@
- Fix incorrect uuid parsing from different parts of code
- Fix update layout on component restore [Taiga #10637](https://tree.taiga.io/project/penpot/issue/10637)
- Fix horizontal scroll in viewer [Github #6290](https://github.com/penpot/penpot/issues/6290)
- Fix detach component in a particular case [Taiga #10837](https://tree.taiga.io/project/penpot/issue/10837)
## 2.6.1

3
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,3 @@
# Penpot's Code of Conduct
Check it at: https://help.penpot.app/contributing-guide/coc/

View File

@@ -1,62 +1,59 @@
# Contributing Guide #
Thank you for your interest in contributing to Penpot. This is a
generic guide that details how to contribute to Penpot in a way that
is efficient for everyone. If you want a specific documentation for
different parts of the platform, please refer to `docs/` directory.
generic guide that details how to contribute to the project in a way that
is efficient for everyone. If you are looking for specific documentation on
different parts of the platform, please refer to the `docs/` directory,
or the rendered version at the [Help Center](https://help.penpot.app/).
## Reporting Bugs ##
We are using [GitHub Issues](https://github.com/penpot/penpot/issues)
for our public bugs. We keep a close eye on this and try to make it
for our public bugs. We keep a close eye on them and try to make it
clear when we have an internal fix in progress. Before filing a new
task, try to make sure your problem doesn't already exist.
If you found a bug, please report it, as far as possible with:
If you found a bug, please report it, as far as possible, with:
- a detailed explanation of steps to reproduce the error
- a browser and the browser version used
- a dev tools console exception stack trace (if it is available)
- the browser and browser version used
- a dev tools console exception stack trace (if available)
If you found a bug that you consider better discuss in private (for
example: security bugs), consider first send an email to
If you found a bug which you think is better to discuss in private (for
example, security bugs), consider first sending an email to
`support@penpot.app`.
**We don't have formal bug bounty program for security reports; this
is an open source application and your contribution will be recognized
**We don't have a formal bug bounty program for security reports; this
is an open source application, and your contribution will be recognized
in the changelog.**
## Pull requests ##
## Pull Requests ##
If you want propose a change or bug fix with the Pull-Request system
firstly you should carefully read the **DCO** section and format your
commits accordingly.
If you want to propose a change or bug fix via a pull request (PR),
you should first carefully read the section **Developer's Certificate of
Origin**. You must also format your code and commits according to the
instructions below.
If you intend to fix a bug it's fine to submit a pull request right
away but we still recommend to file an issue detailing what you're
If you intend to fix a bug, it's fine to submit a pull request right
away, but we still recommend filing an issue detailing what you're
fixing. This is helpful in case we don't accept that specific fix but
want to keep track of the issue.
If you want to implement or start working in a new feature, please
open a **question** / **discussion** issue for it. No pull-request
will be accepted without previous chat about the changes,
independently if it is a new feature, already planned feature or small
quick win.
If you want to implement or start working on a new feature, please
open a **question*- / **discussion*- issue for it. No PR
will be accepted without a prior discussion about the changes,
whether it is a new feature, an already planned one, or a quick win.
If is going to be your first pull request, You can learn how from this
free video series:
https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github
We will use the `easy fix` mark for tag for indicate issues that are
easy for beginners.
If it is your first PR, you can learn how to proceed from
[this free video
series](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
We use the `easy fix` tag to indicate issues that are appropriate for beginners.
## Commit Guidelines ##
We have very precise rules over how our git commit messages can be formatted.
We have very precise rules on how our git commit messages must be formatted.
The commit message format is:
@@ -71,34 +68,37 @@ The commit message format is:
Where type is:
- :bug: `:bug:` a commit that fixes a bug
- :sparkles: `:sparkles:` a commit that an improvement
- :tada: `:tada:` a commit with new feature
- :sparkles: `:sparkles:` a commit that adds an improvement
- :tada: `:tada:` a commit with a new feature
- :recycle: `:recycle:` a commit that introduces a refactor
- :lipstick: `:lipstick:` a commit with cosmetic changes
- :ambulance: `:ambulance:` a commit that fixes critical bug
- :ambulance: `:ambulance:` a commit that fixes a critical bug
- :books: `:books:` a commit that improves or adds documentation
- :construction: `:construction:`: a wip commit
- :construction: `:construction:` a WIP commit
- :boom: `:boom:` a commit with breaking changes
- :wrench: `:wrench:` a commit for config updates
- :zap: `:zap:` a commit with performance improvements
- :whale: `:whale:` a commit for docker related stuff
- :paperclip: `:paperclip:` a commit with other not relevant changes
- :arrow_up: `:arrow_up:` a commit with dependencies updates
- :arrow_down: `:arrow_down:` a commit with dependencies downgrades
- :whale: `:whale:` a commit for Docker-related stuff
- :paperclip: `:paperclip:` a commit with other non-relevant changes
- :arrow_up: `:arrow_up:` a commit with dependency updates
- :arrow_down: `:arrow_down:` a commit with dependency downgrades
- :fire: `:fire:` a commit that removes files or code
- :globe_with_meridians: `:globe_with_meridians:` a commit that adds or updates
translations
More info:
- https://gist.github.com/parmentf/035de27d6ed1dce0b36a
- https://gist.github.com/rxaviers/7360908
Each commit should have:
- A concise subject using imperative mood.
- The subject should have capitalized the first letter, without period
at the end and no larger than 65 characters.
- A concise subject using the imperative mood.
- The subject should capitalize the first letter, omit the period
at the end, and be no longer than 65 characters.
- A blank line between the subject line and the body.
- An entry on the CHANGES.md file if applicable, referencing the
github or taiga issue/user-story using the these same rules.
- An entry in the CHANGES.md file if applicable, referencing the
GitHub or Taiga issue/user story using these same rules.
Examples of good commit messages:
@@ -111,8 +111,30 @@ Examples of good commit messages:
- `:ambulance: Fix critical bug on user registration process`
- `:tada: Add new approach for user registration`
## Formatting and Linting ##
## Code of conduct ##
You will want to make sure your code is formatted and linted before submitting
a PR. We use [cljfmt](https://github.com/weavejester/cljfmt) and
[clj-kondo](https://github.com/clj-kondo/clj-kondo) for this. After installing
them on your system, you can run them with:
```bash
# Check formatting
yarn fmt:clj:check
# Check and fix formatting
yarn fmt:clj
# Run the linter
yarn lint:clj
```
There are more choices in `package.json`.
Ideally, you should run these commands as git pre-commit hooks. A convenient way
of defining them is to use [Husky](https://typicode.github.io/husky/#/).
## Code of Conduct ##
As contributors and maintainers of this project, we pledge to respect
all people who contribute through reporting issues, posting feature
@@ -132,11 +154,11 @@ unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct. Project
contributions that are not aligned with this Code of Conduct. Project
maintainers who do not follow the Code of Conduct may be removed from
the project team.
This code of conduct applies both within project spaces and in public
This Code of Conduct applies both within project spaces and in public
spaces when an individual is representing the project or its
community.
@@ -145,12 +167,11 @@ may be reported by opening an issue or contacting one or more of the
project maintainers.
This Code of Conduct is adapted from the Contributor Covenant, version
1.1.0, available from http://contributor-covenant.org/version/1/1/0/
1.1.0, available from [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
## Developer's Certificate of Origin (DCO)
## Developer's Certificate of Origin (DCO) ##
By submitting code you are agree and can certify the below:
By submitting code you agree to and can certify the following:
Developer's Certificate of Origin 1.1
@@ -178,13 +199,15 @@ By submitting code you are agree and can certify the below:
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
Then, all your code patches (**documentation are excluded**) should
Then, all your code patches (**documentation is excluded**) should
contain a sign-off at the end of the patch/commit description body. It
can be automatically added on adding `-s` parameter to `git commit`.
can be automatically added by adding the `-s` parameter to `git commit`.
This is an example of the aspect of the line:
This is an example of what the line should look like:
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
```
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
```
Please, use your real name (sorry, no pseudonyms or anonymous
contributions are allowed).

View File

@@ -16,36 +16,38 @@
</p>
<p align="center">
<a href="https://penpot.app/"><b>Website</b></a> •
<a href="https://help.penpot.app/technical-guide/getting-started/"><b>Getting Started</b></a> •
<a href="https://help.penpot.app/user-guide/"><b>User Guide</b></a> •
<a href="https://help.penpot.app/user-guide/introduction/info/"><b>Tutorials & Info</b></a> •
<a href="https://penpot.app/"><b>Website</b></a> •
<a href="https://help.penpot.app/user-guide/"><b>User Guide</b></a> •
<a href="https://penpot.app/learning-center"><b>Learning Center</b></a> •
<a href="https://community.penpot.app/"><b>Community</b></a>
</p>
<p align="center">
<a href="https://www.youtube.com/@Penpot"><b>Youtube</b></a> •
<a href="https://peertube.kaleidos.net/a/penpot_app/video-channels"><b>Peertube</b></a> •
<a href="https://www.linkedin.com/company/penpot/"><b>Linkedin</b></a> •
<a href="https://instagram.com/penpot.app"><b>Instagram</b></a> •
<a href="https://fosstodon.org/@penpot/"><b>Mastodon</b></a> •
<a href="https://www.youtube.com/@Penpot"><b>Youtube</b></a> •
<a href="https://peertube.kaleidos.net/a/penpot_app/video-channels"><b>Peertube</b></a> •
<a href="https://www.linkedin.com/company/penpot/"><b>Linkedin</b></a> •
<a href="https://instagram.com/penpot.app"><b>Instagram</b></a> •
<a href="https://fosstodon.org/@penpot/"><b>Mastodon</b></a> •
<a href="https://bsky.app/profile/penpot.app"><b>Bluesky</b></a> •
<a href="https://twitter.com/penpotapp"><b>X</b></a>
</p>
<br />
[Penpot video](https://github.com/penpot/penpot/assets/5446186/b8ad0764-585e-4ddc-b098-9b4090d337cc)
[Penpot video](https://github.com/user-attachments/assets/7c67fd7c-04d3-4c9b-88ec-b6f5e23f8332
)
<br />
Penpot is the first **open-source** design tool for design and code collaboration. Designers can create stunning designs, interactive prototypes, design systems at scale, while developers enjoy ready-to-use code and make their workflow easy and fast. And all of this with no handoff drama.
Penpot is available on browser and [self host](https://penpot.app/self-host). Its web-based and works with open standards (SVG, CSS and HTML). And last but not least, its free!
Available on browser or self-hosted, Penpot works with open standards like SVG, CSS, HTML and JSON, and its free!
Penpots latest [huge release 2.0](https://penpot.app/dev-diaries), takes the platform to a whole new level. This update introduces the ground-breaking [CSS Grid Layout feature](https://penpot.app/penpot-2.0), a complete UI redesign, a new Components system, and much more. Plus, it's faster and more accessible.
The latest updates take Penpot even further. Its the first design tool to integrate native [design tokens](https://penpot.dev/collaboration/design-tokens)—a single source of truth to improve efficiency and collaboration between product design and development.
With the [huge 2.0 release](https://penpot.app/dev-diaries), Penpot took the platform to a whole new level. This update introduces the ground-breaking [CSS Grid Layout feature](https://penpot.app/penpot-2.0), a complete UI redesign, a new Components system, and much more.
For organizations that need extra service for its teams, [get in touch](https://cal.com/team/penpot/talk-to-us)
🎇 **Penpot Fest** is our design, code & Open Source event. Check out the highlights from [Penpot Fest 2023 edition](https://www.youtube.com/watch?v=sOpLZaK5mDc)!
🎇 Design, code, and Open Source meet at [Penpot Fest](https://penpot.app/penpotfest)! Be part of the 2025 edition in Madrid, Spain, on October 9-10.
## Table of contents ##
@@ -61,7 +63,7 @@ Penpots latest [huge release 2.0](https://penpot.app/dev-diaries), takes the
Penpot expresses designs as code. Designers can do their best work and see it will be beautifully implemented by developers in a two-way collaboration.
### Plugin system ###
[Penpot plugins](https://penpot.app/penpothub/plugins) let you expand the platform's capabilities, give you the flexibility to integrate it with other apps, and design custom solutions.
[Penpot plugins](https://penpot.app/penpothub/plugins) let you expand the platform's capabilities, give you the flexibility to integrate it with other apps, and design custom solutions.
### Designed for developers ###
Penpot was built to serve both designers and developers and create a fluid design-code process. You have the choice to enjoy real-time collaboration or play "solo".
@@ -78,6 +80,10 @@ Penpot offers integration into the development toolchain, thanks to its support
### Whats great for design ###
With Penpot you can design libraries to share and reuse; turn design elements into components and tokens to allow reusability and scalability; and build realistic user flows and interactions.
### Design Tokens ###
With Penpots standardized [design tokens](https://penpot.dev/collaboration/design-tokens) format, you can easily reuse and sync tokens across different platforms, workflows, and disciplines.
<br />
<p align="center">
@@ -88,10 +94,9 @@ With Penpot you can design libraries to share and reuse; turn design elements in
## Getting started ##
### Install with Elestio ###
Penpot is the only design & prototype platform that is deployment agnostic. You can use it or deploy it anywhere.
Penpot is the only design & prototype platform that is deployment agnostic. You can use it in our [SAAS](https://design.penpot.app) or deploy it anywhere.
Learn how to install it with Elestio and Docker, or other options on [our website](https://penpot.app/self-host).
Learn how to install it with Docker, Kubernetes, Elestio or other options on [our website](https://penpot.app/self-host).
<br />
<p align="center">
@@ -123,15 +128,21 @@ You will find the following categories:
</p>
<br />
### Code of Conduct ###
Anyone who contributes to Penpot, whether through code, in the community, or at an event, must adhere to the
[code of conduct](https://help.penpot.app/contributing-guide/coc/) and foster a positive and safe environment.
## Contributing ##
Any contribution will make a difference to improve Penpot. How can you get involved?
Any contribution will make a difference to improve Penpot. How can you get involved?
Choose your way:
Choose your way:
- Create and [share Libraries & Templates](https://penpot.app/libraries-templates.html) that will be helpful for the community
- Invite your [team to join](https://design.penpot.app/#/auth/register)
- Star this repo and follow us on Social Media: [Mastodon](https://fosstodon.org/@penpot/), [Youtube](https://www.youtube.com/c/Penpot), [Instagram](https://instagram.com/penpot.app), [Linkedin](https://www.linkedin.com/company/penpotdesign), [Peertube](https://peertube.kaleidos.net/a/penpot_app) and [X](https://twitter.com/penpotapp).
- Give this repo a star and follow us on Social Media: [Mastodon](https://fosstodon.org/@penpot/), [Youtube](https://www.youtube.com/c/Penpot), [Instagram](https://instagram.com/penpot.app), [Linkedin](https://www.linkedin.com/company/penpotdesign), [Peertube](https://peertube.kaleidos.net/a/penpot_app), [X](https://twitter.com/penpotapp) and [BlueSky](https://bsky.app/profile/penpot.app)
- Participate in the [Community](https://community.penpot.app/) space by asking and answering questions; reacting to others articles; opening your own conversations and following along on decisions affecting the project.
- Report bugs with our easy [guide for bugs hunting](https://help.penpot.app/contributing-guide/reporting-bugs/) or [GitHub issues](https://github.com/penpot/penpot/issues)
- Become a [translator](https://help.penpot.app/contributing-guide/translations)

View File

@@ -3,10 +3,10 @@
:deps
{penpot/common {:local/root "../common"}
org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/clojure {:mvn/version "1.12.1"}
org.clojure/tools.namespace {:mvn/version "1.5.0"}
com.github.luben/zstd-jni {:mvn/version "1.5.6-9"}
com.github.luben/zstd-jni {:mvn/version "1.5.7-3"}
io.prometheus/simpleclient {:mvn/version "0.16.0"}
io.prometheus/simpleclient_hotspot {:mvn/version "0.16.0"}
@@ -17,8 +17,15 @@
io.prometheus/simpleclient_httpserver {:mvn/version "0.16.0"}
io.lettuce/lettuce-core {:mvn/version "6.5.2.RELEASE"}
io.lettuce/lettuce-core {:mvn/version "6.7.0.RELEASE"}
;; Minimal dependencies required by lettuce, we need to include them
;; explicitly because clojure dependency management does not support
;; yet the BOM format.
io.micrometer/micrometer-core {:mvn/version "1.14.2"}
io.micrometer/micrometer-observation {:mvn/version "1.14.2"}
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
com.google.guava/guava {:mvn/version "33.4.8-jre"}
funcool/yetti
{:git/tag "v11.4"
@@ -27,15 +34,14 @@
:exclusions [org.slf4j/slf4j-api]}
com.github.seancorfield/next.jdbc
{:mvn/version "1.3.994"}
metosin/reitit-core {:mvn/version "0.7.2"}
{:mvn/version "1.3.1002"}
metosin/reitit-core {:mvn/version "0.9.1"}
nrepl/nrepl {:mvn/version "1.3.1"}
cider/cider-nrepl {:mvn/version "0.52.0"}
org.postgresql/postgresql {:mvn/version "42.7.5"}
org.xerial/sqlite-jdbc {:mvn/version "3.48.0.0"}
org.postgresql/postgresql {:mvn/version "42.7.6"}
org.xerial/sqlite-jdbc {:mvn/version "3.49.1.0"}
com.zaxxer/HikariCP {:mvn/version "6.2.1"}
com.zaxxer/HikariCP {:mvn/version "6.3.0"}
io.whitfin/siphash {:mvn/version "2.0.0"}
@@ -44,7 +50,7 @@
com.github.ben-manes.caffeine/caffeine {:mvn/version "3.2.0"}
org.jsoup/jsoup {:mvn/version "1.18.3"}
org.jsoup/jsoup {:mvn/version "1.20.1"}
org.im4java/im4java
{:git/tag "1.4.0-penpot-2"
:git/sha "e2b3e16"
@@ -55,11 +61,11 @@
org.clojars.pntblnk/clj-ldap {:mvn/version "0.0.17"}
dawran6/emoji {:mvn/version "0.1.5"}
markdown-clj/markdown-clj {:mvn/version "1.12.2"}
markdown-clj/markdown-clj {:mvn/version "1.12.3"}
;; Pretty Print specs
pretty-spec/pretty-spec {:mvn/version "0.1.4"}
software.amazon.awssdk/s3 {:mvn/version "2.28.26"}}
software.amazon.awssdk/s3 {:mvn/version "2.31.55"}}
:paths ["src" "resources" "target/classes"]
:aliases
@@ -74,7 +80,7 @@
:build
{:extra-deps
{io.github.clojure/tools.build {:git/tag "v0.10.6" :git/sha "52cf7d6"}}
{io.github.clojure/tools.build {:git/tag "v0.10.9" :git/sha "e405aac"}}
:ns-default build}
:test

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"

View File

@@ -13,7 +13,7 @@ This will automatically include {{requested-by|abbreviate:25}} in the team, so t
Click the link below to provide team access:
{{ public-uri }}/#/dashboard/members?team-id{{team-id}}&invite-email={{requested-by-email|urlescape}}
{{ public-uri }}/#/dashboard/members?team-id={{team-id}}&invite-email={{requested-by-email|urlescape}}

View File

@@ -1,4 +1,7 @@
[{:id "wireframing-kit"
[{:id "tokens-starter-kit"
:name "Design tokens starter kit"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"},
{:id "wireframing-kit"
:name "Wireframe library"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"}
{:id "prototype-examples"

View File

@@ -35,40 +35,35 @@ def get_prepl_conninfo():
return host, port
def send_eval(expr):
def send(data):
host, port = get_prepl_conninfo()
with socket.create_connection((host, port)) as s:
f = s.makefile(mode="rw")
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))
s.send(expr.encode("utf-8"))
s.send(b":repl/quit\n\n")
json.dump(data, f)
f.write("\n")
f.flush()
with s.makefile() as f:
while True:
line = f.readline()
result = json.loads(line)
tag = result.get("tag", None)
if tag == "ret":
return result.get("val", None), result.get("exception", None)
elif tag == "out":
print(result.get("val"), end="")
else:
raise RuntimeError("unexpected response from PREPL")
while True:
line = f.readline()
result = json.loads(line)
tag = result.get("tag", None)
def encode(val):
return json.dumps(json.dumps(val))
if tag == "ret":
return result.get("val", None), result.get("err", None)
elif tag == "out":
print(result.get("val"), end="")
else:
raise RuntimeError("unexpected response from PREPL")
def print_error(res):
for error in res["via"]:
print("ERR:", error["message"])
break
def print_error(error):
print("ERR:", error["hint"])
def run_cmd(params):
try:
expr = "(app.srepl.cli/exec {})".format(encode(params))
res, failed = send_eval(expr)
if failed:
print_error(res)
res, err = send(params)
if err:
print_error(err)
sys.exit(-1)
return res
@@ -76,19 +71,27 @@ def run_cmd(params):
print("EXC:", str(cause))
sys.exit(-2)
def create_profile(fullname, email, password):
def create_profile(fullname, email, password, skip_tutorial=False, skip_walkthrough=False):
props = {}
if skip_tutorial:
props["viewed-tutorial?"] = True
if skip_walkthrough:
props["viewed-walkthrough?"] = True
params = {
"cmd": "create-profile",
"params": {
"fullname": fullname,
"email": email,
"password": password
"password": password,
**props
}
}
res = run_cmd(params)
print(f"Created: {res['email']} / {res['id']}")
def update_profile(email, fullname, password, is_active):
params = {
"cmd": "update-profile",
@@ -96,7 +99,7 @@ def update_profile(email, fullname, password, is_active):
"email": email,
"fullname": fullname,
"password": password,
"is_active": is_active
"isActive": is_active
}
}
@@ -138,7 +141,7 @@ def derive_password(password):
params = {
"cmd": "derive-password",
"params": {
"password": password,
"password": password
}
}
@@ -175,6 +178,8 @@ parser.add_argument("-n", "--fullname", help="fullname", action="store")
parser.add_argument("-e", "--email", help="email", action="store")
parser.add_argument("-p", "--password", help="password", action="store")
parser.add_argument("-c", "--connect", help="connect to PREPL", action="store", default="tcp://localhost:6063")
parser.add_argument("--skip-tutorial", help="mark tutorial as viewed", action="store_true")
parser.add_argument("--skip-walkthrough", help="mark walkthrough as viewed", action="store_true")
args = parser.parse_args()

View File

@@ -31,6 +31,7 @@ export PENPOT_FLAGS="\
enable-tiered-file-data-storage \
enable-file-validation \
enable-file-schema-validation \
enable-subscriptions \
enable-subscriptions-old";
# Default deletion delay for devenv
@@ -71,15 +72,19 @@ export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
export PENPOT_OBJECTS_STORAGE_FS_DIRECTORY="assets"
export JAVA_OPTS="--enable-preview \
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv-repl.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:+EnableDynamicAgentLoading \
-XX:-OmitStackTraceInFastThrow \
-XX:-OmitStackTraceInFastThrow \
-XX:+UnlockDiagnosticVMOptions \
-XX:+DebugNonSafepoints";
-XX:+DebugNonSafepoints \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";
export OPTIONS="-A:jmx-remote -A:dev"
@@ -102,9 +107,6 @@ export OPTIONS="-A:jmx-remote -A:dev"
# Setup GC
# export OPTIONS="$OPTIONS -J-XX:+UseZGC"
# Enable ImageMagick v7.x support
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
export OPTIONS_EVAL="nil"
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)"

View File

@@ -18,9 +18,9 @@ if [ -f ./environ ]; then
source ./environ
fi
export JVM_OPTS="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --enable-preview $JVM_OPTS"
export JAVA_OPTS="-Dim4java.useV7=true -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --sun-misc-unsafe-memory-access=allow --enable-native-access=ALL-UNNAMED --enable-preview $JVM_OPTS $JAVA_OPTS"
ENTRYPOINT=${1:-app.main};
set -ex
exec $JAVA_CMD $JVM_OPTS -jar penpot.jar -m $ENTRYPOINT
exec $JAVA_CMD $JAVA_OPTS -jar penpot.jar -m $ENTRYPOINT

View File

@@ -24,18 +24,8 @@ export PENPOT_FLAGS="\
enable-tiered-file-data-storage \
enable-file-validation \
enable-file-schema-validation \
enable-subscriptions-old";
export OPTIONS="
-A:jmx-remote -A:dev \
-J-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-J-Djdk.attach.allowAttachSelf \
-J-Dpolyglot.engine.WarnInterpreterOnly=false \
-J-Dlog4j2.configurationFile=log4j2-devenv.xml \
-J-XX:+EnableDynamicAgentLoading \
-J-XX:-OmitStackTraceInFastThrow \
-J-XX:+UnlockDiagnosticVMOptions \
-J-XX:+DebugNonSafepoints"
enable-subscriptions \
enable-subscriptions-old ";
# Default deletion delay for devenv
export PENPOT_DELETION_DELAY="24h"
@@ -46,9 +36,6 @@ export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
# Enable ImageMagick v7.x support
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
# Initialize MINIO config
mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin -q
mc admin user add penpot-s3 penpot-devenv penpot-devenv -q
@@ -66,6 +53,18 @@ export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
entrypoint=${1:-app.main};
set -ex
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:-OmitStackTraceInFastThrow \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";
clojure $OPTIONS -A:dev -M -m $entrypoint;
export OPTIONS="-A:jmx-remote -A:dev"
set -ex
clojure $OPTIONS -M -m $entrypoint;

View File

@@ -9,8 +9,52 @@
for recently imported shapes."
(:require
[app.common.data :as d]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRE DECODE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- pre-clean-bool-content
[shape]
(if-let [content (get shape :bool-content)]
(-> shape
(assoc :content content)
(dissoc :bool-content))
shape))
(defn- pre-clean-shadow-color
[shape]
(d/update-when shape :shadow
(fn [shadows]
(mapv (fn [shadow]
(update shadow :color
(fn [color]
(let [ref-id (get color :id)
ref-file (get color :file-id)]
(-> (d/without-qualified color)
(select-keys [:opacity :color :gradient :image :ref-id :ref-file])
(cond-> ref-id
(assoc :ref-id ref-id))
(cond-> ref-file
(assoc :ref-file ref-file)))))))
shadows))))
(defn clean-shape-pre-decode
"Applies a pre-decode phase migration to the shape"
[shape]
(cond-> shape
(= "bool" (:type shape))
(pre-clean-bool-content)
(contains? shape :shadow)
(pre-clean-shadow-color)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; POST DECODE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- fix-shape-shadow-color
"Some shapes can come with invalid `id` property on shadow colors
caused by incorrect uuid parsing bug that should be already fixed;
@@ -36,9 +80,52 @@
(fn [shadows]
(into [] xform shadows)))))
(defn- fix-root-shape
"Ensure all root objects are well formed shapes"
[shape]
(if (= (:id shape) uuid/zero)
(-> shape
(assoc :parent-id uuid/zero)
(assoc :frame-id uuid/zero)
;; We explicitly dissoc them and let the shape-setup
;; to regenerate it with valid values.
(dissoc :selrect)
(dissoc :points)
(cts/setup-shape))
shape))
(defn- fix-legacy-flex-dir
"This operation is only relevant to old data and it is fixed just
for convenience."
[shape]
(d/update-when shape :layout-flex-dir
(fn [dir]
(case dir
:reverse-row :row-reverse
:reverse-column :column-reverse
dir))))
(defn clean-shape-post-decode
"A shape procesor that expected to be executed after schema decoding
process but before validation."
[shape]
(-> shape
(fix-shape-shadow-color)))
(fix-shape-shadow-color)
(fix-root-shape)
(fix-legacy-flex-dir)))
(defn- fix-container
[container]
(-> container
;; Remove possible `nil` keys on objects
(d/update-when :objects dissoc nil)
(d/update-when :objects d/update-vals clean-shape-post-decode)))
(defn clean-file
[file & {:as _opts}]
(update file :data
(fn [data]
(-> data
(d/update-when :pages-index d/update-vals fix-container)
(d/update-when :components d/update-vals fix-container)
(d/without-nils)))))

View File

@@ -53,6 +53,7 @@
(* 1024 1024 100))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare get-resolved-file-libraries)
(def file-attrs
#{:id
@@ -143,11 +144,13 @@
(reduce #(index-object %1 %2 attr) index coll)))
(defn decode-row
"A generic decode row helper"
[{:keys [data features] :as row}]
(cond-> row
features (assoc :features (db/decode-pgarray features #{}))
data (assoc :data (blob/decode data))))
[{:keys [data changes features] :as row}]
(when row
(cond-> row
features (assoc :features (db/decode-pgarray features #{}))
changes (assoc :changes (blob/decode changes))
data (assoc :data (blob/decode data)))))
(defn decode-file
"A general purpose file decoding function that resolves all external
@@ -156,7 +159,8 @@
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(let [file (->> file
(feat.fmigr/resolve-applied-migrations cfg)
(feat.fdata/resolve-file-data cfg))]
(feat.fdata/resolve-file-data cfg))
libs (delay (get-resolved-file-libraries cfg file))]
(-> file
(update :features db/decode-pgarray #{})
@@ -164,7 +168,7 @@
(update :data feat.fdata/process-pointers deref)
(update :data feat.fdata/process-objects (partial into {}))
(update :data assoc :id id)
(fmg/migrate-file)))))
(fmg/migrate-file libs)))))
(defn get-file
"Get file, resolve all features and apply migrations.
@@ -418,28 +422,35 @@
(db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"])))
(defn process-file
[{:keys [id] :as file}]
(-> file
(update :data (fn [fdata]
(-> fdata
(assoc :id id)
(dissoc :recent-colors))))
(fmg/migrate-file)
(update :data (fn [fdata]
(-> fdata
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(update :colors relink-colors)
(d/without-nils))))))
[cfg {:keys [id] :as file}]
(let [libs (delay (get-resolved-file-libraries cfg file))]
(-> file
(update :data (fn [fdata]
(-> fdata
(assoc :id id)
(dissoc :recent-colors))))
(update :data (fn [fdata]
(-> fdata
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(update :colors relink-colors)
(d/without-nils))))
(fmg/migrate-file libs)
;; NOTE: this is necessary because when we just creating a new
;; file from imported artifact or cloned file there are no
;; migrations registered on the database, so we need to persist
;; all of them, not only the applied
(vary-meta dissoc ::fmg/migrated))))
(defn encode-file
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
(let [file (if (contains? (:features file) "fdata/objects-map")
[{:keys [::db/conn] :as cfg} {:keys [id features] :as file}]
(let [file (if (contains? features "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
file (if (contains? features "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (feat.fdata/enable-pointer-map file)]
(feat.fdata/persist-pointers! cfg id)
@@ -522,3 +533,49 @@
(l/error :hint "file schema validation error" :cause result))))
(insert-file! cfg file opts)))
(def ^:private sql:get-file-libraries
"WITH RECURSIVE libs AS (
SELECT fl.*, flr.synced_at
FROM file AS fl
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
WHERE flr.file_id = ?::uuid
UNION
SELECT fl.*, flr.synced_at
FROM file AS fl
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
JOIN libs AS l ON (flr.file_id = l.id)
)
SELECT l.id,
l.features,
l.project_id,
p.team_id,
l.created_at,
l.modified_at,
l.deleted_at,
l.name,
l.revn,
l.vern,
l.synced_at,
l.is_shared
FROM libs AS l
INNER JOIN project AS p ON (p.id = l.project_id)
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
(defn get-file-libraries
[conn file-id]
(into []
(comp
;; FIXME: :is-indirect set to false to all rows looks
;; completly useless
(map #(assoc % :is-indirect false))
(map decode-row))
(db/exec! conn [sql:get-file-libraries file-id])))
(defn get-resolved-file-libraries
"A helper for preload file libraries"
[{:keys [::db/conn] :as cfg} file]
(->> (get-file-libraries conn (:id file))
(into [file] (map #(get-file cfg (:id %))))
(d/index-by :id)))

View File

@@ -10,7 +10,6 @@
[app.binfile.common :as bfc]
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.features.components-v2 :as feat.compv2]
[clojure.set :as set]
[cuerdas.core :as str]))
@@ -28,13 +27,11 @@
(defn apply-pending-migrations!
"Apply alredy registered pending migrations to files"
[cfg]
(doseq [[feature file-id] (-> bfc/*state* deref :pending-to-migrate)]
[_cfg]
(doseq [[feature _file-id] (-> bfc/*state* deref :pending-to-migrate)]
(case feature
"components/v2"
(feat.compv2/migrate-file! cfg file-id
:validate? (::validate cfg true)
:skip-on-graphic-error? true)
nil
"fdata/shape-data-type"
nil

View File

@@ -551,8 +551,8 @@
(cond-> (and (= idx 0) (some? name))
(assoc :name name))
(assoc :project-id project-id)
(dissoc :thumbnails)
(bfc/process-file))]
(dissoc :thumbnails))
file (bfc/process-file system file)]
;; All features that are enabled and requires explicit migration are
;; added to the state for a posterior migration step.

View File

@@ -281,8 +281,8 @@
(let [file (-> (read-obj cfg :file file-id)
(update :id bfc/lookup-index)
(update :project-id bfc/lookup-index)
(bfc/process-file))]
(update :project-id bfc/lookup-index))
file (bfc/process-file cfg file)]
(events/tap :progress
{:op :import

View File

@@ -12,11 +12,12 @@
[app.binfile.common :as bfc]
[app.binfile.migrations :as bfm]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.files.migrations :as-alias fmg]
[app.common.json :as json]
[app.common.logging :as l]
[app.common.media :as cmedia]
[app.common.schema :as sm]
[app.common.thumbnails :as cth]
[app.common.types.color :as ctcl]
@@ -52,7 +53,7 @@
[:map {:title "Manifest"}
[:version ::sm/int]
[:type :string]
[:referer {:optional true} :string]
[:generated-by {:optional true} :string]
[:files
@@ -72,7 +73,7 @@
[:size ::sm/int]
[:content-type :string]
[:bucket [::sm/one-of {:format :string} sto/valid-buckets]]
[:hash :string]])
[:hash {:optional true} :string]])
(def ^:private schema:file-thumbnail
[:map {:title "FileThumbnail"}
@@ -87,13 +88,19 @@
ctf/schema:file
[:map [:options {:optional true} ctf/schema:options]]])
;; --- HELPERS
(defn- default-now
[o]
(or o (dt/now)))
;; --- ENCODERS
(def encode-file
(sm/encoder schema:file sm/json-transformer))
(def encode-page
(sm/encoder ::ctp/page sm/json-transformer))
(sm/encoder ctp/schema:page sm/json-transformer))
(def encode-shape
(sm/encoder ::cts/shape sm/json-transformer))
@@ -105,7 +112,7 @@
(sm/encoder ::ctc/component sm/json-transformer))
(def encode-color
(sm/encoder ::ctcl/color sm/json-transformer))
(sm/encoder ctcl/schema:library-color sm/json-transformer))
(def encode-typography
(sm/encoder ::cty/typography sm/json-transformer))
@@ -128,13 +135,13 @@
(sm/decoder schema:manifest sm/json-transformer))
(def decode-media
(sm/decoder ::ctf/media sm/json-transformer))
(sm/decoder ctf/schema:media sm/json-transformer))
(def decode-component
(sm/decoder ::ctc/component sm/json-transformer))
(def decode-color
(sm/decoder ::ctcl/color sm/json-transformer))
(sm/decoder ctcl/schema:library-color sm/json-transformer))
(def decode-file
(sm/decoder schema:file sm/json-transformer))
@@ -149,7 +156,7 @@
(sm/decoder ::cty/typography sm/json-transformer))
(def decode-tokens-lib
(sm/decoder ::cto/tokens-lib sm/json-transformer))
(sm/decoder cto/schema:tokens-lib sm/json-transformer))
(def decode-plugin-data
(sm/decoder ::ctpg/plugin-data sm/json-transformer))
@@ -178,7 +185,7 @@
(sm/check-fn ::ctf/media))
(def validate-color
(sm/check-fn ::ctcl/color))
(sm/check-fn ctcl/schema:library-color))
(def validate-component
(sm/check-fn ::ctc/component))
@@ -228,27 +235,13 @@
:always
(bfc/clean-file-features))))))
(defn- resolve-extension
[mtype]
(case mtype
"image/png" ".png"
"image/jpeg" ".jpg"
"image/gif" ".gif"
"image/svg+xml" ".svg"
"image/webp" ".webp"
"font/woff" ".woff"
"font/woff2" ".woff2"
"font/ttf" ".ttf"
"font/otf" ".otf"
"application/octet-stream" ".bin"))
(defn- export-storage-objects
[{:keys [::output] :as cfg}]
(let [storage (sto/resolve cfg)]
(doseq [id (-> bfc/*state* deref :storage-objects not-empty)]
(let [sobject (sto/get-object storage id)
smeta (meta sobject)
ext (resolve-extension (:content-type smeta))
ext (cmedia/mtype->extension (:content-type smeta))
path (str "objects/" id ".json")
params (-> (meta sobject)
(assoc :id (:id sobject))
@@ -379,6 +372,7 @@
params {:type "penpot/export-files"
:version 1
:generated-by (str "penpot/" (:full cf/version))
:refer "penpot"
:files (vec (vals files))
:relations rels}]
(write-entry! output "manifest.json" params))))
@@ -573,7 +567,13 @@
(let [object (->> (read-entry input entry)
(decode-media)
(validate-media))
object (assoc object :file-id file-id)]
object (-> object
(assoc :file-id file-id)
(update :created-at default-now)
;; FIXME: this is set default to true for
;; setting a value, this prop is no longer
;; relevant;
(assoc :is-local true))]
(if (= id (:id object))
(conj result object)
result)))
@@ -602,13 +602,22 @@
(reduce-kv (fn [objects id shape]
(assoc objects id (bfl/clean-shape-post-decode shape)))
objects
objects))))
clean-component-pre-decode
(fn [component]
(d/update-when component :objects
(fn [objects]
(reduce-kv (fn [objects id shape]
(assoc objects id (bfl/clean-shape-pre-decode shape)))
objects
objects))))]
(->> (keep (match-component-entry-fn file-id) entries)
(reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry)
(clean-component-pre-decode)
(decode-component)
(clean-component-post-decode)
(validate-component))]
(clean-component-post-decode))]
(if (= id (:id object))
(assoc result id object)
result)))
@@ -640,10 +649,9 @@
(->> (keep (match-shape-entry-fn file-id page-id) entries)
(reduce (fn [result {:keys [id entry]}]
(let [object (->> (read-entry input entry)
(bfl/clean-shape-pre-decode)
(decode-shape)
(bfl/clean-shape-post-decode)
(validate-shape))]
(bfl/clean-shape-post-decode))]
(if (= id (:id object))
(assoc result id object)
result)))
@@ -689,7 +697,6 @@
components (read-file-components cfg)
plugin-data (read-file-plugin-data cfg)
pages (read-file-pages cfg)]
{:pages (-> pages keys vec)
:pages-index (into {} pages)
:colors colors
@@ -744,9 +751,10 @@
(assoc :data data)
(assoc :name file-name)
(assoc :project-id project-id)
(dissoc :options)
(bfc/process-file))]
(dissoc :options))
file (bfc/process-file cfg file)
file (ctf/check-file file)]
(bfm/register-pending-migrations! cfg file)
(bfc/save-file! cfg file ::db/return-keys false)
@@ -790,7 +798,7 @@
:expected-id (str id)
:found-id (str (:id object))))
(let [ext (resolve-extension (:content-type object))
(let [ext (cmedia/mtype->extension (:content-type object))
path (str "objects/" id ext)
content (->> path
(get-zip-entry input)
@@ -804,13 +812,14 @@
:expected-size (:size object)
:found-size (sto/get-size content)))
(when (not= (:hash object) (sto/get-hash content))
(ex/raise :type :validation
:code :inconsistent-penpot-file
:hint "found corrupted storage object: hash does not match"
:path path
:expected-hash (:hash object)
:found-hash (sto/get-hash content)))
(when-let [hash (get object :hash)]
(when (not= hash (sto/get-hash content))
(ex/raise :type :validation
:code :inconsistent-penpot-file
:hint "found corrupted storage object: hash does not match"
:path path
:expected-hash (:hash object)
:found-hash (sto/get-hash content))))
(let [params (-> object
(dissoc :id :size)
@@ -869,13 +878,8 @@
(defn- import-files
[{:keys [::bfc/timestamp ::bfc/input ::bfc/name] :or {timestamp (dt/now)} :as cfg}]
(dm/assert!
"expected zip file"
(instance? ZipFile input))
(dm/assert!
"expected valid instant"
(dt/instant? timestamp))
(assert (instance? ZipFile input) "expected zip file")
(assert (dt/instant? timestamp) "expected valid instant")
(let [manifest (-> (read-manifest input)
(validate-manifest))
@@ -887,6 +891,7 @@
:hint "unexpected type on manifest"
:manifest manifest))
;; Check if all files referenced on manifest are present
(doseq [{file-id :id features :features} (:files manifest)]
(let [path (str "files/" file-id ".json")]
@@ -947,14 +952,13 @@
[{:keys [::bfc/ids] :as cfg} output]
(dm/assert!
"expected a set of uuid's for `::bfc/ids` parameter"
(and (set? ids)
(every? uuid? ids)))
(assert
(and (set? ids) (every? uuid? ids))
"expected a set of uuid's for `::bfc/ids` parameter")
(dm/assert!
"expected instance of jio/IOFactory for `input`"
(satisfies? jio/IOFactory output))
(assert
(satisfies? jio/IOFactory output)
"expected instance of jio/IOFactory for `input`")
(let [id (uuid/next)
tp (dt/tpoint)
@@ -993,14 +997,14 @@
(defn import-files!
[{:keys [::bfc/input] :as cfg}]
(dm/assert!
"expected valid profile-id and project-id on `cfg`"
(assert
(and (uuid? (::bfc/profile-id cfg))
(uuid? (::bfc/project-id cfg))))
(uuid? (::bfc/project-id cfg)))
"expected valid profile-id and project-id on `cfg`")
(dm/assert!
"expected instance of jio/IOFactory for `input`"
(io/coercible? input))
(assert
(io/coercible? input)
"expected instance of jio/IOFactory for `input`")
(let [id (uuid/next)
tp (dt/tpoint)
@@ -1020,3 +1024,9 @@
:id (str id)
:elapsed (dt/format-duration (tp))
:error? (some? @cs))))))
(defn get-manifest
[path]
(with-open [input (ZipFile. (fs/file path))]
(-> (read-manifest input)
(validate-manifest))))

View File

@@ -42,6 +42,8 @@
org.postgresql.util.PGInterval
org.postgresql.util.PGobject))
(def ^:dynamic *conn* nil)
(declare open)
(declare create-pool)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,10 @@
(:require
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.files.migrations :as fmg]
[app.common.logging :as l]
[app.common.types.path :as path]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.storage :as sto]
@@ -30,7 +33,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn enable-objects-map
[file]
[file & _opts]
(let [update-page
(fn [page]
(if (and (pmap/pointer-map? page)
@@ -136,10 +139,56 @@
(defn enable-pointer-map
"Enable the fdata/pointer-map feature on the file."
[file]
[file & _opts]
(-> file
(update :data (fn [fdata]
(-> fdata
(update :pages-index d/update-vals pmap/wrap)
(d/update-when :components pmap/wrap))))
(update :features conj "fdata/pointer-map")))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PATH-DATA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn enable-path-data
"Enable the fdata/path-data feature on the file."
[file & _opts]
(letfn [(update-object [object]
(if (or (cfh/path-shape? object)
(cfh/bool-shape? object))
(update object :content path/content)
object))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
(-> file
(update :data (fn [data]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(update :features conj "fdata/path-data"))))
(defn disable-path-data
[file & _opts]
(letfn [(update-object [object]
(if (or (cfh/path-shape? object)
(cfh/bool-shape? object))
(update object :content vec)
object))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
(when-let [conn db/*conn*]
(db/delete! conn :file-migration {:file-id (:id file)
:name "0003-convert-path-content"}))
(-> file
(update :data (fn [data]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(update :features disj "fdata/path-data")
(update :migrations disj "0003-convert-path-content")
(vary-meta update ::fmg/migrated disj "0003-convert-path-content"))))

View File

@@ -0,0 +1,31 @@
;; 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.features.logical-deletion
"A code related to handle logical deletion mechanism"
(:require
[app.config :as cf]
[app.util.time :as dt]))
(defn get-deletion-delay
"Calculate the next deleted-at for a resource (file, team, etc) in function
of team settings"
[team]
(if-let [subscription (get team :subscription)]
(cond
(and (= (:type subscription) "unlimited")
(= (:status subscription) "active"))
(dt/duration {:days 30})
(and (= (:type subscription) "enterprise")
(= (:status subscription) "active"))
(dt/duration {:days 90})
:else
(cf/get-deletion-delay))
(cf/get-deletion-delay)))

View File

@@ -9,7 +9,6 @@
(:refer-clojure :exclude [tap])
(:require
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.transit :as t]
[app.http.errors :as errors]
@@ -54,18 +53,20 @@
::yres/status 200
::yres/body (yres/stream-body
(fn [_ output]
(binding [events/*channel* (sp/chan :buf buf :xf (keep encode))]
(let [listener (events/start-listener
(partial write! output)
(partial pu/close! output))]
(try
(let [channel (sp/chan :buf buf :xf (keep encode))
listener (events/start-listener
channel
(partial write! output)
(partial pu/close! output))]
(try
(binding [events/*channel* channel]
(let [result (handler)]
(events/tap :end result))
(catch Throwable cause
(events/tap :error (errors/handle' cause request))
(when-not (ex/instance? java.io.EOFException cause)
(binding [l/*context* (errors/request->context request)]
(l/err :hint "unexpected error on processing sse response" :cause cause))))
(finally
(sp/close! events/*channel*)
(px/await! listener)))))))}))
(events/tap :end result)))
(catch Throwable cause
(let [result (errors/handle' cause request)]
(events/tap channel :error result)))
(finally
(sp/close! channel)
(px/await! listener))))))}))

View File

@@ -108,6 +108,7 @@
[::ip-addr {:optional true} ::sm/text]
[::props {:optional true} [:map-of :keyword :any]]
[::context {:optional true} [:map-of :keyword :any]]
[::tracked-at {:optional true} ::sm/inst]
[::webhooks/event? {:optional true} ::sm/boolean]
[::webhooks/batch-timeout {:optional true} ::dt/duration]
[::webhooks/batch-key {:optional true}
@@ -118,12 +119,12 @@
(defn prepare-event
[cfg mdata params result]
(let [resultm (meta result)
request (-> params meta ::http/request)
profile-id (or (::profile-id resultm)
(:profile-id result)
(::rpc/profile-id params)
uuid/zero)
(let [resultm (meta result)
request (-> params meta ::http/request)
profile-id (or (::profile-id resultm)
(:profile-id result)
(::rpc/profile-id params)
uuid/zero)
session-id (get params ::rpc/external-session-id)
event-origin (get params ::rpc/external-event-origin)
@@ -135,14 +136,14 @@
(clean-props))
token-id (::actoken/id request)
context (-> (::context resultm)
(assoc :external-session-id session-id)
(assoc :external-event-origin event-origin)
(assoc :access-token-id (some-> token-id str))
(d/without-nils))
token-id (::actoken/id request)
context (-> (::context resultm)
(assoc :external-session-id session-id)
(assoc :external-event-origin event-origin)
(assoc :access-token-id (some-> token-id str))
(d/without-nils))
ip-addr (inet/parse-request request)]
ip-addr (inet/parse-request request)]
{::type (or (::type resultm)
(::rpc/type cfg))

View File

@@ -41,7 +41,7 @@
(if (or (instance? java.util.concurrent.CompletionException cause)
(instance? java.util.concurrent.ExecutionException cause))
(-> record
(assoc ::trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false))
(assoc ::trace (ex/format-throwable cause :data? true :explain? false :header? false :summary? false))
(assoc ::l/cause (ex-cause cause))
(record->report))
@@ -64,18 +64,18 @@
message))
@message)
:trace (or (::trace record)
(some-> cause (ex/format-throwable :data? false :explain? false :header? false :summary? false)))}
(some-> cause (ex/format-throwable :data? true :explain? false :header? false :summary? false)))}
(when-let [params (or (:request/params context) (:params context))]
{:params (pp/pprint-str params :length 30 :level 13)})
{:params (pp/pprint-str params :length 20 :level 20)})
(when-let [value (:value context)]
{:value (pp/pprint-str value :length 30 :level 12)})
{:value (pp/pprint-str value :length 30 :level 13)})
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
{:data (pp/pprint-str data :length 30 :level 12)})
{:data (pp/pprint-str data :length 30 :level 13)})
(when-let [explain (ex/explain data :length 30 :level 12)]
(when-let [explain (ex/explain data :length 30 :level 13)]
{:explain explain})))))
(defn error-record?

View File

@@ -15,6 +15,7 @@
[app.config :as cf]
[app.db :as db]
[app.http.client :as http]
[app.loggers.audit :as audit]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.data.json :as json]
@@ -67,18 +68,27 @@
(defmethod ig/init-key ::process-event-handler
[_ cfg]
(fn [{:keys [props] :as task}]
(l/dbg :hint "process webhook event" :name (:name props))
(when-let [items (lookup-webhooks cfg props)]
(l/trc :hint "webhooks found for event" :total (count items))
(db/tx-run! cfg (fn [cfg]
(doseq [item items]
(wrk/submit! (-> cfg
(assoc ::wrk/task :run-webhook)
(assoc ::wrk/queue :webhooks)
(assoc ::wrk/max-retries 3)
(assoc ::wrk/params {:event props
:config item})))))))))
(let [items (lookup-webhooks cfg props)
event {::audit/profile-id (:profile-id props)
::audit/name "webhook"
::audit/type "trigger"
::audit/props {:name (get props :name)
:event-id (get props :id)
:total-affected (count items)}}]
(audit/insert! cfg event)
(when items
(l/trc :hint "webhooks found for event" :total (count items))
(db/tx-run! cfg (fn [cfg]
(doseq [item items]
(wrk/submit! (-> cfg
(assoc ::wrk/task :run-webhook)
(assoc ::wrk/queue :webhooks)
(assoc ::wrk/max-retries 3)
(assoc ::wrk/params {:event props
:config item}))))))))))
;; --- RUN
(declare interpret-exception)

View File

@@ -40,7 +40,6 @@
[app.svgo :as-alias svgo]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[cider.nrepl :refer [cider-nrepl-handler]]
[clojure.test :as test]
[clojure.tools.namespace.repl :as repl]
[cuerdas.core :as str]
@@ -605,7 +604,7 @@
(let [p (promise)]
(when (contains? cf/flags :nrepl-server)
(l/inf :hint "start nrepl server" :port 6064)
(nrepl/start-server :bind "0.0.0.0" :port 6064 :handler cider-nrepl-handler))
(nrepl/start-server :bind "0.0.0.0" :port 6064))
(start)
(deref p))

View File

@@ -8,12 +8,12 @@
"Media & Font postprocessing."
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[app.common.media :as cm]
[app.common.schema :as sm]
[app.common.schema.openapi :as-alias oapi]
[app.common.spec :as us]
[app.common.svg :as csvg]
[app.config :as cf]
[app.db :as-alias db]
[app.storage :as-alias sto]
@@ -22,42 +22,42 @@
[buddy.core.bytes :as bb]
[buddy.core.codecs :as bc]
[clojure.java.shell :as sh]
[clojure.spec.alpha :as s]
[clojure.string]
[clojure.xml :as xml]
[cuerdas.core :as str]
[datoteka.fs :as fs]
[datoteka.io :as io])
(:import
clojure.lang.XMLHandler
java.io.InputStream
javax.xml.XMLConstants
javax.xml.parsers.SAXParserFactory
org.apache.commons.io.IOUtils
org.im4java.core.ConvertCmd
org.im4java.core.IMOperation
org.im4java.core.Info))
(s/def ::path fs/path?)
(s/def ::filename string?)
(s/def ::size integer?)
(s/def ::headers (s/map-of string? string?))
(s/def ::mtype string?)
(def schema:upload
(sm/register!
^{::sm/type ::upload}
[:map {:title "Upload"}
[:filename :string]
[:size ::sm/int]
[:path ::fs/path]
[:mtype {:optional true} :string]
[:headers {:optional true}
[:map-of :string :string]]]))
(s/def ::upload
(s/keys :req-un [::filename ::size ::path]
:opt-un [::mtype ::headers]))
(def ^:private schema:input
[:map {:title "Input"}
[:path ::fs/path]
[:mtype {:optional true} ::sm/text]])
;; A subset of fields from the ::upload spec
(s/def ::input
(s/keys :req-un [::path]
:opt-un [::mtype]))
(sm/register!
^{::sm/type ::upload}
[:map {:title "Upload"}
[:filename :string]
[:size ::sm/int]
[:path ::fs/path]
[:mtype {:optional true} :string]
[:headers {:optional true}
[:map-of :string :string]]])
(def ^:private check-input
(sm/check-fn schema:input))
(defn validate-media-type!
([upload] (validate-media-type! upload cm/valid-image-types))
([upload] (validate-media-type! upload cm/image-types))
([upload allowed]
(when-not (contains? allowed (:mtype upload))
(ex/raise :type :validation
@@ -97,17 +97,44 @@
(catch Throwable e
(process-error e))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SVG PARSING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- secure-parser-factory
[^InputStream input ^XMLHandler handler]
(.. (doto (SAXParserFactory/newInstance)
(.setFeature XMLConstants/FEATURE_SECURE_PROCESSING true)
(.setFeature "http://apache.org/xml/features/disallow-doctype-decl" true))
(newSAXParser)
(parse input handler)))
(defn- strip-doctype
[data]
(cond-> data
(str/includes? data "<!DOCTYPE")
(str/replace #"<\!DOCTYPE[^>]*>" "")))
(defn- parse-svg
[text]
(let [text (strip-doctype text)]
(dm/with-open [istream (IOUtils/toInputStream text "UTF-8")]
(xml/parse istream secure-parser-factory))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; IMAGE THUMBNAILS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(s/def ::width integer?)
(s/def ::height integer?)
(s/def ::format #{:jpeg :webp :png})
(s/def ::quality #(< 0 % 101))
(def ^:private schema:thumbnail-params
[:map {:title "ThumbnailParams"}
[:input schema:input]
[:format [:enum :jpeg :webp :png]]
[:quality [:int {:min 1 :max 100}]]
[:width :int]
[:height :int]])
(s/def ::thumbnail-params
(s/keys :req-un [::input ::format ::width ::height]))
(def ^:private check-thumbnail-params
(sm/check-fn schema:thumbnail-params))
;; Related info on how thumbnails generation
;; http://www.imagemagick.org/Usage/thumbnails/
@@ -129,30 +156,38 @@
:data tmp)))
(defmethod process :generic-thumbnail
[{:keys [quality width height] :as params}]
(us/assert ::thumbnail-params params)
(let [op (doto (IMOperation.)
(.addImage)
(.autoOrient)
(.strip)
(.thumbnail ^Integer (int width) ^Integer (int height) ">")
(.quality (double quality))
(.addImage))]
(generic-process (assoc params :operation op))))
[params]
(let [{:keys [quality width height] :as params}
(check-thumbnail-params params)
operation
(doto (IMOperation.)
(.addImage)
(.autoOrient)
(.strip)
(.thumbnail ^Integer (int width) ^Integer (int height) ">")
(.quality (double quality))
(.addImage))]
(generic-process (assoc params :operation operation))))
(defmethod process :profile-thumbnail
[{:keys [quality width height] :as params}]
(us/assert ::thumbnail-params params)
(let [op (doto (IMOperation.)
(.addImage)
(.autoOrient)
(.strip)
(.thumbnail ^Integer (int width) ^Integer (int height) "^")
(.gravity "center")
(.extent (int width) (int height))
(.quality (double quality))
(.addImage))]
(generic-process (assoc params :operation op))))
[params]
(let [{:keys [quality width height] :as params}
(check-thumbnail-params params)
operation
(doto (IMOperation.)
(.addImage)
(.autoOrient)
(.strip)
(.thumbnail ^Integer (int width) ^Integer (int height) "^")
(.gravity "center")
(.extent (int width) (int height))
(.quality (double quality))
(.addImage))]
(generic-process (assoc params :operation operation))))
(defn get-basic-info-from-svg
[{:keys [tag attrs] :as data}]
@@ -182,12 +217,28 @@
{:width (int width)
:height (int height)})))]))
(defn- get-dimensions-with-orientation [^String path]
;; Image magick doesn't give info about exif rotation so we use the identify command
;; If we are processing an animated gif we use the first frame with -scene 0
(let [dim-result (sh/sh "identify" "-format" "%w %h\n" path)
orient-result (sh/sh "identify" "-format" "%[EXIF:Orientation]\n" path)]
(if (and (= 0 (:exit dim-result))
(= 0 (:exit orient-result)))
(let [[w h] (-> (:out dim-result)
str/trim
(clojure.string/split #"\s+")
(->> (mapv #(Integer/parseInt %))))
orientation (-> orient-result :out str/trim)]
(case orientation
("6" "8") {:width h :height w} ; Rotated 90 or 270 degrees
{:width w :height h})) ; Normal or unknown orientation
nil)))
(defmethod process :info
[{:keys [input] :as params}]
(us/assert ::input input)
(let [{:keys [path mtype]} input]
(let [{:keys [path mtype] :as input} (check-input input)]
(if (= mtype "image/svg+xml")
(let [info (some-> path slurp csvg/parse get-basic-info-from-svg)]
(let [info (some-> path slurp parse-svg get-basic-info-from-svg)]
(when-not info
(ex/raise :type :validation
:code :invalid-svg-file
@@ -202,13 +253,17 @@
:code :media-type-mismatch
:hint (str "Seems like you are uploading a file whose content does not match the extension."
"Expected: " mtype ". Got: " mtype')))
;; For an animated GIF, getImageWidth/Height returns the delta size of one frame (if no frame given
;; it returns size of the last one), whereas getPageWidth/Height always return the full size of
;; any frame.
(assoc input
:width (.getPageWidth instance)
:height (.getPageHeight instance)
:ts (dt/now))))))
(let [{:keys [width height]}
(or (get-dimensions-with-orientation (str path))
(do
(l/warn "Failed to read image dimensions with orientation; falling back to im4java"
{:path path})
{:width (.getPageWidth instance)
:height (.getPageHeight instance)}))]
(assoc input
:width width
:height height
:ts (dt/now)))))))
(defmethod process-error org.im4java.core.InfoException
[error]

View File

@@ -92,9 +92,9 @@
[:string {:max 250}]
[::sm/one-of {:format "string"} valid-event-types]]]
[:props
[:map-of :keyword :any]]
[:map-of :keyword ::sm/any]]
[:context {:optional true}
[:map-of :keyword :any]]])
[:map-of :keyword ::sm/any]]])
(def schema:push-audit-events
[:map {:title "push-audit-events"}

View File

@@ -231,19 +231,21 @@
:hint "email has complaint reports")))
(defn prepare-register
[{:keys [::db/pool] :as cfg} {:keys [email] :as params}]
[{:keys [::db/pool] :as cfg} {:keys [fullname email accept-newsletter-updates] :as params}]
(validate-register-attempt! cfg params)
(let [email (profile/clean-email email)
profile (profile/get-profile-by-email pool email)
params {:email email
:fullname fullname
:password (:password params)
:invitation-token (:invitation-token params)
:backend "penpot"
:iss :prepared-register
:profile-id (:id profile)
:exp (dt/in-future {:days 7})}
:exp (dt/in-future {:days 7})
:props {:newsletter-updates (or accept-newsletter-updates false)}}
params (d/without-nils params)
token (tokens/generate (::setup/props cfg) params)]
@@ -253,8 +255,10 @@
(def schema:prepare-register-profile
[:map {:title "prepare-register-profile"}
[:fullname ::sm/text]
[:email ::sm/email]
[:password schema:password]
[:create-welcome-file {:optional true} :boolean]
[:invitation-token {:optional true} schema:token]])
(sv/defmethod ::prepare-register-profile
@@ -358,13 +362,9 @@
:extra-data ptoken})))
(defn register-profile
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token fullname theme] :as params}]
(let [theme (when (= theme "light") theme)
claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register})
params (-> claims
(into params)
(assoc :fullname fullname)
(assoc :theme theme))
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token] :as params}]
(let [claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register})
params (into claims params)
profile (if-let [profile-id (:profile-id claims)]
(profile/get-profile conn profile-id)
@@ -478,10 +478,7 @@
(def schema:register-profile
[:map {:title "register-profile"}
[:token schema:token]
[:fullname [::sm/word-string {:max 100}]]
[:theme {:optional true} [:string {:max 10}]]
[:create-welcome-file {:optional true} :boolean]])
[:token schema:token]])
(sv/defmethod ::register-profile
{::rpc/auth false

View File

@@ -115,7 +115,8 @@
(db/update! pool :project
{:modified-at (dt/now)}
{:id project-id})
{:id project-id}
{::db/return-keys false})
result))
@@ -133,11 +134,18 @@
::webhooks/event? true
::sse/stream? true
::sm/params schema:import-binfile}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version] :as params}]
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version file] :as params}]
(projects/check-edition-permissions! pool profile-id project-id)
(let [params (-> params
(assoc :profile-id profile-id)
(assoc :version (or version 1)))]
(let [version (or version 1)
params (-> params
(assoc :profile-id profile-id)
(assoc :version version))
manifest (case (int version)
1 nil
3 (bf.v3/get-manifest (:path file)))]
(with-meta
(sse/response (partial import-binfile cfg params))
{::audit/props {:file nil}})))
{::audit/props {:file nil
:generated-by (:generated-by manifest)
:referer (:referer manifest)}})))

View File

@@ -6,6 +6,7 @@
(ns app.rpc.commands.files
(:require
[app.binfile.common :as bfc]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
@@ -23,6 +24,7 @@
[app.db.sql :as-alias sql]
[app.features.fdata :as feat.fdata]
[app.features.file-migrations :as feat.fmigr]
[app.features.logical-deletion :as ldel]
[app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as-alias webhooks]
[app.rpc :as-alias rpc]
@@ -189,7 +191,7 @@
[:is-shared ::sm/boolean]
[:project-id ::sm/uuid]
[:created-at ::dt/instant]
[:data {:optional true} :any]])
[:data {:optional true} ::sm/any]])
(def schema:permissions-mixin
[:map {:title "PermissionsMixin"}
@@ -208,10 +210,11 @@
[:project-id {:optional true} ::sm/uuid]])
(defn- migrate-file
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
[{:keys [::db/conn] :as cfg} {:keys [id] :as file} {:keys [read-only?]}]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)]
(let [;; For avoid unnecesary overhead of creating multiple pointers and
(let [libs (delay (bfc/get-resolved-file-libraries cfg file))
;; For avoid unnecesary overhead of creating multiple pointers and
;; handly internally with objects map in their worst case (when
;; probably all shapes and all pointers will be readed in any
;; case), we just realize/resolve them before applying the
@@ -219,44 +222,45 @@
file (-> file
(update :data feat.fdata/process-pointers deref)
(update :data feat.fdata/process-objects (partial into {}))
(fmg/migrate-file))
(fmg/migrate-file libs))]
;; When file is migrated, we break the rule of no perform
;; mutations on get operations and update the file with all
;; migrations applied
;;
;; WARN: he following code will not work on read-only mode,
;; it is a known issue; we keep is not implemented until we
;; really need this.
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(feat.fdata/enable-pointer-map file)
file)]
(if (or read-only? (db/read-only? conn))
file
(let [;; When file is migrated, we break the rule of no perform
;; mutations on get operations and update the file with all
;; migrations applied
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(feat.fdata/enable-pointer-map file)
file)]
(db/update! conn :file
{:data (blob/encode (:data file))
:version (:version file)
:modified-at (dt/now)
:features (db/create-array conn "text" (:features file))}
{:id id})
(db/update! conn :file
{:data (blob/encode (:data file))
:version (:version file)
:features (db/create-array conn "text" (:features file))}
{:id id}
{::db/return-keys false})
(when (contains? (:features file) "fdata/pointer-map")
(feat.fdata/persist-pointers! cfg id))
(when (contains? (:features file) "fdata/pointer-map")
(feat.fdata/persist-pointers! cfg id))
(feat.fmigr/upsert-migrations! conn file)
(feat.fmigr/resolve-applied-migrations cfg file))))
(feat.fmigr/upsert-migrations! conn file)
(feat.fmigr/resolve-applied-migrations cfg file))))))
(defn get-file
[{:keys [::db/conn ::wrk/executor] :as cfg} id
& {:keys [project-id
migrate?
include-deleted?
lock-for-update?]
lock-for-update?
preload-pointers?]
:or {include-deleted? false
lock-for-update? false
migrate? true}}]
migrate? true
preload-pointers? false}
:as options}]
(assert (db/connection? conn) "expected cfg with valid connection")
@@ -274,10 +278,16 @@
;; because it has heavy and synchronous operations for
;; decoding file body that are not very friendly with virtual
;; threads.
file (px/invoke! executor #(decode-row file))]
file (px/invoke! executor #(decode-row file))
file (if (and migrate? (fmg/need-migration? file))
(migrate-file cfg file options)
file)]
(if preload-pointers?
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(update file :data feat.fdata/process-pointers deref))
(if (and migrate? (fmg/need-migration? file))
(migrate-file cfg file)
file)))
(defn get-minimal-file
@@ -293,7 +303,7 @@
(defn get-file-etag
[{:keys [::rpc/profile-id]} {:keys [modified-at revn vern permissions]}]
(str profile-id "/" revn "/" vern "/"
(str profile-id "/" revn "/" vern "/" (hash fmg/available-migrations) "/"
(dt/format-instant modified-at :iso)
"/"
(uri/map->query-string permissions)))
@@ -475,7 +485,7 @@
(update page :objects update-vals #(dissoc % :thumbnail)))
(defn get-page
[{:keys [::db/conn] :as cfg} {:keys [profile-id file-id page-id object-id] :as params}]
[{:keys [::db/conn] :as cfg} {:keys [profile-id file-id page-id object-id share-id] :as params}]
(when (and (uuid? object-id)
(not (uuid? page-id)))
@@ -483,22 +493,30 @@
:code :params-validation
:hint "page-id is required when object-id is provided"))
(let [team (teams/get-team conn
:profile-id profile-id
:file-id file-id)
(let [perms (get-permissions conn profile-id file-id share-id)
file (get-file cfg file-id)
file (get-file cfg file-id :read-only? true)
_ (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
(cfeat/check-file-features! (:features file)))
proj (db/get conn :project {:id (:project-id file)})
page (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(let [page-id (or page-id (-> file :data :pages first))
page (dm/get-in file [:data :pages-index page-id])]
(if (pmap/pointer-map? page)
(deref page)
page)))]
team (-> (db/get conn :team {:id (:team-id proj)})
(teams/decode-row))
_ (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
(cfeat/check-file-features! (:features file)))
page (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(let [page-id (or page-id (-> file :data :pages first))
page (dm/get-in file [:data :pages-index page-id])]
(if (pmap/pointer-map? page)
(deref page)
page)))]
(when-not perms
(ex/raise :type :not-found
:code :object-not-found
:hint "object not found"))
(cond-> (prune-thumbnails page)
(some? object-id)
@@ -541,7 +559,10 @@
f.project_id,
f.created_at,
f.modified_at,
f.data_backend,
f.data_ref_id,
f.name,
f.version,
f.is_shared,
ft.media_id,
p.team_id
@@ -577,7 +598,11 @@
(teams/check-read-permissions! conn profile-id team-id)
(->> (db/exec! conn [sql:team-shared-files team-id])
(into #{} (comp
(map decode-row)
;; NOTE: this decode operation is a workaround for a
;; fast fix, this should be approached with a more
;; efficient implementation, for now it loads all
;; the files in memory.
(map (partial bfc/decode-file cfg))
(map (fn [row]
(if-let [media-id (:media-id row)]
(-> row
@@ -600,44 +625,6 @@
;; --- COMMAND QUERY: get-file-libraries
(def ^:private sql:get-file-libraries
"WITH RECURSIVE libs AS (
SELECT fl.*, flr.synced_at
FROM file AS fl
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
WHERE flr.file_id = ?::uuid
UNION
SELECT fl.*, flr.synced_at
FROM file AS fl
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
JOIN libs AS l ON (flr.file_id = l.id)
)
SELECT l.id,
l.features,
l.project_id,
p.team_id,
l.created_at,
l.modified_at,
l.deleted_at,
l.name,
l.revn,
l.vern,
l.synced_at,
l.is_shared
FROM libs AS l
INNER JOIN project AS p ON (p.id = l.project_id)
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
(defn get-file-libraries
[conn file-id]
(into []
(comp
;; FIXME: :is-indirect set to false to all rows looks
;; completly useless
(map #(assoc % :is-indirect false))
(map decode-row))
(db/exec! conn [sql:get-file-libraries file-id])))
(def ^:private schema:get-file-libraries
[:map {:title "get-file-libraries"}
[:file-id ::sm/uuid]])
@@ -649,7 +636,7 @@
[{: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)
(get-file-libraries conn file-id)))
(bfc/get-file-libraries conn file-id)))
;; --- COMMAND QUERY: Files that use this File library
@@ -734,7 +721,9 @@
:project-id project-id
:file-id id)
file (get-file cfg id :project-id project-id)]
file (get-file cfg id
:project-id project-id
:read-only? true)]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
@@ -953,12 +942,13 @@
;; --- MUTATION COMMAND: delete-file
(defn- mark-file-deleted
[conn file-id]
(let [file (db/update! conn :file
{:deleted-at (dt/now)}
{:id file-id}
{::db/return-keys [:id :name :is-shared :deleted-at
:project-id :created-at :modified-at]})]
[conn team file-id]
(let [delay (ldel/get-deletion-delay team)
file (db/update! conn :file
{:deleted-at (dt/in-future delay)}
{:id file-id}
{::db/return-keys [:id :name :is-shared :deleted-at
:project-id :created-at :modified-at]})]
(wrk/submit! {::db/conn conn
::wrk/task :delete-object
::wrk/params {:object :file
@@ -974,7 +964,11 @@
(defn- delete-file
[{:keys [::db/conn] :as cfg} {:keys [profile-id id] :as params}]
(check-edition-permissions! conn profile-id id)
(let [file (mark-file-deleted conn id)]
(let [team (teams/get-team conn
:profile-id profile-id
:file-id id)
file (mark-file-deleted conn team id)]
(rph/with-meta (rph/wrap)
{::audit/props {:project-id (:project-id file)
:name (:name file)

View File

@@ -55,8 +55,8 @@
:features features
:ignore-sync-until ignore-sync-until
:modified-at modified-at
:deleted-at deleted-at
:create-page create-page
:deleted-at deleted-at}
{:create-page create-page
:page-id page-id})
file (-> (bfc/insert-file! cfg file)
(bfc/decode-row))]
@@ -111,18 +111,21 @@
::quotes/profile-id profile-id
::quotes/project-id project-id})
;; FIXME: IMPORTANT: this code can have race
;; conditions, because we have no locks for updating
;; team so, creating two files concurrently can lead
;; to lost team features updating
;; FIXME: IMPORTANT: this code can have race conditions, because
;; we have no locks for updating team so, creating two files
;; concurrently can lead to lost team features updating
;; When newly computed features does not match exactly with
;; the features defined on team row, we update it
(when (not= features (:features team))
(let [features (db/create-array conn "text" features)]
(when-let [features (-> features
(set/difference (:features team))
(set/difference cfeat/no-team-inheritable-features)
(not-empty))]
(let [features (->> features
(set/union (:features team))
(db/create-array conn "text"))]
(db/update! conn :team
{:features features}
{:id team-id})))
{:id (:id team)}
{::db/return-keys false})))
(-> (create-file cfg params)
(vary-meta assoc ::audit/props {:team-id team-id}))))

View File

@@ -14,7 +14,6 @@
[app.config :as cf]
[app.db :as db]
[app.db.sql :as sql]
[app.features.components-v2 :as feat.compv2]
[app.features.fdata :as fdata]
[app.loggers.audit :as audit]
[app.rpc :as-alias rpc]
@@ -110,7 +109,7 @@
;; --- MUTATION COMMAND: persist-temp-file
(defn persist-temp-file
[{:keys [::db/conn] :as cfg} {:keys [id ::rpc/profile-id] :as params}]
[{:keys [::db/conn] :as cfg} {:keys [id] :as params}]
(let [file (files/get-file cfg id
:migrate? false
:lock-for-update? true)]
@@ -119,7 +118,6 @@
(ex/raise :type :validation
:code :cant-persist-already-persisted-file))
(let [changes (->> (db/cursor conn
(sql/select :file-change {:file-id id}
{:order-by [[:revn :asc]]})
@@ -147,19 +145,6 @@
:revn 1
:data (blob/encode (:data file))}
{:id id})
(let [team (teams/get-team conn :profile-id profile-id :project-id (:project-id file))
file-features (:features file)
team-features (cfeat/get-team-enabled-features cf/flags team)]
(when (and (contains? team-features "components/v2")
(not (contains? file-features "components/v2")))
;; Migrate components v2
(feat.compv2/migrate-file! cfg
(:id file)
:max-procs 2
:validate? true
:throw-on-validate? true)))
nil)))
(def ^:private schema:persist-temp-file

View File

@@ -10,7 +10,6 @@
[app.common.data.macros :as dm]
[app.common.features :as cfeat]
[app.common.files.helpers :as cfh]
[app.common.files.migrations :as fmg]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.thumbnails :as thc]
@@ -18,7 +17,6 @@
[app.config :as cf]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.fdata :as feat.fdata]
[app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as-alias webhooks]
[app.media :as media]
@@ -200,14 +198,13 @@
(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)
(let [team (teams/get-team conn
:profile-id profile-id
:file-id file-id)
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(-> (files/get-file cfg file-id :migrate? false)
(update :data feat.fdata/process-pointers deref)
(fmg/migrate-file)))]
file (files/get-file cfg file-id
:preload-pointers? true
:read-only? true)]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-file-features! (:features file)))

View File

@@ -20,6 +20,7 @@
[app.db :as db]
[app.features.fdata :as feat.fdata]
[app.features.file-migrations :as feat.fmigr]
[app.features.logical-deletion :as ldel]
[app.http.errors :as errors]
[app.loggers.audit :as audit]
[app.loggers.webhooks :as webhooks]
@@ -177,12 +178,19 @@
:stored-revn (:revn file)}))
;; When newly computed features does not match exactly with
;; the features defined on team row, we update it.
(when (not= features (:features team))
(let [features (db/create-array conn "text" features)]
;; the features defined on team row, we update it
(when-let [features (-> features
(set/difference (:features team))
(set/difference cfeat/no-team-inheritable-features)
(not-empty))]
(let [features (->> features
(set/union (:features team))
(db/create-array conn "text"))]
(db/update! conn :team
{:features features}
{:id (:id team)})))
{:id (:id team)}
{::db/return-keys false})))
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
@@ -202,7 +210,7 @@
Only intended for internal use on this module."
[{:keys [::db/conn ::wrk/executor ::timestamp] :as cfg}
{:keys [profile-id file features changes session-id skip-validate] :as params}]
{:keys [profile-id file team features changes session-id skip-validate] :as params}]
(let [;; Retrieve the file data
file (feat.fmigr/resolve-applied-migrations cfg file)
@@ -236,7 +244,7 @@
:created-at timestamp
:updated-at timestamp
:deleted-at (if (::snapshot-data file)
(dt/plus timestamp (cf/get-deletion-delay))
(dt/plus timestamp (ldel/get-deletion-delay team))
(dt/plus timestamp (dt/duration {:hours 1})))
:file-id (:id file)
:revn (:revn file)
@@ -333,6 +341,7 @@
(-> data
(blob/decode)
(assoc :id (:id file)))))
libs (delay (bfc/get-resolved-file-libraries cfg file))
;; For avoid unnecesary overhead of creating multiple pointers
;; and handly internally with objects map in their worst
@@ -343,7 +352,7 @@
(-> file
(update :data feat.fdata/process-pointers deref)
(update :data feat.fdata/process-objects (partial into {}))
(fmg/migrate-file))
(fmg/migrate-file libs))
file)
file (apply update-fn cfg file args)
@@ -372,13 +381,6 @@
(bfc/encode-file cfg file))))
(defn- get-file-libraries
"A helper for preload file libraries, mainly used for perform file
semantical and structural validation"
[{:keys [::db/conn] :as cfg} file]
(->> (files/get-file-libraries conn (:id file))
(into [file] (map #(bfc/get-file cfg (:id %))))
(d/index-by :id)))
(defn- soft-validate-file-schema!
[file]
@@ -404,7 +406,7 @@
(when (and (or (contains? cf/flags :file-validation)
(contains? cf/flags :soft-file-validation))
(not skip-validate))
(get-file-libraries cfg file))
(bfc/get-resolved-file-libraries cfg file))
;; The main purpose of this atom is provide a contextual state

View File

@@ -12,6 +12,7 @@
[app.common.uuid :as uuid]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.logical-deletion :as ldel]
[app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as-alias webhooks]
[app.media :as media]
@@ -80,9 +81,9 @@
(def ^:private schema:create-font-variant
[:map {:title "create-font-variant"}
[:team-id ::sm/uuid]
[:data [:map-of :string :any]]
[:data [:map-of ::sm/text ::sm/any]]
[:font-id ::sm/uuid]
[:font-family :string]
[:font-family ::sm/text]
[:font-weight [::sm/one-of {:format "number"} valid-weight]]
[:font-style [::sm/one-of {:format "string"} valid-style]]])
@@ -202,32 +203,40 @@
(sv/defmethod ::delete-font
{::doc/added "1.18"
::webhooks/event? true
::sm/params schema:delete-font}
[cfg {:keys [::rpc/profile-id id team-id]}]
(db/tx-run! cfg
(fn [{:keys [::db/conn] :as cfg}]
(teams/check-edition-permissions! conn profile-id team-id)
(let [fonts (db/query conn :team-font-variant
{:team-id team-id
:font-id id
:deleted-at nil}
{::sql/for-update true})
tnow (dt/now)]
::sm/params schema:delete-font
::db/transaction true}
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id team-id]}]
(let [team (teams/get-team conn
:profile-id profile-id
:team-id team-id)
(when-not (seq fonts)
(ex/raise :type :not-found
:code :object-not-found))
fonts (db/query conn :team-font-variant
{:team-id team-id
:font-id id
:deleted-at nil}
{::sql/for-update true})
(doseq [font fonts]
(db/update! conn :team-font-variant
{:deleted-at tnow}
{:id (:id font)}))
delay (ldel/get-deletion-delay team)
tnow (dt/in-future delay)]
(rph/with-meta (rph/wrap)
{::audit/props {:id id
:team-id team-id
:name (:font-family (peek fonts))
:profile-id profile-id}})))))
(teams/check-edition-permissions! (:permissions team))
(when-not (seq fonts)
(ex/raise :type :not-found
:code :object-not-found))
(doseq [font fonts]
(db/update! conn :team-font-variant
{:deleted-at tnow}
{:id (:id font)}
{::db/return-keys false}))
(rph/with-meta (rph/wrap)
{::audit/props {:id id
:team-id team-id
:name (:font-family (peek fonts))
:profile-id profile-id}})))
;; --- DELETE FONT VARIANT
@@ -239,19 +248,23 @@
(sv/defmethod ::delete-font-variant
{::doc/added "1.18"
::webhooks/event? true
::sm/params schema:delete-font-variant}
[cfg {:keys [::rpc/profile-id id team-id]}]
(db/tx-run! cfg
(fn [{:keys [::db/conn] :as cfg}]
(teams/check-edition-permissions! conn profile-id team-id)
(let [variant (db/get conn :team-font-variant
{:id id :team-id team-id}
{::sql/for-update true})]
::sm/params schema:delete-font-variant
::db/transaction true}
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id team-id]}]
(let [team (teams/get-team conn
:profile-id profile-id
:team-id team-id)
variant (db/get conn :team-font-variant
{:id id :team-id team-id}
{::sql/for-update true})
delay (ldel/get-deletion-delay team)]
(db/update! conn :team-font-variant
{:deleted-at (dt/now)}
{:id (:id variant)})
(teams/check-edition-permissions! (:permissions team))
(db/update! conn :team-font-variant
{:deleted-at (dt/in-future delay)}
{:id (:id variant)}
{::db/return-keys false})
(rph/with-meta (rph/wrap)
{::audit/props {:font-family (:font-family variant)
:font-id (:font-id variant)}})))))
(rph/with-meta (rph/wrap)
{::audit/props {:font-family (:font-family variant)
:font-id (:font-id variant)}})))

View File

@@ -56,7 +56,7 @@
(vswap! bfc/*state* update :index bfc/update-index fmeds :id)
;; Process and persist file
(let [file (bfc/process-file file)]
(let [file (bfc/process-file cfg file)]
(bfc/insert-file! cfg file ::db/return-keys false)
;; The file profile creation is optional, so when no profile is

View File

@@ -480,8 +480,7 @@
JOIN team AS t ON (t.id = tpr.team_id)
WHERE tpr.is_owner IS TRUE
AND tpr.profile_id = ?
AND (t.deleted_at IS NULL OR
t.deleted_at > now())
AND t.deleted_at IS NULL
)
SELECT tpr.team_id AS id,
count(tpr.profile_id) - 1 AS participants

View File

@@ -11,6 +11,7 @@
[app.common.schema :as sm]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.logical-deletion :as ldel]
[app.loggers.audit :as-alias audit]
[app.loggers.webhooks :as webhooks]
[app.rpc :as-alias rpc]
@@ -253,9 +254,10 @@
;; --- MUTATION: Delete Project
(defn- delete-project
[conn project-id]
(let [project (db/update! conn :project
{:deleted-at (dt/now)}
[conn team project-id]
(let [delay (ldel/get-deletion-delay team)
project (db/update! conn :project
{:deleted-at (dt/in-future delay)}
{:id project-id}
{::db/return-keys true})]
@@ -272,7 +274,6 @@
project))
(def ^:private schema:delete-project
[:map {:title "delete-project"}
[:id ::sm/uuid]])
@@ -284,7 +285,10 @@
::db/transaction true}
[{:keys [::db/conn]} {:keys [::rpc/profile-id id] :as params}]
(check-edition-permissions! conn profile-id id)
(let [project (delete-project conn id)]
(let [team (teams/get-team conn
:profile-id profile-id
:project-id id)
project (delete-project conn team id)]
(rph/with-meta (rph/wrap)
{::audit/props {:team-id (:team-id project)
:name (:name project)

View File

@@ -17,6 +17,7 @@
[app.db :as db]
[app.db.sql :as sql]
[app.email :as eml]
[app.features.logical-deletion :as ldel]
[app.loggers.audit :as audit]
[app.main :as-alias main]
[app.media :as media]
@@ -76,9 +77,10 @@
(perms/make-check-fn has-read-permissions?))
(defn decode-row
[{:keys [features] :as row}]
[{:keys [features subscription] :as row}]
(cond-> row
(some? features) (assoc :features (db/decode-pgarray features #{}))))
(some? features) (assoc :features (db/decode-pgarray features #{}))
(some? subscription) (assoc :subscription (db/decode-transit-pgobject subscription))))
;; FIXME: move
@@ -113,29 +115,42 @@
;; --- Query: Teams
(declare get-teams)
(def ^:private schema:get-teams
[:map {:title "get-teams"}])
(sv/defmethod ::get-teams
{::doc/added "1.17"
::sm/params schema:get-teams}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
(dm/with-open [conn (db/open pool)]
(get-teams conn profile-id)))
(def sql:get-teams-with-permissions
"select t.*,
"SELECT t.*,
tp.is_owner,
tp.is_admin,
tp.can_edit,
(t.id = ?) as is_default
from team_profile_rel as tp
join team as t on (t.id = tp.team_id)
where t.deleted_at is null
and tp.profile_id = ?
order by tp.created_at asc")
(t.id = ?) AS is_default
FROM team_profile_rel AS tp
JOIN team AS t ON (t.id = tp.team_id)
WHERE t.deleted_at IS null
AND tp.profile_id = ?
ORDER BY tp.created_at ASC")
(def sql:get-teams-with-permissions-and-subscription
"SELECT t.*,
tp.is_owner,
tp.is_admin,
tp.can_edit,
(t.id = ?) AS is_default,
jsonb_build_object(
'~:type', COALESCE(p.props->'~:subscription'->>'~:type', 'professional'),
'~:status', CASE COALESCE(p.props->'~:subscription'->>'~:type', 'professional')
WHEN 'professional' THEN 'active'
ELSE COALESCE(p.props->'~:subscription'->>'~:status', 'incomplete')
END,
'~:seats', p.props->'~:subscription'->'~:quantity'
) AS subscription
FROM team_profile_rel AS tp
JOIN team AS t ON (t.id = tp.team_id)
JOIN team_profile_rel AS tpr
ON (tpr.team_id = t.id AND tpr.is_owner IS true)
JOIN profile AS p
ON (tpr.profile_id = p.id)
WHERE t.deleted_at IS null
AND tp.profile_id = ?
ORDER BY tp.created_at ASC")
(defn process-permissions
[team]
@@ -150,13 +165,53 @@
(dissoc :is-owner :is-admin :can-edit)
(assoc :permissions permissions))))
(def ^:private
xform:process-teams
(comp
(map decode-row)
(map process-permissions)))
(defn get-teams
[conn profile-id]
(let [profile (profile/get-profile conn profile-id)]
(->> (db/exec! conn [sql:get-teams-with-permissions (:default-team-id profile) profile-id])
(map decode-row)
(map process-permissions)
(vec))))
(let [profile (profile/get-profile conn profile-id)
sql (if (contains? cf/flags :subscriptions)
sql:get-teams-with-permissions-and-subscription
sql:get-teams-with-permissions)]
(->> (db/exec! conn [sql (:default-team-id profile) profile-id])
(into [] xform:process-teams))))
(def ^:private schema:get-teams
[:map {:title "get-teams"}])
(sv/defmethod ::get-teams
{::doc/added "1.17"
::sm/params schema:get-teams}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
(dm/with-open [conn (db/open pool)]
(get-teams conn profile-id)))
(def ^:private sql:get-owned-teams
"SELECT t.id, t.name,
(SELECT count(*) FROM team_profile_rel WHERE team_id=t.id) AS total_members,
(SELECT count(*) FROM team_profile_rel WHERE team_id=t.id AND can_edit=true) AS total_editors
FROM team AS t
JOIN team_profile_rel AS tpr ON (tpr.team_id = t.id)
WHERE t.is_default IS false
AND tpr.is_owner IS true
AND tpr.profile_id = ?
AND t.deleted_at IS NULL")
(defn- get-owned-teams
[cfg profile-id]
(->> (db/exec! cfg [sql:get-owned-teams profile-id])
(into [] (map decode-row))))
(sv/defmethod ::get-owned-teams
{::doc/added "2.8.0"
::sm/params schema:get-teams}
[cfg {:keys [::rpc/profile-id]}]
(get-owned-teams cfg profile-id))
;; --- Query: Team (by ID)
@@ -181,39 +236,43 @@
(defn get-team
[conn & {:keys [profile-id team-id project-id file-id] :as params}]
(dm/assert!
"connection or pool is mandatory"
(or (db/connection? conn)
(db/pool? conn)))
(assert (uuid? profile-id) "profile-id is mandatory")
(assert (or (db/connection? conn)
(db/pool? conn))
"connection or pool is mandatory")
(dm/assert!
"profile-id is mandatory"
(uuid? profile-id))
(let [{:keys [default-team-id] :as profile}
(profile/get-profile conn profile-id)
(let [{:keys [default-team-id] :as profile} (profile/get-profile conn profile-id)
result (cond
(some? team-id)
(let [sql (str "WITH teams AS (" sql:get-teams-with-permissions
") SELECT * FROM teams WHERE id=?")]
(db/exec-one! conn [sql default-team-id profile-id team-id]))
sql
(if (contains? cf/flags :subscriptions)
sql:get-teams-with-permissions-and-subscription
sql:get-teams-with-permissions)
(some? project-id)
(let [sql (str "WITH teams AS (" sql:get-teams-with-permissions ") "
"SELECT t.* FROM teams AS t "
" JOIN project AS p ON (p.team_id = t.id) "
" WHERE p.id=?")]
(db/exec-one! conn [sql default-team-id profile-id project-id]))
result
(cond
(some? team-id)
(let [sql (str "WITH teams AS (" sql ") "
"SELECT * FROM teams WHERE id=?")]
(db/exec-one! conn [sql default-team-id profile-id team-id]))
(some? file-id)
(let [sql (str "WITH teams AS (" sql:get-teams-with-permissions ") "
"SELECT t.* FROM teams AS t "
" JOIN project AS p ON (p.team_id = t.id) "
" JOIN file AS f ON (f.project_id = p.id) "
" WHERE f.id=?")]
(db/exec-one! conn [sql default-team-id profile-id file-id]))
(some? project-id)
(let [sql (str "WITH teams AS (" sql ") "
"SELECT t.* FROM teams AS t "
" JOIN project AS p ON (p.team_id = t.id) "
" WHERE p.id=?")]
(db/exec-one! conn [sql default-team-id profile-id project-id]))
:else
(throw (IllegalArgumentException. "invalid arguments")))]
(some? file-id)
(let [sql (str "WITH teams AS (" sql ") "
"SELECT t.* FROM teams AS t "
" JOIN project AS p ON (p.team_id = t.id) "
" JOIN file AS f ON (f.project_id = p.id) "
" WHERE f.id=?")]
(db/exec-one! conn [sql default-team-id profile-id file-id]))
:else
(throw (IllegalArgumentException. "invalid arguments")))]
(when-not result
(ex/raise :type :not-found
@@ -601,13 +660,13 @@
(defn- delete-team
"Mark a team for deletion"
[conn team-id]
[conn {:keys [id] :as team}]
(let [deleted-at (dt/now)
team (db/update! conn :team
{:deleted-at deleted-at}
{:id team-id}
{::db/return-keys true})]
(let [delay (ldel/get-deletion-delay team)
team (db/update! conn :team
{:deleted-at (dt/in-future delay)}
{:id id}
{::db/return-keys true})]
(when (:is-default team)
(ex/raise :type :validation
@@ -617,8 +676,8 @@
(wrk/submit! {::db/conn conn
::wrk/task :delete-object
::wrk/params {:object :team
:deleted-at deleted-at
:id team-id}})
:deleted-at (:deleted-at team)
:id id}})
team))
(def ^:private schema:delete-team
@@ -630,12 +689,14 @@
::sm/params schema:delete-team
::db/transaction true}
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id] :as params}]
(let [perms (get-permissions conn profile-id id)]
(let [team (get-team conn :profile-id profile-id :team-id id)
perms (get team :permissions)]
(when-not (:is-owner perms)
(ex/raise :type :validation
:code :only-owner-can-delete-team))
(delete-team conn id)
(delete-team conn team)
nil))
;; --- Mutation: Team Update Role

View File

@@ -6,6 +6,7 @@
(ns app.rpc.commands.viewer
(:require
[app.binfile.common :as bfc]
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.schema :as sm]
@@ -78,7 +79,7 @@
:always
(update :data select-keys [:id :options :pages :pages-index :components]))
libs (->> (files/get-file-libraries conn file-id)
libs (->> (bfc/get-file-libraries conn file-id)
(mapv (fn [{:keys [id] :as lib}]
(merge lib (files/get-file cfg id)))))

View File

@@ -9,6 +9,7 @@
(:require
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.json :as json]
[app.common.pprint :as pp]
[app.common.schema :as sm]
[app.common.schema.desc-js-like :as smdj]
@@ -19,7 +20,6 @@
[app.http.sse :as-alias sse]
[app.loggers.webhooks :as-alias webhooks]
[app.rpc :as-alias rpc]
[app.util.json :as json]
[app.util.services :as sv]
[app.util.template :as tmpl]
[clojure.java.io :as io]
@@ -86,7 +86,7 @@
(fn [request]
(let [params (:query-params request)
pstyle (:type params "js")
context (assoc context :param-style pstyle)]
context (assoc @context :param-style pstyle)]
{::yres/status 200
::yres/body (-> (io/resource "app/templates/api-doc.tmpl")
@@ -178,8 +178,7 @@
(fn [_]
{::yres/status 200
::yres/headers {"content-type" "application/json; charset=utf-8"}
::yres/body (json/encode context)})
::yres/body (json/encode @context)})
(fn [_]
{::yres/status 404})))
@@ -209,7 +208,7 @@
(defmethod ig/init-key ::routes
[_ {:keys [::rpc/methods] :as cfg}]
[(let [context (prepare-doc-context methods)]
[(let [context (delay (prepare-doc-context methods))]
[["/_doc"
{:handler (doc-handler context)
:allowed-methods #{:get}}]
@@ -217,7 +216,7 @@
{:handler (doc-handler context)
:allowed-methods #{:get}}]])
(let [context (prepare-openapi-context methods)]
(let [context (delay (prepare-openapi-context methods))]
[["/openapi"
{:handler (openapi-handler)
:allowed-methods #{:get}}]

View File

@@ -6,13 +6,17 @@
(ns app.srepl
"Server Repl."
(:refer-clojure :exclude [read-line])
(:require
[app.common.exceptions :as ex]
[app.common.json :as json]
[app.common.logging :as l]
[app.config :as cf]
[app.srepl.cli]
[app.srepl.cli :as cli]
[app.srepl.main]
[app.util.json :as json]
[app.util.locks :as locks]
[app.util.time :as dt]
[clojure.core :as c]
[clojure.core.server :as ccs]
[clojure.main :as cm]
[integrant.core :as ig]))
@@ -28,17 +32,80 @@
:init repl-init
:read ccs/repl-read))
(defn- ex->data
[cause phase]
(let [data (ex-data cause)
explain (ex/explain data)]
(cond-> {:phase phase
:code (get data :code :unknown)
:type (get data :type :unknown)
:hint (or (get data :hint) (ex-message cause))}
(some? explain)
(assoc :explain explain))))
(defn read-line
[]
(if-let [line (c/read-line)]
(try
(l/dbg :hint "decode" :data line)
(json/decode line :key-fn json/read-kebab-key)
(catch Throwable _cause
(l/warn :hint "unable to decode data" :data line)
nil))
::eof))
(defn json-repl
[]
(let [out *out*
lock (locks/create)]
(ccs/prepl *in*
(fn [m]
(binding [*out* out,
*flush-on-newline* true,
*print-readably* true]
(locks/locking lock
(println (json/encode-str m))))))))
(let [lock (locks/create)
out *out*
out-fn
(fn [m]
(locks/locking lock
(binding [*out* out]
(l/warn :hint "write" :data m)
(println (json/encode m :key-fn json/write-camel-key)))))
tapfn
(fn [val]
(out-fn {:tag :tap :val val}))]
(binding [*out* (PrintWriter-on #(out-fn {:tag :out :val %1}) nil true)
*err* (PrintWriter-on #(out-fn {:tag :err :val %1}) nil true)]
(try
(add-tap tapfn)
(loop []
(when (try
(let [data (read-line)
tpoint (dt/tpoint)]
(l/dbg :hint "received" :data (if (= data ::eof) "EOF" data))
(try
(when-not (= data ::eof)
(when-not (nil? data)
(let [result (cli/exec data)
elapsed (tpoint)]
(l/warn :hint "result" :data result)
(out-fn {:tag :ret
:val (if (instance? Throwable result)
(Throwable->map result)
result)
:elapsed (inst-ms elapsed)})))
true)
(catch Throwable cause
(let [elapsed (tpoint)]
(out-fn {:tag :ret
:err (ex->data cause :eval)
:elapsed (inst-ms elapsed)})
true))))
(catch Throwable cause
(out-fn {:tag :ret
:err (ex->data cause :read)})
true))
(recur)))
(finally
(remove-tap tapfn))))))
;; --- State initialization

View File

@@ -9,14 +9,23 @@
(:require
[app.auth :as auth]
[app.common.exceptions :as ex]
[app.common.schema :as sm]
[app.common.uuid :as uuid]
[app.db :as db]
[app.rpc.commands.auth :as cmd.auth]
[app.rpc.commands.profile :as cmd.profile]
[app.util.json :as json]
[app.setup :as-alias setup]
[app.tokens :as tokens]
[app.util.time :as dt]
[cuerdas.core :as str]))
(defn coercer
[schema & {:as opts}]
(let [decode-fn (sm/decoder schema sm/json-transformer)
check-fn (sm/check-fn schema opts)]
(fn [data]
(-> data decode-fn check-fn))))
(defn- get-current-system
[]
(or (deref (requiring-resolve 'app.main/system))
@@ -24,16 +33,21 @@
(defmulti ^:private exec-command ::cmd)
(defmethod exec-command :default
[{:keys [::cmd]}]
(ex/raise :type :internal
:code :not-implemented
:hint (str/ffmt "command '%' not implemented" cmd)))
(defn exec
"Entry point with external tools integrations that uses PREPL
interface for interacting with running penpot backend."
[data]
(let [data (json/decode data)]
(-> {::cmd (keyword (:cmd data "default"))}
(merge (:params data))
(exec-command))))
(-> {::cmd (get data :cmd)}
(merge (:params data))
(exec-command)))
(defmethod exec-command :create-profile
(defmethod exec-command "create-profile"
[{:keys [fullname email password is-active]
:or {is-active true}}]
(some-> (get-current-system)
@@ -49,7 +63,7 @@
(->> (cmd.auth/create-profile! conn params)
(cmd.auth/create-profile-rels! conn)))))))
(defmethod exec-command :update-profile
(defmethod exec-command "update-profile"
[{:keys [fullname email password is-active]}]
(some-> (get-current-system)
(db/tx-run!
@@ -70,7 +84,12 @@
:deleted-at nil})]
(pos? (db/get-update-count res)))))))))
(defmethod exec-command :delete-profile
(defmethod exec-command "echo"
[params]
params)
(defmethod exec-command "delete-profile"
[{:keys [email soft]}]
(when-not email
(ex/raise :type :assertion
@@ -88,7 +107,7 @@
{:email email}))]
(pos? (db/get-update-count res)))))))
(defmethod exec-command :search-profile
(defmethod exec-command "search-profile"
[{:keys [email]}]
(when-not email
(ex/raise :type :assertion
@@ -102,12 +121,130 @@
" where email similar to ? order by created_at desc limit 100")]
(db/exec! conn [sql email]))))))
(defmethod exec-command :derive-password
(defmethod exec-command "derive-password"
[{:keys [password]}]
(auth/derive-password password))
(defmethod exec-command :default
[{:keys [::cmd]}]
(ex/raise :type :internal
:code :not-implemented
:hint (str/ffmt "command '%' not implemented" (name cmd))))
(defmethod exec-command "authenticate"
[{:keys [token]}]
(when-let [system (get-current-system)]
(let [props (get system ::setup/props)]
(tokens/verify props {:token token :iss "authentication"}))))
(def ^:private schema:get-customer
[:map [:id ::sm/uuid]])
(def coerce-get-customer-params
(coercer schema:get-customer
:type :validation
:hint "invalid data provided for `get-customer` rpc call"))
(def sql:get-customer-slots
"WITH teams AS (
SELECT tpr.team_id AS id,
tpr.profile_id AS profile_id
FROM team_profile_rel AS tpr
WHERE tpr.is_owner IS true
AND tpr.profile_id = ?
), teams_with_slots AS (
SELECT tpr.team_id AS id,
count(*) AS total
FROM team_profile_rel AS tpr
WHERE tpr.team_id IN (SELECT id FROM teams)
AND tpr.can_edit IS true
GROUP BY 1
ORDER BY 2
)
SELECT max(total) AS total FROM teams_with_slots;")
(defn- get-customer-slots
[system profile-id]
(let [result (db/exec-one! system [sql:get-customer-slots profile-id])]
(:total result)))
(defmethod exec-command "get-customer"
[params]
(when-let [system (get-current-system)]
(let [{:keys [id] :as params} (coerce-get-customer-params params)
{:keys [props] :as profile} (cmd.profile/get-profile system id)]
{:id (get profile :id)
:name (get profile :fullname)
:email (get profile :email)
:num-editors (get-customer-slots system id)
:subscription (get props :subscription)})))
(def ^:private schema:customer-subscription
[:map {:title "CustomerSubscription"}
[:id ::sm/text]
[:customer-id ::sm/text]
[:type [:enum
"unlimited"
"professional"
"enterprise"]]
[:status [:enum
"active"
"canceled"
"incomplete"
"incomplete_expired"
"pass_due"
"paused"
"trialing"
"unpaid"]]
[:billing-period [:enum
"month"
"day"
"week"
"year"]]
[:quantity :int]
[:description [:maybe ::sm/text]]
[:created-at ::sm/timestamp]
[:start-date [:maybe ::sm/timestamp]]
[:ended-at [:maybe ::sm/timestamp]]
[:trial-end [:maybe ::sm/timestamp]]
[:trial-start [:maybe ::sm/timestamp]]
[:cancel-at [:maybe ::sm/timestamp]]
[:canceled-at [:maybe ::sm/timestamp]]
[:current-period-end ::sm/timestamp]
[:current-period-start ::sm/timestamp]
[:cancel-at-period-end :boolean]
[:cancellation-details
[:map {:title "CancellationDetails"}
[:comment [:maybe ::sm/text]]
[:reason [:maybe ::sm/text]]
[:feedback [:maybe
[:enum
"customer_service"
"low_quality"
"missing_feature"
"other"
"switched_service"
"too_complex"
"too_expensive"
"unused"]]]]]])
(def ^:private schema:update-customer-subscription
[:map
[:id ::sm/uuid]
[:subscription [:maybe schema:customer-subscription]]])
(def coerce-update-customer-subscription-params
(coercer schema:update-customer-subscription
:type :validation
:hint "invalid data provided for `update-customer-subscription` rpc call"))
(defmethod exec-command "update-customer-subscription"
[params]
(when-let [system (get-current-system)]
(let [{:keys [id subscription]} (coerce-update-customer-subscription-params params)
;; FIXME: locking
{:keys [props] :as profile} (cmd.profile/get-profile system id)
props (assoc props :subscription subscription)]
(db/update! system :profile
{:props (db/tjson props)}
{:id id}
{::db/return-keys false})
true)))

View File

@@ -1,306 +0,0 @@
;; 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.srepl.components-v2
(:require
[app.common.fressian :as fres]
[app.common.logging :as l]
[app.db :as db]
[app.features.components-v2 :as feat]
[app.main :as main]
[app.srepl.helpers :as h]
[app.util.events :as events]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[datoteka.fs :as fs]
[datoteka.io :as io]
[promesa.exec :as px]
[promesa.exec.semaphore :as ps]
[promesa.util :as pu]))
(def ^:dynamic *scope* nil)
(def ^:dynamic *semaphore* nil)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRIVATE HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private sql:get-files-by-created-at
"SELECT id, features,
row_number() OVER (ORDER BY created_at DESC) AS rown
FROM file
WHERE deleted_at IS NULL
ORDER BY created_at DESC")
(defn- get-files
[conn]
(->> (db/cursor conn [sql:get-files-by-created-at] {:chunk-size 500})
(map feat/decode-row)
(remove (fn [{:keys [features]}]
(contains? features "components/v2")))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PUBLIC API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn migrate-file!
[file-id & {:keys [rollback? validate? label cache skip-on-graphic-error?]
:or {rollback? true
validate? false
skip-on-graphic-error? true}}]
(l/dbg :hint "migrate:start" :rollback rollback?)
(let [tpoint (dt/tpoint)
file-id (h/parse-uuid file-id)]
(binding [feat/*stats* (atom {})
feat/*cache* cache]
(try
(-> (assoc main/system ::db/rollback rollback?)
(feat/migrate-file! file-id
:validate? validate?
:skip-on-graphic-error? skip-on-graphic-error?
:label label))
(-> (deref feat/*stats*)
(assoc :elapsed (dt/format-duration (tpoint))))
(catch Throwable cause
(l/wrn :hint "migrate:error" :cause cause))
(finally
(let [elapsed (dt/format-duration (tpoint))]
(l/dbg :hint "migrate:end" :rollback rollback? :elapsed elapsed)))))))
(defn migrate-team!
[team-id & {:keys [rollback? skip-on-graphic-error? validate? label cache]
:or {rollback? true
validate? true
skip-on-graphic-error? true}}]
(l/dbg :hint "migrate:start" :rollback rollback?)
(let [team-id (h/parse-uuid team-id)
stats (atom {})
tpoint (dt/tpoint)]
(binding [feat/*stats* stats
feat/*cache* cache]
(try
(-> (assoc main/system ::db/rollback rollback?)
(feat/migrate-team! team-id
:label label
:validate? validate?
:skip-on-graphics-error? skip-on-graphic-error?))
(-> (deref feat/*stats*)
(assoc :elapsed (dt/format-duration (tpoint))))
(catch Throwable cause
(l/dbg :hint "migrate:error" :cause cause))
(finally
(let [elapsed (dt/format-duration (tpoint))]
(l/dbg :hint "migrate:end" :rollback rollback? :elapsed elapsed)))))))
(defn migrate-files!
"A REPL helper for migrate all files.
This function starts multiple concurrent file migration processes
until thw maximum number of jobs is reached which by default has the
value of `1`. This is controled with the `:max-jobs` option.
If you want to run this on multiple machines you will need to specify
the total number of partitions and the current partition.
In order to get the report table populated, you will need to provide
a correct `:label`. That label is also used for persist a file
snaphot before continue with the migration."
[& {:keys [max-jobs max-items rollback? validate?
cache skip-on-graphic-error?
label partitions current-partition]
:or {validate? false
rollback? true
max-jobs 1
current-partition 1
skip-on-graphic-error? true
max-items Long/MAX_VALUE}}]
(when (int? partitions)
(when-not (int? current-partition)
(throw (IllegalArgumentException. "missing `current-partition` parameter")))
(when-not (<= 0 current-partition partitions)
(throw (IllegalArgumentException. "invalid value on `current-partition` parameter"))))
(let [stats (atom {})
tpoint (dt/tpoint)
factory (px/thread-factory :virtual false :prefix "penpot/migration/")
executor (px/cached-executor :factory factory)
sjobs (ps/create :permits max-jobs)
migrate-file
(fn [file-id rown]
(try
(db/tx-run! (assoc main/system ::db/rollback rollback?)
(fn [system]
(db/exec-one! system ["SET LOCAL idle_in_transaction_session_timeout = 0"])
(feat/migrate-file! system file-id
:rown rown
:label label
:validate? validate?
:skip-on-graphic-error? skip-on-graphic-error?)))
(catch Throwable cause
(l/wrn :hint "unexpected error on processing file (skiping)"
:file-id (str file-id))
(events/tap :error
(ex-info "unexpected error on processing file (skiping)"
{:file-id file-id}
cause))
(swap! stats update :errors (fnil inc 0)))
(finally
(ps/release! sjobs))))
process-file
(fn [{:keys [id rown]}]
(ps/acquire! sjobs)
(px/run! executor (partial migrate-file id rown)))]
(l/dbg :hint "migrate:start"
:label label
:rollback rollback?
:max-jobs max-jobs
:max-items max-items)
(binding [feat/*stats* stats
feat/*cache* cache]
(try
(db/tx-run! main/system
(fn [{:keys [::db/conn] :as system}]
(db/exec! conn ["SET LOCAL statement_timeout = 0"])
(db/exec! conn ["SET LOCAL idle_in_transaction_session_timeout = 0"])
(run! process-file
(->> (get-files conn)
(filter (fn [{:keys [rown] :as row}]
(if (int? partitions)
(= current-partition (inc (mod rown partitions)))
true)))
(take max-items)))
;; Close and await tasks
(pu/close! executor)))
(-> (deref stats)
(assoc :elapsed (dt/format-duration (tpoint))))
(catch Throwable cause
(l/dbg :hint "migrate:error" :cause cause)
(events/tap :error cause))
(finally
(let [elapsed (dt/format-duration (tpoint))]
(l/dbg :hint "migrate:end"
:rollback rollback?
:elapsed elapsed)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CACHE POPULATE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def sql:sobjects-for-cache
"SELECT id,
row_number() OVER (ORDER BY created_at) AS index
FROM storage_object
WHERE (metadata->>'~:bucket' = 'file-media-object' OR
metadata->>'~:bucket' IS NULL)
AND metadata->>'~:content-type' = 'image/svg+xml'
AND deleted_at IS NULL
AND size < 1135899
ORDER BY created_at ASC")
(defn populate-cache!
"A REPL helper for migrate all files.
This function starts multiple concurrent file migration processes
until thw maximum number of jobs is reached which by default has the
value of `1`. This is controled with the `:max-jobs` option.
If you want to run this on multiple machines you will need to specify
the total number of partitions and the current partition.
In order to get the report table populated, you will need to provide
a correct `:label`. That label is also used for persist a file
snaphot before continue with the migration."
[& {:keys [max-jobs] :or {max-jobs 1}}]
(let [tpoint (dt/tpoint)
factory (px/thread-factory :virtual false :prefix "penpot/cache/")
executor (px/cached-executor :factory factory)
sjobs (ps/create :permits max-jobs)
retrieve-sobject
(fn [id index]
(let [path (feat/get-sobject-cache-path id)
parent (fs/parent path)]
(try
(when-not (fs/exists? parent)
(fs/create-dir parent))
(if (fs/exists? path)
(l/inf :hint "create cache entry" :status "exists" :index index :id (str id) :path (str path))
(let [svg-data (feat/get-optimized-svg id)]
(with-open [^java.lang.AutoCloseable stream (io/output-stream path)]
(let [writer (fres/writer stream)]
(fres/write! writer svg-data)))
(l/inf :hint "create cache entry" :status "created"
:index index
:id (str id)
:path (str path))))
(catch Throwable cause
(l/wrn :hint "create cache entry"
:status "error"
:index index
:id (str id)
:path (str path)
:cause cause))
(finally
(ps/release! sjobs)))))
process-sobject
(fn [{:keys [id index]}]
(ps/acquire! sjobs)
(px/run! executor (partial retrieve-sobject id index)))]
(l/dbg :hint "migrate:start"
:max-jobs max-jobs)
(try
(binding [feat/*system* main/system]
(run! process-sobject
(db/exec! main/system [sql:sobjects-for-cache]))
;; Close and await tasks
(pu/close! executor))
{:elapsed (dt/format-duration (tpoint))}
(catch Throwable cause
(l/dbg :hint "populate:error" :cause cause))
(finally
(let [elapsed (dt/format-duration (tpoint))]
(l/dbg :hint "populate:end"
:elapsed elapsed))))))

View File

@@ -179,7 +179,7 @@
component-child (first component-children)]
(if (or (nil? child) (nil? component-child))
container
(let [container (if (and (not (ctk/is-main-of? component-child child true))
(let [container (if (and (not (ctk/is-main-of? component-child child))
(nil? (ctk/get-swap-slot child))
(ctk/instance-head? child))
(let [slot (guess-swap-slot component-child component-container)]

View File

@@ -0,0 +1,88 @@
;; 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.srepl.fixes.lost-colors
"A collection of adhoc fixes scripts."
(:require
[app.binfile.common :as bfc]
[app.common.logging :as l]
[app.common.types.color :as types.color]
[app.db :as db]
[app.srepl.helpers :as h]))
(def sql:get-affected-files
"SELECT fm.file_id AS id FROM file_migration AS fm WHERE fm.name = '0008-fix-library-colors-v2'")
(def sql:get-matching-snapshot
"SELECT * FROM file_change
WHERE file_id = ?
AND created_at <= ?
AND label IS NOT NULL
AND data IS NOT NULL
ORDER BY created_at DESC
LIMIT 2")
(defn get-affected-migration
[conn file-id]
(db/get* conn :file-migration
{:name "0008-fix-library-colors-v2"
:file-id file-id}))
(defn get-last-valid-snapshot
[conn migration]
(let [[snapshot] (db/exec! conn [sql:get-matching-snapshot
(:file-id migration)
(:created-at migration)])]
(when snapshot
(let [snapshot (assoc snapshot :id (:file-id snapshot))]
(bfc/decode-file h/*system* snapshot)))))
(defn restore-color
[{:keys [data] :as snapshot} color]
(when-let [scolor (get-in data [:colors (:id color)])]
(-> (select-keys scolor types.color/library-color-attrs)
(types.color/check-library-color))))
(defn restore-missing-colors
[{:keys [id] :as file} & _opts]
(l/inf :hint "process file" :file-id (str id) :name (:name file) :has-colors (-> file :data :colors not-empty boolean))
(if-let [colors (-> file :data :colors not-empty)]
(let [migration (get-affected-migration h/*system* id)]
(if-let [snapshot (get-last-valid-snapshot h/*system* migration)]
(do
(l/inf :hint "using snapshot" :snapshot (:label snapshot))
(let [colors (reduce-kv (fn [colors color-id color]
(if-let [result (restore-color snapshot color)]
(do
(l/inf :hint "restored color" :file-id (str id) :color-id (str color-id))
(assoc colors color-id result))
(do
(l/wrn :hint "ignoring color" :file-id (str id) :color (pr-str color))
colors)))
colors
colors)
file (-> file
(update :data assoc :colors colors)
(update :migrations disj "0008-fix-library-colors-v2"))]
(db/delete! h/*system* :file-migration
{:name "0008-fix-library-colors-v2"
:file-id (:id file)})
file))
(do
(db/delete! h/*system* :file-migration
{:name "0008-fix-library-colors-v2"
:file-id (:id file)})
nil)))
(do
(db/delete! h/*system* :file-migration
{:name "0008-fix-library-colors-v2"
:file-id (:id file)})
nil)))

View File

@@ -13,7 +13,6 @@
[app.common.files.migrations :as fmg]
[app.common.files.validate :as cfv]
[app.db :as db]
[app.features.components-v2 :as feat.comp-v2]
[app.main :as main]
[app.rpc.commands.files :as files]
[app.rpc.commands.files-snapshot :as fsnap]
@@ -62,6 +61,27 @@
{:id id})
team))
(def ^:private sql:get-and-lock-team-files
"SELECT f.id
FROM file AS f
JOIN project AS p ON (p.id = f.project_id)
WHERE p.team_id = ?
AND p.deleted_at IS NULL
AND f.deleted_at IS NULL
FOR UPDATE")
(defn get-team
[conn team-id]
(-> (db/get conn :team {:id team-id}
{::db/remove-deleted false
::db/check-deleted false})
(update :features db/decode-pgarray #{})))
(defn get-and-lock-team-files
[conn team-id]
(transduce (map :id) conj []
(db/plan conn [sql:get-and-lock-team-files team-id])))
(defn reset-file-data!
"Hardcode replace of the data of one file."
[system id data]
@@ -96,7 +116,7 @@
(defn take-team-snapshot!
[system team-id label]
(let [conn (db/get-connection system)]
(->> (feat.comp-v2/get-and-lock-team-files conn team-id)
(->> (get-and-lock-team-files conn team-id)
(reduce (fn [result file-id]
(let [file (fsnap/get-file-snapshots system file-id)]
(fsnap/create-file-snapshot! system file
@@ -108,19 +128,16 @@
(defn restore-team-snapshot!
[system team-id label]
(let [conn (db/get-connection system)
ids (->> (feat.comp-v2/get-and-lock-team-files conn team-id)
ids (->> (get-and-lock-team-files conn team-id)
(into #{}))
snap (search-file-snapshots conn ids label)
ids' (into #{} (map :file-id) snap)
team (-> (feat.comp-v2/get-team conn team-id)
(update :features disj "components/v2"))]
ids' (into #{} (map :file-id) snap)]
(when (not= ids ids')
(throw (RuntimeException. "no uniform snapshot available")))
(feat.comp-v2/update-team! conn team)
(reduce (fn [result {:keys [file-id id]}]
(fsnap/restore-file-snapshot! system file-id id)
(inc result))
@@ -129,13 +146,9 @@
(defn process-file!
[system file-id update-fn & {:keys [label validate? with-libraries?] :or {validate? true} :as opts}]
(let [conn (db/get-connection system)
file (bfc/get-file system file-id ::db/for-update true)
(let [file (bfc/get-file system file-id ::db/for-update true)
libs (when with-libraries?
(->> (files/get-file-libraries conn file-id)
(into [file] (map (fn [{:keys [id]}]
(bfc/get-file system id))))
(d/index-by :id)))
(bfc/get-resolved-file-libraries system file))
file' (when file
(if with-libraries?

View File

@@ -17,12 +17,12 @@
[app.common.files.validate :as cfv]
[app.common.logging :as l]
[app.common.pprint :as p]
[app.common.schema :as sm]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.db.sql :as-alias sql]
[app.features.components-v2 :as feat.comp-v2]
[app.features.fdata :as feat.fdata]
[app.loggers.audit :as audit]
[app.main :as main]
@@ -156,6 +156,10 @@
[file-id & {:as opts}]
(process-file! file-id feat.fdata/enable-pointer-map opts))
(defn enable-path-data-feature-on-file!
[file-id & {:as opts}]
(process-file! file-id feat.fdata/enable-path-data opts))
(defn enable-storage-features-on-file!
[file-id & {:as opts}]
(enable-objects-map-feature-on-file! file-id opts)
@@ -387,14 +391,27 @@
[file-id]
(let [file-id (h/parse-uuid file-id)]
(db/tx-run! (assoc main/system ::db/rollback true)
(fn [{:keys [::db/conn] :as system}]
(let [file (h/get-file system file-id)
libs (->> (files/get-file-libraries conn file-id)
(into [file] (map (fn [{:keys [id]}]
(h/get-file system id))))
(d/index-by :id))]
(fn [system]
(let [file (bfc/get-file system file-id)
libs (bfc/get-resolved-file-libraries system file)]
(cfv/validate-file file libs))))))
(defn validate-file-schema
"Validate structure, referencial integrity and semantic coherence of
all contents of a file. Returns a list of errors."
[file-id]
(let [file-id (h/parse-uuid file-id)]
(db/tx-run! (assoc main/system ::db/rollback true)
(fn [system]
(try
(let [file (bfc/get-file system file-id)]
(cfv/validate-file-schema! file)
(println "OK"))
(catch Exception cause
(if-let [explain (-> cause ex-data ::sm/explain)]
(println (sm/humanize-explain explain))
(ex/print-throwable cause))))))))
(defn repair-file!
"Repair the list of errors detected by validation."
[file-id & {:keys [rollback?] :or {rollback? true} :as opts}]
@@ -416,10 +433,12 @@
"Apply a function to the file. Optionally save the changes or not.
The function receives the decoded and migrated file data."
[file-id update-fn & {:keys [rollback?] :or {rollback? true} :as opts}]
(db/tx-run! (assoc main/system ::db/rollback rollback?)
(fn [system]
(binding [h/*system* system]
(h/process-file! system file-id update-fn opts)))))
(let [file-id (h/parse-uuid file-id)]
(db/tx-run! (assoc main/system ::db/rollback rollback?)
(fn [system]
(binding [h/*system* system
db/*conn* (db/get-connection system)]
(h/process-file! system file-id update-fn opts))))))
(defn process-team-files!
"Apply a function to each file of the specified team."
@@ -431,8 +450,9 @@
(when (string? label)
(h/take-team-snapshot! system team-id label))
(binding [h/*system* system]
(->> (feat.comp-v2/get-and-lock-team-files conn team-id)
(binding [h/*system* system
db/*conn* (db/get-connection system)]
(->> (h/get-and-lock-team-files conn team-id)
(reduce (fn [result file-id]
(if (h/process-file! system file-id update-fn opts)
(inc result)
@@ -471,7 +491,8 @@
:index idx)
(let [system (assoc main/system ::db/rollback rollback?)]
(db/tx-run! system (fn [system]
(binding [h/*system* system]
(binding [h/*system* system
db/*conn* (db/get-connection system)]
(h/process-file! system file-id update-fn opts)))))
(catch Throwable cause

View File

@@ -10,6 +10,7 @@
file is eligible to be garbage collected after some period of
inactivity (the default threshold is 72h)."
(:require
[app.binfile.cleaner :as bfl]
[app.binfile.common :as bfc]
[app.common.files.helpers :as cfh]
[app.common.files.validate :as cfv]
@@ -258,6 +259,7 @@
(if-let [file (get-file cfg file-id)]
(let [file (->> file
(bfc/decode-file cfg)
(bfl/clean-file)
(clean-media! cfg)
(clean-fragments! cfg))
file (assoc file :has-media-trimmed true)]

View File

@@ -9,7 +9,6 @@
of deleted or unreachable objects."
(:require
[app.common.logging :as l]
[app.config :as cf]
[app.db :as db]
[app.storage :as sto]
[app.util.time :as dt]
@@ -18,15 +17,15 @@
(def ^:private sql:get-profiles
"SELECT id, photo_id FROM profile
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-profiles!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-profiles min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-profiles deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id photo-id]}]
(l/trc :hint "permanently delete" :rel "profile" :id (str id))
@@ -41,15 +40,15 @@
(def ^:private sql:get-teams
"SELECT deleted_at, id, photo_id FROM team
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-teams!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-teams min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-teams deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id photo-id deleted-at]}]
(l/trc :hint "permanently delete"
:rel "team"
@@ -69,15 +68,15 @@
"SELECT id, team_id, deleted_at, woff1_file_id, woff2_file_id, otf_file_id, ttf_file_id
FROM team_font_variant
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-fonts!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-fonts min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-fonts deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id team-id deleted-at] :as font}]
(l/trc :hint "permanently delete"
:rel "team-font-variant"
@@ -101,15 +100,15 @@
"SELECT id, deleted_at, team_id
FROM project
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-projects!
[{:keys [::db/conn ::min-age ::chunk-size] :as cfg}]
(->> (db/plan conn [sql:get-projects min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size] :as cfg}]
(->> (db/plan conn [sql:get-projects deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id team-id deleted-at]}]
(l/trc :hint "permanently delete"
:rel "project"
@@ -127,15 +126,15 @@
"SELECT id, deleted_at, project_id, data_backend, data_ref_id
FROM file
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-files!
[{:keys [::db/conn ::sto/storage ::min-age ::chunk-size] :as cfg}]
(->> (db/plan conn [sql:get-files min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::sto/storage ::deletion-threshold ::chunk-size] :as cfg}]
(->> (db/plan conn [sql:get-files deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id deleted-at project-id] :as file}]
(l/trc :hint "permanently delete"
:rel "file"
@@ -156,15 +155,15 @@
"SELECT file_id, revn, media_id, deleted_at
FROM file_thumbnail
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn delete-file-thumbnails!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-thumbnails min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-thumbnails deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [file-id revn media-id deleted-at]}]
(l/trc :hint "permanently delete"
:rel "file-thumbnail"
@@ -185,15 +184,15 @@
"SELECT file_id, object_id, media_id, deleted_at
FROM file_tagged_object_thumbnail
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn delete-file-object-thumbnails!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-object-thumbnails min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-object-thumbnails deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [file-id object-id media-id deleted-at]}]
(l/trc :hint "permanently delete"
:rel "file-tagged-object-thumbnail"
@@ -214,15 +213,15 @@
"SELECT file_id, id, deleted_at, data_ref_id
FROM file_data_fragment
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-file-data-fragments!
[{:keys [::db/conn ::sto/storage ::min-age ::chunk-size] :as cfg}]
(->> (db/plan conn [sql:get-file-data-fragments min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::sto/storage ::deletion-threshold ::chunk-size] :as cfg}]
(->> (db/plan conn [sql:get-file-data-fragments deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [file-id id deleted-at data-ref-id]}]
(l/trc :hint "permanently delete"
:rel "file-data-fragment"
@@ -240,15 +239,15 @@
"SELECT id, file_id, media_id, thumbnail_id, deleted_at
FROM file_media_object
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-file-media-objects!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-media-objects min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-media-objects deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id file-id deleted-at] :as fmo}]
(l/trc :hint "permanently delete"
:rel "file-media-object"
@@ -269,15 +268,15 @@
"SELECT id, file_id, deleted_at, data_backend, data_ref_id
FROM file_change
WHERE deleted_at IS NOT NULL
AND deleted_at < now() - ?::interval
AND deleted_at < now() + ?::interval
ORDER BY deleted_at ASC
LIMIT ?
FOR UPDATE
SKIP LOCKED")
(defn- delete-file-changes!
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-change min-age chunk-size] {:fetch-size 5})
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
(->> (db/plan conn [sql:get-file-change deletion-threshold chunk-size] {:fetch-size 5})
(reduce (fn [total {:keys [id file-id deleted-at] :as xlog}]
(l/trc :hint "permanently delete"
:rel "file-change"
@@ -324,16 +323,13 @@
(defmethod ig/expand-key ::handler
[k v]
{k (assoc v
::min-age (cf/get-deletion-delay)
::chunk-size 100)})
{k (assoc v ::chunk-size 100)})
(defmethod ig/init-key ::handler
[_ cfg]
(fn [{:keys [props] :as task}]
(let [min-age (dt/duration (or (:min-age props) (::min-age cfg)))
cfg (assoc cfg ::min-age (db/interval min-age))]
(let [threshold (dt/duration (get props :deletion-threshold 0))
cfg (assoc cfg ::deletion-threshold (db/interval threshold))]
(loop [procs (map deref deletion-proc-vars)
total 0]
(if-let [proc-fn (first procs)]

View File

@@ -10,7 +10,6 @@
to them. Mainly used in http.sse for progress reporting."
(:refer-clojure :exclude [tap run!])
(:require
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.logging :as l]
[promesa.exec :as px]
@@ -18,33 +17,30 @@
(def ^:dynamic *channel* nil)
(defn channel
[]
(sp/chan :buf 32))
(defn tap
[type data]
(when-let [channel *channel*]
(sp/put! channel [type data])
nil))
([type data]
(when-let [channel *channel*]
(sp/put! channel [type data])
nil))
([channel type data]
(when channel
(sp/put! channel [type data])
nil)))
(defn start-listener
[on-event on-close]
(dm/assert!
"expected active events channel"
(sp/chan? *channel*))
[channel on-event on-close]
(assert (sp/chan? channel) "expected active events channel")
(px/thread
{:virtual true}
(try
(loop []
(when-let [event (sp/take! *channel*)]
(when-let [event (sp/take! channel)]
(let [result (ex/try! (on-event event))]
(if (ex/exception? result)
(do
(l/wrn :hint "unexpected exception" :cause result)
(sp/close! *channel*))
(sp/close! channel))
(recur)))))
(finally
(on-close)))))
@@ -55,7 +51,7 @@
[f on-event]
(binding [*channel* (sp/chan :buf 32)]
(let [listener (start-listener on-event (constantly nil))]
(let [listener (start-listener *channel* on-event (constantly nil))]
(try
(f)
(finally

View File

@@ -222,7 +222,7 @@
([params]
(mark-file-deleted* *system* params))
([conn {:keys [id] :as params}]
(#'files/mark-file-deleted conn id)))
(#'files/mark-file-deleted conn {} id)))
(defn create-team*
([i params] (create-team* *system* i params))

View File

@@ -8,10 +8,10 @@
(:require
[app.common.features :as cfeat]
[app.common.pprint :as pp]
[app.common.pprint :as pp]
[app.common.thumbnails :as thc]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.db.sql :as sql]
[app.http :as http]
@@ -123,8 +123,27 @@
:components-v2 true}
out (th/command! data)]
;; (th/print-result! out)
;; (th/print-result! out)
(t/is (nil? (:error out)))
(let [result (:result out)]
(t/is (some? (:deleted-at result)))
(t/is (= file-id (:id result)))
(t/is (= "new name" (:name result)))
(t/is (= 1 (count (get-in result [:data :pages]))))
(t/is (nil? (:users result))))))
(th/db-update! :file
{:deleted-at (dt/now)}
{:id file-id})
(t/testing "query single file after delete and wait"
(let [data {::th/type :get-file
::rpc/profile-id (:id prof)
:id file-id
:components-v2 true}
out (th/command! data)]
(let [error (:error out)
error-data (ex-data error)]
(t/is (th/ex-info? error))
@@ -195,7 +214,7 @@
(t/is (= 5 (count rows))))
;; The objects-gc should remove unused fragments
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 3 (:processed res))))
;; Check the number of fragments
@@ -230,7 +249,7 @@
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
;; The objects-gc should remove unused fragments
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 3 (:processed res))))
;; Check the number of fragments;
@@ -254,7 +273,7 @@
(t/is (= 4 (count rows)))
(t/is (= 2 (count (remove (comp some? :deleted-at) rows)))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)})]
@@ -324,8 +343,9 @@
:name "image"
:frame-id uuid/zero
:parent-id uuid/zero
:type :image
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}})}])
:type :rect
:fills [{:fill-opacity 1
:fill-image {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
;; Check that reference storage objects on filemediaobjects
;; are the same because of deduplication feature.
@@ -355,7 +375,7 @@
(t/is (= 2 (count rows)))
(t/is (= 1 (count (remove (comp some? :deleted-at) rows)))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 3 (:processed res))))
;; check file media objects
@@ -386,7 +406,7 @@
;; This only clears fragments, the file media objects still referenced because
;; snapshots are preserved
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
;; Mark all snapshots to be a non-snapshot file change
@@ -395,7 +415,7 @@
;; Rerun the file-gc and objects-gc
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
;; Now that file-gc have deleted the file-media-object usage,
@@ -443,7 +463,8 @@
fmo3 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
fmo4 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
fmo5 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
s-shid (uuid/random)
s1-shid (uuid/random)
s2-shid (uuid/random)
t-shid (uuid/random)
page-id (first (get-in file [:data :pages]))]
@@ -462,19 +483,31 @@
:changes
[{:type :add-obj
:page-id page-id
:id s-shid
:id s1-shid
:parent-id uuid/zero
:frame-id uuid/zero
:components-v2 true
:obj (cts/setup-shape
{:id s-shid
{:id s1-shid
:name "image"
:frame-id uuid/zero
:parent-id uuid/zero
:type :image
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}
:fills [{:opacity 1 :fill-image {:id (:id fmo2) :width 100 :height 100 :mtype "image/jpeg"}}]
:strokes [{:opacity 1 :stroke-image {:id (:id fmo3) :width 100 :height 100 :mtype "image/jpeg"}}]})}
:type :rect
:fills [{:fill-opacity 1 :fill-image {:id (:id fmo2) :width 101 :height 100 :mtype "image/jpeg"}}]
:strokes [{:stroke-opacity 1 :stroke-image {:id (:id fmo3) :width 102 :height 100 :mtype "image/jpeg"}}]})}
{:type :add-obj
:page-id page-id
:id s2-shid
:parent-id uuid/zero
:frame-id uuid/zero
:components-v2 true
:obj (cts/setup-shape
{:id s2-shid
:name "image"
:frame-id uuid/zero
:parent-id uuid/zero
:type :rect
:fills [{:fill-opacity 1 :fill-image {:id (:id fmo1) :width 103 :height 100 :mtype "image/jpeg"}}]})}
{:type :add-obj
:page-id page-id
:id t-shid
@@ -500,7 +533,8 @@
{:fills [{:fill-opacity 1
:fill-color "#000000"}]
:text "bye"}]}]}]}
:strokes [{:opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
:strokes [{:stroke-opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
;; run the file-gc task immediately without forced min-age
(t/is (false? (th/run-task! :file-gc {:file-id (:id file)})))
@@ -508,7 +542,7 @@
;; run the task again
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)
@@ -538,10 +572,13 @@
:vern 0
:changes [{:type :del-obj
:page-id (first (get-in file [:data :pages]))
:id s-shid}
:id s1-shid}
{:type :del-obj
:page-id (first (get-in file [:data :pages]))
:id t-shid}])
:id t-shid}
{:type :del-obj
:page-id (first (get-in file [:data :pages]))
:id s2-shid}])
;; Now, we have deleted the usage of pointers to the
;; file-media-objects, if we paste file-gc, they should be marked
@@ -550,7 +587,7 @@
;; This only removes unused fragments, file media are still
;; referenced on snapshots.
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
;; Mark all snapshots to be a non-snapshot file change
@@ -560,7 +597,7 @@
;; Rerun file-gc and objects-gc task for the same file once all snapshots are
;; "expired/deleted"
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 6 (:processed res))))
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)
@@ -712,7 +749,7 @@
;; Now that file-gc have marked for deletion the object
;; thumbnail lets execute the objects-gc task which remove
;; the rows and mark as touched the storage object rows
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 5 (:processed res))))
;; Now that objects-gc have deleted the object thumbnail lets
@@ -741,7 +778,7 @@
(t/is (= 1 (count rows)))
(t/is (= 0 (count (remove (comp some? :deleted-at) rows)))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
;; (pp/pprint res)
(t/is (= 3 (:processed res))))
@@ -876,7 +913,7 @@
:profile-id (:id profile1)})]
;; file is not deleted because it does not meet all
;; conditions to be deleted.
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 0 (:processed result))))
;; query the list of files
@@ -907,7 +944,7 @@
(t/is (= 0 (count result)))))
;; run permanent deletion (should be noop)
(let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 0 (:processed result))))
;; query the list of file libraries of a after hard deletion
@@ -921,7 +958,7 @@
(t/is (= 0 (count result)))))
;; run permanent deletion
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 1 (:processed result))))
;; query the list of file libraries of a after hard deletion
@@ -1176,7 +1213,7 @@
(t/is (= 2 (count rows)))
(t/is (= 1 (count (remove :deleted-at rows)))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 4 (:processed res))))
(let [rows (th/db-query :file-tagged-object-thumbnail {:file-id (:id file)})]
@@ -1232,7 +1269,7 @@
(t/is (= 2 (count rows)))
(t/is (= 1 (count (remove (comp some? :deleted-at) rows)))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
(let [rows (th/db-query :file-thumbnail {:file-id (:id file)})]
@@ -1251,7 +1288,7 @@
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
;; Preventive objects-gc
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 1 (:processed result))))
;; Check the number of fragments before adding the page
@@ -1272,7 +1309,7 @@
(th/run-pending-tasks!))
;; Clean objects after file-gc
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 1 (:processed result))))
;; Check the number of fragments before adding the page
@@ -1324,7 +1361,7 @@
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
;; The objects-gc should remove unused fragments
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {})]
(t/is (= 2 (:processed res))))
;; Check the number of fragments before adding the page
@@ -1712,6 +1749,7 @@
[{:fill-image
{:id (:id fmedia)
:name "test"
:mtype "image/jpeg"
:width 200
:height 200}}]]
@@ -1820,8 +1858,7 @@
(t/is (= (:id file-2) (:file-id (get rows 1))))
(t/is (nil? (:deleted-at (get rows 1)))))
(th/run-task! :objects-gc
{:min-age 0})
(th/run-task! :objects-gc {})
(let [rows (th/db-exec! ["SELECT * FROM file_media_object ORDER BY created_at ASC"])]
(t/is (= 1 (count rows)))

View File

@@ -7,6 +7,7 @@
(ns backend-tests.rpc-font-test
(:require
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.http :as http]
[app.rpc :as-alias rpc]
@@ -144,7 +145,7 @@
(t/is (= 0 (:freeze res)))
(t/is (= 0 (:delete res))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 2 (:processed res))))
(let [res (th/run-task! :storage-gc-touched {:min-age 0})]
@@ -204,7 +205,7 @@
(t/is (= 0 (:freeze res)))
(t/is (= 0 (:delete res))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 1 (:processed res))))
(let [res (th/run-task! :storage-gc-touched {:min-age 0})]
@@ -263,7 +264,7 @@
(t/is (= 0 (:freeze res)))
(t/is (= 0 (:delete res))))
(let [res (th/run-task! :objects-gc {:min-age 0})]
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 1 (:processed res))))
(let [res (th/run-task! :storage-gc-touched {:min-age 0})]

View File

@@ -48,7 +48,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 122785 (:size mobj1)))
(t/is (= 3299 (:size mobj2)))))))
(t/is (= 3297 (:size mobj2)))))))
(t/deftest media-object-upload
(let [prof (th/create-profile* 1)
@@ -85,7 +85,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 312043 (:size mobj1)))
(t/is (= 3901 (:size mobj2)))))))
(t/is (= 3890 (:size mobj2)))))))
(t/deftest media-object-upload-idempotency
@@ -163,7 +163,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 122785 (:size mobj1)))
(t/is (= 3299 (:size mobj2)))))))
(t/is (= 3297 (:size mobj2)))))))
(t/deftest media-object-upload-command
(let [prof (th/create-profile* 1)
@@ -200,7 +200,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 312043 (:size mobj1)))
(t/is (= 3901 (:size mobj2)))))))
(t/is (= 3890 (:size mobj2)))))))
(t/deftest media-object-upload-idempotency-command

View File

@@ -209,16 +209,16 @@
::rpc/profile-id (:id prof1)
:id (:id team1)}
out (th/command! params)]
;; (th/print-result! out)
;; (th/print-result! out)
(let [team (th/db-get :team {:id (:id team1)} {::db/remove-deleted false})]
(t/is (dt/instant? (:deleted-at team)))))
;; Request profile to be deleted
;; Request profile to be deleted
(let [params {::th/type :delete-profile
::rpc/profile-id (:id prof1)}
out (th/command! params)]
;; (th/print-result! out)
;; (th/print-result! out)
(t/is (nil? (:result out)))
(t/is (nil? (:error out)))))))
@@ -379,15 +379,14 @@
(t/deftest prepare-register-and-register-profile-1
(let [data {::th/type :prepare-register-profile
:email "user@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
(t/is (string? token))
;; try register without token
(let [data {::th/type :register-profile
:fullname "foobar"
:accept-terms-and-privacy true}
(let [data {::th/type :register-profile}
out (th/command! data)]
;; (th/print-result! out)
(let [error (:error out)]
@@ -398,11 +397,8 @@
;; try correct register
(let [data {::th/type :register-profile
:token token
:fullname "foobar"
:utm_campaign "utma"
:mtm_campaign "mtma"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}]
:mtm_campaign "mtma"}]
(let [{:keys [result error]} (th/command! data)]
(t/is (nil? error))))
@@ -424,6 +420,7 @@
;; PREPARE REGISTER
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -432,10 +429,7 @@
;; DO REGISTRATION
(let [data {::th/type :register-profile
:token @current-token
:fullname "foobar"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}
:token @current-token}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= 1 (:call-count @mock))))
@@ -445,6 +439,7 @@
;; PREPARE REGISTER: second attempt
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -479,6 +474,7 @@
;; PREPARE REGISTER
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -487,10 +483,7 @@
;; DO REGISTRATION
(let [data {::th/type :register-profile
:token @current-token
:fullname "foobar"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}
:token @current-token}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= 1 (:call-count @mock))))
@@ -504,6 +497,7 @@
;; PREPARE REGISTER: second attempt
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -514,10 +508,7 @@
:return true}]
;; DO REGISTRATION: second attempt
(let [data {::th/type :register-profile
:token @current-token
:fullname "foobar"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}
:token @current-token}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= 0 (:call-count @mock))))))))
@@ -532,6 +523,7 @@
:member-email "user@example.com"})
data {::th/type :prepare-register-profile
:invitation-token itoken
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
@@ -542,8 +534,7 @@
(let [rtoken (:token result)
data {::th/type :register-profile
:token rtoken
:fullname "foobar"}
:token rtoken}
{:keys [result error] :as out} (th/command! data)]
;; (th/print-result! out)
@@ -563,6 +554,7 @@
data {::th/type :prepare-register-profile
:invitation-token itoken
:email "user@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)]
@@ -582,6 +574,7 @@
:member-email "user@example.com"})
data {::th/type :prepare-register-profile
:invitation-token itoken
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
out (th/command! data)]
@@ -604,6 +597,7 @@
data {::th/type :prepare-register-profile
:invitation-token itoken
:email "user@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)]
@@ -624,6 +618,7 @@
data {::th/type :prepare-register-profile
:invitation-token itoken
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
out (th/command! data)]
@@ -636,6 +631,7 @@
(t/deftest prepare-register-with-registration-disabled
(with-redefs [app.config/flags #{}]
(let [data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
out (th/command! data)]
@@ -648,6 +644,7 @@
(t/deftest prepare-register-with-existing-user
(let [profile (th/create-profile* 1)
data {::th/type :prepare-register-profile
:fullname "foobar"
:email (:email profile)
:password "foobar"}
out (th/command! data)]
@@ -660,6 +657,7 @@
(let [pool (:app.db/pool th/*system*)
data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "foobar"}]
@@ -674,6 +672,7 @@
(t/deftest register-profile-with-complained-email
(let [pool (:app.db/pool th/*system*)
data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "foobar"}]
@@ -688,6 +687,7 @@
(t/deftest register-profile-with-email-as-password
(let [data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "USER@example.com"}
out (th/command! data)]

View File

@@ -7,6 +7,7 @@
(ns backend-tests.rpc-project-test
(:require
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.http :as http]
[app.rpc :as-alias rpc]
@@ -178,7 +179,7 @@
;; project is not deleted because it does not meet all
;; conditions to be deleted.
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 0 (:processed result))))
;; query the list of projects
@@ -210,7 +211,7 @@
(t/is (= 1 (count result)))))
;; run permanent deletion (should be noop)
(let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 0 (:processed result))))
;; query the list of files of a after soft deletion
@@ -224,7 +225,7 @@
(t/is (= 0 (count result)))))
;; run permanent deletion
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 1 (:processed result))))
;; query the list of files of a after hard deletion

View File

@@ -8,6 +8,7 @@
(:require
[app.common.logging :as l]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.http :as http]
[app.rpc :as-alias rpc]
@@ -449,6 +450,26 @@
(t/is (nil? res)))))
(t/deftest get-owned-teams
(let [profile1 (th/create-profile* 1 {:is-active true})
profile2 (th/create-profile* 2 {:is-active true})
team1 (th/create-team* 1 {:profile-id (:id profile1)})
team2 (th/create-team* 2 {:profile-id (:id profile2)})
params {::th/type :get-owned-teams
::rpc/profile-id (:id profile1)}
out (th/command! params)]
;; (th/print-result! out)
(t/is (th/success? out))
(let [[item1 :as result] (:result out)]
(t/is (= 1 (count result)))
(t/is (= (:id team1) (:id item1)))
(t/is (= 1 (:total-members item1)))
(t/is (= 1 (:total-editors item1)))
(t/is (not= (:default-team-id profile1) (:id item1))))))
(t/deftest team-deletion-1
(let [profile1 (th/create-profile* 1 {:is-active true})
team (th/create-team* 1 {:profile-id (:id profile1)})
@@ -459,7 +480,7 @@
;; team is not deleted because it does not meet all
;; conditions to be deleted.
(let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 0 (:processed result))))
;; query the list of teams
@@ -493,7 +514,7 @@
(th/run-pending-tasks!)
;; run permanent deletion (should be noop)
(let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})]
(let [result (th/run-task! :objects-gc {})]
(t/is (= 0 (:processed result))))
;; query the list of projects after hard deletion
@@ -507,7 +528,7 @@
(t/is (= :not-found (:type edata)))))
;; run permanent deletion
(let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})]
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 2 (:processed result))))
;; query the list of projects of a after hard deletion
@@ -521,7 +542,6 @@
(let [edata (-> out :error ex-data)]
(t/is (= :not-found (:type edata)))))))
(t/deftest team-deletion-2
(let [storage (-> (:app.storage/storage th/*system*)
(assoc ::sto/backend :assets-fs))
@@ -564,7 +584,7 @@
(t/is (= 1 (count rows)))
(t/is (dt/instant? (:deleted-at (first rows)))))
(let [result (th/run-task! :objects-gc {:min-age 0})]
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
(t/is (= 5 (:processed result))))))
(t/deftest create-team-access-request

View File

@@ -19,25 +19,34 @@ __metadata:
languageName: node
linkType: hard
"@npmcli/agent@npm:^2.0.0":
version: 2.2.2
resolution: "@npmcli/agent@npm:2.2.2"
"@isaacs/fs-minipass@npm:^4.0.0":
version: 4.0.1
resolution: "@isaacs/fs-minipass@npm:4.0.1"
dependencies:
minipass: "npm:^7.0.4"
checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2
languageName: node
linkType: hard
"@npmcli/agent@npm:^3.0.0":
version: 3.0.0
resolution: "@npmcli/agent@npm:3.0.0"
dependencies:
agent-base: "npm:^7.1.0"
http-proxy-agent: "npm:^7.0.0"
https-proxy-agent: "npm:^7.0.1"
lru-cache: "npm:^10.0.1"
socks-proxy-agent: "npm:^8.0.3"
checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae
checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271
languageName: node
linkType: hard
"@npmcli/fs@npm:^3.1.0":
version: 3.1.1
resolution: "@npmcli/fs@npm:3.1.1"
"@npmcli/fs@npm:^4.0.0":
version: 4.0.0
resolution: "@npmcli/fs@npm:4.0.0"
dependencies:
semver: "npm:^7.3.5"
checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99
checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5
languageName: node
linkType: hard
@@ -48,29 +57,17 @@ __metadata:
languageName: node
linkType: hard
"abbrev@npm:^2.0.0":
version: 2.0.0
resolution: "abbrev@npm:2.0.0"
checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372
"abbrev@npm:^3.0.0":
version: 3.0.1
resolution: "abbrev@npm:3.0.1"
checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf
languageName: node
linkType: hard
"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1":
version: 7.1.1
resolution: "agent-base@npm:7.1.1"
dependencies:
debug: "npm:^4.3.4"
checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50
languageName: node
linkType: hard
"aggregate-error@npm:^3.0.0":
version: 3.1.0
resolution: "aggregate-error@npm:3.1.0"
dependencies:
clean-stack: "npm:^2.0.0"
indent-string: "npm:^4.0.0"
checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
version: 7.1.3
resolution: "agent-base@npm:7.1.3"
checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
languageName: node
linkType: hard
@@ -82,9 +79,9 @@ __metadata:
linkType: hard
"ansi-regex@npm:^6.0.1":
version: 6.0.1
resolution: "ansi-regex@npm:6.0.1"
checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08
version: 6.1.0
resolution: "ansi-regex@npm:6.1.0"
checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc
languageName: node
linkType: hard
@@ -141,21 +138,21 @@ __metadata:
linkType: hard
"brace-expansion@npm:^1.1.7":
version: 1.1.11
resolution: "brace-expansion@npm:1.1.11"
version: 1.1.12
resolution: "brace-expansion@npm:1.1.12"
dependencies:
balanced-match: "npm:^1.0.0"
concat-map: "npm:0.0.1"
checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668
checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73
languageName: node
linkType: hard
"brace-expansion@npm:^2.0.1":
version: 2.0.1
resolution: "brace-expansion@npm:2.0.1"
version: 2.0.2
resolution: "brace-expansion@npm:2.0.2"
dependencies:
balanced-match: "npm:^1.0.0"
checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f
checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf
languageName: node
linkType: hard
@@ -175,11 +172,11 @@ __metadata:
languageName: node
linkType: hard
"cacache@npm:^18.0.0":
version: 18.0.3
resolution: "cacache@npm:18.0.3"
"cacache@npm:^19.0.1":
version: 19.0.1
resolution: "cacache@npm:19.0.1"
dependencies:
"@npmcli/fs": "npm:^3.1.0"
"@npmcli/fs": "npm:^4.0.0"
fs-minipass: "npm:^3.0.0"
glob: "npm:^10.2.2"
lru-cache: "npm:^10.0.1"
@@ -187,11 +184,11 @@ __metadata:
minipass-collect: "npm:^2.0.1"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
p-map: "npm:^4.0.0"
ssri: "npm:^10.0.0"
tar: "npm:^6.1.11"
unique-filename: "npm:^3.0.0"
checksum: 10c0/dfda92840bb371fb66b88c087c61a74544363b37a265023223a99965b16a16bbb87661fe4948718d79df6e0cc04e85e62784fbcf1832b2a5e54ff4c46fbb45b7
p-map: "npm:^7.0.2"
ssri: "npm:^12.0.0"
tar: "npm:^7.4.3"
unique-filename: "npm:^4.0.0"
checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c
languageName: node
linkType: hard
@@ -214,17 +211,10 @@ __metadata:
languageName: node
linkType: hard
"chownr@npm:^2.0.0":
version: 2.0.0
resolution: "chownr@npm:2.0.0"
checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1
"chownr@npm:^3.0.0":
version: 3.0.0
resolution: "chownr@npm:3.0.0"
checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10
languageName: node
linkType: hard
@@ -251,26 +241,26 @@ __metadata:
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.0":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
"cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
dependencies:
path-key: "npm:^3.1.0"
shebang-command: "npm:^2.0.0"
which: "npm:^2.0.1"
checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750
checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
version: 4.3.4
resolution: "debug@npm:4.3.4"
version: 4.4.1
resolution: "debug@npm:4.4.1"
dependencies:
ms: "npm:2.1.2"
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736
checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
languageName: node
linkType: hard
@@ -319,9 +309,21 @@ __metadata:
linkType: hard
"exponential-backoff@npm:^3.1.1":
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579
version: 3.1.2
resolution: "exponential-backoff@npm:3.1.2"
checksum: 10c0/d9d3e1eafa21b78464297df91f1776f7fbaa3d5e3f7f0995648ca5b89c069d17055033817348d9f4a43d1c20b0eab84f75af6991751e839df53e4dfd6f22e844
languageName: node
linkType: hard
"fdir@npm:^6.4.4":
version: 6.4.6
resolution: "fdir@npm:6.4.6"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9
languageName: node
linkType: hard
@@ -335,21 +337,12 @@ __metadata:
linkType: hard
"foreground-child@npm:^3.1.0":
version: 3.1.1
resolution: "foreground-child@npm:3.1.1"
version: 3.3.1
resolution: "foreground-child@npm:3.3.1"
dependencies:
cross-spawn: "npm:^7.0.0"
cross-spawn: "npm:^7.0.6"
signal-exit: "npm:^4.0.1"
checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0
languageName: node
linkType: hard
"fs-minipass@npm:^2.0.0":
version: 2.1.0
resolution: "fs-minipass@npm:2.1.0"
dependencies:
minipass: "npm:^3.0.0"
checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004
checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3
languageName: node
linkType: hard
@@ -390,18 +383,19 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^10.2.2, glob@npm:^10.3.10":
version: 10.3.16
resolution: "glob@npm:10.3.16"
"glob@npm:^10.2.2":
version: 10.4.5
resolution: "glob@npm:10.4.5"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^3.1.2"
minimatch: "npm:^9.0.1"
minipass: "npm:^7.0.4"
path-scurry: "npm:^1.11.0"
minimatch: "npm:^9.0.4"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^1.11.1"
bin:
glob: dist/esm/bin.mjs
checksum: 10c0/f7eb4c3e66f221f0be3967c02527047167967549bdf8ed1bd5f6277d43a35191af4e2bb8c89f07a79664958bae088fd06659e69a0f1de462972f1eab52a715e8
checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e
languageName: node
linkType: hard
@@ -420,9 +414,9 @@ __metadata:
linkType: hard
"http-cache-semantics@npm:^4.1.1":
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc
version: 4.2.0
resolution: "http-cache-semantics@npm:4.2.0"
checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37
languageName: node
linkType: hard
@@ -437,12 +431,12 @@ __metadata:
linkType: hard
"https-proxy-agent@npm:^7.0.1":
version: 7.0.4
resolution: "https-proxy-agent@npm:7.0.4"
version: 7.0.6
resolution: "https-proxy-agent@npm:7.0.6"
dependencies:
agent-base: "npm:^7.0.2"
agent-base: "npm:^7.1.2"
debug: "npm:4"
checksum: 10c0/bc4f7c38da32a5fc622450b6cb49a24ff596f9bd48dcedb52d2da3fa1c1a80e100fb506bd59b326c012f21c863c69b275c23de1a01d0b84db396822fdf25e52b
checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
languageName: node
linkType: hard
@@ -469,13 +463,6 @@ __metadata:
languageName: node
linkType: hard
"indent-string@npm:^4.0.0":
version: 4.0.0
resolution: "indent-string@npm:4.0.0"
checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f
languageName: node
linkType: hard
"ip-address@npm:^9.0.5":
version: 9.0.5
resolution: "ip-address@npm:9.0.5"
@@ -518,13 +505,6 @@ __metadata:
languageName: node
linkType: hard
"is-lambda@npm:^1.0.1":
version: 1.0.1
resolution: "is-lambda@npm:1.0.1"
checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d
languageName: node
linkType: hard
"is-number@npm:^7.0.0":
version: 7.0.0
resolution: "is-number@npm:7.0.0"
@@ -547,15 +527,15 @@ __metadata:
linkType: hard
"jackspeak@npm:^3.1.2":
version: 3.1.2
resolution: "jackspeak@npm:3.1.2"
version: 3.4.3
resolution: "jackspeak@npm:3.4.3"
dependencies:
"@isaacs/cliui": "npm:^8.0.2"
"@pkgjs/parseargs": "npm:^0.11.0"
dependenciesMeta:
"@pkgjs/parseargs":
optional: true
checksum: 10c0/5f1922a1ca0f19869e23f0dc4374c60d36e922f7926c76fecf8080cc6f7f798d6a9caac1b9428327d14c67731fd551bb3454cb270a5e13a0718f3b3660ec3d5d
checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9
languageName: node
linkType: hard
@@ -567,36 +547,35 @@ __metadata:
linkType: hard
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
version: 10.2.2
resolution: "lru-cache@npm:10.2.2"
checksum: 10c0/402d31094335851220d0b00985084288136136992979d0e015f0f1697e15d1c86052d7d53ae86b614e5b058425606efffc6969a31a091085d7a2b80a8a1e26d6
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
languageName: node
linkType: hard
"luxon@npm:^3.4.4":
version: 3.4.4
resolution: "luxon@npm:3.4.4"
checksum: 10c0/02e26a0b039c11fd5b75e1d734c8f0332c95510f6a514a9a0991023e43fb233884da02d7f966823ffb230632a733fc86d4a4b1e63c3fbe00058b8ee0f8c728af
version: 3.6.1
resolution: "luxon@npm:3.6.1"
checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5
languageName: node
linkType: hard
"make-fetch-happen@npm:^13.0.0":
version: 13.0.1
resolution: "make-fetch-happen@npm:13.0.1"
"make-fetch-happen@npm:^14.0.3":
version: 14.0.3
resolution: "make-fetch-happen@npm:14.0.3"
dependencies:
"@npmcli/agent": "npm:^2.0.0"
cacache: "npm:^18.0.0"
"@npmcli/agent": "npm:^3.0.0"
cacache: "npm:^19.0.1"
http-cache-semantics: "npm:^4.1.1"
is-lambda: "npm:^1.0.1"
minipass: "npm:^7.0.2"
minipass-fetch: "npm:^3.0.0"
minipass-fetch: "npm:^4.0.0"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
negotiator: "npm:^0.6.3"
proc-log: "npm:^4.2.0"
negotiator: "npm:^1.0.0"
proc-log: "npm:^5.0.0"
promise-retry: "npm:^2.0.1"
ssri: "npm:^10.0.0"
checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e
ssri: "npm:^12.0.0"
checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0
languageName: node
linkType: hard
@@ -609,12 +588,12 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^9.0.1":
version: 9.0.4
resolution: "minimatch@npm:9.0.4"
"minimatch@npm:^9.0.4":
version: 9.0.5
resolution: "minimatch@npm:9.0.5"
dependencies:
brace-expansion: "npm:^2.0.1"
checksum: 10c0/2c16f21f50e64922864e560ff97c587d15fd491f65d92a677a344e970fe62aafdbeafe648965fa96d33c061b4d0eabfe0213466203dd793367e7f28658cf6414
checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed
languageName: node
linkType: hard
@@ -627,18 +606,18 @@ __metadata:
languageName: node
linkType: hard
"minipass-fetch@npm:^3.0.0":
version: 3.0.5
resolution: "minipass-fetch@npm:3.0.5"
"minipass-fetch@npm:^4.0.0":
version: 4.0.1
resolution: "minipass-fetch@npm:4.0.1"
dependencies:
encoding: "npm:^0.1.13"
minipass: "npm:^7.0.3"
minipass-sized: "npm:^1.0.3"
minizlib: "npm:^2.1.2"
minizlib: "npm:^3.0.1"
dependenciesMeta:
encoding:
optional: true
checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b
checksum: 10c0/a3147b2efe8e078c9bf9d024a0059339c5a09c5b1dded6900a219c218cc8b1b78510b62dae556b507304af226b18c3f1aeb1d48660283602d5b6586c399eed5c
languageName: node
linkType: hard
@@ -678,76 +657,68 @@ __metadata:
languageName: node
linkType: hard
"minipass@npm:^5.0.0":
version: 5.0.0
resolution: "minipass@npm:5.0.0"
checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
version: 7.1.2
resolution: "minipass@npm:7.1.2"
checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557
languageName: node
linkType: hard
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4":
version: 7.1.1
resolution: "minipass@npm:7.1.1"
checksum: 10c0/fdccc2f99c31083f45f881fd1e6971d798e333e078ab3c8988fb818c470fbd5e935388ad9adb286397eba50baebf46ef8ff487c8d3f455a69c6f3efc327bdff9
languageName: node
linkType: hard
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
version: 2.1.2
resolution: "minizlib@npm:2.1.2"
"minizlib@npm:^3.0.1":
version: 3.0.2
resolution: "minizlib@npm:3.0.2"
dependencies:
minipass: "npm:^3.0.0"
yallist: "npm:^4.0.0"
checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78
minipass: "npm:^7.1.2"
checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78
languageName: node
linkType: hard
"mkdirp@npm:^1.0.3":
version: 1.0.4
resolution: "mkdirp@npm:1.0.4"
"mkdirp@npm:^3.0.1":
version: 3.0.1
resolution: "mkdirp@npm:3.0.1"
bin:
mkdirp: bin/cmd.js
checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf
mkdirp: dist/cjs/src/bin.js
checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d
languageName: node
linkType: hard
"ms@npm:2.1.2":
version: 2.1.2
resolution: "ms@npm:2.1.2"
checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
"ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
languageName: node
linkType: hard
"negotiator@npm:^0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2
"negotiator@npm:^1.0.0":
version: 1.0.0
resolution: "negotiator@npm:1.0.0"
checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 10.1.0
resolution: "node-gyp@npm:10.1.0"
version: 11.2.0
resolution: "node-gyp@npm:11.2.0"
dependencies:
env-paths: "npm:^2.2.0"
exponential-backoff: "npm:^3.1.1"
glob: "npm:^10.3.10"
graceful-fs: "npm:^4.2.6"
make-fetch-happen: "npm:^13.0.0"
nopt: "npm:^7.0.0"
proc-log: "npm:^3.0.0"
make-fetch-happen: "npm:^14.0.3"
nopt: "npm:^8.0.0"
proc-log: "npm:^5.0.0"
semver: "npm:^7.3.5"
tar: "npm:^6.1.2"
which: "npm:^4.0.0"
tar: "npm:^7.4.3"
tinyglobby: "npm:^0.2.12"
which: "npm:^5.0.0"
bin:
node-gyp: bin/node-gyp.js
checksum: 10c0/9cc821111ca244a01fb7f054db7523ab0a0cd837f665267eb962eb87695d71fb1e681f9e21464cc2fd7c05530dc4c81b810bca1a88f7d7186909b74477491a3c
checksum: 10c0/bd8d8c76b06be761239b0c8680f655f6a6e90b48e44d43415b11c16f7e8c15be346fba0cbf71588c7cdfb52c419d928a7d3db353afc1d952d19756237d8f10b9
languageName: node
linkType: hard
"nodemon@npm:^3.1.2":
version: 3.1.2
resolution: "nodemon@npm:3.1.2"
version: 3.1.10
resolution: "nodemon@npm:3.1.10"
dependencies:
chokidar: "npm:^3.5.2"
debug: "npm:^4"
@@ -761,18 +732,18 @@ __metadata:
undefsafe: "npm:^2.0.5"
bin:
nodemon: bin/nodemon.js
checksum: 10c0/7a091067d766768fb6660b796194b01748bba5dc3f1e3ed3dd5f804bfa305e207d24635755078ee5e7cc53848cea35204901e0a6e51ac64483bb8e9ecb237c95
checksum: 10c0/95b64d647f2c22e85e375b250517b0a4b32c2d2392ad898444e331f70d6b1ab43b17f53a8a1d68d5879ab8401fc6cd6e26f0d2a8736240984f6b5a8435b407c0
languageName: node
linkType: hard
"nopt@npm:^7.0.0":
version: 7.2.1
resolution: "nopt@npm:7.2.1"
"nopt@npm:^8.0.0":
version: 8.1.0
resolution: "nopt@npm:8.1.0"
dependencies:
abbrev: "npm:^2.0.0"
abbrev: "npm:^3.0.0"
bin:
nopt: bin/nopt.js
checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81
checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef
languageName: node
linkType: hard
@@ -783,12 +754,17 @@ __metadata:
languageName: node
linkType: hard
"p-map@npm:^4.0.0":
version: 4.0.0
resolution: "p-map@npm:4.0.0"
dependencies:
aggregate-error: "npm:^3.0.0"
checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75
"p-map@npm:^7.0.2":
version: 7.0.3
resolution: "p-map@npm:7.0.3"
checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c
languageName: node
linkType: hard
"package-json-from-dist@npm:^1.0.0":
version: 1.0.1
resolution: "package-json-from-dist@npm:1.0.1"
checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b
languageName: node
linkType: hard
@@ -799,7 +775,7 @@ __metadata:
languageName: node
linkType: hard
"path-scurry@npm:^1.11.0":
"path-scurry@npm:^1.11.1":
version: 1.11.1
resolution: "path-scurry@npm:1.11.1"
dependencies:
@@ -816,17 +792,17 @@ __metadata:
languageName: node
linkType: hard
"proc-log@npm:^3.0.0":
version: 3.0.0
resolution: "proc-log@npm:3.0.0"
checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc
"picomatch@npm:^4.0.2":
version: 4.0.2
resolution: "picomatch@npm:4.0.2"
checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
languageName: node
linkType: hard
"proc-log@npm:^4.2.0":
version: 4.2.0
resolution: "proc-log@npm:4.2.0"
checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9
"proc-log@npm:^5.0.0":
version: 5.0.0
resolution: "proc-log@npm:5.0.0"
checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3
languageName: node
linkType: hard
@@ -878,11 +854,11 @@ __metadata:
linkType: hard
"semver@npm:^7.3.5, semver@npm:^7.5.3":
version: 7.6.2
resolution: "semver@npm:7.6.2"
version: 7.7.2
resolution: "semver@npm:7.7.2"
bin:
semver: bin/semver.js
checksum: 10c0/97d3441e97ace8be4b1976433d1c32658f6afaff09f143e52c593bae7eef33de19e3e369c88bd985ce1042c6f441c80c6803078d1de2a9988080b66684cbb30c
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
languageName: node
linkType: hard
@@ -926,23 +902,23 @@ __metadata:
linkType: hard
"socks-proxy-agent@npm:^8.0.3":
version: 8.0.3
resolution: "socks-proxy-agent@npm:8.0.3"
version: 8.0.5
resolution: "socks-proxy-agent@npm:8.0.5"
dependencies:
agent-base: "npm:^7.1.1"
agent-base: "npm:^7.1.2"
debug: "npm:^4.3.4"
socks: "npm:^2.7.1"
checksum: 10c0/4950529affd8ccd6951575e21c1b7be8531b24d924aa4df3ee32df506af34b618c4e50d261f4cc603f1bfd8d426915b7d629966c8ce45b05fb5ad8c8b9a6459d
socks: "npm:^2.8.3"
checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6
languageName: node
linkType: hard
"socks@npm:^2.7.1":
version: 2.8.3
resolution: "socks@npm:2.8.3"
"socks@npm:^2.8.3":
version: 2.8.5
resolution: "socks@npm:2.8.5"
dependencies:
ip-address: "npm:^9.0.5"
smart-buffer: "npm:^4.2.0"
checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7
checksum: 10c0/e427d0eb0451cfd04e20b9156ea8c0e9b5e38a8d70f21e55c30fbe4214eda37cfc25d782c63f9adc5fbdad6d062a0f127ef2cefc9a44b6fee2b9ea5d1ed10827
languageName: node
linkType: hard
@@ -970,12 +946,12 @@ __metadata:
languageName: node
linkType: hard
"ssri@npm:^10.0.0":
version: 10.0.6
resolution: "ssri@npm:10.0.6"
"ssri@npm:^12.0.0":
version: 12.0.0
resolution: "ssri@npm:12.0.0"
dependencies:
minipass: "npm:^7.0.3"
checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227
checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d
languageName: node
linkType: hard
@@ -1028,17 +1004,27 @@ __metadata:
languageName: node
linkType: hard
"tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.2.1
resolution: "tar@npm:6.2.1"
"tar@npm:^7.4.3":
version: 7.4.3
resolution: "tar@npm:7.4.3"
dependencies:
chownr: "npm:^2.0.0"
fs-minipass: "npm:^2.0.0"
minipass: "npm:^5.0.0"
minizlib: "npm:^2.1.1"
mkdirp: "npm:^1.0.3"
yallist: "npm:^4.0.0"
checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.0.1"
mkdirp: "npm:^3.0.1"
yallist: "npm:^5.0.0"
checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d
languageName: node
linkType: hard
"tinyglobby@npm:^0.2.12":
version: 0.2.14
resolution: "tinyglobby@npm:0.2.14"
dependencies:
fdir: "npm:^6.4.4"
picomatch: "npm:^4.0.2"
checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
languageName: node
linkType: hard
@@ -1067,21 +1053,21 @@ __metadata:
languageName: node
linkType: hard
"unique-filename@npm:^3.0.0":
version: 3.0.0
resolution: "unique-filename@npm:3.0.0"
"unique-filename@npm:^4.0.0":
version: 4.0.0
resolution: "unique-filename@npm:4.0.0"
dependencies:
unique-slug: "npm:^4.0.0"
checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f
unique-slug: "npm:^5.0.0"
checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc
languageName: node
linkType: hard
"unique-slug@npm:^4.0.0":
version: 4.0.0
resolution: "unique-slug@npm:4.0.0"
"unique-slug@npm:^5.0.0":
version: 5.0.0
resolution: "unique-slug@npm:5.0.0"
dependencies:
imurmurhash: "npm:^0.1.4"
checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635
checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293
languageName: node
linkType: hard
@@ -1096,14 +1082,14 @@ __metadata:
languageName: node
linkType: hard
"which@npm:^4.0.0":
version: 4.0.0
resolution: "which@npm:4.0.0"
"which@npm:^5.0.0":
version: 5.0.0
resolution: "which@npm:5.0.0"
dependencies:
isexe: "npm:^3.1.1"
bin:
node-which: bin/which.js
checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a
checksum: 10c0/e556e4cd8b7dbf5df52408c9a9dd5ac6518c8c5267c8953f5b0564073c66ed5bf9503b14d876d0e9c7844d4db9725fb0dcf45d6e911e17e26ab363dc3965ae7b
languageName: node
linkType: hard
@@ -1130,8 +1116,8 @@ __metadata:
linkType: hard
"ws@npm:^8.17.0":
version: 8.17.0
resolution: "ws@npm:8.17.0"
version: 8.18.2
resolution: "ws@npm:8.18.2"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
@@ -1140,7 +1126,7 @@ __metadata:
optional: true
utf-8-validate:
optional: true
checksum: 10c0/55241ec93a66fdfc4bf4f8bc66c8eb038fda2c7a4ee8f6f157f2ca7dc7aa76aea0c0da0bf3adb2af390074a70a0e45456a2eaf80e581e630b75df10a64b0a990
checksum: 10c0/4b50f67931b8c6943c893f59c524f0e4905bbd183016cfb0f2b8653aa7f28dad4e456b9d99d285bbb67cca4fedd9ce90dfdfaa82b898a11414ebd66ee99141e4
languageName: node
linkType: hard
@@ -1150,3 +1136,10 @@ __metadata:
checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a
languageName: node
linkType: hard
"yallist@npm:^5.0.0":
version: 5.0.0
resolution: "yallist@npm:5.0.0"
checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416
languageName: node
linkType: hard

View File

@@ -1,10 +1,10 @@
{:deps
{org.clojure/clojure {:mvn/version "1.12.0"}
{org.clojure/clojure {:mvn/version "1.12.1"}
org.clojure/data.json {:mvn/version "2.5.1"}
org.clojure/tools.cli {:mvn/version "1.1.230"}
org.clojure/clojurescript {:mvn/version "1.11.132"}
org.clojure/test.check {:mvn/version "1.1.1"}
org.clojure/data.fressian {:mvn/version "1.1.0"}
org.clojure/clojurescript {:mvn/version "1.12.42"}
;; Logging
org.apache.logging.log4j/log4j-api {:mvn/version "2.24.3"}
@@ -12,14 +12,14 @@
org.apache.logging.log4j/log4j-web {:mvn/version "2.24.3"}
org.apache.logging.log4j/log4j-jul {:mvn/version "2.24.3"}
org.apache.logging.log4j/log4j-slf4j2-impl {:mvn/version "2.24.3"}
org.slf4j/slf4j-api {:mvn/version "2.0.16"}
org.slf4j/slf4j-api {:mvn/version "2.0.17"}
pl.tkowalcz.tjahzi/log4j2-appender {:mvn/version "0.9.32"}
selmer/selmer {:mvn/version "1.12.61"}
selmer/selmer {:mvn/version "1.12.62"}
criterium/criterium {:mvn/version "0.4.6"}
metosin/jsonista {:mvn/version "0.3.13"}
metosin/malli {:mvn/version "0.17.0"}
metosin/malli {:mvn/version "0.18.0"}
expound/expound {:mvn/version "0.9.0"}
com.cognitect/transit-clj {:mvn/version "1.0.333"}
@@ -28,9 +28,9 @@
integrant/integrant {:mvn/version "0.13.1"}
funcool/tubax {:mvn/version "2021.05.20-0"}
funcool/cuerdas {:mvn/version "2023.11.09-407"}
funcool/cuerdas {:mvn/version "2025.06.16-414"}
funcool/promesa
{:git/sha "0c5ed6ad033515a2df4b55addea044f60e9653d0"
{:git/sha "f52f58cfacf62f59eab717e2637f37729d0cc383"
:git/url "https://github.com/funcool/promesa"}
funcool/datoteka
@@ -59,7 +59,7 @@
{:dev
{:extra-deps
{org.clojure/tools.namespace {:mvn/version "RELEASE"}
thheller/shadow-cljs {:mvn/version "2.28.20"}
thheller/shadow-cljs {:mvn/version "3.1.5"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "RELEASE"}
@@ -68,7 +68,7 @@
:build
{:extra-deps
{io.github.clojure/tools.build {:git/tag "v0.10.6" :git/sha "52cf7d6"}}
{io.github.clojure/tools.build {:git/tag "v0.10.9" :git/sha "e405aac"}}
:ns-default build}
:test
@@ -76,9 +76,9 @@
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}}
:shadow-cljs
{:main-opts ["-m" "shadow.cljs.devtools.cli"]}
{:main-opts ["-m" "shadow.cljs.devtools.cli"]
:jvm-opts ["--sun-misc-unsafe-memory-access=allow"]}
:outdated
{:extra-deps {com.github.liquidz/antq {:mvn/version "RELEASE"}}
:main-opts ["-m" "antq.core"]}}}

View File

@@ -4,22 +4,20 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
},
"dependencies": {
"luxon": "^3.4.4",
"sax": "^1.4.1"
"luxon": "^3.6.1"
},
"devDependencies": {
"concurrently": "^9.0.1",
"nodemon": "^3.1.7",
"shadow-cljs": "2.28.20",
"concurrently": "^9.1.2",
"nodemon": "^3.1.10",
"source-map-support": "^0.5.21",
"ws": "^8.17.0"
"ws": "^8.18.2"
},
"scripts": {
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",

View File

@@ -2,16 +2,20 @@
export PENPOT_FLAGS="enable-asserts enable-audit-log $PENPOT_FLAGS"
export OPTIONS="
-A:dev \
-J-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-J-Djdk.attach.allowAttachSelf \
-J-Dpolyglot.engine.WarnInterpreterOnly=false \
-J-XX:+EnableDynamicAgentLoading \
-J-XX:-OmitStackTraceInFastThrow \
-J-XX:+UnlockDiagnosticVMOptions \
-J-XX:+DebugNonSafepoints \
-J-Djdk.tracePinnedThreads=full"
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv-repl.xml \
-Djdk.tracePinnedThreads=full \
-XX:+EnableDynamicAgentLoading \
-XX:-OmitStackTraceInFastThrow \
-XX:+UnlockDiagnosticVMOptions \
-XX:+DebugNonSafepoints \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";
export OPTIONS="-A:dev"
export OPTIONS_EVAL="nil"
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)"

View File

@@ -0,0 +1,165 @@
;; 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.common.buffer
"A collection of helpers and macros for work with byte buffers"
(:refer-clojure :exclude [clone])
(:require
[app.common.uuid :as uuid])
#?(:cljs
(:require-macros [app.common.buffer])
:clj
(:import [java.nio ByteBuffer ByteOrder])))
(defmacro read-byte
[target offset]
(if (:ns &env)
`(.getInt8 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(long (.get ~target ~offset)))))
(defmacro read-bool
[target offset]
(if (:ns &env)
`(== 1 (.getInt8 ~target ~offset true))
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(== 1 (.get ~target ~offset)))))
(defmacro read-short
[target offset]
(if (:ns &env)
`(.getInt16 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.getShort ~target ~offset))))
(defmacro read-int
[target offset]
(if (:ns &env)
`(.getInt32 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(long (.getInt ~target ~offset)))))
(defmacro read-float
[target offset]
(if (:ns &env)
`(.getFloat32 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(double (.getFloat ~target ~offset)))))
(defmacro read-uuid
[target offset]
(if (:ns &env)
`(let [a# (.getUint32 ~target (+ ~offset 0) true)
b# (.getUint32 ~target (+ ~offset 4) true)
c# (.getUint32 ~target (+ ~offset 8) true)
d# (.getUint32 ~target (+ ~offset 12) true)]
(uuid/from-unsigned-parts a# b# c# d#))
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(try
(.order ~target ByteOrder/BIG_ENDIAN)
(let [msb# (.getLong ~target (+ ~offset 0))
lsb# (.getLong ~target (+ ~offset 8))]
(java.util.UUID. (long msb#) (long lsb#)))
(finally
(.order ~target ByteOrder/LITTLE_ENDIAN))))))
(defmacro write-byte
[target offset value]
(if (:ns &env)
`(.setInt8 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.put ~target ~offset (unchecked-byte ~value)))))
(defmacro write-short
[target offset value]
(if (:ns &env)
`(.setInt16 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putShort ~target ~offset (unchecked-short ~value)))))
(defmacro write-int
[target offset value]
(if (:ns &env)
`(.setInt32 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putInt ~target ~offset (unchecked-int ~value)))))
(defmacro write-float
[target offset value]
(if (:ns &env)
`(.setFloat32 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putFloat ~target ~offset (unchecked-float ~value)))))
(defmacro write-uuid
[target offset value]
(if (:ns &env)
`(let [barray# (uuid/get-u32 ~value)]
(.setUint32 ~target (+ ~offset 0) (aget barray# 0) true)
(.setUint32 ~target (+ ~offset 4) (aget barray# 1) true)
(.setUint32 ~target (+ ~offset 8) (aget barray# 2) true)
(.setUint32 ~target (+ ~offset 12) (aget barray# 3) true))
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})
value (with-meta value {:tag 'java.util.UUID})]
`(try
(.order ~target ByteOrder/BIG_ENDIAN)
(.putLong ~target (+ ~offset 0) (.getMostSignificantBits ~value))
(.putLong ~target (+ ~offset 8) (.getLeastSignificantBits ~value))
(finally
(.order ~target ByteOrder/LITTLE_ENDIAN))))))
(defn allocate
[size]
#?(:clj (let [buffer (ByteBuffer/allocate (int size))]
(.order buffer ByteOrder/LITTLE_ENDIAN))
:cljs (new js/DataView (new js/ArrayBuffer size))))
(defn clone
[buffer]
#?(:clj
(let [src (.array ^ByteBuffer buffer)
len (alength ^bytes src)
dst (byte-array len)]
(System/arraycopy src 0 dst 0 len)
(let [buffer (ByteBuffer/wrap dst)]
(.order buffer ByteOrder/LITTLE_ENDIAN)))
:cljs
(let [buffer' (.-buffer ^js/DataView buffer)
src-view (js/Uint32Array. buffer')
dst-buff (js/ArrayBuffer. (.-byteLength buffer'))
dst-view (js/Uint32Array. dst-buff)]
(.set dst-view src-view)
(js/DataView. dst-buff))))
(defn equals?
[buffer-a buffer-b]
#?(:clj
(.equals ^ByteBuffer buffer-a
^ByteBuffer buffer-b)
:cljs
(let [buffer-a (.-buffer buffer-a)
buffer-b (.-buffer buffer-b)]
(if (= (.-byteLength buffer-a)
(.-byteLength buffer-b))
(let [cb (js/Uint32Array. buffer-a)
ob (js/Uint32Array. buffer-b)
sz (alength cb)]
(loop [i 0]
(if (< i sz)
(if (== (aget ob i)
(aget cb i))
(recur (inc i))
false)
true)))
false))))
(defn buffer?
[o]
#?(:clj (instance? ByteBuffer o)
:cljs (instance? js/DataView o)))

View File

@@ -33,6 +33,16 @@
(def boolean-or-nil?
(some-fn nil? boolean?))
(defn in-range?
[size i]
(and (< i size) (>= i 0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Commonly used transducers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def xf:map-id (map :id))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Structures
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -6,7 +6,7 @@
(ns app.common.data.macros
"Data retrieval & manipulation specific macros."
(:refer-clojure :exclude [get-in select-keys str with-open min max])
(:refer-clojure :exclude [get-in select-keys str with-open max])
#?(:cljs (:require-macros [app.common.data.macros]))
(:require
#?(:clj [clojure.core :as c]
@@ -144,3 +144,8 @@
(str "expr assert: " (pr-str expr)))]
(when *assert*
`(runtime-assert ~hint (fn [] ~expr))))))
(defn truncate
"Truncates a string to a certain length"
[s max-length]
(subs s 0 (min max-length (count s))))

View File

@@ -47,10 +47,26 @@
`(try ~@exprs (catch Throwable e# nil))))
(defmacro try!
[& exprs]
(if (:ns &env)
`(try ~@exprs (catch :default e# e#))
`(try ~@exprs (catch Throwable e# e#))))
[expr & {:keys [reraise-with on-exception]}]
(let [ex-sym
(gensym "exc")
generate-catch
(fn []
(cond
(map? reraise-with)
`(ex/raise ~@(mapcat identity reraise-with) :cause ~ex-sym)
on-exception
`(let [handler# ~on-exception]
(handler# ~ex-sym))
:else
ex-sym))]
(if (:ns &env)
`(try ~expr (catch :default ~ex-sym ~(generate-catch)))
`(try ~expr (catch Throwable ~ex-sym ~(generate-catch))))))
(defn ex-info?
[v]

View File

@@ -46,6 +46,7 @@
#{"fdata/objects-map"
"fdata/pointer-map"
"fdata/shape-data-type"
"fdata/path-data"
"components/v2"
"styles/v2"
"layout/grid"
@@ -58,12 +59,18 @@
;; A set of features enabled by default
(def default-features
#{"fdata/shape-data-type"
"fdata/path-data"
"styles/v2"
"layout/grid"
"components/v2"
"plugins/runtime"
"design-tokens/v1"})
;; A set of features that should not be propagated to team on creating
;; or modifying a file
(def no-team-inheritable-features
#{"fdata/path-data"})
;; A set of features which only affects on frontend and can be enabled
;; and disabled freely by the user any time. This features does not
;; persist on file features field but can be permanently enabled on
@@ -86,8 +93,9 @@
;; without migration applied)
(def no-migration-features
(-> #{"layout/grid"
"design-tokens/v1"
"fdata/shape-data-type"
"design-tokens/v1"}
"fdata/path-data"}
(into frontend-only-features)
(into backend-only-features)))
@@ -103,9 +111,7 @@
"Translate a flag to a feature name"
[flag]
(case flag
:feature-components-v2 "components/v2"
:feature-styles-v2 "styles/v2"
:feature-grid-layout "layout/grid"
:feature-fdata-objects-map "fdata/objects-map"
:feature-fdata-pointer-map "fdata/pointer-map"
:feature-plugins "plugins/runtime"
@@ -216,6 +222,12 @@
(check-supported-features! file-features)
;; Components v1 is deprecated
(when-not (contains? file-features "components/v2")
(ex/raise :type :restriction
:code :file-in-components-v1
:hint "components v1 is deprecated"))
(let [not-supported (-> file-features
(set/difference enabled-features)
(set/difference backend-only-features)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,6 @@
[app.common.schema.desc-native :as smd]
[app.common.schema.generators :as sg]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
@@ -24,13 +23,13 @@
[app.common.types.grid :as ctg]
[app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl]
[app.common.types.path :as path]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.types.token :as cto]
[app.common.types.token-theme :as ctot]
[app.common.types.tokens-lib :as ctob]
[app.common.types.typographies-list :as ctyl]
[app.common.types.typography :as ctt]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[clojure.set :as set]))
@@ -48,14 +47,14 @@
[:type [:= :assign]]
;; NOTE: the full decoding is happening on the handler because it
;; needs a proper context of the current shape and its type
[:value [:map-of :keyword :any]]
[:value [:map-of :keyword ::sm/any]]
[:ignore-touched {:optional true} :boolean]
[:ignore-geometry {:optional true} :boolean]]]
[:set
[:map {:title "SetOperation"}
[:type [:= :set]]
[:attr :keyword]
[:val :any]
[:val ::sm/any]
[:ignore-touched {:optional true} :boolean]
[:ignore-geometry {:optional true} :boolean]]]
[:set-touched
@@ -239,10 +238,10 @@
[:component-id {:optional true} ::sm/uuid]
[:ignore-touched {:optional true} :boolean]
[:parent-id ::sm/uuid]
[:shapes :any]
[:shapes ::sm/any]
[:index {:optional true} [:maybe :int]]
[:after-shape {:optional true} :any]
[:component-swap {:optional true} :boolean]]]
[:after-shape {:optional true} ::sm/any]
[:allow-altering-copies {:optional true} :boolean]]]
[:reorder-children
[:map {:title "ReorderChildrenChange"}
@@ -251,14 +250,14 @@
[:component-id {:optional true} ::sm/uuid]
[:ignore-touched {:optional true} :boolean]
[:parent-id ::sm/uuid]
[:shapes :any]]]
[:shapes ::sm/any]]]
[:add-page
[:map {:title "AddPageChange"}
[:type [:= :add-page]]
[:id {:optional true} ::sm/uuid]
[:name {:optional true} :string]
[:page {:optional true} :any]]]
[:page {:optional true} ::sm/any]]]
[:mod-page
[:map {:title "ModPageChange"}
@@ -266,7 +265,7 @@
[:id ::sm/uuid]
;; All props are optional, background can be nil because is the
;; way to remove already set background
[:background {:optional true} [:maybe ::ctc/rgb-color]]
[:background {:optional true} [:maybe ctc/schema:hex-color]]
[:name {:optional true} :string]]]
[:set-plugin-data schema:set-plugin-data-change]
@@ -292,12 +291,12 @@
[:add-color
[:map {:title "AddColorChange"}
[:type [:= :add-color]]
[:color ::ctc/color]]]
[:color ctc/schema:library-color]]]
[:mod-color
[:map {:title "ModColorChange"}
[:type [:= :mod-color]]
[:color ::ctc/color]]]
[:color ctc/schema:library-color]]]
[:del-color
[:map {:title "DelColorChange"}
@@ -311,12 +310,12 @@
[:add-media
[:map {:title "AddMediaChange"}
[:type [:= :add-media]]
[:object ::ctf/media-object]]]
[:object ctf/schema:media]]]
[:mod-media
[:map {:title "ModMediaChange"}
[:type [:= :mod-media]]
[:object ::ctf/media-object]]]
[:object ctf/schema:media]]]
[:del-media
[:map {:title "DelMediaChange"}
@@ -328,21 +327,25 @@
[:type [:= :add-component]]
[:id ::sm/uuid]
[:name :string]
[:shapes {:optional true} [:vector {:gen/max 3} :any]]
[:shapes {:optional true} [:vector {:gen/max 3} ::sm/any]]
[:path {:optional true} :string]]]
[:mod-component
[:map {:title "ModCompoenentChange"}
[:type [:= :mod-component]]
[:id ::sm/uuid]
[:shapes {:optional true} [:vector {:gen/max 3} :any]]
[:name {:optional true} :string]]]
[:shapes {:optional true} [:vector {:gen/max 3} ::sm/any]]
[:name {:optional true} :string]
[:variant-id {:optional true} ::sm/uuid]
[:variant-properties {:optional true} [:vector ::ctv/variant-property]]]]
[:del-component
[:map {:title "DelComponentChange"}
[:type [:= :del-component]]
[:id ::sm/uuid]
[:main-instance {:optional true} :any]
;; when it is an undo of a cut-paste, we need to undo the movement
;; of the shapes so we need to move them delta
[:delta {:optional true} ::gpt/point]
[:skip-undelete? {:optional true} :boolean]]]
[:restore-component
@@ -374,7 +377,7 @@
[:update-active-token-themes
[:map {:title "UpdateActiveTokenThemes"}
[:type [:= :update-active-token-themes]]
[:theme-ids [:set :string]]]]
[:theme-paths [:set :string]]]]
[:rename-token-set-group
[:map {:title "RenameTokenSetGroup"}
@@ -403,26 +406,31 @@
[:type [:= :set-token-theme]]
[:theme-name :string]
[:group :string]
[:theme [:maybe ::ctot/token-theme]]]]
[:theme [:maybe ctob/schema:token-theme-attrs]]]]
[:set-tokens-lib
[:map {:title "SetTokensLib"}
[:type [:= :set-tokens-lib]]
[:tokens-lib :any]]]
[:tokens-lib ::sm/any]]]
[:set-token-set
[:map {:title "SetTokenSetChange"}
[:type [:= :set-token-set]]
[:set-name :string]
[:group? :boolean]
[:token-set [:maybe ::ctot/token-set]]]]
[:token-set [:maybe [:fn ctob/token-set?]]]]]
[:set-token
[:map {:title "SetTokenChange"}
[:type [:= :set-token]]
[:set-name :string]
[:token-name :string]
[:token [:maybe ::cto/token]]]]]])
[:token [:maybe ctob/schema:token-attrs]]]]
[:set-base-font-size
[:map {:title "ModBaseFontSize"}
[:type [:= :set-base-font-size]]
[:base-font-size :string]]]]])
(def schema:changes
[:sequential {:gen/max 5 :gen/min 1} schema:change])
@@ -479,7 +487,9 @@
(cts/shape? shape-new))
(ex/raise :type :assertion
:code :data-validation
:hint "invalid shape found after applying changes"
:hint (str "invalid shape found after applying changes on file "
(:id data-new))
:file-id (:id data-new)
::sm/explain (cts/explain-shape shape-new))))))]
(->> (into #{} (map :page-id) items)
@@ -729,27 +739,29 @@
(update-group [group objects]
(let [lookup (d/getf objects)
children (->> group :shapes (map lookup))]
children (get group :shapes)]
(cond
;; If the group is empty we don't make any changes. Will be removed by a later process
(empty? children)
group
(= :bool (:type group))
(gsh/update-bool-selrect group children objects)
(path/update-bool-shape group objects)
(:masked-group group)
(set-mask-selrect group children)
(->> (map lookup children)
(set-mask-selrect group))
:else
(gsh/update-group-selrect group children))))]
(->> (map lookup children)
(gsh/update-group-selrect group)))))]
(if page-id
(d/update-in-when data [:pages-index page-id :objects] reg-objects)
(d/update-in-when data [:components component-id :objects] reg-objects))))
(defmethod process-change :mov-objects
[data {:keys [parent-id shapes index page-id component-id ignore-touched after-shape component-swap syncing]}]
[data {:keys [parent-id shapes index page-id component-id ignore-touched after-shape allow-altering-copies syncing]}]
(letfn [(calculate-invalid-targets [objects shape-id]
(let [reduce-fn #(into %1 (calculate-invalid-targets objects %2))]
(->> (get-in objects [shape-id :shapes])
@@ -764,7 +776,7 @@
(and shape
(not (invalid-targets parent-id))
(not (cfh/components-nesting-loop? objects shape-id parent-id))
(or component-swap ;; On a component swap it's allowed to change the structure of a copy
(or allow-altering-copies ;; In some cases (like a component swap) it's allowed to change the structure of a copy
syncing ;; If we are syncing the changes of a main component, it's allowed to change the structure of a copy
(and
(not (ctk/in-component-copy? (get objects (:parent-id shape)))) ;; We don't want to change the structure of component copies
@@ -915,15 +927,15 @@
(defmethod process-change :add-color
[data {:keys [color]}]
(ctcl/add-color data color))
(ctc/add-color data color))
(defmethod process-change :mod-color
[data {:keys [color]}]
(ctcl/set-color data color))
(ctc/set-color data color))
(defmethod process-change :del-color
[data {:keys [id]}]
(ctcl/delete-color data id))
(ctc/delete-color data id))
;; DEPRECATED: remove before 2.3
(defmethod process-change :add-recent-color
@@ -956,8 +968,8 @@
(ctkl/mod-component data params))
(defmethod process-change :del-component
[data {:keys [id skip-undelete? main-instance]}]
(ctf/delete-component data id skip-undelete? main-instance))
[data {:keys [id skip-undelete? delta]}]
(ctf/delete-component data id skip-undelete? delta))
(defmethod process-change :restore-component
[data {:keys [id page-id]}]
@@ -1015,11 +1027,10 @@
(ctob/delete-set lib' set-name))
(not (ctob/get-set lib' set-name))
(ctob/add-set lib' (ctob/make-token-set token-set))
(ctob/add-set lib' token-set)
:else
(ctob/update-set lib' set-name (fn [prev-token-set]
(ctob/make-token-set (merge prev-token-set token-set)))))))))
(ctob/update-set lib' set-name (fn [_] token-set)))))))
(defmethod process-change :set-token-theme
[data {:keys [group theme-name theme]}]
@@ -1040,9 +1051,9 @@
(ctob/make-token-theme (merge prev-token-theme theme)))))))))
(defmethod process-change :update-active-token-themes
[data {:keys [theme-ids]}]
[data {:keys [theme-paths]}]
(update data :tokens-lib #(-> % (ctob/ensure-tokens-lib)
(ctob/set-active-themes theme-ids))))
(ctob/set-active-themes theme-paths))))
(defmethod process-change :rename-token-set-group
[data {:keys [set-group-path set-group-fname]}]
@@ -1063,6 +1074,13 @@
(ctob/ensure-tokens-lib)
(ctob/move-set-group from-path to-path before-path before-group))))
;; === Base font size
(defmethod process-change :set-base-font-size
[data {:keys [base-font-size]}]
(ctf/set-base-font-size data base-font-size))
;; === Operations
(def ^:private decode-shape

View File

@@ -8,7 +8,6 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.features :as cfeat]
[app.common.files.changes :as cfc]
[app.common.files.helpers :as cfh]
[app.common.geom.matrix :as gmt]
@@ -19,24 +18,26 @@
[app.common.schema :as sm]
[app.common.types.component :as ctk]
[app.common.types.file :as ctf]
[app.common.types.path :as path]
[app.common.types.shape.layout :as ctl]
[app.common.types.tokens-lib :as ctob]
[app.common.uuid :as uuid]))
;; Auxiliary functions to help create a set of changes (undo + redo)
(sm/register!
^{::sm/type ::changes}
[:map {:title "changes"}
[:redo-changes vector?]
[:undo-changes seq?]
[:origin {:optional true} any?]
[:save-undo? {:optional true} boolean?]
[:stack-undo? {:optional true} boolean?]
[:undo-group {:optional true} any?]])
(def schema:changes
(sm/register!
^{::sm/type ::changes}
[:map {:title "changes"}
[:redo-changes vector?]
[:undo-changes seq?]
[:origin {:optional true} ::sm/any]
[:save-undo? {:optional true} boolean?]
[:stack-undo? {:optional true} boolean?]
[:undo-group {:optional true} ::sm/any]]))
(def check-changes!
(sm/check-fn ::changes))
(sm/check-fn schema:changes))
(defn empty-changes
([origin page-id]
@@ -84,8 +85,7 @@
(defn with-objects
[changes objects]
(let [fdata (binding [cfeat/*current* #{"components/v2"}]
(ctf/make-file-data (uuid/next) uuid/zero))
(let [fdata (ctf/make-file-data (uuid/next) uuid/zero)
fdata (assoc-in fdata [:pages-index uuid/zero :objects] objects)]
(vary-meta changes assoc
::file-data fdata
@@ -126,28 +126,40 @@
; TODO: remove this when not needed
(defn- assert-page-id!
[changes]
(dm/assert!
"Give a page-id or call (with-page) before using this function"
(contains? (meta changes) ::page-id)))
(assert
(contains? (meta changes) ::page-id)
"Give a page-id or call (with-page) before using this function"))
(defn- assert-page!
[changes]
(assert
(contains? (meta changes) ::page)
"Give a page or call (with-page) before using this function"))
(defn- assert-container-id!
[changes]
(dm/assert!
"Give a page-id or call (with-container) before using this function"
(assert
(or (contains? (meta changes) ::page-id)
(contains? (meta changes) ::component-id))))
(contains? (meta changes) ::component-id))
"Give a page-id or call (with-container) before using this function"))
(defn- assert-objects!
[changes]
(dm/assert!
"Call (with-objects) before using this function"
(contains? (meta changes) ::file-data)))
(assert
(contains? (meta changes) ::file-data)
"Call (with-objects) before using this function"))
(defn- assert-library!
[changes]
(dm/assert!
"Call (with-library-data) before using this function"
(contains? (meta changes) ::library-data)))
(assert
(contains? (meta changes) ::library-data)
"Call (with-library-data) before using this function"))
(defn- assert-file-data!
[changes]
(assert
(contains? (meta changes) ::file-data)
"Call (with-file-data) before using this function"))
(defn- lookup-objects
[changes]
@@ -156,9 +168,9 @@
(defn apply-changes-local
[changes & {:keys [apply-to-library?]}]
(dm/assert!
"expected valid changes"
(check-changes! changes))
(assert
(check-changes! changes)
"expected valid changes")
(if-let [file-data (::file-data (meta changes))]
(let [library-data (::library-data (meta changes))
@@ -197,6 +209,7 @@
(defn mod-page
([changes options]
(assert-page! changes)
(let [page (::page (meta changes))]
(mod-page changes page options)))
@@ -227,6 +240,7 @@
([changes type id namespace key value]
(set-plugin-data changes type id nil namespace key value))
([changes type id page-id namespace key value]
(assert-file-data! changes)
(let [data (::file-data (meta changes))
old-val
(case type
@@ -293,6 +307,8 @@
(defn set-guide
[changes id guide]
(assert-page-id! changes)
(assert-page! changes)
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:guides id])]
@@ -306,8 +322,11 @@
:page-id page-id
:id id
:params old-val}))))
(defn set-flow
[changes id flow]
(assert-page-id! changes)
(assert-page! changes)
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:flows id])
@@ -326,6 +345,8 @@
(defn set-comment-thread-position
[changes {:keys [id frame-id position] :as thread}]
(assert-page-id! changes)
(assert-page! changes)
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
@@ -347,6 +368,8 @@
(defn set-default-grid
[changes type params]
(assert-page-id! changes)
(assert-page! changes)
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:grids type])
@@ -441,8 +464,8 @@
(some? index)
(assoc :index index)
(:component-swap options)
(assoc :component-swap true)
(:allow-altering-copies options)
(assoc :allow-altering-copies true)
(:ignore-touched options)
(assoc :ignore-touched true))
@@ -450,12 +473,14 @@
(fn [undo-changes shape]
(let [prev-sibling (cfh/get-prev-sibling objects (:id shape))]
(conj undo-changes
{:type :mov-objects
:page-id (::page-id (meta changes))
:parent-id (:parent-id shape)
:shapes [(:id shape)]
:after-shape prev-sibling
:index 0}))) ; index is used in case there is no after-shape (moving bottom shapes)
(cond-> {:type :mov-objects
:page-id (::page-id (meta changes))
:parent-id (:parent-id shape)
:shapes [(:id shape)]
:after-shape prev-sibling
:index 0} ; index is used in case there is no after-shape (moving bottom shapes)
(:allow-altering-copies options)
(assoc :allow-altering-copies true)))))
restore-touched-change
{:type :mod-obj
@@ -480,9 +505,12 @@
(let [old-val (get old attr)
new-val (get new attr)]
(not= old-val new-val)))
new-obj (if with-objects?
(update-fn object objects)
(update-fn object))]
new-obj
(if with-objects?
(update-fn object objects)
(update-fn object))]
(when-not (= object new-obj)
(let [attrs (or attrs (d/concat-set (keys object) (keys new-obj)))]
(filter (partial changed? object new-obj) attrs)))))
@@ -658,10 +686,14 @@
(empty? children) ;; a parent with no children will be deleted,
nil ;; so it does not need resize
(= (:type parent) :bool)
(gsh/update-bool-selrect parent children objects)
(cfh/bool-shape? parent)
(path/update-bool-shape parent objects)
(= (:type parent) :group)
(cfh/group-shape? parent)
;; FIXME: this functions should be
;; normalized in the same way as
;; update-bool in order to make all
;; this code consistent
(if (:masked-group parent)
(gsh/update-mask-selrect parent children)
(gsh/update-group-selrect parent children)))]
@@ -768,10 +800,10 @@
(apply-changes-local))))
(defn update-active-token-themes
[changes token-active-theme-ids prev-token-active-theme-ids]
[changes active-theme-paths prev-active-theme-paths]
(-> changes
(update :redo-changes conj {:type :update-active-token-themes :theme-ids token-active-theme-ids})
(update :undo-changes conj {:type :update-active-token-themes :theme-ids prev-token-active-theme-ids})
(update :redo-changes conj {:type :update-active-token-themes :theme-paths active-theme-paths})
(update :undo-changes conj {:type :update-active-token-themes :theme-paths prev-active-theme-paths})
(apply-changes-local)))
(defn set-token-theme [changes group theme-name theme]
@@ -841,6 +873,7 @@
(defn set-tokens-lib
[changes tokens-lib]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
prev-tokens-lib (get library-data :tokens-lib)]
(-> changes
@@ -885,7 +918,7 @@
(-> changes
(update :redo-changes conj {:type :set-token-set
:set-name name
:token-set (assoc prev-token-set :name new-name)
:token-set (ctob/rename prev-token-set new-name)
:group? false})
(update :undo-changes conj {:type :set-token-set
:set-name new-name
@@ -906,11 +939,11 @@
:group? group?})
(update :undo-changes conj (if prev-token-set
{:type :set-token-set
:set-name (or
;; Undo of edit
(:name token-set)
;; Undo of delete
set-name)
:set-name (if token-set
;; Undo of edit
(ctob/get-name token-set)
;; Undo of delete
set-name)
:token-set prev-token-set
:group? group?}
;; Undo of create
@@ -921,11 +954,11 @@
(apply-changes-local))))
(defn add-component
([changes id path name new-shapes updated-shapes main-instance-id main-instance-page]
(add-component changes id path name new-shapes updated-shapes main-instance-id main-instance-page nil nil nil))
([changes id path name new-shapes updated-shapes main-instance-id main-instance-page annotation]
(add-component changes id path name new-shapes updated-shapes main-instance-id main-instance-page annotation nil nil))
([changes id path name new-shapes updated-shapes main-instance-id main-instance-page annotation variant-id variant-properties & {:keys [apply-changes-local-library?]}]
([changes id path name updated-shapes main-instance-id main-instance-page]
(add-component changes id path name updated-shapes main-instance-id main-instance-page nil nil nil))
([changes id path name updated-shapes main-instance-id main-instance-page annotation]
(add-component changes id path name updated-shapes main-instance-id main-instance-page annotation nil nil))
([changes id path name updated-shapes main-instance-id main-instance-page annotation variant-id variant-properties & {:keys [apply-changes-local-library?]}]
(assert-page-id! changes)
(assert-objects! changes)
(let [page-id (::page-id (meta changes))
@@ -964,11 +997,11 @@
:name name
:main-instance-id main-instance-id
:main-instance-page main-instance-page
:annotation annotation
:variant-id variant-id
:variant-properties variant-properties}
(some? new-shapes) ;; this will be null in components-v2
(assoc :shapes (vec new-shapes))))
:annotation annotation}
(some? variant-id)
(assoc :variant-id variant-id)
(seq variant-properties)
(assoc :variant-properties variant-properties)))
(into (map mk-change) updated-shapes))))
(update :undo-changes
(fn [undo-changes]
@@ -991,27 +1024,39 @@
new-component (update-fn prev-component)]
(if prev-component
(-> changes
(update :redo-changes conj {:type :mod-component
:id id
:name (:name new-component)
:path (:path new-component)
:main-instance-id (:main-instance-id new-component)
:main-instance-page (:main-instance-page new-component)
:annotation (:annotation new-component)
:variant-id (:variant-id new-component)
:variant-properties (:variant-properties new-component)
:objects (:objects new-component) ;; this won't exist in components-v2 (except for deleted components)
:modified-at (:modified-at new-component)})
(update :undo-changes conj {:type :mod-component
:id id
:name (:name prev-component)
:path (:path prev-component)
:main-instance-id (:main-instance-id prev-component)
:main-instance-page (:main-instance-page prev-component)
:annotation (:annotation prev-component)
:variant-id (:variant-id prev-component)
:variant-properties (:variant-properties prev-component)
:objects (:objects prev-component)})
(update :redo-changes conj (cond-> {:type :mod-component
:id id
:name (:name new-component)
:path (:path new-component)
:main-instance-id (:main-instance-id new-component)
:main-instance-page (:main-instance-page new-component)
:annotation (:annotation new-component)
:objects (:objects new-component) ;; for deleted components
:modified-at (:modified-at new-component)}
(some? (:variant-id new-component))
(assoc :variant-id (:variant-id new-component))
(nil? (:variant-id new-component))
(dissoc :variant-id)
(seq (:variant-properties new-component))
(assoc :variant-properties (:variant-properties new-component))
(not (seq (:variant-properties new-component)))
(dissoc :variant-properties)))
(update :undo-changes conj (cond-> {:type :mod-component
:id id
:name (:name prev-component)
:path (:path prev-component)
:main-instance-id (:main-instance-id prev-component)
:main-instance-page (:main-instance-page prev-component)
:annotation (:annotation prev-component)
:objects (:objects prev-component)}
(some? (:variant-id prev-component))
(assoc :variant-id (:variant-id prev-component))
(nil? (:variant-id prev-component))
(dissoc :variant-id)
(seq (:variant-properties prev-component))
(assoc :variant-properties (:variant-properties prev-component))
(not (seq (:variant-properties prev-component)))
(dissoc :variant-properties)))
(cond-> apply-changes-local-library?
(apply-changes-local {:apply-to-library? true})))
changes)))
@@ -1027,7 +1072,7 @@
:page-id page-id})))
(defn restore-component
[changes id page-id main-instance]
[changes id page-id delta]
(assert-library! changes)
(-> changes
(update :redo-changes conj {:type :restore-component
@@ -1035,7 +1080,34 @@
:page-id page-id})
(update :undo-changes conj {:type :del-component
:id id
:main-instance main-instance})))
:delta delta})))
(defn reorder-children
[changes id children]
(assert-page-id! changes)
(assert-objects! changes)
(let [page-id (::page-id (meta changes))
objects (lookup-objects changes)
shape (get objects id)
old-children (:shapes shape)
redo-change
{:type :reorder-children
:parent-id (:id shape)
:page-id page-id
:shapes children}
undo-change
{:type :reorder-children
:parent-id (:id shape)
:page-id page-id
:shapes old-children}]
(-> changes
(update :redo-changes conj redo-change)
(update :undo-changes conj undo-change)
(apply-changes-local))))
(defn reorder-grid-children
[changes ids]
@@ -1083,3 +1155,24 @@
(defn get-objects
[changes]
(dm/get-in (::file-data (meta changes)) [:pages-index uuid/zero :objects]))
(defn get-page
[changes]
(::page (meta changes)))
(defn get-page-id
[changes]
(::page-id (meta changes)))
(defn set-base-font-size
[changes new-base-font-size]
(assert-file-data! changes)
(let [file-data (::file-data (meta changes))
previous-font-size (ctf/get-base-font-size file-data)]
(-> changes
(update :redo-changes conj {:type :set-base-font-size
:base-font-size new-base-font-size})
(update :undo-changes conj {:type :set-base-font-size
:base-font-size previous-font-size})
(apply-changes-local))))

View File

@@ -117,6 +117,12 @@
([shape]
(d/not-empty? (:shapes shape))))
(defn has-layout?
"Returns true if the provided shape has a layout assigned"
[objects id]
(let [shape (get objects id)]
(boolean (and shape (:layout shape)))))
(defn group-like-shape?
([objects id]
(group-like-shape? (get objects id)))
@@ -127,13 +133,41 @@
;; ---- ACCESSORS
(defn get-children-ids
(defn get-selected-type
"Returns the type of the shape if only one, or :multiple if more
than one"
[objects selected]
(if (= 1 (count selected))
(let [shape (get objects (first selected))]
(:type shape))
:multiple))
(defn get-shape-type
"Returns the type of the shape, or 'root' if it's Root Frame, always
as string"
[objects id]
(letfn [(get-children-ids-rec [id processed]
(when (not (contains? processed id))
(when-let [shapes (-> (get objects id) :shapes (some-> vec))]
(into shapes (mapcat #(get-children-ids-rec % (conj processed id))) shapes))))]
(get-children-ids-rec id #{})))
(let [shape (get objects id)]
(if (root? shape)
:root
(dm/get-prop shape :type))))
(defn get-children-ids
"Returns the ids of all the descendants of the shape identified
by the id. Optionally, you can pass an ignore function to indicate
when to ignore a descendant (and all its descendants)"
([objects id]
(get-children-ids objects id {}))
([objects id {:keys [ignore-children-fn]
;;ignore-children-fn should receive a shape and return a boolean
:or {ignore-children-fn (constantly false)}}]
(letfn [(get-children-ids-rec [id processed]
(when-not (contains? processed id)
(when-let [shapes (as-> (get objects id) $
(:shapes $)
(remove ignore-children-fn $)
(some-> $ vec))]
(into shapes (mapcat #(get-children-ids-rec % (conj processed id))) shapes))))]
(get-children-ids-rec id #{}))))
(defn get-children-ids-with-self
[objects id]
@@ -427,11 +461,6 @@
(map #(str/concat base-name (suffix-fn %))
(iterate inc 1))))
(defn ^:private get-suffix
"Default suffix impelemtation"
[copy-count]
(str/concat " " copy-count))
(defn generate-unique-name
"Generates a unique name by selecting the first available name from a generated sequence.
The sequence consists of `base-name` and its variants, avoiding conflicts with `existing-names`.
@@ -445,8 +474,7 @@
Returns:
- A unique name not present in `existing-names`."
[base-name existing-names & {:keys [suffix-fn immediate-suffix?]
:or {suffix-fn get-suffix}}]
[base-name existing-names & {:keys [suffix-fn immediate-suffix? suffix]}]
(dm/assert!
"expected a set of strings"
(coll? existing-names))
@@ -454,9 +482,21 @@
(dm/assert!
"expected a string for `basename`."
(string? base-name))
(let [existing-name-set (cond-> (set existing-names)
(let [suffix-fn (if suffix-fn
suffix-fn
(if suffix
(fn [copy-count]
(str/concat "-"
suffix
(when (> copy-count 1)
(str "-" copy-count))))
(fn [copy-count]
(str/concat " " copy-count))))
existing-name-set (cond-> (set existing-names)
immediate-suffix? (conj base-name))
names (name-seq base-name suffix-fn)]
(->> names
(remove #(contains? existing-name-set %))
first)))
@@ -620,6 +660,9 @@
(map? (:fill-image form))
(update-in [:fill-image :id] lookup-index)
(map? (:stroke-image form))
(update-in [:stroke-image :id] lookup-index)
;; This covers old shapes and the new :fills.
(uuid? (:fill-color-ref-file form))
(update :fill-color-ref-file lookup-index)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -96,7 +96,7 @@
(log/dbg :hint "repairing shape :invalid-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/change-parent (:parent-id args) [shape] nil {:component-swap true})))
(pcb/change-parent (:parent-id args) [shape] nil {:allow-altering-copies true})))
(defmethod repair-error :frame-not-found
[_ {:keys [shape page-id] :as error} file-data _]
@@ -387,7 +387,7 @@
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape)
(pcb/change-parent uuid/zero [shape] nil {:component-swap true}))))
(pcb/change-parent uuid/zero [shape] nil {:allow-altering-copies true}))))
(defmethod repair-error :root-copy-not-allowed
[_ {:keys [shape page-id] :as error} file-data _]
@@ -572,6 +572,46 @@
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
(defmethod repair-error :not-a-variant
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :invalid-variant-id
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :invalid-variant-properties
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-not-main
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :parent-not-variant
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-bad-name
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-bad-variant-name
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :variant-component-bad-name
[_ error file _]
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file)
(defmethod repair-error :default
[_ error file _]
(log/error :hint "Unknown error code, don't know how to repair" :code (:code error))

View File

@@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.svg.shapes-builder
(ns app.common.files.shapes-builder
"A SVG to Shapes builder."
(:require
[app.common.colors :as clr]
@@ -21,7 +21,8 @@
[app.common.math :as mth]
[app.common.schema :as sm :refer [max-safe-int min-safe-int]]
[app.common.svg :as csvg]
[app.common.svg.path :as path]
[app.common.types.path :as path]
[app.common.types.path.segment :as path.segm]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
@@ -218,11 +219,11 @@
(defn create-path-shape [name frame-id svg-data {:keys [attrs] :as data}]
(when (and (contains? attrs :d) (seq (:d attrs)))
(let [transform (csvg/parse-transform (:transform attrs))
content (cond-> (path/parse (:d attrs))
content (cond-> (path/from-string (:d attrs))
(some? transform)
(gsh/transform-content transform))
(path.segm/transform-content transform))
selrect (gsh/content->selrect content)
selrect (path.segm/content->selrect content)
points (grc/rect->points selrect)
origin (gpt/negate (gpt/point svg-data))
attrs (-> (dissoc attrs :d :transform)
@@ -435,16 +436,12 @@
attrs
(-> attrs
(cond-> linecap
(dissoc :strokeLinecap))
(cond-> (some? color)
(dissoc :stroke :strokeWidth :strokeOpacity))
(update
:style
(fn [style]
(-> style
(cond-> linecap
(dissoc :strokeLinecap))
(cond-> (some? color)
(dissoc :stroke :strokeWidth :strokeOpacity)))))
(d/without-nils))]
@@ -461,12 +458,14 @@
(and (some? linecap) (cfh/path-shape? shape)
(or (= linecap :round) (= linecap :square)))
(assoc :stroke-cap-start linecap
:stroke-cap-end linecap)
:stroke-cap-end linecap
:stroke-linecap linecap)
(d/any-key? (dm/get-in shape [:strokes 0])
:strokeColor :strokeOpacity :strokeWidth
:strokeCapStart :strokeCapEnd)
:strokeLinecap :strokeCapStart :strokeCapEnd)
(assoc-in [:strokes 0 :stroke-style] :svg))))
(defn setup-opacity [shape]

View File

@@ -15,6 +15,8 @@
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]))
;; FIXME: move to logic?
(defn prepare-add-shape
[changes shape objects]
(let [index (:index (meta shape))
@@ -35,6 +37,7 @@
(pcb/update-shapes [(:parent-id shape)] #(ctl/push-into-cell % [id] row column)))
(cond-> (ctl/grid-layout? objects (:parent-id shape))
(pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true})))]
[shape changes]))
(defn prepare-move-shapes-into-frame
@@ -44,6 +47,7 @@
to-move (->> shapes
(map (d/getf objects))
(not-empty))]
(if to-move
(-> changes
(cond-> (and remove-layout-data?
@@ -62,6 +66,10 @@
changes id parent-id objects selected index frame-name without-fill? nil))
([changes id parent-id objects selected index frame-name without-fill? target-cell-id]
(prepare-create-artboard-from-selection
changes id parent-id objects selected index frame-name without-fill? target-cell-id nil))
([changes id parent-id objects selected index frame-name without-fill? target-cell-id delta]
(when-let [selected-objs (->> selected
(map (d/getf objects))
(not-empty))]
@@ -83,10 +91,10 @@
parent-id (or parent-id (get selected-obj :parent-id))
base-parent (get objects parent-id)
layout-props
layout-attrs
(when (and (= 1 (count selected))
(ctl/any-layout? base-parent))
(select-keys selected-obj ctl/layout-item-props))
(select-keys selected-obj ctl/layout-child-attrs))
target-cell-id
(if (and (nil? target-cell-id)
@@ -99,10 +107,11 @@
:id))
target-cell-id)
attrs
{:type :frame
:x (:x srect)
:y (:y srect)
:x (cond-> (:x srect) delta (+ (:x delta)))
:y (cond-> (:y srect) delta (+ (:y delta)))
:width (:width srect)
:height (:height srect)}
@@ -120,8 +129,8 @@
:parent-id parent-id
:shapes (into [] selected))
(some? layout-props)
(d/patch-object layout-props)
(some? layout-attrs)
(d/patch-object layout-attrs)
;; Frames from shapes will not be displayed in viewer and no clipped
(or (not= frame-id uuid/zero) without-fill?)

View File

@@ -1,8 +1,13 @@
(ns app.main.ui.workspace.tokens.token
;; 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.common.files.tokens
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.workspace.tokens.tinycolor :as tinycolor]
[clojure.set :as set]
[cuerdas.core :as str]))
@@ -128,18 +133,6 @@
(defn color-token? [token]
(= (:type token) :color))
;; FIXME: this should be precalculated ?
(defn is-reference? [token]
(str/includes? (:value token) "{"))
(defn color-bullet-color [token-color-value]
(when-let [tc (tinycolor/valid-color token-color-value)]
(if (tinycolor/alpha tc)
{:color (tinycolor/->hex-string tc)
:opacity (tinycolor/alpha tc)}
(tinycolor/->hex-string tc))))
(defn resolved-token-bullet-color [{:keys [resolved-value] :as token}]
(when (and resolved-value (color-token? token))
(color-bullet-color resolved-value)))

View File

@@ -10,12 +10,15 @@
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv]
[app.common.schema :as sm]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape-tree :as ctst]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
@@ -56,7 +59,16 @@
:instance-head-not-frame
:misplaced-slot
:missing-slot
:shape-ref-cycle})
:shape-ref-cycle
:not-a-variant
:invalid-variant-id
:invalid-variant-properties
:variant-not-main
:parent-not-variant
:variant-bad-name
:variant-bad-variant-name
:variant-component-bad-name
:variant-component-bad-id})
(def ^:private schema:error
[:map {:title "ValidationError"}
@@ -401,6 +413,68 @@
(check-empty-swap-slot shape file page)
(run! #(check-shape % file page libraries :context :not-component) (:shapes shape)))
(defn- check-variant-container
"Shape is a variant container, so:
-all its children should be variants with variant-id equals to the shape-id
-all the components should have the same properties
"
[shape file page]
(let [shape-id (:id shape)
shapes (:shapes shape)
children (map #(ctst/get-shape page %) shapes)
prop-names (cfv/extract-properties-names (first children) (:data file))]
(doseq [child children]
(if (not (ctk/is-variant? child))
(report-error :not-a-variant
(str/ffmt "Shape % should be a variant" (:id child))
child file page)
(do
(when (not= (:variant-id child) shape-id)
(report-error :invalid-variant-id
(str/ffmt "Variant % has invalid variant-id %" (:id child) (:variant-id child))
child file page))
(when (not= prop-names (cfv/extract-properties-names child (:data file)))
(report-error :invalid-variant-properties
(str/ffmt "Variant % has invalid properties %" (:id child) (vec prop-names))
child file page)))))))
(defn- check-variant
"Shape is a variant, so
-it should be a main component
-its parent should be a variant-container
-its variant-name is derived from the properties
-its name should be tha same as its parent's
"
[shape file page]
(let [parent (ctst/get-shape page (:parent-id shape))
component (ctkl/get-component (:data file) (:component-id shape) true)
name (ctv/properties-to-name (:variant-properties component))]
(when-not (ctk/main-instance? shape)
(report-error :variant-not-main
(str/ffmt "Variant % is not a main instance" (:id shape))
shape file page))
(when-not (ctk/is-variant-container? parent)
(report-error :parent-not-variant
(str/ffmt "Variant % has an invalid parent" (:id shape))
shape file page))
(when-not (= name (:variant-name shape))
(report-error :variant-bad-variant-name
(str/ffmt "Variant % has an invalid variant-name" (:id shape))
shape file page))
(when-not (= (:name parent) (:name shape))
(report-error :variant-bad-name
(str/ffmt "Variant % has an invalid name" (:id shape))
shape file page))
(when-not (= (:name parent) (cfh/merge-path-item (:path component) (:name component)))
(report-error :variant-component-bad-name
(str/ffmt "Component % has an invalid name" (:id shape))
shape file page))
(when-not (= (:variant-id component) (:variant-id shape))
(report-error :variant-component-bad-id
(str/ffmt "Variant % has adifferent variant-id than its component" (:id shape))
shape file page))))
(defn- check-shape
"Validate referential integrity and semantic coherence of
a shape and all its children. Report all errors found.
@@ -421,6 +495,12 @@
(check-parent-children shape file page)
(check-frame shape file page)
(when (ctk/is-variant-container? shape)
(check-variant-container shape file page))
(when (ctk/is-variant? shape)
(check-variant shape file page))
(if (ctk/instance-head? shape)
(if (not= :frame (:type shape))
(report-error :instance-head-not-frame
@@ -496,6 +576,20 @@
"This deleted component has shapes with shape-ref pointing to self"
component file nil :cycles-ids cycles-ids))))
(defn- check-variant-component
"Component is a variant, so:
-Its main should be a variant
-It should have at least one variant property"
[component file]
(let [component-page (ctf/get-component-page (:data file) component)
main-component (if (:deleted component)
(dm/get-in component [:objects (:main-instance-id component)])
(ctst/get-shape component-page (:main-instance-id component)))]
(when-not (ctk/is-variant? main-component)
(report-error :not-a-variant
(str/ffmt "Shape % should be a variant" (:id main-component))
main-component file component-page))))
(defn- check-component
"Validate semantic coherence of a component. Report all errors found."
[component file]
@@ -505,7 +599,10 @@
component file nil))
(when (:deleted component)
(check-component-duplicate-swap-slot component file)
(check-ref-cycles component file)))
(check-ref-cycles component file))
(when (ctk/is-variant? component)
(check-variant-component component file)))
(defn- get-orphan-shapes
[{:keys [objects] :as page}]

View File

@@ -0,0 +1,75 @@
;; 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.common.files.variant
(:require
[app.common.data.macros :as dm]
[app.common.types.component :as ctc]
[app.common.types.components-list :as ctcl]
[app.common.types.variant :as ctv]))
(defn find-variant-components
"Find a list of the components thet belongs to this variant-id"
[data objects variant-id]
;; We can't simply filter components, because we need to maintain the order
(->> (dm/get-in objects [variant-id :shapes])
(map #(dm/get-in objects [% :component-id]))
(map #(ctcl/get-component data % true))
reverse))
(defn extract-properties-names
[shape data]
(->> shape
(#(ctcl/get-component data (:component-id %) true))
:variant-properties
(map :name)))
(defn extract-properties-values
[data objects variant-id]
(->> (find-variant-components data objects variant-id)
(mapcat :variant-properties)
(group-by :name)
(map (fn [[k v]]
{:name k
:value (->> v (map :value) distinct)}))))
(defn get-variant-mains
[component data]
(assert (ctv/valid-variant-component? component) "expected valid component variant")
(when-let [variant-id (:variant-id component)]
(let [page-id (:main-instance-page component)
objects (-> (dm/get-in data [:pages-index page-id])
(get :objects))]
(dm/get-in objects [variant-id :shapes]))))
(defn is-secondary-variant?
[component data]
(let [shapes (get-variant-mains component data)]
(and (seq shapes)
(not= (:main-instance-id component) (last shapes)))))
(defn get-primary-variant
[data component]
(let [page-id (:main-instance-page component)
objects (-> (dm/get-in data [:pages-index page-id])
(get :objects))
variant-id (:variant-id component)]
(->> (dm/get-in objects [variant-id :shapes])
peek
(get objects))))
(defn get-primary-component
[data component-id]
(when-let [component (ctcl/get-component data component-id)]
(if (ctc/is-variant? component)
(->> component
(get-primary-variant data)
:component-id
(ctcl/get-component data))
component)))

View File

@@ -79,7 +79,7 @@
:file-schema-validation
;; Reports the schema validation errors internally.
:soft-file-schema-validation
;; Activates the referential integrity validation during update file; related to components-v2.
;; Activates the referential integrity validation during update file.
:file-validation
;; Reports the referential integrity validation errors internally.
:soft-file-validation
@@ -116,6 +116,8 @@
:terms-and-privacy-checkbox
;; Only for developtment.
:tiered-file-data-storage
:token-units
:token-typography-types
:transit-readable-response
:user-feedback
;; TODO: remove this flag.
@@ -125,7 +127,9 @@
:export-file-v3
:render-wasm-dpr
:hide-release-modal
:subscriptions-old})
:subscriptions
:subscriptions-old
:frontend-binary-fills})
(def all-flags
(set/union email login varia))
@@ -146,7 +150,8 @@
:enable-onboarding
:enable-dashboard-templates-section
:enable-google-fonts-provider
:enable-component-thumbnails])
:enable-component-thumbnails
:enable-render-wasm-dpr])
(defn parse
[& flags]

View File

@@ -126,21 +126,20 @@
o)))
(def schema:matrix
{:type :map
:pred valid-matrix?
:type-properties
{:title "matrix"
:description "Matrix instance"
:error/message "expected a valid matrix instance"
:gen/gen (matrix-generator)
:decode/json decode-matrix
:decode/string decode-matrix
:encode/json matrix->json
:encode/string matrix->str
::oapi/type "string"
::oapi/format "matrix"}})
(sm/register! ::matrix schema:matrix)
(sm/register!
{:type ::matrix
:pred valid-matrix?
:type-properties
{:title "matrix"
:description "Matrix instance"
:error/message "expected a valid matrix instance"
:gen/gen (matrix-generator)
:decode/json decode-matrix
:decode/string decode-matrix
:encode/json matrix->json
:encode/string matrix->str
::oapi/type "string"
::oapi/format "matrix"}}))
;; FIXME: deprecated
(s/def ::a ::us/safe-float)

View File

@@ -5,7 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.common.geom.point
(:refer-clojure :exclude [divide min max abs])
(:refer-clojure :exclude [divide min max abs zero?])
(:require
#?(:clj [app.common.fressian :as fres])
#?(:cljs [cljs.core :as c]
@@ -85,24 +85,22 @@
(into {} p)
p))
;; FIXME: make like matrix
(def schema:point
{:type ::point
:pred valid-point?
:type-properties
{:title "point"
:description "Point"
:error/message "expected a valid point"
:gen/gen (->> (sg/tuple (sg/small-int) (sg/small-int))
(sg/fmap #(apply pos->Point %)))
::oapi/type "string"
::oapi/format "point"
:decode/json decode-point
:decode/string decode-point
:encode/json point->json
:encode/string point->str}})
(sm/register! schema:point)
(sm/register!
{:type ::point
:pred valid-point?
:type-properties
{:title "point"
:description "Point"
:error/message "expected a valid point"
:gen/gen (->> (sg/tuple (sg/small-int) (sg/small-int))
(sg/fmap #(apply pos->Point %)))
::oapi/type "string"
::oapi/format "point"
:decode/json decode-point
:decode/string decode-point
:encode/json point->json
:encode/string point->str}}))
(defn point-like?
[{:keys [x y] :as v}]
@@ -470,6 +468,13 @@
(and ^boolean (mth/almost-zero? (dm/get-prop p :x))
^boolean (mth/almost-zero? (dm/get-prop p :y))))
(defn zero?
[p]
(let [x (dm/get-prop p :x)
y (dm/get-prop p :y)]
(and ^boolean (== 0 x)
^boolean (== 0 y))))
(defn lerp
"Calculates a linear interpolation between two points given a tvalue"
[p1 p2 t]

View File

@@ -10,13 +10,11 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes.bool :as gsb]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.constraints :as gct]
[app.common.geom.shapes.corners :as gsc]
[app.common.geom.shapes.fit-frame :as gsff]
[app.common.geom.shapes.intersect :as gsi]
[app.common.geom.shapes.path :as gsp]
[app.common.geom.shapes.transforms :as gtr]
[app.common.math :as mth]))
@@ -166,7 +164,6 @@
(dm/export gtr/calculate-geometry)
(dm/export gtr/update-group-selrect)
(dm/export gtr/update-mask-selrect)
(dm/export gtr/update-bool-selrect)
(dm/export gtr/apply-transform)
(dm/export gtr/transform-shape)
(dm/export gtr/transform-selrect)
@@ -180,12 +177,6 @@
;; Constratins
(dm/export gct/calc-child-modifiers)
;; PATHS
;; FIXME: rename
(dm/export gsp/content->selrect)
(dm/export gsp/transform-content)
(dm/export gsp/open-path?)
;; Intersection
(dm/export gsi/overlaps?)
(dm/export gsi/overlaps-path?)
@@ -193,9 +184,6 @@
(dm/export gsi/has-point-rect?)
(dm/export gsi/rect-contains-shape?)
;; Bool
(dm/export gsb/calc-bool-content)
;; Constraints
(dm/export gct/default-constraints-h)
(dm/export gct/default-constraints-v)
@@ -206,6 +194,7 @@
;; Rect
(dm/export grc/rect->points)
(dm/export grc/center->rect)
;;
(dm/export gsff/fit-frame-modifiers)

View File

@@ -1,29 +0,0 @@
;; 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.common.geom.shapes.bool
(:require
[app.common.data :as d]
[app.common.files.helpers :as cpf]
[app.common.svg.path.bool :as pb]
[app.common.svg.path.shapes-to-path :as stp]))
(defn calc-bool-content
[shape objects]
(let [extract-content-xf
(comp (map (d/getf objects))
(filter (comp not :hidden))
(remove cpf/svg-raw-shape?)
(map #(stp/convert-to-path % objects))
(map :content))
shapes-content
(into [] extract-content-xf (:shapes shape))]
(pb/content-bool (:bool-type shape) shapes-content)))

View File

@@ -10,8 +10,8 @@
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.rect :as grc]
[app.common.geom.shapes.path :as gsp]
[app.common.math :as mth]))
[app.common.math :as mth]
[app.common.types.path :as path]))
(defn shape-stroke-margin
[shape stroke-width]
@@ -104,7 +104,7 @@
(let [strokes (:strokes shape)
open-path? (and ^boolean (cfh/path-shape? shape)
^boolean (gsp/open-path? shape))
^boolean (path/shape-with-open-path? shape))
stroke-width
(->> strokes

View File

@@ -39,7 +39,7 @@
;;
;; 5. If any track still has an infinite growth limit set its growth limit to its base size.
;; - Distribute extra space accross spaned tracks
;; - Distribute extra space accross spaned tracks
;; - Maximize tracks
;;
;; - Expand flexible tracks
@@ -198,7 +198,7 @@
track-list))
(defn add-auto-size
(defn stretch-tracks
[track-list add-size]
(->> track-list
(mapv (fn [{:keys [type size max-size] :as track}]
@@ -357,7 +357,8 @@
to-idx (+ (dec (get cell prop)) (get cell prop-span))
indexed-tracks (subvec (d/enumerate track-list) from-idx to-idx)
to-allocate (size-to-allocate type parent (get children-map shape-id) cell bounds objects)
to-allocate
(size-to-allocate type parent (get children-map shape-id) cell bounds objects)
;; Remove the size and the tracks that are not allocated
[to-allocate total-frs indexed-tracks]
@@ -466,15 +467,15 @@
row-tracks (set-flex-multi-span parent row-tracks children-map shape-cells bounds objects :row)
;; Once auto sizes have been calculated we get calculate the `fr` unit with the remainining size and adjust the size
free-column-space (max 0 (- bound-width (+ column-total-size-nofr column-total-gap)))
free-row-space (max 0 (- bound-height (+ row-total-size-nofr row-total-gap)))
fr-column-space (max 0 (- bound-width (+ column-total-size-nofr column-total-gap)))
fr-row-space (max 0 (- bound-height (+ row-total-size-nofr row-total-gap)))
;; Get the minimum values for fr's
min-column-fr (min-fr-value column-tracks)
min-row-fr (min-fr-value row-tracks)
column-fr (if auto-width? min-column-fr (mth/finite (/ free-column-space column-frs) 0))
row-fr (if auto-height? min-row-fr (mth/finite (/ free-row-space row-frs) 0))
column-fr (if auto-width? min-column-fr (mth/finite (/ fr-column-space column-frs) 0))
row-fr (if auto-height? min-row-fr (mth/finite (/ fr-row-space row-frs) 0))
column-tracks (set-fr-value column-tracks column-fr auto-width?)
row-tracks (set-fr-value row-tracks row-fr auto-height?)
@@ -483,21 +484,21 @@
column-total-size (tracks-total-size column-tracks)
row-total-size (tracks-total-size row-tracks)
free-column-space (max 0 (if auto-width? 0 (- bound-width (+ column-total-size column-total-gap))))
free-row-space (max 0 (if auto-height? 0 (- bound-height (+ row-total-size row-total-gap))))
auto-column-space (max 0 (if auto-width? 0 (- bound-width (+ column-total-size column-total-gap))))
auto-row-space (max 0 (if auto-height? 0 (- bound-height (+ row-total-size row-total-gap))))
column-autos (tracks-total-autos column-tracks)
row-autos (tracks-total-autos row-tracks)
column-add-auto (/ free-column-space column-autos)
row-add-auto (/ free-row-space row-autos)
column-add-auto (/ auto-column-space column-autos)
row-add-auto (/ auto-row-space row-autos)
column-tracks (cond-> column-tracks
(= :stretch (:layout-justify-content parent))
(add-auto-size column-add-auto))
(stretch-tracks column-add-auto))
row-tracks (cond-> row-tracks
(= :stretch (:layout-align-content parent))
(add-auto-size row-add-auto))
(stretch-tracks row-add-auto))
column-total-size (tracks-total-size column-tracks)
row-total-size (tracks-total-size row-tracks)

View File

@@ -13,9 +13,9 @@
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.path :as gpp]
[app.common.geom.shapes.text :as gte]
[app.common.math :as mth]))
[app.common.math :as mth]
[app.common.types.path.segment :as path.segm]))
(defn orientation
"Given three ordered points gives the orientation
@@ -186,7 +186,7 @@
rect-lines (points->lines rect-points)
path-lines (if simple?
(points->lines (:points shape))
(gpp/path->lines shape))
(path.segm/path->lines shape))
start-point (-> shape :content (first) :params (gpt/point))]
(or (intersects-lines? rect-lines path-lines)

View File

@@ -12,11 +12,10 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes.bool :as gshb]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.path :as gpa]
[app.common.math :as mth]
[app.common.types.modifiers :as ctm]))
[app.common.types.modifiers :as ctm]
[app.common.types.path :as path]))
#?(:clj (set! *warn-on-reflection* true))
@@ -77,7 +76,11 @@
position-data)
position-data))))
;; FIXME: revist usage of mutability
;; FIXME: review performance of this; this function is executing too
;; many times, including when the point vector is 0,0. This function
;; can be implemented in function of transform which is already mor
;; performant
(defn move
"Move the shape relatively to its current
position applying the provided delta."
@@ -95,8 +98,8 @@
(d/update-when :x d/safe+ dx)
(d/update-when :y d/safe+ dy)
(d/update-when :position-data move-position-data mvec)
(cond-> (= :bool type) (update :bool-content gpa/move-content mvec))
(cond-> (= :path type) (update :content gpa/move-content mvec)))))
(cond-> (or (= :bool type) (= :path type))
(update :content path/move-content mvec)))))
;; --- Absolute Movement
@@ -317,14 +320,11 @@
points (gco/transform-points (dm/get-prop shape :points) transform-mtx)
selrect (gco/transform-selrect (dm/get-prop shape :selrect) transform-mtx)
shape (if (= type :bool)
(update shape :bool-content gpa/transform-content transform-mtx)
shape)
shape (if (= type :text)
(update shape :position-data transform-position-data transform-mtx)
shape)
shape (if (= type :path)
(update shape :content gpa/transform-content transform-mtx)
shape (if (or (= type :path) (= type :bool))
(update shape :content path/transform-content transform-mtx)
(assoc shape
:x (dm/get-prop selrect :x)
:y (dm/get-prop selrect :y)
@@ -346,39 +346,45 @@
center (gco/points->center points)
selrect (calculate-selrect points center)
transform (calculate-transform points center selrect)
inverse (when (some? transform) (gmt/inverse transform))]
(if-not (and (some? inverse) (some? transform))
shape
(let [type (dm/get-prop shape :type)
rotation (mod (+ (d/nilv (:rotation shape) 0)
(d/nilv (dm/get-in shape [:modifiers :rotation]) 0))
360)
shape (if (= type :bool)
(update shape :bool-content gpa/transform-content transform-mtx)
shape)
[transform inverse]
(let [transform (calculate-transform points center selrect)
inverse (when (some? transform) (gmt/inverse transform))]
(if (and (some? transform) (some? inverse))
[transform inverse]
[(:transform shape (gmt/matrix)) (:transform-inverse shape (gmt/matrix))]))
shape (if (= type :path)
(update shape :content gpa/transform-content transform-mtx)
(assoc shape
:x (dm/get-prop selrect :x)
:y (dm/get-prop selrect :y)
:width (dm/get-prop selrect :width)
:height (dm/get-prop selrect :height)))]
(-> shape
(assoc :transform transform)
(assoc :transform-inverse inverse)
(assoc :selrect selrect)
(assoc :points points)
(assoc :rotation rotation))))))
type (dm/get-prop shape :type)
rotation (mod (+ (d/nilv (:rotation shape) 0)
(d/nilv (dm/get-in shape [:modifiers :rotation]) 0))
360)
shape (if (or (= type :path) (= type :bool))
(update shape :content path/transform-content transform-mtx)
(assoc shape
:x (dm/get-prop selrect :x)
:y (dm/get-prop selrect :y)
:width (dm/get-prop selrect :width)
:height (dm/get-prop selrect :height)))]
(-> shape
(assoc :transform transform)
(assoc :transform-inverse inverse)
(assoc :selrect selrect)
(assoc :points points)
(assoc :rotation rotation))))
(defn apply-transform
"Given a new set of points transformed, set up the rectangle so it keeps
its properties. We adjust de x,y,width,height and create a custom transform"
[shape transform-mtx]
(if ^boolean (gmt/move? transform-mtx)
(cond
(nil? transform-mtx)
shape
^boolean (gmt/move? transform-mtx)
(apply-transform-move shape transform-mtx)
:else
(apply-transform-generic shape transform-mtx)))
(defn- update-group-viewbox
@@ -450,20 +456,7 @@
(assoc :flip-x (-> mask :flip-x))
(assoc :flip-y (-> mask :flip-y)))))
(defn update-bool-selrect
"Calculates the selrect+points for the boolean shape"
[shape children objects]
(let [bool-content (gshb/calc-bool-content shape objects)
shape (assoc shape :bool-content bool-content)
[points selrect] (gpa/content->points+selrect shape bool-content)]
(if (and (some? selrect) (d/not-empty? points))
(-> shape
(assoc :selrect selrect)
(assoc :points points))
(update-group-selrect shape children))))
;; FIXME: revisit
(defn update-shapes-geometry
[objects ids]
(->> ids
@@ -477,7 +470,7 @@
(update-mask-selrect shape children)
(cfh/bool-shape? shape)
(update-bool-selrect shape children objects)
(path/update-bool-shape shape objects)
(cfh/group-shape? shape)
(update-group-selrect shape children)

View File

@@ -98,7 +98,7 @@
(defn encode
[data & {:as opts}]
#?(:clj (j/write-str data opts)
:cljs (.stringify js/JSON (->js data opts))))
:cljs (.stringify js/JSON (->js data opts) nil (:indent opts))))
(defn decode
[data & {:as opts}]

View File

File diff suppressed because it is too large Load Diff

View File

@@ -10,25 +10,36 @@
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.geom.shapes :as gsh]
[app.common.logic.variants :as clv]
[app.common.logic.variant-properties :as clvp]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as ctt]
[app.common.types.token :as cto]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
[clojure.set :as set]))
(defn- generate-unapply-tokens
"When updating attributes that have a token applied, we must unapply it, because the value
of the attribute now has been given directly, and does not come from the token."
[changes objects changed-sub-attr]
(let [mod-obj-changes (->> (:redo-changes changes)
(let [new-objects (pcb/get-objects changes)
mod-obj-changes (->> (:redo-changes changes)
(filter #(= (:type %) :mod-obj)))
text-changed-attrs
(fn [shape]
(let [new-shape (get new-objects (:id shape))
attrs (ctt/get-diff-attrs (:content shape) (:content new-shape))]
(apply set/union (map cto/shape-attr->token-attrs attrs))))
check-attr (fn [shape changes attr]
(let [tokens (get shape :applied-tokens {})
token-attrs (cto/shape-attr->token-attrs attr changed-sub-attr)]
token-attrs (if (or (not= (:type shape) :text) (not= attr :content))
(cto/shape-attr->token-attrs attr changed-sub-attr)
(text-changed-attrs shape))]
(if (some #(contains? tokens %) token-attrs)
(pcb/update-shapes changes [(:id shape)] #(cto/unapply-token-id % token-attrs))
changes)))
@@ -81,163 +92,185 @@
(pcb/update-shapes ids update-fn {:attrs #{:blocked :hidden}}))))
(defn generate-delete-shapes
[changes file page objects ids {:keys [components-v2 ignore-touched component-swap]}]
(let [ids (cfh/clean-loops objects ids)
([changes file page objects ids options]
(generate-delete-shapes (-> changes
(pcb/with-page page)
(pcb/with-objects objects)
(pcb/with-library-data file))
ids
options))
([changes ids {:keys [ignore-touched
allow-altering-copies
;; We will delete the shapes and its descendants.
;; ignore-children-fn is used to ignore some descendants
;; on the deletion process. It should receive a shape and
;; return a boolean
ignore-children-fn]
:or {ignore-children-fn (constantly false)}}]
(let [objects (pcb/get-objects changes)
data (pcb/get-library-data changes)
page-id (pcb/get-page-id changes)
page (or (pcb/get-page changes)
(ctpl/get-page data page-id))
in-component-copy?
(fn [shape-id]
ids (cfh/clean-loops objects ids)
in-component-copy?
(fn [shape-id]
;; Look for shapes that are inside a component copy, but are
;; not the root. In this case, they must not be deleted,
;; but hidden (to be able to recover them more easily).
;; Unless we are doing a component swap, in which case we want
;; If we want to specifically allow altering the copies, this is
;; a special case, like a component swap, in which case we want
;; to delete the old shape
(let [shape (get objects shape-id)]
(and (ctn/has-any-copy-parent? objects shape)
(not component-swap))))
(let [shape (get objects shape-id)]
(and (ctn/has-any-copy-parent? objects shape)
(not allow-altering-copies))))
[ids-to-delete ids-to-hide]
(if components-v2
(loop [ids-seq (seq ids)
ids-to-delete []
ids-to-hide []]
(let [id (first ids-seq)]
(if (nil? id)
[ids-to-delete ids-to-hide]
(if (in-component-copy? id)
(recur (rest ids-seq)
ids-to-delete
(conj ids-to-hide id))
(recur (rest ids-seq)
(conj ids-to-delete id)
ids-to-hide)))))
[ids []])
[ids-to-delete ids-to-hide]
(loop [ids-seq (seq ids)
ids-to-delete []
ids-to-hide []]
(let [id (first ids-seq)]
(if (nil? id)
[ids-to-delete ids-to-hide]
(if (in-component-copy? id)
(recur (rest ids-seq)
ids-to-delete
(conj ids-to-hide id))
(recur (rest ids-seq)
(conj ids-to-delete id)
ids-to-hide)))))
changes (-> changes
(pcb/with-page page)
(pcb/with-objects objects)
(pcb/with-library-data file))
lookup (d/getf objects)
lookup (d/getf objects)
groups-to-unmask
(reduce (fn [group-ids id]
groups-to-unmask
(reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group,
;; the mask condition must be removed, and it must be
;; converted to a normal group.
(let [obj (lookup id)
parent (lookup (:parent-id obj))]
(if (and (:masked-group parent)
(= id (first (:shapes parent))))
(conj group-ids (:id parent))
group-ids)))
#{}
ids-to-delete)
(let [obj (lookup id)
parent (lookup (:parent-id obj))]
(if (and (:masked-group parent)
(= id (first (:shapes parent))))
(conj group-ids (:id parent))
group-ids)))
#{}
ids-to-delete)
interacting-shapes
(filter (fn [shape]
interacting-shapes
(filter (fn [shape]
;; If any of the deleted shapes is the destination of
;; some interaction, this must be deleted, too.
(let [interactions (:interactions shape)]
(some #(and (ctsi/has-destination %)
(contains? ids-to-delete (:destination %)))
interactions)))
(vals objects))
(let [interactions (:interactions shape)]
(some #(and (ctsi/has-destination %)
(contains? ids-to-delete (:destination %)))
interactions)))
(vals objects))
changes
(reduce (fn [changes {:keys [id] :as flow}]
(if (contains? ids-to-delete (:starting-frame flow))
(pcb/set-flow changes id nil)
changes))
changes
(:flows page))
changes
(reduce (fn [changes {:keys [id] :as flow}]
(if (contains? ids-to-delete (:starting-frame flow))
(-> changes
(pcb/with-page page)
(pcb/set-flow id nil))
changes))
changes
(:flows page))
all-parents
(reduce (fn [res id]
all-parents
(reduce (fn [res id]
;; All parents of any deleted shape must be resized.
(into res (cfh/get-parent-ids objects id)))
(d/ordered-set)
(concat ids-to-delete ids-to-hide))
(into res (cfh/get-parent-ids objects id)))
(d/ordered-set)
(concat ids-to-delete ids-to-hide))
all-children
(->> ids-to-delete ;; Children of deleted shapes must be also deleted.
(reduce (fn [res id]
(into res (cfh/get-children-ids objects id)))
[])
(reverse)
(into (d/ordered-set)))
;; Descendants of deleted shapes must be also deleted,
;; except the ignored ones by the function ignore-children-fn
descendants-to-delete
(->> ids-to-delete
(reduce (fn [res id]
(into res (cfh/get-children-ids
objects
id
{:ignore-children-fn ignore-children-fn})))
[])
(reverse)
(into (d/ordered-set)))
find-all-empty-parents
(fn recursive-find-empty-parents [empty-parents]
(let [all-ids (into empty-parents ids-to-delete)
contains? (partial contains? all-ids)
xform (comp (map lookup)
(filter #(or (cfh/group-shape? %) (cfh/bool-shape? %)))
(remove #(->> (:shapes %) (remove contains?) seq))
(map :id))
parents (into #{} xform all-parents)]
(if (= empty-parents parents)
empty-parents
(recursive-find-empty-parents parents))))
find-all-empty-parents
(fn recursive-find-empty-parents [empty-parents]
(let [all-ids (into empty-parents ids-to-delete)
contains? (partial contains? all-ids)
xform (comp (map lookup)
(filter #(or (cfh/group-shape? %) (cfh/bool-shape? %) (ctk/is-variant-container? %)))
(remove #(->> (:shapes %) (remove contains?) seq))
(map :id))
parents (into #{} xform all-parents)]
(if (= empty-parents parents)
empty-parents
(recursive-find-empty-parents parents))))
empty-parents
empty-parents
;; Any parent whose children are all deleted, must be deleted too.
;; Unless we are during a component swap: in this case we are replacing a shape by
;; If we want to specifically allow altering the copies, this is a special case,
;; for example during a component swap. in this case we are replacing a shape by
;; other one, so must not delete empty parents.
(if-not component-swap
(into (d/ordered-set) (find-all-empty-parents #{}))
#{})
(if-not allow-altering-copies
(into (d/ordered-set) (find-all-empty-parents #{}))
#{})
components-to-delete
(if components-v2
(reduce (fn [components id]
(let [shape (get objects id)]
(if (and (= (:component-file shape) (:id file)) ;; Main instances should exist only in local file
(:main-instance shape)) ;; but check anyway
(conj components (:component-id shape))
components)))
[]
(into ids-to-delete all-children))
[])
components-to-delete
(reduce (fn [components id]
(let [shape (get objects id)]
(if (and (= (:component-file shape) (:id data)) ;; Main instances should exist only in local file
(:main-instance shape)) ;; but check anyway
(conj components (:component-id shape))
components)))
[]
(into ids-to-delete descendants-to-delete))
ids-set (set ids-to-delete)
guides-to-delete
(->> (:guides page)
(vals)
(filter #(contains? ids-set (:frame-id %)))
(map :id))
ids-set (set ids-to-delete)
changes (reduce (fn [changes guide-id]
(pcb/set-flow changes guide-id nil))
changes
guides-to-delete)
guides-to-delete
(->> (:guides page)
(vals)
(filter #(contains? ids-set (:frame-id %)))
(map :id))
changes (reduce (fn [changes component-id]
changes (reduce (fn [changes guide-id]
(-> changes
(pcb/with-page page)
(pcb/set-flow guide-id nil)))
changes
guides-to-delete)
changes (reduce (fn [changes component-id]
;; It's important to delete the component before the main instance, because we
;; need to store the instance position if we want to restore it later.
(pcb/delete-component changes component-id (:id page)))
changes
components-to-delete)
(pcb/delete-component changes component-id (:id page)))
changes
components-to-delete)
changes (-> changes
(generate-update-shape-flags ids-to-hide objects {:hidden true})
(pcb/remove-objects all-children {:ignore-touched true})
(pcb/remove-objects ids-to-delete {:ignore-touched ignore-touched})
(pcb/remove-objects empty-parents)
(pcb/resize-parents all-parents)
(pcb/update-shapes groups-to-unmask
(fn [shape]
(assoc shape :masked-group false)))
(pcb/update-shapes (map :id interacting-shapes)
(fn [shape]
(d/update-when shape :interactions
(fn [interactions]
(into []
(remove #(and (ctsi/has-destination %)
(contains? ids-to-delete (:destination %))))
interactions))))))]
[all-parents changes]))
changes (-> changes
(generate-update-shape-flags ids-to-hide objects {:hidden true})
(pcb/remove-objects descendants-to-delete {:ignore-touched true})
(pcb/remove-objects ids-to-delete {:ignore-touched ignore-touched})
(pcb/remove-objects empty-parents)
(pcb/resize-parents all-parents)
(pcb/update-shapes groups-to-unmask
(fn [shape]
(assoc shape :masked-group false)))
(pcb/update-shapes (map :id interacting-shapes)
(fn [shape]
(d/update-when shape :interactions
(fn [interactions]
(into []
(remove #(and (ctsi/has-destination %)
(contains? ids-to-delete (:destination %))))
interactions))))))]
[all-parents changes])))
(defn generate-relocate
@@ -255,7 +288,7 @@
child-heads-ids (map :id child-heads)
variant-heads (filter ctk/is-variant? child-heads)
variant-shapes (filter ctk/is-variant? shapes)
component-main-parent
(ctn/find-component-main objects parent false)
@@ -339,7 +372,19 @@
(map :id)))
index-cell-data (when to-index (ctl/get-cell-by-index parent to-index))
cell (or cell (and index-cell-data [(:row index-cell-data) (:column index-cell-data)]))]
cell (or cell (and index-cell-data [(:row index-cell-data) (:column index-cell-data)]))
;; Parents that are a variant-container that becomes empty
empty-variant-cont (reduce
(fn [to-delete parent-id]
(let [parent (get objects parent-id)]
(if (and (ctk/is-variant-container? parent)
(empty? (remove (set ids) (:shapes parent))))
(conj to-delete (:id parent))
to-delete)))
#{}
(remove #(= % parent-id) all-parents))]
(-> changes
;; Remove layout-item properties when moving a shape outside a layout
@@ -368,82 +413,11 @@
;; Remove variant info and rename when moving outside a variant-container
(cond-> (not (ctk/is-variant-container? parent))
((fn [changes]
(reduce
(fn [changes shape]
(let [new-name (str/replace (:variant-name shape) #", " " / ")
[cpath cname] (cfh/parse-path-name new-name)]
(-> changes
(pcb/update-component (:component-id shape)
#(-> (dissoc % :variant-id :variant-properties)
(assoc :name cname
:path cpath))
{:apply-changes-local-library? true})
(pcb/update-shapes [(:id shape)]
#(-> (dissoc % :variant-id :variant-name)
(assoc :name new-name))))))
changes
variant-heads))))
(clvp/generate-make-shapes-no-variant variant-shapes))
;; Add variant info and rename when moving into a different variant-container
(cond-> (ctk/is-variant-container? parent)
((fn [changes]
(let [get-base-name #(if (some? (:variant-name %))
(str/replace (:variant-name %) #", " " / ")
(:name %))
calc-num-props #(-> %
get-base-name
cfh/split-path
count)
max-path-items (apply max (map calc-num-props child-heads))
first-comp-id (->> parent
:shapes
first
(get objects)
:component-id)
data (pcb/get-library-data changes)
variant-properties (get-in data [:components first-comp-id :variant-properties])
num-props (count variant-properties)
num-new-props (if (< max-path-items num-props)
0
(- max-path-items num-props))
changes (nth
(iterate #(clv/generate-add-new-property % (:id parent)) changes)
num-new-props)]
(reduce
(fn [changes shape]
(if (= (:id parent) (:variant-id shape))
changes ;; do nothing if we aren't changing the parent
(let [base-name (get-base-name shape)
;; we need to get the updated library data to have access to the current properties
data (pcb/get-library-data changes)
props (clv/path-to-properties
base-name
(get-in data [:components first-comp-id :variant-properties]))
variant-name (clv/properties-to-name props)
[cpath cname] (cfh/parse-path-name (:name parent))]
(-> (pcb/update-component changes
(:component-id shape)
#(assoc % :variant-id (:id parent)
:variant-properties props
:name cname
:path cpath)
{:apply-changes-local-library? true})
(pcb/update-shapes [(:id shape)]
#(assoc % :variant-id (:id parent)
:variant-name variant-name
:name (:name parent)))))))
changes
child-heads)))))
(clvp/generate-make-shapes-variant child-heads parent))
;; Move the shapes
(pcb/change-parent parent-id
@@ -518,7 +492,11 @@
(pcb/update-shapes ids #(assoc % :blocked true)))
;; Resize parent containers that need to
(pcb/resize-parents parents))))
(pcb/resize-parents parents)
;; Remove parents when are a variant-container that becomes empty
(cond-> (seq empty-variant-cont)
(#(second (generate-delete-shapes % empty-variant-cont {})))))))
(defn change-show-in-viewer
[shape hide?]

View File

@@ -20,15 +20,15 @@
(let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib)
active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
prev-hidden-token-theme (ctob/get-hidden-theme tokens-lib)
prev-hidden-theme (ctob/get-hidden-theme tokens-lib)
hidden-token-theme (-> (some-> prev-hidden-token-theme (ctob/set-sets active-token-set-names))
(update-theme-fn))]
hidden-theme (-> (some-> prev-hidden-theme (ctob/set-sets active-token-set-names))
(update-theme-fn))]
(-> changes
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} prev-active-token-themes)
(pcb/set-token-theme (:group prev-hidden-token-theme)
(:name prev-hidden-token-theme)
hidden-token-theme))))
(pcb/update-active-token-themes #{(ctob/theme-path hidden-theme)} prev-active-token-themes)
(pcb/set-token-theme (:group prev-hidden-theme)
(:name prev-hidden-theme)
hidden-theme))))
(defn generate-toggle-token-set
"Toggle a token set at `set-name` in `tokens-lib` without modifying a
@@ -41,7 +41,7 @@
[group-path tokens-lib tokens-lib-theme]
(let [deactivate? (contains? #{:all :partial} (ctob/sets-at-path-all-active? tokens-lib group-path))
sets-names (->> (ctob/get-sets-at-path tokens-lib group-path)
(map :name)
(map ctob/get-name)
(into #{}))]
(if deactivate?
(ctob/disable-sets tokens-lib-theme sets-names)

View File

@@ -0,0 +1,220 @@
;; 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.common.logic.variant-properties
(:require
[app.common.data :as d]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctcl]
[app.common.types.variant :as ctv]
[cuerdas.core :as str]))
(defn generate-update-property-name
[changes variant-id pos new-name]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (cfv/find-variant-components data objects variant-id)
props (-> related-components last :variant-properties)
prop-names (mapv :name props)
prop-names (concat (subvec prop-names 0 pos) (subvec prop-names (inc pos)))
new-name (ctv/update-number-in-repeated-item prop-names new-name)]
(reduce (fn [changes component]
(pcb/update-component
changes (:id component)
#(assoc-in % [:variant-properties pos :name] new-name)
{:apply-changes-local-library? true}))
changes
related-components)))
(defn generate-remove-property
[changes variant-id pos]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (cfv/find-variant-components data objects variant-id)]
(reduce (fn [changes component]
(let [props (:variant-properties component)
props (d/remove-at-index props pos)
main-id (:main-instance-id component)
name (ctv/properties-to-name props)]
(-> changes
(pcb/update-component (:id component) #(assoc % :variant-properties props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
changes
related-components)))
(defn generate-update-property-value
[changes component-id pos value]
(let [data (pcb/get-library-data changes)
component (ctcl/get-component data component-id true)
main-id (:main-instance-id component)
name (-> (:variant-properties component)
(update pos assoc :value value)
ctv/properties-to-name)]
(-> changes
(pcb/update-component component-id #(assoc-in % [:variant-properties pos :value] value)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
(defn generate-set-variant-error
[changes component-id value]
(let [data (pcb/get-library-data changes)
component (ctcl/get-component data component-id true)
main-id (:main-instance-id component)]
(-> changes
(pcb/update-shapes [main-id] (if (str/blank? value)
#(dissoc % :variant-error)
#(assoc % :variant-error value))))))
(defn generate-add-new-property
[changes variant-id & {:keys [fill-values? property-name]}]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (cfv/find-variant-components data objects variant-id)
props (-> related-components last :variant-properties)
next-prop-num (ctv/next-property-number props)
property-name (or property-name (str ctv/property-prefix next-prop-num))
prop-names (mapv :name props)
property-name (ctv/update-number-in-repeated-item prop-names property-name)
[_ changes]
(reduce (fn [[num changes] component]
(let [main-id (:main-instance-id component)
update-props #(-> (d/nilv % [])
(conj {:name property-name
:value (if fill-values? (str ctv/value-prefix num) "")}))
update-name #(if fill-values?
(if (str/empty? %)
(str ctv/value-prefix num)
(str % ", " ctv/value-prefix num))
%)]
[(inc num)
(-> changes
(pcb/update-component (:id component)
#(update % :variant-properties update-props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(update % :variant-name update-name)))]))
[1 changes]
related-components)]
changes))
(defn- generate-make-shape-no-variant
[changes shape]
(let [new-name (ctv/variant-name-to-name shape)
[cpath cname] (cfh/parse-path-name new-name)]
(-> changes
(pcb/update-component (:component-id shape)
#(-> (dissoc % :variant-id :variant-properties)
(assoc :name cname
:path cpath))
{:apply-changes-local-library? true})
(pcb/update-shapes [(:id shape)]
#(-> (dissoc % :variant-id :variant-name)
(assoc :name new-name))))))
(defn generate-make-shapes-no-variant
[changes shapes]
(reduce generate-make-shape-no-variant changes shapes))
(defn- create-new-properties-from-variant
[shape min-props data container-name base-properties]
(let [component (ctcl/get-component data (:component-id shape) true)
add-name? (not= (:name component) container-name)
props (ctv/merge-properties base-properties
(:variant-properties component))
new-props (- min-props
(+ (count props)
(if add-name? 1 0)))
props (ctv/add-new-props props (repeat new-props ""))]
(if add-name?
(ctv/add-new-prop props (:name component))
props)))
(defn- create-new-properties-from-non-variant
[shape min-props container-name base-properties]
(let [;; Remove container name from shape name if present
shape-name (ctv/remove-prefix (:name shape) container-name)]
(ctv/path-to-properties shape-name base-properties min-props)))
(defn generate-make-shapes-variant
[changes shapes variant-container]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
variant-id (:id variant-container)
;; If we are cut-pasting a variant-container, this will be null
;; because it hasn't any shapes yet
first-comp-id (->> variant-container
:shapes
first
(get objects)
:component-id)
base-props (->> (get-in data [:components first-comp-id :variant-properties])
(map #(assoc % :value "")))
num-base-props (count base-props)
[cpath cname] (cfh/parse-path-name (:name variant-container))
container-name (:name variant-container)
create-new-properties
(fn [shape min-props]
(if (ctk/is-variant? shape)
(create-new-properties-from-variant shape min-props data container-name base-props)
(create-new-properties-from-non-variant shape min-props container-name base-props)))
total-props (reduce (fn [m shape]
(max m (count (create-new-properties shape num-base-props))))
0
shapes)
num-new-props (if (or (zero? num-base-props)
(< total-props num-base-props))
0
(- total-props num-base-props))
changes (nth
(iterate #(generate-add-new-property % variant-id) changes)
num-new-props)
changes (pcb/update-shapes changes (map :id shapes)
#(assoc % :variant-id variant-id
:name (:name variant-container)))]
(reduce
(fn [changes shape]
(let [component (ctcl/get-component data (:component-id shape) true)]
(if (or (zero? num-base-props) ;; do nothing if there are no base props
(and (= variant-id (:variant-id shape)) ;; or we are only moving the shape inside its parent (it is
(not (:deleted component)))) ;; the same parent and the component isn't deleted)
changes
(let [props (create-new-properties shape total-props)
variant-name (ctv/properties-to-name props)]
(-> (pcb/update-component changes
(:component-id shape)
#(assoc % :variant-id variant-id
:variant-properties props
:name cname
:path cpath)
{:apply-changes-local-library? true})
(pcb/update-shapes [(:id shape)]
#(assoc % :variant-name variant-name)))))))
changes
shapes)))

View File

@@ -1,160 +1,208 @@
;; 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.common.logic.variants
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.types.components-list :as ctcl]
[cuerdas.core :as str]))
[app.common.files.variant :as cfv]
[app.common.logic.libraries :as cll]
[app.common.logic.shapes :as cls]
[app.common.logic.variant-properties :as clvp]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.variant :as ctv]
[app.common.uuid :as uuid]))
(defn generate-add-new-variant
[changes shape variant-id new-component-id new-shape-id prop-num]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
component-id (:component-id shape)
value (str ctv/value-prefix
(-> (cfv/extract-properties-values data objects variant-id)
last
:value
count
inc))
(def property-prefix "Property")
(def property-regex (re-pattern (str property-prefix "(\\d+)")))
(def value-prefix "Value")
(defn find-related-components
"Find a list of the components thet belongs to this variant-id"
[data objects variant-id]
(->> (dm/get-in objects [variant-id :shapes])
(map #(dm/get-in objects [% :component-id]))
(map #(ctcl/get-component data % true))
reverse))
(defn properties-to-name
"Transform the properties into a name, with the values separated by comma"
[properties]
(->> properties
(map :value)
(remove str/empty?)
(str/join ", ")))
(defn next-property-number
"Returns the next property number, to avoid duplicates on the property names"
[properties]
(let [numbers (keep
#(some->> (:name %) (re-find property-regex) second d/parse-integer)
properties)
max-num (if (seq numbers)
(apply max numbers)
0)]
(inc (max max-num (count properties)))))
(defn path-to-properties
"From a list of properties and a name with path, assign each token of the
path as value of a different property"
[path properties]
(let [next-prop-num (next-property-number properties)
cpath (cfh/split-path path)
assigned (mapv #(assoc % :value (nth cpath %2 "")) properties (range))
remaining (drop (count properties) cpath)
new-properties (map-indexed (fn [i v] {:name (str property-prefix (+ next-prop-num i))
:value v}) remaining)]
(into assigned new-properties)))
(defn- dashes-to-end
[property-values]
(let [dashes (if (some #(= % "--") property-values) ["--"] [])]
(concat (remove #(= % "--") property-values) dashes)))
(defn extract-properties-values
[data objects variant-id]
(->> (find-related-components data objects variant-id)
(mapcat :variant-properties)
(group-by :name)
(map (fn [[k v]]
{:name k
:value (->> v
(map #(if (str/empty? (:value %)) "--" (:value %)))
distinct
dashes-to-end)}))))
(defn generate-update-property-name
[changes variant-id pos new-name]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (find-related-components data objects variant-id)]
(reduce (fn [changes component]
(pcb/update-component
changes (:id component)
#(assoc-in % [:variant-properties pos :name] new-name)
{:apply-changes-local-library? true}))
changes
related-components)))
(defn generate-remove-property
[changes variant-id pos]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (find-related-components data objects variant-id)]
(reduce (fn [changes component]
(let [props (:variant-properties component)
props (d/remove-at-index props pos)
main-id (:main-instance-id component)
name (properties-to-name props)]
(-> changes
(pcb/update-component (:id component) #(assoc % :variant-properties props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
changes
related-components)))
(defn generate-update-property-value
[changes component-id pos value]
(let [data (pcb/get-library-data changes)
component (ctcl/get-component data component-id true)
main-id (:main-instance-id component)
name (-> (:variant-properties component)
(update pos assoc :value value)
properties-to-name)]
[new-shape changes] (-> changes
(cll/generate-duplicate-component
{:data data}
component-id
new-component-id
{:new-shape-id new-shape-id :apply-changes-local-library? true}))]
(-> changes
(pcb/update-component component-id #(assoc-in % [:variant-properties pos :value] value)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(assoc % :variant-name name)))))
(clvp/generate-update-property-value new-component-id prop-num value)
(pcb/change-parent (:parent-id shape) [new-shape] 0))))
(defn- generate-path
[path objects base-id shape]
(let [get-type #(case %
:frame :container
:group :container
:rect :shape
:circle :shape
:bool :shape
:path :shape
%)]
(if (= base-id (:id shape))
path
(generate-path (str path " " (:name shape) (get-type (:type shape))) objects base-id (get objects (:parent-id shape))))))
(defn- add-unique-path
"Adds a new property :shape-path to the shape, with the path of the shape.
Suffixes like -1, -2, etc. are added to ensure uniqueness."
[shapes objects base-id]
(letfn [(unique-path [shape counts]
(let [path (generate-path "" objects base-id shape)
num (get counts path 1)]
[(str path "-" num) (update counts path (fnil inc 1))]))]
(first
(reduce
(fn [[result counts] shape]
(let [[shape-path counts'] (unique-path shape counts)]
[(conj result (assoc shape :shape-path shape-path)) counts']))
[[] {}]
shapes))))
(defn generate-add-new-property
[changes variant-id & {:keys [fill-values?]}]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
related-components (find-related-components data objects variant-id)
(defn- keep-swapped-item
"As part of the keep-touched process on a switch, given a child on the original
copy that was swapped (orig-swapped-child), and its related shape on the new copy
(related-shape-in-new), move the orig-swapped-child into the parent of
related-shape-in-new, fix its swap-slot if needed, and then delete
related-shape-in-new"
[changes related-shape-in-new orig-swapped-child ldata page swap-ref-id]
(let [;; Before to the swap, temporary move the previous
;; shape to the root panel to avoid problems when
;; the previous parent is deleted.
before-changes (-> (pcb/empty-changes)
(pcb/with-page page)
(pcb/with-objects (:objects page))
(pcb/change-parent uuid/zero [orig-swapped-child] 0 {:allow-altering-copies true}))
props (-> related-components first :variant-properties)
next-prop-num (next-property-number props)
property-name (str property-prefix next-prop-num)
objects (pcb/get-objects changes)
prev-swap-slot (ctk/get-swap-slot orig-swapped-child)
current-parent (get objects (:parent-id related-shape-in-new))
pos (d/index-of (:shapes current-parent) (:id related-shape-in-new))]
[_ changes]
(reduce (fn [[num changes] component]
(let [main-id (:main-instance-id component)
update-props #(-> (d/nilv % [])
(conj {:name property-name
:value (if fill-values? (str value-prefix num) "")}))
(-> (pcb/concat-changes before-changes changes)
update-name #(if fill-values?
(if (str/empty? %)
(str value-prefix num)
(str % ", " value-prefix num))
%)]
[(inc num)
(-> changes
(pcb/update-component (:id component)
#(update % :variant-properties update-props)
{:apply-changes-local-library? true})
(pcb/update-shapes [main-id] #(update % :variant-name update-name)))]))
[1 changes]
related-components)]
changes))
;; Move the previous shape to the new parent
(pcb/change-parent (:parent-id related-shape-in-new) [orig-swapped-child] pos {:allow-altering-copies true})
;; We need to update the swap slot only when it pointed
;; to the swap-ref-id. Oterwise this is a swapped item
;; inside a nested copy, so we need to keep it.
(cond->
(= prev-swap-slot swap-ref-id)
(pcb/update-shapes
[(:id orig-swapped-child)]
#(ctk/set-swap-slot % (:shape-ref related-shape-in-new))))
;; Delete new non-swapped item
(cls/generate-delete-shapes ldata page objects (d/ordered-set (:id related-shape-in-new)) {:allow-altering-copies true})
second)))
(defn- child-of-swapped?
"Check if any ancestor of a shape (between base-parent-id and shape) was swapped"
[shape objects base-parent-id]
(let [ancestors (->> (ctn/get-parent-heads objects shape)
;; Ignore ancestors ahead of base-parent
(drop-while #(not= base-parent-id (:id %)))
seq)
num-ancestors (count ancestors)
;; Ignore first and last (base-parent and shape)
ancestors (when (and ancestors (<= 3 num-ancestors))
(subvec (vec ancestors) 1 (dec num-ancestors)))]
(some ctk/get-swap-slot ancestors)))
(defn generate-keep-touched
"This is used as part of the switch process, when you switch from
an original-shape to a new-shape. It generate changes to
copy the touched attributes on the shapes children of the original-shape
into the related children of the new-shape.
This relation is tricky. The shapes are related if:
* On the main components, both have the same name (the name on the copies are ignored)
* Both has the same type of ancestors, on the same order (see generate-path for the
translation of the types)"
[changes new-shape original-shape original-shapes page libraries ldata]
(let [objects (pcb/get-objects changes)
container (ctn/make-container page :page)
page-objects (:objects page)
;; Get the touched children of the original-shape
;; Ignore children of swapped items, because
;; they will be moved without change when
;; managing their swapped ancestor
orig-touched (->> (filter (comp seq :touched) original-shapes)
(remove
#(child-of-swapped? %
page-objects
(:id original-shape))))
;; Adds a :shape-path attribute to the children of the new-shape,
;; that contains the type of its ancestors and its name
new-shapes-w-path (add-unique-path
(reverse (cfh/get-children-with-self objects (:id new-shape)))
objects
(:id new-shape))
;; Creates a map to quickly find a child of the new-shape by its shape-path
new-shapes-map (into {} (map (juxt :shape-path identity)) new-shapes-w-path)
;; The original-shape is in a copy. For the relation rules, we need the referenced
;; shape on the main component
orig-ref-shape (ctf/find-ref-shape nil container libraries original-shape)
orig-ref-objects (-> (ctf/get-component-container-from-head orig-ref-shape libraries)
:objects)
;; Adds a :shape-path attribute to the children of the orig-ref-shape,
;; that contains the type of its ancestors and its name
o-ref-shapes-wp (add-unique-path
(reverse (cfh/get-children-with-self orig-ref-objects (:id orig-ref-shape)))
orig-ref-objects
(:id orig-ref-shape))
;; Creates a map to quickly find a child of the orig-ref-shape by its shape-path
o-ref-shapes-p-map (into {} (map (juxt :id :shape-path)) o-ref-shapes-wp)]
;; Process each touched children of the original-shape
(reduce
(fn [changes orig-child-touched]
(let [;; If the orig-child-touched was swapped, get its swap-slot
swap-slot (ctk/get-swap-slot orig-child-touched)
;; orig-child-touched is in a copy. Get the referenced shape on the main component
;; If there is a swap slot, we will get the referenced shape in another way
orig-ref-shape (when-not swap-slot
;; TODO Maybe just get it from o-ref-shapes-wp
(ctf/find-ref-shape nil container libraries orig-child-touched))
orig-ref-id (if swap-slot
;; If there is a swap slot, find the referenced shape id
(ctf/find-ref-id-for-swapped orig-child-touched container libraries)
;; If there is not a swap slot, get the id from the orig-ref-shape
(:id orig-ref-shape))
;; Get the shape path of the referenced main
shape-path (get o-ref-shapes-p-map orig-ref-id)
;; Get its related shape in the children of new-shape: the one that
;; has the same shape-path
related-shape-in-new (get new-shapes-map shape-path)]
;; If there is a related shape, keep its data
(if related-shape-in-new
(if swap-slot
;; If the orig-child-touched was swapped, keep it
(keep-swapped-item changes related-shape-in-new orig-child-touched
ldata page orig-ref-id)
;; If the orig-child-touched wasn't swapped, copy
;; the touched attributes into it
(cll/update-attrs-on-switch
changes related-shape-in-new orig-child-touched
new-shape original-shape orig-ref-shape container))
changes)))
changes
orig-touched)))

View File

@@ -5,15 +5,22 @@
;; Copyright (c) KALEIDOS INC
(ns app.common.media
"Media assets helpers (images, fonts, etc)"
(:require
[clojure.spec.alpha :as s]
[cuerdas.core :as str]))
;; We have added ".ttf" as string to solve a problem with chrome input selector
(def valid-font-types #{"font/ttf" ".ttf" "font/woff", "application/font-woff" "woff" "font/otf" ".otf" "font/opentype"})
(def valid-image-types #{"image/jpeg", "image/png", "image/webp", "image/gif", "image/svg+xml"})
(def str-image-types (str/join "," valid-image-types))
(def str-font-types (str/join "," valid-font-types))
(def font-types
#{"font/ttf"
"font/woff"
"font/otf"
"font/opentype"})
(def image-types
#{"image/jpeg"
"image/png"
"image/webp"
"image/gif"
"image/svg+xml"})
(defn format->extension
[format]
@@ -48,38 +55,28 @@
(defn mtype->extension [mtype]
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
(case mtype
"image/apng" ".apng"
"image/avif" ".avif"
"image/gif" ".gif"
"image/jpeg" ".jpg"
"image/png" ".png"
"image/svg+xml" ".svg"
"image/webp" ".webp"
"application/zip" ".zip"
"application/penpot" ".penpot"
"application/pdf" ".pdf"
"text/plain" ".txt"
"image/apng" ".apng"
"image/avif" ".avif"
"image/gif" ".gif"
"image/jpeg" ".jpg"
"image/png" ".png"
"image/svg+xml" ".svg"
"image/webp" ".webp"
"application/zip" ".zip"
"application/penpot" ".penpot"
"application/pdf" ".pdf"
"text/plain" ".txt"
"font/woff" ".woff"
"font/woff2" ".woff2"
"font/ttf" ".ttf"
"font/otf" ".otf"
"application/octet-stream" ".bin"
nil))
(s/def ::id uuid?)
(s/def ::name string?)
(s/def ::width number?)
(s/def ::height number?)
(s/def ::created-at inst?)
(s/def ::modified-at inst?)
(s/def ::mtype string?)
(s/def ::uri string?)
(s/def ::media-object
(s/keys :req-un [::id
::name
::width
::height
::mtype
::created-at
::modified-at
::uri]))
(defn strip-image-extension
[filename]
(let [image-extensions-re #"(\.png)|(\.jpg)|(\.jpeg)|(\.webp)|(\.gif)|(\.svg)$"]
(str/replace filename image-extensions-re "")))
(defn parse-font-weight
[variant]

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