Compare commits

..

26 Commits

Author SHA1 Message Date
Luis de Dios
8963104736 🎉 Add MCP server to integrations section in dashboard 2026-01-22 17:14:08 +01:00
Andrey Antukh
dc973dac36 🔧 Enable link workspace packages on plugins 2026-01-22 13:55:41 +01:00
Andrey Antukh
4467827218 ⬆️ Update react dependency on frontend 2026-01-22 13:55:41 +01:00
Andrey Antukh
6470db8d5f Remove mention of yarn on several files 2026-01-22 13:55:41 +01:00
Andrey Antukh
dc44156b53 🐛 Fix text editor wasm playground 2026-01-22 13:55:41 +01:00
Andrey Antukh
f0e53d70ae 🎉 Migrate to PNPM render-wasm module 2026-01-22 13:55:41 +01:00
Andrey Antukh
ef73a263b2 🎉 Migrate to PNPM docs module 2026-01-22 13:55:41 +01:00
Andrey Antukh
9b1e007a49 Replace e2e node server with caddy
Which is already available in the devenv runtime image
2026-01-22 13:55:41 +01:00
Andrey Antukh
ea8632e56a 🐛 Fix frontend test-components scripts 2026-01-22 13:55:41 +01:00
Andrey Antukh
2d00e64ede Fix e2e scripts related to pnpm change 2026-01-22 13:55:41 +01:00
Andrey Antukh
1246250198 🎉 Migrate to PNPM frontend module 2026-01-22 13:55:41 +01:00
Andrey Antukh
34f2943dcd 🎉 Migrate to PNPM library module 2026-01-22 13:55:41 +01:00
Andrey Antukh
3fb78116b8 🎉 Migrate to PNPM backend module 2026-01-22 13:55:41 +01:00
Andrey Antukh
072e415b9e 🎉 Migrate to PNPM common module 2026-01-22 13:55:41 +01:00
Andrey Antukh
67a904824c 🎉 Migrate to PNPM exporter module 2026-01-22 13:55:41 +01:00
Andrey Antukh
68184209be Add the ability to make json/->clj non-recursive 2026-01-22 13:07:05 +01:00
Andrey Antukh
d2295862b4 Add the ability to customize decoder and schema on obj/reify 2026-01-22 13:07:05 +01:00
Andrey Antukh
23cbf33d1b Merge remote-tracking branch 'origin/staging' into develop 2026-01-22 12:34:49 +01:00
David Barragán Merino
2574ad3315 🔧 Fixes to the API documentation deployer 2026-01-22 12:09:38 +01:00
David Barragán Merino
e6b5364a84 🔧 Deploy penpot api documentation 2026-01-22 12:09:38 +01:00
David Barragán Merino
b4ff0ccf3a 🔧 Fixes to the API documentation deployer 2026-01-22 12:08:09 +01:00
Andrey Antukh
6c6666a39a Merge pull request #8158 from penpot/ladybenko-13058-hide-avatar
🐛 Do not hide active users avatar when there are 3 of them
2026-01-22 12:02:53 +01:00
Alonso Torres
656f81f89f ⬆️ Update plugins to 1.4.2 (#8157) 2026-01-21 17:36:58 +01:00
Belén Albeza
eaf64b6e16 ♻️ Make the CSS of presence widgets to adhere to our guidelines 2026-01-21 17:33:18 +01:00
Belén Albeza
560a0d09d5 🐛 Fix hiding avatar when we have 3 active users 2026-01-21 16:01:20 +01:00
Alonso Torres
01a4ffeb8b ⬆️ Updated plugins release to 1.4.0 (#8148) 2026-01-21 15:41:00 +01:00
122 changed files with 15019 additions and 50722 deletions

View File

@@ -7,16 +7,22 @@ on:
- staging
- main
paths:
- "plugins/**"
- ".github/workflows/deploy-plugin-docs.yml"
- "wrangle-penpot-plugins-api-doc.toml"
- "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 or ref'
type: string
description: 'Name of the branch'
type: choice
required: true
default: 'develop'
options:
- develop
- staging
- main
permissions:
contents: read
@@ -24,9 +30,6 @@ permissions:
jobs:
deploy:
runs-on: ubuntu-latest
defaults:
run:
working-directory: plugins
steps:
- name: Extract some useful variables
id: vars
@@ -39,20 +42,44 @@ jobs:
fetch-depth: 0
ref: ${{ steps.vars.outputs.gh_ref }}
# START: Setup Node and PNPM enabling cache
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
cache: "pnpm"
node-version-file: .nvmrc
- name: Setup pnpm
uses: pnpm/action-setup@v4
- 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:
run_install: false
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 dependencies
run: pnpm install --frozen-lockfile
- 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
@@ -68,6 +95,7 @@ jobs:
- 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 }}

4
.gitignore vendored
View File

