Compare commits

..

124 Commits

Author SHA1 Message Date
Alejandro
5621c2c394 Merge pull request #3942 from penpot/niwinz-staging-svg-parse-fill-fix
🐛 Fix several issues on svg path parsing
2023-12-28 10:41:52 +01:00
Alejandro
a506be2897 Merge pull request #3940 from penpot/eva-bugfixing-ui-1
💄 Fix some frontend bugs
2023-12-28 10:37:07 +01:00
Andrey Antukh
74447442b8 Add several improvements to svg path parser tests
And properly reorganize legacy implementations
2023-12-28 10:30:56 +01:00
Andrey Antukh
62b1dc2a4b 🐛 Fix incorrect arc to curve conversion in some cases 2023-12-28 10:30:56 +01:00
Andrey Antukh
88779dd50b 📎 Fix naming of fills react component 2023-12-28 10:30:56 +01:00
Andrey Antukh
ae4f14ece2 Reduce allocation on custom-shape-strokes react component 2023-12-28 10:30:56 +01:00
Andrey Antukh
ad185c4215 🐛 Assign correct fill to match standard svg behavior when no fils found
On parsing svg
2023-12-28 10:30:56 +01:00
Eva
1a1e9b4ecd 💄 Fix some frontend bugs 2023-12-28 09:49:47 +01:00
Andrey Antukh
63b264b494 🐛 Fix incorrect last command tracing on svg path parser 2023-12-27 15:38:14 +01:00
Andrey Antukh
fca33f8451 📎 Fix incorrect version 2023-12-27 12:15:02 +01:00
Hosted Weblate
d7fded19aa Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/
2023-12-27 12:00:17 +01:00
Hosted Weblate
7f47131499 Merge branch 'origin/develop' into Weblate. 2023-12-27 12:00:13 +01:00
Andrey Antukh
85829e53af Merge branch 'staging' into develop 2023-12-27 11:59:31 +01:00
Andrey Antukh
16b37230cc Merge branch 'translations' into staging 2023-12-27 11:59:15 +01:00
Stephan Paternotte
c2f48e4075 🌐 Add translations for: Dutch.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2023-12-27 11:58:25 +01:00
Stephan Paternotte
6e5d5cfc50 🌐 Add translations for: Dutch.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2023-12-27 11:58:25 +01:00
Stephan Paternotte
32439a52db 🌐 Add translations for: Dutch.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2023-12-27 11:58:24 +01:00
Stas Haas
ce8c17e589 🌐 Add translations for: German.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2023-12-27 11:58:24 +01:00
Stas Haas
407e7186a4 🌐 Add translations for: German.
Currently translated at 99.6% (1316 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2023-12-27 11:58:24 +01:00
Swapnil C
a589d79043 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-12-27 11:58:24 +01:00
Aimee
5ce362df8e 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-12-27 11:58:24 +01:00
Locness
2120b40abe 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-12-27 11:58:24 +01:00
Louis Chance
705a1c8b10 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-12-27 11:58:24 +01:00
Oğuz Ersen
a9cafdfc9d 🌐 Add translations for: Turkish.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2023-12-27 11:58:24 +01:00
Luigi
0cb80febf0 🌐 Add translations for: French.
Currently translated at 88.2% (1165 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-12-27 11:58:24 +01:00
Luigi
804fe018ef 🌐 Add translations for: French.
Currently translated at 87.8% (1159 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-12-27 11:58:24 +01:00
Yaron Shahrabani
026c32fe00 🌐 Add translations for: Hebrew.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2023-12-27 11:58:24 +01:00
Hugo Vermaak
96d9786f83 🌐 Add translations for: Afrikaans.
Currently translated at 7.6% (101 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/af/
2023-12-27 11:58:24 +01:00
Madalena Melo
9a5c220c87 🌐 Added translation for: Afrikaans. 2023-12-27 11:58:24 +01:00
TheScientistPT
4a2fb6facd 🌐 Add translations for: Portuguese (Portugal).
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2023-12-27 11:58:24 +01:00
AlexTECPlayz
eb575e9daf 🌐 Add translations for: Romanian.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2023-12-27 11:58:24 +01:00
AlexTECPlayz
a7fc53f325 🌐 Add translations for: Romanian.
Currently translated at 99.3% (1311 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2023-12-27 11:58:24 +01:00
TheScientistPT
24bb49d0bf 🌐 Add translations for: Portuguese (Portugal).
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2023-12-27 11:58:24 +01:00
Yaron Shahrabani
67d3a7f9c5 🌐 Add translations for: Hebrew.
Currently translated at 98.3% (1298 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2023-12-27 11:58:24 +01:00
Linerly
1efc40b6c4 🌐 Add translations for: Indonesian.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2023-12-27 11:58:24 +01:00
Stas Haas
304f6ea96e 🌐 Add translations for: German.
Currently translated at 99.3% (1312 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2023-12-27 11:58:24 +01:00
Merih Güz
2509ab3a5d 🌐 Add translations for: Turkish.
Currently translated at 98.4% (1299 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2023-12-27 11:58:24 +01:00
Stephan Paternotte
163ce9f3b7 🌐 Add translations for: Dutch.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2023-12-27 11:56:54 +01:00
Andrey Antukh
0643ba03a1 Merge branch 'staging' into develop 2023-12-27 11:55:38 +01:00
Andrey Antukh
49d719fb45 🐛 Fix incorrect stream handling on shape move
Bug introduced in the beicon2 upgrade part2 commit
2023-12-27 11:53:05 +01:00
Aitor
4fc892a856 🐛 Fix Update main component thumbnail 2023-12-26 14:51:42 +01:00
Andrey Antukh
88c7ac379b 🐛 Fix unexpected rx scheduler saturation on mouse movement burst
Fixed with custom trailing-edge throttling mechanism
2023-12-26 14:14:20 +01:00
Andrey Antukh
ccf063b8ef ⬆️ Upgrade to beicon2 (part2) 2023-12-26 14:14:20 +01:00
Andrey Antukh
96f5a33f5f ⬆️ Upgrade to beicon2 (part1) 2023-12-26 14:14:20 +01:00
Andrey Antukh
ecee15af5b Improve logging on websocket related code (backend) 2023-12-26 14:14:20 +01:00
Stephan Paternotte
607c3c4517 🌐 Add translations for: Dutch.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2023-12-26 14:11:56 +01:00
Alejandro
7648836725 Merge pull request #3931 from penpot/alotor-fix-layout
🐛 Fix problem with absolutes inside grid
2023-12-26 11:41:44 +01:00
Stephan Paternotte
6b12645bfb 🌐 Add translations for: Dutch.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/nl/
2023-12-22 16:12:30 +00:00
Andrés Moya
92934c6cdd 🔧 Add data-test to ease selection in e2e tests 2023-12-18 18:17:27 +01:00
alonso.torres
52c849ce4b 🐛 Fix problem with absolutes inside grid 2023-12-15 22:18:23 +01:00
Eva
f8dd86da34 💄 Add new UI to viewer area 2023-12-15 22:15:22 +01:00
Andrey Antukh
15f81e557c 🐛 Fix unexpected exception on importing some binary files 2023-12-15 17:54:26 +01:00
Andrey Antukh
60fc1a48a5 🔥 Remove obsolete entry on devenv log config 2023-12-15 17:54:26 +01:00
Eva Marco
51b3556b45 Merge pull request #3920 from penpot/ladybenko-fix-ui-modal-shared-library
🐛 fix modal text color + remove lines in shared library modal
2023-12-15 17:09:02 +01:00
Eva Marco
89c14b25ab Merge pull request #3915 from penpot/alotor-fixes-ui
Fixes new UI
2023-12-15 16:28:51 +01:00
Alejandro
051c0dce78 Merge pull request #3926 from penpot/niwinz-staging-bugfixes-2
🐛 More features related bugfixes
2023-12-15 16:16:39 +01:00
Andrey Antukh
a9dd55b8d2 🐛 Fix incorrect feature detection on frontend code 2023-12-15 16:09:57 +01:00
Andrey Antukh
ca50486639 Simplify feature handling on duplicate-file 2023-12-15 15:18:04 +01:00
Andrey Antukh
0ad2e8a0f2 Make retrieving fdata for thumbnail to no modify the file
This prevents that file to be considered opened just for creating
the thumbnail for it.
2023-12-15 15:18:04 +01:00
Andrey Antukh
ac20451ae7 Simplify feature handling on get-file 2023-12-15 15:18:04 +01:00
Andrey Antukh
47baa21d53 🐛 Fix some edge cases on feature handling on binfile import process 2023-12-15 15:18:04 +01:00
Andrey Antukh
eee28a5793 Simplify feature handling on components-v2 migration functions 2023-12-15 15:18:04 +01:00
Andrey Antukh
da15924de0 🔥 Remove empty lines on ui.dashboard ns file 2023-12-15 15:18:04 +01:00
Andrey Antukh
eabed4325a Integrate feature handling on file data migration
Make it less error prone
2023-12-15 15:18:04 +01:00
Andrey Antukh
f01cad9ce7 🐛 Fix incorrect error reporting on clone template error 2023-12-15 15:18:04 +01:00
Andrey Antukh
78260fbc42 🐛 Fix no migration are applied on accessing to a file 2023-12-15 15:18:04 +01:00
Belén Albeza
7b36a7df8b 🐛 fix line-height and color of the message text in all modals 2023-12-15 13:15:11 +01:00
Andrey Antukh
74d985db13 Merge remote-tracking branch 'origin/staging' into develop 2023-12-14 15:04:45 +01:00
Alejandro
6b042be65c Merge pull request #3924 from penpot/niwinz-staging-bugfixes-1
🐛 Features bugfixes
2023-12-14 12:17:35 +01:00
Alejandro
86a4833c4a Merge pull request #3923 from penpot/hiru-components-bugfix-1
Some components bugfix
2023-12-14 12:15:36 +01:00
Andrey Antukh
e4d86cbb39 🐛 Fix incorrect feature handling on checking file features 2023-12-14 12:09:31 +01:00
Andrey Antukh
611594a392 Add general features handling improvements 2023-12-14 10:35:24 +01:00
Andrey Antukh
bdb1742d59 🐛 Fix incorrect feature checking on move project 2023-12-14 10:35:24 +01:00
Andrey Antukh
ba01f314dd 🐛 Fix incorrect feature context setup on file update 2023-12-14 10:35:24 +01:00
Andrey Antukh
517c913af9 Improve feature handling on file importation process 2023-12-14 10:35:24 +01:00
Andrey Antukh
08b9178a65 🐛 Fix incorrect behavior on set-file-shared rpc method 2023-12-14 10:35:24 +01:00
Andrey Antukh
b19a6321de 🐛 Fix feature validation on moving projects 2023-12-14 10:35:24 +01:00
Andrey Antukh
2dbe7bca07 Add general improvements to file validation and repair api 2023-12-14 10:35:24 +01:00
Andrés Moya
fd5fd87360 🐛 Fix propagation of changes with nested components 2023-12-14 08:54:00 +01:00
Alejandro Alonso
a4919e3b53 Merge remote-tracking branch 'origin/staging' into develop 2023-12-14 06:43:39 +01:00
Alejandro
419cc2e848 Merge pull request #3919 from penpot/palba-bugfixing-10
Bugfixing
2023-12-14 06:34:48 +01:00
Andrey Antukh
db713c2d61 Merge remote-tracking branch 'origin/staging' into develop 2023-12-13 21:28:19 +01:00
Pablo Alba
b5296613de 🐛 Validate and repair also orphan shapes 2023-12-13 17:59:55 +01:00
Belén Albeza
05614a345d 🐛 fix modal text color + remove lines in shared library modal 2023-12-13 16:32:26 +01:00
alonso.torres
4832b718da 🐛 Fix problem with menu colors 2023-12-13 16:12:41 +01:00
alonso.torres
625f99c933 🐛 Fix tutorial button style 2023-12-13 16:12:41 +01:00
alonso.torres
6ed1d223bf 🐛 Add scroll to login 2023-12-13 16:12:41 +01:00
alonso.torres
39856c8f6a 🐛 Improve inspect tab 2023-12-13 16:12:41 +01:00
alonso.torres
e2446fcc62 🐛 Fix problems in inspect tab 2023-12-13 16:12:41 +01:00
alonso.torres
9834195f0e 🐛 Fix scroll so it's not hidden against palette 2023-12-13 16:12:41 +01:00
alonso.torres
ffa37d26fc 🐛 Add padding to inspect help 2023-12-13 16:12:41 +01:00
alonso.torres
db3d7af0b8 🐛 Hide guides parameters when hiding guides 2023-12-13 16:12:41 +01:00
alonso.torres
85c301c26b 🐛 Change order to create layout panel 2023-12-13 16:12:41 +01:00
alonso.torres
18d954faba 🐛 Fix overflow library name 2023-12-13 16:12:41 +01:00
alonso.torres
7d98833e4e 🐛 Reorder buttons for layout menu 2023-12-13 16:12:41 +01:00
alonso.torres
aa3fe1cd2b 🐛 Fix problems with assets 2023-12-13 16:12:41 +01:00
alonso.torres
e884cba002 🐛 Fix visual problems 2023-12-13 16:12:41 +01:00
alonso.torres
030ff398ed Improved dashboard thumbnails 2023-12-13 16:12:41 +01:00
alonso.torres
5522430afe 🐛 Fix hover style in dashboard 2023-12-13 16:12:41 +01:00
alonso.torres
6969f8be03 🐛 Fix presence widget 2023-12-13 16:12:41 +01:00
alonso.torres
9ac8e72b23 🐛 Fix strange visual in assets 2023-12-13 16:12:41 +01:00
Andrés Moya
02986f81bd 🐛 Avoid linking to remote main, when adding a shape to a copy
For example, when doing a reset overrides after a component switch
2023-12-13 15:37:15 +01:00
Pablo Alba
22f4ee82bb 🐛 Show component name in copies component panel for deleted ones 2023-12-13 13:03:14 +01:00
Alejandro Alonso
600b5ecdb7 📎 Prepare new development cycle 2023-12-13 12:49:55 +01:00
Stas Haas
527d7afb00 🌐 Add translations for: German.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2023-11-28 10:04:03 +01:00
Stas Haas
d9fa8bbb06 🌐 Add translations for: German.
Currently translated at 99.6% (1316 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2023-11-23 11:04:30 +00:00
Swapnil C
780edaac3b 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-11-16 19:03:41 +01:00
Aimee
0b6633dc44 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-11-11 14:34:11 +01:00
Locness
ed2461c3ec 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-11-11 14:34:11 +01:00
Louis Chance
ae535b8ea1 🌐 Add translations for: French.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-11-11 14:34:11 +01:00
Oğuz Ersen
7c2fa2392f 🌐 Add translations for: Turkish.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2023-11-06 19:34:48 +01:00
Luigi
736a26a46a 🌐 Add translations for: French.
Currently translated at 88.2% (1165 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-11-06 19:34:47 +01:00
Luigi
23853345cc 🌐 Add translations for: French.
Currently translated at 87.8% (1159 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/fr/
2023-11-05 18:33:20 +01:00
Yaron Shahrabani
69cffe43f3 🌐 Add translations for: Hebrew.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2023-10-16 06:09:42 +02:00
Hugo Vermaak
93df5354e5 🌐 Add translations for: Afrikaans.
Currently translated at 7.6% (101 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/af/
2023-10-13 20:01:12 +02:00
Madalena Melo
161e8b01a5 🌐 Added translation for: Afrikaans. 2023-10-12 11:45:43 +02:00
TheScientistPT
958b442b2e 🌐 Add translations for: Portuguese (Portugal).
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2023-10-11 14:01:31 +02:00
AlexTECPlayz
d3404bd359 🌐 Add translations for: Romanian.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2023-10-10 10:01:12 +00:00
AlexTECPlayz
b49ba9572e 🌐 Add translations for: Romanian.
Currently translated at 99.3% (1311 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/ro/
2023-10-09 09:00:29 +00:00
TheScientistPT
6a397eb262 🌐 Add translations for: Portuguese (Portugal).
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/pt_PT/
2023-10-07 12:12:03 +00:00
Yaron Shahrabani
c09ca021e9 🌐 Add translations for: Hebrew.
Currently translated at 98.3% (1298 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/he/
2023-10-07 12:12:02 +00:00
Linerly
85fbc0352c 🌐 Add translations for: Indonesian.
Currently translated at 100.0% (1320 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/id/
2023-10-07 12:12:02 +00:00
Stas Haas
271384718d 🌐 Add translations for: German.
Currently translated at 99.3% (1312 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/de/
2023-10-07 12:12:01 +00:00
Merih Güz
ff8b6fbd8c 🌐 Add translations for: Turkish.
Currently translated at 98.4% (1299 of 1320 strings)

Translation: Penpot/frontend
Translate-URL: https://hosted.weblate.org/projects/penpot/frontend/tr/
2023-10-07 12:12:00 +00:00
332 changed files with 9439 additions and 4450 deletions

View File

@@ -15,7 +15,7 @@
:hooks
{:analyze-call
{app.common.data.macros/export hooks.export/export
potok.core/reify hooks.export/potok-reify
potok.v2.core/reify hooks.export/potok-reify
app.util.services/defmethod hooks.export/service-defmethod
app.common.record/defrecord hooks.export/penpot-defrecord
app.db/with-atomic hooks.export/penpot-with-atomic

View File

@@ -1,5 +1,17 @@
# CHANGELOG
## :rocket: Next
### :boom: Breaking changes & Deprecations
### :sparkles: New features
### :bug: Bugs fixed
### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!)
## 1.20.0
### :boom: Breaking changes & Deprecations

View File

@@ -30,7 +30,6 @@
<Logger name="app.redis" level="info" />
<Logger name="app.rpc.rlimit" level="info" />
<Logger name="app.rpc.climit" level="info" />
<Logger name="app.rpc.mutations.files" level="info" />
<Logger name="app.common.files.migrations" level="info" />
<Logger name="app.loggers" level="debug" additivity="false">

View File

@@ -9,7 +9,6 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.files.changes :as cp]
[app.common.files.changes-builder :as fcb]
[app.common.files.helpers :as cfh]
@@ -769,58 +768,58 @@
fdata (migrate-graphics fdata)]
(update fdata :options assoc :components-v2 true)))))
(defn- process-fdata
[fdata id]
(-> fdata
(assoc :id id)
(fdata/process-pointers deref)
(fmg/migrate-data)))
(defn- get-file
[system id]
(binding [pmap/*load-fn* (partial fdata/load-pointer system id)]
(-> (files/get-file system id :migrate? false)
(update :data assoc :id id)
(update :data fdata/process-pointers deref)
(fmg/migrate-file))))
(defn- validate-file!
[file libs throw-on-validate?]
(try
(cfv/validate-file! file libs)
(cfv/validate-file-schema! file)
(catch Throwable cause
(if throw-on-validate?
(throw cause)
(l/wrn :hint "migrate:file:validation-error" :cause cause)))))
(defn- process-file
[{:keys [::db/conn] :as system} id & {:keys [validate? throw-on-validate?]}]
(binding [pmap/*tracked* (pmap/create-tracked)
pmap/*load-fn* (partial fdata/load-pointer *system* id)]
(let [file (get-file system id)
(let [file (binding [cfeat/*new* (atom #{})]
(-> (files/get-file system id :migrate? false)
(update :data process-fdata id)
(update :features into (deref cfeat/*new*))
(update :features cfeat/migrate-legacy-features)))
libs (->> (files/get-file-libraries conn id)
(into [file] (comp (map :id) (map (partial get-file system))))
(d/index-by :id))
libs (->> (files/get-file-libraries conn id)
(into [file] (map (fn [{:keys [id]}]
(binding [pmap/*load-fn* (partial fdata/load-pointer system id)]
(-> (files/get-file system id :migrate? false)
(update :data process-fdata id))))))
(d/index-by :id))
file (-> file
(update :data migrate-fdata libs)
(update :features conj "components/v2"))
pmap? (contains? (:features file) "fdata/pointer-map")
_ (when validate?
(validate-file! file libs throw-on-validate?))
file (-> file
(update :data migrate-fdata libs)
(update :features conj "components/v2")
(cond-> pmap? (fdata/enable-pointer-map)))
]
file (if (contains? (:features file) "fdata/objects-map")
(fdata/enable-objects-map file)
file)
(when validate?
(if throw-on-validate?
(cfv/validate-file! file libs)
(doseq [error (cfv/validate-file file libs)]
(l/wrn :hint "migrate:file:validation-error"
:file-id (str (:id file))
:file-name (:name file)
:error error))))
file (if (contains? (:features file) "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (fdata/enable-pointer-map file)]
(fdata/persist-pointers! system id)
file))
file)]
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" (:features file))
:revn (:revn file)}
{:id (:id file)})
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" (:features file))
:revn (:revn file)}
{:id (:id file)}
{::db/return-keys? false})
(when pmap?
(fdata/persist-pointers! system id))
(dissoc file :data))))
(dissoc file :data)))
(defn migrate-file!
[system file-id & {:keys [validate? throw-on-validate?]}]

View File

@@ -81,6 +81,7 @@
(cond
(or (= code :spec-validation)
(= code :params-validation)
(= code :schema-validation)
(= code :data-validation))
(let [explain (ex/explain data)]
{::rres/status 400

View File

@@ -11,7 +11,7 @@
[app.common.exceptions :as ex]
[app.common.features :as cfeat]
[app.common.files.defaults :as cfd]
[app.common.files.migrations :as pmg]
[app.common.files.migrations :as fmg]
[app.common.files.validate :as fval]
[app.common.fressian :as fres]
[app.common.logging :as l]
@@ -693,17 +693,6 @@
(vswap! *state* update :index update-index files)
(vswap! *state* assoc :version version :files files)))
(defn- postprocess-file
[file]
(cond-> file
(and (contains? cfeat/*current* "fdata/objects-map")
(not (contains? cfeat/*previous* "fdata/objects-map")))
(feat.fdata/enable-objects-map)
(and (contains? cfeat/*current* "fdata/pointer-map")
(not (contains? cfeat/*previous* "fdata/pointer-map")))
(feat.fdata/enable-pointer-map)))
(defn- get-remaped-thumbnails
[thumbnails file-id]
(mapv (fn [thumbnail]
@@ -712,6 +701,31 @@
(update :object-id #(str/replace-first % #"^(.*?)/" (str file-id "/")))))
thumbnails))
(defn- process-file
[{:keys [id] :as file}]
(-> file
(update :data (fn [fdata]
(-> fdata
(assoc :id id)
(dissoc :recent-colors)
(cond-> (> (:version fdata) cfd/version)
(assoc :version cfd/version))
;; FIXME: We're temporarily activating all
;; migrations because a problem in the
;; environments messed up with the version
;; numbers When this problem is fixed delete
;; the following line
(cond-> (> (:version fdata) 22)
(assoc :version 22)))))
(fmg/migrate-file)
(update :data (fn [fdata]
(-> fdata
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(d/without-nils))))))
(defmethod read-section :v1/files
[{:keys [::db/conn ::input ::project-id ::enabled-features ::timestamp ::overwrite?] :as system}]
@@ -722,19 +736,7 @@
file-id (:id file)
file-id' (lookup-index file-id)
thumbnails (:thumbnails file)
file (update file :features cfeat/migrate-legacy-features)
features (-> enabled-features
(set/difference cfeat/frontend-only-features)
(set/union (cfeat/check-supported-features! (:features file))))]
;; All features that are enabled and requires explicit migration
;; are added to the state for a posterior migration step
(doseq [feature (-> enabled-features
(set/difference cfeat/no-migration-features)
(set/difference (:features file)))]
(vswap! *state* update :pending-to-migrate (fnil conj []) [feature file-id']))
thumbnails (:thumbnails file)]
(when (not= file-id expected-file-id)
(ex/raise :type :validation
@@ -765,63 +767,62 @@
(l/dbg :hint "update media references" ::l/sync? true)
(vswap! *state* update :media into (map #(update % :id lookup-index)) media))
(binding [cfeat/*current* features
cfeat/*previous* (:features file)
pmap/*tracked* (atom {})]
(let [file (-> file
(assoc :id file-id')
(process-file))
(let [params (-> file
(assoc :id file-id')
(assoc :features features)
(assoc :project-id project-id)
(assoc :created-at timestamp)
(assoc :modified-at timestamp)
(dissoc :thumbnails)
(update :data (fn [data]
(-> data
(dissoc :recent-colors)
(assoc :id file-id')
(cond-> (> (:version data) cfd/version)
(assoc :version cfd/version))
;; All features that are enabled and requires explicit migration are
;; added to the state for a posterior migration step.
_ (doseq [feature (-> enabled-features
(set/difference cfeat/no-migration-features)
(set/difference (:features file)))]
(vswap! *state* update :pending-to-migrate (fnil conj []) [feature file-id']))
;; FIXME: We're temporarily activating all
;; migrations because a problem in the
;; environments messed up with the version
;; numbers When this problem is fixed delete
;; the following line
(assoc :version 22)
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(pmg/migrate-data)
(d/without-nils)))))
file (-> file
(assoc :project-id project-id)
(assoc :created-at timestamp)
(assoc :modified-at timestamp)
(dissoc :thumbnails)
(update :features
(fn [features]
(let [features (cfeat/check-supported-features! features)]
(-> enabled-features
(set/difference cfeat/frontend-only-features)
(set/union features))))))
params (if (contains? cf/flags :file-schema-validation)
(fval/validate-file-schema! params)
params)
_ (when (contains? cf/flags :file-schema-validation)
(fval/validate-file-schema! file))
_ (when (contains? cf/flags :soft-file-schema-validation)
(try
(fval/validate-file-schema! params)
(catch Throwable cause
(l/error :hint "file schema validation error" :cause cause))))
_ (when (contains? cf/flags :soft-file-schema-validation)
(let [result (ex/try! (fval/validate-file-schema! file))]
(when (ex/exception? result)
(l/error :hint "file schema validation error" :cause result))))
params (-> params
(postprocess-file)
(update :features #(db/create-array conn "text" %))
(update :data blob/encode))]
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (feat.fdata/enable-pointer-map file)]
(feat.fdata/persist-pointers! system file-id')
file))
file)
file (-> file
(update :features #(db/create-array conn "text" %))
(update :data blob/encode))]
(l/dbg :hint "create file" :id (str file-id') ::l/sync? true)
(if overwrite?
(create-or-update-file! conn params)
(db/insert! conn :file params))
(feat.fdata/persist-pointers! system file-id')
(create-or-update-file! conn file)
(db/insert! conn :file file))
(when overwrite?
(db/delete! conn :file-thumbnail {:file-id file-id'}))
file-id')))))
file-id'))))
(defmethod read-section :v1/rels
[{:keys [::db/conn ::input ::timestamp]}]

View File

@@ -34,7 +34,6 @@
[app.util.pointer-map :as pmap]
[app.util.services :as sv]
[app.util.time :as dt]
[clojure.set :as set]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]))
@@ -225,9 +224,9 @@
(defn- migrate-file
[{:keys [::db/conn] :as cfg} {:keys [id] :as file}]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)
cfeat/*new* (atom #{})]
pmap/*tracked* (pmap/create-tracked)]
(let [file (fmg/migrate-file file)]
;; NOTE: when file is migrated, we break the rule of no perform
;; mutations on get operations and update the file with all
;; migrations applied
@@ -235,23 +234,26 @@
;; NOTE: the following code will not work on read-only mode, it
;; is a known issue; we keep is not implemented until we really
;; need this
(if (fmg/migrated? file)
(let [file (update file :features cfeat/migrate-legacy-features)
features (set/union (deref cfeat/*new*) (:features file))]
(db/update! conn :file
{:data (blob/encode (:data file))
:features (db/create-array conn "text" features)}
{:id id})
(feat.fdata/persist-pointers! cfg id)
(assoc file :features features))
file))))
(when (fmg/migrated? file)
(db/update! conn :file
{:data (blob/encode (:data 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)))
file)))
(defn get-file
[{:keys [::db/conn] :as cfg} id & {:keys [project-id migrate?
[{:keys [::db/conn] :as cfg} id & {:keys [project-id
migrate?
include-deleted?
lock-for-update?]
:or {include-deleted? false
lock-for-update? false}}]
lock-for-update? false
migrate? true}}]
(dm/assert!
"expected cfg with valid connection"
(db/connection-map? cfg))
@@ -706,11 +708,12 @@
(cfeat/check-client-features! (:features params))
(cfeat/check-file-features! (:features file) (:features params)))
{:name (:name file)
:components-count (count (ctkl/components-seq (:data file)))
:graphics-count (count (get-in file [:data :media] []))
:colors-count (count (get-in file [:data :colors] []))
:typography-count (count (get-in file [:data :typographies] []))}))
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
{:name (:name file)
:components-count (count (ctkl/components-seq (:data file)))
:graphics-count (count (get-in file [:data :media] []))
:colors-count (count (get-in file [:data :colors] []))
:typography-count (count (get-in file [:data :typographies] []))})))
(sv/defmethod ::get-file-summary
"Retrieve a file summary by its ID. Only authenticated users."
@@ -856,8 +859,10 @@
(true? (:is-shared params)))
(let [file (assoc file :is-shared true)]
(db/update! conn :file
{:is-shared false}
{:id id})
{:is-shared true}
{:id id}
::db/return-keys? false)
file)
:else

View File

@@ -10,6 +10,7 @@
[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]
@@ -105,24 +106,12 @@
(letfn [;; function responsible on finding the frame marked to be
;; used as thumbnail; the returned frame always have
;; the :page-id set to the page that it belongs.
(get-thumbnail-frame [file]
;; NOTE: this is a hack for avoid perform blocking
;; operation inside the for loop, clojure lazy-seq uses
;; synchronized blocks that does not plays well with
;; virtual threads where all rpc methods calls are
;; dispatched, so we need to perform the load operation
;; first. This operation forces all pointer maps load into
;; the memory.
;;
;; FIXME: this is no longer true with clojure>=1.12
(let [{:keys [data]} (update file :data feat.fdata/process-pointers pmap/load!)]
;; Then proceed to find the frame set for thumbnail
(d/seek #(or (:use-for-thumbnail %)
(:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21)
(for [page (-> data :pages-index vals)
frame (-> page :objects ctt/get-frames)]
(assoc frame :page-id (:id page))))))
(get-thumbnail-frame [{:keys [data]}]
(d/seek #(or (:use-for-thumbnail %)
(:use-for-thumbnail? %)) ; NOTE: backward comp (remove on v1.21)
(for [page (-> data :pages-index vals)
frame (-> page :objects ctt/get-frames)]
(assoc frame :page-id (:id page)))))
;; function responsible to filter objects data structure of
;; all unneeded shapes if a concrete frame is provided. If no
@@ -166,30 +155,29 @@
objects)))]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(let [frame (get-thumbnail-frame file)
frame-id (:id frame)
page-id (or (:page-id frame)
(-> data :pages first))
(let [frame (get-thumbnail-frame file)
frame-id (:id frame)
page-id (or (:page-id frame)
(-> data :pages first))
page (dm/get-in data [:pages-index page-id])
page (cond-> page (pmap/pointer-map? page) deref)
frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page))))
page (dm/get-in data [:pages-index page-id])
page (cond-> page (pmap/pointer-map? page) deref)
frame-ids (if (some? frame) (list frame-id) (map :id (ctt/get-frames (:objects page))))
obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids)
thumbs (get-object-thumbnails conn id obj-ids)]
obj-ids (map #(thc/fmt-object-id (:id file) page-id % "frame") frame-ids)
thumbs (get-object-thumbnails conn id obj-ids)]
(cond-> page
;; If we have frame, we need to specify it on the page level
;; and remove the all other unrelated objects.
(some? frame-id)
(-> (assoc :thumbnail-frame-id frame-id)
(update :objects filter-objects frame-id))
(cond-> page
;; If we have frame, we need to specify it on the page level
;; and remove the all other unrelated objects.
(some? frame-id)
(-> (assoc :thumbnail-frame-id frame-id)
(update :objects filter-objects frame-id))
;; Assoc the available thumbnails and prune not visible shapes
;; for avoid transfer unnecessary data.
:always
(update :objects assoc-thumbnails page-id thumbs))))))
;; Assoc the available thumbnails and prune not visible shapes
;; for avoid transfer unnecessary data.
:always
(update :objects assoc-thumbnails page-id thumbs)))))
(def ^:private
schema:get-file-data-for-thumbnail
@@ -221,7 +209,10 @@
:profile-id profile-id
:file-id file-id)
file (files/get-file cfg 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)))]
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))

View File

@@ -182,39 +182,39 @@
(defn update-file
[{:keys [::db/conn ::mtx/metrics] :as cfg}
{:keys [id file features changes changes-with-metadata] :as params}]
(binding [cfeat/*current* features
cfeat/*previous* (:features file)]
(let [update-fn (cond-> update-file*
(contains? features "fdata/pointer-map")
(wrap-with-pointer-map-context)
(let [features (-> features
(set/difference cfeat/frontend-only-features)
(set/union (:features file)))
(contains? features "fdata/objects-map")
(wrap-with-objects-map-context))
update-fn (cond-> update-file*
(contains? features "fdata/pointer-map")
(wrap-with-pointer-map-context)
changes (if changes-with-metadata
(->> changes-with-metadata (mapcat :changes) vec)
(vec changes))
(contains? features "fdata/objects-map")
(wrap-with-objects-map-context))
features (-> features
(set/difference cfeat/frontend-only-features)
(set/union (:features file)))]
changes (if changes-with-metadata
(->> changes-with-metadata (mapcat :changes) vec)
(vec changes))]
(when (> (:revn params)
(:revn file))
(ex/raise :type :validation
:code :revn-conflict
:hint "The incoming revision number is greater that stored version."
:context {:incoming-revn (:revn params)
:stored-revn (:revn file)}))
(when (> (:revn params)
(:revn file))
(ex/raise :type :validation
:code :revn-conflict
:hint "The incoming revision number is greater that stored version."
:context {:incoming-revn (:revn params)
:stored-revn (:revn file)}))
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
(mtx/run! metrics {:id :update-file-changes :inc (count changes)})
(when (not= features (:features file))
(let [features (db/create-array conn "text" features)]
(db/update! conn :file
{:features features}
{:id id})))
(when (not= features (:features file))
(let [features (db/create-array conn "text" features)]
(db/update! conn :file
{:features features}
{:id id})))
(binding [cfeat/*current* features
cfeat/*previous* (:features file)]
(let [file (assoc file :features features)
params (-> params
(assoc :file file)
@@ -276,9 +276,7 @@
(try
(val/validate-file-schema! file)
(catch Throwable cause
(l/error :hint "file schema validation error" :cause cause)))
file)
(l/error :hint "file schema validation error" :cause cause))))
(defn- soft-validate-file!
[file libs]
@@ -286,8 +284,7 @@
(val/validate-file! file libs)
(catch Throwable cause
(l/error :hint "file validation error"
:cause cause)))
file)
:cause cause))))
(defn- update-file-data
[{:keys [::db/conn] :as cfg} file changes skip-validate]
@@ -300,7 +297,8 @@
;; WARNING: this ruins performance; maybe we need to find
;; some other way to do general validation
libs (when (and (contains? cf/flags :file-validation)
libs (when (and (or (contains? cf/flags :file-validation)
(contains? cf/flags :soft-file-validation))
(not skip-validate))
(->> (files/get-file-libraries conn (:id file))
(into [file] (map (fn [{:keys [id]}]
@@ -309,37 +307,37 @@
(-> (files/get-file cfg id :migrate? false)
(feat.fdata/process-pointers deref) ; ensure all pointers resolved
(fmg/migrate-file))))))
(d/index-by :id)))]
(d/index-by :id)))
(-> (files/check-version! file)
(update :revn inc)
(update :data cpc/process-changes changes)
file (-> (files/check-version! file)
(update :revn inc)
(update :data cpc/process-changes changes))]
;; If `libs` is defined, then full validation is performed
(cond-> (contains? cf/flags :soft-file-validation)
(soft-validate-file! libs))
(when (contains? cf/flags :soft-file-validation)
(soft-validate-file! file libs))
(cond-> (contains? cf/flags :soft-file-schema-validation)
(soft-validate-file-schema!))
(when (contains? cf/flags :soft-file-schema-validation)
(soft-validate-file-schema! file))
(cond-> (and (contains? cf/flags :file-validation)
(not skip-validate))
(val/validate-file! libs))
(when (and (contains? cf/flags :file-validation)
(not skip-validate))
(val/validate-file! file libs))
(cond-> (and (contains? cf/flags :file-schema-validation)
(not skip-validate))
(val/validate-file-schema!))
(when (and (contains? cf/flags :file-schema-validation)
(not skip-validate))
(val/validate-file-schema! file))
(cond-> (and (contains? cfeat/*current* "fdata/objects-map")
(not (contains? cfeat/*previous* "fdata/objects-map")))
(feat.fdata/enable-objects-map))
(cond-> file
(and (contains? cfeat/*current* "fdata/objects-map")
(not (contains? cfeat/*previous* "fdata/objects-map")))
(feat.fdata/enable-objects-map)
(cond-> (and (contains? cfeat/*current* "fdata/pointer-map")
(not (contains? cfeat/*previous* "fdata/pointer-map")))
(feat.fdata/enable-pointer-map))
(update :data blob/encode))))
(and (contains? cfeat/*current* "fdata/pointer-map")
(not (contains? cfeat/*previous* "fdata/pointer-map")))
(feat.fdata/enable-pointer-map)
:always
(update :data blob/encode))))
(defn- take-snapshot?
"Defines the rule when file `data` snapshot should be saved."

View File

@@ -13,6 +13,7 @@
[app.common.files.migrations :as pmg]
[app.common.schema :as sm]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.features.fdata :as feat.fdata]
[app.http.sse :as sse]
@@ -105,27 +106,34 @@
media
media))
(update-fdata [fdata new-id]
(-> fdata
(assoc :id new-id)
(pmg/migrate-data)
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(d/without-nils)
(feat.fdata/process-pointers pmap/clone)))]
(process-file [{:keys [id] :as file}]
(-> file
(update :data assoc :id id)
(update :data feat.fdata/process-pointers deref)
(pmg/migrate-file)
(update :data (fn [data]
(-> data
(update :pages-index relink-shapes)
(update :components relink-shapes)
(update :media relink-media)
(d/without-nils))))))]
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
pmap/*tracked* (pmap/create-tracked)
cfeat/*new* (atom #{})]
(let [new-id (get index id)
file (-> file
(assoc :id new-id)
(update :data update-fdata new-id)
(update :features into (deref cfeat/*new*))
(update :features cfeat/migrate-legacy-features))]
(feat.fdata/persist-pointers! cfg new-id)
file))))
(let [new-id (get index id)
file (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
(-> (assoc file :id new-id)
(process-file)))
file (if (contains? (:features file) "fdata/objects-map")
(feat.fdata/enable-objects-map file)
file)
file (if (contains? (:features file) "fdata/pointer-map")
(binding [pmap/*tracked* (pmap/create-tracked)]
(let [file (feat.fdata/enable-pointer-map file)]
(feat.fdata/persist-pointers! cfg (:id file))
file))
file)]
file)))
(def sql:get-used-libraries
"select flr.*
@@ -190,20 +198,22 @@
(db/insert! conn :file
(-> file
(update :features #(db/create-array conn "text" %))
(update :data blob/encode)))
(update :data blob/encode))
{::db/return-keys? false})
(db/insert! conn :file-profile-rel
{:file-id (:id file)
:profile-id profile-id
:is-owner true
:is-admin true
:can-edit true})
:can-edit true}
{::db/return-keys? false})
(doseq [params flibs]
(db/insert! conn :file-library-rel params))
(db/insert! conn :file-library-rel params ::db/return-keys? false))
(doseq [params fmeds]
(db/insert! conn :file-media-object params))
(db/insert! conn :file-media-object params ::db/return-keys? false))
file))
@@ -283,7 +293,7 @@
;; --- COMMAND: Move file
(def sql:get-files
"select id, project_id from file where id = ANY(?)")
"select id, features, project_id from file where id = ANY(?)")
(def sql:move-files
"update file set project_id = ? where id = ANY(?)")
@@ -307,7 +317,8 @@
[{:keys [::db/conn] :as cfg} {:keys [profile-id ids project-id] :as params}]
(let [fids (db/create-array conn "uuid" ids)
files (db/exec! conn [sql:get-files fids])
files (->> (db/exec! conn [sql:get-files fids])
(map files/decode-row))
source (into #{} (map :project-id) files)
pids (->> (conj source project-id)
(db/create-array conn "uuid"))]
@@ -327,7 +338,12 @@
;; Check the team compatibility
(let [orig-team (teams/get-team conn :profile-id profile-id :project-id (first source))
dest-team (teams/get-team conn :profile-id profile-id :project-id project-id)]
(cfeat/check-teams-compatibility! orig-team dest-team))
(cfeat/check-teams-compatibility! orig-team dest-team)
;; Check if all pending to move files are compaib
(let [features (cfeat/get-team-enabled-features cf/flags dest-team)]
(doseq [file files]
(cfeat/check-file-features! features (:features file)))))
;; move all files to the project
(db/exec-one! conn [sql:move-files project-id fids])
@@ -384,7 +400,15 @@
;; Check the teams compatibility
(let [orig-team (teams/get-team conn :profile-id profile-id :team-id (:team-id project))
dest-team (teams/get-team conn :profile-id profile-id :team-id team-id)]
(cfeat/check-teams-compatibility! orig-team dest-team))
(cfeat/check-teams-compatibility! orig-team dest-team)
;; Check if all pending to move files are compaib
(let [features (cfeat/get-team-enabled-features cf/flags dest-team)]
(doseq [file (->> (db/query conn :file
{:project-id project-id}
{:columns [:features]})
(map files/decode-row))]
(cfeat/check-file-features! features (:features file)))))
;; move project to the destination team
(db/update! conn :project

View File

@@ -9,6 +9,7 @@
[app.common.spec :as us]
[app.db :as db]
[app.rpc :as-alias rpc]
[app.rpc.commands.files :refer [resolve-public-uri]]
[app.rpc.doc :as-alias doc]
[app.util.services :as sv]
[clojure.spec.alpha :as s]))
@@ -37,12 +38,15 @@
)
select distinct
f.id,
f.revn,
f.project_id,
f.created_at,
f.modified_at,
f.name,
f.is_shared
f.is_shared,
ft.media_id
from file as f
left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn)
inner join projects as pr on (f.project_id = pr.id)
where f.name ilike ('%' || ? || '%')
and (f.deleted_at is null or f.deleted_at > now())
@@ -50,10 +54,16 @@
(defn search-files
[conn profile-id team-id search-term]
(db/exec! conn [sql:search-files
profile-id team-id
profile-id team-id
search-term]))
(->> (db/exec! conn [sql:search-files
profile-id team-id
profile-id team-id
search-term])
(mapv (fn [row]
(if-let [media-id (:media-id row)]
(-> row
(dissoc :media-id)
(assoc :thumbnail-uri (resolve-public-uri media-id)))
(dissoc row :media-id))))))
(s/def ::team-id ::us/uuid)
(s/def ::search-files ::us/string)

View File

@@ -105,7 +105,7 @@
(pmg/migrate-file))))))
(d/index-by :id))
errors (validate/validate-file file libs)
changes (-> (repair/repair-file (:data file) libs errors) :redo-changes)
changes (repair/repair-file file libs errors)
file (-> file
(update :revn inc)

View File

@@ -103,7 +103,7 @@
{:on-open
(fn on-open [channel]
(l/trace :fn "on-open" :conn-id id :channel channel)
(l/dbg :fn "on-open" :conn-id (str id))
(let [options (-> options
(assoc ::channel channel)
(on-connect))
@@ -114,10 +114,10 @@
:on-close
(fn on-close [_channel code reason]
(l/info :fn "on-ws-terminate"
:conn-id id
:code code
:reason reason)
(l/dbg :fn "on-close"
:conn-id (str id)
:code code
:reason reason)
(sp/close! close-ch))
:on-error
@@ -132,18 +132,19 @@
:on-pong
(fn on-pong [_channel data]
(l/trace :fn "on-pong" :data data)
(sp/put! hbeat-ch data))}))
(defn- handle-ping!
[{:keys [::id ::beats ::channel] :as wsp} beat-id]
(l/trace :hint "send ping" :beat beat-id :conn-id id)
(l/trc :hint "send ping" :beat beat-id :conn-id (str id))
(rws/ping channel (encode-beat beat-id))
(let [issued (swap! beats conj (long beat-id))]
(not (>= (count issued) max-missed-heartbeats))))
(defn- start-io-loop!
[{:keys [::id ::close-ch ::input-ch ::output-ch ::heartbeat-ch ::channel ::handler ::beats ::on-rcv-message ::on-snd-message] :as wsp}]
[{:keys [::id ::close-ch ::input-ch ::output-ch ::heartbeat-ch
::channel ::handler ::beats ::on-rcv-message ::on-snd-message]
:as wsp}]
(try
(handler wsp {:type :open})
(loop [i 0]
@@ -154,14 +155,16 @@
(identical? p ping-ch)
(if (handle-ping! wsp i)
(recur (inc i))
(rws/close channel 8802 "missing to many pings"))
(do
(l/trc :hint "closing" :reason "missing to many pings")
(rws/close channel 8802 "missing to many pings")))
(or (identical? p close-ch) (nil? msg))
(do :nothing)
(identical? p heartbeat-ch)
(let [beat (decode-beat msg)]
;; (l/trace :hint "pong" :beat beat :conn-id id)
(l/trc :hint "pong received" :beat beat :conn-id (str id))
(swap! beats disj beat)
(recur i))
@@ -179,7 +182,6 @@
(identical? p output-ch)
(let [message (on-snd-message msg)
message (t/encode-str message {:type :json-verbose})]
;; (l/trace :hint "writing message to output" :message msg)
(rws/send channel message)
(recur i))))))
@@ -188,12 +190,12 @@
(catch java.io.IOException _)
(catch InterruptedException _cause
(l/debug :hint "websocket thread interrumpted" :conn-id id))
(l/dbg :hint "websocket thread interrumpted" :conn-id id))
(catch Throwable cause
(l/error :hint "unhandled exception on websocket thread"
:conn-id id
:cause cause))
(l/err :hint "unhandled exception on websocket thread"
:conn-id id
:cause cause))
(finally
(try
(handler wsp {:type :close})
@@ -212,4 +214,4 @@
(catch Throwable cause
(throw cause)))
(l/trace :hint "websocket thread terminated" :conn-id id))))
(l/trc :hint "websocket thread terminated" :conn-id id))))

View File

@@ -69,12 +69,12 @@
(t/is (not= (:id file1) (:id result)))
;; Check that the new file has a correct file library relation
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id result)})]
(t/is (= 1 (count rows)))
(t/is (= (:id file2) (:library-file-id item))))
;; Check that the new file has a correct file media objects
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
(let [[item :as rows] (th/db-query :file-media-object {:file-id (:id result)})]
(t/is (= 1 (count rows)))
;; Check that both items have different ids
@@ -91,7 +91,7 @@
(t/is (not (contains? (get-in result [:data :media]) (:id mobj)))))
;; Check the total number of files
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
(let [rows (th/db-query :file {:project-id (:id project)})]
(t/is (= 3 (count rows))))))))
(t/deftest duplicate-file-with-deleted-relations
@@ -139,15 +139,15 @@
(t/is (not= (:id file1) (:id result)))
;; Check that there are no relation to a deleted library
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id result)})]
(t/is (= 0 (count rows))))
;; Check that the new file has no media objects
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
(let [[item :as rows] (th/db-query :file-media-object {:file-id (:id result)})]
(t/is (= 0 (count rows))))
;; Check the total number of files
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
(let [rows (th/db-query :file {:project-id (:id project)})]
(t/is (= 3 (count rows))))))))
(t/deftest duplicate-project
@@ -196,16 +196,16 @@
(t/is (not= (:id project) (:id result)))
;; Check the total number of projects (previously is 2, now is 3)
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
(let [rows (th/db-query :project {:team-id (:default-team-id profile)})]
(t/is (= 3 (count rows))))
;; Check that the new project has the same files
(let [p1-files (db/query th/*pool* :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (db/query th/*pool* :file
{:project-id (:id result)}
{:order-by [:name]})]
(let [p1-files (th/db-query :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (th/db-query :file
{:project-id (:id result)}
{:order-by [:name]})]
(t/is (= (count p1-files)
(count p2-files)))
@@ -260,16 +260,16 @@
(t/is (not= (:id project) (:id result)))
;; Check the total number of projects (previously is 2, now is 3)
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
(let [rows (th/db-query :project {:team-id (:default-team-id profile)})]
(t/is (= 3 (count rows))))
;; Check that the new project has only the second file
(let [p1-files (db/query th/*pool* :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (db/query th/*pool* :file
{:project-id (:id result)}
{:order-by [:name]})]
(let [p1-files (th/db-query :file
{:project-id (:id project)}
{:order-by [:name]})
p2-files (th/db-query :file
{:project-id (:id result)}
{:order-by [:name]})]
(t/is (= (count (rest p1-files))
(count p2-files)))
@@ -318,11 +318,11 @@
(t/is (th/ex-of-code? error :cant-move-to-same-project)))
;; initially project1 should have 2 files
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(let [rows (th/db-query :file {:project-id (:id project1)})]
(t/is (= 2 (count rows))))
;; initially project2 should be empty
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(let [rows (th/db-query :file {:project-id (:id project2)})]
(t/is (= 0 (count rows))))
;; move a file1 to project2 (in the same team)
@@ -337,21 +337,21 @@
(t/is (nil? (:result out)))
;; project1 now should contain 1 file
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(let [rows (th/db-query :file {:project-id (:id project1)})]
(t/is (= 1 (count rows))))
;; project2 now should contain 1 file
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(let [rows (th/db-query :file {:project-id (:id project2)})]
(t/is (= 1 (count rows))))
;; file1 should be still linked to file2
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file1)))
(t/is (= (:library-file-id item) (:id file2))))
;; should be no libraries on file2
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
(t/is (= 0 (count rows)))))))
@@ -384,27 +384,27 @@
;; --- initial data checks
;; the project1 should have 3 files
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(let [rows (th/db-query :file {:project-id (:id project1)})]
(t/is (= 3 (count rows))))
;; should be no files on project2
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(let [rows (th/db-query :file {:project-id (:id project2)})]
(t/is (= 0 (count rows))))
;; the file1 should be linked to file2
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file1)))
(t/is (= (:library-file-id item) (:id file2))))
;; the file2 should be linked to file3
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file2)))
(t/is (= (:library-file-id item) (:id file3))))
;; should be no libraries on file3
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file3)})]
(let [rows (th/db-query :file-library-rel {:file-id (:id file3)})]
(t/is (= 0 (count rows))))
;; move to other project in other team
@@ -418,23 +418,23 @@
(t/is (nil? (:result out)))
;; project1 now should have 2 file
(let [[item1 item2 :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(let [[item1 item2 :as rows] (th/db-query :file {:project-id (:id project1)}
{:order-by [:created-at]})]
;; (clojure.pprint/pprint rows)
(t/is (= 2 (count rows)))
(t/is (= (:id item1) (:id file2))))
;; project2 now should have 1 file
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file1))))
;; the moved file1 should not have any link to libraries
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(let [rows (th/db-query :file-library-rel {:file-id (:id file1)})]
(t/is (zero? (count rows))))
;; the file2 should still be linked to file3
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file2)))
(t/is (= (:library-file-id item) (:id file3)))))))
@@ -462,21 +462,21 @@
;; --- initial data checks
;; the project1 should have 2 files
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(let [rows (th/db-query :file {:project-id (:id project1)})]
(t/is (= 2 (count rows))))
;; should be no files on project2
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(let [rows (th/db-query :file {:project-id (:id project2)})]
(t/is (= 0 (count rows))))
;; the file1 should be linked to file2
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(let [[item :as rows] (th/db-query :file-library-rel {:file-id (:id file1)})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item) (:id file1)))
(t/is (= (:library-file-id item) (:id file2))))
;; should be no libraries on file2
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
(t/is (= 0 (count rows))))
;; move the library to other project
@@ -490,22 +490,22 @@
(t/is (nil? (:result out)))
;; project1 now should have 1 file
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(let [[item :as rows] (th/db-query :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file1))))
;; project2 now should have 1 file
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file2))))
;; the file1 should not have any link to libraries
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file1)})]
(let [rows (th/db-query :file-library-rel {:file-id (:id file1)})]
(t/is (zero? (count rows))))
;; the file2 should not have any link to libraries
(let [rows (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(let [rows (th/db-query :file-library-rel {:file-id (:id file2)})]
(t/is (zero? (count rows)))))))
(t/deftest move-project
@@ -538,16 +538,17 @@
;; --- initial data checks
;; the project1 should have 2 files
(let [rows (db/query th/*pool* :file {:project-id (:id project1)})]
(let [rows (th/db-query :file {:project-id (:id project1)})]
(t/is (= 2 (count rows))))
;; the project2 should have 1 file
(let [rows (db/query th/*pool* :file {:project-id (:id project2)})]
(let [rows (th/db-query :file {:project-id (:id project2)})]
(t/is (= 1 (count rows))))
;; the file1 should be linked to file2 and file3
(let [[item1 item2 :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)}
{:order-by [:created-at]})]
(let [[item1 item2 :as rows] (th/db-query :file-library-rel
{:file-id (:id file1)}
{:order-by [:created-at]})]
(t/is (= 2 (count rows)))
(t/is (= (:file-id item1) (:id file1)))
(t/is (= (:library-file-id item1) (:id file2)))
@@ -555,15 +556,14 @@
(t/is (= (:library-file-id item2) (:id file3))))
;; the file2 should not be linked to any file
(let [[rows] (db/query th/*pool* :file-library-rel {:file-id (:id file2)})]
(let [[rows] (th/db-query :file-library-rel {:file-id (:id file2)})]
(t/is (= 0 (count rows))))
;; the file3 should not be linked to any file
(let [[rows] (db/query th/*pool* :file-library-rel {:file-id (:id file3)})]
(let [[rows] (th/db-query :file-library-rel {:file-id (:id file3)})]
(t/is (= 0 (count rows))))
;; move project1 to other team
;; TODO: correct team change of project
(let [data {::th/type :move-project
::rpc/profile-id (:id profile)
:project-id (:id project1)
@@ -574,21 +574,25 @@
(t/is (nil? (:result out)))
;; project1 now should still have 2 files
(let [[item1 item2 :as rows] (db/query th/*pool* :file {:project-id (:id project1)}
{:order-by [:created-at]})]
(let [[item1 item2 :as rows] (th/db-query :file
{:project-id (:id project1)}
{:order-by [:created-at]})]
;; (clojure.pprint/pprint rows)
(t/is (= 2 (count rows)))
(t/is (= (:id item1) (:id file1)))
(t/is (= (:id item2) (:id file2))))
;; project2 now should still have 1 file
(let [[item :as rows] (db/query th/*pool* :file {:project-id (:id project2)})]
(let [[item :as rows] (th/db-query :file {:project-id (:id project2)})]
;; (pp/pprint rows)
(t/is (= 1 (count rows)))
(t/is (= (:id item) (:id file3))))
;; the file1 should be linked to file2 but not file3
(let [[item1 :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id file1)}
{:order-by [:created-at]})]
(let [[item1 :as rows] (th/db-query :file-library-rel
{:file-id (:id file1)}
{:order-by [:created-at]})]
(t/is (= 1 (count rows)))
(t/is (= (:file-id item1) (:id file1)))
(t/is (= (:library-file-id item1) (:id file2)))))))

View File

@@ -51,7 +51,10 @@
"layout/grid"})
;; A set of features enabled by default for each file, they are
;; implicit and are enabled by default and can't be disabled
;; implicit and are enabled by default and can't be disabled. The
;; features listed in this set are mainly freatures addedby file
;; migrations process, so all features referenced in migrations should
;; be here.
(def default-enabled-features
#{"fdata/shape-data-type"})
@@ -190,7 +193,11 @@
([enabled-features file-features]
(check-file-features! enabled-features file-features #{}))
([enabled-features file-features client-features]
(let [file-features (into #{} xf-remove-ephimeral file-features)]
(let [file-features (into #{} xf-remove-ephimeral file-features)
;; We should ignore all features that does not match with the
;; `no-migration-features` set because we can't enable them
;; as-is, because they probably need migrations
client-features (set/intersection client-features no-migration-features)]
(let [not-supported (-> enabled-features
(set/union client-features)
(set/difference file-features)
@@ -208,15 +215,11 @@
(check-supported-features! file-features)
(let [;; We should ignore all features that does not match with
;; the `no-migration-features` set because we can't enable
;; them as-is, because they probably need migrations
client-features (set/intersection client-features no-migration-features)
not-supported (-> file-features
(set/difference enabled-features)
(set/difference client-features)
(set/difference backend-only-features)
(set/difference frontend-only-features))]
(let [not-supported (-> file-features
(set/difference enabled-features)
(set/difference client-features)
(set/difference backend-only-features)
(set/difference frontend-only-features))]
(when (seq not-supported)
(ex/raise :type :restriction

View File

@@ -358,13 +358,13 @@
(defn changed-attrs
"Returns the list of attributes that will change when `update-fn` is applied"
[object update-fn {:keys [attrs]}]
[object objects update-fn {:keys [attrs]}]
(let [changed?
(fn [old new attr]
(let [old-val (get old attr)
new-val (get new attr)]
(not= old-val new-val)))
new-obj (update-fn object)]
new-obj (update-fn object objects)]
(when-not (= object new-obj)
(let [attrs (or attrs (d/concat-set (keys object) (keys new-obj)))]
(filter (partial changed? object new-obj) attrs)))))
@@ -412,7 +412,7 @@
update-shape
(fn [changes id]
(let [old-obj (get objects id)
new-obj (update-fn old-obj)]
new-obj (update-fn old-obj objects)]
(if (= old-obj new-obj)
changes
(let [[rops uops] (-> (or attrs (d/concat-set (keys old-obj) (keys new-obj)))

View File

@@ -42,18 +42,21 @@
(reduce migrate-fn data (range (:version data 0) to-version))))))
(defn migrate-file
[{:keys [id data] :as file}]
(let [data (assoc data :id id)]
(-> file
(assoc ::orig-version (:version data))
(assoc :data (migrate-data data)))))
[{:keys [id data features] :as file}]
(binding [cfeat/*new* (atom #{})]
(let [file (-> file
(update :data assoc :id id)
(update :data migrate-data)
(update :features (fnil into #{}) (deref cfeat/*new*))
(update :features cfeat/migrate-legacy-features))]
(if (or (not= (:version data) (:version (:data file)))
(not= features (:features file)))
(vary-meta file assoc ::migrated true)
file))))
(defn migrated?
[{:keys [data] :as file}]
(or (::migrated file)
(let [version (:version data)]
(> version
(::orig-version file version)))))
[file]
(true? (-> file meta ::migrated)))
;; Default handler, noop
(defmethod migrate :default [data] data)

View File

@@ -28,14 +28,14 @@
(let [repair-shape
(fn [shape]
; Reset geometry to minimal
(log/debug :hint " -> Reset geometry")
(log/debug :hint " -> reset geometry")
(-> shape
(assoc :x 0)
(assoc :y 0)
(assoc :width 0.01)
(assoc :height 0.01)
(cts/setup-rect)))]
(log/info :hint "Repairing shape :invalid-geometry" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :invalid-geometry" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -45,10 +45,10 @@
(let [repair-shape
(fn [shape]
; Set parent to root frame.
(log/debug :hint " -> Set to " :parent-id uuid/zero)
(log/debug :hint " -> set to " :parent-id uuid/zero)
(assoc shape :parent-id uuid/zero))]
(log/info :hint "Repairing shape :parent-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :parent-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -58,10 +58,10 @@
(let [repair-shape
(fn [parent-shape]
; Add shape to parent's children list
(log/debug :hint " -> Add children to" :parent-id (:id parent-shape))
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
(update parent-shape :shapes conj (:id shape)))]
(log/info :hint "Repairing shape :child-not-in-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :child-not-in-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:parent-id shape)] repair-shape))))
@@ -70,17 +70,17 @@
[_ {:keys [shape page-id args] :as error} file-data _]
(let [repair-shape
(fn [parent-shape]
(log/debug :hint " -> Remove child" :child-id (:child-id args))
(log/debug :hint " -> remove child" :child-id (:child-id args))
(update parent-shape :shapes (fn [shapes]
(d/removev #(= (:child-id args) %) shapes))))]
(log/info :hint "Repairing shape :child-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :child-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
(defmethod repair-error :invalid-parent
[_ {:keys [shape page-id args] :as error} file-data _]
(log/info :hint "Repairing shape :invalid-parent" :id (:id shape) :name (:name shape) :page-id page-id)
(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})))
@@ -93,10 +93,10 @@
(let [page (ctpl/get-page file-data page-id)
frame (cfh/get-frame (:objects page) (:parent-id shape))
frame-id (or (:id frame) uuid/zero)]
(log/debug :hint " -> Set to " :frame-id frame-id)
(log/debug :hint " -> set to " :frame-id frame-id)
(assoc shape :frame-id frame-id)))]
(log/info :hint "Repairing shape :frame-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :frame-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -109,10 +109,10 @@
(let [page (ctpl/get-page file-data page-id)
frame (cfh/get-frame (:objects page) (:parent-id shape))
frame-id (or (:id frame) uuid/zero)]
(log/debug :hint " -> Set to " :frame-id frame-id)
(log/debug :hint " -> set to " :frame-id frame-id)
(assoc shape :frame-id frame-id)))]
(log/info :hint "Repairing shape :invalid-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :invalid-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -122,10 +122,10 @@
(let [repair-shape
(fn [shape]
; Set the :shape as main instance root
(log/debug :hint " -> Set :main-instance")
(log/debug :hint " -> set :main-instance")
(assoc shape :main-instance true))]
(log/info :hint "Repairing shape :component-not-main" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :component-not-main" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -135,13 +135,13 @@
(let [repair-shape
(fn [shape]
; Set :component-file to local file
(log/debug :hint " -> Set :component-file to local file")
(log/debug :hint " -> set :component-file to local file")
(assoc shape :component-file (:id file-data)))]
; There is no solution that may recover it with confidence
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
;; shape)]
(log/info :hint "Repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -154,13 +154,13 @@
repair-shape
(fn [shape]
; Detach the shape and convert it to non instance.
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
; There is no solution that may recover it with confidence
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
;; shape)]
(log/info :hint "Repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes shape-ids repair-shape))))
@@ -172,15 +172,15 @@
repair-component
(fn [component]
; Assign main instance in the component to current shape
(log/debug :hint " -> Assign main-instance-id" :component-id (:id component))
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
(assoc component :main-instance-id (:id shape)))
detach-shape
(fn [shape]
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
(log/info :hint "Repairing shape :invalid-main-instance-id" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :invalid-main-instance-id" :id (:id shape) :name (:name shape) :page-id page-id)
(if (and (some? component) (not (:deleted component)))
(-> (pcb/empty-changes nil page-id)
(pcb/with-library-data file-data)
@@ -195,9 +195,9 @@
(let [repair-component
(fn [component]
; Assign main instance in the component to current shape
(log/debug :hint " -> Assign main-instance-page" :component-id (:id component))
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
(assoc component :main-instance-page page-id))]
(log/info :hint "Repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-library-data file-data)
(pcb/update-component (:component-id shape) repair-component))))
@@ -210,7 +210,7 @@
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
shape)]
(log/info :hint "Repairing shape :invalid-main-instance" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :invalid-main-instance" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -220,10 +220,10 @@
(let [repair-shape
(fn [shape]
; Unset the :shape as main instance root
(log/debug :hint " -> Unset :main-instance")
(log/debug :hint " -> unset :main-instance")
(dissoc shape :main-instance))]
(log/info :hint "Repairing shape :component-main" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :component-main" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -233,10 +233,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a top copy root.
(log/debug :hint " -> Set :component-root")
(log/debug :hint " -> set :component-root")
(assoc shape :component-root true))]
(log/info :hint "Repairing shape :should-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :should-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -246,10 +246,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a nested copy root.
(log/debug :hint " -> Unset :component-root")
(log/debug :hint " -> unset :component-root")
(dissoc shape :component-root))]
(log/info :hint "Repairing shape :should-not-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :should-not-be-component-root" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -268,17 +268,17 @@
reassign-shape
(fn [shape]
(log/debug :hint " -> Reassign shape-ref to" :shape-ref (:id matching-shape))
(log/debug :hint " -> reassign shape-ref to" :shape-ref (:id matching-shape))
(assoc shape :shape-ref (:id matching-shape)))
detach-shape
(fn [shape]
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
; If the shape still refers to the remote component, try to find the corresponding near one
; and link to it. If not, detach the shape.
(log/info :hint "Repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
(if (some? matching-shape)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
@@ -294,10 +294,10 @@
(let [repair-shape
(fn [shape]
; Remove shape-ref
(log/debug :hint " -> Unset :shape-ref")
(log/debug :hint " -> unset :shape-ref")
(dissoc shape :shape-ref))]
(log/info :hint "Repairing shape :shape-ref-in-main" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :shape-ref-in-main" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -307,10 +307,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a nested main head.
(log/debug :hint " -> Unset :component-root")
(log/debug :hint " -> unset :component-root")
(dissoc shape :component-root))]
(log/info :hint "Repairing shape :root-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :root-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -320,10 +320,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a top main head.
(log/debug :hint " -> Set :component-root")
(log/debug :hint " -> set :component-root")
(assoc shape :component-root true))]
(log/info :hint "Repairing shape :nested-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :nested-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -333,10 +333,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a nested copy head.
(log/debug :hint " -> Unset :component-root")
(log/debug :hint " -> unset :component-root")
(dissoc shape :component-root))]
(log/info :hint "Repairing shape :root-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :root-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -346,10 +346,10 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a top copy root.
(log/debug :hint " -> Set :component-root")
(log/debug :hint " -> set :component-root")
(assoc shape :component-root true))]
(log/info :hint "Repairing shape :nested-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :nested-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -359,10 +359,10 @@
(let [repair-shape
(fn [shape]
; Detach the shape and convert it to non instance.
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
(log/info :hint "Repairing shape :not-head-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :not-head-main-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -372,10 +372,10 @@
(let [repair-shape
(fn [shape]
; Detach the shape and convert it to non instance.
(log/debug :hint " -> Detach shape" :shape-id (:id shape))
(log/debug :hint " -> detach shape" :shape-id (:id shape))
(ctk/detach-shape shape))]
(log/info :hint "Repairing shape :not-head-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :not-head-copy-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -388,7 +388,7 @@
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
shape)]
(log/info :hint "Repairing shape :not-component-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :not-component-not-allowed" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -398,14 +398,14 @@
(let [repair-shape
(fn [shape]
; Convert the shape in a frame.
(log/debug :hint " -> Set :type :frame")
(log/debug :hint " -> set :type :frame")
(assoc shape :type :frame
:fills []
:hide-in-viewer true
:rx 0
:ry 0))]
(log/info :hint "Repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(log/dbg :hint "repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)
(pcb/with-file-data file-data)
(pcb/update-shapes [(:id shape)] repair-shape))))
@@ -417,13 +417,13 @@
; Remove the objects key, or set it to {} if the component is deleted
(if (:deleted component)
(do
(log/debug :hint " -> Set :objects {}")
(log/debug :hint " -> set :objects {}")
(assoc component :objects {}))
(do
(log/debug :hint " -> Remove :objects")
(log/debug :hint " -> remove :objects")
(dissoc component :objects))))]
(log/info :hint "Repairing component :component-nil-objects-not-allowed" :id (:id shape) :name (:name shape))
(log/dbg :hint "repairing component :component-nil-objects-not-allowed" :id (:id shape) :name (:name shape))
(-> (pcb/empty-changes nil)
(pcb/with-library-data file-data)
(pcb/update-component (:id shape) repair-component))))
@@ -434,13 +434,15 @@
file)
(defn repair-file
[file-data libraries errors]
(log/info :hint "Repairing file" :id (:id file-data) :error-count (count errors))
(reduce (fn [changes error]
(pcb/concat-changes changes
(repair-error (:code error)
error
file-data
libraries)))
(pcb/empty-changes nil)
errors))
[{:keys [data id] :as file} libraries errors]
(log/dbg :hint "repairing file" :id (str id) :errors (count errors))
(let [{:keys [redo-changes]}
(reduce (fn [changes error]
(pcb/concat-changes changes
(repair-error (:code error)
error
data
libraries)))
(pcb/empty-changes nil)
errors)]
redo-changes))

View File

@@ -6,6 +6,7 @@
(ns app.common.files.validate
(:require
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.schema :as sm]
@@ -49,50 +50,50 @@
:not-component-not-allowed
:component-nil-objects-not-allowed})
(def validation-error
[:map {:title "ValidationError"}
[:code {:optional false} [::sm/one-of error-codes]]
[:hint {:optional false} :string]
[:shape {:optional true} :map] ; Cannot validate a shape because here it may be broken
[:file-id ::sm/uuid]
[:page-id ::sm/uuid]])
(def ^:private
schema:error
(sm/define
[:map {:title "ValidationError"}
[:code {:optional false} [::sm/one-of error-codes]]
[:hint {:optional false} :string]
[:shape {:optional true} :map] ; Cannot validate a shape because here it may be broken
[:shape-id {:optional true} ::sm/uuid]
[:file-id ::sm/uuid]
[:page-id ::sm/uuid]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ERROR HANDLING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:dynamic *errors* nil)
(def ^:dynamic ^:private *errors* nil)
(defn report-error!
(defn- report-error
[code hint shape file page & {:as args}]
(if (some? *errors*)
(vswap! *errors* conj {:code code
:hint hint
:shape shape
:file-id (:id file)
:page-id (:id page)
:args args})
(let [error {:code code
:hint hint
:shape shape
:file-id (:id file)
:page-id (:id page)
:shape-id (:id shape)
:args args}]
(let [explain (str/ffmt "file %, page %, shape %"
(:id file)
(:id page)
(:id shape))]
(ex/raise :type :validation
:code code
:hint hint
:args args
:file-id (:id file)
:page-id (:id page)
:shape-id (:id shape)
::explain explain))))
(dm/assert!
"expected a valid `*errors*` dynamic binding"
(some? *errors*))
(dm/assert!
"expected valid error"
(sm/check! schema:error error))
(vswap! *errors* conj error)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; VALIDATION FUNCTIONS
;; PRIVATE API: VALIDATION FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare validate-shape!)
(declare check-shape)
(defn validate-geometry!
(defn- check-geometry
"Validate that the shape has valid coordinates, selrect and points."
[shape file page]
(when (and (not (#{:path :bool} (:type shape)))
@@ -102,175 +103,175 @@
(nil? (:height shape))
(nil? (:selrect shape))
(nil? (:points shape))))
(report-error! :invalid-geometry
"Shape greometry is invalid"
shape file page)))
(report-error :invalid-geometry
"Shape greometry is invalid"
shape file page)))
(defn validate-parent-children!
(defn- check-parent-children
"Validate parent and children exists, and the link is bidirectional."
[shape file page]
(let [parent (ctst/get-shape page (:parent-id shape))]
(if (nil? parent)
(report-error! :parent-not-found
(str/ffmt "Parent % not found" (:parent-id shape))
shape file page)
(report-error :parent-not-found
(str/ffmt "Parent % not found" (:parent-id shape))
shape file page)
(do
(when-not (cfh/root? shape)
(when-not (some #{(:id shape)} (:shapes parent))
(report-error! :child-not-in-parent
(str/ffmt "Shape % not in parent's children list" (:id shape))
shape file page)))
(report-error :child-not-in-parent
(str/ffmt "Shape % not in parent's children list" (:id shape))
shape file page)))
(doseq [child-id (:shapes shape)]
(let [child (ctst/get-shape page child-id)]
(if (nil? child)
(report-error! :child-not-found
(str/ffmt "Child % not found in parent %" child-id (:id shape))
shape file page
:parent-id (:id shape)
:child-id child-id)
(report-error :child-not-found
(str/ffmt "Child % not found in parent %" child-id (:id shape))
shape file page
:parent-id (:id shape)
:child-id child-id)
(when (not= (:parent-id child) (:id shape))
(report-error! :invalid-parent
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
child file page
:parent-id (:id shape))))))))))
(report-error :invalid-parent
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
child file page
:parent-id (:id shape))))))))))
(defn validate-frame!
(defn- check-frame
"Validate that the frame-id shape exists and is indeed a frame. Also
it must point to the parent shape (if this is a frame) or to the
frame-id of the parent (if not)."
[shape file page]
(let [frame (ctst/get-shape page (:frame-id shape))]
(if (nil? frame)
(report-error! :frame-not-found
(str/ffmt "Frame % not found" (:frame-id shape))
shape file page)
(report-error :frame-not-found
(str/ffmt "Frame % not found" (:frame-id shape))
shape file page)
(if (not= (:type frame) :frame)
(report-error! :invalid-frame
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
shape file page)
(report-error :invalid-frame
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
shape file page)
(let [parent (ctst/get-shape page (:parent-id shape))]
(when (some? parent)
(if (= (:type parent) :frame)
(when-not (= (:frame-id shape) (:id parent))
(report-error! :invalid-frame
(str/ffmt "Frame-id should point to parent %" (:id parent))
shape file page))
(report-error :invalid-frame
(str/ffmt "Frame-id should point to parent %" (:id parent))
shape file page))
(when-not (= (:frame-id shape) (:frame-id parent))
(report-error! :invalid-frame
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
shape file page)))))))))
(report-error :invalid-frame
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
shape file page)))))))))
(defn validate-component-main-head!
(defn- check-component-main-head
"Validate shape is a main instance head, component exists
and its main-instance points to this shape."
[shape file page libraries]
(when (nil? (:main-instance shape))
(report-error! :component-not-main
"Shape expected to be main instance"
shape file page))
(report-error :component-not-main
"Shape expected to be main instance"
shape file page))
(when-not (= (:component-file shape) (:id file))
(report-error! :component-main-external
"Main instance should refer to a component in the same file"
shape file page))
(report-error :component-main-external
"Main instance should refer to a component in the same file"
shape file page))
(let [component (ctf/resolve-component shape file libraries :include-deleted? true)]
(if (nil? component)
(report-error! :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page)
(report-error :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page)
(do
(when-not (= (:main-instance-id component) (:id shape))
(report-error! :invalid-main-instance-id
(str/ffmt "Main instance id of component % is not valid" (:component-id shape))
shape file page))
(report-error :invalid-main-instance-id
(str/ffmt "Main instance id of component % is not valid" (:component-id shape))
shape file page))
(when-not (= (:main-instance-page component) (:id page))
(let [component-page (ctf/get-component-page (:data file) component)
main-component (ctst/get-shape component-page (:main-instance-id component))]
;; We must check if the same component has main instances in different pages.
;; In that case one of those instances shouldn't be main
(if (:main-instance main-component)
(report-error! :component-main
"Shape not expected to be main instance"
shape file page)
(report-error! :invalid-main-instance-page
(str/ffmt "Main instance page of component % is not valid" (:component-id shape))
shape file page))))))))
(report-error :component-main
"Shape not expected to be main instance"
shape file page)
(report-error :invalid-main-instance-page
(str/ffmt "Main instance page of component % is not valid" (:component-id shape))
shape file page))))))))
(defn validate-component-not-main-head!
(defn- check-component-not-main-head
"Validate shape is a not-main instance head, component
exists and its main-instance does not point to this
shape."
[shape file page libraries]
(when (true? (:main-instance shape))
(report-error! :component-not-main
"Shape not expected to be main instance"
shape file page))
(report-error :component-not-main
"Shape not expected to be main instance"
shape file page))
(let [library-exists? (or (= (:component-file shape) (:id file))
(contains? libraries (:component-file shape)))
(contains? libraries (:component-file shape)))
component (when library-exists?
(ctf/resolve-component shape file libraries {:include-deleted? true}))]
(if (nil? component)
(when library-exists?
(report-error! :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page))
(report-error :component-not-found
(str/ffmt "Component % not found in file %" (:component-id shape) (:component-file shape))
shape file page))
(when (and (= (:main-instance-id component) (:id shape))
(= (:main-instance-page component) (:id page)))
(report-error! :invalid-main-instance
(str/ffmt "Main instance of component % should not be this shape" (:id component))
shape file page)))))
(report-error :invalid-main-instance
(str/ffmt "Main instance of component % should not be this shape" (:id component))
shape file page)))))
(defn validate-component-not-main-not-head!
(defn- check-component-not-main-not-head
"Validate that this shape is not main instance and not head."
[shape file page]
(when (true? (:main-instance shape))
(report-error! :component-main
"Shape not expected to be main instance"
shape file page))
(report-error :component-main
"Shape not expected to be main instance"
shape file page))
(when (or (some? (:component-id shape))
(some? (:component-file shape)))
(report-error! :component-main
"Shape not expected to be component head"
shape file page)))
(report-error :component-main
"Shape not expected to be component head"
shape file page)))
(defn validate-component-root!
(defn- check-component-root
"Validate that this shape is an instance root."
[shape file page]
(when (nil? (:component-root shape))
(report-error! :should-be-component-root
"Shape should be component root"
shape file page)))
(report-error :should-be-component-root
"Shape should be component root"
shape file page)))
(defn validate-component-not-root!
(defn- check-component-not-root
"Validate that this shape is not an instance root."
[shape file page]
(when (true? (:component-root shape))
(report-error! :should-not-be-component-root
"Shape should not be component root"
shape file page)))
(report-error :should-not-be-component-root
"Shape should not be component root"
shape file page)))
(defn validate-component-ref!
(defn- check-component-ref
"Validate that the referenced shape exists in the near component."
[shape file page libraries]
(let [library-exists? (or (= (:component-file shape) (:id file))
(contains? libraries (:component-file shape)))
(contains? libraries (:component-file shape)))
ref-shape (when library-exists?
(ctf/find-ref-shape file page libraries shape :include-deleted? true))]
(when (and library-exists? (nil? ref-shape))
(report-error! :ref-shape-not-found
(str/ffmt "Referenced shape % not found in near component" (:shape-ref shape))
shape file page))))
(report-error :ref-shape-not-found
(str/ffmt "Referenced shape % not found in near component" (:shape-ref shape))
shape file page))))
(defn validate-component-not-ref!
(defn- check-component-not-ref
"Validate that this shape does not reference other one."
[shape file page]
(when (some? (:shape-ref shape))
(report-error! :shape-ref-in-main
"Shape inside main instance should not have shape-ref"
shape file page)))
(report-error :shape-ref-in-main
"Shape inside main instance should not have shape-ref"
shape file page)))
(defn validate-shape-main-root-top!
(defn- check-shape-main-root-top
"Root shape of a top main instance:
- :main-instance
@@ -278,78 +279,78 @@
- :component-file
- :component-root"
[shape file page libraries]
(validate-component-main-head! shape file page libraries)
(validate-component-root! shape file page)
(validate-component-not-ref! shape file page)
(check-component-main-head shape file page libraries)
(check-component-root shape file page)
(check-component-not-ref shape file page)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :main-top)))
(check-shape child-id file page libraries :context :main-top)))
(defn validate-shape-main-root-nested!
(defn- check-shape-main-root-nested
"Root shape of a nested main instance
- :main-instance
- :component-id
- :component-file"
[shape file page libraries]
(validate-component-main-head! shape file page libraries)
(validate-component-not-root! shape file page)
(validate-component-not-ref! shape file page)
(check-component-main-head shape file page libraries)
(check-component-not-root shape file page)
(check-component-not-ref shape file page)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :main-nested)))
(check-shape child-id file page libraries :context :main-nested)))
(defn validate-shape-copy-root-top!
(defn- check-shape-copy-root-top
"Root shape of a top copy instance
- :component-id
- :component-file
- :component-root
- :shape-ref"
[shape file page libraries]
(validate-component-not-main-head! shape file page libraries)
(validate-component-root! shape file page)
(validate-component-ref! shape file page libraries)
(check-component-not-main-head shape file page libraries)
(check-component-root shape file page)
(check-component-ref shape file page libraries)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :copy-top)))
(check-shape child-id file page libraries :context :copy-top)))
(defn validate-shape-copy-root-nested!
(defn- check-shape-copy-root-nested
"Root shape of a nested copy instance
- :component-id
- :component-file
- :shape-ref"
[shape file page libraries]
(validate-component-not-main-head! shape file page libraries)
(validate-component-not-root! shape file page)
(validate-component-ref! shape file page libraries)
(check-component-not-main-head shape file page libraries)
(check-component-not-root shape file page)
(check-component-ref shape file page libraries)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :copy-nested)))
(check-shape child-id file page libraries :context :copy-nested)))
(defn validate-shape-main-not-root!
(defn- check-shape-main-not-root
"Not-root shape of a main instance (not any attribute)"
[shape file page libraries]
(validate-component-not-main-not-head! shape file page)
(validate-component-not-root! shape file page)
(validate-component-not-ref! shape file page)
(check-component-not-main-not-head shape file page)
(check-component-not-root shape file page)
(check-component-not-ref shape file page)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :main-any)))
(check-shape child-id file page libraries :context :main-any)))
(defn validate-shape-copy-not-root!
(defn- check-shape-copy-not-root
"Not-root shape of a copy instance :shape-ref"
[shape file page libraries]
(validate-component-not-main-not-head! shape file page)
(validate-component-not-root! shape file page)
(validate-component-ref! shape file page libraries)
(check-component-not-main-not-head shape file page)
(check-component-not-root shape file page)
(check-component-ref shape file page libraries)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :copy-any)))
(check-shape child-id file page libraries :context :copy-any)))
(defn validate-shape-not-component!
(defn- check-shape-not-component
"Shape is not in a component or is a fostered children (not any
attribute)"
[shape file page libraries]
(validate-component-not-main-not-head! shape file page)
(validate-component-not-root! shape file page)
(validate-component-not-ref! shape file page)
(check-component-not-main-not-head shape file page)
(check-component-not-root shape file page)
(check-component-not-ref shape file page)
(doseq [child-id (:shapes shape)]
(validate-shape! child-id file page libraries :context :not-component)))
(check-shape child-id file page libraries :context :not-component)))
(defn validate-shape!
(defn- check-shape
"Validate referential integrity and semantic coherence of
a shape and all its children. Report all errors found.
@@ -366,132 +367,140 @@
(let [shape (ctst/get-shape page shape-id)]
(when (some? shape)
(do
(validate-geometry! shape file page)
(validate-parent-children! shape file page)
(validate-frame! shape file page)
(check-geometry shape file page)
(check-parent-children shape file page)
(check-frame shape file page)
(if (ctk/instance-head? shape)
(if (not= :frame (:type shape))
(report-error! :instance-head-not-frame
"Instance head should be a frame"
shape file page)
(report-error :instance-head-not-frame
"Instance head should be a frame"
shape file page)
(if (ctk/instance-root? shape)
(if (ctk/main-instance? shape)
(if (not= context :not-component)
(report-error! :root-main-not-allowed
"Root main component not allowed inside other component"
shape file page)
(validate-shape-main-root-top! shape file page libraries))
(report-error :root-main-not-allowed
"Root main component not allowed inside other component"
shape file page)
(check-shape-main-root-top shape file page libraries))
(if (not= context :not-component)
(report-error! :root-copy-not-allowed
"Root copy component not allowed inside other component"
shape file page)
(validate-shape-copy-root-top! shape file page libraries)))
(report-error :root-copy-not-allowed
"Root copy component not allowed inside other component"
shape file page)
(check-shape-copy-root-top shape file page libraries)))
(if (ctk/main-instance? shape)
(if (= context :not-component)
(report-error! :nested-main-not-allowed
"Nested main component only allowed inside other component"
shape file page)
(validate-shape-main-root-nested! shape file page libraries))
(report-error :nested-main-not-allowed
"Nested main component only allowed inside other component"
shape file page)
(check-shape-main-root-nested shape file page libraries))
(if (= context :not-component)
(report-error! :nested-copy-not-allowed
"Nested copy component only allowed inside other component"
shape file page)
(validate-shape-copy-root-nested! shape file page libraries)))))
(report-error :nested-copy-not-allowed
"Nested copy component only allowed inside other component"
shape file page)
(check-shape-copy-root-nested shape file page libraries)))))
(if (ctk/in-component-copy? shape)
(if-not (#{:copy-top :copy-nested :copy-any} context)
(report-error! :not-head-copy-not-allowed
"Non-root copy only allowed inside a copy"
shape file page)
(validate-shape-copy-not-root! shape file page libraries))
(report-error :not-head-copy-not-allowed
"Non-root copy only allowed inside a copy"
shape file page)
(check-shape-copy-not-root shape file page libraries))
(if (ctn/inside-component-main? (:objects page) shape)
(if-not (#{:main-top :main-nested :main-any} context)
(report-error! :not-head-main-not-allowed
"Non-root main only allowed inside a main component"
shape file page)
(validate-shape-main-not-root! shape file page libraries))
(report-error :not-head-main-not-allowed
"Non-root main only allowed inside a main component"
shape file page)
(check-shape-main-not-root shape file page libraries))
(if (#{:main-top :main-nested :main-any} context)
(report-error! :not-component-not-allowed
"Not compoments are not allowed inside a main"
shape file page)
(validate-shape-not-component! shape file page libraries)))))))))
(report-error :not-component-not-allowed
"Not compoments are not allowed inside a main"
shape file page)
(check-shape-not-component shape file page libraries)))))))))
(defn- check-component
"Validate semantic coherence of a component. Report all errors found."
[component file]
(when (and (contains? component :objects) (nil? (:objects component)))
(report-error :component-nil-objects-not-allowed
"Objects list cannot be nil"
component file nil)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PUBLIC API: VALIDATION FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn validate-file
"Validate full referential integrity and semantic coherence on file data.
Return a list of errors or `nil`"
[{:keys [data features] :as file} libraries]
(when (contains? features "components/v2")
(binding [*errors* (volatile! [])]
(doseq [page (filter :id (ctpl/pages-seq data))]
(let [orphans (->> page
:objects
vals
(filter #(not (contains? (:objects page) (:parent-id %))))
(map :id))]
(check-shape uuid/zero file page libraries)
(doseq [shape-id orphans]
(check-shape shape-id file page libraries))))
(doseq [component (vals (:components data))]
(check-component component file))
(-> *errors* deref not-empty))))
(defn validate-shape
"Validate a shape and all its children. Returns a list of errors."
[shape-id file page libraries]
(binding [*errors* (volatile! [])]
(validate-shape! shape-id file page libraries)
(check-shape shape-id file page libraries)
(deref *errors*)))
(defn validate-component!
"Validate semantic coherence of a component. Report all errors found."
[component file]
(when (and (contains? component :objects) (nil? (:objects component)))
(report-error! :component-nil-objects-not-allowed
"Objects list cannot be nil"
component file nil)))
(defn validate-component
"Validate a component. Returns a list of errors."
[component file]
(binding [*errors* (volatile! [])]
(validate-component! component file)
(check-component component file)
(deref *errors*)))
(def valid-fdata?
(def ^:private valid-fdata?
"Structural validation of file data using defined schema"
(sm/lazy-validator ::ctf/data))
(def get-fdata-explain
(def ^:private get-fdata-explain
"Get schema explain data for file data"
(sm/lazy-explainer ::ctf/data))
(defn validate-file-schema!
[{:keys [id data] :as file}]
(when-not (valid-fdata? data)
(if (some? *errors*)
(vswap! *errors* conj
{:code :invalid-file-data-structure
:hint (str/ffmt "invalid file data structure found on file '%'" id)
:file-id id})
(ex/raise :type :validation
:code :data-validation
:hint (str/ffmt "invalid file data structure found on file '%'" id)
:file-id id
::sm/explain (get-fdata-explain data))))
file)
(ex/raise :type :validation
:code :schema-validation
:hint (str/ffmt "invalid file data structure found on file '%'" id)
:file-id id
::sm/explain (get-fdata-explain data))))
(defn validate-file!
"Validate full referential integrity and semantic coherence on file data.
Raises a validation exception on first error found."
[{:keys [data features] :as file} libraries]
(when (contains? features "components/v2")
(doseq [page (filter :id (ctpl/pages-seq data))]
(let [orphans (->> page
:objects
vals
(filter #(not (contains? (:objects page) (:parent-id %))))
(map :id))]
(validate-shape! uuid/zero file page libraries)
(doseq [shape-id orphans]
(validate-shape! shape-id file page libraries))))
(doseq [component (vals (:components data))]
(validate-component! component file)))
file)
(defn validate-file
"Validate structure, referencial integrity and semantic coherence of
all contents of a file. Returns a list of errors."
Raises an exception"
[file libraries]
(binding [*errors* (volatile! [])]
(validate-file! file libraries)
(deref *errors*)))
(when-let [errors (validate-file file libraries)]
(ex/raise :type :validation
:code :referential-integrity
:hint "error on validating file referential integrity"
:file-id (:id file)
:details errors)))

View File

@@ -353,3 +353,19 @@
(mth/max by1 y1)
(mth/min bx2 x2)
(mth/min by2 y2)))))
(defn fix-aspect-ratio
[bounds aspect-ratio]
(if aspect-ratio
(let [width (dm/get-prop bounds :width)
height (dm/get-prop bounds :height)
target-height (* width aspect-ratio)
target-width (* height (/ 1 aspect-ratio))]
(cond-> bounds
(> target-height height)
(-> (assoc :height target-height)
(update :y - (/ (- target-height height ) 2)))
(< target-height height)
(-> (assoc :width target-width)
(update :x - (/ (- target-width width ) 2)))))
bounds))

View File

@@ -1,3 +1,11 @@
/**
* Performance focused pure java implementation of the
* SVG path parser.
*
* @author KALEIDOS INC
* @license MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
*/
package app.common.svg.path;
import java.util.Arrays;
@@ -61,9 +69,11 @@ public class Parser {
command = MOVE_TO;
params = new Object[] {K_X, this.params[0], K_Y, this.params[1]};
break;
case 'Z':
command = CLOSE_PATH;
break;
case 'L':
command = LINE_TO;
params = new Object[] {K_X, this.params[0], K_Y, this.params[1]};
@@ -636,8 +646,9 @@ public class Parser {
for (int i=0; i<pdata.size(); i++) {
Segment segment = pdata.get(i);
var currentCommand = segment.command;
switch(segment.command) {
switch(currentCommand) {
case 'M':
x = segment.params[0];
y = segment.params[1];
@@ -780,16 +791,6 @@ public class Parser {
var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
result.addAll(segments);
// if (rx == 0 || ry == 0) {
// segment.command = 'C';
// segment.params = new double[] {currentX, currentY, x, y, x, y};
// result.add(segment);
// } else if (currentX != x || currentY != y) {
// var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
// result.addAll(segments);
// }
currentX = x;
currentY = y;
@@ -803,7 +804,7 @@ public class Parser {
break;
}
lastCommand = segment.command;
lastCommand = currentCommand;
}
return result;
@@ -870,7 +871,6 @@ public class Parser {
}
private static void processCurve(double[] curve, double cx, double cy, double rx, double ry, double sinPhi, double cosPhi) {
double x0 = curve[0] * rx;
double y0 = curve[1] * ry;
double x1 = curve[2] * rx;
@@ -910,7 +910,13 @@ public class Parser {
double x1p = ((cosPhi * (x1 - x2)) / 2) + ((sinPhi * (y1 - y2)) / 2);
double y1p = ((-sinPhi * (x1 - x2)) / 2) + ((cosPhi * (y1 - y2)) / 2);
if (x1p == 0 || y1p == 0 || rx == 0 || ry == 0) {
if (x1p == 0 && y1p == 0) {
// we're asked to draw line to itself
return new ArrayList<>();
}
if (rx == 0 || ry == 0) {
// one of the radii is zero
return new ArrayList<>();
}

View File

@@ -2,7 +2,8 @@
* Arc to Bezier curves transformer
*
* Is a modified and google closure compatible version of the a2c
* functions by https://github.com/fontello/svgpath
* functions by https://github.com/fontello/svgpath used as reference
* implementation for tests
*
* @author KALEIDOS INC
* @license MIT License <https://opensource.org/licenses/MIT>
@@ -10,11 +11,11 @@
"use strict";
goog.provide("common_tests.arc_to_bezier");
goog.provide("app.common.svg.path.arc_to_bezier");
// https://raw.githubusercontent.com/fontello/svgpath/master/lib/a2c.js
goog.scope(function() {
const self = common_tests.arc_to_bezier;
const self = app.common.svg.path.arc_to_bezier;
var TAU = Math.PI * 2;
@@ -123,7 +124,7 @@ goog.scope(function() {
return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ];
}
function a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
function calculate_beziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
var sin_phi = Math.sin(phi * TAU / 360);
var cos_phi = Math.cos(phi * TAU / 360);
@@ -132,6 +133,8 @@ goog.scope(function() {
var x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2;
var y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2;
// console.log("L", x1p, y1p)
if (x1p === 0 && y1p === 0) {
// we're asked to draw line to itself
return [];
@@ -204,5 +207,5 @@ goog.scope(function() {
});
}
self.a2c = a2c;
self.calculateBeziers = calculate_beziers;
});

View File

@@ -0,0 +1,325 @@
;; 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.svg.path.legacy-parser1
"The first SVG Path parser implementation.
Written in a mix of CLJS and JS code and used in production until
1.19, used mainly for tests."
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.path :as upg]
[app.common.svg :as csvg]
[app.common.svg.path.arc-to-bezier :as a2b]
[app.common.svg.path.command :as upc]
[cuerdas.core :as str]))
(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*")
;; Matches numbers for path values allows values like... -.01, 10, +12.22
;; 0 and 1 are special because can refer to flags
(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?")
(def flag-regex #"[01]")
(defn extract-params [cmd-str extract-commands]
(loop [result []
extract-idx 0
current {}
remain (-> cmd-str (subs 1) (str/trim))]
(let [[param type] (nth extract-commands extract-idx)
regex (case type
:flag flag-regex
#_:number num-regex)
match (re-find regex remain)]
(if match
(let [value (-> match first csvg/fix-dot-number d/read-string)
remain (str/replace-first remain regex "")
current (assoc current param value)
extract-idx (inc extract-idx)
[result current extract-idx]
(if (>= extract-idx (count extract-commands))
[(conj result current) {} 0]
[result current extract-idx])]
(recur result
extract-idx
current
remain))
(cond-> result
(seq current) (conj current))))))
;; Path specification
;; https://www.w3.org/TR/SVG11/paths.html
(defmulti parse-command (comp str/upper first))
(defmethod parse-command "M" [cmd]
(let [relative (str/starts-with? cmd "m")
param-list (extract-params cmd [[:x :number]
[:y :number]])]
(into [{:command :move-to
:relative relative
:params (first param-list)}]
(for [params (rest param-list)]
{:command :line-to
:relative relative
:params params}))))
(defmethod parse-command "Z" [_]
[{:command :close-path}])
(defmethod parse-command "L" [cmd]
(let [relative (str/starts-with? cmd "l")
param-list (extract-params cmd [[:x :number]
[:y :number]])]
(for [params param-list]
{:command :line-to
:relative relative
:params params})))
(defmethod parse-command "H" [cmd]
(let [relative (str/starts-with? cmd "h")
param-list (extract-params cmd [[:value :number]])]
(for [params param-list]
{:command :line-to-horizontal
:relative relative
:params params})))
(defmethod parse-command "V" [cmd]
(let [relative (str/starts-with? cmd "v")
param-list (extract-params cmd [[:value :number]])]
(for [params param-list]
{:command :line-to-vertical
:relative relative
:params params})))
(defmethod parse-command "C" [cmd]
(let [relative (str/starts-with? cmd "c")
param-list (extract-params cmd [[:c1x :number]
[:c1y :number]
[:c2x :number]
[:c2y :number]
[:x :number]
[:y :number]])
]
(for [params param-list]
{:command :curve-to
:relative relative
:params params})))
(defmethod parse-command "S" [cmd]
(let [relative (str/starts-with? cmd "s")
param-list (extract-params cmd [[:cx :number]
[:cy :number]
[:x :number]
[:y :number]])]
(for [params param-list]
{:command :smooth-curve-to
:relative relative
:params params})))
(defmethod parse-command "Q" [cmd]
(let [relative (str/starts-with? cmd "q")
param-list (extract-params cmd [[:cx :number]
[:cy :number]
[:x :number]
[:y :number]])]
(for [params param-list]
{:command :quadratic-bezier-curve-to
:relative relative
:params params})))
(defmethod parse-command "T" [cmd]
(let [relative (str/starts-with? cmd "t")
param-list (extract-params cmd [[:x :number]
[:y :number]])]
(for [params param-list]
{:command :smooth-quadratic-bezier-curve-to
:relative relative
:params params})))
(defmethod parse-command "A" [cmd]
(let [relative (str/starts-with? cmd "a")
param-list (extract-params cmd [[:rx :number]
[:ry :number]
[:x-axis-rotation :number]
[:large-arc-flag :flag]
[:sweep-flag :flag]
[:x :number]
[:y :number]])]
(for [params param-list]
{:command :elliptical-arc
:relative relative
:params params})))
(defn smooth->curve
[{:keys [params]} pos handler]
(let [{c1x :x c1y :y} (upg/calculate-opposite-handler pos handler)]
{:c1x c1x
:c1y c1y
:c2x (:cx params)
:c2y (:cy params)}))
(defn quadratic->curve
[sp ep cp]
(let [cp1 (-> (gpt/to-vec sp cp)
(gpt/scale (/ 2 3))
(gpt/add sp))
cp2 (-> (gpt/to-vec ep cp)
(gpt/scale (/ 2 3))
(gpt/add ep))]
{:c1x (:x cp1)
:c1y (:y cp1)
:c2x (:x cp2)
:c2y (:y cp2)}))
(defn arc->beziers*
[from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation]
(a2b/calculateBeziers from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation))
(defn arc->beziers [from-p command]
(let [to-command
(fn [[_ _ c1x c1y c2x c2y x y]]
{:command :curve-to
:relative (:relative command)
:params {:c1x c1x :c1y c1y
:c2x c2x :c2y c2y
:x x :y y}})
{from-x :x from-y :y} from-p
{:keys [rx ry x-axis-rotation large-arc-flag sweep-flag x y]} (:params command)
result (arc->beziers* from-x from-y x y large-arc-flag sweep-flag rx ry x-axis-rotation)]
(mapv to-command result)))
(defn simplify-commands
"Removes some commands and convert relative to absolute coordinates"
[commands]
(let [simplify-command
;; prev-pos : previous position for the current path. Necessary for relative commands
;; prev-start : previous move-to necessary for Z commands
;; prev-cc : previous command control point for cubic beziers
;; prev-qc : previous command control point for quadratic curves
(fn [[result prev-pos prev-start prev-cc prev-qc] [command _prev]]
(let [command (assoc command :prev-pos prev-pos)
command
(cond-> command
(:relative command)
(-> (assoc :relative false)
(d/update-in-when [:params :c1x] + (:x prev-pos))
(d/update-in-when [:params :c1y] + (:y prev-pos))
(d/update-in-when [:params :c2x] + (:x prev-pos))
(d/update-in-when [:params :c2y] + (:y prev-pos))
(d/update-in-when [:params :cx] + (:x prev-pos))
(d/update-in-when [:params :cy] + (:y prev-pos))
(d/update-in-when [:params :x] + (:x prev-pos))
(d/update-in-when [:params :y] + (:y prev-pos))
(cond->
(= :line-to-horizontal (:command command))
(d/update-in-when [:params :value] + (:x prev-pos))
(= :line-to-vertical (:command command))
(d/update-in-when [:params :value] + (:y prev-pos)))))
params (:params command)
orig-command command
command
(cond-> command
(= :line-to-horizontal (:command command))
(-> (assoc :command :line-to)
(update :params dissoc :value)
(assoc-in [:params :x] (:value params))
(assoc-in [:params :y] (:y prev-pos)))
(= :line-to-vertical (:command command))
(-> (assoc :command :line-to)
(update :params dissoc :value)
(assoc-in [:params :y] (:value params))
(assoc-in [:params :x] (:x prev-pos)))
(= :smooth-curve-to (:command command))
(-> (assoc :command :curve-to)
(update :params dissoc :cx :cy)
(update :params merge (smooth->curve command prev-pos prev-cc)))
(= :quadratic-bezier-curve-to (:command command))
(-> (assoc :command :curve-to)
(update :params dissoc :cx :cy)
(update :params merge (quadratic->curve prev-pos (gpt/point params) (gpt/point (:cx params) (:cy params)))))
(= :smooth-quadratic-bezier-curve-to (:command command))
(-> (assoc :command :curve-to)
(update :params merge (quadratic->curve prev-pos (gpt/point params) (upg/calculate-opposite-handler prev-pos prev-qc)))))
result (if (= :elliptical-arc (:command command))
(into result (arc->beziers prev-pos command))
(conj result command))
next-cc (case (:command orig-command)
:smooth-curve-to
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
:curve-to
(gpt/point (get-in orig-command [:params :c2x]) (get-in orig-command [:params :c2y]))
(:line-to-horizontal :line-to-vertical)
(gpt/point (get-in command [:params :x]) (get-in command [:params :y]))
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
next-qc (case (:command orig-command)
:quadratic-bezier-curve-to
(gpt/point (get-in orig-command [:params :cx]) (get-in orig-command [:params :cy]))
:smooth-quadratic-bezier-curve-to
(upg/calculate-opposite-handler prev-pos prev-qc)
(gpt/point (get-in orig-command [:params :x]) (get-in orig-command [:params :y])))
next-pos (if (= :close-path (:command command))
prev-start
(upc/command->point prev-pos command))
next-start (if (= :move-to (:command command)) next-pos prev-start)]
[result next-pos next-start next-cc next-qc]))
start (first commands)
start (cond-> start
(:relative start)
(assoc :relative false))
start-pos (gpt/point (:params start))]
(->> (map vector (rest commands) commands)
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
(first))))
(defn parse [path-str]
(if (empty? path-str)
path-str
(let [clean-path-str
(-> path-str
(str/trim)
;; Change "commas" for spaces
(str/replace #"," " ")
;; Remove all consecutive spaces
(str/replace #"\s+" " "))
commands (re-seq commands-regex clean-path-str)]
(-> (mapcat parse-command commands)
(simplify-commands)))))

View File

@@ -4,9 +4,11 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.common.svg.path.legacy
"The first svg path parser implementation in pure clojure, used as reference impl
and for tests."
(ns app.common.svg.path.legacy-parser2
"The second SVG Path parser implementation.
Written in crossplatform CLJC code. Used meanwhile a hight
performance parser is developed in the 1.20 version."
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
@@ -16,7 +18,6 @@
[app.common.svg.path.command :as upc]
[cuerdas.core :as str]))
(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*")
(def regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?")
@@ -296,10 +297,10 @@
y1p (+ (/ (* (- sin-phi) (- x1 x2)) 2)
(/ (* cos-phi (- y1 y2)) 2))]
(if (or (zero? x1p)
(zero? y1p)
(zero? rx)
(zero? ry))
(if (or (and (zero? x1p)
(zero? y1p))
(and (zero? rx)
(zero? ry)))
[]
(let [
rx (mth/abs rx)
@@ -462,19 +463,10 @@
(reduce simplify-command [[start] start-pos start-pos start-pos start-pos])
(first))))
(defn parse
[path-str]
(if (empty? path-str)
path-str
(let [commands (re-seq commands-regex path-str)]
(->> (mapcat parse-command commands)
(simplify-commands)
(map (fn [segment]
;; (prn "LEGACY:" segment)
segment))))))
(simplify-commands)))))

View File

@@ -1,3 +1,11 @@
/**
* Performance focused pure javascript implementation of the
* SVG path parser.
*
* @author KALEIDOS INC
* @license MPL-2.0 <https://www.mozilla.org/en-US/MPL/2.0/>
*/
import cljs from "goog:cljs.core";
const MOVE_TO = cljs.keyword("move-to");
@@ -674,7 +682,13 @@ export function arcToBeziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
let x1p = (cosPhi * (x1 - x2)) / 2 + (sinPhi * (y1 - y2)) / 2;
let y1p = (-sinPhi * (x1 - x2)) / 2 + (cosPhi * (y1 - y2)) / 2;
if (x1p === 0 || y1p === 0 || rx === 0 || ry === 0) {
if (x1p === 0 && y1p === 0) {
// we're asked to draw line to itself
return [];
}
if (rx === 0 || ry === 0) {
// one of the radii is zero
return [];
}
@@ -711,7 +725,7 @@ export function arcToBeziers(x1, y1, x2, y2, fa, fs, rx, ry, phi) {
// commands.
function simplifyPathData(pdata) {
var result = [];
var lastType = null;
var lastCommand = null;
var lastControlX = null;
var lastControlY = null;
@@ -724,8 +738,9 @@ function simplifyPathData(pdata) {
for (let i=0; i<pdata.length; i++) {
const segment = pdata[i];
const currentCommand = segment.command;
switch(segment.command) {
switch(currentCommand) {
case "M":
var x = segment.params[0];
var y = segment.params[1];
@@ -787,7 +802,7 @@ function simplifyPathData(pdata) {
var cx1, cy1;
if (lastType === "C" || lastType === "S") {
if (lastCommand === "C" || lastCommand === "S") {
cx1 = currentX + (currentX - lastControlX);
cy1 = currentY + (currentY - lastControlY);
} else {
@@ -813,7 +828,7 @@ function simplifyPathData(pdata) {
var x1, y1;
if (lastType === "Q" || lastType === "T") {
if (lastCommand === "Q" || lastCommand === "T") {
x1 = currentX + (currentX - lastControlX);
y1 = currentY + (currentY - lastControlY);
} else {
@@ -876,6 +891,7 @@ function simplifyPathData(pdata) {
currentX = x;
currentY = y;
} else if (currentX !== x || currentY !== y) {
var segments = arcToBeziers(currentX, currentY, x, y, fa, fs, rx, ry, phi);
result.push(...segments);
@@ -891,7 +907,7 @@ function simplifyPathData(pdata) {
break;
}
lastCommand = segment.command;
lastCommand = currentCommand;
}
return result;

View File

@@ -229,6 +229,7 @@
:svg-viewbox selrect
:svg-attrs attrs
:svg-transform transform
:strokes []
:fills []})
(gsh/translate-to-frame origin)))))
@@ -355,9 +356,9 @@
(assoc :svg-attrs props))))))
(defn setup-fill
[shape]
(let [color-attr (str/trim (dm/get-in shape [:svg-attrs :fill]))
color-attr (if (= color-attr "currentColor") clr/black color-attr)
[shape]
(let [color-attr (str/trim (dm/get-in shape [:svg-attrs :fill]))
color-attr (if (= color-attr "currentColor") clr/black color-attr)
color-style (str/trim (dm/get-in shape [:svg-attrs :style :fill]))
color-style (if (= color-style "currentColor") clr/black color-style)]
(cond-> shape
@@ -384,6 +385,7 @@
(update :svg-attrs dissoc :fillOpacity)
(assoc-in [:fills 0 :fill-opacity] (-> (dm/get-in shape [:svg-attrs :style :fillOpacity])
(d/parse-double 1)))))))
(defn- setup-stroke
[shape]
(let [attrs (get shape :svg-attrs)
@@ -422,7 +424,8 @@
(dissoc :stroke)
(dissoc :strokeLinecap)
(dissoc :strokeWidth)
(dissoc :strokeOpacity)))))]
(dissoc :strokeOpacity))))
(d/without-nils))]
(cond-> (assoc shape :svg-attrs attrs)
(some? color)
@@ -434,7 +437,7 @@
(and (some? color) (some? width))
(assoc-in [:strokes 0 :stroke-width] width)
(and (some? linecap) (= (:type shape) :path)
(and (some? linecap) (cfh/path-shape? shape)
(or (= linecap :round) (= linecap :square)))
(assoc :stroke-cap-start linecap
:stroke-cap-end linecap)
@@ -464,9 +467,6 @@
(-> (update-in [:svg-attrs :style] dissoc :mixBlendMode)
(assoc :blend-mode (-> (dm/get-in shape [:svg-attrs :style :mixBlendMode]) assert-valid-blend-mode)))))
(defn tag->name
"Given a tag returns its layer name"
[tag]

View File

@@ -1,7 +1,18 @@
(ns app.common.thumbnails
(:require [cuerdas.core :as str]))
(:require
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
(defn fmt-object-id
"Returns ids formatted as a string (object-id)"
[file-id page-id frame-id tag]
(str/ffmt "%/%/%/%" file-id page-id frame-id tag))
(defn file-id?
"Returns ids formatted as a string (file-id)"
[object-id file-id]
(str/starts-with? object-id (str/concat file-id "/")))
(defn get-file-id
[object-id]
(uuid/uuid (str/slice object-id 0 (str/index-of object-id "/"))))

View File

@@ -676,7 +676,7 @@
[id cell])))
(defn remove-grid-column
[parent index]
[parent index objects]
(let [track-num (inc index)
@@ -692,10 +692,10 @@
(-> parent
(update :layout-grid-columns d/remove-at-index index)
(update :layout-grid-cells update-cells)
(assign-cells))))
(assign-cells objects))))
(defn remove-grid-row
[parent index]
[parent index objects]
(let [track-num (inc index)
decrease-track-num (make-decrease-track-num :row :row-span track-num)
@@ -710,7 +710,7 @@
(-> parent
(update :layout-grid-rows d/remove-at-index index)
(update :layout-grid-cells update-cells)
(assign-cells))))
(assign-cells objects))))
(defn- reorder-grid-tracks
"Swap the positions of the tracks info"
@@ -781,6 +781,7 @@
parent
(reorder-grid-tracks parent tracks-props from-index to-index)]
(cond-> parent
move-content?
(swap-track-content prop from-track to-track))))
@@ -828,14 +829,24 @@
(defn check-deassigned-cells
"Clean the cells whith shapes that are no longer in the layout"
[parent]
[parent objects]
(let [child? (set (:shapes parent))
cells (update-vals
(:layout-grid-cells parent)
(fn [cell] (update cell :shapes #(filterv child? %))))]
(let [child-set (set (:shapes parent))
(assoc parent :layout-grid-cells cells)))
assigned?
(fn [id]
(and (contains? child-set id)
(not (position-absolute? objects id))))
cells
(update-vals
(:layout-grid-cells parent)
(fn [cell]
(-> cell
(update :shapes #(filterv assigned? %)))))]
(-> parent
(assoc :layout-grid-cells cells))))
(defn overlapping-cells
"Find overlapping cells"
@@ -902,12 +913,12 @@
;; - Shape duplication
;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
(defn assign-cells
[parent]
[parent objects]
(let [;; TODO: Remove this, shouldn't be happening
;;overlaps (overlapping-cells parent)
;;_ (when (not (empty? overlaps))
;; (.warn js/console "OVERLAPS" overlaps))
parent (cond-> (check-deassigned-cells parent)
parent (cond-> (check-deassigned-cells parent objects)
#_(d/not-empty? overlaps)
#_(fix-overlaps overlaps))
@@ -915,7 +926,9 @@
(into #{} (mapcat (comp :shapes second)) (:layout-grid-cells parent))
no-cell-shapes
(->> (:shapes parent) (remove shape-has-cell?))
(->> (:shapes parent)
(remove shape-has-cell?)
(remove (partial position-absolute? objects)))
parent (position-auto-shapes parent)]
@@ -1174,7 +1187,7 @@
(let [;; Temporary remove the children when moving them
frame (-> frame
(update :shapes #(d/removev children %))
(assign-cells))
(assign-cells objects))
children (->> children (remove #(position-absolute? objects %)))]
@@ -1182,7 +1195,7 @@
(update :shapes d/concat-vec children)
(cond-> (some? cell)
(push-into-cell children row column))
(assign-cells))))
(assign-cells objects))))
(defn add-children-to-index
[parent ids objects to-index]

View File

Binary file not shown.

View File

@@ -10,9 +10,9 @@
[app.common.pprint :as pp]
[app.common.math :as mth]
[app.common.svg.path :as svg.path]
[app.common.svg.path.legacy :as svg.path.legacy]
[app.common.svg.path.legacy-parser2 :as svg.path.legacy2]
[clojure.test :as t]
#?(:cljs [common-tests.arc-to-bezier :as impl])))
#?(:cljs [app.common.svg.path.legacy-parser2 :as svg.path.legacy1])))
(t/deftest parse-test-1
(let [data (str "m -994.563 4564.1423 149.3086 -52.8821 30.1828 "
@@ -23,14 +23,25 @@
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy/parse data)
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(update entry :params #(into (sorted-map) %)))))
result3 #?(:cljs (->> (svg.path.legacy1/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
:clj nil)]
(t/is (= 15
(count result1)
(count result2)))
#?(:cljs
(t/is (= 15
(count result1)
(count result3))))
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)]
@@ -40,6 +51,14 @@
(t/is (= (:params item1)
(:params item2)))
#?(:cljs
(let [item3 (nth result3 i)]
(t/is (= (:command item1)
(:command item3)))
(t/is (= (:params item1)
(:params item3)))))
#_(println "------------------------")
#_(pp/pprint (dissoc item1 :relative))
#_(pp/pprint (dissoc item2 :prev-pos :relative))))))
@@ -92,7 +111,7 @@
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy/parse data)
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
@@ -108,7 +127,6 @@
(t/is (= (:command item1)
(:command item2)))
;; (println "================" (:command item1))
;; (pp/pprint (:params item1))
;; (println "---------")
@@ -124,7 +142,7 @@
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy/parse data)
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
@@ -142,6 +160,198 @@
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001)))))))
(t/deftest parse-test-4
(let [data (str "m480 839-41-37c-70.512-64.747-128.807-120.601-174.884-167.561C218.039 587.48 181.333 "
"545.5 154 508.5S107.5 438 96.5 408 80 347.667 80 317c0-60.103 20.167-110.296 "
"60.5-150.577C180.833 126.141 230.667 106 290 106c38 0 73.167 9 105.5 27s60.5 44 84.5 "
"78c28-36 57.667-62.5 89-79.5S634 106 670 106c59.333 0 109.167 20.141 149.5 60.423C859.833 "
"206.704 880 256.897 880 317c0 30.667-5.5 61-16.5 91s-30.167 63.5-57.5 100.5-64.039 "
"78.98-110.116 125.939C649.807 681.399 591.512 737.253 521 802l-41 37Zm0-79c67.491-61.997 "
"123.03-115.163 166.618-159.498C690.206 556.167 724.833 517.333 750.5 484s43.667-63.045 "
"54-89.135c10.333-26.091 15.5-51.997 15.5-77.72 0-44.097-14-80.312-42-108.645S714.075 166 "
"670.225 166c-34.349 0-66.141 10.5-95.375 31.5C545.617 218.5 522 248 504 286h-49c-17.333"
"-37.333-40.617-66.667-69.85-88-29.234-21.333-61.026-32-95.375-32C245.925 166 210 180.167 "
"182 208.5s-42 64.605-42 108.816c0 25.789 5.167 51.851 15.5 78.184s28.333 56.333 54 90S270 "
"558 314 602s99.333 96.667 166 158Zm0-297Z")
expect [{:command :move-to, :params {:x 480.0, :y 839.0}}
{:command :line-to, :params {:x 439.0, :y 802.0}}
{:command :curve-to, :params {:c1x 368.488, :c1y 737.253, :c2x 310.193, :c2y 681.399, :x 264.116, :y 634.439}}
{:command :curve-to, :params {:c1x 218.039, :c1y 587.48, :c2x 181.333, :c2y 545.5, :x 154.0, :y 508.5}}
{:command :curve-to, :params {:c1x 126.667, :c1y 471.5, :c2x 107.5, :c2y 438.0, :x 96.5, :y 408.0}}
{:command :curve-to, :params {:c1x 85.5, :c1y 378.0, :c2x 80.0, :c2y 347.667, :x 80.0, :y 317.0}}
{:command :curve-to, :params {:c1x 80.0, :c1y 256.897, :c2x 100.167, :c2y 206.704, :x 140.5, :y 166.423}}
{:command :curve-to, :params {:c1x 180.833, :c1y 126.141, :c2x 230.667, :c2y 106.0, :x 290.0, :y 106.0}}
{:command :curve-to, :params {:c1x 328.0, :c1y 106.0, :c2x 363.16700000000003, :c2y 115.0, :x 395.5, :y 133.0}}
{:command :curve-to, :params {:c1x 427.83299999999997, :c1y 151.0, :c2x 456.0, :c2y 177.0, :x 480.0, :y 211.0}}
{:command :curve-to, :params {:c1x 508.0, :c1y 175.0, :c2x 537.667, :c2y 148.5, :x 569.0, :y 131.5}}
{:command :curve-to, :params {:c1x 600.333, :c1y 114.5, :c2x 634.0, :c2y 106.0, :x 670.0, :y 106.0}}
{:command :curve-to, :params {:c1x 729.333, :c1y 106.0, :c2x 779.167, :c2y 126.14099999999999, :x 819.5, :y 166.423}}
{:command :curve-to, :params {:c1x 859.833, :c1y 206.704, :c2x 880.0, :c2y 256.897, :x 880.0, :y 317.0}}
{:command :curve-to, :params {:c1x 880.0, :c1y 347.66700000000003, :c2x 874.5, :c2y 378.0, :x 863.5, :y 408.0}}
{:command :curve-to, :params {:c1x 852.5, :c1y 438.0, :c2x 833.333, :c2y 471.5, :x 806.0, :y 508.5}}
{:command :curve-to, :params {:c1x 778.667, :c1y 545.5, :c2x 741.961, :c2y 587.48, :x 695.884, :y 634.439}}
{:command :curve-to, :params {:c1x 649.807, :c1y 681.399, :c2x 591.512, :c2y 737.253, :x 521.0, :y 802.0}}
{:command :line-to, :params {:x 480.0, :y 839.0}}
{:command :close-path, :params {}}
{:command :move-to, :params {:x 480.0, :y 760.0}}
{:command :curve-to, :params {:c1x 547.491, :c1y 698.003, :c2x 603.03, :c2y 644.837, :x 646.6179999999999, :y 600.502}}
{:command :curve-to, :params {:c1x 690.206, :c1y 556.167, :c2x 724.833, :c2y 517.333, :x 750.5, :y 484.0}}
{:command :curve-to, :params {:c1x 776.167, :c1y 450.66700000000003, :c2x 794.167, :c2y 420.955, :x 804.5, :y 394.865}}
{:command :curve-to, :params {:c1x 814.833, :c1y 368.774, :c2x 820.0, :c2y 342.868, :x 820.0, :y 317.145}}
{:command :curve-to, :params {:c1x 820.0, :c1y 273.048, :c2x 806.0, :c2y 236.83299999999997, :x 778.0, :y 208.5}}
{:command :curve-to, :params {:c1x 750.0, :c1y 180.16700000000003, :c2x 714.075, :c2y 166.0, :x 670.225, :y 166.0}}
{:command :curve-to, :params {:c1x 635.876, :c1y 166.0, :c2x 604.0840000000001, :c2y 176.5, :x 574.85, :y 197.5}}
{:command :curve-to, :params {:c1x 545.617, :c1y 218.5, :c2x 522.0, :c2y 248.0, :x 504.0, :y 286.0}}
{:command :line-to, :params {:x 455.0, :y 286.0}}
{:command :curve-to, :params {:c1x 437.66700000000003, :c1y 248.667, :c2x 414.383, :c2y 219.333, :x 385.15, :y 198.0}}
{:command :curve-to, :params {:c1x 355.916, :c1y 176.667, :c2x 324.12399999999997, :c2y 166.0, :x 289.775, :y 166.0}}
{:command :curve-to, :params {:c1x 245.925, :c1y 166.0, :c2x 210.0, :c2y 180.167, :x 182.0, :y 208.5}}
{:command :curve-to, :params {:c1x 154.0, :c1y 236.833, :c2x 140.0, :c2y 273.105, :x 140.0, :y 317.31600000000003}}
{:command :curve-to, :params {:c1x 140.0, :c1y 343.105, :c2x 145.167, :c2y 369.16700000000003, :x 155.5, :y 395.5}}
{:command :curve-to,
:params {:c1x 165.833, :c1y 421.83299999999997, :c2x 183.833, :c2y 451.83299999999997, :x 209.5, :y 485.5}}
{:command :curve-to, :params {:c1x 235.167, :c1y 519.167, :c2x 270.0, :c2y 558.0, :x 314.0, :y 602.0}}
{:command :curve-to, :params {:c1x 358.0, :c1y 646.0, :c2x 413.33299999999997, :c2y 698.667, :x 480.0, :y 760.0}}
{:command :close-path, :params {}}
{:command :move-to, :params {:x 480.0, :y 463.0}}
{:command :close-path, :params {}}]
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 41
(count result1)
(count result2)))
;; (pp/pprint result1 {:length 50})
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)
item3 (nth expect i)]
(t/is (= (:command item1)
(:command item2)
(:command item3)))
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
(t/is (mth/close? v (get-in item3 [:params k]) 0.000000001))
)))))
(t/deftest parse-test-5
(let [data (str "M363 826"
"q11-56 54-93"
"t101-37"
"h176"
"q22-35 34-75.179 12-40.178 12-84.821 0-125.357-87.321-212.679"
"Q565.357 236 440 236"
"t-212.679 87.321"
"Q140 410.643 140 536"
"q0 105 63 184.5T363 826Zm157 190"
"q-58 0-102"
"-36.5T363 888q-122-26-202.5-124T80 536q0-150 105-255t255-105"
"q150 0 255 105t105 "
"255q0 43-9.5 83.5T763 696q66 0 111.5 47T920 856q0 66-47 113t-113 47H520Zm-80"
"-485Zm200 325ZM520 956h240q42 0 71-29t29-71q0-42-29-71t-71-29H520q-42 0-71 29t"
"-29 71q0 42 29 71t71 29Zm-.175-70Q507 886 498.5 877.325"
"q-8.5-8.676-8.5-21.5 0"
"-12.825 8.675-21.325 8.676-8.5 21.5-8.5 12.825 0 21.325 8.675 8.5 8.676 8.5 "
"21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 8.5Zm120 0Q627 886 618.5 877.325q-8.5"
"-8.676-8.5-21.5 0-12.825 8.675-21.325 8.676-8.5 21.5-8.5 12.825 0 21.325 8.675 "
"8.5 8.676 8.5 21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 8.5Zm120 0Q747 886 "
"738.5 877.325q-8.5-8.676-8.5-21.5 0-12.825 8.675-21.325 8.676-8.5 21.5-8.5 "
"12.825 0 21.325 8.675 8.5 8.676 8.5 21.5 0 12.825-8.675 21.325-8.676 8.5-21.5 "
"8.5Z"
)
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 76
(count result1)
(count result2)))
;; (pp/pprint result1 {:length 100})
;; (pp/pprint result2 {:length 50})
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)
]
(t/is (= (:command item1)
(:command item2)))
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
)))))
(t/deftest parse-test-6
(let [data (str "M3.078 3.548v16.9a.5.5 0 0 0 1 0v-16.9a.5.5 0 0 0-1 0ZM18.422 11.5"
"H7.582a2.5 2.5 0 0 1-2.5-2.5V6.565a2.5 2.5 0 0 1 2.5-2.5"
"h10.84a2.5 2.5 0 0 1 2.5 2.5V9a2.5 2.5 0 0 1-2.5 2.5Z"
"M7.582 5.065a1.5 1.5 0 0 0-1.5 1.5V9a1.5 1.5 0 0 0 1.5 1.5"
"h10.84a1.5 1.5 0 0 0 1.5-1.5V6.565a1.5 1.5 0 0 0-1.5-1.5Z"
"M13.451 19.938H7.582a2.5 2.5 0 0 1-2.5-2.5V15"
"a2.5 2.5 0 0 1 2.5-2.5h5.869a2.5 2.5 0 0 1 2.5 2.5v2.436"
"a2.5 2.5 0 0 1-2.5 2.502ZM7.582 13.5a1.5 1.5 0 0 0-1.5 1.5v2.436"
"a1.5 1.5 0 0 0 1.5 1.5h5.869a1.5 1.5 0 0 0 1.5-1.5V15"
"a1.5 1.5 0 0 0-1.5-1.5Z")
result1 (->> (svg.path/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))
result2 (->> (svg.path.legacy2/parse data)
(mapv (fn [entry]
(update entry :params #(into (sorted-map) %)))))]
(t/is (= 47
(count result1)
(count result2)))
;; (pp/pprint result1 {:length 100})
;; (pp/pprint result2 {:length 50})
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item2 (nth result2 i)
]
(t/is (= (:command item1)
(:command item2)))
(doseq [[k v] (:params item1)]
(t/is (mth/close? v (get-in item2 [:params k]) 0.000000001))
)))
#?(:cljs
(let [result3 (svg.path.legacy1/parse data)]
(t/is (= 47
(count result1)
(count result3)))
(dotimes [i (count result1)]
(let [item1 (nth result1 i)
item3 (nth result2 i)]
(t/is (= (:command item1)
(:command item3)))
(t/is (= (:params item1)
(:params item3)))))))))
(t/deftest arc-to-bezier-1
(let [expected1 [-1.6697754290362354e-13
-5.258016244624741e-13
@@ -179,7 +389,7 @@
(nth expected2 (+ i 2))
0.0000000001))))
(let [[result1 result2 :as total] (svg.path.legacy/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
(let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
(t/is (= (count total) 2))
(dotimes [i (count result1)]
@@ -190,7 +400,96 @@
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001))))))
0.000000000001))))
#?(:cljs
(let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 0 0 30 50 0 0 1 162.55 162.45)]
(t/is (= (count total) 2))
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 i)
0.000000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001)))))
))
(t/deftest arc-to-bezier-2
(let [expected1 [3.0779999999999994,
20.448,
3.0780000082296834,
20.724142369096132,
3.3018576309038683,
20.94799998509884,
3.5779999999999994,
20.94799998509884]
expected2 [3.5779999999999994,
20.94799998509884,
3.854142369096131,
20.94799998509884,
4.077999991770315,
20.724142369096132,
4.077999999999999,
20.448]]
(let [[result1 result2 :as total] (->> (svg.path/arc->beziers 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)
(mapv (fn [segment]
(vec (.-params segment)))))]
(t/is (= (count total) 2))
;; (println "================" 11111111)
;; (pp/pprint expected1 {:width 50})
;; (println "------------")
;; (pp/pprint result1 {:width 50})
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 (+ i 2))
0.0000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 (+ i 2))
0.0000000001))))
(let [[result1 result2 :as total] (svg.path.legacy2/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)]
(t/is (= (count total) 2))
;; (println "================" 11111111)
;; (pp/pprint expected1 {:width 50})
;; (println "------------")
;; (pp/pprint (vec result1) {:width 50})
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 i)
0.000000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001))))
#?(:cljs
(let [[result1 result2 :as total] (svg.path.legacy1/arc->beziers* 3.078 20.448 4.077999999999999 20.448 0 0 0.5 0.5 0)]
(t/is (= (count total) 2))
(dotimes [i (count result1)]
(t/is (mth/close? (nth result1 i)
(nth expected1 i)
0.000000000001)))
(dotimes [i (count result2)]
(t/is (mth/close? (nth result2 i)
(nth expected2 i)
0.000000000001)))))
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -220,14 +519,14 @@
"59.9137 -301.293 -1.0595 -51.375 25.7186 -261.0492 -7.706 ")
pattern [[:x :number] [:y :number]]]
(t/is (= expected (svg.path.legacy/extract-params cmdstr pattern)))))
(t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern)))))
(t/deftest extract-params-legacy-2
(let [expected [{:x -994.563, :y 4564.1423 :r 0}]
cmdstr (str "m -994.563 4564.1423 0")
pattern [[:x :number] [:y :number] [:r :flag]]]
(t/is (= expected (svg.path.legacy/extract-params cmdstr pattern)))))
(t/is (= expected (svg.path.legacy2/extract-params cmdstr pattern)))))
(t/deftest extract-params-legacy-3
(let [cmdstr (str "a1.42 1.42 0 00-1.415-1.416 1.42 1.42 0 00-1.416 1.416 "
@@ -245,7 +544,7 @@
[:sweep-flag :flag]
[:x :number]
[:y :number]]
result (svg.path.legacy/extract-params cmdstr pattern)]
result (svg.path.legacy2/extract-params cmdstr pattern)]
(t/is (= (nth result 0)
(nth expected 0)))

View File

@@ -6,10 +6,17 @@
org.clojure/clojure {:mvn/version "1.11.1"}
binaryage/devtools {:mvn/version "RELEASE"}
metosin/reitit-core {:mvn/version "0.5.18"}
funcool/beicon {:mvn/version "2021.07.05-1"}
funcool/okulary {:mvn/version "2022.04.11-16"}
funcool/potok {:mvn/version "2022.12.16-71"}
funcool/potok2
{:git/tag "v2.0"
:git/sha "2bb377b"
:git/url "https://github.com/funcool/potok.git"}
funcool/beicon2
{:git/tag "v2.0"
:git/sha "e7135e0"
:git/url "https://github.com/funcool/beicon.git"}
funcool/rumext
{:git/tag "v2.7"

View File

@@ -46,6 +46,7 @@
"@storybook/react": "^7.5.3",
"@storybook/react-vite": "^7.5.3",
"@storybook/testing-library": "^0.2.2",
"@types/node": "^20.10.5",
"animate.css": "^4.1.1",
"autoprefixer": "^10.4.15",
"concurrently": "^8.2.2",
@@ -73,13 +74,13 @@
"sass": "^1.66.1",
"shadow-cljs": "2.26.2",
"storybook": "^7.5.3",
"typescript": "^5.3.3",
"vite": "^5.0.2"
},
"dependencies": {
"date-fns": "^2.30.0",
"draft-js": "^0.11.7",
"eventsource-parser": "^1.1.1",
"gl-matrix": "^3.4.3",
"highlight.js": "^11.8.0",
"js-beautify": "^1.14.9",
"jszip": "^3.10.1",
@@ -91,7 +92,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-virtualized": "^9.22.3",
"rxjs": "~7.8.1",
"rxjs": "8.0.0-alpha.13",
"sax": "^1.2.4",
"source-map-support": "^0.5.21",
"tdigest": "^0.1.2",

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M10.357 2.813h2.828v2.829m-10.37 4.713v2.829h2.828m7.071-9.9l-9.428 9.429z"/>
</svg>

After

Width:  |  Height:  |  Size: 206 B

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 279 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M2.4 8a6 6 0 111.758 4.242M2.4 8l2.1-2zm0 0L1 5.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 182 B

View File

@@ -148,6 +148,7 @@
stroke: var(--button-tertiary-foreground-color-active);
}
}
&:global(.disabled),
&[disabled],
&:disabled {

View File

@@ -4,7 +4,7 @@
//
// Copyright (c) KALEIDOS INC
@use "sass:color" as color;
@use "sass:color";
:root {
// DARK
@@ -25,16 +25,19 @@
--dark-ok-color: var(--strong-green);
--dark-warning-color: #ff6432;
--dark-pending-color: var(--lilac);
--dark-error-color: #ec1f1f;
--dark-error-color: #ff3277;
--default-presence-color: #dee563;
// LIGHT
--light-gray-1: #fff;
--light-gray-2: #e8eaee;
--light-gray-2-30: rgba(232, 234, 238, 0.3);
--light-gray-2-80: rgba(232, 234, 238, 0.8);
--light-gray-3: #f3f4f6;
--light-gray-4: #eef0f2;
--black: #000;
--off-black: #495e74;
--off-black-30: #{color.change(#495e74, $alpha: 0.3)};
--purple: #6911d4;
--purple-30: rgba(105, 17, 212, 0.2);
--blue: #1345aa;

View File

@@ -157,6 +157,8 @@
.btn-primary {
@extend .button-primary;
text-transform: uppercase;
font-size: $fs-14;
font-weight: $fw400;
}
.btn-secondary {

View File

@@ -14,6 +14,7 @@
--scrollbar-background-color: var(--color-foreground-secondary);
--panel-background-color: var(--color-background-primary);
--panel-title-background-color: var(--color-background-secondary);
--presence-color: var(--default-presence-color);
// BUTTONS
--button-foreground-hover: var(--color-accent-primary);
@@ -179,7 +180,7 @@
--menu-shortcut-foreground-color: var(--color-foreground-secondary);
--menu-shortcut-foreground-color-selected: var(--color-foreground-primary);
--menu-shortcut-foreground-color-hover: var(--color-foreground-primary);
--menu-shadow-color: var(--color-background-subtle);
--menu-shadow-color: var(--shadow-color);
--menu-background-color-disabled: var(--color-background-primary);
--menu-foreground-color-disabled: var(--color-foreground-secondary);
--menu-border-color-disabled: var(--color-background-quaternary);
@@ -297,4 +298,16 @@
--tag-background-color: var(--color-accent-primary);
--link-foreground-color: var(--color-accent-primary);
--resize-area-background-color: var(--color-background-primary);
--resize-area-border-color: var(--color-background-quaternary);
// VIEWER
--viewer-background-color: var(--color-background-secondary);
--viewer-paginator-background-color: var(--color-background-tertiary);
--viewer-controls-background-color: var(--color-background-primary);
--viewer-inspect-border-color: var(--color-background-tertiary);
--viewer-thumbnails-control-foreground-color: var(--color-foreground-secondary);
--viewer-thumbnail-border-color: var(--color-accent-primary);
--viewer-thumbnail-background-color-selected: var(--color-accent-primary-muted);
}

View File

@@ -63,6 +63,13 @@
line-height: 1.2;
}
@mixin codeTypography {
font-family: "robotomono", monospace;
font-size: $fs-12;
font-weight: $fw400;
line-height: 1.2;
}
@mixin textEllipsis {
max-width: 99%;
overflow: hidden;

View File

@@ -21,6 +21,7 @@
--color-accent-primary-muted: var(--green-30);
--color-accent-secondary: var(--lilac);
--color-accent-tertiary: var(--strong-green);
--overlay-color: rgba(0, 0, 0, 0.4);
--ok-color: var(--dark-ok-color);
--warning-color: var(--dark-warning-color);
@@ -28,5 +29,8 @@
--error-color: var(--dark-error-color);
--canvas-color: var(--color-canvas);
--shadow-color: var(--dark-gray-2-30);
--radio-button-box-shadow: 0 0 0 1px var(--dark-gray-2-30) inset;
@include meta.load-css("hljs-dark-theme");
}

View File

@@ -1,98 +1,124 @@
/**
* Panda Syntax Theme for Highlight.js
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
*/
/*!
Theme: GitHub Dark Dimmed
Description: Dark dimmed theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Colors taken from GitHub's CSS
*/
.hljs {
color: #e6e6e6;
background: #2a2c2d;
color: #adbac7;
background: #22272e;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #f47067;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #dcbdfb;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #6cb6ff;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #96d0ff;
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #f69d50;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #768390;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #8ddb8c;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #adbac7;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #316dca;
font-weight: bold;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #eac55f;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #adbac7;
font-style: italic;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #adbac7;
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #b4f1b4;
background-color: #1b4721;
}
.hljs-comment,
.hljs-quote {
color: #bbbbbb;
font-style: italic;
}
.hljs-params {
color: #bbbbbb;
}
.hljs-punctuation,
.hljs-attr {
color: #e6e6e6;
}
.hljs-selector-tag,
.hljs-name,
.hljs-meta {
color: #ff4b82;
}
.hljs-operator,
.hljs-char.escape_ {
color: #b084eb;
}
.hljs-keyword,
.hljs-deletion {
color: #ff75b5;
/* prettylights-syntax-markup-deleted */
color: #ffd8d3;
background-color: #78191b;
}
.hljs-regexp,
.hljs-selector-pseudo,
.hljs-selector-attr,
.hljs-variable.language_ {
color: #ff9ac1;
}
.hljs-subst,
.hljs-property,
.hljs-code,
.hljs-formula,
.hljs-section,
.hljs-title.function_ {
color: #45a9f9;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition,
.hljs-selector-class,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-meta .hljs-string {
color: #19f9d8;
}
.hljs-variable,
.hljs-template-variable,
.hljs-number,
.hljs-literal,
.hljs-type,
.hljs-char.escape_,
.hljs-link,
.hljs-built_in,
.hljs-title,
.hljs-selector-id,
.hljs-tag,
.hljs-doctag,
.hljs-attribute,
.hljs-template-tag,
.hljs-meta .hljs-keyword,
.hljs-punctuation {
color: #ffb86c;
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
}

View File

@@ -1,94 +1,126 @@
/**
* Panda Syntax Theme for Highlight.js
* Based on: https://github.com/tinkertrain/panda-syntax-vscode
* Author: Annmarie Switzer <https://github.com/annmarie-switzer>
*/
/*!
Theme: GitHub
Description: Light theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-light
Current colors taken from GitHub's CSS
*/
.hljs {
color: #2a2c2d;
background: #e6e6e6;
color: #24292e;
background: #ffffff;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #d73a49;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #6f42c1;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #005cc5;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #032f62;
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #e36209;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #6a737d;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #22863a;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #24292e;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #005cc5;
font-weight: bold;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #735c0f;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #24292e;
font-style: italic;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #24292e;
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #22863a;
background-color: #f0fff4;
}
.hljs-comment,
.hljs-quote {
color: #676b79;
font-style: italic;
}
.hljs-params {
color: #676b79;
}
.hljs-punctuation,
.hljs-attr {
color: #2a2c2d;
}
.hljs-selector-tag,
.hljs-name,
.hljs-meta,
.hljs-operator,
.hljs-char.escape_ {
color: #c56200;
}
.hljs-keyword,
.hljs-deletion {
color: #d92792;
/* prettylights-syntax-markup-deleted */
color: #b31d28;
background-color: #ffeef0;
}
.hljs-regexp,
.hljs-selector-pseudo,
.hljs-selector-attr,
.hljs-variable.language_ {
color: #cc5e91;
}
.hljs-subst,
.hljs-property,
.hljs-code,
.hljs-formula,
.hljs-section,
.hljs-title.function_ {
color: #3787c7;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet,
.hljs-addition,
.hljs-selector-class,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-meta .hljs-string {
color: #0d7d6c;
}
.hljs-variable,
.hljs-template-variable,
.hljs-number,
.hljs-literal,
.hljs-type,
.hljs-char.escape_,
.hljs-link,
.hljs-built_in,
.hljs-title,
.hljs-selector-id,
.hljs-tag,
.hljs-doctag,
.hljs-attribute,
.hljs-template-tag,
.hljs-meta .hljs-keyword {
color: #7641bb;
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
color: #6a737d;
}

View File

@@ -21,11 +21,16 @@
--color-accent-primary-muted: var(--purple-30);
--color-accent-secondary: var(--blue);
--color-accent-tertiary: var(--strong-purple);
--overlay-color: rgba(255, 255, 255, 0.4);
--ok-color: var(--light-ok-color);
--warning-color: var(--light-warning-color);
--pending-color: var(--light-pending-color);
--error-color: var(--light-error-color);
--canvas-color: var(--color-canvas);
--shadow-color: var(--off-black-30);
--radio-button-box-shadow: 0 0 0 1px var(--light-gray-2) inset;
@include meta.load-css("hljs-light-theme");
}

View File

@@ -16,7 +16,7 @@
[app.util.webapi :as wapi]
[app.util.zip :as uz]
[app.worker.export :as e]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cuerdas.core :as str]))
(defn parse-data [data]
@@ -55,7 +55,7 @@
(->> (rx/from (vals media))
(rx/map #(assoc % :file-id file-id))
(rx/flat-map
(rx/merge-map
(fn [media]
(let [file-path (str/concat file-id "/media/" (:id media) (cm/mtype->extension (:mtype media)))
blob (data-uri->blob (:uri media))]
@@ -79,38 +79,38 @@
render-stream
(->> files-stream
(rx/flat-map vals)
(rx/flat-map e/process-pages)
(rx/merge-map vals)
(rx/merge-map e/process-pages)
(rx/observe-on :async)
(rx/flat-map e/get-page-data)
(rx/merge-map e/get-page-data)
(rx/share))
colors-stream
(->> files-stream
(rx/flat-map vals)
(rx/merge-map vals)
(rx/map #(vector (:id %) (get-in % [:data :colors])))
(rx/filter #(d/not-empty? (second %)))
(rx/map e/parse-library-color))
typographies-stream
(->> files-stream
(rx/flat-map vals)
(rx/merge-map vals)
(rx/map #(vector (:id %) (get-in % [:data :typographies])))
(rx/filter #(d/not-empty? (second %)))
(rx/map e/parse-library-typographies))
media-stream
(->> files-stream
(rx/flat-map vals)
(rx/merge-map vals)
(rx/map #(vector (:id %) (get-in % [:data :media])))
(rx/filter #(d/not-empty? (second %)))
(rx/flat-map parse-library-media))
(rx/merge-map parse-library-media))
components-stream
(->> files-stream
(rx/flat-map vals)
(rx/merge-map vals)
(rx/filter #(d/not-empty? (ctkl/components-seq (:data %))))
(rx/flat-map e/parse-library-components))
(rx/merge-map e/parse-library-components))
pages-stream
(->> render-stream
@@ -132,7 +132,7 @@
typographies-stream)
(rx/reduce conj [])
(rx/with-latest-from files-stream)
(rx/flat-map (fn [[data _]]
(rx/merge-map (fn [[data _]]
(->> (uz/compress-files data)
(rx/map #(vector file %)))))))))

View File

@@ -8,7 +8,7 @@
(:require
[app.common.uuid :as uuid]
[app.main.render :as r]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[promesa.core :as p]))
(defn render-page-export
@@ -22,7 +22,7 @@
(fn [resolve reject]
(->> (r/render-page data)
(rx/take 1)
(rx/subs resolve reject))) )))
(rx/subs! resolve reject))) )))
(defn exports []
#js {:renderPage render-page-export})

View File

@@ -28,10 +28,10 @@
[app.util.dom :as dom]
[app.util.i18n :as i18n]
[app.util.theme :as theme]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[debug]
[features]
[potok.core :as ptk]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(log/setup! {:app :info})

View File

@@ -9,8 +9,8 @@
(:require
[app.common.exceptions :as ex]
[app.common.transit :as t]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defrecord BroadcastMessage [id type data]
cljs.core/IDeref

View File

@@ -14,8 +14,8 @@
[app.common.uuid :as uuid]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.repo :as rp]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(def ^:private schema:comment-thread
(sm/define

View File

@@ -14,8 +14,8 @@
[app.main.features :as features]
[app.main.repo :as rp]
[app.util.i18n :refer [tr]]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SHARE LINK
@@ -106,9 +106,8 @@
(:typography-count summary))]
(modal/show
{:type :confirm
:message ""
:title (tr "modals.add-shared-confirm.message" (:name summary))
:hint (if (zero? count) (tr "modals.add-shared-confirm-empty.hint") (tr "modals.add-shared-confirm.hint"))
:message (if (zero? count) (tr "modals.add-shared-confirm-empty.hint") (tr "modals.add-shared-confirm.hint"))
:cancel-label (if (zero? count) (tr "labels.cancel") :omit)
:accept-label (tr "modals.add-shared-confirm.accept")
:accept-style :primary

View File

@@ -30,9 +30,9 @@
[app.util.time :as dt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[clojure.set :as set]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(log/set-level! :warn)
@@ -419,7 +419,7 @@
(rx/map di/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/cmd! :update-team-photo %))
(rx/do on-success)
(rx/tap on-success)
(rx/map du/fetch-teams)
(rx/catch on-error))))))
@@ -831,9 +831,15 @@
(ptk/reify ::set-file-thumbnail
ptk/UpdateEvent
(update [_ state]
(-> state
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)))))
(letfn [(update-search-files [files]
(->> files
(mapv #(cond-> %
(= file-id (:id %))
(assoc :thumbnail-uri thumbnail-uri)))))]
(-> state
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)
(d/update-when :dashboard-search-result update-search-files))))))
;; --- EVENT: create-file

View File

@@ -17,9 +17,10 @@
[app.util.object :as obj]
[app.util.storage :refer [storage]]
[app.util.time :as dt]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[beicon.v2.operators :as rxo]
[lambdaisland.uri :as u]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(l/set-level! :info)
@@ -238,7 +239,7 @@
profile (->> (rx/from-atom storage {:emit-current-value? true})
(rx/map :profile)
(rx/map :id)
(rx/dedupe))]
(rx/pipe (rxo/distinct-contiguous)))]
(l/debug :hint "event instrumentation initialized")
@@ -259,12 +260,12 @@
(l/debug :hint "events chunk persisted" :total (count chunk))))
(rx/map (constantly chunk))))))
(rx/take-until stoper)
(rx/subs (fn [chunk]
(swap! buffer remove-from-buffer (count chunk)))
(fn [cause]
(l/error :hint "unexpected error on audit persistence" :cause cause))
(fn []
(l/debug :hint "audit persistence terminated"))))
(rx/subs! (fn [chunk]
(swap! buffer remove-from-buffer (count chunk)))
(fn [cause]
(l/error :hint "unexpected error on audit persistence" :cause cause))
(fn []
(l/debug :hint "audit persistence terminated"))))
(->> stream
(rx/with-latest-from profile)
@@ -290,10 +291,10 @@
(rx/switch-map #(rx/timer (inst-ms session-timeout)))
(rx/take-until stoper)
(rx/subs (fn [_]
(l/debug :hint "session reinitialized")
(reset! session nil))
(fn [cause]
(l/error :hint "error on event batching stream" :cause cause))
(fn []
(l/debug :hitn "events batching stream terminated")))))))))
(rx/subs! (fn [_]
(l/debug :hint "session reinitialized")
(reset! session nil))
(fn [cause]
(l/error :hint "error on event batching stream" :cause cause))
(fn []
(l/debug :hitn "events batching stream terminated")))))))))

