Compare commits

..

1 Commits

Author SHA1 Message Date
Andrey Antukh
24af82752c 🐛 Prevent exception on open-new-window when no window is returned
Fixes https://github.com/penpot/penpot/issues/7787
2026-01-21 09:29:24 +01:00
151 changed files with 50053 additions and 15193 deletions

View File

@@ -1,101 +0,0 @@
name: Plugins/api-doc deployer
on:
push:
branches:
- develop
- staging
- main
paths:
- "plugins/libs/plugin-types/index.d.ts"
- "plugins/libs/plugin-types/REAME.md"
- "plugins/tools/typedoc.css"
- "plugins/CHANGELOG.md"
- "plugins/wrangle-penpot-plugins-api-doc.toml"
workflow_dispatch:
inputs:
gh_ref:
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Extract some useful variables
id: vars
run: |
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- name: Enable PNPM
working-directory: ./plugins
shell: bash
run: |
corepack enable;
corepack install;
- name: Get pnpm store path
id: pnpm-store
working-directory: ./plugins
shell: bash
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
# END: Setup Node and PNPM enabling cache
- name: Install deps
working-directory: ./plugins
shell: bash
run: |
pnpm install --no-frozen-lockfile;
pnpm add -D -w wrangler@latest;
- name: Build docs
working-directory: plugins
shell: bash
run: pnpm run build:doc
- name: Select Worker name
run: |
REF="${{ steps.vars.outputs.gh_ref }}"
case "$REF" in
main) echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV ;;
staging) echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV ;;
develop) echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV ;;
*) echo "Unsupported branch ${REF}" && exit 1 ;;
esac
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
workingDirectory: plugins
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --config wrangle-penpot-plugins-api-doc.toml --name ${{ env.WORKER_NAME }}

View File

@@ -1,11 +0,0 @@
name: Plugins/package deployer
on:
workflow_dispatch:
jobs:
print_text_job:
runs-on: ubuntu-latest
steps:
- name: Print Hello World
run: echo "Hello, World!"

View File

@@ -1,11 +0,0 @@
name: Plugins/packages deployer
on:
workflow_dispatch:
jobs:
print_text_job:
runs-on: ubuntu-latest
steps:
- name: Print Hello World
run: echo "Hello, World!"

2
.gitignore vendored
View File

@@ -54,8 +54,6 @@
/exporter/target /exporter/target
/frontend/.storybook/preview-body.html /frontend/.storybook/preview-body.html
/frontend/.storybook/preview-head.html /frontend/.storybook/preview-head.html
/frontend/playwright-report/
/frontend/text-editor/src/wasm/
/frontend/dist/ /frontend/dist/
/frontend/npm-debug.log /frontend/npm-debug.log
/frontend/out/ /frontend/out/

105
.gitpod.yml Normal file
View File

@@ -0,0 +1,105 @@
image:
file: docker/gitpod/Dockerfile
ports:
# nginx
- port: 3449
onOpen: open-preview
# frontend nREPL
- port: 3447
onOpen: ignore
visibility: private
# frontend shadow server
- port: 3448
onOpen: ignore
visibility: private
# backend
- port: 6060
onOpen: ignore
# exporter shadow server
- port: 9630
onOpen: ignore
visibility: private
# exporter http server
- port: 6061
onOpen: ignore
# mailhog web interface
- port: 8025
onOpen: ignore
# mailhog postfix
- port: 1025
onOpen: ignore
# postgres
- port: 5432
onOpen: ignore
# redis
- port: 6379
onOpen: ignore
# openldap
- port: 389
onOpen: ignore
tasks:
# https://github.com/gitpod-io/gitpod/issues/666#issuecomment-534347856
- name: gulp
command: >
cd $GITPOD_REPO_ROOT/frontend/;
yarn && gp sync-done 'frontend-yarn';
npx gulp --theme=${PENPOT_THEME} watch
- name: frontend shadow watch
command: >
cd $GITPOD_REPO_ROOT/frontend/;
gp sync-await 'frontend-yarn';
npx shadow-cljs watch main
- init: gp await-port 5432 && psql -f $GITPOD_REPO_ROOT/docker/gitpod/files/postgresql_init.sql
name: backend
command: >
cd $GITPOD_REPO_ROOT/backend/;
./scripts/start-dev
- name: exporter shadow watch
command:
cd $GITPOD_REPO_ROOT/exporter/;
gp sync-await 'frontend-yarn';
yarn && npx shadow-cljs watch main
- name: exporter web server
command: >
cd $GITPOD_REPO_ROOT/exporter/;
./scripts/wait-and-start.sh
- name: signed terminal
before: >
[[ ! -z ${GNUGPG} ]] &&
cd ~ &&
rm -rf .gnupg &&
echo ${GNUGPG} | base64 -d | tar --no-same-owner -xzvf -
init: >
[[ ! -z ${GNUGPG_KEY} ]] &&
git config --global commit.gpgsign true &&
git config --global user.signingkey ${GNUGPG_KEY}
command: cd $GITPOD_REPO_ROOT
- name: redis
command: redis-server
- before: go get github.com/mailhog/MailHog
name: mailhog
command: MailHog
- name: Nginx
command: >
nginx &&
multitail /var/log/nginx/access.log -I /var/log/nginx/error.log

40
.travis.yml Normal file
View File

@@ -0,0 +1,40 @@
dist: xenial
language: generic
sudo: required
cache:
directories:
- $HOME/.m2
services:
- docker
branches:
only:
- master
- develop
install:
- curl -O https://download.clojure.org/install/linux-install-1.10.1.447.sh
- chmod +x linux-install-1.10.1.447.sh
- sudo ./linux-install-1.10.1.447.sh
before_script:
- env | sort
script:
- ./manage.sh build-devenv
- ./manage.sh run-frontend-tests
- ./manage.sh run-backend-tests
- ./manage.sh build-images
- ./manage.sh run
after_script:
- docker images
notifications:
email: false
env:
- NODE_VERSION=10.16.0

11
.yarnrc.yml Normal file
View File

@@ -0,0 +1,11 @@
enableGlobalCache: true
enableImmutableCache: false
enableImmutableInstalls: false
enableTelemetry: false
httpTimeout: 600000
nodeLinker: node-modules

View File

@@ -25,7 +25,7 @@
- Fix error message on components doesn't close automatically [Taiga #12012](https://tree.taiga.io/project/penpot/issue/12012) - Fix error message on components doesn't close automatically [Taiga #12012](https://tree.taiga.io/project/penpot/issue/12012)
- Fix incorrect default option on tokens import dialog [Github #8051](https://github.com/penpot/penpot/pull/8051) - Fix incorrect default option on tokens import dialog [Github #8051](https://github.com/penpot/penpot/pull/8051)
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110) - Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058) - Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
## 2.13.0 (Unreleased) ## 2.13.0 (Unreleased)
@@ -58,9 +58,6 @@
- Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959) - Fix dropdown option width in Guides columns dropdown [Taiga #12959](https://tree.taiga.io/project/penpot/issue/12959)
- Fix typos on download modal [Taiga #12865](https://tree.taiga.io/project/penpot/issue/12865) - Fix typos on download modal [Taiga #12865](https://tree.taiga.io/project/penpot/issue/12865)
- Fix problem with text editor maintaining previous styles [Taiga #12835](https://tree.taiga.io/project/penpot/issue/12835) - Fix problem with text editor maintaining previous styles [Taiga #12835](https://tree.taiga.io/project/penpot/issue/12835)
- Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110)
- Fix allow negative spread values on shadow token creation [Taiga #13167](https://tree.taiga.io/project/penpot/issue/13167)
- Fix spanish translations on import export token modal [Taiga #13171](https://tree.taiga.io/project/penpot/issue/13171)
## 2.12.1 ## 2.12.1

View File

@@ -120,12 +120,17 @@ them on your system, you can run them with:
```bash ```bash
# Check formatting # Check formatting
./scripts/fmt yarn fmt:clj:check
# Lint # Check and fix formatting
./scripts/lint yarn fmt:clj
# Run the linter
yarn lint:clj
``` ```
There are more choices in `package.json`.
Ideally, you should run these commands as git pre-commit hooks. A convenient way Ideally, you should run these commands as git pre-commit hooks. A convenient way
of defining them is to use [Husky](https://typicode.github.io/husky/#/). of defining them is to use [Husky](https://typicode.github.io/husky/#/).

7
backend/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

View File

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

306
backend/pnpm-lock.yaml generated
View File

@@ -1,306 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
luxon:
specifier: ^3.4.4
version: 3.7.2
sax:
specifier: ^1.4.1
version: 1.4.3
devDependencies:
nodemon:
specifier: ^3.1.2
version: 3.1.11
source-map-support:
specifier: ^0.5.21
version: 0.5.21
ws:
specifier: ^8.17.0
version: 8.18.3
packages:
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
ignore-by-default@1.0.1:
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
luxon@3.7.2:
resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==}
engines: {node: '>=12'}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nodemon@3.1.11:
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
engines: {node: '>=10'}
hasBin: true
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
pstree.remy@1.1.8:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
sax@1.4.3:
resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==}
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
hasBin: true
simple-update-notifier@2.0.0:
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
engines: {node: '>=10'}
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
touch@3.1.1:
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
hasBin: true
undefsafe@2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
ws@8.18.3:
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
snapshots:
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
balanced-match@1.0.2: {}
binary-extensions@2.3.0: {}
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
braces@3.0.3:
dependencies:
fill-range: 7.1.1
buffer-from@1.1.2: {}
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
braces: 3.0.3
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
concat-map@0.0.1: {}
debug@4.4.3(supports-color@5.5.0):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 5.5.0
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
fsevents@2.3.3:
optional: true
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
has-flag@3.0.0: {}
ignore-by-default@1.0.1: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
is-extglob@2.1.1: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-number@7.0.0: {}
luxon@3.7.2: {}
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.12
ms@2.1.3: {}
nodemon@3.1.11:
dependencies:
chokidar: 3.6.0
debug: 4.4.3(supports-color@5.5.0)
ignore-by-default: 1.0.1
minimatch: 3.1.2
pstree.remy: 1.1.8
semver: 7.7.3
simple-update-notifier: 2.0.0
supports-color: 5.5.0
touch: 3.1.1
undefsafe: 2.0.5
normalize-path@3.0.0: {}
picomatch@2.3.1: {}
pstree.remy@1.1.8: {}
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
sax@1.4.3: {}
semver@7.7.3: {}
simple-update-notifier@2.0.0:
dependencies:
semver: 7.7.3
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
source-map@0.6.1: {}
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
touch@3.1.1: {}
undefsafe@2.0.5: {}
ws@8.18.3: {}

View File

View File

@@ -124,6 +124,8 @@
(throw (IllegalArgumentException. "invalid email body provided"))) (throw (IllegalArgumentException. "invalid email body provided")))
(doseq [[name content] attachments] (doseq [[name content] attachments]
(prn "attachment" name)
(let [attachment-part (MimeBodyPart.)] (let [attachment-part (MimeBodyPart.)]
(.setFileName attachment-part ^String name) (.setFileName attachment-part ^String name)
(.setContent attachment-part ^String content (str "text/plain; charset=" charset)) (.setContent attachment-part ^String content (str "text/plain; charset=" charset))

1145
backend/yarn.lock Normal file
View File

File diff suppressed because it is too large Load Diff

7
common/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

View File

@@ -59,7 +59,7 @@
thheller/shadow-cljs {:mvn/version "3.2.0"} thheller/shadow-cljs {:mvn/version "3.2.0"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"} com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"} com.bhauman/rebel-readline {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "0.4.6"} criterium/criterium {:mvn/version "RELEASE"}
mockery/mockery {:mvn/version "RELEASE"}} mockery/mockery {:mvn/version "RELEASE"}}
:extra-paths ["test" "dev"]} :extra-paths ["test" "dev"]}

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6", "packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
"type": "module", "type": "module",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -23,9 +23,9 @@
"fmt:clj:check": "cljfmt check --parallel=false src/ test/", "fmt:clj:check": "cljfmt check --parallel=false src/ test/",
"fmt:clj": "cljfmt fix --parallel=true src/ test/", "fmt:clj": "cljfmt fix --parallel=true src/ test/",
"lint:clj": "clj-kondo --parallel=true --lint src/", "lint:clj": "clj-kondo --parallel=true --lint src/",
"lint": "pnpm run lint:clj", "lint": "yarn run lint:clj",
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"", "watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"",
"build:test": "clojure -M:dev:shadow-cljs compile test", "build:test": "clojure -M:dev:shadow-cljs compile test",
"test": "pnpm run build:test && node target/tests/test.js" "test": "yarn run build:test && node target/tests/test.js"
} }
} }

489
common/pnpm-lock.yaml generated
View File

@@ -1,489 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
date-fns:
specifier: ^4.1.0
version: 4.1.0
devDependencies:
concurrently:
specifier: ^9.1.2
version: 9.2.1
nodemon:
specifier: ^3.1.10
version: 3.1.11
source-map-support:
specifier: ^0.5.21
version: 0.5.21
ws:
specifier: ^8.18.2
version: 8.18.3
packages:
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
concurrently@9.2.1:
resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==}
engines: {node: '>=18'}
hasBin: true
date-fns@4.1.0:
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
has-flag@3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
ignore-by-default@1.0.1:
resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
nodemon@3.1.11:
resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==}
engines: {node: '>=10'}
hasBin: true
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
pstree.remy@1.1.8:
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
rxjs@7.8.2:
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
hasBin: true
shell-quote@1.8.3:
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
engines: {node: '>= 0.4'}
simple-update-notifier@2.0.0:
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
engines: {node: '>=10'}
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
engines: {node: '>=10'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
touch@3.1.1:
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
hasBin: true
tree-kill@1.2.2:
resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==}
hasBin: true
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
undefsafe@2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
ws@8.18.3:
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
snapshots:
ansi-regex@5.0.1: {}
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
balanced-match@1.0.2: {}
binary-extensions@2.3.0: {}
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
braces@3.0.3:
dependencies:
fill-range: 7.1.1
buffer-from@1.1.2: {}
chalk@4.1.2:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
braces: 3.0.3
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
cliui@8.0.1:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
concat-map@0.0.1: {}
concurrently@9.2.1:
dependencies:
chalk: 4.1.2
rxjs: 7.8.2
shell-quote: 1.8.3
supports-color: 8.1.1
tree-kill: 1.2.2
yargs: 17.7.2
date-fns@4.1.0: {}
debug@4.4.3(supports-color@5.5.0):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 5.5.0
emoji-regex@8.0.0: {}
escalade@3.2.0: {}
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
fsevents@2.3.3:
optional: true
get-caller-file@2.0.5: {}
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
has-flag@3.0.0: {}
has-flag@4.0.0: {}
ignore-by-default@1.0.1: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
is-extglob@2.1.1: {}
is-fullwidth-code-point@3.0.0: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-number@7.0.0: {}
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.12
ms@2.1.3: {}
nodemon@3.1.11:
dependencies:
chokidar: 3.6.0
debug: 4.4.3(supports-color@5.5.0)
ignore-by-default: 1.0.1
minimatch: 3.1.2
pstree.remy: 1.1.8
semver: 7.7.3
simple-update-notifier: 2.0.0
supports-color: 5.5.0
touch: 3.1.1
undefsafe: 2.0.5
normalize-path@3.0.0: {}
picomatch@2.3.1: {}
pstree.remy@1.1.8: {}
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
require-directory@2.1.1: {}
rxjs@7.8.2:
dependencies:
tslib: 2.8.1
semver@7.7.3: {}
shell-quote@1.8.3: {}
simple-update-notifier@2.0.0:
dependencies:
semver: 7.7.3
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
source-map: 0.6.1
source-map@0.6.1: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
supports-color@8.1.1:
dependencies:
has-flag: 4.0.0
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
touch@3.1.1: {}
tree-kill@1.2.2: {}
tslib@2.8.1: {}
undefsafe@2.0.5: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
ws@8.18.3: {}
y18n@5.0.8: {}
yargs-parser@21.1.1: {}
yargs@17.7.2:
dependencies:
cliui: 8.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1

View File

View File

@@ -3,5 +3,5 @@
set -ex set -ex
corepack enable; corepack enable;
corepack install; corepack install;
pnpm install; yarn install;
pnpm run test; yarn run test;

View File