@@ -21,7 +21,6 @@
.rebel_readline_history
.repl
.shadow-cljs
.pnpm-store/
/*.jpg
/*.md
/*.png
@@ -55,6 +54,8 @@
/exporter/target
/frontend/.storybook/preview-body.html
/frontend/.storybook/preview-head.html
/frontend/playwright-report/
/frontend/text-editor/src/wasm/
/frontend/dist/
/frontend/npm-debug.log
/frontend/out/
@@ -73,7 +74,6 @@
/library/target/
/library/*.zip
/external
/penpot-nitrate
clj-profiler/
node_modules

View File

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

View File

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

View File

@@ -25,6 +25,7 @@
- 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 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)
## 2.13.0 (Unreleased)

View File

@@ -120,17 +120,12 @@ them on your system, you can run them with:
```bash
# Check formatting
yarn fmt:clj:check
./scripts/fmt
# Check and fix formatting
yarn fmt:clj
# Run the linter
yarn lint:clj
# Lint
./scripts/lint
```
There are more choices in `package.json`.
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/#/).

7
backend/.gitignore vendored
View File

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

View File

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

306
backend/pnpm-lock.yaml generated Normal file
View File

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

@@ -36,8 +36,7 @@ export PENPOT_FLAGS="\
enable-file-validation \
enable-file-schema-validation \
enable-redis-cache \
enable-subscriptions \
enable-nitrate";
enable-subscriptions";
# Default deletion delay for devenv
export PENPOT_DELETION_DELAY="24h"
@@ -56,8 +55,6 @@ export PENPOT_OBJECTS_STORAGE_BACKEND=s3
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
export PENPOT_NITRATE_BACKEND_URI=http://localhost:3000/control-center
export JAVA_OPTS="\
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
-Djdk.attach.allowAttachSelf \

View File

@@ -225,8 +225,6 @@
[:netty-io-threads {:optional true} ::sm/int]
[:executor-threads {:optional true} ::sm/int]
[:nitrate-backend-uri {:optional true} ::sm/uri]
;; DEPRECATED
[:assets-storage-backend {:optional true} :keyword]
[:storage-assets-fs-directory {:optional true} :string]

View File

@@ -323,7 +323,6 @@
{::http.client/client (ig/ref ::http.client/client)
::db/pool (ig/ref ::db/pool)
::rds/pool (ig/ref ::rds/pool)
:app.nitrate/client (ig/ref :app.nitrate/client)
::wrk/executor (ig/ref ::wrk/netty-executor)
::session/manager (ig/ref ::session/manager)
::ldap/provider (ig/ref ::ldap/provider)
@@ -340,9 +339,6 @@
::email/blacklist (ig/ref ::email/blacklist)
::email/whitelist (ig/ref ::email/whitelist)}
:app.nitrate/client
{::http.client/client (ig/ref ::http.client/client)}
:app.rpc/management-methods
{::http.client/client (ig/ref ::http.client/client)
::db/pool (ig/ref ::db/pool)
@@ -352,7 +348,6 @@
::sto/storage (ig/ref ::sto/storage)
::mtx/metrics (ig/ref ::mtx/metrics)
::mbus/msgbus (ig/ref ::mbus/msgbus)
:app.nitrate/client (ig/ref :app.nitrate/client)
::rds/client (ig/ref ::rds/client)
::setup/props (ig/ref ::setup/props)}

View File

@@ -1,133 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.nitrate
"Module that make calls to the external nitrate aplication"
(:require
[app.common.logging :as l]
[app.common.schema :as sm]
[app.config :as cf]
[app.http.client :as http]
[app.rpc :as-alias rpc]
[app.setup :as-alias setup]
[app.util.json :as json]
[clojure.core :as c]
[integrant.core :as ig]))
(def baseuri (cf/get :nitrate-backend-uri))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- coercer
[schema & {:as opts}]
(let [decode-fn (sm/decoder schema sm/json-transformer)
check-fn (sm/check-fn schema opts)]
(fn [data]
(-> data decode-fn check-fn))))
(defn- request-builder
[cfg method uri management-key profile-id]
(fn []
(http/req! cfg {:method method
:headers {"content-type" "application/json"
"accept" "application/json"
"x-shared-key" management-key
"x-profile-id" (str profile-id)}
:uri uri
:version :http1.1})))
(defn- with-retries
[handler max-retries]
(fn []
(loop [attempt 1]
(let [result (try
(handler)
(catch Exception e
(if (< attempt max-retries)
::retry
(do
;; TODO Error handling
(l/error :hint "request fail after multiple retries" :cause e)
nil))))]
(if (= result ::retry)
(recur (inc attempt))
result)))))
(defn- with-validate [handler uri schema]
(fn []
(let [coercer-http (coercer schema
:type :validation
:hint (str "invalid data received calling " uri))]
(try
(coercer-http (-> (handler) :body json/decode))
(catch Exception e
;; TODO Error handling
(l/error :hint "error validating json response" :cause e)
nil)))))
(defn- request-to-nitrate
[{:keys [::management-key] :as cfg} method uri schema {:keys [::rpc/profile-id] :as params}]
(let [full-http-call (-> (request-builder cfg method uri management-key profile-id)
(with-retries 3)
(with-validate uri schema))]
(full-http-call)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; API
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn call
[cfg method params]
(when (contains? cf/flags :nitrate)
(let [client (get cfg ::client)
method (get client method)]
(method params))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private schema:organization
[:map
[:id ::sm/text]
[:name ::sm/text]])
(def ^:private schema:user
[:map
[:valid ::sm/boolean]])
(defn- get-team-org
[cfg {:keys [team-id] :as params}]
(request-to-nitrate cfg :get (str baseuri "/api/teams/" (str team-id)) schema:organization params))
(defn- is-valid-user
[cfg {:keys [profile-id] :as params}]
(request-to-nitrate cfg :get (str baseuri "/api/users/" (str profile-id)) schema:user params))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INITIALIZATION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod ig/init-key ::client
[_ {:keys [::setup/props] :as cfg}]
(if (contains? cf/flags :nitrate)
(let [management-key (or (cf/get :management-api-key)
(get props :management-key))
cfg (assoc cfg ::management-key management-key)]
{:get-team-org (partial get-team-org cfg)
:is-valid-user (partial is-valid-user cfg)})
{}))
(defmethod ig/halt-key! ::client
[_ {:keys []}]
(do :stuff))

View File

@@ -301,7 +301,6 @@
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
(->> (sv/scan-ns
'app.rpc.management.subscription
'app.rpc.management.nitrate
'app.rpc.management.exporter)
(map (partial process-method cfg "management" wrap-management))
(into {}))))

View File

@@ -21,7 +21,6 @@
[app.loggers.audit :as audit]
[app.main :as-alias main]
[app.media :as media]
[app.nitrate :as nitrate]
[app.rpc :as-alias rpc]
[app.rpc.climit :as climit]
[app.rpc.doc :as-alias doc]
@@ -48,6 +47,7 @@
(def schema:props
[:map {:title "ProfileProps"}
[:plugins {:optional true} schema:plugin-registry]
[:mcp-status {:optional true} ::sm/boolean]
[:newsletter-updates {:optional true} ::sm/boolean]
[:newsletter-news {:optional true} ::sm/boolean]
[:onboarding-team-id {:optional true} ::sm/uuid]
@@ -89,17 +89,6 @@
;; --- QUERY: Get profile (own)
(defn- add-nitrate-licence-to-profile
[cfg profile]
(try
(let [nitrate-licence (nitrate/call cfg :is-valid-user {:profile-id (:id profile)})]
(assoc profile :nitrate-licence (:valid nitrate-licence)))
(catch Throwable cause
(prn :hint "failed to get nitrate licence"
:profile-id (:id profile)
:cause cause)
profile)))
(sv/defmethod ::get-profile
{::rpc/auth false
::doc/added "1.18"
@@ -110,13 +99,9 @@
;; no profile-id is in session, and when db call raises not found. In all other
;; cases we need to reraise the exception.
(try
(let [profile (-> (get-profile pool profile-id)
(strip-private-attrs)
(update :props filter-props))]
(if (contains? cf/flags :nitrate)
(add-nitrate-licence-to-profile cfg profile)
profile))
(-> (get-profile pool profile-id)
(strip-private-attrs)
(update :props filter-props))
(catch Throwable _
{:id uuid/zero :fullname "Anonymous User"})))

View File

@@ -23,7 +23,6 @@
[app.main :as-alias main]
[app.media :as media]
[app.msgbus :as mbus]
[app.nitrate :as nitrate]
[app.rpc :as-alias rpc]
[app.rpc.commands.profile :as profile]
[app.rpc.doc :as-alias doc]
@@ -173,12 +172,6 @@
(map decode-row)
(map process-permissions)))
(defn- add-org-to-team
[cfg team params]
(let [params (assoc (or params {}) :team-id (:id team))
org (nitrate/call cfg :get-team-org params)]
(assoc team :organization-id (:id org) :organization-name (:name org))))
(defn get-teams
[conn profile-id]
(let [profile (profile/get-profile conn profile-id)
@@ -197,9 +190,7 @@
::sm/params schema:get-teams}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
(dm/with-open [conn (db/open pool)]
(cond->> (get-teams conn profile-id)
(contains? cf/flags :nitrate)
(map #(add-org-to-team cfg % params)))))
(get-teams conn profile-id)))
(def ^:private sql:get-owned-teams
"SELECT t.id, t.name,

View File

@@ -1,112 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.rpc.management.nitrate
"Internal Nitrate HTTP API.
Provides authenticated access to organization management and token validation endpoints.
All requests must include a valid shared key token in the `x-shared-key` header, and
a cookie `auth-token` with the user token.
They will return `401 Unauthorized` if the shared key or user token are invalid."
(:require
[app.common.schema :as sm]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.msgbus :as mbus]
[app.rpc :as-alias rpc]
[app.rpc.commands.files :as files]
[app.rpc.commands.profile :as profile]
[app.rpc.doc :as doc]
[app.util.services :as sv]))
;; ---- API: authenticate
(def ^:private schema:profile
[:map
[:id ::sm/uuid]
[:name :string]
[:email :string]
[:photo-url :string]])
(sv/defmethod ::authenticate
"Authenticate an user
@api GET /authenticate
@returns
200 OK: Returns the authenticated user."
{::doc/added "2.12"
::sm/result schema:profile}
[cfg {:keys [::rpc/profile-id] :as params}]
(let [profile (profile/get-profile cfg profile-id)]
{:id (get profile :id)
:name (get profile :fullname)
:email (get profile :email)
:photo-url (files/resolve-public-uri (get profile :photo-id))}))
;; ---- API: get-teams
(def ^:private sql:get-teams
"SELECT t.*
FROM team AS t
JOIN team_profile_rel AS tpr ON t.id = tpr.team_id
WHERE tpr.profile_id = ?
AND tpr.is_owner = 't'
AND t.is_default = 'f'
AND t.deleted_at is null;")
(def ^:private schema:team
[:map
[:id ::sm/uuid]
[:name :string]])
(def ^:private schema:get-teams-result
[:vector schema:team])
(sv/defmethod ::get-teams
"List teams for which current user is owner.
@api GET /get-teams
@returns
200 OK: Returns the list of teams for the user."
{::doc/added "2.12"
::sm/result schema:get-teams-result}
[cfg {:keys [::rpc/profile-id]}]
(when (contains? cf/flags :nitrate)
(let [current-user-id (-> (profile/get-profile cfg profile-id) :id)]
(->> (db/exec! cfg [sql:get-teams current-user-id])
(map #(select-keys % [:id :name]))))))
;; ---- API: notify-team-change
(def ^:private schema:notify-team-change
[:map
[:id ::sm/uuid]
[:organization-id ::sm/text]])
(sv/defmethod ::notify-team-change
"Notify to Penpot a team change from nitrate
@api POST /notify-team-change
@returns
200 OK"
{::doc/added "2.12"
::sm/params schema:notify-team-change
::rpc/auth false}
[cfg {:keys [id organization-id organization-name]}]
(when (contains? cf/flags :nitrate)
(let [msgbus (::mbus/msgbus cfg)]
(mbus/pub! msgbus
;;TODO There is a bug on dashboard with teams notifications.
;;For now we send it to uuid/zero instead of team-id
:topic uuid/zero
:message {:type :team-org-change
:team-id id
:organization-id organization-id
:organization-name organization-name}))))

View File

File diff suppressed because it is too large Load Diff

7
common/.gitignore vendored
View File

@@ -1,7 +0,0 @@
.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"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "0.4.6"}
mockery/mockery {:mvn/version "RELEASE"}}
:extra-paths ["test" "dev"]}

View File

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

489
common/pnpm-lock.yaml generated Normal file
View File

@@ -0,0 +1,489 @@
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
corepack enable;
corepack install;
yarn install;
yarn run test;
pnpm install;
pnpm run test;

View File

@@ -145,10 +145,7 @@
;; A temporal flag, enables backend code use more extensivelly
;; redis for caching data
:redis-cache
;; Activates the nitrate module
:nitrate})
:redis-cache})
(def all-flags
(set/union email login varia))

View File

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

View File

File diff suppressed because it is too large Load Diff

View File

@@ -141,14 +141,8 @@ http {
proxy_pass http://127.0.0.1:5000;
}
location /control-center {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location /nitrate/ {
proxy_pass http://127.0.0.1:3000/;
}
location /wasm-playground {

View File

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

View File

@@ -29,9 +29,8 @@ update_flags /var/www/app/js/config.js
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE:-367001600} # Default to 350MiB
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"

View File

@@ -139,14 +139,6 @@ http {
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
}
location /control-center {
proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $http_cf_connecting_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass $PENPOT_NITRATE_URI$request_uri;
}
include /etc/nginx/overrides/server.d/*.conf;
location / {

View File

@@ -10,16 +10,15 @@ To view this site locally, first set up the environment:
# only if necessary
nvm install
nvm use
# only if necessary
corepack enable
yarn install
pnpm install
```
And launch a development server:
```sh
yarn start
pnpm start
```
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-plantuml": "^1.4.1"
},
"packageManager": "yarn@4.3.1"
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
}

2065
docs/pnpm-lock.yaml generated Normal file
View File

File diff suppressed because it is too large Load Diff

View File

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

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:
```bash
yarn run test
yarn run test:watch
pnpm run test
pnpm run test:watch
```
#### Test output

View File

@@ -217,7 +217,7 @@ repository:
```bash
# cd <repo>/frontend
yarn run translations
pnpm run translations
```
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:
```bash
yarn run build:app
./scripts/build
```
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
```
3. Run the tests with <code class="language-bash">yarn</code>:
3. Run the tests with <code class="language-bash">pnpm</code>:
```bash
yarn test:e2e
pnpm run 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.
```bash
# run in parallel with 4 workers
yarn test:e2e --workers 4
pnpm run test:e2e --workers 4
```
#### 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).
> 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>.
> 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>.
### 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
Use <code class="language-bash">yarn watch:storybook</code> to develop the Design System components with the help of Storybook.
Use <code class="language-bash">pnpm run 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.

View File

File diff suppressed because it is too large Load Diff

7
exporter/.gitignore vendored
View File

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

View File

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

1048
exporter/pnpm-lock.yaml generated Normal file
View File

File diff suppressed because it is too large Load Diff

View File

View File

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

View File

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

View File

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

View File

File diff suppressed because it is too large Load Diff

14
frontend/.gitignore vendored
View File

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

View File

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

449
frontend/packages/draft-js/pnpm-lock.yaml generated Normal file
View File

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

@@ -1,424 +0,0 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"@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",
"main": "index.js",
"type": "module",
"packageManager": "yarn@4.3.1",
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
"author": "Craig Campbell",
"license": "Apache-2.0 WITH LLVM-exception"
}

View File

@@ -1,12 +0,0 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"@penpot/mousetrap@workspace:.":
version: 0.0.0-use.local
resolution: "@penpot/mousetrap@workspace:."
languageName: unknown
linkType: soft

View File

@@ -0,0 +1,17 @@
diff --git a/lib/zip-core-base.js b/lib/zip-core-base.js
index 142155c8c3ab1a6caa7c370e8a2931a954556ca9..61b0fe6efb91312f437750e0002218f218afad8b 100644
--- a/lib/zip-core-base.js
+++ b/lib/zip-core-base.js
@@ -28,12 +28,6 @@
import { configure } from "./core/configuration.js";
-try {
- configure({ baseURI: import.meta.url });
-} catch {
- // ignored
-}
-
export * from "./zip-core-reader.js";
export * from "./zip-core-writer.js";
export {

View File

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

View File

@@ -5,3 +5,19 @@ export const presenceFixture = {
"~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
"~: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

@@ -216,32 +216,4 @@ test.describe("Tokens: Sets Tab", () => {
await expect(tokenSetItems.nth(1)).toHaveAttribute("aria-checked", "false");
await expect(tokenSetItems.nth(2)).toHaveAttribute("aria-checked", "true");
});
test("Display active set and verify if is enabled", async ({ page }) => {
const { tokenThemesSetsSidebar, tokensSidebar, tokenSetItems } =
await setupTokensFile(page);
// Create set
await tokenThemesSetsSidebar
.getByRole("button", { name: "Add set" })
.click();
await changeSetInput(tokenThemesSetsSidebar, "Inactive set");
await tokenThemesSetsSidebar
.getByRole("button", { name: "Inactive set" })
.click();
let activeSetTitle = await tokensSidebar.getByTestId(
"active-token-set-title",
);
await expect(activeSetTitle).toHaveText("TOKENS - Inactive set");
const inactiveSetInfo = await tokensSidebar.getByTitle(
"This set is not active.",
);
await expect(inactiveSetInfo).toBeVisible();
// Switch active set
await tokenThemesSetsSidebar.getByRole("button", { name: "theme" }).click();
await expect(activeSetTitle).toHaveText("TOKENS - theme");
await expect(inactiveSetInfo).not.toBeVisible();
});
});

View File

@@ -1,6 +1,6 @@
import { test, expect } from "@playwright/test";
import { WorkspacePage } from "../pages/WorkspacePage";
import { presenceFixture } from "../../data/workspace/ws-notifications";
import { presenceFixture, joinFixture2, joinFixture3 } from "../../data/workspace/ws-notifications";
test.beforeEach(async ({ page }) => {
await WorkspacePage.init(page);
@@ -40,6 +40,28 @@ test("User receives presence notifications updates in the workspace", async ({
).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 }) => {
const workspacePage = new WorkspacePage(page);
await workspacePage.setupEmptyFile();

8718
frontend/pnpm-lock.yaml generated Normal file
View File

File diff suppressed because it is too large Load Diff

View File

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

@@ -700,19 +700,6 @@
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 {
@include bodySmallTypography;
display: flex;

View File

@@ -18,10 +18,9 @@ export VERSION_TAG="${VERSION}-${BUILD_TS}";
# performant code on macros (example: rumext)
export NODE_ENV=production;
corepack enable;
corepack install;
yarn install;
pnpm install;
rm -rf target/dist;
rm -rf resources/public;
@@ -33,9 +32,9 @@ pushd ../render-wasm;
./build
popd
yarn run build:app:main $EXTRA_PARAMS;
yarn run build:app:libs;
yarn run build:app:assets;
pnpm run build:app:main $EXTRA_PARAMS;
pnpm run build:app:libs;
pnpm run build:app:assets;
sed -i "s/\.\/render.js/.\/render.js?version=$VERSION_TAG/g" resources/public/js/worker/main*.js
@@ -43,6 +42,6 @@ rsync -avr resources/public/ target/dist/
if [ "$INCLUDE_STORYBOOK" = "yes" ]; then
# build storybook
yarn run build:storybook || exit 1;
pnpm run build:storybook || exit 1;
rsync -avr storybook-static/ target/dist/storybook-static;
fi

View File

@@ -14,6 +14,6 @@ export NODE_ENV=production;
corepack enable;
corepack install || exit 1;
yarn install || exit 1;
pnpm install || exit 1;
yarn run build:storybook || exit 1;
pnpm run build:storybook || exit 1;

View File

@@ -1,20 +0,0 @@
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 install;
yarn install;
yarn playwright install chromium;
pnpm install;
pnpx playwright install chromium;

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,6 @@
[app.common.time :as ct]
[app.common.types.project :refer [valid-project?]]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.main.constants :as mconst]
[app.main.data.common :as dcm]
[app.main.data.event :as ev]
@@ -684,25 +683,12 @@
(rx/of (dcm/change-team-role params)
(modal/hide)))))
(defn handle-change-team-org
[{:keys [team-id organization-id organization-name] :as message}]
(ptk/reify ::handle-change-team-org
ptk/UpdateEvent
(update [_ state]
(if (and (contains? cf/flags :nitrate)
(contains? (:teams state) team-id))
(-> state
(assoc-in [:teams team-id :organization-id] organization-id)
(assoc-in [:teams team-id :organization-name] organization-name))
state))))
(defn- process-message
[{:keys [type] :as msg}]
(case type
:notification (dcm/handle-notification msg)
:team-role-change (handle-change-team-role msg)
:team-membership-change (dcm/team-membership-change msg)
:team-org-change (handle-change-team-org msg)
nil))

View File

@@ -197,7 +197,7 @@
:settings-options
:settings-feedback
:settings-subscription
:settings-access-tokens
:settings-integrations
:settings-notifications)
(let [params (get params :query)
error-report-id (some-> params :error-report-id uuid/parse*)]

View File

@@ -1,46 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.dashboard.nitrate-form
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.modal :as modal]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.icons :as deprecated-icon]
[app.util.dom :as dom]
[rumext.v2 :as mf]))
(mf/defc nitrate-form-modal
{::mf/register modal/components
::mf/register-as :nitrate-form}
[]
(let [on-click
(mf/use-fn
(fn []
(dom/open-new-window "/control-center/licenses/start")))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-container)}
[:div {:class (stl/css :nitrate-form)}
[:div {:class (stl/css :modal-header)}
[:h2 {:class (stl/css :modal-title)}
"BUY NITRATE"]
[:button {:class (stl/css :modal-close-btn)
:on-click modal/hide!} deprecated-icon/close]]
[:div {:class (stl/css :modal-content)}
"Nitrate is so cool! You should buy it!"]
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
[:> button* {:variant "primary"
:on-click on-click}
"BUY NOW!"]]]]]]))

View File

@@ -1,52 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-container {
@extend .modal-container-base;
}
.modal-header {
margin-bottom: deprecated.$s-24;
}
.modal-title {
@include deprecated.uppercaseTitleTipography;
color: var(--modal-title-foreground-color);
}
.modal-close-btn {
@extend .modal-close-btn-base;
}
.modal-content {
margin-bottom: deprecated.$s-24;
}
.nitrate-form {
min-width: deprecated.$s-400;
}
.action-buttons {
@extend .modal-action-btns;
}
.cancel-button {
@extend .modal-cancel-btn;
}
.accept-btn {
@extend .modal-accept-btn;
&.danger {
@extend .modal-danger-btn;
}
}

View File

@@ -26,7 +26,6 @@
[app.main.ui.components.link :refer [link]]
[app.main.ui.dashboard.comments :refer [comments-icon* comments-section]]
[app.main.ui.dashboard.inline-edition :refer [inline-edition]]
[app.main.ui.dashboard.nitrate-form]
[app.main.ui.dashboard.project-menu :refer [project-menu*]]
[app.main.ui.dashboard.subscription :refer [dashboard-cta*
get-subscription-type
@@ -281,8 +280,8 @@
(mf/defc teams-selector-dropdown*
{::mf/private true}
[{:keys [team profile teams show-default-team allow-create-teams allow-create-org] :rest props}]
(let [on-create-team-click
[{:keys [team profile teams] :rest props}]
(let [on-create-click
(mf/use-fn #(st/emit! (modal/show :team-form {})))
on-team-click
@@ -291,27 +290,18 @@
(let [team-id (-> (dom/get-current-target event)
(dom/get-data "value")
(uuid/parse))]
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))
on-create-org-click
(mf/use-fn
(fn []
(if (:nitrate-licence profile)
;; TODO update when org creation route is ready
(dom/open-new-window "/control-center/org/create")
(st/emit! (modal/show :nitrate-form {})))))]
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))]
[:> dropdown-menu* props
(when show-default-team
[:> dropdown-menu-item* {:on-click on-team-click
:data-value (:default-team-id profile)
:class (stl/css :team-dropdown-item)}
[:span {:class (stl/css :penpot-icon)} deprecated-icon/logo-icon]
[:> dropdown-menu-item* {:on-click on-team-click
:data-value (:default-team-id profile)
:class (stl/css :team-dropdown-item)}
[:span {:class (stl/css :penpot-icon)} deprecated-icon/logo-icon]
[:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")]
(when (= (:default-team-id profile) (:id team))
tick-icon)])
[:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")]
(when (= (:default-team-id profile) (:id team))
tick-icon)]
(for [team-item (remove :is-default (vals teams))]
[:> dropdown-menu-item* {:on-click on-team-click
@@ -332,19 +322,11 @@
(when (= (:id team-item) (:id team))
tick-icon)])
(when allow-create-teams
[:hr {:role "separator" :class (stl/css :team-separator)}]
[:> dropdown-menu-item* {:on-click on-create-team-click
:class (stl/css :team-dropdown-item :action)}
[:span {:class (stl/css :icon-wrapper)} add-icon]
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]])
(when allow-create-org
[:hr {:role "separator" :class (stl/css :team-separator)}]
[:> dropdown-menu-item* {:on-click on-create-org-click
:class (stl/css :team-dropdown-item :action)}
[:span {:class (stl/css :icon-wrapper)} add-icon]
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-org")]])]))
[:hr {:role "separator" :class (stl/css :team-separator)}]
[:> dropdown-menu-item* {:on-click on-create-click
:class (stl/css :team-dropdown-item :action)}
[:span {:class (stl/css :icon-wrapper)} add-icon]
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]]]))
(mf/defc team-options-dropdown*
{::mf/private true}
@@ -494,80 +476,9 @@
:data-testid "delete-team"}
(tr "dashboard.delete-team")])]))
(mf/defc sidebar-org-switch*
[{:keys [team profile]}]
(let [teams (->> (mf/deref refs/teams)
vals
(group-by :organization-id)
(map (fn [[_group entries]] (first entries)))
vec
(d/index-by :id))
teams (update-vals teams
(fn [t]
(assoc t :name (str "ORG: " (:organization-name t)))))
team (assoc team :name (str "ORG: " (:organization-name team)))
show-teams-menu*
(mf/use-state false)
show-teams-menu?
(deref show-teams-menu*)
on-show-teams-click
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(swap! show-teams-menu* not)))
on-show-teams-keydown
(mf/use-fn
(fn [event]
(when (or (kbd/space? event)
(kbd/enter? event))
(dom/prevent-default event)
(dom/stop-propagation event)
(some-> (dom/get-current-target event)
(dom/click!)))))
close-teams-menu
(mf/use-fn #(reset! show-teams-menu* false))]
[:div {:class (stl/css :sidebar-team-switch)}
[:div {:class (stl/css :switch-content)}
[:button {:class (stl/css :current-team)
:on-click on-show-teams-click
:on-key-down on-show-teams-keydown}
[:div {:class (stl/css :team-name)}
[:img {:src (cf/resolve-team-photo-url team)
:class (stl/css :team-picture)
:alt (:name team)}]
[:span {:class (stl/css :team-text) :title (:name team)} (:name team)]]
arrow-icon]]
;; Teams Dropdown
[:> teams-selector-dropdown* {:show show-teams-menu?
:on-close close-teams-menu
:id "organizations-list"
:class (stl/css :dropdown :teams-dropdown)
:team team
:profile profile
:teams teams
:show-default-team false
:allow-create-teams false
:allow-create-org true}]]))
(mf/defc sidebar-team-switch*
[{:keys [team profile]}]
(let [nitrate? (contains? cf/flags :nitrate)
org-id (when nitrate? (:organization-id team))
teams (cond->> (mf/deref refs/teams)
nitrate?
(filter #(= (-> % val :organization-id) org-id)))
(let [teams (mf/deref refs/teams)
subscription
(get team :subscription)
@@ -675,10 +586,7 @@
:class (stl/css :dropdown :teams-dropdown)
:team team
:profile profile
:teams teams
:show-default-team true
:allow-create-teams true
:allow-create-org false}]
:teams teams}]
[:> team-options-dropdown* {:show show-team-options-menu?
:on-close close-team-options-menu
@@ -795,8 +703,6 @@
[:*
[:div {:class (stl/css-case :sidebar-content true)
:ref container}
(when (contains? cf/flags :nitrate)
[:> sidebar-org-switch* {:team team :profile profile}])
[:> sidebar-team-switch* {:team team :profile profile}]
[:> sidebar-search* {:search-term search-term

View File

@@ -18,6 +18,7 @@ $sz-32: px2rem(32);
$sz-36: px2rem(36);
$sz-40: px2rem(40);
$sz-48: px2rem(48);
$sz-64: px2rem(64);
$sz-88: px2rem(88);
$sz-96: px2rem(96);
$sz-120: px2rem(120);

View File

@@ -8,6 +8,7 @@
(:require
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.controls.input :refer [input*]]
[app.main.ui.ds.controls.select :refer [select*]]
[app.util.dom :as dom]
[app.util.forms :as fm]
[app.util.keyboard :as k]
@@ -47,6 +48,23 @@
[:> input* props]))
(mf/defc form-select*
[{:keys [name] :as props}]
(let [select-name name
form (mf/use-ctx context)
value (get-in @form [:data select-name] "")
handle-change
(fn [event]
(let [value (if (string? event) event (dom/get-target-val event))]
(fm/on-input-change form select-name value)))
props
(mf/spread-props props {:on-change handle-change
:value value})]
[:> select* props]))
(mf/defc form-submit*
[{:keys [disabled on-submit] :rest props}]
@@ -80,4 +98,4 @@
(when (fn? on-submit)
(on-submit form event))))]
[:> (mf/provider context) {:value form}
[:form {:class class :on-submit on-submit'} children]]))
[:form {:class class :on-submit on-submit'} children]]))

View File

@@ -36,7 +36,7 @@
["/feedback" :settings-feedback]
["/options" :settings-options]
["/subscriptions" :settings-subscription]
["/access-tokens" :settings-access-tokens]
["/integrations" :settings-integrations]
["/notifications" :settings-notifications]]
["/frame-preview" :frame-preview]

View File

@@ -13,10 +13,10 @@
[app.main.store :as st]
[app.main.ui.hooks :as hooks]
[app.main.ui.modal :refer [modal-container*]]
[app.main.ui.settings.access-tokens :refer [access-tokens-page]]
[app.main.ui.settings.change-email]
[app.main.ui.settings.delete-account]
[app.main.ui.settings.feedback :refer [feedback-page*]]
[app.main.ui.settings.integrations :refer [integrations-page*]]
[app.main.ui.settings.notifications :refer [notifications-page*]]
[app.main.ui.settings.options :refer [options-page]]
[app.main.ui.settings.password :refer [password-page]]
@@ -73,8 +73,8 @@
:settings-subscription
[:> subscription-page* {:profile profile}]
:settings-access-tokens
[:& access-tokens-page]
:settings-integrations
[:> integrations-page*]
:settings-notifications
[:& notifications-page* {:profile profile}])]]]]))

View File

@@ -1,291 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.settings.access-tokens
(:require-macros [app.main.style :as stl])
(:require
[app.common.schema :as sm]
[app.common.time :as ct]
[app.main.data.modal :as modal]
[app.main.data.notifications :as ntf]
[app.main.data.profile :as du]
[app.main.store :as st]
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
[app.main.ui.components.forms :as fm]
[app.main.ui.icons :as deprecated-icon]
[app.util.clipboard :as clipboard]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def ^:private clipboard-icon
(deprecated-icon/icon-xref :clipboard (stl/css :clipboard-icon)))
(def ^:private close-icon
(deprecated-icon/icon-xref :close (stl/css :close-icon)))
(def ^:private menu-icon
(deprecated-icon/icon-xref :menu (stl/css :menu-icon)))
(def tokens-ref
(l/derived :access-tokens st/state))
(def token-created-ref
(l/derived :access-token-created st/state))
(def ^:private schema:form
[:map {:title "AccessTokenForm"}
[:name [::sm/text {:max 250}]]
[:expiration-date [::sm/text {:max 250}]]])
(def initial-data
{:name "" :expiration-date "never"})
(mf/defc access-token-modal
{::mf/register modal/components
::mf/register-as :access-token}
[]
(let [form (fm/use-form
:initial initial-data
:schema schema:form)
created (mf/deref token-created-ref)
created? (mf/use-state false)
on-success
(mf/use-fn
(mf/deps created)
(fn [_]
(let [message (tr "dashboard.access-tokens.create.success")]
(st/emit! (du/fetch-access-tokens)
(ntf/success message)
(reset! created? true)))))
on-close
(mf/use-fn
(mf/deps created)
(fn [_]
(reset! created? false)
(st/emit! (modal/hide))))
on-error
(mf/use-fn
(fn [_]
(st/emit! (ntf/error (tr "errors.generic"))
(modal/hide))))
on-submit
(mf/use-fn
(fn [form]
(let [cdata (:clean-data @form)
mdata {:on-success (partial on-success form)
:on-error (partial on-error form)}
expiration (:expiration-date cdata)
params (cond-> {:name (:name cdata)
:perms (:perms cdata)}
(not= "never" expiration) (assoc :expiration expiration))]
(st/emit! (du/create-access-token
(with-meta params mdata))))))
copy-token
(mf/use-fn
(mf/deps created)
(fn [event]
(dom/prevent-default event)
(clipboard/to-clipboard (:token created))
(st/emit! (ntf/show {:level :info
:type :toast
:content (tr "dashboard.access-tokens.copied-success")
:timeout 7000}))))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-container)}
[:& fm/form {:form form :on-submit on-submit}
[:div {:class (stl/css :modal-header)}
[:h2 {:class (stl/css :modal-title)} (tr "modals.create-access-token.title")]
[:button {:class (stl/css :modal-close-btn)
:on-click on-close}
close-icon]]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :fields-row)}
[:& fm/input {:type "text"
:auto-focus? true
:form form
:name :name
:disabled @created?
:label (tr "modals.create-access-token.name.label")
:show-success? true
:placeholder (tr "modals.create-access-token.name.placeholder")}]]
[:div {:class (stl/css :fields-row)}
[:div {:class (stl/css :select-title)}
(tr "modals.create-access-token.expiration-date.label")]
[:& fm/select {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :key "never"}
{:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :key "720h"}
{:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :key "1440h"}
{:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :key "2160h"}
{:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :key "4320h"}]
:default "never"
:disabled @created?
:name :expiration-date}]
(when @created?
[:span {:class (stl/css :token-created-info)}
(if (:expires-at created)
(tr "dashboard.access-tokens.token-will-expire" (ct/format-inst (:expires-at created) "PPP"))
(tr "dashboard.access-tokens.token-will-not-expire"))])]
[:div {:class (stl/css :fields-row)}
(when @created?
[:div {:class (stl/css :custon-input-wrapper)}
[:input {:type "text"
:value (:token created "")
:class (stl/css :custom-input-token)
:read-only true}]
[:button {:title (tr "modals.create-access-token.copy-token")
:class (stl/css :copy-btn)
:on-click copy-token}
clipboard-icon]])
#_(when @created?
[:button {:class (stl/css :copy-btn)
:title (tr "modals.create-access-token.copy-token")
:on-click copy-token}
[:span {:class (stl/css :token-value)} (:token created "")]
[:span {:class (stl/css :icon)}
i/clipboard]])]]
[:div {:class (stl/css :modal-footer)}
[:div {:class (stl/css :action-buttons)}
(if @created?
[:input {:class (stl/css :cancel-button)
:type "button"
:value (tr "labels.close")
:on-click modal/hide!}]
[:*
[:input {:class (stl/css :cancel-button)
:type "button"
:value (tr "labels.cancel")
:on-click modal/hide!}]
[:> fm/submit-button*
{:large? false :label (tr "modals.create-access-token.submit-label")}]])]]]]]))
(mf/defc access-tokens-hero
[]
(let [on-click (mf/use-fn #(st/emit! (modal/show :access-token {})))]
[:div {:class (stl/css :access-tokens-hero)}
[:h2 {:class (stl/css :hero-title)} (tr "dashboard.access-tokens.personal")]
[:p {:class (stl/css :hero-desc)} (tr "dashboard.access-tokens.personal.description")]
[:button {:class (stl/css :hero-btn)
:on-click on-click}
(tr "dashboard.access-tokens.create")]]))
(mf/defc access-token-actions
[{:keys [on-delete]}]
(let [local (mf/use-state {:menu-open false})
show? (:menu-open @local)
options (mf/with-memo [on-delete]
[{:name (tr "labels.delete")
:id "access-token-delete"
:handler on-delete}])
menu-ref (mf/use-ref)
on-menu-close
(mf/use-fn #(swap! local assoc :menu-open false))
on-menu-click
(mf/use-fn
(fn [event]
(dom/prevent-default event)
(swap! local assoc :menu-open true)))
on-keydown
(mf/use-fn
(mf/deps on-menu-click)
(fn [event]
(when (kbd/enter? event)
(dom/stop-propagation event)
(on-menu-click event))))]
[:button {:class (stl/css :menu-btn)
:tab-index "0"
:ref menu-ref
:on-click on-menu-click
:on-key-down on-keydown}
menu-icon
[:> context-menu*
{:on-close on-menu-close
:show show?
:fixed true
:min-width true
:top "auto"
:left "auto"
:options options}]]))
(mf/defc access-token-item
{::mf/wrap [mf/memo]}
[{:keys [token] :as props}]
(let [expires-at (:expires-at token)
expires-txt (some-> expires-at (ct/format-inst "PPP"))
expired? (and (some? expires-at) (> (ct/now) expires-at))
delete-fn
(mf/use-fn
(mf/deps token)
(fn []
(let [params {:id (:id token)}
mdata {:on-success #(st/emit! (du/fetch-access-tokens))}]
(st/emit! (du/delete-access-token (with-meta params mdata))))))
on-delete
(mf/use-fn
(mf/deps delete-fn)
(fn []
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-acces-token.title")
:message (tr "modals.delete-acces-token.message")
:accept-label (tr "modals.delete-acces-token.accept")
:on-accept delete-fn}))))]
[:div {:class (stl/css :table-row)}
[:div {:class (stl/css :table-field :field-name)}
(str (:name token))]
[:div {:class (stl/css-case :expiration-date true
:expired expired?)}
(cond
(nil? expires-at) (tr "dashboard.access-tokens.no-expiration")
expired? (tr "dashboard.access-tokens.expired-on" expires-txt)
:else (tr "dashboard.access-tokens.expires-on" expires-txt))]
[:div {:class (stl/css :table-field :actions)}
[:& access-token-actions
{:on-delete on-delete}]]]))
(mf/defc access-tokens-page
[]
(let [tokens (mf/deref tokens-ref)]
(mf/with-effect []
(dom/set-html-title (tr "title.settings.access-tokens"))
(st/emit! (du/fetch-access-tokens)))
[:div {:class (stl/css :dashboard-access-tokens)}
[:& access-tokens-hero]
(if (empty? tokens)
[:div {:class (stl/css :access-tokens-empty)}
[:div (tr "dashboard.access-tokens.empty.no-access-tokens")]
[:div (tr "dashboard.access-tokens.empty.add-one")]]
[:div {:class (stl/css :dashboard-table)}
[:div {:class (stl/css :table-rows)}
(for [token tokens]
[:& access-token-item {:token token :key (:id token)}])]])]))

View File

@@ -1,202 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
// ACCESS TOKENS PAGE
.dashboard-access-tokens {
display: grid;
grid-template-rows: auto 1fr;
margin: deprecated.$s-80 auto deprecated.$s-120 auto;
gap: deprecated.$s-32;
width: deprecated.$s-800;
}
// hero
.access-tokens-hero {
display: grid;
grid-template-rows: auto auto 1fr;
gap: deprecated.$s-32;
width: deprecated.$s-500;
font-size: deprecated.$fs-14;
margin: deprecated.$s-16 auto 0 auto;
}
.hero-title {
@include deprecated.bigTitleTipography;
color: var(--title-foreground-color-hover);
}
.hero-desc {
color: var(--title-foreground-color);
margin-bottom: 0;
font-size: deprecated.$fs-14;
}
.hero-btn {
@extend .button-primary;
}
// table empty
.access-tokens-empty {
display: grid;
place-items: center;
align-content: center;
height: deprecated.$s-156;
max-width: deprecated.$s-1000;
width: 100%;
padding: deprecated.$s-32;
border: deprecated.$s-1 solid var(--panel-border-color);
border-radius: deprecated.$br-8;
color: var(--dashboard-list-text-foreground-color);
}
// Access tokens table
.dashboard-table {
height: fit-content;
}
.table-rows {
display: grid;
grid-auto-rows: deprecated.$s-64;
gap: deprecated.$s-16;
width: 100%;
height: 100%;
max-width: deprecated.$s-1000;
margin-top: deprecated.$s-16;
color: var(--title-foreground-color);
}
.table-row {
display: grid;
grid-template-columns: 43% 1fr auto;
align-items: center;
height: deprecated.$s-64;
width: 100%;
padding: 0 deprecated.$s-16;
border-radius: deprecated.$br-8;
background-color: var(--dashboard-list-background-color);
color: var(--dashboard-list-foreground-color);
}
.field-name {
@include deprecated.textEllipsis;
display: grid;
width: 43%;
min-width: deprecated.$s-300;
}
.expiration-date {
@include deprecated.flexCenter;
min-width: deprecated.$s-76;
width: fit-content;
height: deprecated.$s-24;
border-radius: deprecated.$br-8;
color: var(--dashboard-list-text-foreground-color);
}
.expired {
@include deprecated.headlineSmallTypography;
padding: 0 deprecated.$s-6;
color: var(--pill-foreground-color);
background-color: var(--status-widget-background-color-warning);
}
.actions {
position: relative;
}
.menu-icon {
@extend .button-icon;
stroke: var(--icon-foreground);
}
.menu-btn {
@include deprecated.buttonStyle;
}
// Create access token modal
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-container {
@extend .modal-container-base;
min-width: deprecated.$s-408;
}
.modal-header {
margin-bottom: deprecated.$s-24;
}
.modal-title {
@include deprecated.uppercaseTitleTipography;
color: var(--modal-title-foreground-color);
}
.modal-close-btn {
@extend .modal-close-btn-base;
}
.modal-content {
@include deprecated.flexColumn;
gap: deprecated.$s-24;
@include deprecated.bodySmallTypography;
margin-bottom: deprecated.$s-24;
}
.select-title {
@include deprecated.bodySmallTypography;
color: var(--modal-title-foreground-color);
}
.custon-input-wrapper {
@include deprecated.flexRow;
border-radius: deprecated.$br-8;
height: deprecated.$s-32;
background-color: var(--input-background-color);
}
.custom-input-token {
@extend .input-element;
@include deprecated.bodySmallTypography;
margin: 0;
flex-grow: 1;
&:focus {
outline: none;
border: deprecated.$s-1 solid var(--input-border-color-active);
}
}
.token-value {
@include deprecated.textEllipsis;
@include deprecated.bodySmallTypography;
flex-grow: 1;
}
.copy-btn {
@include deprecated.flexCenter;
@extend .button-secondary;
height: deprecated.$s-28;
width: deprecated.$s-28;
}
.clipboard-icon {
@extend .button-icon-small;
}
.token-created-info {
color: var(--modal-text-foreground-color);
}
.action-buttons {
@extend .modal-action-btns;
button {
@extend .modal-accept-btn;
}
}
.cancel-button {
@extend .modal-cancel-btn;
}

View File

@@ -0,0 +1,385 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.settings.integrations
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.schema :as sm]
[app.common.time :as ct]
[app.config :as cf]
[app.main.data.modal :as modal]
[app.main.data.notifications :as ntf]
[app.main.data.profile :as du]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.context-menu-a11y :refer [context-menu*]]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.controls.input :refer [input*]]
[app.main.ui.ds.controls.switch :refer [switch*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.ds.foundations.typography :as t]
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.main.ui.ds.notifications.shared.notification-pill :refer [notification-pill*]]
[app.main.ui.forms :as fc]
[app.util.clipboard :as clipboard]
[app.util.dom :as dom]
[app.util.forms :as fm]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def tokens-ref
(l/derived :access-tokens st/state))
(def token-created-ref
(l/derived :access-token-created st/state))
(def ^:private schema:form
[:map {:title "AccessTokenForm"}
[:name [::sm/text {:max 250}]]
[:expiration-date [::sm/text {:max 250}]]])
(def initial-data
{:name ""
:expiration-date "never"})
(mf/defc access-token-modal*
{::mf/register modal/components
::mf/register-as :access-token}
[]
(let [form (fm/use-form
:initial initial-data
:schema schema:form)
created (mf/deref token-created-ref)
created? (mf/use-state false)
on-success
(mf/use-fn
(mf/deps created)
(fn [_]
(let [message (tr "dashboard.access-tokens.create.success")]
(st/emit! (du/fetch-access-tokens)
(ntf/success message)
(reset! created? true)))))
on-close
(mf/use-fn
(mf/deps created)
(fn [_]
(reset! created? false)
(st/emit! (modal/hide))))
on-error
(mf/use-fn
(fn [_]
(st/emit! (ntf/error (tr "errors.generic"))
(modal/hide))))
on-submit
(mf/use-fn
(fn [form]
(let [cdata (:clean-data @form)
mdata {:on-success (partial on-success form)
:on-error (partial on-error form)}
expiration (:expiration-date cdata)
params (cond-> {:name (:name cdata)
:perms (:perms cdata)}
(not= "never" expiration) (assoc :expiration expiration))]
(st/emit! (du/create-access-token
(with-meta params mdata))))))
copy-to-clipboard
(mf/use-fn
(mf/deps created)
(fn [event]
(dom/prevent-default event)
(clipboard/to-clipboard (:token created))
(st/emit! (ntf/show {:level :info
:type :toast
:content (tr "dashboard.access-tokens.copied-success")
:timeout 7000}))))]
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-container)}
[:> icon-button* {:variant "ghost"
:class (stl/css :modal-close-button)
:aria-label (tr "labels.close")
:on-click on-close
:icon i/close}]
(if @created?
[:*
[:> text* {:as "h2"
:typography t/headline-large
:class (stl/css :modal-header)}
(tr "modals.create-access-token.title")]
[:> notification-pill* {:level :info
:type :context}
"This is unique token is non-recuperable. If you lose it, you will need to create a new one."]
[:div {:class (stl/css :modal-content)}
[:div {:class (stl/css :modal-input-wrapper)}
[:> input* {:type "text"
:default-value (:token created "")
:read-only true}]
[:> icon-button* {:variant "secondary"
:aria-label (tr "modals.create-access-token.copy-token")
:on-click copy-to-clipboard
:icon i/clipboard}]]
[:> text* {:as "div"
:typography t/body-small
:class (stl/css :modal-token-created-info)}
(if (:expires-at created)
(tr "dashboard.access-tokens.token-will-expire" (ct/format-inst (:expires-at created) "PPP"))
(tr "dashboard.access-tokens.token-will-not-expire"))]]
[:div {:class (stl/css :modal-footer)}
[:> button* {:variant "secondary"
:on-click modal/hide!}
(tr "labels.close")]]]
;; ------------------
[:*
[:> fc/form* {:form form
:class (stl/css :modal-form)
:on-submit on-submit}
[:> text* {:as "h2"
:typography t/headline-large
:class (stl/css :modal-header)}
(tr "modals.create-access-token.title")]
[:div {:class (stl/css :modal-content)}
[:> fc/form-input* {:type "text"
:auto-focus? true
:form form
:name :name
:disabled false
:label (tr "modals.create-access-token.name.label")
:show-success? true
:placeholder (tr "modals.create-access-token.name.placeholder")}]]
[:div {:class (stl/css :modal-content)}
[:> text* {:as "label"
:typography t/body-small
:for :expiration-date
:class (stl/css :modal-select-label)}
(tr "modals.create-access-token.expiration-date.label")]
[:> fc/form-select* {:options [{:label (tr "dashboard.access-tokens.expiration-never") :value "never" :id "never"}
{:label (tr "dashboard.access-tokens.expiration-30-days") :value "720h" :id "720h"}
{:label (tr "dashboard.access-tokens.expiration-60-days") :value "1440h" :id "1440h"}
{:label (tr "dashboard.access-tokens.expiration-90-days") :value "2160h" :id "2160h"}
{:label (tr "dashboard.access-tokens.expiration-180-days") :value "4320h" :id "4320h"}]
:default-selected "never"
:name :expiration-date}]]
[:div {:class (stl/css :modal-footer)}
[:> button* {:variant "secondary"
:on-click modal/hide!}
(tr "labels.cancel")]
[:> fc/form-submit* {:variant "primary"}
(tr "modals.create-access-token.submit-label")]]]])]]))
(mf/defc token-actions*
{::mf/private true}
[{:keys [on-delete]}]
(let [local (mf/use-state {:menu-open false})
show? (:menu-open @local)
options (mf/with-memo [on-delete]
[{:name (tr "labels.delete")
:id "access-token-delete"
:handler on-delete}])
menu-ref (mf/use-ref)
on-menu-close
(mf/use-fn
#(swap! local assoc :menu-open false))
on-menu-click
(mf/use-fn
(fn [event]
(dom/prevent-default event)
(swap! local assoc :menu-open true)))
on-keydown
(mf/use-fn
(mf/deps on-menu-click)
(fn [event]
(when (kbd/enter? event)
(dom/stop-propagation event)
(on-menu-click event))))]
[:div {:class (stl/css :item-actions)}
[:> icon-button* {:variant "ghost"
:class (stl/css :item-button)
:ref menu-ref
:aria-pressed show?
:aria-label (tr "labels.options")
:on-click on-menu-click
:on-key-down on-keydown
:icon i/menu}]
[:> context-menu* {:on-close on-menu-close
:show show?
:fixed true
:min-width true
:top "auto"
:left "auto"
:options options}]]))
(mf/defc token-item*
{::mf/private true
::mf/wrap [mf/memo]}
[{:keys [token]}]
(let [expires-at (:expires-at token)
expires-txt (some-> expires-at (ct/format-inst "PPP"))
expired? (and (some? expires-at) (> (ct/now) expires-at))
delete-fn
(mf/use-fn
(mf/deps token)
(fn []
(let [params {:id (:id token)}
mdata {:on-success #(st/emit! (du/fetch-access-tokens))}]
(st/emit! (du/delete-access-token (with-meta params mdata))))))
on-delete
(mf/use-fn
(mf/deps delete-fn)
(fn []
(st/emit! (modal/show {:type :confirm
:title (tr "modals.delete-acces-token.title")
:message (tr "modals.delete-acces-token.message")
:accept-label (tr "modals.delete-acces-token.accept")
:on-accept delete-fn}))))]
[:div {:class (stl/css :item)}
[:> text* {:as "div"
:typography t/body-medium
:title (:name token)
:class (stl/css :item-title)}
(:name token)]
[:> text* {:as "div"
:typography t/body-small
:class (stl/css-case :item-subtitle true
:warning expired?)}
(cond
(nil? expires-at) (tr "dashboard.access-tokens.no-expiration")
expired? (tr "dashboard.access-tokens.expired-on" expires-txt)
:else (tr "dashboard.access-tokens.expires-on" expires-txt))]
[:> token-actions* {:on-delete on-delete}]]))
(mf/defc mcp-server-section*
{::mf/private true}
[]
(let [profile (mf/deref refs/profile)
active? (d/nilv (-> profile :props :mcp-status) false)
handle-mcp-status-change
(mf/use-fn
(fn [value]
(st/emit! (du/update-profile-props {:mcp-status value}))))]
[:section {:class (stl/css :mcp)}
[:div
[:> heading* {:level 2
:typography t/title-medium
:class (stl/css :mcp-title)}
"MCP Server"]
[:> text* {:as "p"
:typography t/body-medium
:class (stl/css :mcp-description)}
"The Penpot MCP Server enables MCP clients to interact directly with Penpot design files."]]
[:div
[:> text* {:as "h3"
:typography t/headline-small
:class (stl/css :mcp-headline)}
"Status"]
[:> switch* {:label (if active? "Enabled" "Disabled")
:default-checked active?
:on-change handle-mcp-status-change}]]
[:div
[:> text* {:as "h3"
:typography t/headline-small
:class (stl/css :mcp-headline)}
"MCP keys"]
[:> button* {:variant "primary"}
"Regenerate MCP key"]]]))
(mf/defc access-tokens-section*
{::mf/private true}
[]
(let [tokens (mf/deref tokens-ref)
on-click
(mf/use-fn
#(st/emit! (modal/show :access-token {})))]
(mf/with-effect []
(dom/set-html-title (tr "title.settings.access-tokens"))
(st/emit! (du/fetch-access-tokens)))
[:section {:class (stl/css :access-tokens-section)}
[:> heading* {:level 2
:typography t/title-medium
:class (stl/css :mcp-title)}
(tr "dashboard.access-tokens.personal")]
[:> text* {:as "p"
:typography t/body-medium
:class (stl/css :mcp-description)}
(tr "dashboard.access-tokens.personal.description")]
[:> button* {:variant "primary"
:on-click on-click}
(tr "dashboard.access-tokens.create")]
(if (empty? tokens)
[:div {:class (stl/css :frame)}
[:> text* {:as "div"
:typography t/body-medium
:class (stl/css :frame-text-center)}
[:div (tr "dashboard.access-tokens.empty.no-access-tokens")]
[:div (tr "dashboard.access-tokens.empty.add-one")]]]
[:div {:class (stl/css :list)}
(for [token tokens]
[:> token-item* {:key (:id token)
:token token}])])]))
(mf/defc integrations-page*
[]
[:div {:class (stl/css :integrations)}
[:> heading* {:level 1
:typography t/title-large
:class (stl/css :integrations-title)}
"Integrations"]
(when (contains? cf/flags :mcp-server)
[:> mcp-server-section*])
(when (and (contains? cf/flags :mcp-server)
(contains? cf/flags :access-tokens))
[:hr {:class (stl/css :separator)}])
(when (contains? cf/flags :access-tokens)
[:> access-tokens-section*])])

View File

@@ -0,0 +1,160 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
@use "ds/_borders.scss" as *;
@use "ds/_sizes.scss" as *;
@use "ds/spacing.scss" as *;
@use "ds/mixins.scss" as *;
.integrations {
display: grid;
grid-template-rows: auto 1fr;
margin: $sz-88 auto $sz-120 auto;
gap: $sz-32;
width: $sz-500;
}
.integrations-title {
color: var(--color-foreground-primary);
}
.access-tokens-section {
display: grid;
grid-template-rows: auto auto 1fr;
gap: var(--sp-s);
}
.mcp {
display: flex;
flex-direction: column;
gap: var(--sp-l);
}
.mcp-title {
color: var(--color-foreground-primary);
margin: var(--sp-s) 0;
}
.mcp-headline {
color: var(--color-foreground-primary);
}
.mcp-description {
color: var(--color-foreground-secondary);
}
.separator {
border: 1px solid var(--color-background-quaternary);
margin: 0;
}
.frame {
border: 1px solid var(--color-background-quaternary);
padding: var(--sp-m);
border-radius: $br-8;
}
.frame-text-center {
text-align: center;
color: var(--color-foreground-secondary);
}
.list {
display: grid;
grid-auto-rows: $sz-64;
gap: var(--sp-m);
}
.item {
display: grid;
grid-template-columns: 40% 1fr auto;
align-items: center;
background-color: var(--color-background-tertiary);
border-radius: $br-8;
}
.item-title {
@include textEllipsis;
align-content: center;
block-size: $sz-64;
padding: 0 var(--sp-l);
color: var(--color-foreground-primary);
}
.item-subtitle {
align-content: center;
block-size: $sz-64;
padding: 0 var(--sp-l);
color: var(--color-foreground-secondary);
&.warning {
padding: 0 var(--sp-m);
block-size: $sz-32;
inline-size: fit-content;
color: var(--pill-foreground-color);
background-color: var(--color-background-warning);
border: 1px solid var(--color-accent-warning);
border-radius: $br-8;
}
}
.item-actions {
position: relative;
}
.item-button {
block-size: $sz-64;
inline-size: $sz-48;
border-radius: 0 var(--sp-s) var(--sp-s) 0;
}
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-container {
@extend .modal-container-base;
min-width: deprecated.$s-408;
position: relative;
}
.modal-form {
display: flex;
flex-direction: column;
gap: var(--sp-xxxl);
}
.modal-close-button {
position: absolute;
top: var(--sp-s);
right: var(--sp-s);
}
.modal-header {
color: var(--color-foreground-primary);
}
.modal-footer {
display: flex;
justify-content: right;
gap: var(--sp-s);
}
.modal-select-label {
color: var(--color-foreground-primary);
}
.modal-input-wrapper {
display: flex;
align-items: center;
justify-content: center;
}
.modal-token-created-info {
color: var(--color-foreground-secondary);
}

View File

@@ -43,8 +43,8 @@
(def ^:private go-settings-subscription
#(st/emit! (rt/nav :settings-subscription)))
(def ^:private go-settings-access-tokens
#(st/emit! (rt/nav :settings-access-tokens)))
(def ^:private go-settings-integrations
#(st/emit! (rt/nav :settings-integrations)))
(def ^:private go-settings-notifications
#(st/emit! (rt/nav :settings-notifications)))
@@ -66,7 +66,7 @@
options? (= section :settings-options)
feedback? (= section :settings-feedback)
subscription? (= section :settings-subscription)
access-tokens? (= section :settings-access-tokens)
integrations? (= section :settings-integrations)
notifications? (= section :settings-notifications)
team-id (or (dtm/get-last-team-id)
(:default-team-id profile))
@@ -115,12 +115,13 @@
:data-testid "settings-subscription"}
[:span {:class (stl/css :element-title)} (tr "subscription.labels")]])
(when (contains? cf/flags :access-tokens)
[:li {:class (stl/css-case :current access-tokens?
(when (or (contains? cf/flags :access-tokens)
(contains? cf/flags :mcp-server))
[:li {:class (stl/css-case :current integrations?
:settings-item true)
:on-click go-settings-access-tokens
:data-testid "settings-access-tokens"}
[:span {:class (stl/css :element-title)} (tr "labels.access-tokens")]])
:on-click go-settings-integrations
:data-testid "settings-integrations"}
[:span {:class (stl/css :element-title)} (tr "labels.integrations")]])
[:hr {:class (stl/css :sidebar-separator)}]

View File

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

View File

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

View File

@@ -46,7 +46,7 @@
{::mf/private true}
[{:keys [tokens-lib selected-token-set-id]}]
(let [selected-token-set
(mf/with-memo [tokens-lib selected-token-set-id]
(mf/with-memo [tokens-lib]
(when selected-token-set-id
(some-> tokens-lib (ctob/get-set selected-token-set-id))))
@@ -62,20 +62,18 @@
[:div {:class (stl/css :sets-header-container)}
[:> text* {:as "span"
:typography "headline-small"
:class (stl/css :sets-header)
:data-testid "active-token-set-title"}
:class (stl/css :sets-header)}
(tr "workspace.tokens.tokens-section-title" (ctob/get-name selected-token-set))]
(when (and (some? selected-token-set-id)
(not (token-set-active? (ctob/get-name selected-token-set))))
[:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")}
[:div {:class (stl/css :sets-header-status) :title (tr "workspace.tokens.inactive-set-description")}
;; NOTE: when no set in tokens-lib, the selected-token-set-id
;; will be `nil`, so for properly hide the inactive message we
;; check that at least `selected-token-set-id` has a value
(when (and (some? selected-token-set-id)
(not (token-set-active? (ctob/get-name selected-token-set))))
[:*
[:> icon* {:class (stl/css :sets-header-status-icon) :icon-id i/eye-off}]
[:> text* {:as "span" :typography "body-small" :class (stl/css :sets-header-status-text)}
(tr "workspace.tokens.inactive-set")]]])]))
(tr "workspace.tokens.inactive-set")]])]]))
(mf/defc tokens-section*
{::mf/private true}
@@ -160,7 +158,7 @@
[:& token-context-menu]
[:> token-node-context-menu* {:on-delete-node delete-node}]
[:> selected-set-info* {:tokens-lib tokens-lib
[:& selected-set-info* {:tokens-lib tokens-lib
:selected-token-set-id selected-token-set-id}]
(for [type filled-group]

View File

@@ -172,6 +172,22 @@
schema-1 (c/get params :schema-1)
this? (c/get params :this false)
decode-expr
(c/get params :decode/fn)
decode-options
(c/get params :decode/options)
decode-options
(if (and decode-expr decode-options)
(do
(println "WARN: decode/fn and decode/options are excluding, ignoring decode/options")
nil)
decode-options)
decode-expr
(or decode-expr 'app.common.json/->clj)
fn-sym
(-> (gensym (str "internal-fn-" (str/slug pname) "-"))
(with-meta {:tag 'function}))
@@ -180,9 +196,20 @@
(-> (gensym (str "coercer-fn-" (str/slug pname) "-"))
(with-meta {:tag 'function}))
decode-sym
(-> (gensym (str "decode-fn-" (str/slug pname) "-"))
(with-meta {:tag 'function}))
schema-sym
(-> (gensym (str "schema-" (str/slug pname) "-"))
(with-meta {:tag 'function}))
wrap-sym
(-> (gensym (str "wrap-fn-" (str/slug pname) "-"))
(with-meta {:tag 'function}))]
(with-meta {:tag 'function}))
val-sym
(gensym (str "val-" (str/slug pname) "-"))]
(concat
(when wrap
@@ -198,20 +225,66 @@
get-expr)])
(when set-expr
[(make-sym pname "set-fn")
(if this?
`(fn [v#]
(let [~this-sym (~'js* "this")
~fn-sym ~set-expr]
(.call ~fn-sym ~this-sym ~this-sym v#)))
set-expr)])
(concat
(when (and schema-n (not (list? schema-n)))
[coercer-sym `(sm/coercer ~schema-n)])
(when (and schema-n (list? schema-n))
[schema-sym schema-n])
[decode-sym decode-expr]
[(make-sym pname "set-fn")
(if this?
`(fn [~val-sym]
(let [~@(if (and schema-n (list? schema-n))
[schema-sym `(~schema-sym ~val-sym)
coercer-sym `(sm/coercer ~schema-sym)]
[])
~this-sym (~'js* "this")
~fn-sym ~set-expr
~val-sym ~(if schema-n
(if decode-expr
`(~decode-sym ~val-sym)
`(~decode-sym ~val-sym ~decode-options))
val-sym)
~val-sym ~(if schema-n
`(~coercer-sym ~val-sym)
val-sym)]
(.call ~fn-sym ~this-sym ~this-sym ~val-sym)))
`(fn [~val-sym]
(let [~@(if (and schema-n (list? schema-n))
[schema-sym `(~schema-sym ~val-sym)
coercer-sym `(sm/coercer ~schema-sym)]
[])
~this-sym (~'js* "this")
~fn-sym ~set-expr
~val-sym ~(if schema-n
(if decode-expr
`(~decode-sym ~val-sym)
`(~decode-sym ~val-sym ~decode-options))
val-sym)
~val-sym ~(if schema-n
`(~coercer-sym ~val-sym)
val-sym)]
(.call ~fn-sym ~this-sym ~val-sym))))]))
(when fn-expr
(concat
(when schema-1
[coercer-sym `(sm/coercer ~schema-1)])
(when schema-n
[coercer-sym `(sm/coercer ~schema-n)])
(cond
(and schema-n (not (list? schema-n)))
[coercer-sym `(sm/coercer ~schema-n)]
(and schema-n (list? schema-n))
[schema-sym schema-n]
(and schema-1 (not (list? schema-1)))
[coercer-sym `(sm/coercer ~schema-1)]
(and schema-1 (list? schema-1))
[schema-sym schema-1])
[decode-sym decode-expr]
[(make-sym pname "get-fn")
`(fn []
@@ -222,17 +295,42 @@
`(.bind ~fn-sym ~this-sym))
~@(if schema-1
[fn-sym `(fn* [param#]
(let [param# (json/->clj param#)
param# (~coercer-sym param#)]
(~fn-sym param#)))]
[fn-sym `(fn* [~val-sym]
(let [~val-sym
~(if decode-expr
`(~decode-sym ~val-sym)
`(~decode-sym ~val-sym ~decode-options))
~@(if (or (and schema-n (list? schema-n))
(and schema-1 (list? schema-1)))
[schema-sym `(~schema-sym ~val-sym)
coercer-sym `(sm/coercer ~schema-sym)]
[])
~val-sym
(~coercer-sym ~val-sym)]
(~fn-sym ~val-sym)))]
[])
~@(if schema-n
[fn-sym `(fn* []
(let [params# (into-array (cljs.core/js-arguments))
params# (mfu/bean params#)
params# (~coercer-sym params#)]
(apply ~fn-sym params#)))]
(let [~val-sym
(into-array (cljs.core/js-arguments))
~val-sym
~(if decode-expr
`(~decode-sym ~val-sym)
`(~decode-sym ~val-sym ~decode-options))
~@(if (or (and schema-n (list? schema-n))
(and schema-1 (list? schema-1)))
[schema-sym `(~schema-sym ~val-sym)
coercer-sym `(sm/coercer ~schema-sym)]
[])
~val-sym
(~coercer-sym ~val-sym)]
(apply ~fn-sym ~val-sym)))]
[])
~@(if wrap
[fn-sym `(~wrap-sym ~fn-sym)]

View File

@@ -7,7 +7,7 @@
To start the development environment run:
```sh
yarn run dev
pnpm run dev
```
### Testing
@@ -15,14 +15,14 @@ yarn run dev
For running unit tests and running coverage:
```sh
yarn run test
yarn run coverage
pnpm run test
pnpm run coverage
```
> If you want, you can run the [vitest](https://vitest.dev/) UI by running:
```sh
yarn run test:ui
pnpm run test:ui
```
## How to build it
@@ -30,7 +30,7 @@ yarn run test:ui
The editor can be built and updated inside Penpot using the following command:
```sh
PENPOT_SOURCE_PATH=/path/to/penpot/repository yarn build:update
PENPOT_SOURCE_PATH=/path/to/penpot/repository pnpm build:update
```
This command is going to search for the file located in `frontend/src/app/main/ui/workspace/shapes/text/new_editor/TextEditor.js` and update it.

View File

@@ -1,31 +1,31 @@
{
"name": "@penpot/text-editor",
"private": true,
"version": "0.0.0",
"version": "1.0.0",
"type": "module",
"main": "src/editor/TextEditor.js",
"scripts": {
"dev": "vite",
"coverage": "vitest run --coverage",
"wasm:update": "cp ../resources/public/js/render_wasm.wasm ./src/wasm/render_wasm.wasm && cp ../resources/public/js/render_wasm.js ./src/wasm/render_wasm.js",
"wasm:update": "mkdir -p src/wasm && cp ../resources/public/js/render-wasm.wasm ./src/wasm/render-wasm.wasm && cp ../resources/public/js/render-wasm.js ./src/wasm/render-wasm.js",
"test": "vitest --run",
"fmt:js": "yarn run prettier -c src/**/*.js",
"fmt:js": "pnpx prettier -c src/**/*.js",
"test:watch": "vitest",
"test:watch:ui": "vitest --ui",
"test:watch:e2e": "vitest --browser"
},
"devDependencies": {
"@playwright/test": "^1.45.1",
"@types/node": "^20.14.10",
"@types/node": "^25.0.3",
"@vitest/browser": "^1.6.0",
"@vitest/coverage-v8": "^1.6.0",
"@vitest/ui": "^1.6.0",
"esbuild": "^0.24.0",
"jsdom": "^25.0.0",
"esbuild": "^0.27.2",
"jsdom": "^27.4.0",
"playwright": "^1.45.1",
"prettier": "^3.3.3",
"prettier": "^3.7.4",
"vite": "^5.3.1",
"vitest": "^1.6.0"
},
"packageManager": "yarn@4.3.1"
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6"
}