View File

@@ -15,8 +15,8 @@
[app.util.dom :as dom]
[app.util.time :as dt]
[app.util.websocket :as ws]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(def default-timeout 5000)

View File

@@ -19,9 +19,9 @@
[app.util.i18n :refer [tr]]
[app.util.storage :refer [storage]]
[app.util.webapi :as wa]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General purpose events & IMPL

View File

@@ -11,7 +11,7 @@
[app.main.data.messages :as dm]
[app.main.store :as st]
[app.util.i18n :refer [tr]]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]))

View File

@@ -9,8 +9,8 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.schema :as sm]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(declare hide)
(declare show)

View File

@@ -10,7 +10,7 @@
[app.common.uuid :as uuid]
[app.main.store :as st]
[cljs.core :as c]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(defonce components (atom {}))

View File

@@ -16,10 +16,10 @@
[app.main.refs :as refs]
[app.util.code-gen :as cg]
[app.util.timers :as ts]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[clojure.set :as set]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(def style-type "css")
(def markup-type "html")
@@ -80,7 +80,7 @@
(rx/merge-map fonts/fetch-font-css)
(rx/reduce conj [])
(rx/map #(str/join "\n" %))
(rx/subs
(rx/subs!
(fn [fontfaces-css]
(let [style-code
(dm/str

View File

@@ -13,7 +13,7 @@
[app.common.schema :as sm]
[app.config :as cf]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(log/set-level! :warn)

View File

@@ -21,8 +21,8 @@
[app.util.i18n :as i18n]
[app.util.router :as rt]
[app.util.storage :refer [storage]]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; --- SCHEMAS
@@ -434,7 +434,7 @@
(rx/map di/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/cmd! :update-profile-photo %))
(rx/do on-success)
(rx/tap on-success)
(rx/map (constantly (fetch-profile)))
(rx/catch on-error))))))

View File

@@ -21,8 +21,8 @@
[app.main.repo :as rp]
[app.util.globals :as ug]
[app.util.router :as rt]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; --- Local State Initialization

View File

@@ -11,8 +11,8 @@
[app.common.uri :as u]
[app.config :as cf]
[app.util.websocket :as ws]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(l/set-level! :error)
@@ -44,7 +44,7 @@
(ptk/reify ::initialize
ptk/WatchEvent
(watch [_ state stream]
(l/trace :hint "event:initialize" :fn "watch")
(l/trace :hint "initialize" :fn "watch")
(let [sid (:session-id state)
uri (prepare-uri {:session-id sid})
ws (ws/create uri)]

View File

@@ -78,10 +78,10 @@
[app.util.router :as rt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(def default-workspace-local {:zoom 1})
@@ -444,7 +444,7 @@
uris (into #{} xform (wsh/lookup-page-objects state page-id))]
(->> (rx/from uris)
(rx/subs #(http/fetch-data-uri % false)))))))
(rx/subs! #(http/fetch-data-uri % false)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Workspace Page CRUD
@@ -872,10 +872,10 @@
(pcb/update-shapes [parent-id] #(ctl/add-children-to-index % ids objects to-index)))
(pcb/update-shapes parents
(fn [parent]
(fn [parent objects]
(cond-> parent
(ctl/grid-layout? parent)
(ctl/assign-cells))))
(ctl/assign-cells objects))))
(pcb/reorder-grid-children parents)

View File

@@ -18,9 +18,9 @@
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(defn selected-shapes-idx
[state]

View File

@@ -20,8 +20,8 @@
[app.main.data.workspace.undo :as dwu]
[app.main.store :as st]
[app.main.worker :as uw]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module
(log/set-level! :warn)
@@ -70,7 +70,7 @@
update-layout-ids
(->> ids
(map (d/getf objects))
(filter #(some update-layout-attr? (pcb/changed-attrs % update-fn {:attrs attrs})))
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs})))
(map :id))
changes (reduce

View File

@@ -8,7 +8,7 @@
(:require
[app.common.files.helpers :as cfh]
[app.common.uuid :as uuid]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
;; --- Shape attrs (Layers Sidebar)

View File

@@ -23,8 +23,8 @@
[app.main.data.workspace.undo :as dwu]
[app.util.color :as uc]
[app.util.storage :refer [storage]]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; A set of keys that are used for shared state identifiers
(def ^:const colorpicker-selected-broadcast-key ::colorpicker-selected)
@@ -353,7 +353,7 @@
;; Stream that updates the stroke/width and stops if `esc` pressed
(->> sub
(rx/take-until stop?)
(rx/flat-map update-events))
(rx/merge-map update-events))
;; Hide the modal if the stop event is emitted
(->> stop?

View File

@@ -22,8 +22,8 @@
[app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.router :as rt]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(declare handle-interrupt)
(declare handle-comment-layer-click)

View File

@@ -13,8 +13,8 @@
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.util.router :as rt]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module
(log/set-level! :warn)

View File

@@ -14,8 +14,8 @@
[app.main.data.workspace.drawing.common :as common]
[app.main.data.workspace.drawing.curve :as curve]
[app.main.data.workspace.path :as path]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(declare start-drawing)
(declare handle-drawing)

View File

@@ -24,9 +24,10 @@
[app.main.data.workspace.state-helpers :as wsh]
[app.main.snap :as snap]
[app.main.streams :as ms]
[app.util.array :as array]
[app.util.mouse :as mse]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn adjust-ratio
[point initial]
@@ -128,12 +129,11 @@
(->> ms/mouse-position
(rx/filter #(> (gpt/distance % initial) (/ 2 zoom)))
(rx/with-latest vector ms/mouse-position-shift)
(rx/with-latest conj ms/mouse-position-mod)
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-mod)
(rx/switch-map
(fn [[point :as current]]
(->> (snap/closest-snap-point page-id [shape] objects layout zoom focus point)
(rx/map #(conj current %)))))
(rx/map (partial array/conj current)))))
(rx/map
(fn [[_ shift? mod? point]]
#(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step snap-prec)) shift? mod?)))))

View File

@@ -16,8 +16,8 @@
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.worker :as uw]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn clear-drawing
[]

View File

@@ -23,8 +23,8 @@
[app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.path.simplify-curve :as ups]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(def simplify-tolerance 0.3)

View File

@@ -10,8 +10,8 @@
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn interrupt? [e] (= e :interrupt))

View File

@@ -10,8 +10,8 @@
[app.common.geom.shapes :as gsh]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; This event will update the file so the boolean data has a pre-generated path data
;; to increase performance.

View File

@@ -7,8 +7,8 @@
(ns app.main.data.workspace.fix-broken-shapes
(:require
[app.main.data.workspace.changes :as dch]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn- generate-broken-link-changes
[attr {:keys [objects id] :as container}]

View File

@@ -12,8 +12,8 @@
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.fonts :as fonts]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; This event will update the file so the texts with non existing custom fonts try to be fixed.
;; This can happen when:

View File

@@ -12,8 +12,8 @@
[app.common.files.changes-builder :as pcb]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Grid

View File

@@ -9,7 +9,7 @@
[app.common.geom.rect :as grc]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.state-helpers :as wsh]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(defn hover-grid-cell
[grid-id cell-id add-to-set]

View File

@@ -10,8 +10,8 @@
[app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwc]
[app.main.store :as st]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts

View File

@@ -19,8 +19,8 @@
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn shapes-for-grouping
[objects selected]

View File

@@ -13,8 +13,8 @@
[app.common.types.page :as ctp]
[app.main.data.workspace.changes :as dwc]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn make-update-guide [guide]
(fn [other]

View File

@@ -8,7 +8,7 @@
(:require
[app.common.data.macros :as dm]
[clojure.set :as set]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
;; --- Manage shape's highlight status

View File

@@ -20,8 +20,8 @@
[app.main.data.workspace.undo :as dwu]
[app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; --- Flows

View File

@@ -11,9 +11,9 @@
[app.common.math :as mth]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
;; -- Opacity ----------------------------------------------------------

View File

@@ -10,7 +10,7 @@
[app.common.data.macros :as dm]
[app.util.storage :refer [storage]]
[clojure.set :as set]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(def valid-flags
#{:sitemap

View File

@@ -45,9 +45,9 @@
[app.util.i18n :refer [tr]]
[app.util.router :as rt]
[app.util.time :as dt]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
(log/set-level! :warn)
@@ -754,6 +754,18 @@
(dch/commit-changes (assoc nonlocal-changes
:file-id file-id)))))))))))
(defn- update-component-thumbnail-sync
[state component-id file-id tag]
(let [current-file-id (:current-file-id state)
current-file? (= current-file-id file-id)
data (if current-file?
(get state :workspace-data)
(get-in state [:workspace-libraries file-id :data]))
component (ctkl/get-component data component-id)
page-id (:main-instance-page component)
root-id (:main-instance-id component)]
(dwt/request-thumbnail file-id page-id root-id tag)))
(defn update-component-sync
([shape-id file-id] (update-component-sync shape-id file-id nil))
([shape-id file-id undo-group]
@@ -761,14 +773,18 @@
ptk/WatchEvent
(watch [_ state _]
(let [current-file-id (:current-file-id state)
current-file? (= current-file-id file-id)
page (wsh/lookup-page state)
shape (ctn/get-shape page shape-id)
component-id (:component-id shape)
undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(update-component shape-id undo-group)
(sync-file current-file-id file-id :components (:component-id shape) undo-group)
(when (not= current-file-id file-id)
(update-component-thumbnail-sync state component-id file-id "frame")
(update-component-thumbnail-sync state component-id file-id "component")
(when (not current-file?)
(sync-file file-id file-id :components (:component-id shape) undo-group))
(dwu/commit-undo-transaction undo-id)))))))
@@ -795,7 +811,8 @@
(ptk/reify ::update-component-thumbnail
ptk/WatchEvent
(watch [_ state _]
(let [data (get state :workspace-data)
(rx/of (update-component-thumbnail-sync state component-id file-id "component"))
#_(let [data (get state :workspace-data)
component (ctkl/get-component data component-id)
page-id (:main-instance-page component)
root-id (:main-instance-id component)]

View File

@@ -182,10 +182,10 @@
(-> changes
(pcb/update-shapes
[(:parent-id first-shape)]
(fn [shape]
(fn [shape objects]
(-> shape
(ctl/push-into-cell [(:id first-shape)] row column)
(ctl/assign-cells))))
(ctl/assign-cells objects))))
(pcb/reorder-grid-children [(:parent-id first-shape)])))
changes)
@@ -603,6 +603,23 @@
(generate-detach-instance changes container shape-id))))
changes)))
(defn- find-main-container
"Find the container that has the main shape."
[container-inst shape-inst shape-main library component]
(loop [shape-inst' shape-inst
component' component]
(let [container (ctf/get-component-container library component')] ; TODO: this won't work if some intermediate component is in a different library
(if (some? (ctn/get-shape container (:id shape-main))) ; for this to work we need to have access to the libraries list here
container
(let [parent (ctn/get-shape container-inst (:parent-id shape-inst'))
shape-inst' (ctn/get-head-shape (:objects container-inst) parent)
component' (or (ctkl/get-component library (:component-id shape-inst'))
(ctkl/get-deleted-component library (:component-id shape-inst')))]
(if (some? component)
(recur shape-inst'
component')
nil))))))
(defn- generate-sync-shape-direct-recursive
[changes container shape-inst component library shape-main root-inst root-main reset? initial-root? redirect-shaperef components-v2]
(log/debug :msg "Sync shape direct recursive"
@@ -639,7 +656,7 @@
set-remote-synced?
(change-remote-synced shape-inst container true))
component-container (ctf/get-component-container library component)
component-container (find-main-container container shape-inst shape-main library component)
children-inst (vec (ctn/get-direct-children container shape-inst))
children-main (vec (ctn/get-direct-children component-container shape-main))
@@ -948,11 +965,11 @@
(:id parent-shape)
(:frame-id parent-shape)))
(nil? (:shape-ref original-shape))
(assoc :shape-ref (:id original-shape))
set-remote-synced?
(assoc :remote-synced true))))
(assoc :remote-synced true)
:always
(assoc :shape-ref (:id original-shape)))))
update-original-shape (fn [original-shape _new-shape]
original-shape)

