mirror of
https://github.com/penpot/penpot.git
synced 2026-01-28 08:13:29 -05:00
Compare commits
44 Commits
eva-extrac
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ca82821c1 | ||
|
|
a90f672a5e | ||
|
|
f76598f638 | ||
|
|
eacc033567 | ||
|
|
71c349479f | ||
|
|
fda31624c1 | ||
|
|
7f640569bd | ||
|
|
91f1323802 | ||
|
|
dbd4a2366f | ||
|
|
cbb6d098a7 | ||
|
|
b6f5000d1c | ||
|
|
0527124f2f | ||
|
|
faf91ac70d | ||
|
|
9ca76c745f | ||
|
|
89935e2174 | ||
|
|
7f27e0326d | ||
|
|
9c539dfb2f | ||
|
|
50a4cf8b99 | ||
|
|
f5996a7235 | ||
|
|
e8fd4698c9 | ||
|
|
0ab126748f | ||
|
|
71a5ab9913 | ||
|
|
61969f3eb5 | ||
|
|
bd2ef8057e | ||
|
|
9808b6ca57 | ||
|
|
2523096fdd | ||
|
|
de41cb5488 | ||
|
|
8e63c4e3e8 | ||
|
|
b40ccaf030 | ||
|
|
7d3ac38749 | ||
|
|
d5abc52dac | ||
|
|
8d1bc6c50c | ||
|
|
3112b240a0 | ||
|
|
56fd66b91a | ||
|
|
3b96eb5476 | ||
|
|
2a7c24f6fd | ||
|
|
947aa22dee | ||
|
|
1ce0b60e3d | ||
|
|
5209a8b423 | ||
|
|
f4f4f5bbb5 | ||
|
|
ef80901400 | ||
|
|
5306bed548 | ||
|
|
92a319ddd1 | ||
|
|
68a6d4c9a8 |
@@ -45,6 +45,15 @@
|
|||||||
:potok/reify-type
|
:potok/reify-type
|
||||||
{:level :error}
|
{:level :error}
|
||||||
|
|
||||||
|
:redundant-primitive-coercion
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
|
:unused-excluded-var
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
|
:unresolved-excluded-var
|
||||||
|
{:level :off}
|
||||||
|
|
||||||
:missing-protocol-method
|
:missing-protocol-method
|
||||||
{:level :off}
|
{:level :off}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
:remove-multiple-non-indenting-spaces? false
|
:remove-multiple-non-indenting-spaces? false
|
||||||
:remove-surrounding-whitespace? true
|
:remove-surrounding-whitespace? true
|
||||||
:remove-consecutive-blank-lines? false
|
:remove-consecutive-blank-lines? false
|
||||||
|
:indent-line-comments? true
|
||||||
|
:parallel? true
|
||||||
|
:align-form-columns? false
|
||||||
|
;; :align-map-columns? false
|
||||||
|
;; :align-single-column-lines? false
|
||||||
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
:extra-indents {rumext.v2/fnc [[:inner 0]]
|
||||||
cljs.test/async [[:inner 0]]
|
cljs.test/async [[:inner 0]]
|
||||||
promesa.exec/thread [[:inner 0]]
|
promesa.exec/thread [[:inner 0]]
|
||||||
|
|||||||
21
.github/workflows/build-nitrate-module.yml
vendored
21
.github/workflows/build-nitrate-module.yml
vendored
@@ -1,21 +0,0 @@
|
|||||||
name: _NITRATE MODULE
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '36 5-20 * * 1-5'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-bundle:
|
|
||||||
uses: ./.github/workflows/build-bundle.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
gh_ref: "nitrate-module"
|
|
||||||
build_wasm: "yes"
|
|
||||||
build_storybook: "yes"
|
|
||||||
|
|
||||||
build-docker:
|
|
||||||
needs: build-bundle
|
|
||||||
uses: ./.github/workflows/build-docker.yml
|
|
||||||
secrets: inherit
|
|
||||||
with:
|
|
||||||
gh_ref: "nitrate-module"
|
|
||||||
28
.github/workflows/plugins-deploy-api-doc.yml
vendored
28
.github/workflows/plugins-deploy-api-doc.yml
vendored
@@ -7,11 +7,11 @@ on:
|
|||||||
- staging
|
- staging
|
||||||
- main
|
- main
|
||||||
paths:
|
paths:
|
||||||
- "plugins/libs/plugin-types/index.d.ts"
|
- 'plugins/libs/plugin-types/index.d.ts'
|
||||||
- "plugins/libs/plugin-types/REAME.md"
|
- 'plugins/libs/plugin-types/REAME.md'
|
||||||
- "plugins/tools/typedoc.css"
|
- 'plugins/tools/typedoc.css'
|
||||||
- "plugins/CHANGELOG.md"
|
- 'plugins/CHANGELOG.md'
|
||||||
- "plugins/wrangler-penpot-plugins-api-doc.toml"
|
- 'plugins/wrangler-penpot-plugins-api-doc.toml'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
gh_ref:
|
gh_ref:
|
||||||
@@ -86,12 +86,24 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
REF="${{ steps.vars.outputs.gh_ref }}"
|
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||||
case "$REF" in
|
case "$REF" in
|
||||||
main) echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV ;;
|
main)
|
||||||
staging) echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV ;;
|
echo "WORKER_NAME=penpot-plugins-api-doc-pro" >> $GITHUB_ENV
|
||||||
develop) echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV ;;
|
echo "WORKER_URI=doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-api-doc-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-api-doc-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-api-doc.toml
|
||||||
|
|
||||||
- name: Deploy to Cloudflare Workers
|
- name: Deploy to Cloudflare Workers
|
||||||
uses: cloudflare/wrangler-action@v3
|
uses: cloudflare/wrangler-action@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
123
.github/workflows/plugins-deploy-styles-doc.yml
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
name: Plugins/styles-doc deployer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'plugins/apps/example-styles/**'
|
||||||
|
- 'plugins/libs/plugins-styles/**'
|
||||||
|
- 'plugins/wrangler-penpot-plugins-styles-doc.toml'
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
gh_ref:
|
||||||
|
description: 'Name of the branch'
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
default: 'develop'
|
||||||
|
options:
|
||||||
|
- develop
|
||||||
|
- staging
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Extract some useful variables
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ steps.vars.outputs.gh_ref }}
|
||||||
|
|
||||||
|
# START: Setup Node and PNPM enabling cache
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
|
||||||
|
- name: Enable PNPM
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
corepack enable;
|
||||||
|
corepack install;
|
||||||
|
|
||||||
|
- name: Get pnpm store path
|
||||||
|
id: pnpm-store
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Cache pnpm store
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
|
||||||
|
key: ${{ runner.os }}-pnpm-${{ hashFiles('plugins/pnpm-lock.yaml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pnpm-
|
||||||
|
# END: Setup Node and PNPM enabling cache
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
working-directory: ./plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pnpm install --no-frozen-lockfile;
|
||||||
|
pnpm add -D -w wrangler@latest;
|
||||||
|
|
||||||
|
- name: Build styles
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: npx nx run example-styles:build
|
||||||
|
|
||||||
|
- name: Select Worker name
|
||||||
|
run: |
|
||||||
|
REF="${{ steps.vars.outputs.gh_ref }}"
|
||||||
|
case "$REF" in
|
||||||
|
main)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-pro" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.penpot.app" >> $GITHUB_ENV ;;
|
||||||
|
staging)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-pre" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
develop)
|
||||||
|
echo "WORKER_NAME=penpot-plugins-styles-doc-hourly" >> $GITHUB_ENV
|
||||||
|
echo "WORKER_URI=styles-doc.plugins.hourly.penpot.dev" >> $GITHUB_ENV ;;
|
||||||
|
*) echo "Unsupported branch ${REF}" && exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Set the custom url
|
||||||
|
working-directory: plugins
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sed -i "s/WORKER_URI/${{ env.WORKER_URI }}/g" wrangler-penpot-plugins-styles-doc.toml
|
||||||
|
|
||||||
|
- 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 wrangler-penpot-plugins-styles-doc.toml --name ${{ env.WORKER_NAME }}
|
||||||
|
|
||||||
|
- name: Notify Mattermost
|
||||||
|
if: failure()
|
||||||
|
uses: mattermost/action-mattermost-notify@master
|
||||||
|
with:
|
||||||
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
|
TEXT: |
|
||||||
|
❌ 🧩💅 *[PENPOT PLUGINS] Error deploying Styles documentation.*
|
||||||
|
📄 Triggered from ref: `${{ inputs.gh_ref }}`
|
||||||
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
@infra
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -21,6 +21,7 @@
|
|||||||
.rebel_readline_history
|
.rebel_readline_history
|
||||||
.repl
|
.repl
|
||||||
.shadow-cljs
|
.shadow-cljs
|
||||||
|
.pnpm-store/
|
||||||
/*.jpg
|
/*.jpg
|
||||||
/*.md
|
/*.md
|
||||||
/*.png
|
/*.png
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
/backend/resources/public/media
|
/backend/resources/public/media
|
||||||
/backend/target/
|
/backend/target/
|
||||||
/backend/experiments
|
/backend/experiments
|
||||||
|
/backend/scripts/_env.local
|
||||||
/bundle*
|
/bundle*
|
||||||
/cd.md
|
/cd.md
|
||||||
/clj-profiler/
|
/clj-profiler/
|
||||||
@@ -74,6 +76,7 @@
|
|||||||
/library/target/
|
/library/target/
|
||||||
/library/*.zip
|
/library/*.zip
|
||||||
/external
|
/external
|
||||||
|
/penpot-nitrate
|
||||||
|
|
||||||
clj-profiler/
|
clj-profiler/
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058)
|
- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058)
|
||||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||||
|
- Fix boolean operators in menu for boards [Taiga #13174](https://tree.taiga.io/project/penpot/issue/13174)
|
||||||
|
|
||||||
## 2.13.0 (Unreleased)
|
## 2.13.0 (Unreleased)
|
||||||
|
|
||||||
@@ -69,7 +70,8 @@
|
|||||||
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135)
|
||||||
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
- Fix unhandled exception on open-new-window helper [Github #7787](https://github.com/penpot/penpot/issues/7787)
|
||||||
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
- Fix incorrect handling of input values on layout gap and padding inputs [Github #8113](https://github.com/penpot/penpot/issues/8113)
|
||||||
|
- Fix several race conditions on path editor [Github #8187](https://github.com/penpot/penpot/pull/8187)
|
||||||
|
- Fix app freeze when introducing an error on a very long token name [Taiga #13214](https://tree.taiga.io/project/penpot/issue/13214)
|
||||||
|
|
||||||
## 2.12.1
|
## 2.12.1
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
|
export PENPOT_NITRATE_SHARED_KEY=super-secret-nitrate-api-key
|
||||||
|
export PENPOT_EXPORTER_SHARED_KEY=super-secret-exporter-api-key
|
||||||
export PENPOT_SECRET_KEY=super-secret-devenv-key
|
export PENPOT_SECRET_KEY=super-secret-devenv-key
|
||||||
|
|
||||||
|
# DEPRECATED: only used for subscriptions
|
||||||
|
export PENPOT_MANAGEMENT_API_KEY=super-secret-management-api-key
|
||||||
|
|
||||||
export PENPOT_HOST=devenv
|
export PENPOT_HOST=devenv
|
||||||
export PENPOT_PUBLIC_URI=https://localhost:3449
|
export PENPOT_PUBLIC_URI=https://localhost:3449
|
||||||
|
|
||||||
@@ -13,6 +18,7 @@ export PENPOT_FLAGS="\
|
|||||||
disable-login-with-google \
|
disable-login-with-google \
|
||||||
disable-login-with-github \
|
disable-login-with-github \
|
||||||
disable-login-with-gitlab \
|
disable-login-with-gitlab \
|
||||||
|
disable-telemetry \
|
||||||
enable-backend-worker \
|
enable-backend-worker \
|
||||||
enable-backend-asserts \
|
enable-backend-asserts \
|
||||||
disable-feature-fdata-pointer-map \
|
disable-feature-fdata-pointer-map \
|
||||||
@@ -55,6 +61,8 @@ export PENPOT_OBJECTS_STORAGE_BACKEND=s3
|
|||||||
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
export PENPOT_OBJECTS_STORAGE_S3_ENDPOINT=http://minio:9000
|
||||||
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
export PENPOT_OBJECTS_STORAGE_S3_BUCKET=penpot
|
||||||
|
|
||||||
|
export PENPOT_NITRATE_BACKEND_URI=http://localhost:3000/control-center
|
||||||
|
|
||||||
export JAVA_OPTS="\
|
export JAVA_OPTS="\
|
||||||
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager \
|
||||||
-Djdk.attach.allowAttachSelf \
|
-Djdk.attach.allowAttachSelf \
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
# Initialize MINIO config
|
# Initialize MINIO config
|
||||||
setup_minio;
|
setup_minio;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
|
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
export OPTIONS="-A:dev"
|
export OPTIONS="-A:dev"
|
||||||
|
|
||||||
entrypoint=${1:-app.main};
|
entrypoint=${1:-app.main};
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
SCRIPT_DIR=$(dirname $0);
|
SCRIPT_DIR=$(dirname $0);
|
||||||
source $SCRIPT_DIR/_env;
|
source $SCRIPT_DIR/_env;
|
||||||
|
|
||||||
|
if [ -f $SCRIPT_DIR/_env.local ]; then
|
||||||
|
source $SCRIPT_DIR/_env.local;
|
||||||
|
fi
|
||||||
|
|
||||||
# Initialize MINIO config
|
# Initialize MINIO config
|
||||||
setup_minio;
|
setup_minio;
|
||||||
|
|
||||||
|
|||||||
@@ -873,11 +873,8 @@
|
|||||||
(import-storage-objects cfg)
|
(import-storage-objects cfg)
|
||||||
|
|
||||||
(let [files (get manifest :files)
|
(let [files (get manifest :files)
|
||||||
result (reduce (fn [result {:keys [id] :as file}]
|
result (reduce (fn [result file]
|
||||||
(let [name' (get file :name)
|
(let [name' (get file :name)
|
||||||
name' (if (map? name)
|
|
||||||
(get name id)
|
|
||||||
name')
|
|
||||||
file (assoc file :name name')]
|
file (assoc file :name name')]
|
||||||
(conj result (import-file cfg file))))
|
(conj result (import-file cfg file))))
|
||||||
[]
|
[]
|
||||||
|
|||||||
@@ -102,6 +102,8 @@
|
|||||||
[:http-server-io-threads {:optional true} ::sm/int]
|
[:http-server-io-threads {:optional true} ::sm/int]
|
||||||
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
[:http-server-max-worker-threads {:optional true} ::sm/int]
|
||||||
|
|
||||||
|
[:exporter-shared-key {:optional true} :string]
|
||||||
|
[:nitrate-shared-key {:optional true} :string]
|
||||||
[:management-api-key {:optional true} :string]
|
[:management-api-key {:optional true} :string]
|
||||||
|
|
||||||
[:telemetry-uri {:optional true} :string]
|
[:telemetry-uri {:optional true} :string]
|
||||||
@@ -225,6 +227,8 @@
|
|||||||
[:netty-io-threads {:optional true} ::sm/int]
|
[:netty-io-threads {:optional true} ::sm/int]
|
||||||
[:executor-threads {:optional true} ::sm/int]
|
[:executor-threads {:optional true} ::sm/int]
|
||||||
|
|
||||||
|
[:nitrate-backend-uri {:optional true} ::sm/uri]
|
||||||
|
|
||||||
;; DEPRECATED
|
;; DEPRECATED
|
||||||
[:assets-storage-backend {:optional true} :keyword]
|
[:assets-storage-backend {:optional true} :keyword]
|
||||||
[:storage-assets-fs-directory {:optional true} :string]
|
[:storage-assets-fs-directory {:optional true} :string]
|
||||||
|
|||||||
@@ -13,13 +13,13 @@
|
|||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http.middleware :as mw]
|
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.rpc.commands.profile :as cmd.profile]
|
[app.rpc.commands.profile :as cmd.profile]
|
||||||
[app.setup :as-alias setup]
|
[app.setup :as-alias setup]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[app.worker :as-alias wrk]
|
[app.worker :as-alias wrk]
|
||||||
[integrant.core :as ig]
|
[integrant.core :as ig]
|
||||||
|
[yetti.request :as yreq]
|
||||||
[yetti.response :as-alias yres]))
|
[yetti.response :as-alias yres]))
|
||||||
|
|
||||||
;; ---- ROUTES
|
;; ---- ROUTES
|
||||||
@@ -49,28 +49,40 @@
|
|||||||
(fn [cfg request]
|
(fn [cfg request]
|
||||||
(db/tx-run! cfg handler request)))))})
|
(db/tx-run! cfg handler request)))))})
|
||||||
|
|
||||||
|
(def ^:private shared-key-auth
|
||||||
|
{:name ::shared-key-auth
|
||||||
|
:compile
|
||||||
|
(fn [_ _]
|
||||||
|
(fn [handler key]
|
||||||
|
(if key
|
||||||
|
(fn [request]
|
||||||
|
(if-let [key' (yreq/get-header request "x-shared-key")]
|
||||||
|
(if (= key key')
|
||||||
|
(handler request)
|
||||||
|
{::yres/status 403})
|
||||||
|
{::yres/status 403}))
|
||||||
|
(fn [_ _]
|
||||||
|
{::yres/status 403}))))})
|
||||||
|
|
||||||
(defmethod ig/init-key ::routes
|
(defmethod ig/init-key ::routes
|
||||||
[_ {:keys [::setup/props] :as cfg}]
|
[_ cfg]
|
||||||
|
|
||||||
(let [management-key (or (cf/get :management-api-key)
|
["" {:middleware [[shared-key-auth (cf/get :management-api-key)]
|
||||||
(get props :management-key))]
|
[default-system cfg]
|
||||||
|
[transaction]]}
|
||||||
|
["/authenticate"
|
||||||
|
{:handler authenticate
|
||||||
|
:allowed-methods #{:post}}]
|
||||||
|
|
||||||
["" {:middleware [[mw/shared-key-auth management-key]
|
["/get-customer"
|
||||||
[default-system cfg]
|
{:handler get-customer
|
||||||
[transaction]]}
|
:transaction true
|
||||||
["/authenticate"
|
:allowed-methods #{:post}}]
|
||||||
{:handler authenticate
|
|
||||||
:allowed-methods #{:post}}]
|
|
||||||
|
|
||||||
["/get-customer"
|
["/update-customer"
|
||||||
{:handler get-customer
|
{:handler update-customer
|
||||||
:transaction true
|
:allowed-methods #{:post}
|
||||||
:allowed-methods #{:post}}]
|
:transaction true}]])
|
||||||
|
|
||||||
["/update-customer"
|
|
||||||
{:handler update-customer
|
|
||||||
:allowed-methods #{:post}
|
|
||||||
:transaction true}]]))
|
|
||||||
|
|
||||||
;; ---- HELPERS
|
;; ---- HELPERS
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
[app.http.errors :as errors]
|
[app.http.errors :as errors]
|
||||||
[app.tokens :as tokens]
|
[app.tokens :as tokens]
|
||||||
[app.util.pointer-map :as pmap]
|
[app.util.pointer-map :as pmap]
|
||||||
[buddy.core.codecs :as bc]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[yetti.adapter :as yt]
|
[yetti.adapter :as yt]
|
||||||
[yetti.middleware :as ymw]
|
[yetti.middleware :as ymw]
|
||||||
@@ -301,16 +300,20 @@
|
|||||||
:compile (constantly wrap-auth)})
|
:compile (constantly wrap-auth)})
|
||||||
|
|
||||||
(defn- wrap-shared-key-auth
|
(defn- wrap-shared-key-auth
|
||||||
[handler shared-key]
|
[handler keys]
|
||||||
(if shared-key
|
(if (seq keys)
|
||||||
(let [shared-key (if (string? shared-key)
|
(fn [request]
|
||||||
shared-key
|
(if-let [[key-id key] (some-> (yreq/get-header request "x-shared-key")
|
||||||
(bc/bytes->b64-str shared-key true))]
|
(str/split #"\s+" 2))]
|
||||||
(fn [request]
|
(let [key-id (-> key-id str/lower keyword)]
|
||||||
(let [key (yreq/get-header request "x-shared-key")]
|
(if (and (string? key)
|
||||||
(if (= key shared-key)
|
(contains? keys key-id)
|
||||||
(handler (assoc request ::http/auth-with-shared-key true))
|
(= key (get keys key-id)))
|
||||||
{::yres/status 403}))))
|
(-> request
|
||||||
|
(assoc ::http/auth-key-id key-id)
|
||||||
|
(handler))
|
||||||
|
{::yres/status 403}))
|
||||||
|
{::yres/status 403}))
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
{::yres/status 403})))
|
{::yres/status 403})))
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
(ns app.http.sse
|
(ns app.http.sse
|
||||||
"SSE (server sent events) helpers"
|
"SSE (server sent events) helpers"
|
||||||
(:refer-clojure :exclude [tap])
|
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
|||||||
@@ -140,10 +140,14 @@
|
|||||||
client-version (get-client-version request)
|
client-version (get-client-version request)
|
||||||
client-user-agent (get-client-user-agent request)
|
client-user-agent (get-client-user-agent request)
|
||||||
session-id (get-external-session-id request)
|
session-id (get-external-session-id request)
|
||||||
token-id (::actoken/id request)]
|
key-id (::http/auth-key-id request)
|
||||||
|
token-id (::actoken/id request)
|
||||||
|
token-type (::actoken/type request)]
|
||||||
(d/without-nils
|
(d/without-nils
|
||||||
{:external-session-id session-id
|
{:external-session-id session-id
|
||||||
|
:initiator (or key-id "app")
|
||||||
:access-token-id (some-> token-id str)
|
:access-token-id (some-> token-id str)
|
||||||
|
:access-token-type (some-> token-type str)
|
||||||
:client-event-origin client-event-origin
|
:client-event-origin client-event-origin
|
||||||
:client-user-agent client-user-agent
|
:client-user-agent client-user-agent
|
||||||
:client-version client-version
|
:client-version client-version
|
||||||
|
|||||||
@@ -275,8 +275,7 @@
|
|||||||
::email/whitelist (ig/ref ::email/whitelist)}
|
::email/whitelist (ig/ref ::email/whitelist)}
|
||||||
|
|
||||||
::mgmt/routes
|
::mgmt/routes
|
||||||
{::db/pool (ig/ref ::db/pool)
|
{::db/pool (ig/ref ::db/pool)}
|
||||||
::setup/props (ig/ref ::setup/props)}
|
|
||||||
|
|
||||||
:app.http/router
|
:app.http/router
|
||||||
{::session/manager (ig/ref ::session/manager)
|
{::session/manager (ig/ref ::session/manager)
|
||||||
@@ -323,6 +322,7 @@
|
|||||||
{::http.client/client (ig/ref ::http.client/client)
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
::rds/pool (ig/ref ::rds/pool)
|
::rds/pool (ig/ref ::rds/pool)
|
||||||
|
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||||
::wrk/executor (ig/ref ::wrk/netty-executor)
|
::wrk/executor (ig/ref ::wrk/netty-executor)
|
||||||
::session/manager (ig/ref ::session/manager)
|
::session/manager (ig/ref ::session/manager)
|
||||||
::ldap/provider (ig/ref ::ldap/provider)
|
::ldap/provider (ig/ref ::ldap/provider)
|
||||||
@@ -339,6 +339,10 @@
|
|||||||
::email/blacklist (ig/ref ::email/blacklist)
|
::email/blacklist (ig/ref ::email/blacklist)
|
||||||
::email/whitelist (ig/ref ::email/whitelist)}
|
::email/whitelist (ig/ref ::email/whitelist)}
|
||||||
|
|
||||||
|
:app.nitrate/client
|
||||||
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
|
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||||
|
|
||||||
:app.rpc/management-methods
|
:app.rpc/management-methods
|
||||||
{::http.client/client (ig/ref ::http.client/client)
|
{::http.client/client (ig/ref ::http.client/client)
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
@@ -348,17 +352,18 @@
|
|||||||
::sto/storage (ig/ref ::sto/storage)
|
::sto/storage (ig/ref ::sto/storage)
|
||||||
::mtx/metrics (ig/ref ::mtx/metrics)
|
::mtx/metrics (ig/ref ::mtx/metrics)
|
||||||
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
::mbus/msgbus (ig/ref ::mbus/msgbus)
|
||||||
|
:app.nitrate/client (ig/ref :app.nitrate/client)
|
||||||
::rds/client (ig/ref ::rds/client)
|
::rds/client (ig/ref ::rds/client)
|
||||||
::setup/props (ig/ref ::setup/props)}
|
::setup/props (ig/ref ::setup/props)}
|
||||||
|
|
||||||
::rpc/routes
|
::rpc/routes
|
||||||
{::rpc/methods (ig/ref :app.rpc/methods)
|
{::rpc/methods (ig/ref :app.rpc/methods)
|
||||||
::rpc/management-methods (ig/ref :app.rpc/management-methods)
|
::rpc/management-methods (ig/ref :app.rpc/management-methods)
|
||||||
|
|
||||||
;; FIXME: revisit if db/pool is necessary here
|
;; FIXME: revisit if db/pool is necessary here
|
||||||
::db/pool (ig/ref ::db/pool)
|
::db/pool (ig/ref ::db/pool)
|
||||||
::session/manager (ig/ref ::session/manager)
|
::session/manager (ig/ref ::session/manager)
|
||||||
::setup/props (ig/ref ::setup/props)}
|
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||||
|
|
||||||
::wrk/registry
|
::wrk/registry
|
||||||
{::mtx/metrics (ig/ref ::mtx/metrics)
|
{::mtx/metrics (ig/ref ::mtx/metrics)
|
||||||
@@ -446,6 +451,11 @@
|
|||||||
;; module requires the migrations to run before initialize.
|
;; module requires the migrations to run before initialize.
|
||||||
::migrations (ig/ref :app.migrations/migrations)}
|
::migrations (ig/ref :app.migrations/migrations)}
|
||||||
|
|
||||||
|
::setup/shared-keys
|
||||||
|
{::setup/props (ig/ref ::setup/props)
|
||||||
|
:nitrate (cf/get :nitrate-shared-key)
|
||||||
|
:exporter (cf/get :exporter-shared-key)}
|
||||||
|
|
||||||
::setup/clock
|
::setup/clock
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|||||||
130
backend/src/app/nitrate.clj
Normal file
130
backend/src/app/nitrate.clj
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
(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]))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; HELPERS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn- request-builder
|
||||||
|
[cfg method uri shared-key profile-id]
|
||||||
|
(fn []
|
||||||
|
(http/req! cfg {:method method
|
||||||
|
:headers {"content-type" "application/json"
|
||||||
|
"accept" "application/json"
|
||||||
|
"x-shared-key" shared-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 (sm/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
|
||||||
|
[cfg method uri schema {:keys [::rpc/profile-id] :as params}]
|
||||||
|
(let [shared-key (-> cfg ::setup/shared-keys :nitrate)
|
||||||
|
full-http-call (-> (request-builder cfg method uri shared-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}]
|
||||||
|
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||||
|
(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}]
|
||||||
|
(let [baseuri (cf/get :nitrate-backend-uri)]
|
||||||
|
(request-to-nitrate cfg :get (str baseuri "/api/users/" (str profile-id)) schema:user params)))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; INITIALIZATION
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defmethod ig/init-key ::client
|
||||||
|
[_ cfg]
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
{:get-team-org (partial get-team-org cfg)
|
||||||
|
:is-valid-user (partial is-valid-user cfg)}))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; UTILS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
|
(defn add-nitrate-licence-to-profile
|
||||||
|
[cfg profile]
|
||||||
|
(try
|
||||||
|
(let [nitrate-licence (call cfg :is-valid-user {:profile-id (:id profile)})]
|
||||||
|
(assoc profile :nitrate-licence (:valid nitrate-licence)))
|
||||||
|
(catch Throwable cause
|
||||||
|
(l/error :hint "failed to get nitrate licence"
|
||||||
|
:profile-id (:id profile)
|
||||||
|
:cause cause)
|
||||||
|
profile)))
|
||||||
|
|
||||||
|
(defn add-org-to-team
|
||||||
|
[cfg team params]
|
||||||
|
(let [params (assoc (or params {}) :team-id (:id team))
|
||||||
|
org (call cfg :get-team-org params)]
|
||||||
|
(assoc team :organization-id (:id org) :organization-name (:name org))))
|
||||||
@@ -92,11 +92,11 @@
|
|||||||
(fn [{:keys [params path-params method] :as request}]
|
(fn [{:keys [params path-params method] :as request}]
|
||||||
(let [handler-name (:type path-params)
|
(let [handler-name (:type path-params)
|
||||||
etag (yreq/get-header request "if-none-match")
|
etag (yreq/get-header request "if-none-match")
|
||||||
|
|
||||||
|
key-id (get request ::http/auth-key-id)
|
||||||
profile-id (or (::session/profile-id request)
|
profile-id (or (::session/profile-id request)
|
||||||
(::actoken/profile-id request)
|
(::actoken/profile-id request)
|
||||||
(if (::http/auth-with-shared-key request)
|
(if key-id uuid/zero nil))
|
||||||
uuid/zero
|
|
||||||
nil))
|
|
||||||
|
|
||||||
ip-addr (inet/parse-request request)
|
ip-addr (inet/parse-request request)
|
||||||
|
|
||||||
@@ -298,10 +298,12 @@
|
|||||||
|
|
||||||
(defn- resolve-management-methods
|
(defn- resolve-management-methods
|
||||||
[cfg]
|
[cfg]
|
||||||
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)]
|
(let [cfg (assoc cfg ::type "management" ::metrics-id :rpc-management-timing)
|
||||||
(->> (sv/scan-ns
|
mods (cond->> (list 'app.rpc.management.exporter)
|
||||||
'app.rpc.management.subscription
|
(contains? cf/flags :nitrate)
|
||||||
'app.rpc.management.exporter)
|
(cons 'app.rpc.management.nitrate))]
|
||||||
|
|
||||||
|
(->> (apply sv/scan-ns mods)
|
||||||
(map (partial process-method cfg "management" wrap-management))
|
(map (partial process-method cfg "management" wrap-management))
|
||||||
(into {}))))
|
(into {}))))
|
||||||
|
|
||||||
@@ -345,23 +347,20 @@
|
|||||||
|
|
||||||
(defmethod ig/assert-key ::routes
|
(defmethod ig/assert-key ::routes
|
||||||
[_ params]
|
[_ params]
|
||||||
|
(assert (map? (::setup/shared-keys params)))
|
||||||
(assert (db/pool? (::db/pool params)) "expect valid database pool")
|
(assert (db/pool? (::db/pool params)) "expect valid database pool")
|
||||||
(assert (some? (::setup/props params)))
|
|
||||||
(assert (session/manager? (::session/manager params)) "expect valid session manager")
|
(assert (session/manager? (::session/manager params)) "expect valid session manager")
|
||||||
(assert (valid-methods? (::methods params)) "expect valid methods map")
|
(assert (valid-methods? (::methods params)) "expect valid methods map")
|
||||||
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
|
(assert (valid-methods? (::management-methods params)) "expect valid methods map"))
|
||||||
|
|
||||||
(defmethod ig/init-key ::routes
|
(defmethod ig/init-key ::routes
|
||||||
[_ {:keys [::methods ::management-methods ::setup/props] :as cfg}]
|
[_ {:keys [::methods ::management-methods ::setup/shared-keys] :as cfg}]
|
||||||
|
|
||||||
(let [public-uri (cf/get :public-uri)
|
|
||||||
management-key (or (cf/get :management-api-key)
|
|
||||||
(get props :management-key))]
|
|
||||||
|
|
||||||
|
(let [public-uri (cf/get :public-uri)]
|
||||||
["/api"
|
["/api"
|
||||||
["/management"
|
["/management"
|
||||||
["/methods/:type"
|
["/methods/:type"
|
||||||
{:middleware [[mw/shared-key-auth management-key]
|
{:middleware [[mw/shared-key-auth shared-keys]
|
||||||
[session/authz cfg]]
|
[session/authz cfg]]
|
||||||
:handler (make-rpc-handler management-methods)}]
|
:handler (make-rpc-handler management-methods)}]
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
[app.loggers.audit :as audit]
|
[app.loggers.audit :as audit]
|
||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.media :as media]
|
[app.media :as media]
|
||||||
|
[app.nitrate :as nitrate]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.climit :as climit]
|
[app.rpc.climit :as climit]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
@@ -88,6 +89,8 @@
|
|||||||
|
|
||||||
;; --- QUERY: Get profile (own)
|
;; --- QUERY: Get profile (own)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(sv/defmethod ::get-profile
|
(sv/defmethod ::get-profile
|
||||||
{::rpc/auth false
|
{::rpc/auth false
|
||||||
::doc/added "1.18"
|
::doc/added "1.18"
|
||||||
@@ -98,9 +101,13 @@
|
|||||||
;; no profile-id is in session, and when db call raises not found. In all other
|
;; no profile-id is in session, and when db call raises not found. In all other
|
||||||
;; cases we need to reraise the exception.
|
;; cases we need to reraise the exception.
|
||||||
(try
|
(try
|
||||||
(-> (get-profile pool profile-id)
|
(let [profile (-> (get-profile pool profile-id)
|
||||||
(strip-private-attrs)
|
(strip-private-attrs)
|
||||||
(update :props filter-props))
|
(update :props filter-props))]
|
||||||
|
(if (contains? cf/flags :nitrate)
|
||||||
|
(nitrate/add-nitrate-licence-to-profile cfg profile)
|
||||||
|
profile))
|
||||||
|
|
||||||
(catch Throwable _
|
(catch Throwable _
|
||||||
{:id uuid/zero :fullname "Anonymous User"})))
|
{:id uuid/zero :fullname "Anonymous User"})))
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
[app.main :as-alias main]
|
[app.main :as-alias main]
|
||||||
[app.media :as media]
|
[app.media :as media]
|
||||||
[app.msgbus :as mbus]
|
[app.msgbus :as mbus]
|
||||||
|
[app.nitrate :as nitrate]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
[app.rpc.commands.profile :as profile]
|
[app.rpc.commands.profile :as profile]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
@@ -190,7 +191,9 @@
|
|||||||
::sm/params schema:get-teams}
|
::sm/params schema:get-teams}
|
||||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id] :as params}]
|
||||||
(dm/with-open [conn (db/open pool)]
|
(dm/with-open [conn (db/open pool)]
|
||||||
(get-teams conn profile-id)))
|
(cond->> (get-teams conn profile-id)
|
||||||
|
(contains? cf/flags :nitrate)
|
||||||
|
(map #(nitrate/add-org-to-team cfg % params)))))
|
||||||
|
|
||||||
(def ^:private sql:get-owned-teams
|
(def ^:private sql:get-owned-teams
|
||||||
"SELECT t.id, t.name,
|
"SELECT t.id, t.name,
|
||||||
|
|||||||
@@ -248,11 +248,11 @@
|
|||||||
|
|
||||||
invitations (into #{}
|
invitations (into #{}
|
||||||
(comp
|
(comp
|
||||||
;; We don't re-send invitations to
|
;; We don't re-send invitations to
|
||||||
;; already existing members
|
;; already existing members
|
||||||
(remove #(contains? team-members (:email %)))
|
(remove #(contains? team-members (:email %)))
|
||||||
;; We don't send invitations to
|
;; We don't send invitations to
|
||||||
;; join-requested members
|
;; join-requested members
|
||||||
(remove #(contains? join-requests (:email %)))
|
(remove #(contains? join-requests (:email %)))
|
||||||
(map (fn [{:keys [email role]}]
|
(map (fn [{:keys [email role]}]
|
||||||
(create-invitation cfg
|
(create-invitation cfg
|
||||||
|
|||||||
82
backend/src/app/rpc/management/nitrate.clj
Normal file
82
backend/src/app/rpc/management/nitrate.clj
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
;; 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 RPC API. Provides authenticated access to
|
||||||
|
organization management and token validation endpoints."
|
||||||
|
(:require
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.common.types.profile :refer [schema:profile]]
|
||||||
|
[app.common.types.team :refer [schema:team]]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[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
|
||||||
|
|
||||||
|
(sv/defmethod ::authenticate
|
||||||
|
"Authenticate the current user"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params [:map]
|
||||||
|
::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 IS TRUE
|
||||||
|
AND t.is_default IS FALSE
|
||||||
|
AND t.deleted_at IS NULL;")
|
||||||
|
|
||||||
|
(def ^:private schema:get-teams-result
|
||||||
|
[:vector schema:team])
|
||||||
|
|
||||||
|
(sv/defmethod ::get-teams
|
||||||
|
"List teams for which current user is owner"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params [:map]
|
||||||
|
::sm/result schema:get-teams-result}
|
||||||
|
[cfg {:keys [::rpc/profile-id]}]
|
||||||
|
(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"
|
||||||
|
{::doc/added "2.14"
|
||||||
|
::sm/params schema:notify-team-change
|
||||||
|
::rpc/auth false}
|
||||||
|
[cfg {:keys [id organization-id organization-name]}]
|
||||||
|
(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})))
|
||||||
@@ -1,183 +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.subscription
|
|
||||||
(:require
|
|
||||||
[app.common.logging :as l]
|
|
||||||
[app.common.schema :as sm]
|
|
||||||
[app.common.schema.generators :as sg]
|
|
||||||
[app.common.time :as ct]
|
|
||||||
[app.db :as db]
|
|
||||||
[app.rpc :as-alias rpc]
|
|
||||||
[app.rpc.commands.profile :as profile]
|
|
||||||
[app.rpc.doc :as doc]
|
|
||||||
[app.util.services :as sv]))
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: AUTHENTICATE
|
|
||||||
|
|
||||||
(def ^:private
|
|
||||||
schema:authenticate-params
|
|
||||||
[:map {:title "authenticate-params"}])
|
|
||||||
|
|
||||||
(def ^:private
|
|
||||||
schema:authenticate-result
|
|
||||||
[:map {:title "authenticate-result"}
|
|
||||||
[:profile-id ::sm/uuid]])
|
|
||||||
|
|
||||||
(sv/defmethod ::auth
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:authenticate-params
|
|
||||||
::sm/result schema:authenticate-result}
|
|
||||||
[_ {:keys [::rpc/profile-id]}]
|
|
||||||
{:profile-id profile-id})
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: GET-CUSTOMER
|
|
||||||
|
|
||||||
;; FIXME: move to app.common.time
|
|
||||||
(def ^:private schema:timestamp
|
|
||||||
(sm/type-schema
|
|
||||||
{:type ::timestamp
|
|
||||||
:pred ct/inst?
|
|
||||||
:type-properties
|
|
||||||
{:title "inst"
|
|
||||||
:description "The same as :app.common.time/inst but encodes to epoch"
|
|
||||||
:error/message "should be an instant"
|
|
||||||
:gen/gen (->> (sg/small-int)
|
|
||||||
(sg/fmap (fn [v] (ct/inst v))))
|
|
||||||
:decode/string #(some-> % ct/inst)
|
|
||||||
:encode/string #(some-> % inst-ms)
|
|
||||||
:decode/json #(some-> % ct/inst)
|
|
||||||
:encode/json #(some-> % inst-ms)}}))
|
|
||||||
|
|
||||||
(def ^:private schema:subscription
|
|
||||||
[:map {:title "Subscription"}
|
|
||||||
[:id ::sm/text]
|
|
||||||
[:customer-id ::sm/text]
|
|
||||||
[:type [:enum
|
|
||||||
"unlimited"
|
|
||||||
"professional"
|
|
||||||
"enterprise"]]
|
|
||||||
[:status [:enum
|
|
||||||
"active"
|
|
||||||
"canceled"
|
|
||||||
"incomplete"
|
|
||||||
"incomplete_expired"
|
|
||||||
"past_due"
|
|
||||||
"paused"
|
|
||||||
"trialing"
|
|
||||||
"unpaid"]]
|
|
||||||
|
|
||||||
[:billing-period [:enum
|
|
||||||
"month"
|
|
||||||
"day"
|
|
||||||
"week"
|
|
||||||
"year"]]
|
|
||||||
[:quantity :int]
|
|
||||||
[:description [:maybe ::sm/text]]
|
|
||||||
[:created-at schema:timestamp]
|
|
||||||
[:start-date [:maybe schema:timestamp]]
|
|
||||||
[:ended-at [:maybe schema:timestamp]]
|
|
||||||
[:trial-end [:maybe schema:timestamp]]
|
|
||||||
[:trial-start [:maybe schema:timestamp]]
|
|
||||||
[:cancel-at [:maybe schema:timestamp]]
|
|
||||||
[:canceled-at [:maybe schema:timestamp]]
|
|
||||||
[:current-period-end [:maybe schema:timestamp]]
|
|
||||||
[:current-period-start [:maybe schema:timestamp]]
|
|
||||||
[:cancel-at-period-end :boolean]
|
|
||||||
|
|
||||||
[:cancellation-details
|
|
||||||
[:map {:title "CancellationDetails"}
|
|
||||||
[:comment [:maybe ::sm/text]]
|
|
||||||
[:reason [:maybe ::sm/text]]
|
|
||||||
[:feedback [:maybe
|
|
||||||
[:enum
|
|
||||||
"customer_service"
|
|
||||||
"low_quality"
|
|
||||||
"missing_feature"
|
|
||||||
"other"
|
|
||||||
"switched_service"
|
|
||||||
"too_complex"
|
|
||||||
"too_expensive"
|
|
||||||
"unused"]]]]]])
|
|
||||||
|
|
||||||
(def ^:private sql:get-customer-slots
|
|
||||||
"WITH teams AS (
|
|
||||||
SELECT tpr.team_id AS id,
|
|
||||||
tpr.profile_id AS profile_id
|
|
||||||
FROM team_profile_rel AS tpr
|
|
||||||
WHERE tpr.is_owner IS true
|
|
||||||
AND tpr.profile_id = ?
|
|
||||||
), teams_with_slots AS (
|
|
||||||
SELECT tpr.team_id AS id,
|
|
||||||
count(*) AS total
|
|
||||||
FROM team_profile_rel AS tpr
|
|
||||||
WHERE tpr.team_id IN (SELECT id FROM teams)
|
|
||||||
AND tpr.can_edit IS true
|
|
||||||
GROUP BY 1
|
|
||||||
ORDER BY 2
|
|
||||||
)
|
|
||||||
SELECT max(total) AS total FROM teams_with_slots;")
|
|
||||||
|
|
||||||
(defn- get-customer-slots
|
|
||||||
[cfg profile-id]
|
|
||||||
(let [result (db/exec-one! cfg [sql:get-customer-slots profile-id])]
|
|
||||||
(:total result)))
|
|
||||||
|
|
||||||
(def ^:private schema:get-customer-params
|
|
||||||
[:map])
|
|
||||||
|
|
||||||
(def ^:private schema:get-customer-result
|
|
||||||
[:map
|
|
||||||
[:id ::sm/uuid]
|
|
||||||
[:name :string]
|
|
||||||
[:num-editors ::sm/int]
|
|
||||||
[:subscription {:optional true} schema:subscription]])
|
|
||||||
|
|
||||||
(sv/defmethod ::get-customer
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:get-customer-params
|
|
||||||
::sm/result schema:get-customer-result}
|
|
||||||
[cfg {:keys [::rpc/profile-id]}]
|
|
||||||
(let [profile (profile/get-profile cfg profile-id)]
|
|
||||||
{:id (get profile :id)
|
|
||||||
:name (get profile :fullname)
|
|
||||||
:email (get profile :email)
|
|
||||||
:num-editors (get-customer-slots cfg profile-id)
|
|
||||||
:subscription (-> profile :props :subscription)}))
|
|
||||||
|
|
||||||
|
|
||||||
;; ---- RPC METHOD: GET-CUSTOMER
|
|
||||||
|
|
||||||
(def ^:private schema:update-customer-params
|
|
||||||
[:map
|
|
||||||
[:subscription [:maybe schema:subscription]]])
|
|
||||||
|
|
||||||
(def ^:private schema:update-customer-result
|
|
||||||
[:map])
|
|
||||||
|
|
||||||
(sv/defmethod ::update-customer
|
|
||||||
{::doc/added "2.12"
|
|
||||||
::sm/params schema:update-customer-params
|
|
||||||
::sm/result schema:update-customer-result}
|
|
||||||
[cfg {:keys [::rpc/profile-id subscription]}]
|
|
||||||
(let [{:keys [props] :as profile}
|
|
||||||
(profile/get-profile cfg profile-id ::db/for-update true)
|
|
||||||
|
|
||||||
props
|
|
||||||
(assoc props :subscription subscription)]
|
|
||||||
|
|
||||||
(l/dbg :hint "update customer"
|
|
||||||
:profile-id (str profile-id)
|
|
||||||
:subscription-type (get subscription :type)
|
|
||||||
:subscription-status (get subscription :status)
|
|
||||||
:subscription-quantity (get subscription :quantity))
|
|
||||||
|
|
||||||
(db/update! cfg :profile
|
|
||||||
{:props (db/tjson props)}
|
|
||||||
{:id profile-id}
|
|
||||||
{::db/return-keys false})
|
|
||||||
|
|
||||||
nil))
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
[app.setup.templates]
|
[app.setup.templates]
|
||||||
[buddy.core.codecs :as bc]
|
[buddy.core.codecs :as bc]
|
||||||
[buddy.core.nonce :as bn]
|
[buddy.core.nonce :as bn]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[integrant.core :as ig]))
|
[integrant.core :as ig]))
|
||||||
|
|
||||||
(defn- generate-random-key
|
(defn- generate-random-key
|
||||||
@@ -88,7 +89,38 @@
|
|||||||
(-> (get-all-props conn)
|
(-> (get-all-props conn)
|
||||||
(assoc :secret-key secret)
|
(assoc :secret-key secret)
|
||||||
(assoc :tokens-key (keys/derive secret :salt "tokens"))
|
(assoc :tokens-key (keys/derive secret :salt "tokens"))
|
||||||
(assoc :management-key (keys/derive secret :salt "management"))
|
|
||||||
(update :instance-id handle-instance-id conn (db/read-only? pool)))))))
|
(update :instance-id handle-instance-id conn (db/read-only? pool)))))))
|
||||||
|
|
||||||
(sm/register! ::props [:map-of :keyword ::sm/any])
|
(sm/register! ::props [:map-of :keyword ::sm/any])
|
||||||
|
|
||||||
|
|
||||||
|
(defmethod ig/init-key ::shared-keys
|
||||||
|
[_ {:keys [::props] :as cfg}]
|
||||||
|
(let [secret (get props :secret-key)]
|
||||||
|
(d/without-nils
|
||||||
|
{:exporter
|
||||||
|
(let [key (or (get cfg :exporter)
|
||||||
|
(-> (keys/derive secret :salt "exporter")
|
||||||
|
(bc/bytes->b64-str true)))]
|
||||||
|
(if (or (str/empty? key)
|
||||||
|
(str/blank? key))
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "exporter key is disabled because empty string found")
|
||||||
|
nil)
|
||||||
|
(do
|
||||||
|
(l/inf :hint "exporter key initialized" :key (d/obfuscate-string key))
|
||||||
|
key)))
|
||||||
|
|
||||||
|
:nitrate
|
||||||
|
(let [key (or (get cfg :nitrate)
|
||||||
|
(-> (keys/derive secret :salt "nitrate")
|
||||||
|
(bc/bytes->b64-str true)))]
|
||||||
|
(if (or (str/empty? key)
|
||||||
|
(str/blank? key))
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "nitrate key is disabled because empty string found")
|
||||||
|
nil)
|
||||||
|
(do
|
||||||
|
(l/inf :hint "nitrate key initialized" :key (d/obfuscate-string key))
|
||||||
|
key)))})))
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"A generic asynchronous events notifications subsystem; used mainly
|
"A generic asynchronous events notifications subsystem; used mainly
|
||||||
for mark event points in functions and be able to attach listeners
|
for mark event points in functions and be able to attach listeners
|
||||||
to them. Mainly used in http.sse for progress reporting."
|
to them. Mainly used in http.sse for progress reporting."
|
||||||
(:refer-clojure :exclude [tap run!])
|
(:refer-clojure :exclude [run!])
|
||||||
(:require
|
(:require
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.logging :as l]
|
[app.common.logging :as l]
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
(t/deftest shared-key-auth
|
(t/deftest shared-key-auth
|
||||||
(let [handler (#'app.http.middleware/wrap-shared-key-auth
|
(let [handler (#'app.http.middleware/wrap-shared-key-auth
|
||||||
(fn [req] {::yres/status 200})
|
(fn [req] {::yres/status 200})
|
||||||
"secret-key")]
|
{:test1 "secret-key"})]
|
||||||
|
|
||||||
(let [response (handler (->DummyRequest {} {}))]
|
(let [response (handler (->DummyRequest {} {}))]
|
||||||
(t/is (= 403 (::yres/status response))))
|
(t/is (= 403 (::yres/status response))))
|
||||||
@@ -95,6 +95,9 @@
|
|||||||
(t/is (= 403 (::yres/status response))))
|
(t/is (= 403 (::yres/status response))))
|
||||||
|
|
||||||
(let [response (handler (->DummyRequest {"x-shared-key" "secret-key"} {}))]
|
(let [response (handler (->DummyRequest {"x-shared-key" "secret-key"} {}))]
|
||||||
|
(t/is (= 403 (::yres/status response))))
|
||||||
|
|
||||||
|
(let [response (handler (->DummyRequest {"x-shared-key" "test1 secret-key"} {}))]
|
||||||
(t/is (= 200 (::yres/status response))))))
|
(t/is (= 200 (::yres/status response))))))
|
||||||
|
|
||||||
(t/deftest access-token-authz
|
(t/deftest access-token-authz
|
||||||
|
|||||||
@@ -841,7 +841,7 @@
|
|||||||
out (th/command! data)
|
out (th/command! data)
|
||||||
error (:error out)]
|
error (:error out)]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (th/ex-info? error))
|
(t/is (th/ex-info? error))
|
||||||
(t/is (th/ex-of-type? error :not-found))))
|
(t/is (th/ex-of-type? error :not-found))))
|
||||||
|
|
||||||
@@ -863,7 +863,7 @@
|
|||||||
out (th/command! data)
|
out (th/command! data)
|
||||||
error (:error out)]
|
error (:error out)]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (th/ex-info? error))
|
(t/is (th/ex-info? error))
|
||||||
(t/is (th/ex-of-type? error :not-found))))
|
(t/is (th/ex-of-type? error :not-found))))
|
||||||
|
|
||||||
@@ -1261,7 +1261,7 @@
|
|||||||
(t/is (= 1 (count rows)))
|
(t/is (= 1 (count rows)))
|
||||||
(t/is (every? #(some? (:data %)) rows)))
|
(t/is (every? #(some? (:data %)) rows)))
|
||||||
|
|
||||||
;; Mark the file ellegible again for GC
|
;; Mark the file ellegible again for GC
|
||||||
(th/db-update! :file
|
(th/db-update! :file
|
||||||
{:has-media-trimmed false}
|
{:has-media-trimmed false}
|
||||||
{:id (:id file)})
|
{:id (:id file)})
|
||||||
@@ -1318,7 +1318,7 @@
|
|||||||
{:file-id (:id file)
|
{:file-id (:id file)
|
||||||
:type "fragment"}
|
:type "fragment"}
|
||||||
{:order-by [:created-at]})]
|
{:order-by [:created-at]})]
|
||||||
;; (pp/pprint rows)
|
;; (pp/pprint rows)
|
||||||
(t/is (= 2 (count rows)))
|
(t/is (= 2 (count rows)))
|
||||||
(t/is (nil? (:data row1)))
|
(t/is (nil? (:data row1)))
|
||||||
(t/is (= "storage" (:backend row1)))
|
(t/is (= "storage" (:backend row1)))
|
||||||
|
|||||||
@@ -536,7 +536,7 @@
|
|||||||
:token rtoken}
|
:token rtoken}
|
||||||
|
|
||||||
{:keys [result error] :as out} (th/command! data)]
|
{:keys [result error] :as out} (th/command! data)]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? error))
|
(t/is (nil? error))
|
||||||
(t/is (map? result))
|
(t/is (map? result))
|
||||||
(t/is (string? (:invitation-token result))))))
|
(t/is (string? (:invitation-token result))))))
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
:team-id (:id team)
|
:team-id (:id team)
|
||||||
:name "test project"}
|
:name "test project"}
|
||||||
out (th/command! data)]
|
out (th/command! data)]
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
|
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(let [result (:result out)]
|
(let [result (:result out)]
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
:id project-id}
|
:id project-id}
|
||||||
out (th/command! data)]
|
out (th/command! data)]
|
||||||
|
|
||||||
;; (th/print-result! out)
|
;; (th/print-result! out)
|
||||||
(t/is (nil? (:error out)))
|
(t/is (nil? (:error out)))
|
||||||
(t/is (nil? (:result out))))
|
(t/is (nil? (:result out))))
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -1092,9 +1092,9 @@
|
|||||||
(if (number? num)
|
(if (number? num)
|
||||||
(try
|
(try
|
||||||
(let [num-str (mth/to-fixed num precision)
|
(let [num-str (mth/to-fixed num precision)
|
||||||
;; Remove all trailing zeros after the comma 100.00000
|
;; Remove all trailing zeros after the comma 100.00000
|
||||||
num-str (str/replace num-str trail-zeros-regex-1 "")]
|
num-str (str/replace num-str trail-zeros-regex-1 "")]
|
||||||
;; Remove trailing zeros after a decimal number: 0.001|00|
|
;; Remove trailing zeros after a decimal number: 0.001|00|
|
||||||
(if-let [m (re-find trail-zeros-regex-2 num-str)]
|
(if-let [m (re-find trail-zeros-regex-2 num-str)]
|
||||||
(str/replace num-str (first m) (second m))
|
(str/replace num-str (first m) (second m))
|
||||||
num-str))
|
num-str))
|
||||||
|
|||||||
@@ -241,7 +241,20 @@
|
|||||||
(with-out-str
|
(with-out-str
|
||||||
(print-all cause)))))
|
(print-all cause)))))
|
||||||
|
|
||||||
#?(:clj
|
(defn print-throwable
|
||||||
(defn print-throwable
|
[cause & {:as opts}]
|
||||||
[cause & {:as opts}]
|
#?(:clj
|
||||||
(println (format-throwable cause opts))))
|
(println (format-throwable cause opts))
|
||||||
|
:cljs
|
||||||
|
(let [prefix (get opts :prefix "exception")
|
||||||
|
title (str prefix ": " (ex-message cause))
|
||||||
|
exdata (ex-data cause)]
|
||||||
|
(js/console.group title)
|
||||||
|
(when-let [explain (get exdata ::sm/explain)]
|
||||||
|
(println (sm/humanize-explain explain)))
|
||||||
|
|
||||||
|
(js/console.log "\nData:")
|
||||||
|
(pp/pprint (dissoc exdata ::sm/explain))
|
||||||
|
|
||||||
|
(js/console.log "\nTrace:")
|
||||||
|
(js/console.error (.-stack cause)))))
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set parent to root frame.
|
;; Set parent to root frame.
|
||||||
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
(log/debug :hint " -> set to " :parent-id uuid/zero)
|
||||||
(assoc shape :parent-id uuid/zero))]
|
(assoc shape :parent-id uuid/zero))]
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [parent-shape]
|
(fn [parent-shape]
|
||||||
; Add shape to parent's children list
|
;; Add shape to parent's children list
|
||||||
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
(log/debug :hint " -> add children to" :parent-id (:id parent-shape))
|
||||||
(update parent-shape :shapes conj (:id shape)))]
|
(update parent-shape :shapes conj (:id shape)))]
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Remove duplicated
|
;; Remove duplicated
|
||||||
(log/debug :hint " -> remove duplicated children")
|
(log/debug :hint " -> remove duplicated children")
|
||||||
(update shape :shapes distinct))]
|
(update shape :shapes distinct))]
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Locate the first frame in parents and set frame-id to it.
|
;; Locate the first frame in parents and set frame-id to it.
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
(let [page (ctpl/get-page file-data page-id)
|
||||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||||
frame-id (or (:id frame) uuid/zero)]
|
frame-id (or (:id frame) uuid/zero)]
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Locate the first frame in parents and set frame-id to it.
|
;; Locate the first frame in parents and set frame-id to it.
|
||||||
(let [page (ctpl/get-page file-data page-id)
|
(let [page (ctpl/get-page file-data page-id)
|
||||||
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
frame (cfh/get-frame (:objects page) (:parent-id shape))
|
||||||
frame-id (or (:id frame) uuid/zero)]
|
frame-id (or (:id frame) uuid/zero)]
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set the :shape as main instance root
|
;; Set the :shape as main instance root
|
||||||
(log/debug :hint " -> set :main-instance")
|
(log/debug :hint " -> set :main-instance")
|
||||||
(assoc shape :main-instance true))]
|
(assoc shape :main-instance true))]
|
||||||
|
|
||||||
@@ -147,12 +147,13 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Set :component-file to local file
|
;; Set :component-file to local file
|
||||||
(log/debug :hint " -> set :component-file to local file")
|
(log/debug :hint " -> set :component-file to local file")
|
||||||
(assoc shape :component-file (:id file-data)))]
|
(assoc shape :component-file (:id file-data)))]
|
||||||
; There is no solution that may recover it with confidence
|
|
||||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
;; There is no solution that may recover it with confidence
|
||||||
;; shape)]
|
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
|
;; shape)]
|
||||||
|
|
||||||
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :component-main-external" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -166,12 +167,12 @@
|
|||||||
|
|
||||||
repair-shape
|
repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
;; (log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
;; shape)]
|
;; shape)]
|
||||||
|
|
||||||
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :component-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -184,7 +185,7 @@
|
|||||||
|
|
||||||
repair-component
|
repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Assign main instance in the component to current shape
|
;; Assign main instance in the component to current shape
|
||||||
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
(log/debug :hint " -> assign main-instance-id" :component-id (:id component))
|
||||||
(assoc component :main-instance-id (:id shape)))
|
(assoc component :main-instance-id (:id shape)))
|
||||||
|
|
||||||
@@ -207,7 +208,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Assign main instance in the component to current shape
|
;; Assign main instance in the component to current shape
|
||||||
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
(log/debug :hint " -> assign main-instance-page" :component-id (:id component))
|
||||||
(assoc component :main-instance-page page-id))]
|
(assoc component :main-instance-page page-id))]
|
||||||
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :invalid-main-instance-page" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
@@ -219,7 +220,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
shape)]
|
shape)]
|
||||||
|
|
||||||
@@ -232,7 +233,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Unset the :shape as main instance root
|
;; Unset the :shape as main instance root
|
||||||
(log/debug :hint " -> unset :main-instance")
|
(log/debug :hint " -> unset :main-instance")
|
||||||
(dissoc shape :main-instance))]
|
(dissoc shape :main-instance))]
|
||||||
|
|
||||||
@@ -245,7 +246,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top copy root.
|
;; Convert the shape in a top copy root.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -258,7 +259,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested copy root.
|
;; Convert the shape in a nested copy root.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -307,8 +308,8 @@
|
|||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
; If the shape still refers to the remote component, try to find the corresponding near one
|
;; If the shape still refers to the remote component, try to find the corresponding near one
|
||||||
; and link to it. If not, detach the shape.
|
;; and link to it. If not, detach the shape.
|
||||||
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
(log/dbg :hint "repairing shape :ref-shape-not-found" :id (:id shape) :name (:name shape) :page-id page-id)
|
||||||
(if (some? matching-shape)
|
(if (some? matching-shape)
|
||||||
(-> (pcb/empty-changes nil page-id)
|
(-> (pcb/empty-changes nil page-id)
|
||||||
@@ -324,7 +325,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert shape in a normal copy, removing nested copy status
|
;; Convert shape in a normal copy, removing nested copy status
|
||||||
(log/debug :hint " -> unhead shape")
|
(log/debug :hint " -> unhead shape")
|
||||||
(ctk/unhead-shape shape))]
|
(ctk/unhead-shape shape))]
|
||||||
|
|
||||||
@@ -337,7 +338,7 @@
|
|||||||
[_ {:keys [shape page-id args] :as error} file-data _]
|
[_ {:keys [shape page-id args] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert shape in a nested head, adding component info
|
;; Convert shape in a nested head, adding component info
|
||||||
(log/debug :hint " -> reroot shape")
|
(log/debug :hint " -> reroot shape")
|
||||||
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
(ctk/rehead-shape shape (:component-file args) (:component-id args)))]
|
||||||
|
|
||||||
@@ -350,8 +351,9 @@
|
|||||||
[_ {:keys [shape args] :as error} file-data _]
|
[_ {:keys [shape args] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
(let [objects (:objects component) ;; we only have encounter this on deleted components,
|
(let [objects (:objects component)
|
||||||
;; so the relevant objects are inside the component
|
;; we only have encounter this on deleted components,
|
||||||
|
;; so the relevant objects are inside the component
|
||||||
to-detach (->> (:cycles-ids args)
|
to-detach (->> (:cycles-ids args)
|
||||||
(map #(get objects %))
|
(map #(get objects %))
|
||||||
(map #(ctn/get-head-shape objects %))
|
(map #(ctn/get-head-shape objects %))
|
||||||
@@ -378,7 +380,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Remove shape-ref
|
;; Remove shape-ref
|
||||||
(log/debug :hint " -> unset :shape-ref")
|
(log/debug :hint " -> unset :shape-ref")
|
||||||
(dissoc shape :shape-ref))]
|
(dissoc shape :shape-ref))]
|
||||||
|
|
||||||
@@ -391,7 +393,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested main head.
|
;; Convert the shape in a nested main head.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -404,7 +406,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top main head.
|
;; Convert the shape in a top main head.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -418,7 +420,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a nested copy head.
|
;; Convert the shape in a nested copy head.
|
||||||
(log/debug :hint " -> unset :component-root")
|
(log/debug :hint " -> unset :component-root")
|
||||||
(dissoc shape :component-root))]
|
(dissoc shape :component-root))]
|
||||||
|
|
||||||
@@ -431,7 +433,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a top copy root.
|
;; Convert the shape in a top copy root.
|
||||||
(log/debug :hint " -> set :component-root")
|
(log/debug :hint " -> set :component-root")
|
||||||
(assoc shape :component-root true))]
|
(assoc shape :component-root true))]
|
||||||
|
|
||||||
@@ -444,7 +446,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
@@ -457,7 +459,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Detach the shape and convert it to non instance.
|
;; Detach the shape and convert it to non instance.
|
||||||
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
(log/debug :hint " -> detach shape" :shape-id (:id shape))
|
||||||
(ctk/detach-shape shape))]
|
(ctk/detach-shape shape))]
|
||||||
|
|
||||||
@@ -470,7 +472,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; There is no solution that may recover it with confidence
|
;; There is no solution that may recover it with confidence
|
||||||
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
(log/warn :hint " -> CANNOT REPAIR THIS AUTOMATICALLY.")
|
||||||
shape)]
|
shape)]
|
||||||
|
|
||||||
@@ -483,7 +485,7 @@
|
|||||||
[_ {:keys [shape page-id] :as error} file-data _]
|
[_ {:keys [shape page-id] :as error} file-data _]
|
||||||
(let [repair-shape
|
(let [repair-shape
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
; Convert the shape in a frame.
|
;; Convert the shape in a frame.
|
||||||
(log/debug :hint " -> set :type :frame")
|
(log/debug :hint " -> set :type :frame")
|
||||||
(assoc shape :type :frame
|
(assoc shape :type :frame
|
||||||
:fills []
|
:fills []
|
||||||
@@ -502,7 +504,7 @@
|
|||||||
[_ {:keys [shape] :as error} file-data _]
|
[_ {:keys [shape] :as error} file-data _]
|
||||||
(let [repair-component
|
(let [repair-component
|
||||||
(fn [component]
|
(fn [component]
|
||||||
; Remove the objects key, or set it to {} if the component is deleted
|
;; Remove the objects key, or set it to {} if the component is deleted
|
||||||
(if (:deleted component)
|
(if (:deleted component)
|
||||||
(do
|
(do
|
||||||
(log/debug :hint " -> set :objects {}")
|
(log/debug :hint " -> set :objects {}")
|
||||||
|
|||||||
@@ -430,8 +430,8 @@
|
|||||||
(assoc :frame-id frame-id)
|
(assoc :frame-id frame-id)
|
||||||
(assoc :svg-viewbox vbox)
|
(assoc :svg-viewbox vbox)
|
||||||
(assoc :svg-attrs props)
|
(assoc :svg-attrs props)
|
||||||
;; We need to ensure fills are empty on import process
|
;; We need to ensure fills are empty on import process
|
||||||
;; because setup-shape assings one by default.
|
;; because setup-shape assings one by default.
|
||||||
(assoc :fills [])
|
(assoc :fills [])
|
||||||
(merge radius-attrs)))))
|
(merge radius-attrs)))))
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,10 @@
|
|||||||
|
|
||||||
;; A temporal flag, enables backend code use more extensivelly
|
;; A temporal flag, enables backend code use more extensivelly
|
||||||
;; redis for caching data
|
;; redis for caching data
|
||||||
:redis-cache})
|
:redis-cache
|
||||||
|
|
||||||
|
;; Activates the nitrate module
|
||||||
|
:nitrate})
|
||||||
|
|
||||||
(def all-flags
|
(def all-flags
|
||||||
(set/union email login varia))
|
(set/union email login varia))
|
||||||
|
|||||||
@@ -124,33 +124,51 @@
|
|||||||
|
|
||||||
(defn adjust-to-viewport
|
(defn adjust-to-viewport
|
||||||
([viewport srect] (adjust-to-viewport viewport srect nil))
|
([viewport srect] (adjust-to-viewport viewport srect nil))
|
||||||
([viewport srect {:keys [padding] :or {padding 0}}]
|
([viewport srect {:keys [padding min-zoom] :or {padding 0 min-zoom nil}}]
|
||||||
(let [gprop (/ (:width viewport)
|
(let [gprop (/ (:width viewport)
|
||||||
(:height viewport))
|
(:height viewport))
|
||||||
srect (-> srect
|
srect-padded (-> srect
|
||||||
(update :x #(- % padding))
|
(update :x #(- % padding))
|
||||||
(update :y #(- % padding))
|
(update :y #(- % padding))
|
||||||
(update :width #(+ % padding padding))
|
(update :width #(+ % padding padding))
|
||||||
(update :height #(+ % padding padding)))
|
(update :height #(+ % padding padding)))
|
||||||
width (:width srect)
|
width (:width srect-padded)
|
||||||
height (:height srect)
|
height (:height srect-padded)
|
||||||
lprop (/ width height)]
|
lprop (/ width height)
|
||||||
(cond
|
adjusted-rect
|
||||||
(> gprop lprop)
|
(cond
|
||||||
(let [width' (* (/ width lprop) gprop)
|
(> gprop lprop)
|
||||||
padding (/ (- width' width) 2)]
|
(let [width' (* (/ width lprop) gprop)
|
||||||
(-> srect
|
padding (/ (- width' width) 2)]
|
||||||
(update :x #(- % padding))
|
(-> srect-padded
|
||||||
(assoc :width width')
|
(update :x #(- % padding))
|
||||||
(grc/update-rect :position)))
|
(assoc :width width')
|
||||||
|
(grc/update-rect :position)))
|
||||||
|
|
||||||
(< gprop lprop)
|
(< gprop lprop)
|
||||||
(let [height' (/ (* height lprop) gprop)
|
(let [height' (/ (* height lprop) gprop)
|
||||||
padding (/ (- height' height) 2)]
|
padding (/ (- height' height) 2)]
|
||||||
(-> srect
|
(-> srect-padded
|
||||||
(update :y #(- % padding))
|
(update :y #(- % padding))
|
||||||
(assoc :height height')
|
(assoc :height height')
|
||||||
(grc/update-rect :position)))
|
(grc/update-rect :position)))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(grc/update-rect srect :position)))))
|
(grc/update-rect srect-padded :position))]
|
||||||
|
;; If min-zoom is specified and the resulting zoom would be below it,
|
||||||
|
;; return a rect with the original top-left corner centered in the viewport
|
||||||
|
;; instead of using the aspect-ratio-adjusted rect (which can push coords
|
||||||
|
;; extremely far with extreme aspect ratios).
|
||||||
|
(if (and (some? min-zoom)
|
||||||
|
(< (/ (:width viewport) (:width adjusted-rect)) min-zoom))
|
||||||
|
(let [anchor-x (:x srect)
|
||||||
|
anchor-y (:y srect)
|
||||||
|
vbox-width (/ (:width viewport) min-zoom)
|
||||||
|
vbox-height (/ (:height viewport) min-zoom)]
|
||||||
|
(-> adjusted-rect
|
||||||
|
(assoc :x (- anchor-x (/ vbox-width 2))
|
||||||
|
:y (- anchor-y (/ vbox-height 2))
|
||||||
|
:width vbox-width
|
||||||
|
:height vbox-height)
|
||||||
|
(grc/update-rect :position)))
|
||||||
|
adjusted-rect))))
|
||||||
|
|||||||
@@ -49,9 +49,9 @@
|
|||||||
(def log-container-ids #{})
|
(def log-container-ids #{})
|
||||||
|
|
||||||
(def updatable-attrs (->> (seq (keys ctk/sync-attrs))
|
(def updatable-attrs (->> (seq (keys ctk/sync-attrs))
|
||||||
;; We don't update the flex-child attrs
|
;; We don't update the flex-child attrs
|
||||||
(remove ctk/swap-keep-attrs)
|
(remove ctk/swap-keep-attrs)
|
||||||
;; We don't do automatic update of the `layout-grid-cells` property.
|
;; We don't do automatic update of the `layout-grid-cells` property.
|
||||||
(remove #(= :layout-grid-cells %))))
|
(remove #(= :layout-grid-cells %))))
|
||||||
|
|
||||||
(defn enabled-shape?
|
(defn enabled-shape?
|
||||||
@@ -1898,10 +1898,10 @@
|
|||||||
(gsh/absolute-move shape new-pos)))
|
(gsh/absolute-move shape new-pos)))
|
||||||
|
|
||||||
(defn- switch-path-change-value
|
(defn- switch-path-change-value
|
||||||
[prev-shape ;; The shape before the switch
|
[prev-shape ; The shape before the switch
|
||||||
current-shape ;; The shape after the switch (a clean copy)
|
current-shape ; The shape after the switch (a clean copy)
|
||||||
ref-shape ;; The referenced shape on the main component
|
ref-shape ; The referenced shape on the main component
|
||||||
;; before the switch
|
; before the switch
|
||||||
attr]
|
attr]
|
||||||
(let [old-width (-> ref-shape :selrect :width)
|
(let [old-width (-> ref-shape :selrect :width)
|
||||||
new-width (-> prev-shape :selrect :width)
|
new-width (-> prev-shape :selrect :width)
|
||||||
@@ -1918,10 +1918,9 @@
|
|||||||
|
|
||||||
|
|
||||||
(defn- switch-text-change-value
|
(defn- switch-text-change-value
|
||||||
[prev-content ;; The :content of the text before the switch
|
[prev-content ; The :content of the text before the switch
|
||||||
current-content ;; The :content of the text after the switch (a clean copy)
|
current-content ; The :content of the text after the switch (a clean copy)
|
||||||
ref-content touched] ;; The :content of the referenced text on the main component
|
ref-content touched] ; The :content of the referenced text on the main component before the switch
|
||||||
;; before the switch
|
|
||||||
(let [;; We need the differences between the contents on the main
|
(let [;; We need the differences between the contents on the main
|
||||||
;; components. current-content is the content of a clean copy,
|
;; components. current-content is the content of a clean copy,
|
||||||
;; so for all effects its the same as the content on its main
|
;; so for all effects its the same as the content on its main
|
||||||
@@ -2845,8 +2844,8 @@
|
|||||||
duplicating-component?
|
duplicating-component?
|
||||||
true
|
true
|
||||||
(and remove-swap-slot?
|
(and remove-swap-slot?
|
||||||
;; only remove swap slot of children when the current shape
|
;; only remove swap slot of children when the current shape
|
||||||
;; is not a subinstance head nor a instance root
|
;; is not a subinstance head nor a instance root
|
||||||
(not subinstance-head?)
|
(not subinstance-head?)
|
||||||
(not instance-root?))
|
(not instance-root?))
|
||||||
variant-props))
|
variant-props))
|
||||||
@@ -2902,7 +2901,7 @@
|
|||||||
variant-props)
|
variant-props)
|
||||||
changes))
|
changes))
|
||||||
|
|
||||||
;; We need to check the changes to get the ids-map
|
;; We need to check the changes to get the ids-map
|
||||||
ids-map
|
ids-map
|
||||||
(into {}
|
(into {}
|
||||||
(comp
|
(comp
|
||||||
|
|||||||
@@ -138,12 +138,12 @@
|
|||||||
ids (cfh/clean-loops objects ids)
|
ids (cfh/clean-loops objects ids)
|
||||||
in-component-copy?
|
in-component-copy?
|
||||||
(fn [shape-id]
|
(fn [shape-id]
|
||||||
;; Look for shapes that are inside a component copy, but are
|
;; Look for shapes that are inside a component copy, but are
|
||||||
;; not the root. In this case, they must not be deleted,
|
;; not the root. In this case, they must not be deleted,
|
||||||
;; but hidden (to be able to recover them more easily).
|
;; but hidden (to be able to recover them more easily).
|
||||||
;; If we want to specifically allow altering the copies, this is
|
;; If we want to specifically allow altering the copies, this is
|
||||||
;; a special case, like a component swap, in which case we want
|
;; a special case, like a component swap, in which case we want
|
||||||
;; to delete the old shape
|
;; to delete the old shape
|
||||||
(let [shape (get objects shape-id)]
|
(let [shape (get objects shape-id)]
|
||||||
(and (ctn/has-any-copy-parent? objects shape)
|
(and (ctn/has-any-copy-parent? objects shape)
|
||||||
(not allow-altering-copies))))
|
(not allow-altering-copies))))
|
||||||
@@ -168,9 +168,9 @@
|
|||||||
groups-to-unmask
|
groups-to-unmask
|
||||||
(when-not ignore-mask
|
(when-not ignore-mask
|
||||||
(reduce (fn [group-ids id]
|
(reduce (fn [group-ids id]
|
||||||
;; When the shape to delete is the mask of a masked group,
|
;; When the shape to delete is the mask of a masked group,
|
||||||
;; the mask condition must be removed, and it must be
|
;; the mask condition must be removed, and it must be
|
||||||
;; converted to a normal group.
|
;; converted to a normal group.
|
||||||
(let [obj (lookup id)
|
(let [obj (lookup id)
|
||||||
parent (lookup (:parent-id obj))]
|
parent (lookup (:parent-id obj))]
|
||||||
(if (and (:masked-group parent)
|
(if (and (:masked-group parent)
|
||||||
@@ -183,8 +183,8 @@
|
|||||||
|
|
||||||
interacting-shapes
|
interacting-shapes
|
||||||
(filter (fn [shape]
|
(filter (fn [shape]
|
||||||
;; If any of the deleted shapes is the destination of
|
;; If any of the deleted shapes is the destination of
|
||||||
;; some interaction, this must be deleted, too.
|
;; some interaction, this must be deleted, too.
|
||||||
(let [interactions (:interactions shape)]
|
(let [interactions (:interactions shape)]
|
||||||
(some #(and (ctsi/has-destination %)
|
(some #(and (ctsi/has-destination %)
|
||||||
(contains? ids-to-delete (:destination %)))
|
(contains? ids-to-delete (:destination %)))
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
|
|
||||||
all-parents
|
all-parents
|
||||||
(reduce (fn [res id]
|
(reduce (fn [res id]
|
||||||
;; All parents of any deleted shape must be resized.
|
;; All parents of any deleted shape must be resized.
|
||||||
(into res (cfh/get-parent-ids objects id)))
|
(into res (cfh/get-parent-ids objects id)))
|
||||||
(d/ordered-set)
|
(d/ordered-set)
|
||||||
(concat ids-to-delete ids-to-hide))
|
(concat ids-to-delete ids-to-hide))
|
||||||
@@ -239,10 +239,10 @@
|
|||||||
(recursive-find-empty-parents parents))))
|
(recursive-find-empty-parents parents))))
|
||||||
|
|
||||||
empty-parents
|
empty-parents
|
||||||
;; Any parent whose children are all deleted, must be deleted too.
|
;; Any parent whose children are all deleted, must be deleted too.
|
||||||
;; If we want to specifically allow altering the copies, this is a special case,
|
;; If we want to specifically allow altering the copies, this is a special case,
|
||||||
;; for example during a component swap. in this case we are replacing a shape by
|
;; for example during a component swap. in this case we are replacing a shape by
|
||||||
;; other one, so must not delete empty parents.
|
;; other one, so must not delete empty parents.
|
||||||
(if-not allow-altering-copies
|
(if-not allow-altering-copies
|
||||||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||||
#{})
|
#{})
|
||||||
@@ -274,8 +274,8 @@
|
|||||||
guides-to-delete)
|
guides-to-delete)
|
||||||
|
|
||||||
changes (reduce (fn [changes component-id]
|
changes (reduce (fn [changes component-id]
|
||||||
;; It's important to delete the component before the main instance, because we
|
;; It's important to delete the component before the main instance, because we
|
||||||
;; need to store the instance position if we want to restore it later.
|
;; need to store the instance position if we want to restore it later.
|
||||||
(pcb/delete-component changes component-id (:id page)))
|
(pcb/delete-component changes component-id (:id page)))
|
||||||
changes
|
changes
|
||||||
components-to-delete)
|
components-to-delete)
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
result #{}]
|
result #{}]
|
||||||
|
|
||||||
(if-not current-id
|
(if-not current-id
|
||||||
;; Base case, no next element
|
;; Base case, no next element
|
||||||
result
|
result
|
||||||
|
|
||||||
(let [group (get objects current-id)]
|
(let [group (get objects current-id)]
|
||||||
@@ -335,14 +335,14 @@
|
|||||||
(not= current-id parent-id)
|
(not= current-id parent-id)
|
||||||
(empty? (remove removed-id? (:shapes group))))
|
(empty? (remove removed-id? (:shapes group))))
|
||||||
|
|
||||||
;; Adds group to the remove and check its parent
|
;; Adds group to the remove and check its parent
|
||||||
(let [to-check (concat to-check [(cfh/get-parent-id objects current-id)])]
|
(let [to-check (concat to-check [(cfh/get-parent-id objects current-id)])]
|
||||||
(recur (first to-check)
|
(recur (first to-check)
|
||||||
(rest to-check)
|
(rest to-check)
|
||||||
(conj removed-id? current-id)
|
(conj removed-id? current-id)
|
||||||
(conj result current-id)))
|
(conj result current-id)))
|
||||||
|
|
||||||
;; otherwise recur
|
;; otherwise recur
|
||||||
(recur (first to-check)
|
(recur (first to-check)
|
||||||
(rest to-check)
|
(rest to-check)
|
||||||
removed-id?
|
removed-id?
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
"Check if any ancestor of a shape (between base-parent-id and shape) was swapped"
|
"Check if any ancestor of a shape (between base-parent-id and shape) was swapped"
|
||||||
[shape objects base-parent-id]
|
[shape objects base-parent-id]
|
||||||
(let [ancestors (->> (ctn/get-parent-heads objects shape)
|
(let [ancestors (->> (ctn/get-parent-heads objects shape)
|
||||||
;; Ignore ancestors ahead of base-parent
|
;; Ignore ancestors ahead of base-parent
|
||||||
(drop-while #(not= base-parent-id (:id %)))
|
(drop-while #(not= base-parent-id (:id %)))
|
||||||
seq)
|
seq)
|
||||||
num-ancestors (count ancestors)
|
num-ancestors (count ancestors)
|
||||||
|
|||||||
@@ -222,7 +222,7 @@
|
|||||||
:else
|
:else
|
||||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||||
|
|
||||||
;; We add an end-of-line when finish a paragraph
|
;; We add an end-of-line when finish a paragraph
|
||||||
new-acc
|
new-acc
|
||||||
(if (= (:type node) "paragraph")
|
(if (= (:type node) "paragraph")
|
||||||
(let [[hs ht] (first new-acc)]
|
(let [[hs ht] (first new-acc)]
|
||||||
|
|||||||
@@ -458,13 +458,13 @@
|
|||||||
(map #(cfh/components-nesting-loop? objects (:id %) (:id parent)))
|
(map #(cfh/components-nesting-loop? objects (:id %) (:id parent)))
|
||||||
(every? nil?)))]
|
(every? nil?)))]
|
||||||
(or
|
(or
|
||||||
;;We don't want to change the structure of component copies
|
;;We don't want to change the structure of component copies
|
||||||
(ctk/in-component-copy? parent)
|
(ctk/in-component-copy? parent)
|
||||||
(has-any-copy-parent? objects parent)
|
(has-any-copy-parent? objects parent)
|
||||||
;; If we are moving something containing a main instance the container can't be part of a component (neither main nor copy)
|
;; If we are moving something containing a main instance the container can't be part of a component (neither main nor copy)
|
||||||
(and selected-main-instance? parent-in-component?)
|
(and selected-main-instance? parent-in-component?)
|
||||||
;; Avoid placing a shape as a direct or indirect child of itself,
|
;; Avoid placing a shape as a direct or indirect child of itself,
|
||||||
;; or inside its main component if it's in a copy.
|
;; or inside its main component if it's in a copy.
|
||||||
comps-nesting-loop?)))
|
comps-nesting-loop?)))
|
||||||
|
|
||||||
(defn find-valid-parent-and-frame-ids
|
(defn find-valid-parent-and-frame-ids
|
||||||
|
|||||||
@@ -775,9 +775,9 @@
|
|||||||
file-data (cond-> file-data
|
file-data (cond-> file-data
|
||||||
(d/not-empty? used-components)
|
(d/not-empty? used-components)
|
||||||
(absorb-components used-components library-data))
|
(absorb-components used-components library-data))
|
||||||
;; Note that absorbed components may also be using colors
|
;; Note that absorbed components may also be using colors
|
||||||
;; and typographies. This is the reason of doing this first
|
;; and typographies. This is the reason of doing this first
|
||||||
;; and accumulating file data for the next ones.
|
;; and accumulating file data for the next ones.
|
||||||
|
|
||||||
used-colors (find-asset-type-usages file-data library-data :color)
|
used-colors (find-asset-type-usages file-data library-data :color)
|
||||||
file-data (cond-> file-data
|
file-data (cond-> file-data
|
||||||
@@ -1017,7 +1017,7 @@
|
|||||||
libs-to-show
|
libs-to-show
|
||||||
(-> libs-to-show
|
(-> libs-to-show
|
||||||
(add-component library-id component-id))))))
|
(add-component library-id component-id))))))
|
||||||
;; (find-used-components-cumulative page root)
|
;; (find-used-components-cumulative page root)
|
||||||
|
|
||||||
libs-to-show
|
libs-to-show
|
||||||
components))
|
components))
|
||||||
|
|||||||
@@ -306,7 +306,7 @@
|
|||||||
|
|
||||||
(-write-to [_ heap offset]
|
(-write-to [_ heap offset]
|
||||||
(let [buffer' (.-buffer ^js/DataView dbuffer)
|
(let [buffer' (.-buffer ^js/DataView dbuffer)
|
||||||
;; Calculate byte size: 4 bytes header + (size * FILL-U8-SIZE)
|
;; Calculate byte size: 4 bytes header + (size * FILL-U8-SIZE)
|
||||||
byte-size (+ 4 (* size FILL-U8-SIZE))
|
byte-size (+ 4 (* size FILL-U8-SIZE))
|
||||||
;; Create Uint32Array with exact size needed (convert bytes to u32 elements)
|
;; Create Uint32Array with exact size needed (convert bytes to u32 elements)
|
||||||
u32-array (js/Uint32Array. buffer' 0 (/ byte-size 4))]
|
u32-array (js/Uint32Array. buffer' 0 (/ byte-size 4))]
|
||||||
|
|||||||
@@ -13,14 +13,15 @@
|
|||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.schema.generators :as sg]))
|
[app.common.schema.generators :as sg]))
|
||||||
|
|
||||||
;; WARNING: options are not deleted when changing event or action type, so it can be
|
;; WARNING: options are not deleted when changing event or action
|
||||||
;; restored if the user changes it back later.
|
;; type, so it can be restored if the user changes it back later.
|
||||||
;;
|
;;
|
||||||
;; But that means that an interaction may have for example a delay or
|
;; But that means that an interaction may have for example a delay or
|
||||||
;; destination, even if its type does not require it (but a previous type did).
|
;; destination, even if its type does not require it (but a previous
|
||||||
|
;; type did).
|
||||||
;;
|
;;
|
||||||
;; So make sure to use has-delay/has-destination... functions, or similar,
|
;; So make sure to use has-delay/has-destination... functions, or
|
||||||
;; before reading them.
|
;; similar, before reading them.
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; SCHEMA
|
;; SCHEMA
|
||||||
@@ -452,16 +453,15 @@
|
|||||||
(gpt/point 0 0)))
|
(gpt/point 0 0)))
|
||||||
|
|
||||||
(defn calc-overlay-position
|
(defn calc-overlay-position
|
||||||
[interaction ;; interaction data
|
[interaction ; interaction data
|
||||||
shape ;; Shape with the interaction
|
shape ; Shape with the interaction
|
||||||
objects ;; the objects tree
|
objects ; the objects tree
|
||||||
relative-to-shape ;; the interaction position is realtive to this
|
relative-to-shape ; the interaction position is realtive to this shape
|
||||||
;; sape
|
base-frame ; the base frame of the current interaction
|
||||||
base-frame ;; the base frame of the current interaction
|
dest-frame ; the frame to display with this interaction
|
||||||
dest-frame ;; the frame to display with this interaction
|
frame-offset] ; if this interaction starts in a frame opened
|
||||||
frame-offset] ;; if this interaction starts in a frame opened
|
; on another interaction, this is the position
|
||||||
;; on another interaction, this is the position
|
; of that frame
|
||||||
;; of that frame
|
|
||||||
(assert (check-interaction interaction))
|
(assert (check-interaction interaction))
|
||||||
(assert (has-overlay-opts interaction)
|
(assert (has-overlay-opts interaction)
|
||||||
"expected compatible interaction map")
|
"expected compatible interaction map")
|
||||||
|
|||||||
@@ -382,9 +382,9 @@
|
|||||||
keep-ids? (:id shape)
|
keep-ids? (:id shape)
|
||||||
:else (uuid/next))
|
:else (uuid/next))
|
||||||
|
|
||||||
;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame)
|
;; Assign the correct frame-id for the given parent. It's the parent-id (if parent is frame)
|
||||||
;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls
|
;; or the parent's frame-id otherwise. Only for the first cloned shapes. In recursive calls
|
||||||
;; this is not needed.
|
;; this is not needed.
|
||||||
frame-id (cond
|
frame-id (cond
|
||||||
(and (nil? frame-id) (cfh/frame-shape? dest-objects parent-id))
|
(and (nil? frame-id) (cfh/frame-shape? dest-objects parent-id))
|
||||||
parent-id
|
parent-id
|
||||||
|
|||||||
@@ -19,3 +19,10 @@
|
|||||||
|
|
||||||
(def schema:role
|
(def schema:role
|
||||||
[::sm/one-of {:title "TeamRole"} valid-roles])
|
[::sm/one-of {:title "TeamRole"} valid-roles])
|
||||||
|
|
||||||
|
;; FIXME: specify more fields
|
||||||
|
(def schema:team
|
||||||
|
[:map {:title "Team"}
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:name :string]])
|
||||||
|
|
||||||
|
|||||||
@@ -393,7 +393,7 @@
|
|||||||
:else
|
:else
|
||||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||||
|
|
||||||
;; We add an end-of-line when finish a paragraph
|
;; We add an end-of-line when finish a paragraph
|
||||||
new-acc
|
new-acc
|
||||||
(if (= (:type node) "paragraph")
|
(if (= (:type node) "paragraph")
|
||||||
(let [[hs ht] (first new-acc)]
|
(let [[hs ht] (first new-acc)]
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
|
|
||||||
(def token-name-ref
|
(def token-name-ref
|
||||||
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
[:re {:title "TokenNameRef" :gen/gen sg/text}
|
||||||
#"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"])
|
#"^[a-zA-Z0-9_-][a-zA-Z0-9$_-]*(\.[a-zA-Z0-9$_-]+)*$"])
|
||||||
|
|
||||||
(def ^:private schema:color
|
(def ^:private schema:color
|
||||||
[:map
|
[:map
|
||||||
@@ -474,8 +474,6 @@
|
|||||||
:height #{:sizing :dimensions}
|
:height #{:sizing :dimensions}
|
||||||
:max-width #{:sizing :dimensions}
|
:max-width #{:sizing :dimensions}
|
||||||
:max-height #{:sizing :dimensions}
|
:max-height #{:sizing :dimensions}
|
||||||
:min-width #{:sizing :dimensions}
|
|
||||||
:min-height #{:sizing :dimensions}
|
|
||||||
:x #{:dimensions}
|
:x #{:dimensions}
|
||||||
:y #{:dimensions}
|
:y #{:dimensions}
|
||||||
:rotation #{:number :rotation}
|
:rotation #{:number :rotation}
|
||||||
|
|||||||
@@ -693,7 +693,7 @@
|
|||||||
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||||
#{(:id main-child)}
|
#{(:id main-child)}
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
;; Update the attrs on all the content tree
|
;; Update the attrs on all the content tree
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
||||||
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
||||||
@@ -851,7 +851,7 @@
|
|||||||
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
|
||||||
#{(:id main-child)}
|
#{(:id main-child)}
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
;; Update the attrs on all the content tree
|
;; Update the attrs on all the content tree
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
|
||||||
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
(assoc-in [:content :children 0 :children 0 :font-size] "32")
|
||||||
|
|||||||
@@ -267,7 +267,7 @@
|
|||||||
page' (thf/current-page file')
|
page' (thf/current-page file')
|
||||||
objects' (:objects page')]
|
objects' (:objects page')]
|
||||||
|
|
||||||
;; ==== Check
|
;; ==== Check
|
||||||
(thf/validate-file! file')
|
(thf/validate-file! file')
|
||||||
(t/is (= (count (:components data)) 2))
|
(t/is (= (count (:components data)) 2))
|
||||||
(t/is (= (count (:components data')) 4))
|
(t/is (= (count (:components data')) 4))
|
||||||
|
|||||||
@@ -179,9 +179,9 @@ RUN set -eux; \
|
|||||||
|
|
||||||
FROM base AS setup-utils
|
FROM base AS setup-utils
|
||||||
|
|
||||||
ENV CLJKONDO_VERSION=2025.07.28 \
|
ENV CLJKONDO_VERSION=2026.01.19 \
|
||||||
BABASHKA_VERSION=1.12.208 \
|
BABASHKA_VERSION=1.12.208 \
|
||||||
CLJFMT_VERSION=0.13.1
|
CLJFMT_VERSION=0.15.6
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
ARCH="$(dpkg --print-architecture)"; \
|
ARCH="$(dpkg --print-architecture)"; \
|
||||||
@@ -398,7 +398,6 @@ COPY files/Caddyfile /home/
|
|||||||
COPY files/selfsigned.crt /home/
|
COPY files/selfsigned.crt /home/
|
||||||
COPY files/selfsigned.key /home/
|
COPY files/selfsigned.key /home/
|
||||||
COPY files/start-tmux.sh /home/start-tmux.sh
|
COPY files/start-tmux.sh /home/start-tmux.sh
|
||||||
COPY files/start-tmux-back.sh /home/start-tmux-back.sh
|
|
||||||
COPY files/entrypoint.sh /home/entrypoint.sh
|
COPY files/entrypoint.sh /home/entrypoint.sh
|
||||||
COPY files/init.sh /home/init.sh
|
COPY files/init.sh /home/init.sh
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
auto_https off
|
auto_https off
|
||||||
}
|
}
|
||||||
|
|
||||||
localhost:3449 {
|
localhost:3449 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
tls /home/selfsigned.crt /home/selfsigned.key
|
tls /home/selfsigned.crt /home/selfsigned.key
|
||||||
|
header -Strict-Transport-Security
|
||||||
}
|
}
|
||||||
|
|
||||||
http://localhost:3450 {
|
http://localhost:3450 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
}
|
}
|
||||||
|
|
||||||
http://penpot-devenv-main:3450 {
|
http://penpot-devenv-main:3450 {
|
||||||
reverse_proxy localhost:4449
|
reverse_proxy localhost:4449
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,8 +141,14 @@ http {
|
|||||||
proxy_pass http://127.0.0.1:5000;
|
proxy_pass http://127.0.0.1:5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /nitrate/ {
|
location /control-center {
|
||||||
proxy_pass http://127.0.0.1:3000/;
|
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 /wasm-playground {
|
location /wasm-playground {
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ update_flags /var/www/app/js/config.js
|
|||||||
|
|
||||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
|
||||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
|
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
|
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_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE" \
|
||||||
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
|
< /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)"
|
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
|
||||||
|
|||||||
@@ -139,6 +139,14 @@ http {
|
|||||||
proxy_pass $PENPOT_BACKEND_URI/ws/notifications;
|
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;
|
include /etc/nginx/overrides/server.d/*.conf;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ desc: Create, deploy, and use the Penpot plugin API with our comprehensive docum
|
|||||||
|
|
||||||
# Penpot plugins API
|
# Penpot plugins API
|
||||||
|
|
||||||
We've got all the documentation you need for the API right <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/">here</a>.
|
We've got all the documentation you need for the API right <a target="_blank" href="https://doc.plugins.penpot.app/">here</a>.
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ desc: See the Penpot plugin API changelog for version 1.0! Find breaking changes
|
|||||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f680.png"></g-emoji> Epics and highlights</code>
|
||||||
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
- This marks the release of version 1.0, and from this point forward, we’ll do our best to avoid making any more breaking changes (or make deprecations backward compatible).
|
||||||
- We’ve redone the documentation. You can check the API here:
|
- We’ve redone the documentation. You can check the API here:
|
||||||
[https://penpot-plugins-api-doc.pages.dev/](https://penpot-plugins-api-doc.pages.dev/)
|
[https://doc.plugins.penpot.app/](https://doc.plugins.penpot.app/)
|
||||||
- New samples repository with lots of samples to use the API:
|
- New samples repository with lots of samples to use the API:
|
||||||
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
[https://github.com/penpot/penpot-plugins-samples](https://github.com/penpot/penpot-plugins-samples)
|
||||||
|
|
||||||
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
### <g-emoji class="g-emoji" alias="boom" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"><img class="emoji" alt="boom" height="20" width="20" src="https://github.githubassets.com/images/icons/emoji/unicode/1f4a5.png"></g-emoji> Breaking changes & Deprecations
|
||||||
|
|
||||||
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://penpot-plugins-api-doc.pages.dev/) for more details.
|
- Changed types names to remove the Penpot prefix. So for example: <code class="language-js">PenpotShape</code> becomes <code class="language-js">Shape</code>; <code class="language-js">PenpotFile</code> becomes <code class="language-js">File</code>, and so on. Check the [API documentation](https://doc.plugins.penpot.app/) for more details.
|
||||||
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
- Changes on the <code class="language-js">penpot.on</code> and <code class="language-js">penpot.off</code> methods.
|
||||||
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
Previously you had to send the original callback to the off method in order to remove an event listener. Now, <code class="language-js">penpot.on</code> will return an *id* that you can pass to the <code class="language-js">penpot.off</code> method in order to remove the listener.
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ There are two libraries that can help you with your plugin's development. They a
|
|||||||
|
|
||||||
### Plugin styles
|
### Plugin styles
|
||||||
|
|
||||||
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">Plugin styles</a>.
|
<code class="language-js">@penpot/plugin-styles</code> contains styles to help build the UI for Penpot plugins. To check the styles go to <a target="_blank" href="https://styles-doc.plugins.penpot.app/">Plugin styles</a>.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @penpot/plugin-styles
|
npm install @penpot/plugin-styles
|
||||||
@@ -139,7 +139,7 @@ parent.postMessage(responseMessage, targetOrigin);
|
|||||||
|
|
||||||
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
By using these message-based events, any data retrieved through the Penpot API can be communicated to and from your plugin interface seamlessly.
|
||||||
|
|
||||||
For more detailed information, refer to the [Penpot Plugins API Documentation](https://penpot-plugins-api-doc.pages.dev/).
|
For more detailed information, refer to the [Penpot Plugins API Documentation](https://doc.plugins.penpot.app/).
|
||||||
|
|
||||||
## 2.5. Step 5. Build the plugin file
|
## 2.5. Step 5. Build the plugin file
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ penpot.library.local.createTypography();
|
|||||||
|
|
||||||
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
Penpot has dark and light modes, and you can easily add this to your plugin so your interface adapts to both themes. When you add theme support, your plugin will automatically sync with Penpot's interface settings, so the user experience is consistent no matter which mode is selected. This makes your plugin look better and also ensures it stays in line with Penpot's overall design.
|
||||||
|
|
||||||
Just a heads-up: if you use the <a target="_blank" href="https://penpot-plugins-styles.pages.dev/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
Just a heads-up: if you use the <a target="_blank" href="https://styles-doc.plugins.penpot.app/">plugin-styles library</a>, many elements will automatically adapt to dark or light mode without any extra effort from you. However, if you need to customize specific elements, be sure to use the selectors provided in the <code class="language-bash">styles.css</code> of the example.
|
||||||
|
|
||||||
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
<a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/theme">Theme example</a>
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ The plugin <a target="_blank" href="https://www.npmjs.com/package/@penpot/plugin
|
|||||||
|
|
||||||
### Is the API ready to use the prototyping features?
|
### Is the API ready to use the prototyping features?
|
||||||
|
|
||||||
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotFlow">PenpotFlow</a> or <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/PenpotInteraction">PenpotInteraction</a> interfaces.
|
Absolutely! You can definitely create flows and interactions in the same elements as in the interface, like frames, shapes, and groups. Just check out the API documentation for the methods: createFlow, addInteraction, or removeInteraction. And if you need more help, you can always check out the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Flow">Flow</a> or <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Interaction">Interaction</a> interfaces.
|
||||||
|
|
||||||
### Are there any security or quality criteria I should be aware of?
|
### Are there any security or quality criteria I should be aware of?
|
||||||
|
|
||||||
@@ -48,7 +48,8 @@ There are no set requirements. However, we can recommend the use of <a target="_
|
|||||||
|
|
||||||
### Is it necessary to create plugins with a UI?
|
### Is it necessary to create plugins with a UI?
|
||||||
|
|
||||||
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette-penpot-plugin.pages.dev/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot-plugins/tree/main/apps/create-palette-plugin">here</a>
|
No, it’s completely optional, in fact, we have an example of a plugin without UI. Try the plugin using this url to install it: <code class="language-js">https:\/\/create-palette.plugins.penpot.app/assets/manifest.json</code> or check the code <a target="_blank" href="https://github.com/penpot/penpot/tree/main/plugins/apps/create-palette-plugin">here</a>
|
||||||
|
|
||||||
|
|
||||||
### Can I create components?
|
### Can I create components?
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ Yes, it is possible to create components using:
|
|||||||
createComponent(shapes: Shape[]): LibraryComponent;
|
createComponent(shapes: Shape[]): LibraryComponent;
|
||||||
```
|
```
|
||||||
|
|
||||||
Take a look at the Penpot Library methods in the <a target="_blank" href="https://penpot-plugins-api-doc.pages.dev/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
Take a look at the Penpot Library methods in the <a target="_blank" href="https://doc.plugins.penpot.app/interfaces/Library">API documentation</a> or this <a target="_blank" href="https://github.com/penpot/penpot-plugins-samples/tree/main/components-library">simple example</a>.
|
||||||
|
|
||||||
### Is there a place where I can share my plugin?
|
### Is there a place where I can share my plugin?
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,13 @@ You need to provide the plugin's manifest URL for the installation. If there are
|
|||||||
|
|
||||||
| Name | URL |
|
| Name | URL |
|
||||||
| ------------- | ------------------------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------- |
|
||||||
| Lorem Ipsum | https://lorem-ipsum-penpot-plugin.pages.dev/assets/manifest.json |
|
| Color palette | https://create-palette.plugins.penpot.app/assets/manifest.json |
|
||||||
| Contrast | https://contrast-penpot-plugin.pages.dev/assets/manifest.json |
|
| Contrast | https://contrast.plugins.penpot.app/assets/manifest.json |
|
||||||
| Feather icons | https://icons-penpot-plugin.pages.dev/assets/manifest.json |
|
| Feather icons | https://icons.plugins.penpot.app/assets/manifest.json |
|
||||||
| Tables | https://table-penpot-plugin.pages.dev/assets/manifest.json |
|
| Lorem ipsum | https://lorem-ipsum.plugins.penpot.app/assets/manifest.json |
|
||||||
| Color palette | https://create-palette-penpot-plugin.pages.dev/assets/manifest.json |
|
| Rename layers | https://rename-layers.plugins.penpot.app/assets/manifest.json |
|
||||||
| Rename layers | https://rename-layers-penpot-plugin.pages.dev/assets/manifest.json |
|
| Tables | https://table.plugins.penpot.app/assets/manifest.json |
|
||||||
|
|
||||||
|
|
||||||
## 1.4. Plugin's basics
|
## 1.4. Plugin's basics
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/penpot/penpot"
|
"url": "https://github.com/penpot/penpot"
|
||||||
|
|||||||
@@ -12,11 +12,14 @@
|
|||||||
["node:process" :as process]
|
["node:process" :as process]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.flags :as flags]
|
[app.common.flags :as flags]
|
||||||
|
[app.common.logging :as l]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.version :as v]
|
[app.common.version :as v]
|
||||||
[cljs.core :as c]
|
[cljs.core :as c]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
(l/set-level! :info)
|
||||||
|
|
||||||
(def ^:private defaults
|
(def ^:private defaults
|
||||||
{:public-uri "http://localhost:3449"
|
{:public-uri "http://localhost:3449"
|
||||||
:tenant "default"
|
:tenant "default"
|
||||||
@@ -30,7 +33,7 @@
|
|||||||
[:map {:title "config"}
|
[:map {:title "config"}
|
||||||
[:secret-key :string]
|
[:secret-key :string]
|
||||||
[:public-uri {:optional true} ::sm/uri]
|
[:public-uri {:optional true} ::sm/uri]
|
||||||
[:management-api-key {:optional true} :string]
|
[:exporter-shared-key {:optional true} :string]
|
||||||
[:host {:optional true} :string]
|
[:host {:optional true} :string]
|
||||||
[:tenant {:optional true} :string]
|
[:tenant {:optional true} :string]
|
||||||
[:flags {:optional true} [::sm/set :keyword]]
|
[:flags {:optional true} [::sm/set :keyword]]
|
||||||
@@ -98,8 +101,10 @@
|
|||||||
(c/get config key default)))
|
(c/get config key default)))
|
||||||
|
|
||||||
(def management-key
|
(def management-key
|
||||||
(or (c/get config :management-api-key)
|
(let [key (or (c/get config :exporter-shared-key)
|
||||||
(let [secret-key (c/get config :secret-key)
|
(let [secret-key (c/get config :secret-key)
|
||||||
derived-key (crypto/hkdfSync "blake2b512" secret-key, "management" "" 32)]
|
derived-key (crypto/hkdfSync "blake2b512" secret-key, "exporter" "" 32)]
|
||||||
(-> (.from buffer/Buffer derived-key)
|
(-> (.from buffer/Buffer derived-key)
|
||||||
(.toString "base64url")))))
|
(.toString "base64url"))))]
|
||||||
|
(l/inf :hint "exporter key initialized" :key (d/obfuscate-string key))
|
||||||
|
key))
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
(p/mcat (fn [blob]
|
(p/mcat (fn [blob]
|
||||||
(let [fdata (new http/FormData)
|
(let [fdata (new http/FormData)
|
||||||
agent (new http/Agent #js {:connect #js {:rejectUnauthorized false}})
|
agent (new http/Agent #js {:connect #js {:rejectUnauthorized false}})
|
||||||
headers #js {"X-Shared-Key" cf/management-key
|
headers #js {"X-Shared-Key" (str "exporter " cf/management-key)
|
||||||
"Authorization" (str "Bearer " auth-token)}
|
"Authorization" (str "Bearer " auth-token)}
|
||||||
|
|
||||||
request #js {:headers headers
|
request #js {:headers headers
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"author": "Kaleidos INC",
|
"author": "Kaleidos INC",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
"packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264",
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"defaults"
|
"defaults"
|
||||||
],
|
],
|
||||||
|
|||||||
155
frontend/playwright/data/render-wasm/get-file-flex-layouts.json
Normal file
155
frontend/playwright/data/render-wasm/get-file-flex-layouts.json
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
{
|
||||||
|
"~:features": {
|
||||||
|
"~#set": [
|
||||||
|
"fdata/path-data",
|
||||||
|
"design-tokens/v1",
|
||||||
|
"variants/v1",
|
||||||
|
"layout/grid",
|
||||||
|
"fdata/objects-map",
|
||||||
|
"components/v2",
|
||||||
|
"fdata/shape-data-type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:team-id": "~ud7430f09-4f59-8049-8007-6277bb7586f6",
|
||||||
|
"~:permissions": {
|
||||||
|
"~:type": "~:membership",
|
||||||
|
"~:is-owner": true,
|
||||||
|
"~:is-admin": true,
|
||||||
|
"~:can-edit": true,
|
||||||
|
"~:can-read": true,
|
||||||
|
"~:is-logged": true
|
||||||
|
},
|
||||||
|
"~:has-media-trimmed": false,
|
||||||
|
"~:comment-thread-seqn": 0,
|
||||||
|
"~:name": "flex_index_position",
|
||||||
|
"~:revn": 114,
|
||||||
|
"~:modified-at": "~m1769430362161",
|
||||||
|
"~:vern": 0,
|
||||||
|
"~:id": "~u31fe2e21-73e7-80f3-8007-73894fb58240",
|
||||||
|
"~:is-shared": false,
|
||||||
|
"~:migrations": {
|
||||||
|
"~#ordered-set": [
|
||||||
|
"legacy-2",
|
||||||
|
"legacy-3",
|
||||||
|
"legacy-5",
|
||||||
|
"legacy-6",
|
||||||
|
"legacy-7",
|
||||||
|
"legacy-8",
|
||||||
|
"legacy-9",
|
||||||
|
"legacy-10",
|
||||||
|
"legacy-11",
|
||||||
|
"legacy-12",
|
||||||
|
"legacy-13",
|
||||||
|
"legacy-14",
|
||||||
|
"legacy-16",
|
||||||
|
"legacy-17",
|
||||||
|
"legacy-18",
|
||||||
|
"legacy-19",
|
||||||
|
"legacy-25",
|
||||||
|
"legacy-26",
|
||||||
|
"legacy-27",
|
||||||
|
"legacy-28",
|
||||||
|
"legacy-29",
|
||||||
|
"legacy-31",
|
||||||
|
"legacy-32",
|
||||||
|
"legacy-33",
|
||||||
|
"legacy-34",
|
||||||
|
"legacy-36",
|
||||||
|
"legacy-37",
|
||||||
|
"legacy-38",
|
||||||
|
"legacy-39",
|
||||||
|
"legacy-40",
|
||||||
|
"legacy-41",
|
||||||
|
"legacy-42",
|
||||||
|
"legacy-43",
|
||||||
|
"legacy-44",
|
||||||
|
"legacy-45",
|
||||||
|
"legacy-46",
|
||||||
|
"legacy-47",
|
||||||
|
"legacy-48",
|
||||||
|
"legacy-49",
|
||||||
|
"legacy-50",
|
||||||
|
"legacy-51",
|
||||||
|
"legacy-52",
|
||||||
|
"legacy-53",
|
||||||
|
"legacy-54",
|
||||||
|
"legacy-55",
|
||||||
|
"legacy-56",
|
||||||
|
"legacy-57",
|
||||||
|
"legacy-59",
|
||||||
|
"legacy-62",
|
||||||
|
"legacy-65",
|
||||||
|
"legacy-66",
|
||||||
|
"legacy-67",
|
||||||
|
"0001-remove-tokens-from-groups",
|
||||||
|
"0002-normalize-bool-content-v2",
|
||||||
|
"0002-clean-shape-interactions",
|
||||||
|
"0003-fix-root-shape",
|
||||||
|
"0003-convert-path-content-v2",
|
||||||
|
"0005-deprecate-image-type",
|
||||||
|
"0006-fix-old-texts-fills",
|
||||||
|
"0008-fix-library-colors-v4",
|
||||||
|
"0009-clean-library-colors",
|
||||||
|
"0009-add-partial-text-touched-flags",
|
||||||
|
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||||
|
"0011-fix-invalid-text-touched-flags",
|
||||||
|
"0012-fix-position-data",
|
||||||
|
"0013-fix-component-path",
|
||||||
|
"0013-clear-invalid-strokes-and-fills",
|
||||||
|
"0014-fix-tokens-lib-duplicate-ids",
|
||||||
|
"0014-clear-components-nil-objects",
|
||||||
|
"0015-fix-text-attrs-blank-strings",
|
||||||
|
"0015-clean-shadow-color",
|
||||||
|
"0016-copy-fills-from-position-data-to-text-node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"~:version": 67,
|
||||||
|
"~:project-id": "~ud7430f09-4f59-8049-8007-6277bb765abd",
|
||||||
|
"~:created-at": "~m1769007798998",
|
||||||
|
"~:backend": "legacy-db",
|
||||||
|
"~:data": {
|
||||||
|
"~:pages": [
|
||||||
|
"~u02e9633d-4ce7-80da-8007-736558496fa8"
|
||||||
|
],
|
||||||
|
"~:pages-index": {
|
||||||
|
"~u02e9633d-4ce7-80da-8007-736558496fa8": {
|
||||||
|
"~:id": "~u02e9633d-4ce7-80da-8007-736558496fa8",
|
||||||
|
"~:name": "Page 1",
|
||||||
|
"~:objects": {
|
||||||
|
"~#penpot/objects-map/v2": {
|
||||||
|
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^I\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~u77c71dba-32ee-804c-8007-736561cf857f\"]]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff457": "[\"~#shape\",[\"^ \",\"~:y\",396.00000357564704,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",396.00000357564704]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",476.00000357564704]],[\"^>\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",476.00000357564704]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff457\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704,\"^9\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",396.00000357564704,\"~:x2\",768.9999775886536,\"~:y2\",476.00000357564704]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2885": "[\"~#shape\",[\"^ \",\"~:y\",612.0000188344361,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9999165534973,\"~:y\",612.0000188344361]],[\"^>\",[\"^ \",\"~:x\",684.9999165534973,\"~:y\",612.0000188344361]],[\"^>\",[\"^ \",\"~:x\",684.9999165534973,\"~:y\",692.0000188344361]],[\"^>\",[\"^ \",\"~:x\",604.9999165534973,\"~:y\",692.0000188344361]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2885\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:strokes\",[],\"~:x\",604.9999165534973,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9999165534973,\"~:y\",612.0000188344361,\"^9\",80,\"~:height\",80,\"~:x1\",604.9999165534973,\"~:y1\",612.0000188344361,\"~:x2\",684.9999165534973,\"~:y2\",692.0000188344361]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2886": "[\"~#shape\",[\"^ \",\"~:y\",636.0000188344361,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",611.9999165534973,\"~:y\",636.0000188344361]],[\"^K\",[\"^ \",\"~:x\",677.9999165534973,\"~:y\",636.0000188344361]],[\"^K\",[\"^ \",\"~:x\",677.9999165534973,\"~:y\",668.0000188344361]],[\"^K\",[\"^ \",\"~:x\",611.9999165534973,\"~:y\",668.0000188344361]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:strokes\",[],\"~:x\",611.9999165534973,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",611.9999165534973,\"~:y\",636.0000188344361,\"^E\",66,\"~:height\",32,\"~:x1\",611.9999165534973,\"~:y1\",636.0000188344361,\"~:x2\",677.9999165534973,\"~:y2\",668.0000188344361]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508aa2887\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2887": "[\"~#shape\",[\"^ \",\"~:y\",644.0000188344361,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",623.9999165534973,\"~:y\",644.0000188344361]],[\"^K\",[\"^ \",\"~:x\",665.9999165534973,\"~:y\",644.0000188344361]],[\"^K\",[\"^ \",\"~:x\",665.9999165534973,\"~:y\",660.0000188344361]],[\"^K\",[\"^ \",\"~:x\",623.9999165534973,\"~:y\",660.0000188344361]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2887\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\",\"~:strokes\",[],\"~:x\",623.9999165534973,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",623.9999165534973,\"~:y\",644.0000188344361,\"^D\",42,\"~:height\",16,\"~:x1\",623.9999165534973,\"~:y1\",644.0000188344361,\"~:x2\",665.9999165534973,\"~:y2\",660.0000188344361]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508aa2888\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508aa2888": "[\"~#shape\",[\"^ \",\"~:y\",645.0000188344363,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",625.9999165534973,\"~:y\",645.0000188344363]],[\"^S\",[\"^ \",\"~:x\",663.9999165534973,\"~:y\",645.0000188344363]],[\"^S\",[\"^ \",\"~:x\",663.9999165534973,\"~:y\",660.0000188344359]],[\"^S\",[\"^ \",\"~:x\",625.9999165534973,\"~:y\",660.0000188344363]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2888\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2887\",\"~:position-data\",[[\"^ \",\"~:y\",659.3400268554688,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.94000244140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",626.0299682617188,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.08001708984375,\"^L\",\"Label\"]],\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508aa2887\",\"~:strokes\",[],\"~:x\",625.9999165534973,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",625.9999165534973,\"~:y\",645.0000188344363,\"^Q\",38,\"^11\",15,\"~:x1\",625.9999165534973,\"~:y1\",645.0000188344363,\"~:x2\",663.9999165534973,\"~:y2\",660.0000188344363]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff45a": "[\"~#shape\",[\"^ \",\"~:y\",429.00000357564727,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",429.00000357564727]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",429.00000357564727]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",444.0000035756468]],[\"^S\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",444.00000357564727]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff45a\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cff459\",\"~:position-data\",[[\"^ \",\"~:y\",443.3399963378906,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.93994140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",710.030029296875,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.079986572265625,\"^L\",\"Label\"]],\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cff459\",\"~:strokes\",[],\"~:x\",709.9999775886536,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",429.00000357564727,\"^Q\",38,\"^11\",15,\"~:x1\",709.9999775886536,\"~:y1\",429.00000357564727,\"~:x2\",747.9999775886536,\"~:y2\",444.00000357564727]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff459": "[\"~#shape\",[\"^ \",\"~:y\",428.00000357564704,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",428.00000357564704]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",428.00000357564704]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",444.00000357564704]],[\"^K\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",444.00000357564704]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff459\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cff458\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cff458\",\"~:strokes\",[],\"~:x\",707.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",428.00000357564704,\"^D\",42,\"~:height\",16,\"~:x1\",707.9999775886536,\"~:y1\",428.00000357564704,\"~:x2\",749.9999775886536,\"~:y2\",444.00000357564704]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cff45a\"]]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cff458": "[\"~#shape\",[\"^ \",\"~:y\",420.00000357564704,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",420.00000357564704]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",420.00000357564704]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",452.00000357564704]],[\"^K\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",452.00000357564704]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cff458\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:strokes\",[],\"~:x\",695.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",420.00000357564704,\"^E\",66,\"~:height\",32,\"~:x1\",695.9999775886536,\"~:y1\",420.00000357564704,\"~:x2\",761.9999775886536,\"~:y2\",452.00000357564704]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cff459\"]]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cf857f": "[\"~#shape\",[\"^ \",\"~:y\",395.99997913999186,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 1\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",593.0000386238098,\"~:y\",395.99997913999186]],[\"^J\",[\"^ \",\"~:x\",865.0000386238098,\"~:y\",395.99997913999186]],[\"^J\",[\"^ \",\"~:x\",865.0000386238098,\"~:y\",475.9999669761459]],[\"^J\",[\"^ \",\"~:x\",593.0000386238098,\"~:y\",475.9999669761459]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cf857f\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",593.0000386238098,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",593.0000386238098,\"~:y\",395.99997913999186,\"^D\",272,\"~:height\",79.99998783615405,\"~:x1\",593.0000386238098,\"~:y1\",395.99997913999186,\"~:x2\",865.0000386238098,\"~:y2\",475.9999669761459]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.99998783615405,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cf8584\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d50980078e": "[\"~#shape\",[\"^ \",\"~:y\",720.0000478045426,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 4\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",592.9998555183411,\"~:y\",720.0000478045426]],[\"^J\",[\"^ \",\"~:x\",864.9998555183411,\"~:y\",720.0000478045426]],[\"^J\",[\"^ \",\"~:x\",864.9998555183411,\"~:y\",800.0000356406968]],[\"^J\",[\"^ \",\"~:x\",592.9998555183411,\"~:y\",800.0000356406968]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:column-reverse\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",592.9998555183411,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",592.9998555183411,\"~:y\",720.0000478045426,\"^D\",272,\"~:height\",79.9999878361541,\"~:x1\",592.9998555183411,\"~:y1\",720.0000478045426,\"~:x2\",864.9998555183411,\"~:y2\",800.0000356406968]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.9999878361541,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d50980078f\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d50980078f": "[\"~#shape\",[\"^ \",\"~:y\",719.9999806874634,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",719.9999806874634]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",719.9999806874634]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",799.9999806874634]],[\"^;\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",799.9999806874634]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078e\",\"~:strokes\",[],\"~:x\",604.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",719.9999806874634,\"^7\",80,\"~:height\",80,\"~:x1\",604.9999775886536,\"~:y1\",719.9999806874634,\"~:x2\",684.9999775886536,\"~:y2\",799.9999806874634]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d509800790\",\"~u94eaebe4-addd-80d1-8007-79d509800791\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508a9dc2f": "[\"~#shape\",[\"^ \",\"~:y\",612.000024916359,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 3\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",592.9999165534973,\"~:y\",612.000024916359]],[\"^J\",[\"^ \",\"~:x\",864.9999165534973,\"~:y\",612.000024916359]],[\"^J\",[\"^ \",\"~:x\",864.9999165534973,\"~:y\",692.0000127525132]],[\"^J\",[\"^ \",\"~:x\",592.9999165534973,\"~:y\",692.0000127525132]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",592.9999165534973,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",592.9999165534973,\"~:y\",612.000024916359,\"^D\",272,\"~:height\",79.9999878361541,\"~:x1\",592.9999165534973,\"~:y1\",612.000024916359,\"~:x2\",864.9999165534973,\"~:y2\",692.0000127525132]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.9999878361541,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800790": "[\"~#shape\",[\"^ \",\"~:y\",720.0000417226197,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9998555183411,\"~:y\",720.0000417226197]],[\"^>\",[\"^ \",\"~:x\",684.9998555183411,\"~:y\",720.0000417226197]],[\"^>\",[\"^ \",\"~:x\",684.9998555183411,\"~:y\",800.0000417226197]],[\"^>\",[\"^ \",\"~:x\",604.9998555183411,\"~:y\",800.0000417226197]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800790\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:strokes\",[],\"~:x\",604.9998555183411,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9998555183411,\"~:y\",720.0000417226197,\"^9\",80,\"~:height\",80,\"~:x1\",604.9998555183411,\"~:y1\",720.0000417226197,\"~:x2\",684.9998555183411,\"~:y2\",800.0000417226197]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d508a9dc30": "[\"~#shape\",[\"^ \",\"~:y\",612.0000188344361,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",612.0000188344361]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",612.0000188344361]],[\"^;\",[\"^ \",\"~:x\",684.9999775886536,\"~:y\",692.0000188344361]],[\"^;\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",692.0000188344361]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc30\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d508a9dc2f\",\"~:strokes\",[],\"~:x\",604.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",604.9999775886536,\"~:y\",612.0000188344361,\"^7\",80,\"~:height\",80,\"~:x1\",604.9999775886536,\"~:y1\",612.0000188344361,\"~:x2\",684.9999775886536,\"~:y2\",692.0000188344361]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d508aa2885\",\"~u94eaebe4-addd-80d1-8007-79d508aa2886\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800791": "[\"~#shape\",[\"^ \",\"~:y\",744.0000417226197,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",611.9998555183411,\"~:y\",744.0000417226197]],[\"^K\",[\"^ \",\"~:x\",677.9998555183411,\"~:y\",744.0000417226197]],[\"^K\",[\"^ \",\"~:x\",677.9998555183411,\"~:y\",776.0000417226197]],[\"^K\",[\"^ \",\"~:x\",611.9998555183411,\"~:y\",776.0000417226197]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800791\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d50980078f\",\"~:strokes\",[],\"~:x\",611.9998555183411,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",611.9998555183411,\"~:y\",744.0000417226197,\"^E\",66,\"~:height\",32,\"~:x1\",611.9998555183411,\"~:y1\",744.0000417226197,\"~:x2\",677.9998555183411,\"~:y2\",776.0000417226197]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d509800792\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800792": "[\"~#shape\",[\"^ \",\"~:y\",752.0000417226197,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",623.9998555183411,\"~:y\",752.0000417226197]],[\"^K\",[\"^ \",\"~:x\",665.9998555183411,\"~:y\",752.0000417226197]],[\"^K\",[\"^ \",\"~:x\",665.9998555183411,\"~:y\",768.0000417226197]],[\"^K\",[\"^ \",\"~:x\",623.9998555183411,\"~:y\",768.0000417226197]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800792\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d509800791\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d509800791\",\"~:strokes\",[],\"~:x\",623.9998555183411,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",623.9998555183411,\"~:y\",752.0000417226197,\"^D\",42,\"~:height\",16,\"~:x1\",623.9998555183411,\"~:y1\",752.0000417226197,\"~:x2\",665.9998555183411,\"~:y2\",768.0000417226197]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d509800793\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d509800793": "[\"~#shape\",[\"^ \",\"~:y\",753.0000417226199,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",625.9998555183411,\"~:y\",753.0000417226199]],[\"^S\",[\"^ \",\"~:x\",663.9998555183411,\"~:y\",753.0000417226199]],[\"^S\",[\"^ \",\"~:x\",663.9998555183411,\"~:y\",768.0000417226195]],[\"^S\",[\"^ \",\"~:x\",625.9998555183411,\"~:y\",768.0000417226199]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d509800793\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d509800792\",\"~:position-data\",[[\"^ \",\"~:y\",767.340087890625,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.93994140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",626.0299072265625,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.08001708984375,\"^L\",\"Label\"]],\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d509800792\",\"~:strokes\",[],\"~:x\",625.9998555183411,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",625.9998555183411,\"~:y\",753.0000417226199,\"^Q\",38,\"^11\",15,\"~:x1\",625.9998555183411,\"~:y1\",753.0000417226199,\"~:x2\",663.9998555183411,\"~:y2\",768.0000417226199]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]",
|
||||||
|
"~u77c71dba-32ee-804c-8007-736561cf8584": "[\"~#shape\",[\"^ \",\"~:y\",396.00000357564704,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",396.00000357564704]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",476.00000357564704]],[\"^;\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",476.00000357564704]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u77c71dba-32ee-804c-8007-736561cf8584\",\"~:parent-id\",\"~u77c71dba-32ee-804c-8007-736561cf857f\",\"~:frame-id\",\"~u77c71dba-32ee-804c-8007-736561cf857f\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",396.00000357564704,\"^7\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",396.00000357564704,\"~:x2\",768.9999775886536,\"~:y2\",476.00000357564704]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u77c71dba-32ee-804c-8007-736561cff457\",\"~u77c71dba-32ee-804c-8007-736561cff458\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d6859": "[\"~#shape\",[\"^ \",\"~:y\",504.00000202817546,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",12,\"~:p3\",0,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:wrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Board Parent 2\",\"~:layout-align-items\",\"~:start\",\"~:width\",272,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",592.9999775886536,\"~:y\",504.00000202817546]],[\"^J\",[\"^ \",\"~:x\",864.9999775886536,\"~:y\",504.00000202817546]],[\"^J\",[\"^ \",\"~:x\",864.9999775886536,\"~:y\",583.9999898643296]],[\"^J\",[\"^ \",\"~:x\",592.9999775886536,\"~:y\",583.9999898643296]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-item-v-sizing\",\"~:fix\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-flex-dir\",\"~:row-reverse\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",592.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",592.9999775886536,\"~:y\",504.00000202817546,\"^D\",272,\"~:height\",79.9999878361541,\"~:x1\",592.9999775886536,\"~:y1\",504.00000202817546,\"~:x2\",864.9999775886536,\"~:y2\",583.9999898643296]],\"~:fills\",[],\"~:flip-x\",null,\"^15\",79.9999878361541,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685a\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685a": "[\"~#shape\",[\"^ \",\"~:y\",503.9999959462525,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:hide-in-viewer\",true,\"~:name\",\"Board Child\",\"~:width\",80,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",503.9999959462525]],[\"^;\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",583.9999959462525]],[\"^;\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",583.9999959462525]]],\"~:show-content\",true,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d6859\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525,\"^7\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",503.9999959462525,\"~:x2\",768.9999775886536,\"~:y2\",583.9999959462525]],\"~:fills\",[],\"~:flip-x\",null,\"^K\",80,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685b\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685b": "[\"~#shape\",[\"^ \",\"~:y\",503.9999959462525,\"~:rx\",8,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",80,\"~:transforming\",false,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",503.9999959462525]],[\"^>\",[\"^ \",\"~:x\",768.9999775886536,\"~:y\",583.9999959462525]],[\"^>\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",583.9999959462525]]],\"~:r2\",8,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685b\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:strokes\",[],\"~:x\",688.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",688.9999775886536,\"~:y\",503.9999959462525,\"^9\",80,\"~:height\",80,\"~:x1\",688.9999775886536,\"~:y1\",503.9999959462525,\"~:x2\",768.9999775886536,\"~:y2\",583.9999959462525]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#e8e9ea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^M\",80,\"~:flip-y\",null]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685c": "[\"~#shape\",[\"^ \",\"~:y\",527.9999959462525,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:rx\",8,\"~:layout-padding\",[\"^ \",\"~:p1\",8,\"~:p2\",12,\"~:p3\",8,\"~:p4\",12],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"Dark / Button / Primary / Text / Default\",\"~:layout-align-items\",\"~:center\",\"~:width\",66,\"~:layout-padding-type\",\"~:simple\",\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",527.9999959462525]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",527.9999959462525]],[\"^K\",[\"^ \",\"~:x\",761.9999775886536,\"~:y\",559.9999959462525]],[\"^K\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",559.9999959462525]]],\"~:r2\",8,\"~:show-content\",true,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",4,\"~:column-gap\",4],\"~:transform-inverse\",[\"^;\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:r3\",8,\"~:layout-justify-content\",\"^D\",\"~:r1\",8,\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:layout-flex-dir\",\"~:row\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685a\",\"~:strokes\",[],\"~:x\",695.9999775886536,\"~:proportion\",1,\"~:r4\",8,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",695.9999775886536,\"~:y\",527.9999959462525,\"^E\",66,\"~:height\",32,\"~:x1\",695.9999775886536,\"~:y1\",527.9999959462525,\"~:x2\",761.9999775886536,\"~:y2\",559.9999959462525]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#7efff5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"~:ry\",8,\"^17\",32,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685d\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685d": "[\"~#shape\",[\"^ \",\"~:y\",535.9999959462525,\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",2,\"~:p3\",0,\"~:p4\",2],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:layout-wrap-type\",\"~:nowrap\",\"~:layout\",\"~:flex\",\"~:hide-in-viewer\",true,\"~:name\",\"_Utilities / Text / White\",\"~:layout-align-items\",\"~:start\",\"~:width\",42,\"~:layout-padding-type\",\"~:simple\",\"~:transforming\",false,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",535.9999959462525]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",535.9999959462525]],[\"^K\",[\"^ \",\"~:x\",749.9999775886536,\"~:y\",551.9999959462525]],[\"^K\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",551.9999959462525]]],\"~:show-content\",true,\"~:layout-item-h-sizing\",\"~:auto\",\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",6],\"~:transform-inverse\",[\"^:\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:layout-justify-content\",\"~:center\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685d\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\",\"~:layout-flex-dir\",\"~:column\",\"~:layout-align-content\",\"~:stretch\",\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685c\",\"~:strokes\",[],\"~:x\",707.9999775886536,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",707.9999775886536,\"~:y\",535.9999959462525,\"^D\",42,\"~:height\",16,\"~:x1\",707.9999775886536,\"~:y1\",535.9999959462525,\"~:x2\",749.9999775886536,\"~:y2\",551.9999959462525]],\"~:fills\",[],\"~:flip-x\",null,\"^16\",16,\"~:flip-y\",null,\"~:shapes\",[\"~u94eaebe4-addd-80d1-8007-79d5055d685e\"]]]",
|
||||||
|
"~u94eaebe4-addd-80d1-8007-79d5055d685e": "[\"~#shape\",[\"^ \",\"~:y\",536.9999959462527,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:auto-width\",\"~:index\",null,\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^8\",\"paragraph-set\",\"^9\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:path\",\"\",\"~:font-style\",\"normal\",\"^9\",[[\"^ \",\"^:\",\"1.2\",\"^;\",\"\",\"^<\",\"normal\",\"~:text-transform\",\"uppercase\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-work-sans\",\"~:font-size\",\"12\",\"~:font-weight\",\"500\",\"~:modified-at\",\"2024-06-04T14:15:09.786Z\",\"~:font-variant-id\",\"500\",\"~:text-decoration\",\"underline\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-color-ref-file\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"~:fill-opacity\",1,\"~:fill-color-ref-id\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:font-family\",\"Work Sans\",\"~:text\",\"Label\"]],\"^=\",\"uppercase\",\"^>\",\"center\",\"^?\",\"gfont-work-sans\",\"^@\",\"12\",\"^A\",\"500\",\"^8\",\"paragraph\",\"^B\",\"2024-06-04T14:15:09.786Z\",\"^C\",\"500\",\"^D\",\"underline\",\"^E\",\"0\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"^K\",\"Work Sans\"]]]],\"~:vertical-align\",\"center\",\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^I\",1]]],\"~:hide-in-viewer\",true,\"~:name\",\"Input\",\"~:saved-component-root\",null,\"~:width\",38,\"^8\",\"^L\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",536.9999959462527]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",536.9999959462527]],[\"^S\",[\"^ \",\"~:x\",747.9999775886536,\"~:y\",551.9999959462523]],[\"^S\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",551.9999959462527]]],\"~:layout-item-h-sizing\",\"~:fix\",\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u02e9633d-4ce7-80da-8007-736558496fa8\",\"~:id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685e\",\"~:parent-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685d\",\"~:position-data\",[[\"^ \",\"~:y\",551.3400268554688,\"^:\",\"1.2\",\"^<\",\"normal\",\"^=\",\"uppercase\",\"^>\",\"left\",\"^?\",\"sourcesanspro\",\"^@\",\"12\",\"^A\",\"500\",\"~:text-direction\",\"ltr\",\"^Q\",37.93994140625,\"^C\",\"regular\",\"^D\",\"underline\",\"^E\",\"0\",\"~:x\",710.030029296875,\"^F\",[[\"^ \",\"^G\",\"#000000\",\"^H\",\"~ucaa70d02-51e1-81ae-8007-735e7de3d7bc\",\"^I\",1,\"^J\",\"~udfa92acf-7d18-8079-8003-baba8789d8af\"]],\"~:direction\",\"ltr\",\"^K\",\"Work Sans\",\"~:height\",14.08001708984375,\"^L\",\"Label\"]],\"~:frame-id\",\"~u94eaebe4-addd-80d1-8007-79d5055d685d\",\"~:strokes\",[],\"~:x\",709.9999775886536,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",709.9999775886536,\"~:y\",536.9999959462527,\"^Q\",38,\"^11\",15,\"~:x1\",709.9999775886536,\"~:y1\",536.9999959462527,\"~:x2\",747.9999775886536,\"~:y2\",551.9999959462527]],\"^F\",[],\"~:flip-x\",null,\"^11\",15,\"~:flip-y\",null]]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"~:id": "~u31fe2e21-73e7-80f3-8007-73894fb58240",
|
||||||
|
"~:options": {
|
||||||
|
"~:components-v2": true,
|
||||||
|
"~:base-font-size": "16px"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,10 +58,10 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
|
|
||||||
async waitForTextSpan(nth = 0) {
|
async waitForTextSpan(nth = 0) {
|
||||||
if (!nth) {
|
if (!nth) {
|
||||||
return this.page.waitForSelector('[data-itype="inline"]');
|
return this.page.waitForSelector('[data-itype="span"]');
|
||||||
}
|
}
|
||||||
return this.page.waitForSelector(
|
return this.page.waitForSelector(
|
||||||
`[data-itype="inline"]:nth-child(${nth})`,
|
`[data-itype="span"]:nth-child(${nth})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,22 @@ test("Renders a file with shadows applied to any kind of shape", async ({
|
|||||||
await expect(workspace.canvas).toHaveScreenshot();
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Renders a file with flex layouts and different directions", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const workspace = new WasmWorkspacePage(page);
|
||||||
|
await workspace.setupEmptyFile();
|
||||||
|
await workspace.mockGetFile("render-wasm/get-file-flex-layouts.json");
|
||||||
|
|
||||||
|
await workspace.goToWorkspace({
|
||||||
|
id: "31fe2e21-73e7-80f3-8007-73894fb58240",
|
||||||
|
pageId: "02e9633d-4ce7-80da-8007-736558496fa8",
|
||||||
|
});
|
||||||
|
await workspace.waitForFirstRenderWithoutUI();
|
||||||
|
|
||||||
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
|
});
|
||||||
|
|
||||||
test("Renders a file with a closed path shape with multiple segments using strokes and shadow", async ({
|
test("Renders a file with a closed path shape with multiple segments using strokes and shadow", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -759,87 +759,4 @@ test.describe("Tokens: Apply token", () => {
|
|||||||
});
|
});
|
||||||
await expect(StrokeWidthPillSmall).toBeVisible();
|
await expect(StrokeWidthPillSmall).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User applies margin token to a shape", async ({ page }) => {
|
|
||||||
const workspace = new WorkspacePage(page, {
|
|
||||||
textEditor: true,
|
|
||||||
});
|
|
||||||
// Set up
|
|
||||||
await workspace.mockConfigFlags(["enable-feature-token-input"]);
|
|
||||||
await workspace.setupEmptyFile();
|
|
||||||
await workspace.mockGetFile("workspace/get-file-layout-stroke-token-json");
|
|
||||||
await workspace.goToWorkspace();
|
|
||||||
|
|
||||||
// Select shape apply stroke
|
|
||||||
await workspace.layers
|
|
||||||
.getByTestId("layer-row")
|
|
||||||
.nth(1)
|
|
||||||
.getByRole("button", { name: "Toggle layer" })
|
|
||||||
.click();
|
|
||||||
|
|
||||||
await workspace.layers.getByTestId("layer-row").nth(2).click();
|
|
||||||
|
|
||||||
const rightSidebar = page.getByTestId("right-sidebar");
|
|
||||||
await expect(rightSidebar).toBeVisible();
|
|
||||||
await rightSidebar.getByTestId("add-stroke").click();
|
|
||||||
|
|
||||||
// Apply margin token from token panel
|
|
||||||
const tokensTab = page.getByRole("tab", { name: "Tokens" });
|
|
||||||
await expect(tokensTab).toBeVisible();
|
|
||||||
await tokensTab.click();
|
|
||||||
await page.getByRole("button", { name: "Dimensions 4" }).click();
|
|
||||||
await page.getByRole("button", { name: "dim", exact: true }).click();
|
|
||||||
const tokensSidebar = workspace.tokensSidebar;
|
|
||||||
await expect(
|
|
||||||
tokensSidebar.getByRole("button", { name: "dim.md" }),
|
|
||||||
).toBeVisible();
|
|
||||||
await tokensSidebar
|
|
||||||
.getByRole("button", { name: "dim.md" })
|
|
||||||
.click({ button: "right" });
|
|
||||||
await page
|
|
||||||
.getByTestId("tokens-context-menu-for-token")
|
|
||||||
.getByText("Spacing")
|
|
||||||
.hover();
|
|
||||||
await page
|
|
||||||
.getByTestId("tokens-context-menu-for-token")
|
|
||||||
.getByText("Horizontal")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
// Check if token pill is visible on right sidebar
|
|
||||||
const layoutItemSectionSidebar = rightSidebar.getByRole("region", {
|
|
||||||
name: "layout item menu",
|
|
||||||
});
|
|
||||||
await expect(layoutItemSectionSidebar).toBeVisible();
|
|
||||||
const marginPillMd = layoutItemSectionSidebar.getByRole("button", {
|
|
||||||
name: "dim.md",
|
|
||||||
});
|
|
||||||
await expect(marginPillMd).toBeVisible();
|
|
||||||
|
|
||||||
await marginPillMd.click();
|
|
||||||
const dimensionTokenOptionXl = page.getByRole("option", { name: "dim.xl" });
|
|
||||||
await expect(dimensionTokenOptionXl).toBeVisible();
|
|
||||||
await dimensionTokenOptionXl.click();
|
|
||||||
|
|
||||||
const marginPillXL = layoutItemSectionSidebar.getByRole("button", {
|
|
||||||
name: "dim.xl",
|
|
||||||
});
|
|
||||||
await expect(marginPillXL).toBeVisible();
|
|
||||||
|
|
||||||
// Detach token from right sidebar and apply another from dropdown
|
|
||||||
const detachButton = layoutItemSectionSidebar.getByRole("button", {
|
|
||||||
name: "Detach token",
|
|
||||||
});
|
|
||||||
await detachButton.click();
|
|
||||||
await expect(marginPillXL).not.toBeVisible();
|
|
||||||
const horizontalMarginInput = layoutItemSectionSidebar.getByText('Horizontal marginOpen token');
|
|
||||||
await expect(horizontalMarginInput).toBeVisible();
|
|
||||||
|
|
||||||
const tokenDropdown = horizontalMarginInput.getByRole('button', { name: 'Open token list' });
|
|
||||||
await tokenDropdown.click();
|
|
||||||
|
|
||||||
await expect(dimensionTokenOptionXl).toBeVisible();
|
|
||||||
await dimensionTokenOptionXl.click();
|
|
||||||
|
|
||||||
await expect(marginPillXL).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,88 +12,118 @@ test.beforeEach(async ({ page }) => {
|
|||||||
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json");
|
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-tokens.json");
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe("Tokens: Remapping Feature", () => {
|
const createToken = async (page, type, name, textFieldName, value) => {
|
||||||
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
|
const { tokensUpdateCreateModal } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base token
|
||||||
|
await tokensTabPanel
|
||||||
|
.getByRole("button", { name: `Add Token: ${type}` })
|
||||||
|
.click();
|
||||||
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
|
await nameField.fill(name);
|
||||||
|
|
||||||
|
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
|
name: textFieldName,
|
||||||
|
});
|
||||||
|
await colorField.fill(value);
|
||||||
|
|
||||||
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
|
name: "Save",
|
||||||
|
});
|
||||||
|
await submitButton.click();
|
||||||
|
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
const renameToken = async (page, oldName, newName) => {
|
||||||
|
const { tokensUpdateCreateModal, tokensSidebar, tokenContextMenuForToken } =
|
||||||
|
await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
||||||
|
|
||||||
|
const baseToken = tokensSidebar.getByRole("button", {
|
||||||
|
name: oldName,
|
||||||
|
});
|
||||||
|
await baseToken.click({ button: "right" });
|
||||||
|
await tokenContextMenuForToken.getByText("Edit token").click();
|
||||||
|
|
||||||
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
|
await nameField.fill(newName);
|
||||||
|
|
||||||
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
|
name: "Save",
|
||||||
|
});
|
||||||
|
await submitButton.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCompositeDerivedToken = async (page, type, name, reference) => {
|
||||||
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
|
const { tokensUpdateCreateModal } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
await tokensTabPanel
|
||||||
|
.getByRole("button", { name: `Add Token: ${type}` })
|
||||||
|
.click();
|
||||||
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
|
const nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
|
name: "Name",
|
||||||
|
});
|
||||||
|
await nameField.fill(name);
|
||||||
|
|
||||||
|
const referenceToggle = tokensUpdateCreateModal.getByTestId("reference-opt");
|
||||||
|
await referenceToggle.click();
|
||||||
|
|
||||||
|
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
|
name: "Reference",
|
||||||
|
});
|
||||||
|
await referenceField.fill(reference);
|
||||||
|
|
||||||
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
|
name: "Save",
|
||||||
|
});
|
||||||
|
await submitButton.click();
|
||||||
|
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
test.describe("Remapping Tokens", () => {
|
||||||
test.describe("Box Shadow Token Remapping", () => {
|
test.describe("Box Shadow Token Remapping", () => {
|
||||||
test("User renames box shadow token with alias references", async ({
|
test("User renames box shadow token with alias references", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { tokensSidebar } = await setupTokensFile(page, {
|
||||||
tokensUpdateCreateModal,
|
flags: ["enable-token-shadow"],
|
||||||
tokensSidebar,
|
});
|
||||||
tokenContextMenuForToken,
|
|
||||||
} = await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base shadow token
|
// Create base shadow token
|
||||||
await tokensTabPanel
|
await createToken(page, "Shadow", "base-shadow", "Color", "#000000");
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("base-shadow");
|
|
||||||
|
|
||||||
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Color",
|
|
||||||
});
|
|
||||||
await colorField.fill("#000000");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived shadow token that references base-shadow
|
// Create derived shadow token that references base-shadow
|
||||||
await tokensTabPanel
|
await createCompositeDerivedToken(
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
page,
|
||||||
.click();
|
"Shadow",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"derived-shadow",
|
||||||
|
"{base-shadow}",
|
||||||
nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
);
|
||||||
name: "Name",
|
|
||||||
});
|
|
||||||
await nameField.fill("derived-shadow");
|
|
||||||
|
|
||||||
const referenceToggle =
|
|
||||||
tokensUpdateCreateModal.getByTestId("reference-opt");
|
|
||||||
await referenceToggle.click();
|
|
||||||
|
|
||||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Reference",
|
|
||||||
});
|
|
||||||
await referenceField.fill("{base-shadow}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename base-shadow token
|
// Rename base-shadow token
|
||||||
const baseToken = tokensSidebar.getByRole("button", {
|
await renameToken(page, "base-shadow", "foundation-shadow");
|
||||||
name: "base-shadow",
|
|
||||||
});
|
|
||||||
await baseToken.click({ button: "right" });
|
|
||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("foundation-shadow");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
|
|
||||||
// Check for remapping modal
|
// Check for remapping modal
|
||||||
const remappingModal = page.getByTestId("token-remapping-modal");
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
await expect(remappingModal).toContainText("1");
|
await expect(remappingModal).toContainText("base-shadow");
|
||||||
|
await expect(remappingModal).toContainText("foundation-shadow");
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -116,51 +146,16 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
workspacePage,
|
workspacePage,
|
||||||
} = await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
} = await setupTokensFile(page, { flags: ["enable-token-shadow"] });
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base shadow token
|
// Create base shadow token
|
||||||
await tokensTabPanel
|
await createToken(page, "Shadow", "primary-shadow", "Color", "#000000");
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("primary-shadow");
|
|
||||||
|
|
||||||
let colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Color",
|
|
||||||
});
|
|
||||||
await colorField.fill("#000000");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived shadow token that references base
|
// Create derived shadow token that references base
|
||||||
await tokensTabPanel
|
await createCompositeDerivedToken(
|
||||||
.getByRole("button", { name: "Add Token: Shadow" })
|
page,
|
||||||
.click();
|
"Shadow",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"card-shadow",
|
||||||
|
"{primary-shadow}",
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
);
|
||||||
await nameField.fill("card-shadow");
|
|
||||||
|
|
||||||
const referenceToggle =
|
|
||||||
tokensUpdateCreateModal.getByTestId("reference-opt");
|
|
||||||
await referenceToggle.click();
|
|
||||||
|
|
||||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Reference",
|
|
||||||
});
|
|
||||||
await referenceField.fill("{primary-shadow}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Apply the referenced token to a shape
|
// Apply the referenced token to a shape
|
||||||
await page.getByRole("tab", { name: "Layers" }).click();
|
await page.getByRole("tab", { name: "Layers" }).click();
|
||||||
@@ -183,16 +178,16 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
await tokenContextMenuForToken.getByText("Edit token").click();
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
await nameField.fill("main-shadow");
|
await nameField.fill("main-shadow");
|
||||||
|
|
||||||
// Update the color value
|
// Update the color value
|
||||||
colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
const colorField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Color",
|
name: "Color",
|
||||||
});
|
});
|
||||||
await colorField.fill("#FF0000");
|
await colorField.fill("#FF0000");
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
@@ -202,7 +197,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -259,73 +254,25 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
// Create base typography token
|
// Create base typography token
|
||||||
await tokensTabPanel
|
await createToken(page, "Typography", "base-text", "Font size", "16");
|
||||||
.getByRole("button", { name: "Add Token: Typography" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("base-text");
|
|
||||||
|
|
||||||
const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Font size",
|
|
||||||
});
|
|
||||||
await fontSizeField.fill("16");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived typography token
|
// Create derived typography token
|
||||||
await tokensTabPanel
|
await createCompositeDerivedToken(
|
||||||
.getByRole("button", { name: "Add Token: Typography" })
|
page,
|
||||||
.click();
|
"Typography",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"body-text",
|
||||||
|
"{base-text}",
|
||||||
nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
);
|
||||||
name: "Name",
|
|
||||||
});
|
|
||||||
await nameField.fill("body-text");
|
|
||||||
|
|
||||||
const referenceToggle =
|
|
||||||
tokensUpdateCreateModal.getByTestId("reference-opt");
|
|
||||||
await referenceToggle.click();
|
|
||||||
|
|
||||||
const referenceField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Reference",
|
|
||||||
});
|
|
||||||
await referenceField.fill("{base-text}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename base token
|
// Rename base token
|
||||||
const baseToken = tokensSidebar.getByRole("button", {
|
await renameToken(page, "base-text", "default-text");
|
||||||
name: "base-text",
|
|
||||||
});
|
|
||||||
await baseToken.click({ button: "right" });
|
|
||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("default-text");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
|
|
||||||
// Check for remapping modal
|
// Check for remapping modal
|
||||||
const remappingModal = page.getByTestId("token-remapping-modal");
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -351,24 +298,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
||||||
|
|
||||||
// Create base typography token
|
// Create base typography token
|
||||||
await tokensTabPanel
|
await createToken(page, "Typography", "body-style", "Font size", "16");
|
||||||
.getByRole("button", { name: "Add Token: Typography" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("body-style");
|
|
||||||
|
|
||||||
let fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
|
||||||
name: "Font size",
|
|
||||||
});
|
|
||||||
await fontSizeField.fill("16");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived typography token
|
// Create derived typography token
|
||||||
await tokensTabPanel
|
await tokensTabPanel
|
||||||
@@ -376,7 +306,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
.click();
|
.click();
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
|
|
||||||
nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
let nameField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Name",
|
name: "Name",
|
||||||
});
|
});
|
||||||
await nameField.fill("paragraph-style");
|
await nameField.fill("paragraph-style");
|
||||||
@@ -390,7 +320,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
});
|
});
|
||||||
await referenceField.fill("{body-style}");
|
await referenceField.fill("{body-style}");
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
@@ -421,7 +351,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await nameField.fill("text-base");
|
await nameField.fill("text-base");
|
||||||
|
|
||||||
// Update the font size value
|
// Update the font size value
|
||||||
fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", {
|
||||||
name: "Font size",
|
name: "Font size",
|
||||||
});
|
});
|
||||||
await fontSizeField.fill("18");
|
await fontSizeField.fill("18");
|
||||||
@@ -436,7 +366,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -471,72 +401,29 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
test("User renames border radius token with alias references", async ({
|
test("User renames border radius token with alias references", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { tokensSidebar } = await setupTokensFile(page);
|
||||||
tokensUpdateCreateModal,
|
|
||||||
tokensSidebar,
|
|
||||||
tokenContextMenuForToken,
|
|
||||||
} = await setupTokensFile(page);
|
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base border radius token
|
// Create base border radius token
|
||||||
await tokensTabPanel
|
await createToken(page, "Border Radius", "base-radius", "Value", "4");
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("base-radius");
|
|
||||||
|
|
||||||
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField.fill("4");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived border radius token
|
// Create derived border radius token
|
||||||
await tokensTabPanel
|
await createToken(
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
page,
|
||||||
.click();
|
"Border Radius",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"card-radius",
|
||||||
|
"Value",
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
"{base-radius}",
|
||||||
await nameField.fill("card-radius");
|
);
|
||||||
|
|
||||||
const valueField2 = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField2.fill("{base-radius}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename base token
|
// Rename base token
|
||||||
const baseToken = tokensSidebar.getByRole("button", {
|
await renameToken(page, "base-radius", "primary-radius");
|
||||||
name: "base-radius",
|
|
||||||
});
|
|
||||||
await baseToken.click({ button: "right" });
|
|
||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("primary-radius");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
|
|
||||||
// Check for remapping modal
|
// Check for remapping modal
|
||||||
const remappingModal = page.getByTestId("token-remapping-modal");
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -558,43 +445,17 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
tokenContextMenuForToken,
|
tokenContextMenuForToken,
|
||||||
} = await setupTokensFile(page);
|
} = await setupTokensFile(page);
|
||||||
|
|
||||||
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
|
|
||||||
|
|
||||||
// Create base border radius token
|
// Create base border radius token
|
||||||
await tokensTabPanel
|
await createToken(page, "Border Radius", "radius-sm", "Value", "4");
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
|
||||||
.click();
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
|
||||||
|
|
||||||
let nameField = tokensUpdateCreateModal.getByLabel("Name");
|
|
||||||
await nameField.fill("radius-sm");
|
|
||||||
|
|
||||||
let valueField = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField.fill("4");
|
|
||||||
|
|
||||||
let submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Create derived border radius token
|
// Create derived border radius token
|
||||||
await tokensTabPanel
|
await createToken(
|
||||||
.getByRole("button", { name: "Add Token: Border Radius" })
|
page,
|
||||||
.click();
|
"Border Radius",
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
"button-radius",
|
||||||
|
"Value",
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
"{radius-sm}",
|
||||||
await nameField.fill("button-radius");
|
);
|
||||||
|
|
||||||
const valueField2 = tokensUpdateCreateModal.getByLabel("Value");
|
|
||||||
await valueField2.fill("{radius-sm}");
|
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
|
||||||
name: "Save",
|
|
||||||
});
|
|
||||||
await submitButton.click();
|
|
||||||
await expect(tokensUpdateCreateModal).not.toBeVisible();
|
|
||||||
|
|
||||||
// Rename and update value of base token
|
// Rename and update value of base token
|
||||||
const radiusToken = tokensSidebar.getByRole("button", {
|
const radiusToken = tokensSidebar.getByRole("button", {
|
||||||
@@ -604,14 +465,14 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await tokenContextMenuForToken.getByText("Edit token").click();
|
await tokenContextMenuForToken.getByText("Edit token").click();
|
||||||
|
|
||||||
await expect(tokensUpdateCreateModal).toBeVisible();
|
await expect(tokensUpdateCreateModal).toBeVisible();
|
||||||
nameField = tokensUpdateCreateModal.getByLabel("Name");
|
const nameField = tokensUpdateCreateModal.getByLabel("Name");
|
||||||
await nameField.fill("radius-base");
|
await nameField.fill("radius-base");
|
||||||
|
|
||||||
// Update the value
|
// Update the value
|
||||||
valueField = tokensUpdateCreateModal.getByLabel("Value");
|
const valueField = tokensUpdateCreateModal.getByLabel("Value");
|
||||||
await valueField.fill("8");
|
await valueField.fill("8");
|
||||||
|
|
||||||
submitButton = tokensUpdateCreateModal.getByRole("button", {
|
const submitButton = tokensUpdateCreateModal.getByRole("button", {
|
||||||
name: "Save",
|
name: "Save",
|
||||||
});
|
});
|
||||||
await submitButton.click();
|
await submitButton.click();
|
||||||
@@ -621,7 +482,7 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const confirmButton = remappingModal.getByRole("button", {
|
const confirmButton = remappingModal.getByRole("button", {
|
||||||
name: /remap/i,
|
name: "remap tokens",
|
||||||
});
|
});
|
||||||
await confirmButton.click();
|
await confirmButton.click();
|
||||||
|
|
||||||
@@ -648,4 +509,82 @@ test.describe("Tokens: Remapping Feature", () => {
|
|||||||
await expect(currentValue).toHaveValue("{radius-base}");
|
await expect(currentValue).toHaveValue("{radius-base}");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.describe("Cancel remap", () => {
|
||||||
|
test("Only rename - breaks reference", async ({ page }) => {
|
||||||
|
const { tokensSidebar } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base shadow token
|
||||||
|
await createToken(page, "Shadow", "base-shadow", "Color", "#000000");
|
||||||
|
|
||||||
|
// Create derived shadow token that references base-shadow
|
||||||
|
await createCompositeDerivedToken(
|
||||||
|
page,
|
||||||
|
"Shadow",
|
||||||
|
"derived-shadow",
|
||||||
|
"{base-shadow}",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rename base-shadow token
|
||||||
|
await renameToken(page, "base-shadow", "foundation-shadow");
|
||||||
|
|
||||||
|
// Check for remapping modal
|
||||||
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
|
const cancelButton = remappingModal.getByRole("button", {
|
||||||
|
name: "don't remap",
|
||||||
|
});
|
||||||
|
await cancelButton.click();
|
||||||
|
|
||||||
|
// Verify token was renamed
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.getByRole("button", {
|
||||||
|
name: "foundation-shadow",
|
||||||
|
}),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.locator('[aria-label="Missing reference"]'),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Cancel process - no changes applied", async ({ page }) => {
|
||||||
|
const { tokensSidebar } = await setupTokensFile(page, {
|
||||||
|
flags: ["enable-token-shadow"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create base shadow token
|
||||||
|
await createToken(page, "Shadow", "base-shadow", "Color", "#000000");
|
||||||
|
|
||||||
|
// Create derived shadow token that references base-shadow
|
||||||
|
await createCompositeDerivedToken(
|
||||||
|
page,
|
||||||
|
"Shadow",
|
||||||
|
"derived-shadow",
|
||||||
|
"{base-shadow}",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Rename base-shadow token
|
||||||
|
await renameToken(page, "base-shadow", "foundation-shadow");
|
||||||
|
|
||||||
|
// Check for remapping modal
|
||||||
|
const remappingModal = page.getByTestId("token-remapping-modal");
|
||||||
|
await expect(remappingModal).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
|
const closeButton = remappingModal.getByRole("button", {
|
||||||
|
name: "close",
|
||||||
|
});
|
||||||
|
await closeButton.click();
|
||||||
|
|
||||||
|
// Verify original token name still exists
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.getByRole("button", { name: "base-shadow" }),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(
|
||||||
|
tokensSidebar.getByRole("button", { name: "derived-shadow" }),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -216,4 +216,32 @@ test.describe("Tokens: Sets Tab", () => {
|
|||||||
await expect(tokenSetItems.nth(1)).toHaveAttribute("aria-checked", "false");
|
await expect(tokenSetItems.nth(1)).toHaveAttribute("aria-checked", "false");
|
||||||
await expect(tokenSetItems.nth(2)).toHaveAttribute("aria-checked", "true");
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
226
frontend/pnpm-lock.yaml
generated
226
frontend/pnpm-lock.yaml
generated
@@ -99,7 +99,7 @@ importers:
|
|||||||
version: 1.15.4
|
version: 1.15.4
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^27.4.0
|
specifier: ^27.4.0
|
||||||
version: 27.4.0
|
version: 27.4.0(canvas@3.2.1)
|
||||||
lodash:
|
lodash:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
@@ -210,7 +210,7 @@ importers:
|
|||||||
version: 7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
version: 7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.0.18
|
specifier: ^4.0.18
|
||||||
version: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
version: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
wait-on:
|
wait-on:
|
||||||
specifier: ^9.0.3
|
specifier: ^9.0.3
|
||||||
version: 9.0.3
|
version: 9.0.3
|
||||||
@@ -265,12 +265,15 @@ importers:
|
|||||||
'@vitest/ui':
|
'@vitest/ui':
|
||||||
specifier: ^1.6.0
|
specifier: ^1.6.0
|
||||||
version: 1.6.1(vitest@1.6.1)
|
version: 1.6.1(vitest@1.6.1)
|
||||||
|
canvas:
|
||||||
|
specifier: ^3.2.1
|
||||||
|
version: 3.2.1
|
||||||
esbuild:
|
esbuild:
|
||||||
specifier: ^0.27.2
|
specifier: ^0.27.2
|
||||||
version: 0.27.2
|
version: 0.27.2
|
||||||
jsdom:
|
jsdom:
|
||||||
specifier: ^27.4.0
|
specifier: ^27.4.0
|
||||||
version: 27.4.0
|
version: 27.4.0(canvas@3.2.1)
|
||||||
playwright:
|
playwright:
|
||||||
specifier: ^1.45.1
|
specifier: ^1.45.1
|
||||||
version: 1.57.0
|
version: 1.57.0
|
||||||
@@ -282,7 +285,7 @@ importers:
|
|||||||
version: 5.4.21(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)
|
version: 5.4.21(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^1.6.0
|
specifier: ^1.6.0
|
||||||
version: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
version: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -1751,6 +1754,9 @@ packages:
|
|||||||
bintrees@1.0.2:
|
bintrees@1.0.2:
|
||||||
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||||
|
|
||||||
body-parser@2.2.1:
|
body-parser@2.2.1:
|
||||||
resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
|
resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -1779,6 +1785,9 @@ packages:
|
|||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||||
|
|
||||||
buffer@6.0.3:
|
buffer@6.0.3:
|
||||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||||
|
|
||||||
@@ -1809,6 +1818,10 @@ packages:
|
|||||||
caniuse-lite@1.0.30001762:
|
caniuse-lite@1.0.30001762:
|
||||||
resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==}
|
resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==}
|
||||||
|
|
||||||
|
canvas@3.2.1:
|
||||||
|
resolution: {integrity: sha512-ej1sPFR5+0YWtaVp6S1N1FVz69TQCqmrkGeRvQxZeAB1nAIcjNTHVwrZtYtWFFBmQsF40/uDLehsW5KuYC99mg==}
|
||||||
|
engines: {node: ^18.12.0 || >= 20.9.0}
|
||||||
|
|
||||||
chai@4.5.0:
|
chai@4.5.0:
|
||||||
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -1851,6 +1864,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
|
|
||||||
|
chownr@1.1.4:
|
||||||
|
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
|
||||||
|
|
||||||
chownr@2.0.0:
|
chownr@2.0.0:
|
||||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2092,6 +2108,10 @@ packages:
|
|||||||
decimal.js@10.6.0:
|
decimal.js@10.6.0:
|
||||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||||
|
|
||||||
|
decompress-response@6.0.0:
|
||||||
|
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
deep-eql@4.1.4:
|
deep-eql@4.1.4:
|
||||||
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
|
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2100,6 +2120,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
deep-extend@0.6.0:
|
||||||
|
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
|
||||||
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
||||||
deepmerge@4.3.1:
|
deepmerge@4.3.1:
|
||||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2144,6 +2168,10 @@ packages:
|
|||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
detect-libc@2.1.2:
|
||||||
|
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
dettle@1.0.5:
|
dettle@1.0.5:
|
||||||
resolution: {integrity: sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA==}
|
resolution: {integrity: sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA==}
|
||||||
|
|
||||||
@@ -2232,6 +2260,9 @@ packages:
|
|||||||
encoding@0.1.13:
|
encoding@0.1.13:
|
||||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||||
|
|
||||||
|
end-of-stream@1.4.5:
|
||||||
|
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||||
|
|
||||||
entities@2.2.0:
|
entities@2.2.0:
|
||||||
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
|
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
|
||||||
|
|
||||||
@@ -2329,6 +2360,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
|
||||||
engines: {node: '>=16.17'}
|
engines: {node: '>=16.17'}
|
||||||
|
|
||||||
|
expand-template@2.0.3:
|
||||||
|
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
expect-type@1.3.0:
|
expect-type@1.3.0:
|
||||||
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
@@ -2421,6 +2456,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
fs-constants@1.0.0:
|
||||||
|
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||||
|
|
||||||
fs-extra@10.1.0:
|
fs-extra@10.1.0:
|
||||||
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -2493,6 +2531,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-eFmhDi2xQ+2reMRY2AbJ2oa10uFOl1oyGbAKdCZiNOk94NJHi7aN0OBELSC9v35ZAPQdr+uRBi93/Gu4SlBdrA==}
|
resolution: {integrity: sha512-eFmhDi2xQ+2reMRY2AbJ2oa10uFOl1oyGbAKdCZiNOk94NJHi7aN0OBELSC9v35ZAPQdr+uRBi93/Gu4SlBdrA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
github-from-package@0.0.0:
|
||||||
|
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -3042,6 +3083,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
mimic-response@3.1.0:
|
||||||
|
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
min-indent@1.0.1:
|
min-indent@1.0.1:
|
||||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -3080,6 +3125,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
mkdirp-classic@0.5.3:
|
||||||
|
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||||
|
|
||||||
mkdirp@1.0.4:
|
mkdirp@1.0.4:
|
||||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -3112,6 +3160,9 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
napi-build-utils@2.0.0:
|
||||||
|
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
|
||||||
|
|
||||||
negotiator@0.6.4:
|
negotiator@0.6.4:
|
||||||
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
|
resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@@ -3123,6 +3174,10 @@ packages:
|
|||||||
nice-try@1.0.5:
|
nice-try@1.0.5:
|
||||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||||
|
|
||||||
|
node-abi@3.87.0:
|
||||||
|
resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
node-addon-api@7.1.1:
|
node-addon-api@7.1.1:
|
||||||
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
@@ -3415,6 +3470,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
prebuild-install@7.1.3:
|
||||||
|
resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
prettier@3.5.3:
|
prettier@3.5.3:
|
||||||
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
|
resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@@ -3472,6 +3532,9 @@ packages:
|
|||||||
pstree.remy@1.1.8:
|
pstree.remy@1.1.8:
|
||||||
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||||
|
|
||||||
|
pump@3.0.3:
|
||||||
|
resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==}
|
||||||
|
|
||||||
punycode@1.4.1:
|
punycode@1.4.1:
|
||||||
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
||||||
|
|
||||||
@@ -3497,6 +3560,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
|
rc@1.2.8:
|
||||||
|
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
react-docgen-typescript@2.4.0:
|
react-docgen-typescript@2.4.0:
|
||||||
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
|
resolution: {integrity: sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3884,6 +3951,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
simple-concat@1.0.1:
|
||||||
|
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
|
||||||
|
|
||||||
|
simple-get@4.0.1:
|
||||||
|
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||||
|
|
||||||
simple-update-notifier@2.0.0:
|
simple-update-notifier@2.0.0:
|
||||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -4016,6 +4089,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==}
|
resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
strip-json-comments@2.0.1:
|
||||||
|
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
strip-literal@2.1.1:
|
strip-literal@2.1.1:
|
||||||
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
|
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
|
||||||
|
|
||||||
@@ -4069,6 +4146,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
|
resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
|
tar-fs@2.1.4:
|
||||||
|
resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
|
||||||
|
|
||||||
|
tar-stream@2.2.0:
|
||||||
|
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
tar@6.2.1:
|
tar@6.2.1:
|
||||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -4195,6 +4279,9 @@ packages:
|
|||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
|
tunnel-agent@0.6.0:
|
||||||
|
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||||
|
|
||||||
type-detect@4.1.0:
|
type-detect@4.1.0:
|
||||||
resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
|
resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -5411,7 +5498,7 @@ snapshots:
|
|||||||
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
'@vitest/runner': 4.0.18
|
'@vitest/runner': 4.0.18
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- react
|
- react
|
||||||
- react-dom
|
- react-dom
|
||||||
@@ -5583,7 +5670,7 @@ snapshots:
|
|||||||
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
||||||
playwright: 1.58.0
|
playwright: 1.58.0
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
- msw
|
- msw
|
||||||
@@ -5595,7 +5682,7 @@ snapshots:
|
|||||||
'@vitest/utils': 1.6.1
|
'@vitest/utils': 1.6.1
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
playwright: 1.57.0
|
playwright: 1.57.0
|
||||||
|
|
||||||
@@ -5608,7 +5695,7 @@ snapshots:
|
|||||||
pngjs: 7.0.0
|
pngjs: 7.0.0
|
||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
ws: 8.19.0
|
ws: 8.19.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -5631,7 +5718,7 @@ snapshots:
|
|||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
strip-literal: 2.1.1
|
strip-literal: 2.1.1
|
||||||
test-exclude: 6.0.0
|
test-exclude: 6.0.0
|
||||||
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -5647,7 +5734,7 @@ snapshots:
|
|||||||
obug: 2.1.1
|
obug: 2.1.1
|
||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
tinyrainbow: 3.0.3
|
tinyrainbow: 3.0.3
|
||||||
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
vitest: 4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
|
|
||||||
@@ -5740,7 +5827,7 @@ snapshots:
|
|||||||
pathe: 1.1.2
|
pathe: 1.1.2
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)
|
vitest: 1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)
|
||||||
|
|
||||||
'@vitest/utils@1.6.1':
|
'@vitest/utils@1.6.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5908,6 +5995,12 @@ snapshots:
|
|||||||
|
|
||||||
bintrees@1.0.2: {}
|
bintrees@1.0.2: {}
|
||||||
|
|
||||||
|
bl@4.1.0:
|
||||||
|
dependencies:
|
||||||
|
buffer: 5.7.1
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
body-parser@2.2.1:
|
body-parser@2.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
bytes: 3.1.2
|
bytes: 3.1.2
|
||||||
@@ -5949,6 +6042,11 @@ snapshots:
|
|||||||
|
|
||||||
buffer-from@1.1.2: {}
|
buffer-from@1.1.2: {}
|
||||||
|
|
||||||
|
buffer@5.7.1:
|
||||||
|
dependencies:
|
||||||
|
base64-js: 1.5.1
|
||||||
|
ieee754: 1.2.1
|
||||||
|
|
||||||
buffer@6.0.3:
|
buffer@6.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
base64-js: 1.5.1
|
base64-js: 1.5.1
|
||||||
@@ -5981,6 +6079,11 @@ snapshots:
|
|||||||
|
|
||||||
caniuse-lite@1.0.30001762: {}
|
caniuse-lite@1.0.30001762: {}
|
||||||
|
|
||||||
|
canvas@3.2.1:
|
||||||
|
dependencies:
|
||||||
|
node-addon-api: 7.1.1
|
||||||
|
prebuild-install: 7.1.3
|
||||||
|
|
||||||
chai@4.5.0:
|
chai@4.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
assertion-error: 1.1.0
|
assertion-error: 1.1.0
|
||||||
@@ -6038,6 +6141,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
|
|
||||||
|
chownr@1.1.4: {}
|
||||||
|
|
||||||
chownr@2.0.0: {}
|
chownr@2.0.0: {}
|
||||||
|
|
||||||
ci-info@3.9.0: {}
|
ci-info@3.9.0: {}
|
||||||
@@ -6273,12 +6378,18 @@ snapshots:
|
|||||||
|
|
||||||
decimal.js@10.6.0: {}
|
decimal.js@10.6.0: {}
|
||||||
|
|
||||||
|
decompress-response@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
mimic-response: 3.1.0
|
||||||
|
|
||||||
deep-eql@4.1.4:
|
deep-eql@4.1.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
type-detect: 4.1.0
|
type-detect: 4.1.0
|
||||||
|
|
||||||
deep-eql@5.0.2: {}
|
deep-eql@5.0.2: {}
|
||||||
|
|
||||||
|
deep-extend@0.6.0: {}
|
||||||
|
|
||||||
deepmerge@4.3.1: {}
|
deepmerge@4.3.1: {}
|
||||||
|
|
||||||
default-browser-id@5.0.1: {}
|
default-browser-id@5.0.1: {}
|
||||||
@@ -6313,6 +6424,8 @@ snapshots:
|
|||||||
detect-libc@1.0.3:
|
detect-libc@1.0.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
detect-libc@2.1.2: {}
|
||||||
|
|
||||||
dettle@1.0.5: {}
|
dettle@1.0.5: {}
|
||||||
|
|
||||||
diff-sequences@29.6.3: {}
|
diff-sequences@29.6.3: {}
|
||||||
@@ -6407,6 +6520,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
iconv-lite: 0.6.3
|
iconv-lite: 0.6.3
|
||||||
|
|
||||||
|
end-of-stream@1.4.5:
|
||||||
|
dependencies:
|
||||||
|
once: 1.4.0
|
||||||
|
|
||||||
entities@2.2.0: {}
|
entities@2.2.0: {}
|
||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
@@ -6588,6 +6705,8 @@ snapshots:
|
|||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
strip-final-newline: 3.0.0
|
strip-final-newline: 3.0.0
|
||||||
|
|
||||||
|
expand-template@2.0.3: {}
|
||||||
|
|
||||||
expect-type@1.3.0: {}
|
expect-type@1.3.0: {}
|
||||||
|
|
||||||
expr-eval-fork@2.0.2: {}
|
expr-eval-fork@2.0.2: {}
|
||||||
@@ -6711,6 +6830,8 @@ snapshots:
|
|||||||
|
|
||||||
fresh@2.0.0: {}
|
fresh@2.0.0: {}
|
||||||
|
|
||||||
|
fs-constants@1.0.0: {}
|
||||||
|
|
||||||
fs-extra@10.1.0:
|
fs-extra@10.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@@ -6789,6 +6910,8 @@ snapshots:
|
|||||||
readable-stream: 4.7.0
|
readable-stream: 4.7.0
|
||||||
safe-buffer: 5.2.1
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
github-from-package@0.0.0: {}
|
||||||
|
|
||||||
glob-parent@5.1.2:
|
glob-parent@5.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -7163,7 +7286,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
argparse: 2.0.1
|
argparse: 2.0.1
|
||||||
|
|
||||||
jsdom@27.4.0:
|
jsdom@27.4.0(canvas@3.2.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@acemir/cssom': 0.9.30
|
'@acemir/cssom': 0.9.30
|
||||||
'@asamuzakjp/dom-selector': 6.7.6
|
'@asamuzakjp/dom-selector': 6.7.6
|
||||||
@@ -7185,6 +7308,8 @@ snapshots:
|
|||||||
whatwg-url: 15.1.0
|
whatwg-url: 15.1.0
|
||||||
ws: 8.18.3
|
ws: 8.18.3
|
||||||
xml-name-validator: 5.0.0
|
xml-name-validator: 5.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
canvas: 3.2.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@exodus/crypto'
|
- '@exodus/crypto'
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -7342,6 +7467,8 @@ snapshots:
|
|||||||
|
|
||||||
mimic-fn@4.0.0: {}
|
mimic-fn@4.0.0: {}
|
||||||
|
|
||||||
|
mimic-response@3.1.0: {}
|
||||||
|
|
||||||
min-indent@1.0.1: {}
|
min-indent@1.0.1: {}
|
||||||
|
|
||||||
minimatch@10.1.1:
|
minimatch@10.1.1:
|
||||||
@@ -7375,6 +7502,8 @@ snapshots:
|
|||||||
minipass: 3.3.6
|
minipass: 3.3.6
|
||||||
yallist: 4.0.0
|
yallist: 4.0.0
|
||||||
|
|
||||||
|
mkdirp-classic@0.5.3: {}
|
||||||
|
|
||||||
mkdirp@1.0.4: {}
|
mkdirp@1.0.4: {}
|
||||||
|
|
||||||
mkdirp@3.0.1: {}
|
mkdirp@3.0.1: {}
|
||||||
@@ -7396,14 +7525,19 @@ snapshots:
|
|||||||
|
|
||||||
nanoid@3.3.11: {}
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
|
napi-build-utils@2.0.0: {}
|
||||||
|
|
||||||
negotiator@0.6.4: {}
|
negotiator@0.6.4: {}
|
||||||
|
|
||||||
negotiator@1.0.0: {}
|
negotiator@1.0.0: {}
|
||||||
|
|
||||||
nice-try@1.0.5: {}
|
nice-try@1.0.5: {}
|
||||||
|
|
||||||
node-addon-api@7.1.1:
|
node-abi@3.87.0:
|
||||||
optional: true
|
dependencies:
|
||||||
|
semver: 7.7.3
|
||||||
|
|
||||||
|
node-addon-api@7.1.1: {}
|
||||||
|
|
||||||
node-fetch@2.7.0(encoding@0.1.13):
|
node-fetch@2.7.0(encoding@0.1.13):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -7704,6 +7838,21 @@ snapshots:
|
|||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
source-map-js: 1.2.1
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
prebuild-install@7.1.3:
|
||||||
|
dependencies:
|
||||||
|
detect-libc: 2.1.2
|
||||||
|
expand-template: 2.0.3
|
||||||
|
github-from-package: 0.0.0
|
||||||
|
minimist: 1.2.8
|
||||||
|
mkdirp-classic: 0.5.3
|
||||||
|
napi-build-utils: 2.0.0
|
||||||
|
node-abi: 3.87.0
|
||||||
|
pump: 3.0.3
|
||||||
|
rc: 1.2.8
|
||||||
|
simple-get: 4.0.1
|
||||||
|
tar-fs: 2.1.4
|
||||||
|
tunnel-agent: 0.6.0
|
||||||
|
|
||||||
prettier@3.5.3: {}
|
prettier@3.5.3: {}
|
||||||
|
|
||||||
prettier@3.7.4: {}
|
prettier@3.7.4: {}
|
||||||
@@ -7755,6 +7904,11 @@ snapshots:
|
|||||||
|
|
||||||
pstree.remy@1.1.8: {}
|
pstree.remy@1.1.8: {}
|
||||||
|
|
||||||
|
pump@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
end-of-stream: 1.4.5
|
||||||
|
once: 1.4.0
|
||||||
|
|
||||||
punycode@1.4.1: {}
|
punycode@1.4.1: {}
|
||||||
|
|
||||||
punycode@2.3.1: {}
|
punycode@2.3.1: {}
|
||||||
@@ -7776,6 +7930,13 @@ snapshots:
|
|||||||
iconv-lite: 0.7.1
|
iconv-lite: 0.7.1
|
||||||
unpipe: 1.0.0
|
unpipe: 1.0.0
|
||||||
|
|
||||||
|
rc@1.2.8:
|
||||||
|
dependencies:
|
||||||
|
deep-extend: 0.6.0
|
||||||
|
ini: 1.3.8
|
||||||
|
minimist: 1.2.8
|
||||||
|
strip-json-comments: 2.0.1
|
||||||
|
|
||||||
react-docgen-typescript@2.4.0(typescript@5.9.3):
|
react-docgen-typescript@2.4.0(typescript@5.9.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript: 5.9.3
|
typescript: 5.9.3
|
||||||
@@ -8249,6 +8410,14 @@ snapshots:
|
|||||||
|
|
||||||
signal-exit@4.1.0: {}
|
signal-exit@4.1.0: {}
|
||||||
|
|
||||||
|
simple-concat@1.0.1: {}
|
||||||
|
|
||||||
|
simple-get@4.0.1:
|
||||||
|
dependencies:
|
||||||
|
decompress-response: 6.0.0
|
||||||
|
once: 1.4.0
|
||||||
|
simple-concat: 1.0.1
|
||||||
|
|
||||||
simple-update-notifier@2.0.0:
|
simple-update-notifier@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.7.3
|
semver: 7.7.3
|
||||||
@@ -8404,6 +8573,8 @@ snapshots:
|
|||||||
|
|
||||||
strip-indent@4.1.1: {}
|
strip-indent@4.1.1: {}
|
||||||
|
|
||||||
|
strip-json-comments@2.0.1: {}
|
||||||
|
|
||||||
strip-literal@2.1.1:
|
strip-literal@2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
js-tokens: 9.0.1
|
js-tokens: 9.0.1
|
||||||
@@ -8487,6 +8658,21 @@ snapshots:
|
|||||||
|
|
||||||
sync-message-port@1.1.3: {}
|
sync-message-port@1.1.3: {}
|
||||||
|
|
||||||
|
tar-fs@2.1.4:
|
||||||
|
dependencies:
|
||||||
|
chownr: 1.1.4
|
||||||
|
mkdirp-classic: 0.5.3
|
||||||
|
pump: 3.0.3
|
||||||
|
tar-stream: 2.2.0
|
||||||
|
|
||||||
|
tar-stream@2.2.0:
|
||||||
|
dependencies:
|
||||||
|
bl: 4.1.0
|
||||||
|
end-of-stream: 1.4.5
|
||||||
|
fs-constants: 1.0.0
|
||||||
|
inherits: 2.0.4
|
||||||
|
readable-stream: 3.6.2
|
||||||
|
|
||||||
tar@6.2.1:
|
tar@6.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
chownr: 2.0.0
|
chownr: 2.0.0
|
||||||
@@ -8587,6 +8773,10 @@ snapshots:
|
|||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
|
tunnel-agent@0.6.0:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
type-detect@4.1.0: {}
|
type-detect@4.1.0: {}
|
||||||
|
|
||||||
type-is@2.0.1:
|
type-is@2.0.1:
|
||||||
@@ -8757,7 +8947,7 @@ snapshots:
|
|||||||
sass-embedded: 1.97.1
|
sass-embedded: 1.97.1
|
||||||
yaml: 2.8.2
|
yaml: 2.8.2
|
||||||
|
|
||||||
vitest@1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1):
|
vitest@1.6.1(@types/node@25.0.3)(@vitest/browser@1.6.1)(@vitest/ui@1.6.1)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 1.6.1
|
'@vitest/expect': 1.6.1
|
||||||
'@vitest/runner': 1.6.1
|
'@vitest/runner': 1.6.1
|
||||||
@@ -8783,7 +8973,7 @@ snapshots:
|
|||||||
'@types/node': 25.0.3
|
'@types/node': 25.0.3
|
||||||
'@vitest/browser': 1.6.1(playwright@1.57.0)(vitest@1.6.1)
|
'@vitest/browser': 1.6.1(playwright@1.57.0)(vitest@1.6.1)
|
||||||
'@vitest/ui': 1.6.1(vitest@1.6.1)
|
'@vitest/ui': 1.6.1(vitest@1.6.1)
|
||||||
jsdom: 27.4.0
|
jsdom: 27.4.0(canvas@3.2.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- less
|
- less
|
||||||
- lightningcss
|
- lightningcss
|
||||||
@@ -8794,7 +8984,7 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- terser
|
- terser
|
||||||
|
|
||||||
vitest@4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2):
|
vitest@4.0.18(@types/node@25.0.3)(@vitest/browser-playwright@4.0.18)(jsdom@27.4.0(canvas@3.2.1))(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.0.18
|
'@vitest/expect': 4.0.18
|
||||||
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))
|
||||||
@@ -8819,7 +9009,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/node': 25.0.3
|
'@types/node': 25.0.3
|
||||||
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
'@vitest/browser-playwright': 4.0.18(playwright@1.58.0)(vite@7.3.1(@types/node@25.0.3)(sass-embedded@1.97.1)(sass@1.97.1)(yaml@2.8.2))(vitest@4.0.18)
|
||||||
jsdom: 27.4.0
|
jsdom: 27.4.0(canvas@3.2.1)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- jiti
|
- jiti
|
||||||
- less
|
- less
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
[app.common.time :as ct]
|
[app.common.time :as ct]
|
||||||
[app.common.types.project :refer [valid-project?]]
|
[app.common.types.project :refer [valid-project?]]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cf]
|
||||||
[app.main.constants :as mconst]
|
[app.main.constants :as mconst]
|
||||||
[app.main.data.common :as dcm]
|
[app.main.data.common :as dcm]
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
@@ -80,7 +81,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(reduce (fn [state {:keys [id] :as project}]
|
(reduce (fn [state {:keys [id] :as project}]
|
||||||
;; Replace completely instead of merge to ensure deleted-at is removed
|
;; Replace completely instead of merge to ensure deleted-at is removed
|
||||||
(assoc-in state [:projects id] project))
|
(assoc-in state [:projects id] project))
|
||||||
state
|
state
|
||||||
projects))))
|
projects))))
|
||||||
@@ -683,12 +684,25 @@
|
|||||||
(rx/of (dcm/change-team-role params)
|
(rx/of (dcm/change-team-role params)
|
||||||
(modal/hide)))))
|
(modal/hide)))))
|
||||||
|
|
||||||
|
(defn handle-change-team-org
|
||||||
|
[{:keys [team-id organization-id organization-name]}]
|
||||||
|
(ptk/reify ::handle-change-team-org
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(if (contains? cf/flags :nitrate)
|
||||||
|
(d/update-in-when state [:teams team-id] assoc
|
||||||
|
:organization-id organization-id
|
||||||
|
:organization-name organization-name)
|
||||||
|
state))))
|
||||||
|
|
||||||
|
|
||||||
(defn- process-message
|
(defn- process-message
|
||||||
[{:keys [type] :as msg}]
|
[{:keys [type] :as msg}]
|
||||||
(case type
|
(case type
|
||||||
:notification (dcm/handle-notification msg)
|
:notification (dcm/handle-notification msg)
|
||||||
:team-role-change (handle-change-team-role msg)
|
:team-role-change (handle-change-team-role msg)
|
||||||
:team-membership-change (dcm/team-membership-change msg)
|
:team-membership-change (dcm/team-membership-change msg)
|
||||||
|
:team-org-change (handle-change-team-org msg)
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -104,15 +104,15 @@
|
|||||||
variant (or (.getEnglishName ^js font "preferredSubfamily")
|
variant (or (.getEnglishName ^js font "preferredSubfamily")
|
||||||
(.getEnglishName ^js font "fontSubfamily"))
|
(.getEnglishName ^js font "fontSubfamily"))
|
||||||
|
|
||||||
;; Vertical metrics determine the baseline in a text and the space between lines of
|
;; Vertical metrics determine the baseline in a text and the space between lines of
|
||||||
;; text. For historical reasons, there are three pairs of ascender/descender
|
;; text. For historical reasons, there are three pairs of ascender/descender
|
||||||
;; values, known as hhea, OS/2 and uSWin metrics. Depending on the font, operating
|
;; values, known as hhea, OS/2 and uSWin metrics. Depending on the font, operating
|
||||||
;; system and application a different set will be used to render text on the
|
;; system and application a different set will be used to render text on the
|
||||||
;; screen. On Mac, Safari and Chrome use the hhea values to render text. Firefox
|
;; screen. On Mac, Safari and Chrome use the hhea values to render text. Firefox
|
||||||
;; will respect the useTypoMetrics setting and will use the OS/2 if it is set. If
|
;; will respect the useTypoMetrics setting and will use the OS/2 if it is set. If
|
||||||
;; the useTypoMetrics is not set, Firefox will also use metrics from the hhea
|
;; the useTypoMetrics is not set, Firefox will also use metrics from the hhea
|
||||||
;; table. On Windows, all browsers use the usWin metrics, but respect the
|
;; table. On Windows, all browsers use the usWin metrics, but respect the
|
||||||
;; useTypoMetrics setting and if set will use the OS/2 values.
|
;; useTypoMetrics setting and if set will use the OS/2 values.
|
||||||
|
|
||||||
hhea-ascender (abs (-> ^js font .-tables .-hhea .-ascender))
|
hhea-ascender (abs (-> ^js font .-tables .-hhea .-ascender))
|
||||||
hhea-descender (abs (-> ^js font .-tables .-hhea .-descender))
|
hhea-descender (abs (-> ^js font .-tables .-hhea .-descender))
|
||||||
|
|||||||
@@ -156,8 +156,8 @@
|
|||||||
(defn- update-plugin-permissions-peek
|
(defn- update-plugin-permissions-peek
|
||||||
[{:keys [plugin-id url]}]
|
[{:keys [plugin-id url]}]
|
||||||
(when url
|
(when url
|
||||||
;; If the saved manifest has a URL we fetch the manifest to check
|
;; If the saved manifest has a URL we fetch the manifest to check
|
||||||
;; for updates
|
;; for updates
|
||||||
(->> (fetch-manifest url)
|
(->> (fetch-manifest url)
|
||||||
(rx/subs!
|
(rx/subs!
|
||||||
(fn [new-manifest]
|
(fn [new-manifest]
|
||||||
|
|||||||
@@ -410,25 +410,25 @@
|
|||||||
(string? value)
|
(string? value)
|
||||||
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-shadow value)]}
|
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value-shadow value)]}
|
||||||
|
|
||||||
;; Empty value
|
;; Empty value
|
||||||
(nil? value) {:errors [(wte/get-error-code :error.token/empty-input)]}
|
(nil? value) {:errors [(wte/get-error-code :error.token/empty-input)]}
|
||||||
|
|
||||||
;; Invalid value
|
;; Invalid value
|
||||||
(not (js/Array.isArray value)) {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]}
|
(not (js/Array.isArray value)) {:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]}
|
||||||
|
|
||||||
;; Array of shadows
|
;; Array of shadows
|
||||||
:else
|
:else
|
||||||
(let [converted (js->clj value :keywordize-keys true)
|
(let [converted (js->clj value :keywordize-keys true)
|
||||||
;; Parse each shadow with its index
|
;; Parse each shadow with its index
|
||||||
parsed-shadows (map-indexed
|
parsed-shadows (map-indexed
|
||||||
(fn [idx shadow-map]
|
(fn [idx shadow-map]
|
||||||
(parse-single-shadow shadow-map idx))
|
(parse-single-shadow shadow-map idx))
|
||||||
converted)
|
converted)
|
||||||
|
|
||||||
;; Collect all errors from all shadows
|
;; Collect all errors from all shadows
|
||||||
all-errors (mapcat :errors parsed-shadows)
|
all-errors (mapcat :errors parsed-shadows)
|
||||||
|
|
||||||
;; Collect all values from shadows that have values
|
;; Collect all values from shadows that have values
|
||||||
all-values (into [] (keep :value parsed-shadows))]
|
all-values (into [] (keep :value parsed-shadows))]
|
||||||
|
|
||||||
(if (seq all-errors)
|
(if (seq all-errors)
|
||||||
|
|||||||
@@ -214,8 +214,8 @@
|
|||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [change-fn
|
(let [change-fn
|
||||||
(fn [shape attrs]
|
(fn [node attrs]
|
||||||
(update shape :fills types.fills/prepend attrs))
|
(update node :fills types.fills/prepend attrs))
|
||||||
undo-id
|
undo-id
|
||||||
(js/Symbol)]
|
(js/Symbol)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
|
|||||||
@@ -1292,9 +1292,9 @@
|
|||||||
(rx/take 1 workspace-data-s)
|
(rx/take 1 workspace-data-s)
|
||||||
(rx/take 1 workspace-data-s)
|
(rx/take 1 workspace-data-s)
|
||||||
workspace-data-s)
|
workspace-data-s)
|
||||||
;; Need to get the file data before the change, so deleted shapes
|
;; Need to get the file data before the change, so deleted shapes
|
||||||
;; still exist, for example. We initialize the buffer with three
|
;; still exist, for example. We initialize the buffer with three
|
||||||
;; copies of the initial state
|
;; copies of the initial state
|
||||||
(rx/buffer 3 1))
|
(rx/buffer 3 1))
|
||||||
|
|
||||||
changes-s
|
changes-s
|
||||||
|
|||||||
@@ -231,12 +231,12 @@
|
|||||||
:timeout nil
|
:timeout nil
|
||||||
:tag :media-loading}))
|
:tag :media-loading}))
|
||||||
(->> (if (seq uris)
|
(->> (if (seq uris)
|
||||||
;; Media objects is a list of URL's pointing to the path
|
;; Media objects is a list of URL's pointing to the path
|
||||||
(process-uris params)
|
(process-uris params)
|
||||||
;; Media objects are blob of data to be upload
|
;; Media objects are blob of data to be upload
|
||||||
(process-blobs params))
|
(process-blobs params))
|
||||||
|
|
||||||
;; Every stream has its own sideeffect. We need to ignore the result
|
;; Every stream has its own sideeffect. We need to ignore the result
|
||||||
(rx/ignore)
|
(rx/ignore)
|
||||||
(rx/catch #(handle-media-error % on-error))
|
(rx/catch #(handle-media-error % on-error))
|
||||||
(rx/finalize #(st/emit! (ntf/hide :tag :media-loading))))))))
|
(rx/finalize #(st/emit! (ntf/hide :tag :media-loading))))))))
|
||||||
|
|||||||
@@ -793,8 +793,8 @@
|
|||||||
(-> options
|
(-> options
|
||||||
(assoc :reg-objects? true)
|
(assoc :reg-objects? true)
|
||||||
(assoc :ignore-tree ignore-tree)
|
(assoc :ignore-tree ignore-tree)
|
||||||
;; Attributes that can change in the transform. This
|
;; Attributes that can change in the transform. This
|
||||||
;; way we don't have to check all the attributes
|
;; way we don't have to check all the attributes
|
||||||
(assoc :attrs transform-attrs))
|
(assoc :attrs transform-attrs))
|
||||||
|
|
||||||
update-shape
|
update-shape
|
||||||
|
|||||||
@@ -439,7 +439,7 @@
|
|||||||
add-new-variant? (and
|
add-new-variant? (and
|
||||||
;; The parent is a variant container
|
;; The parent is a variant container
|
||||||
(-> parent-id objects ctc/is-variant-container?)
|
(-> parent-id objects ctc/is-variant-container?)
|
||||||
;; Any of the shapes is a main instance
|
;; Any of the shapes is a main instance
|
||||||
(some (comp ctc/main-instance? objects) ids))
|
(some (comp ctc/main-instance? objects) ids))
|
||||||
|
|
||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
([value shape-ids attributes]
|
([value shape-ids attributes]
|
||||||
(update-stroke-color value shape-ids attributes nil))
|
(update-stroke-color value shape-ids attributes nil))
|
||||||
|
|
||||||
;; The attributes param is needed to have the same arity that other update functions
|
;; The attributes param is needed to have the same arity that other update functions
|
||||||
([value shape-ids _attributes page-id]
|
([value shape-ids _attributes page-id]
|
||||||
(when-let [color (value->color value)]
|
(when-let [color (value->color value)]
|
||||||
(dwsh/update-shapes shape-ids
|
(dwsh/update-shapes shape-ids
|
||||||
@@ -539,11 +539,10 @@
|
|||||||
value shape-ids
|
value shape-ids
|
||||||
#{:stroke-width}
|
#{:stroke-width}
|
||||||
page-id))
|
page-id))
|
||||||
|
(some attributes #{:max-width :max-height})
|
||||||
(some attributes #{:max-width :max-height :layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w})
|
|
||||||
(conj #(update-layout-sizing-limits
|
(conj #(update-layout-sizing-limits
|
||||||
value shape-ids
|
value shape-ids
|
||||||
(set (filter attributes #{:max-width :max-height :layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w}))
|
(set (filter attributes #{:max-width :max-height}))
|
||||||
page-id))))
|
page-id))))
|
||||||
|
|
||||||
(defn apply-dimensions-token
|
(defn apply-dimensions-token
|
||||||
|
|||||||
@@ -1121,15 +1121,15 @@
|
|||||||
:cell cell))
|
:cell cell))
|
||||||
|
|
||||||
add-component-to-variant? (and
|
add-component-to-variant? (and
|
||||||
;; Any of the shapes is a head
|
;; Any of the shapes is a head
|
||||||
(some (comp ctk/instance-head? objects) ids)
|
(some (comp ctk/instance-head? objects) ids)
|
||||||
;; Any ancestor of the destination parent is a variant
|
;; Any ancestor of the destination parent is a variant
|
||||||
(->> (cfh/get-parents-with-self objects frame-id)
|
(->> (cfh/get-parents-with-self objects frame-id)
|
||||||
(some ctk/is-variant?)))
|
(some ctk/is-variant?)))
|
||||||
add-new-variant? (and
|
add-new-variant? (and
|
||||||
;; The parent is a variant container
|
;; The parent is a variant container
|
||||||
(-> frame-id objects ctk/is-variant-container?)
|
(-> frame-id objects ctk/is-variant-container?)
|
||||||
;; Any of the shapes is a main instance
|
;; Any of the shapes is a main instance
|
||||||
(some (comp ctk/main-instance? objects) ids))]
|
(some (comp ctk/main-instance? objects) ids))]
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
|
|||||||
@@ -447,8 +447,8 @@
|
|||||||
:stroke-opacity 1
|
:stroke-opacity 1
|
||||||
:stroke-width 2}
|
:stroke-width 2}
|
||||||
|
|
||||||
;; Move the position of the variant container so the main shape doesn't
|
;; Move the position of the variant container so the main shape doesn't
|
||||||
;; change its position
|
;; change its position
|
||||||
delta (or delta
|
delta (or delta
|
||||||
(if (ctsl/any-layout? parent)
|
(if (ctsl/any-layout? parent)
|
||||||
(gpt/point 0 0)
|
(gpt/point 0 0)
|
||||||
@@ -456,8 +456,8 @@
|
|||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
|
|
||||||
|
|
||||||
;;TODO Refactor all called methods in order to be able to
|
;;TODO Refactor all called methods in order to be able to
|
||||||
;;generate changes instead of call the events
|
;;generate changes instead of call the events
|
||||||
|
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
(when (not= name (:name main))
|
(when (not= name (:name main))
|
||||||
(dwl/rename-component component-id name))
|
(dwl/rename-component component-id name))
|
||||||
|
|
||||||
;; Create variant container
|
;; Create variant container
|
||||||
(dwsh/create-artboard-from-shapes [main-instance-id] variant-id nil nil nil delta flex?)
|
(dwsh/create-artboard-from-shapes [main-instance-id] variant-id nil nil nil delta flex?)
|
||||||
(cl/remove-all-fills variant-vec {:color clr/black :opacity 1})
|
(cl/remove-all-fills variant-vec {:color clr/black :opacity 1})
|
||||||
(when flex? (dwsl/create-layout-from-id variant-id :flex))
|
(when flex? (dwsl/create-layout-from-id variant-id :flex))
|
||||||
@@ -476,12 +476,12 @@
|
|||||||
(cl/add-stroke variant-vec stroke-props)
|
(cl/add-stroke variant-vec stroke-props)
|
||||||
(set-variant-id component-id variant-id))
|
(set-variant-id component-id variant-id))
|
||||||
|
|
||||||
;; Add the necessary number of new properties, with default values
|
;; Add the necessary number of new properties, with default values
|
||||||
(rx/from
|
(rx/from
|
||||||
(repeatedly num-props
|
(repeatedly num-props
|
||||||
#(add-new-property variant-id {:fill-values? true})))
|
#(add-new-property variant-id {:fill-values? true})))
|
||||||
|
|
||||||
;; When the component has path, set the path items as properties values
|
;; When the component has path, set the path items as properties values
|
||||||
(when (> (count cpath) 1)
|
(when (> (count cpath) 1)
|
||||||
(rx/from
|
(rx/from
|
||||||
(map
|
(map
|
||||||
@@ -634,7 +634,7 @@
|
|||||||
prefix (->> shapes
|
prefix (->> shapes
|
||||||
(mapv #(cpn/split-path (:name %)))
|
(mapv #(cpn/split-path (:name %)))
|
||||||
(common-prefix))
|
(common-prefix))
|
||||||
;; When the common parent is root, add a wrapper
|
;; When the common parent is root, add a wrapper
|
||||||
add-wrapper? (empty? prefix)
|
add-wrapper? (empty? prefix)
|
||||||
first-shape (first shapes)
|
first-shape (first shapes)
|
||||||
delta (gpt/point (- (:x rect) (:x first-shape) 30)
|
delta (gpt/point (- (:x rect) (:x first-shape) 30)
|
||||||
@@ -667,10 +667,10 @@
|
|||||||
(dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))
|
(dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))
|
||||||
(ev/event {::ev/name "combine-as-variants" ::ev/origin trigger :number-of-combined (count ids)}))
|
(ev/event {::ev/name "combine-as-variants" ::ev/origin trigger :number-of-combined (count ids)}))
|
||||||
|
|
||||||
;; NOTE: we need to schedule a commit into a
|
;; NOTE: we need to schedule a commit into a
|
||||||
;; microtask for ensure that all the scheduled
|
;; microtask for ensure that all the scheduled
|
||||||
;; microtask of previous events execute before the
|
;; microtask of previous events execute before the
|
||||||
;; commit
|
;; commit
|
||||||
(->> (rx/of (dwu/commit-undo-transaction undo-id))
|
(->> (rx/of (dwu/commit-undo-transaction undo-id))
|
||||||
(rx/observe-on :async)))))))
|
(rx/observe-on :async)))))))
|
||||||
|
|
||||||
@@ -705,7 +705,7 @@
|
|||||||
(let [libraries (dsh/lookup-libraries state)
|
(let [libraries (dsh/lookup-libraries state)
|
||||||
component-id (:component-id shape)
|
component-id (:component-id shape)
|
||||||
component (ctf/get-component libraries (:component-file shape) component-id :include-deleted? false)]
|
component (ctf/get-component libraries (:component-file shape) component-id :include-deleted? false)]
|
||||||
;; If the value is already val, do nothing
|
;; If the value is already val, do nothing
|
||||||
(when (not= val (dm/get-in component [:variant-properties pos :value]))
|
(when (not= val (dm/get-in component [:variant-properties pos :value]))
|
||||||
(let [current-page-objects (dsh/lookup-page-objects state)
|
(let [current-page-objects (dsh/lookup-page-objects state)
|
||||||
variant-id (:variant-id component)
|
variant-id (:variant-id component)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
|
|
||||||
(or (> (:width srect) width)
|
(or (> (:width srect) width)
|
||||||
(> (:height srect) height))
|
(> (:height srect) height))
|
||||||
(let [srect (gal/adjust-to-viewport size srect {:padding 40})
|
(let [srect (gal/adjust-to-viewport size srect {:padding 40 :min-zoom 0.01})
|
||||||
zoom (/ (:width size) (:width srect))]
|
zoom (/ (:width size) (:width srect))]
|
||||||
|
|
||||||
(-> local
|
(-> local
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
state
|
state
|
||||||
(update state :workspace-local
|
(update state :workspace-local
|
||||||
(fn [{:keys [vport] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(let [srect (gal/adjust-to-viewport vport srect {:padding 160})
|
(let [srect (gal/adjust-to-viewport vport srect {:padding 160 :min-zoom 0.01})
|
||||||
zoom (/ (:width vport) (:width srect))]
|
zoom (/ (:width vport) (:width srect))]
|
||||||
(-> local
|
(-> local
|
||||||
(assoc :zoom zoom)
|
(assoc :zoom zoom)
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
(gsh/shapes->rect))]
|
(gsh/shapes->rect))]
|
||||||
(update state :workspace-local
|
(update state :workspace-local
|
||||||
(fn [{:keys [vport] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(let [srect (gal/adjust-to-viewport vport srect {:padding 40})
|
(let [srect (gal/adjust-to-viewport vport srect {:padding 40 :min-zoom 0.01})
|
||||||
zoom (/ (:width vport) (:width srect))]
|
zoom (/ (:width vport) (:width srect))]
|
||||||
(-> local
|
(-> local
|
||||||
(assoc :zoom zoom)
|
(assoc :zoom zoom)
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
(fn [{:keys [vport] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(let [srect (gal/adjust-to-viewport
|
(let [srect (gal/adjust-to-viewport
|
||||||
vport srect
|
vport srect
|
||||||
{:padding 40})
|
{:padding 40 :min-zoom 0.01})
|
||||||
zoom (/ (:width vport)
|
zoom (/ (:width vport)
|
||||||
(:width srect))]
|
(:width srect))]
|
||||||
(-> local
|
(-> local
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
;; Will contain the latest error report assigned
|
;; Will contain the latest error report assigned
|
||||||
(def last-report nil)
|
(def last-report nil)
|
||||||
|
|
||||||
|
;; Will contain last uncaught exception
|
||||||
|
(def last-exception nil)
|
||||||
|
|
||||||
(defn- print-data!
|
(defn- print-data!
|
||||||
[data]
|
[data]
|
||||||
(-> data
|
(-> data
|
||||||
@@ -338,7 +341,6 @@
|
|||||||
(print-data! werror)
|
(print-data! werror)
|
||||||
(print-explain! werror))))))))
|
(print-explain! werror))))))))
|
||||||
|
|
||||||
|
|
||||||
(defonce uncaught-error-handler
|
(defonce uncaught-error-handler
|
||||||
(letfn [(is-ignorable-exception? [cause]
|
(letfn [(is-ignorable-exception? [cause]
|
||||||
(let [message (ex-message cause)]
|
(let [message (ex-message cause)]
|
||||||
@@ -349,10 +351,31 @@
|
|||||||
|
|
||||||
(on-unhandled-error [event]
|
(on-unhandled-error [event]
|
||||||
(.preventDefault ^js event)
|
(.preventDefault ^js event)
|
||||||
(when-let [error (unchecked-get event "error")]
|
(when-let [cause (unchecked-get event "error")]
|
||||||
(when-not (is-ignorable-exception? error)
|
(set! last-exception cause)
|
||||||
(on-error error))))]
|
(when-not (is-ignorable-exception? cause)
|
||||||
|
(ex/print-throwable cause :prefix "uncaught exception")
|
||||||
|
(st/async-emit!
|
||||||
|
(ntf/show {:content (tr "errors.unexpected-exception" (ex-message cause))
|
||||||
|
:type :toast
|
||||||
|
:level :error
|
||||||
|
:timeout 3000})))))
|
||||||
|
|
||||||
|
|
||||||
|
(on-unhandled-rejection [event]
|
||||||
|
(.preventDefault ^js event)
|
||||||
|
(when-let [cause (unchecked-get event "reason")]
|
||||||
|
(set! last-exception cause)
|
||||||
|
(ex/print-throwable cause :prefix "uncaught rejection")
|
||||||
|
(st/async-emit!
|
||||||
|
(ntf/show {:content (tr "errors.unexpected-exception" (ex-message cause))
|
||||||
|
:type :toast
|
||||||
|
:level :error
|
||||||
|
:timeout 3000}))))]
|
||||||
|
|
||||||
(.addEventListener glob/window "error" on-unhandled-error)
|
(.addEventListener glob/window "error" on-unhandled-error)
|
||||||
|
(.addEventListener glob/window "unhandledrejection" on-unhandled-rejection)
|
||||||
(fn []
|
(fn []
|
||||||
(.removeEventListener glob/window "error" on-unhandled-error))))
|
(.removeEventListener glob/window "error" on-unhandled-error)
|
||||||
|
(.removeEventListener glob/window "unhandledrejection" on-unhandled-rejection))))
|
||||||
|
|
||||||
|
|||||||
@@ -214,8 +214,8 @@
|
|||||||
([font-id variant-id]
|
([font-id variant-id]
|
||||||
(log/dbg :action "try-ensure-loaded!" :font-id font-id :variant-id variant-id)
|
(log/dbg :action "try-ensure-loaded!" :font-id font-id :variant-id variant-id)
|
||||||
(if-not (exists? js/window)
|
(if-not (exists? js/window)
|
||||||
;; If we are in the worker environment, we just mark it as loaded
|
;; If we are in the worker environment, we just mark it as loaded
|
||||||
;; without really loading it.
|
;; without really loading it.
|
||||||
(do
|
(do
|
||||||
(swap! loaded-hints conj {:font-id font-id :font-variant-id variant-id})
|
(swap! loaded-hints conj {:font-id font-id :font-variant-id variant-id})
|
||||||
(p/resolved font-id))
|
(p/resolved font-id))
|
||||||
|
|||||||
@@ -133,7 +133,7 @@
|
|||||||
[{:keys [shape] :as props}]
|
[{:keys [shape] :as props}]
|
||||||
(let [childs (mapv #(get objects %) (:shapes shape))]
|
(let [childs (mapv #(get objects %) (:shapes shape))]
|
||||||
(if (and (map? (:content shape))
|
(if (and (map? (:content shape))
|
||||||
;; tspan shouldn't be contained in a group or have svg defs
|
;; tspan shouldn't be contained in a group or have svg defs
|
||||||
(not= :tspan (get-in shape [:content :tag]))
|
(not= :tspan (get-in shape [:content :tag]))
|
||||||
(or (= :svg (get-in shape [:content :tag]))
|
(or (= :svg (get-in shape [:content :tag]))
|
||||||
(contains? shape :svg-attrs)))
|
(contains? shape :svg-attrs)))
|
||||||
|
|||||||
@@ -762,7 +762,7 @@
|
|||||||
h (:height viewport)
|
h (:height viewport)
|
||||||
|
|
||||||
comment-width 284 ;; TODO: this is the width set via CSS in an outer container…
|
comment-width 284 ;; TODO: this is the width set via CSS in an outer container…
|
||||||
;; We should probably do this in a different way.
|
;; We should probably do this in a different way.
|
||||||
|
|
||||||
orientation-left? (>= (+ base-x comment-width (:x bubble-margin)) w)
|
orientation-left? (>= (+ base-x comment-width (:x bubble-margin)) w)
|
||||||
orientation-top? (>= base-y (/ h 2))
|
orientation-top? (>= base-y (/ h 2))
|
||||||
|
|||||||
@@ -198,7 +198,7 @@
|
|||||||
:valid (and touched? (not error))
|
:valid (and touched? (not error))
|
||||||
:invalid (and touched? error)
|
:invalid (and touched? error)
|
||||||
:disabled disabled)
|
:disabled disabled)
|
||||||
;; :empty (str/empty? value)
|
;; :empty (str/empty? value)
|
||||||
|
|
||||||
|
|
||||||
on-focus #(reset! focus? true)
|
on-focus #(reset! focus? true)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
[app.main.ui.dashboard.team-form]
|
[app.main.ui.dashboard.team-form]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.icons :as deprecated-icon]
|
[app.main.ui.icons :as deprecated-icon]
|
||||||
|
[app.main.ui.nitrate.nitrate-form]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.dom.dnd :as dnd]
|
[app.util.dom.dnd :as dnd]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
@@ -280,8 +281,8 @@
|
|||||||
|
|
||||||
(mf/defc teams-selector-dropdown*
|
(mf/defc teams-selector-dropdown*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
[{:keys [team profile teams] :rest props}]
|
[{:keys [team profile teams show-default-team allow-create-teams allow-create-org] :rest props}]
|
||||||
(let [on-create-click
|
(let [on-create-team-click
|
||||||
(mf/use-fn #(st/emit! (modal/show :team-form {})))
|
(mf/use-fn #(st/emit! (modal/show :team-form {})))
|
||||||
|
|
||||||
on-team-click
|
on-team-click
|
||||||
@@ -290,18 +291,27 @@
|
|||||||
(let [team-id (-> (dom/get-current-target event)
|
(let [team-id (-> (dom/get-current-target event)
|
||||||
(dom/get-data "value")
|
(dom/get-data "value")
|
||||||
(uuid/parse))]
|
(uuid/parse))]
|
||||||
(st/emit! (dcm/go-to-dashboard-recent :team-id team-id)))))]
|
(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 {})))))]
|
||||||
|
|
||||||
[:> dropdown-menu* props
|
[:> dropdown-menu* props
|
||||||
|
|
||||||
[:> dropdown-menu-item* {:on-click on-team-click
|
(when show-default-team
|
||||||
:data-value (:default-team-id profile)
|
[:> dropdown-menu-item* {:on-click on-team-click
|
||||||
:class (stl/css :team-dropdown-item)}
|
:data-value (:default-team-id profile)
|
||||||
[:span {:class (stl/css :penpot-icon)} deprecated-icon/logo-icon]
|
: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")]
|
[:span {:class (stl/css :team-text)} (tr "dashboard.your-penpot")]
|
||||||
(when (= (:default-team-id profile) (:id team))
|
(when (= (:default-team-id profile) (:id team))
|
||||||
tick-icon)]
|
tick-icon)])
|
||||||
|
|
||||||
(for [team-item (remove :is-default (vals teams))]
|
(for [team-item (remove :is-default (vals teams))]
|
||||||
[:> dropdown-menu-item* {:on-click on-team-click
|
[:> dropdown-menu-item* {:on-click on-team-click
|
||||||
@@ -322,11 +332,19 @@
|
|||||||
(when (= (:id team-item) (:id team))
|
(when (= (:id team-item) (:id team))
|
||||||
tick-icon)])
|
tick-icon)])
|
||||||
|
|
||||||
[:hr {:role "separator" :class (stl/css :team-separator)}]
|
(when allow-create-teams
|
||||||
[:> dropdown-menu-item* {:on-click on-create-click
|
[:hr {:role "separator" :class (stl/css :team-separator)}]
|
||||||
:class (stl/css :team-dropdown-item :action)}
|
[:> dropdown-menu-item* {:on-click on-create-team-click
|
||||||
[:span {:class (stl/css :icon-wrapper)} add-icon]
|
:class (stl/css :team-dropdown-item :action)}
|
||||||
[:span {:class (stl/css :team-text)} (tr "dashboard.create-new-team")]]]))
|
[: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")]])]))
|
||||||
|
|
||||||
(mf/defc team-options-dropdown*
|
(mf/defc team-options-dropdown*
|
||||||
{::mf/private true}
|
{::mf/private true}
|
||||||
@@ -476,9 +494,80 @@
|
|||||||
:data-testid "delete-team"}
|
:data-testid "delete-team"}
|
||||||
(tr "dashboard.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*
|
(mf/defc sidebar-team-switch*
|
||||||
[{:keys [team profile]}]
|
[{:keys [team profile]}]
|
||||||
(let [teams (mf/deref refs/teams)
|
(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)))
|
||||||
|
|
||||||
subscription
|
subscription
|
||||||
(get team :subscription)
|
(get team :subscription)
|
||||||
@@ -586,7 +675,10 @@
|
|||||||
:class (stl/css :dropdown :teams-dropdown)
|
:class (stl/css :dropdown :teams-dropdown)
|
||||||
:team team
|
:team team
|
||||||
:profile profile
|
:profile profile
|
||||||
:teams teams}]
|
:teams teams
|
||||||
|
:show-default-team true
|
||||||
|
:allow-create-teams true
|
||||||
|
:allow-create-org false}]
|
||||||
|
|
||||||
[:> team-options-dropdown* {:show show-team-options-menu?
|
[:> team-options-dropdown* {:show show-team-options-menu?
|
||||||
:on-close close-team-options-menu
|
:on-close close-team-options-menu
|
||||||
@@ -703,6 +795,8 @@
|
|||||||
[:*
|
[:*
|
||||||
[:div {:class (stl/css-case :sidebar-content true)
|
[:div {:class (stl/css-case :sidebar-content true)
|
||||||
:ref container}
|
:ref container}
|
||||||
|
(when (contains? cf/flags :nitrate)
|
||||||
|
[:> sidebar-org-switch* {:team team :profile profile}])
|
||||||
[:> sidebar-team-switch* {:team team :profile profile}]
|
[:> sidebar-team-switch* {:team team :profile profile}]
|
||||||
|
|
||||||
[:> sidebar-search* {:search-term search-term
|
[:> sidebar-search* {:search-term search-term
|
||||||
|
|||||||
@@ -225,10 +225,10 @@
|
|||||||
(and
|
(and
|
||||||
(= subscription-type "unlimited")
|
(= subscription-type "unlimited")
|
||||||
(or
|
(or
|
||||||
;; common: seats < 25 and diff >= 4
|
;; common: seats < 25 and diff >= 4
|
||||||
(and (< seats 25)
|
(and (< seats 25)
|
||||||
(>= (- editors seats) 4))
|
(>= (- editors seats) 4))
|
||||||
;; special: reached 25+ editors, seats < 25 and there is overuse
|
;; special: reached 25+ editors, seats < 25 and there is overuse
|
||||||
(and (< seats 25)
|
(and (< seats 25)
|
||||||
(>= editors 25)
|
(>= editors 25)
|
||||||
(> editors seats)))))))
|
(> editors seats)))))))
|
||||||
|
|||||||
@@ -189,7 +189,6 @@
|
|||||||
:float
|
:float
|
||||||
:string
|
:string
|
||||||
[:= :multiple]]]]
|
[:= :multiple]]]]
|
||||||
[:text-icon {:optional true} :string]
|
|
||||||
[:default {:optional true} [:maybe :string]]
|
[:default {:optional true} [:maybe :string]]
|
||||||
[:placeholder {:optional true} :string]
|
[:placeholder {:optional true} :string]
|
||||||
[:icon {:optional true} [:maybe schema:icon]]
|
[:icon {:optional true} [:maybe schema:icon]]
|
||||||
@@ -217,8 +216,7 @@
|
|||||||
is-selected-on-focus nillable
|
is-selected-on-focus nillable
|
||||||
tokens applied-token empty-to-end
|
tokens applied-token empty-to-end
|
||||||
on-change on-blur on-focus on-detach
|
on-change on-blur on-focus on-detach
|
||||||
property align ref name
|
property align ref name]
|
||||||
text-icon]
|
|
||||||
:rest props}]
|
:rest props}]
|
||||||
|
|
||||||
(let [;; NOTE: we use mfu/bean here for transparently handle
|
(let [;; NOTE: we use mfu/bean here for transparently handle
|
||||||
@@ -639,23 +637,14 @@
|
|||||||
:on-change store-raw-value
|
:on-change store-raw-value
|
||||||
:variant "comfortable"
|
:variant "comfortable"
|
||||||
:disabled disabled
|
:disabled disabled
|
||||||
:slot-start (when (or icon text-icon)
|
:slot-start (when icon
|
||||||
(mf/html
|
(mf/html [:> tooltip*
|
||||||
[:> tooltip*
|
{:content property
|
||||||
{:content property
|
:id property}
|
||||||
:id property}
|
[:> icon* {:icon-id icon
|
||||||
(cond
|
:size "s"
|
||||||
icon
|
:aria-labelledby property
|
||||||
[:> icon*
|
:class (stl/css :icon)}]]))
|
||||||
{:icon-id icon
|
|
||||||
:size "s"
|
|
||||||
:aria-labelledby property
|
|
||||||
:class (stl/css :icon)}]
|
|
||||||
|
|
||||||
text-icon
|
|
||||||
[:div {:class (stl/css :text-icon)
|
|
||||||
:aria-labelledby property}
|
|
||||||
text-icon])]))
|
|
||||||
:slot-end (when-not disabled
|
:slot-end (when-not disabled
|
||||||
(when (some? tokens)
|
(when (some? tokens)
|
||||||
(mf/html [:> icon-button* {:variant "ghost"
|
(mf/html [:> icon-button* {:variant "ghost"
|
||||||
@@ -687,23 +676,14 @@
|
|||||||
:disabled disabled
|
:disabled disabled
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:class inner-class
|
:class inner-class
|
||||||
:slot-start (when (or icon text-icon)
|
:slot-start (when icon
|
||||||
(mf/html
|
(mf/html [:> tooltip*
|
||||||
[:> tooltip*
|
{:content property
|
||||||
{:content property
|
:id property}
|
||||||
:id property}
|
[:> icon* {:icon-id icon
|
||||||
(cond
|
:size "s"
|
||||||
icon
|
:aria-labelledby property
|
||||||
[:> icon*
|
:class (stl/css :icon)}]]))
|
||||||
{:icon-id icon
|
|
||||||
:size "s"
|
|
||||||
:aria-labelledby property
|
|
||||||
:class (stl/css :icon)}]
|
|
||||||
|
|
||||||
text-icon
|
|
||||||
[:div {:class (stl/css :text-icon)
|
|
||||||
:aria-labelledby property}
|
|
||||||
text-icon])]))
|
|
||||||
:token-wrapper-ref token-wrapper-ref
|
:token-wrapper-ref token-wrapper-ref
|
||||||
:token-detach-btn-ref token-detach-btn-ref
|
:token-detach-btn-ref token-detach-btn-ref
|
||||||
:detach-token detach-token})))]
|
:detach-token detach-token})))]
|
||||||
@@ -738,41 +718,21 @@
|
|||||||
(mf/with-effect [dropdown-options]
|
(mf/with-effect [dropdown-options]
|
||||||
(mf/set-ref-val! options-ref dropdown-options))
|
(mf/set-ref-val! options-ref dropdown-options))
|
||||||
|
|
||||||
(if (some? icon)
|
[:div {:class (dm/str class " " (stl/css :input-wrapper))
|
||||||
[:div {:class (dm/str class " " (stl/css :input-wrapper))
|
:ref wrapper-ref}
|
||||||
:ref wrapper-ref}
|
|
||||||
|
|
||||||
(if (and (some? token-applied)
|
(if (and (some? token-applied)
|
||||||
(not= :multiple token-applied))
|
(not= :multiple token-applied))
|
||||||
[:> token-field* token-props]
|
[:> token-field* token-props]
|
||||||
[:> input-field* input-props])
|
[:> input-field* input-props])
|
||||||
|
|
||||||
(when ^boolean is-open
|
(when ^boolean is-open
|
||||||
(let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)]
|
(let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)]
|
||||||
[:> options-dropdown* {:on-click on-option-click
|
[:> options-dropdown* {:on-click on-option-click
|
||||||
:id listbox-id
|
:id listbox-id
|
||||||
:options options
|
:options options
|
||||||
:selected selected-id
|
:selected selected-id
|
||||||
:focused focused-id
|
:focused focused-id
|
||||||
:align align
|
:align align
|
||||||
:empty-to-end empty-to-end
|
:empty-to-end empty-to-end
|
||||||
:ref set-option-ref}]))]
|
:ref set-option-ref}]))]))
|
||||||
[:div {:class (dm/str class " " (stl/css :input-wrapper))
|
|
||||||
:aria-labelledby property
|
|
||||||
:ref wrapper-ref}
|
|
||||||
|
|
||||||
(if (and (some? token-applied)
|
|
||||||
(not= :multiple token-applied))
|
|
||||||
[:> token-field* token-props]
|
|
||||||
[:> input-field* input-props])
|
|
||||||
|
|
||||||
(when ^boolean is-open
|
|
||||||
(let [options (if (delay? dropdown-options) @dropdown-options dropdown-options)]
|
|
||||||
[:> options-dropdown* {:on-click on-option-click
|
|
||||||
:id listbox-id
|
|
||||||
:options options
|
|
||||||
:selected selected-id
|
|
||||||
:focused focused-id
|
|
||||||
:align align
|
|
||||||
:empty-to-end empty-to-end
|
|
||||||
:ref set-option-ref}]))])))
|
|
||||||
|
|||||||
@@ -29,14 +29,7 @@
|
|||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
color: var(--color-foreground-secondary);
|
color: var(--color-foreground-secondary);
|
||||||
min-inline-size: var(--sp-l);
|
min-width: var(--sp-l);
|
||||||
}
|
|
||||||
|
|
||||||
.text-icon {
|
|
||||||
color: var(--color-foreground-secondary);
|
|
||||||
@include t.use-typography("code-font");
|
|
||||||
inline-size: fit-content;
|
|
||||||
min-inline-size: px2rem(40);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.invisible-button {
|
.invisible-button {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
--token-field-outline-color: none;
|
--token-field-outline-color: none;
|
||||||
--token-field-height: var(--sp-xxxl);
|
--token-field-height: var(--sp-xxxl);
|
||||||
--token-field-margin: unset;
|
--token-field-margin: unset;
|
||||||
display: inline-flex;
|
display: grid;
|
||||||
column-gap: var(--sp-xs);
|
column-gap: var(--sp-xs);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"React error boundary components"
|
"React error boundary components"
|
||||||
(:require
|
(:require
|
||||||
["react-error-boundary" :as reb]
|
["react-error-boundary" :as reb]
|
||||||
|
[app.common.exceptions :as ex]
|
||||||
[app.main.errors :as errors]
|
[app.main.errors :as errors]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[goog.functions :as gfn]
|
[goog.functions :as gfn]
|
||||||
@@ -34,7 +35,8 @@
|
|||||||
;; very small amount of time, so we debounce for 100ms for
|
;; very small amount of time, so we debounce for 100ms for
|
||||||
;; avoid duplicate and redundant reports
|
;; avoid duplicate and redundant reports
|
||||||
(gfn/debounce (fn [error info]
|
(gfn/debounce (fn [error info]
|
||||||
(js/console.log "Cause stack: \n" (.-stack error))
|
(set! errors/last-exception error)
|
||||||
|
(ex/print-throwable error)
|
||||||
(js/console.error
|
(js/console.error
|
||||||
"Component trace: \n"
|
"Component trace: \n"
|
||||||
(unchecked-get info "componentStack")
|
(unchecked-get info "componentStack")
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user