View File

@@ -1,7 +1,7 @@
import "./style.css";
import "./fonts.css";
import "./editor/TextEditor.css";
import initWasmModule from "./wasm/render_wasm.js";
import initWasmModule from "./wasm/render-wasm.js";
import { UUID } from "./playground/uuid.js";
import { Rect, Point } from "./playground/geom.js";
import { WASMModuleWrapper } from "./playground/wasm.js";

View File

File diff suppressed because it is too large Load Diff

View File

@@ -433,9 +433,6 @@ msgstr "(copy)"
msgid "dashboard.create-new-team"
msgstr "Create new team"
msgid "dashboard.create-new-org"
msgstr "Create new org"
#: src/app/main/ui/workspace/main_menu.cljs:661
msgid "dashboard.create-version-menu"
msgstr "Pin this version"
@@ -2471,6 +2468,10 @@ msgstr "Info"
msgid "labels.installed-fonts"
msgstr "Installed fonts"
#: src/app/main/ui/settings/sidebar.cljs:123
msgid "labels.integrations"
msgstr "Integrations"
#: src/app/main/ui/static.cljs:396
msgid "labels.internal-error.desc-message-first"
msgstr "Something bad happened."

View File

@@ -442,9 +442,6 @@ msgstr "(copia)"
msgid "dashboard.create-new-team"
msgstr "Crear nuevo equipo"
msgid "dashboard.create-new-org"
msgstr "Crear nueva organización"
#: src/app/main/ui/workspace/main_menu.cljs:661
msgid "dashboard.create-version-menu"
msgstr "Guardar esta versión"
@@ -2442,6 +2439,10 @@ msgstr "Información"
msgid "labels.installed-fonts"
msgstr "Fuentes instaladas"
#: src/app/main/ui/settings/sidebar.cljs:123
msgid "labels.integrations"
msgstr "Integraciones"
#: src/app/main/ui/static.cljs:396
msgid "labels.internal-error.desc-message-first"
msgstr "Ha ocurrido algo extraño."