View File

@@ -30,9 +30,9 @@
[app.main.store :as st]
[app.util.http :as http]
[app.util.i18n :refer [tr]]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]
[potok.v2.core :as ptk]
[promesa.core :as p]
[tubax.core :as tubax]))
@@ -122,13 +122,13 @@
(->> (rx/from uris)
(rx/filter (comp not svg-url?))
(rx/mapcat upload)
(rx/do on-image))
(rx/tap on-image))
(->> (rx/from uris)
(rx/filter svg-url?)
(rx/merge-map (partial fetch-svg name))
(rx/merge-map svg->clj)
(rx/do on-svg)))))
(rx/tap on-svg)))))
(defn- process-blobs
[{:keys [file-id local? name blobs force-media on-image on-svg]}]
@@ -154,14 +154,14 @@
(rx/filter (comp not svg-blob?))
(rx/map prepare-blob)
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
(rx/do on-image))
(rx/tap on-image))
(->> (rx/from blobs)
(rx/map dmm/validate-file)
(rx/filter svg-blob?)
(rx/merge-map extract-content)
(rx/merge-map svg->clj)
(rx/do on-svg)))))
(rx/tap on-svg)))))
(defn handle-media-error [error on-error]
(if (ex/ex-info? error)
@@ -278,7 +278,7 @@
(rx/map dmm/validate-file)
(rx/map prepare)
(rx/mapcat #(rp/cmd! :upload-file-media-object %))
(rx/do on-upload-success)
(rx/tap on-upload-success)
(rx/catch handle-media-error))))))
;; --- Upload File Media objects
@@ -423,7 +423,7 @@
:timeout nil
:tag :media-loading}))
(->> (rp/cmd! :clone-file-media-object params)
(rx/do on-success)
(rx/tap on-success)
(rx/catch on-error)
(rx/finalize #(st/emit! (msg/hide-tag :media-loading)))))))))

View File

@@ -27,8 +27,8 @@
[app.main.data.workspace.guides :as-alias dwg]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
;; -- temporary modifiers -------------------------------------------
@@ -191,14 +191,14 @@
;; Temporary remove the children when moving them
frame (-> frame
(update :shapes #(d/removev ids %))
(ctl/assign-cells))
(ctl/assign-cells objects))
ids (->> ids (remove #(ctl/position-absolute? objects %)))
frame (-> frame
(update :shapes d/concat-vec ids)
(cond-> (some? cell)
(ctl/push-into-cell ids row column))
(ctl/assign-cells))]
(ctl/assign-cells objects))]
(-> modifiers
(ctm/change-property :layout-grid-rows (:layout-grid-rows frame))

View File

@@ -19,10 +19,11 @@
[app.util.globals :refer [global]]
[app.util.mouse :as mse]
[app.util.object :as obj]
[app.util.rxops :as rxs]
[app.util.time :as dt]
[beicon.core :as rx]
[beicon.v2.core :as rx]
[clojure.set :as set]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(declare process-message)
(declare handle-presence)
@@ -82,7 +83,7 @@
;; position changes.
(->> stream
(rx/filter mse/pointer-event?)
(rx/sample 50)
(rx/pipe (rxs/throttle 100))
(rx/map #(handle-pointer-send file-id (:pt %)))))
(rx/take-until stoper))]
@@ -132,7 +133,7 @@
"#faa6b7" ; salmon
"#f9b489" ; orange
"#fdcd79" ; soft-orange
"#dee563" ; yellow
"#dee563" ; yellow -> default presence color
"#b1e96f" ; yellow-green
})

View File

@@ -13,8 +13,8 @@
[app.main.data.workspace.path.helpers :as helpers]
[app.main.data.workspace.path.state :as st]
[app.main.data.workspace.state-helpers :as wsh]
[beicon.core :as rx]
[potok.core :as ptk]))
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
(defn generate-path-changes
"Generates changes to update the new content of the shape"

View File

@@ -8,7 +8,7 @@
(:require
[app.common.schema :as sm]
[app.main.data.workspace.path.state :as st]
[potok.core :as ptk]))
[potok.v2.core :as ptk]))
(def valid-commands
#{:move-to

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