mirror of
https://github.com/penpot/penpot.git
synced 2026-01-07 22:09:05 -05:00
Compare commits
430 Commits
add-numeri
...
2.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44bc4b7fa4 | ||
|
|
5c14f486d7 | ||
|
|
0cbd980b68 | ||
|
|
2e726b62c3 | ||
|
|
b68c426cd1 | ||
|
|
d00de7d5a4 | ||
|
|
2fbd4b07e0 | ||
|
|
523373dfa2 | ||
|
|
b71ec4bfe0 | ||
|
|
51107c3fc9 | ||
|
|
abef9f3cf7 | ||
|
|
40c300fa1a | ||
|
|
5b704faf79 | ||
|
|
d24eab7241 | ||
|
|
c8fef97598 | ||
|
|
44e3e4a641 | ||
|
|
f3616c68a0 | ||
|
|
9ea3f81bc4 | ||
|
|
accd5226d7 | ||
|
|
16a1fd14e5 | ||
|
|
824bb19c7e | ||
|
|
d0f3e0f0b0 | ||
|
|
43ba2b05e8 | ||
|
|
d5ccb704b2 | ||
|
|
6d21fcc9de | ||
|
|
77741b49a7 | ||
|
|
a7e0cfc609 | ||
|
|
50a6355537 | ||
|
|
264aef277d | ||
|
|
78d0e6d059 | ||
|
|
6d41d36b3a | ||
|
|
bb97df373e | ||
|
|
a41af032cd | ||
|
|
86ee4f55c5 | ||
|
|
63cd3ae025 | ||
|
|
cafb7abb53 | ||
|
|
e5b6c4a9e0 | ||
|
|
1d5bad5523 | ||
|
|
96d6868b45 | ||
|
|
b739d8bd0c | ||
|
|
dd803dc1de | ||
|
|
b627c10737 | ||
|
|
95f4a9bd29 | ||
|
|
a2b8f19ff3 | ||
|
|
898182e3d5 | ||
|
|
e03c822b51 | ||
|
|
a3aabf3b7d | ||
|
|
5c4fd97541 | ||
|
|
3010abbf64 | ||
|
|
e6a7eed7a9 | ||
|
|
daf3b5caa8 | ||
|
|
34d65ed1c8 | ||
|
|
27c624ae0f | ||
|
|
3831b3034e | ||
|
|
00390a1349 | ||
|
|
17bfed137c | ||
|
|
77ef26b207 | ||
|
|
26239a15f2 | ||
|
|
207974fe6c | ||
|
|
b52e2fa681 | ||
|
|
bf719b587f | ||
|
|
61109c91e3 | ||
|
|
4915a97c2c | ||
|
|
903aba5642 | ||
|
|
82583f5079 | ||
|
|
4561392791 | ||
|
|
8e8b2acddd | ||
|
|
93cbd99932 | ||
|
|
15c91a5de5 | ||
|
|
7f2e819789 | ||
|
|
4947bf480b | ||
|
|
c51ae35fc5 | ||
|
|
81564dbfa9 | ||
|
|
56472a95de | ||
|
|
9e5bc3675c | ||
|
|
787c066357 | ||
|
|
82bedda604 | ||
|
|
1b67be2f36 | ||
|
|
1a8a9df2b7 | ||
|
|
e1ce7ec787 | ||
|
|
546b7d5f60 | ||
|
|
fb6121bf92 | ||
|
|
8cdcfb70e2 | ||
|
|
8c2dc1f22d | ||
|
|
1b041d949c | ||
|
|
1a8f36562b | ||
|
|
3d0c3013e5 | ||
|
|
3274a74611 | ||
|
|
3a0ac577f8 | ||
|
|
cf78e68787 | ||
|
|
43ba30c694 | ||
|
|
520ea0e094 | ||
|
|
0c260c626b | ||
|
|
db4721f692 | ||
|
|
f8d63f5d9d | ||
|
|
0f46efc117 | ||
|
|
909838c8c4 | ||
|
|
86e36061fb | ||
|
|
fbdabcd913 | ||
|
|
191ea3d02a | ||
|
|
b747ccc382 | ||
|
|
892c9ab12c | ||
|
|
b595d5abf8 | ||
|
|
cb46d643ac | ||
|
|
03a82c18cb | ||
|
|
a559547f97 | ||
|
|
b91d703060 | ||
|
|
7bde3d0ec1 | ||
|
|
54643b79f6 | ||
|
|
f0b82864dd | ||
|
|
29244776f0 | ||
|
|
40c33c7dcc | ||
|
|
1a6fcb5daf | ||
|
|
34febfc833 | ||
|
|
2c0abea254 | ||
|
|
4524782282 | ||
|
|
2a5b087aa4 | ||
|
|
1f034654a0 | ||
|
|
1576016999 | ||
|
|
e3cce104e1 | ||
|
|
a24631ac66 | ||
|
|
fd81ea6a84 | ||
|
|
a3c7151157 | ||
|
|
2d4fc3e05f | ||
|
|
b01dea20d6 | ||
|
|
3f40a830fd | ||
|
|
50e9816526 | ||
|
|
4c0165da62 | ||
|
|
42d36bae0a | ||
|
|
6dd0f4f164 | ||
|
|
f7c4bd77be | ||
|
|
5d72954611 | ||
|
|
9930f54558 | ||
|
|
667c9ddbb9 | ||
|
|
4f09005586 | ||
|
|
cbc98a761f | ||
|
|
c3b306201d | ||
|
|
a772b442c8 | ||
|
|
332bbc71c3 | ||
|
|
c5b0206bf0 | ||
|
|
22ca1ab5f9 | ||
|
|
acf0d02c5d | ||
|
|
5ccf34fdae | ||
|
|
41c8bba1df | ||
|
|
325a78a967 | ||
|
|
409ff31c30 | ||
|
|
8cb42a63e5 | ||
|
|
2af1feafb6 | ||
|
|
691a67b595 | ||
|
|
f7e94accc3 | ||
|
|
fc655224af | ||
|
|
8b0ead6832 | ||
|
|
10ae4dd3f7 | ||
|
|
34d6e86e42 | ||
|
|
481d1ec53a | ||
|
|
c6f4ee1974 | ||
|
|
f20032199a | ||
|
|
d0aac65c76 | ||
|
|
f21e546bc1 | ||
|
|
874a658369 | ||
|
|
c254ebd7c3 | ||
|
|
267a3af1e5 | ||
|
|
b8ee7cad26 | ||
|
|
26efc9f0c8 | ||
|
|
1d593e1287 | ||
|
|
b34c161fc3 | ||
|
|
ed0c84a069 | ||
|
|
29466b47fe | ||
|
|
b385f055e0 | ||
|
|
5faa619bc4 | ||
|
|
74430aad21 | ||
|
|
01dda6dd6b | ||
|
|
b2bc481672 | ||
|
|
8c337f508b | ||
|
|
f9f45dc612 | ||
|
|
93542854c1 | ||
|
|
858f94c6d2 | ||
|
|
f932d663b0 | ||
|
|
63631e60cd | ||
|
|
6018cd67c5 | ||
|
|
4856d0e957 | ||
|
|
995ca4d7d9 | ||
|
|
d5d254a7f3 | ||
|
|
69f45dc811 | ||
|
|
7534ad283c | ||
|
|
3d6695171d | ||
|
|
8fa28ce176 | ||
|
|
f03a0de665 | ||
|
|
555af2fa52 | ||
|
|
0683b20b25 | ||
|
|
163ae639ff | ||
|
|
354ba91aa6 | ||
|
|
38d9a9d2d5 | ||
|
|
beb3d16693 | ||
|
|
5770c0cb02 | ||
|
|
46ce9500fc | ||
|
|
d008ea9edd | ||
|
|
eba8d02c18 | ||
|
|
bb9daf7c03 | ||
|
|
f1232fc461 | ||
|
|
9584e1b02d | ||
|
|
96ccac5085 | ||
|
|
2540d58096 | ||
|
|
de2695682d | ||
|
|
e464d8cf9c | ||
|
|
a8433bcef3 | ||
|
|
644dd9ff44 | ||
|
|
27d2724153 | ||
|
|
c647d122d8 | ||
|
|
e4a65f3a04 | ||
|
|
389f1d6502 | ||
|
|
b91a670198 | ||
|
|
5650629b73 | ||
|
|
a4310b4213 | ||
|
|
a8d4b293dc | ||
|
|
fec7d5cff2 | ||
|
|
5e254ff3f6 | ||
|
|
8c20159fb0 | ||
|
|
79e1c29306 | ||
|
|
fcd3e5c34c | ||
|
|
8b9f15f414 | ||
|
|
b2aaa5f0df | ||
|
|
8922e7454f | ||
|
|
4f7d97a31e | ||
|
|
e32af5e71e | ||
|
|
6cb5b812da | ||
|
|
ca24e23775 | ||
|
|
28ed62fb2c | ||
|
|
9c4b60e95f | ||
|
|
6719902647 | ||
|
|
cf8307af8f | ||
|
|
8c54cb764f | ||
|
|
4a9fef12eb | ||
|
|
ea4e69f381 | ||
|
|
5fed12d807 | ||
|
|
a2f41a7a40 | ||
|
|
f4fd9fa13d | ||
|
|
8a0aa20789 | ||
|
|
1b3a200010 | ||
|
|
70263ba901 | ||
|
|
5c8f6dd498 | ||
|
|
a2abaea637 | ||
|
|
55997a3d4a | ||
|
|
a30ab17605 | ||
|
|
ab0219876e | ||
|
|
19961f440a | ||
|
|
db84eb365b | ||
|
|
b7f97dbeea | ||
|
|
4cbaef1451 | ||
|
|
e1adbdfd9f | ||
|
|
687e1e7b0b | ||
|
|
055ee27be0 | ||
|
|
47af278f5e | ||
|
|
29ad99d685 | ||
|
|
3a8b312f6d | ||
|
|
8f55269522 | ||
|
|
f86ce38f04 | ||
|
|
b97a3f9783 | ||
|
|
91807151ba | ||
|
|
5c225a51ce | ||
|
|
3d61924162 | ||
|
|
580013bc3f | ||
|
|
9ec5467e2a | ||
|
|
b113736321 | ||
|
|
22db773b2e | ||
|
|
91636ffc41 | ||
|
|
7160334cb9 | ||
|
|
d08d2f49ac | ||
|
|
8f774a3611 | ||
|
|
d9d2cc7b4e | ||
|
|
2e0fd6ec1b | ||
|
|
d2d39aad30 | ||
|
|
d6e0001ac4 | ||
|
|
793c01a0a1 | ||
|
|
bbac5d050e | ||
|
|
5a7d9e3f18 | ||
|
|
1a921c2750 | ||
|
|
39f145b8b1 | ||
|
|
6611769dc9 | ||
|
|
b3d230ba16 | ||
|
|
ddae0026fe | ||
|
|
9fc3f4858a | ||
|
|
1120c7c7fb | ||
|
|
e9bd44b819 | ||
|
|
2244bf6aa7 | ||
|
|
f4ef4a705c | ||
|
|
c40de5fb87 | ||
|
|
9733c41ae4 | ||
|
|
fe8d9fdd76 | ||
|
|
401fa823a0 | ||
|
|
3da3281a56 | ||
|
|
3131eec271 | ||
|
|
1909189ce0 | ||
|
|
0ec0917b6d | ||
|
|
0e4c535edc | ||
|
|
46f330fef3 | ||
|
|
f067c86b02 | ||
|
|
2b6a91819b | ||
|
|
1f652fe364 | ||
|
|
4c10aeefe6 | ||
|
|
e70da78a77 | ||
|
|
27ab910a64 | ||
|
|
c1fa6be7c4 | ||
|
|
2398c1fc2b | ||
|
|
13859f90b9 | ||
|
|
e2724d180b | ||
|
|
c6bccafd98 | ||
|
|
1357ab34eb | ||
|
|
6e9ee3d310 | ||
|
|
5816695246 | ||
|
|
0d9160506b | ||
|
|
c3c6628bf1 | ||
|
|
8642ffba46 | ||
|
|
25372c3edf | ||
|
|
e13d1743da | ||
|
|
02d1a1f0b1 | ||
|
|
08aeb93710 | ||
|
|
04f0f77cd8 | ||
|
|
15adf1bd06 | ||
|
|
1080ffc6b8 | ||
|
|
1450672341 | ||
|
|
483e88d6a3 | ||
|
|
9fee16f4a9 | ||
|
|
89a09346a5 | ||
|
|
77fa235965 | ||
|
|
03e4ca12be | ||
|
|
229c9b8385 | ||
|
|
a4fab5c5bd | ||
|
|
d8913ab18b | ||
|
|
7ef2f4e67e | ||
|
|
1d065e68f4 | ||
|
|
c9ceceb7e9 | ||
|
|
ad26efaa5d | ||
|
|
a3e17047a4 | ||
|
|
0552ef55cf | ||
|
|
d4c6063378 | ||
|
|
f23e460b2a | ||
|
|
35b29bb203 | ||
|
|
cd02905d1f | ||
|
|
87d917bc2e | ||
|
|
e8d1ea24d1 | ||
|
|
ad842872fb | ||
|
|
90744c182e | ||
|
|
78aaf28532 | ||
|
|
4e2f905a26 | ||
|
|
d2cd99ed44 | ||
|
|
885231e9a1 | ||
|
|
baabfe2de8 | ||
|
|
facb0227a0 | ||
|
|
f6fe41af96 | ||
|
|
f225fce9a1 | ||
|
|
3570d29575 | ||
|
|
f8489a521f | ||
|
|
cc76a42088 | ||
|
|
50cc70201d | ||
|
|
e88b3bae5a | ||
|
|
2b2939b4b7 | ||
|
|
6b25720155 | ||
|
|
96d099b71e | ||
|
|
fab9e842e8 | ||
|
|
ee022e225c | ||
|
|
1b3fcb0432 | ||
|
|
37f88067b9 | ||
|
|
2650eccd09 | ||
|
|
969b171510 | ||
|
|
4b22a0ebfb | ||
|
|
eafea7aec9 | ||
|
|
ce23fee292 | ||
|
|
f3d734357a | ||
|
|
d31f64796f | ||
|
|
3a27a5a542 | ||
|
|
2a04f78337 | ||
|
|
aeee05c90d | ||
|
|
6fc63f14a0 | ||
|
|
f33c1fb530 | ||
|
|
75170bb043 | ||
|
|
c0a98288d0 | ||
|
|
7d5739b663 | ||
|
|
fe60016124 | ||
|
|
5c58a04fc2 | ||
|
|
04a1f8475d | ||
|
|
3c05f09fd1 | ||
|
|
5eaea63ca8 | ||
|
|
bcfa9a82ea | ||
|
|
d9649eaedd | ||
|
|
170d35dde2 | ||
|
|
2943f80db5 | ||
|
|
46b0e4f0e7 | ||
|
|
878952f7b5 | ||
|
|
f84ffc3562 | ||
|
|
e9edebbbb5 | ||
|
|
14afd58eac | ||
|
|
827d39a406 | ||
|
|
e4a1c373bb | ||
|
|
be13704934 | ||
|
|
88e77e3218 | ||
|
|
443cabe94e | ||
|
|
c7c8e91183 | ||
|
|
327db5a1a3 | ||
|
|
bcb74822d2 | ||
|
|
790d422100 | ||
|
|
419a949816 | ||
|
|
1ad6ee6e38 | ||
|
|
da10425800 | ||
|
|
3e4c80fa27 | ||
|
|
179a5654e7 | ||
|
|
bc38bd6a9c | ||
|
|
1c5d182a90 | ||
|
|
a85a42d367 | ||
|
|
1a705cee24 | ||
|
|
a771ca91ab | ||
|
|
4326e2c5a4 | ||
|
|
050ffa235c | ||
|
|
fdd6502671 | ||
|
|
e698fd7d35 | ||
|
|
ac0b74e11a | ||
|
|
24d4871b23 | ||
|
|
b5d96d312a | ||
|
|
f88420efb5 | ||
|
|
c301d95f20 | ||
|
|
e74cf1836f | ||
|
|
dfdc1ac35b | ||
|
|
97a5a93694 | ||
|
|
dd0b8f8f6e | ||
|
|
a706907b26 | ||
|
|
a3b4fc9545 | ||
|
|
ddfd55261d | ||
|
|
5b8d1c1ca6 | ||
|
|
24e2948407 | ||
|
|
c569c71306 |
@@ -1,5 +1,53 @@
|
||||
version: 2.1
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: "fmt check"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
|
||||
- run:
|
||||
name: "lint clj common"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:common
|
||||
|
||||
- run:
|
||||
name: "lint clj frontend"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:frontend
|
||||
|
||||
- run:
|
||||
name: "lint clj backend"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:backend
|
||||
|
||||
- run:
|
||||
name: "lint clj exporter"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:exporter
|
||||
|
||||
- run:
|
||||
name: "lint clj library"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:library
|
||||
|
||||
test-common:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
@@ -17,15 +65,7 @@ jobs:
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "common/deps.edn"}}
|
||||
|
||||
- run:
|
||||
name: "fmt check & linter"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
yarn run lint:clj
|
||||
- v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: "JVM tests"
|
||||
@@ -37,12 +77,16 @@ jobs:
|
||||
name: "NODE tests"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run test
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: v1-dependencies-{{ checksum "common/deps.edn"}}
|
||||
- ~/.yarn
|
||||
- ~/.gitlibs
|
||||
- ~/.cache/ms-playwright
|
||||
key: v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
|
||||
|
||||
test-frontend:
|
||||
docker:
|
||||
@@ -61,36 +105,68 @@ jobs:
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: "prepopulate linter cache"
|
||||
working_directory: "./common"
|
||||
name: "install dependencies"
|
||||
working_directory: "./frontend"
|
||||
# We install playwright here because the dependent tasks
|
||||
# uses the same cache as this task so we prepopulate it
|
||||
command: |
|
||||
yarn install
|
||||
yarn run lint:clj
|
||||
yarn run playwright install chromium
|
||||
|
||||
- run:
|
||||
name: "fmt check & linter"
|
||||
name: "lint scss on frontend"
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
yarn run fmt:js:check
|
||||
yarn run lint:scss
|
||||
yarn run lint:clj
|
||||
|
||||
- run:
|
||||
name: "unit tests"
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run test
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}
|
||||
- ~/.yarn
|
||||
- ~/.gitlibs
|
||||
- ~/.cache/ms-playwright
|
||||
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
test-library:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx6g
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: Install dependencies and build
|
||||
working_directory: "./library"
|
||||
command: |
|
||||
yarn install
|
||||
|
||||
- run:
|
||||
name: Build and Test
|
||||
working_directory: "./library"
|
||||
command: |
|
||||
./scripts/build
|
||||
yarn run test
|
||||
|
||||
test-components:
|
||||
docker:
|
||||
@@ -109,14 +185,14 @@ jobs:
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
yarn
|
||||
npx playwright install --with-deps
|
||||
yarn install
|
||||
yarn run playwright install chromium
|
||||
|
||||
- run:
|
||||
name: Build Storybook
|
||||
@@ -148,7 +224,7 @@ jobs:
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: "integration tests"
|
||||
@@ -158,7 +234,7 @@ jobs:
|
||||
yarn run build:app:assets
|
||||
yarn run build:app
|
||||
yarn run build:app:libs
|
||||
yarn run playwright install --with-deps chromium
|
||||
yarn run playwright install chromium
|
||||
yarn run test:e2e -x --workers=4
|
||||
|
||||
test-backend:
|
||||
@@ -185,21 +261,6 @@ jobs:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "backend/deps.edn" }}
|
||||
|
||||
- run:
|
||||
name: "prepopulate linter cache"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run lint:clj
|
||||
|
||||
- run:
|
||||
name: "fmt check & linter"
|
||||
working_directory: "./backend"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
yarn run lint:clj
|
||||
|
||||
- run:
|
||||
name: "tests"
|
||||
working_directory: "./backend"
|
||||
@@ -215,37 +276,9 @@ jobs:
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
- ~/.gitlibs
|
||||
key: v1-dependencies-{{ checksum "backend/deps.edn" }}
|
||||
|
||||
test-exporter:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: "prepopulate linter cache"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run lint:clj
|
||||
|
||||
- run:
|
||||
name: "fmt check & linter"
|
||||
working_directory: "./exporter"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
yarn run lint:clj
|
||||
|
||||
test-render-wasm:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
@@ -278,10 +311,32 @@ jobs:
|
||||
workflows:
|
||||
penpot:
|
||||
jobs:
|
||||
- test-frontend
|
||||
- test-components
|
||||
- test-integration
|
||||
- test-backend
|
||||
- test-common
|
||||
- test-exporter
|
||||
- lint
|
||||
- test-frontend:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-library:
|
||||
requires:
|
||||
- test-frontend: success
|
||||
- lint: success
|
||||
|
||||
- test-components:
|
||||
requires:
|
||||
- test-frontend: success
|
||||
- lint: success
|
||||
|
||||
- test-integration:
|
||||
requires:
|
||||
- test-frontend: success
|
||||
- lint: success
|
||||
|
||||
- test-backend:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-common:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-render-wasm
|
||||
|
||||
129
.github/workflows/build-bundles.yml
vendored
Normal file
129
.github/workflows/build-bundles.yml
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
name: Build and Upload Penpot Bundles non-prod
|
||||
|
||||
on:
|
||||
# Create bundler for every tag
|
||||
push:
|
||||
tags:
|
||||
- '**' # Pattern matched against refs/tags
|
||||
# Create bundler every hour between 5:00 and 20:00 on working days
|
||||
schedule:
|
||||
- cron: '0 5-20 * * 1-5'
|
||||
# Create bundler from manual action
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
zip_mode:
|
||||
# zip_mode defines how the build artifacts are packaged:
|
||||
# - 'individual': creates one ZIP file per component (frontend, backend, exporter)
|
||||
# - 'all': creates a single ZIP containing all components
|
||||
# - null: for the rest of cases (non-manual events)
|
||||
description: 'Bundle packaging mode'
|
||||
required: false
|
||||
default: 'individual'
|
||||
type: choice
|
||||
options:
|
||||
- individual
|
||||
- all
|
||||
|
||||
jobs:
|
||||
build-bundles:
|
||||
name: Build and Upload Penpot Bundles
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Extract somer useful variables
|
||||
id: vars
|
||||
run: |
|
||||
echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||
echo "gh_branch=${{ github.base_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||
|
||||
# Set up Docker Buildx for multi-arch build
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Run manage.sh build-bundle from host
|
||||
run: ./manage.sh build-bundle
|
||||
|
||||
- name: Prepare directories for zipping
|
||||
run: |
|
||||
mkdir zips
|
||||
mv bundles penpot
|
||||
|
||||
- name: Create zip bundles for zip_mode == 'all'
|
||||
if: ${{ github.event.inputs.zip_mode == 'all' }}
|
||||
run: |
|
||||
echo "📦 Packaging Penpot 'all' bundles..."
|
||||
zip -r zips/penpot-all-bundles.zip penpot
|
||||
|
||||
- name: Create zip bundles for zip_mode != 'all'
|
||||
if: ${{ github.event.inputs.zip_mode != 'all' }}
|
||||
run: |
|
||||
echo "📦 Packaging Penpot 'individual' bundles..."
|
||||
zip -r zips/penpot-frontend.zip penpot/frontend
|
||||
zip -r zips/penpot-backend.zip penpot/backend
|
||||
zip -r zips/penpot-exporter.zip penpot/exporter
|
||||
|
||||
- name: Upload unified 'all' bundle
|
||||
if: ${{ github.event.inputs.zip_mode == 'all' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: penpot-all-bundles
|
||||
path: zips/penpot-all-bundles.zip
|
||||
|
||||
- name: Upload individual bundles
|
||||
if: ${{ github.event.inputs.zip_mode != 'all' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: penpot-individual-bundles
|
||||
path: |
|
||||
zips/penpot-frontend.zip
|
||||
zips/penpot-backend.zip
|
||||
zips/penpot-exporter.zip
|
||||
|
||||
- name: Upload unified 'all' bundle to S3
|
||||
if: ${{ github.event.inputs.zip_mode == 'all' }}
|
||||
run: |
|
||||
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.gh_branch}}.zip
|
||||
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.commit_hash }}.zip
|
||||
|
||||
- name: Upload 'individual' bundles to S3
|
||||
if: ${{ github.event.inputs.zip_mode != 'all' }}
|
||||
run: |
|
||||
for name in penpot-frontend penpot-backend penpot-exporter; do
|
||||
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.gh_branch }}-latest.zip
|
||||
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.commit_hash }}.zip
|
||||
done
|
||||
|
||||
- name: Notify Mattermost about automatic bundles
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
TEXT: |
|
||||
📦 *Penpot bundle automatically generated*
|
||||
📄 PR: ${{ github.event.pull_request.title }}
|
||||
🔁 From: \`${{ github.head_ref }}\` to \`{{ github.base_ref }}\`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
- name: Notify Mattermost about manual bundles
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
TEXT: |
|
||||
📦 *Penpot bundle manually generated*
|
||||
📄 Triggered from branch: `${{ github.ref_name}}`
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
- name: Print artifact summary URL
|
||||
run: |
|
||||
echo "📦 Artifacts available at:"
|
||||
echo "🔗 https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -30,6 +30,7 @@
|
||||
/*.zip
|
||||
/.clj-kondo/.cache
|
||||
/_dump
|
||||
/notes
|
||||
/backend/*.md
|
||||
/backend/*.sql
|
||||
/backend/*.txt
|
||||
@@ -40,6 +41,7 @@
|
||||
/backend/resources/public/assets
|
||||
/backend/resources/public/media
|
||||
/backend/target/
|
||||
/backend/experiments
|
||||
/bundle*
|
||||
/cd.md
|
||||
/clj-profiler/
|
||||
@@ -50,9 +52,6 @@
|
||||
/exporter/target
|
||||
/frontend/.storybook/preview-body.html
|
||||
/frontend/.storybook/preview-head.html
|
||||
/frontend/cypress/fixtures/validuser.json
|
||||
/frontend/cypress/videos/*/
|
||||
/frontend/cypress/videos/*/
|
||||
/frontend/dist/
|
||||
/frontend/npm-debug.log
|
||||
/frontend/out/
|
||||
@@ -69,6 +68,8 @@
|
||||
/vendor/svgclean/bundle*.js
|
||||
/web
|
||||
/library/target/
|
||||
/library/*.zip
|
||||
/external
|
||||
|
||||
clj-profiler/
|
||||
node_modules
|
||||
|
||||
128
CHANGES.md
128
CHANGES.md
@@ -1,78 +1,40 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.8.0 (Next / Unreleased)
|
||||
## 2.8.1
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix unexpected exception on processing old texts [Github #6889](https://github.com/penpot/penpot/pull/6889)
|
||||
- Fix error on inspect tab when selecting multiple shapes [Taiga #11655](https://tree.taiga.io/project/penpot/issue/11655)
|
||||
- Fix missing package for the penport_exporter Docker image [GitHub #7205](https://github.com/penpot/penpot/issues/7025)
|
||||
|
||||
|
||||
## 2.8.0
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
**Breaking changes on penpot library:**
|
||||
**Penpot Library**
|
||||
|
||||
The library entrypoint API object has been changed. From now you start creating a new
|
||||
build context, from where you can add multiple files and attach media. This change add the
|
||||
ability to build more than one file at same time and export them in an unique .penpot
|
||||
file.
|
||||
The initial prototype is completly reworked for provide a more consistent API
|
||||
and to have proper validation and params decoding. All the details can be found
|
||||
on [its own changelog](library/CHANGES.md)
|
||||
|
||||
```js
|
||||
const context = penpot.createBuildContext()
|
||||
**Penpot migrate from Redis to Valkey**
|
||||
|
||||
context.addFile({name:"aa"})
|
||||
context.addPage({name:"aa"})
|
||||
context.closePage()
|
||||
context.closeFile()
|
||||
|
||||
;; barray is instance of Uint8Array
|
||||
const barray = penpot.exportAsBytes(context);
|
||||
```
|
||||
|
||||
The previous `file.export()` method has been removed and several alternatives are
|
||||
added as first level functions on penpot library API entrypoint:
|
||||
|
||||
- `exportAsBytes(BuildContext context) -> Promise<Uint8Array>`
|
||||
- `exportAsBlob(BuildContext context) -> Promise<Blob>`
|
||||
- `exportStream(BuildContext context, WritableStream stream) -> Promise<Void>`
|
||||
|
||||
The stream variant allows writting data as it is generated to the stream, without the need
|
||||
to store the generated output entirelly in the memory.
|
||||
|
||||
There are also relevant semantic changes in how components should be created: this
|
||||
refactor removes all notions of the old components (v1). Since v2, the shapes that are
|
||||
part of a component live on a page. So, from now on, to create a component, you should
|
||||
first create a frame, then add shapes and/or groups to that frame, and then create a
|
||||
component by declaring that frame as the component root.
|
||||
|
||||
A non exhaustive list of changes:
|
||||
|
||||
- Change the signature of the `addPage` method: it now accepts an object (as a single argument) where you can pass `id`,
|
||||
`name`, and `background` props (instead of the previous positional arguments)
|
||||
- Rename the `createRect` method to `addRect`
|
||||
- Rename the `createCircle` method to `addCircle`
|
||||
- Rename the `createPath` method to `addPath`
|
||||
- Rename the `createText` method to `addText`
|
||||
- Rename the `addArtboard` method to `addBoard`
|
||||
- Rename `startComponent` to `addComponent` (to preserve the naming style)
|
||||
- Rename `createComponentInstance` to `addComponentInstance` (to preserve the naming style)
|
||||
- Remove `lookupShape`
|
||||
- Remove `asMap`
|
||||
- Remove `updateLibraryColor` (use `addLibraryColor` if you just need to replace a color)
|
||||
- Remove `deleteLibraryColor` (this library is intended to build files)
|
||||
- Remove `updateLibraryTypography` (use `addLibraryTypography` if you just need to replace a typography)
|
||||
- Remove `deleteLibraryTypography` (this library is intended to build files)
|
||||
- Remove `add/update/deleteLibraryMedia` (they are no longer supported by Penpot and have been replaced by components)
|
||||
- Remove `deleteObject` (this library is intended to build files)
|
||||
- Remove `updateObject` (this library is intended to build files)
|
||||
- Remove `finishComponent` (it is no longer necessary; see below for more details on component creation changes)
|
||||
|
||||
- Change the `getCurrentPageId` function to a read-only `currentPageId` property
|
||||
- Add `currentFileId` read-only property
|
||||
- Add `currentFrameId` read-only property
|
||||
- Add `lastId` read-only property
|
||||
As [Valkey](https://valkey.io/) is an opne-souce fork of [Redis](https://redis.io/)
|
||||
version 7.2.4, this version of Penpot will be compatible with Redis but may diverge
|
||||
in future versions. Therefore, **migration from Redis to ValKey is recommended for all
|
||||
on-premises instances** that want to keep up to date.
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
- Add Serbian language [GitHub #5002](https://github.com/penpot/penpot/issues/5002) by [crnobog69](https://github.com/crnobog69)
|
||||
|
||||
### :sparkles: New features
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
- Optimize profile setup flow for better user experience [Taiga #10028](https://tree.taiga.io/project/penpot/us/10028)
|
||||
- Rewrite path shape data PathData encoding [Taiga #8542](https://tree.taiga.io/project/penpot/us/8542?milestone=441308)
|
||||
- Update base image for Docker Backend and Exporter to Ubuntu 24.04
|
||||
- Update base image for Docker Frontend to Nginx 1.28.0
|
||||
- Allow multi file token import [Github #27](https://github.com/tokens-studio/penpot/issues/27)
|
||||
@@ -80,14 +42,58 @@ A non exhaustive list of changes:
|
||||
- Deselect layers (and path nodes) with Ctrl+Shift+Drag [Github #2509](https://github.com/penpot/penpot/issues/2509)
|
||||
- Copy to SVG from contextual menu [Github #838](https://github.com/penpot/penpot/issues/838)
|
||||
- Add styles for Inkeep Chat at workspace [Taiga #10708](https://tree.taiga.io/project/penpot/us/10708)
|
||||
- Add configuration for air gapped installations with Docker
|
||||
- Support system color scheme [Github #5030](https://github.com/penpot/penpot/issues/5030)
|
||||
- Persist ruler visibility across files and reloads [GitHub #4586](https://github.com/penpot/penpot/issues/4586)
|
||||
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
|
||||
- Add tooltip component to DS [Taiga 9220](https://tree.taiga.io/project/penpot/us/9220)
|
||||
- Allow multi file token export [Taiga #10144](https://tree.taiga.io/project/penpot/us/10144)
|
||||
- Fix problem when double click on hidden shapes [Taiga #11314](https://tree.taiga.io/project/penpot/issue/11314)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix getCurrentUser for plugins api [Taiga #11057](https://tree.taiga.io/project/penpot/issue/11057)
|
||||
- Fix spacing / sizes of different elements in the measurements section of the design tab [Taiga #11076](https://tree.taiga.io/project/penpot/issue/11076)
|
||||
- Fix selection of short paths [Github #4472](https://github.com/penpot/penpot/issues/4472)
|
||||
- Fix element positioning on the right side to adjust to grid [#11073](https://tree.taiga.io/project/penpot/issue/11073)
|
||||
- Fix palette is over sidebar [#11160](https://tree.taiga.io/project/penpot/issue/11160)
|
||||
- Fix font size input not displaying "mixed" when multiple texts are selected [Taiga #11177](https://tree.taiga.io/project/penpot/issue/11177)
|
||||
- Misalignments at Create account [Taiga #11315](https://tree.taiga.io/project/penpot/issue/11315)
|
||||
- Fix issue with importing files where flex/grid is used [Taiga #11334](https://tree.taiga.io/project/penpot/issue/11334)
|
||||
- Fix wrong color in the export progress bar [Taiga #11299](https://tree.taiga.io/project/penpot/issue/11299)
|
||||
- Fix right sidebar width overflow on long layer names [Taiga #11212](https://tree.taiga.io/project/penpot/issue/11212)
|
||||
- Fix comment icon fill [Taiga #11388](https://tree.taiga.io/project/penpot/issue/11388)
|
||||
- Fix gap on radio-buttons component [Taiga #11360](https://tree.taiga.io/project/penpot/issue/11360)
|
||||
- Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
|
||||
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
|
||||
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
|
||||
- Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337](https://tree.taiga.io/project/penpot/issue/11337)
|
||||
- Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061)
|
||||
- Fix entering long project name [Taiga #11417](https://tree.taiga.io/project/penpot/issue/11417)
|
||||
- Fix slow color picker [Taiga #11019](https://tree.taiga.io/project/penpot/issue/11019)
|
||||
- Fix tooltip position after click [Taiga #11405](https://tree.taiga.io/project/penpot/issue/11405)
|
||||
- Fix incorrect media translation on paste text with fill images [Github #6845](https://github.com/penpot/penpot/pull/6845)
|
||||
|
||||
## 2.7.0 (Unreleased)
|
||||
## 2.7.2
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Update plugins runtime [Github #6604](https://github.com/penpot/penpot/pull/6604)
|
||||
- Backport from develop a minor fix that enables import of files
|
||||
generated by penpot library [Github #6614](https://github.com/penpot/penpot/pull/6614)
|
||||
- Fix copy in error message [GitHub #6615](https://github.com/penpot/penpot/pull/6615)
|
||||
- Fix url on invitation link [Taiga #11284](https://tree.taiga.io/project/penpot/issue/11284)
|
||||
|
||||
|
||||
## 2.7.1
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix incorrect handling of strokes with images on importing files
|
||||
- Fix tokens disappearing after manual additions [Taiga #11063](https://tree.taiga.io/project/penpot/issue/11063)
|
||||
|
||||
|
||||
## 2.7.0
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
|
||||
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Penpot's Code of Conduct
|
||||
|
||||
Check it at: https://help.penpot.app/contributing-guide/coc/
|
||||
14
README.md
14
README.md
@@ -34,7 +34,8 @@
|
||||
|
||||
<br />
|
||||
|
||||
[Penpot video](https://github.com/user-attachments/assets/08b83119-c090-4a74-86ed-7bfbdda9a793)
|
||||
[Penpot video](https://github.com/user-attachments/assets/7c67fd7c-04d3-4c9b-88ec-b6f5e23f8332
|
||||
)
|
||||
|
||||
<br />
|
||||
|
||||
@@ -93,10 +94,9 @@ With Penpot’s standardized [design tokens](https://penpot.dev/collaboration/de
|
||||
|
||||
## Getting started ##
|
||||
|
||||
### Install with Elestio ###
|
||||
Penpot is the only design & prototype platform that is deployment agnostic. You can use it or deploy it anywhere.
|
||||
Penpot is the only design & prototype platform that is deployment agnostic. You can use it in our [SAAS](https://design.penpot.app) or deploy it anywhere.
|
||||
|
||||
Learn how to install it with Elestio and Docker, or other options on [our website](https://penpot.app/self-host).
|
||||
Learn how to install it with Docker, Kubernetes, Elestio or other options on [our website](https://penpot.app/self-host).
|
||||
<br />
|
||||
|
||||
<p align="center">
|
||||
@@ -128,6 +128,12 @@ You will find the following categories:
|
||||
</p>
|
||||
<br />
|
||||
|
||||
### Code of Conduct ###
|
||||
|
||||
Anyone who contributes to Penpot, whether through code, in the community, or at an event, must adhere to the
|
||||
[code of conduct](https://help.penpot.app/contributing-guide/coc/) and foster a positive and safe environment.
|
||||
|
||||
|
||||
## Contributing ##
|
||||
|
||||
Any contribution will make a difference to improve Penpot. How can you get involved?
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
:deps
|
||||
{penpot/common {:local/root "../common"}
|
||||
org.clojure/clojure {:mvn/version "1.12.0"}
|
||||
org.clojure/clojure {:mvn/version "1.12.1"}
|
||||
org.clojure/tools.namespace {:mvn/version "1.5.0"}
|
||||
|
||||
com.github.luben/zstd-jni {:mvn/version "1.5.7-3"}
|
||||
@@ -17,8 +17,15 @@
|
||||
|
||||
io.prometheus/simpleclient_httpserver {:mvn/version "0.16.0"}
|
||||
|
||||
io.lettuce/lettuce-core {:mvn/version "6.6.0.RELEASE"}
|
||||
io.lettuce/lettuce-core {:mvn/version "6.7.0.RELEASE"}
|
||||
;; Minimal dependencies required by lettuce, we need to include them
|
||||
;; explicitly because clojure dependency management does not support
|
||||
;; yet the BOM format.
|
||||
io.micrometer/micrometer-core {:mvn/version "1.14.2"}
|
||||
io.micrometer/micrometer-observation {:mvn/version "1.14.2"}
|
||||
|
||||
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
|
||||
com.google.guava/guava {:mvn/version "33.4.8-jre"}
|
||||
|
||||
funcool/yetti
|
||||
{:git/tag "v11.4"
|
||||
@@ -28,11 +35,10 @@
|
||||
|
||||
com.github.seancorfield/next.jdbc
|
||||
{:mvn/version "1.3.1002"}
|
||||
metosin/reitit-core {:mvn/version "0.8.0"}
|
||||
metosin/reitit-core {:mvn/version "0.9.1"}
|
||||
nrepl/nrepl {:mvn/version "1.3.1"}
|
||||
cider/cider-nrepl {:mvn/version "0.55.7"}
|
||||
|
||||
org.postgresql/postgresql {:mvn/version "42.7.5"}
|
||||
org.postgresql/postgresql {:mvn/version "42.7.6"}
|
||||
org.xerial/sqlite-jdbc {:mvn/version "3.49.1.0"}
|
||||
|
||||
com.zaxxer/HikariCP {:mvn/version "6.3.0"}
|
||||
@@ -59,7 +65,7 @@
|
||||
|
||||
;; Pretty Print specs
|
||||
pretty-spec/pretty-spec {:mvn/version "0.1.4"}
|
||||
software.amazon.awssdk/s3 {:mvn/version "2.31.48"}}
|
||||
software.amazon.awssdk/s3 {:mvn/version "2.31.55"}}
|
||||
|
||||
:paths ["src" "resources" "target/classes"]
|
||||
:aliases
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
|
||||
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/penpot/penpot"
|
||||
|
||||
@@ -13,7 +13,7 @@ This will automatically include {{requested-by|abbreviate:25}} in the team, so t
|
||||
|
||||
Click the link below to provide team access:
|
||||
|
||||
{{ public-uri }}/#/dashboard/members?team-id{{team-id}}&invite-email={{requested-by-email|urlescape}}
|
||||
{{ public-uri }}/#/dashboard/members?team-id={{team-id}}&invite-email={{requested-by-email|urlescape}}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
[{:id "wireframing-kit"
|
||||
[{:id "tokens-starter-kit"
|
||||
:name "Design tokens starter kit"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"},
|
||||
{:id "wireframing-kit"
|
||||
:name "Wireframe library"
|
||||
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"}
|
||||
{:id "prototype-examples"
|
||||
|
||||
@@ -77,8 +77,9 @@ export JAVA_OPTS="\
|
||||
-Djdk.attach.allowAttachSelf \
|
||||
-Dlog4j2.configurationFile=log4j2-devenv-repl.xml \
|
||||
-Djdk.tracePinnedThreads=full \
|
||||
-Dim4java.useV7=true \
|
||||
-XX:+EnableDynamicAgentLoading \
|
||||
-XX:-OmitStackTraceInFastThrow \
|
||||
-XX:-OmitStackTraceInFastThrow \
|
||||
-XX:+UnlockDiagnosticVMOptions \
|
||||
-XX:+DebugNonSafepoints \
|
||||
--sun-misc-unsafe-memory-access=allow \
|
||||
@@ -106,9 +107,6 @@ export OPTIONS="-A:jmx-remote -A:dev"
|
||||
# Setup GC
|
||||
# export OPTIONS="$OPTIONS -J-XX:+UseZGC"
|
||||
|
||||
# Enable ImageMagick v7.x support
|
||||
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
|
||||
|
||||
export OPTIONS_EVAL="nil"
|
||||
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)"
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ if [ -f ./environ ]; then
|
||||
source ./environ
|
||||
fi
|
||||
|
||||
export JVM_OPTS="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --enable-native-access=ALL-UNNAMED --enable-preview $JVM_OPTS"
|
||||
export JAVA_OPTS="-Dim4java.useV7=true -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --sun-misc-unsafe-memory-access=allow --enable-native-access=ALL-UNNAMED --enable-preview $JVM_OPTS $JAVA_OPTS"
|
||||
|
||||
ENTRYPOINT=${1:-app.main};
|
||||
|
||||
set -ex
|
||||
exec $JAVA_CMD $JVM_OPTS -jar penpot.jar -m $ENTRYPOINT
|
||||
exec $JAVA_CMD $JAVA_OPTS -jar penpot.jar -m $ENTRYPOINT
|
||||
|
||||
@@ -36,9 +36,6 @@ export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
|
||||
# Setup default multipart upload size to 300MiB
|
||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
|
||||
|
||||
# Enable ImageMagick v7.x support
|
||||
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
|
||||
|
||||
# Initialize MINIO config
|
||||
mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin -q
|
||||
mc admin user add penpot-s3 penpot-devenv penpot-devenv -q
|
||||
@@ -61,10 +58,8 @@ export JAVA_OPTS="\
|
||||
-Djdk.attach.allowAttachSelf \
|
||||
-Dlog4j2.configurationFile=log4j2-devenv.xml \
|
||||
-Djdk.tracePinnedThreads=full \
|
||||
-XX:+EnableDynamicAgentLoading \
|
||||
-XX:-OmitStackTraceInFastThrow \
|
||||
-XX:+UnlockDiagnosticVMOptions \
|
||||
-XX:+DebugNonSafepoints \
|
||||
-Dim4java.useV7=true \
|
||||
-XX:-OmitStackTraceInFastThrow \
|
||||
--sun-misc-unsafe-memory-access=allow \
|
||||
--enable-preview \
|
||||
--enable-native-access=ALL-UNNAMED";
|
||||
|
||||
@@ -16,16 +16,40 @@
|
||||
;; PRE DECODE
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- pre-clean-bool-content
|
||||
[shape]
|
||||
(if-let [content (get shape :bool-content)]
|
||||
(-> shape
|
||||
(assoc :content content)
|
||||
(dissoc :bool-content))
|
||||
shape))
|
||||
|
||||
(defn- pre-clean-shadow-color
|
||||
[shape]
|
||||
(d/update-when shape :shadow
|
||||
(fn [shadows]
|
||||
(mapv (fn [shadow]
|
||||
(update shadow :color
|
||||
(fn [color]
|
||||
(let [ref-id (get color :id)
|
||||
ref-file (get color :file-id)]
|
||||
(-> (d/without-qualified color)
|
||||
(select-keys [:opacity :color :gradient :image :ref-id :ref-file])
|
||||
(cond-> ref-id
|
||||
(assoc :ref-id ref-id))
|
||||
(cond-> ref-file
|
||||
(assoc :ref-file ref-file)))))))
|
||||
shadows))))
|
||||
|
||||
(defn clean-shape-pre-decode
|
||||
"Applies a pre-decode phase migration to the shape"
|
||||
[shape]
|
||||
(if (= "bool" (:type shape))
|
||||
(if-let [content (get shape :bool-content)]
|
||||
(-> shape
|
||||
(assoc :content content)
|
||||
(dissoc :bool-content))
|
||||
shape)
|
||||
shape))
|
||||
(cond-> shape
|
||||
(= "bool" (:type shape))
|
||||
(pre-clean-bool-content)
|
||||
|
||||
(contains? shape :shadow)
|
||||
(pre-clean-shadow-color)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; POST DECODE
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
(* 1024 1024 100))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(declare get-resolved-file-libraries)
|
||||
|
||||
(def file-attrs
|
||||
#{:id
|
||||
@@ -143,11 +144,13 @@
|
||||
(reduce #(index-object %1 %2 attr) index coll)))
|
||||
|
||||
(defn decode-row
|
||||
"A generic decode row helper"
|
||||
[{:keys [data features] :as row}]
|
||||
(cond-> row
|
||||
features (assoc :features (db/decode-pgarray features #{}))
|
||||
data (assoc :data (blob/decode data))))
|
||||
[{:keys [data changes features] :as row}]
|
||||
(when row
|
||||
(cond-> row
|
||||
features (assoc :features (db/decode-pgarray features #{}))
|
||||
changes (assoc :changes (blob/decode changes))
|
||||
data (assoc :data (blob/decode data)))))
|
||||
|
||||
|
||||
(defn decode-file
|
||||
"A general purpose file decoding function that resolves all external
|
||||
@@ -156,7 +159,8 @@
|
||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
|
||||
(let [file (->> file
|
||||
(feat.fmigr/resolve-applied-migrations cfg)
|
||||
(feat.fdata/resolve-file-data cfg))]
|
||||
(feat.fdata/resolve-file-data cfg))
|
||||
libs (delay (get-resolved-file-libraries cfg file))]
|
||||
|
||||
(-> file
|
||||
(update :features db/decode-pgarray #{})
|
||||
@@ -164,7 +168,7 @@
|
||||
(update :data feat.fdata/process-pointers deref)
|
||||
(update :data feat.fdata/process-objects (partial into {}))
|
||||
(update :data assoc :id id)
|
||||
(fmg/migrate-file)))))
|
||||
(fmg/migrate-file libs)))))
|
||||
|
||||
(defn get-file
|
||||
"Get file, resolve all features and apply migrations.
|
||||
@@ -418,26 +422,27 @@
|
||||
(db/exec-one! conn ["SET CONSTRAINTS ALL DEFERRED"])))
|
||||
|
||||
(defn process-file
|
||||
[{:keys [id] :as file}]
|
||||
(-> file
|
||||
(update :data (fn [fdata]
|
||||
(-> fdata
|
||||
(assoc :id id)
|
||||
(dissoc :recent-colors))))
|
||||
(fmg/migrate-file)
|
||||
(update :data (fn [fdata]
|
||||
(-> fdata
|
||||
(update :pages-index relink-shapes)
|
||||
(update :components relink-shapes)
|
||||
(update :media relink-media)
|
||||
(update :colors relink-colors)
|
||||
(d/without-nils))))
|
||||
[cfg {:keys [id] :as file}]
|
||||
(let [libs (delay (get-resolved-file-libraries cfg file))]
|
||||
(-> file
|
||||
(update :data (fn [fdata]
|
||||
(-> fdata
|
||||
(assoc :id id)
|
||||
(dissoc :recent-colors))))
|
||||
(update :data (fn [fdata]
|
||||
(-> fdata
|
||||
(update :pages-index relink-shapes)
|
||||
(update :components relink-shapes)
|
||||
(update :media relink-media)
|
||||
(update :colors relink-colors)
|
||||
(d/without-nils))))
|
||||
(fmg/migrate-file libs)
|
||||
|
||||
;; NOTE: this is necessary because when we just creating a new
|
||||
;; file from imported artifact or cloned file there are no
|
||||
;; migrations registered on the database, so we need to persist
|
||||
;; all of them, not only the applied
|
||||
(vary-meta dissoc ::fmg/migrated)))
|
||||
;; NOTE: this is necessary because when we just creating a new
|
||||
;; file from imported artifact or cloned file there are no
|
||||
;; migrations registered on the database, so we need to persist
|
||||
;; all of them, not only the applied
|
||||
(vary-meta dissoc ::fmg/migrated))))
|
||||
|
||||
(defn encode-file
|
||||
[{:keys [::db/conn] :as cfg} {:keys [id features] :as file}]
|
||||
@@ -528,3 +533,49 @@
|
||||
(l/error :hint "file schema validation error" :cause result))))
|
||||
|
||||
(insert-file! cfg file opts)))
|
||||
|
||||
|
||||
(def ^:private sql:get-file-libraries
|
||||
"WITH RECURSIVE libs AS (
|
||||
SELECT fl.*, flr.synced_at
|
||||
FROM file AS fl
|
||||
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
||||
WHERE flr.file_id = ?::uuid
|
||||
UNION
|
||||
SELECT fl.*, flr.synced_at
|
||||
FROM file AS fl
|
||||
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
||||
JOIN libs AS l ON (flr.file_id = l.id)
|
||||
)
|
||||
SELECT l.id,
|
||||
l.features,
|
||||
l.project_id,
|
||||
p.team_id,
|
||||
l.created_at,
|
||||
l.modified_at,
|
||||
l.deleted_at,
|
||||
l.name,
|
||||
l.revn,
|
||||
l.vern,
|
||||
l.synced_at,
|
||||
l.is_shared
|
||||
FROM libs AS l
|
||||
INNER JOIN project AS p ON (p.id = l.project_id)
|
||||
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
|
||||
|
||||
(defn get-file-libraries
|
||||
[conn file-id]
|
||||
(into []
|
||||
(comp
|
||||
;; FIXME: :is-indirect set to false to all rows looks
|
||||
;; completly useless
|
||||
(map #(assoc % :is-indirect false))
|
||||
(map decode-row))
|
||||
(db/exec! conn [sql:get-file-libraries file-id])))
|
||||
|
||||
(defn get-resolved-file-libraries
|
||||
"A helper for preload file libraries"
|
||||
[{:keys [::db/conn] :as cfg} file]
|
||||
(->> (get-file-libraries conn (:id file))
|
||||
(into [file] (map #(get-file cfg (:id %))))
|
||||
(d/index-by :id)))
|
||||
|
||||
@@ -551,8 +551,8 @@
|
||||
(cond-> (and (= idx 0) (some? name))
|
||||
(assoc :name name))
|
||||
(assoc :project-id project-id)
|
||||
(dissoc :thumbnails)
|
||||
(bfc/process-file))]
|
||||
(dissoc :thumbnails))
|
||||
file (bfc/process-file system file)]
|
||||
|
||||
;; All features that are enabled and requires explicit migration are
|
||||
;; added to the state for a posterior migration step.
|
||||
|
||||
@@ -281,8 +281,8 @@
|
||||
|
||||
(let [file (-> (read-obj cfg :file file-id)
|
||||
(update :id bfc/lookup-index)
|
||||
(update :project-id bfc/lookup-index)
|
||||
(bfc/process-file))]
|
||||
(update :project-id bfc/lookup-index))
|
||||
file (bfc/process-file cfg file)]
|
||||
|
||||
(events/tap :progress
|
||||
{:op :import
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
[app.binfile.common :as bfc]
|
||||
[app.binfile.migrations :as bfm]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.migrations :as-alias fmg]
|
||||
@@ -54,7 +53,7 @@
|
||||
[:map {:title "Manifest"}
|
||||
[:version ::sm/int]
|
||||
[:type :string]
|
||||
|
||||
[:referer {:optional true} :string]
|
||||
[:generated-by {:optional true} :string]
|
||||
|
||||
[:files
|
||||
@@ -113,7 +112,7 @@
|
||||
(sm/encoder ::ctc/component sm/json-transformer))
|
||||
|
||||
(def encode-color
|
||||
(sm/encoder ::ctcl/color sm/json-transformer))
|
||||
(sm/encoder ctcl/schema:library-color sm/json-transformer))
|
||||
|
||||
(def encode-typography
|
||||
(sm/encoder ::cty/typography sm/json-transformer))
|
||||
@@ -142,7 +141,7 @@
|
||||
(sm/decoder ::ctc/component sm/json-transformer))
|
||||
|
||||
(def decode-color
|
||||
(sm/decoder ::ctcl/color sm/json-transformer))
|
||||
(sm/decoder ctcl/schema:library-color sm/json-transformer))
|
||||
|
||||
(def decode-file
|
||||
(sm/decoder schema:file sm/json-transformer))
|
||||
@@ -157,7 +156,7 @@
|
||||
(sm/decoder ::cty/typography sm/json-transformer))
|
||||
|
||||
(def decode-tokens-lib
|
||||
(sm/decoder ::cto/tokens-lib sm/json-transformer))
|
||||
(sm/decoder cto/schema:tokens-lib sm/json-transformer))
|
||||
|
||||
(def decode-plugin-data
|
||||
(sm/decoder ::ctpg/plugin-data sm/json-transformer))
|
||||
@@ -186,7 +185,7 @@
|
||||
(sm/check-fn ::ctf/media))
|
||||
|
||||
(def validate-color
|
||||
(sm/check-fn ::ctcl/color))
|
||||
(sm/check-fn ctcl/schema:library-color))
|
||||
|
||||
(def validate-component
|
||||
(sm/check-fn ::ctc/component))
|
||||
@@ -373,6 +372,7 @@
|
||||
params {:type "penpot/export-files"
|
||||
:version 1
|
||||
:generated-by (str "penpot/" (:full cf/version))
|
||||
:refer "penpot"
|
||||
:files (vec (vals files))
|
||||
:relations rels}]
|
||||
(write-entry! output "manifest.json" params))))
|
||||
@@ -617,8 +617,7 @@
|
||||
(let [object (->> (read-entry input entry)
|
||||
(clean-component-pre-decode)
|
||||
(decode-component)
|
||||
(clean-component-post-decode)
|
||||
(validate-component))]
|
||||
(clean-component-post-decode))]
|
||||
(if (= id (:id object))
|
||||
(assoc result id object)
|
||||
result)))
|
||||
@@ -652,8 +651,7 @@
|
||||
(let [object (->> (read-entry input entry)
|
||||
(bfl/clean-shape-pre-decode)
|
||||
(decode-shape)
|
||||
(bfl/clean-shape-post-decode)
|
||||
(validate-shape))]
|
||||
(bfl/clean-shape-post-decode))]
|
||||
(if (= id (:id object))
|
||||
(assoc result id object)
|
||||
result)))
|
||||
@@ -699,7 +697,6 @@
|
||||
components (read-file-components cfg)
|
||||
plugin-data (read-file-plugin-data cfg)
|
||||
pages (read-file-pages cfg)]
|
||||
|
||||
{:pages (-> pages keys vec)
|
||||
:pages-index (into {} pages)
|
||||
:colors colors
|
||||
@@ -754,8 +751,10 @@
|
||||
(assoc :data data)
|
||||
(assoc :name file-name)
|
||||
(assoc :project-id project-id)
|
||||
(dissoc :options)
|
||||
(bfc/process-file))]
|
||||
(dissoc :options))
|
||||
|
||||
file (bfc/process-file cfg file)
|
||||
file (ctf/check-file file)]
|
||||
|
||||
(bfm/register-pending-migrations! cfg file)
|
||||
(bfc/save-file! cfg file ::db/return-keys false)
|
||||
@@ -879,13 +878,8 @@
|
||||
(defn- import-files
|
||||
[{:keys [::bfc/timestamp ::bfc/input ::bfc/name] :or {timestamp (dt/now)} :as cfg}]
|
||||
|
||||
(dm/assert!
|
||||
"expected zip file"
|
||||
(instance? ZipFile input))
|
||||
|
||||
(dm/assert!
|
||||
"expected valid instant"
|
||||
(dt/instant? timestamp))
|
||||
(assert (instance? ZipFile input) "expected zip file")
|
||||
(assert (dt/instant? timestamp) "expected valid instant")
|
||||
|
||||
(let [manifest (-> (read-manifest input)
|
||||
(validate-manifest))
|
||||
@@ -897,6 +891,7 @@
|
||||
:hint "unexpected type on manifest"
|
||||
:manifest manifest))
|
||||
|
||||
|
||||
;; Check if all files referenced on manifest are present
|
||||
(doseq [{file-id :id features :features} (:files manifest)]
|
||||
(let [path (str "files/" file-id ".json")]
|
||||
@@ -957,14 +952,13 @@
|
||||
|
||||
[{:keys [::bfc/ids] :as cfg} output]
|
||||
|
||||
(dm/assert!
|
||||
"expected a set of uuid's for `::bfc/ids` parameter"
|
||||
(and (set? ids)
|
||||
(every? uuid? ids)))
|
||||
(assert
|
||||
(and (set? ids) (every? uuid? ids))
|
||||
"expected a set of uuid's for `::bfc/ids` parameter")
|
||||
|
||||
(dm/assert!
|
||||
"expected instance of jio/IOFactory for `input`"
|
||||
(satisfies? jio/IOFactory output))
|
||||
(assert
|
||||
(satisfies? jio/IOFactory output)
|
||||
"expected instance of jio/IOFactory for `input`")
|
||||
|
||||
(let [id (uuid/next)
|
||||
tp (dt/tpoint)
|
||||
@@ -1003,14 +997,14 @@
|
||||
(defn import-files!
|
||||
[{:keys [::bfc/input] :as cfg}]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid profile-id and project-id on `cfg`"
|
||||
(assert
|
||||
(and (uuid? (::bfc/profile-id cfg))
|
||||
(uuid? (::bfc/project-id cfg))))
|
||||
(uuid? (::bfc/project-id cfg)))
|
||||
"expected valid profile-id and project-id on `cfg`")
|
||||
|
||||
(dm/assert!
|
||||
"expected instance of jio/IOFactory for `input`"
|
||||
(io/coercible? input))
|
||||
(assert
|
||||
(io/coercible? input)
|
||||
"expected instance of jio/IOFactory for `input`")
|
||||
|
||||
(let [id (uuid/next)
|
||||
tp (dt/tpoint)
|
||||
@@ -1030,3 +1024,9 @@
|
||||
:id (str id)
|
||||
:elapsed (dt/format-duration (tp))
|
||||
:error? (some? @cs))))))
|
||||
|
||||
(defn get-manifest
|
||||
[path]
|
||||
(with-open [input (ZipFile. (fs/file path))]
|
||||
(-> (read-manifest input)
|
||||
(validate-manifest))))
|
||||
|
||||
31
backend/src/app/features/logical_deletion.clj
Normal file
31
backend/src/app/features/logical_deletion.clj
Normal file
@@ -0,0 +1,31 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.features.logical-deletion
|
||||
"A code related to handle logical deletion mechanism"
|
||||
(:require
|
||||
[app.config :as cf]
|
||||
[app.util.time :as dt]))
|
||||
|
||||
(defn get-deletion-delay
|
||||
"Calculate the next deleted-at for a resource (file, team, etc) in function
|
||||
of team settings"
|
||||
[team]
|
||||
(if-let [subscription (get team :subscription)]
|
||||
(cond
|
||||
(and (= (:type subscription) "unlimited")
|
||||
(= (:status subscription) "active"))
|
||||
(dt/duration {:days 30})
|
||||
|
||||
(and (= (:type subscription) "enterprise")
|
||||
(= (:status subscription) "active"))
|
||||
(dt/duration {:days 90})
|
||||
|
||||
:else
|
||||
(cf/get-deletion-delay))
|
||||
|
||||
(cf/get-deletion-delay)))
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
(if (or (instance? java.util.concurrent.CompletionException cause)
|
||||
(instance? java.util.concurrent.ExecutionException cause))
|
||||
(-> record
|
||||
(assoc ::trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false))
|
||||
(assoc ::trace (ex/format-throwable cause :data? true :explain? false :header? false :summary? false))
|
||||
(assoc ::l/cause (ex-cause cause))
|
||||
(record->report))
|
||||
|
||||
@@ -64,18 +64,18 @@
|
||||
message))
|
||||
@message)
|
||||
:trace (or (::trace record)
|
||||
(some-> cause (ex/format-throwable :data? false :explain? false :header? false :summary? false)))}
|
||||
(some-> cause (ex/format-throwable :data? true :explain? false :header? false :summary? false)))}
|
||||
|
||||
(when-let [params (or (:request/params context) (:params context))]
|
||||
{:params (pp/pprint-str params :length 30 :level 13)})
|
||||
{:params (pp/pprint-str params :length 20 :level 20)})
|
||||
|
||||
(when-let [value (:value context)]
|
||||
{:value (pp/pprint-str value :length 30 :level 12)})
|
||||
{:value (pp/pprint-str value :length 30 :level 13)})
|
||||
|
||||
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
|
||||
{:data (pp/pprint-str data :length 30 :level 12)})
|
||||
{:data (pp/pprint-str data :length 30 :level 13)})
|
||||
|
||||
(when-let [explain (ex/explain data :length 30 :level 12)]
|
||||
(when-let [explain (ex/explain data :length 30 :level 13)]
|
||||
{:explain explain})))))
|
||||
|
||||
(defn error-record?
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
[app.svgo :as-alias svgo]
|
||||
[app.util.time :as dt]
|
||||
[app.worker :as-alias wrk]
|
||||
[cider.nrepl :refer [cider-nrepl-handler]]
|
||||
[clojure.test :as test]
|
||||
[clojure.tools.namespace.repl :as repl]
|
||||
[cuerdas.core :as str]
|
||||
@@ -605,7 +604,7 @@
|
||||
(let [p (promise)]
|
||||
(when (contains? cf/flags :nrepl-server)
|
||||
(l/inf :hint "start nrepl server" :port 6064)
|
||||
(nrepl/start-server :bind "0.0.0.0" :port 6064 :handler cider-nrepl-handler))
|
||||
(nrepl/start-server :bind "0.0.0.0" :port 6064))
|
||||
|
||||
(start)
|
||||
(deref p))
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
(sm/check-fn schema:input))
|
||||
|
||||
(defn validate-media-type!
|
||||
([upload] (validate-media-type! upload cm/valid-image-types))
|
||||
([upload] (validate-media-type! upload cm/image-types))
|
||||
([upload allowed]
|
||||
(when-not (contains? allowed (:mtype upload))
|
||||
(ex/raise :type :validation
|
||||
|
||||
@@ -231,13 +231,14 @@
|
||||
:hint "email has complaint reports")))
|
||||
|
||||
(defn prepare-register
|
||||
[{:keys [::db/pool] :as cfg} {:keys [email accept-newsletter-updates] :as params}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [fullname email accept-newsletter-updates] :as params}]
|
||||
|
||||
(validate-register-attempt! cfg params)
|
||||
|
||||
(let [email (profile/clean-email email)
|
||||
profile (profile/get-profile-by-email pool email)
|
||||
params {:email email
|
||||
:fullname fullname
|
||||
:password (:password params)
|
||||
:invitation-token (:invitation-token params)
|
||||
:backend "penpot"
|
||||
@@ -254,8 +255,10 @@
|
||||
|
||||
(def schema:prepare-register-profile
|
||||
[:map {:title "prepare-register-profile"}
|
||||
[:fullname ::sm/text]
|
||||
[:email ::sm/email]
|
||||
[:password schema:password]
|
||||
[:create-welcome-file {:optional true} :boolean]
|
||||
[:invitation-token {:optional true} schema:token]])
|
||||
|
||||
(sv/defmethod ::prepare-register-profile
|
||||
@@ -359,13 +362,9 @@
|
||||
:extra-data ptoken})))
|
||||
|
||||
(defn register-profile
|
||||
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token fullname theme] :as params}]
|
||||
(let [theme (when (= theme "light") theme)
|
||||
claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register})
|
||||
params (-> claims
|
||||
(into params)
|
||||
(assoc :fullname fullname)
|
||||
(assoc :theme theme))
|
||||
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token] :as params}]
|
||||
(let [claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register})
|
||||
params (into claims params)
|
||||
|
||||
profile (if-let [profile-id (:profile-id claims)]
|
||||
(profile/get-profile conn profile-id)
|
||||
@@ -479,10 +478,7 @@
|
||||
|
||||
(def schema:register-profile
|
||||
[:map {:title "register-profile"}
|
||||
[:token schema:token]
|
||||
[:fullname [::sm/word-string {:max 100}]]
|
||||
[:theme {:optional true} [:string {:max 10}]]
|
||||
[:create-welcome-file {:optional true} :boolean]])
|
||||
[:token schema:token]])
|
||||
|
||||
(sv/defmethod ::register-profile
|
||||
{::rpc/auth false
|
||||
|
||||
@@ -134,11 +134,18 @@
|
||||
::webhooks/event? true
|
||||
::sse/stream? true
|
||||
::sm/params schema:import-binfile}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version] :as params}]
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id project-id version file] :as params}]
|
||||
(projects/check-edition-permissions! pool profile-id project-id)
|
||||
(let [params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
(assoc :version (or version 1)))]
|
||||
(let [version (or version 1)
|
||||
params (-> params
|
||||
(assoc :profile-id profile-id)
|
||||
(assoc :version version))
|
||||
manifest (case (int version)
|
||||
1 nil
|
||||
3 (bf.v3/get-manifest (:path file)))]
|
||||
|
||||
(with-meta
|
||||
(sse/response (partial import-binfile cfg params))
|
||||
{::audit/props {:file nil}})))
|
||||
{::audit/props {:file nil
|
||||
:generated-by (:generated-by manifest)
|
||||
:referer (:referer manifest)}})))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
(ns app.rpc.commands.files
|
||||
(:require
|
||||
[app.binfile.common :as bfc]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
@@ -23,6 +24,7 @@
|
||||
[app.db.sql :as-alias sql]
|
||||
[app.features.fdata :as feat.fdata]
|
||||
[app.features.file-migrations :as feat.fmigr]
|
||||
[app.features.logical-deletion :as ldel]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
@@ -211,7 +213,8 @@
|
||||
[{:keys [::db/conn] :as cfg} {:keys [id] :as file} {:keys [read-only?]}]
|
||||
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
|
||||
pmap/*tracked* (pmap/create-tracked)]
|
||||
(let [;; For avoid unnecesary overhead of creating multiple pointers and
|
||||
(let [libs (delay (bfc/get-resolved-file-libraries cfg file))
|
||||
;; For avoid unnecesary overhead of creating multiple pointers and
|
||||
;; handly internally with objects map in their worst case (when
|
||||
;; probably all shapes and all pointers will be readed in any
|
||||
;; case), we just realize/resolve them before applying the
|
||||
@@ -219,7 +222,7 @@
|
||||
file (-> file
|
||||
(update :data feat.fdata/process-pointers deref)
|
||||
(update :data feat.fdata/process-objects (partial into {}))
|
||||
(fmg/migrate-file))]
|
||||
(fmg/migrate-file libs))]
|
||||
|
||||
(if (or read-only? (db/read-only? conn))
|
||||
file
|
||||
@@ -556,7 +559,10 @@
|
||||
f.project_id,
|
||||
f.created_at,
|
||||
f.modified_at,
|
||||
f.data_backend,
|
||||
f.data_ref_id,
|
||||
f.name,
|
||||
f.version,
|
||||
f.is_shared,
|
||||
ft.media_id,
|
||||
p.team_id
|
||||
@@ -592,7 +598,11 @@
|
||||
(teams/check-read-permissions! conn profile-id team-id)
|
||||
(->> (db/exec! conn [sql:team-shared-files team-id])
|
||||
(into #{} (comp
|
||||
(map decode-row)
|
||||
;; NOTE: this decode operation is a workaround for a
|
||||
;; fast fix, this should be approached with a more
|
||||
;; efficient implementation, for now it loads all
|
||||
;; the files in memory.
|
||||
(map (partial bfc/decode-file cfg))
|
||||
(map (fn [row]
|
||||
(if-let [media-id (:media-id row)]
|
||||
(-> row
|
||||
@@ -615,44 +625,6 @@
|
||||
|
||||
;; --- COMMAND QUERY: get-file-libraries
|
||||
|
||||
(def ^:private sql:get-file-libraries
|
||||
"WITH RECURSIVE libs AS (
|
||||
SELECT fl.*, flr.synced_at
|
||||
FROM file AS fl
|
||||
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
||||
WHERE flr.file_id = ?::uuid
|
||||
UNION
|
||||
SELECT fl.*, flr.synced_at
|
||||
FROM file AS fl
|
||||
JOIN file_library_rel AS flr ON (flr.library_file_id = fl.id)
|
||||
JOIN libs AS l ON (flr.file_id = l.id)
|
||||
)
|
||||
SELECT l.id,
|
||||
l.features,
|
||||
l.project_id,
|
||||
p.team_id,
|
||||
l.created_at,
|
||||
l.modified_at,
|
||||
l.deleted_at,
|
||||
l.name,
|
||||
l.revn,
|
||||
l.vern,
|
||||
l.synced_at,
|
||||
l.is_shared
|
||||
FROM libs AS l
|
||||
INNER JOIN project AS p ON (p.id = l.project_id)
|
||||
WHERE l.deleted_at IS NULL OR l.deleted_at > now();")
|
||||
|
||||
(defn get-file-libraries
|
||||
[conn file-id]
|
||||
(into []
|
||||
(comp
|
||||
;; FIXME: :is-indirect set to false to all rows looks
|
||||
;; completly useless
|
||||
(map #(assoc % :is-indirect false))
|
||||
(map decode-row))
|
||||
(db/exec! conn [sql:get-file-libraries file-id])))
|
||||
|
||||
(def ^:private schema:get-file-libraries
|
||||
[:map {:title "get-file-libraries"}
|
||||
[:file-id ::sm/uuid]])
|
||||
@@ -664,7 +636,7 @@
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id]}]
|
||||
(dm/with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(get-file-libraries conn file-id)))
|
||||
(bfc/get-file-libraries conn file-id)))
|
||||
|
||||
|
||||
;; --- COMMAND QUERY: Files that use this File library
|
||||
@@ -970,12 +942,13 @@
|
||||
;; --- MUTATION COMMAND: delete-file
|
||||
|
||||
(defn- mark-file-deleted
|
||||
[conn file-id]
|
||||
(let [file (db/update! conn :file
|
||||
{:deleted-at (dt/now)}
|
||||
{:id file-id}
|
||||
{::db/return-keys [:id :name :is-shared :deleted-at
|
||||
:project-id :created-at :modified-at]})]
|
||||
[conn team file-id]
|
||||
(let [delay (ldel/get-deletion-delay team)
|
||||
file (db/update! conn :file
|
||||
{:deleted-at (dt/in-future delay)}
|
||||
{:id file-id}
|
||||
{::db/return-keys [:id :name :is-shared :deleted-at
|
||||
:project-id :created-at :modified-at]})]
|
||||
(wrk/submit! {::db/conn conn
|
||||
::wrk/task :delete-object
|
||||
::wrk/params {:object :file
|
||||
@@ -991,7 +964,11 @@
|
||||
(defn- delete-file
|
||||
[{:keys [::db/conn] :as cfg} {:keys [profile-id id] :as params}]
|
||||
(check-edition-permissions! conn profile-id id)
|
||||
(let [file (mark-file-deleted conn id)]
|
||||
(let [team (teams/get-team conn
|
||||
:profile-id profile-id
|
||||
:file-id id)
|
||||
file (mark-file-deleted conn team id)]
|
||||
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:project-id (:project-id file)
|
||||
:name (:name file)
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
[app.db :as db]
|
||||
[app.features.fdata :as feat.fdata]
|
||||
[app.features.file-migrations :as feat.fmigr]
|
||||
[app.features.logical-deletion :as ldel]
|
||||
[app.http.errors :as errors]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.loggers.webhooks :as webhooks]
|
||||
@@ -209,7 +210,7 @@
|
||||
|
||||
Only intended for internal use on this module."
|
||||
[{:keys [::db/conn ::wrk/executor ::timestamp] :as cfg}
|
||||
{:keys [profile-id file features changes session-id skip-validate] :as params}]
|
||||
{:keys [profile-id file team features changes session-id skip-validate] :as params}]
|
||||
|
||||
(let [;; Retrieve the file data
|
||||
file (feat.fmigr/resolve-applied-migrations cfg file)
|
||||
@@ -243,7 +244,7 @@
|
||||
:created-at timestamp
|
||||
:updated-at timestamp
|
||||
:deleted-at (if (::snapshot-data file)
|
||||
(dt/plus timestamp (cf/get-deletion-delay))
|
||||
(dt/plus timestamp (ldel/get-deletion-delay team))
|
||||
(dt/plus timestamp (dt/duration {:hours 1})))
|
||||
:file-id (:id file)
|
||||
:revn (:revn file)
|
||||
@@ -340,6 +341,7 @@
|
||||
(-> data
|
||||
(blob/decode)
|
||||
(assoc :id (:id file)))))
|
||||
libs (delay (bfc/get-resolved-file-libraries cfg file))
|
||||
|
||||
;; For avoid unnecesary overhead of creating multiple pointers
|
||||
;; and handly internally with objects map in their worst
|
||||
@@ -350,7 +352,7 @@
|
||||
(-> file
|
||||
(update :data feat.fdata/process-pointers deref)
|
||||
(update :data feat.fdata/process-objects (partial into {}))
|
||||
(fmg/migrate-file))
|
||||
(fmg/migrate-file libs))
|
||||
file)
|
||||
|
||||
file (apply update-fn cfg file args)
|
||||
@@ -379,13 +381,6 @@
|
||||
|
||||
(bfc/encode-file cfg file))))
|
||||
|
||||
(defn- get-file-libraries
|
||||
"A helper for preload file libraries, mainly used for perform file
|
||||
semantical and structural validation"
|
||||
[{:keys [::db/conn] :as cfg} file]
|
||||
(->> (files/get-file-libraries conn (:id file))
|
||||
(into [file] (map #(bfc/get-file cfg (:id %))))
|
||||
(d/index-by :id)))
|
||||
|
||||
(defn- soft-validate-file-schema!
|
||||
[file]
|
||||
@@ -411,7 +406,7 @@
|
||||
(when (and (or (contains? cf/flags :file-validation)
|
||||
(contains? cf/flags :soft-file-validation))
|
||||
(not skip-validate))
|
||||
(get-file-libraries cfg file))
|
||||
(bfc/get-resolved-file-libraries cfg file))
|
||||
|
||||
|
||||
;; The main purpose of this atom is provide a contextual state
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[app.common.uuid :as uuid]
|
||||
[app.db :as db]
|
||||
[app.db.sql :as-alias sql]
|
||||
[app.features.logical-deletion :as ldel]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as-alias webhooks]
|
||||
[app.media :as media]
|
||||
@@ -202,32 +203,40 @@
|
||||
(sv/defmethod ::delete-font
|
||||
{::doc/added "1.18"
|
||||
::webhooks/event? true
|
||||
::sm/params schema:delete-font}
|
||||
[cfg {:keys [::rpc/profile-id id team-id]}]
|
||||
(db/tx-run! cfg
|
||||
(fn [{:keys [::db/conn] :as cfg}]
|
||||
(teams/check-edition-permissions! conn profile-id team-id)
|
||||
(let [fonts (db/query conn :team-font-variant
|
||||
{:team-id team-id
|
||||
:font-id id
|
||||
:deleted-at nil}
|
||||
{::sql/for-update true})
|
||||
tnow (dt/now)]
|
||||
::sm/params schema:delete-font
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id team-id]}]
|
||||
(let [team (teams/get-team conn
|
||||
:profile-id profile-id
|
||||
:team-id team-id)
|
||||
|
||||
(when-not (seq fonts)
|
||||
(ex/raise :type :not-found
|
||||
:code :object-not-found))
|
||||
fonts (db/query conn :team-font-variant
|
||||
{:team-id team-id
|
||||
:font-id id
|
||||
:deleted-at nil}
|
||||
{::sql/for-update true})
|
||||
|
||||
(doseq [font fonts]
|
||||
(db/update! conn :team-font-variant
|
||||
{:deleted-at tnow}
|
||||
{:id (:id font)}))
|
||||
delay (ldel/get-deletion-delay team)
|
||||
tnow (dt/in-future delay)]
|
||||
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:id id
|
||||
:team-id team-id
|
||||
:name (:font-family (peek fonts))
|
||||
:profile-id profile-id}})))))
|
||||
(teams/check-edition-permissions! (:permissions team))
|
||||
|
||||
(when-not (seq fonts)
|
||||
(ex/raise :type :not-found
|
||||
:code :object-not-found))
|
||||
|
||||
|
||||
(doseq [font fonts]
|
||||
(db/update! conn :team-font-variant
|
||||
{:deleted-at tnow}
|
||||
{:id (:id font)}
|
||||
{::db/return-keys false}))
|
||||
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:id id
|
||||
:team-id team-id
|
||||
:name (:font-family (peek fonts))
|
||||
:profile-id profile-id}})))
|
||||
|
||||
;; --- DELETE FONT VARIANT
|
||||
|
||||
@@ -239,19 +248,23 @@
|
||||
(sv/defmethod ::delete-font-variant
|
||||
{::doc/added "1.18"
|
||||
::webhooks/event? true
|
||||
::sm/params schema:delete-font-variant}
|
||||
[cfg {:keys [::rpc/profile-id id team-id]}]
|
||||
(db/tx-run! cfg
|
||||
(fn [{:keys [::db/conn] :as cfg}]
|
||||
(teams/check-edition-permissions! conn profile-id team-id)
|
||||
(let [variant (db/get conn :team-font-variant
|
||||
{:id id :team-id team-id}
|
||||
{::sql/for-update true})]
|
||||
::sm/params schema:delete-font-variant
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id team-id]}]
|
||||
(let [team (teams/get-team conn
|
||||
:profile-id profile-id
|
||||
:team-id team-id)
|
||||
variant (db/get conn :team-font-variant
|
||||
{:id id :team-id team-id}
|
||||
{::sql/for-update true})
|
||||
delay (ldel/get-deletion-delay team)]
|
||||
|
||||
(db/update! conn :team-font-variant
|
||||
{:deleted-at (dt/now)}
|
||||
{:id (:id variant)})
|
||||
(teams/check-edition-permissions! (:permissions team))
|
||||
(db/update! conn :team-font-variant
|
||||
{:deleted-at (dt/in-future delay)}
|
||||
{:id (:id variant)}
|
||||
{::db/return-keys false})
|
||||
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:font-family (:font-family variant)
|
||||
:font-id (:font-id variant)}})))))
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:font-family (:font-family variant)
|
||||
:font-id (:font-id variant)}})))
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
(vswap! bfc/*state* update :index bfc/update-index fmeds :id)
|
||||
|
||||
;; Process and persist file
|
||||
(let [file (bfc/process-file file)]
|
||||
(let [file (bfc/process-file cfg file)]
|
||||
(bfc/insert-file! cfg file ::db/return-keys false)
|
||||
|
||||
;; The file profile creation is optional, so when no profile is
|
||||
|
||||
@@ -480,8 +480,7 @@
|
||||
JOIN team AS t ON (t.id = tpr.team_id)
|
||||
WHERE tpr.is_owner IS TRUE
|
||||
AND tpr.profile_id = ?
|
||||
AND (t.deleted_at IS NULL OR
|
||||
t.deleted_at > now())
|
||||
AND t.deleted_at IS NULL
|
||||
)
|
||||
SELECT tpr.team_id AS id,
|
||||
count(tpr.profile_id) - 1 AS participants
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.db :as db]
|
||||
[app.db.sql :as-alias sql]
|
||||
[app.features.logical-deletion :as ldel]
|
||||
[app.loggers.audit :as-alias audit]
|
||||
[app.loggers.webhooks :as webhooks]
|
||||
[app.rpc :as-alias rpc]
|
||||
@@ -253,9 +254,10 @@
|
||||
;; --- MUTATION: Delete Project
|
||||
|
||||
(defn- delete-project
|
||||
[conn project-id]
|
||||
(let [project (db/update! conn :project
|
||||
{:deleted-at (dt/now)}
|
||||
[conn team project-id]
|
||||
(let [delay (ldel/get-deletion-delay team)
|
||||
project (db/update! conn :project
|
||||
{:deleted-at (dt/in-future delay)}
|
||||
{:id project-id}
|
||||
{::db/return-keys true})]
|
||||
|
||||
@@ -272,7 +274,6 @@
|
||||
|
||||
project))
|
||||
|
||||
|
||||
(def ^:private schema:delete-project
|
||||
[:map {:title "delete-project"}
|
||||
[:id ::sm/uuid]])
|
||||
@@ -284,7 +285,10 @@
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn]} {:keys [::rpc/profile-id id] :as params}]
|
||||
(check-edition-permissions! conn profile-id id)
|
||||
(let [project (delete-project conn id)]
|
||||
(let [team (teams/get-team conn
|
||||
:profile-id profile-id
|
||||
:project-id id)
|
||||
project (delete-project conn team id)]
|
||||
(rph/with-meta (rph/wrap)
|
||||
{::audit/props {:team-id (:team-id project)
|
||||
:name (:name project)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
[app.db :as db]
|
||||
[app.db.sql :as sql]
|
||||
[app.email :as eml]
|
||||
[app.features.logical-deletion :as ldel]
|
||||
[app.loggers.audit :as audit]
|
||||
[app.main :as-alias main]
|
||||
[app.media :as media]
|
||||
@@ -138,7 +139,8 @@
|
||||
'~:status', CASE COALESCE(p.props->'~:subscription'->>'~:type', 'professional')
|
||||
WHEN 'professional' THEN 'active'
|
||||
ELSE COALESCE(p.props->'~:subscription'->>'~:status', 'incomplete')
|
||||
END
|
||||
END,
|
||||
'~:seats', p.props->'~:quantity'
|
||||
) AS subscription
|
||||
FROM team_profile_rel AS tp
|
||||
JOIN team AS t ON (t.id = tp.team_id)
|
||||
@@ -148,7 +150,7 @@
|
||||
ON (tpr.profile_id = p.id)
|
||||
WHERE t.deleted_at IS null
|
||||
AND tp.profile_id = ?
|
||||
ORDER BY tp.created_at ASC;")
|
||||
ORDER BY tp.created_at ASC")
|
||||
|
||||
(defn process-permissions
|
||||
[team]
|
||||
@@ -233,39 +235,43 @@
|
||||
(defn get-team
|
||||
[conn & {:keys [profile-id team-id project-id file-id] :as params}]
|
||||
|
||||
(dm/assert!
|
||||
"connection or pool is mandatory"
|
||||
(or (db/connection? conn)
|
||||
(db/pool? conn)))
|
||||
(assert (uuid? profile-id) "profile-id is mandatory")
|
||||
(assert (or (db/connection? conn)
|
||||
(db/pool? conn))
|
||||
"connection or pool is mandatory")
|
||||
|
||||
(dm/assert!
|
||||
"profile-id is mandatory"
|
||||
(uuid? profile-id))
|
||||
(let [{:keys [default-team-id] :as profile}
|
||||
(profile/get-profile conn profile-id)
|
||||
|
||||
(let [{:keys [default-team-id] :as profile} (profile/get-profile conn profile-id)
|
||||
result (cond
|
||||
(some? team-id)
|
||||
(let [sql (str "WITH teams AS (" sql:get-teams-with-permissions
|
||||
") SELECT * FROM teams WHERE id=?")]
|
||||
(db/exec-one! conn [sql default-team-id profile-id team-id]))
|
||||
sql
|
||||
(if (contains? cf/flags :subscriptions)
|
||||
sql:get-teams-with-permissions-and-subscription
|
||||
sql:get-teams-with-permissions)
|
||||
|
||||
(some? project-id)
|
||||
(let [sql (str "WITH teams AS (" sql:get-teams-with-permissions ") "
|
||||
"SELECT t.* FROM teams AS t "
|
||||
" JOIN project AS p ON (p.team_id = t.id) "
|
||||
" WHERE p.id=?")]
|
||||
(db/exec-one! conn [sql default-team-id profile-id project-id]))
|
||||
result
|
||||
(cond
|
||||
(some? team-id)
|
||||
(let [sql (str "WITH teams AS (" sql ") "
|
||||
"SELECT * FROM teams WHERE id=?")]
|
||||
(db/exec-one! conn [sql default-team-id profile-id team-id]))
|
||||
|
||||
(some? file-id)
|
||||
(let [sql (str "WITH teams AS (" sql:get-teams-with-permissions ") "
|
||||
"SELECT t.* FROM teams AS t "
|
||||
" JOIN project AS p ON (p.team_id = t.id) "
|
||||
" JOIN file AS f ON (f.project_id = p.id) "
|
||||
" WHERE f.id=?")]
|
||||
(db/exec-one! conn [sql default-team-id profile-id file-id]))
|
||||
(some? project-id)
|
||||
(let [sql (str "WITH teams AS (" sql ") "
|
||||
"SELECT t.* FROM teams AS t "
|
||||
" JOIN project AS p ON (p.team_id = t.id) "
|
||||
" WHERE p.id=?")]
|
||||
(db/exec-one! conn [sql default-team-id profile-id project-id]))
|
||||
|
||||
:else
|
||||
(throw (IllegalArgumentException. "invalid arguments")))]
|
||||
(some? file-id)
|
||||
(let [sql (str "WITH teams AS (" sql ") "
|
||||
"SELECT t.* FROM teams AS t "
|
||||
" JOIN project AS p ON (p.team_id = t.id) "
|
||||
" JOIN file AS f ON (f.project_id = p.id) "
|
||||
" WHERE f.id=?")]
|
||||
(db/exec-one! conn [sql default-team-id profile-id file-id]))
|
||||
|
||||
:else
|
||||
(throw (IllegalArgumentException. "invalid arguments")))]
|
||||
|
||||
(when-not result
|
||||
(ex/raise :type :not-found
|
||||
@@ -653,13 +659,13 @@
|
||||
|
||||
(defn- delete-team
|
||||
"Mark a team for deletion"
|
||||
[conn team-id]
|
||||
[conn {:keys [id] :as team}]
|
||||
|
||||
(let [deleted-at (dt/now)
|
||||
team (db/update! conn :team
|
||||
{:deleted-at deleted-at}
|
||||
{:id team-id}
|
||||
{::db/return-keys true})]
|
||||
(let [delay (ldel/get-deletion-delay team)
|
||||
team (db/update! conn :team
|
||||
{:deleted-at (dt/in-future delay)}
|
||||
{:id id}
|
||||
{::db/return-keys true})]
|
||||
|
||||
(when (:is-default team)
|
||||
(ex/raise :type :validation
|
||||
@@ -669,8 +675,8 @@
|
||||
(wrk/submit! {::db/conn conn
|
||||
::wrk/task :delete-object
|
||||
::wrk/params {:object :team
|
||||
:deleted-at deleted-at
|
||||
:id team-id}})
|
||||
:deleted-at (:deleted-at team)
|
||||
:id id}})
|
||||
team))
|
||||
|
||||
(def ^:private schema:delete-team
|
||||
@@ -682,12 +688,14 @@
|
||||
::sm/params schema:delete-team
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id id] :as params}]
|
||||
(let [perms (get-permissions conn profile-id id)]
|
||||
(let [team (get-team conn :profile-id profile-id :team-id id)
|
||||
perms (get team :permissions)]
|
||||
|
||||
(when-not (:is-owner perms)
|
||||
(ex/raise :type :validation
|
||||
:code :only-owner-can-delete-team))
|
||||
|
||||
(delete-team conn id)
|
||||
(delete-team conn team)
|
||||
nil))
|
||||
|
||||
;; --- Mutation: Team Update Role
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
(ns app.rpc.commands.viewer
|
||||
(:require
|
||||
[app.binfile.common :as bfc]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.schema :as sm]
|
||||
@@ -78,7 +79,7 @@
|
||||
:always
|
||||
(update :data select-keys [:id :options :pages :pages-index :components]))
|
||||
|
||||
libs (->> (files/get-file-libraries conn file-id)
|
||||
libs (->> (bfc/get-file-libraries conn file-id)
|
||||
(mapv (fn [{:keys [id] :as lib}]
|
||||
(merge lib (files/get-file cfg id)))))
|
||||
|
||||
|
||||
88
backend/src/app/srepl/fixes/lost_colors.clj
Normal file
88
backend/src/app/srepl/fixes/lost_colors.clj
Normal file
@@ -0,0 +1,88 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.srepl.fixes.lost-colors
|
||||
"A collection of adhoc fixes scripts."
|
||||
(:require
|
||||
[app.binfile.common :as bfc]
|
||||
[app.common.logging :as l]
|
||||
[app.common.types.color :as types.color]
|
||||
[app.db :as db]
|
||||
[app.srepl.helpers :as h]))
|
||||
|
||||
(def sql:get-affected-files
|
||||
"SELECT fm.file_id AS id FROM file_migration AS fm WHERE fm.name = '0008-fix-library-colors-v2'")
|
||||
|
||||
(def sql:get-matching-snapshot
|
||||
"SELECT * FROM file_change
|
||||
WHERE file_id = ?
|
||||
AND created_at <= ?
|
||||
AND label IS NOT NULL
|
||||
AND data IS NOT NULL
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 2")
|
||||
|
||||
(defn get-affected-migration
|
||||
[conn file-id]
|
||||
(db/get* conn :file-migration
|
||||
{:name "0008-fix-library-colors-v2"
|
||||
:file-id file-id}))
|
||||
|
||||
(defn get-last-valid-snapshot
|
||||
[conn migration]
|
||||
(let [[snapshot] (db/exec! conn [sql:get-matching-snapshot
|
||||
(:file-id migration)
|
||||
(:created-at migration)])]
|
||||
(when snapshot
|
||||
(let [snapshot (assoc snapshot :id (:file-id snapshot))]
|
||||
(bfc/decode-file h/*system* snapshot)))))
|
||||
|
||||
(defn restore-color
|
||||
[{:keys [data] :as snapshot} color]
|
||||
(when-let [scolor (get-in data [:colors (:id color)])]
|
||||
(-> (select-keys scolor types.color/library-color-attrs)
|
||||
(types.color/check-library-color))))
|
||||
|
||||
(defn restore-missing-colors
|
||||
[{:keys [id] :as file} & _opts]
|
||||
(l/inf :hint "process file" :file-id (str id) :name (:name file) :has-colors (-> file :data :colors not-empty boolean))
|
||||
(if-let [colors (-> file :data :colors not-empty)]
|
||||
(let [migration (get-affected-migration h/*system* id)]
|
||||
(if-let [snapshot (get-last-valid-snapshot h/*system* migration)]
|
||||
(do
|
||||
(l/inf :hint "using snapshot" :snapshot (:label snapshot))
|
||||
(let [colors (reduce-kv (fn [colors color-id color]
|
||||
(if-let [result (restore-color snapshot color)]
|
||||
(do
|
||||
(l/inf :hint "restored color" :file-id (str id) :color-id (str color-id))
|
||||
(assoc colors color-id result))
|
||||
(do
|
||||
(l/wrn :hint "ignoring color" :file-id (str id) :color (pr-str color))
|
||||
colors)))
|
||||
colors
|
||||
colors)
|
||||
file (-> file
|
||||
(update :data assoc :colors colors)
|
||||
(update :migrations disj "0008-fix-library-colors-v2"))]
|
||||
|
||||
(db/delete! h/*system* :file-migration
|
||||
{:name "0008-fix-library-colors-v2"
|
||||
:file-id (:id file)})
|
||||
file))
|
||||
|
||||
(do
|
||||
(db/delete! h/*system* :file-migration
|
||||
{:name "0008-fix-library-colors-v2"
|
||||
:file-id (:id file)})
|
||||
nil)))
|
||||
|
||||
(do
|
||||
(db/delete! h/*system* :file-migration
|
||||
{:name "0008-fix-library-colors-v2"
|
||||
:file-id (:id file)})
|
||||
nil)))
|
||||
|
||||
|
||||
@@ -146,13 +146,9 @@
|
||||
|
||||
(defn process-file!
|
||||
[system file-id update-fn & {:keys [label validate? with-libraries?] :or {validate? true} :as opts}]
|
||||
(let [conn (db/get-connection system)
|
||||
file (bfc/get-file system file-id ::db/for-update true)
|
||||
(let [file (bfc/get-file system file-id ::db/for-update true)
|
||||
libs (when with-libraries?
|
||||
(->> (files/get-file-libraries conn file-id)
|
||||
(into [file] (map (fn [{:keys [id]}]
|
||||
(bfc/get-file system id))))
|
||||
(d/index-by :id)))
|
||||
(bfc/get-resolved-file-libraries system file))
|
||||
|
||||
file' (when file
|
||||
(if with-libraries?
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
[app.common.files.validate :as cfv]
|
||||
[app.common.logging :as l]
|
||||
[app.common.pprint :as p]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
@@ -390,14 +391,27 @@
|
||||
[file-id]
|
||||
(let [file-id (h/parse-uuid file-id)]
|
||||
(db/tx-run! (assoc main/system ::db/rollback true)
|
||||
(fn [{:keys [::db/conn] :as system}]
|
||||
(let [file (h/get-file system file-id)
|
||||
libs (->> (files/get-file-libraries conn file-id)
|
||||
(into [file] (map (fn [{:keys [id]}]
|
||||
(h/get-file system id))))
|
||||
(d/index-by :id))]
|
||||
(fn [system]
|
||||
(let [file (bfc/get-file system file-id)
|
||||
libs (bfc/get-resolved-file-libraries system file)]
|
||||
(cfv/validate-file file libs))))))
|
||||
|
||||
(defn validate-file-schema
|
||||
"Validate structure, referencial integrity and semantic coherence of
|
||||
all contents of a file. Returns a list of errors."
|
||||
[file-id]
|
||||
(let [file-id (h/parse-uuid file-id)]
|
||||
(db/tx-run! (assoc main/system ::db/rollback true)
|
||||
(fn [system]
|
||||
(try
|
||||
(let [file (bfc/get-file system file-id)]
|
||||
(cfv/validate-file-schema! file)
|
||||
(println "OK"))
|
||||
(catch Exception cause
|
||||
(if-let [explain (-> cause ex-data ::sm/explain)]
|
||||
(println (sm/humanize-explain explain))
|
||||
(ex/print-throwable cause))))))))
|
||||
|
||||
(defn repair-file!
|
||||
"Repair the list of errors detected by validation."
|
||||
[file-id & {:keys [rollback?] :or {rollback? true} :as opts}]
|
||||
@@ -477,7 +491,8 @@
|
||||
:index idx)
|
||||
(let [system (assoc main/system ::db/rollback rollback?)]
|
||||
(db/tx-run! system (fn [system]
|
||||
(binding [h/*system* system]
|
||||
(binding [h/*system* system
|
||||
db/*conn* (db/get-connection system)]
|
||||
(h/process-file! system file-id update-fn opts)))))
|
||||
|
||||
(catch Throwable cause
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
of deleted or unreachable objects."
|
||||
(:require
|
||||
[app.common.logging :as l]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.storage :as sto]
|
||||
[app.util.time :as dt]
|
||||
@@ -18,15 +17,15 @@
|
||||
(def ^:private sql:get-profiles
|
||||
"SELECT id, photo_id FROM profile
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-profiles!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-profiles min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-profiles deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id photo-id]}]
|
||||
(l/trc :hint "permanently delete" :rel "profile" :id (str id))
|
||||
|
||||
@@ -41,15 +40,15 @@
|
||||
(def ^:private sql:get-teams
|
||||
"SELECT deleted_at, id, photo_id FROM team
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-teams!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-teams min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-teams deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id photo-id deleted-at]}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "team"
|
||||
@@ -69,15 +68,15 @@
|
||||
"SELECT id, team_id, deleted_at, woff1_file_id, woff2_file_id, otf_file_id, ttf_file_id
|
||||
FROM team_font_variant
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-fonts!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-fonts min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-fonts deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id team-id deleted-at] :as font}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "team-font-variant"
|
||||
@@ -101,15 +100,15 @@
|
||||
"SELECT id, deleted_at, team_id
|
||||
FROM project
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-projects!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-projects min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-projects deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id team-id deleted-at]}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "project"
|
||||
@@ -127,15 +126,15 @@
|
||||
"SELECT id, deleted_at, project_id, data_backend, data_ref_id
|
||||
FROM file
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-files!
|
||||
[{:keys [::db/conn ::sto/storage ::min-age ::chunk-size] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-files min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::sto/storage ::deletion-threshold ::chunk-size] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-files deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id deleted-at project-id] :as file}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "file"
|
||||
@@ -156,15 +155,15 @@
|
||||
"SELECT file_id, revn, media_id, deleted_at
|
||||
FROM file_thumbnail
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn delete-file-thumbnails!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-thumbnails min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-thumbnails deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [file-id revn media-id deleted-at]}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "file-thumbnail"
|
||||
@@ -185,15 +184,15 @@
|
||||
"SELECT file_id, object_id, media_id, deleted_at
|
||||
FROM file_tagged_object_thumbnail
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn delete-file-object-thumbnails!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-object-thumbnails min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-object-thumbnails deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [file-id object-id media-id deleted-at]}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "file-tagged-object-thumbnail"
|
||||
@@ -214,15 +213,15 @@
|
||||
"SELECT file_id, id, deleted_at, data_ref_id
|
||||
FROM file_data_fragment
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-file-data-fragments!
|
||||
[{:keys [::db/conn ::sto/storage ::min-age ::chunk-size] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-data-fragments min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::sto/storage ::deletion-threshold ::chunk-size] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-data-fragments deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [file-id id deleted-at data-ref-id]}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "file-data-fragment"
|
||||
@@ -240,15 +239,15 @@
|
||||
"SELECT id, file_id, media_id, thumbnail_id, deleted_at
|
||||
FROM file_media_object
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-file-media-objects!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-media-objects min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-media-objects deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id file-id deleted-at] :as fmo}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "file-media-object"
|
||||
@@ -269,15 +268,15 @@
|
||||
"SELECT id, file_id, deleted_at, data_backend, data_ref_id
|
||||
FROM file_change
|
||||
WHERE deleted_at IS NOT NULL
|
||||
AND deleted_at < now() - ?::interval
|
||||
AND deleted_at < now() + ?::interval
|
||||
ORDER BY deleted_at ASC
|
||||
LIMIT ?
|
||||
FOR UPDATE
|
||||
SKIP LOCKED")
|
||||
|
||||
(defn- delete-file-changes!
|
||||
[{:keys [::db/conn ::min-age ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-change min-age chunk-size] {:fetch-size 5})
|
||||
[{:keys [::db/conn ::deletion-threshold ::chunk-size ::sto/storage] :as cfg}]
|
||||
(->> (db/plan conn [sql:get-file-change deletion-threshold chunk-size] {:fetch-size 5})
|
||||
(reduce (fn [total {:keys [id file-id deleted-at] :as xlog}]
|
||||
(l/trc :hint "permanently delete"
|
||||
:rel "file-change"
|
||||
@@ -324,16 +323,13 @@
|
||||
|
||||
(defmethod ig/expand-key ::handler
|
||||
[k v]
|
||||
{k (assoc v
|
||||
::min-age (cf/get-deletion-delay)
|
||||
::chunk-size 100)})
|
||||
{k (assoc v ::chunk-size 100)})
|
||||
|
||||
(defmethod ig/init-key ::handler
|
||||
[_ cfg]
|
||||
(fn [{:keys [props] :as task}]
|
||||
(let [min-age (dt/duration (or (:min-age props) (::min-age cfg)))
|
||||
cfg (assoc cfg ::min-age (db/interval min-age))]
|
||||
|
||||
(let [threshold (dt/duration (get props :deletion-threshold 0))
|
||||
cfg (assoc cfg ::deletion-threshold (db/interval threshold))]
|
||||
(loop [procs (map deref deletion-proc-vars)
|
||||
total 0]
|
||||
(if-let [proc-fn (first procs)]
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
([params]
|
||||
(mark-file-deleted* *system* params))
|
||||
([conn {:keys [id] :as params}]
|
||||
(#'files/mark-file-deleted conn id)))
|
||||
(#'files/mark-file-deleted conn {} id)))
|
||||
|
||||
(defn create-team*
|
||||
([i params] (create-team* *system* i params))
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
(:require
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.thumbnails :as thc]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.db.sql :as sql]
|
||||
[app.http :as http]
|
||||
@@ -123,8 +123,27 @@
|
||||
:components-v2 true}
|
||||
out (th/command! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:error out)))
|
||||
|
||||
(let [result (:result out)]
|
||||
(t/is (some? (:deleted-at result)))
|
||||
(t/is (= file-id (:id result)))
|
||||
(t/is (= "new name" (:name result)))
|
||||
(t/is (= 1 (count (get-in result [:data :pages]))))
|
||||
(t/is (nil? (:users result))))))
|
||||
|
||||
(th/db-update! :file
|
||||
{:deleted-at (dt/now)}
|
||||
{:id file-id})
|
||||
|
||||
(t/testing "query single file after delete and wait"
|
||||
(let [data {::th/type :get-file
|
||||
::rpc/profile-id (:id prof)
|
||||
:id file-id
|
||||
:components-v2 true}
|
||||
out (th/command! data)]
|
||||
(let [error (:error out)
|
||||
error-data (ex-data error)]
|
||||
(t/is (th/ex-info? error))
|
||||
@@ -195,7 +214,7 @@
|
||||
(t/is (= 5 (count rows))))
|
||||
|
||||
;; The objects-gc should remove unused fragments
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 3 (:processed res))))
|
||||
|
||||
;; Check the number of fragments
|
||||
@@ -230,7 +249,7 @@
|
||||
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
|
||||
|
||||
;; The objects-gc should remove unused fragments
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 3 (:processed res))))
|
||||
|
||||
;; Check the number of fragments;
|
||||
@@ -254,7 +273,7 @@
|
||||
(t/is (= 4 (count rows)))
|
||||
(t/is (= 2 (count (remove (comp some? :deleted-at) rows)))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)})]
|
||||
@@ -324,8 +343,9 @@
|
||||
:name "image"
|
||||
:frame-id uuid/zero
|
||||
:parent-id uuid/zero
|
||||
:type :image
|
||||
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}})}])
|
||||
:type :rect
|
||||
:fills [{:fill-opacity 1
|
||||
:fill-image {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
|
||||
|
||||
;; Check that reference storage objects on filemediaobjects
|
||||
;; are the same because of deduplication feature.
|
||||
@@ -355,7 +375,7 @@
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (= 1 (count (remove (comp some? :deleted-at) rows)))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 3 (:processed res))))
|
||||
|
||||
;; check file media objects
|
||||
@@ -386,7 +406,7 @@
|
||||
|
||||
;; This only clears fragments, the file media objects still referenced because
|
||||
;; snapshots are preserved
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
;; Mark all snapshots to be a non-snapshot file change
|
||||
@@ -395,7 +415,7 @@
|
||||
|
||||
;; Rerun the file-gc and objects-gc
|
||||
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
;; Now that file-gc have deleted the file-media-object usage,
|
||||
@@ -443,7 +463,8 @@
|
||||
fmo3 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
fmo4 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
fmo5 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
|
||||
s-shid (uuid/random)
|
||||
s1-shid (uuid/random)
|
||||
s2-shid (uuid/random)
|
||||
t-shid (uuid/random)
|
||||
|
||||
page-id (first (get-in file [:data :pages]))]
|
||||
@@ -462,19 +483,31 @@
|
||||
:changes
|
||||
[{:type :add-obj
|
||||
:page-id page-id
|
||||
:id s-shid
|
||||
:id s1-shid
|
||||
:parent-id uuid/zero
|
||||
:frame-id uuid/zero
|
||||
:components-v2 true
|
||||
:obj (cts/setup-shape
|
||||
{:id s-shid
|
||||
{:id s1-shid
|
||||
:name "image"
|
||||
:frame-id uuid/zero
|
||||
:parent-id uuid/zero
|
||||
:type :image
|
||||
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}
|
||||
:fills [{:opacity 1 :fill-image {:id (:id fmo2) :width 100 :height 100 :mtype "image/jpeg"}}]
|
||||
:strokes [{:opacity 1 :stroke-image {:id (:id fmo3) :width 100 :height 100 :mtype "image/jpeg"}}]})}
|
||||
:type :rect
|
||||
:fills [{:fill-opacity 1 :fill-image {:id (:id fmo2) :width 101 :height 100 :mtype "image/jpeg"}}]
|
||||
:strokes [{:stroke-opacity 1 :stroke-image {:id (:id fmo3) :width 102 :height 100 :mtype "image/jpeg"}}]})}
|
||||
{:type :add-obj
|
||||
:page-id page-id
|
||||
:id s2-shid
|
||||
:parent-id uuid/zero
|
||||
:frame-id uuid/zero
|
||||
:components-v2 true
|
||||
:obj (cts/setup-shape
|
||||
{:id s2-shid
|
||||
:name "image"
|
||||
:frame-id uuid/zero
|
||||
:parent-id uuid/zero
|
||||
:type :rect
|
||||
:fills [{:fill-opacity 1 :fill-image {:id (:id fmo1) :width 103 :height 100 :mtype "image/jpeg"}}]})}
|
||||
{:type :add-obj
|
||||
:page-id page-id
|
||||
:id t-shid
|
||||
@@ -500,7 +533,8 @@
|
||||
{:fills [{:fill-opacity 1
|
||||
:fill-color "#000000"}]
|
||||
:text "bye"}]}]}]}
|
||||
:strokes [{:opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
|
||||
:strokes [{:stroke-opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
|
||||
|
||||
|
||||
;; run the file-gc task immediately without forced min-age
|
||||
(t/is (false? (th/run-task! :file-gc {:file-id (:id file)})))
|
||||
@@ -508,7 +542,7 @@
|
||||
;; run the task again
|
||||
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)
|
||||
@@ -538,10 +572,13 @@
|
||||
:vern 0
|
||||
:changes [{:type :del-obj
|
||||
:page-id (first (get-in file [:data :pages]))
|
||||
:id s-shid}
|
||||
:id s1-shid}
|
||||
{:type :del-obj
|
||||
:page-id (first (get-in file [:data :pages]))
|
||||
:id t-shid}])
|
||||
:id t-shid}
|
||||
{:type :del-obj
|
||||
:page-id (first (get-in file [:data :pages]))
|
||||
:id s2-shid}])
|
||||
|
||||
;; Now, we have deleted the usage of pointers to the
|
||||
;; file-media-objects, if we paste file-gc, they should be marked
|
||||
@@ -550,7 +587,7 @@
|
||||
|
||||
;; This only removes unused fragments, file media are still
|
||||
;; referenced on snapshots.
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
;; Mark all snapshots to be a non-snapshot file change
|
||||
@@ -560,7 +597,7 @@
|
||||
;; Rerun file-gc and objects-gc task for the same file once all snapshots are
|
||||
;; "expired/deleted"
|
||||
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 6 (:processed res))))
|
||||
|
||||
(let [rows (th/db-query :file-data-fragment {:file-id (:id file)
|
||||
@@ -712,7 +749,7 @@
|
||||
;; Now that file-gc have marked for deletion the object
|
||||
;; thumbnail lets execute the objects-gc task which remove
|
||||
;; the rows and mark as touched the storage object rows
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 5 (:processed res))))
|
||||
|
||||
;; Now that objects-gc have deleted the object thumbnail lets
|
||||
@@ -741,7 +778,7 @@
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (= 0 (count (remove (comp some? :deleted-at) rows)))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
;; (pp/pprint res)
|
||||
(t/is (= 3 (:processed res))))
|
||||
|
||||
@@ -876,7 +913,7 @@
|
||||
:profile-id (:id profile1)})]
|
||||
;; file is not deleted because it does not meet all
|
||||
;; conditions to be deleted.
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; query the list of files
|
||||
@@ -907,7 +944,7 @@
|
||||
(t/is (= 0 (count result)))))
|
||||
|
||||
;; run permanent deletion (should be noop)
|
||||
(let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; query the list of file libraries of a after hard deletion
|
||||
@@ -921,7 +958,7 @@
|
||||
(t/is (= 0 (count result)))))
|
||||
|
||||
;; run permanent deletion
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 1 (:processed result))))
|
||||
|
||||
;; query the list of file libraries of a after hard deletion
|
||||
@@ -1176,7 +1213,7 @@
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (= 1 (count (remove :deleted-at rows)))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 4 (:processed res))))
|
||||
|
||||
(let [rows (th/db-query :file-tagged-object-thumbnail {:file-id (:id file)})]
|
||||
@@ -1232,7 +1269,7 @@
|
||||
(t/is (= 2 (count rows)))
|
||||
(t/is (= 1 (count (remove (comp some? :deleted-at) rows)))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
(let [rows (th/db-query :file-thumbnail {:file-id (:id file)})]
|
||||
@@ -1251,7 +1288,7 @@
|
||||
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
|
||||
|
||||
;; Preventive objects-gc
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 1 (:processed result))))
|
||||
|
||||
;; Check the number of fragments before adding the page
|
||||
@@ -1272,7 +1309,7 @@
|
||||
(th/run-pending-tasks!))
|
||||
|
||||
;; Clean objects after file-gc
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 1 (:processed result))))
|
||||
|
||||
;; Check the number of fragments before adding the page
|
||||
@@ -1324,7 +1361,7 @@
|
||||
(t/is (true? (th/run-task! :file-gc {:min-age 0 :file-id (:id file)})))
|
||||
|
||||
;; The objects-gc should remove unused fragments
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
;; Check the number of fragments before adding the page
|
||||
@@ -1821,8 +1858,7 @@
|
||||
(t/is (= (:id file-2) (:file-id (get rows 1))))
|
||||
(t/is (nil? (:deleted-at (get rows 1)))))
|
||||
|
||||
(th/run-task! :objects-gc
|
||||
{:min-age 0})
|
||||
(th/run-task! :objects-gc {})
|
||||
|
||||
(let [rows (th/db-exec! ["SELECT * FROM file_media_object ORDER BY created_at ASC"])]
|
||||
(t/is (= 1 (count rows)))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns backend-tests.rpc-font-test
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
@@ -144,7 +145,7 @@
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 2 (:processed res))))
|
||||
|
||||
(let [res (th/run-task! :storage-gc-touched {:min-age 0})]
|
||||
@@ -204,7 +205,7 @@
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 1 (:processed res))))
|
||||
|
||||
(let [res (th/run-task! :storage-gc-touched {:min-age 0})]
|
||||
@@ -263,7 +264,7 @@
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res))))
|
||||
|
||||
(let [res (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [res (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 1 (:processed res))))
|
||||
|
||||
(let [res (th/run-task! :storage-gc-touched {:min-age 0})]
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
(t/is (sto/object? mobj1))
|
||||
(t/is (sto/object? mobj2))
|
||||
(t/is (= 122785 (:size mobj1)))
|
||||
(t/is (= 3299 (:size mobj2)))))))
|
||||
(t/is (= 3297 (:size mobj2)))))))
|
||||
|
||||
(t/deftest media-object-upload
|
||||
(let [prof (th/create-profile* 1)
|
||||
@@ -85,7 +85,7 @@
|
||||
(t/is (sto/object? mobj1))
|
||||
(t/is (sto/object? mobj2))
|
||||
(t/is (= 312043 (:size mobj1)))
|
||||
(t/is (= 3901 (:size mobj2)))))))
|
||||
(t/is (= 3890 (:size mobj2)))))))
|
||||
|
||||
|
||||
(t/deftest media-object-upload-idempotency
|
||||
@@ -163,7 +163,7 @@
|
||||
(t/is (sto/object? mobj1))
|
||||
(t/is (sto/object? mobj2))
|
||||
(t/is (= 122785 (:size mobj1)))
|
||||
(t/is (= 3299 (:size mobj2)))))))
|
||||
(t/is (= 3297 (:size mobj2)))))))
|
||||
|
||||
(t/deftest media-object-upload-command
|
||||
(let [prof (th/create-profile* 1)
|
||||
@@ -200,7 +200,7 @@
|
||||
(t/is (sto/object? mobj1))
|
||||
(t/is (sto/object? mobj2))
|
||||
(t/is (= 312043 (:size mobj1)))
|
||||
(t/is (= 3901 (:size mobj2)))))))
|
||||
(t/is (= 3890 (:size mobj2)))))))
|
||||
|
||||
|
||||
(t/deftest media-object-upload-idempotency-command
|
||||
|
||||
@@ -209,16 +209,16 @@
|
||||
::rpc/profile-id (:id prof1)
|
||||
:id (:id team1)}
|
||||
out (th/command! params)]
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
|
||||
(let [team (th/db-get :team {:id (:id team1)} {::db/remove-deleted false})]
|
||||
(t/is (dt/instant? (:deleted-at team)))))
|
||||
|
||||
;; Request profile to be deleted
|
||||
;; Request profile to be deleted
|
||||
(let [params {::th/type :delete-profile
|
||||
::rpc/profile-id (:id prof1)}
|
||||
out (th/command! params)]
|
||||
;; (th/print-result! out)
|
||||
;; (th/print-result! out)
|
||||
(t/is (nil? (:result out)))
|
||||
(t/is (nil? (:error out)))))))
|
||||
|
||||
@@ -379,15 +379,14 @@
|
||||
(t/deftest prepare-register-and-register-profile-1
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:email "user@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)
|
||||
token (get-in out [:result :token])]
|
||||
(t/is (string? token))
|
||||
|
||||
;; try register without token
|
||||
(let [data {::th/type :register-profile
|
||||
:fullname "foobar"
|
||||
:accept-terms-and-privacy true}
|
||||
(let [data {::th/type :register-profile}
|
||||
out (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
(let [error (:error out)]
|
||||
@@ -398,11 +397,8 @@
|
||||
;; try correct register
|
||||
(let [data {::th/type :register-profile
|
||||
:token token
|
||||
:fullname "foobar"
|
||||
:utm_campaign "utma"
|
||||
:mtm_campaign "mtma"
|
||||
:accept-terms-and-privacy true
|
||||
:accept-newsletter-subscription true}]
|
||||
:mtm_campaign "mtma"}]
|
||||
(let [{:keys [result error]} (th/command! data)]
|
||||
(t/is (nil? error))))
|
||||
|
||||
@@ -424,6 +420,7 @@
|
||||
;; PREPARE REGISTER
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:email "hello@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)
|
||||
token (get-in out [:result :token])]
|
||||
@@ -432,10 +429,7 @@
|
||||
|
||||
;; DO REGISTRATION
|
||||
(let [data {::th/type :register-profile
|
||||
:token @current-token
|
||||
:fullname "foobar"
|
||||
:accept-terms-and-privacy true
|
||||
:accept-newsletter-subscription true}
|
||||
:token @current-token}
|
||||
out (th/command! data)]
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= 1 (:call-count @mock))))
|
||||
@@ -445,6 +439,7 @@
|
||||
;; PREPARE REGISTER: second attempt
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:email "hello@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)
|
||||
token (get-in out [:result :token])]
|
||||
@@ -479,6 +474,7 @@
|
||||
;; PREPARE REGISTER
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:email "hello@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)
|
||||
token (get-in out [:result :token])]
|
||||
@@ -487,10 +483,7 @@
|
||||
|
||||
;; DO REGISTRATION
|
||||
(let [data {::th/type :register-profile
|
||||
:token @current-token
|
||||
:fullname "foobar"
|
||||
:accept-terms-and-privacy true
|
||||
:accept-newsletter-subscription true}
|
||||
:token @current-token}
|
||||
out (th/command! data)]
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= 1 (:call-count @mock))))
|
||||
@@ -504,6 +497,7 @@
|
||||
;; PREPARE REGISTER: second attempt
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:email "hello@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)
|
||||
token (get-in out [:result :token])]
|
||||
@@ -514,10 +508,7 @@
|
||||
:return true}]
|
||||
;; DO REGISTRATION: second attempt
|
||||
(let [data {::th/type :register-profile
|
||||
:token @current-token
|
||||
:fullname "foobar"
|
||||
:accept-terms-and-privacy true
|
||||
:accept-newsletter-subscription true}
|
||||
:token @current-token}
|
||||
out (th/command! data)]
|
||||
(t/is (nil? (:error out)))
|
||||
(t/is (= 0 (:call-count @mock))))))))
|
||||
@@ -532,6 +523,7 @@
|
||||
:member-email "user@example.com"})
|
||||
data {::th/type :prepare-register-profile
|
||||
:invitation-token itoken
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "foobar"}
|
||||
|
||||
@@ -542,8 +534,7 @@
|
||||
|
||||
(let [rtoken (:token result)
|
||||
data {::th/type :register-profile
|
||||
:token rtoken
|
||||
:fullname "foobar"}
|
||||
:token rtoken}
|
||||
|
||||
{:keys [result error] :as out} (th/command! data)]
|
||||
;; (th/print-result! out)
|
||||
@@ -563,6 +554,7 @@
|
||||
data {::th/type :prepare-register-profile
|
||||
:invitation-token itoken
|
||||
:email "user@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)]
|
||||
|
||||
@@ -582,6 +574,7 @@
|
||||
:member-email "user@example.com"})
|
||||
data {::th/type :prepare-register-profile
|
||||
:invitation-token itoken
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "foobar"}
|
||||
out (th/command! data)]
|
||||
@@ -604,6 +597,7 @@
|
||||
data {::th/type :prepare-register-profile
|
||||
:invitation-token itoken
|
||||
:email "user@example.com"
|
||||
:fullname "foobar"
|
||||
:password "foobar"}
|
||||
out (th/command! data)]
|
||||
|
||||
@@ -624,6 +618,7 @@
|
||||
|
||||
data {::th/type :prepare-register-profile
|
||||
:invitation-token itoken
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "foobar"}
|
||||
out (th/command! data)]
|
||||
@@ -636,6 +631,7 @@
|
||||
(t/deftest prepare-register-with-registration-disabled
|
||||
(with-redefs [app.config/flags #{}]
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "foobar"}
|
||||
out (th/command! data)]
|
||||
@@ -648,6 +644,7 @@
|
||||
(t/deftest prepare-register-with-existing-user
|
||||
(let [profile (th/create-profile* 1)
|
||||
data {::th/type :prepare-register-profile
|
||||
:fullname "foobar"
|
||||
:email (:email profile)
|
||||
:password "foobar"}
|
||||
out (th/command! data)]
|
||||
@@ -660,6 +657,7 @@
|
||||
|
||||
(let [pool (:app.db/pool th/*system*)
|
||||
data {::th/type :prepare-register-profile
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "foobar"}]
|
||||
|
||||
@@ -674,6 +672,7 @@
|
||||
(t/deftest register-profile-with-complained-email
|
||||
(let [pool (:app.db/pool th/*system*)
|
||||
data {::th/type :prepare-register-profile
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "foobar"}]
|
||||
|
||||
@@ -688,6 +687,7 @@
|
||||
|
||||
(t/deftest register-profile-with-email-as-password
|
||||
(let [data {::th/type :prepare-register-profile
|
||||
:fullname "foobar"
|
||||
:email "user@example.com"
|
||||
:password "USER@example.com"}
|
||||
out (th/command! data)]
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns backend-tests.rpc-project-test
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
@@ -178,7 +179,7 @@
|
||||
|
||||
;; project is not deleted because it does not meet all
|
||||
;; conditions to be deleted.
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; query the list of projects
|
||||
@@ -210,7 +211,7 @@
|
||||
(t/is (= 1 (count result)))))
|
||||
|
||||
;; run permanent deletion (should be noop)
|
||||
(let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; query the list of files of a after soft deletion
|
||||
@@ -224,7 +225,7 @@
|
||||
(t/is (= 0 (count result)))))
|
||||
|
||||
;; run permanent deletion
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 1 (:processed result))))
|
||||
|
||||
;; query the list of files of a after hard deletion
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.common.logging :as l]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.db :as db]
|
||||
[app.http :as http]
|
||||
[app.rpc :as-alias rpc]
|
||||
@@ -476,7 +477,7 @@
|
||||
|
||||
;; team is not deleted because it does not meet all
|
||||
;; conditions to be deleted.
|
||||
(let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; query the list of teams
|
||||
@@ -510,7 +511,7 @@
|
||||
(th/run-pending-tasks!)
|
||||
|
||||
;; run permanent deletion (should be noop)
|
||||
(let [result (th/run-task! :objects-gc {:min-age (dt/duration {:minutes 1})})]
|
||||
(let [result (th/run-task! :objects-gc {})]
|
||||
(t/is (= 0 (:processed result))))
|
||||
|
||||
;; query the list of projects after hard deletion
|
||||
@@ -524,7 +525,7 @@
|
||||
(t/is (= :not-found (:type edata)))))
|
||||
|
||||
;; run permanent deletion
|
||||
(let [result (th/run-task! :objects-gc {:min-age (dt/duration 0)})]
|
||||
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 2 (:processed result))))
|
||||
|
||||
;; query the list of projects of a after hard deletion
|
||||
@@ -538,7 +539,6 @@
|
||||
(let [edata (-> out :error ex-data)]
|
||||
(t/is (= :not-found (:type edata)))))))
|
||||
|
||||
|
||||
(t/deftest team-deletion-2
|
||||
(let [storage (-> (:app.storage/storage th/*system*)
|
||||
(assoc ::sto/backend :assets-fs))
|
||||
@@ -581,7 +581,7 @@
|
||||
(t/is (= 1 (count rows)))
|
||||
(t/is (dt/instant? (:deleted-at (first rows)))))
|
||||
|
||||
(let [result (th/run-task! :objects-gc {:min-age 0})]
|
||||
(let [result (th/run-task! :objects-gc {:deletion-threshold (cf/get-deletion-delay)})]
|
||||
(t/is (= 5 (:processed result))))))
|
||||
|
||||
(t/deftest create-team-access-request
|
||||
|
||||
@@ -19,25 +19,34 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@npmcli/agent@npm:^2.0.0":
|
||||
version: 2.2.2
|
||||
resolution: "@npmcli/agent@npm:2.2.2"
|
||||
"@isaacs/fs-minipass@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "@isaacs/fs-minipass@npm:4.0.1"
|
||||
dependencies:
|
||||
minipass: "npm:^7.0.4"
|
||||
checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@npmcli/agent@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "@npmcli/agent@npm:3.0.0"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.1.0"
|
||||
http-proxy-agent: "npm:^7.0.0"
|
||||
https-proxy-agent: "npm:^7.0.1"
|
||||
lru-cache: "npm:^10.0.1"
|
||||
socks-proxy-agent: "npm:^8.0.3"
|
||||
checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae
|
||||
checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@npmcli/fs@npm:^3.1.0":
|
||||
version: 3.1.1
|
||||
resolution: "@npmcli/fs@npm:3.1.1"
|
||||
"@npmcli/fs@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "@npmcli/fs@npm:4.0.0"
|
||||
dependencies:
|
||||
semver: "npm:^7.3.5"
|
||||
checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99
|
||||
checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -48,29 +57,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"abbrev@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "abbrev@npm:2.0.0"
|
||||
checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372
|
||||
"abbrev@npm:^3.0.0":
|
||||
version: 3.0.1
|
||||
resolution: "abbrev@npm:3.0.1"
|
||||
checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1":
|
||||
version: 7.1.1
|
||||
resolution: "agent-base@npm:7.1.1"
|
||||
dependencies:
|
||||
debug: "npm:^4.3.4"
|
||||
checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aggregate-error@npm:^3.0.0":
|
||||
version: 3.1.0
|
||||
resolution: "aggregate-error@npm:3.1.0"
|
||||
dependencies:
|
||||
clean-stack: "npm:^2.0.0"
|
||||
indent-string: "npm:^4.0.0"
|
||||
checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039
|
||||
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
|
||||
version: 7.1.3
|
||||
resolution: "agent-base@npm:7.1.3"
|
||||
checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -82,9 +79,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"ansi-regex@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "ansi-regex@npm:6.0.1"
|
||||
checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08
|
||||
version: 6.1.0
|
||||
resolution: "ansi-regex@npm:6.1.0"
|
||||
checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -141,21 +138,21 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^1.1.7":
|
||||
version: 1.1.11
|
||||
resolution: "brace-expansion@npm:1.1.11"
|
||||
version: 1.1.12
|
||||
resolution: "brace-expansion@npm:1.1.12"
|
||||
dependencies:
|
||||
balanced-match: "npm:^1.0.0"
|
||||
concat-map: "npm:0.0.1"
|
||||
checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668
|
||||
checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"brace-expansion@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "brace-expansion@npm:2.0.1"
|
||||
version: 2.0.2
|
||||
resolution: "brace-expansion@npm:2.0.2"
|
||||
dependencies:
|
||||
balanced-match: "npm:^1.0.0"
|
||||
checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f
|
||||
checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -175,11 +172,11 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cacache@npm:^18.0.0":
|
||||
version: 18.0.3
|
||||
resolution: "cacache@npm:18.0.3"
|
||||
"cacache@npm:^19.0.1":
|
||||
version: 19.0.1
|
||||
resolution: "cacache@npm:19.0.1"
|
||||
dependencies:
|
||||
"@npmcli/fs": "npm:^3.1.0"
|
||||
"@npmcli/fs": "npm:^4.0.0"
|
||||
fs-minipass: "npm:^3.0.0"
|
||||
glob: "npm:^10.2.2"
|
||||
lru-cache: "npm:^10.0.1"
|
||||
@@ -187,11 +184,11 @@ __metadata:
|
||||
minipass-collect: "npm:^2.0.1"
|
||||
minipass-flush: "npm:^1.0.5"
|
||||
minipass-pipeline: "npm:^1.2.4"
|
||||
p-map: "npm:^4.0.0"
|
||||
ssri: "npm:^10.0.0"
|
||||
tar: "npm:^6.1.11"
|
||||
unique-filename: "npm:^3.0.0"
|
||||
checksum: 10c0/dfda92840bb371fb66b88c087c61a74544363b37a265023223a99965b16a16bbb87661fe4948718d79df6e0cc04e85e62784fbcf1832b2a5e54ff4c46fbb45b7
|
||||
p-map: "npm:^7.0.2"
|
||||
ssri: "npm:^12.0.0"
|
||||
tar: "npm:^7.4.3"
|
||||
unique-filename: "npm:^4.0.0"
|
||||
checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -214,17 +211,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chownr@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "chownr@npm:2.0.0"
|
||||
checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clean-stack@npm:^2.0.0":
|
||||
version: 2.2.0
|
||||
resolution: "clean-stack@npm:2.2.0"
|
||||
checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1
|
||||
"chownr@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "chownr@npm:3.0.0"
|
||||
checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -251,26 +241,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cross-spawn@npm:^7.0.0":
|
||||
version: 7.0.3
|
||||
resolution: "cross-spawn@npm:7.0.3"
|
||||
"cross-spawn@npm:^7.0.6":
|
||||
version: 7.0.6
|
||||
resolution: "cross-spawn@npm:7.0.6"
|
||||
dependencies:
|
||||
path-key: "npm:^3.1.0"
|
||||
shebang-command: "npm:^2.0.0"
|
||||
which: "npm:^2.0.1"
|
||||
checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750
|
||||
checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
|
||||
version: 4.3.4
|
||||
resolution: "debug@npm:4.3.4"
|
||||
version: 4.4.1
|
||||
resolution: "debug@npm:4.4.1"
|
||||
dependencies:
|
||||
ms: "npm:2.1.2"
|
||||
ms: "npm:^2.1.3"
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736
|
||||
checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -319,9 +309,21 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"exponential-backoff@npm:^3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "exponential-backoff@npm:3.1.1"
|
||||
checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579
|
||||
version: 3.1.2
|
||||
resolution: "exponential-backoff@npm:3.1.2"
|
||||
checksum: 10c0/d9d3e1eafa21b78464297df91f1776f7fbaa3d5e3f7f0995648ca5b89c069d17055033817348d9f4a43d1c20b0eab84f75af6991751e839df53e4dfd6f22e844
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fdir@npm:^6.4.4":
|
||||
version: 6.4.6
|
||||
resolution: "fdir@npm:6.4.6"
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -335,21 +337,12 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"foreground-child@npm:^3.1.0":
|
||||
version: 3.1.1
|
||||
resolution: "foreground-child@npm:3.1.1"
|
||||
version: 3.3.1
|
||||
resolution: "foreground-child@npm:3.3.1"
|
||||
dependencies:
|
||||
cross-spawn: "npm:^7.0.0"
|
||||
cross-spawn: "npm:^7.0.6"
|
||||
signal-exit: "npm:^4.0.1"
|
||||
checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs-minipass@npm:^2.0.0":
|
||||
version: 2.1.0
|
||||
resolution: "fs-minipass@npm:2.1.0"
|
||||
dependencies:
|
||||
minipass: "npm:^3.0.0"
|
||||
checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004
|
||||
checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -390,18 +383,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:^10.2.2, glob@npm:^10.3.10":
|
||||
version: 10.3.16
|
||||
resolution: "glob@npm:10.3.16"
|
||||
"glob@npm:^10.2.2":
|
||||
version: 10.4.5
|
||||
resolution: "glob@npm:10.4.5"
|
||||
dependencies:
|
||||
foreground-child: "npm:^3.1.0"
|
||||
jackspeak: "npm:^3.1.2"
|
||||
minimatch: "npm:^9.0.1"
|
||||
minipass: "npm:^7.0.4"
|
||||
path-scurry: "npm:^1.11.0"
|
||||
minimatch: "npm:^9.0.4"
|
||||
minipass: "npm:^7.1.2"
|
||||
package-json-from-dist: "npm:^1.0.0"
|
||||
path-scurry: "npm:^1.11.1"
|
||||
bin:
|
||||
glob: dist/esm/bin.mjs
|
||||
checksum: 10c0/f7eb4c3e66f221f0be3967c02527047167967549bdf8ed1bd5f6277d43a35191af4e2bb8c89f07a79664958bae088fd06659e69a0f1de462972f1eab52a715e8
|
||||
checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -420,9 +414,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"http-cache-semantics@npm:^4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "http-cache-semantics@npm:4.1.1"
|
||||
checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc
|
||||
version: 4.2.0
|
||||
resolution: "http-cache-semantics@npm:4.2.0"
|
||||
checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -437,12 +431,12 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"https-proxy-agent@npm:^7.0.1":
|
||||
version: 7.0.4
|
||||
resolution: "https-proxy-agent@npm:7.0.4"
|
||||
version: 7.0.6
|
||||
resolution: "https-proxy-agent@npm:7.0.6"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.0.2"
|
||||
agent-base: "npm:^7.1.2"
|
||||
debug: "npm:4"
|
||||
checksum: 10c0/bc4f7c38da32a5fc622450b6cb49a24ff596f9bd48dcedb52d2da3fa1c1a80e100fb506bd59b326c012f21c863c69b275c23de1a01d0b84db396822fdf25e52b
|
||||
checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -469,13 +463,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"indent-string@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "indent-string@npm:4.0.0"
|
||||
checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ip-address@npm:^9.0.5":
|
||||
version: 9.0.5
|
||||
resolution: "ip-address@npm:9.0.5"
|
||||
@@ -518,13 +505,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-lambda@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "is-lambda@npm:1.0.1"
|
||||
checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-number@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "is-number@npm:7.0.0"
|
||||
@@ -547,15 +527,15 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"jackspeak@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "jackspeak@npm:3.1.2"
|
||||
version: 3.4.3
|
||||
resolution: "jackspeak@npm:3.4.3"
|
||||
dependencies:
|
||||
"@isaacs/cliui": "npm:^8.0.2"
|
||||
"@pkgjs/parseargs": "npm:^0.11.0"
|
||||
dependenciesMeta:
|
||||
"@pkgjs/parseargs":
|
||||
optional: true
|
||||
checksum: 10c0/5f1922a1ca0f19869e23f0dc4374c60d36e922f7926c76fecf8080cc6f7f798d6a9caac1b9428327d14c67731fd551bb3454cb270a5e13a0718f3b3660ec3d5d
|
||||
checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -567,36 +547,35 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
|
||||
version: 10.2.2
|
||||
resolution: "lru-cache@npm:10.2.2"
|
||||
checksum: 10c0/402d31094335851220d0b00985084288136136992979d0e015f0f1697e15d1c86052d7d53ae86b614e5b058425606efffc6969a31a091085d7a2b80a8a1e26d6
|
||||
version: 10.4.3
|
||||
resolution: "lru-cache@npm:10.4.3"
|
||||
checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"luxon@npm:^3.4.4":
|
||||
version: 3.4.4
|
||||
resolution: "luxon@npm:3.4.4"
|
||||
checksum: 10c0/02e26a0b039c11fd5b75e1d734c8f0332c95510f6a514a9a0991023e43fb233884da02d7f966823ffb230632a733fc86d4a4b1e63c3fbe00058b8ee0f8c728af
|
||||
version: 3.6.1
|
||||
resolution: "luxon@npm:3.6.1"
|
||||
checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"make-fetch-happen@npm:^13.0.0":
|
||||
version: 13.0.1
|
||||
resolution: "make-fetch-happen@npm:13.0.1"
|
||||
"make-fetch-happen@npm:^14.0.3":
|
||||
version: 14.0.3
|
||||
resolution: "make-fetch-happen@npm:14.0.3"
|
||||
dependencies:
|
||||
"@npmcli/agent": "npm:^2.0.0"
|
||||
cacache: "npm:^18.0.0"
|
||||
"@npmcli/agent": "npm:^3.0.0"
|
||||
cacache: "npm:^19.0.1"
|
||||
http-cache-semantics: "npm:^4.1.1"
|
||||
is-lambda: "npm:^1.0.1"
|
||||
minipass: "npm:^7.0.2"
|
||||
minipass-fetch: "npm:^3.0.0"
|
||||
minipass-fetch: "npm:^4.0.0"
|
||||
minipass-flush: "npm:^1.0.5"
|
||||
minipass-pipeline: "npm:^1.2.4"
|
||||
negotiator: "npm:^0.6.3"
|
||||
proc-log: "npm:^4.2.0"
|
||||
negotiator: "npm:^1.0.0"
|
||||
proc-log: "npm:^5.0.0"
|
||||
promise-retry: "npm:^2.0.1"
|
||||
ssri: "npm:^10.0.0"
|
||||
checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e
|
||||
ssri: "npm:^12.0.0"
|
||||
checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -609,12 +588,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimatch@npm:^9.0.1":
|
||||
version: 9.0.4
|
||||
resolution: "minimatch@npm:9.0.4"
|
||||
"minimatch@npm:^9.0.4":
|
||||
version: 9.0.5
|
||||
resolution: "minimatch@npm:9.0.5"
|
||||
dependencies:
|
||||
brace-expansion: "npm:^2.0.1"
|
||||
checksum: 10c0/2c16f21f50e64922864e560ff97c587d15fd491f65d92a677a344e970fe62aafdbeafe648965fa96d33c061b4d0eabfe0213466203dd793367e7f28658cf6414
|
||||
checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -627,18 +606,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass-fetch@npm:^3.0.0":
|
||||
version: 3.0.5
|
||||
resolution: "minipass-fetch@npm:3.0.5"
|
||||
"minipass-fetch@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "minipass-fetch@npm:4.0.1"
|
||||
dependencies:
|
||||
encoding: "npm:^0.1.13"
|
||||
minipass: "npm:^7.0.3"
|
||||
minipass-sized: "npm:^1.0.3"
|
||||
minizlib: "npm:^2.1.2"
|
||||
minizlib: "npm:^3.0.1"
|
||||
dependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b
|
||||
checksum: 10c0/a3147b2efe8e078c9bf9d024a0059339c5a09c5b1dded6900a219c218cc8b1b78510b62dae556b507304af226b18c3f1aeb1d48660283602d5b6586c399eed5c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -678,76 +657,68 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "minipass@npm:5.0.0"
|
||||
checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462
|
||||
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
|
||||
version: 7.1.2
|
||||
resolution: "minipass@npm:7.1.2"
|
||||
checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4":
|
||||
version: 7.1.1
|
||||
resolution: "minipass@npm:7.1.1"
|
||||
checksum: 10c0/fdccc2f99c31083f45f881fd1e6971d798e333e078ab3c8988fb818c470fbd5e935388ad9adb286397eba50baebf46ef8ff487c8d3f455a69c6f3efc327bdff9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "minizlib@npm:2.1.2"
|
||||
"minizlib@npm:^3.0.1":
|
||||
version: 3.0.2
|
||||
resolution: "minizlib@npm:3.0.2"
|
||||
dependencies:
|
||||
minipass: "npm:^3.0.0"
|
||||
yallist: "npm:^4.0.0"
|
||||
checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78
|
||||
minipass: "npm:^7.1.2"
|
||||
checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mkdirp@npm:^1.0.3":
|
||||
version: 1.0.4
|
||||
resolution: "mkdirp@npm:1.0.4"
|
||||
"mkdirp@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "mkdirp@npm:3.0.1"
|
||||
bin:
|
||||
mkdirp: bin/cmd.js
|
||||
checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf
|
||||
mkdirp: dist/cjs/src/bin.js
|
||||
checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "ms@npm:2.1.2"
|
||||
checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
|
||||
"ms@npm:^2.1.3":
|
||||
version: 2.1.3
|
||||
resolution: "ms@npm:2.1.3"
|
||||
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"negotiator@npm:^0.6.3":
|
||||
version: 0.6.3
|
||||
resolution: "negotiator@npm:0.6.3"
|
||||
checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2
|
||||
"negotiator@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "negotiator@npm:1.0.0"
|
||||
checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-gyp@npm:latest":
|
||||
version: 10.1.0
|
||||
resolution: "node-gyp@npm:10.1.0"
|
||||
version: 11.2.0
|
||||
resolution: "node-gyp@npm:11.2.0"
|
||||
dependencies:
|
||||
env-paths: "npm:^2.2.0"
|
||||
exponential-backoff: "npm:^3.1.1"
|
||||
glob: "npm:^10.3.10"
|
||||
graceful-fs: "npm:^4.2.6"
|
||||
make-fetch-happen: "npm:^13.0.0"
|
||||
nopt: "npm:^7.0.0"
|
||||
proc-log: "npm:^3.0.0"
|
||||
make-fetch-happen: "npm:^14.0.3"
|
||||
nopt: "npm:^8.0.0"
|
||||
proc-log: "npm:^5.0.0"
|
||||
semver: "npm:^7.3.5"
|
||||
tar: "npm:^6.1.2"
|
||||
which: "npm:^4.0.0"
|
||||
tar: "npm:^7.4.3"
|
||||
tinyglobby: "npm:^0.2.12"
|
||||
which: "npm:^5.0.0"
|
||||
bin:
|
||||
node-gyp: bin/node-gyp.js
|
||||
checksum: 10c0/9cc821111ca244a01fb7f054db7523ab0a0cd837f665267eb962eb87695d71fb1e681f9e21464cc2fd7c05530dc4c81b810bca1a88f7d7186909b74477491a3c
|
||||
checksum: 10c0/bd8d8c76b06be761239b0c8680f655f6a6e90b48e44d43415b11c16f7e8c15be346fba0cbf71588c7cdfb52c419d928a7d3db353afc1d952d19756237d8f10b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nodemon@npm:^3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "nodemon@npm:3.1.2"
|
||||
version: 3.1.10
|
||||
resolution: "nodemon@npm:3.1.10"
|
||||
dependencies:
|
||||
chokidar: "npm:^3.5.2"
|
||||
debug: "npm:^4"
|
||||
@@ -761,18 +732,18 @@ __metadata:
|
||||
undefsafe: "npm:^2.0.5"
|
||||
bin:
|
||||
nodemon: bin/nodemon.js
|
||||
checksum: 10c0/7a091067d766768fb6660b796194b01748bba5dc3f1e3ed3dd5f804bfa305e207d24635755078ee5e7cc53848cea35204901e0a6e51ac64483bb8e9ecb237c95
|
||||
checksum: 10c0/95b64d647f2c22e85e375b250517b0a4b32c2d2392ad898444e331f70d6b1ab43b17f53a8a1d68d5879ab8401fc6cd6e26f0d2a8736240984f6b5a8435b407c0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^7.0.0":
|
||||
version: 7.2.1
|
||||
resolution: "nopt@npm:7.2.1"
|
||||
"nopt@npm:^8.0.0":
|
||||
version: 8.1.0
|
||||
resolution: "nopt@npm:8.1.0"
|
||||
dependencies:
|
||||
abbrev: "npm:^2.0.0"
|
||||
abbrev: "npm:^3.0.0"
|
||||
bin:
|
||||
nopt: bin/nopt.js
|
||||
checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81
|
||||
checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -783,12 +754,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-map@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "p-map@npm:4.0.0"
|
||||
dependencies:
|
||||
aggregate-error: "npm:^3.0.0"
|
||||
checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75
|
||||
"p-map@npm:^7.0.2":
|
||||
version: 7.0.3
|
||||
resolution: "p-map@npm:7.0.3"
|
||||
checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"package-json-from-dist@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "package-json-from-dist@npm:1.0.1"
|
||||
checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -799,7 +775,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-scurry@npm:^1.11.0":
|
||||
"path-scurry@npm:^1.11.1":
|
||||
version: 1.11.1
|
||||
resolution: "path-scurry@npm:1.11.1"
|
||||
dependencies:
|
||||
@@ -816,17 +792,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proc-log@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "proc-log@npm:3.0.0"
|
||||
checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc
|
||||
"picomatch@npm:^4.0.2":
|
||||
version: 4.0.2
|
||||
resolution: "picomatch@npm:4.0.2"
|
||||
checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proc-log@npm:^4.2.0":
|
||||
version: 4.2.0
|
||||
resolution: "proc-log@npm:4.2.0"
|
||||
checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9
|
||||
"proc-log@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "proc-log@npm:5.0.0"
|
||||
checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -878,11 +854,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^7.3.5, semver@npm:^7.5.3":
|
||||
version: 7.6.2
|
||||
resolution: "semver@npm:7.6.2"
|
||||
version: 7.7.2
|
||||
resolution: "semver@npm:7.7.2"
|
||||
bin:
|
||||
semver: bin/semver.js
|
||||
checksum: 10c0/97d3441e97ace8be4b1976433d1c32658f6afaff09f143e52c593bae7eef33de19e3e369c88bd985ce1042c6f441c80c6803078d1de2a9988080b66684cbb30c
|
||||
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -926,23 +902,23 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"socks-proxy-agent@npm:^8.0.3":
|
||||
version: 8.0.3
|
||||
resolution: "socks-proxy-agent@npm:8.0.3"
|
||||
version: 8.0.5
|
||||
resolution: "socks-proxy-agent@npm:8.0.5"
|
||||
dependencies:
|
||||
agent-base: "npm:^7.1.1"
|
||||
agent-base: "npm:^7.1.2"
|
||||
debug: "npm:^4.3.4"
|
||||
socks: "npm:^2.7.1"
|
||||
checksum: 10c0/4950529affd8ccd6951575e21c1b7be8531b24d924aa4df3ee32df506af34b618c4e50d261f4cc603f1bfd8d426915b7d629966c8ce45b05fb5ad8c8b9a6459d
|
||||
socks: "npm:^2.8.3"
|
||||
checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"socks@npm:^2.7.1":
|
||||
version: 2.8.3
|
||||
resolution: "socks@npm:2.8.3"
|
||||
"socks@npm:^2.8.3":
|
||||
version: 2.8.5
|
||||
resolution: "socks@npm:2.8.5"
|
||||
dependencies:
|
||||
ip-address: "npm:^9.0.5"
|
||||
smart-buffer: "npm:^4.2.0"
|
||||
checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7
|
||||
checksum: 10c0/e427d0eb0451cfd04e20b9156ea8c0e9b5e38a8d70f21e55c30fbe4214eda37cfc25d782c63f9adc5fbdad6d062a0f127ef2cefc9a44b6fee2b9ea5d1ed10827
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -970,12 +946,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ssri@npm:^10.0.0":
|
||||
version: 10.0.6
|
||||
resolution: "ssri@npm:10.0.6"
|
||||
"ssri@npm:^12.0.0":
|
||||
version: 12.0.0
|
||||
resolution: "ssri@npm:12.0.0"
|
||||
dependencies:
|
||||
minipass: "npm:^7.0.3"
|
||||
checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227
|
||||
checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1028,17 +1004,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:^6.1.11, tar@npm:^6.1.2":
|
||||
version: 6.2.1
|
||||
resolution: "tar@npm:6.2.1"
|
||||
"tar@npm:^7.4.3":
|
||||
version: 7.4.3
|
||||
resolution: "tar@npm:7.4.3"
|
||||
dependencies:
|
||||
chownr: "npm:^2.0.0"
|
||||
fs-minipass: "npm:^2.0.0"
|
||||
minipass: "npm:^5.0.0"
|
||||
minizlib: "npm:^2.1.1"
|
||||
mkdirp: "npm:^1.0.3"
|
||||
yallist: "npm:^4.0.0"
|
||||
checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537
|
||||
"@isaacs/fs-minipass": "npm:^4.0.0"
|
||||
chownr: "npm:^3.0.0"
|
||||
minipass: "npm:^7.1.2"
|
||||
minizlib: "npm:^3.0.1"
|
||||
mkdirp: "npm:^3.0.1"
|
||||
yallist: "npm:^5.0.0"
|
||||
checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinyglobby@npm:^0.2.12":
|
||||
version: 0.2.14
|
||||
resolution: "tinyglobby@npm:0.2.14"
|
||||
dependencies:
|
||||
fdir: "npm:^6.4.4"
|
||||
picomatch: "npm:^4.0.2"
|
||||
checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1067,21 +1053,21 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-filename@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "unique-filename@npm:3.0.0"
|
||||
"unique-filename@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "unique-filename@npm:4.0.0"
|
||||
dependencies:
|
||||
unique-slug: "npm:^4.0.0"
|
||||
checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f
|
||||
unique-slug: "npm:^5.0.0"
|
||||
checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unique-slug@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "unique-slug@npm:4.0.0"
|
||||
"unique-slug@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "unique-slug@npm:5.0.0"
|
||||
dependencies:
|
||||
imurmurhash: "npm:^0.1.4"
|
||||
checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635
|
||||
checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1096,14 +1082,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"which@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "which@npm:4.0.0"
|
||||
"which@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "which@npm:5.0.0"
|
||||
dependencies:
|
||||
isexe: "npm:^3.1.1"
|
||||
bin:
|
||||
node-which: bin/which.js
|
||||
checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a
|
||||
checksum: 10c0/e556e4cd8b7dbf5df52408c9a9dd5ac6518c8c5267c8953f5b0564073c66ed5bf9503b14d876d0e9c7844d4db9725fb0dcf45d6e911e17e26ab363dc3965ae7b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1130,8 +1116,8 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"ws@npm:^8.17.0":
|
||||
version: 8.17.0
|
||||
resolution: "ws@npm:8.17.0"
|
||||
version: 8.18.2
|
||||
resolution: "ws@npm:8.18.2"
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: ">=5.0.2"
|
||||
@@ -1140,7 +1126,7 @@ __metadata:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
checksum: 10c0/55241ec93a66fdfc4bf4f8bc66c8eb038fda2c7a4ee8f6f157f2ca7dc7aa76aea0c0da0bf3adb2af390074a70a0e45456a2eaf80e581e630b75df10a64b0a990
|
||||
checksum: 10c0/4b50f67931b8c6943c893f59c524f0e4905bbd183016cfb0f2b8653aa7f28dad4e456b9d99d285bbb67cca4fedd9ce90dfdfaa82b898a11414ebd66ee99141e4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1150,3 +1136,10 @@ __metadata:
|
||||
checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"yallist@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "yallist@npm:5.0.0"
|
||||
checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{:deps
|
||||
{org.clojure/clojure {:mvn/version "1.12.0"}
|
||||
{org.clojure/clojure {:mvn/version "1.12.1"}
|
||||
org.clojure/data.json {:mvn/version "2.5.1"}
|
||||
org.clojure/tools.cli {:mvn/version "1.1.230"}
|
||||
org.clojure/clojurescript {:mvn/version "1.12.38"}
|
||||
org.clojure/test.check {:mvn/version "1.1.1"}
|
||||
org.clojure/data.fressian {:mvn/version "1.1.0"}
|
||||
org.clojure/clojurescript {:mvn/version "1.12.42"}
|
||||
|
||||
;; Logging
|
||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.24.3"}
|
||||
@@ -28,7 +28,7 @@
|
||||
integrant/integrant {:mvn/version "0.13.1"}
|
||||
|
||||
funcool/tubax {:mvn/version "2021.05.20-0"}
|
||||
funcool/cuerdas {:mvn/version "2023.11.09-407"}
|
||||
funcool/cuerdas {:mvn/version "2025.06.16-414"}
|
||||
funcool/promesa
|
||||
{:git/sha "f52f58cfacf62f59eab717e2637f37729d0cc383"
|
||||
:git/url "https://github.com/funcool/promesa"}
|
||||
@@ -59,7 +59,7 @@
|
||||
{:dev
|
||||
{:extra-deps
|
||||
{org.clojure/tools.namespace {:mvn/version "RELEASE"}
|
||||
thheller/shadow-cljs {:mvn/version "3.0.5"}
|
||||
thheller/shadow-cljs {:mvn/version "3.1.5"}
|
||||
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
|
||||
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
||||
criterium/criterium {:mvn/version "RELEASE"}
|
||||
|
||||
@@ -4,21 +4,20 @@
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
|
||||
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/penpot/penpot"
|
||||
},
|
||||
"dependencies": {
|
||||
"luxon": "^3.4.4"
|
||||
"luxon": "^3.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^9.0.1",
|
||||
"nodemon": "^3.1.7",
|
||||
"shadow-cljs": "3.0.5",
|
||||
"concurrently": "^9.1.2",
|
||||
"nodemon": "^3.1.10",
|
||||
"source-map-support": "^0.5.21",
|
||||
"ws": "^8.17.0"
|
||||
"ws": "^8.18.2"
|
||||
},
|
||||
"scripts": {
|
||||
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
|
||||
|
||||
165
common/src/app/common/buffer.cljc
Normal file
165
common/src/app/common/buffer.cljc
Normal file
@@ -0,0 +1,165 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.common.buffer
|
||||
"A collection of helpers and macros for work with byte buffers"
|
||||
(:refer-clojure :exclude [clone])
|
||||
(:require
|
||||
[app.common.uuid :as uuid])
|
||||
#?(:cljs
|
||||
(:require-macros [app.common.buffer])
|
||||
:clj
|
||||
(:import [java.nio ByteBuffer ByteOrder])))
|
||||
|
||||
(defmacro read-byte
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(.getInt8 ~target ~offset true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(long (.get ~target ~offset)))))
|
||||
|
||||
(defmacro read-bool
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(== 1 (.getInt8 ~target ~offset true))
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(== 1 (.get ~target ~offset)))))
|
||||
|
||||
(defmacro read-short
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(.getInt16 ~target ~offset true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.getShort ~target ~offset))))
|
||||
|
||||
(defmacro read-int
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(.getInt32 ~target ~offset true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(long (.getInt ~target ~offset)))))
|
||||
|
||||
(defmacro read-float
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(.getFloat32 ~target ~offset true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(double (.getFloat ~target ~offset)))))
|
||||
|
||||
(defmacro read-uuid
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(let [a# (.getUint32 ~target (+ ~offset 0) true)
|
||||
b# (.getUint32 ~target (+ ~offset 4) true)
|
||||
c# (.getUint32 ~target (+ ~offset 8) true)
|
||||
d# (.getUint32 ~target (+ ~offset 12) true)]
|
||||
(uuid/from-unsigned-parts a# b# c# d#))
|
||||
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(try
|
||||
(.order ~target ByteOrder/BIG_ENDIAN)
|
||||
(let [msb# (.getLong ~target (+ ~offset 0))
|
||||
lsb# (.getLong ~target (+ ~offset 8))]
|
||||
(java.util.UUID. (long msb#) (long lsb#)))
|
||||
(finally
|
||||
(.order ~target ByteOrder/LITTLE_ENDIAN))))))
|
||||
|
||||
(defmacro write-byte
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(.setInt8 ~target ~offset ~value true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.put ~target ~offset (unchecked-byte ~value)))))
|
||||
|
||||
(defmacro write-short
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(.setInt16 ~target ~offset ~value true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.putShort ~target ~offset (unchecked-short ~value)))))
|
||||
|
||||
(defmacro write-int
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(.setInt32 ~target ~offset ~value true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.putInt ~target ~offset (unchecked-int ~value)))))
|
||||
|
||||
(defmacro write-float
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(.setFloat32 ~target ~offset ~value true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.putFloat ~target ~offset (unchecked-float ~value)))))
|
||||
|
||||
(defmacro write-uuid
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(let [barray# (uuid/get-u32 ~value)]
|
||||
(.setUint32 ~target (+ ~offset 0) (aget barray# 0) true)
|
||||
(.setUint32 ~target (+ ~offset 4) (aget barray# 1) true)
|
||||
(.setUint32 ~target (+ ~offset 8) (aget barray# 2) true)
|
||||
(.setUint32 ~target (+ ~offset 12) (aget barray# 3) true))
|
||||
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})
|
||||
value (with-meta value {:tag 'java.util.UUID})]
|
||||
`(try
|
||||
(.order ~target ByteOrder/BIG_ENDIAN)
|
||||
(.putLong ~target (+ ~offset 0) (.getMostSignificantBits ~value))
|
||||
(.putLong ~target (+ ~offset 8) (.getLeastSignificantBits ~value))
|
||||
(finally
|
||||
(.order ~target ByteOrder/LITTLE_ENDIAN))))))
|
||||
|
||||
(defn allocate
|
||||
[size]
|
||||
#?(:clj (let [buffer (ByteBuffer/allocate (int size))]
|
||||
(.order buffer ByteOrder/LITTLE_ENDIAN))
|
||||
:cljs (new js/DataView (new js/ArrayBuffer size))))
|
||||
|
||||
(defn clone
|
||||
[buffer]
|
||||
#?(:clj
|
||||
(let [src (.array ^ByteBuffer buffer)
|
||||
len (alength ^bytes src)
|
||||
dst (byte-array len)]
|
||||
(System/arraycopy src 0 dst 0 len)
|
||||
(let [buffer (ByteBuffer/wrap dst)]
|
||||
(.order buffer ByteOrder/LITTLE_ENDIAN)))
|
||||
:cljs
|
||||
(let [buffer' (.-buffer ^js/DataView buffer)
|
||||
src-view (js/Uint32Array. buffer')
|
||||
dst-buff (js/ArrayBuffer. (.-byteLength buffer'))
|
||||
dst-view (js/Uint32Array. dst-buff)]
|
||||
(.set dst-view src-view)
|
||||
(js/DataView. dst-buff))))
|
||||
|
||||
(defn equals?
|
||||
[buffer-a buffer-b]
|
||||
#?(:clj
|
||||
(.equals ^ByteBuffer buffer-a
|
||||
^ByteBuffer buffer-b)
|
||||
|
||||
:cljs
|
||||
(let [buffer-a (.-buffer buffer-a)
|
||||
buffer-b (.-buffer buffer-b)]
|
||||
(if (= (.-byteLength buffer-a)
|
||||
(.-byteLength buffer-b))
|
||||
(let [cb (js/Uint32Array. buffer-a)
|
||||
ob (js/Uint32Array. buffer-b)
|
||||
sz (alength cb)]
|
||||
(loop [i 0]
|
||||
(if (< i sz)
|
||||
(if (== (aget ob i)
|
||||
(aget cb i))
|
||||
(recur (inc i))
|
||||
false)
|
||||
true)))
|
||||
false))))
|
||||
|
||||
(defn buffer?
|
||||
[o]
|
||||
#?(:clj (instance? ByteBuffer o)
|
||||
:cljs (instance? js/DataView o)))
|
||||
@@ -33,6 +33,16 @@
|
||||
(def boolean-or-nil?
|
||||
(some-fn nil? boolean?))
|
||||
|
||||
(defn in-range?
|
||||
[size i]
|
||||
(and (< i size) (>= i 0)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Commonly used transducers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def xf:map-id (map :id))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Structures
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -47,10 +47,26 @@
|
||||
`(try ~@exprs (catch Throwable e# nil))))
|
||||
|
||||
(defmacro try!
|
||||
[& exprs]
|
||||
(if (:ns &env)
|
||||
`(try ~@exprs (catch :default e# e#))
|
||||
`(try ~@exprs (catch Throwable e# e#))))
|
||||
[expr & {:keys [reraise-with on-exception]}]
|
||||
(let [ex-sym
|
||||
(gensym "exc")
|
||||
|
||||
generate-catch
|
||||
(fn []
|
||||
(cond
|
||||
(map? reraise-with)
|
||||
`(ex/raise ~@(mapcat identity reraise-with) :cause ~ex-sym)
|
||||
|
||||
on-exception
|
||||
`(let [handler# ~on-exception]
|
||||
(handler# ~ex-sym))
|
||||
|
||||
:else
|
||||
ex-sym))]
|
||||
|
||||
(if (:ns &env)
|
||||
`(try ~expr (catch :default ~ex-sym ~(generate-catch)))
|
||||
`(try ~expr (catch Throwable ~ex-sym ~(generate-catch))))))
|
||||
|
||||
(defn ex-info?
|
||||
[v]
|
||||
|
||||
@@ -10,15 +10,20 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.files.changes :as ch]
|
||||
;; [app.common.features :as cfeat]
|
||||
[app.common.files.helpers :as cph]
|
||||
[app.common.files.migrations :as fmig]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.svg :as csvg]
|
||||
[app.common.time :as dt]
|
||||
[app.common.types.color :as types.color]
|
||||
[app.common.types.component :as types.comp]
|
||||
[app.common.types.file :as types.file]
|
||||
[app.common.types.page :as types.page]
|
||||
[app.common.types.path :as types.path]
|
||||
[app.common.types.shape :as types.shape]
|
||||
[app.common.types.typography :as types.typography]
|
||||
[app.common.uuid :as uuid]
|
||||
@@ -112,7 +117,7 @@
|
||||
(sm/decode-fn types.shape/schema:shape-attrs sm/json-transformer))
|
||||
|
||||
(def decode-library-color
|
||||
(sm/decode-fn types.color/schema:color sm/json-transformer))
|
||||
(sm/decode-fn types.color/schema:library-color sm/json-transformer))
|
||||
|
||||
(def decode-library-typography
|
||||
(sm/decode-fn types.typography/schema:typography sm/json-transformer))
|
||||
@@ -133,20 +138,6 @@
|
||||
(def decode-add-component
|
||||
(sm/decode-fn schema:add-component sm/json-transformer))
|
||||
|
||||
(def schema:add-component-instance
|
||||
[:map
|
||||
[:component-id ::sm/uuid]
|
||||
[:file-id {:optional true} ::sm/uuid]
|
||||
[:frame-id {:optional true} ::sm/uuid]
|
||||
[:page-id {:optional true} ::sm/uuid]])
|
||||
|
||||
(def ^:private check-add-component-instance
|
||||
(sm/check-fn schema:add-component-instance
|
||||
:hint "invalid arguments passed for add-component-instance"))
|
||||
|
||||
(def decode-add-component-instance
|
||||
(sm/decode-fn schema:add-component-instance sm/json-transformer))
|
||||
|
||||
(def schema:add-bool
|
||||
[:map
|
||||
[:group-id ::sm/uuid]
|
||||
@@ -198,11 +189,31 @@
|
||||
(-> (get-current-objects state)
|
||||
(get shape-id)))
|
||||
|
||||
;; WORKAROUND: A copy of features from staging for make the library
|
||||
;; generate files compatible with version released right now. This
|
||||
;; should be removed and replaced with cfeat/default-features when 2.8
|
||||
;; version is released
|
||||
|
||||
(def default-features
|
||||
#{"fdata/shape-data-type"
|
||||
"styles/v2"
|
||||
"layout/grid"
|
||||
"components/v2"
|
||||
"plugins/runtime"
|
||||
"design-tokens/v1"})
|
||||
|
||||
;; WORKAROUND: the same as features
|
||||
(def available-migrations
|
||||
(-> fmig/available-migrations
|
||||
(disj "003-convert-path-content")
|
||||
(disj "0002-clean-shape-interactions")
|
||||
(disj "0003-fix-root-shape")))
|
||||
|
||||
(defn add-file
|
||||
[state params]
|
||||
(let [params (-> params
|
||||
(assoc :features cfeat/default-features)
|
||||
(assoc :migrations fmig/available-migrations)
|
||||
(assoc :features default-features)
|
||||
(assoc :migrations available-migrations)
|
||||
(update :id default-uuid))
|
||||
file (types.file/make-file params :create-page false)]
|
||||
(-> state
|
||||
@@ -287,7 +298,7 @@
|
||||
|
||||
(defn close-group
|
||||
[state]
|
||||
(let [group-id (-> state :parent-stack peek)
|
||||
(let [group-id (-> state ::parent-stack peek)
|
||||
group (get-shape state group-id)
|
||||
children (->> (get group :shapes)
|
||||
(into [] (keep (partial get-shape state)))
|
||||
@@ -323,6 +334,34 @@
|
||||
(commit-change state change :add-container true)))]
|
||||
(update state ::parent-stack pop))))
|
||||
|
||||
(defn- update-bool-style-properties
|
||||
[bool-shape objects]
|
||||
(let [xform
|
||||
(comp
|
||||
(map (d/getf objects))
|
||||
(remove cph/frame-shape?)
|
||||
(remove types.comp/is-variant?))
|
||||
|
||||
children
|
||||
(->> (get bool-shape :shapes)
|
||||
(into [] xform)
|
||||
(not-empty))]
|
||||
|
||||
(when-not children
|
||||
(ex/raise :type :validation
|
||||
:code :empty-children
|
||||
:hint "expected a group with at least one shape for creating a bool"))
|
||||
|
||||
(let [head (if (= type :difference)
|
||||
(first children)
|
||||
(last children))
|
||||
fills (if (and (contains? head :svg-attrs) (empty? (:fills head)))
|
||||
types.path/default-bool-fills
|
||||
(get head :fills))]
|
||||
(-> bool-shape
|
||||
(assoc :fills fills)
|
||||
(assoc :stroks (get head :strokes))))))
|
||||
|
||||
(defn add-bool
|
||||
[state params]
|
||||
(let [{:keys [group-id type]}
|
||||
@@ -331,32 +370,40 @@
|
||||
group
|
||||
(get-shape state group-id)
|
||||
|
||||
children
|
||||
(->> (get group :shapes)
|
||||
(not-empty))]
|
||||
objects
|
||||
(get-current-objects state)
|
||||
|
||||
(assert (some? children) "expect group to have at least 1 element")
|
||||
bool
|
||||
(-> group
|
||||
(assoc :type :bool)
|
||||
(assoc :bool-type type)
|
||||
(update-bool-style-properties objects)
|
||||
(types.path/update-bool-shape objects))
|
||||
|
||||
(let [objects (get-current-objects state)
|
||||
bool (-> group
|
||||
(assoc :type :bool)
|
||||
(gsh/update-bool objects))
|
||||
change {:type :mod-obj
|
||||
:id (:id bool)
|
||||
:operations
|
||||
[{:type :set :attr :content :val (:content bool) :ignore-touched true}
|
||||
{:type :set :attr :type :val :bool :ignore-touched true}
|
||||
{:type :set :attr :bool-type :val type :ignore-touched true}
|
||||
{:type :set :attr :selrect :val (:selrect bool) :ignore-touched true}
|
||||
{:type :set :attr :points :val (:points bool) :ignore-touched true}
|
||||
{:type :set :attr :x :val (-> bool :selrect :x) :ignore-touched true}
|
||||
{:type :set :attr :y :val (-> bool :selrect :y) :ignore-touched true}
|
||||
{:type :set :attr :width :val (-> bool :selrect :width) :ignore-touched true}
|
||||
{:type :set :attr :height :val (-> bool :selrect :height) :ignore-touched true}]}]
|
||||
selrect
|
||||
(get bool :selrect)
|
||||
|
||||
(-> state
|
||||
(commit-change change :add-container true)
|
||||
(assoc ::last-id group-id)))))
|
||||
operations
|
||||
[{:type :set :attr :content :val (:content bool) :ignore-touched true}
|
||||
{:type :set :attr :type :val :bool :ignore-touched true}
|
||||
{:type :set :attr :bool-type :val type :ignore-touched true}
|
||||
{:type :set :attr :selrect :val selrect :ignore-touched true}
|
||||
{:type :set :attr :points :val (:points bool) :ignore-touched true}
|
||||
{:type :set :attr :x :val (get selrect :x) :ignore-touched true}
|
||||
{:type :set :attr :y :val (get selrect :y) :ignore-touched true}
|
||||
{:type :set :attr :width :val (get selrect :width) :ignore-touched true}
|
||||
{:type :set :attr :height :val (get selrect :height) :ignore-touched true}
|
||||
{:type :set :attr :fills :val (:fills bool) :ignore-touched true}
|
||||
{:type :set :attr :strokes :val (:strokes bool) :ignore-touched true}]
|
||||
|
||||
change
|
||||
{:type :mod-obj
|
||||
:id (:id bool)
|
||||
:operations operations}]
|
||||
|
||||
(-> state
|
||||
(commit-change change :add-container true)
|
||||
(assoc ::last-id group-id))))
|
||||
|
||||
(defn add-shape
|
||||
[state params]
|
||||
@@ -432,33 +479,6 @@
|
||||
(commit-change change1)
|
||||
(commit-change change2))))
|
||||
|
||||
|
||||
(defn add-component-instance
|
||||
[state params]
|
||||
|
||||
(let [{:keys [component-id file-id frame-id page-id]}
|
||||
(check-add-component-instance params)
|
||||
|
||||
file-id
|
||||
(or file-id (get state ::current-file-id))
|
||||
|
||||
frame-id
|
||||
(or frame-id (get state ::current-frame-id))
|
||||
|
||||
page-id
|
||||
(or page-id (get state ::current-page-id))
|
||||
|
||||
change
|
||||
{:type :mod-obj
|
||||
:id frame-id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set :attr :component-root :val true}
|
||||
{:type :set :attr :component-id :val component-id}
|
||||
{:type :set :attr :component-file :val file-id}]}]
|
||||
|
||||
(commit-change state change)))
|
||||
|
||||
(defn delete-shape
|
||||
[file id]
|
||||
(commit-change
|
||||
@@ -554,11 +574,13 @@
|
||||
:size (get blob :size)})
|
||||
(update ::file-media assoc id
|
||||
{:id id
|
||||
:created-at (dt/now)
|
||||
:name name
|
||||
:width width
|
||||
:height height
|
||||
:file-id file-id
|
||||
:media-id media-id
|
||||
:is-local true
|
||||
:mtype (get blob :mtype)})
|
||||
|
||||
(assoc ::last-id id))))
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
[app.common.schema.desc-native :as smd]
|
||||
[app.common.schema.generators :as sg]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.colors-list :as ctcl]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.container :as ctn]
|
||||
@@ -24,6 +23,7 @@
|
||||
[app.common.types.grid :as ctg]
|
||||
[app.common.types.page :as ctp]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
@@ -265,7 +265,7 @@
|
||||
[:id ::sm/uuid]
|
||||
;; All props are optional, background can be nil because is the
|
||||
;; way to remove already set background
|
||||
[:background {:optional true} [:maybe ::ctc/rgb-color]]
|
||||
[:background {:optional true} [:maybe ctc/schema:hex-color]]
|
||||
[:name {:optional true} :string]]]
|
||||
|
||||
[:set-plugin-data schema:set-plugin-data-change]
|
||||
@@ -291,12 +291,12 @@
|
||||
[:add-color
|
||||
[:map {:title "AddColorChange"}
|
||||
[:type [:= :add-color]]
|
||||
[:color ::ctc/color]]]
|
||||
[:color ctc/schema:library-color]]]
|
||||
|
||||
[:mod-color
|
||||
[:map {:title "ModColorChange"}
|
||||
[:type [:= :mod-color]]
|
||||
[:color ::ctc/color]]]
|
||||
[:color ctc/schema:library-color]]]
|
||||
|
||||
[:del-color
|
||||
[:map {:title "DelColorChange"}
|
||||
@@ -377,7 +377,7 @@
|
||||
[:update-active-token-themes
|
||||
[:map {:title "UpdateActiveTokenThemes"}
|
||||
[:type [:= :update-active-token-themes]]
|
||||
[:theme-ids [:set :string]]]]
|
||||
[:theme-paths [:set :string]]]]
|
||||
|
||||
[:rename-token-set-group
|
||||
[:map {:title "RenameTokenSetGroup"}
|
||||
@@ -487,7 +487,9 @@
|
||||
(cts/shape? shape-new))
|
||||
(ex/raise :type :assertion
|
||||
:code :data-validation
|
||||
:hint "invalid shape found after applying changes"
|
||||
:hint (str "invalid shape found after applying changes on file "
|
||||
(:id data-new))
|
||||
:file-id (:id data-new)
|
||||
::sm/explain (cts/explain-shape shape-new))))))]
|
||||
|
||||
(->> (into #{} (map :page-id) items)
|
||||
@@ -744,7 +746,7 @@
|
||||
group
|
||||
|
||||
(= :bool (:type group))
|
||||
(gsh/update-bool group objects)
|
||||
(path/update-bool-shape group objects)
|
||||
|
||||
(:masked-group group)
|
||||
(->> (map lookup children)
|
||||
@@ -925,15 +927,15 @@
|
||||
|
||||
(defmethod process-change :add-color
|
||||
[data {:keys [color]}]
|
||||
(ctcl/add-color data color))
|
||||
(ctc/add-color data color))
|
||||
|
||||
(defmethod process-change :mod-color
|
||||
[data {:keys [color]}]
|
||||
(ctcl/set-color data color))
|
||||
(ctc/set-color data color))
|
||||
|
||||
(defmethod process-change :del-color
|
||||
[data {:keys [id]}]
|
||||
(ctcl/delete-color data id))
|
||||
(ctc/delete-color data id))
|
||||
|
||||
;; DEPRECATED: remove before 2.3
|
||||
(defmethod process-change :add-recent-color
|
||||
@@ -1050,9 +1052,9 @@
|
||||
(ctob/make-token-theme (merge prev-token-theme theme)))))))))
|
||||
|
||||
(defmethod process-change :update-active-token-themes
|
||||
[data {:keys [theme-ids]}]
|
||||
[data {:keys [theme-paths]}]
|
||||
(update data :tokens-lib #(-> % (ctob/ensure-tokens-lib)
|
||||
(ctob/set-active-themes theme-ids))))
|
||||
(ctob/set-active-themes theme-paths))))
|
||||
|
||||
(defmethod process-change :rename-token-set-group
|
||||
[data {:keys [set-group-path set-group-fname]}]
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.uuid :as uuid]))
|
||||
@@ -160,7 +161,6 @@
|
||||
(contains? (meta changes) ::file-data)
|
||||
"Call (with-file-data) before using this function"))
|
||||
|
||||
|
||||
(defn- lookup-objects
|
||||
[changes]
|
||||
(let [data (::file-data (meta changes))]
|
||||
@@ -523,7 +523,6 @@
|
||||
:or {ignore-geometry? false ignore-touched false with-objects? false}}]
|
||||
(assert-container-id! changes)
|
||||
(assert-objects! changes)
|
||||
(assert-page-id! changes)
|
||||
(let [page-id (::page-id (meta changes))
|
||||
component-id (::component-id (meta changes))
|
||||
objects (lookup-objects changes)
|
||||
@@ -685,10 +684,10 @@
|
||||
(empty? children) ;; a parent with no children will be deleted,
|
||||
nil ;; so it does not need resize
|
||||
|
||||
(= (:type parent) :bool)
|
||||
(gsh/update-bool parent objects)
|
||||
(cfh/bool-shape? parent)
|
||||
(path/update-bool-shape parent objects)
|
||||
|
||||
(= (:type parent) :group)
|
||||
(cfh/group-shape? parent)
|
||||
;; FIXME: this functions should be
|
||||
;; normalized in the same way as
|
||||
;; update-bool in order to make all
|
||||
@@ -799,10 +798,10 @@
|
||||
(apply-changes-local))))
|
||||
|
||||
(defn update-active-token-themes
|
||||
[changes token-active-theme-ids prev-token-active-theme-ids]
|
||||
[changes active-theme-paths prev-active-theme-paths]
|
||||
(-> changes
|
||||
(update :redo-changes conj {:type :update-active-token-themes :theme-ids token-active-theme-ids})
|
||||
(update :undo-changes conj {:type :update-active-token-themes :theme-ids prev-token-active-theme-ids})
|
||||
(update :redo-changes conj {:type :update-active-token-themes :theme-paths active-theme-paths})
|
||||
(update :undo-changes conj {:type :update-active-token-themes :theme-paths prev-active-theme-paths})
|
||||
(apply-changes-local)))
|
||||
|
||||
(defn set-token-theme [changes group theme-name theme]
|
||||
@@ -1174,4 +1173,4 @@
|
||||
|
||||
(update :undo-changes conj {:type :set-base-font-size
|
||||
:base-font-size previous-font-size})
|
||||
(apply-changes-local))))
|
||||
(apply-changes-local))))
|
||||
|
||||
@@ -117,6 +117,12 @@
|
||||
([shape]
|
||||
(d/not-empty? (:shapes shape))))
|
||||
|
||||
(defn has-layout?
|
||||
"Returns true if the provided shape has a layout assigned"
|
||||
[objects id]
|
||||
(let [shape (get objects id)]
|
||||
(boolean (and shape (:layout shape)))))
|
||||
|
||||
(defn group-like-shape?
|
||||
([objects id]
|
||||
(group-like-shape? (get objects id)))
|
||||
@@ -127,6 +133,24 @@
|
||||
|
||||
;; ---- ACCESSORS
|
||||
|
||||
(defn get-selected-type
|
||||
"Returns the type of the shape if only one, or :multiple if more
|
||||
than one"
|
||||
[objects selected]
|
||||
(if (= 1 (count selected))
|
||||
(let [shape (get objects (first selected))]
|
||||
(:type shape))
|
||||
:multiple))
|
||||
|
||||
(defn get-shape-type
|
||||
"Returns the type of the shape, or 'root' if it's Root Frame, always
|
||||
as string"
|
||||
[objects id]
|
||||
(let [shape (get objects id)]
|
||||
(if (root? shape)
|
||||
:root
|
||||
(dm/get-prop shape :type))))
|
||||
|
||||
(defn get-children-ids
|
||||
[objects id]
|
||||
(letfn [(get-children-ids-rec [id processed]
|
||||
@@ -626,6 +650,9 @@
|
||||
(map? (:fill-image form))
|
||||
(update-in [:fill-image :id] lookup-index)
|
||||
|
||||
(map? (:stroke-image form))
|
||||
(update-in [:stroke-image :id] lookup-index)
|
||||
|
||||
;; This covers old shapes and the new :fills.
|
||||
(uuid? (:fill-color-ref-file form))
|
||||
(update :fill-color-ref-file lookup-index)
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.svg :as csvg]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.color :as types.color]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.fill :as types.fill]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.path.segment :as path.segment]
|
||||
[app.common.types.shape :as cts]
|
||||
@@ -58,18 +59,21 @@
|
||||
(map :name))
|
||||
|
||||
(defn migrate
|
||||
[{:keys [id] :as file}]
|
||||
[{:keys [id] :as file} libs]
|
||||
|
||||
(let [diff
|
||||
(set/difference available-migrations (:migrations file))
|
||||
|
||||
data (-> (:data file)
|
||||
(assoc :libs libs))
|
||||
|
||||
data
|
||||
(reduce migrate-data (:data file) diff)
|
||||
(reduce migrate-data data diff)
|
||||
|
||||
data
|
||||
(-> data
|
||||
(assoc :id id)
|
||||
(dissoc :version))]
|
||||
(dissoc :version :libs))]
|
||||
|
||||
(-> file
|
||||
(assoc :data data)
|
||||
@@ -88,24 +92,35 @@
|
||||
result))
|
||||
|
||||
(defn migrate-file
|
||||
[file]
|
||||
[file libs]
|
||||
(binding [cfeat/*new* (atom #{})]
|
||||
(let [version (or (:version file)
|
||||
(-> file :data :version))]
|
||||
(-> file
|
||||
(assoc :version cfd/version)
|
||||
(update :migrations
|
||||
(fn [migrations]
|
||||
(if (nil? migrations)
|
||||
(generate-migrations-from-version version)
|
||||
migrations)))
|
||||
;; NOTE: in some future we can consider to apply
|
||||
;; a migration to the whole database and remove
|
||||
;; this code from this function that executes on
|
||||
;; each file migration operation
|
||||
(update :features cfeat/migrate-legacy-features)
|
||||
(migrate)
|
||||
(update :features (fnil into #{}) (deref cfeat/*new*))))))
|
||||
(let [version
|
||||
(or (:version file) (-> file :data :version))
|
||||
|
||||
migrations
|
||||
(not-empty (get file :migrations))
|
||||
|
||||
file
|
||||
(-> file
|
||||
(assoc :version cfd/version)
|
||||
(assoc :migrations
|
||||
(if migrations
|
||||
migrations
|
||||
(generate-migrations-from-version version)))
|
||||
;; NOTE: in some future we can consider to apply a
|
||||
;; migration to the whole database and remove this code
|
||||
;; from this function that executes on each file
|
||||
;; migration operation
|
||||
(update :features cfeat/migrate-legacy-features)
|
||||
(migrate libs)
|
||||
(update :features (fnil into #{}) (deref cfeat/*new*)))]
|
||||
|
||||
;; NOTE: When we have no previous migrations, we report all
|
||||
;; migrations as migrated in order to correctly persist them all
|
||||
;; and not only the really applied migrations
|
||||
(if (not migrations)
|
||||
(vary-meta file assoc ::migrated (:migrations file))
|
||||
file))))
|
||||
|
||||
(defn migrated?
|
||||
[file]
|
||||
@@ -822,7 +837,7 @@
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(def ^:private valid-fill?
|
||||
(sm/lazy-validator ::cts/fill))
|
||||
(sm/lazy-validator types.fill/schema:fill))
|
||||
|
||||
(defmethod migrate-data "legacy-43"
|
||||
[data _]
|
||||
@@ -840,7 +855,7 @@
|
||||
|
||||
(update-object [object]
|
||||
(if (cfh/text-shape? object)
|
||||
(update object :content #(txt/transform-nodes identity update-text-node %))
|
||||
(update object :content #(txt/transform-nodes txt/is-content-node? update-text-node %))
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
@@ -1000,15 +1015,12 @@
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(def ^:private valid-color?
|
||||
(sm/lazy-validator ::ctc/color))
|
||||
|
||||
(defmethod migrate-data "legacy-51"
|
||||
[data _]
|
||||
(let [update-colors
|
||||
(fn [colors]
|
||||
(into {} (filter #(-> % val valid-color?) colors)))]
|
||||
(update data :colors update-colors)))
|
||||
(into {} (filter #(-> % val types.color/valid-library-color?) colors)))]
|
||||
(d/update-when data :colors update-colors)))
|
||||
|
||||
(defmethod migrate-data "legacy-52"
|
||||
[data _]
|
||||
@@ -1022,7 +1034,6 @@
|
||||
|
||||
(update data :pages-index d/update-vals update-page)))
|
||||
|
||||
|
||||
(defmethod migrate-data "legacy-53"
|
||||
[data _]
|
||||
(migrate-data data "legacy-26"))
|
||||
@@ -1093,7 +1104,7 @@
|
||||
;; The text shape also can has fills on the text
|
||||
;; fragments so we need to fix fills there
|
||||
(cond-> (cfh/text-shape? object)
|
||||
(update :content (partial txt/transform-nodes identity fix-fills)))))
|
||||
(update :content (partial txt/transform-nodes txt/is-content-node? fix-fills)))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
@@ -1265,21 +1276,21 @@
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0002-normalize-bool-content"
|
||||
(defmethod migrate-data "0002-normalize-bool-content-v2"
|
||||
[data _]
|
||||
(letfn [(update-object [object]
|
||||
;; NOTE: we still preserve the previous value for possible
|
||||
;; rollback, we still need to perform an other migration
|
||||
;; for properly delete the bool-content prop from shapes
|
||||
;; once the know the migration was OK
|
||||
(if (cfh/bool-shape? object)
|
||||
(if-let [content (:bool-content object)]
|
||||
(assoc object :content content)
|
||||
object)
|
||||
(if (contains? object :content)
|
||||
(dissoc object :bool-content)
|
||||
(let [content (:bool-content object)]
|
||||
(-> object
|
||||
(assoc :content content)
|
||||
(dissoc :bool-content))))
|
||||
|
||||
(dissoc object :bool-content :bool-type)))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
@@ -1307,23 +1318,215 @@
|
||||
(d/update-when :components d/update-vals update-container)
|
||||
(d/without-nils))))
|
||||
|
||||
(defmethod migrate-data "0003-convert-path-content"
|
||||
(defmethod migrate-data "0003-convert-path-content-v2"
|
||||
[data _]
|
||||
(some-> cfeat/*new* (swap! conj "fdata/path-data"))
|
||||
|
||||
(letfn [(update-object [object]
|
||||
(if (or (cfh/bool-shape? object)
|
||||
(cfh/path-shape? object))
|
||||
(update object :content path/content)
|
||||
object))
|
||||
(let [decode-segments
|
||||
(sm/decoder path/schema:segments sm/json-transformer)
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects update-vals update-object))]
|
||||
update-object
|
||||
(fn [object]
|
||||
(if (or (cfh/bool-shape? object)
|
||||
(cfh/path-shape? object))
|
||||
(let [content (get object :content)
|
||||
content (cond
|
||||
(path/content? content)
|
||||
content
|
||||
|
||||
(nil? content)
|
||||
(path/content [])
|
||||
|
||||
:else
|
||||
(-> content
|
||||
(decode-segments)
|
||||
(path/content)))]
|
||||
(assoc object :content content))
|
||||
object))
|
||||
|
||||
update-container
|
||||
(fn [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0004-clean-shadow-color"
|
||||
[data _]
|
||||
(let [decode-color (sm/decoder types.color/schema:color sm/json-transformer)
|
||||
|
||||
clean-shadow-color
|
||||
(fn [color]
|
||||
(let [ref-id (get color :id)
|
||||
ref-file (get color :file-id)]
|
||||
(-> (d/without-qualified color)
|
||||
(select-keys [:opacity :color :gradient :image :ref-id :ref-file])
|
||||
(cond-> ref-id
|
||||
(assoc :ref-id ref-id))
|
||||
(cond-> ref-file
|
||||
(assoc :ref-file ref-file))
|
||||
(decode-color))))
|
||||
|
||||
clean-shadow
|
||||
(fn [shadow]
|
||||
(update shadow :color clean-shadow-color))
|
||||
|
||||
update-object
|
||||
(fn [object]
|
||||
(d/update-when object :shadow #(mapv clean-shadow %)))
|
||||
|
||||
update-container
|
||||
(fn [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0005-deprecate-image-type"
|
||||
[data _]
|
||||
(letfn [(update-object [object]
|
||||
(if (cfh/image-shape? object)
|
||||
(let [metadata (:metadata object)
|
||||
fills (into [{:fill-image (assoc metadata :keep-aspect-ratio false)
|
||||
:opacity 1}]
|
||||
(:fills object))]
|
||||
(-> object
|
||||
(assoc :fills fills)
|
||||
(dissoc :metadata)
|
||||
(assoc :type :rect)))
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0006-fix-old-texts-fills"
|
||||
[data _]
|
||||
(letfn [(fix-fills [node]
|
||||
(let [fills (if (and (not (seq (:fills node)))
|
||||
(or (some? (:fill-color node))
|
||||
(some? (:fill-opacity node))
|
||||
(some? (:fill-color-gradient node))))
|
||||
[(d/without-nils (select-keys node [:fill-color :fill-opacity :fill-color-gradient
|
||||
:fill-color-ref-id :fill-color-ref-file]))]
|
||||
(:fills node))]
|
||||
(-> node
|
||||
(assoc :fills fills)
|
||||
(dissoc :fill-color :fill-opacity :fill-color-gradient
|
||||
:fill-color-ref-id :fill-color-ref-file))))
|
||||
|
||||
(update-object [object]
|
||||
(if (cfh/text-shape? object)
|
||||
(update object :content (partial txt/transform-nodes txt/is-content-node? fix-fills))
|
||||
object))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects d/update-vals update-object))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(def ^:private valid-stroke?
|
||||
(sm/lazy-validator cts/schema:stroke))
|
||||
|
||||
(defmethod migrate-data "0007-clear-invalid-strokes-and-fills-v2"
|
||||
[data _]
|
||||
(letfn [(clear-color-image [image]
|
||||
(select-keys image types.color/image-attrs))
|
||||
|
||||
(clear-color-gradient [gradient]
|
||||
(select-keys gradient types.color/gradient-attrs))
|
||||
|
||||
(clear-stroke [stroke]
|
||||
(-> stroke
|
||||
(select-keys cts/stroke-attrs)
|
||||
(d/update-when :stroke-color-gradient clear-color-gradient)
|
||||
(d/update-when :stroke-image clear-color-image)
|
||||
(d/update-when :stroke-style #(if (#{:svg :none} %) :solid %))))
|
||||
|
||||
(fix-strokes [strokes]
|
||||
(->> (map clear-stroke strokes)
|
||||
(filterv valid-stroke?)))
|
||||
|
||||
;; Fixes shapes with nested :fills in the :fills attribute
|
||||
;; introduced in a migration `0006-fix-old-texts-fills` when
|
||||
;; txt/transform-nodes with identity pred was broken
|
||||
(remove-nested-fills [[fill :as fills]]
|
||||
(if (and (= 1 (count fills))
|
||||
(contains? fill :fills))
|
||||
(:fills fill)
|
||||
fills))
|
||||
|
||||
(clear-fill [fill]
|
||||
(-> fill
|
||||
(select-keys types.fill/fill-attrs)
|
||||
(d/update-when :fill-image clear-color-image)
|
||||
(d/update-when :fill-color-gradient clear-color-gradient)))
|
||||
|
||||
(fix-fills [fills]
|
||||
(->> fills
|
||||
(remove-nested-fills)
|
||||
(map clear-fill)
|
||||
(filterv valid-fill?)))
|
||||
|
||||
(fix-object [object]
|
||||
(-> object
|
||||
(d/update-when :strokes fix-strokes)
|
||||
(d/update-when :fills fix-fills)))
|
||||
|
||||
(fix-text-content [content]
|
||||
(->> content
|
||||
(txt/transform-nodes txt/is-content-node? fix-object)
|
||||
(txt/transform-nodes txt/is-paragraph-set-node? #(dissoc % :fills))))
|
||||
|
||||
(update-shape [object]
|
||||
(-> object
|
||||
(fix-object)
|
||||
;; The text shape also can has strokes and fils on the
|
||||
;; text fragments so we need to fix them there
|
||||
(cond-> (cfh/text-shape? object)
|
||||
(update :content fix-text-content))))
|
||||
|
||||
(update-container [container]
|
||||
(d/update-when container :objects d/update-vals update-shape))]
|
||||
|
||||
(-> data
|
||||
(update :pages-index d/update-vals update-container)
|
||||
(d/update-when :components d/update-vals update-container))))
|
||||
|
||||
(defmethod migrate-data "0008-fix-library-colors-v4"
|
||||
[data _]
|
||||
(letfn [(clear-color-opacity [color]
|
||||
(if (and (contains? color :opacity)
|
||||
(nil? (get color :opacity)))
|
||||
(assoc color :opacity 1)
|
||||
color))
|
||||
|
||||
(clear-color [color]
|
||||
(-> color
|
||||
(select-keys types.color/library-color-attrs)
|
||||
(clear-color-opacity)
|
||||
(d/without-nils)))]
|
||||
|
||||
(d/update-when data :colors d/update-vals clear-color)))
|
||||
|
||||
(defmethod migrate-data "0009-clean-library-colors"
|
||||
[data _]
|
||||
(d/update-when data :colors
|
||||
(fn [colors]
|
||||
(reduce-kv (fn [colors id color]
|
||||
(if (types.color/valid-library-color? color)
|
||||
colors
|
||||
(dissoc colors id)))
|
||||
colors
|
||||
colors))))
|
||||
|
||||
(def available-migrations
|
||||
(into (d/ordered-set)
|
||||
["legacy-2"
|
||||
@@ -1379,7 +1582,13 @@
|
||||
"legacy-66"
|
||||
"legacy-67"
|
||||
"0001-remove-tokens-from-groups"
|
||||
"0002-normalize-bool-content"
|
||||
"0002-normalize-bool-content-v2"
|
||||
"0002-clean-shape-interactions"
|
||||
"0003-fix-root-shape"
|
||||
"0003-convert-path-content"]))
|
||||
"0003-convert-path-content-v2"
|
||||
"0004-clean-shadow-color"
|
||||
"0005-deprecate-image-type"
|
||||
"0006-fix-old-texts-fills"
|
||||
"0007-clear-invalid-strokes-and-fills-v2"
|
||||
"0008-fix-library-colors-v4"
|
||||
"0009-clean-library-colors"]))
|
||||
|
||||
@@ -91,10 +91,10 @@
|
||||
parent-id (or parent-id (get selected-obj :parent-id))
|
||||
base-parent (get objects parent-id)
|
||||
|
||||
layout-props
|
||||
layout-attrs
|
||||
(when (and (= 1 (count selected))
|
||||
(ctl/any-layout? base-parent))
|
||||
(select-keys selected-obj ctl/layout-item-props))
|
||||
(select-keys selected-obj ctl/layout-child-attrs))
|
||||
|
||||
target-cell-id
|
||||
(if (and (nil? target-cell-id)
|
||||
@@ -129,8 +129,8 @@
|
||||
:parent-id parent-id
|
||||
:shapes (into [] selected))
|
||||
|
||||
(some? layout-props)
|
||||
(d/patch-object layout-props)
|
||||
(some? layout-attrs)
|
||||
(d/patch-object layout-attrs)
|
||||
|
||||
;; Frames from shapes will not be displayed in viewer and no clipped
|
||||
(or (not= frame-id uuid/zero) without-fill?)
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.component :as ctc]
|
||||
[app.common.types.components-list :as ctcl]
|
||||
[app.common.types.variant :as ctv]
|
||||
[cuerdas.core :as str]))
|
||||
[app.common.types.variant :as ctv]))
|
||||
|
||||
|
||||
(defn find-variant-components
|
||||
@@ -21,11 +20,6 @@
|
||||
(map #(ctcl/get-component data % true))
|
||||
reverse))
|
||||
|
||||
(defn- dashes-to-end
|
||||
[property-values]
|
||||
(let [dashes (if (some #(= % "--") property-values) ["--"] [])]
|
||||
(concat (remove #(= % "--") property-values) dashes)))
|
||||
|
||||
|
||||
(defn extract-properties-names
|
||||
[shape data]
|
||||
@@ -42,10 +36,7 @@
|
||||
(group-by :name)
|
||||
(map (fn [[k v]]
|
||||
{:name k
|
||||
:value (->> v
|
||||
(map #(if (str/empty? (:value %)) "--" (:value %)))
|
||||
distinct
|
||||
dashes-to-end)}))))
|
||||
:value (->> v (map :value) distinct)}))))
|
||||
|
||||
(defn get-variant-mains
|
||||
[component data]
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
:terms-and-privacy-checkbox
|
||||
;; Only for developtment.
|
||||
:tiered-file-data-storage
|
||||
:token-units
|
||||
:transit-readable-response
|
||||
:user-feedback
|
||||
;; TODO: remove this flag.
|
||||
@@ -126,7 +127,8 @@
|
||||
:render-wasm-dpr
|
||||
:hide-release-modal
|
||||
:subscriptions
|
||||
:subscriptions-old})
|
||||
:subscriptions-old
|
||||
:frontend-binary-fills})
|
||||
|
||||
(def all-flags
|
||||
(set/union email login varia))
|
||||
|
||||
@@ -164,7 +164,6 @@
|
||||
(dm/export gtr/calculate-geometry)
|
||||
(dm/export gtr/update-group-selrect)
|
||||
(dm/export gtr/update-mask-selrect)
|
||||
(dm/export gtr/update-bool)
|
||||
(dm/export gtr/apply-transform)
|
||||
(dm/export gtr/transform-shape)
|
||||
(dm/export gtr/transform-selrect)
|
||||
|
||||
@@ -346,29 +346,32 @@
|
||||
|
||||
center (gco/points->center points)
|
||||
selrect (calculate-selrect points center)
|
||||
transform (calculate-transform points center selrect)
|
||||
inverse (when (some? transform) (gmt/inverse transform))]
|
||||
|
||||
(if-not (and (some? inverse) (some? transform))
|
||||
shape
|
||||
(let [type (dm/get-prop shape :type)
|
||||
rotation (mod (+ (d/nilv (:rotation shape) 0)
|
||||
(d/nilv (dm/get-in shape [:modifiers :rotation]) 0))
|
||||
360)
|
||||
[transform inverse]
|
||||
(let [transform (calculate-transform points center selrect)
|
||||
inverse (when (some? transform) (gmt/inverse transform))]
|
||||
(if (and (some? transform) (some? inverse))
|
||||
[transform inverse]
|
||||
[(:transform shape (gmt/matrix)) (:transform-inverse shape (gmt/matrix))]))
|
||||
|
||||
shape (if (or (= type :path) (= type :bool))
|
||||
(update shape :content path/transform-content transform-mtx)
|
||||
(assoc shape
|
||||
:x (dm/get-prop selrect :x)
|
||||
:y (dm/get-prop selrect :y)
|
||||
:width (dm/get-prop selrect :width)
|
||||
:height (dm/get-prop selrect :height)))]
|
||||
(-> shape
|
||||
(assoc :transform transform)
|
||||
(assoc :transform-inverse inverse)
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points)
|
||||
(assoc :rotation rotation))))))
|
||||
type (dm/get-prop shape :type)
|
||||
rotation (mod (+ (d/nilv (:rotation shape) 0)
|
||||
(d/nilv (dm/get-in shape [:modifiers :rotation]) 0))
|
||||
360)
|
||||
|
||||
shape (if (or (= type :path) (= type :bool))
|
||||
(update shape :content path/transform-content transform-mtx)
|
||||
(assoc shape
|
||||
:x (dm/get-prop selrect :x)
|
||||
:y (dm/get-prop selrect :y)
|
||||
:width (dm/get-prop selrect :width)
|
||||
:height (dm/get-prop selrect :height)))]
|
||||
(-> shape
|
||||
(assoc :transform transform)
|
||||
(assoc :transform-inverse inverse)
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points)
|
||||
(assoc :rotation rotation))))
|
||||
|
||||
(defn apply-transform
|
||||
"Given a new set of points transformed, set up the rectangle so it keeps
|
||||
@@ -453,13 +456,6 @@
|
||||
(assoc :flip-x (-> mask :flip-x))
|
||||
(assoc :flip-y (-> mask :flip-y)))))
|
||||
|
||||
(defn update-bool
|
||||
"Calculates the selrect+points for the boolean shape"
|
||||
[shape objects]
|
||||
(let [content (path/calc-bool-content shape objects)
|
||||
shape (assoc shape :content content)]
|
||||
(path/update-geometry shape)))
|
||||
|
||||
;; FIXME: revisit
|
||||
(defn update-shapes-geometry
|
||||
[objects ids]
|
||||
@@ -474,7 +470,7 @@
|
||||
(update-mask-selrect shape children)
|
||||
|
||||
(cfh/bool-shape? shape)
|
||||
(update-bool shape objects)
|
||||
(path/update-bool-shape shape objects)
|
||||
|
||||
(cfh/group-shape? shape)
|
||||
(update-group-selrect shape children)
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
(defn encode
|
||||
[data & {:as opts}]
|
||||
#?(:clj (j/write-str data opts)
|
||||
:cljs (.stringify js/JSON (->js data opts))))
|
||||
:cljs (.stringify js/JSON (->js data opts) nil (:indent opts))))
|
||||
|
||||
(defn decode
|
||||
[data & {:as opts}]
|
||||
|
||||
@@ -1663,24 +1663,29 @@
|
||||
{:type :reg-objects
|
||||
:shapes all-parents})]))))
|
||||
|
||||
|
||||
(defn- add-update-attr-operations
|
||||
[attr dest-shape origin-shape roperations uoperations touched]
|
||||
(let [;; position-data is a special case because can be affected by :geometry-group and :content-group
|
||||
(let [orig-value (get origin-shape attr)
|
||||
dest-value (get dest-shape attr)
|
||||
;; position-data is a special case because can be affected by :geometry-group and :content-group
|
||||
;; so, if the position-data changes but the geometry is touched we need to reset the position-data
|
||||
;; so it's calculated again
|
||||
reset-pos-data?
|
||||
(and (cfh/text-shape? origin-shape)
|
||||
(= attr :position-data)
|
||||
(not= (get origin-shape attr) (get dest-shape attr))
|
||||
(not= orig-value dest-value)
|
||||
(touched :geometry-group))
|
||||
|
||||
val (cond
|
||||
;; If position data changes and the geometry group is touched
|
||||
;; we need to put to nil so we can regenerate it
|
||||
reset-pos-data? nil
|
||||
:else orig-value)
|
||||
|
||||
roperation {:type :set
|
||||
:attr attr
|
||||
:val (cond
|
||||
;; If position data changes and the geometry group is touched
|
||||
;; we need to put to nil so we can regenerate it
|
||||
reset-pos-data? nil
|
||||
:else (get origin-shape attr))
|
||||
:val val
|
||||
:ignore-touched true}
|
||||
uoperation {:type :set
|
||||
:attr attr
|
||||
@@ -1731,10 +1736,13 @@
|
||||
:always
|
||||
(generate-update-tokens container dest-shape origin-shape touched omit-touched?))
|
||||
|
||||
(let [attr-group (get ctk/sync-attrs attr)
|
||||
(let [attr-group (get ctk/sync-attrs attr)
|
||||
skip-operations? (or (= (get origin-shape attr) (get dest-shape attr))
|
||||
(and (touched attr-group)
|
||||
omit-touched?))
|
||||
|
||||
[roperations' uoperations']
|
||||
(if (or (= (get origin-shape attr) (get dest-shape attr))
|
||||
(and (touched attr-group) omit-touched?))
|
||||
(if skip-operations?
|
||||
[roperations uoperations]
|
||||
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched))]
|
||||
(recur (next attrs)
|
||||
|
||||
@@ -16,19 +16,30 @@
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.text :as ctt]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.uuid :as uuid]))
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(defn- generate-unapply-tokens
|
||||
"When updating attributes that have a token applied, we must unapply it, because the value
|
||||
of the attribute now has been given directly, and does not come from the token."
|
||||
[changes objects changed-sub-attr]
|
||||
(let [mod-obj-changes (->> (:redo-changes changes)
|
||||
(let [new-objects (pcb/get-objects changes)
|
||||
mod-obj-changes (->> (:redo-changes changes)
|
||||
(filter #(= (:type %) :mod-obj)))
|
||||
|
||||
text-changed-attrs
|
||||
(fn [shape]
|
||||
(let [new-shape (get new-objects (:id shape))
|
||||
attrs (ctt/get-diff-attrs (:content shape) (:content new-shape))]
|
||||
(apply set/union (map cto/shape-attr->token-attrs attrs))))
|
||||
|
||||
check-attr (fn [shape changes attr]
|
||||
(let [tokens (get shape :applied-tokens {})
|
||||
token-attrs (cto/shape-attr->token-attrs attr changed-sub-attr)]
|
||||
token-attrs (if (or (not= (:type shape) :text) (not= attr :content))
|
||||
(cto/shape-attr->token-attrs attr changed-sub-attr)
|
||||
(text-changed-attrs shape))]
|
||||
(if (some #(contains? tokens %) token-attrs)
|
||||
(pcb/update-shapes changes [(:id shape)] #(cto/unapply-token-id % token-attrs))
|
||||
changes)))
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
(let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib)
|
||||
active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
|
||||
|
||||
prev-hidden-token-theme (ctob/get-hidden-theme tokens-lib)
|
||||
prev-hidden-theme (ctob/get-hidden-theme tokens-lib)
|
||||
|
||||
hidden-token-theme (-> (some-> prev-hidden-token-theme (ctob/set-sets active-token-set-names))
|
||||
(update-theme-fn))]
|
||||
hidden-theme (-> (some-> prev-hidden-theme (ctob/set-sets active-token-set-names))
|
||||
(update-theme-fn))]
|
||||
(-> changes
|
||||
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} prev-active-token-themes)
|
||||
(pcb/set-token-theme (:group prev-hidden-token-theme)
|
||||
(:name prev-hidden-token-theme)
|
||||
hidden-token-theme))))
|
||||
(pcb/update-active-token-themes #{(ctob/theme-path hidden-theme)} prev-active-token-themes)
|
||||
(pcb/set-token-theme (:group prev-hidden-theme)
|
||||
(:name prev-hidden-theme)
|
||||
hidden-theme))))
|
||||
|
||||
(defn generate-toggle-token-set
|
||||
"Toggle a token set at `set-name` in `tokens-lib` without modifying a
|
||||
|
||||
@@ -9,11 +9,18 @@
|
||||
(:require
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
;; We have added ".ttf" as string to solve a problem with chrome input selector
|
||||
(def valid-font-types #{"font/ttf" ".ttf" "font/woff", "application/font-woff" "woff" "font/otf" ".otf" "font/opentype"})
|
||||
(def valid-image-types #{"image/jpeg", "image/png", "image/webp", "image/gif", "image/svg+xml"})
|
||||
(def str-image-types (str/join "," valid-image-types))
|
||||
(def str-font-types (str/join "," valid-font-types))
|
||||
(def font-types
|
||||
#{"font/ttf"
|
||||
"font/woff"
|
||||
"font/otf"
|
||||
"font/opentype"})
|
||||
|
||||
(def image-types
|
||||
#{"image/jpeg"
|
||||
"image/png"
|
||||
"image/webp"
|
||||
"image/gif"
|
||||
"image/svg+xml"})
|
||||
|
||||
(defn format->extension
|
||||
[format]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.common.schema
|
||||
(:refer-clojure :exclude [deref merge parse-uuid parse-long parse-double parse-boolean type])
|
||||
(:refer-clojure :exclude [deref merge parse-uuid parse-long parse-double parse-boolean type keys])
|
||||
#?(:cljs (:require-macros [app.common.schema :refer [ignoring]]))
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
@@ -118,6 +118,21 @@
|
||||
[& transformers]
|
||||
(apply mt/transformer transformers))
|
||||
|
||||
(defn entries
|
||||
"Get map entires of a map schema"
|
||||
[schema]
|
||||
(m/entries schema default-options))
|
||||
|
||||
(def ^:private xf:map-key
|
||||
(map key))
|
||||
|
||||
(defn keys
|
||||
"Given a map schema, return all keys as set"
|
||||
[schema]
|
||||
(->> (entries schema)
|
||||
(into #{} xf:map-key)))
|
||||
|
||||
|
||||
;; (defn key-transformer
|
||||
;; [& {:as opts}]
|
||||
;; (mt/key-transformer opts))
|
||||
@@ -702,7 +717,10 @@
|
||||
(fn [v]
|
||||
(and (pred v)
|
||||
(>= max v)))
|
||||
pred)]
|
||||
pred)
|
||||
|
||||
gen (or (get props :gen/gen)
|
||||
(sg/small-int :max max :min min))]
|
||||
|
||||
{:pred pred
|
||||
:type-properties
|
||||
@@ -710,7 +728,7 @@
|
||||
:description "int"
|
||||
:error/message "expected to be int/long"
|
||||
:error/code "errors.invalid-integer"
|
||||
:gen/gen (sg/small-int :max max :min min)
|
||||
:gen/gen gen
|
||||
:decode/string parse-long
|
||||
:decode/json parse-long
|
||||
::oapi/type "integer"
|
||||
@@ -768,10 +786,11 @@
|
||||
(>= max v)))
|
||||
pred)
|
||||
|
||||
gen (sg/one-of
|
||||
(sg/small-int :max max :min min)
|
||||
(->> (sg/small-double :max max :min min)
|
||||
(sg/fmap #(mth/precision % 2))))]
|
||||
gen (or (get props :gen/gen)
|
||||
(sg/one-of
|
||||
(sg/small-int :max max :min min)
|
||||
(->> (sg/small-double :max max :min min)
|
||||
(sg/fmap #(mth/precision % 2)))))]
|
||||
|
||||
{:pred pred
|
||||
:type-properties
|
||||
@@ -786,7 +805,9 @@
|
||||
|
||||
(register! ::safe-int [::int {:max max-safe-int :min min-safe-int}])
|
||||
(register! ::safe-double [::double {:max max-safe-int :min min-safe-int}])
|
||||
(register! ::safe-number [::number {:max max-safe-int :min min-safe-int}])
|
||||
(register! ::safe-number [::number {:gen/gen (sg/small-double)
|
||||
:max max-safe-int
|
||||
:min min-safe-int}])
|
||||
|
||||
(defn parse-boolean
|
||||
[v]
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:refer-clojure :exclude [set subseq uuid filter map let boolean vector keyword int double])
|
||||
#?(:cljs (:require-macros [app.common.schema.generators]))
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.common.schema.registry :as sr]
|
||||
[app.common.uri :as u]
|
||||
[app.common.uuid :as uuid]
|
||||
@@ -40,7 +41,8 @@
|
||||
|
||||
(defn small-double
|
||||
[& {:keys [min max] :or {min -100 max 100}}]
|
||||
(tg/double* {:min min, :max max, :infinite? false, :NaN? false}))
|
||||
(->> (tg/double* {:min min, :max max, :infinite? false, :NaN? false})
|
||||
(tg/fmap #(mth/precision % 2))))
|
||||
|
||||
(defn small-int
|
||||
[& {:keys [min max] :or {min -100 max 100}}]
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
[app.common.test-helpers.components :as thc]
|
||||
[app.common.test-helpers.files :as thf]
|
||||
[app.common.test-helpers.shapes :as ths]
|
||||
[app.common.types.container :as ctn]))
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.shape :as cts]))
|
||||
|
||||
;; ----- File building
|
||||
|
||||
@@ -27,6 +29,14 @@
|
||||
:name "Rect1"}
|
||||
params)))
|
||||
|
||||
(defn add-text
|
||||
[file text-label content & {:keys [text-params] :as text}]
|
||||
(let [shape (-> (cts/setup-shape {:type :text :x 0 :y 0})
|
||||
(txt/change-text content))]
|
||||
(ths/add-sample-shape file text-label
|
||||
(merge shape
|
||||
text-params))))
|
||||
|
||||
(defn add-frame
|
||||
[file frame-label & {:keys [] :as params}]
|
||||
;; Generated shape tree:
|
||||
@@ -58,6 +68,18 @@
|
||||
:parent-label frame-label}
|
||||
child-params))))
|
||||
|
||||
(defn add-frame-with-text
|
||||
[file frame-label child-label text & {:keys [frame-params child-params]}]
|
||||
(let [shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width})
|
||||
(txt/change-text text)
|
||||
(assoc :position-data nil
|
||||
:parent-label frame-label))]
|
||||
(-> file
|
||||
(add-frame frame-label frame-params)
|
||||
(ths/add-sample-shape child-label
|
||||
(merge shape
|
||||
child-params)))))
|
||||
|
||||
(defn add-minimal-component
|
||||
[file component-label root-label
|
||||
& {:keys [component-params root-params]}]
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
(ns app.common.test-helpers.shapes
|
||||
(:require
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data :as d]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.test-helpers.files :as thf]
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.colors-list :as ctcl]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape :as cts]
|
||||
@@ -81,9 +82,31 @@
|
||||
(:id page)
|
||||
#(ctst/set-shape % (ctn/set-shape-attr shape attr val)))))))
|
||||
|
||||
(defn sample-color
|
||||
[label & {:keys [] :as params}]
|
||||
(ctc/make-color (assoc params :id (thi/new-id! label))))
|
||||
(defn update-shape-text
|
||||
[file shape-label attr val & {:keys [page-label]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
shape (ctst/get-shape page (thi/id shape-label))]
|
||||
(update file :data
|
||||
(fn [file-data]
|
||||
(ctpl/update-page file-data
|
||||
(:id page)
|
||||
#(ctst/set-shape % (txt/update-text-content shape
|
||||
txt/is-content-node?
|
||||
d/txt-merge
|
||||
{attr val})))))))
|
||||
|
||||
(defn sample-library-color
|
||||
[label & {:keys [name path color opacity gradient image]}]
|
||||
(-> {:id (thi/new-id! label)
|
||||
:name (or name color "Black")
|
||||
:path path
|
||||
:color (or color "#000000")
|
||||
:opacity (or opacity 1)
|
||||
:gradient gradient
|
||||
:image image}
|
||||
(d/without-nils)))
|
||||
|
||||
(defn sample-fill-color
|
||||
[& {:keys [fill-color fill-opacity] :as params}]
|
||||
@@ -101,8 +124,8 @@
|
||||
|
||||
(defn add-sample-library-color
|
||||
[file label & {:keys [] :as params}]
|
||||
(let [color (sample-color label params)]
|
||||
(update file :data ctcl/add-color color)))
|
||||
(let [color (sample-library-color label params)]
|
||||
(update file :data ctc/add-color color)))
|
||||
|
||||
(defn sample-typography
|
||||
[label & {:keys [] :as params}]
|
||||
@@ -126,4 +149,4 @@
|
||||
(fn [file-data]
|
||||
(ctpl/update-page file-data
|
||||
(:id page)
|
||||
#(ctst/set-shape % (assoc origin :interactions interactions)))))))
|
||||
#(ctst/set-shape % (assoc origin :interactions interactions)))))))
|
||||
@@ -36,9 +36,9 @@
|
||||
(ctob/get-token token-name)))))
|
||||
|
||||
(defn token-data-eq?
|
||||
"Compare token data without comparing modified timestamp"
|
||||
"Compare token data without comparing unstable fields."
|
||||
[t1 t2]
|
||||
(= (dissoc t1 :modified-at) (dissoc t2 :modified-at)))
|
||||
(= (dissoc t1 :id :modified-at) (dissoc t2 :id :modified-at)))
|
||||
|
||||
(defn- set-stroke-width
|
||||
[shape stroke-width]
|
||||
|
||||
@@ -121,30 +121,6 @@
|
||||
{:name "Source Sans Pro Regular"}
|
||||
(select-keys default-text-attrs typography-fields)))
|
||||
|
||||
(defn transform-nodes
|
||||
([transform root]
|
||||
(transform-nodes identity transform root))
|
||||
([pred transform root]
|
||||
(walk/postwalk
|
||||
(fn [item]
|
||||
(if (and (map? item) (pred item))
|
||||
(transform item)
|
||||
item))
|
||||
root)))
|
||||
|
||||
(defn xform-nodes
|
||||
"The same as transform but instead of receiving a funcion, receives
|
||||
a transducer."
|
||||
[xf root]
|
||||
(let [rf (fn [_ v] v)]
|
||||
(walk/postwalk
|
||||
(fn [item]
|
||||
(let [rf (xf rf)]
|
||||
(if (map? item)
|
||||
(d/nilv (rf nil item) item)
|
||||
item)))
|
||||
root)))
|
||||
|
||||
(defn node-seq
|
||||
([root] (node-seq identity root))
|
||||
([match? root]
|
||||
@@ -154,8 +130,12 @@
|
||||
|
||||
(defn is-text-node?
|
||||
[node]
|
||||
(and (string? (:text node))
|
||||
(not= (:text node) "")))
|
||||
(and (nil? (:type node))
|
||||
(string? (:text node))))
|
||||
|
||||
(defn is-paragraph-set-node?
|
||||
[node]
|
||||
(= "paragraph-set" (:type node)))
|
||||
|
||||
(defn is-paragraph-node?
|
||||
[node]
|
||||
@@ -165,6 +145,38 @@
|
||||
[node]
|
||||
(= "root" (:type node)))
|
||||
|
||||
(defn is-node?
|
||||
[node]
|
||||
(or ^boolean (is-text-node? node)
|
||||
^boolean (is-paragraph-node? node)
|
||||
^boolean (is-paragraph-set-node? node)
|
||||
^boolean (is-root-node? node)))
|
||||
|
||||
(defn is-content-node?
|
||||
"Only matches content nodes, ignoring the paragraph-set nodes."
|
||||
[node]
|
||||
(or ^boolean (is-text-node? node)
|
||||
^boolean (is-paragraph-node? node)
|
||||
^boolean (is-root-node? node)))
|
||||
|
||||
(defn transform-nodes
|
||||
([transform root]
|
||||
(transform-nodes identity transform root))
|
||||
([pred transform root]
|
||||
(walk/postwalk
|
||||
(fn [item]
|
||||
(if (and (is-node? item) (pred item))
|
||||
(transform item)
|
||||
item))
|
||||
root)))
|
||||
|
||||
(defn update-text-content
|
||||
[shape pred-fn update-fn attrs]
|
||||
(let [update-attrs-fn #(update-fn % attrs)
|
||||
transform #(transform-nodes pred-fn update-attrs-fn %)]
|
||||
(-> shape
|
||||
(update :content transform))))
|
||||
|
||||
(defn generate-shape-name
|
||||
[text]
|
||||
(subs text 0 (min 280 (count text))))
|
||||
|
||||
@@ -208,9 +208,13 @@
|
||||
([data] (encode-str data nil))
|
||||
([data opts]
|
||||
#?(:cljs
|
||||
(let [t (:type opts :json)
|
||||
w (t/writer t {:handlers @write-handler-map})]
|
||||
(t/write w data))
|
||||
(let [type (:type opts :json)
|
||||
params {:handlers @write-handler-map}
|
||||
params (if (:with-meta opts)
|
||||
(assoc params :transform t/write-meta)
|
||||
params)
|
||||
writer (t/writer type params)]
|
||||
(t/write writer data))
|
||||
:clj
|
||||
(->> (encode data opts)
|
||||
(bytes->str)))))
|
||||
@@ -219,9 +223,10 @@
|
||||
([data] (decode-str data nil))
|
||||
([data opts]
|
||||
#?(:cljs
|
||||
(let [t (:type opts :json)
|
||||
r (t/reader t {:handlers @read-handler-map})]
|
||||
(t/read r data))
|
||||
(let [type (:type opts :json)
|
||||
params {:handlers @read-handler-map}
|
||||
reader (t/reader type params)]
|
||||
(t/read reader data))
|
||||
:clj
|
||||
(-> (str->bytes data)
|
||||
(decode opts)))))
|
||||
|
||||
@@ -8,23 +8,36 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.media :as cm]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.schema.generators :as sg]
|
||||
[app.common.schema.openapi :as-alias oapi]
|
||||
[app.common.text :as txt]
|
||||
[app.common.time :as dt]
|
||||
[app.common.types.plugins :as ctpg]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMAS & TYPES
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def rgb-color-re
|
||||
(def ^:private required-color-attrs
|
||||
"A set used for proper check if color should contain only one of the
|
||||
attrs listed in this set."
|
||||
#{:image :gradient :color})
|
||||
|
||||
(defn has-valid-color-attrs?
|
||||
"Check if color has correct color attrs"
|
||||
[color]
|
||||
(let [attrs (set (keys color))
|
||||
result (set/intersection attrs required-color-attrs)]
|
||||
(= 1 (count result))))
|
||||
|
||||
(def ^:private hex-color-rx
|
||||
#"^#(?:[0-9a-fA-F]{3}){1,2}$")
|
||||
|
||||
(defn- generate-rgb-color
|
||||
[]
|
||||
(def ^:private hex-color-generator
|
||||
(sg/fmap (fn [_]
|
||||
#?(:clj (format "#%06x" (rand-int 16rFFFFFF))
|
||||
:cljs
|
||||
@@ -37,38 +50,48 @@
|
||||
(.. b (toString 16) (padStart 2 "0"))))))
|
||||
sg/int))
|
||||
|
||||
(defn rgb-color-string?
|
||||
(defn hex-color-string?
|
||||
[o]
|
||||
(and (string? o) (some? (re-matches rgb-color-re o))))
|
||||
(and (string? o) (some? (re-matches hex-color-rx o))))
|
||||
|
||||
(def schema:rgb-color
|
||||
(def schema:hex-color
|
||||
(sm/register!
|
||||
{:type ::rgb-color
|
||||
:pred rgb-color-string?
|
||||
{:type ::hex-color
|
||||
:pred hex-color-string?
|
||||
:type-properties
|
||||
{:title "rgb-color"
|
||||
:description "RGB Color String"
|
||||
:error/message "expected a valid RGB color"
|
||||
:error/code "errors.invalid-rgb-color"
|
||||
:gen/gen (generate-rgb-color)
|
||||
{:title "hex-color"
|
||||
:description "HEX Color String"
|
||||
:error/message "expected a valid HEX color"
|
||||
:error/code "errors.invalid-hex-color"
|
||||
:gen/gen hex-color-generator
|
||||
::oapi/type "integer"
|
||||
::oapi/format "int64"}}))
|
||||
|
||||
(def schema:plain-color
|
||||
[:map [:color schema:hex-color]])
|
||||
|
||||
(def schema:image
|
||||
[:map {:title "ImageColor"}
|
||||
[:width ::sm/int]
|
||||
[:height ::sm/int]
|
||||
[:mtype ::sm/text]
|
||||
[:map {:title "ImageColor" :closed true}
|
||||
[:width [::sm/int {:min 0 :gen/gen sg/int}]]
|
||||
[:height [::sm/int {:min 0 :gen/gen sg/int}]]
|
||||
[:mtype {:gen/gen (sg/elements cm/image-types)} ::sm/text]
|
||||
[:id ::sm/uuid]
|
||||
[:name {:optional true} ::sm/text]
|
||||
[:keep-aspect-ratio {:optional true} :boolean]])
|
||||
|
||||
(def image-attrs
|
||||
"A set of attrs that corresponds to image data type"
|
||||
(sm/keys schema:image))
|
||||
|
||||
(def schema:image-color
|
||||
[:map [:image schema:image]])
|
||||
|
||||
(def gradient-types
|
||||
#{:linear :radial})
|
||||
|
||||
(def schema:gradient
|
||||
[:map {:title "Gradient"}
|
||||
[:type [::sm/one-of #{:linear :radial}]]
|
||||
[:map {:title "Gradient" :closed true}
|
||||
[:type [::sm/one-of gradient-types]]
|
||||
[:start-x ::sm/safe-number]
|
||||
[:start-y ::sm/safe-number]
|
||||
[:end-x ::sm/safe-number]
|
||||
@@ -77,86 +100,90 @@
|
||||
[:stops
|
||||
[:vector {:min 1 :gen/max 2}
|
||||
[:map {:title "GradientStop"}
|
||||
[:color schema:rgb-color]
|
||||
[:opacity {:optional true} [:maybe ::sm/safe-number]]
|
||||
[:offset ::sm/safe-number]]]]])
|
||||
[:color schema:hex-color]
|
||||
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
|
||||
[:offset [::sm/number {:min 0 :max 1}]]]]]])
|
||||
|
||||
(def gradient-attrs
|
||||
"A set of attrs that corresponds to gradient data type"
|
||||
(sm/keys schema:gradient))
|
||||
|
||||
(def schema:gradient-color
|
||||
[:map [:gradient schema:gradient]])
|
||||
|
||||
(def schema:color-attrs
|
||||
[:map {:title "ColorAttrs"}
|
||||
[:id {:optional true} ::sm/uuid]
|
||||
[:name {:optional true} :string]
|
||||
[:path {:optional true} [:maybe :string]]
|
||||
[:value {:optional true} [:maybe :string]]
|
||||
[:color {:optional true} [:maybe schema:rgb-color]]
|
||||
[:opacity {:optional true} [:maybe ::sm/safe-number]]
|
||||
[:modified-at {:optional true} ::sm/inst]
|
||||
[:map {:title "ColorAttrs" :closed true}
|
||||
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
|
||||
[:ref-id {:optional true} ::sm/uuid]
|
||||
[:ref-file {:optional true} ::sm/uuid]
|
||||
[:gradient {:optional true} [:maybe schema:gradient]]
|
||||
[:image {:optional true} [:maybe schema:image]]
|
||||
[:ref-file {:optional true} ::sm/uuid]])
|
||||
|
||||
;; This schema represent an "applied color"
|
||||
(def schema:color
|
||||
[:and
|
||||
[:merge {:title "Color"}
|
||||
schema:color-attrs
|
||||
(sm/optional-keys schema:plain-color)
|
||||
(sm/optional-keys schema:gradient-color)
|
||||
(sm/optional-keys schema:image-color)]
|
||||
[:fn has-valid-color-attrs?]])
|
||||
|
||||
(def color-attrs
|
||||
(into required-color-attrs (sm/keys schema:color-attrs)))
|
||||
|
||||
(def schema:library-color-attrs
|
||||
[:map {:title "ColorAttrs" :closed true}
|
||||
[:id ::sm/uuid]
|
||||
[:name ::sm/text]
|
||||
[:path {:optional true} :string]
|
||||
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
|
||||
[:modified-at {:optional true} ::sm/inst]
|
||||
[:plugin-data {:optional true} ::ctpg/plugin-data]])
|
||||
|
||||
(def schema:color
|
||||
[:and schema:color-attrs
|
||||
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
||||
|
||||
(def schema:recent-color
|
||||
[:and
|
||||
[:map {:title "RecentColor"}
|
||||
[:opacity {:optional true} [:maybe ::sm/safe-number]]
|
||||
[:color {:optional true} [:maybe schema:rgb-color]]
|
||||
[:gradient {:optional true} [:maybe schema:gradient]]
|
||||
[:image {:optional true} [:maybe schema:image]]]
|
||||
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
||||
|
||||
;; Same as color but with :id prop required
|
||||
(def schema:library-color
|
||||
"Used for in-transit representation of a color (per example when user
|
||||
clicks a color on assets sidebar, the color should be properly identified with
|
||||
the file-id where it belongs)"
|
||||
[:and
|
||||
(sm/required-keys schema:color-attrs [:id])
|
||||
[::sm/contains-any {:strict true} [:color :gradient :image]]])
|
||||
[:merge
|
||||
schema:library-color-attrs
|
||||
(sm/optional-keys schema:plain-color)
|
||||
(sm/optional-keys schema:gradient-color)
|
||||
(sm/optional-keys schema:image-color)]
|
||||
[:fn has-valid-color-attrs?]])
|
||||
|
||||
;; FIXME: revisit if we really need this all registers
|
||||
(sm/register! ::color schema:color)
|
||||
(sm/register! ::gradient schema:gradient)
|
||||
(sm/register! ::image-color schema:image)
|
||||
(sm/register! ::recent-color schema:recent-color)
|
||||
(sm/register! ::color-attrs schema:color-attrs)
|
||||
(def library-color-attrs
|
||||
(into required-color-attrs (sm/keys schema:library-color-attrs)))
|
||||
|
||||
(def valid-color?
|
||||
(sm/lazy-validator schema:color))
|
||||
|
||||
(def valid-library-color?
|
||||
(sm/lazy-validator schema:library-color))
|
||||
|
||||
(def check-color
|
||||
(sm/check-fn schema:color :hint "expected valid color"))
|
||||
|
||||
(def check-library-color
|
||||
(sm/check-fn schema:library-color :hint "expected valid library color"))
|
||||
|
||||
(def check-recent-color
|
||||
(sm/check-fn schema:recent-color :hint "expected valid recent color"))
|
||||
(sm/check-fn schema:library-color :hint "expected valid color"))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HELPERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- factory
|
||||
|
||||
(defn make-color
|
||||
[{:keys [id name path value color opacity ref-id ref-file gradient image]}]
|
||||
(-> {:id (or id (uuid/next))
|
||||
:name (or name color "Black")
|
||||
:path path
|
||||
:value value
|
||||
:color (or color "#000000")
|
||||
:opacity (or opacity 1)
|
||||
:ref-id ref-id
|
||||
:ref-file ref-file
|
||||
:gradient gradient
|
||||
:image image}
|
||||
(d/without-nils)))
|
||||
(defn library-color->color
|
||||
"Converts a library color data structure to a plain color data structure"
|
||||
[lcolor file-id]
|
||||
(-> lcolor
|
||||
(select-keys [:image :gradient :color :opacity])
|
||||
(assoc :ref-id (get lcolor :id))
|
||||
(assoc :ref-file file-id)
|
||||
(vary-meta assoc
|
||||
:path (get lcolor :path)
|
||||
:name (get lcolor :name))))
|
||||
|
||||
;; --- fill
|
||||
|
||||
(defn fill->shape-color
|
||||
(defn fill->color
|
||||
[fill]
|
||||
(d/without-nils
|
||||
{:color (:fill-color fill)
|
||||
@@ -178,126 +205,130 @@
|
||||
|
||||
(defn attach-fill-color
|
||||
[shape position ref-id ref-file]
|
||||
(-> shape
|
||||
(assoc-in [:fills position :fill-color-ref-id] ref-id)
|
||||
(assoc-in [:fills position :fill-color-ref-file] ref-file)))
|
||||
(d/update-in-when shape [:fills position]
|
||||
(fn [fill]
|
||||
(-> fill
|
||||
(assoc :fill-color-ref-file ref-file)
|
||||
(assoc :fill-color-ref-id ref-id)))))
|
||||
|
||||
(defn detach-fill-color
|
||||
[shape position]
|
||||
(-> shape
|
||||
(d/dissoc-in [:fills position :fill-color-ref-id])
|
||||
(d/dissoc-in [:fills position :fill-color-ref-file])))
|
||||
(d/update-in-when shape [:fills position] dissoc :fill-color-ref-id :fill-color-ref-file))
|
||||
|
||||
;; stroke
|
||||
|
||||
(defn stroke->shape-color
|
||||
(defn stroke->color
|
||||
[stroke]
|
||||
(d/without-nils {:color (:stroke-color stroke)
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)
|
||||
:ref-id (:stroke-color-ref-id stroke)
|
||||
:ref-file (:stroke-color-ref-file stroke)}))
|
||||
(d/without-nils
|
||||
{:color (str/lower (:stroke-color stroke))
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:gradient (:stroke-color-gradient stroke)
|
||||
:image (:stroke-image stroke)
|
||||
:ref-id (:stroke-color-ref-id stroke)
|
||||
:ref-file (:stroke-color-ref-file stroke)}))
|
||||
|
||||
(defn set-stroke-color
|
||||
[shape position color opacity gradient image]
|
||||
(update-in shape [:strokes position]
|
||||
(fn [stroke]
|
||||
(d/without-nils (assoc stroke
|
||||
:stroke-color color
|
||||
:stroke-opacity opacity
|
||||
:stroke-color-gradient gradient
|
||||
:stroke-image image)))))
|
||||
(d/update-in-when shape [:strokes position]
|
||||
(fn [stroke]
|
||||
(-> stroke
|
||||
(assoc :stroke-color color)
|
||||
(assoc :stroke-opacity opacity)
|
||||
(assoc :stroke-color-gradient gradient)
|
||||
(assoc :stroke-image image)
|
||||
(d/without-nils)))))
|
||||
|
||||
(defn attach-stroke-color
|
||||
[shape position ref-id ref-file]
|
||||
(-> shape
|
||||
(assoc-in [:strokes position :stroke-color-ref-id] ref-id)
|
||||
(assoc-in [:strokes position :stroke-color-ref-file] ref-file)))
|
||||
(d/update-in-when shape [:strokes position]
|
||||
(fn [stroke]
|
||||
(-> stroke
|
||||
(assoc :stroke-color-ref-id ref-id)
|
||||
(assoc :stroke-color-ref-file ref-file)))))
|
||||
|
||||
(defn detach-stroke-color
|
||||
[shape position]
|
||||
(-> shape
|
||||
(d/dissoc-in [:strokes position :stroke-color-ref-id])
|
||||
(d/dissoc-in [:strokes position :stroke-color-ref-file])))
|
||||
(d/update-in-when shape [:strokes position] dissoc :stroke-color-ref-id :stroke-color-ref-file))
|
||||
|
||||
;; shadow
|
||||
|
||||
(defn shadow->shape-color
|
||||
(defn shadow->color
|
||||
[shadow]
|
||||
(d/without-nils {:color (-> shadow :color :color)
|
||||
:opacity (-> shadow :color :opacity)
|
||||
:gradient (-> shadow :color :gradient)
|
||||
:ref-id (-> shadow :color :id)
|
||||
:ref-file (-> shadow :color :file-id)}))
|
||||
(:color shadow))
|
||||
|
||||
(defn set-shadow-color
|
||||
[shape position color opacity gradient]
|
||||
(update-in shape [:shadow position :color]
|
||||
(fn [shadow-color]
|
||||
(d/without-nils (assoc shadow-color
|
||||
:color color
|
||||
:opacity opacity
|
||||
:gradient gradient)))))
|
||||
(d/update-in-when shape [:shadow position :color]
|
||||
(fn [shadow-color]
|
||||
(-> shadow-color
|
||||
(assoc :color color)
|
||||
(assoc :opacity opacity)
|
||||
(assoc :gradient gradient)
|
||||
(d/without-nils)))))
|
||||
|
||||
(defn attach-shadow-color
|
||||
[shape position ref-id ref-file]
|
||||
(-> shape
|
||||
(assoc-in [:shadow position :color :id] ref-id)
|
||||
(assoc-in [:shadow position :color :file-id] ref-file)))
|
||||
(d/update-in-when shape [:shadow position :color]
|
||||
(fn [color]
|
||||
(-> color
|
||||
(assoc :ref-id ref-id)
|
||||
(assoc :ref-file ref-file)))))
|
||||
|
||||
(defn detach-shadow-color
|
||||
[shape position]
|
||||
(-> shape
|
||||
(d/dissoc-in [:shadow position :color :id])
|
||||
(d/dissoc-in [:shadow position :color :file-id])))
|
||||
(d/update-in-when shape [:shadow position :color] dissoc :ref-id :ref-file))
|
||||
|
||||
;; grid
|
||||
|
||||
(defn grid->shape-color
|
||||
;: FIXME: revisit colors...... WTF
|
||||
(defn grid->color
|
||||
[grid]
|
||||
(d/without-nils {:color (-> grid :params :color :color)
|
||||
:opacity (-> grid :params :color :opacity)
|
||||
:gradient (-> grid :params :color :gradient)
|
||||
:ref-id (-> grid :params :color :id)
|
||||
:ref-file (-> grid :params :color :file-id)}))
|
||||
(let [color (-> grid :params :color)]
|
||||
(d/without-nils
|
||||
{:color (-> color :color)
|
||||
:opacity (-> color :opacity)
|
||||
:gradient (-> color :gradient)
|
||||
:ref-id (-> color :id)
|
||||
:ref-file (-> color :file-id)})))
|
||||
|
||||
(defn set-grid-color
|
||||
[shape position color opacity gradient]
|
||||
(update-in shape [:grids position :params :color]
|
||||
(fn [grid-color]
|
||||
(d/without-nils (assoc grid-color
|
||||
:color color
|
||||
:opacity opacity
|
||||
:gradient gradient)))))
|
||||
(d/update-in-when shape [:grids position :params :color]
|
||||
(fn [grid-color]
|
||||
(-> grid-color
|
||||
(assoc :color color)
|
||||
(assoc :opacity opacity)
|
||||
(assoc :gradient gradient)
|
||||
(d/without-nils)))))
|
||||
|
||||
(defn attach-grid-color
|
||||
[shape position ref-id ref-file]
|
||||
(-> shape
|
||||
(assoc-in [:grids position :params :color :id] ref-id)
|
||||
(assoc-in [:grids position :params :color :file-id] ref-file)))
|
||||
(d/update-in-when shape [:grids position :params :color]
|
||||
(fn [color]
|
||||
(-> color
|
||||
(assoc :ref-id ref-id)
|
||||
(assoc :ref-file ref-file)))))
|
||||
|
||||
(defn detach-grid-color
|
||||
[shape position]
|
||||
(-> shape
|
||||
(d/dissoc-in [:grids position :params :color :id])
|
||||
(d/dissoc-in [:grids position :params :color :file-id])))
|
||||
(d/update-in-when shape [:grids position :params :color] dissoc :ref-id :ref-file))
|
||||
|
||||
;; --- Helpers for all colors in a shape
|
||||
|
||||
(defn get-text-node-colors
|
||||
"Get all colors used by a node of a text shape"
|
||||
[node]
|
||||
(concat (map fill->shape-color (:fills node))
|
||||
(map stroke->shape-color (:strokes node))))
|
||||
(concat (map fill->color (:fills node))
|
||||
(map stroke->color (:strokes node))))
|
||||
|
||||
(defn get-all-colors
|
||||
"Get all colors used by a shape, in any section."
|
||||
[shape]
|
||||
(concat (map fill->shape-color (:fills shape))
|
||||
(map stroke->shape-color (:strokes shape))
|
||||
(map shadow->shape-color (:shadow shape))
|
||||
(concat (map fill->color (:fills shape))
|
||||
(map stroke->color (:strokes shape))
|
||||
(map shadow->color (:shadow shape))
|
||||
(when (= (:type shape) :frame)
|
||||
(map grid->shape-color (:grids shape)))
|
||||
(map grid->color (:grids shape)))
|
||||
(when (= (:type shape) :text)
|
||||
(reduce (fn [colors node]
|
||||
(concat colors (get-text-node-colors node)))
|
||||
@@ -326,7 +357,7 @@
|
||||
(let [process-fill (fn [shape [position fill]]
|
||||
(process-fn shape
|
||||
position
|
||||
(fill->shape-color fill)
|
||||
(fill->color fill)
|
||||
set-fill-color
|
||||
attach-fill-color
|
||||
detach-fill-color))
|
||||
@@ -334,7 +365,7 @@
|
||||
process-stroke (fn [shape [position stroke]]
|
||||
(process-fn shape
|
||||
position
|
||||
(stroke->shape-color stroke)
|
||||
(stroke->color stroke)
|
||||
set-stroke-color
|
||||
attach-stroke-color
|
||||
detach-stroke-color))
|
||||
@@ -342,7 +373,7 @@
|
||||
process-shadow (fn [shape [position shadow]]
|
||||
(process-fn shape
|
||||
position
|
||||
(shadow->shape-color shadow)
|
||||
(shadow->color shadow)
|
||||
set-shadow-color
|
||||
attach-shadow-color
|
||||
detach-shadow-color))
|
||||
@@ -350,7 +381,7 @@
|
||||
process-grid (fn [shape [position grid]]
|
||||
(process-fn shape
|
||||
position
|
||||
(grid->shape-color grid)
|
||||
(grid->color grid)
|
||||
set-grid-color
|
||||
attach-grid-color
|
||||
detach-grid-color))
|
||||
@@ -407,114 +438,76 @@
|
||||
|
||||
(process-shape-colors shape sync-color)))
|
||||
|
||||
(defn- eq-recent-color?
|
||||
[c1 c2]
|
||||
(or (= c1 c2)
|
||||
(and (some? (:color c1))
|
||||
(some? (:color c2))
|
||||
(= (:color c1) (:color c2)))))
|
||||
|
||||
(defn add-recent-color
|
||||
"Moves the color to the top of the list and then truncates up to 15"
|
||||
[state file-id color]
|
||||
(update state file-id (fn [colors]
|
||||
(let [colors (d/removev (partial eq-recent-color? color) colors)
|
||||
colors (conj colors color)]
|
||||
(cond-> colors
|
||||
(> (count colors) 15)
|
||||
(subvec 1))))))
|
||||
|
||||
(defn stroke->color-att
|
||||
[stroke file-id shared-libs]
|
||||
(let [color-file-id (:stroke-color-ref-file stroke)
|
||||
color-id (:stroke-color-ref-id stroke)
|
||||
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
|
||||
is-shared? (contains? shared-libs-colors color-id)
|
||||
has-color? (or (not (nil? (:stroke-color stroke))) (not (nil? (:stroke-color-gradient stroke))))
|
||||
attrs (if (or is-shared? (= color-file-id file-id))
|
||||
(d/without-nils {:color (str/lower (:stroke-color stroke))
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:id color-id
|
||||
:file-id color-file-id
|
||||
:gradient (:stroke-color-gradient stroke)})
|
||||
(d/without-nils {:color (str/lower (:stroke-color stroke))
|
||||
:opacity (:stroke-opacity stroke)
|
||||
:gradient (:stroke-color-gradient stroke)}))]
|
||||
(defn- stroke->color-att
|
||||
[stroke file-id libraries]
|
||||
(let [ref-file (:stroke-color-ref-file stroke)
|
||||
ref-id (:stroke-color-ref-id stroke)
|
||||
shared-colors (dm/get-in libraries [ref-file :data :colors])
|
||||
is-shared? (contains? shared-colors ref-id)
|
||||
has-color? (or (:stroke-color stroke)
|
||||
(:stroke-color-gradient stroke))
|
||||
attrs (cond-> (stroke->color stroke)
|
||||
(not (or is-shared? (= ref-file file-id)))
|
||||
(dissoc :ref-id :ref-file))]
|
||||
(when has-color?
|
||||
{:attrs attrs
|
||||
:prop :stroke
|
||||
:shape-id (:shape-id stroke)
|
||||
:index (:index stroke)})))
|
||||
|
||||
(defn shadow->color-att
|
||||
[shadow file-id shared-libs]
|
||||
(let [color-file-id (dm/get-in shadow [:color :file-id])
|
||||
color-id (dm/get-in shadow [:color :id])
|
||||
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
|
||||
is-shared? (contains? shared-libs-colors color-id)
|
||||
attrs (if (or is-shared? (= color-file-id file-id))
|
||||
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
|
||||
:opacity (dm/get-in shadow [:color :opacity])
|
||||
:id color-id
|
||||
:file-id (dm/get-in shadow [:color :file-id])
|
||||
:gradient (dm/get-in shadow [:color :gradient])})
|
||||
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
|
||||
:opacity (dm/get-in shadow [:color :opacity])
|
||||
:gradient (dm/get-in shadow [:color :gradient])}))]
|
||||
|
||||
|
||||
(defn- shadow->color-att
|
||||
[shadow file-id libraries]
|
||||
(let [color (get shadow :color)
|
||||
ref-file (get color :ref-file)
|
||||
ref-id (get color :ref-id)
|
||||
shared-colors (dm/get-in libraries [ref-file :data :colors])
|
||||
is-shared? (contains? shared-colors ref-id)
|
||||
attrs (cond-> (shadow->color shadow)
|
||||
(not (or is-shared? (= ref-file file-id)))
|
||||
(dissoc :ref-file :ref-id))]
|
||||
{:attrs attrs
|
||||
:prop :shadow
|
||||
:shape-id (:shape-id shadow)
|
||||
:index (:index shadow)}))
|
||||
|
||||
(defn text->color-att
|
||||
[fill file-id shared-libs]
|
||||
(let [color-file-id (:fill-color-ref-file fill)
|
||||
color-id (:fill-color-ref-id fill)
|
||||
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
|
||||
is-shared? (contains? shared-libs-colors color-id)
|
||||
attrs (if (or is-shared? (= color-file-id file-id))
|
||||
(d/without-nils {:color (str/lower (:fill-color fill))
|
||||
:opacity (:fill-opacity fill)
|
||||
:id color-id
|
||||
:file-id color-file-id
|
||||
:gradient (:fill-color-gradient fill)})
|
||||
(d/without-nils {:color (str/lower (:fill-color fill))
|
||||
:opacity (:fill-opacity fill)
|
||||
:gradient (:fill-color-gradient fill)}))]
|
||||
(defn- text->color-att
|
||||
[fill file-id libraries]
|
||||
(let [ref-file (:fill-color-ref-file fill)
|
||||
ref-id (:fill-color-ref-id fill)
|
||||
shared-colors (dm/get-in libraries [ref-file :data :colors])
|
||||
is-shared? (contains? shared-colors ref-id)
|
||||
attrs (cond-> (fill->color fill)
|
||||
(not (or is-shared? (= ref-file file-id)))
|
||||
(dissoc :ref-file :ref-id))]
|
||||
|
||||
{:attrs attrs
|
||||
:prop :content
|
||||
:shape-id (:shape-id fill)
|
||||
:index (:index fill)}))
|
||||
|
||||
(defn treat-node
|
||||
(defn- treat-node
|
||||
[node shape-id]
|
||||
(map-indexed #(assoc %2 :shape-id shape-id :index %1) node))
|
||||
|
||||
(defn extract-text-colors
|
||||
[text file-id shared-libs]
|
||||
(let [content (txt/node-seq txt/is-text-node? (:content text))
|
||||
content-filtered (map :fills content)
|
||||
indexed (mapcat #(treat-node % (:id text)) content-filtered)]
|
||||
(map #(text->color-att % file-id shared-libs) indexed)))
|
||||
(defn- extract-text-colors
|
||||
[text file-id libraries]
|
||||
(->> (txt/node-seq txt/is-text-node? (:content text))
|
||||
(map :fills)
|
||||
(mapcat #(treat-node % (:id text)))
|
||||
(map #(text->color-att % file-id libraries))))
|
||||
|
||||
(defn- fill->color-att
|
||||
[fill file-id libraries]
|
||||
(let [ref-file (:fill-color-ref-file fill)
|
||||
ref-id (:fill-color-ref-id fill)
|
||||
shared-colors (dm/get-in libraries [ref-file :data :colors])
|
||||
is-shared? (contains? shared-colors ref-id)
|
||||
has-color? (or (:fill-color fill)
|
||||
(:fill-color-gradient fill))
|
||||
attrs (cond-> (fill->color fill)
|
||||
(not (or is-shared? (= ref-file file-id)))
|
||||
(dissoc :ref-file :ref-id))]
|
||||
|
||||
(defn fill->color-att
|
||||
[fill file-id shared-libs]
|
||||
(let [color-file-id (:fill-color-ref-file fill)
|
||||
color-id (:fill-color-ref-id fill)
|
||||
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
|
||||
is-shared? (contains? shared-libs-colors color-id)
|
||||
has-color? (or (not (nil? (:fill-color fill))) (not (nil? (:fill-color-gradient fill))))
|
||||
attrs (if (or is-shared? (= color-file-id file-id))
|
||||
(d/without-nils {:color (str/lower (:fill-color fill))
|
||||
:opacity (:fill-opacity fill)
|
||||
:id color-id
|
||||
:file-id color-file-id
|
||||
:gradient (:fill-color-gradient fill)})
|
||||
(d/without-nils {:color (str/lower (:fill-color fill))
|
||||
:opacity (:fill-opacity fill)
|
||||
:gradient (:fill-color-gradient fill)}))]
|
||||
(when has-color?
|
||||
{:attrs attrs
|
||||
:prop :fill
|
||||
@@ -522,21 +515,67 @@
|
||||
:index (:index fill)})))
|
||||
|
||||
(defn extract-all-colors
|
||||
[shapes file-id shared-libs]
|
||||
[shapes file-id libraries]
|
||||
(reduce
|
||||
(fn [list shape]
|
||||
(fn [result shape]
|
||||
(let [fill-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:fills shape))
|
||||
stroke-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:strokes shape))
|
||||
shadow-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:shadow shape))]
|
||||
(if (= :text (:type shape))
|
||||
(-> list
|
||||
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
|
||||
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)
|
||||
(into (extract-text-colors shape file-id shared-libs)))
|
||||
(-> result
|
||||
(into (map #(stroke->color-att % file-id libraries)) stroke-obj)
|
||||
(into (map #(shadow->color-att % file-id libraries)) shadow-obj)
|
||||
(into (extract-text-colors shape file-id libraries)))
|
||||
|
||||
(-> list
|
||||
(into (map #(fill->color-att % file-id shared-libs)) fill-obj)
|
||||
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
|
||||
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)))))
|
||||
(-> result
|
||||
(into (map #(fill->color-att % file-id libraries)) fill-obj)
|
||||
(into (map #(stroke->color-att % file-id libraries)) stroke-obj)
|
||||
(into (map #(shadow->color-att % file-id libraries)) shadow-obj)))))
|
||||
[]
|
||||
shapes))
|
||||
|
||||
(defn colors-seq
|
||||
[file-data]
|
||||
(vals (:colors file-data)))
|
||||
|
||||
(defn- touch
|
||||
[color]
|
||||
(assoc color :modified-at (dt/now)))
|
||||
|
||||
(defn add-color
|
||||
[file-data color]
|
||||
(update file-data :colors assoc (:id color) (touch color)))
|
||||
|
||||
(defn get-color
|
||||
[file-data color-id]
|
||||
(get-in file-data [:colors color-id]))
|
||||
|
||||
(defn get-ref-color
|
||||
[library-data color]
|
||||
(when (= (:ref-file color) (:id library-data))
|
||||
(get-color library-data (:ref-id color))))
|
||||
|
||||
(defn set-color
|
||||
[file-data color]
|
||||
(d/assoc-in-when file-data [:colors (:id color)] (touch color)))
|
||||
|
||||
(defn update-color
|
||||
[file-data color-id f & args]
|
||||
(d/update-in-when file-data [:colors color-id] #(-> (apply f % args)
|
||||
(touch))))
|
||||
|
||||
(defn delete-color
|
||||
[file-data color-id]
|
||||
(update file-data :colors dissoc color-id))
|
||||
|
||||
(defn used-colors-changed-since
|
||||
"Find all usages of any color in the library by the given shape, of colors
|
||||
that have ben modified after the date."
|
||||
[shape library since-date]
|
||||
(->> (get-all-colors shape)
|
||||
(keep #(get-ref-color (:data library) %))
|
||||
(remove #(< (:modified-at %) since-date)) ;; Note that :modified-at may be nil
|
||||
(map (fn [color] {:shape-id (:id shape)
|
||||
:asset-id (:id color)
|
||||
:asset-type :color}))))
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.common.types.colors-list
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.time :as dt]
|
||||
[app.common.types.color :as ctc]))
|
||||
|
||||
(defn colors-seq
|
||||
[file-data]
|
||||
(vals (:colors file-data)))
|
||||
|
||||
(defn- touch
|
||||
[color]
|
||||
(assoc color :modified-at (dt/now)))
|
||||
|
||||
(defn add-color
|
||||
[file-data color]
|
||||
(update file-data :colors assoc (:id color) (touch color)))
|
||||
|
||||
(defn get-color
|
||||
[file-data color-id]
|
||||
(get-in file-data [:colors color-id]))
|
||||
|
||||
(defn get-ref-color
|
||||
[library-data color]
|
||||
(when (= (:ref-file color) (:id library-data))
|
||||
(get-color library-data (:ref-id color))))
|
||||
|
||||
(defn set-color
|
||||
[file-data color]
|
||||
(d/assoc-in-when file-data [:colors (:id color)] (touch color)))
|
||||
|
||||
(defn update-color
|
||||
[file-data color-id f & args]
|
||||
(d/update-in-when file-data [:colors color-id] #(-> (apply f % args)
|
||||
(touch))))
|
||||
|
||||
(defn delete-color
|
||||
[file-data color-id]
|
||||
(update file-data :colors dissoc color-id))
|
||||
|
||||
(defn used-colors-changed-since
|
||||
"Find all usages of any color in the library by the given shape, of colors
|
||||
that have ben modified after the date."
|
||||
[shape library since-date]
|
||||
(->> (ctc/get-all-colors shape)
|
||||
(keep #(get-ref-color (:data library) %))
|
||||
(remove #(< (:modified-at %) since-date)) ;; Note that :modified-at may be nil
|
||||
(map (fn [color] {:shape-id (:id shape)
|
||||
:asset-id (:id color)
|
||||
:asset-type :color}))))
|
||||
@@ -19,7 +19,8 @@
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.token :as ctt]
|
||||
[app.common.uuid :as uuid]))
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMA
|
||||
@@ -534,8 +535,6 @@
|
||||
indicating if shape is touched or not."
|
||||
[shape attr val & {:keys [ignore-touched ignore-geometry]}]
|
||||
(let [group (get ctk/sync-attrs attr)
|
||||
token-groups (when (= attr :applied-tokens)
|
||||
(get-token-groups shape val))
|
||||
shape-val (get shape attr)
|
||||
|
||||
ignore?
|
||||
@@ -566,22 +565,30 @@
|
||||
(gsh/close-attrs? attr val shape-val))
|
||||
|
||||
touched?
|
||||
(and group (not equal?) (not (and ignore-geometry is-geometry?)))]
|
||||
(and group
|
||||
(not equal?)
|
||||
(not (and ignore-geometry is-geometry?)))
|
||||
|
||||
token-groups (if (= attr :applied-tokens)
|
||||
(get-token-groups shape val)
|
||||
#{})
|
||||
|
||||
groups (cond-> token-groups
|
||||
(and group (not equal?))
|
||||
(set/union #{group}))]
|
||||
(cond-> shape
|
||||
;; Depending on the origin of the attribute change, we need or not to
|
||||
;; set the "touched" flag for the group the attribute belongs to.
|
||||
;; In some cases we need to ignore touched only if the attribute is
|
||||
;; geometric (position, width or transformation).
|
||||
(and in-copy?
|
||||
(or (and group (not equal?)) (seq token-groups))
|
||||
(not ignore?) (not (and ignore-geometry is-geometry?)))
|
||||
(not-empty groups)
|
||||
(not ignore?)
|
||||
(not (and ignore-geometry is-geometry?)))
|
||||
(-> (update :touched (fn [touched]
|
||||
(reduce #(ctk/set-touched-group %1 %2)
|
||||
touched
|
||||
(if group
|
||||
(cons group token-groups)
|
||||
token-groups))))
|
||||
groups)))
|
||||
(dissoc :remote-synced))
|
||||
|
||||
(nil? val)
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.text :as ct]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.colors-list :as ctcl]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.container :as ctn]
|
||||
@@ -59,7 +58,7 @@
|
||||
[:is-local {:optional true} :boolean]])
|
||||
|
||||
(def schema:colors
|
||||
[:map-of {:gen/max 5} ::sm/uuid ::ctc/color])
|
||||
[:map-of {:gen/max 5} ::sm/uuid ctc/schema:library-color])
|
||||
|
||||
(def schema:components
|
||||
[:map-of {:gen/max 5} ::sm/uuid ::ctn/container])
|
||||
@@ -299,7 +298,6 @@
|
||||
(ctkl/get-component (:data component-file) (:component-id head-shape) include-deleted?))]
|
||||
(when (some? component)
|
||||
(get-ref-shape (:data component-file) component shape :with-context? with-context?))))]
|
||||
|
||||
(some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape))))
|
||||
|
||||
(defn advance-shape-ref
|
||||
@@ -489,7 +487,7 @@
|
||||
[file-data library-data asset-type]
|
||||
(let [assets-seq (case asset-type
|
||||
:component (ctkl/components-seq library-data)
|
||||
:color (ctcl/colors-seq library-data)
|
||||
:color (ctc/colors-seq library-data)
|
||||
:typography (ctyl/typographies-seq library-data))
|
||||
|
||||
find-usages-in-container
|
||||
@@ -528,7 +526,7 @@
|
||||
(letfn [(used-assets-shape [shape]
|
||||
(concat
|
||||
(ctkl/used-components-changed-since shape library since-date)
|
||||
(ctcl/used-colors-changed-since shape library since-date)
|
||||
(ctc/used-colors-changed-since shape library since-date)
|
||||
(ctyl/used-typographies-changed-since shape library since-date)))
|
||||
|
||||
(used-assets-container [container]
|
||||
@@ -664,7 +662,7 @@
|
||||
%
|
||||
shapes)))]
|
||||
(as-> file-data $
|
||||
(ctcl/add-color $ color)
|
||||
(ctc/add-color $ color)
|
||||
(reduce remap-shapes $ usages))))]
|
||||
|
||||
(reduce absorb-color
|
||||
@@ -1047,4 +1045,4 @@
|
||||
|
||||
(defn set-base-font-size
|
||||
[file-data base-font-size]
|
||||
(assoc-in file-data [:options :base-font-size] base-font-size))
|
||||
(assoc-in file-data [:options :base-font-size] base-font-size))
|
||||
|
||||
68
common/src/app/common/types/fill.cljc
Normal file
68
common/src/app/common/types/fill.cljc
Normal file
@@ -0,0 +1,68 @@
|
||||
;; 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.types.fill
|
||||
(:require
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.color :as types.color]
|
||||
[app.common.types.fill.impl :as impl]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(def ^:const MAX-GRADIENT-STOPS impl/MAX-GRADIENT-STOPS)
|
||||
(def ^:const MAX-FILLS impl/MAX-FILLS)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMAS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def schema:fill-attrs
|
||||
[:map {:title "FillAttrs" :closed true}
|
||||
[:fill-color-ref-file {:optional true} ::sm/uuid]
|
||||
[:fill-color-ref-id {:optional true} ::sm/uuid]
|
||||
[:fill-opacity {:optional true} [::sm/number {:min 0 :max 1}]]
|
||||
[:fill-color {:optional true} types.color/schema:hex-color]
|
||||
[:fill-color-gradient {:optional true} types.color/schema:gradient]
|
||||
[:fill-image {:optional true} types.color/schema:image]])
|
||||
|
||||
(def fill-attrs
|
||||
"A set of attrs that corresponds to fill data type"
|
||||
(sm/keys schema:fill-attrs))
|
||||
|
||||
(def valid-fill-attrs
|
||||
"A set used for proper check if color should contain only one of the
|
||||
attrs listed in this set."
|
||||
#{:fill-image :fill-color :fill-color-gradient})
|
||||
|
||||
(defn has-valid-fill-attrs?
|
||||
"Check if color has correct color attrs"
|
||||
[color]
|
||||
(let [attrs (set (keys color))
|
||||
result (set/intersection attrs valid-fill-attrs)]
|
||||
(= 1 (count result))))
|
||||
|
||||
(def schema:fill
|
||||
[:and schema:fill-attrs
|
||||
[:fn has-valid-fill-attrs?]])
|
||||
|
||||
(def check-fill
|
||||
(sm/check-fn schema:fill))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HELPERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; CONSTRUCTORS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn from-plain
|
||||
[o]
|
||||
(assert (every? check-fill o) "expected valid fills vector")
|
||||
(impl/from-plain o))
|
||||
|
||||
(defn fills?
|
||||
[o]
|
||||
(impl/fills? o))
|
||||
404
common/src/app/common/types/fill/impl.cljc
Normal file
404
common/src/app/common/types/fill/impl.cljc
Normal file
@@ -0,0 +1,404 @@
|
||||
;; 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.types.fill.impl
|
||||
(:require
|
||||
#?(:clj [clojure.data.json :as json])
|
||||
#?(:cljs [app.common.weak-map :as weak-map])
|
||||
[app.common.buffer :as buf]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.math :as mth]
|
||||
[app.common.transit :as t]))
|
||||
|
||||
;; FIXME: Get these from the wasm module, and tweak the values
|
||||
;; (we'd probably want 12 stops at most)
|
||||
(def ^:const MAX-GRADIENT-STOPS 16)
|
||||
(def ^:const MAX-FILLS 8)
|
||||
|
||||
(def ^:const GRADIENT-STOP-SIZE 8)
|
||||
(def ^:const GRADIENT-BYTE-SIZE 156)
|
||||
(def ^:const SOLID-BYTE-SIZE 4)
|
||||
(def ^:const IMAGE-BYTE-SIZE 28)
|
||||
(def ^:const METADATA-BYTE-SIZE 36)
|
||||
(def ^:const FILL-BYTE-SIZE
|
||||
(+ 4 (mth/max GRADIENT-BYTE-SIZE
|
||||
IMAGE-BYTE-SIZE
|
||||
SOLID-BYTE-SIZE)))
|
||||
|
||||
(def ^:private xf:take-stops
|
||||
(take MAX-GRADIENT-STOPS))
|
||||
|
||||
(def ^:private xf:take-fills
|
||||
(take MAX-FILLS))
|
||||
|
||||
(defn- hex->rgb
|
||||
"Encode an hex string as rgb (int32)"
|
||||
[hex]
|
||||
(let [hex (subs hex 1)]
|
||||
#?(:clj (Integer/parseInt hex 16)
|
||||
:cljs (js/parseInt hex 16))))
|
||||
|
||||
(defn- rgb->rgba
|
||||
"Use the first 2 bytes of in32 for encode the alpha channel"
|
||||
[n alpha]
|
||||
(let [result (mth/floor (* alpha 0xff))
|
||||
result (unchecked-int result)
|
||||
result (bit-shift-left result 24)
|
||||
result (bit-or result n)]
|
||||
result))
|
||||
|
||||
(defn- get-color-hex
|
||||
[n]
|
||||
(let [n (bit-and n 0x00ffffff)
|
||||
n #?(:clj n :cljs (.toString n 16))]
|
||||
(dm/str "#" #?(:clj (String/format "%06x" (into-array Object [n]))
|
||||
:cljs (.padStart n 6 "0")))))
|
||||
|
||||
(defn- get-color-alpha
|
||||
[rgb]
|
||||
(let [n (bit-and rgb 0xff000000)
|
||||
n (unsigned-bit-shift-right n 24)]
|
||||
(mth/precision (/ (float n) 0xff) 2)))
|
||||
|
||||
(defn- write-solid-fill
|
||||
[offset buffer color alpha]
|
||||
(buf/write-byte buffer (+ offset 0) 0x00)
|
||||
(buf/write-int buffer (+ offset 4)
|
||||
(-> (hex->rgb color)
|
||||
(rgb->rgba alpha)))
|
||||
(+ offset FILL-BYTE-SIZE))
|
||||
|
||||
(defn- write-gradient-fill
|
||||
[offset buffer gradient opacity]
|
||||
(let [start-x (:start-x gradient)
|
||||
start-y (:start-y gradient)
|
||||
end-x (:end-x gradient)
|
||||
end-y (:end-y gradient)
|
||||
width (:width gradient 0)
|
||||
stops (into [] xf:take-stops (:stops gradient))
|
||||
type (if (= (:type gradient) :linear)
|
||||
0x01
|
||||
0x02)]
|
||||
|
||||
(buf/write-byte buffer (+ offset 0) type)
|
||||
(buf/write-float buffer (+ offset 4) start-x)
|
||||
(buf/write-float buffer (+ offset 8) start-y)
|
||||
(buf/write-float buffer (+ offset 12) end-x)
|
||||
(buf/write-float buffer (+ offset 16) end-y)
|
||||
(buf/write-float buffer (+ offset 20) opacity)
|
||||
(buf/write-float buffer (+ offset 24) width)
|
||||
(buf/write-byte buffer (+ offset 28) (count stops))
|
||||
|
||||
(loop [stops (seq stops)
|
||||
offset' (+ offset 32)]
|
||||
(if-let [stop (first stops)]
|
||||
(let [color (-> (hex->rgb (:color stop))
|
||||
(rgb->rgba (:opacity stop 1)))]
|
||||
;; NOTE: we write the color as signed integer but on rust
|
||||
;; side it will be read as unsigned, on the end the binary
|
||||
;; repr of the data is the same independently on how it is
|
||||
;; interpreted
|
||||
(buf/write-int buffer (+ offset' 0) color)
|
||||
(buf/write-float buffer (+ offset' 4) (:offset stop))
|
||||
(recur (rest stops)
|
||||
(+ offset' GRADIENT-STOP-SIZE)))
|
||||
(+ offset FILL-BYTE-SIZE)))))
|
||||
|
||||
(defn- write-image-fill
|
||||
[offset buffer opacity image]
|
||||
(let [image-id (get image :id)
|
||||
image-width (get image :width)
|
||||
image-height (get image :height)]
|
||||
(buf/write-byte buffer (+ offset 0) 0x03)
|
||||
(buf/write-uuid buffer (+ offset 4) image-id)
|
||||
(buf/write-float buffer (+ offset 20) opacity)
|
||||
(buf/write-int buffer (+ offset 24) image-width)
|
||||
(buf/write-int buffer (+ offset 28) image-height)
|
||||
(+ offset FILL-BYTE-SIZE)))
|
||||
|
||||
(defn- write-metadata
|
||||
[offset buffer fill]
|
||||
(let [ref-id (:fill-color-ref-id fill)
|
||||
ref-file (:fill-color-ref-file fill)
|
||||
mtype (dm/get-in fill [:fill-image :mtype])]
|
||||
|
||||
(when mtype
|
||||
(let [val (case mtype
|
||||
"image/jpeg" 0x01
|
||||
"image/png" 0x02
|
||||
"image/gif" 0x03
|
||||
"image/webp" 0x04
|
||||
"image/svg+xml" 0x05)]
|
||||
(buf/write-short buffer (+ offset 2) val)))
|
||||
|
||||
(if (and (some? ref-file)
|
||||
(some? ref-id))
|
||||
(do
|
||||
(buf/write-byte buffer (+ offset 0) 0x01)
|
||||
(buf/write-uuid buffer (+ offset 4) ref-file)
|
||||
(buf/write-uuid buffer (+ offset 20) ref-id))
|
||||
(do
|
||||
(buf/write-byte buffer (+ offset 0) 0x00)))))
|
||||
|
||||
(defn- read-stop
|
||||
[buffer offset]
|
||||
(let [rgba (buf/read-int buffer (+ offset 0))
|
||||
soff (buf/read-float buffer (+ offset 4))]
|
||||
{:color (get-color-hex rgba)
|
||||
:opacity (get-color-alpha rgba)
|
||||
:offset (mth/precision soff 2)}))
|
||||
|
||||
(defn- read-fill
|
||||
"Read segment from binary buffer at specified index"
|
||||
[dbuffer mbuffer index]
|
||||
(let [doffset (+ 4 (* index FILL-BYTE-SIZE))
|
||||
moffset (* index METADATA-BYTE-SIZE)
|
||||
type (buf/read-byte dbuffer doffset)
|
||||
refs? (buf/read-bool mbuffer (+ moffset 0))
|
||||
fill (case type
|
||||
0
|
||||
(let [rgba (buf/read-int dbuffer (+ doffset 4))]
|
||||
{:fill-color (get-color-hex rgba)
|
||||
:fill-opacity (get-color-alpha rgba)})
|
||||
|
||||
(1 2)
|
||||
(let [start-x (buf/read-float dbuffer (+ doffset 4))
|
||||
start-y (buf/read-float dbuffer (+ doffset 8))
|
||||
end-x (buf/read-float dbuffer (+ doffset 12))
|
||||
end-y (buf/read-float dbuffer (+ doffset 16))
|
||||
alpha (buf/read-float dbuffer (+ doffset 20))
|
||||
width (buf/read-float dbuffer (+ doffset 24))
|
||||
stops (buf/read-byte dbuffer (+ doffset 28))
|
||||
type (if (= type 1)
|
||||
:linear
|
||||
:radial)
|
||||
stops (loop [index 0
|
||||
result []]
|
||||
(if (< index stops)
|
||||
(recur (inc index)
|
||||
(conj result (read-stop dbuffer (+ doffset 32 (* GRADIENT-STOP-SIZE index)))))
|
||||
result))]
|
||||
|
||||
{:fill-opacity alpha
|
||||
:fill-color-gradient {:start-x start-x
|
||||
:start-y start-y
|
||||
:end-x end-x
|
||||
:end-y end-y
|
||||
:width width
|
||||
:stops stops
|
||||
:type type}})
|
||||
|
||||
3
|
||||
(let [id (buf/read-uuid dbuffer (+ doffset 4))
|
||||
alpha (buf/read-float dbuffer (+ doffset 20))
|
||||
width (buf/read-int dbuffer (+ doffset 24))
|
||||
height (buf/read-int dbuffer (+ doffset 28))
|
||||
mtype (buf/read-short mbuffer (+ moffset 2))
|
||||
mtype (case mtype
|
||||
0x01 "image/jpeg"
|
||||
0x02 "image/png"
|
||||
0x03 "image/gif"
|
||||
0x04 "image/webp"
|
||||
0x05 "image/svg+xml")]
|
||||
{:fill-opacity alpha
|
||||
:fill-image {:id id
|
||||
:width width
|
||||
:height height
|
||||
:mtype mtype
|
||||
;; FIXME: we are not encodign the name, looks useless
|
||||
:name "sample"}}))]
|
||||
|
||||
(if refs?
|
||||
(let [ref-file (buf/read-uuid mbuffer (+ moffset 4))
|
||||
ref-id (buf/read-uuid mbuffer (+ moffset 20))]
|
||||
(-> fill
|
||||
(assoc :fill-color-ref-id ref-id)
|
||||
(assoc :fill-color-ref-file ref-file)))
|
||||
fill)))
|
||||
|
||||
(declare from-plain)
|
||||
|
||||
#?(:clj
|
||||
(deftype Fills [size dbuffer mbuffer ^:unsynchronized-mutable hash]
|
||||
Object
|
||||
(equals [_ other]
|
||||
(if (instance? Fills other)
|
||||
(and (buf/equals? dbuffer (.-dbuffer ^Fills other))
|
||||
(buf/equals? mbuffer (.-mbuffer ^Fills other)))
|
||||
false))
|
||||
|
||||
json/JSONWriter
|
||||
(-write [this writter options]
|
||||
(json/-write (vec this) writter options))
|
||||
|
||||
clojure.lang.IHashEq
|
||||
(hasheq [this]
|
||||
(when-not hash
|
||||
(set! hash (clojure.lang.Murmur3/hashOrdered (seq this))))
|
||||
hash)
|
||||
|
||||
clojure.lang.Sequential
|
||||
clojure.lang.Seqable
|
||||
(seq [_]
|
||||
(when (pos? size)
|
||||
((fn next-seq [i]
|
||||
(when (< i size)
|
||||
(cons (read-fill dbuffer mbuffer i)
|
||||
(lazy-seq (next-seq (inc i))))))
|
||||
0)))
|
||||
|
||||
clojure.lang.IReduceInit
|
||||
(reduce [_ f start]
|
||||
(loop [index 0
|
||||
result start]
|
||||
(if (< index size)
|
||||
(let [result (f result (read-fill dbuffer mbuffer index))]
|
||||
(if (reduced? result)
|
||||
@result
|
||||
(recur (inc index) result)))
|
||||
result)))
|
||||
|
||||
clojure.lang.Indexed
|
||||
(nth [_ i]
|
||||
(if (d/in-range? size i)
|
||||
(read-fill dbuffer mbuffer i)
|
||||
nil))
|
||||
|
||||
(nth [_ i default]
|
||||
(if (d/in-range? size i)
|
||||
(read-fill dbuffer mbuffer i)
|
||||
default))
|
||||
|
||||
clojure.lang.Counted
|
||||
(count [_] size))
|
||||
|
||||
:cljs
|
||||
#_:clj-kondo/ignore
|
||||
(deftype Fills [size dbuffer mbuffer cache ^:mutable __hash]
|
||||
cljs.core/ISequential
|
||||
cljs.core/IEquiv
|
||||
(-equiv [this other]
|
||||
(if (instance? Fills other)
|
||||
(and ^boolean (buf/equals? (.-dbuffer ^Fills other) dbuffer)
|
||||
^boolean (buf/equals? (.-mbuffer ^Fills other) mbuffer))
|
||||
false))
|
||||
|
||||
cljs.core/IEncodeJS
|
||||
(-clj->js [this]
|
||||
(clj->js (vec this)))
|
||||
|
||||
;; cljs.core/APersistentVector
|
||||
cljs.core/IAssociative
|
||||
(-assoc [coll k v]
|
||||
(if (number? k)
|
||||
(-> (vec coll)
|
||||
(assoc k v)
|
||||
(from-plain))
|
||||
(throw (js/Error. "Vector's key for assoc must be a number."))))
|
||||
|
||||
(-contains-key? [coll k]
|
||||
(if (integer? k)
|
||||
(and (<= 0 k) (< k size))
|
||||
false))
|
||||
|
||||
cljs.core/IReduce
|
||||
(-reduce [_ f]
|
||||
(loop [index 1
|
||||
result (if (pos? size)
|
||||
(read-fill dbuffer mbuffer 0)
|
||||
nil)]
|
||||
(if (< index size)
|
||||
(let [result (f result (read-fill dbuffer mbuffer index))]
|
||||
(if (reduced? result)
|
||||
@result
|
||||
(recur (inc index) result)))
|
||||
result)))
|
||||
|
||||
(-reduce [_ f start]
|
||||
(loop [index 0
|
||||
result start]
|
||||
(if (< index size)
|
||||
(let [result (f result (read-fill dbuffer mbuffer index))]
|
||||
(if (reduced? result)
|
||||
@result
|
||||
(recur (inc index) result)))
|
||||
result)))
|
||||
|
||||
cljs.core/IHash
|
||||
(-hash [coll]
|
||||
(caching-hash coll hash-ordered-coll __hash))
|
||||
|
||||
cljs.core/ICounted
|
||||
(-count [_] size)
|
||||
|
||||
cljs.core/IIndexed
|
||||
(-nth [_ i]
|
||||
(if (d/in-range? size i)
|
||||
(read-fill dbuffer mbuffer i)
|
||||
nil))
|
||||
|
||||
(-nth [_ i default]
|
||||
(if (d/in-range? i size)
|
||||
(read-fill dbuffer mbuffer i)
|
||||
default))
|
||||
|
||||
cljs.core/ISeqable
|
||||
(-seq [this]
|
||||
(when (pos? size)
|
||||
((fn next-seq [i]
|
||||
(when (< i size)
|
||||
(cons (read-fill dbuffer mbuffer i)
|
||||
(lazy-seq (next-seq (inc i))))))
|
||||
0)))))
|
||||
|
||||
(defn from-plain
|
||||
[fills]
|
||||
(let [fills (into [] xf:take-fills fills)
|
||||
total (count fills)
|
||||
dbuffer (buf/allocate (+ 4 (* MAX-FILLS FILL-BYTE-SIZE)))
|
||||
mbuffer (buf/allocate (* total METADATA-BYTE-SIZE))]
|
||||
|
||||
(buf/write-byte dbuffer 0 total)
|
||||
|
||||
(loop [index 0]
|
||||
(when (< index total)
|
||||
(let [fill (nth fills index)
|
||||
doffset (+ 4 (* index FILL-BYTE-SIZE))
|
||||
moffset (* index METADATA-BYTE-SIZE)
|
||||
opacity (get fill :fill-opacity 1)]
|
||||
|
||||
(if-let [color (get fill :fill-color)]
|
||||
(do
|
||||
(write-solid-fill doffset dbuffer color opacity)
|
||||
(write-metadata moffset mbuffer fill)
|
||||
(recur (inc index)))
|
||||
(if-let [gradient (get fill :fill-color-gradient)]
|
||||
(do
|
||||
(write-gradient-fill doffset dbuffer gradient opacity)
|
||||
(write-metadata moffset mbuffer fill)
|
||||
(recur (inc index)))
|
||||
(if-let [image (get fill :fill-image)]
|
||||
(do
|
||||
(write-image-fill doffset dbuffer opacity image)
|
||||
(write-metadata moffset mbuffer fill)
|
||||
(recur (inc index)))
|
||||
(recur (inc index))))))))
|
||||
|
||||
#?(:cljs (Fills. total dbuffer mbuffer (weak-map/create) nil)
|
||||
:clj (Fills. total dbuffer mbuffer nil))))
|
||||
|
||||
(defn fills?
|
||||
[o]
|
||||
(instance? Fills o))
|
||||
|
||||
(t/add-handlers!
|
||||
{:id "penpot/fills"
|
||||
:class Fills
|
||||
:wfn (fn [^Fills fills]
|
||||
(vec fills))
|
||||
:rfn #?(:cljs from-plain
|
||||
:clj identity)})
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.colors :as clr]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.color :as ctc]))
|
||||
[app.common.types.color :refer [schema:hex-color]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMA
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
(def schema:grid-color
|
||||
[:map {:title "PageGridColor"}
|
||||
[:color ::ctc/rgb-color]
|
||||
[:color schema:hex-color]
|
||||
[:opacity ::sm/safe-number]])
|
||||
|
||||
(def schema:column-params
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as-alias gpt]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.color :as-alias ctc]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.grid :as ctg]
|
||||
[app.common.types.plugins :as ctpg]
|
||||
[app.common.types.shape :as cts]
|
||||
@@ -57,7 +57,7 @@
|
||||
[:flows {:optional true} schema:flows]
|
||||
[:guides {:optional true} schema:guides]
|
||||
[:plugin-data {:optional true} ::ctpg/plugin-data]
|
||||
[:background {:optional true} ::ctc/rgb-color]
|
||||
[:background {:optional true} ctc/schema:hex-color]
|
||||
|
||||
[:comment-thread-positions {:optional true}
|
||||
[:map-of ::sm/uuid schema:comment-thread-position]]])
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.files.helpers :as cpf]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
@@ -22,6 +23,17 @@
|
||||
|
||||
#?(:clj (set! *warn-on-reflection* true))
|
||||
|
||||
(def ^:cosnt bool-group-style-properties bool/group-style-properties)
|
||||
(def ^:const bool-style-properties bool/style-properties)
|
||||
(def ^:const default-bool-fills bool/default-fills)
|
||||
|
||||
(def schema:content impl/schema:content)
|
||||
(def schema:segments impl/schema:segments)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TRANSFORMATIONS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn content?
|
||||
[o]
|
||||
(impl/path-data? o))
|
||||
@@ -40,9 +52,9 @@
|
||||
[data]
|
||||
(impl/from-string data))
|
||||
|
||||
(defn check-path-content
|
||||
(defn check-content
|
||||
[content]
|
||||
(impl/check-content-like content))
|
||||
(impl/check-content content))
|
||||
|
||||
(defn get-byte-size
|
||||
"Get byte size of a path content"
|
||||
@@ -68,7 +80,7 @@
|
||||
(defn apply-content-modifiers
|
||||
"Apply delta modifiers over the path content"
|
||||
[content modifiers]
|
||||
(assert (impl/check-content-like content))
|
||||
(assert (impl/check-content content))
|
||||
|
||||
(letfn [(apply-to-index [content [index params]]
|
||||
(if (contains? content index)
|
||||
@@ -188,7 +200,18 @@
|
||||
contents
|
||||
(sequence extract-content-xf (:shapes shape))]
|
||||
|
||||
(bool/calculate-content (:bool-type shape) contents)))
|
||||
(ex/try!
|
||||
(bool/calculate-content (:bool-type shape) contents)
|
||||
|
||||
:on-exception
|
||||
(fn [cause]
|
||||
(ex/raise :type :internal
|
||||
:code :invalid-path-content
|
||||
:hint (str "unable to calculate bool content for shape " (:id shape))
|
||||
:shapes (:shapes shape)
|
||||
:type (:bool-type shape)
|
||||
:content (vec contents)
|
||||
:cause cause)))))
|
||||
|
||||
(defn calc-bool-content
|
||||
"Calculate the boolean content from shape and objects. Returns a
|
||||
@@ -197,6 +220,13 @@
|
||||
(-> (calc-bool-content* shape objects)
|
||||
(impl/path-data)))
|
||||
|
||||
(defn update-bool-shape
|
||||
"Calculates the selrect+points for the boolean shape"
|
||||
[shape objects]
|
||||
(let [content (calc-bool-content shape objects)
|
||||
shape (assoc shape :content content)]
|
||||
(update-geometry shape)))
|
||||
|
||||
(defn shape-with-open-path?
|
||||
[shape]
|
||||
(let [svg? (contains? shape :svg-attrs)
|
||||
|
||||
@@ -18,28 +18,13 @@
|
||||
(def default-fills
|
||||
[{:fill-color clr/black}])
|
||||
|
||||
(def style-group-properties
|
||||
[:shadow :blur])
|
||||
(def group-style-properties
|
||||
#{:shadow :blur})
|
||||
|
||||
;; FIXME: revisit
|
||||
(def style-properties
|
||||
(into style-group-properties
|
||||
[:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-gradient
|
||||
:fill-color-ref-file
|
||||
:fill-color-ref-id
|
||||
:fill-image
|
||||
:fills
|
||||
:stroke-color
|
||||
:stroke-color-ref-file
|
||||
:stroke-color-ref-id
|
||||
:stroke-opacity
|
||||
:stroke-style
|
||||
:stroke-width
|
||||
:stroke-alignment
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
:strokes]))
|
||||
(into group-style-properties
|
||||
[:fills :strokes]))
|
||||
|
||||
(defn add-previous
|
||||
([content]
|
||||
@@ -427,7 +412,7 @@
|
||||
|
||||
(defn calculate-content
|
||||
"Create a bool content from a collection of contents and specified
|
||||
type."
|
||||
type. Returns plain segments"
|
||||
[bool-type contents]
|
||||
;; We apply the boolean operation in to each pair and the result to the next
|
||||
;; element
|
||||
|
||||
@@ -27,13 +27,11 @@
|
||||
|
||||
(defn make-move-to [to]
|
||||
{:command :move-to
|
||||
:relative false
|
||||
:params {:x (:x to)
|
||||
:y (:y to)}})
|
||||
|
||||
(defn make-line-to [to]
|
||||
{:command :line-to
|
||||
:relative false
|
||||
:params {:x (:x to)
|
||||
:y (:y to)}})
|
||||
|
||||
@@ -65,7 +63,6 @@
|
||||
(defn make-curve-to
|
||||
[to h1 h2]
|
||||
{:command :curve-to
|
||||
:relative false
|
||||
:params (make-curve-params to h1 h2)})
|
||||
|
||||
(defn prefix->coords [prefix]
|
||||
@@ -98,7 +95,7 @@
|
||||
(defn segment->point
|
||||
([segment] (segment->point segment :x))
|
||||
([segment coord]
|
||||
(let [params (get segment :params)]
|
||||
(when-let [params (not-empty (get segment :params))]
|
||||
(case coord
|
||||
:c1 (gpt/point (get params :c1x)
|
||||
(get params :c1y))
|
||||
|
||||
@@ -7,19 +7,21 @@
|
||||
(ns app.common.types.path.impl
|
||||
"Contains schemas and data type implementation for PathData binary
|
||||
and plain formats"
|
||||
#?(:cljs
|
||||
(:require-macros [app.common.types.path.impl :refer [read-float read-short write-float write-short]]))
|
||||
(:refer-clojure :exclude [-lookup -reduce])
|
||||
#?(:cljs (:require-macros [app.common.types.path.impl]))
|
||||
(:require
|
||||
#?(:clj [app.common.fressian :as fres])
|
||||
#?(:clj [clojure.data.json :as json])
|
||||
#?(:cljs [app.common.weak-map :as weak-map])
|
||||
[app.common.buffer :as buf]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.schema.generators :as sg]
|
||||
[app.common.svg.path :as svg.path]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.path :as-alias path])
|
||||
[app.common.types.path :as-alias path]
|
||||
[cuerdas.core :as str])
|
||||
(:import
|
||||
#?(:cljs [goog.string StringBuffer]
|
||||
:clj [java.nio ByteBuffer ByteOrder])))
|
||||
@@ -42,107 +44,56 @@
|
||||
;; IMPL HELPERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmacro read-short
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(.getInt16 ~target ~offset true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.getShort ~target ~offset))))
|
||||
|
||||
(defmacro read-float
|
||||
[target offset]
|
||||
(if (:ns &env)
|
||||
`(.getFloat32 ~target ~offset true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(double (.getFloat ~target ~offset)))))
|
||||
|
||||
(defmacro write-float
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(.setFloat32 ~target ~offset ~value true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.putFloat ~target ~offset ~value))))
|
||||
|
||||
(defmacro write-short
|
||||
[target offset value]
|
||||
(if (:ns &env)
|
||||
`(.setInt16 ~target ~offset ~value true)
|
||||
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
|
||||
`(.putShort ~target ~offset ~value))))
|
||||
|
||||
(defmacro with-cache
|
||||
"A helper macro that facilitates cache handling for content
|
||||
instance, only relevant on CLJS"
|
||||
[target key & expr]
|
||||
(if (:ns &env)
|
||||
(let [cache (gensym "cache-")
|
||||
target (with-meta target {:tag 'js})]
|
||||
`(let [~cache (.-cache ~target)
|
||||
~'result (.get ~cache ~key)]
|
||||
(let [target (with-meta target {:tag 'js})]
|
||||
`(let [~'cache (.-cache ~target)
|
||||
~'result (.get ~'cache ~key)]
|
||||
(if ~'result
|
||||
(do
|
||||
~'result)
|
||||
(let [~'result (do ~@expr)]
|
||||
(.set ~cache ~key ~'result)
|
||||
(.set ~'cache ~key ~'result)
|
||||
~'result))))
|
||||
`(do ~@expr)))
|
||||
|
||||
(defn- allocate
|
||||
[n-segments]
|
||||
#?(:clj (let [buffer (ByteBuffer/allocate (* n-segments SEGMENT-BYTE-SIZE))]
|
||||
(.order buffer ByteOrder/LITTLE_ENDIAN))
|
||||
:cljs (new js/ArrayBuffer (* n-segments SEGMENT-BYTE-SIZE))))
|
||||
|
||||
(defn- clone-buffer
|
||||
[buffer]
|
||||
#?(:clj
|
||||
(let [src (.array ^ByteBuffer buffer)
|
||||
len (alength ^bytes src)
|
||||
dst (byte-array len)]
|
||||
(System/arraycopy src 0 dst 0 len)
|
||||
(let [buffer (ByteBuffer/wrap dst)]
|
||||
(.order buffer ByteOrder/LITTLE_ENDIAN)))
|
||||
:cljs
|
||||
(let [src-view (js/Uint32Array. buffer)
|
||||
dst-buff (js/ArrayBuffer. (.-byteLength buffer))
|
||||
dst-view (js/Uint32Array. dst-buff)]
|
||||
(.set dst-view src-view)
|
||||
dst-buff)))
|
||||
|
||||
(defn- impl-transform-segment
|
||||
"Apply a transformation to a segment located under specified offset"
|
||||
[buffer offset a b c d e f]
|
||||
(let [t (read-short buffer offset)]
|
||||
(let [t (buf/read-short buffer offset)]
|
||||
(case t
|
||||
(1 2)
|
||||
(let [x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))
|
||||
x (+ (* x a) (* y c) e)
|
||||
y (+ (* x b) (* y d) f)]
|
||||
(write-float buffer (+ offset 20) x)
|
||||
(write-float buffer (+ offset 24) y))
|
||||
(let [x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))
|
||||
x' (+ (* x a) (* y c) e)
|
||||
y' (+ (* x b) (* y d) f)]
|
||||
(buf/write-float buffer (+ offset 20) x')
|
||||
(buf/write-float buffer (+ offset 24) y'))
|
||||
|
||||
3
|
||||
(let [c1x (read-float buffer (+ offset 4))
|
||||
c1y (read-float buffer (+ offset 8))
|
||||
c2x (read-float buffer (+ offset 12))
|
||||
c2y (read-float buffer (+ offset 16))
|
||||
x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))
|
||||
(let [c1x (buf/read-float buffer (+ offset 4))
|
||||
c1y (buf/read-float buffer (+ offset 8))
|
||||
c2x (buf/read-float buffer (+ offset 12))
|
||||
c2y (buf/read-float buffer (+ offset 16))
|
||||
x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))
|
||||
|
||||
c1x (+ (* c1x a) (* c1y c) e)
|
||||
c1y (+ (* c1x b) (* c1y d) f)
|
||||
c2x (+ (* c2x a) (* c2y c) e)
|
||||
c2y (+ (* c2x b) (* c2y d) f)
|
||||
x (+ (* x a) (* y c) e)
|
||||
y (+ (* x b) (* y d) f)]
|
||||
c1x' (+ (* c1x a) (* c1y c) e)
|
||||
c1y' (+ (* c1x b) (* c1y d) f)
|
||||
c2x' (+ (* c2x a) (* c2y c) e)
|
||||
c2y' (+ (* c2x b) (* c2y d) f)
|
||||
x' (+ (* x a) (* y c) e)
|
||||
y' (+ (* x b) (* y d) f)]
|
||||
|
||||
(write-float buffer (+ offset 4) c1x)
|
||||
(write-float buffer (+ offset 8) c1y)
|
||||
(write-float buffer (+ offset 12) c2x)
|
||||
(write-float buffer (+ offset 16) c2y)
|
||||
(write-float buffer (+ offset 20) x)
|
||||
(write-float buffer (+ offset 24) y))
|
||||
(buf/write-float buffer (+ offset 4) c1x')
|
||||
(buf/write-float buffer (+ offset 8) c1y')
|
||||
(buf/write-float buffer (+ offset 12) c2x')
|
||||
(buf/write-float buffer (+ offset 16) c2y')
|
||||
(buf/write-float buffer (+ offset 20) x')
|
||||
(buf/write-float buffer (+ offset 24) y'))
|
||||
|
||||
nil)))
|
||||
|
||||
@@ -166,13 +117,13 @@
|
||||
result (transient initial)]
|
||||
(if (< index size)
|
||||
(let [offset (* index SEGMENT-BYTE-SIZE)
|
||||
type (read-short buffer offset)
|
||||
c1x (read-float buffer (+ offset 4))
|
||||
c1y (read-float buffer (+ offset 8))
|
||||
c2x (read-float buffer (+ offset 12))
|
||||
c2y (read-float buffer (+ offset 16))
|
||||
x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))
|
||||
type (buf/read-short buffer offset)
|
||||
c1x (buf/read-float buffer (+ offset 4))
|
||||
c1y (buf/read-float buffer (+ offset 8))
|
||||
c2x (buf/read-float buffer (+ offset 12))
|
||||
c2y (buf/read-float buffer (+ offset 16))
|
||||
x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))
|
||||
type (case type
|
||||
1 :line-to
|
||||
2 :move-to
|
||||
@@ -191,13 +142,13 @@
|
||||
result initial]
|
||||
(if (< index size)
|
||||
(let [offset (* index SEGMENT-BYTE-SIZE)
|
||||
type (read-short buffer offset)
|
||||
c1x (read-float buffer (+ offset 4))
|
||||
c1y (read-float buffer (+ offset 8))
|
||||
c2x (read-float buffer (+ offset 12))
|
||||
c2y (read-float buffer (+ offset 16))
|
||||
x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))
|
||||
type (buf/read-short buffer offset)
|
||||
c1x (buf/read-float buffer (+ offset 4))
|
||||
c1y (buf/read-float buffer (+ offset 8))
|
||||
c2x (buf/read-float buffer (+ offset 12))
|
||||
c2y (buf/read-float buffer (+ offset 16))
|
||||
x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))
|
||||
type (case type
|
||||
1 :line-to
|
||||
2 :move-to
|
||||
@@ -212,13 +163,13 @@
|
||||
(defn impl-lookup
|
||||
[buffer index f]
|
||||
(let [offset (* index SEGMENT-BYTE-SIZE)
|
||||
type (read-short buffer offset)
|
||||
c1x (read-float buffer (+ offset 4))
|
||||
c1y (read-float buffer (+ offset 8))
|
||||
c2x (read-float buffer (+ offset 12))
|
||||
c2y (read-float buffer (+ offset 16))
|
||||
x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))
|
||||
type (buf/read-short buffer offset)
|
||||
c1x (buf/read-float buffer (+ offset 4))
|
||||
c1y (buf/read-float buffer (+ offset 8))
|
||||
c2x (buf/read-float buffer (+ offset 12))
|
||||
c2y (buf/read-float buffer (+ offset 16))
|
||||
x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))
|
||||
type (case type
|
||||
1 :line-to
|
||||
2 :move-to
|
||||
@@ -230,27 +181,27 @@
|
||||
(defn- to-string-segment*
|
||||
[buffer offset type ^StringBuilder builder]
|
||||
(case (long type)
|
||||
1 (let [x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))]
|
||||
1 (let [x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))]
|
||||
(doto builder
|
||||
(.append "M")
|
||||
(.append x)
|
||||
(.append ",")
|
||||
(.append y)))
|
||||
2 (let [x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))]
|
||||
2 (let [x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))]
|
||||
(doto builder
|
||||
(.append "L")
|
||||
(.append x)
|
||||
(.append ",")
|
||||
(.append y)))
|
||||
|
||||
3 (let [c1x (read-float buffer (+ offset 4))
|
||||
c1y (read-float buffer (+ offset 8))
|
||||
c2x (read-float buffer (+ offset 12))
|
||||
c2y (read-float buffer (+ offset 16))
|
||||
x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))]
|
||||
3 (let [c1x (buf/read-float buffer (+ offset 4))
|
||||
c1y (buf/read-float buffer (+ offset 8))
|
||||
c2x (buf/read-float buffer (+ offset 12))
|
||||
c2y (buf/read-float buffer (+ offset 16))
|
||||
x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))]
|
||||
(doto builder
|
||||
(.append "C")
|
||||
(.append c1x)
|
||||
@@ -275,7 +226,7 @@
|
||||
(loop [index 0]
|
||||
(when (< index size)
|
||||
(let [offset (* index SEGMENT-BYTE-SIZE)
|
||||
type (read-short buffer offset)]
|
||||
type (buf/read-short buffer offset)]
|
||||
(to-string-segment* buffer offset type builder)
|
||||
(recur (inc index)))))
|
||||
|
||||
@@ -285,26 +236,26 @@
|
||||
"Read segment from binary buffer at specified index"
|
||||
[buffer index]
|
||||
(let [offset (* index SEGMENT-BYTE-SIZE)
|
||||
type (read-short buffer offset)]
|
||||
type (buf/read-short buffer offset)]
|
||||
(case (long type)
|
||||
1 (let [x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))]
|
||||
1 (let [x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))]
|
||||
{:command :move-to
|
||||
:params {:x (double x)
|
||||
:y (double y)}})
|
||||
|
||||
2 (let [x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))]
|
||||
2 (let [x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))]
|
||||
{:command :line-to
|
||||
:params {:x (double x)
|
||||
:y (double y)}})
|
||||
|
||||
3 (let [c1x (read-float buffer (+ offset 4))
|
||||
c1y (read-float buffer (+ offset 8))
|
||||
c2x (read-float buffer (+ offset 12))
|
||||
c2y (read-float buffer (+ offset 16))
|
||||
x (read-float buffer (+ offset 20))
|
||||
y (read-float buffer (+ offset 24))]
|
||||
3 (let [c1x (buf/read-float buffer (+ offset 4))
|
||||
c1y (buf/read-float buffer (+ offset 8))
|
||||
c2x (buf/read-float buffer (+ offset 12))
|
||||
c2y (buf/read-float buffer (+ offset 16))
|
||||
x (buf/read-float buffer (+ offset 20))
|
||||
y (buf/read-float buffer (+ offset 24))]
|
||||
{:command :curve-to
|
||||
:params {:x (double x)
|
||||
:y (double y)
|
||||
@@ -316,10 +267,6 @@
|
||||
4 {:command :close-path
|
||||
:params {}})))
|
||||
|
||||
(defn- in-range?
|
||||
[size i]
|
||||
(and (< i size) (>= i 0)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TYPE: PATH-DATA
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -334,12 +281,12 @@
|
||||
|
||||
(equals [_ other]
|
||||
(if (instance? PathData other)
|
||||
(.equals ^ByteBuffer buffer (.-buffer ^PathData other))
|
||||
(buf/equals? buffer (.-buffer ^PathData other))
|
||||
false))
|
||||
|
||||
ITransformable
|
||||
(-transform [_ m]
|
||||
(let [buffer (clone-buffer buffer)]
|
||||
(let [buffer (buf/clone buffer)]
|
||||
(impl-transform buffer m size)
|
||||
(PathData. size buffer nil)))
|
||||
|
||||
@@ -361,7 +308,7 @@
|
||||
clojure.lang.IHashEq
|
||||
(hasheq [this]
|
||||
(when-not hash
|
||||
(set! hash (clojure.lang.Murmur3/hashOrdered (seq this))))
|
||||
(set! hash (clojure.lang.Murmur3/hashOrdered (vec this))))
|
||||
hash)
|
||||
|
||||
clojure.lang.Sequential
|
||||
@@ -387,12 +334,12 @@
|
||||
|
||||
clojure.lang.Indexed
|
||||
(nth [_ i]
|
||||
(if (in-range? size i)
|
||||
(if (d/in-range? size i)
|
||||
(read-segment buffer i)
|
||||
nil))
|
||||
|
||||
(nth [_ i default]
|
||||
(if (in-range? size i)
|
||||
(if (d/in-range? size i)
|
||||
(read-segment buffer i)
|
||||
default))
|
||||
|
||||
@@ -408,10 +355,10 @@
|
||||
|
||||
:cljs
|
||||
#_:clj-kondo/ignore
|
||||
(deftype PathData [size buffer dview cache ^:mutable __hash]
|
||||
(deftype PathData [size buffer cache ^:mutable __hash]
|
||||
Object
|
||||
(toString [_]
|
||||
(to-string dview size))
|
||||
(to-string buffer size))
|
||||
|
||||
IPathData
|
||||
(-get-byte-size [_]
|
||||
@@ -421,56 +368,43 @@
|
||||
;; NOTE: we still use u8 because until the heap refactor merge
|
||||
;; we can't guarrantee the alignment of offset on 4 bytes
|
||||
(assert (instance? js/ArrayBuffer into-buffer))
|
||||
(let [size (.-byteLength buffer)
|
||||
mem (js/Uint8Array. into-buffer offset size)]
|
||||
(.set mem (js/Uint8Array. buffer))))
|
||||
(let [buffer' (.-buffer ^js/DataView buffer)
|
||||
size (.-byteLength buffer')
|
||||
mem (js/Uint8Array. into-buffer offset size)]
|
||||
(.set mem (js/Uint8Array. buffer'))))
|
||||
|
||||
ITransformable
|
||||
(-transform [this m]
|
||||
(let [buffer (clone-buffer buffer)
|
||||
dview (js/DataView. buffer)]
|
||||
(impl-transform dview m size)
|
||||
(PathData. size buffer dview (weak-map/create) nil)))
|
||||
(let [buffer (buf/clone buffer)]
|
||||
(impl-transform buffer m size)
|
||||
(PathData. size buffer (weak-map/create) nil)))
|
||||
|
||||
(-walk [_ f initial]
|
||||
(impl-walk dview f initial size))
|
||||
(impl-walk buffer f initial size))
|
||||
|
||||
(-reduce [_ f initial]
|
||||
(impl-reduce dview f initial size))
|
||||
(impl-reduce buffer f initial size))
|
||||
|
||||
(-lookup [_ index f]
|
||||
(when (and (<= 0 index)
|
||||
(< index size))
|
||||
(impl-lookup dview index f)))
|
||||
(impl-lookup buffer index f)))
|
||||
|
||||
cljs.core/ISequential
|
||||
cljs.core/IEquiv
|
||||
(-equiv [this other]
|
||||
(if (instance? PathData other)
|
||||
(let [obuffer (.-buffer other)]
|
||||
(if (= (.-byteLength obuffer)
|
||||
(.-byteLength buffer))
|
||||
(let [cb (js/Uint32Array. buffer)
|
||||
ob (js/Uint32Array. obuffer)
|
||||
sz (alength cb)]
|
||||
(loop [i 0]
|
||||
(if (< i sz)
|
||||
(if (= (aget ob i)
|
||||
(aget cb i))
|
||||
(recur (inc i))
|
||||
false)
|
||||
true)))
|
||||
false))
|
||||
(buf/equals? buffer (.-buffer other))
|
||||
false))
|
||||
|
||||
cljs.core/IReduce
|
||||
(-reduce [_ f]
|
||||
(loop [index 1
|
||||
result (if (pos? size)
|
||||
(read-segment dview 0)
|
||||
(read-segment buffer 0)
|
||||
nil)]
|
||||
(if (< index size)
|
||||
(let [result (f result (read-segment dview index))]
|
||||
(let [result (f result (read-segment buffer index))]
|
||||
(if (reduced? result)
|
||||
@result
|
||||
(recur (inc index) result)))
|
||||
@@ -480,7 +414,7 @@
|
||||
(loop [index 0
|
||||
result start]
|
||||
(if (< index size)
|
||||
(let [result (f result (read-segment dview index))]
|
||||
(let [result (f result (read-segment buffer index))]
|
||||
(if (reduced? result)
|
||||
@result
|
||||
(recur (inc index) result)))
|
||||
@@ -495,13 +429,13 @@
|
||||
|
||||
cljs.core/IIndexed
|
||||
(-nth [_ i]
|
||||
(if (in-range? size i)
|
||||
(read-segment dview i)
|
||||
(if (d/in-range? size i)
|
||||
(read-segment buffer i)
|
||||
nil))
|
||||
|
||||
(-nth [_ i default]
|
||||
(if (in-range? i size)
|
||||
(read-segment dview i)
|
||||
(if (d/in-range? i size)
|
||||
(read-segment buffer i)
|
||||
default))
|
||||
|
||||
cljs.core/ISeqable
|
||||
@@ -509,7 +443,7 @@
|
||||
(when (pos? size)
|
||||
((fn next-seq [i]
|
||||
(when (< i size)
|
||||
(cons (read-segment dview i)
|
||||
(cons (read-segment buffer i)
|
||||
(lazy-seq (next-seq (inc i))))))
|
||||
0)))
|
||||
|
||||
@@ -574,18 +508,6 @@
|
||||
(= (:command e1) :move-to))))}
|
||||
schema:segment])
|
||||
|
||||
(def schema:content-like
|
||||
[:sequential schema:segment])
|
||||
|
||||
(def check-content-like
|
||||
(sm/check-fn schema:content-like))
|
||||
|
||||
(def check-segment
|
||||
(sm/check-fn schema:segment))
|
||||
|
||||
(def ^:private check-segments
|
||||
(sm/check-fn schema:segments))
|
||||
|
||||
(defn path-data?
|
||||
[o]
|
||||
(instance? PathData o))
|
||||
@@ -593,37 +515,42 @@
|
||||
(declare from-string)
|
||||
(declare from-plain)
|
||||
|
||||
;; Mainly used on backend: features/components_v2.clj
|
||||
(sm/register! ::path/segment schema:segment)
|
||||
(sm/register! ::path/segments schema:segments)
|
||||
(def schema:content
|
||||
(sm/type-schema
|
||||
{:type ::path/content
|
||||
:compile
|
||||
(fn [_ _ _]
|
||||
(let [decoder (delay (sm/decoder schema:segments sm/json-transformer))
|
||||
generator (->> (sg/generator schema:segments)
|
||||
(sg/filter not-empty)
|
||||
(sg/fmap from-plain))]
|
||||
{:pred path-data?
|
||||
:type-properties
|
||||
{:gen/gen generator
|
||||
:encode/json identity
|
||||
:decode/json (fn [s]
|
||||
(cond
|
||||
(string? s)
|
||||
(if (str/empty? s)
|
||||
(from-plain [])
|
||||
(from-string s))
|
||||
|
||||
(sm/register!
|
||||
{:type ::path/content
|
||||
:compile
|
||||
(fn [_ _ _]
|
||||
(let [decoder (delay (sm/decoder schema:segments sm/json-transformer))
|
||||
generator (->> (sg/generator schema:segments)
|
||||
(sg/filter not-empty)
|
||||
(sg/fmap from-plain))]
|
||||
{:pred path-data?
|
||||
:type-properties
|
||||
{:gen/gen generator
|
||||
:encode/json identity
|
||||
:decode/json (fn [s]
|
||||
(cond
|
||||
(string? s)
|
||||
(from-string s)
|
||||
(vector? s)
|
||||
(let [decode-fn (deref decoder)]
|
||||
(-> (decode-fn s)
|
||||
(from-plain)))
|
||||
|
||||
(vector? s)
|
||||
(let [decode-fn (deref decoder)]
|
||||
(-> (decode-fn s)
|
||||
(from-plain)))
|
||||
:else
|
||||
s))}}))}))
|
||||
|
||||
:else
|
||||
s))}}))})
|
||||
(def check-plain-content
|
||||
(sm/check-fn schema:segments))
|
||||
|
||||
(def check-path-content
|
||||
(sm/check-fn ::path/content))
|
||||
(def check-segment
|
||||
(sm/check-fn schema:segment))
|
||||
|
||||
(def check-content
|
||||
(sm/check-fn schema:content))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; CONSTRUCTORS & PREDICATES
|
||||
@@ -659,17 +586,15 @@
|
||||
(let [size (.-byteLength buffer)
|
||||
count (long (/ size SEGMENT-BYTE-SIZE))]
|
||||
(PathData. count
|
||||
buffer
|
||||
(js/DataView. buffer)
|
||||
(weak-map/create)
|
||||
nil))
|
||||
|
||||
(instance? js/DataView buffer)
|
||||
(let [dview buffer
|
||||
buffer (.-buffer dview)
|
||||
size (.-byteLength buffer)
|
||||
count (long (/ size SEGMENT-BYTE-SIZE))]
|
||||
(PathData. count buffer dview (weak-map/create) nil))
|
||||
(let [buffer' (.-buffer ^js/DataView buffer)
|
||||
size (.-byteLength ^js/ArrayBuffer buffer')
|
||||
count (long (/ size SEGMENT-BYTE-SIZE))]
|
||||
(PathData. count buffer (weak-map/create) nil))
|
||||
|
||||
(instance? js/Uint8Array buffer)
|
||||
(from-bytes (.-buffer buffer))
|
||||
@@ -686,12 +611,10 @@
|
||||
(defn from-plain
|
||||
"Create a PathData instance from plain data structures"
|
||||
[segments]
|
||||
(assert (check-segments segments))
|
||||
(assert (check-plain-content segments))
|
||||
|
||||
(let [total (count segments)
|
||||
#?@(:cljs [buffer' (allocate total)
|
||||
buffer (new js/DataView buffer')]
|
||||
:clj [buffer (allocate total)])]
|
||||
(let [total (count segments)
|
||||
buffer (buf/allocate (* total SEGMENT-BYTE-SIZE))]
|
||||
(loop [index 0]
|
||||
(when (< index total)
|
||||
(let [segment (nth segments index)
|
||||
@@ -701,18 +624,18 @@
|
||||
(let [params (get segment :params)
|
||||
x (float (get params :x))
|
||||
y (float (get params :y))]
|
||||
(write-short buffer offset 1)
|
||||
(write-float buffer (+ offset 20) x)
|
||||
(write-float buffer (+ offset 24) y))
|
||||
(buf/write-short buffer offset 1)
|
||||
(buf/write-float buffer (+ offset 20) x)
|
||||
(buf/write-float buffer (+ offset 24) y))
|
||||
|
||||
:line-to
|
||||
(let [params (get segment :params)
|
||||
x (float (get params :x))
|
||||
y (float (get params :y))]
|
||||
|
||||
(write-short buffer offset 2)
|
||||
(write-float buffer (+ offset 20) x)
|
||||
(write-float buffer (+ offset 24) y))
|
||||
(buf/write-short buffer offset 2)
|
||||
(buf/write-float buffer (+ offset 20) x)
|
||||
(buf/write-float buffer (+ offset 24) y))
|
||||
|
||||
:curve-to
|
||||
(let [params (get segment :params)
|
||||
@@ -723,16 +646,16 @@
|
||||
c2x (float (get params :c2x x))
|
||||
c2y (float (get params :c2y y))]
|
||||
|
||||
(write-short buffer offset 3)
|
||||
(write-float buffer (+ offset 4) c1x)
|
||||
(write-float buffer (+ offset 8) c1y)
|
||||
(write-float buffer (+ offset 12) c2x)
|
||||
(write-float buffer (+ offset 16) c2y)
|
||||
(write-float buffer (+ offset 20) x)
|
||||
(write-float buffer (+ offset 24) y))
|
||||
(buf/write-short buffer offset 3)
|
||||
(buf/write-float buffer (+ offset 4) c1x)
|
||||
(buf/write-float buffer (+ offset 8) c1y)
|
||||
(buf/write-float buffer (+ offset 12) c2x)
|
||||
(buf/write-float buffer (+ offset 16) c2y)
|
||||
(buf/write-float buffer (+ offset 20) x)
|
||||
(buf/write-float buffer (+ offset 24) y))
|
||||
|
||||
:close-path
|
||||
(write-short buffer offset 4))
|
||||
(buf/write-short buffer offset 4))
|
||||
(recur (inc index)))))
|
||||
|
||||
(from-bytes buffer)))
|
||||
@@ -763,7 +686,7 @@
|
||||
:class PathData
|
||||
:wfn (fn [^PathData pdata]
|
||||
(let [buffer (.-buffer pdata)]
|
||||
#?(:cljs (js/Uint8Array. buffer)
|
||||
#?(:cljs (js/Uint8Array. (.-buffer ^js/DataView buffer))
|
||||
:clj (.array ^ByteBuffer buffer))))
|
||||
:rfn from-bytes})
|
||||
|
||||
|
||||
@@ -54,11 +54,12 @@
|
||||
result)))
|
||||
{})))
|
||||
|
||||
;; FIXME: can be optimized with internal reduction
|
||||
(defn point-indices
|
||||
[content point]
|
||||
(->> (d/enumerate content)
|
||||
(filter (fn [[_ segment]] (= point (helpers/segment->point segment))))
|
||||
(mapv (fn [[index _]] index))))
|
||||
(map (fn [[index _]] index))))
|
||||
|
||||
(defn handler-indices
|
||||
"Return an index where the key is the positions and the values the handlers"
|
||||
@@ -272,41 +273,51 @@
|
||||
|
||||
(defn- remove-line-curves
|
||||
"Remove all curves that have both handlers in the same position that the
|
||||
beginning and end points. This makes them really line-to commands"
|
||||
[content]
|
||||
(let [with-prev (d/enumerate (d/with-prev content))
|
||||
process-command
|
||||
(fn [content [index [command prev]]]
|
||||
beginning and end points. This makes them really line-to commands.
|
||||
|
||||
(let [cur-point (helpers/segment->point command)
|
||||
NOTE: works with plain format so it expects to receive a vector"
|
||||
[content]
|
||||
(assert (vector? content) "expected a plain format for `content`")
|
||||
|
||||
(let [with-prev (d/enumerate (d/with-prev content))
|
||||
|
||||
process-segment
|
||||
(fn [content [index [segment prev]]]
|
||||
(let [cur-point (helpers/segment->point segment)
|
||||
pre-point (helpers/segment->point prev)
|
||||
handler-c1 (get-handler command :c1)
|
||||
handler-c2 (get-handler command :c2)]
|
||||
(if (and (= :curve-to (:command command))
|
||||
handler-c1 (get-handler segment :c1)
|
||||
handler-c2 (get-handler segment :c2)]
|
||||
(if (and (= :curve-to (:command segment))
|
||||
(= cur-point handler-c2)
|
||||
(= pre-point handler-c1))
|
||||
(assoc content index {:command :line-to
|
||||
:params (into {} cur-point)})
|
||||
content)))]
|
||||
|
||||
(reduce process-command content with-prev)))
|
||||
(reduce process-segment content with-prev)))
|
||||
|
||||
(defn make-corner-point
|
||||
"Changes the content to make a point a 'corner'"
|
||||
[content point]
|
||||
(let [handlers (-> (get-handlers content)
|
||||
(get point))
|
||||
change-content
|
||||
(let [handlers
|
||||
(-> (get-handlers content)
|
||||
(get point))
|
||||
|
||||
transform-content
|
||||
(fn [content [index prefix]]
|
||||
(let [cx (d/prefix-keyword prefix :x)
|
||||
cy (d/prefix-keyword prefix :y)]
|
||||
(-> content
|
||||
(assoc-in [index :params cx] (:x point))
|
||||
(assoc-in [index :params cy] (:y point)))))]
|
||||
(as-> content $
|
||||
(reduce change-content $ handlers)
|
||||
(remove-line-curves $))))
|
||||
(assoc-in [index :params cy] (:y point)))))
|
||||
|
||||
content
|
||||
(reduce transform-content (vec content) handlers)
|
||||
|
||||
content
|
||||
(remove-line-curves content)]
|
||||
|
||||
(impl/from-plain content)))
|
||||
|
||||
(defn- line->curve
|
||||
[from-p segment]
|
||||
@@ -346,69 +357,95 @@
|
||||
|
||||
(def ^:private xf:mapcat-points
|
||||
(comp
|
||||
(mapcat #(vector (:next-p %) (:prev-p %)))
|
||||
(mapcat #(list (:next-p %) (:prev-p %)))
|
||||
(remove nil?)))
|
||||
|
||||
(defn make-curve-point
|
||||
"Changes the content to make the point a 'curve'. The handlers will be positioned
|
||||
in the same vector that results from the previous->next points but with fixed length."
|
||||
"Changes the content to make the point a 'curve'. The handlers will be
|
||||
positioned in the same vector that results from the previous->next
|
||||
points but with fixed length; return a plain segments vector"
|
||||
[content point]
|
||||
|
||||
(let [indices (point-indices content point)
|
||||
vectors (map (fn [index]
|
||||
(let [segment (nth content index)
|
||||
prev-i (dec index)
|
||||
prev (when (not (= :move-to (:command segment)))
|
||||
(get content prev-i))
|
||||
next-i (inc index)
|
||||
next (get content next-i)
|
||||
(let [;; We perform this operation before because it can be
|
||||
;; optimized with internal reduction so is better to use the
|
||||
;; PathData type before converting it to plain vector.
|
||||
indices
|
||||
(point-indices content point)
|
||||
|
||||
next (when (not (= :move-to (:command next)))
|
||||
next)]
|
||||
{:index index
|
||||
:prev-i (when (some? prev) prev-i)
|
||||
:prev-c prev
|
||||
:prev-p (helpers/segment->point prev)
|
||||
:next-i (when (some? next) next-i)
|
||||
:next-c next
|
||||
:next-p (helpers/segment->point next)
|
||||
:segment segment}))
|
||||
indices)
|
||||
;; We transform content to a plain format for execute the
|
||||
;; algorithm because right now is the only way to execute it
|
||||
content
|
||||
(vec content)
|
||||
|
||||
points (into #{} xf:mapcat-points vectors)]
|
||||
vectors
|
||||
(map (fn [index]
|
||||
(let [segment (get content index)
|
||||
prev-i (dec index)
|
||||
prev (when (not (= :move-to (:command segment)))
|
||||
(get content prev-i))
|
||||
next-i (inc index)
|
||||
next (get content next-i)
|
||||
next (when (not (= :move-to (:command next)))
|
||||
next)]
|
||||
{:index index
|
||||
:prev-i (when (some? prev) prev-i)
|
||||
:prev-c prev
|
||||
:prev-p (helpers/segment->point prev)
|
||||
:next-i (when (some? next) next-i)
|
||||
:next-c next
|
||||
:next-p (helpers/segment->point next)
|
||||
:segment segment}))
|
||||
indices)
|
||||
|
||||
points
|
||||
(into #{} xf:mapcat-points vectors)]
|
||||
|
||||
(if (= (count points) 2)
|
||||
(let [v1 (gpt/to-vec (first points) point)
|
||||
v2 (gpt/to-vec (first points) (second points))
|
||||
(let [[fpoint spoint] (vec points)
|
||||
v1 (gpt/to-vec fpoint point)
|
||||
v2 (gpt/to-vec fpoint spoint)
|
||||
vp (gpt/project v1 v2)
|
||||
vh (gpt/subtract v1 vp)
|
||||
|
||||
add-curve
|
||||
(fn [content {:keys [index prev-p next-p next-i]}]
|
||||
(let [cur-segment (get content index)
|
||||
(let [curr-segment (get content index)
|
||||
curr-command (get curr-segment :command)
|
||||
|
||||
next-segment (get content next-i)
|
||||
next-command (get next-segment :command)
|
||||
|
||||
;; New handlers for prev-point and next-point
|
||||
prev-h (when (some? prev-p) (gpt/add prev-p vh))
|
||||
next-h (when (some? next-p) (gpt/add next-p vh))
|
||||
prev-h
|
||||
(when (some? prev-p) (gpt/add prev-p vh))
|
||||
|
||||
next-h
|
||||
(when (some? next-p) (gpt/add next-p vh))
|
||||
|
||||
;; Correct 1/3 to the point improves the curve
|
||||
prev-correction (when (some? prev-h) (gpt/scale (gpt/to-vec prev-h point) (/ 1 3)))
|
||||
next-correction (when (some? next-h) (gpt/scale (gpt/to-vec next-h point) (/ 1 3)))
|
||||
prev-correction
|
||||
(when (some? prev-h) (gpt/scale (gpt/to-vec prev-h point) (/ 1 3)))
|
||||
|
||||
next-correction
|
||||
(when (some? next-h) (gpt/scale (gpt/to-vec next-h point) (/ 1 3)))
|
||||
|
||||
prev-h
|
||||
(when (some? prev-h) (gpt/add prev-h prev-correction))
|
||||
|
||||
next-h
|
||||
(when (some? next-h) (gpt/add next-h next-correction))]
|
||||
|
||||
prev-h (when (some? prev-h) (gpt/add prev-h prev-correction))
|
||||
next-h (when (some? next-h) (gpt/add next-h next-correction))]
|
||||
(cond-> content
|
||||
(and (= :line-to (:command cur-segment)) (some? prev-p))
|
||||
(and (= :line-to curr-command) (some? prev-p))
|
||||
(update index helpers/update-curve-to prev-p prev-h)
|
||||
|
||||
(and (= :line-to (:command next-segment)) (some? next-p))
|
||||
(and (= :line-to next-command) (some? next-p))
|
||||
(update next-i helpers/update-curve-to next-h next-p)
|
||||
|
||||
(and (= :curve-to (:command cur-segment)) (some? prev-p))
|
||||
(and (= :curve-to curr-command) (some? prev-p))
|
||||
(update index update-handler :c2 prev-h)
|
||||
|
||||
(and (= :curve-to (:command next-segment)) (some? next-p))
|
||||
(and (= :curve-to next-command) (some? next-p))
|
||||
(update next-i update-handler :c1 next-h))))]
|
||||
|
||||
(reduce add-curve content vectors))
|
||||
@@ -587,27 +624,38 @@
|
||||
(rest content))))))))
|
||||
|
||||
(defn join-nodes
|
||||
"Creates new segments between points that weren't previously"
|
||||
"Creates new segments between points that weren't previously.
|
||||
Returns plain segments vector."
|
||||
[content points]
|
||||
|
||||
(let [segments-set (into #{}
|
||||
(map (juxt :start :end))
|
||||
(get-segments-with-points content points))
|
||||
(let [;; Materialize the content to a vector (plain format)
|
||||
content
|
||||
(vec content)
|
||||
|
||||
create-line-command (fn [point other]
|
||||
[(helpers/make-move-to point)
|
||||
(helpers/make-line-to other)])
|
||||
segments-set
|
||||
(into #{}
|
||||
(map (juxt :start :end))
|
||||
(get-segments-with-points content points))
|
||||
|
||||
not-segment? (fn [point other] (and (not (contains? segments-set [point other]))
|
||||
(not (contains? segments-set [other point]))))
|
||||
create-line-segment
|
||||
(fn [point other]
|
||||
[(helpers/make-move-to point)
|
||||
(helpers/make-line-to other)])
|
||||
|
||||
new-content (->> (d/map-perm create-line-command not-segment? points)
|
||||
(flatten)
|
||||
(into []))]
|
||||
not-segment?
|
||||
(fn [point other]
|
||||
(and (not (contains? segments-set [point other]))
|
||||
(not (contains? segments-set [other point]))))
|
||||
|
||||
;; FIXME: implement map-perm in terms of transducer, will
|
||||
;; improve performance and remove the need to use flatten
|
||||
new-content
|
||||
(->> (d/map-perm create-line-segment not-segment? points)
|
||||
(flatten)
|
||||
(into []))]
|
||||
|
||||
(into content new-content)))
|
||||
|
||||
|
||||
(defn separate-nodes
|
||||
"Removes the segments between the points given"
|
||||
[content points]
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
[app.common.schema.generators :as sg]
|
||||
[app.common.text :as txt]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.color :as types.color]
|
||||
[app.common.types.fill :refer [schema:fill]]
|
||||
[app.common.types.grid :as ctg]
|
||||
[app.common.types.path :as path]
|
||||
[app.common.types.path.segment :as path.segment]
|
||||
@@ -119,36 +120,47 @@
|
||||
(def schema:points
|
||||
[:vector {:gen/max 4 :gen/min 4} ::gpt/point])
|
||||
|
||||
(def schema:fill
|
||||
(sm/register!
|
||||
^{::sm/type ::fill}
|
||||
[:map {:title "Fill"}
|
||||
[:fill-color {:optional true} ::ctc/rgb-color]
|
||||
[:fill-opacity {:optional true} ::sm/safe-number]
|
||||
[:fill-color-gradient {:optional true} [:maybe ::ctc/gradient]]
|
||||
[:fill-color-ref-file {:optional true} [:maybe ::sm/uuid]]
|
||||
[:fill-color-ref-id {:optional true} [:maybe ::sm/uuid]]
|
||||
[:fill-image {:optional true} ::ctc/image-color]]))
|
||||
;; FIXME: the register is necessary until this is moved to a separated
|
||||
;; ns because it is used on shapes.text
|
||||
(def valid-stroke-attrs
|
||||
"A set used for proper check if color should contain only one of the
|
||||
attrs listed in this set."
|
||||
#{:stroke-image :stroke-color :stroke-color-gradient})
|
||||
|
||||
(defn has-valid-stroke-attrs?
|
||||
"Check if color has correct color attrs"
|
||||
[color]
|
||||
(let [attrs (set (keys color))
|
||||
result (set/intersection attrs valid-stroke-attrs)]
|
||||
(= 1 (count result))))
|
||||
|
||||
(def schema:stroke-attrs
|
||||
[:map {:title "StrokeAttrs" :closed true}
|
||||
[:stroke-color-ref-file {:optional true} ::sm/uuid]
|
||||
[:stroke-color-ref-id {:optional true} ::sm/uuid]
|
||||
[:stroke-opacity {:optional true} ::sm/safe-number]
|
||||
[:stroke-style {:optional true}
|
||||
[::sm/one-of #{:solid :dotted :dashed :mixed}]]
|
||||
[:stroke-width {:optional true} ::sm/safe-number]
|
||||
[:stroke-alignment {:optional true}
|
||||
[::sm/one-of #{:center :inner :outer}]]
|
||||
[:stroke-cap-start {:optional true}
|
||||
[::sm/one-of stroke-caps]]
|
||||
[:stroke-cap-end {:optional true}
|
||||
[::sm/one-of stroke-caps]]
|
||||
[:stroke-color {:optional true} types.color/schema:hex-color]
|
||||
[:stroke-color-gradient {:optional true} types.color/schema:gradient]
|
||||
[:stroke-image {:optional true} types.color/schema:image]])
|
||||
|
||||
(def stroke-attrs
|
||||
"A set of attrs that corresponds to stroke data type"
|
||||
(sm/keys schema:stroke-attrs))
|
||||
|
||||
(def schema:stroke
|
||||
(sm/register!
|
||||
^{::sm/type ::stroke}
|
||||
[:map {:title "Stroke"}
|
||||
[:stroke-color {:optional true} :string]
|
||||
[:stroke-color-ref-file {:optional true} ::sm/uuid]
|
||||
[:stroke-color-ref-id {:optional true} ::sm/uuid]
|
||||
[:stroke-opacity {:optional true} ::sm/safe-number]
|
||||
[:stroke-style {:optional true}
|
||||
[::sm/one-of #{:solid :dotted :dashed :mixed :none :svg}]]
|
||||
[:stroke-width {:optional true} ::sm/safe-number]
|
||||
[:stroke-alignment {:optional true}
|
||||
[::sm/one-of #{:center :inner :outer}]]
|
||||
[:stroke-cap-start {:optional true}
|
||||
[::sm/one-of stroke-caps]]
|
||||
[:stroke-cap-end {:optional true}
|
||||
[::sm/one-of stroke-caps]]
|
||||
[:stroke-color-gradient {:optional true} ::ctc/gradient]
|
||||
[:stroke-image {:optional true} ::ctc/image-color]]))
|
||||
[:and schema:stroke-attrs
|
||||
[:fn has-valid-stroke-attrs?]]))
|
||||
|
||||
(def check-stroke
|
||||
(sm/check-fn schema:stroke))
|
||||
@@ -216,7 +228,7 @@
|
||||
[:blur {:optional true} ::ctsb/blur]
|
||||
[:grow-type {:optional true}
|
||||
[::sm/one-of grow-types]]
|
||||
[:applied-tokens {:optional true} ::cto/applied-tokens]
|
||||
[:applied-tokens {:optional true} cto/schema:applied-tokens]
|
||||
[:plugin-data {:optional true} ::ctpg/plugin-data]])
|
||||
|
||||
(def schema:group-attrs
|
||||
@@ -234,7 +246,7 @@
|
||||
[:map {:title "BoolAttrs"}
|
||||
[:shapes [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]
|
||||
[:bool-type [::sm/one-of bool-types]]
|
||||
[:content ::path/content]])
|
||||
[:content path/schema:content]])
|
||||
|
||||
(def ^:private schema:rect-attrs
|
||||
[:map {:title "RectAttrs"}])
|
||||
@@ -259,7 +271,7 @@
|
||||
|
||||
(def ^:private schema:path-attrs
|
||||
[:map {:title "PathAttrs"}
|
||||
[:content ::path/content]])
|
||||
[:content path/schema:content]])
|
||||
|
||||
(def ^:private schema:text-attrs
|
||||
[:map {:title "TextAttrs"}
|
||||
@@ -301,7 +313,7 @@
|
||||
:title "Shape"}
|
||||
[:group
|
||||
[:merge {:title "GroupShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:group-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -309,8 +321,8 @@
|
||||
|
||||
[:frame
|
||||
[:merge {:title "FrameShape"}
|
||||
ctsl/schema:layout-child-attrs
|
||||
ctsl/schema:layout-attrs
|
||||
::ctsl/layout-attrs
|
||||
schema:frame-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -320,14 +332,14 @@
|
||||
|
||||
[:bool
|
||||
[:merge {:title "BoolShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:bool-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-base-attrs]]
|
||||
|
||||
[:rect
|
||||
[:merge {:title "RectShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:rect-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -335,7 +347,7 @@
|
||||
|
||||
[:circle
|
||||
[:merge {:title "CircleShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:circle-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -343,7 +355,7 @@
|
||||
|
||||
[:image
|
||||
[:merge {:title "ImageShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:image-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -351,7 +363,7 @@
|
||||
|
||||
[:svg-raw
|
||||
[:merge {:title "SvgRawShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:svg-raw-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -359,14 +371,14 @@
|
||||
|
||||
[:path
|
||||
[:merge {:title "PathShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:path-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-base-attrs]]
|
||||
|
||||
[:text
|
||||
[:merge {:title "TextShape"}
|
||||
ctsl/schema:layout-attrs
|
||||
ctsl/schema:layout-child-attrs
|
||||
schema:text-attrs
|
||||
schema:shape-generic-attrs
|
||||
schema:shape-geom-attrs
|
||||
@@ -670,23 +682,6 @@
|
||||
:r3
|
||||
:r4})
|
||||
|
||||
(def ^:private layout-extract-props
|
||||
#{:layout
|
||||
:layout-flex-dir
|
||||
:layout-gap-type
|
||||
:layout-gap
|
||||
:layout-wrap-type
|
||||
:layout-align-items
|
||||
:layout-align-content
|
||||
:layout-justify-items
|
||||
:layout-justify-content
|
||||
:layout-padding-type
|
||||
:layout-padding
|
||||
:layout-grid-dir
|
||||
:layout-grid-rows
|
||||
:layout-grid-columns
|
||||
:layout-grid-cells})
|
||||
|
||||
(defn extract-props
|
||||
"Retrieves an object with the 'pasteable' properties for a shape."
|
||||
[shape]
|
||||
@@ -717,9 +712,8 @@
|
||||
(assoc-props node txt/text-node-attrs)))
|
||||
props)))
|
||||
|
||||
(extract-layout-props
|
||||
[props shape]
|
||||
(d/patch-object props (select-keys shape layout-extract-props)))]
|
||||
(extract-layout-attrs [props shape]
|
||||
(d/patch-object props (select-keys shape ctsl/layout-attrs)))]
|
||||
|
||||
(let [;; For texts we don't extract the fill
|
||||
extract-props
|
||||
@@ -727,7 +721,7 @@
|
||||
(-> shape
|
||||
(select-keys extract-props)
|
||||
(cond-> (cfh/text-shape? shape) (extract-text-props shape))
|
||||
(cond-> (ctsl/any-layout? shape) (extract-layout-props shape))))))
|
||||
(cond-> (ctsl/any-layout? shape) (extract-layout-attrs shape))))))
|
||||
|
||||
(defn patch-props
|
||||
"Given the object of `extract-props` applies it to a shape. Adapt the shape if necesary"
|
||||
@@ -752,7 +746,7 @@
|
||||
(d/patch-object (select-keys props txt/text-node-attrs))))))))))
|
||||
|
||||
(patch-layout-props [shape props]
|
||||
(let [shape (d/patch-object shape (select-keys props layout-extract-props))]
|
||||
(let [shape (d/patch-object shape (select-keys props ctsl/layout-attrs))]
|
||||
(cond-> shape
|
||||
(ctsl/grid-layout? shape)
|
||||
(ctsl/assign-cells objects))))]
|
||||
@@ -761,5 +755,3 @@
|
||||
(d/patch-object (select-keys props basic-extract-props))
|
||||
(cond-> (cfh/text-shape? shape) (patch-text-props props))
|
||||
(cond-> (cfh/frame-shape? shape) (patch-layout-props props)))))
|
||||
|
||||
(def MAX-GRADIENT-STOPS 16)
|
||||
@@ -43,7 +43,6 @@
|
||||
;; :layout-item-absolute ;; boolean
|
||||
;; :layout-item-z-index ;; int
|
||||
|
||||
|
||||
(def layout-types
|
||||
#{:flex :grid})
|
||||
|
||||
@@ -74,49 +73,6 @@
|
||||
(def justify-items-types
|
||||
#{:start :end :center :stretch})
|
||||
|
||||
(def layout-item-props
|
||||
[:layout-item-margin
|
||||
:layout-item-margin-type
|
||||
:layout-item-h-sizing
|
||||
:layout-item-v-sizing
|
||||
:layout-item-max-h
|
||||
:layout-item-min-h
|
||||
:layout-item-max-w
|
||||
:layout-item-min-w
|
||||
:layout-item-absolute
|
||||
:layout-item-z-index])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::layout-attrs}
|
||||
[:map {:title "LayoutAttrs"}
|
||||
[:layout {:optional true} [::sm/one-of layout-types]]
|
||||
[:layout-flex-dir {:optional true} [::sm/one-of flex-direction-types]]
|
||||
[:layout-gap {:optional true}
|
||||
[:map
|
||||
[:row-gap {:optional true} ::sm/safe-number]
|
||||
[:column-gap {:optional true} ::sm/safe-number]]]
|
||||
[:layout-gap-type {:optional true} [::sm/one-of gap-types]]
|
||||
[:layout-wrap-type {:optional true} [::sm/one-of wrap-types]]
|
||||
[:layout-padding-type {:optional true} [::sm/one-of padding-type]]
|
||||
[:layout-padding {:optional true}
|
||||
[:map
|
||||
[:p1 ::sm/safe-number]
|
||||
[:p2 ::sm/safe-number]
|
||||
[:p3 ::sm/safe-number]
|
||||
[:p4 ::sm/safe-number]]]
|
||||
[:layout-justify-content {:optional true} [::sm/one-of justify-content-types]]
|
||||
[:layout-justify-items {:optional true} [::sm/one-of justify-items-types]]
|
||||
[:layout-align-content {:optional true} [::sm/one-of align-content-types]]
|
||||
[:layout-align-items {:optional true} [::sm/one-of align-items-types]]
|
||||
|
||||
[:layout-grid-dir {:optional true} [::sm/one-of grid-direction-types]]
|
||||
[:layout-grid-rows {:optional true}
|
||||
[:vector {:gen/max 2} ::grid-track]]
|
||||
[:layout-grid-columns {:optional true}
|
||||
[:vector {:gen/max 2} ::grid-track]]
|
||||
[:layout-grid-cells {:optional true}
|
||||
[:map-of {:gen/max 5} ::sm/uuid ::grid-cell]]])
|
||||
|
||||
;; Grid types
|
||||
(def grid-track-types
|
||||
#{:percent :flex :auto :fixed})
|
||||
@@ -130,29 +86,59 @@
|
||||
(def grid-cell-justify-self-types
|
||||
#{:auto :start :center :end :stretch})
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::grid-cell}
|
||||
[:map {:title "GridCell"}
|
||||
[:id ::sm/uuid]
|
||||
[:area-name {:optional true} :string]
|
||||
[:row ::sm/safe-int]
|
||||
[:row-span ::sm/safe-int]
|
||||
[:column ::sm/safe-int]
|
||||
[:column-span ::sm/safe-int]
|
||||
[:position {:optional true} [::sm/one-of grid-position-types]]
|
||||
[:align-self {:optional true} [::sm/one-of grid-cell-align-self-types]]
|
||||
[:justify-self {:optional true} [::sm/one-of grid-cell-justify-self-types]]
|
||||
[:shapes
|
||||
[:vector {:gen/max 1} ::sm/uuid]]])
|
||||
(def ^:private schema:grid-cell
|
||||
[:map {:title "GridCell"}
|
||||
[:id ::sm/uuid]
|
||||
[:area-name {:optional true} :string]
|
||||
[:row ::sm/safe-int]
|
||||
[:row-span ::sm/safe-int]
|
||||
[:column ::sm/safe-int]
|
||||
[:column-span ::sm/safe-int]
|
||||
[:position {:optional true} [::sm/one-of grid-position-types]]
|
||||
[:align-self {:optional true} [::sm/one-of grid-cell-align-self-types]]
|
||||
[:justify-self {:optional true} [::sm/one-of grid-cell-justify-self-types]]
|
||||
[:shapes
|
||||
[:vector {:gen/max 1} ::sm/uuid]]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::grid-track}
|
||||
[:map {:title "GridTrack"}
|
||||
[:type [::sm/one-of grid-track-types]]
|
||||
[:value {:optional true} [:maybe ::sm/safe-number]]])
|
||||
(def ^:private schema:grid-track
|
||||
[:map {:title "GridTrack"}
|
||||
[:type [::sm/one-of grid-track-types]]
|
||||
[:value {:optional true} [:maybe ::sm/safe-number]]])
|
||||
|
||||
(def check-grid-track!
|
||||
(sm/check-fn ::grid-track))
|
||||
(def schema:layout-attrs
|
||||
[:map {:title "LayoutAttrs"}
|
||||
[:layout {:optional true} [::sm/one-of layout-types]]
|
||||
[:layout-flex-dir {:optional true} [::sm/one-of flex-direction-types]]
|
||||
[:layout-gap {:optional true}
|
||||
[:map
|
||||
[:row-gap {:optional true} ::sm/safe-number]
|
||||
[:column-gap {:optional true} ::sm/safe-number]]]
|
||||
[:layout-gap-type {:optional true} [::sm/one-of gap-types]]
|
||||
[:layout-wrap-type {:optional true} [::sm/one-of wrap-types]]
|
||||
[:layout-padding-type {:optional true} [::sm/one-of padding-type]]
|
||||
[:layout-padding {:optional true}
|
||||
[:map
|
||||
[:p1 ::sm/safe-number]
|
||||
[:p2 ::sm/safe-number]
|
||||
[:p3 ::sm/safe-number]
|
||||
[:p4 ::sm/safe-number]]]
|
||||
[:layout-justify-content {:optional true} [::sm/one-of justify-content-types]]
|
||||
[:layout-justify-items {:optional true} [::sm/one-of justify-items-types]]
|
||||
[:layout-align-content {:optional true} [::sm/one-of align-content-types]]
|
||||
[:layout-align-items {:optional true} [::sm/one-of align-items-types]]
|
||||
[:layout-grid-dir {:optional true} [::sm/one-of grid-direction-types]]
|
||||
[:layout-grid-rows {:optional true}
|
||||
[:vector {:gen/max 2} schema:grid-track]]
|
||||
[:layout-grid-columns {:optional true}
|
||||
[:vector {:gen/max 2} schema:grid-track]]
|
||||
[:layout-grid-cells {:optional true}
|
||||
[:map-of {:gen/max 5} ::sm/uuid schema:grid-cell]]])
|
||||
|
||||
(def ^:private check-grid-track
|
||||
(sm/check-fn schema:grid-track))
|
||||
|
||||
(def layout-attrs
|
||||
(sm/keys schema:layout-attrs))
|
||||
|
||||
;; LAYOUT CHILDREN
|
||||
|
||||
@@ -168,7 +154,7 @@
|
||||
(def item-align-self-types
|
||||
#{:start :end :center :stretch})
|
||||
|
||||
(def schema:layout-attrs
|
||||
(def schema:layout-child-attrs
|
||||
[:map {:title "LayoutChildAttrs"}
|
||||
[:layout-item-margin-type {:optional true} [::sm/one-of item-margin-types]]
|
||||
[:layout-item-margin {:optional true}
|
||||
@@ -187,6 +173,9 @@
|
||||
[:layout-item-absolute {:optional true} :boolean]
|
||||
[:layout-item-z-index {:optional true} ::sm/safe-number]])
|
||||
|
||||
(def layout-child-attrs
|
||||
(sm/keys schema:layout-child-attrs))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SCHEMAS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -194,8 +183,6 @@
|
||||
(def valid-layouts
|
||||
#{:flex :grid})
|
||||
|
||||
(sm/register! ::layout [::sm/one-of valid-layouts])
|
||||
|
||||
(defn flex-layout?
|
||||
([objects id]
|
||||
(flex-layout? (get objects id)))
|
||||
@@ -754,9 +741,7 @@
|
||||
([type parent value]
|
||||
(add-grid-track type parent value nil))
|
||||
([type parent value index]
|
||||
(dm/assert!
|
||||
"expected a valid grid definition for `value`"
|
||||
(check-grid-track! value))
|
||||
(assert (check-grid-track value))
|
||||
|
||||
(let [[tracks-prop tracks-prop-other prop prop-other prop-span prop-span-other]
|
||||
(if (= type :column)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
[:blur ::sm/safe-number]
|
||||
[:spread ::sm/safe-number]
|
||||
[:hidden :boolean]
|
||||
[:color ::ctc/color]])
|
||||
[:color ctc/schema:color]])
|
||||
|
||||
(def check-shadow
|
||||
(sm/check-fn schema:shadow))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns app.common.types.shape.text
|
||||
(:require
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.fill :refer [schema:fill]]
|
||||
[app.common.types.shape :as-alias shape]
|
||||
[app.common.types.shape.text.position-data :as-alias position-data]))
|
||||
|
||||
@@ -34,7 +35,7 @@
|
||||
[:key {:optional true} :string]
|
||||
[:fills {:optional true}
|
||||
[:maybe
|
||||
[:vector {:gen/max 2} ::shape/fill]]]
|
||||
[:vector {:gen/max 2} schema:fill]]]
|
||||
[:font-family {:optional true} :string]
|
||||
[:font-size {:optional true} :string]
|
||||
[:font-style {:optional true} :string]
|
||||
@@ -51,7 +52,7 @@
|
||||
[:key {:optional true} :string]
|
||||
[:fills {:optional true}
|
||||
[:maybe
|
||||
[:vector {:gen/max 2} ::shape/fill]]]
|
||||
[:vector {:gen/max 2} schema:fill]]]
|
||||
[:font-family {:optional true} :string]
|
||||
[:font-size {:optional true} :string]
|
||||
[:font-style {:optional true} :string]
|
||||
@@ -75,7 +76,7 @@
|
||||
[:y ::sm/safe-number]
|
||||
[:width ::sm/safe-number]
|
||||
[:height ::sm/safe-number]
|
||||
[:fills [:vector {:gen/max 2} ::shape/fill]]
|
||||
[:fills [:vector {:gen/max 2} schema:fill]]
|
||||
[:font-family {:optional true} :string]
|
||||
[:font-size {:optional true} :string]
|
||||
[:font-style {:optional true} :string]
|
||||
|
||||
168
common/src/app/common/types/text.cljc
Normal file
168
common/src/app/common/types/text.cljc
Normal file
@@ -0,0 +1,168 @@
|
||||
;; 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.types.text
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(defn- compare-text-content
|
||||
"Given two content text structures, conformed by maps and vectors,
|
||||
compare them, and returns a set with the differences info.
|
||||
If the structures are equal, it returns an empty set. If the structure
|
||||
has changed, it returns :text-content-structure. There are two
|
||||
callbacks to specify what to return when there is a text change with
|
||||
the same structure, and when attributes change."
|
||||
[a b {:keys [text-cb attribute-cb] :as callbacks}]
|
||||
(cond
|
||||
;; If a and b are equal, there is no diff
|
||||
(= a b)
|
||||
#{}
|
||||
|
||||
;; If types are different, the structure is different
|
||||
(not= (type a) (type b))
|
||||
#{:text-content-structure}
|
||||
|
||||
;; If they are maps, check the keys
|
||||
(map? a)
|
||||
(let [keys (-> (set/union (set (keys a)) (set (keys b)))
|
||||
(disj :key))] ;; We have to ignore :key because it is a draft artifact
|
||||
(reduce
|
||||
(fn [acc k]
|
||||
(let [v1 (get a k)
|
||||
v2 (get b k)]
|
||||
(cond
|
||||
;; If the key is :children, keep digging
|
||||
(= k :children)
|
||||
(if (not= (count v1) (count v2))
|
||||
#{:text-content-structure}
|
||||
(into acc
|
||||
(apply set/union
|
||||
(map #(compare-text-content %1 %2 callbacks) v1 v2))))
|
||||
|
||||
;; If the key is :text, and they are different, it is a text differece
|
||||
(= k :text)
|
||||
(if (not= v1 v2)
|
||||
(text-cb acc)
|
||||
acc)
|
||||
|
||||
:else
|
||||
;; If the key is not :text, and they are different, it is an attribute differece
|
||||
(if (not= v1 v2)
|
||||
(attribute-cb acc k)
|
||||
acc))))
|
||||
#{}
|
||||
keys))
|
||||
|
||||
:else
|
||||
#{:text-content-structure}))
|
||||
|
||||
(defn equal-attrs?
|
||||
"Given a text structure, and a map of attrs, check that all the internal attrs in
|
||||
paragraphs and sentences have the same attrs"
|
||||
[item attrs]
|
||||
(let [item-attrs (dissoc item :text :type :key :children)]
|
||||
(and
|
||||
(or (empty? item-attrs)
|
||||
(= attrs (dissoc item :text :type :key :children)))
|
||||
(every? #(equal-attrs? % attrs) (:children item)))))
|
||||
|
||||
(defn get-first-paragraph-text-attrs
|
||||
"Given a content text structure, extract it's first paragraph
|
||||
text attrs"
|
||||
[content]
|
||||
(-> content
|
||||
(dm/get-in [:children 0 :children 0])
|
||||
(dissoc :text :type :key :children)))
|
||||
|
||||
(defn get-diff-type
|
||||
"Given two content text structures, conformed by maps and vectors,
|
||||
compare them, and returns a set with the type of differences.
|
||||
The possibilities are
|
||||
:text-content-text
|
||||
:text-content-attribute,
|
||||
:text-content-structure
|
||||
:text-content-structure-same-attrs."
|
||||
[a b]
|
||||
(let [diff-type (compare-text-content a b
|
||||
{:text-cb (fn [acc] (conj acc :text-content-text))
|
||||
:attribute-cb (fn [acc _] (conj acc :text-content-attribute))})]
|
||||
(if-not (contains? diff-type :text-content-structure)
|
||||
diff-type
|
||||
(let [;; get attrs of the first paragraph of the first paragraph-set
|
||||
attrs (get-first-paragraph-text-attrs a)]
|
||||
(if (and (equal-attrs? a attrs)
|
||||
(equal-attrs? b attrs))
|
||||
#{:text-content-structure :text-content-structure-same-attrs}
|
||||
diff-type)))))
|
||||
|
||||
(defn get-diff-attrs
|
||||
"Given two content text structures, conformed by maps and vectors,
|
||||
compare them, and returns a set with the attributes that have changed.
|
||||
This is independent of the text structure, so if the structure changes
|
||||
but the attributes are the same, it will return an empty set."
|
||||
[a b]
|
||||
(let [diff-attrs (compare-text-content a b
|
||||
{:text-cb identity
|
||||
:attribute-cb (fn [acc attr] (conj acc attr))})]
|
||||
(if-not (contains? diff-attrs :text-content-structure)
|
||||
diff-attrs
|
||||
(let [;; get attrs of the first paragraph of the first paragraph-set
|
||||
attrs (get-first-paragraph-text-attrs a)]
|
||||
(if (and (equal-attrs? a attrs)
|
||||
(equal-attrs? b attrs))
|
||||
#{}
|
||||
(disj diff-attrs :text-content-structure))))))
|
||||
|
||||
;; TODO We know that there are cases that the blocks of texts are separated
|
||||
;; differently: ["one" " " "two"], ["one " "two"], ["one" " two"]
|
||||
;; so this won't work for 100% of the situations. But it's good enough for now,
|
||||
;; we can iterate on the solution again in the future if needed.
|
||||
(defn equal-structure?
|
||||
"Given two content text structures, check that the structures are equal.
|
||||
This means that all the :children keys at any level has the same number of
|
||||
entries"
|
||||
[a b]
|
||||
(cond
|
||||
(not= (type a) (type b))
|
||||
false
|
||||
|
||||
(map? a)
|
||||
(let [children-a (:children a)
|
||||
children-b (:children b)]
|
||||
(if (not= (count children-a) (count children-b))
|
||||
false
|
||||
(every? true?
|
||||
(map equal-structure? children-a children-b))))
|
||||
|
||||
:else
|
||||
true))
|
||||
|
||||
(defn copy-text-keys
|
||||
"Given two equal content text structures, deep copy all the keys :text
|
||||
from origin to destiny"
|
||||
[origin destiny]
|
||||
(cond
|
||||
(map? origin)
|
||||
(into {}
|
||||
(for [k (keys origin) :when (not= k :key)] ;; We ignore :key because it is a draft artifact
|
||||
(cond
|
||||
(= :children k)
|
||||
[k (vec (map #(copy-text-keys %1 %2) (get origin k) (get destiny k)))]
|
||||
(= :text k)
|
||||
[k (:text origin)]
|
||||
:else
|
||||
[k (get destiny k)])))))
|
||||
|
||||
(defn copy-attrs-keys
|
||||
"Given a content text structure and a list of attrs, copy that
|
||||
attrs values on all the content tree"
|
||||
[content attrs]
|
||||
(into {}
|
||||
(for [[k v] content]
|
||||
(if (= :children k)
|
||||
[k (vec (map #(copy-attrs-keys %1 attrs) v))]
|
||||
[k (get attrs k v)]))))
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.schema.registry :as sr]
|
||||
[clojure.data :as data]
|
||||
[clojure.set :as set]
|
||||
[malli.util :as mu]))
|
||||
@@ -17,16 +16,10 @@
|
||||
;; HELPERS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn merge-schemas
|
||||
"Merge registered schemas."
|
||||
[& schema-keys]
|
||||
(let [schemas (map #(get @sr/registry %) schema-keys)]
|
||||
(reduce sm/merge schemas)))
|
||||
|
||||
(defn schema-keys
|
||||
(defn- schema-keys
|
||||
"Converts registed map schema into set of keys."
|
||||
[registered-schema]
|
||||
(->> (get @sr/registry registered-schema)
|
||||
[schema]
|
||||
(->> schema
|
||||
(sm/schema)
|
||||
(mu/keys)
|
||||
(into #{})))
|
||||
@@ -40,7 +33,7 @@
|
||||
:border-radius "borderRadius"
|
||||
:color "color"
|
||||
:dimensions "dimension"
|
||||
:numeric "numeric"
|
||||
:number "number"
|
||||
:opacity "opacity"
|
||||
:other "other"
|
||||
:rotation "rotation"
|
||||
@@ -55,95 +48,86 @@
|
||||
(def token-types
|
||||
(into #{} (keys token-type->dtcg-token-type)))
|
||||
|
||||
(defn valid-token-type?
|
||||
[t]
|
||||
(token-types t))
|
||||
|
||||
(def token-name-ref
|
||||
[:and :string [:re #"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"]])
|
||||
|
||||
(defn valid-token-name-ref?
|
||||
[n]
|
||||
(string? n))
|
||||
(def ^:private schema:color
|
||||
[:map
|
||||
[:fill {:optional true} token-name-ref]
|
||||
[:stroke-color {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::color}
|
||||
[:map
|
||||
[:fill {:optional true} token-name-ref]
|
||||
[:stroke-color {:optional true} token-name-ref]])
|
||||
(def color-keys (schema-keys schema:color))
|
||||
|
||||
(def color-keys (schema-keys ::color))
|
||||
(def ^:private schema:border-radius
|
||||
[:map
|
||||
[:r1 {:optional true} token-name-ref]
|
||||
[:r2 {:optional true} token-name-ref]
|
||||
[:r3 {:optional true} token-name-ref]
|
||||
[:r4 {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::border-radius}
|
||||
[:map
|
||||
[:r1 {:optional true} token-name-ref]
|
||||
[:r2 {:optional true} token-name-ref]
|
||||
[:r3 {:optional true} token-name-ref]
|
||||
[:r4 {:optional true} token-name-ref]])
|
||||
(def border-radius-keys (schema-keys schema:border-radius))
|
||||
|
||||
(def border-radius-keys (schema-keys ::border-radius))
|
||||
(def ^:private schema:stroke-width
|
||||
[:map
|
||||
[:stroke-width {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::stroke-width}
|
||||
[:map
|
||||
[:stroke-width {:optional true} token-name-ref]])
|
||||
(def stroke-width-keys (schema-keys schema:stroke-width))
|
||||
|
||||
(def stroke-width-keys (schema-keys ::stroke-width))
|
||||
(def ^:private schema:sizing
|
||||
[:map
|
||||
[:width {:optional true} token-name-ref]
|
||||
[:height {:optional true} token-name-ref]
|
||||
[:layout-item-min-w {:optional true} token-name-ref]
|
||||
[:layout-item-max-w {:optional true} token-name-ref]
|
||||
[:layout-item-min-h {:optional true} token-name-ref]
|
||||
[:layout-item-max-h {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::sizing}
|
||||
[:map
|
||||
[:width {:optional true} token-name-ref]
|
||||
[:height {:optional true} token-name-ref]
|
||||
[:layout-item-min-w {:optional true} token-name-ref]
|
||||
[:layout-item-max-w {:optional true} token-name-ref]
|
||||
[:layout-item-min-h {:optional true} token-name-ref]
|
||||
[:layout-item-max-h {:optional true} token-name-ref]])
|
||||
(def sizing-keys (schema-keys schema:sizing))
|
||||
|
||||
(def sizing-keys (schema-keys ::sizing))
|
||||
(def ^:private schema:opacity
|
||||
[:map
|
||||
[:opacity {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::opacity}
|
||||
[:map
|
||||
[:opacity {:optional true} token-name-ref]])
|
||||
(def opacity-keys (schema-keys schema:opacity))
|
||||
|
||||
(def opacity-keys (schema-keys ::opacity))
|
||||
(def ^:private schema:spacing
|
||||
[:map
|
||||
[:row-gap {:optional true} token-name-ref]
|
||||
[:column-gap {:optional true} token-name-ref]
|
||||
[:p1 {:optional true} token-name-ref]
|
||||
[:p2 {:optional true} token-name-ref]
|
||||
[:p3 {:optional true} token-name-ref]
|
||||
[:p4 {:optional true} token-name-ref]
|
||||
[:m1 {:optional true} token-name-ref]
|
||||
[:m2 {:optional true} token-name-ref]
|
||||
[:m3 {:optional true} token-name-ref]
|
||||
[:m4 {:optional true} token-name-ref]
|
||||
[:x {:optional true} token-name-ref]
|
||||
[:y {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::spacing}
|
||||
[:map
|
||||
[:row-gap {:optional true} token-name-ref]
|
||||
[:column-gap {:optional true} token-name-ref]
|
||||
[:p1 {:optional true} token-name-ref]
|
||||
[:p2 {:optional true} token-name-ref]
|
||||
[:p3 {:optional true} token-name-ref]
|
||||
[:p4 {:optional true} token-name-ref]
|
||||
[:m1 {:optional true} token-name-ref]
|
||||
[:m2 {:optional true} token-name-ref]
|
||||
[:m3 {:optional true} token-name-ref]
|
||||
[:m4 {:optional true} token-name-ref]
|
||||
[:x {:optional true} token-name-ref]
|
||||
[:y {:optional true} token-name-ref]])
|
||||
(def spacing-keys (schema-keys schema:spacing))
|
||||
|
||||
(def spacing-keys (schema-keys ::spacing))
|
||||
(def ^:private schema:dimensions
|
||||
[:merge
|
||||
schema:sizing
|
||||
schema:spacing
|
||||
schema:stroke-width
|
||||
schema:border-radius])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::dimensions}
|
||||
[:merge
|
||||
::sizing
|
||||
::spacing
|
||||
::stroke-width
|
||||
::border-radius])
|
||||
(def dimensions-keys (schema-keys schema:dimensions))
|
||||
|
||||
(def dimensions-keys (schema-keys ::dimensions))
|
||||
(def ^:private schema:rotation
|
||||
[:map
|
||||
[:rotation {:optional true} token-name-ref]])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::rotation}
|
||||
[:map
|
||||
[:rotation {:optional true} token-name-ref]])
|
||||
(def rotation-keys (schema-keys schema:rotation))
|
||||
|
||||
(def rotation-keys (schema-keys ::rotation))
|
||||
(def ^:private schema:number
|
||||
[:map
|
||||
[:rotation {:optional true} token-name-ref]
|
||||
[:line-height {:optional true} token-name-ref]])
|
||||
|
||||
(def number-keys (schema-keys schema:number))
|
||||
|
||||
(def all-keys (set/union color-keys
|
||||
border-radius-keys
|
||||
@@ -152,21 +136,21 @@
|
||||
opacity-keys
|
||||
spacing-keys
|
||||
dimensions-keys
|
||||
rotation-keys))
|
||||
rotation-keys
|
||||
number-keys))
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::tokens}
|
||||
[:map {:title "Applied Tokens"}])
|
||||
(def ^:private schema:tokens
|
||||
[:map {:title "Applied Tokens"}])
|
||||
|
||||
(sm/register!
|
||||
^{::sm/type ::applied-tokens}
|
||||
[:merge
|
||||
::tokens
|
||||
::border-radius
|
||||
::sizing
|
||||
::spacing
|
||||
::rotation
|
||||
::dimensions])
|
||||
(def schema:applied-tokens
|
||||
[:merge
|
||||
schema:tokens
|
||||
schema:border-radius
|
||||
schema:sizing
|
||||
schema:spacing
|
||||
schema:rotation
|
||||
schema:number
|
||||
schema:dimensions])
|
||||
|
||||
(defn shape-attr->token-attrs
|
||||
([shape-attr] (shape-attr->token-attrs shape-attr nil))
|
||||
@@ -197,7 +181,8 @@
|
||||
(sizing-keys shape-attr) #{shape-attr}
|
||||
(opacity-keys shape-attr) #{shape-attr}
|
||||
(spacing-keys shape-attr) #{shape-attr}
|
||||
(rotation-keys shape-attr) #{shape-attr})))
|
||||
(rotation-keys shape-attr) #{shape-attr}
|
||||
(number-keys shape-attr) #{shape-attr})))
|
||||
|
||||
(defn token-attr->shape-attr
|
||||
[token-attr]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,7 @@
|
||||
|
||||
(def property-prefix "Property")
|
||||
(def property-regex (re-pattern (str property-prefix "(\\d+)")))
|
||||
(def property-max-length 60)
|
||||
(def value-prefix "Value ")
|
||||
|
||||
|
||||
@@ -134,7 +135,9 @@
|
||||
(->> (str/split s ",")
|
||||
(mapv #(str/split % "=" 2))
|
||||
(every? #(and (= 2 (count %))
|
||||
(not (str/blank? (first %)))))))
|
||||
(not (str/blank? (first %)))
|
||||
(< (count (first %)) property-max-length)
|
||||
(< (count (second %)) property-max-length)))))
|
||||
|
||||
|
||||
(defn find-properties-to-remove
|
||||
|
||||
50
common/test/common_tests/buffer_test.cljc
Normal file
50
common/test/common_tests/buffer_test.cljc
Normal file
@@ -0,0 +1,50 @@
|
||||
;; 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 common-tests.buffer-test
|
||||
(:require
|
||||
[app.common.buffer :as buf]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/deftest allocate
|
||||
(let [b (buf/allocate 1)]
|
||||
(t/is (buf/buffer? b))))
|
||||
|
||||
(t/deftest rw-byte
|
||||
(let [b (buf/allocate 1)]
|
||||
(buf/write-byte b 0 123)
|
||||
(let [res (buf/read-byte b 0)]
|
||||
(t/is (= 123 res)))
|
||||
|
||||
(buf/write-byte b 0 252)
|
||||
(let [res (buf/read-byte b 0)]
|
||||
(t/is (= -4 res)))))
|
||||
|
||||
(t/deftest rw-int
|
||||
(let [b (buf/allocate 4)]
|
||||
(buf/write-int b 0 123)
|
||||
(let [res (buf/read-int b 0)]
|
||||
(t/is (= 123 res)))))
|
||||
|
||||
(t/deftest rw-float
|
||||
(let [b (buf/allocate 4)]
|
||||
(buf/write-float b 0 123)
|
||||
(let [res (buf/read-float b 0)]
|
||||
(t/is (= 123.0 res)))))
|
||||
|
||||
(t/deftest rw-short
|
||||
(let [b (buf/allocate 2)]
|
||||
(buf/write-short b 0 123)
|
||||
(let [res (buf/read-short b 0)]
|
||||
(t/is (= 123 res)))))
|
||||
|
||||
(t/deftest rw-uuid
|
||||
(let [b (buf/allocate 16)
|
||||
id (uuid/next)]
|
||||
(buf/write-uuid b 0 id)
|
||||
(let [res (buf/read-uuid b 0)]
|
||||
(t/is (= id res)))))
|
||||
@@ -21,6 +21,6 @@
|
||||
(let [file {:data {:sum 1}
|
||||
:id 1
|
||||
:migrations (d/ordered-set "test/1")}
|
||||
file' (cfm/migrate file)]
|
||||
file' (cfm/migrate file nil)]
|
||||
(t/is (= cfm/available-migrations (:migrations file')))
|
||||
(t/is (= 3 (:sum (:data file'))))))))
|
||||
|
||||
@@ -114,11 +114,8 @@
|
||||
(let [modifiers (ctm/resize-modifiers (gpt/point 0 0) (gpt/point 0 0))
|
||||
shape-before (create-test-shape :rect {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
|
||||
(t/is (close? (get-in shape-before [:selrect :width])
|
||||
(get-in shape-after [:selrect :width])))
|
||||
(t/is (close? (get-in shape-before [:selrect :height])
|
||||
(get-in shape-after [:selrect :height])))))
|
||||
(t/is (close? 0.01 (get-in shape-after [:selrect :width])))
|
||||
(t/is (close? 0.01 (get-in shape-after [:selrect :height])))))
|
||||
|
||||
(t/testing "Transform shape with rotation modifiers"
|
||||
(t/are [type]
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
(ns common-tests.logic.token-apply-test
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.test-helpers.compositions :as tho]
|
||||
@@ -13,6 +14,7 @@
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.test-helpers.shapes :as ths]
|
||||
[app.common.test-helpers.tokens :as tht]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
@@ -53,7 +55,8 @@
|
||||
(ctob/make-token :name "token-dimensions"
|
||||
:type :dimensions
|
||||
:value 100))))
|
||||
(tho/add-frame :frame1)))
|
||||
(tho/add-frame :frame1)
|
||||
(tho/add-text :text1 "Hello World")))
|
||||
|
||||
(defn- apply-all-tokens
|
||||
[file]
|
||||
@@ -64,7 +67,8 @@
|
||||
(tht/apply-token-to-shape :frame1 "token-stroke-width" [:stroke-width] [:stroke-width] 2)
|
||||
(tht/apply-token-to-shape :frame1 "token-color" [:stroke-color] [:stroke-color] "#00ff00")
|
||||
(tht/apply-token-to-shape :frame1 "token-color" [:fill] [:fill] "#00ff00")
|
||||
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)))
|
||||
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)
|
||||
(tht/apply-token-to-shape :text1 "token-color" [:fill] [:fill] "#00ff00")))
|
||||
|
||||
(t/deftest apply-tokens-to-shape
|
||||
(let [;; ==== Setup
|
||||
@@ -171,6 +175,7 @@
|
||||
(apply-all-tokens))
|
||||
page (thf/current-page file)
|
||||
frame1 (ths/get-shape file :frame1)
|
||||
text1 (ths/get-shape file :text1)
|
||||
|
||||
;; ==== Action
|
||||
changes (-> (-> (pcb/empty-changes nil)
|
||||
@@ -190,13 +195,25 @@
|
||||
(ctn/set-shape-attr :width 0)
|
||||
(ctn/set-shape-attr :height 0)))
|
||||
(:objects page)
|
||||
{})
|
||||
(cls/generate-update-shapes [(:id text1)]
|
||||
(fn [shape]
|
||||
(txt/update-text-content
|
||||
shape
|
||||
txt/is-content-node?
|
||||
d/txt-merge
|
||||
{:fills (ths/sample-fills-color :fill-color "#fabada")}))
|
||||
(:objects page)
|
||||
{}))
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
frame1' (ths/get-shape file' :frame1)
|
||||
applied-tokens' (:applied-tokens frame1')]
|
||||
frame1' (ths/get-shape file' :frame1)
|
||||
text1' (ths/get-shape file' :text1)
|
||||
applied-tokens-frame' (:applied-tokens frame1')
|
||||
applied-tokens-text' (:applied-tokens text1')]
|
||||
|
||||
;; ==== Check
|
||||
(t/is (= (count applied-tokens') 0))))
|
||||
(t/is (= (count applied-tokens-frame') 0))
|
||||
(t/is (= (count applied-tokens-text') 0))))
|
||||
@@ -36,7 +36,7 @@
|
||||
redo-lib (tht/get-tokens-lib redo)
|
||||
undo (thf/apply-undo-changes redo changes)
|
||||
undo-lib (tht/get-tokens-lib undo)]
|
||||
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib))))
|
||||
|
||||
;; Undo
|
||||
@@ -56,7 +56,7 @@
|
||||
redo-lib (tht/get-tokens-lib redo)
|
||||
undo (thf/apply-undo-changes redo changes)
|
||||
undo-lib (tht/get-tokens-lib undo)]
|
||||
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib))))
|
||||
|
||||
;; Undo
|
||||
@@ -65,8 +65,8 @@
|
||||
(t/testing "toggling an set with hidden theme already active will toggle set in hidden theme"
|
||||
(let [file (setup-file #(-> %
|
||||
(ctob/add-set (ctob/make-token-set :name "foo/bar"))
|
||||
(ctob/add-theme (ctob/make-hidden-token-theme))
|
||||
(ctob/set-active-themes #{ctob/hidden-token-theme-path})))
|
||||
(ctob/add-theme (ctob/make-hidden-theme))
|
||||
(ctob/set-active-themes #{ctob/hidden-theme-path})))
|
||||
|
||||
changes (-> (pcb/empty-changes)
|
||||
(pcb/with-library-data (:data file))
|
||||
@@ -265,7 +265,7 @@
|
||||
:type :color})
|
||||
file (setup-file #(-> (ctob/add-set % (ctob/make-token-set :name set-name))
|
||||
(ctob/add-token-in-set set-name token)))
|
||||
prev-token-set (-> file tht/get-tokens-lib :sets first)
|
||||
prev-token-set (-> file tht/get-tokens-lib (ctob/get-set set-name))
|
||||
new-set-name "foo1"
|
||||
changes (-> (pcb/empty-changes)
|
||||
(pcb/with-library-data (:data file))
|
||||
@@ -299,7 +299,7 @@
|
||||
redo-lib (tht/get-tokens-lib redo)
|
||||
undo (thf/apply-undo-changes redo changes)
|
||||
undo-lib (tht/get-tokens-lib undo)]
|
||||
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{"foo/bar/baz" "foo/bar/baz/baz-child"} (:sets (ctob/get-hidden-theme redo-lib))))
|
||||
|
||||
;; Undo
|
||||
@@ -323,7 +323,7 @@
|
||||
undo (thf/apply-undo-changes redo changes)
|
||||
undo-lib (tht/get-tokens-lib undo)]
|
||||
(t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib))))
|
||||
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
|
||||
|
||||
;; Undo
|
||||
(t/is (= #{"/theme"} (ctob/get-active-theme-paths undo-lib))))))
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
(ns common-tests.runner
|
||||
(:require
|
||||
[clojure.test :as t]
|
||||
[common-tests.buffer-test]
|
||||
[common-tests.colors-test]
|
||||
[common-tests.data-test]
|
||||
[common-tests.files-changes-test]
|
||||
@@ -38,6 +39,7 @@
|
||||
[common-tests.time-test]
|
||||
[common-tests.types.absorb-assets-test]
|
||||
[common-tests.types.components-test]
|
||||
[common-tests.types.fill-test]
|
||||
[common-tests.types.modifiers-test]
|
||||
[common-tests.types.path-data-test]
|
||||
[common-tests.types.shape-decode-encode-test]
|
||||
@@ -56,6 +58,7 @@
|
||||
(defn -main
|
||||
[& args]
|
||||
(t/run-tests
|
||||
'common-tests.buffer-test
|
||||
'common-tests.colors-test
|
||||
'common-tests.data-test
|
||||
'common-tests.files-changes-test
|
||||
@@ -89,6 +92,7 @@
|
||||
'common-tests.types.components-test
|
||||
'common-tests.types.modifiers-test
|
||||
'common-tests.types.path-data-test
|
||||
'common-tests.types.fill-test
|
||||
'common-tests.types.shape-decode-encode-test
|
||||
'common-tests.types.shape-interactions-test
|
||||
'common-tests.types.tokens-lib-test
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.test-helpers.shapes :as ths]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.colors-list :as ctcl]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.file :as ctf]
|
||||
@@ -80,7 +80,7 @@
|
||||
_ (thf/validate-file! file')
|
||||
|
||||
;; Get
|
||||
colors' (ctcl/colors-seq (ctf/file-data file'))
|
||||
colors' (ctc/colors-seq (ctf/file-data file'))
|
||||
shape1' (ths/get-shape file' :shape1)
|
||||
fill' (first (:fills shape1'))]
|
||||
|
||||
|
||||
214
common/test/common_tests/types/fill_test.cljc
Normal file
214
common/test/common_tests/types/fill_test.cljc
Normal file
@@ -0,0 +1,214 @@
|
||||
;; 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 common-tests.types.fill-test
|
||||
(:require
|
||||
#?(:clj [app.common.fressian :as fres])
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.pprint :as pp]
|
||||
[app.common.schema.generators :as sg]
|
||||
[app.common.schema.test :as smt]
|
||||
[app.common.transit :as trans]
|
||||
[app.common.types.fill :as types.fill]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(defn equivalent-fill?
|
||||
[fill-a fill-b]
|
||||
;; (prn "-------------------")
|
||||
;; (app.common.pprint/pprint fill-a)
|
||||
;; (app.common.pprint/pprint fill-b)
|
||||
|
||||
(and (= (get fill-a :fill-color-ref-file)
|
||||
(get fill-b :fill-color-ref-file))
|
||||
|
||||
(= (get fill-a :fill-color-ref-id)
|
||||
(get fill-b :fill-color-ref-id))
|
||||
|
||||
(or (and (contains? fill-a :fill-color)
|
||||
(= (:fill-color fill-a)
|
||||
(:fill-color fill-b))
|
||||
(mth/close? (:fill-opacity fill-a 1.0)
|
||||
(:fill-opacity fill-b 1.0)))
|
||||
(and (contains? fill-a :fill-image)
|
||||
(mth/close? (:fill-opacity fill-a 1.0)
|
||||
(:fill-opacity fill-b 1.0))
|
||||
(let [image-a (:fill-image fill-a)
|
||||
image-b (:fill-image fill-b)]
|
||||
(and (= (:id image-a)
|
||||
(:id image-b))
|
||||
(= (:mtype image-a)
|
||||
(:mtype image-b))
|
||||
(mth/close? (:width image-a)
|
||||
(:width image-b))
|
||||
(mth/close? (:height image-a)
|
||||
(:height image-b)))))
|
||||
(and (contains? fill-a :fill-color-gradient)
|
||||
(mth/close? (:fill-opacity fill-a 1)
|
||||
(:fill-opacity fill-b 1))
|
||||
(let [gradient-a (:fill-color-gradient fill-a)
|
||||
gradient-b (:fill-color-gradient fill-b)]
|
||||
(and (= (count (:stops gradient-a))
|
||||
(count (:stops gradient-b)))
|
||||
(= (get gradient-a :type)
|
||||
(get gradient-b :type))
|
||||
(mth/close? (get gradient-a :start-x)
|
||||
(get gradient-b :start-x))
|
||||
(mth/close? (get gradient-a :start-y)
|
||||
(get gradient-b :start-y))
|
||||
(mth/close? (get gradient-a :end-x)
|
||||
(get gradient-b :end-x))
|
||||
(mth/close? (get gradient-a :end-y)
|
||||
(get gradient-b :end-y))
|
||||
(mth/close? (get gradient-a :width)
|
||||
(get gradient-b :width))
|
||||
(every? true?
|
||||
(map (fn [stop-a stop-b]
|
||||
(and (= (get stop-a :color)
|
||||
(get stop-b :color))
|
||||
(mth/close? (get stop-a :opacity 1)
|
||||
(get stop-b :opacity 1))
|
||||
(mth/close? (get stop-a :offset)
|
||||
(get stop-b :offset))))
|
||||
(get gradient-a :stops)
|
||||
(get gradient-b :stops)))))))))
|
||||
|
||||
|
||||
(def sample-fill-1
|
||||
{:fill-color "#fabada"
|
||||
:fill-opacity 0.7})
|
||||
|
||||
(t/deftest build-from-plain-1
|
||||
(let [fills (types.fill/from-plain [sample-fill-1])]
|
||||
(t/is (types.fill/fills? fills))
|
||||
(t/is (= 1 (count fills)))
|
||||
(t/is (equivalent-fill? (first fills) sample-fill-1))))
|
||||
|
||||
(def sample-fill-2
|
||||
{:fill-color-ref-file #uuid "4fcb3db7-d281-8004-8006-3a97e2e142ad"
|
||||
:fill-color-ref-id #uuid "fb19956a-c9e0-8056-8006-3a9c78f531c6"
|
||||
:fill-image {:width 200, :height 100, :mtype "image/gif",
|
||||
:id #uuid "b30f028d-cc2f-8035-8006-3a93bd0e137b",
|
||||
:name "ovba",
|
||||
:keep-aspect-ratio false}})
|
||||
|
||||
(t/deftest build-from-plain-2
|
||||
(let [fills (types.fill/from-plain [sample-fill-2])]
|
||||
(t/is (types.fill/fills? fills))
|
||||
(t/is (= 1 (count fills)))
|
||||
(t/is (equivalent-fill? (first fills) sample-fill-2))))
|
||||
|
||||
(def sample-fill-3
|
||||
{:fill-color-ref-id #uuid "fb19956a-c9e0-8056-8006-3a9c78f531c6"
|
||||
:fill-color-ref-file #uuid "fb19956a-c9e0-8056-8006-3a9c78f531c5"
|
||||
:fill-color-gradient
|
||||
{:type :linear,
|
||||
:start-x 0.75,
|
||||
:start-y 3.0,
|
||||
:end-x 1.0,
|
||||
:end-y 1.5,
|
||||
:width 200,
|
||||
:stops [{:color "#631aa8", :offset 0.5}]}})
|
||||
|
||||
(t/deftest build-from-plain-3
|
||||
(let [fills (types.fill/from-plain [sample-fill-3])]
|
||||
(t/is (types.fill/fills? fills))
|
||||
(t/is (= 1 (count fills)))
|
||||
(t/is (equivalent-fill? (first fills) sample-fill-3))))
|
||||
|
||||
(def sample-fill-4
|
||||
{:fill-color-ref-file #uuid "2eef07f1-e38a-8062-8006-3aa264d5b784",
|
||||
:fill-color-gradient
|
||||
{:type :radial,
|
||||
:start-x 0.5,
|
||||
:start-y -1.0,
|
||||
:end-x -0.5,
|
||||
:end-y 2,
|
||||
:width 0.5,
|
||||
:stops [{:color "#781025", :offset 0.0} {:color "#035c3f", :offset 0.2}]},
|
||||
:fill-opacity 1.0,
|
||||
:fill-color-ref-id #uuid "2eef07f1-e38a-8062-8006-3aa264d5b785"})
|
||||
|
||||
(t/deftest build-from-plain-4
|
||||
(let [fills (types.fill/from-plain [sample-fill-4])]
|
||||
(t/is (types.fill/fills? fills))
|
||||
(t/is (= 1 (count fills)))
|
||||
(t/is (equivalent-fill? (first fills) sample-fill-4))))
|
||||
|
||||
(def sample-fill-5
|
||||
{:fill-color-ref-file #uuid "b0f76f9a-f548-806e-8006-3aa4456131d1",
|
||||
:fill-color-ref-id #uuid "b0f76f9a-f548-806e-8006-3aa445618851",
|
||||
:fill-color-gradient
|
||||
{:type :radial,
|
||||
:start-x -0.86,
|
||||
:start-y 6.0,
|
||||
:end-x 0.25,
|
||||
:end-y -0.5,
|
||||
:width 3.8,
|
||||
:stops [{:color "#bba1aa", :opacity 0.37, :offset 0.84}]}})
|
||||
|
||||
(t/deftest build-from-plain-5
|
||||
(let [fills (types.fill/from-plain [sample-fill-5])]
|
||||
(t/is (types.fill/fills? fills))
|
||||
(t/is (= 1 (count fills)))
|
||||
(t/is (equivalent-fill? (first fills) sample-fill-5))))
|
||||
|
||||
(def sample-fill-6
|
||||
{:fill-color-gradient
|
||||
{:type :linear,
|
||||
:start-x 3.5,
|
||||
:start-y 0.39,
|
||||
:end-x -1.87,
|
||||
:end-y 1.95,
|
||||
:width 2.62,
|
||||
:stops [{:color "#e15610", :offset 0.4} {:color "#005a9e", :opacity 0.62, :offset 0.81}]}})
|
||||
|
||||
(t/deftest build-from-plain-6
|
||||
(let [fills (types.fill/from-plain [sample-fill-6])]
|
||||
(t/is (types.fill/fills? fills))
|
||||
(t/is (= 1 (count fills)))
|
||||
(t/is (equivalent-fill? (first fills) sample-fill-6))))
|
||||
|
||||
(t/deftest fills-datatype-roundtrip
|
||||
(smt/check!
|
||||
(smt/for [fill (->> (sg/generator types.fill/schema:fill)
|
||||
(sg/fmap d/without-nils)
|
||||
(sg/fmap (fn [fill]
|
||||
(cond-> fill
|
||||
(and (not (and (contains? fill :fill-color-ref-id)
|
||||
(contains? fill :fill-color-ref-file)))
|
||||
(or (contains? fill :fill-color-ref-id)
|
||||
(contains? fill :fill-color-ref-file)))
|
||||
(-> (assoc :fill-color-ref-file (uuid/next))
|
||||
(assoc :fill-color-ref-id (uuid/next)))))))]
|
||||
(let [bfills (types.fill/from-plain [fill])]
|
||||
(and (= (count bfills) 1)
|
||||
(equivalent-fill? (first bfills) fill))))
|
||||
{:num 2000}))
|
||||
|
||||
(t/deftest equality-operation
|
||||
(let [fills1 (types.fill/from-plain [sample-fill-6])
|
||||
fills2 (types.fill/from-plain [sample-fill-6])]
|
||||
(t/is (= fills1 fills2))))
|
||||
|
||||
(t/deftest reduce-impl
|
||||
(let [fills1 (types.fill/from-plain [sample-fill-6])
|
||||
fills2 (reduce (fn [result fill]
|
||||
(conj result fill))
|
||||
[]
|
||||
fills1)
|
||||
fills3 (types.fill/from-plain fills2)]
|
||||
(t/is (= fills1 fills3))))
|
||||
|
||||
(t/deftest indexed-access
|
||||
(let [fills1 (types.fill/from-plain [sample-fill-6])
|
||||
fill0 (nth fills1 0)
|
||||
fill1 (nth fills1 1)]
|
||||
(t/is (nil? fill1))
|
||||
(t/is (equivalent-fill? fill0 sample-fill-6))))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user