Compare commits
110 Commits
2.14.0-RC1
...
azazeln28-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55539e83bd | ||
|
|
740e790585 | ||
|
|
ed23c55550 | ||
|
|
5b5c868a87 | ||
|
|
1a3ac6bdf8 | ||
|
|
de5d4f4292 | ||
|
|
2bd7c10e09 | ||
|
|
495371c079 | ||
|
|
75b1c0c1b1 | ||
|
|
5ea4b03108 | ||
|
|
0fef5b7e5d | ||
|
|
8a1fdd9dd1 | ||
|
|
a080a9e646 | ||
|
|
a728d5a5f2 | ||
|
|
6072234230 | ||
|
|
41f2877801 | ||
|
|
e2576d049a | ||
|
|
4db9c373e6 | ||
|
|
09a9407867 | ||
|
|
7be03e2ea6 | ||
|
|
05165ce014 | ||
|
|
96677713fc | ||
|
|
f41eca12f4 | ||
|
|
baa44119f4 | ||
|
|
7d3e434167 | ||
|
|
40233e3316 | ||
|
|
e2b5f936f5 | ||
|
|
614c6ed300 | ||
|
|
4975f28a3d | ||
|
|
f5109c7df2 | ||
|
|
12a1cb1d32 | ||
|
|
84ba6f0002 | ||
|
|
a12b59d101 | ||
|
|
32d4026641 | ||
|
|
4477b2b4a0 | ||
|
|
9e51fa198a | ||
|
|
d176da8012 | ||
|
|
20862c2da3 | ||
|
|
1b8afccba2 | ||
|
|
dd856ecf50 | ||
|
|
f4e79af3cd | ||
|
|
3e758826fe | ||
|
|
2cf66c948d | ||
|
|
145198c148 | ||
|
|
eddfc4c4b2 | ||
|
|
e6e34af391 | ||
|
|
4ee908fc89 | ||
|
|
bdcf448f3f | ||
|
|
c58054d19c | ||
|
|
a7ab506c5c | ||
|
|
c7f644ab2a | ||
|
|
3d41dc276e | ||
|
|
cb5cacbcee | ||
|
|
337cfc2d3e | ||
|
|
c2ee31e791 | ||
|
|
360937f613 | ||
|
|
f6d0414449 | ||
|
|
4d05827fa9 | ||
|
|
48fb9fa6ea | ||
|
|
cee974a906 | ||
|
|
c74cf3fa37 | ||
|
|
7c1ddd3d7d | ||
|
|
4965f6d859 | ||
|
|
a3cd90da7f | ||
|
|
2b130c7e52 | ||
|
|
a1a7f643ec | ||
|
|
70013fde74 | ||
|
|
916107ce04 | ||
|
|
8eb5bd3dd8 | ||
|
|
5718698bff | ||
|
|
c41b9214c5 | ||
|
|
fb80c8f45b | ||
|
|
009dc4485a | ||
|
|
b8f3bee3ac | ||
|
|
f00b222262 | ||
|
|
b28457860c | ||
|
|
23b268b414 | ||
|
|
32706a1460 | ||
|
|
cd4b9ddd47 | ||
|
|
f0e3f1a319 | ||
|
|
afb252f42e | ||
|
|
4185a7a6f3 | ||
|
|
0dda7bd9ee | ||
|
|
30106f8524 | ||
|
|
7ef16a2b69 | ||
|
|
137febcbab | ||
|
|
f98c0bbd16 | ||
|
|
71ec51919e | ||
|
|
1cb113dfeb | ||
|
|
b45aec13ab | ||
|
|
7f3212d5a4 | ||
|
|
19592fadd8 | ||
|
|
643cd6f61f | ||
|
|
0b2dfe7297 | ||
|
|
fe6fb0534c | ||
|
|
f2d09a6140 | ||
|
|
5ae2351e5a | ||
|
|
b5f4ce0a71 | ||
|
|
166dc05ff2 | ||
|
|
619e2387dc | ||
|
|
813c804d45 | ||
|
|
63f0c68977 | ||
|
|
1f2a234458 | ||
|
|
b281870c50 | ||
|
|
3909bc0fc1 | ||
|
|
b6427ecaac | ||
|
|
e82319c49e | ||
|
|
ce63bae92d | ||
|
|
1349789a7b | ||
|
|
d7203ef24c |
14
CHANGES.md
@@ -3,10 +3,7 @@
|
||||
## 2.14.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
- Deprecate `PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE` in favour of `PENPOT_HTTP_SERVER_MAX_BODY_SIZE`.
|
||||
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
@@ -34,13 +31,20 @@
|
||||
- Fix boolean operators in menu for boards [Taiga #13174](https://tree.taiga.io/project/penpot/issue/13174)
|
||||
- Fix viewer can update library [Taiga #13186](https://tree.taiga.io/project/penpot/issue/13186)
|
||||
- Fix remove fill affects different element than selected [Taiga #13128](https://tree.taiga.io/project/penpot/issue/13128)
|
||||
- Fix 45 rotated board titles rendered incorrectly [Taiga #13306](https://tree.taiga.io/project/penpot/issue/13306)
|
||||
|
||||
## 2.13.3
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Revert yetti (http server) update, because that caused a regression on multipart uploads
|
||||
|
||||
## 2.13.2
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix modifying shapes by apply negative tokens to border radius [Taiga #13317](https://tree.taiga.io/project/penpot/issue/13317)
|
||||
- Fix security issue (Path Traversal Vulnerability) on fonts related RPC method
|
||||
- Fix arbitrary file read security issue on create-font-variant rpc method (https://github.com/penpot/penpot/security/advisories/GHSA-xp3f-g8rq-9px2)
|
||||
|
||||
|
||||
## 2.13.1
|
||||
|
||||
@@ -98,7 +98,6 @@
|
||||
[:http-server-port {:optional true} ::sm/int]
|
||||
[:http-server-host {:optional true} :string]
|
||||
[:http-server-max-body-size {:optional true} ::sm/int]
|
||||
[:http-server-max-multipart-body-size {:optional true} ::sm/int]
|
||||
[:http-server-io-threads {:optional true} ::sm/int]
|
||||
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
(def default-params
|
||||
{::port 6060
|
||||
::host "0.0.0.0"
|
||||
::max-body-size 31457280 ; default 30 MiB
|
||||
::max-multipart-body-size 367001600}) ; default 350 MiB
|
||||
::max-body-size 367001600 ; default 350 MiB
|
||||
})
|
||||
|
||||
(defmethod ig/expand-key ::server
|
||||
[k v]
|
||||
@@ -56,7 +56,6 @@
|
||||
[::io-threads {:optional true} ::sm/int]
|
||||
[::max-worker-threads {:optional true} ::sm/int]
|
||||
[::max-body-size {:optional true} ::sm/int]
|
||||
[::max-multipart-body-size {:optional true} ::sm/int]
|
||||
[::router {:optional true} [:fn r/router?]]
|
||||
[::handler {:optional true} ::sm/fn]])
|
||||
|
||||
@@ -79,7 +78,7 @@
|
||||
{:http/port port
|
||||
:http/host host
|
||||
:http/max-body-size (::max-body-size cfg)
|
||||
:http/max-multipart-body-size (::max-multipart-body-size cfg)
|
||||
:http/max-multipart-body-size (::max-body-size cfg)
|
||||
:xnio/direct-buffers false
|
||||
:xnio/io-threads (::io-threads cfg)
|
||||
:xnio/max-worker-threads (::max-worker-threads cfg)
|
||||
|
||||
@@ -226,11 +226,10 @@
|
||||
::http/server
|
||||
{::http/port (cf/get :http-server-port)
|
||||
::http/host (cf/get :http-server-host)
|
||||
::http/router (ig/ref ::http/router)
|
||||
::http/io-threads (cf/get :http-server-io-threads)
|
||||
::http/max-worker-threads (cf/get :http-server-max-worker-threads)
|
||||
::http/max-body-size (cf/get :http-server-max-body-size)
|
||||
::http/max-multipart-body-size (cf/get :http-server-max-multipart-body-size)
|
||||
::http/router (ig/ref ::http/router)
|
||||
::mtx/metrics (ig/ref ::mtx/metrics)}
|
||||
|
||||
::ldap/provider
|
||||
|
||||
@@ -58,4 +58,3 @@
|
||||
(when (nil? (:data file))
|
||||
(migrate-file conn file)))
|
||||
(db/exec-one! conn ["drop table page cascade;"])))
|
||||
|
||||
|
||||
@@ -605,31 +605,31 @@
|
||||
add-undo-change-shape
|
||||
(fn [change-set id]
|
||||
(let [shape (get objects id)]
|
||||
(conj
|
||||
change-set
|
||||
{:type :add-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:parent-id (:parent-id shape)
|
||||
:frame-id (:frame-id shape)
|
||||
:index (cfh/get-position-on-parent objects id)
|
||||
:obj (cond-> shape
|
||||
(contains? shape :shapes)
|
||||
(assoc :shapes []))})))
|
||||
(cond-> change-set
|
||||
(some? shape)
|
||||
(conj {:type :add-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:parent-id (:parent-id shape)
|
||||
:frame-id (:frame-id shape)
|
||||
:index (cfh/get-position-on-parent objects id)
|
||||
:obj (cond-> shape
|
||||
(contains? shape :shapes)
|
||||
(assoc :shapes []))}))))
|
||||
|
||||
add-undo-change-parent
|
||||
(fn [change-set id]
|
||||
(let [shape (get objects id)
|
||||
prev-sibling (cfh/get-prev-sibling objects (:id shape))]
|
||||
(conj
|
||||
change-set
|
||||
{:type :mov-objects
|
||||
:page-id page-id
|
||||
:parent-id (:parent-id shape)
|
||||
:shapes [id]
|
||||
:after-shape prev-sibling
|
||||
:index 0
|
||||
:ignore-touched true})))]
|
||||
(cond-> change-set
|
||||
(some? shape)
|
||||
(conj {:type :mov-objects
|
||||
:page-id page-id
|
||||
:parent-id (:parent-id shape)
|
||||
:shapes [id]
|
||||
:after-shape prev-sibling
|
||||
:index 0
|
||||
:ignore-touched true}))))]
|
||||
|
||||
(-> changes
|
||||
(update :redo-changes #(reduce add-redo-change % ids))
|
||||
@@ -1150,3 +1150,24 @@
|
||||
[changes]
|
||||
(::page-id (meta changes)))
|
||||
|
||||
|
||||
(defn set-text-content
|
||||
[changes id content prev-content]
|
||||
(assert-page-id! changes)
|
||||
(let [page-id (::page-id (meta changes))
|
||||
|
||||
redo-change
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id id
|
||||
:operations [{:type :set :attr :content :val content}]}
|
||||
|
||||
undo-change
|
||||
{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id id
|
||||
:operations [{:type :set :attr :content :val prev-content}]}]
|
||||
|
||||
(-> changes
|
||||
(update :redo-changes conj redo-change)
|
||||
(update :undo-changes conj undo-change))))
|
||||
|
||||
@@ -115,21 +115,25 @@
|
||||
(defn get-frames
|
||||
"Retrieves all frame objects as vector"
|
||||
([objects] (get-frames objects nil))
|
||||
([objects {:keys [skip-components? skip-copies?]
|
||||
([objects {:keys [skip-components? skip-copies? ignore-index?]
|
||||
:or {skip-components? false
|
||||
skip-copies? false}}]
|
||||
(->> (or (-> objects meta ::index-frames)
|
||||
(let [lookup (d/getf objects)
|
||||
xform (comp (remove #(= uuid/zero %))
|
||||
(keep lookup)
|
||||
(filter cfh/frame-shape?))]
|
||||
(->> (keys objects)
|
||||
(sequence xform))))
|
||||
(remove #(or (and ^boolean skip-components?
|
||||
^boolean (ctk/instance-head? %))
|
||||
(and ^boolean skip-copies?
|
||||
(and ^boolean (ctk/instance-head? %)
|
||||
(not ^boolean (ctk/main-instance? %)))))))))
|
||||
skip-copies? false
|
||||
ignore-index? false}}]
|
||||
(let [frame-index
|
||||
(if (and (not ignore-index?) (-> objects meta ::index-frames))
|
||||
(-> objects meta ::index-frames)
|
||||
(let [lookup (d/getf objects)
|
||||
xform (comp (remove #(= uuid/zero %))
|
||||
(keep lookup)
|
||||
(filter cfh/frame-shape?))]
|
||||
(->> (keys objects)
|
||||
(sequence xform))))]
|
||||
(->> frame-index
|
||||
(remove #(or (and ^boolean skip-components?
|
||||
^boolean (ctk/instance-head? %))
|
||||
(and ^boolean skip-copies?
|
||||
(and ^boolean (ctk/instance-head? %)
|
||||
(not ^boolean (ctk/main-instance? %))))))))))
|
||||
|
||||
(defn get-frames-ids
|
||||
"Retrieves all frame ids as vector"
|
||||
|
||||
@@ -50,6 +50,7 @@ services:
|
||||
- 4400:4400
|
||||
- 4401:4401
|
||||
- 4402:4402
|
||||
- 4403:4403
|
||||
|
||||
# Plugins
|
||||
- 4200:4200
|
||||
|
||||
@@ -68,7 +68,7 @@ RUN set -eux; \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--strip-debug \
|
||||
--add-modules java.base,jdk.management.agent,java.se,jdk.compiler,jdk.javadoc,jdk.attach,jdk.unsupported \
|
||||
--add-modules java.base,jdk.management.agent,java.se,jdk.compiler,jdk.javadoc,jdk.attach,jdk.unsupported,jdk.jfr,jdk.jcmd \
|
||||
--output /opt/jre;
|
||||
|
||||
FROM ubuntu:24.04 AS image
|
||||
|
||||
@@ -5,7 +5,8 @@ ENV LANG=en_US.UTF-8 \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
NODE_VERSION=v22.22.0 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
PATH=/opt/node/bin:/opt/imagick/bin:$PATH
|
||||
PATH=/opt/node/bin:/opt/imagick/bin:$PATH \
|
||||
PLAYWRIGHT_BROWSERS_PATH=/opt/penpot/browsers
|
||||
|
||||
RUN set -ex; \
|
||||
useradd -U -M -u 1001 -s /bin/false -d /opt/penpot penpot; \
|
||||
|
||||
@@ -30,11 +30,9 @@ x-uri: &penpot-public-uri
|
||||
PENPOT_PUBLIC_URI: http://localhost:9001
|
||||
|
||||
x-body-size: &penpot-http-body-size
|
||||
# Max body size (30MiB); Used for plain requests, should never be
|
||||
# greater than multi-part size
|
||||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
|
||||
# Max multipart body size (350MiB)
|
||||
# Max body size
|
||||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||||
# Deprecation warning: this variable is deprecated. Use PENPOT_HTTP_SERVER_MAX_BODY (defaults to 367001600)
|
||||
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: 367001600
|
||||
|
||||
## Penpot SECRET KEY. It serves as a master key from which other keys for subsystems
|
||||
|
||||
@@ -30,8 +30,8 @@ update_flags /var/www/app/js/config.js
|
||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
||||
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
|
||||
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||
export PENPOT_HTTP_SERVER_MAX_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_BODY_SIZE:-367001600} # Default to 350MiB
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE" \
|
||||
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
|
||||
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
||||
|
||||
@@ -76,7 +76,7 @@ http {
|
||||
listen [::]:8080 default_server;
|
||||
server_name _;
|
||||
|
||||
client_max_body_size $PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE;
|
||||
client_max_body_size $PENPOT_HTTP_SERVER_MAX_BODY_SIZE;
|
||||
charset utf-8;
|
||||
|
||||
etag off;
|
||||
|
||||
@@ -188,8 +188,8 @@ server {
|
||||
server_name penpot.mycompany.com;
|
||||
|
||||
# This value should be in sync with the corresponding in the docker-compose.yml
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 31457280
|
||||
client_max_body_size 31457280;
|
||||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||||
client_max_body_size 367001600;
|
||||
|
||||
# Logs: Configure your logs following the best practices inside your company
|
||||
access_log /path/to/penpot.access.log;
|
||||
|
||||
@@ -15,6 +15,7 @@ pnpm run build;
|
||||
|
||||
cp pnpm-lock.yaml target/;
|
||||
cp package.json target/;
|
||||
touch target/pnpm-workspace.yaml;
|
||||
|
||||
cat <<EOF | tee target/setup
|
||||
#/usr/bin/env bash
|
||||
@@ -22,7 +23,7 @@ set -e;
|
||||
corepack enable;
|
||||
corepack install;
|
||||
pnpm install
|
||||
pnpx playwright install chromium;
|
||||
pnpm exec playwright install chromium;
|
||||
EOF
|
||||
|
||||
chmod +x target/setup;
|
||||
|
||||
@@ -40,14 +40,16 @@
|
||||
"watch:app:libs": "node ./scripts/build-libs.js --watch",
|
||||
"watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook",
|
||||
"clear:shadow-cache": "rm -rf .shadow-cljs",
|
||||
"clear:wasm": "cargo clean --manifest-path ../render-wasm/Cargo.toml",
|
||||
"watch": "exit 0",
|
||||
"watch:app": "pnpm run clear:shadow-cache && pnpm run build:wasm && concurrently --kill-others-on-fail \"pnpm run watch:app:assets\" \"pnpm run watch:app:main\" \"pnpm run watch:app:libs\"",
|
||||
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\""
|
||||
"watch:app": "pnpm run clear:shadow-cache && pnpm run clear:wasm && pnpm run build:wasm && concurrently --kill-others-on-fail \"pnpm run watch:app:assets\" \"pnpm run watch:app:main\" \"pnpm run watch:app:libs\"",
|
||||
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\"",
|
||||
"postinstall": "(cd ../plugins/libs/plugins-runtime; pnpm install; pnpm run build)"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@penpot/draft-js": "workspace:./packages/draft-js",
|
||||
"@penpot/mousetrap": "workspace:./packages/mousetrap",
|
||||
"@penpot/plugins-runtime": "1.4.2",
|
||||
"@penpot/plugins-runtime": "link:../plugins/dist/plugins-runtime",
|
||||
"@penpot/svgo": "penpot/svgo#v3.2",
|
||||
"@penpot/text-editor": "workspace:./text-editor",
|
||||
"@penpot/tokenscript": "workspace:./packages/tokenscript",
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true
|
||||
},
|
||||
"~:name": "Default",
|
||||
"~:modified-at": "~m1713533116375",
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f7920a",
|
||||
"~:created-at": "~m1713533116375",
|
||||
"~:is-default": true
|
||||
}
|
||||
]
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"~:email": "foo@example.com",
|
||||
"~:is-demo": false,
|
||||
"~:auth-backend": "penpot",
|
||||
"~:fullname": "Princesa Leia",
|
||||
"~:modified-at": "~m1713533116365",
|
||||
"~:is-active": true,
|
||||
"~:default-project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
|
||||
"~:is-muted": false,
|
||||
"~:default-team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||
"~:created-at": "~m1713533116365",
|
||||
"~:is-blocked": false,
|
||||
"~:props": {
|
||||
"~:nudge": {
|
||||
"~:big": 10,
|
||||
"~:small": 1
|
||||
},
|
||||
"~:v2-info-shown": true,
|
||||
"~:viewed-tutorial?": false,
|
||||
"~:viewed-walkthrough?": false,
|
||||
"~:onboarding-viewed": true,
|
||||
"~:builtin-templates-collapsed-status":
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,814 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"render-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~ueba8fa2e-4140-8084-8005-448635d7a724",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "gaps",
|
||||
"~:revn": 79,
|
||||
"~:modified-at": "~m1771855365377",
|
||||
"~:vern": 0,
|
||||
"~:id": "~ueffcbebc-b8c8-802f-8007-9a0b2e2c863f",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~ueba8fa2e-4140-8084-8005-448635da32b4",
|
||||
"~:created-at": "~m1771591980210",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~ueffcbebc-b8c8-802f-8007-9a0b2e2c8640"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~ueffcbebc-b8c8-802f-8007-9a0b2e2c8640": {
|
||||
"~:objects": {
|
||||
"~u00000000-0000-0000-0000-000000000000": {
|
||||
"~#shape": {
|
||||
"~:y": 0,
|
||||
"~:hide-fill-on-export": false,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:name": "Root Frame",
|
||||
"~:width": 0.01,
|
||||
"~:type": "~:frame",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0,
|
||||
"~:y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0.01,
|
||||
"~:y": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0.01,
|
||||
"~:y": 0.01
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0,
|
||||
"~:y": 0.01
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:r2": 0,
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:r3": 0,
|
||||
"~:r1": 0,
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [],
|
||||
"~:x": 0,
|
||||
"~:proportion": 1,
|
||||
"~:r4": 0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 0,
|
||||
"~:y": 0,
|
||||
"~:width": 0.01,
|
||||
"~:height": 0.01,
|
||||
"~:x1": 0,
|
||||
"~:y1": 0,
|
||||
"~:x2": 0.01,
|
||||
"~:y2": 0.01
|
||||
}
|
||||
},
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#FFFFFF",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:flip-x": null,
|
||||
"~:height": 0.01,
|
||||
"~:flip-y": null,
|
||||
"~:shapes": [
|
||||
"~u36e8a3ad-2b63-8008-8007-9a0b2f24ca4e",
|
||||
"~ufbc43ead-a2ce-8058-8007-9a0daf843e09",
|
||||
"~ufbc43ead-a2ce-8058-8007-9a0dbe2f49b8",
|
||||
"~u5bebb998-d617-801b-8007-9a3fbd5cc804",
|
||||
"~u80e2fa5a-cd1c-8043-8007-9d8aaca49f40"
|
||||
]
|
||||
}
|
||||
},
|
||||
"~ufbc43ead-a2ce-8058-8007-9a0dbe2f49b8": {
|
||||
"~#shape": {
|
||||
"~:y": null,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:grow-type": "~:fixed",
|
||||
"~:content": {
|
||||
"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAAD/f5dEM2EsRAIAAAAAAAAAAAAAAAAAAAAAAAAAUhmnRABACkQCAAAAAAAAAAAAAAAAAAAAAAAAAP8/vET//01EAgAAAAAAAAAAAAAAAAAAAAAAAAD/f5dEM2EsRA=="
|
||||
},
|
||||
"~:name": "Path",
|
||||
"~:width": null,
|
||||
"~:type": "~:path",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1212.00003372852,
|
||||
"~:y": 553.000012923003
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1506.00004755679,
|
||||
"~:y": 553.000012923003
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1506.00004755679,
|
||||
"~:y": 823.999993849517
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1212.00003372852,
|
||||
"~:y": 823.999993849517
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:r2": 0,
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:r3": 0,
|
||||
"~:r1": 0,
|
||||
"~:id": "~ufbc43ead-a2ce-8058-8007-9a0dbe2f49b8",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [
|
||||
{
|
||||
"~:stroke-alignment": "~:inner",
|
||||
"~:stroke-style": "~:solid",
|
||||
"~:stroke-color": "#000000",
|
||||
"~:stroke-opacity": 1,
|
||||
"~:stroke-width": 10
|
||||
}
|
||||
],
|
||||
"~:x": null,
|
||||
"~:proportion": 1,
|
||||
"~:shadow": [],
|
||||
"~:r4": 0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 1212.00003372852,
|
||||
"~:y": 553.000012923003,
|
||||
"~:width": 294.000013828278,
|
||||
"~:height": 270.999980926514,
|
||||
"~:x1": 1212.00003372852,
|
||||
"~:y1": 553.000012923003,
|
||||
"~:x2": 1506.00004755679,
|
||||
"~:y2": 823.999993849517
|
||||
}
|
||||
},
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:flip-x": null,
|
||||
"~:height": null,
|
||||
"~:flip-y": null
|
||||
}
|
||||
},
|
||||
"~u36e8a3ad-2b63-8008-8007-9a0b2f24ca4e": {
|
||||
"~#shape": {
|
||||
"~:y": 122.000001761754,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:hide-in-viewer": false,
|
||||
"~:name": "Rectangle",
|
||||
"~:width": 463.999987447937,
|
||||
"~:type": "~:rect",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 694.000014750112,
|
||||
"~:y": 122.000001761754
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1158.00000219805,
|
||||
"~:y": 122.000001761754
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1158.00000219805,
|
||||
"~:y": 499.999980116278
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 694.000014750112,
|
||||
"~:y": 499.999980116278
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:r2": 0,
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:r3": 0,
|
||||
"~:r1": 0,
|
||||
"~:id": "~u36e8a3ad-2b63-8008-8007-9a0b2f24ca4e",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [
|
||||
{
|
||||
"~:stroke-alignment": "~:inner",
|
||||
"~:stroke-style": "~:solid",
|
||||
"~:stroke-color": "#000000",
|
||||
"~:stroke-opacity": 1,
|
||||
"~:stroke-width": 100
|
||||
},
|
||||
{
|
||||
"~:stroke-alignment": "~:outer",
|
||||
"~:stroke-style": "~:solid",
|
||||
"~:stroke-color": "#000000",
|
||||
"~:stroke-opacity": 1,
|
||||
"~:stroke-width": 100
|
||||
}
|
||||
],
|
||||
"~:x": 694.000014750113,
|
||||
"~:proportion": 1,
|
||||
"~:shadow": [],
|
||||
"~:r4": 0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 694.000014750113,
|
||||
"~:y": 122.000001761754,
|
||||
"~:width": 463.999987447937,
|
||||
"~:height": 377.999978354524,
|
||||
"~:x1": 694.000014750113,
|
||||
"~:y1": 122.000001761754,
|
||||
"~:x2": 1158.00000219805,
|
||||
"~:y2": 499.999980116278
|
||||
}
|
||||
},
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:flip-x": null,
|
||||
"~:height": 377.999978354524,
|
||||
"~:flip-y": null
|
||||
}
|
||||
},
|
||||
"~ufbc43ead-a2ce-8058-8007-9a0daf843e09": {
|
||||
"~#shape": {
|
||||
"~:y": 262.999997589325,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:grow-type": "~:fixed",
|
||||
"~:hide-in-viewer": false,
|
||||
"~:name": "Ellipse",
|
||||
"~:width": 266.000036716461,
|
||||
"~:type": "~:circle",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1271.00000137752,
|
||||
"~:y": 262.999997589325
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1537.00003809398,
|
||||
"~:y": 262.999997589325
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1537.00003809398,
|
||||
"~:y": 483.000033828949
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1271.00000137752,
|
||||
"~:y": 483.000033828949
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:r2": 0,
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:r3": 0,
|
||||
"~:r1": 0,
|
||||
"~:id": "~ufbc43ead-a2ce-8058-8007-9a0daf843e09",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [
|
||||
{
|
||||
"~:stroke-alignment": "~:inner",
|
||||
"~:stroke-style": "~:solid",
|
||||
"~:stroke-color": "#000000",
|
||||
"~:stroke-opacity": 1,
|
||||
"~:stroke-width": 10
|
||||
}
|
||||
],
|
||||
"~:x": 1271.00000137752,
|
||||
"~:proportion": 1,
|
||||
"~:shadow": [
|
||||
{
|
||||
"~:id": "~u9c6321b5-aeab-809f-8007-971f9e232191",
|
||||
"~:style": "~:drop-shadow",
|
||||
"~:color": {
|
||||
"~:color": "#000000",
|
||||
"~:opacity": 1
|
||||
},
|
||||
"~:offset-x": 4,
|
||||
"~:offset-y": 4,
|
||||
"~:blur": 0,
|
||||
"~:spread": 0,
|
||||
"~:hidden": true
|
||||
}
|
||||
],
|
||||
"~:r4": 0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 1271.00000137752,
|
||||
"~:y": 262.999997589325,
|
||||
"~:width": 266.000036716461,
|
||||
"~:height": 220.000036239624,
|
||||
"~:x1": 1271.00000137752,
|
||||
"~:y1": 262.999997589325,
|
||||
"~:x2": 1537.00003809398,
|
||||
"~:y2": 483.000033828949
|
||||
}
|
||||
},
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:flip-x": null,
|
||||
"~:height": 220.000036239624,
|
||||
"~:flip-y": null
|
||||
}
|
||||
},
|
||||
"~u80e2fa5a-cd1c-8043-8007-9d8aaca49f40": {
|
||||
"~#shape": {
|
||||
"~:y": -286.999972473494,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:grow-type": "~:auto-width",
|
||||
"~:content": {
|
||||
"~:type": "root",
|
||||
"~:key": "1srkh8oc2vd",
|
||||
"~:children": [
|
||||
{
|
||||
"~:type": "paragraph-set",
|
||||
"~:children": [
|
||||
{
|
||||
"~:line-height": "1.2",
|
||||
"~:font-style": "normal",
|
||||
"~:children": [
|
||||
{
|
||||
"~:line-height": "1.2",
|
||||
"~:font-style": "normal",
|
||||
"~:typography-ref-id": null,
|
||||
"~:text-transform": "none",
|
||||
"~:font-id": "sourcesanspro",
|
||||
"~:key": "170uyffw5ph",
|
||||
"~:font-size": "400",
|
||||
"~:font-weight": "400",
|
||||
"~:typography-ref-file": null,
|
||||
"~:font-variant-id": "regular",
|
||||
"~:text-decoration": "none",
|
||||
"~:letter-spacing": "0",
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:font-family": "sourcesanspro",
|
||||
"~:text": "HELLO"
|
||||
}
|
||||
],
|
||||
"~:typography-ref-id": null,
|
||||
"~:text-transform": "none",
|
||||
"~:text-align": "left",
|
||||
"~:font-id": "sourcesanspro",
|
||||
"~:key": "psg8ayj675",
|
||||
"~:font-size": "400",
|
||||
"~:font-weight": "400",
|
||||
"~:typography-ref-file": null,
|
||||
"~:text-direction": "ltr",
|
||||
"~:type": "paragraph",
|
||||
"~:font-variant-id": "regular",
|
||||
"~:text-decoration": "none",
|
||||
"~:letter-spacing": "0",
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:font-family": "sourcesanspro"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"~:vertical-align": "top"
|
||||
},
|
||||
"~:hide-in-viewer": false,
|
||||
"~:name": "HELLO",
|
||||
"~:width": 1116.00003953244,
|
||||
"~:type": "~:text",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 545.000013504691,
|
||||
"~:y": -286.999972473494
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1661.00005303713,
|
||||
"~:y": -286.999972473494
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1661.00005303713,
|
||||
"~:y": 193.000017549648
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 545.000013504691,
|
||||
"~:y": 193.000017549648
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:id": "~u80e2fa5a-cd1c-8043-8007-9d8aaca49f40",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:position-data": [
|
||||
{
|
||||
"~:y": 211.980041503906,
|
||||
"~:line-height": "1.2",
|
||||
"~:font-style": "normal",
|
||||
"~:text-transform": "none",
|
||||
"~:text-align": "left",
|
||||
"~:font-id": "sourcesanspro",
|
||||
"~:font-size": "400",
|
||||
"~:font-weight": "400",
|
||||
"~:text-direction": "ltr",
|
||||
"~:width": 1115.22998046875,
|
||||
"~:font-variant-id": "regular",
|
||||
"~:text-decoration": "none",
|
||||
"~:letter-spacing": "0",
|
||||
"~:x": 545,
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:direction": "ltr",
|
||||
"~:font-family": "sourcesanspro",
|
||||
"~:height": 517.960021972656,
|
||||
"~:text": "HELLO"
|
||||
}
|
||||
],
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [
|
||||
{
|
||||
"~:stroke-style": "~:solid",
|
||||
"~:stroke-alignment": "~:inner",
|
||||
"~:stroke-width": 5,
|
||||
"~:stroke-color": "#000000",
|
||||
"~:stroke-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:x": 545.000013504691,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 545.000013504691,
|
||||
"~:y": -286.999972473494,
|
||||
"~:width": 1116.00003953244,
|
||||
"~:height": 479.999990023141,
|
||||
"~:x1": 545.000013504691,
|
||||
"~:y1": -286.999972473494,
|
||||
"~:x2": 1661.00005303713,
|
||||
"~:y2": 193.000017549648
|
||||
}
|
||||
},
|
||||
"~:flip-x": null,
|
||||
"~:height": 479.999990023141,
|
||||
"~:flip-y": null
|
||||
}
|
||||
},
|
||||
"~u5bebb998-d617-801b-8007-9a3fbd5cc804": {
|
||||
"~#shape": {
|
||||
"~:y": 543.00001095581,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:hide-in-viewer": false,
|
||||
"~:name": "Rectangle",
|
||||
"~:width": 463.999987447937,
|
||||
"~:type": "~:rect",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 693.999990768432,
|
||||
"~:y": 543.00001095581
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1157.99997821637,
|
||||
"~:y": 543.00001095581
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 1157.99997821637,
|
||||
"~:y": 920.999989310334
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 693.999990768432,
|
||||
"~:y": 920.999989310334
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:r2": 0,
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 0
|
||||
}
|
||||
},
|
||||
"~:r3": 0,
|
||||
"~:r1": 0,
|
||||
"~:id": "~u5bebb998-d617-801b-8007-9a3fbd5cc804",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [
|
||||
{
|
||||
"~:stroke-alignment": "~:inner",
|
||||
"~:stroke-style": "~:solid",
|
||||
"~:stroke-color": "#000000",
|
||||
"~:stroke-opacity": 1,
|
||||
"~:stroke-width": 100
|
||||
}
|
||||
],
|
||||
"~:x": 693.999990768432,
|
||||
"~:proportion": 1,
|
||||
"~:shadow": [],
|
||||
"~:r4": 0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 693.999990768432,
|
||||
"~:y": 543.00001095581,
|
||||
"~:width": 463.999987447937,
|
||||
"~:height": 377.999978354524,
|
||||
"~:x1": 693.999990768432,
|
||||
"~:y1": 543.00001095581,
|
||||
"~:x2": 1157.99997821637,
|
||||
"~:y2": 920.999989310334
|
||||
}
|
||||
},
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#ffffff",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:flip-x": null,
|
||||
"~:height": 377.999978354524,
|
||||
"~:flip-y": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:id": "~ueffcbebc-b8c8-802f-8007-9a0b2e2c8640",
|
||||
"~:name": "Page 1",
|
||||
"~:background": "#000000"
|
||||
}
|
||||
},
|
||||
"~:id": "~ueffcbebc-b8c8-802f-8007-9a0b2e2c863f",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
1161
frontend/playwright/data/render-wasm/get-solid-shadows.json
Normal file
2826
frontend/playwright/data/render-wasm/get-solid-strokes-shadows.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"~:file-id": "~u52c4e771-3853-8190-8007-9506c70e8100",
|
||||
"~:id": "~u4173a29d-4020-80b4-8007-96527ba9d8af",
|
||||
"~:created-at": "~m1771342236330",
|
||||
"~:modified-at": "~m1771342236330",
|
||||
"~:type": "fragment",
|
||||
"~:backend": "db",
|
||||
"~:data": {
|
||||
"~:id": "~uecb0cfd0-0f0b-81f7-8007-950628f9665b",
|
||||
"~:name": "Page 1",
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~ude9c6736-45ce-80a1-8007-950643da554d": "[\"~#shape\",[\"^ \",\"~:y\",383.99998939037323,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"R1\",\"~:width\",74,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",831.999992787838,\"~:y\",383.99998939037323]],[\"^<\",[\"^ \",\"~:x\",905.999992787838,\"~:y\",383.99998939037323]],[\"^<\",[\"^ \",\"~:x\",905.999992787838,\"~:y\",429.99998664855957]],[\"^<\",[\"^ \",\"~:x\",831.999992787838,\"~:y\",429.99998664855957]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~uecb0cfd0-0f0b-81f7-8007-950628f9665b\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~ude9c6736-45ce-80a1-8007-950643da554d\",\"~:parent-id\",\"~ude9c6736-45ce-80a1-8007-95063a202e52\",\"~:frame-id\",\"~ude9c6736-45ce-80a1-8007-95063a202e52\",\"~:strokes\",[],\"~:x\",831.999992787838,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",831.999992787838,\"~:y\",383.99998939037323,\"^8\",74,\"~:height\",45.99999725818634,\"~:x1\",831.999992787838,\"~:y1\",383.99998939037323,\"~:x2\",905.999992787838,\"~:y2\",429.99998664855957]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^K\",45.99999725818634,\"~:flip-y\",null]]",
|
||||
"~ude9c6736-45ce-80a1-8007-95065b4599ea": "[\"~#shape\",[\"^ \",\"~:y\",439.999969256975,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"R2\",\"~:width\",300.00007388362076,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",832.0000015918991,\"~:y\",439.99996925697496]],[\"^<\",[\"^ \",\"~:x\",1132.00007547552,\"~:y\",439.99996925697496]],[\"^<\",[\"^ \",\"~:x\",1132.00007547552,\"~:y\",589.9999658711585]],[\"^<\",[\"^ \",\"~:x\",832.0000015918991,\"~:y\",589.9999658711585]]],\"~:r2\",0,\"~:layout-item-h-sizing\",\"~:fix\",\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~uecb0cfd0-0f0b-81f7-8007-950628f9665b\",\"~:layout-item-v-sizing\",\"^?\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~ude9c6736-45ce-80a1-8007-95065b4599ea\",\"~:parent-id\",\"~ude9c6736-45ce-80a1-8007-95063a202e52\",\"~:frame-id\",\"~ude9c6736-45ce-80a1-8007-95063a202e52\",\"~:strokes\",[],\"~:x\",832.0000015918993,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",832.0000015918993,\"~:y\",439.999969256975,\"^8\",300.00007388362076,\"~:height\",149.9999966141835,\"~:x1\",832.0000015918993,\"~:y1\",439.999969256975,\"~:x2\",1132.0000754755201,\"~:y2\",589.9999658711586]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^N\",149.9999966141835,\"~:flip-y\",null]]",
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~uecb0cfd0-0f0b-81f7-8007-950628f9665b\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^I\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~ude9c6736-45ce-80a1-8007-95062aa41d6b\"]]]",
|
||||
"~ude9c6736-45ce-80a1-8007-95062aa41d6b": "[\"~#shape\",[\"^ \",\"~:y\",342.0000208153068,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",0,\"~:p3\",0,\"~:p4\",0],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",false,\"~:name\",\"A\",\"~:layout-align-items\",\"~:start\",\"~:width\",392.9999889135361,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",787.9999763965607,\"~:y\",342.0000208153068]],[\"^L\",[\"^ \",\"~:x\",1180.9999653100967,\"~:y\",342.0000208153068]],[\"^L\",[\"^ \",\"~:x\",1180.9999653100967,\"~:y\",704.000018450968]],[\"^L\",[\"^ \",\"~:x\",787.9999763965607,\"~:y\",704.000018450968]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~uecb0cfd0-0f0b-81f7-8007-950628f9665b\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:r3\",0,\"~:layout-justify-content\",\"^E\",\"~:r1\",0,\"~:id\",\"~ude9c6736-45ce-80a1-8007-95062aa41d6b\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",787.9999763965607,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",787.9999763965607,\"~:y\",342.0000208153068,\"^F\",392.9999889135361,\"~:height\",361.9999976356612,\"~:x1\",787.9999763965607,\"~:y1\",342.0000208153068,\"~:x2\",1180.9999653100967,\"~:y2\",704.000018450968]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^19\",361.9999976356612,\"~:flip-y\",null,\"~:shapes\",[\"~ude9c6736-45ce-80a1-8007-95062fafbb88\"]]]",
|
||||
"~ude9c6736-45ce-80a1-8007-95063a202e52": "[\"~#shape\",[\"^ \",\"~:y\",374.00001145409465,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",10,\"~:p2\",10,\"~:p3\",10,\"~:p4\",10],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"C\",\"~:layout-align-items\",\"~:start\",\"~:width\",320.00009969013945,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",821.9999915631233,\"~:y\",374.0000114540946]],[\"^L\",[\"^ \",\"~:x\",1142.0000912532628,\"~:y\",374.0000114540946]],[\"^L\",[\"^ \",\"~:x\",1142.0000912532628,\"~:y\",600.0000518384436]],[\"^L\",[\"^ \",\"~:x\",821.9999915631233,\"~:y\",600.0000518384436]]],\"~:r2\",0,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",10,\"~:column-gap\",0],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~uecb0cfd0-0f0b-81f7-8007-950628f9665b\",\"~:layout-item-v-sizing\",\"^O\",\"~:r3\",0,\"~:layout-justify-content\",\"^E\",\"~:r1\",0,\"~:id\",\"~ude9c6736-45ce-80a1-8007-95063a202e52\",\"~:parent-id\",\"~ude9c6736-45ce-80a1-8007-95062fafbb88\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~ude9c6736-45ce-80a1-8007-95062fafbb88\",\"~:strokes\",[[\"^ \",\"~:stroke-alignment\",\"~:inner\",\"~:stroke-style\",\"~:solid\",\"~:stroke-color\",\"#000000\",\"~:stroke-opacity\",1,\"~:stroke-width\",1]],\"~:x\",821.9999915631233,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",821.9999915631233,\"~:y\",374.00001145409465,\"^F\",320.00009969013945,\"~:height\",226.000040384349,\"~:x1\",821.9999915631233,\"~:y1\",374.00001145409465,\"~:x2\",1142.0000912532628,\"~:y2\",600.0000518384436]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^1A\",226.000040384349,\"~:flip-y\",null,\"~:shapes\",[\"~ude9c6736-45ce-80a1-8007-95065b4599ea\",\"~ude9c6736-45ce-80a1-8007-950643da554d\"]]]",
|
||||
"~ude9c6736-45ce-80a1-8007-95062fafbb88": "[\"~#shape\",[\"^ \",\"~:y\",342.0000083402533,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",31.999953508377075,\"~:p2\",34.000026052944804,\"~:p3\",31.999953508377075,\"~:p4\",34.000026052944804],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"B\",\"~:layout-align-items\",\"~:start\",\"~:width\",392.9999979687366,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",788.0000295450811,\"~:y\",342.00000834025326]],[\"^L\",[\"^ \",\"~:x\",1181.0000275138177,\"~:y\",342.00000834025326]],[\"^L\",[\"^ \",\"~:x\",1181.0000275138177,\"~:y\",631.9999659712]],[\"^L\",[\"^ \",\"~:x\",788.0000295450811,\"~:y\",631.9999659712]]],\"~:r2\",0,\"~:layout-item-h-sizing\",\"~:fill\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~uecb0cfd0-0f0b-81f7-8007-950628f9665b\",\"~:layout-item-v-sizing\",\"~:auto\",\"~:r3\",0,\"~:layout-justify-content\",\"^E\",\"~:r1\",0,\"~:id\",\"~ude9c6736-45ce-80a1-8007-95062fafbb88\",\"~:parent-id\",\"~ude9c6736-45ce-80a1-8007-95062aa41d6b\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~ude9c6736-45ce-80a1-8007-95062aa41d6b\",\"~:strokes\",[[\"^ \",\"~:stroke-alignment\",\"~:inner\",\"~:stroke-style\",\"~:solid\",\"~:stroke-color\",\"#000000\",\"~:stroke-opacity\",1,\"~:stroke-width\",1]],\"~:x\",788.0000295450811,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",788.0000295450811,\"~:y\",342.0000083402533,\"^F\",392.9999979687366,\"~:height\",289.99995763094677,\"~:x1\",788.0000295450811,\"~:y1\",342.0000083402533,\"~:x2\",1181.0000275138177,\"~:y2\",631.9999659712]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^1B\",289.99995763094677,\"~:flip-y\",null,\"~:shapes\",[\"~ude9c6736-45ce-80a1-8007-95063a202e52\"]]]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
frontend/playwright/data/workspace/get-file-13382.json
Normal file
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~ud715d0a5-a44e-8056-8005-a79999e18b64",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "bug flex",
|
||||
"~:revn": 33,
|
||||
"~:modified-at": "~m1771342236324",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u52c4e771-3853-8190-8007-9506c70e8100",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~u76eab896-accf-81a5-8007-2b264ebe7817",
|
||||
"~:created-at": "~m1771255281717",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~uecb0cfd0-0f0b-81f7-8007-950628f9665b"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~uecb0cfd0-0f0b-81f7-8007-950628f9665b": {
|
||||
"~#penpot/pointer": [
|
||||
"~u4173a29d-4020-80b4-8007-96527ba9d8af",
|
||||
{
|
||||
"~:created-at": "~m1771342236327"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"~:id": "~u52c4e771-3853-8190-8007-9506c70e8100",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
136
frontend/playwright/data/workspace/get-file-13385-2.json
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/objects-map",
|
||||
"render-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~u99e49e93-362f-80ef-8007-3450ea52c9a4",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "BUG 13385",
|
||||
"~:revn": 3,
|
||||
"~:modified-at": "~m1771254407745",
|
||||
"~:vern": 1173241426,
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-950361d24e43",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~ucd8f7672-e5d1-810f-8007-87e124eda82a",
|
||||
"~:created-at": "~m1771254391625",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u3ea49ce0-9d99-8197-8007-950361d24e44"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u3ea49ce0-9d99-8197-8007-950361d24e44": {
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~ue0e81bed-4dc2-805c-8007-95036a4a3131\",\"~ue0e81bed-4dc2-805c-8007-95036c27428b\"]]]",
|
||||
"~ue0e81bed-4dc2-805c-8007-95036a4a3131": "[\"~#shape\",[\"^ \",\"~:y\",252,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",177,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",156,\"~:y\",252]],[\"^<\",[\"^ \",\"~:x\",333,\"~:y\",252]],[\"^<\",[\"^ \",\"~:x\",333,\"~:y\",389]],[\"^<\",[\"^ \",\"~:x\",156,\"~:y\",389]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~ue0e81bed-4dc2-805c-8007-95036a4a3131\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",156,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",156,\"~:y\",252,\"^8\",177,\"~:height\",137,\"~:x1\",156,\"~:y1\",252,\"~:x2\",333,\"~:y2\",389]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",137,\"~:flip-y\",null]]",
|
||||
"~ue0e81bed-4dc2-805c-8007-95036c27428b": "[\"~#shape\",[\"^ \",\"~:y\",250,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Ellipse\",\"~:width\",148,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",362,\"~:y\",250]],[\"^<\",[\"^ \",\"~:x\",510,\"~:y\",250]],[\"^<\",[\"^ \",\"~:x\",510,\"~:y\",389]],[\"^<\",[\"^ \",\"~:x\",362,\"~:y\",389]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~ue0e81bed-4dc2-805c-8007-95036c27428b\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",362,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",362,\"~:y\",250,\"^8\",148,\"~:height\",139,\"~:x1\",362,\"~:y1\",250,\"~:x2\",510,\"~:y2\",389]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",139,\"~:flip-y\",null]]"
|
||||
}
|
||||
},
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-950361d24e44",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
},
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-950361d24e43",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
135
frontend/playwright/data/workspace/get-file-13385.json
Normal file
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/objects-map",
|
||||
"render-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~u99e49e93-362f-80ef-8007-3450ea52c9a4",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "BUG 13385",
|
||||
"~:revn": 2,
|
||||
"~:modified-at": "~m1771254464312",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-950361d24e43",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~ucd8f7672-e5d1-810f-8007-87e124eda82a",
|
||||
"~:created-at": "~m1771254391625",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u3ea49ce0-9d99-8197-8007-950361d24e44"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u3ea49ce0-9d99-8197-8007-950361d24e44": {
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~ue0e81bed-4dc2-805c-8007-95036a4a3131\"]]]",
|
||||
"~ue0e81bed-4dc2-805c-8007-95036a4a3131": "[\"~#shape\",[\"^ \",\"~:y\",252,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",177,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",156,\"~:y\",252]],[\"^<\",[\"^ \",\"~:x\",333,\"~:y\",252]],[\"^<\",[\"^ \",\"~:x\",333,\"~:y\",389]],[\"^<\",[\"^ \",\"~:x\",156,\"~:y\",389]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~ue0e81bed-4dc2-805c-8007-95036a4a3131\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",156,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",156,\"~:y\",252,\"^8\",177,\"~:height\",137,\"~:x1\",156,\"~:y1\",252,\"~:x2\",333,\"~:y2\",389]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",137,\"~:flip-y\",null]]"
|
||||
}
|
||||
},
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-950361d24e44",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
},
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-950361d24e43",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
135
frontend/playwright/data/workspace/get-file-13415.json
Normal file
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/objects-map",
|
||||
"render-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~u99e49e93-362f-80ef-8007-3450ea52c9a4",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "BUG 13415",
|
||||
"~:revn": 14,
|
||||
"~:modified-at": "~m1771334256704",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u0472abff-2573-8186-8007-961793e53f45",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~ucd8f7672-e5d1-810f-8007-87e124eda82a",
|
||||
"~:created-at": "~m1771326794644",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u0472abff-2573-8186-8007-961793e53f46"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u0472abff-2573-8186-8007-961793e53f46": {
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~uaef184da-e9c1-80f8-8007-961cf253d534\"]]]",
|
||||
"~uaef184da-e9c1-80f8-8007-961cf253d534": "[\"~#shape\",[\"^ \",\"~:y\",286,\"~:layout-grid-columns\",[[\"^ \",\"~:type\",\"~:flex\",\"~:value\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",0,\"~:p3\",0,\"~:p4\",0],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:grid\",\"~:hide-in-viewer\",false,\"~:name\",\"Board\",\"~:layout-align-items\",\"~:start\",\"~:width\",298,\"~:layout-grid-cells\",[\"^ \",\"~uaef184da-e9c1-80f8-8007-961cf50d67b4\",[\"^ \",\"~:justify-self\",\"~:auto\",\"~:column\",1,\"~:id\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b4\",\"~:position\",\"^L\",\"~:column-span\",1,\"~:align-self\",\"^L\",\"~:row\",1,\"~:row-span\",1,\"~:shapes\",[]],\"~uaef184da-e9c1-80f8-8007-961cf50d67b5\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b5\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",1,\"^S\",1,\"^T\",[]],\"~uaef184da-e9c1-80f8-8007-961cf50d67b6\",[\"^ \",\"^K\",\"^L\",\"^M\",1,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b6\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]],\"~uaef184da-e9c1-80f8-8007-961cf50d67b7\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b7\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]]],\"~:layout-padding-type\",\"~:simple\",\"^2\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",322,\"~:y\",286]],[\"^10\",[\"^ \",\"~:x\",620,\"~:y\",286]],[\"^10\",[\"^ \",\"~:x\",620,\"~:y\",552]],[\"^10\",[\"^ \",\"~:x\",322,\"~:y\",552]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^>\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:layout-justify-content\",\"~:stretch\",\"~:r1\",0,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf253d534\",\"~:layout-justify-items\",\"^G\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-align-content\",\"^19\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",322,\"~:proportion\",1,\"~:r4\",0,\"~:layout-grid-rows\",[[\"^ \",\"^2\",\"^3\",\"^4\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",322,\"~:y\",286,\"^H\",298,\"~:height\",266,\"~:x1\",322,\"~:y1\",286,\"~:x2\",620,\"~:y2\",552]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:layout-grid-dir\",\"^R\",\"~:flip-x\",null,\"^1E\",266,\"~:flip-y\",null,\"^T\",[]]]"
|
||||
}
|
||||
},
|
||||
"~:id": "~u0472abff-2573-8186-8007-961793e53f46",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
},
|
||||
"~:id": "~u0472abff-2573-8186-8007-961793e53f45",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"~:file-id": "~u3a4d7ec7-c391-8146-8007-9a05c41da6b9",
|
||||
"~:id": "~u3a4d7ec7-c391-8146-8007-9dd6c998fbc4",
|
||||
"~:created-at": "~m1771846681191",
|
||||
"~:modified-at": "~m1771846681191",
|
||||
"~:type": "fragment",
|
||||
"~:backend": "db",
|
||||
"~:data": {
|
||||
"~:id": "~u95b23c15-79f9-81ba-8007-99d81b5290dd",
|
||||
"~:name": "Page 1",
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u95b23c15-79f9-81ba-8007-99d81b5290dd\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^I\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~ucfb31a9c-83c2-806f-8007-9dbf43043bdf\"]]]",
|
||||
"~ucfb31a9c-83c2-806f-8007-9dbf43043be0": "[\"~#shape\",[\"^ \",\"~:y\",-218.99999605032087,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",5,\"~:p2\",5,\"~:p3\",5,\"~:p4\",5],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Container\",\"~:layout-align-items\",\"~:start\",\"~:width\",431.99994866329087,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",608.9999813066789,\"~:y\",-218.99999605032087]],[\"^J\",[\"^ \",\"~:x\",1040.9999299699698,\"~:y\",-218.99999605032087]],[\"^J\",[\"^ \",\"~:x\",1040.9999299699698,\"~:y\",-177.00001533586985]],[\"^J\",[\"^ \",\"~:x\",608.9999813066789,\"~:y\",-177.00001533586985]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:fill\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u95b23c15-79f9-81ba-8007-99d81b5290dd\",\"~:layout-item-v-sizing\",\"~:auto\",\"~:layout-justify-content\",\"^C\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be0\",\"~:parent-id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043bdf\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043bdf\",\"~:strokes\",[],\"~:x\",608.9999813066788,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",608.9999813066788,\"~:y\",-218.99999605032087,\"^D\",431.99994866329087,\"~:height\",41.99998071445103,\"~:x1\",608.9999813066788,\"~:y1\",-218.99999605032087,\"~:x2\",1040.9999299699698,\"~:y2\",-177.00001533586985]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffc0cb\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^1:\",41.99998071445103,\"~:flip-y\",null,\"~:shapes\",[\"~ucfb31a9c-83c2-806f-8007-9dbf43043be2\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be3\"]]]",
|
||||
"~ucfb31a9c-83c2-806f-8007-9dbf43043be2": "[\"~#shape\",[\"^ \",\"~:y\",-178.00000568505413,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",false,\"~:name\",\"show / hide me\",\"~:width\",99.98206911702209,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",614.0000002576337,\"~:y\",-178.00000568505413]],[\"^:\",[\"^ \",\"~:x\",713.9820693746558,\"~:y\",-178.00000568505413]],[\"^:\",[\"^ \",\"~:x\",713.9820693746558,\"~:y\",-148.0000135081636]],[\"^:\",[\"^ \",\"~:x\",614.0000002576337,\"~:y\",-148.0000135081636]]],\"~:r2\",0,\"~:layout-item-h-sizing\",\"~:fix\",\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:layout-item-v-sizing\",\"^=\",\"~:r3\",0,\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:r1\",0,\"~:hidden\",true,\"~:id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be2\",\"~:parent-id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be0\",\"~:frame-id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be0\",\"~:strokes\",[],\"~:x\",614.0000002576337,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",614.0000002576337,\"~:y\",-178.00000568505413,\"^6\",99.98206911702209,\"~:height\",29.999992176890544,\"~:x1\",614.0000002576337,\"~:y1\",-178.00000568505413,\"~:x2\",713.9820693746558,\"~:y2\",-148.0000135081636]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^P\",29.999992176890544,\"~:flip-y\",null]]",
|
||||
"~ucfb31a9c-83c2-806f-8007-9dbf43043be3": "[\"~#shape\",[\"^ \",\"~:y\",-213.99999587313152,\"~:hide-fill-on-export\",false,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Full width\",\"~:width\",422.00001200500014,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",613.9999939062393,\"~:y\",-213.99999587313152]],[\"^<\",[\"^ \",\"~:x\",1036.0000059112394,\"~:y\",-213.99999587313152]],[\"^<\",[\"^ \",\"~:x\",1036.0000059112394,\"~:y\",-182.00001303926604]],[\"^<\",[\"^ \",\"~:x\",613.9999939062393,\"~:y\",-182.00001303926604]]],\"~:r2\",8,\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:fix\",\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^4\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u95b23c15-79f9-81ba-8007-99d81b5290dd\",\"~:layout-item-v-sizing\",\"^@\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be3\",\"~:parent-id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be0\",\"~:frame-id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043be0\",\"~:strokes\",[],\"~:x\",613.9999939062393,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",613.9999939062393,\"~:y\",-213.99999587313152,\"^8\",422.00001200500014,\"~:height\",31.999982833865488,\"~:x1\",613.9999939062393,\"~:y1\",-213.99999587313152,\"~:x2\",1036.0000059112394,\"~:y2\",-182.00001303926604]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#212426\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^O\",31.999982833865488,\"~:flip-y\",null,\"~:shapes\",[]]]",
|
||||
"~ucfb31a9c-83c2-806f-8007-9dbf43043bdf": "[\"~#shape\",[\"^ \",\"~:y\",-228.99999763039506,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",10,\"~:p2\",10,\"~:p3\",10,\"~:p4\",10],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Parent\",\"~:layout-align-items\",\"~:start\",\"~:width\",451.999905143128,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",599.0000149607649,\"~:y\",-228.99999763039506]],[\"^J\",[\"^ \",\"~:x\",1050.999920103893,\"~:y\",-228.99999763039506]],[\"^J\",[\"^ \",\"~:x\",1050.999920103893,\"~:y\",-167.0000160450801]],[\"^J\",[\"^ \",\"~:x\",599.0000149607649,\"~:y\",-167.0000160450801]]],\"~:r2\",0,\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:fix\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",10,\"~:column-gap\",8],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u95b23c15-79f9-81ba-8007-99d81b5290dd\",\"~:layout-item-v-sizing\",\"~:auto\",\"~:r3\",0,\"~:layout-justify-content\",\"^C\",\"~:r1\",0,\"~:id\",\"~ucfb31a9c-83c2-806f-8007-9dbf43043bdf\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",599.0000149607649,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",599.0000149607649,\"~:y\",-228.99999763039506,\"^D\",451.999905143128,\"~:height\",61.99998158531497,\"~:x1\",599.0000149607649,\"~:y1\",-228.99999763039506,\"~:x2\",1050.999920103893,\"~:y2\",-167.0000160450801]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^1:\",61.99998158531497,\"~:flip-y\",null,\"~:shapes\",[\"~ucfb31a9c-83c2-806f-8007-9dbf43043be0\"]]]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
frontend/playwright/data/workspace/get-file-13468.json
Normal file
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~ud715d0a5-a44e-8056-8005-a79999e18b64",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "test-bug-flex",
|
||||
"~:revn": 114,
|
||||
"~:modified-at": "~m1771846681183",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u3a4d7ec7-c391-8146-8007-9a05c41da6b9",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~u76eab896-accf-81a5-8007-2b264ebe7817",
|
||||
"~:created-at": "~m1771590560885",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u95b23c15-79f9-81ba-8007-99d81b5290dd"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u95b23c15-79f9-81ba-8007-99d81b5290dd": {
|
||||
"~#penpot/pointer": [
|
||||
"~u3a4d7ec7-c391-8146-8007-9dd6c998fbc4",
|
||||
{
|
||||
"~:created-at": "~m1771846681187"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"~:id": "~u3a4d7ec7-c391-8146-8007-9a05c41da6b9",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-95037190405b",
|
||||
"~:label": "Version 000",
|
||||
"~:revn": 1,
|
||||
"~:version": 67,
|
||||
"~:created-at": "~m1771254407745",
|
||||
"~:modified-at": "~m1771254407745",
|
||||
"~:created-by": "user",
|
||||
"~:profile-id": "~u99e49e93-362f-80ef-8007-3450ea5204aa"
|
||||
},
|
||||
{
|
||||
"~:revn": 0,
|
||||
"~:modified-at": "~m1771254406526",
|
||||
"~:deleted-at": "~m1771340806524",
|
||||
"~:created-by": "system",
|
||||
"~:label": "internal/snapshot/0",
|
||||
"~:id": "~u3ea49ce0-9d99-8197-8007-9503705f8b9b",
|
||||
"~:profile-id": "~u99e49e93-362f-80ef-8007-3450ea5204aa",
|
||||
"~:version": 67,
|
||||
"~:created-at": "~m1771254406526"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"~:id": "~u99e49e93-362f-80ef-8007-3450ea5204aa",
|
||||
"~:email": "belen@example.com",
|
||||
"~:name": "Belén Albeza",
|
||||
"~:fullname": "Belén Albeza",
|
||||
"~:is-active": true
|
||||
}
|
||||
]
|
||||
18
frontend/playwright/data/workspace/update-file-13415.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"~:revn": 14,
|
||||
"~:lagged": [
|
||||
{
|
||||
"~:id": "~u0472abff-2573-8186-8007-96347d525f65",
|
||||
"~:revn": 15,
|
||||
"~:file-id": "~u0472abff-2573-8186-8007-961793e53f45",
|
||||
"~:session-id": "~uf25e6d2f-d10c-8021-8007-96344433f08d",
|
||||
"~:changes": [
|
||||
{
|
||||
"~:type": "~:del-obj",
|
||||
"~:page-id": "~u0472abff-2573-8186-8007-961793e53f46",
|
||||
"~:id": "~uaef184da-e9c1-80f8-8007-961cf253d534"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
export class BasePage {
|
||||
static async init(page) {
|
||||
await BasePage.mockConfigFlags(page, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mocks multiple RPC calls in a single call.
|
||||
*
|
||||
@@ -22,7 +26,7 @@ export class BasePage {
|
||||
* @param {*} options
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async mockRPC(page, path, jsonFilename, options) {
|
||||
static async mockRPC(page, path, jsonFilename = "", options = {}) {
|
||||
if (!page) {
|
||||
throw new TypeError("Invalid page argument. Must be a Playwright page.");
|
||||
}
|
||||
@@ -41,7 +45,7 @@ export class BasePage {
|
||||
return page.route(url, (route) =>
|
||||
route.fulfill({
|
||||
...interceptConfig,
|
||||
path: `playwright/data/${jsonFilename}`,
|
||||
path: jsonFilename ? `playwright/data/${jsonFilename}` : undefined,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,8 @@ import { MockWebSocketHelper } from "../../helpers/MockWebSocketHelper";
|
||||
import BasePage from "./BasePage";
|
||||
|
||||
export class BaseWebSocketPage extends BasePage {
|
||||
/**
|
||||
* This should be called on `test.beforeEach`.
|
||||
*
|
||||
* @param {Page} page
|
||||
* @returns
|
||||
*/
|
||||
static async initWebSockets(page) {
|
||||
static async init(page) {
|
||||
await super.init(page);
|
||||
await MockWebSocketHelper.init(page);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,54 +3,62 @@ import { BaseWebSocketPage } from "./BaseWebSocketPage";
|
||||
|
||||
export class DashboardPage extends BaseWebSocketPage {
|
||||
static async init(page) {
|
||||
await BaseWebSocketPage.initWebSockets(page);
|
||||
await super.init(page);
|
||||
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockConfigFlags(page, ["disable-onboarding"]);
|
||||
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"logged-in-user/get-teams-default.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-font-variants?team-id=*",
|
||||
"workspace/get-font-variants-empty.json",
|
||||
);
|
||||
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"logged-in-user/get-projects-default.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"logged-in-user/get-team-members-your-penpot.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-team-users?team-id=*",
|
||||
"logged-in-user/get-team-users-single-user.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-unread-comment-threads?team-id=*",
|
||||
"logged-in-user/get-team-users-single-user.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-team-recent-files?team-id=*",
|
||||
"logged-in-user/get-team-recent-files-empty.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-profiles-for-file-comments",
|
||||
"workspace/get-profile-for-file-comments.json",
|
||||
);
|
||||
await BaseWebSocketPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-builtin-templates",
|
||||
"logged-in-user/get-built-in-templates-empty.json",
|
||||
);
|
||||
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in.json",
|
||||
);
|
||||
}
|
||||
|
||||
static anyTeamId = "c7ce0794-0992-8105-8004-38e630f40f6d";
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { BasePage } from "./BasePage";
|
||||
|
||||
export class LoginPage extends BasePage {
|
||||
static async init(page) {
|
||||
await super.init(page);
|
||||
}
|
||||
|
||||
constructor(page) {
|
||||
super(page);
|
||||
this.loginButton = page.getByRole("button", { name: "Continue" });
|
||||
|
||||
@@ -29,8 +29,13 @@ export class RegisterPage extends BasePage {
|
||||
);
|
||||
}
|
||||
|
||||
static async init(page) {
|
||||
await BasePage.init(page);
|
||||
}
|
||||
|
||||
static async initWithLoggedOutUser(page) {
|
||||
await this.mockRPC(page, "get-profile", "get-profile-anonymous.json");
|
||||
await BasePage.init(page);
|
||||
await BasePage.mockRPC(page, "get-profile", "get-profile-anonymous.json");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import { DashboardPage } from "./DashboardPage";
|
||||
|
||||
export class SubscriptionProfilePage extends DashboardPage {
|
||||
static async init(page) {
|
||||
await DashboardPage.initWebSockets(page);
|
||||
await super.init(page);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
await super.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
|
||||
@@ -4,16 +4,6 @@ export class ViewerPage extends BaseWebSocketPage {
|
||||
static anyFileId = "c7ce0794-0992-8105-8004-38f280443849";
|
||||
static anyPageId = "c7ce0794-0992-8105-8004-38f28044384a";
|
||||
|
||||
/**
|
||||
* This should be called on `test.beforeEach`.
|
||||
*
|
||||
* @param {Page} page
|
||||
* @returns
|
||||
*/
|
||||
static async init(page) {
|
||||
await BaseWebSocketPage.initWebSockets(page);
|
||||
}
|
||||
|
||||
async setupLoggedInUser() {
|
||||
await this.mockRPC(
|
||||
"get-profile",
|
||||
|
||||
@@ -35,8 +35,8 @@ export class WasmWorkspacePage extends WorkspacePage {
|
||||
return WasmWorkspacePage.mockConfigFlags(this.page, flags);
|
||||
}
|
||||
|
||||
constructor(page) {
|
||||
super(page);
|
||||
constructor(page, options) {
|
||||
super(page, options);
|
||||
this.canvas = page.getByTestId("canvas-wasm-shapes");
|
||||
}
|
||||
|
||||
@@ -54,6 +54,19 @@ export class WasmWorkspacePage extends WorkspacePage {
|
||||
await this.hideUI();
|
||||
}
|
||||
|
||||
async getRenderCount() {
|
||||
return this.page.evaluate(() => window.wasmRenderCount || 0);
|
||||
}
|
||||
|
||||
async waitForNextRender(previousCount = null) {
|
||||
const baseCount =
|
||||
previousCount === null ? await this.getRenderCount() : previousCount;
|
||||
await this.page.waitForFunction(
|
||||
(count) => (window.wasmRenderCount || 0) > count,
|
||||
baseCount,
|
||||
);
|
||||
}
|
||||
|
||||
async hideUI() {
|
||||
await this.page.keyboard.press("\\");
|
||||
await expect(this.pageName).not.toBeVisible();
|
||||
|
||||
@@ -35,45 +35,9 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
}
|
||||
|
||||
async waitForEditor() {
|
||||
return this.page.waitForSelector('[data-itype="editor"]');
|
||||
}
|
||||
|
||||
async waitForRoot() {
|
||||
return this.page.waitForSelector('[data-itype="root"]');
|
||||
}
|
||||
|
||||
async waitForParagraph(nth) {
|
||||
if (!nth) {
|
||||
return this.page.waitForSelector('[data-itype="paragraph"]');
|
||||
}
|
||||
return this.page.waitForSelector(
|
||||
`[data-itype="paragraph"]:nth-child(${nth})`,
|
||||
);
|
||||
}
|
||||
|
||||
async waitForParagraphStyle(nth, styleName) {
|
||||
const paragraph = await this.waitForParagraph(nth);
|
||||
return this.waitForStyle(paragraph, styleName);
|
||||
}
|
||||
|
||||
async waitForTextSpan(nth = 0) {
|
||||
if (!nth) {
|
||||
return this.page.waitForSelector('[data-itype="span"]');
|
||||
}
|
||||
return this.page.waitForSelector(
|
||||
`[data-itype="span"]:nth-child(${nth})`,
|
||||
);
|
||||
}
|
||||
|
||||
async waitForTextSpanContent(nth = 0) {
|
||||
const textSpan = await this.waitForTextSpan(nth);
|
||||
const textContent = await textSpan.textContent();
|
||||
return textContent;
|
||||
}
|
||||
|
||||
async waitForTextSpanStyle(nth, styleName) {
|
||||
const textSpan = await this.waitForTextSpan(nth);
|
||||
return this.waitForStyle(textSpan, styleName);
|
||||
const typographyInput =
|
||||
this.workspacePage.rightSidebar.getByLabel("Font Size");
|
||||
await expect(typographyInput).toBeVisible();
|
||||
}
|
||||
|
||||
async startEditing() {
|
||||
@@ -81,24 +45,27 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
return this.waitForEditor();
|
||||
}
|
||||
|
||||
stopEditing() {
|
||||
return this.page.keyboard.press("Escape");
|
||||
async stopEditing() {
|
||||
await this.page.keyboard.press("Escape");
|
||||
}
|
||||
|
||||
async moveToLeft(amount = 0) {
|
||||
for (let i = 0; i < amount; i++) {
|
||||
await this.page.keyboard.press("ArrowLeft");
|
||||
}
|
||||
await this.waitForIdle();
|
||||
}
|
||||
|
||||
async moveToRight(amount = 0) {
|
||||
for (let i = 0; i < amount; i++) {
|
||||
await this.page.keyboard.press("ArrowRight");
|
||||
}
|
||||
await this.waitForIdle();
|
||||
}
|
||||
|
||||
async moveFromStart(offset = 0) {
|
||||
await this.page.keyboard.press("ArrowLeft");
|
||||
await this.page.keyboard.press("Home");
|
||||
await this.waitForIdle();
|
||||
await this.moveToRight(offset);
|
||||
}
|
||||
|
||||
@@ -125,7 +92,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
await expect(locator).toBeVisible();
|
||||
await locator.focus();
|
||||
await locator.fill(`${newValue}`);
|
||||
await locator.blur();
|
||||
await this.page.keyboard.press("Enter");
|
||||
}
|
||||
|
||||
changeFontSize(newValue) {
|
||||
@@ -139,6 +106,10 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
changeLetterSpacing(newValue) {
|
||||
return this.changeNumericInput(this.letterSpacing, newValue);
|
||||
}
|
||||
|
||||
async waitForIdle() {
|
||||
await this.page.evaluate(() => new Promise((resolve) => globalThis.requestIdleCallback(resolve)));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -148,9 +119,9 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
* @returns
|
||||
*/
|
||||
static async init(page) {
|
||||
await BaseWebSocketPage.initWebSockets(page);
|
||||
await super.init(page);
|
||||
|
||||
await BaseWebSocketPage.mockRPCs(page, {
|
||||
await super.mockRPCs(page, {
|
||||
"get-profile": "logged-in-user/get-profile-logged-in.json",
|
||||
"get-team-users?file-id=*":
|
||||
"logged-in-user/get-team-users-single-user.json",
|
||||
@@ -198,10 +169,10 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
`[id="shape-00000000-0000-0000-0000-000000000000"]`,
|
||||
);
|
||||
this.toolbarOptions = page.getByTestId("toolbar-options");
|
||||
this.rectShapeButton = page.getByTestId("toolbar-options").getByRole("button", { name: "Rectangle" });
|
||||
this.ellipseShapeButton = page.getByTestId("toolbar-options").getByRole("button", { name: "Ellipse" });
|
||||
this.moveButton = page.getByTestId("toolbar-options").getByRole("button", { name: "Move" });
|
||||
this.boardButton = page.getByTestId("toolbar-options").getByRole("button", { name: "Board" });
|
||||
this.rectShapeButton = page.getByRole("button", { name: "Rectangle (R)" });
|
||||
this.ellipseShapeButton = page.getByRole("button", { name: "Ellipse (E)" });
|
||||
this.moveButton = page.getByRole("button", { name: "Move (V)" });
|
||||
this.boardButton = page.getByRole("button", { name: "Board (B)" });
|
||||
this.toggleToolbarButton = page.getByRole("button", {
|
||||
name: "Toggle toolbar",
|
||||
});
|
||||
@@ -317,7 +288,6 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
body,
|
||||
}),
|
||||
);
|
||||
// await this.mockRPC(/get\-file\?/, jsonFile);
|
||||
}
|
||||
|
||||
async mockGetAsset(regex, asset) {
|
||||
@@ -391,10 +361,12 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
const timeToWait = options?.timeToWait ?? 100;
|
||||
await this.page.keyboard.press("T");
|
||||
await this.page.waitForTimeout(timeToWait);
|
||||
|
||||
const layersCountBefore = await this.layers.getByTestId("layer-row").count();
|
||||
await this.clickAndMove(x1, y1, x2, y2);
|
||||
await expect(this.page.getByTestId("text-editor")).toBeVisible();
|
||||
|
||||
if (initialText) {
|
||||
await this.waitForSelectedShapeName("Text");
|
||||
await this.page.keyboard.type(initialText);
|
||||
}
|
||||
}
|
||||
@@ -432,6 +404,8 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
return content !== "";
|
||||
}, { timeout: 1000 });
|
||||
|
||||
await this.page.waitForTimeout(3000);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -445,7 +419,8 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
await this.viewport.click({ button: "right" });
|
||||
return this.page.getByText("Paste", { exact: true }).click();
|
||||
}
|
||||
return this.page.keyboard.press("ControlOrMeta+V");
|
||||
await this.page.keyboard.press("ControlOrMeta+V");
|
||||
await this.page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
async panOnViewportAt(x, y, width, height) {
|
||||
@@ -494,10 +469,23 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
|
||||
async expectSelectedLayer(name) {
|
||||
await expect(
|
||||
this.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ has: this.page.getByText(name) }),
|
||||
).toHaveClass(/selected/);
|
||||
this.layers.getByRole("checkbox", { name, checked: true }),
|
||||
).toBeVisible();
|
||||
}
|
||||
|
||||
async getSelectedShapeName() {
|
||||
const selectedLayer = this.layers
|
||||
.getByRole("checkbox", { checked: true })
|
||||
.first();
|
||||
await selectedLayer.waitFor({ state: "visible" });
|
||||
return (await selectedLayer.innerText()).trim();
|
||||
}
|
||||
|
||||
async waitForSelectedShapeName(expectedName) {
|
||||
const selectedLayer = this.layers
|
||||
.getByRole("checkbox", { checked: true })
|
||||
.first();
|
||||
await expect(selectedLayer).toHaveText(expectedName);
|
||||
}
|
||||
|
||||
async expectHiddenToolbarOptions() {
|
||||
|
||||
@@ -243,6 +243,46 @@ test("Renders a file with a closed path shape with multiple segments using strok
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders solid shadows after select all and zoom to selected", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-solid-shadows.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
id: "93113137-fe66-80fb-8007-99ca9fd96841",
|
||||
pageId: "93113137-fe66-80fb-8007-99ca9fd96842",
|
||||
});
|
||||
await workspace.waitForFirstRender();
|
||||
|
||||
await workspace.viewport.click();
|
||||
await page.keyboard.press("ControlOrMeta+A");
|
||||
const previousRenderCount = await workspace.getRenderCount();
|
||||
await page.keyboard.press("f");
|
||||
await workspace.waitForNextRender(previousRenderCount);
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders strokes with solid shadows", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-solid-strokes-shadows.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
id: "93113137-fe66-80fb-8007-99cfd5cbf361",
|
||||
pageId: "93113137-fe66-80fb-8007-99cfd5cbf362",
|
||||
});
|
||||
await workspace.waitForFirstRender();
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders a file with paths and svg attrs", async ({ page }) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -356,3 +396,63 @@ test("Renders shapes with multiple fills and blur", async ({
|
||||
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Keeps component visible when focusing after creating it", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-not-empty.json");
|
||||
await workspace.mockRPC(
|
||||
"update-file?id=*",
|
||||
"workspace/update-file-create-rect.json",
|
||||
);
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "6191cd35-bb1f-81f7-8004-7cc63d087374",
|
||||
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
||||
});
|
||||
await workspace.waitForFirstRender();
|
||||
|
||||
await workspace.clickLayers();
|
||||
await workspace.clickLeafLayer("Rectangle");
|
||||
await page.keyboard.press("ControlOrMeta+k");
|
||||
|
||||
const componentLayer = workspace.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ has: page.getByTestId("icon-component") })
|
||||
.first();
|
||||
await expect(componentLayer).toBeVisible();
|
||||
await componentLayer.click();
|
||||
|
||||
const previousRenderCount = await workspace.getRenderCount();
|
||||
await page.keyboard.press("f");
|
||||
await workspace.waitForNextRender(previousRenderCount);
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Check inner stroke artifacts", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-inner-strokes-artifacts.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
id: "effcbebc-b8c8-802f-8007-9a0b2e2c863f",
|
||||
pageId: "effcbebc-b8c8-802f-8007-9a0b2e2c8640",
|
||||
});
|
||||
await workspace.waitForFirstRenderWithoutUI();
|
||||
|
||||
const previousRenderCount = await workspace.getRenderCount();
|
||||
await page.keyboard.press("ControlOrMeta++");
|
||||
await workspace.waitForNextRender(previousRenderCount);
|
||||
|
||||
// Stricter comparison: artifacts are very subtle
|
||||
await expect(workspace.canvas).toHaveScreenshot({
|
||||
maxDiffPixelRatio: 0,
|
||||
threshold: 0.1,
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 348 KiB After Width: | Height: | Size: 348 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 140 KiB |
@@ -3,11 +3,6 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test.describe("Dashboard Deleted Page", () => {
|
||||
|
||||
@@ -3,11 +3,6 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test("BUG 10421 - Fix libraries context menu", async ({ page }) => {
|
||||
|
||||
@@ -3,11 +3,6 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test("BUG 12359 - Selected invitations count is not pluralized", async ({
|
||||
|
||||
@@ -3,11 +3,7 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
|
||||
@@ -3,11 +3,6 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test("Dashboad page has title ", async ({ page }) => {
|
||||
|
||||
@@ -189,8 +189,8 @@ test("BUG 7760 - Layout losing properties when changing parents", async ({
|
||||
await workspacePage.clickLeafLayer("Flex Board");
|
||||
|
||||
// Move the first board into the second
|
||||
const hAuto = await workspacePage.page.getByTestId("behaviour-h-auto");
|
||||
const vAuto = await workspacePage.page.getByTestId("behaviour-v-auto");
|
||||
const hAuto = await workspacePage.page.getByTitle("Fit content (Horizontal)");
|
||||
const vAuto = await workspacePage.page.getByTitle("Fit content (Vertical)");
|
||||
|
||||
await expect(vAuto.locator("input")).toBeChecked();
|
||||
await expect(hAuto.locator("input")).toBeChecked();
|
||||
|
||||
@@ -2,6 +2,8 @@ import { test, expect } from "@playwright/test";
|
||||
import { LoginPage } from "../pages/LoginPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await LoginPage.init(page);
|
||||
|
||||
const login = new LoginPage(page);
|
||||
await login.initWithLoggedOutUser();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import OnboardingPage from "../pages/OnboardingPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockConfigFlags(page, ["enable-onboarding"]);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
|
||||
@@ -3,11 +3,6 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test("Navigate to penpot changelog from profile menu", async ({ page }) => {
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { Clipboard } from "../../helpers/Clipboard";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
|
||||
const timeToWait = 100;
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page, context }) => {
|
||||
await Clipboard.enable(context, Clipboard.Permission.ALL);
|
||||
|
||||
await WorkspacePage.init(page);
|
||||
await WorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]);
|
||||
await WasmWorkspacePage.init(page);
|
||||
await WasmWorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ context }) => {
|
||||
@@ -17,39 +15,36 @@ test.afterEach(async ({ context }) => {
|
||||
|
||||
test("Create a new text shape", async ({ page }) => {
|
||||
const initialText = "Lorem ipsum";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.goToWorkspace();
|
||||
await workspace.createTextShape(190, 150, 300, 200, initialText);
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe(initialText);
|
||||
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await workspace.waitForSelectedShapeName(initialText);
|
||||
});
|
||||
|
||||
test("Create a new text shape from pasting text", async ({ page, context }) => {
|
||||
const textToPaste = "Lorem ipsum";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC("update-file?id=*", "text-editor/update-file.json");
|
||||
await workspace.goToWorkspace();
|
||||
await workspace.moveButton.click();
|
||||
|
||||
await Clipboard.writeText(page, textToPaste);
|
||||
|
||||
await workspace.clickAt(190, 150);
|
||||
await workspace.paste("keyboard");
|
||||
|
||||
await page.waitForTimeout(timeToWait);
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe(textToPaste);
|
||||
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await expect(workspace.layers.getByText(textToPaste)).toBeVisible();
|
||||
});
|
||||
|
||||
test("Create a new text shape from pasting text using context menu", async ({
|
||||
@@ -57,27 +52,26 @@ test("Create a new text shape from pasting text using context menu", async ({
|
||||
context,
|
||||
}) => {
|
||||
const textToPaste = "Lorem ipsum";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.goToWorkspace();
|
||||
await workspace.moveButton.click();
|
||||
|
||||
await Clipboard.writeText(page, textToPaste);
|
||||
|
||||
await workspace.clickAt(190, 150);
|
||||
await workspace.paste("context-menu");
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe(textToPaste);
|
||||
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await expect(workspace.layers.getByText(textToPaste)).toBeVisible();
|
||||
});
|
||||
|
||||
test("Update an already created text shape by appending text", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -87,15 +81,14 @@ test("Update an already created text shape by appending text", async ({
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.moveFromEnd(0);
|
||||
await page.keyboard.type(" dolor sit amet");
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Lorem ipsum dolor sit amet");
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet");
|
||||
});
|
||||
|
||||
test("Update an already created text shape by prepending text", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -105,15 +98,14 @@ test("Update an already created text shape by prepending text", async ({
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.moveFromStart(0);
|
||||
await page.keyboard.type("Dolor sit amet ");
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Dolor sit amet Lorem ipsum");
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Dolor sit amet Lorem ipsum");
|
||||
});
|
||||
|
||||
test.skip("Update an already created text shape by inserting text in between", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -123,9 +115,8 @@ test.skip("Update an already created text shape by inserting text in between", a
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.moveFromStart(5);
|
||||
await page.keyboard.type(" dolor sit amet");
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Lorem dolor sit amet ipsum");
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Lorem dolor sit amet ipsum");
|
||||
});
|
||||
|
||||
test("Update a new text shape appending text by pasting text", async ({
|
||||
@@ -133,7 +124,7 @@ test("Update a new text shape appending text by pasting text", async ({
|
||||
context,
|
||||
}) => {
|
||||
const textToPaste = " dolor sit amet";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -146,9 +137,8 @@ test("Update a new text shape appending text by pasting text", async ({
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.moveFromEnd();
|
||||
await workspace.paste("keyboard");
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Lorem ipsum dolor sit amet");
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Lorem ipsum dolor sit amet");
|
||||
});
|
||||
|
||||
test.skip("Update a new text shape prepending text by pasting text", async ({
|
||||
@@ -156,7 +146,7 @@ test.skip("Update a new text shape prepending text by pasting text", async ({
|
||||
context,
|
||||
}) => {
|
||||
const textToPaste = "Dolor sit amet ";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -169,16 +159,17 @@ test.skip("Update a new text shape prepending text by pasting text", async ({
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.moveFromStart();
|
||||
await workspace.paste("keyboard");
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Dolor sit amet Lorem ipsum");
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Update a new text shape replacing (starting) text with pasted text", async ({
|
||||
page,
|
||||
}) => {
|
||||
const textToPaste = "Dolor sit amet";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -192,17 +183,15 @@ test("Update a new text shape replacing (starting) text with pasted text", async
|
||||
|
||||
await workspace.paste("keyboard");
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Dolor sit amet ipsum");
|
||||
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Dolor sit amet ipsum");
|
||||
});
|
||||
|
||||
test("Update a new text shape replacing (ending) text with pasted text", async ({
|
||||
page,
|
||||
}) => {
|
||||
const textToPaste = "dolor sit amet";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -216,17 +205,15 @@ test("Update a new text shape replacing (ending) text with pasted text", async (
|
||||
|
||||
await workspace.paste("keyboard");
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Lorem dolor sit amet");
|
||||
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Lorem dolor sit amet");
|
||||
});
|
||||
|
||||
test("Update a new text shape replacing (in between) text with pasted text", async ({
|
||||
page,
|
||||
}) => {
|
||||
const textToPaste = "dolor sit amet";
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -240,16 +227,14 @@ test("Update a new text shape replacing (in between) text with pasted text", asy
|
||||
|
||||
await workspace.paste("keyboard");
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Lordolor sit ametsum");
|
||||
|
||||
await workspace.textEditor.stopEditing();
|
||||
await workspace.waitForSelectedShapeName("Lordolor sit ametsum");
|
||||
});
|
||||
|
||||
test("Update text font size selecting a part of it (starting)", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -260,18 +245,16 @@ test("Update text font size selecting a part of it (starting)", async ({
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.selectFromStart(5);
|
||||
await workspace.textEditor.changeFontSize(36);
|
||||
|
||||
const textContent1 = await workspace.textEditor.waitForTextSpanContent(1);
|
||||
expect(textContent1).toBe("Lorem");
|
||||
const textContent2 = await workspace.textEditor.waitForTextSpanContent(2);
|
||||
expect(textContent2).toBe(" ipsum");
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test.skip("Update text line height selecting a part of it (starting)", async ({
|
||||
test("Update text line height selecting a part of it (starting)", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -281,24 +264,17 @@ test.skip("Update text line height selecting a part of it (starting)", async ({
|
||||
await workspace.clickLeafLayer("Lorem ipsum");
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.selectFromStart(5);
|
||||
await workspace.textEditor.changeLineHeight(1.4);
|
||||
|
||||
const lineHeight = await workspace.textEditor.waitForParagraphStyle(
|
||||
1,
|
||||
"line-height",
|
||||
);
|
||||
expect(lineHeight).toBe("1.4");
|
||||
|
||||
const textContent = await workspace.textEditor.waitForTextSpanContent();
|
||||
expect(textContent).toBe("Lorem ipsum");
|
||||
|
||||
await workspace.textEditor.changeLineHeight(4.4);
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test.skip("Update text letter spacing selecting a part of it (starting)", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page, {
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
await workspace.setupEmptyFile();
|
||||
@@ -309,16 +285,14 @@ test.skip("Update text letter spacing selecting a part of it (starting)", async
|
||||
await workspace.textEditor.startEditing();
|
||||
await workspace.textEditor.selectFromStart(5);
|
||||
await workspace.textEditor.changeLetterSpacing(10);
|
||||
|
||||
const textContent1 = await workspace.textEditor.waitForTextSpanContent(1);
|
||||
expect(textContent1).toBe("Lorem");
|
||||
const textContent2 = await workspace.textEditor.waitForTextSpanContent(2);
|
||||
expect(textContent2).toBe(" ipsum");
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
await workspace.hideUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("BUG 11552 - Apply styles to the current caret", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("text-editor/get-file-11552.json");
|
||||
await workspace.mockRPC(
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -1493,10 +1493,8 @@ test.describe("Tokens - creation", () => {
|
||||
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||
await nameField.fill(newTokenTitle);
|
||||
|
||||
const referenceTabButton = tokensUpdateCreateModal.getByRole("button", {
|
||||
name: "Use a reference",
|
||||
});
|
||||
referenceTabButton.click();
|
||||
const referenceTabButton = tokensUpdateCreateModal.getByTestId("reference-opt");
|
||||
await referenceTabButton.click();
|
||||
|
||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||
name: "Reference",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
import { BaseWebSocketPage } from "../pages/BaseWebSocketPage";
|
||||
import { Clipboard } from "../../helpers/Clipboard";
|
||||
|
||||
@@ -7,7 +7,7 @@ test.beforeEach(async ({ page, context }) => {
|
||||
await Clipboard.enable(context, Clipboard.Permission.ALL);
|
||||
|
||||
await WasmWorkspacePage.init(page);
|
||||
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-variants.json");
|
||||
await WasmWorkspacePage.mockConfigFlags(page, ["enable-feature-variants"]);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ context }) => {
|
||||
@@ -383,24 +383,26 @@ test("User cut paste a component with path inside a variant", async ({
|
||||
|
||||
const variant = await findVariant(workspacePage, 0);
|
||||
|
||||
//Create a component
|
||||
// Create a component
|
||||
await workspacePage.ellipseShapeButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||
await workspacePage.clickLeafLayer("Ellipse");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
await workspacePage.page.waitForTimeout(3000);
|
||||
|
||||
//Rename the component
|
||||
// Rename the component
|
||||
await workspacePage.layers.getByText("Ellipse").dblclick();
|
||||
await workspacePage.page
|
||||
.getByTestId("layer-item")
|
||||
.getByRole("textbox")
|
||||
.pressSequentially("button / hover");
|
||||
await workspacePage.page.keyboard.press("Enter");
|
||||
await workspacePage.page.waitForTimeout(3000);
|
||||
|
||||
//Cut the component
|
||||
// Cut the component
|
||||
await workspacePage.cut("keyboard");
|
||||
|
||||
//Paste the component inside the variant
|
||||
// Paste the component inside the variant
|
||||
await variant.container.click();
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
@@ -427,6 +429,7 @@ test("User drag and drop a component with path inside a variant", async ({
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||
await workspacePage.clickLeafLayer("Ellipse");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
await workspacePage.page.waitForTimeout(3000);
|
||||
|
||||
//Rename the component
|
||||
await workspacePage.layers.getByText("Ellipse").dblclick();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
import { presenceFixture } from "../../data/workspace/ws-notifications";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WasmWorkspacePage.init(page);
|
||||
@@ -106,9 +105,37 @@ test("BUG 11006 - Fix history panel shortcut", async ({ page }) => {
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
await page.keyboard.press("Control+Alt+h");
|
||||
await page.keyboard.press("ControlOrMeta+Alt+h");
|
||||
|
||||
await expect(
|
||||
workspacePage.rightSidebar.getByText("There are no versions yet"),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("BUG 13385 - Fix viewport not updating when restoring version", async ({ page }) => {
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockGetFile("workspace/get-file-13385.json");
|
||||
await workspacePage.mockRPC("get-profiles-for-file-comments?file-id=*", "workspace/get-profiles-for-file-comments-13385.json");
|
||||
|
||||
// navigate to workspace and check that the circle shape is not there
|
||||
await workspacePage.goToWorkspace();
|
||||
await expect(workspacePage.layers.getByText("Ellipse")).not.toBeVisible();
|
||||
|
||||
// mock network requests to restore the version
|
||||
await workspacePage.mockGetFile("workspace/get-file-13385-2.json");
|
||||
await workspacePage.mockRPC("get-file-snapshots?file-id=*", "workspace/get-file-snapshots-13385.json");
|
||||
await workspacePage.mockRPC("restore-file-snapshot", "", {
|
||||
status: 204,
|
||||
});
|
||||
|
||||
// request to restore the version
|
||||
await workspacePage.rightSidebar.getByRole("button", { name: "History" }).click();
|
||||
await workspacePage.rightSidebar.getByRole("button", { name: "Open version menu" }).click();
|
||||
await workspacePage.rightSidebar.getByRole("button", { name: "Restore" }).click();
|
||||
// confirm modal
|
||||
await workspacePage.page.getByRole("button", { name: /Restore/i }).click();
|
||||
|
||||
// assert that the circle shape exists
|
||||
await expect(workspacePage.layers.getByText("Ellipse")).toBeVisible();
|
||||
});
|
||||
@@ -23,4 +23,63 @@ test("BUG 13305 - Fix resize board to fit content", async ({ page }) => {
|
||||
await expect(workspacePage.rightSidebar.getByTitle("Height").getByRole("textbox")).toHaveValue("630");
|
||||
await expect(workspacePage.rightSidebar.getByTitle("X axis").getByRole("textbox")).toHaveValue("110");
|
||||
await expect(workspacePage.rightSidebar.getByTitle("Y axis").getByRole("textbox")).toHaveValue("110");
|
||||
});
|
||||
});
|
||||
|
||||
test("BUG 13382 - Fix problem with flex layout", async ({ page }) => {
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockGetFile("workspace/get-file-13382.json");
|
||||
|
||||
await workspacePage.mockRPC(
|
||||
"get-file-fragment?file-id=*&fragment-id=*",
|
||||
"workspace/get-file-13382-fragment.json",
|
||||
);
|
||||
|
||||
await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-empty.json");
|
||||
|
||||
await workspacePage.goToWorkspace({
|
||||
fileId: "52c4e771-3853-8190-8007-9506c70e8100",
|
||||
pageId: "ecb0cfd0-0f0b-81f7-8007-950628f9665b",
|
||||
});
|
||||
|
||||
await workspacePage.clickToggableLayer("A");
|
||||
await workspacePage.clickToggableLayer("B");
|
||||
await workspacePage.clickToggableLayer("C");
|
||||
await workspacePage.clickLeafLayer("R2");
|
||||
|
||||
const heightText = workspacePage.rightSidebar.getByTitle("Height").getByPlaceholder('--');
|
||||
await heightText.fill("200");
|
||||
await heightText.press("Enter");
|
||||
|
||||
await workspacePage.clickLeafLayer("B");
|
||||
await expect(workspacePage.rightSidebar.getByTitle("Height").getByRole("textbox")).toHaveValue("340");
|
||||
|
||||
});
|
||||
|
||||
test("BUG 13468 - Fix problem with flex propagation", async ({ page }) => {
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockGetFile("workspace/get-file-13468.json");
|
||||
|
||||
await workspacePage.mockRPC(
|
||||
"get-file-fragment?file-id=*&fragment-id=*",
|
||||
"workspace/get-file-13468-fragment.json",
|
||||
);
|
||||
|
||||
await workspacePage.mockRPC("update-file?id=*", "workspace/update-file-empty.json");
|
||||
|
||||
await workspacePage.goToWorkspace({
|
||||
fileId: "3a4d7ec7-c391-8146-8007-9a05c41da6b9",
|
||||
pageId: "95b23c15-79f9-81ba-8007-99d81b5290dd",
|
||||
});
|
||||
0
|
||||
await workspacePage.clickToggableLayer("Parent");
|
||||
await workspacePage.clickToggableLayer("Container");
|
||||
|
||||
await workspacePage.sidebar.getByRole('button', { name: 'Show' }).click();
|
||||
|
||||
await workspacePage.clickLeafLayer("Container");
|
||||
await expect(workspacePage.rightSidebar.getByTitle("Height").getByRole("textbox")).toHaveValue("76");
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -353,33 +353,24 @@ test("Copy/paste properties", async ({ page, context }) => {
|
||||
await page.getByText("Copy/Paste as").hover();
|
||||
await page.getByText("Paste properties").click();
|
||||
|
||||
await page
|
||||
.getByTestId("layer-item")
|
||||
.getByText("Rectangle")
|
||||
.first()
|
||||
.click({ button: "right" });
|
||||
await page.getByText("Rectangle").first().click({ button: "right" });
|
||||
await page.getByText("Copy/Paste as").hover();
|
||||
await page.getByText("Paste properties").click();
|
||||
|
||||
await page.getByText("Board").nth(2).click({ button: "right" });
|
||||
await page.getByText("Copy/Paste as").hover();
|
||||
await page.getByText("Paste properties").click();
|
||||
|
||||
await page
|
||||
.getByTestId("layer-item")
|
||||
.getByText("Board")
|
||||
.locator("div")
|
||||
.filter({ hasText: "Path" })
|
||||
.nth(1)
|
||||
.click({ button: "right" });
|
||||
await page.getByText("Copy/Paste as").hover();
|
||||
await page.getByText("Paste properties").click();
|
||||
|
||||
await page
|
||||
.getByTestId("layer-item")
|
||||
.getByText("Path")
|
||||
.click({ button: "right" });
|
||||
await page.getByText("Copy/Paste as").hover();
|
||||
await page.getByText("Paste properties").click();
|
||||
|
||||
await page
|
||||
.getByTestId("layer-item")
|
||||
.getByText("Ellipse")
|
||||
.click({ button: "right" });
|
||||
await page.getByText("Ellipse").click({ button: "right" });
|
||||
await page.getByText("Copy/Paste as").hover();
|
||||
await page.getByText("Paste properties").click();
|
||||
});
|
||||
@@ -492,3 +483,25 @@ test("Bug 8371 - Flatten option is not visible in context menu", async ({
|
||||
.filter({ visible: true }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("BUG 13415 - Grid layout overlay is not removed when deleting a board", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.mockGetFile("workspace/get-file-13415.json");
|
||||
await workspacePage.mockRPC(
|
||||
"update-file?id=*",
|
||||
"workspace/update-file-13415.json",
|
||||
);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.clickLeafLayer("Board");
|
||||
|
||||
const currentRenderCount = await workspacePage.getRenderCount();
|
||||
await workspacePage.page.keyboard.press("Delete");
|
||||
|
||||
await workspacePage.waitForNextRender(currentRenderCount);
|
||||
await workspacePage.hideUI();
|
||||
await expect(workspacePage.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
@@ -3,11 +3,6 @@ import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test("User goes to an empty dashboard", async ({ page }) => {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { test, expect } from "@playwright/test";
|
||||
import { LoginPage } from "../pages/LoginPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await LoginPage.init(page);
|
||||
|
||||
const login = new LoginPage(page);
|
||||
await login.initWithLoggedOutUser();
|
||||
await login.page.goto("/#/auth/login");
|
||||
|
||||
47
frontend/pnpm-lock.yaml
generated
@@ -20,8 +20,8 @@ importers:
|
||||
specifier: workspace:./packages/mousetrap
|
||||
version: link:packages/mousetrap
|
||||
'@penpot/plugins-runtime':
|
||||
specifier: 1.4.2
|
||||
version: 1.4.2
|
||||
specifier: link:../plugins/dist/plugins-runtime
|
||||
version: link:../plugins/dist/plugins-runtime
|
||||
'@penpot/svgo':
|
||||
specifier: penpot/svgo#v3.2
|
||||
version: svgo@https://codeload.github.com/penpot/svgo/tar.gz/8c9b0e32e9cb5f106085260bd9375f3c91a5010b
|
||||
@@ -581,15 +581,6 @@ packages:
|
||||
'@dabh/diagnostics@2.0.8':
|
||||
resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
|
||||
|
||||
'@endo/cache-map@1.1.0':
|
||||
resolution: {integrity: sha512-owFGshs/97PDw9oguZqU/px8Lv1d0KjAUtDUiPwKHNXRVUE/jyettEbRoTbNJR1OaI8biMn6bHr9kVJsOh6dXw==}
|
||||
|
||||
'@endo/env-options@1.1.11':
|
||||
resolution: {integrity: sha512-p9OnAPsdqoX4YJsE98e3NBVhIr2iW9gNZxHhAI2/Ul5TdRfoOViItzHzTqrgUVopw6XxA1u1uS6CykLMDUxarA==}
|
||||
|
||||
'@endo/immutable-arraybuffer@1.1.2':
|
||||
resolution: {integrity: sha512-u+NaYB2aqEugQ3u7w3c5QNkPogf8q/xGgsPaqdY6pUiGWtYiTiFspKFcha6+oeZhWXWQ23rf0KrUq0kfuzqYyQ==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1258,12 +1249,6 @@ packages:
|
||||
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@penpot/plugin-types@1.4.2':
|
||||
resolution: {integrity: sha512-O8wU6RSYE8bIVU7g8cSTYi32ppxs3R13dq7X3Nn9tmDaJjBOKOBpVLuoRPIp3fJC65fv8/7om0sdrtFoL5v19g==}
|
||||
|
||||
'@penpot/plugins-runtime@1.4.2':
|
||||
resolution: {integrity: sha512-y9TDZOnb96JBW9E33dHKpmTMeAPXLtHDIZruUVjtM8hBJWZK7RCv+vAGDGxeoZJC/OB2YAHrCZG+mukePBzcuQ==}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -4636,9 +4621,6 @@ packages:
|
||||
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
ses@1.14.0:
|
||||
resolution: {integrity: sha512-T07hNgOfVRTLZGwSS50RnhqrG3foWP+rM+Q5Du4KUQyMLFI3A8YA4RKl0jjZzhihC1ZvDGrWi/JMn4vqbgr/Jg==}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5499,9 +5481,6 @@ packages:
|
||||
peerDependencies:
|
||||
zod: ^3.25.0 || ^4.0.0
|
||||
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
zod@4.3.6:
|
||||
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
|
||||
|
||||
@@ -5775,12 +5754,6 @@ snapshots:
|
||||
enabled: 2.0.0
|
||||
kuler: 2.0.0
|
||||
|
||||
'@endo/cache-map@1.1.0': {}
|
||||
|
||||
'@endo/env-options@1.1.11': {}
|
||||
|
||||
'@endo/immutable-arraybuffer@1.1.2': {}
|
||||
|
||||
'@esbuild/aix-ppc64@0.21.5':
|
||||
optional: true
|
||||
|
||||
@@ -6297,14 +6270,6 @@ snapshots:
|
||||
'@parcel/watcher-win32-x64': 2.5.6
|
||||
optional: true
|
||||
|
||||
'@penpot/plugin-types@1.4.2': {}
|
||||
|
||||
'@penpot/plugins-runtime@1.4.2':
|
||||
dependencies:
|
||||
'@penpot/plugin-types': 1.4.2
|
||||
ses: 1.14.0
|
||||
zod: 3.25.76
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
optional: true
|
||||
|
||||
@@ -10000,12 +9965,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ses@1.14.0:
|
||||
dependencies:
|
||||
'@endo/cache-map': 1.1.0
|
||||
'@endo/env-options': 1.1.11
|
||||
'@endo/immutable-arraybuffer': 1.1.2
|
||||
|
||||
set-function-length@1.2.2:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
@@ -10974,6 +10933,4 @@ snapshots:
|
||||
dependencies:
|
||||
zod: 4.3.6
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zod@4.3.6: {}
|
||||
|
||||
@@ -244,6 +244,13 @@
|
||||
--assets-component-second-border-selected: var(--color-background-primary);
|
||||
--assets-component-hightlight: var(--color-accent-secondary);
|
||||
|
||||
--radio-btns-background-color: var(--color-background-tertiary);
|
||||
--radio-btn-background-color-selected: var(--color-background-quaternary);
|
||||
--radio-btn-foreground-color: var(--color-foreground-secondary);
|
||||
--radio-btn-foreground-color-selected: var(--color-accent-primary);
|
||||
--radio-btn-border-color: var(--color-background-tertiary);
|
||||
--radio-btn-border-color-selected: var(--color-background-quaternary);
|
||||
|
||||
--library-name-foreground-color: var(--color-foreground-primary);
|
||||
--library-content-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
@@ -416,6 +423,13 @@
|
||||
--tab-border-color: var(--color-background-tertiary);
|
||||
--tab-border-color-selected: var(--color-background-secondary);
|
||||
|
||||
--radio-btns-background-color: var(--color-background-tertiary);
|
||||
--radio-btn-background-color-selected: var(--color-background-primary);
|
||||
--radio-btn-foreground-color: var(--color-foreground-secondary);
|
||||
--radio-btn-foreground-color-selected: var(--color-accent-primary);
|
||||
--radio-btn-border-color: var(--color-background-tertiary);
|
||||
--radio-btn-border-color-selected: var(--color-background-secondary);
|
||||
|
||||
--button-icon-background-color-selected: var(--color-background-primary);
|
||||
--button-icon-border-color-selected: var(--color-background-secondary);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<meta name="twitter:creator" content="@penpotapp">
|
||||
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)">
|
||||
<link id="theme" href="css/main.css?version={{& version_tag}}" rel="stylesheet" type="text/css" />
|
||||
<link href="css/ui.css?ts={{& ts}}" rel="stylesheet" type="text/css" />
|
||||
<link href="css/ui.css?ts={{& version_tag}}" rel="stylesheet" type="text/css" />
|
||||
{{#isDebug}}
|
||||
<link href="css/debug.css?version={{& version_tag}}" rel="stylesheet" type="text/css" />
|
||||
{{/isDebug}}
|
||||
|
||||
@@ -4,4 +4,9 @@ TARGET=${1:-app};
|
||||
|
||||
set -ex
|
||||
|
||||
exec pnpm run watch:$TARGET
|
||||
rm -rf node_modules;
|
||||
|
||||
corepack enable;
|
||||
corepack install;
|
||||
pnpm install;
|
||||
pnpm run watch:$TARGET
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
Structured tokens are non-primitive token types like `typography` or `box-shadow`."
|
||||
[^js token-symbol]
|
||||
(if (instance? js/Array (.-value token-symbol))
|
||||
(mapv structured-token->penpot-map (.-value token-symbol))
|
||||
(mapv tokenscript-symbols->penpot-unit (.-value token-symbol))
|
||||
(let [entries (es6-iterator-seq (.entries (.-value token-symbol)))]
|
||||
(into {} (map (fn [[k v :as V]]
|
||||
[(keyword k) (tokenscript-symbols->penpot-unit v)])
|
||||
@@ -88,7 +88,7 @@
|
||||
(defn tokenscript-symbols->penpot-unit [^js v]
|
||||
(cond
|
||||
(structured-token? v) (structured-token->penpot-map v)
|
||||
(list-symbol? v) (tokenscript-symbols->penpot-unit (.nth 1 v))
|
||||
(list-symbol? v) (structured-token->penpot-map v)
|
||||
(color-symbol? v) (.-value (.to v "hex"))
|
||||
(rem-number-with-unit? v) (rem->px v)
|
||||
:else (.-value v)))
|
||||
|
||||
@@ -222,9 +222,16 @@
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :thumbnails thumbnails)
|
||||
(update :files assoc file-id file)))))
|
||||
(let [pending-version-id (:workspace-pending-file-version-id state)
|
||||
state (-> state
|
||||
(assoc :thumbnails thumbnails)
|
||||
(update :files assoc file-id file)
|
||||
(dissoc :workspace-pending-file-version-id))]
|
||||
(cond-> state
|
||||
(some? pending-version-id)
|
||||
(assoc :workspace-file-version-id pending-version-id)
|
||||
(nil? pending-version-id)
|
||||
(dissoc :workspace-file-version-id))))))
|
||||
|
||||
(defn zoom-to-frame
|
||||
[]
|
||||
@@ -280,192 +287,197 @@
|
||||
(wasm.api/process-object shape))))))
|
||||
|
||||
(defn initialize-workspace
|
||||
[team-id file-id]
|
||||
(assert (uuid? team-id) "expected valud uuid for `team-id`")
|
||||
(assert (uuid? file-id) "expected valud uuid for `file-id`")
|
||||
([team-id file-id]
|
||||
(initialize-workspace team-id file-id nil))
|
||||
([team-id file-id version-id]
|
||||
(assert (uuid? team-id) "expected valud uuid for `team-id`")
|
||||
(assert (uuid? file-id) "expected valud uuid for `file-id`")
|
||||
|
||||
(ptk/reify ::initialize-workspace
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :recent-colors (:recent-colors storage/user))
|
||||
(assoc :recent-fonts (:recent-fonts storage/user))
|
||||
(assoc :current-file-id file-id)
|
||||
(assoc :workspace-presence {})))
|
||||
(ptk/reify ::initialize-workspace
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :recent-colors (:recent-colors storage/user))
|
||||
(assoc :recent-fonts (:recent-fonts storage/user))
|
||||
(assoc :current-file-id file-id)
|
||||
(assoc :workspace-presence {})
|
||||
;; Store pending version-id; bundle-fetched will set workspace-file-version-id
|
||||
;; when the new bundle is applied so the viewport re-inits with new data
|
||||
(assoc :workspace-pending-file-version-id version-id)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
|
||||
rparams (rt/get-params state)
|
||||
features (features/get-enabled-features state team-id)
|
||||
render-wasm? (contains? features "render-wasm/v1")]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
|
||||
rparams (rt/get-params state)
|
||||
features (features/get-enabled-features state team-id)
|
||||
render-wasm? (contains? features "render-wasm/v1")]
|
||||
|
||||
(log/debug :hint "initialize-workspace"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
(log/debug :hint "initialize-workspace"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
;; Fetch all essential data that should be loaded before the file
|
||||
(rx/merge
|
||||
(if ^boolean render-wasm?
|
||||
(->> (rx/from @wasm/module)
|
||||
(rx/filter true?)
|
||||
(rx/tap (fn [_]
|
||||
(let [event (ug/event "penpot:wasm:loaded")]
|
||||
(ug/dispatch! event))))
|
||||
(rx/ignore))
|
||||
(rx/empty))
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
;; Fetch all essential data that should be loaded before the file
|
||||
(rx/merge
|
||||
(if ^boolean render-wasm?
|
||||
(->> (rx/from @wasm/module)
|
||||
(rx/filter true?)
|
||||
(rx/tap (fn [_]
|
||||
(let [event (ug/event "penpot:wasm:loaded")]
|
||||
(ug/dispatch! event))))
|
||||
(rx/ignore))
|
||||
(rx/empty))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
|
||||
(rx/of (ntf/hide)
|
||||
(dcmt/retrieve-comment-threads file-id)
|
||||
(dcmt/fetch-profiles)
|
||||
(df/fetch-fonts team-id)))
|
||||
(rx/of (ntf/hide)
|
||||
(dcmt/retrieve-comment-threads file-id)
|
||||
(dcmt/fetch-profiles)
|
||||
(df/fetch-fonts team-id)))
|
||||
|
||||
;; Once the essential data is fetched, lets proceed to
|
||||
;; fetch teh file bunldle
|
||||
(rx/of (fetch-bundle file-id features)))
|
||||
;; Once the essential data is fetched, lets proceed to
|
||||
;; fetch teh file bunldle
|
||||
(rx/of (fetch-bundle file-id features)))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [file]}]
|
||||
(log/debug :hint "bundle fetched"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [file]}]
|
||||
(log/debug :hint "bundle fetched"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
|
||||
(rx/of (dpj/initialize-project (:project-id file))
|
||||
(dwn/initialize team-id file-id)
|
||||
(dwsl/initialize-shape-layout)
|
||||
(fetch-libraries file-id features)
|
||||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:team-id team-id
|
||||
:file-id file-id}))))))
|
||||
(rx/of (dpj/initialize-project (:project-id file))
|
||||
(dwn/initialize team-id file-id)
|
||||
(dwsl/initialize-shape-layout)
|
||||
(fetch-libraries file-id features)
|
||||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:team-id team-id
|
||||
:file-id file-id}))))))
|
||||
|
||||
;; Install dev perf observers once the workspace is ready
|
||||
(when (contains? cf/flags :perf-logs)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/take 1)
|
||||
(rx/tap (fn [_] (perf/setup)))))
|
||||
;; Install dev perf observers once the workspace is ready
|
||||
(when (contains? cf/flags :perf-logs)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/take 1)
|
||||
(rx/tap (fn [_] (perf/setup)))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwl/go-to-local-component :id component-id :update-layout? (:update-layout rparams)))))
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwl/go-to-local-component :id component-id :update-layout? (:update-layout rparams)))))
|
||||
|
||||
(when (:board-id rparams)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwv/initialize-viewport))
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
(when (:board-id rparams)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwv/initialize-viewport))
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwcm/navigate-to-comment-id comment-id))))
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwcm/navigate-to-comment-id comment-id))))
|
||||
|
||||
(when render-wasm?
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [redo-changes]}]
|
||||
(let [added (->> redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(map :id))]
|
||||
(->> (rx/from added)
|
||||
(rx/map process-wasm-object)))))))
|
||||
(when render-wasm?
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [redo-changes]}]
|
||||
(let [added (->> redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(map :id))]
|
||||
(->> (rx/from added)
|
||||
(rx/map process-wasm-object)))))))
|
||||
|
||||
(when render-wasm?
|
||||
(let [local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(and (= :local (:source %))
|
||||
(not (contains? (:tags %) :position-data))))
|
||||
(rx/filter (complement empty?)))
|
||||
(when render-wasm?
|
||||
(let [local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(and (= :local (:source %))
|
||||
(not (contains? (:tags %) :position-data))))
|
||||
(rx/filter (complement empty?)))
|
||||
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> local-commits-s (rx/debounce 1000))
|
||||
(->> stream (rx/filter dps/force-persist?)))
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> local-commits-s (rx/debounce 1000))
|
||||
(->> stream (rx/filter dps/force-persist?)))
|
||||
|
||||
objects-s
|
||||
(rx/from-atom refs/workspace-page-objects {:emit-current-value? true})
|
||||
objects-s
|
||||
(rx/from-atom refs/workspace-page-objects {:emit-current-value? true})
|
||||
|
||||
current-page-id-s
|
||||
(rx/from-atom refs/current-page-id {:emit-current-value? true})]
|
||||
current-page-id-s
|
||||
(rx/from-atom refs/current-page-id {:emit-current-value? true})]
|
||||
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/with-latest-from objects-s)
|
||||
(rx/map
|
||||
(fn [[commits objects]]
|
||||
(->> commits
|
||||
(mapcat :redo-changes)
|
||||
(filter #(contains? #{:mod-obj :add-obj} (:type %)))
|
||||
(filter #(cfh/text-shape? objects (:id %)))
|
||||
(map #(vector
|
||||
(:id %)
|
||||
(wasm.api/calculate-position-data (get objects (:id %))))))))
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/with-latest-from objects-s)
|
||||
(rx/map
|
||||
(fn [[commits objects]]
|
||||
(->> commits
|
||||
(mapcat :redo-changes)
|
||||
(filter #(contains? #{:mod-obj :add-obj} (:type %)))
|
||||
(filter #(cfh/text-shape? objects (:id %)))
|
||||
(map #(vector
|
||||
(:id %)
|
||||
(wasm.api/calculate-position-data (get objects (:id %))))))))
|
||||
|
||||
(rx/with-latest-from current-page-id-s)
|
||||
(rx/map
|
||||
(fn [[text-position-data page-id]]
|
||||
(let [changes
|
||||
(->> text-position-data
|
||||
(mapv (fn [[id position-data]]
|
||||
{:type :mod-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set
|
||||
:attr :position-data
|
||||
:val position-data
|
||||
:ignore-touched true
|
||||
:ignore-geometry true}]})))]
|
||||
(when (d/not-empty? changes)
|
||||
(dch/commit-changes
|
||||
{:redo-changes changes :undo-changes []
|
||||
:save-undo? false
|
||||
:tags #{:position-data}})))))
|
||||
(rx/take-until stoper-s))))
|
||||
(rx/with-latest-from current-page-id-s)
|
||||
(rx/map
|
||||
(fn [[text-position-data page-id]]
|
||||
(let [changes
|
||||
(->> text-position-data
|
||||
(mapv (fn [[id position-data]]
|
||||
{:type :mod-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set
|
||||
:attr :position-data
|
||||
:val position-data
|
||||
:ignore-touched true
|
||||
:ignore-geometry true}]})))]
|
||||
(when (d/not-empty? changes)
|
||||
(dch/commit-changes
|
||||
{:redo-changes changes :undo-changes []
|
||||
:save-undo? false
|
||||
:tags #{:position-data}})))))
|
||||
(rx/take-until stoper-s))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))))
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(let [name (dm/str "workspace-" file-id)]
|
||||
(unchecked-set ug/global "name" name)))))
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(let [name (dm/str "workspace-" file-id)]
|
||||
(unchecked-set ug/global "name" name))))))
|
||||
|
||||
(defn finalize-workspace
|
||||
[_team-id file-id]
|
||||
|
||||
@@ -197,11 +197,12 @@
|
||||
objects (:objects page)
|
||||
|
||||
undo-id (or (:undo-id options) (js/Symbol))
|
||||
[all-parents changes] (-> (pcb/empty-changes it (:id page))
|
||||
(cls/generate-delete-shapes fdata page objects ids
|
||||
{:ignore-touched (:allow-altering-copies options)
|
||||
:undo-group (:undo-group options)
|
||||
:undo-id undo-id}))]
|
||||
[all-parents changes]
|
||||
(-> (pcb/empty-changes it (:id page))
|
||||
(cls/generate-delete-shapes fdata page objects ids
|
||||
{:ignore-touched (:allow-altering-copies options)
|
||||
:undo-group (:undo-group options)
|
||||
:undo-id undo-id}))]
|
||||
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dc/detach-comment-thread ids)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.attrs :as attrs]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
@@ -19,6 +20,7 @@
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
@@ -916,11 +918,11 @@
|
||||
(update-in state [:workspace-text-modifier shape-id] {:position-data position-data}))))
|
||||
|
||||
(defn v2-update-text-shape-content
|
||||
[id content & {:keys [update-name? name finalize? save-undo?]
|
||||
:or {update-name? false name nil finalize? false save-undo? true}}]
|
||||
[id content & {:keys [update-name? name finalize? save-undo? original-content]
|
||||
:or {update-name? false name nil finalize? false save-undo? true original-content nil}}]
|
||||
(ptk/reify ::v2-update-text-shape-content
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(watch [it state _]
|
||||
(if (features/active-feature? state "render-wasm/v1")
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)
|
||||
@@ -950,11 +952,11 @@
|
||||
new-shape))
|
||||
{:save-undo? save-undo? :undo-group (when new-shape? id)})
|
||||
|
||||
(let [modifiers (dwwt/resize-wasm-text-modifiers shape content)
|
||||
options {:undo-group (when new-shape? id)}]
|
||||
(if (and (not= :fixed (:grow-type shape)) finalize?)
|
||||
(dwm/apply-wasm-modifiers modifiers options)
|
||||
(dwm/set-wasm-modifiers modifiers options))))
|
||||
(when-let [modifiers (dwwt/resize-wasm-text-modifiers shape content)]
|
||||
(let [options {:undo-group (when new-shape? id)}]
|
||||
(if (and (not= :fixed (:grow-type shape)) finalize?)
|
||||
(dwm/apply-wasm-modifiers modifiers options)
|
||||
(dwm/set-wasm-modifiers modifiers options)))))
|
||||
|
||||
(when finalize?
|
||||
(rx/concat
|
||||
@@ -970,7 +972,13 @@
|
||||
{:save-undo? false}))
|
||||
(dws/deselect-shape id)
|
||||
(dwsh/delete-shapes #{id})))
|
||||
(rx/of (dwt/finish-transform))))))
|
||||
(rx/of
|
||||
;; This commit is necesary for undo and component propagation
|
||||
;; on finalization
|
||||
(dch/commit-changes
|
||||
(-> (pcb/empty-changes it (:current-page-id state))
|
||||
(pcb/set-text-content id content original-content)))
|
||||
(dwt/finish-transform))))))
|
||||
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)
|
||||
|
||||
@@ -620,61 +620,68 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
;; We do not allow to apply tokens while text editor is open.
|
||||
(when (empty? (get state :workspace-editor-state))
|
||||
(let [attributes-to-remove
|
||||
;; Remove atomic typography tokens when applying composite and vice-verca
|
||||
(cond
|
||||
(ctt/typography-token-keys (:type token)) (set/union attributes-to-remove ctt/typography-keys)
|
||||
(ctt/typography-keys (:type token)) (set/union attributes-to-remove ctt/typography-token-keys)
|
||||
:else attributes-to-remove)]
|
||||
(when-let [tokens (some-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib)
|
||||
(ctob/get-tokens-in-active-sets))]
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [undo-id (js/Symbol)
|
||||
objects (dsh/lookup-page-objects state)
|
||||
selected-shapes (select-keys objects shape-ids)
|
||||
;; The classic text editor sets :workspace-editor-state; the WASM text editor
|
||||
;; does not, so we also check :workspace-local :edition for text shapes.
|
||||
(let [edition (get-in state [:workspace-local :edition])
|
||||
objects (dsh/lookup-page-objects state)
|
||||
text-editing? (and (some? edition)
|
||||
(= :text (:type (get objects edition))))]
|
||||
(when (and (empty? (get state :workspace-editor-state))
|
||||
(not text-editing?))
|
||||
(let [attributes-to-remove
|
||||
;; Remove atomic typography tokens when applying composite and vice-verca
|
||||
(cond
|
||||
(ctt/typography-token-keys (:type token)) (set/union attributes-to-remove ctt/typography-keys)
|
||||
(ctt/typography-keys (:type token)) (set/union attributes-to-remove ctt/typography-token-keys)
|
||||
:else attributes-to-remove)]
|
||||
(when-let [tokens (some-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib)
|
||||
(ctob/get-tokens-in-active-sets))]
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [undo-id (js/Symbol)
|
||||
objects (dsh/lookup-page-objects state)
|
||||
selected-shapes (select-keys objects shape-ids)
|
||||
|
||||
shapes (->> selected-shapes
|
||||
(filter (fn [[_ shape]]
|
||||
(or
|
||||
(and (ctsl/any-layout-immediate-child? objects shape)
|
||||
(some ctt/spacing-margin-keys attributes))
|
||||
(and (ctt/any-appliable-attr-for-shape? attributes (:type shape) (:layout shape))
|
||||
(all-attrs-appliable-for-token? attributes (:type token)))))))
|
||||
shape-ids (d/nilv (keys shapes) [])
|
||||
any-variant? (->> shapes vals (some ctk/is-variant?) boolean)
|
||||
shapes (->> selected-shapes
|
||||
(filter (fn [[_ shape]]
|
||||
(or
|
||||
(and (ctsl/any-layout-immediate-child? objects shape)
|
||||
(some ctt/spacing-margin-keys attributes))
|
||||
(and (ctt/any-appliable-attr-for-shape? attributes (:type shape) (:layout shape))
|
||||
(all-attrs-appliable-for-token? attributes (:type token)))))))
|
||||
shape-ids (d/nilv (keys shapes) [])
|
||||
any-variant? (->> shapes vals (some ctk/is-variant?) boolean)
|
||||
|
||||
resolved-value (get-in resolved-tokens [(cfo/token-identifier token) :resolved-value])
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)
|
||||
tokenized-attributes (cfo/attributes-map attributes token)
|
||||
type (:type token)]
|
||||
(rx/concat
|
||||
(rx/of
|
||||
(st/emit! (ev/event {::ev/name "apply-tokens"
|
||||
:type type
|
||||
:applied-to attributes
|
||||
:applied-to-variant any-variant?}))
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dwsh/update-shapes shape-ids (fn [shape]
|
||||
(cond-> shape
|
||||
attributes-to-remove
|
||||
(update :applied-tokens #(apply (partial dissoc %) attributes-to-remove))
|
||||
:always
|
||||
(update :applied-tokens merge tokenized-attributes)))))
|
||||
(when on-update-shape
|
||||
(let [res (on-update-shape resolved-value shape-ids attributes)]
|
||||
;; Composed updates return observables and need to be executed differently
|
||||
(if (rx/observable? res)
|
||||
res
|
||||
(rx/of res))))
|
||||
(rx/of (dwu/commit-undo-transaction undo-id)))))))))))))
|
||||
resolved-value (get-in resolved-tokens [(cfo/token-identifier token) :resolved-value])
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)
|
||||
tokenized-attributes (cfo/attributes-map attributes token)
|
||||
type (:type token)]
|
||||
(rx/concat
|
||||
(rx/of
|
||||
(st/emit! (ev/event {::ev/name "apply-tokens"
|
||||
:type type
|
||||
:applied-to attributes
|
||||
:applied-to-variant any-variant?}))
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dwsh/update-shapes shape-ids (fn [shape]
|
||||
(cond-> shape
|
||||
attributes-to-remove
|
||||
(update :applied-tokens #(apply (partial dissoc %) attributes-to-remove))
|
||||
:always
|
||||
(update :applied-tokens merge tokenized-attributes)))))
|
||||
(when on-update-shape
|
||||
(let [res (on-update-shape resolved-value shape-ids attributes)]
|
||||
;; Composed updates return observables and need to be executed differently
|
||||
(if (rx/observable? res)
|
||||
res
|
||||
(rx/of res))))
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))))))))
|
||||
|
||||
(defn apply-spacing-token-separated
|
||||
"Handles edge-case for spacing token when applying token via toggle button.
|
||||
|
||||
@@ -548,7 +548,7 @@
|
||||
modif-tree
|
||||
(dwm/build-modif-tree ids objects get-modifier)]
|
||||
|
||||
(rx/of (dwm/apply-wasm-modifiers modif-tree)))
|
||||
(rx/of (dwm/apply-wasm-modifiers modif-tree :ignore-touched (:ignore-touched options))))
|
||||
|
||||
(let [page-id (or (:page-id options)
|
||||
(:current-page-id state))
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
(rx/take 1)
|
||||
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
|
||||
(rx/tap #(th/clear-queue!))
|
||||
(rx/map #(dw/initialize-workspace team-id file-id)))
|
||||
(rx/map #(dw/initialize-workspace team-id file-id id)))
|
||||
(case origin
|
||||
:version
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"}))
|
||||
@@ -231,7 +231,7 @@
|
||||
(rx/filter #(or (nil? %) (= :saved %)))
|
||||
(rx/take 1)
|
||||
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
|
||||
(rx/map #(dw/initialize-workspace team-id file-id)))
|
||||
(rx/map #(dw/initialize-workspace team-id file-id id)))
|
||||
|
||||
(->> (rx/of 1)
|
||||
(rx/tap resolve)
|
||||
|
||||
@@ -27,27 +27,28 @@
|
||||
(resize-wasm-text-modifiers shape (:content shape)))
|
||||
|
||||
([{:keys [id points selrect grow-type] :as shape} content]
|
||||
(wasm.api/use-shape id)
|
||||
(wasm.api/set-shape-text-content id content)
|
||||
(wasm.api/set-shape-text-images id content)
|
||||
(when id
|
||||
(wasm.api/use-shape id)
|
||||
(wasm.api/set-shape-text-content id content)
|
||||
(wasm.api/set-shape-text-images id content)
|
||||
|
||||
(let [dimension (wasm.api/get-text-dimensions)
|
||||
width-scale (if (#{:fixed :auto-height} grow-type)
|
||||
1.0
|
||||
(/ (:width dimension) (:width selrect)))
|
||||
height-scale (if (= :fixed grow-type)
|
||||
1.0
|
||||
(/ (:height dimension) (:height selrect)))
|
||||
resize-v (gpt/point width-scale height-scale)
|
||||
origin (first points)]
|
||||
(let [dimension (wasm.api/get-text-dimensions)
|
||||
width-scale (if (#{:fixed :auto-height} grow-type)
|
||||
1.0
|
||||
(/ (:width dimension) (:width selrect)))
|
||||
height-scale (if (= :fixed grow-type)
|
||||
1.0
|
||||
(/ (:height dimension) (:height selrect)))
|
||||
resize-v (gpt/point width-scale height-scale)
|
||||
origin (first points)]
|
||||
|
||||
{id
|
||||
{:modifiers
|
||||
(ctm/resize-modifiers
|
||||
resize-v
|
||||
origin
|
||||
(:transform shape (gmt/matrix))
|
||||
(:transform-inverse shape (gmt/matrix)))}})))
|
||||
{id
|
||||
{:modifiers
|
||||
(ctm/resize-modifiers
|
||||
resize-v
|
||||
origin
|
||||
(:transform shape (gmt/matrix))
|
||||
(:transform-inverse shape (gmt/matrix)))}}))))
|
||||
|
||||
(defn resize-wasm-text
|
||||
"Resize a single text shape (auto-width/auto-height) by id.
|
||||
|
||||
@@ -86,6 +86,24 @@
|
||||
:else
|
||||
(enabled-by-flags? state feature))))
|
||||
|
||||
(defn active-features?
|
||||
"Given a state and a set of features, check if the features are all enabled."
|
||||
([state a]
|
||||
(js/console.warn "Please, use active-feature? instead")
|
||||
(active-feature? state a))
|
||||
([state a b]
|
||||
(and ^boolean (active-feature? state a)
|
||||
^boolean (active-feature? state b)))
|
||||
([state a b c]
|
||||
(and ^boolean (active-feature? state a)
|
||||
^boolean (active-feature? state b)
|
||||
^boolean (active-feature? state c)))
|
||||
([state a b c & others]
|
||||
(and ^boolean (active-feature? state a)
|
||||
^boolean (active-feature? state b)
|
||||
^boolean (active-feature? state c)
|
||||
^boolean (every? #(active-feature? state %) others))))
|
||||
|
||||
(def ^:private features-ref
|
||||
(l/derived (l/key :features) st/state))
|
||||
|
||||
|
||||
@@ -183,9 +183,6 @@
|
||||
[id]
|
||||
(l/derived #(contains? % id) selected-shapes))
|
||||
|
||||
(def highlighted-shapes
|
||||
(l/derived :highlighted workspace-local))
|
||||
|
||||
(def export-in-progress?
|
||||
(l/derived :export-in-progress? export))
|
||||
|
||||
@@ -256,6 +253,9 @@
|
||||
(def workspace-layout
|
||||
(l/derived :workspace-layout st/state))
|
||||
|
||||
(def workspace-file-version-id
|
||||
(l/derived :workspace-file-version-id st/state))
|
||||
|
||||
(def snap-pixel?
|
||||
(l/derived #(contains? % :snap-pixel-grid) workspace-layout))
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
[app.util.dom :as dom]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc file-uploader*
|
||||
(mf/defc file-uploader
|
||||
{::mf/forward-ref true}
|
||||
[{:keys [accept multi label-text label-class input-id on-selected data-testid]} input-ref]
|
||||
[{:keys [accept multi label-text label-class input-id on-selected data-testid] :as props} input-ref]
|
||||
(let [opt-pick-one #(if multi % (first %))
|
||||
|
||||
on-files-selected
|
||||
|
||||
107
frontend/src/app/main/ui/components/radio_buttons.cljs
Normal file
@@ -0,0 +1,107 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.components.radio-buttons
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.util.dom :as dom]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def context
|
||||
(mf/create-context nil))
|
||||
|
||||
(mf/defc radio-button
|
||||
{::mf/props :obj}
|
||||
[{:keys [icon id value disabled title icon-class type]}]
|
||||
(let [context (mf/use-ctx context)
|
||||
allow-empty (unchecked-get context "allow-empty")
|
||||
type (if ^boolean type
|
||||
type
|
||||
(if ^boolean allow-empty
|
||||
"checkbox"
|
||||
"radio"))
|
||||
|
||||
on-change (unchecked-get context "on-change")
|
||||
selected (unchecked-get context "selected")
|
||||
name (unchecked-get context "name")
|
||||
|
||||
encode-fn (unchecked-get context "encode-fn")
|
||||
checked? (= selected value)
|
||||
|
||||
value (encode-fn value)]
|
||||
|
||||
|
||||
[:label {:html-for id
|
||||
:data-testid id
|
||||
:title title
|
||||
:class (stl/css-case
|
||||
:radio-icon true
|
||||
:checked checked?
|
||||
:disabled disabled)}
|
||||
|
||||
(if (some? icon)
|
||||
[:> icon* {:icon-id icon :class icon-class :aria-hidden true}]
|
||||
[:span {:class (stl/css :title-name)} value])
|
||||
|
||||
[:input {:id id
|
||||
:on-change on-change
|
||||
:type type
|
||||
:name name
|
||||
:disabled disabled
|
||||
:value value
|
||||
:default-checked checked?}]]))
|
||||
|
||||
(mf/defc radio-buttons
|
||||
{::mf/props :obj}
|
||||
[{:keys [name children on-change selected class wide encode-fn decode-fn allow-empty] :as props}]
|
||||
(let [encode-fn (d/nilv encode-fn identity)
|
||||
decode-fn (d/nilv decode-fn identity)
|
||||
nitems (if (array? children)
|
||||
(count (keep identity children))
|
||||
1)
|
||||
;; FIXME: we should handle this with CSS
|
||||
width (mf/with-memo [nitems]
|
||||
(if (= wide true)
|
||||
"unset"
|
||||
(fmt/format-pixels
|
||||
(+ (* 4 (- nitems 1))
|
||||
(* 32 nitems)))))
|
||||
|
||||
on-change'
|
||||
(mf/use-fn
|
||||
(mf/deps selected on-change)
|
||||
(fn [event]
|
||||
(let [input (dom/get-target event)
|
||||
value (dom/get-target-val event)
|
||||
|
||||
;; Only allow null values when the "allow-empty" prop is true
|
||||
value (when (or (not allow-empty)
|
||||
(not= value selected)) value)]
|
||||
(when (fn? on-change)
|
||||
(on-change (decode-fn value) event))
|
||||
(dom/blur! input))))
|
||||
|
||||
context-value
|
||||
(mf/spread-object props
|
||||
;; We pass a special metadata for disable
|
||||
;; key casing transformation in this
|
||||
;; concrete case, because this component
|
||||
;; uses legacy mode and props are in
|
||||
;; kebab-case style
|
||||
^{::mf/transform false}
|
||||
{:on-change on-change'
|
||||
:encode-fn encode-fn
|
||||
:decode-fn decode-fn})]
|
||||
|
||||
[:& (mf/provider context) {:value context-value}
|
||||
[:div {:class (dm/str class " " (stl/css :radio-btn-wrapper))
|
||||
:style {:width width}
|
||||
:key (dm/str name "-" selected)}
|
||||
children]]))
|
||||
79
frontend/src/app/main/ui/components/radio_buttons.scss
Normal file
@@ -0,0 +1,79 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
|
||||
.radio-btn-wrapper {
|
||||
@include deprecated.flexCenter;
|
||||
border-radius: deprecated.$br-8;
|
||||
height: deprecated.$s-32;
|
||||
background-color: var(--input-background-color);
|
||||
gap: deprecated.$s-4;
|
||||
}
|
||||
|
||||
.radio-icon {
|
||||
--radio-icon-border-color: var(--radio-btn-border-color);
|
||||
|
||||
@include deprecated.buttonStyle;
|
||||
@include deprecated.flexCenter;
|
||||
@include deprecated.focusRadio;
|
||||
height: deprecated.$s-32;
|
||||
flex-grow: 1;
|
||||
border-radius: deprecated.$s-8;
|
||||
box-sizing: border-box;
|
||||
border: deprecated.$br-2 solid var(--radio-icon-border-color);
|
||||
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--radio-btn-foreground-color);
|
||||
}
|
||||
.title-name {
|
||||
@include deprecated.uppercaseTitleTipography;
|
||||
color: var(--radio-btn-foreground-color);
|
||||
}
|
||||
&:hover {
|
||||
svg {
|
||||
stroke: var(--radio-btn-foreground-color-selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checked {
|
||||
--radio-icon-border-color: var(--radio-btn-border-color-selected);
|
||||
|
||||
background-color: var(--radio-btn-background-color-selected);
|
||||
svg {
|
||||
stroke: var(--radio-btn-foreground-color-selected);
|
||||
}
|
||||
.title-name {
|
||||
color: var(--radio-btn-foreground-color-selected);
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
border: deprecated.$s-2 solid transparent;
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
}
|
||||
.title-name {
|
||||
color: var(--button-foreground-color-disabled);
|
||||
}
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
border: deprecated.$s-2 solid transparent;
|
||||
svg {
|
||||
stroke: var(--button-foreground-color-disabled);
|
||||
}
|
||||
.title-name {
|
||||
color: var(--button-foreground-color-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader*]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.notifications.context-notification :refer [context-notification]]
|
||||
@@ -184,11 +184,11 @@
|
||||
:on-click on-click
|
||||
:tab-index "0"}
|
||||
[:span (tr "labels.add-custom-font")]
|
||||
[:> file-uploader* {:input-id "font-upload"
|
||||
:accept accept-font-types
|
||||
:multi true
|
||||
:ref input-ref
|
||||
:on-selected on-selected}]]
|
||||
[:& file-uploader {:input-id "font-upload"
|
||||
:accept accept-font-types
|
||||
:multi true
|
||||
:ref input-ref
|
||||
:on-selected on-selected}]]
|
||||
|
||||
(when-let [url cf/terms-of-service-uri]
|
||||
[:& context-notification {:content (tr "dashboard.fonts.hero-text2" url)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader*]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.ds.product.loader :refer [loader*]]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.notifications.context-notification :refer [context-notification]]
|
||||
@@ -58,10 +58,10 @@
|
||||
[{:keys [project-id on-finish-import]} external-ref]
|
||||
(let [on-file-selected (use-import-file project-id on-finish-import)]
|
||||
[:form.import-file {:aria-hidden "true"}
|
||||
[:> file-uploader* {:accept ".penpot,.zip"
|
||||
:multi true
|
||||
:ref external-ref
|
||||
:on-selected on-file-selected}]]))
|
||||
[:& file-uploader {:accept ".penpot,.zip"
|
||||
:multi true
|
||||
:ref external-ref
|
||||
:on-selected on-file-selected}]]))
|
||||
|
||||
(defn- update-entry-name
|
||||
[entries file-id new-name]
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader*]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.dashboard.change-owner]
|
||||
[app.main.ui.dashboard.subscription :refer [members-cta*
|
||||
@@ -1315,10 +1315,10 @@
|
||||
[:img {:class (stl/css :team-image)
|
||||
:src (cfg/resolve-team-photo-url team)}]
|
||||
(when can-edit
|
||||
[:> file-uploader* {:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref finput
|
||||
:on-selected on-file-selected}])]
|
||||
[:& file-uploader {:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref finput
|
||||
:on-selected on-file-selected}])]
|
||||
[:div {:class (stl/css :block-label)}
|
||||
(tr "dashboard.team-info")]
|
||||
[:div {:class (stl/css :block-text)}
|
||||
|
||||
@@ -11,10 +11,8 @@
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as k]
|
||||
@@ -99,11 +97,8 @@
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-title)} title]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:class (stl/css :modal-close-btn)
|
||||
:icon i/close
|
||||
:aria-label (tr "labels.close")
|
||||
:on-click cancel-fn}]]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click cancel-fn} deprecated-icon/close]]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
(when (and (string? subtitle) (not= subtitle ""))
|
||||
@@ -129,10 +124,14 @@
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
(when-not (= cancel-label :omit)
|
||||
[:> button* {:variant "secondary"
|
||||
:on-click cancel-fn}
|
||||
cancel-label])
|
||||
[:input {:class (stl/css :cancel-button)
|
||||
:type "button"
|
||||
:value cancel-label
|
||||
:on-click cancel-fn}])
|
||||
|
||||
[:> button* {:variant (if (= accept-style :danger) "destructive" "primary")
|
||||
:on-click accept-fn}
|
||||
accept-label]]]]]))
|
||||
[:input {:class (stl/css-case :accept-btn true
|
||||
:danger (= accept-style :danger)
|
||||
:primary (= accept-style :primary))
|
||||
:type "button"
|
||||
:value accept-label
|
||||
:on-click accept-fn}]]]]]))
|
||||
|
||||
@@ -33,9 +33,7 @@
|
||||
}
|
||||
|
||||
.modal-close-btn {
|
||||
position: absolute;
|
||||
top: var(--sp-s);
|
||||
right: var(--sp-s);
|
||||
@extend .modal-close-btn-base;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
@@ -55,6 +53,17 @@
|
||||
@extend .modal-action-btns;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
@extend .modal-cancel-btn;
|
||||
}
|
||||
|
||||
.accept-btn {
|
||||
@extend .modal-accept-btn;
|
||||
&.danger {
|
||||
@extend .modal-danger-btn;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-scd-msg {
|
||||
margin-block: 0;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
;; FIXME: deprecated, should be refactored in two components and use
|
||||
;; the generic progress reporter
|
||||
|
||||
(mf/defc progress-widget*
|
||||
(mf/defc progress-widget
|
||||
{::mf/wrap [mf/memo]}
|
||||
[]
|
||||
(let [state (mf/deref refs/export)
|
||||
|
||||
@@ -19,10 +19,7 @@
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.code-block :refer [code-block]]
|
||||
[app.main.ui.components.copy-button :refer [copy-button*]]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.controls.radio-buttons :refer [radio-buttons*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]]
|
||||
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.shapes.text.fontfaces :refer [shapes->fonts]]
|
||||
@@ -263,9 +260,8 @@
|
||||
[:div {:class (stl/css-case :element-options true
|
||||
:viewer-code-block (= :viewer from))}
|
||||
[:div {:class (stl/css :attributes-block)}
|
||||
[:> button* {:variant "secondary"
|
||||
:class (stl/css :download-button)
|
||||
:on-click handle-copy-all-code}
|
||||
[:button {:class (stl/css :download-button)
|
||||
:on-click handle-copy-all-code}
|
||||
"Copy all code"]]
|
||||
|
||||
#_[:div.attributes-block
|
||||
@@ -292,10 +288,10 @@
|
||||
;; :options [{:label "CSS" :value "css"}]}]
|
||||
|
||||
[:div {:class (stl/css :action-btns)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label "Expand"
|
||||
:on-click on-expand
|
||||
:icon i/code}]
|
||||
[:button {:class (stl/css :expand-button)
|
||||
:on-click on-expand}
|
||||
deprecated-icon/code]
|
||||
|
||||
[:> copy-button* {:data copy-css-fn
|
||||
:class (stl/css :css-copy-btn)
|
||||
:on-copied on-style-copied}]]]
|
||||
@@ -322,21 +318,21 @@
|
||||
:rotated collapsed-markup?)}
|
||||
deprecated-icon/arrow]]
|
||||
|
||||
[:> radio-buttons* {:selected markup-type
|
||||
:on-change set-markup
|
||||
:name "listing-style"
|
||||
:options [{:id "html"
|
||||
:label "HTML"
|
||||
:value "html"}
|
||||
{:id "svg"
|
||||
:label "SVG"
|
||||
:value "svg"}]}]
|
||||
[:& radio-buttons {:selected markup-type
|
||||
:on-change set-markup
|
||||
:class (stl/css :code-lang-options)
|
||||
:wide true
|
||||
:name "listing-style"}
|
||||
[:& radio-button {:value "html"
|
||||
:id :html}]
|
||||
[:& radio-button {:value "svg"
|
||||
:id :svg}]]
|
||||
|
||||
[:div {:class (stl/css :action-btns)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label "Expand"
|
||||
:on-click on-expand
|
||||
:icon i/code}]
|
||||
[:button {:class (stl/css :expand-button)
|
||||
:on-click on-expand}
|
||||
deprecated-icon/code]
|
||||
|
||||
[:> copy-button* {:data copy-html-fn
|
||||
:class (stl/css :html-copy-btn)
|
||||
:on-copied on-markup-copied}]]]
|
||||
|
||||
@@ -17,18 +17,16 @@
|
||||
padding-inline: var(--sp-m);
|
||||
}
|
||||
|
||||
.attributes-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 12px;
|
||||
}
|
||||
|
||||
.viewer-code-block {
|
||||
height: calc(100vh - #{deprecated.$s-108}); // TODO: Fix this hardcoded value
|
||||
}
|
||||
|
||||
.download-button {
|
||||
margin: var(--sp-s) 0;
|
||||
@extend .button-secondary;
|
||||
@include deprecated.uppercaseTitleTipography;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
margin: deprecated.$s-8 0;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
@@ -75,6 +73,7 @@
|
||||
gap: deprecated.$s-4;
|
||||
}
|
||||
|
||||
.expand-button,
|
||||
.css-copy-btn,
|
||||
.html-copy-btn {
|
||||
@extend .button-tertiary;
|
||||
@@ -86,6 +85,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.code-lang-options {
|
||||
max-width: deprecated.$s-108;
|
||||
}
|
||||
.code-lang-select {
|
||||
@include deprecated.uppercaseTitleTipography;
|
||||
width: deprecated.$s-72;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
[app.common.types.component :as ctk]
|
||||
[app.main.data.viewer :as dv]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.sidebar.layer-item :refer [layer-item-inner]]
|
||||
[app.main.ui.workspace.sidebar.layer-item :refer [layer-item-inner*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
[okulary.core :as l]
|
||||
@@ -26,7 +26,6 @@
|
||||
(mf/defc layer-item
|
||||
[{:keys [item selected objects depth component-child? hide-toggle?] :as props}]
|
||||
(let [id (:id item)
|
||||
hidden? (:hidden item)
|
||||
selected? (contains? selected id)
|
||||
item-ref (mf/use-ref nil)
|
||||
depth (+ depth 1)
|
||||
@@ -68,18 +67,17 @@
|
||||
(when (and (= (count selected) 1) selected?)
|
||||
(dom/scroll-into-view-if-needed! (mf/ref-val item-ref) true))))
|
||||
|
||||
[:& layer-item-inner
|
||||
[:> layer-item-inner*
|
||||
{:ref item-ref
|
||||
:item item
|
||||
:depth depth
|
||||
:read-only? true
|
||||
:highlighted? false
|
||||
:selected? selected?
|
||||
:component-tree? component-tree?
|
||||
:hidden? hidden?
|
||||
:filtered? false
|
||||
:expanded? expanded?
|
||||
:hide-toggle? hide-toggle?
|
||||
:is-read-only true
|
||||
:is-highlighted false
|
||||
:is-selected selected?
|
||||
:is-component-tree component-tree?
|
||||
:is-filtered false
|
||||
:is-expanded expanded?
|
||||
:hide-toggle hide-toggle?
|
||||
:on-select-shape select-shape
|
||||
:on-toggle-collapse toggle-collapse}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
[app.main.data.profile :as du]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader*]]
|
||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
@@ -110,11 +110,11 @@
|
||||
[:span {:class (stl/css :update-overlay)
|
||||
:on-click on-image-click} (tr "labels.update")]
|
||||
[:img {:src photo}]
|
||||
[:> file-uploader* {:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref input-ref
|
||||
:on-selected on-file-selected
|
||||
:data-testid "profile-image-input"}]]]))
|
||||
[:& file-uploader {:accept "image/jpeg,image/png"
|
||||
:multi false
|
||||
:ref input-ref
|
||||
:on-selected on-file-selected
|
||||
:data-testid "profile-image-input"}]]]))
|
||||
|
||||
;; --- Profile Page
|
||||
|
||||
|
||||
@@ -27,8 +27,9 @@
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc comments-menu*
|
||||
{::mf/memo true}
|
||||
(mf/defc comments-menu
|
||||
{::mf/props :obj
|
||||
::mf/memo true}
|
||||
[]
|
||||
(let [state (mf/deref refs/comments-local)
|
||||
cmode (:mode state)
|
||||
|
||||
@@ -14,13 +14,10 @@
|
||||
[app.main.data.viewer.shortcuts :as sc]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.exports.assets :refer [progress-widget*]]
|
||||
[app.main.ui.exports.assets :refer [progress-widget]]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.viewer.comments :refer [comments-menu*]]
|
||||
[app.main.ui.viewer.comments :refer [comments-menu]]
|
||||
[app.main.ui.viewer.interactions :refer [flows-menu* interactions-menu*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
@@ -36,12 +33,20 @@
|
||||
[]
|
||||
(modal/show! :login-register {}))
|
||||
|
||||
(mf/defc zoom-widget*
|
||||
{::mf/memo true}
|
||||
[{:keys [zoom on-increase on-decrease on-zoom-reset on-fullscreen on-zoom-fit on-zoom-fill]}]
|
||||
(let [open* (mf/use-state false)
|
||||
open? (deref open*)
|
||||
(mf/defc zoom-widget
|
||||
{::mf/memo true
|
||||
::mf/props :obj}
|
||||
[{:keys [zoom
|
||||
on-increase
|
||||
on-decrease
|
||||
on-zoom-reset
|
||||
on-fullscreen
|
||||
on-zoom-fit
|
||||
on-zoom-fill]
|
||||
:as props}]
|
||||
|
||||
(let [open* (mf/use-state false)
|
||||
open? (deref open*)
|
||||
open-dropdown
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
@@ -70,7 +75,7 @@
|
||||
|
||||
[:div {:class (stl/css-case :zoom-widget true
|
||||
:selected open?)
|
||||
:on-click (if open? close-dropdown open-dropdown)
|
||||
:on-click open-dropdown
|
||||
:title (tr "workspace.header.zoom")}
|
||||
[:span {:class (stl/css :label)} (fmt/format-percent zoom)]
|
||||
[:& dropdown {:show open?
|
||||
@@ -78,18 +83,18 @@
|
||||
[:ul {:class (stl/css :dropdown)}
|
||||
[:li {:class (stl/css :basic-zoom-bar)}
|
||||
[:span {:class (stl/css :zoom-btns)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "shortcuts.decrease-zoom")
|
||||
:on-click on-decrease
|
||||
:icon i/remove}]
|
||||
[:p {:class (stl/css :zoom-text)}
|
||||
[:button {:class (stl/css :zoom-btn)
|
||||
:on-click on-decrease}
|
||||
[:span {:class (stl/css :zoom-icon)}
|
||||
deprecated-icon/remove-icon]]
|
||||
[:p {:class (stl/css :zoom-text)}
|
||||
(fmt/format-percent zoom)]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "shortcuts.increase-zoom")
|
||||
:on-click on-increase
|
||||
:icon i/add}]]
|
||||
[:> button* {:variant "ghost"
|
||||
:on-click on-zoom-reset}
|
||||
[:button {:class (stl/css :zoom-btn)
|
||||
:on-click on-increase}
|
||||
[:span {:class (stl/css :zoom-icon)}
|
||||
deprecated-icon/add]]]
|
||||
[:button {:class (stl/css :reset-btn)
|
||||
:on-click on-zoom-reset}
|
||||
(tr "workspace.header.reset-zoom")]]
|
||||
|
||||
[:li {:class (stl/css :zoom-option)
|
||||
@@ -114,7 +119,7 @@
|
||||
[:span {:class (stl/css :shortcut-key)
|
||||
:key (dm/str "zoom-fullscreen-" sc)} sc])]]]]]))
|
||||
|
||||
(mf/defc header-options*
|
||||
(mf/defc header-options
|
||||
[{:keys [section zoom page file index permissions interactions-mode share]}]
|
||||
(let [fullscreen? (mf/deref fullscreen-ref)
|
||||
|
||||
@@ -154,7 +159,6 @@
|
||||
handle-zoom-fit
|
||||
(mf/use-fn
|
||||
#(st/emit! dv/zoom-to-fit))]
|
||||
|
||||
(mf/with-effect [permissions share]
|
||||
(when (and
|
||||
(:in-team permissions)
|
||||
@@ -163,7 +167,7 @@
|
||||
(open-share-dialog)))
|
||||
|
||||
[:div {:class (stl/css :options-zone)}
|
||||
[:> progress-widget*]
|
||||
[:& progress-widget]
|
||||
|
||||
(case section
|
||||
:interactions [:*
|
||||
@@ -171,41 +175,40 @@
|
||||
[:> flows-menu* {:page page :index index}])
|
||||
[:> interactions-menu*
|
||||
{:interactions-mode interactions-mode}]]
|
||||
:comments [:> comments-menu*]
|
||||
:comments [:& comments-menu]
|
||||
[:div {:class (stl/css :view-options)}])
|
||||
|
||||
[:> zoom-widget* {:zoom zoom
|
||||
:on-increase handle-increase
|
||||
:on-decrease handle-decrease
|
||||
:on-zoom-reset handle-zoom-reset
|
||||
:on-zoom-fill handle-zoom-fill
|
||||
:on-zoom-fit handle-zoom-fit
|
||||
:on-fullscreen toggle-fullscreen}]
|
||||
[:& zoom-widget
|
||||
{:zoom zoom
|
||||
:on-increase handle-increase
|
||||
:on-decrease handle-decrease
|
||||
:on-zoom-reset handle-zoom-reset
|
||||
:on-zoom-fill handle-zoom-fill
|
||||
:on-zoom-fit handle-zoom-fit
|
||||
:on-fullscreen toggle-fullscreen}]
|
||||
|
||||
(when (:in-team permissions)
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "viewer.header.edit-in-workspace")
|
||||
:on-click go-to-workspace
|
||||
:icon i/curve}])
|
||||
[:span {:on-click go-to-workspace
|
||||
:class (stl/css :edit-btn)}
|
||||
deprecated-icon/curve])
|
||||
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-pressed fullscreen?
|
||||
:aria-label (tr "viewer.header.fullscreen")
|
||||
:on-click toggle-fullscreen
|
||||
:icon i/expand}]
|
||||
[:span {:title (tr "viewer.header.fullscreen")
|
||||
:class (stl/css-case :fullscreen-btn true
|
||||
:selected fullscreen?)
|
||||
:on-click toggle-fullscreen}
|
||||
deprecated-icon/expand]
|
||||
|
||||
(when (:in-team permissions)
|
||||
[:> button* {:variant "primary"
|
||||
:class (stl/css :share-btn)
|
||||
:on-click open-share-dialog}
|
||||
[:button {:on-click open-share-dialog
|
||||
:class (stl/css :share-btn)}
|
||||
(tr "labels.share")])
|
||||
|
||||
(when-not (:is-logged permissions)
|
||||
[:span {:on-click open-login-dialog
|
||||
:class (stl/css :go-log-btn)} (tr "labels.log-or-sign")])]))
|
||||
|
||||
(mf/defc header-sitemap*
|
||||
[{:keys [project file page frame toggle-thumbnails]}]
|
||||
(mf/defc header-sitemap
|
||||
[{:keys [project file page frame toggle-thumbnails] :as props}]
|
||||
(let [project-name (:name project)
|
||||
file-name (:name file)
|
||||
page-name (:name page)
|
||||
@@ -314,44 +317,44 @@
|
||||
:pointer-events (when-not (:in-team permissions) "none")}}
|
||||
penpot-logo-icon]
|
||||
|
||||
[:> header-sitemap* {:project project
|
||||
:file file
|
||||
:page page
|
||||
:frame frame
|
||||
:toggle-thumbnails toggle-thumbnails
|
||||
:index index}]]
|
||||
[:& header-sitemap {:project project
|
||||
:file file
|
||||
:page page
|
||||
:frame frame
|
||||
:toggle-thumbnails toggle-thumbnails
|
||||
:index index}]]
|
||||
|
||||
[:div {:class (stl/css :mode-zone)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-pressed (= section :interactions)
|
||||
:aria-label (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))
|
||||
:data-value "interactions"
|
||||
:on-click navigate
|
||||
:icon i/play}]
|
||||
[:button {:on-click navigate
|
||||
:data-value "interactions"
|
||||
:class (stl/css-case :mode-zone-btn true
|
||||
:selected (= section :interactions))
|
||||
:title (tr "viewer.header.interactions-section" (sc/get-tooltip :open-interactions))}
|
||||
deprecated-icon/play]
|
||||
|
||||
(when (or (:in-team permissions)
|
||||
(= (:who-comment permissions) "all"))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-pressed (= section :comments)
|
||||
:aria-label (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))
|
||||
:data-value "comments"
|
||||
:on-click navigate
|
||||
:icon i/comments}])
|
||||
[:button {:on-click navigate
|
||||
:data-value "comments"
|
||||
:class (stl/css-case :mode-zone-btn true
|
||||
:selected (= section :comments))
|
||||
:title (tr "viewer.header.comments-section" (sc/get-tooltip :open-comments))}
|
||||
deprecated-icon/comments])
|
||||
|
||||
(when (or (:in-team permissions)
|
||||
(and (= (:type permissions) :share-link)
|
||||
(= (:who-inspect permissions) "all")))
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-pressed (= section :inspect)
|
||||
:aria-label (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect))
|
||||
:on-click go-to-inspect
|
||||
:icon i/code}])]
|
||||
[:button {:on-click go-to-inspect
|
||||
:class (stl/css-case :mode-zone-btn true
|
||||
:selected (= section :inspect))
|
||||
:title (tr "viewer.header.inspect-section" (sc/get-tooltip :open-inspect))}
|
||||
deprecated-icon/code])]
|
||||
|
||||
[:> header-options* {:section section
|
||||
:permissions permissions
|
||||
:page page
|
||||
:file file
|
||||
:index index
|
||||
:zoom zoom
|
||||
:interactions-mode interactions-mode
|
||||
:share share}]]))
|
||||
[:& header-options {:section section
|
||||
:permissions permissions
|
||||
:page page
|
||||
:file file
|
||||
:index index
|
||||
:zoom zoom
|
||||
:interactions-mode interactions-mode
|
||||
:share share}]]))
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
grid-column: 1 / span 1;
|
||||
grid-row: 1 / span 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
grid-template-columns: 1fr deprecated.$s-92 1fr;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: deprecated.$s-48;
|
||||
@@ -130,9 +130,23 @@
|
||||
|
||||
// SECTION BUTTONS
|
||||
.mode-zone {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--sp-xs);
|
||||
@include deprecated.flexRow;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mode-zone-btn {
|
||||
@extend .button-tertiary;
|
||||
@include deprecated.flexCenter;
|
||||
height: deprecated.$s-32;
|
||||
width: deprecated.$s-28;
|
||||
padding: 0;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
@extend .button-icon-selected;
|
||||
}
|
||||
|
||||
// OPTION AREA
|
||||
@@ -151,8 +165,33 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
@extend .button-tertiary;
|
||||
@include deprecated.flexCenter;
|
||||
height: deprecated.$s-32;
|
||||
width: deprecated.$s-28;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.share-btn {
|
||||
margin-left: var(--sp-xs);
|
||||
@extend .button-primary;
|
||||
height: deprecated.$s-32;
|
||||
min-width: deprecated.$s-72;
|
||||
margin-left: deprecated.$s-4;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
@extend .button-tertiary;
|
||||
@include deprecated.flexCenter;
|
||||
height: deprecated.$s-32;
|
||||
width: deprecated.$s-28;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.go-log-btn {
|
||||
@@ -206,15 +245,43 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.zoom-btn {
|
||||
@extend .button-tertiary;
|
||||
height: deprecated.$s-28;
|
||||
width: deprecated.$s-28;
|
||||
border-radius: deprecated.$br-8;
|
||||
.zoom-icon {
|
||||
@include deprecated.flexCenter;
|
||||
width: deprecated.$s-24;
|
||||
height: deprecated.$s-32;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.zoom-icon svg {
|
||||
stroke: var(--button-tertiary-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zoom-text {
|
||||
@include deprecated.flexCenter;
|
||||
height: 100%;
|
||||
min-width: deprecated.$s-48;
|
||||
min-width: deprecated.$s-64;
|
||||
padding: 0;
|
||||
margin: 0 deprecated.$s-2;
|
||||
color: var(--modal-title-foreground-color);
|
||||
}
|
||||
|
||||
.reset-btn {
|
||||
@extend .button-tertiary;
|
||||
color: var(--button-tertiary-foreground-color-hover);
|
||||
height: deprecated.$s-28;
|
||||
border-radius: deprecated.$br-8;
|
||||
}
|
||||
|
||||
.zoom-option {
|
||||
@extend .menu-item-base;
|
||||
.shortcuts {
|
||||
|
||||