mirror of
https://github.com/penpot/penpot.git
synced 2026-01-02 19:38:48 -05:00
Compare commits
61 Commits
test-inner
...
niwinz-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7f65259b1 | ||
|
|
9280d3937c | ||
|
|
8b12e5d996 | ||
|
|
63763aace7 | ||
|
|
45f98b716e | ||
|
|
fd2e5e31e2 | ||
|
|
d1f660c70c | ||
|
|
119d901ce3 | ||
|
|
856dd2238d | ||
|
|
31017654bb | ||
|
|
91337c074a | ||
|
|
0200ba15f2 | ||
|
|
957d51c68f | ||
|
|
a0fd066af1 | ||
|
|
4ed307be7a | ||
|
|
c4260c17b2 | ||
|
|
692471c927 | ||
|
|
5b62936ab4 | ||
|
|
ada0d8b293 | ||
|
|
b5a6867058 | ||
|
|
7fe20b65dc | ||
|
|
e5638cd769 | ||
|
|
8e79dfcb82 | ||
|
|
508db99a57 | ||
|
|
3c6c9894da | ||
|
|
972b23e6c0 | ||
|
|
28f550d533 | ||
|
|
2b20f75fd4 | ||
|
|
4d6d7a6a3d | ||
|
|
0f88253dd5 | ||
|
|
db1ab7be69 | ||
|
|
fcbe9d92dc | ||
|
|
9998ce0bb4 | ||
|
|
6061391c89 | ||
|
|
eabf6e36ed | ||
|
|
04274e53fa | ||
|
|
52dd9271a9 | ||
|
|
8f5a81e179 | ||
|
|
a940c08da9 | ||
|
|
3de4473251 | ||
|
|
0735140f07 | ||
|
|
dc8a07099d | ||
|
|
8e3996fbb0 | ||
|
|
67762d9450 | ||
|
|
90dcf04fb0 | ||
|
|
f84c236e02 | ||
|
|
63959a22cc | ||
|
|
8840246425 | ||
|
|
62ec66cd15 | ||
|
|
e3b87390f6 | ||
|
|
d9ab28e6ed | ||
|
|
9183dbbc43 | ||
|
|
74d00473e9 | ||
|
|
1c70f5a36b | ||
|
|
b23e0c0642 | ||
|
|
7f62652870 | ||
|
|
78d31ab11a | ||
|
|
0a80c47901 | ||
|
|
77f1046fc8 | ||
|
|
553b73a83c | ||
|
|
00a45cb274 |
@@ -1,305 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: "fmt check"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
|
||||
- run:
|
||||
name: "lint clj common"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:common
|
||||
|
||||
- run:
|
||||
name: "lint clj frontend"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:frontend
|
||||
|
||||
- run:
|
||||
name: "lint clj backend"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:backend
|
||||
|
||||
- run:
|
||||
name: "lint clj exporter"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:exporter
|
||||
|
||||
- run:
|
||||
name: "lint clj library"
|
||||
working_directory: "."
|
||||
command: |
|
||||
yarn run lint:clj:library
|
||||
|
||||
test-common:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: "JVM tests"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
clojure -M:dev:test
|
||||
|
||||
- run:
|
||||
name: "NODE tests"
|
||||
working_directory: "./common"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run test
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
- ~/.yarn
|
||||
- ~/.gitlibs
|
||||
- ~/.cache/ms-playwright
|
||||
key: v1-dependencies-{{ checksum "common/deps.edn"}}-{{ checksum "common/yarn.lock" }}
|
||||
|
||||
test-frontend:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: "install dependencies"
|
||||
working_directory: "./frontend"
|
||||
# We install playwright here because the dependent tasks
|
||||
# uses the same cache as this task so we prepopulate it
|
||||
command: |
|
||||
yarn install
|
||||
yarn run playwright install chromium --with-deps
|
||||
|
||||
- run:
|
||||
name: "lint scss on frontend"
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
yarn run lint:scss
|
||||
|
||||
- run:
|
||||
name: "unit tests"
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
yarn run test
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
- ~/.yarn
|
||||
- ~/.gitlibs
|
||||
- ~/.cache/ms-playwright
|
||||
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
test-library:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx6g
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: Install dependencies and build
|
||||
working_directory: "./library"
|
||||
command: |
|
||||
yarn install
|
||||
|
||||
- run:
|
||||
name: Build and Test
|
||||
working_directory: "./library"
|
||||
command: |
|
||||
./scripts/build
|
||||
yarn run test
|
||||
|
||||
test-components:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx6g -Xms2g
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
yarn install
|
||||
yarn run playwright install chromium
|
||||
|
||||
- run:
|
||||
name: Build Storybook
|
||||
working_directory: "./frontend"
|
||||
command: yarn run build:storybook
|
||||
|
||||
- run:
|
||||
name: Serve Storybook and run tests
|
||||
working_directory: "./frontend"
|
||||
command: |
|
||||
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
|
||||
"npx http-server storybook-static --port 6006 --silent" \
|
||||
"npx wait-on tcp:6006 && yarn test:storybook"
|
||||
|
||||
test-backend:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
- image: cimg/postgres:14.5
|
||||
environment:
|
||||
POSTGRES_USER: penpot_test
|
||||
POSTGRES_PASSWORD: penpot_test
|
||||
POSTGRES_DB: penpot_test
|
||||
- image: cimg/redis:7.0.5
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
|
||||
environment:
|
||||
JAVA_OPTS: -Xmx4g -Xms100m -XX:+UseSerialGC
|
||||
NODE_OPTIONS: --max-old-space-size=4096
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
keys:
|
||||
- v1-dependencies-{{ checksum "backend/deps.edn" }}
|
||||
|
||||
- run:
|
||||
name: "tests"
|
||||
working_directory: "./backend"
|
||||
command: |
|
||||
clojure -M:dev:test --reporter kaocha.report/documentation
|
||||
|
||||
environment:
|
||||
PENPOT_TEST_DATABASE_URI: "postgresql://localhost/penpot_test"
|
||||
PENPOT_TEST_DATABASE_USERNAME: penpot_test
|
||||
PENPOT_TEST_DATABASE_PASSWORD: penpot_test
|
||||
PENPOT_TEST_REDIS_URI: "redis://localhost/1"
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.m2
|
||||
- ~/.gitlibs
|
||||
key: v1-dependencies-{{ checksum "backend/deps.edn" }}
|
||||
|
||||
test-render-wasm:
|
||||
docker:
|
||||
- image: penpotapp/devenv:latest
|
||||
|
||||
working_directory: ~/repo
|
||||
resource_class: medium+
|
||||
environment:
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: "fmt check"
|
||||
working_directory: "./render-wasm"
|
||||
command: |
|
||||
cargo fmt --check
|
||||
|
||||
- run:
|
||||
name: "lint"
|
||||
working_directory: "./render-wasm"
|
||||
command: |
|
||||
./lint
|
||||
|
||||
- run:
|
||||
name: "cargo tests"
|
||||
working_directory: "./render-wasm"
|
||||
command: |
|
||||
./test
|
||||
|
||||
workflows:
|
||||
penpot:
|
||||
jobs:
|
||||
- test-frontend:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-library:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-components:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-backend:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- test-common:
|
||||
requires:
|
||||
- lint: success
|
||||
|
||||
- lint
|
||||
- test-render-wasm
|
||||
15
.github/workflows/build-tag.yml
vendored
15
.github/workflows/build-tag.yml
vendored
@@ -21,6 +21,21 @@ jobs:
|
||||
with:
|
||||
gh_ref: ${{ github.ref_name }}
|
||||
|
||||
notify:
|
||||
name: Notifications
|
||||
needs: build-docker
|
||||
|
||||
steps:
|
||||
- name: Notify Mattermost
|
||||
uses: mattermost/action-mattermost-notify@master
|
||||
with:
|
||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||
TEXT: |
|
||||
🐳 *[PENPOT] Docker image available.*
|
||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@infra
|
||||
|
||||
publish-final-tag:
|
||||
if: ${{ !contains(github.ref_name, '-RC') && !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && contains(github.ref_name, '.') }}
|
||||
needs: build-docker
|
||||
|
||||
96
.github/workflows/tests.yml
vendored
96
.github/workflows/tests.yml
vendored
@@ -8,8 +8,6 @@ on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- reopened
|
||||
- synchronize
|
||||
push:
|
||||
branches:
|
||||
@@ -17,12 +15,12 @@ on:
|
||||
- staging
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
group: ${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: "Code Linter"
|
||||
name: "Linter"
|
||||
runs-on: ubuntu-24.04
|
||||
container: penpotapp/devenv:latest
|
||||
|
||||
@@ -32,10 +30,7 @@ jobs:
|
||||
|
||||
- name: Check clojure code format
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install
|
||||
yarn run fmt:clj:check
|
||||
./scripts/lint
|
||||
|
||||
test-common:
|
||||
name: "Common Tests"
|
||||
@@ -54,10 +49,7 @@ jobs:
|
||||
- name: Run tests on NODE
|
||||
working-directory: ./common
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run test;
|
||||
./scripts/test
|
||||
|
||||
test-frontend:
|
||||
name: "Frontend Tests"
|
||||
@@ -71,25 +63,36 @@ jobs:
|
||||
- name: Unit Tests
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run test;
|
||||
./scripts/test
|
||||
|
||||
- name: Component Tests
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run build:storybook
|
||||
./scripts/test-components
|
||||
|
||||
npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
|
||||
"npx http-server storybook-static --port 6006 --silent" \
|
||||
"npx wait-on tcp:6006 && yarn test:storybook"
|
||||
test-render-wasm:
|
||||
name: "Render WASM Tests"
|
||||
runs-on: ubuntu-24.04
|
||||
container: penpotapp/devenv:latest
|
||||
|
||||
- name: Check SCSS Format
|
||||
working-directory: ./frontend
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Format
|
||||
working-directory: ./render-wasm
|
||||
run: |
|
||||
yarn run lint:scss;
|
||||
cargo fmt --check
|
||||
|
||||
- name: Lint
|
||||
working-directory: ./render-wasm
|
||||
run: |
|
||||
./lint
|
||||
|
||||
- name: Test
|
||||
working-directory: ./render-wasm
|
||||
run: |
|
||||
./test
|
||||
|
||||
test-backend:
|
||||
name: "Backend Tests"
|
||||
@@ -142,11 +145,7 @@ jobs:
|
||||
- name: Run tests
|
||||
working-directory: ./library
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run build:bundle;
|
||||
yarn run test;
|
||||
./scripts/test
|
||||
|
||||
build-integration:
|
||||
name: "Build Integration Bundle"
|
||||
@@ -160,17 +159,7 @@ jobs:
|
||||
- name: Build Bundle
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install
|
||||
yarn run build:app:assets
|
||||
yarn run build:app
|
||||
yarn run build:app:libs
|
||||
|
||||
- name: Build WASM
|
||||
working-directory: "./render-wasm"
|
||||
run: |
|
||||
./build release
|
||||
./scripts/build 0.0.0
|
||||
|
||||
- name: Store Bundle Cache
|
||||
uses: actions/cache@v4
|
||||
@@ -178,6 +167,7 @@ jobs:
|
||||
key: "integration-bundle-${{ github.sha }}"
|
||||
path: frontend/resources/public
|
||||
|
||||
|
||||
test-integration-1:
|
||||
name: "Integration Tests 1/4"
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -197,11 +187,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run test:e2e -x --workers=2 --reporter=list --shard="1/4";
|
||||
./scripts/test-e2e --shard="1/4";
|
||||
|
||||
- name: Upload test result
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -231,11 +217,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run test:e2e -x --workers=2 --reporter=list --shard "2/4";
|
||||
./scripts/test-e2e --shard="2/4";
|
||||
|
||||
- name: Upload test result
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -265,11 +247,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run test:e2e -x --workers=2 --reporter=list --shard "3/4";
|
||||
./scripts/test-e2e --shard="3/4";
|
||||
|
||||
- name: Upload test result
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -281,7 +259,7 @@ jobs:
|
||||
retention-days: 3
|
||||
|
||||
test-integration-4:
|
||||
name: "Integration Tests 3/4"
|
||||
name: "Integration Tests 4/4"
|
||||
runs-on: ubuntu-24.04
|
||||
container: penpotapp/devenv:latest
|
||||
needs: build-integration
|
||||
@@ -299,11 +277,7 @@ jobs:
|
||||
- name: Run Tests
|
||||
working-directory: ./frontend
|
||||
run: |
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run test:e2e -x --workers=2 --reporter=list --shard "4/4";
|
||||
./scripts/test-e2e --shard="4/4";
|
||||
|
||||
- name: Upload test result
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -80,3 +80,4 @@ node_modules
|
||||
/playwright/.cache/
|
||||
/render-wasm/target/
|
||||
/**/.yarn/*
|
||||
/.pnpm-store
|
||||
13
CHANGES.md
13
CHANGES.md
@@ -1,5 +1,18 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 2.13.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
||||
## 2.12.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
From: {{profile.fullname}} <{{profile.email}}> / {{profile.id}}
|
||||
Subject: {{feedback-subject}}
|
||||
Type: {{feedback-type}}
|
||||
{%- if feedback-error-href %}
|
||||
|
||||
{% if feedback-error-href %}
|
||||
HREF: {{feedback-error-href}}
|
||||
{% endif -%}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
:uid uuid/zero})
|
||||
body (t/encode {:events events})
|
||||
headers {"content-type" "application/transit+json"
|
||||
"origin" (cf/get :public-uri)
|
||||
"origin" (str (cf/get :public-uri))
|
||||
"cookie" (u/map->query-string {:auth-token token})}
|
||||
params {:uri uri
|
||||
:timeout 12000
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[app.common.exceptions :as ex]
|
||||
[selmer.parser :as sp]))
|
||||
|
||||
(sp/cache-off!)
|
||||
;; (sp/cache-off!)
|
||||
|
||||
(defn render
|
||||
[path context]
|
||||
|
||||
@@ -318,3 +318,35 @@
|
||||
;; check that we have all no objects
|
||||
(let [rows (th/db-exec! ["select * from storage_object where deleted_at is null"])]
|
||||
(t/is (= 0 (count rows))))))
|
||||
|
||||
(t/deftest tempfile-bucket-test
|
||||
(let [storage (-> (:app.storage/storage th/*system*)
|
||||
(configure-storage-backend))
|
||||
content1 (sto/content "content1")
|
||||
now (ct/now)
|
||||
|
||||
object1 (sto/put-object! storage {::sto/content content1
|
||||
::sto/touched-at (ct/plus now {:minutes 1})
|
||||
:bucket "tempfile"
|
||||
:content-type "text/plain"})]
|
||||
|
||||
|
||||
(binding [ct/*clock* (clock/fixed now)]
|
||||
(let [res (th/run-task! :storage-gc-touched {})]
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 0 (:delete res)))))
|
||||
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:minutes 1}))]
|
||||
(let [res (th/run-task! :storage-gc-touched {})]
|
||||
(t/is (= 0 (:freeze res)))
|
||||
(t/is (= 1 (:delete res)))))
|
||||
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:hours 1}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 0 (:deleted res)))))
|
||||
|
||||
(binding [ct/*clock* (clock/fixed (ct/plus now {:hours 2}))]
|
||||
(let [res (th/run-task! :storage-gc-deleted {})]
|
||||
(t/is (= 0 (:deleted res)))))))
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
org.slf4j/slf4j-api {:mvn/version "2.0.17"}
|
||||
pl.tkowalcz.tjahzi/log4j2-appender {:mvn/version "0.9.40"}
|
||||
|
||||
selmer/selmer {:mvn/version "1.12.62"}
|
||||
selmer/selmer {:mvn/version "1.12.69"}
|
||||
criterium/criterium {:mvn/version "0.4.6"}
|
||||
|
||||
metosin/jsonista {:mvn/version "0.3.13"}
|
||||
@@ -29,7 +29,6 @@
|
||||
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
|
||||
integrant/integrant {:mvn/version "1.0.0"}
|
||||
|
||||
funcool/tubax {:mvn/version "2021.05.20-0"}
|
||||
funcool/cuerdas {:mvn/version "2025.06.16-414"}
|
||||
funcool/promesa
|
||||
{:git/sha "46048fc0d4bf5466a2a4121f5d52aefa6337f2e8"
|
||||
@@ -48,12 +47,8 @@
|
||||
com.sun.mail/jakarta.mail {:mvn/version "2.0.2"}
|
||||
org.la4j/la4j {:mvn/version "0.6.0"}
|
||||
|
||||
;; exception printing
|
||||
fipp/fipp {:mvn/version "0.6.29"}
|
||||
|
||||
me.flowthing/pp {:mvn/version "2024-11-13.77"}
|
||||
|
||||
|
||||
io.aviso/pretty {:mvn/version "1.4.4"}
|
||||
environ/environ {:mvn/version "1.2.0"}}
|
||||
:paths ["src" "vendor" "target/classes"]
|
||||
|
||||
7
common/scripts/test
Executable file
7
common/scripts/test
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run test;
|
||||
@@ -9,10 +9,10 @@
|
||||
(:refer-clojure :exclude [get-in select-keys str with-open max])
|
||||
#?(:cljs (:require-macros [app.common.data.macros]))
|
||||
(:require
|
||||
#?(:clj [cljs.analyzer.api :as aapi])
|
||||
#?(:clj [clojure.core :as c]
|
||||
:cljs [cljs.core :as c])
|
||||
[app.common.data :as d]
|
||||
[cljs.analyzer.api :as aapi]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defmacro select-keys
|
||||
@@ -44,42 +44,43 @@
|
||||
[& params]
|
||||
`(str/concat ~@params))
|
||||
|
||||
(defmacro export
|
||||
"A helper macro that allows reexport a var in a current namespace."
|
||||
[v]
|
||||
(if (boolean (:ns &env))
|
||||
#?(:clj
|
||||
(defmacro export
|
||||
"A helper macro that allows reexport a var in a current namespace."
|
||||
[v]
|
||||
(if (boolean (:ns &env))
|
||||
|
||||
;; Code for ClojureScript
|
||||
(let [mdata (aapi/resolve &env v)
|
||||
arglists (second (get-in mdata [:meta :arglists]))
|
||||
sym (symbol (c/name v))
|
||||
andsym (symbol "&")
|
||||
procarg #(if (= % andsym) % (gensym "param"))]
|
||||
(if (pos? (count arglists))
|
||||
`(def
|
||||
~(with-meta sym (:meta mdata))
|
||||
(fn ~@(for [args arglists]
|
||||
(let [args (map procarg args)]
|
||||
(if (some #(= andsym %) args)
|
||||
(let [[sargs dargs] (split-with #(not= andsym %) args)]
|
||||
`([~@sargs ~@dargs] (apply ~v ~@sargs ~@(rest dargs))))
|
||||
`([~@args] (~v ~@args)))))))
|
||||
`(def ~(with-meta sym (:meta mdata)) ~v)))
|
||||
;; Code for ClojureScript
|
||||
(let [mdata (aapi/resolve &env v)
|
||||
arglists (second (get-in mdata [:meta :arglists]))
|
||||
sym (symbol (c/name v))
|
||||
andsym (symbol "&")
|
||||
procarg #(if (= % andsym) % (gensym "param"))]
|
||||
(if (pos? (count arglists))
|
||||
`(def
|
||||
~(with-meta sym (:meta mdata))
|
||||
(fn ~@(for [args arglists]
|
||||
(let [args (map procarg args)]
|
||||
(if (some #(= andsym %) args)
|
||||
(let [[sargs dargs] (split-with #(not= andsym %) args)]
|
||||
`([~@sargs ~@dargs] (apply ~v ~@sargs ~@(rest dargs))))
|
||||
`([~@args] (~v ~@args)))))))
|
||||
`(def ~(with-meta sym (:meta mdata)) ~v)))
|
||||
|
||||
;; Code for Clojure
|
||||
(let [vr (resolve v)
|
||||
m (meta vr)
|
||||
n (:name m)
|
||||
n (with-meta n
|
||||
(cond-> {}
|
||||
(:dynamic m) (assoc :dynamic true)
|
||||
(:protocol m) (assoc :protocol (:protocol m))))]
|
||||
`(let [m# (meta ~vr)]
|
||||
(def ~n (deref ~vr))
|
||||
(alter-meta! (var ~n) merge (dissoc m# :name))
|
||||
;; (when (:macro m#)
|
||||
;; (.setMacro (var ~n)))
|
||||
~vr))))
|
||||
;; Code for Clojure
|
||||
(let [vr (resolve v)
|
||||
m (meta vr)
|
||||
n (:name m)
|
||||
n (with-meta n
|
||||
(cond-> {}
|
||||
(:dynamic m) (assoc :dynamic true)
|
||||
(:protocol m) (assoc :protocol (:protocol m))))]
|
||||
`(let [m# (meta ~vr)]
|
||||
(def ~n (deref ~vr))
|
||||
(alter-meta! (var ~n) merge (dissoc m# :name))
|
||||
;; (when (:macro m#)
|
||||
;; (.setMacro (var ~n)))
|
||||
~vr)))))
|
||||
|
||||
(defmacro fmt
|
||||
"String interpolation helper. Can only be used with strings known at
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
[app.common.schema :as sm]
|
||||
[clojure.core :as c]
|
||||
[clojure.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[expound.alpha :as expound])
|
||||
[cuerdas.core :as str])
|
||||
#?(:clj
|
||||
(:import
|
||||
clojure.lang.IPersistentMap)))
|
||||
@@ -110,13 +109,6 @@
|
||||
(contains? data :explain))
|
||||
(explain (:explain data) opts)
|
||||
|
||||
(and (contains? data ::s/problems)
|
||||
(contains? data ::s/value)
|
||||
(contains? data ::s/spec))
|
||||
(binding [s/*explain-out* expound/printer]
|
||||
(with-out-str
|
||||
(s/explain-out (update data ::s/problems #(take (:length opts 10) %)))))
|
||||
|
||||
(contains? data ::sm/explain)
|
||||
(sm/humanize-explain (::sm/explain data) opts)))
|
||||
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
"
|
||||
#?(:cljs (:require-macros [app.common.logging :as l]))
|
||||
(:require
|
||||
#?(:clj [clojure.edn :as edn]
|
||||
:cljs [cljs.reader :as edn])
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.pprint :as pp]
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.logic.variant-properties :as clvp]
|
||||
[app.common.path-names :as cpn]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.types.container :as ctn]
|
||||
@@ -35,8 +34,7 @@
|
||||
[app.common.types.typography :as cty]
|
||||
[app.common.types.variant :as ctv]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]
|
||||
[clojure.spec.alpha :as s]))
|
||||
[clojure.set :as set]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
||||
(log/set-level! :warn)
|
||||
@@ -473,10 +471,10 @@
|
||||
If an asset id is given, only shapes linked to this particular asset will
|
||||
be synchronized."
|
||||
[changes file-id asset-type asset-id library-id libraries current-file-id]
|
||||
(s/assert #{:colors :components :typographies} asset-type)
|
||||
(s/assert (s/nilable ::us/uuid) asset-id)
|
||||
(s/assert ::us/uuid file-id)
|
||||
(s/assert ::us/uuid library-id)
|
||||
(assert (contains? #{:colors :components :typographies} asset-type))
|
||||
(assert (or (nil? asset-id) (uuid? asset-id)))
|
||||
(assert (uuid? file-id))
|
||||
(assert (uuid? library-id))
|
||||
|
||||
(container-log :info asset-id
|
||||
:msg "Sync file with library"
|
||||
@@ -510,10 +508,10 @@
|
||||
If an asset id is given, only shapes linked to this particular asset will
|
||||
be synchronized."
|
||||
[changes file-id asset-type asset-id library-id libraries current-file-id]
|
||||
(s/assert #{:colors :components :typographies} asset-type)
|
||||
(s/assert (s/nilable ::us/uuid) asset-id)
|
||||
(s/assert ::us/uuid file-id)
|
||||
(s/assert ::us/uuid library-id)
|
||||
(assert (contains? #{:colors :components :typographies} asset-type))
|
||||
(assert (or (nil? asset-id) (uuid? asset-id)))
|
||||
(assert (uuid? file-id))
|
||||
(assert (uuid? library-id))
|
||||
|
||||
(container-log :info asset-id
|
||||
:msg "Sync local components with library"
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
(:refer-clojure :exclude [deref merge parse-uuid parse-long parse-double parse-boolean type keys])
|
||||
#?(:cljs (:require-macros [app.common.schema :refer [ignoring]]))
|
||||
(:require
|
||||
#?(:clj [malli.dev.pretty :as mdp])
|
||||
#?(:clj [malli.dev.virhe :as v])
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pprint :as pp]
|
||||
@@ -19,8 +21,6 @@
|
||||
[clojure.core :as c]
|
||||
[cuerdas.core :as str]
|
||||
[malli.core :as m]
|
||||
[malli.dev.pretty :as mdp]
|
||||
[malli.dev.virhe :as v]
|
||||
[malli.error :as me]
|
||||
[malli.generator :as mg]
|
||||
[malli.registry :as mr]
|
||||
@@ -245,27 +245,30 @@
|
||||
:level (d/nilv level 8)
|
||||
:length (d/nilv length 12)})))))
|
||||
|
||||
(defmethod v/-format ::schemaless-explain
|
||||
[_ explanation printer]
|
||||
{:body [:group
|
||||
(v/-block "Value" (v/-visit (me/error-value explanation printer) printer) printer) :break :break
|
||||
(v/-block "Errors" (v/-visit (me/humanize (me/with-spell-checking explanation)) printer) printer)]})
|
||||
#?(:clj
|
||||
(defmethod v/-format ::schemaless-explain
|
||||
[_ explanation printer]
|
||||
{:body [:group
|
||||
(v/-block "Value" (v/-visit (me/error-value explanation printer) printer) printer) :break :break
|
||||
(v/-block "Errors" (v/-visit (me/humanize (me/with-spell-checking explanation)) printer) printer)]}))
|
||||
|
||||
(defmethod v/-format ::explain
|
||||
[_ {:keys [schema] :as explanation} printer]
|
||||
{:body [:group
|
||||
(v/-block "Value" (v/-visit (me/error-value explanation printer) printer) printer) :break :break
|
||||
(v/-block "Errors" (v/-visit (me/humanize (me/with-spell-checking explanation)) printer) printer) :break :break
|
||||
(v/-block "Schema" (v/-visit schema printer) printer)]})
|
||||
#?(:clj
|
||||
(defmethod v/-format ::explain
|
||||
[_ {:keys [schema] :as explanation} printer]
|
||||
{:body [:group
|
||||
(v/-block "Value" (v/-visit (me/error-value explanation printer) printer) printer) :break :break
|
||||
(v/-block "Errors" (v/-visit (me/humanize (me/with-spell-checking explanation)) printer) printer) :break :break
|
||||
(v/-block "Schema" (v/-visit schema printer) printer)]}))
|
||||
|
||||
(defn pretty-explain
|
||||
"A helper that allows print a console-friendly output for the
|
||||
explain; should not be used for other purposes"
|
||||
[explain & {:keys [variant message]
|
||||
:or {variant ::explain
|
||||
message "Validation Error"}}]
|
||||
(let [explain (fn [] (me/with-error-messages explain))]
|
||||
((mdp/prettifier variant message explain default-options))))
|
||||
#?(:clj
|
||||
(defn pretty-explain
|
||||
"A helper that allows print a console-friendly output for the explain;
|
||||
should not be used for other purposes"
|
||||
[explain & {:keys [variant message]
|
||||
:or {variant ::explain
|
||||
message "Validation Error"}}]
|
||||
(let [explain (fn [] (me/with-error-messages explain))]
|
||||
((mdp/prettifier variant message explain default-options)))))
|
||||
|
||||
(defmacro ignoring
|
||||
[expr]
|
||||
@@ -299,6 +302,13 @@
|
||||
::explain explain}))))
|
||||
value))))
|
||||
|
||||
(defn coercer
|
||||
[schema & {:as opts}]
|
||||
(let [decode-fn (decoder schema json-transformer)
|
||||
check-fn (check-fn schema opts)]
|
||||
(fn [data]
|
||||
(-> data decode-fn check-fn))))
|
||||
|
||||
(defn check
|
||||
"A helper intended to be used on assertions for validate/check the
|
||||
schema over provided data. Raises an assertion exception.
|
||||
@@ -842,38 +852,6 @@
|
||||
choices))]
|
||||
{:pred pred}))})
|
||||
|
||||
;; (register!
|
||||
;; {:type ::inst
|
||||
;; :pred tm/instant?
|
||||
;; :type-properties
|
||||
;; {:title "inst"
|
||||
;; :description "Satisfies Inst protocol"
|
||||
;; :error/message "should be an instant"
|
||||
;; :gen/gen (->> (sg/small-int :min 0 :max 100000)
|
||||
;; (sg/fmap (fn [v] (tm/parse-inst v))))
|
||||
|
||||
;; :decode/string tm/parse-inst
|
||||
;; :encode/string tm/format-inst
|
||||
;; :decode/json tm/parse-inst
|
||||
;; :encode/json tm/format-inst
|
||||
;; ::oapi/type "string"
|
||||
;; ::oapi/format "iso"}})
|
||||
|
||||
;; (register!
|
||||
;; {:type ::timestamp
|
||||
;; :pred tm/instant?
|
||||
;; :type-properties
|
||||
;; {:title "inst"
|
||||
;; :description "Satisfies Inst protocol, the same as ::inst but encodes to epoch"
|
||||
;; :error/message "should be an instant"
|
||||
;; :gen/gen (->> (sg/small-int)
|
||||
;; (sg/fmap (fn [v] (tm/parse-inst v))))
|
||||
;; :decode/string tm/parse-inst
|
||||
;; :encode/string inst-ms
|
||||
;; :decode/json tm/parse-inst
|
||||
;; :encode/json inst-ms
|
||||
;; ::oapi/type "string"
|
||||
;; ::oapi/format "number"}})
|
||||
|
||||
#?(:clj
|
||||
(register!
|
||||
@@ -951,7 +929,7 @@
|
||||
:pred #(and (string? %) (not (str/blank? %)))
|
||||
:property-pred
|
||||
(fn [{:keys [min max] :as props}]
|
||||
(if (seq props)
|
||||
(if (or min max)
|
||||
(fn [value]
|
||||
(let [size (count value)]
|
||||
(cond
|
||||
@@ -1025,6 +1003,9 @@
|
||||
(def valid-safe-number?
|
||||
(lazy-validator ::safe-number))
|
||||
|
||||
(def valid-safe-int?
|
||||
(lazy-validator ::safe-int))
|
||||
|
||||
(def valid-text?
|
||||
(validator ::text))
|
||||
|
||||
|
||||
@@ -25,48 +25,6 @@ RUN set -ex; \
|
||||
binutils \
|
||||
build-essential autoconf libtool pkg-config
|
||||
|
||||
|
||||
################################################################################
|
||||
## IMAGE MAGICK
|
||||
################################################################################
|
||||
|
||||
FROM base AS build-imagemagick
|
||||
|
||||
ENV IMAGEMAGICK_VERSION=7.1.1-47 \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN set -ex; \
|
||||
apt-get -qq update; \
|
||||
apt-get -qq upgrade; \
|
||||
apt-get -qqy --no-install-recommends install \
|
||||
libltdl-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libtiff-dev \
|
||||
libwebp-dev \
|
||||
libopenexr-dev \
|
||||
libfftw3-dev \
|
||||
libzip-dev \
|
||||
liblcms2-dev \
|
||||
liblzma-dev \
|
||||
libzstd-dev \
|
||||
libheif-dev \
|
||||
librsvg2-dev \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN set -eux; \
|
||||
curl -LfsSo /tmp/magick.tar.gz https://github.com/ImageMagick/ImageMagick/archive/refs/tags/${IMAGEMAGICK_VERSION}.tar.gz; \
|
||||
mkdir -p /tmp/magick; \
|
||||
cd /tmp/magick; \
|
||||
tar -xf /tmp/magick.tar.gz --strip-components=1; \
|
||||
./configure --prefix=/opt/imagick; \
|
||||
make -j 2; \
|
||||
make install; \
|
||||
rm -rf /opt/imagick/lib/libMagick++*; \
|
||||
rm -rf /opt/imagick/include; \
|
||||
rm -rf /opt/imagick/share;
|
||||
|
||||
################################################################################
|
||||
## NODE SETUP
|
||||
################################################################################
|
||||
@@ -107,7 +65,7 @@ RUN set -eux; \
|
||||
|
||||
FROM base AS setup-jvm
|
||||
|
||||
ENV CLOJURE_VERSION=1.12.2.1565
|
||||
ENV CLOJURE_VERSION=1.12.3.1577
|
||||
|
||||
RUN set -eux; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
@@ -385,7 +343,7 @@ ENV LANG='C.UTF-8' \
|
||||
RUSTUP_HOME="/opt/rustup" \
|
||||
PATH="/opt/jdk/bin:/opt/utils/bin:/opt/clojure/bin:/opt/node/bin:/opt/imagick/bin:/opt/cargo/bin:$PATH"
|
||||
|
||||
COPY --from=build-imagemagick /opt/imagick /opt/imagick
|
||||
COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick
|
||||
COPY --from=setup-jvm /opt/jdk /opt/jdk
|
||||
COPY --from=setup-jvm /opt/clojure /opt/clojure
|
||||
COPY --from=setup-node /opt/node /opt/node
|
||||
|
||||
@@ -67,6 +67,11 @@ services:
|
||||
- PENPOT_LDAP_ATTRS_FULLNAME=cn
|
||||
- PENPOT_LDAP_ATTRS_PHOTO=jpegPhoto
|
||||
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- main
|
||||
|
||||
minio:
|
||||
image: "minio/minio:RELEASE.2025-04-03T14-56-28Z"
|
||||
command: minio server /mnt/data --console-address ":9001"
|
||||
@@ -78,10 +83,6 @@ services:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 9001:9001
|
||||
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
|
||||
@@ -12,7 +12,7 @@ http {
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
keepalive_timeout 0;
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
@@ -223,16 +223,6 @@ http {
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
}
|
||||
|
||||
location ~ ^/js/config.js$ {
|
||||
add_header Cache-Control "no-store, no-cache, max-age=0" always;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|jpg|svg|png|mjs|map)$ {
|
||||
# We set no cache only on devenv
|
||||
add_header Cache-Control "no-store, no-cache, max-age=0" always;
|
||||
# add_header Cache-Control "max-age=604800" always; # 7 days
|
||||
}
|
||||
|
||||
location ~ ^/(/|css|fonts|images|js|wasm|mjs|map) {
|
||||
}
|
||||
|
||||
@@ -240,9 +230,10 @@ http {
|
||||
return 301 " /404";
|
||||
}
|
||||
|
||||
add_header Last-Modified $date_gmt;
|
||||
add_header Cache-Control "no-store, no-cache, max-age=0" always;
|
||||
if_modified_since off;
|
||||
add_header Cache-Control "no-store";
|
||||
add_header Connection close always;
|
||||
# This header is what we need to use on prod
|
||||
# add_header Cache-Control "public, must-revalidate, max-age=0";
|
||||
try_files $uri /index.html$is_args$args /index.html =404;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
docs/img/variants/07-variants-boolean.webp
Normal file
BIN
docs/img/variants/07-variants-boolean.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -107,6 +107,25 @@ desc: Streamline your design workflow with Penpot's Components guide! Learn to c
|
||||
<li>Select the variant copy, press right-click, and select the menu option <strong>Restore variant</strong> (will show if the main component still exists).</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="component-variants-toggle">Toggle for boolean variants</h3>
|
||||
<p>When a variant property represents a boolean state, Penpot can display it as a toggle instead of a dropdown. This offers a quicker and more visual way to switch between two opposite values when working with copies.</p>
|
||||
<p>The toggle appears in place of the property values dropdown, <strong>only when a copy is selected</strong>.</p>
|
||||
<figure>
|
||||
<img src="/img/variants/07-variants-boolean.webp" alt="Boolean variant option" />
|
||||
</figure>
|
||||
<h4>Accepted value pairs</h4>
|
||||
<p>For Penpot to recognize the property as a boolean and display the toggle, the property must be defined with exactly two opposing values. These can be any of the following pairs:</p>
|
||||
<ul>
|
||||
<li><code>true</code> / <code>false</code></li>
|
||||
<li><code>on</code> / <code>off</code></li>
|
||||
<li><code>yes</code> / <code>no</code></li>
|
||||
</ul>
|
||||
<p>The order of the values does not matter. Penpot automatically maps them to ON and OFF states:</p>
|
||||
<ul>
|
||||
<li><strong>ON state:</strong> <code>true</code>, <code>yes</code>, <code>on</code></li>
|
||||
<li><strong>OFF state:</strong> <code>false</code>, <code>no</code>, <code>off</code></li>
|
||||
</ul>
|
||||
|
||||
<h3 id="component-use-variants">Use variants</h3>
|
||||
<p>Once you have created your variants, you can place a copy of a component with variants into your design and then switch between its different versions.</p>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
|
||||
"packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/penpot/penpot"
|
||||
@@ -16,9 +16,9 @@
|
||||
"date-fns": "^4.1.0",
|
||||
"generic-pool": "^3.9.0",
|
||||
"inflation": "^2.1.0",
|
||||
"ioredis": "^5.8.1",
|
||||
"playwright": "^1.55.1",
|
||||
"raw-body": "^3.0.1",
|
||||
"ioredis": "^5.8.2",
|
||||
"playwright": "^1.57.0",
|
||||
"raw-body": "^3.0.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"svgo": "penpot/svgo#v3.1",
|
||||
"xml-js": "^1.6.11",
|
||||
|
||||
@@ -243,7 +243,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bytes@npm:3.1.2":
|
||||
"bytes@npm:~3.1.2":
|
||||
version: 3.1.2
|
||||
resolution: "bytes@npm:3.1.2"
|
||||
checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e
|
||||
@@ -442,7 +442,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"depd@npm:2.0.0, depd@npm:~2.0.0":
|
||||
"depd@npm:~2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "depd@npm:2.0.0"
|
||||
checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c
|
||||
@@ -577,9 +577,9 @@ __metadata:
|
||||
date-fns: "npm:^4.1.0"
|
||||
generic-pool: "npm:^3.9.0"
|
||||
inflation: "npm:^2.1.0"
|
||||
ioredis: "npm:^5.8.1"
|
||||
playwright: "npm:^1.55.1"
|
||||
raw-body: "npm:^3.0.1"
|
||||
ioredis: "npm:^5.8.2"
|
||||
playwright: "npm:^1.57.0"
|
||||
raw-body: "npm:^3.0.2"
|
||||
source-map-support: "npm:^0.5.21"
|
||||
svgo: "penpot/svgo#v3.1"
|
||||
ws: "npm:^8.18.3"
|
||||
@@ -682,16 +682,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-errors@npm:2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "http-errors@npm:2.0.0"
|
||||
"http-errors@npm:~2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "http-errors@npm:2.0.1"
|
||||
dependencies:
|
||||
depd: "npm:2.0.0"
|
||||
inherits: "npm:2.0.4"
|
||||
setprototypeof: "npm:1.2.0"
|
||||
statuses: "npm:2.0.1"
|
||||
toidentifier: "npm:1.0.1"
|
||||
checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19
|
||||
depd: "npm:~2.0.0"
|
||||
inherits: "npm:~2.0.4"
|
||||
setprototypeof: "npm:~1.2.0"
|
||||
statuses: "npm:~2.0.2"
|
||||
toidentifier: "npm:~1.0.1"
|
||||
checksum: 10c0/fb38906cef4f5c83952d97661fe14dc156cb59fe54812a42cd448fa57b5c5dfcb38a40a916957737bd6b87aab257c0648d63eb5b6a9ca9f548e105b6072712d4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -715,15 +715,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iconv-lite@npm:0.7.0":
|
||||
version: 0.7.0
|
||||
resolution: "iconv-lite@npm:0.7.0"
|
||||
dependencies:
|
||||
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
|
||||
checksum: 10c0/2382400469071c55b6746c531eed5fa4d033e5db6690b7331fb2a5f59a30d7a9782932e92253db26df33c1cf46fa200a3fbe524a2a7c62037c762283f188ec2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iconv-lite@npm:^0.6.2":
|
||||
version: 0.6.3
|
||||
resolution: "iconv-lite@npm:0.6.3"
|
||||
@@ -733,6 +724,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"iconv-lite@npm:~0.7.0":
|
||||
version: 0.7.0
|
||||
resolution: "iconv-lite@npm:0.7.0"
|
||||
dependencies:
|
||||
safer-buffer: "npm:>= 2.1.2 < 3.0.0"
|
||||
checksum: 10c0/2382400469071c55b6746c531eed5fa4d033e5db6690b7331fb2a5f59a30d7a9782932e92253db26df33c1cf46fa200a3fbe524a2a7c62037c762283f188ec2f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ieee754@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "ieee754@npm:1.2.1"
|
||||
@@ -754,16 +754,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"inherits@npm:2.0.4, inherits@npm:~2.0.3":
|
||||
"inherits@npm:~2.0.3, inherits@npm:~2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "inherits@npm:2.0.4"
|
||||
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ioredis@npm:^5.8.1":
|
||||
version: 5.8.1
|
||||
resolution: "ioredis@npm:5.8.1"
|
||||
"ioredis@npm:^5.8.2":
|
||||
version: 5.8.2
|
||||
resolution: "ioredis@npm:5.8.2"
|
||||
dependencies:
|
||||
"@ioredis/commands": "npm:1.4.0"
|
||||
cluster-key-slot: "npm:^1.1.0"
|
||||
@@ -774,7 +774,7 @@ __metadata:
|
||||
redis-errors: "npm:^1.2.0"
|
||||
redis-parser: "npm:^3.0.0"
|
||||
standard-as-callback: "npm:^2.1.0"
|
||||
checksum: 10c0/4ed66444017150da027bce940a24bf726994691e2a7b3aa11d52f8aeb37f258068cc171af4d9c61247acafc28eb086fa8a7c79420b8e8d2907d2f74f39584465
|
||||
checksum: 10c0/305e385f811d49908899e32c2de69616cd059f909afd9e0a53e54f596b1a5835ee3449bfc6a3c49afbc5a2fd27990059e316cc78f449c94024957bd34c826d88
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1105,27 +1105,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright-core@npm:1.55.1":
|
||||
version: 1.55.1
|
||||
resolution: "playwright-core@npm:1.55.1"
|
||||
"playwright-core@npm:1.57.0":
|
||||
version: 1.57.0
|
||||
resolution: "playwright-core@npm:1.57.0"
|
||||
bin:
|
||||
playwright-core: cli.js
|
||||
checksum: 10c0/39837a8c1232ec27486eac8c3fcacc0b090acc64310f7f9004b06715370fc426f944e3610fe8c29f17cd3d68280ed72c75f660c02aa5b5cf0eb34bab0031308f
|
||||
checksum: 10c0/798e35d83bf48419a8c73de20bb94d68be5dde68de23f95d80a0ebe401e3b83e29e3e84aea7894d67fa6c79d2d3d40cc5bcde3e166f657ce50987aaa2421b6a9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"playwright@npm:^1.55.1":
|
||||
version: 1.55.1
|
||||
resolution: "playwright@npm:1.55.1"
|
||||
"playwright@npm:^1.57.0":
|
||||
version: 1.57.0
|
||||
resolution: "playwright@npm:1.57.0"
|
||||
dependencies:
|
||||
fsevents: "npm:2.3.2"
|
||||
playwright-core: "npm:1.55.1"
|
||||
playwright-core: "npm:1.57.0"
|
||||
dependenciesMeta:
|
||||
fsevents:
|
||||
optional: true
|
||||
bin:
|
||||
playwright: cli.js
|
||||
checksum: 10c0/b84a97b0d764403df512f5bbb10c7343974e151a28202cc06f90883a13e8a45f4491a0597f0ae5fb03a026746cbc0d200f0f32195bfaa381aee5ca5770626771
|
||||
checksum: 10c0/ab03c99a67b835bdea9059f516ad3b6e42c21025f9adaa161a4ef6bc7ca716dcba476d287140bb240d06126eb23f889a8933b8f5f1f1a56b80659d92d1358899
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1160,15 +1160,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"raw-body@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "raw-body@npm:3.0.1"
|
||||
"raw-body@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "raw-body@npm:3.0.2"
|
||||
dependencies:
|
||||
bytes: "npm:3.1.2"
|
||||
http-errors: "npm:2.0.0"
|
||||
iconv-lite: "npm:0.7.0"
|
||||
unpipe: "npm:1.0.0"
|
||||
checksum: 10c0/892f4fbd21ecab7e2fed0f045f7af9e16df7e8050879639d4e482784a2f4640aaaa33d916a0e98013f23acb82e09c2e3c57f84ab97104449f728d22f65a7d79a
|
||||
bytes: "npm:~3.1.2"
|
||||
http-errors: "npm:~2.0.1"
|
||||
iconv-lite: "npm:~0.7.0"
|
||||
unpipe: "npm:~1.0.0"
|
||||
checksum: 10c0/d266678d08e1e7abea62c0ce5864344e980fa81c64f6b481e9842c5beaed2cdcf975f658a3ccd67ad35fc919c1f6664ccc106067801850286a6cbe101de89f29
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1269,7 +1269,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"setprototypeof@npm:1.2.0":
|
||||
"setprototypeof@npm:~1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "setprototypeof@npm:1.2.0"
|
||||
checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc
|
||||
@@ -1367,10 +1367,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"statuses@npm:2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "statuses@npm:2.0.1"
|
||||
checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0
|
||||
"statuses@npm:~2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "statuses@npm:2.0.2"
|
||||
checksum: 10c0/a9947d98ad60d01f6b26727570f3bcceb6c8fa789da64fe6889908fe2e294d57503b14bf2b5af7605c2d36647259e856635cd4c49eab41667658ec9d0080ec3f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1499,7 +1499,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"toidentifier@npm:1.0.1":
|
||||
"toidentifier@npm:~1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "toidentifier@npm:1.0.1"
|
||||
checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1
|
||||
@@ -1531,7 +1531,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"unpipe@npm:1.0.0":
|
||||
"unpipe@npm:~1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "unpipe@npm:1.0.0"
|
||||
checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
diff --git a/lib/zip-fs.js b/lib/zip-fs.js
|
||||
index 1444c0f00e5f1ad6c13521f90a7f3c6659d81116..90e38baef5365c2abbcb9337f7ab37f800e883a4 100644
|
||||
--- a/lib/zip-fs.js
|
||||
+++ b/lib/zip-fs.js
|
||||
@@ -33,12 +33,7 @@ import { initShimAsyncCodec } from "./core/util/stream-codec-shim.js";
|
||||
import { terminateWorkers } from "./core/codec-pool.js";
|
||||
|
||||
let baseURL;
|
||||
-try {
|
||||
- baseURL = import.meta.url;
|
||||
- // eslint-disable-next-line no-unused-vars
|
||||
-} catch (_) {
|
||||
- // ignored
|
||||
-}
|
||||
+
|
||||
configure({ baseURL });
|
||||
configureWebWorker(configure);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
diff --git a/lib/zip-core-base.js b/lib/zip-core-base.js
|
||||
index 142155c8c3ab1a6caa7c370e8a2931a954556ca9..61b0fe6efb91312f437750e0002218f218afad8b 100644
|
||||
--- a/lib/zip-core-base.js
|
||||
+++ b/lib/zip-core-base.js
|
||||
@@ -28,12 +28,6 @@
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
|
||||
-try {
|
||||
- configure({ baseURI: import.meta.url });
|
||||
-} catch {
|
||||
- // ignored
|
||||
-}
|
||||
-
|
||||
export * from "./zip-core-reader.js";
|
||||
export * from "./zip-core-writer.js";
|
||||
export {
|
||||
@@ -8,6 +8,11 @@
|
||||
metosin/reitit-core {:mvn/version "0.9.1"}
|
||||
funcool/okulary {:mvn/version "2022.04.11-16"}
|
||||
|
||||
funcool/tubax
|
||||
{:git/tag "v2025.11.28"
|
||||
:git/sha "2d9a986"
|
||||
:git/url "https://github.com/funcool/tubax.git"}
|
||||
|
||||
funcool/potok2
|
||||
{:git/tag "v2.2"
|
||||
:git/sha "0f7e15a"
|
||||
@@ -20,8 +25,8 @@
|
||||
:git/url "https://github.com/funcool/beicon.git"}
|
||||
|
||||
funcool/rumext
|
||||
{:git/tag "v2.24"
|
||||
:git/sha "17a0c94"
|
||||
{:git/tag "v2.25"
|
||||
:git/sha "27e5a1a"
|
||||
:git/url "https://github.com/funcool/rumext.git"}
|
||||
|
||||
instaparse/instaparse {:mvn/version "1.5.0"}
|
||||
@@ -42,7 +47,7 @@
|
||||
:dev
|
||||
{:extra-paths ["dev"]
|
||||
:extra-deps
|
||||
{thheller/shadow-cljs {:mvn/version "3.2.0"}
|
||||
{thheller/shadow-cljs {:mvn/version "3.2.2"}
|
||||
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
|
||||
org.clojure/tools.namespace {:mvn/version "RELEASE"}
|
||||
criterium/criterium {:mvn/version "RELEASE"}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"license": "MPL-2.0",
|
||||
"author": "Kaleidos INC",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
|
||||
"packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8",
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
],
|
||||
@@ -14,10 +14,10 @@
|
||||
"url": "https://github.com/penpot/penpot"
|
||||
},
|
||||
"resolutions": {
|
||||
"@zip.js/zip.js@npm:^2.7.44": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"playwright": "1.52.0",
|
||||
"playwright-core": "1.52.0"
|
||||
"playwright-core": "1.52.0",
|
||||
"@zip.js/zip.js@npm:^2.7.44": "patch:@zip.js/zip.js@npm%3A2.8.11#~/.yarn/patches/@zip.js-zip.js-npm-2.8.11-b131c96df8.patch"
|
||||
},
|
||||
"scripts": {
|
||||
"build:app:assets": "node ./scripts/build-app-assets.js",
|
||||
@@ -27,6 +27,7 @@
|
||||
"build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook",
|
||||
"build:app:libs": "node ./scripts/build-libs.js",
|
||||
"build:app:main": "clojure -M:dev:shadow-cljs release main worker",
|
||||
"build:app:worker": "clojure -M:dev:shadow-cljs release worker",
|
||||
"build:app": "yarn run clear:shadow-cache && yarn run build:app:main && yarn run build:app:libs",
|
||||
"e2e:server": "node ./scripts/e2e-server.js",
|
||||
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
|
||||
@@ -52,83 +53,74 @@
|
||||
"watch:storybook:assets": "node ./scripts/watch-storybook.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.52.0",
|
||||
"@storybook/addon-docs": "10.0.4",
|
||||
"@storybook/addon-themes": "10.0.4",
|
||||
"@storybook/addon-vitest": "10.0.4",
|
||||
"@storybook/react-vite": "10.0.4",
|
||||
"@types/node": "^22.15.21",
|
||||
"@penpot/draft-js": "portal:./packages/draft-js",
|
||||
"@penpot/mousetrap": "portal:./packages/mousetrap",
|
||||
"@penpot/plugins-runtime": "1.3.2",
|
||||
"@penpot/svgo": "penpot/svgo#v3.1",
|
||||
"@penpot/text-editor": "portal:./text-editor",
|
||||
"@playwright/test": "1.57.0",
|
||||
"@storybook/addon-docs": "10.1.0",
|
||||
"@storybook/addon-themes": "10.1.0",
|
||||
"@storybook/addon-vitest": "10.1.0",
|
||||
"@storybook/react-vite": "10.1.0",
|
||||
"@tokens-studio/sd-transforms": "1.2.11",
|
||||
"@types/node": "^22.19.1",
|
||||
"@vitest/browser": "3.2.4",
|
||||
"@vitest/coverage-v8": "3.2.4",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.8.11#~/.yarn/patches/@zip.js-zip.js-npm-2.8.11-b131c96df8.patch",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"compression": "^1.8.1",
|
||||
"concurrently": "^9.2.1",
|
||||
"esbuild": "^0.25.9",
|
||||
"date-fns": "^4.1.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"eventsource-parser": "^3.0.6",
|
||||
"express": "^5.1.0",
|
||||
"fancy-log": "^2.0.0",
|
||||
"getopts": "^2.3.0",
|
||||
"gettext-parser": "^8.0.0",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-gzip": "^1.4.2",
|
||||
"gulp-mustache": "^5.0.0",
|
||||
"gulp-postcss": "^10.0.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"gulp-svg-sprite": "^2.0.3",
|
||||
"jsdom": "^27.0.0",
|
||||
"map-stream": "0.0.7",
|
||||
"marked": "^15.0.12",
|
||||
"mkdirp": "^3.0.1",
|
||||
"mustache": "^4.2.0",
|
||||
"nodemon": "^3.1.10",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"p-limit": "^6.2.0",
|
||||
"playwright": "1.56.1",
|
||||
"postcss": "^8.5.4",
|
||||
"postcss-clean": "^1.2.2",
|
||||
"prettier": "3.5.3",
|
||||
"pretty-time": "^1.1.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"sass": "^1.89.0",
|
||||
"sass-embedded": "^1.89.0",
|
||||
"storybook": "10.0.4",
|
||||
"svg-sprite": "^2.0.4",
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "^6.3.5",
|
||||
"vitest": "^3.2.0",
|
||||
"wasm-pack": "^0.13.1",
|
||||
"watcher": "^2.3.1",
|
||||
"workerpool": "^9.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@penpot/draft-js": "portal:./vendor/draft-js",
|
||||
"@penpot/hljs": "portal:./vendor/hljs",
|
||||
"@penpot/mousetrap": "portal:./vendor/mousetrap",
|
||||
"@penpot/plugins-runtime": "1.3.2",
|
||||
"@penpot/svgo": "penpot/svgo#v3.1",
|
||||
"@penpot/text-editor": "portal:./text-editor",
|
||||
"@tokens-studio/sd-transforms": "1.2.11",
|
||||
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",
|
||||
"compression": "^1.8.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"eventsource-parser": "^3.0.6",
|
||||
"highlight.js": "^11.10.0",
|
||||
"js-beautify": "^1.15.4",
|
||||
"jsdom": "^27.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"map-stream": "0.0.7",
|
||||
"marked": "^17.0.1",
|
||||
"mkdirp": "^3.0.1",
|
||||
"mustache": "^4.2.0",
|
||||
"nodemon": "^3.1.11",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"opentype.js": "^1.3.4",
|
||||
"p-limit": "^7.2.0",
|
||||
"playwright": "1.57.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-clean": "^1.2.2",
|
||||
"postcss-modules": "^6.0.1",
|
||||
"prettier": "3.7.1",
|
||||
"pretty-time": "^1.1.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"randomcolor": "^0.6.2",
|
||||
"react": "19.1.1",
|
||||
"react-dom": "19.1.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-virtualized": "^9.22.6",
|
||||
"rimraf": "^6.1.2",
|
||||
"rxjs": "8.0.0-alpha.14",
|
||||
"sax": "^1.4.1",
|
||||
"sass": "^1.89.0",
|
||||
"sass-embedded": "^1.89.0",
|
||||
"sax": "^1.4.3",
|
||||
"source-map-support": "^0.5.21",
|
||||
"storybook": "10.1.0",
|
||||
"style-dictionary": "5.0.0-rc.1",
|
||||
"svg-sprite": "^2.0.4",
|
||||
"tdigest": "^0.1.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"ua-parser-js": "2.0.5",
|
||||
"typescript": "^5.9.3",
|
||||
"ua-parser-js": "2.0.6",
|
||||
"vite": "^6.4.1",
|
||||
"vitest": "^3.2.4",
|
||||
"wasm-pack": "^0.13.1",
|
||||
"watcher": "^2.3.1",
|
||||
"workerpool": "^10.0.1",
|
||||
"xregexp": "^5.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ export const {
|
||||
RichTextEditorUtil,
|
||||
SelectionState,
|
||||
convertFromRaw,
|
||||
convertToRaw
|
||||
convertToRaw,
|
||||
EditorBlock,
|
||||
Editor
|
||||
} = pkg;
|
||||
|
||||
import DraftPasteProcessor from 'draft-js/lib/DraftPasteProcessor.js';
|
||||
@@ -8,7 +8,8 @@
|
||||
"author": "Andrey Antukh",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"draft-js": "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0"
|
||||
"draft-js": "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0",
|
||||
"immutable": "^5.1.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=0.17.0",
|
||||
@@ -173,12 +173,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@penpot/draft-js-wrapper@workspace:.":
|
||||
"@penpot/draft-js@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@penpot/draft-js-wrapper@workspace:."
|
||||
resolution: "@penpot/draft-js@workspace:."
|
||||
dependencies:
|
||||
draft-js: "penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0"
|
||||
esbuild: "npm:^0.24.0"
|
||||
immutable: "npm:^5.1.4"
|
||||
peerDependencies:
|
||||
react: ">=0.17.0"
|
||||
react-dom: ">=0.17.0"
|
||||
@@ -320,6 +321,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"immutable@npm:^5.1.4":
|
||||
version: 5.1.4
|
||||
resolution: "immutable@npm:5.1.4"
|
||||
checksum: 10c0/f1c98382e4cde14a0b218be3b9b2f8441888da8df3b8c064aa756071da55fbed6ad696e5959982508456332419be9fdeaf29b2e58d0eadc45483cc16963c0446
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"immutable@npm:~3.7.4":
|
||||
version: 3.7.6
|
||||
resolution: "immutable@npm:3.7.6"
|
||||
17
frontend/patches/@zip.js__zip.js.patch
Normal file
17
frontend/patches/@zip.js__zip.js.patch
Normal file
@@ -0,0 +1,17 @@
|
||||
diff --git a/lib/zip-core-base.js b/lib/zip-core-base.js
|
||||
index 142155c8c3ab1a6caa7c370e8a2931a954556ca9..61b0fe6efb91312f437750e0002218f218afad8b 100644
|
||||
--- a/lib/zip-core-base.js
|
||||
+++ b/lib/zip-core-base.js
|
||||
@@ -28,12 +28,6 @@
|
||||
|
||||
import { configure } from "./core/configuration.js";
|
||||
|
||||
-try {
|
||||
- configure({ baseURI: import.meta.url });
|
||||
-} catch {
|
||||
- // ignored
|
||||
-}
|
||||
-
|
||||
export * from "./zip-core-reader.js";
|
||||
export * from "./zip-core-writer.js";
|
||||
export {
|
||||
@@ -22,9 +22,9 @@ export default defineConfig({
|
||||
workers: 1,
|
||||
/* Timeout for expects (longer in CI) */
|
||||
|
||||
timeout: 60000,
|
||||
timeout: 80000,
|
||||
expect: {
|
||||
timeout: process.env.CI ? 30000 : 5000,
|
||||
timeout: process.env.CI ? 40000 : 5000,
|
||||
},
|
||||
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -73,7 +73,7 @@ export class BasePage {
|
||||
}
|
||||
|
||||
static async mockConfigFlags(page, flags) {
|
||||
const url = "**/js/config.js?ts=*";
|
||||
const url = "**/js/config.js";
|
||||
return await page.route(url, (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
|
||||
@@ -42,7 +42,7 @@ export class WasmWorkspacePage extends WorkspacePage {
|
||||
}
|
||||
|
||||
async waitForFirstRenderWithoutUI() {
|
||||
await waitForFirstRender();
|
||||
await this.waitForFirstRender();
|
||||
await this.hideUI();
|
||||
}
|
||||
|
||||
|
||||
@@ -258,6 +258,22 @@ test("Renders a file with nested frames with inherited blur", async ({
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders a file with nested clipping frames", async ({ page }) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile(
|
||||
"render-wasm/get-file-frame-nested-clipping.json",
|
||||
);
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
id: "44471494-966a-8178-8006-c5bd93f0fe72",
|
||||
pageId: "44471494-966a-8178-8006-c5bd93f0fe73",
|
||||
});
|
||||
await workspace.waitForFirstRenderWithoutUI();
|
||||
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders a clipped frame with a large blur drop shadow", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -9,403 +9,399 @@ test.beforeEach(async ({ page }) => {
|
||||
]);
|
||||
});
|
||||
|
||||
test.describe("Subscriptions: dashboard", () => {
|
||||
test("Team with unlimited subscription has specific icon in menu", async ({
|
||||
test("Team with unlimited subscription has specific icon in menu", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToSecondTeamDashboard();
|
||||
await expect(page.getByTestId("subscription-icon")).toBeVisible();
|
||||
});
|
||||
|
||||
test("The Unlimited subscription has its name in the sidebar dropdown", async ({
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage-one-editor.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToDashboard();
|
||||
|
||||
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||
"Unlimited plan (trial)",
|
||||
);
|
||||
});
|
||||
|
||||
test("When the subscription status is unpaid, the sidebar dropdown displays the name Professional for the Unlimited subscription", async ({
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-unpaid-subscription.json",
|
||||
);
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToDashboard();
|
||||
|
||||
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||
"Professional plan",
|
||||
);
|
||||
});
|
||||
|
||||
test("When the subscription status is canceled, the sidebar dropdown displays the name Professional for the Enterprise subscription", async ({
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-enterprise-canceled-subscription.json",
|
||||
);
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToDashboard();
|
||||
|
||||
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||
"Professional plan",
|
||||
);
|
||||
});
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToSecondTeamDashboard();
|
||||
await expect(page.getByTestId("subscription-icon")).toBeVisible();
|
||||
});
|
||||
|
||||
test.describe("Subscriptions: team members and invitations", () => {
|
||||
test("Team settings has susbscription name and no manage subscription link when is member", async ({
|
||||
test("The Unlimited subscription has its name in the sidebar dropdown", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in.json",
|
||||
);
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-member.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-member.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-stats?team-id=*",
|
||||
"dashboard/get-team-stats.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamSettingsSection();
|
||||
await expect(page.getByText("Unlimited (trial)")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Manage your subscription" }),
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("Team settings has susbscription name and manage subscription link when is owner", async ({
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage-one-editor.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-stats?team-id=*",
|
||||
"dashboard/get-team-stats.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamSettingsSection();
|
||||
|
||||
await expect(page.getByText("Unlimited (trial)")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Manage your subscription" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("Members tab has warning message when user has more seats than editors.", async ({
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-eight-member.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamMembersSection();
|
||||
|
||||
const ctas = page.getByTestId("cta");
|
||||
await expect(ctas).toHaveCount(2);
|
||||
await expect(
|
||||
page.getByText("Inviting people while on the unlimited plan"),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("Invitations tab has warning message when user has more seats than editors.", async ({
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToDashboard();
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-eight-member.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-invitations?team-id=*",
|
||||
"subscription/get-team-invitations.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamInvitationsSection();
|
||||
|
||||
const ctas = page.getByTestId("cta");
|
||||
await expect(ctas).toHaveCount(2);
|
||||
await expect(
|
||||
page.getByText("Inviting people while on the unlimited plan"),
|
||||
).toBeVisible();
|
||||
});
|
||||
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||
"Unlimited plan (trial)",
|
||||
);
|
||||
});
|
||||
|
||||
test("The sidebar dropdown displays the correct subscription name when status is Unpaid", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-unpaid-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToDashboard();
|
||||
|
||||
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||
"Professional plan",
|
||||
);
|
||||
});
|
||||
|
||||
test("The sidebar dropdown displays the correct subscription name when status is cancelled", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-enterprise-canceled-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
await dashboardPage.goToDashboard();
|
||||
|
||||
await expect(page.getByTestId("subscription-name")).toHaveText(
|
||||
"Professional plan",
|
||||
);
|
||||
});
|
||||
|
||||
test("Team settings has susbscription name and no manage subscription link when is member", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-member.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-member.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-stats?team-id=*",
|
||||
"dashboard/get-team-stats.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamSettingsSection();
|
||||
await expect(page.getByText("Unlimited (trial)")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Manage your subscription" }),
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("Team settings has susbscription name and manage subscription link when is owner", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-stats?team-id=*",
|
||||
"dashboard/get-team-stats.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamSettingsSection();
|
||||
|
||||
await expect(page.getByText("Unlimited (trial)")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "Manage your subscription" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("Members tab has warning message when user has more seats than editors", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-eight-member.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamMembersSection();
|
||||
|
||||
const ctas = page.getByTestId("cta");
|
||||
await expect(ctas).toHaveCount(2);
|
||||
await expect(
|
||||
page.getByText("Inviting people while on the unlimited plan"),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("Invitations tab has warning message when user has more seats than editors", async ({
|
||||
page,
|
||||
}) => {
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-info",
|
||||
"subscription/get-team-info-subscriptions.json",
|
||||
);
|
||||
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-subscription-owner.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-projects?team-id=*",
|
||||
"dashboard/get-projects-second-team.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-members?team-id=*",
|
||||
"subscription/get-team-members-subscription-eight-member.json",
|
||||
);
|
||||
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-team-invitations?team-id=*",
|
||||
"subscription/get-team-invitations.json",
|
||||
);
|
||||
|
||||
await dashboardPage.mockRPC(
|
||||
"push-audit-events",
|
||||
"workspace/audit-event-empty.json",
|
||||
);
|
||||
|
||||
await dashboardPage.goToSecondTeamInvitationsSection();
|
||||
|
||||
const ctas = page.getByTestId("cta");
|
||||
await expect(ctas).toHaveCount(2);
|
||||
await expect(
|
||||
page.getByText("Inviting people while on the unlimited plan"),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -499,7 +499,7 @@ test.describe("Tokens: Tokens Tab", () => {
|
||||
await valueField.fill("");
|
||||
// TODO: We need to fix this translation
|
||||
await expect(
|
||||
tokensUpdateCreateModal.getByText("Empty field"),
|
||||
tokensUpdateCreateModal.getByText("Token value cannot be empty"),
|
||||
).toBeVisible();
|
||||
await valueSaturationSelector.click({ position: { x: 50, y: 50 } });
|
||||
await expect(valueField).toHaveValue(/^#[A-Fa-f\d]+$/);
|
||||
|
||||
6
frontend/pnpm-workspace.yaml
Normal file
6
frontend/pnpm-workspace.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
patchedDependencies:
|
||||
'@zip.js/zip.js': patches/@zip.js__zip.js.patch
|
||||
|
||||
|
||||
shamefullyHoist: true
|
||||
recursiveInstall: true
|
||||
@@ -25,14 +25,14 @@
|
||||
<link rel="icon" href="images/favicon.png" />
|
||||
{{# manifest}}
|
||||
<script>window.penpotWorkerURI="{{& worker_main}}"</script>
|
||||
<script defer src="{{& config}}"></script>
|
||||
<script defer src="{{& polyfills}}"></script>
|
||||
<script src="{{& config}}"></script>
|
||||
<script src="{{& polyfills}}"></script>
|
||||
{{/manifest}}
|
||||
|
||||
<script>
|
||||
window.penpotTranslations = JSON.parse({{& translations}});
|
||||
window.penpotVersion = "%version%";
|
||||
window.penpotBuildDate = "%buildDate%";
|
||||
<script type="module">
|
||||
globalThis.penpotTranslations = JSON.parse({{& translations}});
|
||||
globalThis.penpotVersion = "%version%";
|
||||
globalThis.penpotBuildDate = "%buildDate%";
|
||||
</script>
|
||||
<!--cookie-consent-->
|
||||
</head>
|
||||
@@ -44,9 +44,11 @@
|
||||
<section id="modal"></section>
|
||||
|
||||
{{# manifest}}
|
||||
<script defer src="js/libs.js?ts={{& ts}}"></script>
|
||||
<script defer src="{{& shared}}"></script>
|
||||
<script defer src="{{& main}}"></script>
|
||||
<script type="module" src="{{& libs}}"></script>
|
||||
<script type="module">
|
||||
import { init } from "{{& main}}";
|
||||
init();
|
||||
</script>
|
||||
{{/manifest}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
</head>
|
||||
<body>
|
||||
{{# manifest}}
|
||||
<script src="js/libs.js?ts={{& ts}}"></script>
|
||||
<script src="{{& shared}}"></script>
|
||||
<script src="{{& rasterizer}}"></script>
|
||||
<script type="module" src="{{& libs}}"></script>
|
||||
<script type="module">
|
||||
import { init } from "{{& rasterizer}}";
|
||||
init();
|
||||
</script>
|
||||
{{/manifest}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
{{# manifest}}
|
||||
<script src="js/libs.js?ts={{& ts}}"></script>
|
||||
<script src="{{& shared}}"></script>
|
||||
<script src="{{& render}}"></script>
|
||||
<script type="module" src="{{& libs}}"></script>
|
||||
<script type="module">
|
||||
import { init } from "{{& render}}";
|
||||
init();
|
||||
</script>
|
||||
{{/manifest}}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -193,26 +193,30 @@ async function readShadowManifest() {
|
||||
|
||||
const index = {
|
||||
ts: ts,
|
||||
config: "js/config.js?ts=" + ts,
|
||||
polyfills: "js/polyfills.js?ts=" + ts,
|
||||
worker_main: "js/worker/main.js?ts=" + ts,
|
||||
config: "./js/config.js",
|
||||
polyfills: "./js/polyfills.js",
|
||||
worker_main: "./js/worker/main.js",
|
||||
libs: "./js/libs.js",
|
||||
};
|
||||
|
||||
for (let item of content) {
|
||||
index[item.name] = "js/" + item["output-name"];
|
||||
index[item.name] = "./js/" + item["output-name"] + "";
|
||||
}
|
||||
|
||||
return index;
|
||||
} catch (cause) {
|
||||
return {
|
||||
const index = {
|
||||
ts: ts,
|
||||
config: "js/config.js?ts=" + ts,
|
||||
polyfills: "js/polyfills.js?ts=" + ts,
|
||||
main: "js/main.js?ts=" + ts,
|
||||
shared: "js/shared.js?ts=" + ts,
|
||||
worker_main: "js/worker/main.js?ts=" + ts,
|
||||
rasterizer: "js/rasterizer.js?ts=" + ts,
|
||||
config: "./js/config.js",
|
||||
polyfills: "./js/polyfills.js",
|
||||
main: "./js/main.js",
|
||||
shared: "./js/shared.js",
|
||||
worker_main: "./js/worker/main.js",
|
||||
rasterizer: "./js/rasterizer.js",
|
||||
libs: "./js/libs.js",
|
||||
};
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ yarn install || exit 1;
|
||||
rm -rf resources/public;
|
||||
rm -rf target/dist;
|
||||
|
||||
yarn run build:app:main --config-merge "{:release-version \"${CURRENT_HASH}-${TS}\"}" $EXTRA_PARAMS || exit 1
|
||||
mkdir -p resources/public;
|
||||
mkdir -p target/dist;
|
||||
|
||||
yarn run build:app:main $EXTRA_PARAMS || exit 1
|
||||
|
||||
if [ "$INCLUDE_WASM" = "yes" ]; then
|
||||
yarn run build:wasm || exit 1;
|
||||
@@ -35,19 +38,18 @@ fi
|
||||
yarn run build:app:libs || exit 1;
|
||||
yarn run build:app:assets || exit 1;
|
||||
|
||||
mkdir -p target/dist;
|
||||
rsync -avr resources/public/ target/dist/
|
||||
|
||||
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/index.html;
|
||||
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/render.html;
|
||||
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./target/dist/rasterizer.html;
|
||||
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/index.html;
|
||||
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./target/dist/rasterizer.html;
|
||||
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./resources/public/index.html;
|
||||
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./resources/public/render.html;
|
||||
sed -i -re "s/\%version\%/$CURRENT_VERSION/g" ./resources/public/rasterizer.html;
|
||||
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./resources/public/index.html;
|
||||
sed -i -re "s/\%buildDate\%/$BUILD_DATE/g" ./resources/public/rasterizer.html;
|
||||
|
||||
if [ "$INCLUDE_WASM" = "yes" ]; then
|
||||
sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./target/dist/js/render_wasm.js;
|
||||
sed -i "s/version=develop/version=$CURRENT_VERSION/g" ./resources/public/js/render_wasm.js;
|
||||
fi
|
||||
|
||||
rsync -avr resources/public/ target/dist/;
|
||||
|
||||
if [ "$INCLUDE_STORYBOOK" = "yes" ]; then
|
||||
# build storybook
|
||||
yarn run build:storybook || exit 1;
|
||||
|
||||
@@ -31,9 +31,9 @@ const rebuildNotify = {
|
||||
const config = {
|
||||
entryPoints: ["target/index.js"],
|
||||
bundle: true,
|
||||
format: "iife",
|
||||
format: "esm",
|
||||
banner: {
|
||||
js: '"use strict"; var global = globalThis;',
|
||||
js: '"use strict";\nvar global = globalThis;',
|
||||
},
|
||||
outfile: "resources/public/js/libs.js",
|
||||
plugins: [fixReactVirtualized, rebuildNotify],
|
||||
|
||||
9
frontend/scripts/test
Executable file
9
frontend/scripts/test
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
|
||||
yarn run lint:scss;
|
||||
yarn run test;
|
||||
13
frontend/scripts/test-components
Executable file
13
frontend/scripts/test-components
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run build:storybook
|
||||
|
||||
exec npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
|
||||
"npx http-server storybook-static --port 6006 --silent" \
|
||||
"npx wait-on tcp:6006 && yarn test:storybook"
|
||||
8
frontend/scripts/test-e2e
Executable file
8
frontend/scripts/test-e2e
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
corepack enable;
|
||||
corepack install;
|
||||
yarn install;
|
||||
yarn run playwright install chromium --with-deps;
|
||||
yarn run test:e2e -x --workers=2 --reporter=list "$@";
|
||||
@@ -6,13 +6,12 @@
|
||||
|
||||
:builds
|
||||
{:main
|
||||
{:target :browser
|
||||
{:target :esm
|
||||
:output-dir "resources/public/js/"
|
||||
:asset-path "/js"
|
||||
:devtools {:watch-dir "resources/public"
|
||||
:reload-strategy :full}
|
||||
:build-options {:manifest-name "manifest.json"}
|
||||
:module-loader true
|
||||
:modules
|
||||
{:shared
|
||||
{:entries []}
|
||||
@@ -20,7 +19,7 @@
|
||||
:main
|
||||
{:entries [app.main app.plugins.api]
|
||||
:depends-on #{:shared}
|
||||
:init-fn app.main/init}
|
||||
:exports {init app.main/init}}
|
||||
|
||||
:util-highlight
|
||||
{:entries [app.util.code-highlight]
|
||||
@@ -50,12 +49,12 @@
|
||||
:render
|
||||
{:entries [app.render]
|
||||
:depends-on #{:shared}
|
||||
:init-fn app.render/init}
|
||||
:exports {init app.render/init}}
|
||||
|
||||
:rasterizer
|
||||
{:entries [app.rasterizer]
|
||||
:depends-on #{:shared}
|
||||
:init-fn app.rasterizer/init}}
|
||||
:exports {init app.rasterizer/init}}}
|
||||
|
||||
:js-options
|
||||
{:entry-keys ["module" "browser" "main"]
|
||||
@@ -75,11 +74,10 @@
|
||||
:compiler-options
|
||||
{:fn-invoke-direct true
|
||||
:optimizations #shadow/env ["PENPOT_BUILD_OPTIMIZATIONS" :as :keyword :default :advanced]
|
||||
:output-wrapper true
|
||||
:rename-prefix-namespace "PENPOT"
|
||||
:source-map true
|
||||
:elide-asserts true
|
||||
:anon-fn-naming-policy :off
|
||||
:cross-chunk-method-motion false
|
||||
:source-map-detail-level :all}}}
|
||||
|
||||
:worker
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.profile :refer [schema:profile]]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
@@ -484,7 +483,7 @@
|
||||
|
||||
(defn delete-access-token
|
||||
[{:keys [id] :as params}]
|
||||
(us/assert! ::us/uuid id)
|
||||
(assert (uuid? id))
|
||||
(ptk/reify ::delete-access-token
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
|
||||
@@ -31,27 +31,28 @@
|
||||
[app.main.ui.static :as static]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.modules :as mod]
|
||||
[app.util.theme :as theme]
|
||||
[beicon.v2.core :as rx]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def auth-page
|
||||
(mf/lazy-component app.main.ui.auth/auth))
|
||||
(mf/lazy #(mod/load 'app.main.ui.auth/auth-page*)))
|
||||
|
||||
(def verify-token-page
|
||||
(mf/lazy-component app.main.ui.auth.verify-token/verify-token))
|
||||
(def verify-token-page*
|
||||
(mf/lazy #(mod/load 'app.main.ui.auth.verify-token/verify-token-page*)))
|
||||
|
||||
(def viewer-page*
|
||||
(mf/lazy-component app.main.ui.viewer/viewer*))
|
||||
(mf/lazy #(mod/load 'app.main.ui.viewer/viewer-page*)))
|
||||
|
||||
(def dashboard-page*
|
||||
(mf/lazy-component app.main.ui.dashboard/dashboard*))
|
||||
(mf/lazy #(mod/load 'app.main.ui.dashboard/dashboard-page*)))
|
||||
|
||||
(def settings-page*
|
||||
(mf/lazy-component app.main.ui.settings/settings*))
|
||||
(mf/lazy #(mod/load 'app.main.ui.settings/settings-page*)))
|
||||
|
||||
(def workspace-page*
|
||||
(mf/lazy-component app.main.ui.workspace/workspace*))
|
||||
(mf/lazy #(mod/load 'app.main.ui.workspace/workspace-page*)))
|
||||
|
||||
(mf/defc workspace-legacy-redirect*
|
||||
{::mf/props :obj
|
||||
@@ -189,7 +190,7 @@
|
||||
[:? [:& auth-page {:route route}]]
|
||||
|
||||
:auth-verify-token
|
||||
[:? [:& verify-token-page {:route route}]]
|
||||
[:? [:& verify-token-page* {:route route}]]
|
||||
|
||||
(:settings-profile
|
||||
:settings-password
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc auth
|
||||
{::mf/props :obj}
|
||||
(mf/defc auth*
|
||||
[{:keys [route]}]
|
||||
(let [section (dm/get-in route [:data :name])
|
||||
is-register (or
|
||||
@@ -69,3 +68,9 @@
|
||||
|
||||
(when (= section :auth-register)
|
||||
[:& terms-register])]]))
|
||||
|
||||
|
||||
(mf/defc auth-page*
|
||||
{::mf/lazy-load true}
|
||||
[props]
|
||||
[:> auth* props])
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
(rt/nav :auth-login)
|
||||
(ntf/warn (tr "errors.unexpected-token"))))
|
||||
|
||||
(mf/defc verify-token
|
||||
[{:keys [route] :as props}]
|
||||
(let [token (get-in route [:query-params :token])
|
||||
(mf/defc verify-token*
|
||||
[{:keys [route]}]
|
||||
(let [token (get-in route [:query-params :token])
|
||||
bad-token (mf/use-state false)]
|
||||
|
||||
(mf/with-effect []
|
||||
@@ -99,3 +99,8 @@
|
||||
[:> static/invalid-token {}]
|
||||
[:> loader* {:title (tr "labels.loading")
|
||||
:overlay true}])))
|
||||
|
||||
(mf/defc verify-token-page*
|
||||
{::mf/lazy-load true}
|
||||
[props]
|
||||
[:> verify-token* props])
|
||||
|
||||
@@ -8,24 +8,28 @@
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.util.modules :as modules]
|
||||
[cuerdas.core :as str]
|
||||
[promesa.core :as p]
|
||||
[rumext.v2 :as mf]
|
||||
[shadow.lazy :as lazy]))
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def highlight-fn
|
||||
(lazy/loadable app.util.code-highlight/highlight!))
|
||||
(delay (modules/load-fn 'app.util.code-highlight/highlight!)))
|
||||
|
||||
(mf/defc code-block
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [code type]}]
|
||||
(let [block-ref (mf/use-ref)
|
||||
code (str/trim code)]
|
||||
code (str/trim code)]
|
||||
|
||||
(mf/with-effect [code type]
|
||||
(when-let [node (mf/ref-val block-ref)]
|
||||
(p/let [highlight-fn (lazy/load highlight-fn)]
|
||||
(highlight-fn node))))
|
||||
(->> @highlight-fn
|
||||
(p/fmap (fn [f] (f)))
|
||||
(p/fnly (fn [f cause]
|
||||
(if cause
|
||||
(js/console.error cause)
|
||||
(f node)))))))
|
||||
|
||||
[:pre {:class (dm/str type " " (stl/css :code-display)) :ref block-ref} code]))
|
||||
|
||||
|
||||
@@ -247,7 +247,6 @@
|
||||
(swap! storage/session dissoc :template))))))
|
||||
|
||||
(mf/defc dashboard*
|
||||
{::mf/props :obj}
|
||||
[{:keys [profile project-id team-id search-term plugin-url template section]}]
|
||||
(let [team (mf/deref refs/team)
|
||||
projects (mf/deref refs/projects)
|
||||
@@ -313,3 +312,8 @@
|
||||
:section section
|
||||
:search-term search-term
|
||||
:team team}]]]))
|
||||
|
||||
(mf/defc dashboard-page*
|
||||
{::mf/lazy-load true}
|
||||
[props]
|
||||
[:> dashboard* props])
|
||||
|
||||
@@ -78,3 +78,9 @@
|
||||
|
||||
:settings-notifications
|
||||
[:& notifications-page* {:profile profile}])]]]]))
|
||||
|
||||
(mf/defc settings-page*
|
||||
{::mf/lazy-load true}
|
||||
[props]
|
||||
[:> settings* props])
|
||||
|
||||
|
||||
@@ -624,7 +624,6 @@
|
||||
;; --- Component: Viewer
|
||||
|
||||
(mf/defc viewer*
|
||||
{::mf/props :obj}
|
||||
[{:keys [file-id share-id page-id] :as props}]
|
||||
(mf/with-effect [file-id page-id share-id]
|
||||
(let [params {:file-id file-id
|
||||
@@ -643,3 +642,8 @@
|
||||
[:> loader* {:title (tr "labels.loading")
|
||||
:overlay true}]))
|
||||
|
||||
|
||||
(mf/defc viewer-page*
|
||||
{::mf/lazy-load true}
|
||||
[props]
|
||||
[:> viewer* props])
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
(dsh/lookup-page state file-id page-id))))
|
||||
st/state))
|
||||
|
||||
(mf/defc workspace-page*
|
||||
(mf/defc workspace-inner*
|
||||
{::mf/private true}
|
||||
[{:keys [page-id file-id file layout wglobal]}]
|
||||
(let [page-ref (mf/with-memo [file-id page-id]
|
||||
@@ -252,10 +252,16 @@
|
||||
:touch-action "none"}}
|
||||
[:> context-menu*]
|
||||
(if (and file-loaded? page-id)
|
||||
[:> workspace-page*
|
||||
[:> workspace-inner*
|
||||
{:page-id page-id
|
||||
:file-id file-id
|
||||
:file file
|
||||
:wglobal wglobal
|
||||
:layout layout}]
|
||||
[:> workspace-loader*])]]]]]]))
|
||||
|
||||
(mf/defc workspace-page*
|
||||
{::mf/lazy-load true}
|
||||
[props]
|
||||
[:> workspace* props])
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
(ns app.main.ui.workspace.shapes.text.editor
|
||||
(:require
|
||||
["draft-js" :as draft]
|
||||
["@penpot/draft-js" :as draft]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
[app.util.object :as obj]
|
||||
[app.util.text.content :as content]
|
||||
[app.util.text.content.styles :as styles]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn get-contrast-color [background-color]
|
||||
@@ -268,7 +269,12 @@
|
||||
"bottom" "flex-end"
|
||||
nil))
|
||||
|
||||
;;
|
||||
(defn- font-family-from-font-id [font-id]
|
||||
(if (str/includes? font-id "gfont-noto-sans")
|
||||
(let [lang (str/replace font-id #"gfont\-noto\-sans\-" "")]
|
||||
(if (>= (count lang) 3) (str/capital lang) (str/upper lang)))
|
||||
"Noto Color Emoji"))
|
||||
|
||||
;; Text Editor Wrapper
|
||||
;; This is an SVG element that wraps the HTML editor.
|
||||
;;
|
||||
@@ -281,6 +287,10 @@
|
||||
(let [shape-id (dm/get-prop shape :id)
|
||||
modifiers (dm/get-in modifiers [shape-id :modifiers])
|
||||
|
||||
fallback-fonts (wasm.api/fonts-from-text-content (:content shape) false)
|
||||
fallback-families (map (fn [font]
|
||||
(font-family-from-font-id (:font-id font))) fallback-fonts)
|
||||
|
||||
clip-id (dm/str "text-edition-clip" shape-id)
|
||||
|
||||
text-modifier-ref
|
||||
@@ -317,7 +327,7 @@
|
||||
max-height (max height selrect-height)
|
||||
valign (-> shape :content :vertical-align)
|
||||
y (:y selrect)
|
||||
y (if (> height selrect-height)
|
||||
y (if (and valign (> height selrect-height))
|
||||
(case valign
|
||||
"bottom" (- y (- height selrect-height))
|
||||
"center" (- y (/ (- height selrect-height) 2))
|
||||
@@ -341,7 +351,8 @@
|
||||
render-wasm?
|
||||
(obj/merge!
|
||||
#js {"--editor-container-width" (dm/str width "px")
|
||||
"--editor-container-height" (dm/str height "px")})
|
||||
"--editor-container-height" (dm/str height "px")
|
||||
"--fallback-families" (dm/str (str/join ", " fallback-families))})
|
||||
|
||||
(not render-wasm?)
|
||||
(obj/merge!
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.path-names :as cpn]
|
||||
[app.common.spec :as us]
|
||||
[app.common.thumbnails :as thc]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
@@ -38,7 +37,6 @@
|
||||
[app.util.i18n :as i18n :refer [c tr]]
|
||||
[app.util.strings :refer [matches-search]]
|
||||
[app.util.timers :as ts]
|
||||
[cljs.spec.alpha :as s]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
@@ -97,10 +95,6 @@
|
||||
(str (str/slice (:path asset) (count path)))
|
||||
(cpn/merge-path-item (:name asset))))
|
||||
|
||||
(s/def ::asset-name ::us/not-empty-string)
|
||||
(s/def ::name-group-form
|
||||
(s/keys :req-un [::asset-name]))
|
||||
|
||||
(def initial-context-menu-state
|
||||
{:open? false :top nil :left nil})
|
||||
|
||||
|
||||
@@ -38,30 +38,18 @@
|
||||
(features/use-feature "render-wasm/v1")
|
||||
|
||||
has-invalid-shapes?
|
||||
(if render-wasm-enabled?
|
||||
false
|
||||
(some (fn [shape]
|
||||
(or (cfh/frame-shape? shape)
|
||||
(cfh/text-shape? shape)))
|
||||
shapes-with-children))
|
||||
(some (if render-wasm-enabled?
|
||||
cfh/frame-shape?
|
||||
#(or (cfh/frame-shape? %) (cfh/text-shape? %)))
|
||||
shapes-with-children)
|
||||
|
||||
head-not-group-like?
|
||||
(and (= 1 total-selected)
|
||||
(not is-group?)
|
||||
(not is-bool?))
|
||||
|
||||
disabled-bool-btns
|
||||
(if render-wasm-enabled?
|
||||
false
|
||||
(or (zero? total-selected)
|
||||
has-invalid-shapes?
|
||||
head-not-group-like?))
|
||||
|
||||
disabled-flatten
|
||||
(if render-wasm-enabled?
|
||||
false
|
||||
(or (zero? total-selected)
|
||||
has-invalid-shapes?))
|
||||
disabled-bool-btns (or (zero? total-selected) has-invalid-shapes? head-not-group-like?)
|
||||
disabled-flatten (or (zero? total-selected) has-invalid-shapes?)
|
||||
|
||||
on-change
|
||||
(mf/use-fn
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- token-value-error-fn
|
||||
[{:keys [value]}]
|
||||
(when (or (str/empty? value)
|
||||
(str/blank? value))
|
||||
(tr "workspace.tokens.empty-input")))
|
||||
|
||||
(defn- make-schema
|
||||
[tokens-tree]
|
||||
(sm/schema
|
||||
@@ -44,7 +50,7 @@
|
||||
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
|
||||
#(not (cft/token-name-path-exists? % tokens-tree))]]]
|
||||
|
||||
[:value ::sm/text]
|
||||
[:value [::sm/text {:error/fn token-value-error-fn}]]
|
||||
|
||||
[:resolved-value ::sm/any]
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- token-value-error-fn
|
||||
[{:keys [value]}]
|
||||
(when (or (str/empty? value)
|
||||
(str/blank? value))
|
||||
(tr "workspace.tokens.empty-input")))
|
||||
|
||||
(defn- make-schema
|
||||
[tokens-tree]
|
||||
(sm/schema
|
||||
@@ -44,7 +50,7 @@
|
||||
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
|
||||
#(not (cft/token-name-path-exists? % tokens-tree))]]]
|
||||
|
||||
[:value ::sm/text]
|
||||
[:value [::sm/text {:error/fn token-value-error-fn}]]
|
||||
|
||||
[:resolved-value ::sm/any]
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- token-value-error-fn
|
||||
[{:keys [value]}]
|
||||
(when (or (str/empty? value)
|
||||
(str/blank? value))
|
||||
(tr "workspace.tokens.empty-input")))
|
||||
|
||||
(defn- make-schema
|
||||
[tokens-tree]
|
||||
(sm/schema
|
||||
@@ -44,7 +50,7 @@
|
||||
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
|
||||
#(not (cft/token-name-path-exists? % tokens-tree))]]]
|
||||
|
||||
[:value ::sm/text]
|
||||
[:value [::sm/text {:error/fn token-value-error-fn}]]
|
||||
|
||||
[:resolved-value ::sm/any]
|
||||
|
||||
|
||||
@@ -205,6 +205,7 @@
|
||||
;; TODO: Review this value vs default-value
|
||||
:value (or value "")
|
||||
:hint-message (:message hint)
|
||||
:variant "comfortable"
|
||||
:slot-start swatch
|
||||
:hint-type (:type hint)})
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
(mf/spread-props props {:on-change on-change
|
||||
:default-value value
|
||||
:hint-message (:message hint)
|
||||
:variant "comfortable"
|
||||
:hint-type (:type hint)})
|
||||
|
||||
props
|
||||
|
||||
@@ -32,6 +32,12 @@
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- token-value-error-fn
|
||||
[{:keys [value]}]
|
||||
(when (or (str/empty? value)
|
||||
(str/blank? value))
|
||||
(tr "workspace.tokens.empty-input")))
|
||||
|
||||
(defn- make-schema
|
||||
[tokens-tree]
|
||||
(sm/schema
|
||||
@@ -44,7 +50,7 @@
|
||||
[:fn {:error/fn #(tr "workspace.tokens.token-name-duplication-validation-error" (:value %))}
|
||||
#(not (cft/token-name-path-exists? % tokens-tree))]]]
|
||||
|
||||
[:value ::sm/text]
|
||||
[:value [::sm/text {:error/fn token-value-error-fn}]]
|
||||
|
||||
[:resolved-value ::sm/any]
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(ns app.plugins.comments
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.spec :as us]
|
||||
[app.common.schema :as sm]
|
||||
[app.main.data.comments :as dc]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.comments :as dwc]
|
||||
@@ -118,7 +118,8 @@
|
||||
(fn [position]
|
||||
(let [position (parser/parse-point position)]
|
||||
(cond
|
||||
(or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position))))
|
||||
(or (not (sm/valid-safe-number? (:x position)))
|
||||
(not (sm/valid-safe-number? (:y position))))
|
||||
(u/display-not-valid :position "Not valid point")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(ns app.plugins.flex
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
@@ -133,7 +133,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -148,7 +148,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -163,7 +163,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -178,7 +178,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -194,7 +194,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -209,7 +209,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -224,7 +224,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -239,7 +239,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -296,7 +296,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(us/safe-int? value)
|
||||
(sm/valid-safe-int? value)
|
||||
(u/display-not-valid :zIndex value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -359,7 +359,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :verticalMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -374,7 +374,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :horizontalMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -389,7 +389,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :topMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -404,7 +404,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :rightMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -419,7 +419,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :bottomMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -434,7 +434,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :leftMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -449,7 +449,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :maxWidth value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -464,7 +464,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :minWidth value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -479,7 +479,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :maxHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -494,7 +494,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :minHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(ns app.plugins.grid
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.spec :as us]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
@@ -126,7 +126,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -141,7 +141,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -156,7 +156,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -171,7 +171,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -186,7 +186,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -201,7 +201,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -216,7 +216,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -231,7 +231,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -248,7 +248,7 @@
|
||||
(u/display-not-valid :addRow-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (us/safe-number? value)))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addRow-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -261,14 +261,14 @@
|
||||
(fn [index type value]
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :addRowAtIndex-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :addRowAtIndex-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (us/safe-number? value)))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addRowAtIndex-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -285,7 +285,7 @@
|
||||
(u/display-not-valid :addColumn-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :lex type))
|
||||
(not (us/safe-number? value)))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addColumn-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -297,14 +297,14 @@
|
||||
:addColumnAtIndex
|
||||
(fn [index type value]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :addColumnAtIndex-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :addColumnAtIndex-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (us/safe-number? value)))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addColumnAtIndex-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -317,7 +317,7 @@
|
||||
:removeRow
|
||||
(fn [index]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :removeRow index)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -329,7 +329,7 @@
|
||||
:removeColumn
|
||||
(fn [index]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :removeColumn index)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -342,14 +342,14 @@
|
||||
(fn [index type value]
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :setColumn-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :setColumn-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (us/safe-number? value)))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :setColumn-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -362,14 +362,14 @@
|
||||
(fn [index type value]
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :setRow-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :setRow-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (us/safe-number? value)))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :setRow-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -393,10 +393,10 @@
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild-child child)
|
||||
|
||||
(or (< row 0) (not (us/safe-int? row)))
|
||||
(or (< row 0) (not (sm/valid-safe-int? row)))
|
||||
(u/display-not-valid :appendChild-row row)
|
||||
|
||||
(or (< column 0) (not (us/safe-int? column)))
|
||||
(or (< column 0) (not (sm/valid-safe-int? column)))
|
||||
(u/display-not-valid :appendChild-column column)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -431,7 +431,7 @@
|
||||
(let [cell (locate-cell self)
|
||||
shape (u/proxy->shape self)]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :row-value value)
|
||||
|
||||
(nil? cell)
|
||||
@@ -451,7 +451,7 @@
|
||||
(let [shape (u/proxy->shape self)
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rowSpan-value value)
|
||||
|
||||
(nil? cell)
|
||||
@@ -471,7 +471,7 @@
|
||||
(let [shape (u/proxy->shape self)
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :column-value value)
|
||||
|
||||
(nil? cell)
|
||||
@@ -491,7 +491,7 @@
|
||||
(let [shape (u/proxy->shape self)
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :columnSpan-value value)
|
||||
|
||||
(nil? cell)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.spec :as us]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.color :as cc]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.comments :as dc]
|
||||
@@ -299,7 +299,7 @@
|
||||
(fn [orientation value board]
|
||||
(let [shape (u/proxy->shape board)]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :addRulerGuide "Value not a safe number")
|
||||
|
||||
(not (contains? #{"vertical" "horizontal"} orientation))
|
||||
@@ -345,8 +345,8 @@
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :addCommentThread "Content not valid")
|
||||
|
||||
(or (not (us/safe-number? (:x position)))
|
||||
(not (us/safe-number? (:y position))))
|
||||
(or (not (sm/valid-safe-number? (:x position)))
|
||||
(not (sm/valid-safe-number? (:y position))))
|
||||
(u/display-not-valid :addCommentThread "Position not valid")
|
||||
|
||||
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.spec :as us]
|
||||
[app.common.schema :as sm]
|
||||
[app.main.data.workspace.guides :as dwgu]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.format :as format]
|
||||
@@ -77,7 +77,7 @@
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :position "Not valid position")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
[app.common.path-names :as cpn]
|
||||
[app.common.record :as crc]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.svg.path :as svg.path]
|
||||
[app.common.types.color :as clr]
|
||||
[app.common.types.component :as ctk]
|
||||
@@ -325,7 +324,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (us/safe-int? value)) (< value 0))
|
||||
(or (not (sm/valid-safe-int? value)) (< value 0))
|
||||
(u/display-not-valid :borderRadius value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -341,7 +340,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusTopLeft value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -357,7 +356,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusTopRight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -373,7 +372,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusBottomRight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -389,7 +388,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (us/safe-int? value))
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusBottomLeft value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -405,7 +404,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (us/safe-number? value)) (< value 0) (> value 1))
|
||||
(or (not (sm/valid-safe-number? value)) (< value 0) (> value 1))
|
||||
(u/display-not-valid :opacity value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -492,7 +491,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :x value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -510,7 +509,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :y value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -555,7 +554,7 @@
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :parentX value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -582,7 +581,7 @@
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :parentY value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -609,7 +608,7 @@
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :frameX value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -636,7 +635,7 @@
|
||||
:set
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :frameY value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -795,10 +794,10 @@
|
||||
:resize
|
||||
(fn [width height]
|
||||
(cond
|
||||
(or (not (us/safe-number? width)) (<= width 0))
|
||||
(or (not (sm/valid-safe-number? width)) (<= width 0))
|
||||
(u/display-not-valid :resize width)
|
||||
|
||||
(or (not (us/safe-number? height)) (<= height 0))
|
||||
(or (not (sm/valid-safe-number? height)) (<= height 0))
|
||||
(u/display-not-valid :resize height)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -1048,10 +1047,10 @@
|
||||
(not (cfh/text-shape? shape))
|
||||
(u/display-not-valid :getRange-shape "shape is not text")
|
||||
|
||||
(or (not (us/safe-int? start)) (< start 0) (> start end))
|
||||
(or (not (sm/valid-safe-int? start)) (< start 0) (> start end))
|
||||
(u/display-not-valid :getRange-start start)
|
||||
|
||||
(not (us/safe-int? end))
|
||||
(not (sm/valid-safe-int? end))
|
||||
(u/display-not-valid :getRange-end end)
|
||||
|
||||
:else
|
||||
@@ -1078,7 +1077,7 @@
|
||||
:setParentIndex
|
||||
(fn [index]
|
||||
(cond
|
||||
(not (us/safe-int? index))
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :setParentIndex index)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@@ -1230,7 +1229,7 @@
|
||||
(fn [orientation value]
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :addRulerGuide "Value not a safe number")
|
||||
|
||||
(not (contains? #{"vertical" "horizontal"} orientation))
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
(ns app.plugins.viewport
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.spec :as us]
|
||||
[app.common.schema :as sm]
|
||||
[app.main.data.workspace.viewport :as dwv]
|
||||
[app.main.data.workspace.zoom :as dwz]
|
||||
[app.main.store :as st]
|
||||
@@ -37,10 +37,10 @@
|
||||
(let [new-x (obj/get value "x")
|
||||
new-y (obj/get value "y")]
|
||||
(cond
|
||||
(not (us/safe-number? new-x))
|
||||
(not (sm/valid-safe-number? new-x))
|
||||
(u/display-not-valid :center-x new-x)
|
||||
|
||||
(not (us/safe-number? new-y))
|
||||
(not (sm/valid-safe-number? new-y))
|
||||
(u/display-not-valid :center-y new-y)
|
||||
|
||||
:else
|
||||
@@ -62,7 +62,7 @@
|
||||
:set
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (us/safe-number? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :zoom value)
|
||||
|
||||
:else
|
||||
|
||||
@@ -125,47 +125,40 @@
|
||||
[:embed {:optional true} :boolean]
|
||||
[:skip-children {:optional true} :boolean]
|
||||
[:object-id
|
||||
[:or
|
||||
::sm/uuid
|
||||
::sm/coll-of-uuid]]])
|
||||
[:or [::sm/set ::sm/uuid] ::sm/uuid]]])
|
||||
|
||||
(def ^:private render-objects-decoder
|
||||
(sm/lazy-decoder schema:render-objects
|
||||
sm/string-transformer))
|
||||
|
||||
(def ^:private render-objects-validator
|
||||
(sm/lazy-validator schema:render-objects))
|
||||
(def ^:private coerce-render-objects-params
|
||||
(sm/coercer schema:render-objects))
|
||||
|
||||
(defn- render-objects
|
||||
[params]
|
||||
(let [{:keys [file-id page-id embed share-id object-id skip-children] :as params} (render-objects-decoder params)]
|
||||
(if-not (render-objects-validator params)
|
||||
(do
|
||||
(js/console.error "invalid arguments")
|
||||
(sm/pretty-explain schema:render-objects params)
|
||||
nil)
|
||||
(try
|
||||
(let [{:keys [file-id page-id embed share-id object-id skip-children] :as params}
|
||||
(coerce-render-objects-params params)]
|
||||
(st/emit! (fetch-objects-bundle :file-id file-id :page-id page-id :share-id share-id :object-id object-id))
|
||||
(if (uuid? object-id)
|
||||
(mf/html
|
||||
[:& object-svg
|
||||
{:file-id file-id
|
||||
:page-id page-id
|
||||
:share-id share-id
|
||||
:object-id object-id
|
||||
:embed embed
|
||||
:skip-children skip-children}])
|
||||
|
||||
(do
|
||||
(st/emit! (fetch-objects-bundle :file-id file-id :page-id page-id :share-id share-id :object-id object-id))
|
||||
|
||||
(if (uuid? object-id)
|
||||
(mf/html
|
||||
[:& object-svg
|
||||
{:file-id file-id
|
||||
:page-id page-id
|
||||
:share-id share-id
|
||||
:object-id object-id
|
||||
:embed embed
|
||||
:skip-children skip-children}])
|
||||
|
||||
(mf/html
|
||||
[:& objects-svg
|
||||
{:file-id file-id
|
||||
:page-id page-id
|
||||
:share-id share-id
|
||||
:object-ids (into #{} object-id)
|
||||
:embed embed
|
||||
:skip-children skip-children}]))))))
|
||||
(mf/html
|
||||
[:& objects-svg
|
||||
{:file-id file-id
|
||||
:page-id page-id
|
||||
:share-id share-id
|
||||
:object-ids (into #{} object-id)
|
||||
:embed embed
|
||||
:skip-children skip-children}])))
|
||||
(catch :default cause
|
||||
(when-let [explain (-> cause ex-data ::sm/explain)]
|
||||
(js/console.log "Unexpected error")
|
||||
(js/console.log (sm/humanize-explain explain)))
|
||||
(mf/html [:span "Unexpected error:" (ex-message cause)]))))
|
||||
|
||||
;; ---- COMPONENTS SPRITE
|
||||
|
||||
@@ -242,39 +235,37 @@
|
||||
[:embed {:optional true} :boolean]
|
||||
[:component-id {:optional true} ::sm/uuid]])
|
||||
|
||||
(def ^:private render-components-decoder
|
||||
(sm/lazy-decoder schema:render-components
|
||||
sm/string-transformer))
|
||||
|
||||
(def ^:private render-components-validator
|
||||
(sm/lazy-validator schema:render-components))
|
||||
(def ^:private coerce-render-components-params
|
||||
(sm/coercer schema:render-components))
|
||||
|
||||
(defn render-components
|
||||
[params]
|
||||
(let [{:keys [file-id component-id embed] :as params} (render-components-decoder params)]
|
||||
(if-not (render-components-validator params)
|
||||
(do
|
||||
(js/console.error "invalid arguments")
|
||||
(sm/pretty-explain schema:render-components params)
|
||||
nil)
|
||||
(try
|
||||
(let [{:keys [file-id component-id embed] :as params}
|
||||
(coerce-render-components-params params)]
|
||||
|
||||
(do
|
||||
(st/emit! (ptk/reify ::initialize-render-components
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(rx/merge
|
||||
(rx/of (fetch-team :file-id file-id))
|
||||
(st/emit! (ptk/reify ::initialize-render-components
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(rx/merge
|
||||
(rx/of (fetch-team :file-id file-id))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::team-fetched))
|
||||
(rx/observe-on :async)
|
||||
(rx/map (constantly params))
|
||||
(rx/map fetch-components-bundle))))))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::team-fetched))
|
||||
(rx/observe-on :async)
|
||||
(rx/map (constantly params))
|
||||
(rx/map fetch-components-bundle))))))
|
||||
|
||||
(mf/html
|
||||
[:& components-svg
|
||||
{:component-id component-id
|
||||
:embed embed}])))))
|
||||
(mf/html
|
||||
[:& components-svg
|
||||
{:component-id component-id
|
||||
:embed embed}]))
|
||||
|
||||
(catch :default cause
|
||||
(when-let [explain (-> cause ex-data ::sm/explain)]
|
||||
(js/console.log "Unexpected error")
|
||||
(js/console.log (sm/humanize-explain explain)))
|
||||
(mf/html [:span "Unexpected error:" (ex-message cause)]))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SETUP
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
[app.util.debug :as dbg]
|
||||
[app.util.functions :as fns]
|
||||
[app.util.globals :as ug]
|
||||
[app.util.modules :as mod]
|
||||
[app.util.text.content :as tc]
|
||||
[beicon.v2.core :as rx]
|
||||
[promesa.core :as p]
|
||||
@@ -475,9 +476,9 @@
|
||||
(dissoc :style)
|
||||
(merge style)
|
||||
(select-keys allowed-keys))
|
||||
fill-rule (or (-> attrs :fill-rule sr/translate-fill-rule) (-> attrs :fillRule sr/translate-fill-rule))
|
||||
stroke-linecap (or (-> attrs :stroke-linecap sr/translate-stroke-linecap) (-> attrs :strokeLinecap sr/translate-stroke-linecap))
|
||||
stroke-linejoin (or (-> attrs :stroke-linejoin sr/translate-stroke-linejoin) (-> attrs :strokeLinejoin sr/translate-stroke-linejoin))
|
||||
fill-rule (-> (or (:fill-rule attrs) (:fillRule attrs)) sr/translate-fill-rule)
|
||||
stroke-linecap (-> (or (:stroke-linecap attrs) (:strokeLinecap attrs)) sr/translate-stroke-linecap)
|
||||
stroke-linejoin (-> (or (:stroke-linejoin attrs) (:strokeLinejoin attrs)) sr/translate-stroke-linejoin)
|
||||
fill-none (= "none" (-> attrs :fill))]
|
||||
(h/call wasm/internal-module "_set_shape_svg_attrs" fill-rule stroke-linecap stroke-linejoin fill-none)))
|
||||
|
||||
@@ -785,19 +786,10 @@
|
||||
hidden)))
|
||||
shadows))
|
||||
|
||||
(defn set-shape-text-content
|
||||
"This function sets shape text content and returns a stream that loads the needed fonts asynchronously"
|
||||
[shape-id content]
|
||||
|
||||
(h/call wasm/internal-module "_clear_shape_text")
|
||||
|
||||
(set-shape-vertical-align (get content :vertical-align))
|
||||
|
||||
(defn fonts-from-text-content [content fallback-fonts-only?]
|
||||
(let [paragraph-set (first (get content :children))
|
||||
paragraphs (get paragraph-set :children)
|
||||
fonts (fonts/get-content-fonts content)
|
||||
total (count paragraphs)]
|
||||
|
||||
(loop [index 0
|
||||
emoji? false
|
||||
langs #{}]
|
||||
@@ -814,20 +806,36 @@
|
||||
emoji? (if emoji? emoji? (t/contains-emoji? text))
|
||||
langs (t/collect-used-languages langs text)]
|
||||
|
||||
(t/write-shape-text spans paragraph text)
|
||||
;; FIXME: this should probably be somewhere else
|
||||
(when fallback-fonts-only? (t/write-shape-text spans paragraph text))
|
||||
|
||||
(recur (inc index)
|
||||
emoji?
|
||||
langs))))
|
||||
|
||||
(let [updated-fonts
|
||||
(-> fonts
|
||||
(-> #{}
|
||||
(cond-> ^boolean emoji? (f/add-emoji-font))
|
||||
(f/add-noto-fonts langs))
|
||||
result (f/store-fonts shape-id updated-fonts)]
|
||||
fallback-fonts (filter #(get % :is-fallback) updated-fonts)]
|
||||
|
||||
(h/call wasm/internal-module "_update_shape_text_layout")
|
||||
(if fallback-fonts-only? updated-fonts fallback-fonts))))))
|
||||
|
||||
result)))))
|
||||
(defn set-shape-text-content
|
||||
"This function sets shape text content and returns a stream that loads the needed fonts asynchronously"
|
||||
[shape-id content]
|
||||
|
||||
(h/call wasm/internal-module "_clear_shape_text")
|
||||
|
||||
(set-shape-vertical-align (get content :vertical-align))
|
||||
|
||||
(let [fonts (fonts/get-content-fonts content)
|
||||
fallback-fonts (fonts-from-text-content content true)
|
||||
all-fonts (concat fonts fallback-fonts)
|
||||
result (f/store-fonts shape-id all-fonts)]
|
||||
(f/load-fallback-fonts-for-editor! fallback-fonts)
|
||||
(h/call wasm/internal-module "_update_shape_text_layout")
|
||||
result))
|
||||
|
||||
(defn set-shape-grow-type
|
||||
[grow-type]
|
||||
@@ -1354,7 +1362,7 @@
|
||||
(delay
|
||||
(if (exists? js/dynamicImport)
|
||||
(let [uri (cf/resolve-static-asset "js/render_wasm.js")]
|
||||
(->> (js/dynamicImport (str uri))
|
||||
(->> (mod/import uri)
|
||||
(p/mcat init-wasm-module)
|
||||
(p/fmap
|
||||
(fn [default]
|
||||
|
||||
@@ -266,6 +266,12 @@
|
||||
|
||||
(store-font-id shape-id font-data asset-id emoji? fallback?)))
|
||||
|
||||
;; FIXME: This is a temporary function to load the fallback fonts for the editor.
|
||||
;; Once we render the editor content within wasm, we can remove this function.
|
||||
(defn load-fallback-fonts-for-editor!
|
||||
[fonts]
|
||||
(doseq [font fonts]
|
||||
(fonts/ensure-loaded! (:font-id font) (:font-variant-id font))))
|
||||
|
||||
(defn store-fonts
|
||||
[shape-id fonts]
|
||||
@@ -277,7 +283,8 @@
|
||||
:font-variant-id "regular"
|
||||
:style 0
|
||||
:weight 400
|
||||
:is-emoji true}))
|
||||
:is-emoji true
|
||||
:is-fallback true}))
|
||||
|
||||
(def noto-fonts
|
||||
{:japanese {:font-id "gfont-noto-sans-jp" :font-variant-id "regular" :style 0 :weight 400 :is-fallback true}
|
||||
|
||||
@@ -67,15 +67,17 @@ function filterAllowedTypes(options) {
|
||||
* @param {string} type
|
||||
* @returns {boolean}
|
||||
*/
|
||||
return function filter(type) {
|
||||
function filter(type) {
|
||||
if (
|
||||
(!("allowHTMLPaste" in options) || !options["allowHTMLPaste"]) &&
|
||||
type === "text/html"
|
||||
type === "text/html"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return allowedTypes.includes(type);
|
||||
};
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,19 +87,22 @@ function filterAllowedTypes(options) {
|
||||
* @returns {Function<AllowedTypesFilterFunction>}
|
||||
*/
|
||||
function filterAllowedItems(options) {
|
||||
|
||||
/**
|
||||
* @param {DataTransferItem}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
return function filter(item) {
|
||||
function filter(item) {
|
||||
if (
|
||||
(!("allowHTMLPaste" in options) || !options["allowHTMLPaste"]) &&
|
||||
item.type === "text/html"
|
||||
item.type === "text/html"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return allowedTypes.includes(item.type);
|
||||
};
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
(ns app.util.code-highlight
|
||||
(:require
|
||||
["@penpot/hljs" :as hljs]
|
||||
["highlight.js$default" :as hljs]
|
||||
[app.util.dom :as dom]))
|
||||
|
||||
(defn highlight!
|
||||
{:lazy-loadable true}
|
||||
[node]
|
||||
(dom/set-data! node "highlighted" nil)
|
||||
(hljs/highlightElement node))
|
||||
|
||||
@@ -20,7 +20,7 @@ goog.provide("app.util.globals");
|
||||
goog.scope(function () {
|
||||
var self = app.util.globals;
|
||||
|
||||
self.global = goog.global;
|
||||
self.global = globalThis;
|
||||
|
||||
function createMockedEventEmitter(k) {
|
||||
/* Allow mocked objects to be event emitters, so other modules
|
||||
|
||||
@@ -92,8 +92,9 @@
|
||||
is executed in the critical part (application bootstrap) and used in
|
||||
many parts of the application."
|
||||
[data]
|
||||
(reset! locale (or (get storage/global ::locale) (autodetect)))
|
||||
(set! translations data))
|
||||
(set! translations data)
|
||||
(reset! locale (or (get storage/global ::locale) (autodetect))))
|
||||
|
||||
|
||||
(defn set-locale!
|
||||
[lname]
|
||||
|
||||
17
frontend/src/app/util/modules.clj
Normal file
17
frontend/src/app/util/modules.clj
Normal file
@@ -0,0 +1,17 @@
|
||||
;; 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.util.modules
|
||||
(:refer-clojure :exclude [load resolve]))
|
||||
|
||||
(defmacro load
|
||||
[thing]
|
||||
`(-> (shadow.esm/load-by-name ~thing)
|
||||
(.then (fn [f#] (cljs.core/js-obj "default" (f#))))))
|
||||
|
||||
(defmacro load-fn
|
||||
[thing]
|
||||
`(shadow.esm/load-by-name ~thing))
|
||||
16
frontend/src/app/util/modules.cljs
Normal file
16
frontend/src/app/util/modules.cljs
Normal file
@@ -0,0 +1,16 @@
|
||||
;; 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.util.modules
|
||||
(:refer-clojure :exclude [import])
|
||||
(:require-macros [app.util.modules])
|
||||
(:require
|
||||
[shadow.esm :refer [dynamic-import]]))
|
||||
|
||||
(defn import
|
||||
"Dynamic esm module import import"
|
||||
[path]
|
||||
(dynamic-import (str path)))
|
||||
@@ -38,7 +38,8 @@
|
||||
(str v "px")
|
||||
|
||||
(and (= k :font-family) (seq v))
|
||||
(str/quote v)
|
||||
;; pick just first family, avoid quoting twice, and add var(--fallback-families)
|
||||
(str/concat (str/quote (str/unquote (first (str/split v ",")))) ", var(--fallback-families)")
|
||||
|
||||
:else
|
||||
v))
|
||||
@@ -53,7 +54,7 @@
|
||||
(str/slice v 0 -2)
|
||||
|
||||
(= k :font-family)
|
||||
(str/unquote v)
|
||||
(str/unquote (str/replace v ", var(--fallback-families)" ""))
|
||||
|
||||
:else
|
||||
v))
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
(rx/push! bus message))))
|
||||
|
||||
handle-error
|
||||
(fn [error]
|
||||
(on-error worker (.-data error)))]
|
||||
(fn [event]
|
||||
(on-error worker (.-data event)))]
|
||||
|
||||
(.addEventListener instance "message" handle-message)
|
||||
(.addEventListener instance "error" handle-error)
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[app.render-wasm.wasm :as wasm]
|
||||
[app.util.http :as http]
|
||||
[app.util.modules :as mod]
|
||||
[app.worker.impl :as impl]
|
||||
[beicon.v2.core :as rx]
|
||||
[okulary.core :as l]
|
||||
[promesa.core :as p]
|
||||
[rumext.v2 :as mf]
|
||||
[shadow.esm :refer (dynamic-import)]))
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(log/set-level! :trace)
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
(def init-wasm
|
||||
(delay
|
||||
(let [uri (cf/resolve-static-asset "js/render_wasm.js")]
|
||||
(-> (dynamic-import (str uri))
|
||||
(-> (mod/import (str uri))
|
||||
(p/then #(wasm.api/init-wasm-module %))
|
||||
(p/then #(set! wasm/internal-module %))))))
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
isLikeParagraph,
|
||||
} from "./Paragraph.js";
|
||||
import { isDisplayBlock, normalizeStyles } from "./Style.js";
|
||||
import { sanitizeFontFamily } from "./Style.js";
|
||||
|
||||
const DEFAULT_FONT_SIZE = "14px";
|
||||
const DEFAULT_FONT_WEIGHT = 400;
|
||||
@@ -87,16 +88,16 @@ export function mapContentFragmentFromDocument(document, root, styleDefaults) {
|
||||
styleDefaults?.getPropertyValue("font-size") ?? DEFAULT_FONT_SIZE,
|
||||
);
|
||||
}
|
||||
const fontFamily = textSpan.style.getPropertyValue("font-family");
|
||||
let fontFamily = textSpan.style.getPropertyValue("font-family");
|
||||
if (!fontFamily) {
|
||||
console.warn("font-family", fontFamily);
|
||||
const fontFamilyValue =
|
||||
fontFamily =
|
||||
styleDefaults?.getPropertyValue("font-family") ?? DEFAULT_FONT_FAMILY;
|
||||
const quotedFontFamily = fontFamilyValue.startsWith('"')
|
||||
? fontFamilyValue
|
||||
: `"${fontFamilyValue}"`;
|
||||
textSpan.style.setProperty("font-family", quotedFontFamily);
|
||||
}
|
||||
|
||||
fontFamily = sanitizeFontFamily(fontFamily);
|
||||
textSpan.style.setProperty("font-family", fontFamily);
|
||||
|
||||
const fontWeight = textSpan.style.getPropertyValue("font-weight");
|
||||
if (!fontWeight) {
|
||||
console.warn("font-weight", fontWeight);
|
||||
@@ -144,18 +145,29 @@ export function htmlToText(html) {
|
||||
tmp.innerHTML = html;
|
||||
|
||||
const blockTags = [
|
||||
"P", "DIV", "SECTION", "ARTICLE", "HEADER", "FOOTER",
|
||||
"UL", "OL", "LI", "TABLE", "TR", "TD", "TH", "PRE"
|
||||
"P",
|
||||
"DIV",
|
||||
"SECTION",
|
||||
"ARTICLE",
|
||||
"HEADER",
|
||||
"FOOTER",
|
||||
"UL",
|
||||
"OL",
|
||||
"LI",
|
||||
"TABLE",
|
||||
"TR",
|
||||
"TD",
|
||||
"TH",
|
||||
"PRE",
|
||||
];
|
||||
|
||||
function walk(node) {
|
||||
let text = "";
|
||||
|
||||
node.childNodes.forEach(child => {
|
||||
node.childNodes.forEach((child) => {
|
||||
if (child.nodeType === Node.TEXT_NODE) {
|
||||
text += child.textContent;
|
||||
} else if (child.nodeType === Node.ELEMENT_NODE) {
|
||||
|
||||
if (child.tagName === "BR") {
|
||||
text += "\n";
|
||||
}
|
||||
@@ -178,7 +190,6 @@ export function htmlToText(html) {
|
||||
return result.trim();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Maps any HTML into a valid content DOM element.
|
||||
*
|
||||
@@ -187,10 +198,14 @@ export function htmlToText(html) {
|
||||
* @param {boolean} [allowHTMLPaste=false]
|
||||
* @returns {DocumentFragment}
|
||||
*/
|
||||
export function mapContentFragmentFromHTML(html, styleDefaults, allowHTMLPaste) {
|
||||
export function mapContentFragmentFromHTML(
|
||||
html,
|
||||
styleDefaults,
|
||||
allowHTMLPaste,
|
||||
) {
|
||||
if (allowHTMLPaste) {
|
||||
try {
|
||||
const parser = new DOMParser()
|
||||
const parser = new DOMParser();
|
||||
const document = parser.parseFromString(html, "text/html");
|
||||
return mapContentFragmentFromDocument(document, styleDefaults);
|
||||
} catch (error) {
|
||||
|
||||
@@ -19,10 +19,31 @@ const DEFAULT_FONT_WEIGHT = "400";
|
||||
* @param {string} value
|
||||
*/
|
||||
export function sanitizeFontFamily(value) {
|
||||
if (value && value.length > 0 && !value.startsWith('"')) {
|
||||
return `"${value}"`;
|
||||
} else {
|
||||
// NOTE: This is a fix for a bug introduced earlier that have might modified the font-family in the model
|
||||
// adding extra double quotes.
|
||||
if (value && value.startsWith('""')) {
|
||||
//remove the first and last quotes
|
||||
value = value.slice(1).replace(/"([^"]*)$/, "$1");
|
||||
|
||||
// remove quotes from font-family in 1-word font-families
|
||||
// and repeated values
|
||||
value = [
|
||||
...new Set(
|
||||
value
|
||||
.split(", ")
|
||||
.map((x) => (x.includes(" ") ? x : x.replace(/"/g, ""))),
|
||||
),
|
||||
].join(", ");
|
||||
}
|
||||
|
||||
if (!value || value === "") {
|
||||
return "var(--fallback-families)";
|
||||
} else if (value.endsWith(" var(--fallback-families)")) {
|
||||
return value;
|
||||
} else if (value.startsWith('"')) {
|
||||
return `${value}, var(--fallback-families)`;
|
||||
} else {
|
||||
return `"${value}", var(--fallback-families)`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
frontend/vendor/hljs/index.js
vendored
5
frontend/vendor/hljs/index.js
vendored
@@ -1,5 +0,0 @@
|
||||
import h from "highlight.js";
|
||||
|
||||
export function highlightElement(node) {
|
||||
return h.highlightElement(node);
|
||||
}
|
||||
13
frontend/vendor/hljs/package.json
vendored
13
frontend/vendor/hljs/package.json
vendored
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "@penpot/hljs",
|
||||
"version": "1.0.0",
|
||||
"description": "Penpot Hightlight.js ESM wrapper",
|
||||
"main": "index.js",
|
||||
"packageManager": "yarn@4.3.1",
|
||||
"author": "Andrey Antukh",
|
||||
"license": "MPL-2.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"highlight.js": "^11.10.0"
|
||||
}
|
||||
}
|
||||
21
frontend/vendor/hljs/yarn.lock
vendored
21
frontend/vendor/hljs/yarn.lock
vendored
@@ -1,21 +0,0 @@
|
||||
# This file is generated by running "yarn install" inside your project.
|
||||
# Manual changes might be lost - proceed with caution!
|
||||
|
||||
__metadata:
|
||||
version: 8
|
||||
cacheKey: 10c0
|
||||
|
||||
"@penpot/hljs@workspace:.":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@penpot/hljs@workspace:."
|
||||
dependencies:
|
||||
highlight.js: "npm:^11.10.0"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"highlight.js@npm:^11.10.0":
|
||||
version: 11.10.0
|
||||
resolution: "highlight.js@npm:11.10.0"
|
||||
checksum: 10c0/cd8bf7ef06cbd72ddb83580ecabe769f08f062be8bb82d2eb492d31c17f7480d1f8d14a66fc81deee0601645435f19f04c470510563f847242a41ccff0ab873e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
4
frontend/vendor/tubax/saxjs.cljs
vendored
4
frontend/vendor/tubax/saxjs.cljs
vendored
@@ -1,4 +0,0 @@
|
||||
(ns tubax.saxjs
|
||||
(:require ["sax" :as sax]))
|
||||
|
||||
(goog/exportSymbol "sax" sax)
|
||||
1777
frontend/yarn.lock
1777
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user