@@ -75,25 +75,20 @@
#?(:cljs #?(:cljs
(defn ->clj (defn ->clj
[o & {:keys [key-fn val-fn recursive] :or {key-fn read-kebab-key val-fn identity recursive true}}] [o & {:keys [key-fn val-fn] :or {key-fn read-kebab-key val-fn identity}}]
(let [f (fn this-fn [x] (let [f (fn this-fn [x]
(let [x (val-fn x)] (let [x (val-fn x)]
(cond (cond
(array? x) (array? x)
(persistent! (persistent!
(.reduce ^js/Array x (.reduce ^js/Array x
#(conj! %1 (if recursive #(conj! %1 (this-fn %2))
(this-fn %2)
%2))
(transient []))) (transient [])))
(identical? (type x) js/Object) (identical? (type x) js/Object)
(persistent! (persistent!
(.reduce ^js/Array (js-keys x) (.reduce ^js/Array (js-keys x)
#(assoc! %1 (key-fn %2) #(assoc! %1 (key-fn %2) (this-fn (unchecked-get x %2)))
(if recursive
(this-fn (unchecked-get x %2))
(unchecked-get x %2)))
(transient {}))) (transient {})))
:else :else

1291
common/yarn.lock Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
sudo chown penpot:users /home/penpot
cd ~;
source ~/.bashrc
set -e;
echo "[start-tmux.sh] Installing node dependencies"
pushd ~/penpot/exporter/
yarn install
popd
tmux -2 new-session -d -s penpot
tmux rename-window -t penpot:0 'exporter'
tmux select-window -t penpot:0
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
tmux send-keys -t penpot 'rm -f target/app.js*' enter C-l
tmux send-keys -t penpot 'clojure -M:dev:shadow-cljs watch main' enter
tmux split-window -v
tmux send-keys -t penpot 'cd penpot/exporter' enter C-l
tmux send-keys -t penpot './scripts/wait-and-start.sh' enter
tmux new-window -t penpot:1 -n 'backend'
tmux select-window -t penpot:1
tmux send-keys -t penpot 'cd penpot/backend' enter C-l
tmux send-keys -t penpot './scripts/start-dev' enter
tmux -2 attach-session -t penpot

View File

@@ -112,6 +112,10 @@ COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick
WORKDIR /opt/penpot/exporter WORKDIR /opt/penpot/exporter
USER penpot:penpot USER penpot:penpot
RUN ./setup RUN set -ex; \
corepack install; \
yarn install; \
yarn run playwright install chromium; \
rm -rf /opt/penpot/.yarn
CMD ["node", "app.js"] CMD ["node", "app.js"]

View File

@@ -152,9 +152,9 @@ services:
# AWS_ACCESS_KEY_ID: <KEY_ID> # AWS_ACCESS_KEY_ID: <KEY_ID>
# AWS_SECRET_ACCESS_KEY: <ACCESS_KEY> # AWS_SECRET_ACCESS_KEY: <ACCESS_KEY>
# PENPOT_OBJECTS_STORAGE_BACKEND: s3 # PENPOT_ASSETS_STORAGE_BACKEND: assets-s3
# PENPOT_OBJECTS_STORAGE_S3_ENDPOINT: <ENDPOINT> # PENPOT_STORAGE_ASSETS_S3_ENDPOINT: <ENDPOINT>
# PENPOT_OBJECTS_STORAGE_S3_BUCKET: <BUKET_NAME> # PENPOT_STORAGE_ASSETS_S3_BUCKET: <BUKET_NAME>
## Telemetry. When enabled, a periodical process will send anonymous data about this ## Telemetry. When enabled, a periodical process will send anonymous data about this
## instance. Telemetry data will enable us to learn how the application is used, ## instance. Telemetry data will enable us to learn how the application is used,

View File

@@ -10,15 +10,16 @@ To view this site locally, first set up the environment:
# only if necessary # only if necessary
nvm install nvm install
nvm use nvm use
# only if necessary
corepack enable corepack enable
pnpm install yarn install
``` ```
And launch a development server: And launch a development server:
```sh ```sh
pnpm start yarn start
``` ```
You can then point a browser to [http://localhost:8080](http://localhost:8080). You can then point a browser to [http://localhost:8080](http://localhost:8080).

View File

@@ -39,5 +39,5 @@
"markdown-it-anchor": "^9.0.1", "markdown-it-anchor": "^9.0.1",
"markdown-it-plantuml": "^1.4.1" "markdown-it-plantuml": "^1.4.1"
}, },
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48" "packageManager": "yarn@4.3.1"
} }

2065
docs/pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -6,5 +6,5 @@ corepack enable;
corepack install; corepack install;
rm -rf ./_dist rm -rf ./_dist
pnpm install yarn install
pnpm run build yarn run build

View File

@@ -114,7 +114,14 @@ configuration.
The callback has the following format: The callback has the following format:
```html ```html
https://<your_domain>/api/auth/oidc/callback https://<your_domain>/api/auth/oauth/<oauth_provider>/callback
```
You will need to change <your_domain> and <oauth_provider> according to your setup.
This is how it looks with Gitlab provider:
```html
https://<your_domain>/api/auth/oauth/gitlab/callback
``` ```
#### Google #### Google

View File

@@ -281,8 +281,8 @@ for how to define custom metadata and other ways of selecting tests.
it, but for now we use shadow-cljs with <code class="language-text">package.json</code> scripts: it, but for now we use shadow-cljs with <code class="language-text">package.json</code> scripts:
```bash ```bash
pnpm run test yarn run test
pnpm run test:watch yarn run test:watch
``` ```
#### Test output #### Test output

View File

@@ -217,7 +217,7 @@ repository:
```bash ```bash
# cd <repo>/frontend # cd <repo>/frontend
pnpm run translations yarn run translations
``` ```
At Penpot core team we maintain manually the english and spanish .po files. All At Penpot core team we maintain manually the english and spanish .po files. All
@@ -308,7 +308,7 @@ Ensure your development environment docker image is up to date.
This is not required, but it may be convenient to compile Penpot in release mode before running the tests. This way they will be much quicker and stable. For this, go to the frontend window in the tmux session (<code class="language-bash">Ctrl + b 1</code>), interrupt the watch process with <code class="language-bash">Ctrl + C</code> and type: This is not required, but it may be convenient to compile Penpot in release mode before running the tests. This way they will be much quicker and stable. For this, go to the frontend window in the tmux session (<code class="language-bash">Ctrl + b 1</code>), interrupt the watch process with <code class="language-bash">Ctrl + C</code> and type:
```bash ```bash
./scripts/build yarn run build:app
``` ```
Obviously, in this mode if you make changes to the source code, you will need to repeat the build manually each time. It may be useful to use wath mode when debugging a single test, and use release mode to run all the suite. Obviously, in this mode if you make changes to the source code, you will need to repeat the build manually each time. It may be useful to use wath mode when debugging a single test, and use release mode to run all the suite.
@@ -328,17 +328,17 @@ Here's how to run the tests with a headless browser (i.e. within the terminal, n
cd penpot/frontend cd penpot/frontend
``` ```
3. Run the tests with <code class="language-bash">pnpm</code>: 3. Run the tests with <code class="language-bash">yarn</code>:
```bash ```bash
pnpm run test:e2e yarn test:e2e
``` ```
> 💡 **TIP:** By default, the tests will _not_ run in parallel. You can set the amount of workers to run the tests with <code class="language-bash">--workers</code>. Note that, depending on your machine, this might make some tests flaky. > 💡 **TIP:** By default, the tests will _not_ run in parallel. You can set the amount of workers to run the tests with <code class="language-bash">--workers</code>. Note that, depending on your machine, this might make some tests flaky.
```bash ```bash
# run in parallel with 4 workers # run in parallel with 4 workers
pnpm run test:e2e --workers 4 yarn test:e2e --workers 4
``` ```
#### Running the tests in Chromium #### Running the tests in Chromium
@@ -356,7 +356,7 @@ npx playwright test --ui
> ❗️ **IMPORTANT**: You might need to [install Playwright's browsers and dependencies](https://playwright.dev/docs/intro) in your host machine with: <code class="language-bash">npx playwright install --with-deps</code>. In case you are using a Linux distribution other than Ubuntu, [you might need to install the dependencies manually](https://github.com/microsoft/playwright/issues/11122). > ❗️ **IMPORTANT**: You might need to [install Playwright's browsers and dependencies](https://playwright.dev/docs/intro) in your host machine with: <code class="language-bash">npx playwright install --with-deps</code>. In case you are using a Linux distribution other than Ubuntu, [you might need to install the dependencies manually](https://github.com/microsoft/playwright/issues/11122).
> You will also need pnpm in your host nodejs. For this, do <code class="language-bash">corepack enable</code> and then just <code class="language-bash">pnpm</code>. > You will also need yarn in your host nodejs. For this, do <code class="language-bash">corepack enable</code> and then just <code class="language-bash">yarn</code>.
### How to write a test ### How to write a test

View File

@@ -677,7 +677,7 @@ The Storybook is available at the <code class="language-bash">/storybook</code>
#### Local development #### Local development
Use <code class="language-bash">pnpm run watch:storybook</code> to develop the Design System components with the help of Storybook. Use <code class="language-bash">yarn watch:storybook</code> to develop the Design System components with the help of Storybook.
> **⚠️ WARNING**: Do stop any existing Shadow CLJS and asset compilation jobs (like the ones running at tabs <code class="language-bash">0</code> and <code class="language-bash">1</code> in the devenv tmux), because <code class="language-bash">watch:storybook</code> will spawn their own. > **⚠️ WARNING**: Do stop any existing Shadow CLJS and asset compilation jobs (like the ones running at tabs <code class="language-bash">0</code> and <code class="language-bash">1</code> in the devenv tmux), because <code class="language-bash">watch:storybook</code> will spawn their own.

3169
docs/yarn.lock Normal file
View File

File diff suppressed because it is too large Load Diff

7
exporter/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6", "packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/penpot/penpot" "url": "https://github.com/penpot/penpot"
@@ -30,10 +30,10 @@
}, },
"scripts": { "scripts": {
"clear:shadow-cache": "rm -rf .shadow-cljs && rm -rf target", "clear:shadow-cache": "rm -rf .shadow-cljs && rm -rf target",
"watch:app": "pnpm run clear:shadow-cache && clojure -M:dev:shadow-cljs watch main", "watch:app": "yarn run clear:shadow-cache && clojure -M:dev:shadow-cljs watch main",
"watch": "pnpm run watch:app", "watch": "yarn run watch:app",
"build:app": "clojure -M:dev:shadow-cljs release main", "build:app": "clojure -M:dev:shadow-cljs release main",
"build": "pnpm run clear:shadow-cache && pnpm run build:app", "build": "yarn run clear:shadow-cache && yarn run build:app",
"fmt:clj:check": "cljfmt check --parallel=false src/", "fmt:clj:check": "cljfmt check --parallel=false src/",
"fmt:clj": "cljfmt fix --parallel=true src/", "fmt:clj": "cljfmt fix --parallel=true src/",
"lint:clj": "clj-kondo --parallel --lint src/" "lint:clj": "clj-kondo --parallel --lint src/"

1048
exporter/pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

View File

@@ -7,13 +7,15 @@ export NODE_ENV=production;
corepack enable; corepack enable;
corepack install || exit 1; corepack install || exit 1;
pnpm install || exit 1; yarn install || exit 1;
rm -rf target rm -rf target
# Build the application # Build the application
pnpm run build; yarn run build;
cp pnpm-lock.yaml target/; # Copy package*.json files
cp ../.yarnrc.yml target/;
cp yarn.lock target/;
cp package.json target/; cp package.json target/;
cat <<EOF | tee target/setup cat <<EOF | tee target/setup
@@ -21,8 +23,8 @@ cat <<EOF | tee target/setup
set -e; set -e;
corepack enable; corepack enable;
corepack install; corepack install;
pnpm install yarn install
pnpx playwright install chromium; yarn run playwright install chromium;
EOF EOF
chmod +x target/setup; chmod +x target/setup;

View File

@@ -4,5 +4,5 @@ set -e;
corepack enable; corepack enable;
corepack install; corepack install;
pnpm install; yarn install;
pnpx playwright install chromium yarn playwright install chromium

View File

@@ -4,4 +4,4 @@ TARGET=${1:-app};
set -ex set -ex
exec pnpm run watch:$TARGET exec yarn run watch:$TARGET

1658
exporter/yarn.lock Normal file
View File

File diff suppressed because it is too large Load Diff

14
frontend/.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/**/visual-specs/**/*.png

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6", "packageManager": "yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f",
"browserslist": [ "browserslist": [
"defaults" "defaults"
], ],
@@ -13,25 +13,32 @@
"type": "git", "type": "git",
"url": "https://github.com/penpot/penpot" "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"
},
"scripts": { "scripts": {
"build:app:assets": "node ./scripts/build-app-assets.js", "build:app:assets": "node ./scripts/build-app-assets.js",
"build:storybook": "pnpm run build:storybook:assets && pnpm run build:storybook:cljs && storybook build", "build:storybook": "yarn run build:storybook:assets && yarn run build:storybook:cljs && storybook build",
"build:storybook:assets": "node ./scripts/build-storybook-assets.js", "build:storybook:assets": "node ./scripts/build-storybook-assets.js",
"build:wasm": "../render-wasm/build", "build:wasm": "../render-wasm/build",
"build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook", "build:storybook:cljs": "clojure -M:dev:shadow-cljs compile storybook",
"build:app:libs": "node ./scripts/build-libs.js", "build:app:libs": "node ./scripts/build-libs.js",
"build:app:main": "clojure -M:dev:shadow-cljs release main worker", "build:app:main": "clojure -M:dev:shadow-cljs release main worker",
"build:app:worker": "clojure -M:dev:shadow-cljs release worker", "build:app:worker": "clojure -M:dev:shadow-cljs release worker",
"build:app": "pnpm run clear:shadow-cache && pnpm run build:app:main && pnpm run build:app:libs", "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/", "fmt:clj": "cljfmt fix --parallel=true src/ test/",
"fmt:clj:check": "cljfmt check --parallel=false src/ test/", "fmt:clj:check": "cljfmt check --parallel=false src/ test/",
"fmt:js": "pnpx prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js -w", "fmt:js": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js -c text-editor/**/*.js -w",
"fmt:js:check": "pnpx prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js text-editor/**/*.js", "fmt:js:check": "yarn run prettier -c src/**/*.stories.jsx -c playwright/**/*.js -c scripts/**/*.js text-editor/**/*.js",
"lint:clj": "clj-kondo --parallel --lint src/", "lint:clj": "clj-kondo --parallel --lint src/",
"lint:scss": "pnpx prettier -c resources/styles -c src/**/*.scss", "lint:scss": "yarn run prettier -c resources/styles -c src/**/*.scss",
"lint:scss:fix": "pnpx prettier -c resources/styles -c src/**/*.scss -w", "lint:scss:fix": "yarn run prettier -c resources/styles -c src/**/*.scss -w",
"build:test": "clojure -M:dev:shadow-cljs compile test", "build:test": "clojure -M:dev:shadow-cljs compile test",
"test": "pnpm run build:test && node target/tests/test.js", "test": "yarn run build:test && node target/tests/test.js",
"test:storybook": "vitest run --project=storybook", "test:storybook": "vitest run --project=storybook",
"watch:test": "mkdir -p target/tests && concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests --exec 'node target/tests/test.js'\"", "watch:test": "mkdir -p target/tests && concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests --exec 'node target/tests/test.js'\"",
"test:e2e": "playwright test --project default", "test:e2e": "playwright test --project default",
@@ -41,31 +48,31 @@
"watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook", "watch:app:main": "clojure -M:dev:shadow-cljs watch main worker storybook",
"clear:shadow-cache": "rm -rf .shadow-cljs", "clear:shadow-cache": "rm -rf .shadow-cljs",
"watch": "exit 0", "watch": "exit 0",
"watch:app": "pnpm run clear:shadow-cache && pnpm run build:wasm && concurrently --kill-others-on-fail \"pnpm run watch:app:assets\" \"pnpm run watch:app:main\" \"pnpm run watch:app:libs\"", "watch:app": "yarn run clear:shadow-cache && concurrently --kill-others-on-fail \"yarn run watch:app:assets\" \"yarn run watch:app:main\" \"yarn run watch:app:libs\"",
"watch:storybook": "pnpm run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\"" "watch:storybook": "yarn run build:storybook:assets && concurrently --kill-others-on-fail \"storybook dev -p 6006 --no-open\" \"node ./scripts/watch-storybook.js\""
}, },
"devDependencies": { "devDependencies": {
"@penpot/draft-js": "workspace:./packages/draft-js", "@penpot/draft-js": "portal:./packages/draft-js",
"@penpot/mousetrap": "workspace:./packages/mousetrap", "@penpot/mousetrap": "portal:./packages/mousetrap",
"@penpot/plugins-runtime": "1.4.2", "@penpot/plugins-runtime": "1.3.2",
"@penpot/svgo": "penpot/svgo#v3.2", "@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "workspace:./text-editor", "@penpot/text-editor": "portal:./text-editor",
"@playwright/test": "1.57.0", "@playwright/test": "1.57.0",
"@storybook/addon-docs": "10.1.11", "@storybook/addon-docs": "10.1.11",
"@storybook/addon-themes": "10.1.11", "@storybook/addon-themes": "10.1.11",
"@storybook/addon-vitest": "10.1.11", "@storybook/addon-vitest": "10.1.11",
"@storybook/react-vite": "10.1.11", "@storybook/react-vite": "10.1.11",
"@tokens-studio/sd-transforms": "1.2.11", "@tokens-studio/sd-transforms": "1.2.11",
"@types/node": "^25.0.3", "@types/node": "^22.19.3",
"@vitest/browser": "4.0.16", "@vitest/browser": "4.0.16",
"@vitest/browser-playwright": "^4.0.16", "@vitest/browser-playwright": "^4.0.16",
"@vitest/coverage-v8": "4.0.16", "@vitest/coverage-v8": "4.0.16",
"@zip.js/zip.js": "2.8.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",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"compression": "^1.8.1", "compression": "^1.8.1",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"esbuild": "^0.27.2", "esbuild": "^0.25.9",
"eventsource-parser": "^3.0.6", "eventsource-parser": "^3.0.6",
"express": "^5.1.0", "express": "^5.1.0",
"fancy-log": "^2.0.0", "fancy-log": "^2.0.0",
@@ -84,7 +91,7 @@
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"opentype.js": "^1.3.4", "opentype.js": "^1.3.4",
"p-limit": "^6.2.0", "p-limit": "^6.2.0",
"playwright": "1.57.0", "playwright": "1.56.1",
"postcss": "^8.5.4", "postcss": "^8.5.4",
"postcss-clean": "^1.2.2", "postcss-clean": "^1.2.2",
"postcss-modules": "^6.0.1", "postcss-modules": "^6.0.1",
@@ -92,9 +99,9 @@
"pretty-time": "^1.1.0", "pretty-time": "^1.1.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"randomcolor": "^0.6.2", "randomcolor": "^0.6.2",
"react": "19.2.3", "react": "19.1.1",
"react-dom": "19.2.3", "react-dom": "19.1.1",
"react-error-boundary": "^6.1.0", "react-error-boundary": "^6.0.0",
"react-virtualized": "^9.22.6", "react-virtualized": "^9.22.6",
"rimraf": "^6.0.1", "rimraf": "^6.0.1",
"rxjs": "8.0.0-alpha.14", "rxjs": "8.0.0-alpha.14",
@@ -108,7 +115,7 @@
"tdigest": "^0.1.2", "tdigest": "^0.1.2",
"tinycolor2": "^1.6.0", "tinycolor2": "^1.6.0",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"ua-parser-js": "2.0.7", "ua-parser-js": "2.0.5",
"vite": "^7.3.0", "vite": "^7.3.0",
"vitest": "^4.0.16", "vitest": "^4.0.16",
"wait-on": "^9.0.3", "wait-on": "^9.0.3",

View File

@@ -4,7 +4,7 @@
"description": "Penpot Draft-JS Wrapper", "description": "Penpot Draft-JS Wrapper",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6", "packageManager": "yarn@4.3.1",
"author": "Andrey Antukh", "author": "Andrey Antukh",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
@@ -16,6 +16,6 @@
"react-dom": ">=0.17.0" "react-dom": ">=0.17.0"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.27.2" "esbuild": "^0.24.0"
} }
} }

View File

@@ -1,449 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
draft-js:
specifier: penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0
version: https://codeload.github.com/penpot/draft-js/tar.gz/4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
immutable:
specifier: ^5.1.4
version: 5.1.4
react:
specifier: '>=0.17.0'
version: 19.2.3
react-dom:
specifier: '>=0.17.0'
version: 19.2.3(react@19.2.3)
devDependencies:
esbuild:
specifier: ^0.27.2
version: 0.27.2
packages:
'@esbuild/aix-ppc64@0.27.2':
resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.27.2':
resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.27.2':
resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.27.2':
resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.27.2':
resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.27.2':
resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.27.2':
resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.2':
resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.27.2':
resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.27.2':
resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.27.2':
resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.27.2':
resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.27.2':
resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.27.2':
resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.27.2':
resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.27.2':
resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.27.2':
resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.27.2':
resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.2':
resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.27.2':
resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.2':
resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.27.2':
resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.27.2':
resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.27.2':
resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.27.2':
resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.27.2':
resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
cross-fetch@3.2.0:
resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==}
draft-js@https://codeload.github.com/penpot/draft-js/tar.gz/4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0:
resolution: {tarball: https://codeload.github.com/penpot/draft-js/tar.gz/4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0}
version: 0.11.7
peerDependencies:
react: '>=0.14.0'
react-dom: '>=0.14.0'
esbuild@0.27.2:
resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==}
engines: {node: '>=18'}
hasBin: true
fbjs-css-vars@1.0.2:
resolution: {integrity: sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==}
fbjs@3.0.5:
resolution: {integrity: sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==}
immutable@3.7.6:
resolution: {integrity: sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==}
engines: {node: '>=0.8.0'}
immutable@5.1.4:
resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
promise@7.3.1:
resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==}
react-dom@19.2.3:
resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==}
peerDependencies:
react: ^19.2.3
react@19.2.3:
resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==}
engines: {node: '>=0.10.0'}
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
ua-parser-js@1.0.41:
resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==}
hasBin: true
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
snapshots:
'@esbuild/aix-ppc64@0.27.2':
optional: true
'@esbuild/android-arm64@0.27.2':
optional: true
'@esbuild/android-arm@0.27.2':
optional: true
'@esbuild/android-x64@0.27.2':
optional: true
'@esbuild/darwin-arm64@0.27.2':
optional: true
'@esbuild/darwin-x64@0.27.2':
optional: true
'@esbuild/freebsd-arm64@0.27.2':
optional: true
'@esbuild/freebsd-x64@0.27.2':
optional: true
'@esbuild/linux-arm64@0.27.2':
optional: true
'@esbuild/linux-arm@0.27.2':
optional: true
'@esbuild/linux-ia32@0.27.2':
optional: true
'@esbuild/linux-loong64@0.27.2':
optional: true
'@esbuild/linux-mips64el@0.27.2':
optional: true
'@esbuild/linux-ppc64@0.27.2':
optional: true
'@esbuild/linux-riscv64@0.27.2':
optional: true
'@esbuild/linux-s390x@0.27.2':
optional: true
'@esbuild/linux-x64@0.27.2':
optional: true
'@esbuild/netbsd-arm64@0.27.2':
optional: true
'@esbuild/netbsd-x64@0.27.2':
optional: true
'@esbuild/openbsd-arm64@0.27.2':
optional: true
'@esbuild/openbsd-x64@0.27.2':
optional: true
'@esbuild/openharmony-arm64@0.27.2':
optional: true
'@esbuild/sunos-x64@0.27.2':
optional: true
'@esbuild/win32-arm64@0.27.2':
optional: true
'@esbuild/win32-ia32@0.27.2':
optional: true
'@esbuild/win32-x64@0.27.2':
optional: true
asap@2.0.6: {}
cross-fetch@3.2.0:
dependencies:
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
draft-js@https://codeload.github.com/penpot/draft-js/tar.gz/4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
fbjs: 3.0.5
immutable: 3.7.6
object-assign: 4.1.1
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
transitivePeerDependencies:
- encoding
esbuild@0.27.2:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.2
'@esbuild/android-arm': 0.27.2
'@esbuild/android-arm64': 0.27.2
'@esbuild/android-x64': 0.27.2
'@esbuild/darwin-arm64': 0.27.2
'@esbuild/darwin-x64': 0.27.2
'@esbuild/freebsd-arm64': 0.27.2
'@esbuild/freebsd-x64': 0.27.2
'@esbuild/linux-arm': 0.27.2
'@esbuild/linux-arm64': 0.27.2
'@esbuild/linux-ia32': 0.27.2
'@esbuild/linux-loong64': 0.27.2
'@esbuild/linux-mips64el': 0.27.2
'@esbuild/linux-ppc64': 0.27.2
'@esbuild/linux-riscv64': 0.27.2
'@esbuild/linux-s390x': 0.27.2
'@esbuild/linux-x64': 0.27.2
'@esbuild/netbsd-arm64': 0.27.2
'@esbuild/netbsd-x64': 0.27.2
'@esbuild/openbsd-arm64': 0.27.2
'@esbuild/openbsd-x64': 0.27.2
'@esbuild/openharmony-arm64': 0.27.2
'@esbuild/sunos-x64': 0.27.2
'@esbuild/win32-arm64': 0.27.2
'@esbuild/win32-ia32': 0.27.2
'@esbuild/win32-x64': 0.27.2
fbjs-css-vars@1.0.2: {}
fbjs@3.0.5:
dependencies:
cross-fetch: 3.2.0
fbjs-css-vars: 1.0.2
loose-envify: 1.4.0
object-assign: 4.1.1
promise: 7.3.1
setimmediate: 1.0.5
ua-parser-js: 1.0.41
transitivePeerDependencies:
- encoding
immutable@3.7.6: {}
immutable@5.1.4: {}
js-tokens@4.0.0: {}
loose-envify@1.4.0:
dependencies:
js-tokens: 4.0.0
node-fetch@2.7.0:
dependencies:
whatwg-url: 5.0.0
object-assign@4.1.1: {}
promise@7.3.1:
dependencies:
asap: 2.0.6
react-dom@19.2.3(react@19.2.3):
dependencies:
react: 19.2.3
scheduler: 0.27.0
react@19.2.3: {}
scheduler@0.27.0: {}
setimmediate@1.0.5: {}
tr46@0.0.3: {}
ua-parser-js@1.0.41: {}
webidl-conversions@3.0.1: {}
whatwg-url@5.0.0:
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1