View File

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@ all the necessary data to import them into a Penpot application).
### How to install
```bash
yarn add @penpot/library
pnpm add @penpot/library
```
### Example of use

View File

@@ -3,15 +3,12 @@
"version": "1.2.0-RC1",
"license": "MPL-2.0",
"author": "Kaleidos INC",
"packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8",
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/penpot/penpot.git"
},
"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"
},
"files": [
"target/library/penpot.js",
"target/library/penpot.js.map"
@@ -27,18 +24,18 @@
},
"scripts": {
"clear:shadow-cache": "rm -rf .shadow-cljs",
"build": "yarn run clear:shadow-cache && clojure -M:dev:shadow-cljs release library",
"build": "pnpm run clear:shadow-cache && clojure -M:dev:shadow-cljs release library",
"build:bundle": "./scripts/build",
"fmt:clj": "cljfmt fix --parallel=true src/ test/",
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",
"lint:clj": "clj-kondo --parallel --lint src/",
"test": "node --test",
"watch:test": "node --test --watch",
"watch": "yarn run clear:shadow-cache && clojure -M:dev:shadow-cljs watch library"
"watch": "pnpm run clear:shadow-cache && clojure -M:dev:shadow-cljs watch library"
},
"devDependencies": {
"@types/node": "^22.18.12",
"@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",
"@zip.js/zip.js": "2.8.11",
"concurrently": "^9.2.1",
"date-fns": "^4.1.0",
"nodemon": "^3.1.10",

View File

@@ -0,0 +1,17 @@
diff --git a/lib/zip-core-base.js b/lib/zip-core-base.js
index 142155c8c3ab1a6caa7c370e8a2931a954556ca9..61b0fe6efb91312f437750e0002218f218afad8b 100644
--- a/lib/zip-core-base.js
+++ b/lib/zip-core-base.js
@@ -28,12 +28,6 @@
import { configure } from "./core/configuration.js";
-try {
- configure({ baseURI: import.meta.url });
-} catch {
- // ignored
-}
-
export * from "./zip-core-reader.js";
export * from "./zip-core-writer.js";
export {

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