View File

@@ -0,0 +1,424 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"@esbuild/aix-ppc64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/aix-ppc64@npm:0.24.0"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/android-arm64@npm:0.24.0"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/android-arm@npm:0.24.0"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/android-x64@npm:0.24.0"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/darwin-arm64@npm:0.24.0"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/darwin-x64@npm:0.24.0"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/freebsd-arm64@npm:0.24.0"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/freebsd-x64@npm:0.24.0"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-arm64@npm:0.24.0"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-arm@npm:0.24.0"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-ia32@npm:0.24.0"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-loong64@npm:0.24.0"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-mips64el@npm:0.24.0"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-ppc64@npm:0.24.0"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-riscv64@npm:0.24.0"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-s390x@npm:0.24.0"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/linux-x64@npm:0.24.0"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/netbsd-x64@npm:0.24.0"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/openbsd-arm64@npm:0.24.0"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/openbsd-x64@npm:0.24.0"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/sunos-x64@npm:0.24.0"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/win32-arm64@npm:0.24.0"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/win32-ia32@npm:0.24.0"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.24.0":
version: 0.24.0
resolution: "@esbuild/win32-x64@npm:0.24.0"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@penpot/draft-js@workspace:.":
version: 0.0.0-use.local
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"
languageName: unknown
linkType: soft
"asap@npm:~2.0.3":
version: 2.0.6
resolution: "asap@npm:2.0.6"
checksum: 10c0/c6d5e39fe1f15e4b87677460bd66b66050cd14c772269cee6688824c1410a08ab20254bb6784f9afb75af9144a9f9a7692d49547f4d19d715aeb7c0318f3136d
languageName: node
linkType: hard
"cross-fetch@npm:^3.1.5":
version: 3.1.8
resolution: "cross-fetch@npm:3.1.8"
dependencies:
node-fetch: "npm:^2.6.12"
checksum: 10c0/4c5e022ffe6abdf380faa6e2373c0c4ed7ef75e105c95c972b6f627c3f083170b6886f19fb488a7fa93971f4f69dcc890f122b0d97f0bf5f41ca1d9a8f58c8af
languageName: node
linkType: hard
"draft-js@penpot/draft-js.git#4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0":
version: 0.11.7
resolution: "draft-js@https://github.com/penpot/draft-js.git#commit=4a99b2a6020b2af97f6dc5fa1b4275ec16b559a0"
dependencies:
fbjs: "npm:^3.0.4"
immutable: "npm:~3.7.4"
object-assign: "npm:^4.1.1"
peerDependencies:
react: ">=0.14.0"
react-dom: ">=0.14.0"
checksum: 10c0/dcd6fd9481b445c0df31a414d5bf0b84ad691d50ac90d805b65c36fb4d26b1ada787f37a63cb437e2a1b6d8dc0f95b4f3c41f6a8082480235ab48b391900a43b
languageName: node
linkType: hard
"esbuild@npm:^0.24.0":
version: 0.24.0
resolution: "esbuild@npm:0.24.0"
dependencies:
"@esbuild/aix-ppc64": "npm:0.24.0"
"@esbuild/android-arm": "npm:0.24.0"
"@esbuild/android-arm64": "npm:0.24.0"
"@esbuild/android-x64": "npm:0.24.0"
"@esbuild/darwin-arm64": "npm:0.24.0"
"@esbuild/darwin-x64": "npm:0.24.0"
"@esbuild/freebsd-arm64": "npm:0.24.0"
"@esbuild/freebsd-x64": "npm:0.24.0"
"@esbuild/linux-arm": "npm:0.24.0"
"@esbuild/linux-arm64": "npm:0.24.0"
"@esbuild/linux-ia32": "npm:0.24.0"
"@esbuild/linux-loong64": "npm:0.24.0"
"@esbuild/linux-mips64el": "npm:0.24.0"
"@esbuild/linux-ppc64": "npm:0.24.0"
"@esbuild/linux-riscv64": "npm:0.24.0"
"@esbuild/linux-s390x": "npm:0.24.0"
"@esbuild/linux-x64": "npm:0.24.0"
"@esbuild/netbsd-x64": "npm:0.24.0"
"@esbuild/openbsd-arm64": "npm:0.24.0"
"@esbuild/openbsd-x64": "npm:0.24.0"
"@esbuild/sunos-x64": "npm:0.24.0"
"@esbuild/win32-arm64": "npm:0.24.0"
"@esbuild/win32-ia32": "npm:0.24.0"
"@esbuild/win32-x64": "npm:0.24.0"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
"@esbuild/android-arm":
optional: true
"@esbuild/android-arm64":
optional: true
"@esbuild/android-x64":
optional: true
"@esbuild/darwin-arm64":
optional: true
"@esbuild/darwin-x64":
optional: true
"@esbuild/freebsd-arm64":
optional: true
"@esbuild/freebsd-x64":
optional: true
"@esbuild/linux-arm":
optional: true
"@esbuild/linux-arm64":
optional: true
"@esbuild/linux-ia32":
optional: true
"@esbuild/linux-loong64":
optional: true
"@esbuild/linux-mips64el":
optional: true
"@esbuild/linux-ppc64":
optional: true
"@esbuild/linux-riscv64":
optional: true
"@esbuild/linux-s390x":
optional: true
"@esbuild/linux-x64":
optional: true
"@esbuild/netbsd-x64":
optional: true
"@esbuild/openbsd-arm64":
optional: true
"@esbuild/openbsd-x64":
optional: true
"@esbuild/sunos-x64":
optional: true
"@esbuild/win32-arm64":
optional: true
"@esbuild/win32-ia32":
optional: true
"@esbuild/win32-x64":
optional: true
bin:
esbuild: bin/esbuild
checksum: 10c0/9f1aadd8d64f3bff422ae78387e66e51a5e09de6935a6f987b6e4e189ed00fdc2d1bc03d2e33633b094008529c8b6e06c7ad1a9782fb09fec223bf95998c0683
languageName: node
linkType: hard
"fbjs-css-vars@npm:^1.0.0":
version: 1.0.2
resolution: "fbjs-css-vars@npm:1.0.2"
checksum: 10c0/dfb64116b125a64abecca9e31477b5edb9a2332c5ffe74326fe36e0a72eef7fc8a49b86adf36c2c293078d79f4524f35e80f5e62546395f53fb7c9e69821f54f
languageName: node
linkType: hard
"fbjs@npm:^3.0.4":
version: 3.0.5
resolution: "fbjs@npm:3.0.5"
dependencies:
cross-fetch: "npm:^3.1.5"
fbjs-css-vars: "npm:^1.0.0"
loose-envify: "npm:^1.0.0"
object-assign: "npm:^4.1.0"
promise: "npm:^7.1.1"
setimmediate: "npm:^1.0.5"
ua-parser-js: "npm:^1.0.35"
checksum: 10c0/66d0a2fc9a774f9066e35ac2ac4bf1245931d27f3ac287c7d47e6aa1fc152b243c2109743eb8f65341e025621fb51a12038fadb9fd8fda2e3ddae04ebab06f91
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"
checksum: 10c0/efe2bbb2620aa897afbb79545b9eda4dd3dc072e05ae7004895a7efb43187e4265612a88f8723f391eb1c87c46c52fd11e2d1968e42404450c63e49558d7ca4e
languageName: node
linkType: hard
"js-tokens@npm:^3.0.0 || ^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed
languageName: node
linkType: hard
"loose-envify@npm:^1.0.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
js-tokens: "npm:^3.0.0 || ^4.0.0"
bin:
loose-envify: cli.js
checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e
languageName: node
linkType: hard
"node-fetch@npm:^2.6.12":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
whatwg-url: "npm:^5.0.0"
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8
languageName: node
linkType: hard
"object-assign@npm:^4.1.0, object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414
languageName: node
linkType: hard
"promise@npm:^7.1.1":
version: 7.3.1
resolution: "promise@npm:7.3.1"
dependencies:
asap: "npm:~2.0.3"
checksum: 10c0/742e5c0cc646af1f0746963b8776299701ad561ce2c70b49365d62c8db8ea3681b0a1bf0d4e2fe07910bf72f02d39e51e8e73dc8d7503c3501206ac908be107f
languageName: node
linkType: hard
"setimmediate@npm:^1.0.5":
version: 1.0.5
resolution: "setimmediate@npm:1.0.5"
checksum: 10c0/5bae81bfdbfbd0ce992893286d49c9693c82b1bcc00dcaaf3a09c8f428fdeacf4190c013598b81875dfac2b08a572422db7df779a99332d0fce186d15a3e4d49
languageName: node
linkType: hard
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11
languageName: node
linkType: hard
"ua-parser-js@npm:^1.0.35":
version: 1.0.39
resolution: "ua-parser-js@npm:1.0.39"
bin:
ua-parser-js: script/cli.js
checksum: 10c0/c6452b0c683000f10975cb0a7e74cb1119ea95d4522ae85f396fa53b0b17884358a24ffdd86a66030c6b2981bdc502109a618c79fdaa217ee9032c9e46fcc78a
languageName: node
linkType: hard
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db
languageName: node
linkType: hard
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
dependencies:
tr46: "npm:~0.0.3"
webidl-conversions: "npm:^3.0.0"
checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5
languageName: node
linkType: hard

View File

@@ -4,7 +4,7 @@
"description": "Simple library for handling keyboard shortcuts", "description": "Simple library for handling keyboard shortcuts",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6", "packageManager": "yarn@4.3.1",
"author": "Craig Campbell", "author": "Craig Campbell",
"license": "Apache-2.0 WITH LLVM-exception" "license": "Apache-2.0 WITH LLVM-exception"
} }

View File

@@ -0,0 +1,12 @@
# 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/mousetrap@workspace:.":
version: 0.0.0-use.local
resolution: "@penpot/mousetrap@workspace:."
languageName: unknown
linkType: soft

View File

@@ -1,17 +0,0 @@
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 {

View File

@@ -80,7 +80,7 @@ export default defineConfig({
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
webServer: { webServer: {
timeout: 2 * 60 * 1000, timeout: 2 * 60 * 1000,
command: "caddy file-server --root resources/public/ --listen :3000", command: "yarn run e2e:server",
url: "http://localhost:3000", url: "http://localhost:3000",
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, },

View File

@@ -5,19 +5,3 @@ export const presenceFixture = {
"~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", "~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:topic": "~uc7ce0794-0992-8105-8004-38f280443849", "~:topic": "~uc7ce0794-0992-8105-8004-38f280443849",
}; };
export const joinFixture2 = {
"~:type": "~:join-file",
"~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:session-id": "~u37730924-d520-80f1-8004-4ae6e5c3942e",
"~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:topic": "~uc7ce0794-0992-8105-8004-38f280443849",
};
export const joinFixture3 = {
"~:type": "~:join-file",
"~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:session-id": "~u37730924-d520-80f1-8004-4ae6e5c3942f",
"~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~:topic": "~uc7ce0794-0992-8105-8004-38f280443849",
};

View File

@@ -158,7 +158,7 @@ test.describe("Tokens - creation", () => {
const selfReferenceError = "Token has self reference"; const selfReferenceError = "Token has self reference";
const missingReferenceError = "Missing token references"; const missingReferenceError = "Missing token references";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
await tokensSidebar await tokensSidebar
.getByRole("button", { name: "Add Token: Color" }) .getByRole("button", { name: "Add Token: Color" })
@@ -189,7 +189,7 @@ test.describe("Tokens - creation", () => {
// 2. Invalid value → disabled + error message // 2. Invalid value → disabled + error message
await valueField.fill("1"); await valueField.fill("1");
const invalidValueErrorNode = const invalidValueErrorNode =
tokensUpdateCreateModal.getByText(invalidValueError); tokensUpdateCreateModal.getByText(invalidValueError);
await expect(invalidValueErrorNode).toBeVisible(); await expect(invalidValueErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -197,7 +197,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -207,7 +207,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("{color.primary}"); await valueField.fill("{color.primary}");
const selfRefErrorNode = const selfRefErrorNode =
tokensUpdateCreateModal.getByText(selfReferenceError); tokensUpdateCreateModal.getByText(selfReferenceError);
await expect(selfRefErrorNode).toBeVisible(); await expect(selfRefErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -320,7 +320,7 @@ test.describe("Tokens - creation", () => {
const missingReferenceError = "Missing token references"; const missingReferenceError = "Missing token references";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
// Open modal // Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
@@ -356,7 +356,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -366,7 +366,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("{my-token}"); await valueField.fill("{my-token}");
const selfRefErrorNode = const selfRefErrorNode =
tokensUpdateCreateModal.getByText(selfReferenceError); tokensUpdateCreateModal.getByText(selfReferenceError);
await expect(selfRefErrorNode).toBeVisible(); await expect(selfRefErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -459,13 +459,13 @@ test.describe("Tokens - creation", () => {
test("User creates font weight token", async ({ page }) => { test("User creates font weight token", async ({ page }) => {
const invalidValueError = const invalidValueError =
"Invalid font weight value: use numeric values (100-950) or standard names (thin, light, regular, bold, etc.) optionally followed by 'Italic'"; "Invalid font weight value: use numeric values (100-950) or standard names (thin, light, regular, bold, etc.) optionally followed by 'Italic'";
const emptyNameError = "Name should be at least 1 character"; const emptyNameError = "Name should be at least 1 character";
const selfReferenceError = "Token has self reference"; const selfReferenceError = "Token has self reference";
const missingReferenceError = "Missing token references"; const missingReferenceError = "Missing token references";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
// Open modal // Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
@@ -501,7 +501,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("red"); await valueField.fill("red");
const invalidValueErrorNode = const invalidValueErrorNode =
tokensUpdateCreateModal.getByText(invalidValueError); tokensUpdateCreateModal.getByText(invalidValueError);
await expect(invalidValueErrorNode).toBeVisible(); await expect(invalidValueErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -510,7 +510,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -520,7 +520,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("{my-token}"); await valueField.fill("{my-token}");
const selfRefErrorNode = const selfRefErrorNode =
tokensUpdateCreateModal.getByText(selfReferenceError); tokensUpdateCreateModal.getByText(selfReferenceError);
await expect(selfRefErrorNode).toBeVisible(); await expect(selfRefErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -595,13 +595,13 @@ test.describe("Tokens - creation", () => {
test("User creates text case token", async ({ page }) => { test("User creates text case token", async ({ page }) => {
const invalidValueError = const invalidValueError =
"Invalid token value: only none, Uppercase, Lowercase or Capitalize are accepted"; "Invalid token value: only none, Uppercase, Lowercase or Capitalize are accepted";
const emptyNameError = "Name should be at least 1 character"; const emptyNameError = "Name should be at least 1 character";
const selfReferenceError = "Token has self reference"; const selfReferenceError = "Token has self reference";
const missingReferenceError = "Missing token references"; const missingReferenceError = "Missing token references";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
// Open modal // Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
@@ -637,7 +637,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("red"); await valueField.fill("red");
const invalidValueErrorNode = const invalidValueErrorNode =
tokensUpdateCreateModal.getByText(invalidValueError); tokensUpdateCreateModal.getByText(invalidValueError);
await expect(invalidValueErrorNode).toBeVisible(); await expect(invalidValueErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -646,7 +646,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -656,7 +656,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("{my-token}"); await valueField.fill("{my-token}");
const selfRefErrorNode = const selfRefErrorNode =
tokensUpdateCreateModal.getByText(selfReferenceError); tokensUpdateCreateModal.getByText(selfReferenceError);
await expect(selfRefErrorNode).toBeVisible(); await expect(selfRefErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -711,13 +711,13 @@ test.describe("Tokens - creation", () => {
test("User creates text decoration token", async ({ page }) => { test("User creates text decoration token", async ({ page }) => {
const invalidValueError = const invalidValueError =
"Invalid token value: only none, underline and strike-through are accepted"; "Invalid token value: only none, underline and strike-through are accepted";
const emptyNameError = "Name should be at least 1 character"; const emptyNameError = "Name should be at least 1 character";
const selfReferenceError = "Token has self reference"; const selfReferenceError = "Token has self reference";
const missingReferenceError = "Missing token references"; const missingReferenceError = "Missing token references";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
// Open modal // Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
@@ -755,7 +755,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("red"); await valueField.fill("red");
const invalidValueErrorNode = const invalidValueErrorNode =
tokensUpdateCreateModal.getByText(invalidValueError); tokensUpdateCreateModal.getByText(invalidValueError);
await expect(invalidValueErrorNode).toBeVisible(); await expect(invalidValueErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -764,7 +764,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -774,7 +774,7 @@ test.describe("Tokens - creation", () => {
await valueField.fill("{my-token}"); await valueField.fill("{my-token}");
const selfRefErrorNode = const selfRefErrorNode =
tokensUpdateCreateModal.getByText(selfReferenceError); tokensUpdateCreateModal.getByText(selfReferenceError);
await expect(selfRefErrorNode).toBeVisible(); await expect(selfRefErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -831,7 +831,7 @@ test.describe("Tokens - creation", () => {
const emptyNameError = "Name should be at least 1 character"; const emptyNameError = "Name should be at least 1 character";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page, { flags: ["enable-token-shadow"] }); await setupEmptyTokensFile(page, { flags: ["enable-token-shadow"] });
// Open modal // Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
@@ -900,7 +900,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -977,9 +977,9 @@ test.describe("Tokens - creation", () => {
await nameField.fill("my-token-2"); await nameField.fill("my-token-2");
const referenceToggle = const referenceToggle =
tokensUpdateCreateModal.getByTestId("reference-opt"); tokensUpdateCreateModal.getByTestId("reference-opt");
const compositeToggle = const compositeToggle =
tokensUpdateCreateModal.getByTestId("composite-opt"); tokensUpdateCreateModal.getByTestId("composite-opt");
await referenceToggle.click(); await referenceToggle.click();
const referenceInput = tokensUpdateCreateModal.getByPlaceholder( const referenceInput = tokensUpdateCreateModal.getByPlaceholder(
@@ -1012,7 +1012,7 @@ test.describe("Tokens - creation", () => {
page, page,
}) => { }) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page); await setupTypographyTokensFile(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel await tokensTabPanel
@@ -1038,201 +1038,15 @@ test.describe("Tokens - creation", () => {
// Switch to reference tab, should not be submittable either // Switch to reference tab, should not be submittable either
const referenceTabButton = const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt"); tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click(); await referenceTabButton.click();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
}); });
test("User creates shadow token with negative spread", async ({ page }) => {
const emptyNameError = "Name should be at least 1 character";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page, {flags: ["enable-token-shadow"]});
// Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
const addTokenButton = tokensTabPanel.getByRole("button", {
name: `Add Token: Shadow`,
});
await addTokenButton.click();
await expect(tokensUpdateCreateModal).toBeVisible();
await expect(
tokensUpdateCreateModal.getByPlaceholder(
"Enter a value or alias with {alias}",
),
).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Color",
});
const offsetXField = tokensUpdateCreateModal.getByRole("textbox", {
name: "X",
});
const offsetYField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Y",
});
const blurField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Blur",
});
const spreadField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Spread",
});
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
// 1. Check default values
await expect(offsetXField).toHaveValue("4");
await expect(offsetYField).toHaveValue("4");
await expect(blurField).toHaveValue("4");
await expect(spreadField).toHaveValue("0");
// 2. Name filled + empty value → disabled
await nameField.fill("my-token");
await expect(submitButton).toBeDisabled();
// 3. Invalid color → disabled + error message
await colorField.fill("1");
await expect(
tokensUpdateCreateModal.getByText("Invalid color value: 1"),
).toBeVisible();
await expect(submitButton).toBeDisabled();
await colorField.fill("{missing-reference}");
await expect(
tokensUpdateCreateModal.getByText(
"Missing token references: missing-reference",
),
).toBeVisible();
// 4. Empty name → disabled + error message
await nameField.fill("");
const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled();
//
// ------- SUCCESSFUL FIELDS -------
//
// 5. Valid color → resolved
await colorField.fill("red");
await expect(
tokensUpdateCreateModal.getByText("Resolved value: #ff0000"),
).toBeVisible();
const colorSwatch = tokensUpdateCreateModal.getByTestId(
"token-form-color-bullet",
);
await colorSwatch.click();
const rampSelector = tokensUpdateCreateModal.getByTestId(
"value-saturation-selector",
);
await expect(rampSelector).toBeVisible();
await rampSelector.click({ position: { x: 50, y: 50 } });
await expect(
tokensUpdateCreateModal.getByText("Resolved value:"),
).toBeVisible();
const sliderOpacity = tokensUpdateCreateModal.getByTestId("slider-opacity");
await sliderOpacity.click({ position: { x: 50, y: 0 } });
await expect(
tokensUpdateCreateModal.getByRole("textbox", { name: "Color" }),
).toHaveValue(/rgba\s*\([^)]*\)/);
// 6. Valid offset → resolved
await offsetXField.fill("3 + 3");
await expect(
tokensUpdateCreateModal.getByText("Resolved value: 6"),
).toBeVisible();
await offsetYField.fill("3 + 7");
await expect(
tokensUpdateCreateModal.getByText("Resolved value: 10"),
).toBeVisible();
// 7. Valid blur → resolved
await blurField.fill("3 + 1");
await expect(
tokensUpdateCreateModal.getByText("Resolved value: 4"),
).toBeVisible();
// 8. Valid spread → resolved
await spreadField.fill("3 - 3");
await expect(
tokensUpdateCreateModal.getByText("Resolved value: 0"),
).toBeVisible();
await spreadField.fill("1 - 3");
await expect(
tokensUpdateCreateModal.getByText("Resolved value: -2"),
).toBeVisible();
await nameField.fill("my-token");
await expect(submitButton).toBeEnabled();
await submitButton.click();
await expect(
tokensTabPanel.getByRole("button", { name: "my-token" }),
).toBeEnabled();
//
// ------- SECOND TOKEN WITH VALID REFERENCE -------
//
await addTokenButton.click();
await nameField.fill("my-token-2");
const referenceToggle =
tokensUpdateCreateModal.getByTestId("reference-opt");
const compositeToggle =
tokensUpdateCreateModal.getByTestId("composite-opt");
await referenceToggle.click();
const referenceInput = tokensUpdateCreateModal.getByPlaceholder(
"Enter a token shadow alias",
);
await expect(referenceInput).toBeVisible();
await compositeToggle.click();
await expect(colorField).toBeVisible();
await referenceToggle.click();
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
name: "Reference",
});
await referenceField.fill("{my-token}");
await expect(
tokensUpdateCreateModal.getByText(
"Resolved value: - X: 6 - Y: 10 - Blur: 4 - Spread: -2",
),
).toBeVisible();
await expect(submitButton).toBeEnabled();
await submitButton.click();
await expect(
tokensTabPanel.getByRole("button", { name: "my-token-2" }),
).toBeEnabled();
});
test("User creates typography token", async ({ page }) => { test("User creates typography token", async ({ page }) => {
const emptyNameError = "Name should be at least 1 character"; const emptyNameError = "Name should be at least 1 character";
const { tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
// Open modal // Open modal
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
@@ -1287,7 +1101,7 @@ test.describe("Tokens - creation", () => {
await nameField.fill(""); await nameField.fill("");
const emptyNameErrorNode = const emptyNameErrorNode =
tokensUpdateCreateModal.getByText(emptyNameError); tokensUpdateCreateModal.getByText(emptyNameError);
await expect(emptyNameErrorNode).toBeVisible(); await expect(emptyNameErrorNode).toBeVisible();
await expect(submitButton).toBeDisabled(); await expect(submitButton).toBeDisabled();
@@ -1443,9 +1257,9 @@ test.describe("Tokens - creation", () => {
await nameField.fill("my-token-2"); await nameField.fill("my-token-2");
const referenceToggle = const referenceToggle =
tokensUpdateCreateModal.getByTestId("reference-opt"); tokensUpdateCreateModal.getByTestId("reference-opt");
const compositeToggle = const compositeToggle =
tokensUpdateCreateModal.getByTestId("composite-opt"); tokensUpdateCreateModal.getByTestId("composite-opt");
await referenceToggle.click(); await referenceToggle.click();
@@ -1479,7 +1293,7 @@ test.describe("Tokens - creation", () => {
test("User adds typography token with reference", async ({ page }) => { test("User adds typography token with reference", async ({ page }) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page); await setupTypographyTokensFile(page);
const newTokenTitle = "NewReference"; const newTokenTitle = "NewReference";
@@ -1508,7 +1322,7 @@ test.describe("Tokens - creation", () => {
}); });
const resolvedValue = const resolvedValue =
await tokensUpdateCreateModal.getByText("Resolved value:"); await tokensUpdateCreateModal.getByText("Resolved value:");
await expect(resolvedValue).toBeVisible(); await expect(resolvedValue).toBeVisible();
await expect(resolvedValue).toContainText("Font Family: 42dot Sans"); await expect(resolvedValue).toContainText("Font Family: 42dot Sans");
await expect(resolvedValue).toContainText("Font Size: 100"); await expect(resolvedValue).toContainText("Font Size: 100");
@@ -1531,7 +1345,7 @@ test.describe("Tokens - creation", () => {
test("User creates grouped color token", async ({ page }) => { test("User creates grouped color token", async ({ page }) => {
const { workspacePage, tokensUpdateCreateModal, tokensSidebar } = const { workspacePage, tokensUpdateCreateModal, tokensSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
await tokensSidebar await tokensSidebar
.getByRole("button", { name: "Add Token: Color" }) .getByRole("button", { name: "Add Token: Color" })
@@ -1591,7 +1405,7 @@ test.describe("Tokens - creation", () => {
test("User duplicate color token", async ({ page }) => { test("User duplicate color token", async ({ page }) => {
const { tokensSidebar, tokenContextMenuForToken } = const { tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page); await setupTokensFile(page);
await expect(tokensSidebar).toBeVisible(); await expect(tokensSidebar).toBeVisible();
@@ -1615,95 +1429,95 @@ test.describe("Tokens - creation", () => {
test("User creates grouped color token", async ({ page }) => { test("User creates grouped color token", async ({ page }) => {
const { workspacePage, tokensUpdateCreateModal, tokensSidebar } = const { workspacePage, tokensUpdateCreateModal, tokensSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
await tokensSidebar await tokensSidebar
.getByRole("button", { name: "Add Token: Color" }) .getByRole("button", { name: "Add Token: Color" })
.click(); .click();
// Create grouped color token with mouse // Create grouped color token with mouse
await expect(tokensUpdateCreateModal).toBeVisible(); await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name"); const nameField = tokensUpdateCreateModal.getByLabel("Name");
const valueField = tokensUpdateCreateModal.getByLabel("Value"); const valueField = tokensUpdateCreateModal.getByLabel("Value");
await nameField.click(); await nameField.click();
await nameField.fill("dark.primary"); await nameField.fill("dark.primary");
await valueField.click(); await valueField.click();
await valueField.fill("red"); await valueField.fill("red");
const submitButton = tokensUpdateCreateModal.getByRole("button", { const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save", name: "Save",
}); });
await expect(submitButton).toBeEnabled(); await expect(submitButton).toBeEnabled();
await submitButton.click(); await submitButton.click();
await unfoldTokenTree(tokensSidebar, "color", "dark.primary"); await unfoldTokenTree(tokensSidebar, "color", "dark.primary");
await expect(tokensSidebar.getByLabel("primary")).toBeEnabled(); await expect(tokensSidebar.getByLabel("primary")).toBeEnabled();
});
test("User cant create regular token with value missing", async ({
page,
}) => {
const { tokensUpdateCreateModal } = await setupEmptyTokensFile(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel
.getByRole("button", { name: "Add Token: Color" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
}); });
// Initially submit button should be disabled test("User cant create regular token with value missing", async ({
await expect(submitButton).toBeDisabled(); page,
}) => {
const { tokensUpdateCreateModal } = await setupEmptyTokensFile(page);
// Fill in name but leave value empty const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await nameField.click(); await tokensTabPanel
await nameField.fill("primary"); .getByRole("button", { name: "Add Token: Color" })
.click();
// Submit button should remain disabled when value is empty await expect(tokensUpdateCreateModal).toBeVisible();
await expect(submitButton).toBeDisabled();
});
test("User duplicate color token", async ({ page }) => { const nameField = tokensUpdateCreateModal.getByLabel("Name");
const { tokensSidebar, tokenContextMenuForToken } = const submitButton = tokensUpdateCreateModal.getByRole("button", {
await setupTokensFile(page); name: "Save",
});
await expect(tokensSidebar).toBeVisible(); // Initially submit button should be disabled
await expect(submitButton).toBeDisabled();
unfoldTokenTree(tokensSidebar, "color", "colors.blue.100"); // Fill in name but leave value empty
await nameField.click();
await nameField.fill("primary");
const colorToken = tokensSidebar.getByRole("button", { // Submit button should remain disabled when value is empty
name: "100", await expect(submitButton).toBeDisabled();
}); });
await colorToken.click({ button: "right" }); test("User duplicate color token", async ({ page }) => {
await expect(tokenContextMenuForToken).toBeVisible(); const { tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
await tokenContextMenuForToken.getByText("Duplicate token").click(); await expect(tokensSidebar).toBeVisible();
await expect(tokenContextMenuForToken).not.toBeVisible();
await expect( unfoldTokenTree(tokensSidebar, "color", "colors.blue.100");
tokensSidebar.getByRole("button", { name: "colors.blue.100-copy" }),
).toBeVisible(); const colorToken = tokensSidebar.getByRole("button", {
}); name: "100",
});
await colorToken.click({ button: "right" });
await expect(tokenContextMenuForToken).toBeVisible();
await tokenContextMenuForToken.getByText("Duplicate token").click();
await expect(tokenContextMenuForToken).not.toBeVisible();
await expect(
tokensSidebar.getByRole("button", { name: "colors.blue.100-copy" }),
).toBeVisible();
});
test.describe("Tokens tab - edition", () => { test.describe("Tokens tab - edition", () => {
test("User edits typography token and all fields are valid", async ({ test("User edits typography token and all fields are valid", async ({
page, page,
}) => { }) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } = const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page); await setupTypographyTokensFile(page);
await tokensSidebar await tokensSidebar
.getByRole("button") .getByRole("button")
@@ -1724,8 +1538,8 @@ test.describe("Tokens tab - edition", () => {
// Fill font-family to verify to verify that input value doesn't get split into list of characters // Fill font-family to verify to verify that input value doesn't get split into list of characters
const fontFamilyField = tokensUpdateCreateModal const fontFamilyField = tokensUpdateCreateModal
.getByLabel("Font family") .getByLabel("Font family")
.first(); .first();
await fontFamilyField.fill("OneWord"); await fontFamilyField.fill("OneWord");
// Invalidate incorrect values for font size // Invalidate incorrect values for font size
@@ -1746,11 +1560,11 @@ test.describe("Tokens tab - edition", () => {
const fontWeightField = tokensUpdateCreateModal.getByLabel(/Font Weight/i); const fontWeightField = tokensUpdateCreateModal.getByLabel(/Font Weight/i);
const letterSpacingField = const letterSpacingField =
tokensUpdateCreateModal.getByLabel(/Letter Spacing/i); tokensUpdateCreateModal.getByLabel(/Letter Spacing/i);
const lineHeightField = tokensUpdateCreateModal.getByLabel(/Line Height/i); const lineHeightField = tokensUpdateCreateModal.getByLabel(/Line Height/i);
const textCaseField = tokensUpdateCreateModal.getByLabel(/Text Case/i); const textCaseField = tokensUpdateCreateModal.getByLabel(/Text Case/i);
const textDecorationField = const textDecorationField =
tokensUpdateCreateModal.getByLabel(/Text Decoration/i); tokensUpdateCreateModal.getByLabel(/Text Decoration/i);
// Capture all values before switching tabs // Capture all values before switching tabs
const originalValues = { const originalValues = {
@@ -1765,14 +1579,14 @@ test.describe("Tokens tab - edition", () => {
// Switch to reference tab and back to composite tab // Switch to reference tab and back to composite tab
const referenceTabButton = const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt"); tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click(); await referenceTabButton.click();
// Empty reference tab should be disabled // Empty reference tab should be disabled
await expect(saveButton).toBeDisabled(); await expect(saveButton).toBeDisabled();
const compositeTabButton = const compositeTabButton =
tokensUpdateCreateModal.getByTestId("composite-opt"); tokensUpdateCreateModal.getByTestId("composite-opt");
await compositeTabButton.click(); await compositeTabButton.click();
// Filled composite tab should be enabled // Filled composite tab should be enabled
@@ -1799,7 +1613,7 @@ test.describe("Tokens tab - edition", () => {
page, page,
}) => { }) => {
const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } = const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page); await setupTokensFile(page);
await expect(tokensSidebar).toBeVisible(); await expect(tokensSidebar).toBeVisible();
@@ -1835,7 +1649,7 @@ test.describe("Tokens tab - edition", () => {
page, page,
}) => { }) => {
const { workspacePage, tokensUpdateCreateModal, tokenThemesSetsSidebar } = const { workspacePage, tokensUpdateCreateModal, tokenThemesSetsSidebar } =
await setupEmptyTokensFile(page); await setupEmptyTokensFile(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" }); const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel await tokensTabPanel
@@ -1890,7 +1704,7 @@ test.describe("Tokens tab - edition", () => {
test.describe("Tokens tab - delete", () => { test.describe("Tokens tab - delete", () => {
test("User delete color token", async ({ page }) => { test("User delete color token", async ({ page }) => {
const { tokensSidebar, tokenContextMenuForToken } = const { tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page); await setupTokensFile(page);
await expect(tokensSidebar).toBeVisible(); await expect(tokensSidebar).toBeVisible();

View File

@@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test"; import { test, expect } from "@playwright/test";
import { WorkspacePage } from "../pages/WorkspacePage"; import { WorkspacePage } from "../pages/WorkspacePage";
import { presenceFixture, joinFixture2, joinFixture3 } from "../../data/workspace/ws-notifications"; import { presenceFixture } from "../../data/workspace/ws-notifications";
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page); await WorkspacePage.init(page);
@@ -40,28 +40,6 @@ test("User receives presence notifications updates in the workspace", async ({
).toHaveCount(2); ).toHaveCount(2);
}); });
test("BUG 13058 - Presence list shows up to 3 user avatars", async ({
page,
}) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();
await workspacePage.goToWorkspace();
await workspacePage.sendPresenceMessage(presenceFixture);
await workspacePage.sendPresenceMessage(joinFixture2);
await expect(
page.getByTestId("active-users-list").getByAltText("Princesa Leia"),
).toHaveCount(3);
await workspacePage.sendPresenceMessage(joinFixture3);
await expect(
page.getByTestId("active-users-list").getByAltText("Princesa Leia"),
).toHaveCount(2);
await expect(page.getByTestId("active-users-list").getByText("+2")).toBeVisible();
});
test("User draws a rect", async ({ page }) => { test("User draws a rect", async ({ page }) => {
const workspacePage = new WorkspacePage(page); const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile(); await workspacePage.setupEmptyFile();

8718
frontend/pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
patchedDependencies:
'@zip.js/zip.js@2.8.11': patches/@zip.js__zip.js@2.8.11.patch
shamefullyHoist: true
packages:
- "packages/draft-js"
- "packages/mousetrap"
- "text-editor"

View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 138 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

View File

@@ -700,6 +700,19 @@
background-color: var(--menu-shortcut-background-color); background-color: var(--menu-shortcut-background-color);
} }
.user-icon {
@include flexCenter;
@include bodySmallTypography;
height: $s-24;
width: $s-24;
border-radius: $br-circle;
margin-left: calc(-1 * $s-4);
img {
border-radius: $br-circle;
border: $s-2 solid var(--user-count-foreground-color);
}
}
.mixed-bar { .mixed-bar {
@include bodySmallTypography; @include bodySmallTypography;
display: flex; display: flex;

View File

@@ -17,18 +17,17 @@
<meta name="twitter:site" content="@penpotapp"> <meta name="twitter:site" content="@penpotapp">
<meta name="twitter:creator" content="@penpotapp"> <meta name="twitter:creator" content="@penpotapp">
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)"> <meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)">
<link id="theme" href="css/main.css?version={{& version_tag}}" rel="stylesheet" type="text/css" /> <link id="theme" href="css/main.css?version={{& version}}" rel="stylesheet" type="text/css" />
{{#isDebug}} {{#isDebug}}
<link href="css/debug.css?version={{& version_tag}}" rel="stylesheet" type="text/css" /> <link href="css/debug.css?version={{& version}}" rel="stylesheet" type="text/css" />
{{/isDebug}} {{/isDebug}}
<link rel="icon" href="images/favicon.png?version={{& version_tag }}" /> <link rel="icon" href="images/favicon.png" />
<script type="importmap">{{& manifest.importmap }}</script> <script type="importmap">{{& manifest.importmap }}</script>
<script type="module"> <script type="module">
globalThis.penpotVersion = "{{& version}}"; globalThis.penpotVersion = "{{& version}}";
globalThis.penpotVersionTag = "{{& version_tag}}";
globalThis.penpotBuildDate = "{{& build_date}}"; globalThis.penpotBuildDate = "{{& build_date}}";
globalThis.penpotWorkerURI = "{{& manifest.worker_main}}"; globalThis.penpotWorkerURI = "{{& manifest.worker_main}}";
</script> </script>

View File

@@ -3,11 +3,10 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Penpot - Rasterizer</title> <title>Penpot - Rasterizer</title>
<link rel="icon" href="images/favicon.png?version={{& version_tag }}" /> <link rel="icon" href="images/favicon.png" />
<script> <script>
globalThis.penpotVersion = "{{& version}}"; globalThis.penpotVersion = "{{& version}}";
globalThis.penpotVersionTag = "{{& version_tag}}";
globalThis.penpotBuildDate = "{{& build_date}}"; globalThis.penpotBuildDate = "{{& build_date}}";
globalThis.penpotWorkerURI = "{{& manifest.worker_main}}"; globalThis.penpotWorkerURI = "{{& manifest.worker_main}}";
</script> </script>

View File

@@ -4,12 +4,10 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge" /> <meta http-equiv="x-ua-compatible" content="ie=edge" />
<title>Penpot - Render</title> <title>Penpot - Render</title>
<link rel="icon" href="images/favicon.png" />
<link rel="icon" href="images/favicon.png?version={{& version_tag }}" />
<script> <script>
globalThis.penpotVersion = "{{& version}}"; globalThis.penpotVersion = "{{& version}}";
globalThis.penpotVersionTag = "{{& version_tag}}";
globalThis.penpotBuildDate = "{{& build_date}}"; globalThis.penpotBuildDate = "{{& build_date}}";
</script> </script>

View File

@@ -27,11 +27,9 @@ export function startWorker() {
}); });
} }
export const IS_DEBUG = process.env.NODE_ENV !== "production"; export const isDebug = process.env.NODE_ENV !== "production";
export const BUILD_DATE = process.env.BUILD_DATE || new Date().toString(); export const CURRENT_VERSION = process.env.CURRENT_VERSION || "develop";
export const BUILD_TS = process.env.BUILD_TS || Date.now(); export const BUILD_DATE = process.env.BUILD_DATE || "" + new Date();
export const VERSION = process.env.VERSION || "develop";
export const VERSION_TAG = process.env.VERSION_TAG || VERSION;
async function findFiles(basePath, predicate, options = {}) { async function findFiles(basePath, predicate, options = {}) {
predicate = predicate =
@@ -51,8 +49,7 @@ async function findFiles(basePath, predicate, options = {}) {
function syncDirs(originPath, destPath) { function syncDirs(originPath, destPath) {
const command = `rsync -ar --delete ${originPath} ${destPath}`; const command = `rsync -ar --delete ${originPath} ${destPath}`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {proc.exec(command, (cause, stdout) => {
proc.exec(command, (cause, stdout) => {
if (cause) { if (cause) {
reject(cause); reject(cause);
} else { } else {
@@ -175,7 +172,6 @@ export async function watch(baseDir, predicate, callback) {
const watcher = new Watcher(baseDir, { const watcher = new Watcher(baseDir, {
persistent: true, persistent: true,
recursive: true, recursive: true,
debounce: 500,
}); });
watcher.on("change", (path) => { watcher.on("change", (path) => {
@@ -183,15 +179,6 @@ export async function watch(baseDir, predicate, callback) {
callback(path); callback(path);
} }
}); });
watcher.on("error", (cause) => {
console.log("WATCHER ERROR", cause);
});
}
export async function ensureDirectories() {
await fs.mkdir("./resources/public/js/worker/", { recursive: true });
await fs.mkdir("./resources/public/css/", { recursive: true });
} }
async function readManifestFile(resource) { async function readManifestFile(resource) {
@@ -206,30 +193,27 @@ async function generateManifest() {
render_main: "./js/render.js", render_main: "./js/render.js",
rasterizer_main: "./js/rasterizer.js", rasterizer_main: "./js/rasterizer.js",
config: "./js/config.js?version=" + VERSION_TAG, config: "./js/config.js?version=" + CURRENT_VERSION,
polyfills: "./js/polyfills.js?version=" + VERSION_TAG, polyfills: "./js/polyfills.js?version=" + CURRENT_VERSION,
libs: "./js/libs.js?version=" + VERSION_TAG, libs: "./js/libs.js?version=" + CURRENT_VERSION,
worker_main: "./js/worker/main.js?version=" + VERSION_TAG, worker_main: "./js/worker/main.js?version=" + CURRENT_VERSION,
default_translations: "./js/translation.en.js?version=" + VERSION_TAG, default_translations: "./js/translation.en.js?version=" + CURRENT_VERSION,
importmap: JSON.stringify({ importmap: JSON.stringify({
imports: { "imports": {
"./js/shared.js": "./js/shared.js?version=" + VERSION_TAG, "./js/shared.js": "./js/shared.js?version=" + CURRENT_VERSION,
"./js/main.js": "./js/main.js?version=" + VERSION_TAG, "./js/main.js": "./js/main.js?version=" + CURRENT_VERSION,
"./js/render.js": "./js/render.js?version=" + VERSION_TAG, "./js/render.js": "./js/render.js?version=" + CURRENT_VERSION,
"./js/render-wasm.js": "./js/render-wasm.js?version=" + VERSION_TAG, "./js/render-wasm.js": "./js/render-wasm.js?version=" + CURRENT_VERSION,
"./js/rasterizer.js": "./js/rasterizer.js?version=" + VERSION_TAG, "./js/rasterizer.js": "./js/rasterizer.js?version=" + CURRENT_VERSION,
"./js/main-dashboard.js": "./js/main-dashboard.js": "./js/main-dashboard.js?version=" + CURRENT_VERSION,
"./js/main-dashboard.js?version=" + VERSION_TAG, "./js/main-auth.js": "./js/main-auth.js?version=" + CURRENT_VERSION,
"./js/main-auth.js": "./js/main-auth.js?version=" + VERSION_TAG, "./js/main-viewer.js": "./js/main-viewer.js?version=" + CURRENT_VERSION,
"./js/main-viewer.js": "./js/main-viewer.js?version=" + VERSION_TAG, "./js/main-settings.js": "./js/main-settings.js?version=" + CURRENT_VERSION,
"./js/main-settings.js": "./js/main-settings.js?version=" + VERSION_TAG, "./js/main-workspace.js": "./js/main-workspace.js?version=" + CURRENT_VERSION,
"./js/main-workspace.js": "./js/util-highlight.js": "./js/util-highlight.js?version=" + CURRENT_VERSION
"./js/main-workspace.js?version=" + VERSION_TAG, }
"./js/util-highlight.js": })
"./js/util-highlight.js?version=" + VERSION_TAG,
},
}),
}; };
return index; return index;
@@ -238,12 +222,11 @@ async function generateManifest() {
async function renderTemplate(path, context = {}, partials = {}) { async function renderTemplate(path, context = {}, partials = {}) {
const content = await fs.readFile(path, { encoding: "utf-8" }); const content = await fs.readFile(path, { encoding: "utf-8" });
const ts = Math.floor(new Date());
context = Object.assign({}, context, { context = Object.assign({}, context, {
isDebug: IS_DEBUG, ts: ts,
version: VERSION, isDebug,
version_tag: VERSION_TAG,
build_date: BUILD_DATE,
build_ts: BUILD_TS,
}); });
return mustache.render(content, context, partials); return mustache.render(content, context, partials);
@@ -274,9 +257,6 @@ const markedOptions = {
marked.use(markedOptions); marked.use(markedOptions);
export async function compileTranslations() { export async function compileTranslations() {
const outputDir = "resources/public/js/";
await fs.mkdir(outputDir, { recursive: true });
const langs = [ const langs = [
"ar", "ar",
"ca", "ca",
@@ -358,6 +338,7 @@ export async function compileTranslations() {
} }
const esm = `export default ${JSON.stringify(result, null, 0)};\n`; const esm = `export default ${JSON.stringify(result, null, 0)};\n`;
const outputDir = "resources/public/js/";
const outputFile = ph.join(outputDir, "translation." + lang + ".js"); const outputFile = ph.join(outputDir, "translation." + lang + ".js");
await fs.writeFile(outputFile, esm); await fs.writeFile(outputFile, esm);
} }
@@ -409,6 +390,7 @@ async function generateSvgSprites() {
} }
async function generateTemplates() { async function generateTemplates() {
const isDebug = process.env.NODE_ENV !== "production";
await fs.mkdir("./resources/public/", { recursive: true }); await fs.mkdir("./resources/public/", { recursive: true });
const manifest = await generateManifest(); const manifest = await generateManifest();
@@ -434,6 +416,9 @@ async function generateTemplates() {
const context = { const context = {
manifest: manifest, manifest: manifest,
version: CURRENT_VERSION,
build_date: BUILD_DATE,
isDebug,
}; };
content = await renderTemplate( content = await renderTemplate(
@@ -465,17 +450,11 @@ async function generateTemplates() {
); );
await fs.writeFile("./.storybook/preview-head.html", content); await fs.writeFile("./.storybook/preview-head.html", content);
content = await renderTemplate( content = await renderTemplate("resources/templates/render.mustache", context);
"resources/templates/render.mustache",
context,
);
await fs.writeFile("./resources/public/render.html", content); await fs.writeFile("./resources/public/render.html", content);
content = await renderTemplate( content = await renderTemplate("resources/templates/rasterizer.mustache", context);
"resources/templates/rasterizer.mustache",
context,
);
await fs.writeFile("./resources/public/rasterizer.html", content); await fs.writeFile("./resources/public/rasterizer.html", content);
} }
@@ -508,7 +487,7 @@ export async function compileStyles() {
await fs.mkdir("./resources/public/css", { recursive: true }); await fs.mkdir("./resources/public/css", { recursive: true });
await fs.writeFile("./resources/public/css/main.css", result); await fs.writeFile("./resources/public/css/main.css", result);
if (IS_DEBUG) { if (isDebug) {
let debugCSS = await compileSassDebug(worker); let debugCSS = await compileSassDebug(worker);
await fs.writeFile("./resources/public/css/debug.css", debugCSS); await fs.writeFile("./resources/public/css/debug.css", debugCSS);
} }
@@ -521,43 +500,17 @@ export async function compileStyles() {
export async function compileSvgSprites() { export async function compileSvgSprites() {
const start = process.hrtime(); const start = process.hrtime();
log.info("init: compile svgsprite"); log.info("init: compile svgsprite");
let error = false; await generateSvgSprites();
try {
await generateSvgSprites();
} catch (cause) {
error = cause;
}
const end = process.hrtime(start); const end = process.hrtime(start);
log.info("done: compile svgsprite", `(${ppt(end)})`);
if (error) {
log.error("error: compile svgsprite", `(${ppt(end)})`);
console.error(error);
} else {
log.info("done: compile svgsprite", `(${ppt(end)})`);
}
} }
export async function compileTemplates() { export async function compileTemplates() {
const start = process.hrtime(); const start = process.hrtime();
let error = false;
log.info("init: compile templates"); log.info("init: compile templates");
await generateTemplates();
try {
await generateTemplates();
} catch (cause) {
error = cause;
}
const end = process.hrtime(start); const end = process.hrtime(start);
log.info("done: compile templates", `(${ppt(end)})`);
if (error) {
log.error("error: compile templates", `(${ppt(end)})`);
console.error(error);
} else {
log.info("done: compile templates", `(${ppt(end)})`);
}
} }
export async function compilePolyfills() { export async function compilePolyfills() {

View File

@@ -28,12 +28,14 @@ async function compileFile(path) {
], ],
sourceMap: false, sourceMap: false,
}); });
// console.dir(result);
resolve({ resolve({
inputPath: path, inputPath: path,
outputPath: dest, outputPath: dest,
css: result.css, css: result.css,
}); });
} catch (cause) { } catch (cause) {
console.error(cause);
reject(cause); reject(cause);
} }
}); });

View File

@@ -2,25 +2,26 @@
# NOTE: this script should be called from the parent directory to # NOTE: this script should be called from the parent directory to
# properly work. # properly work.
set -ex
export INCLUDE_STORYBOOK=${BUILD_STORYBOOK:-no}; export INCLUDE_STORYBOOK=${BUILD_STORYBOOK:-no};
export INCLUDE_WASM=${BUILD_WASM:-yes}; export INCLUDE_WASM=${BUILD_WASM:-yes};
export EXTRA_PARAMS=$SHADOWCLJS_EXTRA_PARAMS; export CURRENT_VERSION=$1;
export BUILD_DATE=$(date -R); export BUILD_DATE=$(date -R);
export BUILD_TS=$(date +%s); export CURRENT_HASH=${CURRENT_HASH:-$(git rev-parse --short HEAD)};
export EXTRA_PARAMS=$SHADOWCLJS_EXTRA_PARAMS;
export VERSION=${1:-develop}; export TS=$(date +%s);
export VERSION_TAG="${VERSION}-${BUILD_TS}";
# Some cljs reacts on this environment variable for define more # Some cljs reacts on this environment variable for define more
# performant code on macros (example: rumext) # performant code on macros (example: rumext)
export NODE_ENV=production; export NODE_ENV=production;
echo "Current path:"
echo $PATH
set -ex
corepack enable; corepack enable;
corepack install; corepack install;
pnpm install; yarn install || exit 1;
rm -rf target/dist; rm -rf target/dist;
rm -rf resources/public; rm -rf resources/public;
@@ -32,16 +33,16 @@ pushd ../render-wasm;
./build ./build
popd popd
pnpm run build:app:main $EXTRA_PARAMS; yarn run build:app:main $EXTRA_PARAMS;
pnpm run build:app:libs; yarn run build:app:libs;
pnpm run build:app:assets; yarn run build:app:assets;
sed -i "s/\.\/render.js/.\/render.js?version=$VERSION_TAG/g" resources/public/js/worker/main*.js sed -i "s/\.\/render.js/.\/render.js?version=$CURRENT_VERSION/g" resources/public/js/worker/main*.js
rsync -avr resources/public/ target/dist/ rsync -avr resources/public/ target/dist/
if [ "$INCLUDE_STORYBOOK" = "yes" ]; then if [ "$INCLUDE_STORYBOOK" = "yes" ]; then
# build storybook # build storybook
pnpm run build:storybook || exit 1; yarn run build:storybook || exit 1;
rsync -avr storybook-static/ target/dist/storybook-static; rsync -avr storybook-static/ target/dist/storybook-static;
fi fi

View File

@@ -1,6 +1,5 @@
import * as h from "./_helpers.js"; import * as h from "./_helpers.js";
await h.ensureDirectories();
await h.compileStyles(); await h.compileStyles();
await h.copyAssets(); await h.copyAssets();
await h.copyWasmPlayground(); await h.copyWasmPlayground();

View File

@@ -2,18 +2,20 @@
# NOTE: this script should be called from the parent directory to # NOTE: this script should be called from the parent directory to
# properly work. # properly work.
set -ex export CURRENT_VERSION=$1;
export BUILD_TS=$(date +%s);
export BUILD_DATE=$(date -R); export BUILD_DATE=$(date -R);
export CURRENT_HASH=${CURRENT_HASH:-$(git rev-parse --short HEAD)};
export VERSION=${1:-develop}; export TS=$(date +%s);
export VERSION_TAG="${VERSION}-${BUILD_TS}";
export NODE_ENV=production; export NODE_ENV=production;
echo "Current path:"
echo $PATH
set -ex
corepack enable; corepack enable;
corepack install || exit 1; corepack install || exit 1;
pnpm install || exit 1; yarn install || exit 1;
pnpm run build:storybook || exit 1; yarn run build:storybook || exit 1;

View File

@@ -1,7 +1,7 @@
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import * as h from "./_helpers.js"; import * as h from "./_helpers.js";
await fs.mkdir("resources/public/js", { recursive: true }); await fs.mkdir("resources/public/js", {recursive: true});
await h.compileStorybookStyles(); await h.compileStorybookStyles();
await h.copyAssets(); await h.copyAssets();

View File

@@ -0,0 +1,20 @@
import express from "express";
import compression from "compression";
import { fileURLToPath } from "url";
import path from "path";
const app = express();
const port = 3000;
app.use(compression());
const staticPath = path.join(
fileURLToPath(import.meta.url),
"../../resources/public",
);
app.use(express.static(staticPath));
app.listen(port, () => {
console.log(`Listening at 0.0.0.0:${port}`);
});

View File

@@ -2,5 +2,5 @@
corepack enable; corepack enable;
corepack install; corepack install;
pnpm install; yarn install;
pnpx playwright install chromium; yarn playwright install chromium;

View File

@@ -3,7 +3,7 @@
set -ex set -ex
corepack enable; corepack enable;
corepack install; corepack install;
pnpm install; yarn install;
pnpm run lint:scss; yarn run lint:scss;
pnpm run test; yarn run test;

View File

@@ -5,5 +5,6 @@ SCRIPT_DIR=$(dirname $0);
set -ex set -ex
$SCRIPT_DIR/setup; $SCRIPT_DIR/setup;
pnpm run build:storybook
pnpm run test:storybook yarn run build:storybook
yarn run test:storybook

View File

@@ -5,4 +5,5 @@ SCRIPT_DIR=$(dirname $0);
set -ex set -ex
$SCRIPT_DIR/setup; $SCRIPT_DIR/setup;
pnpm run test:e2e -x --workers=2 --reporter=list "$@";
yarn run test:e2e -x --workers=2 --reporter=list "$@";

View File

@@ -4,4 +4,4 @@ TARGET=${1:-app};
set -ex set -ex
exec pnpm run watch:$TARGET exec yarn run watch:$TARGET

View File

@@ -12,31 +12,19 @@ let sass = null;
async function compileSassAll() { async function compileSassAll() {
const start = process.hrtime(); const start = process.hrtime();
let error = false;
log.info("init: compile styles"); log.info("init: compile styles");
try { sass = await h.compileSassAll(worker);
sass = await h.compileSassAll(worker); let output = await h.concatSass(sass);
let output = await h.concatSass(sass); await fs.writeFile("./resources/public/css/main.css", output);
await fs.writeFile("./resources/public/css/main.css", output);
if (isDebug) { if (isDebug) {
let debugCSS = await h.compileSassDebug(worker); let debugCSS = await h.compileSassDebug(worker);
await fs.writeFile("./resources/public/css/debug.css", debugCSS); await fs.writeFile("./resources/public/css/debug.css", debugCSS);
}
} catch (cause) {
error = cause;
} }
const end = process.hrtime(start); const end = process.hrtime(start);
log.info("done: compile styles", `(${ppt(end)})`);
if (error) {
log.error("error: compile styles", `(${ppt(end)})`);
console.error(error);
} else {
log.info("done: compile styles", `(${ppt(end)})`);
}
} }
async function compileSass(path) { async function compileSass(path) {
@@ -60,7 +48,7 @@ async function compileSass(path) {
} }
} }
await h.ensureDirectories(); await fs.mkdir("./resources/public/css/", { recursive: true });
await compileSassAll(); await compileSassAll();
await h.copyAssets(); await h.copyAssets();
await h.copyWasmPlayground(); await h.copyWasmPlayground();

View File

@@ -95,7 +95,6 @@
(def browser (parse-browser)) (def browser (parse-browser))
(def platform (parse-platform)) (def platform (parse-platform))
(def version-tag (obj/get global "penpotVersionTag"))
(def terms-of-service-uri (obj/get global "penpotTermsOfServiceURI")) (def terms-of-service-uri (obj/get global "penpotTermsOfServiceURI"))
(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI")) (def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI"))
(def flex-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/")) (def flex-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
@@ -191,8 +190,9 @@
(defn resolve-href (defn resolve-href
[resource] [resource]
(let [href (-> public-uri (let [version (get version :full)
(u/ensure-path-slash) href (-> public-uri
(u/join resource) (u/ensure-path-slash)
(get :path))] (u/join resource)
(str href "?version=" version-tag))) (get :path))]
(str href "?version=" version)))

View File

@@ -126,7 +126,7 @@
If the `value` is not parseable and/or has missing references returns a map with `:errors`. If the `value` is not parseable and/or has missing references returns a map with `:errors`.
If the `value` is parseable but is out of range returns a map with `warnings`." If the `value` is parseable but is out of range returns a map with `warnings`."
[value] [value]
(let [missing-references? (seq (cto/find-token-value-references value)) (let [missing-references? (seq (seq (cto/find-token-value-references value)))
parsed-value (cft/parse-token-value value) parsed-value (cft/parse-token-value value)
out-of-scope (not (<= 0 (:value parsed-value) 1)) out-of-scope (not (<= 0 (:value parsed-value) 1))
references (seq (cto/find-token-value-references value))] references (seq (cto/find-token-value-references value))]
@@ -152,14 +152,15 @@
[value] [value]
(let [missing-references? (seq (cto/find-token-value-references value)) (let [missing-references? (seq (cto/find-token-value-references value))
parsed-value (cft/parse-token-value value) parsed-value (cft/parse-token-value value)
out-of-scope (< (:value parsed-value) 0)] out-of-scope (< (:value parsed-value) 0)
references (seq (cto/find-token-value-references value))]
(cond (cond
(and parsed-value (not out-of-scope)) (and parsed-value (not out-of-scope))
parsed-value parsed-value
missing-references? references
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference missing-references?)] {:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)]
:references missing-references?} :references references}
(and (not missing-references?) out-of-scope) (and (not missing-references?) out-of-scope)
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-stroke-width value)]} {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-stroke-width value)]}
@@ -364,7 +365,7 @@
"Parses shadow spread value (non-negative number)." "Parses shadow spread value (non-negative number)."
[value] [value]
(let [parsed (parse-sd-token-general-value value) (let [parsed (parse-sd-token-general-value value)
valid? (:value parsed)] valid? (and (:value parsed) (>= (:value parsed) 0))]
(cond (cond
valid? valid?
parsed parsed

View File

@@ -27,10 +27,8 @@
[app.main.data.workspace.colors :as wdc] [app.main.data.workspace.colors :as wdc]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.texts :as dwt]
[app.main.data.workspace.transforms :as dwtr] [app.main.data.workspace.transforms :as dwtr]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.features :as features]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.store :as st] [app.main.store :as st]
[app.util.i18n :refer [tr]] [app.util.i18n :refer [tr]]
@@ -302,20 +300,11 @@
update-fn (fn [node _] update-fn (fn [node _]
(-> node (-> node
(d/txt-merge txt-attrs) (d/txt-merge txt-attrs)
(cty/remove-typography-from-node))) (cty/remove-typography-from-node)))]
;; Check if any attribute affects text layout (requires resize) (dwsh/update-shapes shape-ids
affects-layout? (some #(contains? txt-attrs %) [:font-size :font-family :font-weight :letter-spacing :line-height])] #(txt/update-text-content % update-node? update-fn nil)
(ptk/reify ::generate-text-shape-update {:ignore-touched true
ptk/WatchEvent :page-id page-id})))
(watch [_ state _]
(cond-> (rx/of (dwsh/update-shapes shape-ids
#(txt/update-text-content % update-node? update-fn nil)
{:ignore-touched true
:page-id page-id}))
(and affects-layout?
(features/active-feature? state "render-wasm/v1"))
(rx/merge
(rx/of (dwt/resize-wasm-text-all shape-ids))))))))
(defn update-line-height (defn update-line-height
([value shape-ids attributes] (update-line-height value shape-ids attributes nil)) ([value shape-ids attributes] (update-line-height value shape-ids attributes nil))
@@ -364,17 +353,11 @@
(-> node (-> node
(d/txt-merge txt-attrs) (d/txt-merge txt-attrs)
(cty/remove-typography-from-node))))] (cty/remove-typography-from-node))))]
(ptk/reify ::generate-font-family-text-shape-update (dwsh/update-shapes shape-ids
ptk/WatchEvent (fn [shape]
(watch [_ state _] (txt/update-text-content shape update-node? #(update-fn %1 (ctst/font-weight-applied? shape)) nil))
(cond-> (rx/of (dwsh/update-shapes shape-ids {:ignore-touched true
(fn [shape] :page-id page-id})))
(txt/update-text-content shape update-node? #(update-fn %1 (ctst/font-weight-applied? shape)) nil))
{:ignore-touched true
:page-id page-id}))
(features/active-feature? state "render-wasm/v1")
(rx/merge
(rx/of (dwt/resize-wasm-text-all shape-ids))))))))
(defn- create-font-family-text-attrs (defn- create-font-family-text-attrs
[value] [value]
@@ -442,16 +425,10 @@
(-> node (-> node
(d/txt-merge txt-attrs) (d/txt-merge txt-attrs)
(cty/remove-typography-from-node))))] (cty/remove-typography-from-node))))]
(ptk/reify ::generate-font-weight-text-shape-update (dwsh/update-shapes shape-ids
ptk/WatchEvent #(txt/update-text-content % update-node? update-fn nil)
(watch [_ state _] {:ignore-touched true
(cond-> (rx/of (dwsh/update-shapes shape-ids :page-id page-id})))
#(txt/update-text-content % update-node? update-fn nil)
{:ignore-touched true
:page-id page-id}))
(features/active-feature? state "render-wasm/v1")
(rx/merge
(rx/of (dwt/resize-wasm-text-all shape-ids))))))))
(defn update-font-weight (defn update-font-weight
([value shape-ids attributes] (update-font-weight value shape-ids attributes nil)) ([value shape-ids attributes] (update-font-weight value shape-ids attributes nil))

View File

@@ -43,7 +43,7 @@
[:h1 {:class (stl/css :logo-container)} [:h1 {:class (stl/css :logo-container)}
[:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} deprecated-icon/logo]] [:a {:href "#/" :title "Penpot" :class (stl/css :logo-btn)} deprecated-icon/logo]]
[:div {:class (stl/css :login-illustration)} [:div {:class (stl/css :login-illustration)}
[:img {:src "images/registration-illustration.png"}]] deprecated-icon/login-illustration]
[:section {:class (stl/css :auth-content)} [:section {:class (stl/css :auth-content)}

View File

@@ -50,9 +50,14 @@
justify-content: center; justify-content: center;
grid-column: 1 / 4; grid-column: 1 / 4;
width: 40vw; width: 40vw;
height: auto;
justify-self: center; justify-self: center;
svg {
width: 100%;
fill: var(--color-foreground-primary);
height: auto;
}
@media (max-width: 992px) { @media (max-width: 992px) {
display: none; display: none;
} }

View File

@@ -193,15 +193,16 @@
restore-fn restore-fn
(fn [_] (fn [_]
(st/emit! (dd/restore-files-immediately (st/emit! (dd/restore-files-immediately
(with-meta {:team-id current-team-id (with-meta {:team-id (:id current-team)
:ids (into #{} d/xf:map-id files)} :ids (into #{} d/xf:map-id files)}
{:on-success #(st/emit! (ntf/success (tr "dashboard.restore-success-notification" (:name file))) {:on-success #(st/emit! (ntf/success (tr "dashboard.restore-success-notification" (:name file)))
(dd/fetch-projects current-team-id) (dd/fetch-projects (:id current-team))
(dd/fetch-deleted-files current-team-id)) (dd/fetch-deleted-files (:id current-team)))
:on-error #(st/emit! (ntf/error (tr "dashboard.errors.error-on-restore-file" (:name file))))})))) :on-error #(st/emit! (ntf/error (tr "dashboard.errors.error-on-restore-file" (:name file))))}))))
on-restore-immediately on-restore-immediately
(fn [] (fn []
(prn files)
(st/emit! (st/emit!
(modal/show {:type :confirm (modal/show {:type :confirm
:title (tr "dashboard-restore-file-confirmation.title") :title (tr "dashboard-restore-file-confirmation.title")
@@ -213,7 +214,7 @@
on-delete-immediately on-delete-immediately
(fn [] (fn []
(let [accept-fn #(st/emit! (dd/delete-files-immediately (let [accept-fn #(st/emit! (dd/delete-files-immediately
{:team-id current-team-id {:team-id (:id current-team)
:ids (into #{} d/xf:map-id files)}))] :ids (into #{} d/xf:map-id files)}))]
(st/emit! (st/emit!
(modal/show {:type :confirm (modal/show {:type :confirm
@@ -243,7 +244,8 @@
(for [project current-projects] (for [project current-projects]
{:name (get-project-name project) {:name (get-project-name project)
:id (get-project-id project) :id (get-project-id project)
:handler (on-move current-team-id (:id project))}) :handler (on-move (:id current-team)
(:id project))})
(when (seq other-teams) (when (seq other-teams)
[{:name (tr "dashboard.move-to-other-team") [{:name (tr "dashboard.move-to-other-team")
:id "move-to-other-team" :id "move-to-other-team"

View File

@@ -10,25 +10,15 @@ import Components from "@target/components";
const { RadioButtons } = Components; const { RadioButtons } = Components;
const options = [ const options = [
{ id: "left", label: "Left", value: "left" }, {id: "left", label: "Left", value: "left" },
{ id: "center", label: "Center", value: "center" }, {id: "center", label: "Center", value: "center" },
{ id: "right", label: "Right", value: "right" }, {id: "right", label: "Right", value: "right" },
]; ];
const optionsIcon = [ const optionsIcon = [
{ id: "left", label: "Left align", value: "left", icon: "text-align-left" }, {id: "left", label: "Left align", value: "left", icon: "text-align-left" },
{ {id: "center", label: "Center align", value: "center", icon: "text-align-center" },
id: "center", {id: "right", label: "Right align", value: "right", icon: "text-align-right" },
label: "Center align",
value: "center",
icon: "text-align-center",
},
{
id: "right",
label: "Right align",
value: "right",
icon: "text-align-right",
},
]; ];
export default { export default {

View File

@@ -18,6 +18,7 @@
(def ^:svg-id brand-google "brand-google") (def ^:svg-id brand-google "brand-google")
(def ^:svg-id loader "loader") (def ^:svg-id loader "loader")
(def ^:svg-id logo-error-screen "logo-error-screen") (def ^:svg-id logo-error-screen "logo-error-screen")
(def ^:svg-id login-illustration "login-illustration")
(def ^:svg-id logo-subscription "logo-subscription") (def ^:svg-id logo-subscription "logo-subscription")
(def ^:svg-id logo-subscription-light "logo-subscription-light") (def ^:svg-id logo-subscription-light "logo-subscription-light")
(def ^:svg-id marketing-arrows "marketing-arrows") (def ^:svg-id marketing-arrows "marketing-arrows")

View File

@@ -23,7 +23,6 @@
touched? (and (contains? (:data @form) input-name) touched? (and (contains? (:data @form) input-name)
(get-in @form [:touched input-name])) (get-in @form [:touched input-name]))
error (get-in @form [:errors input-name]) error (get-in @form [:errors input-name])
value (get-in @form [:data input-name] "") value (get-in @form [:data input-name] "")
@@ -53,8 +52,7 @@
(let [form (mf/use-ctx context) (let [form (mf/use-ctx context)
disabled? (or (and (some? form) disabled? (or (and (some? form)
(or (not (:valid @form)) (or (not (:valid @form))
(seq (:async-errors @form)) (seq (:external-errors @form))))
(seq (:extra-errors @form))))
(true? disabled)) (true? disabled))
handle-key-down-save handle-key-down-save
(mf/use-fn (mf/use-fn

View File

@@ -17,6 +17,7 @@
(def ^:icon logo (icon-xref :penpot-logo)) (def ^:icon logo (icon-xref :penpot-logo))
(def ^:icon logo-icon (icon-xref :penpot-logo-icon)) (def ^:icon logo-icon (icon-xref :penpot-logo-icon))
(def ^:icon logo-error-screen (icon-xref :logo-error-screen)) (def ^:icon logo-error-screen (icon-xref :logo-error-screen))
(def ^:icon login-illustration (icon-xref :login-illustration))
(def ^:icon logo-subscription (icon-xref :logo-subscription)) (def ^:icon logo-subscription (icon-xref :logo-subscription))
(def ^:icon logo-subscription-light (icon-xref :logo-subscription-light)) (def ^:icon logo-subscription-light (icon-xref :logo-subscription-light))

View File

@@ -22,7 +22,7 @@
(let [profile (assoc profile :color color) (let [profile (assoc profile :color color)
full-name (:fullname profile)] full-name (:fullname profile)]
[:li {:class (stl/css :session-icon) [:li {:class (stl/css :session-icon)
:style {:z-index (dm/str (+ 2 (* -1 index))) :style {:z-index (dm/str (+ 1 (* -1 index)))
:background-color color} :background-color color}
:title full-name} :title full-name}
[:img {:alt full-name [:img {:alt full-name
@@ -37,11 +37,9 @@
sessions (vals presence) sessions (vals presence)
num-sessions (count sessions) num-sessions (count sessions)
max-avatar-count 3
avatar-count (if (= num-sessions max-avatar-count) max-avatar-count (- max-avatar-count 1))
open* (mf/use-state false) open* (mf/use-state false)
open? (and ^boolean (deref open*) (> num-sessions max-avatar-count)) open? (and ^boolean (deref open*) (> num-sessions 2))
on-open on-open
(mf/use-fn (mf/use-fn
(fn [] (fn []
@@ -69,10 +67,10 @@
[:button {:class (stl/css-case :active-users true) [:button {:class (stl/css-case :active-users true)
:on-click on-open} :on-click on-open}
[:ul {:class (stl/css :active-users-list) :data-testid "active-users-list"} [:ul {:class (stl/css :active-users-list) :data-testid "active-users-list"}
(when (> num-sessions max-avatar-count) (when (> num-sessions 2)
[:li {:class (stl/css :users-num)} (dm/str "+" (+ 1 (- num-sessions max-avatar-count)))]) [:span {:class (stl/css :users-num)} (dm/str "+" (- num-sessions 2))])
(for [[index session] (d/enumerate (take avatar-count sessions))] (for [[index session] (d/enumerate (take 2 sessions))]
[:& session-widget [:& session-widget
{:color (:color session) {:color (:color session)
:index index :index index

View File

@@ -4,71 +4,50 @@
// //
// Copyright (c) KALEIDOS INC // Copyright (c) KALEIDOS INC
@use "ds/typography.scss" as t; @use "refactor/common-refactor.scss" as deprecated;
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
.active-users, .active-users,
.active-users-opened { .active-users-opened {
background: none; @include deprecated.buttonStyle;
cursor: pointer;
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
margin: 0; margin: 0;
padding: 0 var(--sp-xs); padding: 0 deprecated.$s-4;
border: none; border-radius: deprecated.$br-8;
border-radius: $br-8; .active-users-list {
} display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
margin: 0;
.active-users-list { .users-num {
display: flex; @extend .user-icon;
flex-direction: row-reverse; background-color: var(--user-count-background-color);
justify-content: flex-end; color: var(--user-count-foreground-color);
margin: 0; z-index: deprecated.$z-index-2;
gap: var(--user-list-gap, 0); border: deprecated.$s-2 solid var(--user-count-foreground-color);
} }
.session-icon {
%user-icon { @extend .user-icon;
@include t.use-typography("body-small"); }
display: grid;
place-content: center;
height: $sz-24;
width: $sz-24;
border-radius: $br-circle;
margin-inline-start: calc(-1 * var(--sp-xs));
img {
border-radius: $br-circle;
border: $b-2 solid var(--user-count-foreground-color);
} }
} }
.users-num {
@extend %user-icon;
background-color: var(--user-count-background-color);
color: var(--user-count-foreground-color);
z-index: 3; // FIXME: this is hardcoded because of the way its component uses z-index from cljs
border: $b-2 solid var(--user-count-foreground-color);
margin-inline-start: var(--user-list-inline-margin, calc(-1 * var(--sp-xs)));
}
.session-icon {
@extend %user-icon;
margin-inline-start: var(--user-list-inline-margin, calc(-1 * var(--sp-xs)));
}
.active-users-opened { .active-users-opened {
position: absolute; position: absolute;
right: calc(-1 * var(--sp-xxs)); right: calc(-1 * deprecated.$s-2);
top: calc(-1 * var(--sp-xxs)); top: calc(-1 * deprecated.$s-2);
padding: var(--sp-s); padding: deprecated.$s-8;
margin: calc(-1 * var(--sp-xxs)) calc(-1 * var(--sp-xs)) 0 0; margin: calc(-1 * deprecated.$s-2) calc(-1 * deprecated.$s-4) 0 0;
background-color: var(--menu-background-color); background-color: var(--menu-background-color);
z-index: 4; // FIXME: this is hardcoded because of the way its component uses z-index from cljs z-index: deprecated.$z-index-4;
.active-users-list {
--user-list-gap: var(--sp-xs); gap: deprecated.$s-4;
--user-list-inline-margin: 0; .users-num,
.session-icon {
margin-left: 0;
}
}
} }

View File

@@ -90,8 +90,7 @@
instance instance
(dwt/create-editor editor-node canvas-node options) (dwt/create-editor editor-node canvas-node options)
;; Store original content to compare name later update-name? (nil? content)
original-content content
on-key-up on-key-up
(fn [event] (fn [event]
@@ -102,22 +101,10 @@
on-blur on-blur
(fn [] (fn []
(when-let [content (content/dom->cljs (dwt/get-editor-root instance))] (when-let [content (content/dom->cljs (dwt/get-editor-root instance))]
(let [state @st/state (st/emit! (dwt/v2-update-text-shape-content shape-id content
objects (dsh/lookup-page-objects state) :update-name? update-name?
shape (get objects shape-id) :name (gen-name instance)
current-name (:name shape) :finalize? true)))
generated-name (gen-name instance)
;; Update name if: (1) it's a new shape (nil original content), or
;; (2) the current name matches the generated name from original content
;; (meaning it was never manually renamed)
update-name? (or (nil? original-content)
(and (some? current-name)
(some? original-content)
(= current-name (txt/generate-shape-name (txt/content->text original-content)))))]
(st/emit! (dwt/v2-update-text-shape-content shape-id content
:update-name? update-name?
:name generated-name
:finalize? true))))
(let [container-node (mf/ref-val container-ref)] (let [container-node (mf/ref-val container-ref)]
(dom/set-style! container-node "opacity" 0))) (dom/set-style! container-node "opacity" 0)))

View File

@@ -61,7 +61,7 @@
(mf/defc page-item (mf/defc page-item
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [page index deletable? selected? editing? hovering? current-page-id]}] [{:keys [page index deletable? selected? editing? hovering?]}]
(let [input-ref (mf/use-ref) (let [input-ref (mf/use-ref)
id (:id page) id (:id page)
delete-fn (mf/use-fn (mf/deps id) #(st/emit! (dw/delete-page id))) delete-fn (mf/use-fn (mf/deps id) #(st/emit! (dw/delete-page id)))
@@ -72,10 +72,8 @@
(mf/use-fn (mf/use-fn
(mf/deps id) (mf/deps id)
(fn [] (fn []
;; For the wasm renderer, apply a blur effect to the viewport canvas ;; when using the wasm renderer, apply a blur effect to the viewport canvas
;; when we navigate to a different page. (if (features/active-feature? @st/state "render-wasm/v1")
(if (and (features/active-feature? @st/state "render-wasm/v1")
(not= id current-page-id))
(do (do
(wasm.api/capture-canvas-pixels) (wasm.api/capture-canvas-pixels)
(wasm.api/apply-canvas-blur) (wasm.api/apply-canvas-blur)
@@ -205,13 +203,12 @@
(mf/defc page-item-wrapper (mf/defc page-item-wrapper
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [page-id index deletable? selected? editing? current-page-id]}] [{:keys [page-id index deletable? selected? editing?]}]
(let [page-ref (mf/with-memo [page-id] (let [page-ref (mf/with-memo [page-id]
(make-page-ref page-id)) (make-page-ref page-id))
page (mf/deref page-ref)] page (mf/deref page-ref)]
[:& page-item {:page page [:& page-item {:page page
:index index :index index
:current-page-id current-page-id
:deletable? deletable? :deletable? deletable?
:selected? selected? :selected? selected?
:editing? editing?}])) :editing? editing?}]))
@@ -234,7 +231,6 @@
:deletable? deletable? :deletable? deletable?
:editing? (= page-id editing-page-id) :editing? (= page-id editing-page-id)
:selected? (= page-id current-page-id) :selected? (= page-id current-page-id)
:current-page-id current-page-id
:key page-id}])]])) :key page-id}])]]))
;; --- Sitemap Toolbox ;; --- Sitemap Toolbox

View File

@@ -53,12 +53,10 @@
(defn- resolve-value (defn- resolve-value
[tokens prev-token token-name value] [tokens prev-token value]
(let [token (let [token
{:value value {:value value
:name (if (str/blank? token-name) :name "__PENPOT__TOKEN__NAME__PLACEHOLDER__"}
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
token-name)}
tokens tokens
(-> tokens (-> tokens
@@ -133,7 +131,6 @@
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
touched? touched?
@@ -263,10 +260,10 @@
:else :else
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name token-name] (mf/with-effect [resolve-stream tokens token input-name]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]
@@ -312,7 +309,7 @@
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
error error
(get-in @form [:errors :value value-subfield index input-name]) (get-in @form [:errors :value value-subfield index input-name])
@@ -425,10 +422,10 @@
:hint-message (:message error)}) :hint-message (:message error)})
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name index value-subfield token-name] (mf/with-effect [resolve-stream tokens token input-name index value-subfield]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]

View File

@@ -49,12 +49,10 @@
;; validate data within the form state. ;; validate data within the form state.
(defn- resolve-value (defn- resolve-value
[tokens prev-token token-name value] [tokens prev-token value]
(let [token (let [token
{:value (cto/split-font-family value) {:value (cto/split-font-family value)
:name (if (str/blank? token-name) :name "__PENPOT__TOKEN__NAME__PLACEHOLDER__"}
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
token-name)}
tokens tokens
(-> tokens (-> tokens
@@ -75,7 +73,6 @@
[{:keys [token tokens name] :rest props}] [{:keys [token tokens name] :rest props}]
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
touched? touched?
(and (contains? (:data @form) input-name) (and (contains? (:data @form) input-name)
@@ -155,10 +152,10 @@
:hint-message (:message error)}) :hint-message (:message error)})
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name touched? token-name] (mf/with-effect [resolve-stream tokens token input-name touched?]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]
@@ -203,7 +200,7 @@
[{:keys [token tokens name] :rest props}] [{:keys [token tokens name] :rest props}]
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
error error
(get-in @form [:errors :value input-name]) (get-in @form [:errors :value input-name])
@@ -279,10 +276,10 @@
:hint-message (:message error)}) :hint-message (:message error)})
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name token-name] (mf/with-effect [resolve-stream tokens token input-name]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]

View File

@@ -139,12 +139,10 @@
(defn- resolve-value (defn- resolve-value
[tokens prev-token token-name value] [tokens prev-token value]
(let [token (let [token
{:value value {:value value
:name (if (str/blank? token-name) :name "__PENPOT__TOKEN__NAME__PLACEHOLDER__"}
"__PENPOT__TOKEN__NAME__PLACEHOLDER__"
token-name)}
tokens tokens
(-> tokens (-> tokens
;; Remove previous token when renaming a token ;; Remove previous token when renaming a token
@@ -165,7 +163,6 @@
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
touched? touched?
(and (contains? (:data @form) input-name) (and (contains? (:data @form) input-name)
@@ -209,11 +206,11 @@
:hint-message (:message error)}) :hint-message (:message error)})
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name token-name] (mf/with-effect [resolve-stream tokens token input-name]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]
@@ -239,14 +236,12 @@
(on-composite-input-change form field value false)) (on-composite-input-change form field value false))
([form field value trim?] ([form field value trim?]
(letfn [(clean-errors [errors] (letfn [(clean-errors [errors]
(some-> errors (-> errors
(update :value #(when (map? %) (dissoc % field))) (dissoc field)
(update :value #(when (seq %) %)) (not-empty)))]
(not-empty)))]
(swap! form (fn [state] (swap! form (fn [state]
(-> state (-> state
(assoc-in [:data :value field] (if trim? (str/trim value) value)) (assoc-in [:data :value field] (if trim? (str/trim value) value))
(assoc-in [:touched :value field] true)
(update :errors clean-errors) (update :errors clean-errors)
(update :extra-errors clean-errors))))))) (update :extra-errors clean-errors)))))))
@@ -255,7 +250,6 @@
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
error error
(get-in @form [:errors :value input-name]) (get-in @form [:errors :value input-name])
@@ -263,9 +257,6 @@
value value
(get-in @form [:data :value input-name] "") (get-in @form [:data :value input-name] "")
touched?
(get-in @form [:touched :value input-name])
resolve-stream resolve-stream
(mf/with-memo [token] (mf/with-memo [token]
(if-let [value (get-in token [:value input-name])] (if-let [value (get-in token [:value input-name])]
@@ -293,7 +284,7 @@
:hint-message (:message hint) :hint-message (:message hint)
:hint-type (:type hint)}) :hint-type (:type hint)})
props props
(if (and touched? error) (if error
(mf/spread-props props {:hint-type "error" (mf/spread-props props {:hint-type "error"
:hint-message (:message error)}) :hint-message (:message error)})
props) props)
@@ -302,10 +293,10 @@
(mf/spread-props props {:hint-formated true}) (mf/spread-props props {:hint-formated true})
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name name token-name] (mf/with-effect [resolve-stream tokens token input-name name]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]
@@ -341,7 +332,6 @@
message (tr "workspace.tokens.resolved-value" (or resolved-value value))] message (tr "workspace.tokens.resolved-value" (or resolved-value value))]
(swap! form update :errors dissoc :value) (swap! form update :errors dissoc :value)
(swap! form update :extra-errors dissoc :value) (swap! form update :extra-errors dissoc :value)
(swap! form update :async-errors dissoc :reference)
(if (= input-value (str resolved-value)) (if (= input-value (str resolved-value))
(reset! hint* {}) (reset! hint* {})
(reset! hint* {:message message :type "hint"})))))))] (reset! hint* {:message message :type "hint"})))))))]
@@ -369,7 +359,7 @@
(let [form (mf/use-ctx fc/context) (let [form (mf/use-ctx fc/context)
input-name name input-name name
token-name (get-in @form [:data :name] nil)
error error
(get-in @form [:errors :value value-subfield index input-name]) (get-in @form [:errors :value value-subfield index input-name])
@@ -414,10 +404,10 @@
(mf/spread-props props {:hint-formated true}) (mf/spread-props props {:hint-formated true})
props)] props)]
(mf/with-effect [resolve-stream tokens token input-name index value-subfield token-name] (mf/with-effect [resolve-stream tokens token input-name index value-subfield]
(let [subs (->> resolve-stream (let [subs (->> resolve-stream
(rx/debounce 300) (rx/debounce 300)
(rx/mapcat (partial resolve-value tokens token token-name)) (rx/mapcat (partial resolve-value tokens token))
(rx/map (fn [result] (rx/map (fn [result]
(d/update-when result :error (d/update-when result :error
(fn [error] (fn [error]

View File

@@ -23,20 +23,19 @@
(let [token-type (let [token-type
(or (:type token) token-type) (or (:type token) token-type)
tokens-in-selected-set
(mf/deref refs/workspace-all-tokens-in-selected-set)
token-path token-path
(mf/with-memo [token] (mf/with-memo [token]
(cft/token-name->path (:name token))) (cft/token-name->path (:name token)))
tokens-tree-in-selected-set all-tokens (mf/deref refs/workspace-all-tokens-map)
(mf/with-memo [token-path tokens-in-selected-set]
(-> (ctob/tokens-tree tokens-in-selected-set) all-tokens
(mf/with-memo [token-path all-tokens]
(-> (ctob/tokens-tree all-tokens)
(d/dissoc-in token-path))) (d/dissoc-in token-path)))
props props
(mf/spread-props props {:token-type token-type (mf/spread-props props {:token-type token-type
:tokens-tree-in-selected-set tokens-tree-in-selected-set :all-token-tree all-tokens
:token token}) :token token})
text-case-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-case-value-enter")}) text-case-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-case-value-enter")})
text-decoration-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-decoration-value-enter")}) text-decoration-props (mf/spread-props props {:input-value-placeholder (tr "workspace.tokens.text-decoration-value-enter")})

View File

@@ -89,7 +89,7 @@
action action
is-create is-create
selected-token-set-id selected-token-set-id
tokens-tree-in-selected-set all-token-tree
token-type token-type
make-schema make-schema
input-component input-component
@@ -105,6 +105,13 @@
active-tab* (mf/use-state #(if (cft/is-reference? token) :reference :composite)) active-tab* (mf/use-state #(if (cft/is-reference? token) :reference :composite))
active-tab (deref active-tab*) active-tab (deref active-tab*)
on-toggle-tab
(mf/use-fn
(mf/deps)
(fn [new-tab]
(let [new-tab (keyword new-tab)]
(reset! active-tab* new-tab))))
token token
(mf/with-memo [token] (mf/with-memo [token]
(or token {:type token-type})) (or token {:type token-type}))
@@ -114,7 +121,8 @@
token-title (str/lower (:title token-properties)) token-title (str/lower (:title token-properties))
tokens (mf/deref refs/workspace-all-tokens-map) tokens
(mf/deref refs/workspace-active-theme-sets-tokens)
tokens-in-selected-set tokens-in-selected-set
(mf/deref refs/workspace-all-tokens-in-selected-set) (mf/deref refs/workspace-all-tokens-in-selected-set)
@@ -129,8 +137,8 @@
(assoc (:name token) token))) (assoc (:name token) token)))
schema schema
(mf/with-memo [tokens-tree-in-selected-set active-tab] (mf/with-memo [all-token-tree active-tab]
(make-schema tokens-tree-in-selected-set active-tab)) (make-schema all-token-tree active-tab))
initial initial
(mf/with-memo [token] (mf/with-memo [token]
@@ -143,17 +151,6 @@
(fm/use-form :schema schema (fm/use-form :schema schema
:initial initial) :initial initial)
on-toggle-tab
(mf/use-fn
(mf/deps form)
(fn [new-tab]
(let [new-tab (keyword new-tab)]
(if (= new-tab :reference)
(swap! form assoc-in [:async-errors :reference]
{:message "Need valid reference"})
(swap! form update :async-errors dissoc :reference))
(reset! active-tab* new-tab))))
on-cancel on-cancel
(mf/use-fn (mf/use-fn
(fn [e] (fn [e]

View File

@@ -282,11 +282,15 @@
(let [n (d/parse-double blur)] (let [n (d/parse-double blur)]
(or (nil? n) (not (< n 0)))))]]] (or (nil? n) (not (< n 0)))))]]]
[:spread {:optional true} [:spread {:optional true}
[:maybe :string]] [:and
[:maybe :string]
[:fn {:error/fn #(tr "workspace.tokens.shadow-token-spread-value-error")}
(fn [spread]
(let [n (d/parse-double spread)]
(or (nil? n) (not (< n 0)))))]]]
[:color {:optional true} [:maybe :string]] [:color {:optional true} [:maybe :string]]
[:color-result {:optional true} ::sm/any] [:color-result {:optional true} ::sm/any]
[:inset {:optional true} [:maybe :boolean]]]]] [:inset {:optional true} [:maybe :boolean]]]]]
(if (= active-tab :reference) (if (= active-tab :reference)
[:reference {:optional false} ::sm/text] [:reference {:optional false} ::sm/text]
[:reference {:optional true} [:maybe :string]])]] [:reference {:optional true} [:maybe :string]])]]

View File

@@ -144,7 +144,7 @@
modifiers (hooks/use-equal-memo modifiers) modifiers (hooks/use-equal-memo modifiers)
shapes (hooks/use-equal-memo shapes)] shapes (hooks/use-equal-memo shapes)]
[:g.outlines.blurrable [:g.outlines
[:& shape-outlines-render {:shapes shapes [:& shape-outlines-render {:shapes shapes
:zoom zoom :zoom zoom
:modifiers modifiers}]])) :modifiers modifiers}]]))

View File

@@ -252,7 +252,7 @@
edition (mf/deref refs/selected-edition) edition (mf/deref refs/selected-edition)
grid-edition? (ctl/grid-layout? objects edition)] grid-edition? (ctl/grid-layout? objects edition)]
[:g.frame-titles.blurrable [:g.frame-titles
(for [{:keys [id parent-id] :as shape} shapes] (for [{:keys [id parent-id] :as shape} shapes]
(when (and (when (and
(not= id uuid/zero) (not= id uuid/zero)

View File

@@ -424,7 +424,6 @@
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:preserveAspectRatio "xMidYMid meet" :preserveAspectRatio "xMidYMid meet"
:key (str "viewport" page-id) :key (str "viewport" page-id)
:id "viewport-controls"
:view-box (utils/format-viewbox vbox) :view-box (utils/format-viewbox vbox)
:ref on-viewport-ref :ref on-viewport-ref
:class (dm/str @cursor (when drawing-tool " drawing") " " (stl/css :viewport-controls)) :class (dm/str @cursor (when drawing-tool " drawing") " " (stl/css :viewport-controls))
@@ -474,7 +473,7 @@
:zoom zoom}] :zoom zoom}]
(when (ctl/any-layout? outlined-frame) (when (ctl/any-layout? outlined-frame)
[:g.ghost-outline.blurrable [:g.ghost-outline
[:& outline/shape-outlines [:& outline/shape-outlines
{:objects base-objects {:objects base-objects
:selected selected :selected selected

View File

@@ -1185,6 +1185,7 @@
{:cmd :export-shapes {:cmd :export-shapes
:profile-id (:profile-id @st/state) :profile-id (:profile-id @st/state)
:wait true :wait true
:skip-children (:skip-children value false)
:exports [{:file-id file-id :exports [{:file-id file-id
:page-id page-id :page-id page-id
:object-id id :object-id id

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