Compare commits

..

1 Commits

Author SHA1 Message Date
Aitor Moreno
c598ace7c4 WIP 2025-06-03 16:51:33 +02:00
344 changed files with 19703 additions and 23961 deletions

View File

@@ -136,38 +136,6 @@ jobs:
- ~/.cache/ms-playwright
key: v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
test-library:
docker:
- image: penpotapp/devenv:latest
working_directory: ~/repo
resource_class: medium+
environment:
JAVA_OPTS: -Xmx6g
NODE_OPTIONS: --max-old-space-size=4096
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "frontend/deps.edn"}}-{{ checksum "frontend/yarn.lock" }}
- run:
name: Install dependencies and build
working_directory: "./library"
command: |
yarn install
- run:
name: Build and Test
working_directory: "./library"
command: |
./scripts/build
yarn run test
test-components:
docker:
- image: penpotapp/devenv:latest
@@ -316,11 +284,6 @@ workflows:
requires:
- lint: success
- test-library:
requires:
- test-frontend: success
- lint: success
- test-components:
requires:
- test-frontend: success

View File

@@ -1,128 +0,0 @@
name: Build and Upload Penpot Bundles non-prod
on:
# Create bundler for every tag
push:
tags:
- '**' # Pattern matched against refs/tags
# Create bundler every hour between 5:00 and 20:00 on working days
schedule:
- cron: '0 5-20 * * 1-5'
# Create bundler from manual action
workflow_dispatch:
inputs:
zip_mode:
# zip_mode defines how the build artifacts are packaged:
# - 'individual': creates one ZIP file per component (frontend, backend, exporter)
# - 'all': creates a single ZIP containing all components
description: 'Bundle packaging mode'
required: false
default: 'individual'
type: choice
options:
- individual
- all
jobs:
build-bundles:
name: Build and Upload Penpot Bundles
runs-on: ubuntu-24.04
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract somer useful variables
id: vars
run: |
echo "commit_hash=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
echo "gh_branch=${{ github.base_ref || github.ref_name }}" >> $GITHUB_OUTPUT
# Set up Docker Buildx for multi-arch build
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Run manage.sh build-bundle from host
run: ./manage.sh build-bundle
- name: Prepare directories for zipping
run: |
mkdir zips
mv bundles penpot
- name: Create zip bundles for zip_mode == 'all'
if: ${{ github.event.inputs.zip_mode == 'all' }}
run: |
echo "📦 Packaging Penpot 'all' bundles..."
zip -r zips/penpot-all-bundles.zip penpot
- name: Create zip bundles for zip_mode == 'individual'
if: ${{ github.event.inputs.zip_mode == 'individual' }}
run: |
echo "📦 Packaging Penpot 'individual' bundles..."
zip -r zips/penpot-frontend.zip penpot/frontend
zip -r zips/penpot-backend.zip penpot/backend
zip -r zips/penpot-exporter.zip penpot/exporter
- name: Upload unified 'all' bundle
if: ${{ github.event.inputs.zip_mode == 'all' }}
uses: actions/upload-artifact@v4
with:
name: penpot-all-bundles
path: zips/penpot-all-bundles.zip
- name: Upload individual bundles
if: ${{ github.event.inputs.zip_mode == 'individual' }}
uses: actions/upload-artifact@v4
with:
name: penpot-individual-bundles
path: |
zips/penpot-frontend.zip
zips/penpot-backend.zip
zips/penpot-exporter.zip
- name: Upload unified 'all' bundle to S3
if: ${{ github.event.inputs.zip_mode == 'all' }}
run: |
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.gh_branch}}.zip
aws s3 cp zips/penpot-all-bundles.zip s3://${{ secrets.S3_BUCKET }}/penpot-all-bundles-${{ steps.vars.outputs.commit_hash }}.zip
- name: Upload 'individual' bundles to S3
if: ${{ github.event.inputs.zip_mode == 'individual' }}
run: |
for name in penpot-frontend penpot-backend penpot-exporter; do
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.gh_branch }}-latest.zip
aws s3 cp zips/${name}.zip s3://${{ secrets.S3_BUCKET }}/${name}-${{ steps.vars.outputs.commit_hash }}.zip
done
- name: Notify Mattermost about automatic bundles
if: github.event_name == 'pull_request'
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
TEXT: |
📦 *Penpot bundle automatically generated*
📄 PR: ${{ github.event.pull_request.title }}
🔁 From: \`${{ github.head_ref }}\` to \`{{ github.base_ref }}\`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Notify Mattermost about manual bundles
if: github.event_name == 'workflow_dispatch'
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
TEXT: |
📦 *Penpot bundle manually generated*
📄 Triggered from branch: `${{ github.ref_name}}`
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: Print artifact summary URL
run: |
echo "📦 Artifacts available at:"
echo "🔗 https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"

1
.gitignore vendored
View File

@@ -30,7 +30,6 @@
/*.zip
/.clj-kondo/.cache
/_dump
/notes
/backend/*.md
/backend/*.sql
/backend/*.txt

View File

@@ -12,20 +12,11 @@ The initial prototype is completly reworked for provide a more consistent API
and to have proper validation and params decoding. All the details can be found
on [its own changelog](library/CHANGES.md)
**Penpot migrate from Redis to Valkey**
As [Valkey](https://valkey.io/) is an opne-souce fork of [Redis](https://redis.io/)
version 7.2.4, this version of Penpot will be compatible with Redis but may diverge
in future versions. Therefore, **migration from Redis to ValKey is recommended for all
on-premises instances** that want to keep up to date.
### :heart: Community contributions (Thank you!)
- Add Serbian language [GitHub #5002](https://github.com/penpot/penpot/issues/5002) by [crnobog69](https://github.com/crnobog69)
### :sparkles: New features & Enhancements
### :sparkles: New features
- Optimize profile setup flow for better user experience [Taiga #10028](https://tree.taiga.io/project/penpot/us/10028)
- Rewrite path shape data PathData encoding [Taiga #8542](https://tree.taiga.io/project/penpot/us/8542?milestone=441308)
- Update base image for Docker Backend and Exporter to Ubuntu 24.04
- Update base image for Docker Frontend to Nginx 1.28.0
- Allow multi file token import [Github #27](https://github.com/tokens-studio/penpot/issues/27)
@@ -33,46 +24,28 @@ on-premises instances** that want to keep up to date.
- Deselect layers (and path nodes) with Ctrl+Shift+Drag [Github #2509](https://github.com/penpot/penpot/issues/2509)
- Copy to SVG from contextual menu [Github #838](https://github.com/penpot/penpot/issues/838)
- Add styles for Inkeep Chat at workspace [Taiga #10708](https://tree.taiga.io/project/penpot/us/10708)
- On components overrides, separate the content of the text from the rest of properties [Taiga #7434](https://tree.taiga.io/project/penpot/us/7434)
- Add configuration for air gapped installations with Docker
- Support system color scheme [Github #5030](https://github.com/penpot/penpot/issues/5030)
- Persist ruler visibility across files and reloads [GitHub #4586](https://github.com/penpot/penpot/issues/4586)
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
- Add tooltip component to DS [Taiga 9220](https://tree.taiga.io/project/penpot/us/9220)
- Allow multi file token export [Taiga #10144](https://tree.taiga.io/project/penpot/us/10144)
- Fix problem when double click on hidden shapes [Taiga #11314](https://tree.taiga.io/project/penpot/issue/11314)
### :bug: Bugs fixed
- Fix getCurrentUser for plugins api [Taiga #11057](https://tree.taiga.io/project/penpot/issue/11057)
- Fix spacing / sizes of different elements in the measurements section of the design tab [Taiga #11076](https://tree.taiga.io/project/penpot/issue/11076)
- Fix selection of short paths [Github #4472](https://github.com/penpot/penpot/issues/4472)
- Fix element positioning on the right side to adjust to grid [#11073](https://tree.taiga.io/project/penpot/issue/11073)
- Fix palette is over sidebar [#11160](https://tree.taiga.io/project/penpot/issue/11160)
- Fix font size input not displaying "mixed" when multiple texts are selected [Taiga #11177](https://tree.taiga.io/project/penpot/issue/11177)
- Misalignments at Create account [Taiga #11315](https://tree.taiga.io/project/penpot/issue/11315)
- Fix issue with importing files where flex/grid is used [Taiga #11334](https://tree.taiga.io/project/penpot/issue/11334)
- Fix wrong color in the export progress bar [Taiga #11299](https://tree.taiga.io/project/penpot/issue/11299)
- Fix right sidebar width overflow on long layer names [Taiga #11212](https://tree.taiga.io/project/penpot/issue/11212)
- Fix comment icon fill [Taiga #11388](https://tree.taiga.io/project/penpot/issue/11388)
- Fix gap on radio-buttons component [Taiga #11360](https://tree.taiga.io/project/penpot/issue/11360)
- Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
- Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337](https://tree.taiga.io/project/penpot/issue/11337)
- Fix shortcut error pressing G+W from the View Mode [Taiga #11061](https://tree.taiga.io/project/penpot/issue/11061)
- Fix entering long project name [Taiga #11417](https://tree.taiga.io/project/penpot/issue/11417)
- Fix slow color picker [Taiga #11019](https://tree.taiga.io/project/penpot/issue/11019)
- Fix tooltip position after click [Taiga #11405](https://tree.taiga.io/project/penpot/issue/11405)
## 2.7.2
## 2.7.2 (Unreleased)
### :bug: Bugs fixed
- Update plugins runtime [Github #6604](https://github.com/penpot/penpot/pull/6604)
- Backport from develop a minor fix that enables import of files
generated by penpot library [Github #6614](https://github.com/penpot/penpot/pull/6614)
- Fix copy in error message [GitHub #6615](https://github.com/penpot/penpot/pull/6615)
- Fix url on invitation link [Taiga #11284](https://tree.taiga.io/project/penpot/issue/11284)
## 2.7.1

View File

@@ -34,8 +34,7 @@
<br />
[Penpot video](https://github.com/user-attachments/assets/7c67fd7c-04d3-4c9b-88ec-b6f5e23f8332
)
[Penpot video](https://github.com/user-attachments/assets/08b83119-c090-4a74-86ed-7bfbdda9a793)
<br />

View File

@@ -3,7 +3,7 @@
:deps
{penpot/common {:local/root "../common"}
org.clojure/clojure {:mvn/version "1.12.1"}
org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/tools.namespace {:mvn/version "1.5.0"}
com.github.luben/zstd-jni {:mvn/version "1.5.7-3"}
@@ -17,15 +17,8 @@
io.prometheus/simpleclient_httpserver {:mvn/version "0.16.0"}
io.lettuce/lettuce-core {:mvn/version "6.7.0.RELEASE"}
;; Minimal dependencies required by lettuce, we need to include them
;; explicitly because clojure dependency management does not support
;; yet the BOM format.
io.micrometer/micrometer-core {:mvn/version "1.14.2"}
io.micrometer/micrometer-observation {:mvn/version "1.14.2"}
io.lettuce/lettuce-core {:mvn/version "6.6.0.RELEASE"}
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
com.google.guava/guava {:mvn/version "33.4.8-jre"}
funcool/yetti
{:git/tag "v11.4"
@@ -35,10 +28,11 @@
com.github.seancorfield/next.jdbc
{:mvn/version "1.3.1002"}
metosin/reitit-core {:mvn/version "0.9.1"}
metosin/reitit-core {:mvn/version "0.8.0"}
nrepl/nrepl {:mvn/version "1.3.1"}
cider/cider-nrepl {:mvn/version "0.55.7"}
org.postgresql/postgresql {:mvn/version "42.7.6"}
org.postgresql/postgresql {:mvn/version "42.7.5"}
org.xerial/sqlite-jdbc {:mvn/version "3.49.1.0"}
com.zaxxer/HikariCP {:mvn/version "6.3.0"}
@@ -65,7 +59,7 @@
;; Pretty Print specs
pretty-spec/pretty-spec {:mvn/version "0.1.4"}
software.amazon.awssdk/s3 {:mvn/version "2.31.55"}}
software.amazon.awssdk/s3 {:mvn/version "2.31.48"}}
:paths ["src" "resources" "target/classes"]
:aliases

View File

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

View File

@@ -13,7 +13,7 @@ This will automatically include {{requested-by|abbreviate:25}} in the team, so t
Click the link below to provide team access:
{{ public-uri }}/#/dashboard/members?team-id={{team-id}}&invite-email={{requested-by-email|urlescape}}
{{ public-uri }}/#/dashboard/members?team-id{{team-id}}&invite-email={{requested-by-email|urlescape}}

View File

@@ -1,7 +1,4 @@
[{:id "tokens-starter-kit"
:name "Design tokens starter kit"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Tokens%20starter%20kit.penpot"},
{:id "wireframing-kit"
[{:id "wireframing-kit"
:name "Wireframe library"
:file-uri "https://github.com/penpot/penpot-files/raw/refs/heads/main/Wireframing%20kit%20v1.1.penpot"}
{:id "prototype-examples"

View File

@@ -77,9 +77,8 @@ export JAVA_OPTS="\
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv-repl.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:+EnableDynamicAgentLoading \
-XX:-OmitStackTraceInFastThrow \
-XX:-OmitStackTraceInFastThrow \
-XX:+UnlockDiagnosticVMOptions \
-XX:+DebugNonSafepoints \
--sun-misc-unsafe-memory-access=allow \
@@ -107,6 +106,9 @@ export OPTIONS="-A:jmx-remote -A:dev"
# Setup GC
# export OPTIONS="$OPTIONS -J-XX:+UseZGC"
# Enable ImageMagick v7.x support
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
export OPTIONS_EVAL="nil"
# export OPTIONS_EVAL="(set! *warn-on-reflection* true)"

View File

@@ -18,9 +18,9 @@ if [ -f ./environ ]; then
source ./environ
fi
export JAVA_OPTS="-Dim4java.useV7=true -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --sun-misc-unsafe-memory-access=allow --enable-native-access=ALL-UNNAMED --enable-preview $JVM_OPTS $JAVA_OPTS"
export JVM_OPTS="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager -Dlog4j2.configurationFile=log4j2.xml -XX:-OmitStackTraceInFastThrow --enable-native-access=ALL-UNNAMED --enable-preview $JVM_OPTS"
ENTRYPOINT=${1:-app.main};
set -ex
exec $JAVA_CMD $JAVA_OPTS -jar penpot.jar -m $ENTRYPOINT
exec $JAVA_CMD $JVM_OPTS -jar penpot.jar -m $ENTRYPOINT

View File

@@ -36,6 +36,9 @@ export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
# Enable ImageMagick v7.x support
# export OPTIONS="-J-Dim4java.useV7=true $OPTIONS";
# Initialize MINIO config
mc alias set penpot-s3/ http://minio:9000 minioadmin minioadmin -q
mc admin user add penpot-s3 penpot-devenv penpot-devenv -q
@@ -58,8 +61,10 @@ export JAVA_OPTS="\
-Djdk.attach.allowAttachSelf \
-Dlog4j2.configurationFile=log4j2-devenv.xml \
-Djdk.tracePinnedThreads=full \
-Dim4java.useV7=true \
-XX:-OmitStackTraceInFastThrow \
-XX:+EnableDynamicAgentLoading \
-XX:-OmitStackTraceInFastThrow \
-XX:+UnlockDiagnosticVMOptions \
-XX:+DebugNonSafepoints \
--sun-misc-unsafe-memory-access=allow \
--enable-preview \
--enable-native-access=ALL-UNNAMED";

View File

@@ -16,40 +16,16 @@
;; PRE DECODE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- pre-clean-bool-content
[shape]
(if-let [content (get shape :bool-content)]
(-> shape
(assoc :content content)
(dissoc :bool-content))
shape))
(defn- pre-clean-shadow-color
[shape]
(d/update-when shape :shadow
(fn [shadows]
(mapv (fn [shadow]
(update shadow :color
(fn [color]
(let [ref-id (get color :id)
ref-file (get color :file-id)]
(-> (d/without-qualified color)
(select-keys [:opacity :color :gradient :image :ref-id :ref-file])
(cond-> ref-id
(assoc :ref-id ref-id))
(cond-> ref-file
(assoc :ref-file ref-file)))))))
shadows))))
(defn clean-shape-pre-decode
"Applies a pre-decode phase migration to the shape"
[shape]
(cond-> shape
(= "bool" (:type shape))
(pre-clean-bool-content)
(contains? shape :shadow)
(pre-clean-shadow-color)))
(if (= "bool" (:type shape))
(if-let [content (get shape :bool-content)]
(-> shape
(assoc :content content)
(dissoc :bool-content))
shape)
shape))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; POST DECODE

View File

@@ -113,7 +113,7 @@
(sm/encoder ::ctc/component sm/json-transformer))
(def encode-color
(sm/encoder ctcl/schema:library-color sm/json-transformer))
(sm/encoder ::ctcl/color sm/json-transformer))
(def encode-typography
(sm/encoder ::cty/typography sm/json-transformer))
@@ -142,7 +142,7 @@
(sm/decoder ::ctc/component sm/json-transformer))
(def decode-color
(sm/decoder ctcl/schema:library-color sm/json-transformer))
(sm/decoder ::ctcl/color sm/json-transformer))
(def decode-file
(sm/decoder schema:file sm/json-transformer))
@@ -157,7 +157,7 @@
(sm/decoder ::cty/typography sm/json-transformer))
(def decode-tokens-lib
(sm/decoder cto/schema:tokens-lib sm/json-transformer))
(sm/decoder ::cto/tokens-lib sm/json-transformer))
(def decode-plugin-data
(sm/decoder ::ctpg/plugin-data sm/json-transformer))
@@ -186,7 +186,7 @@
(sm/check-fn ::ctf/media))
(def validate-color
(sm/check-fn ctcl/schema:library-color))
(sm/check-fn ::ctcl/color))
(def validate-component
(sm/check-fn ::ctc/component))
@@ -617,7 +617,8 @@
(let [object (->> (read-entry input entry)
(clean-component-pre-decode)
(decode-component)
(clean-component-post-decode))]
(clean-component-post-decode)
(validate-component))]
(if (= id (:id object))
(assoc result id object)
result)))
@@ -651,7 +652,8 @@
(let [object (->> (read-entry input entry)
(bfl/clean-shape-pre-decode)
(decode-shape)
(bfl/clean-shape-post-decode))]
(bfl/clean-shape-post-decode)
(validate-shape))]
(if (= id (:id object))
(assoc result id object)
result)))
@@ -697,6 +699,7 @@
components (read-file-components cfg)
plugin-data (read-file-plugin-data cfg)
pages (read-file-pages cfg)]
{:pages (-> pages keys vec)
:pages-index (into {} pages)
:colors colors
@@ -753,8 +756,7 @@
(assoc :project-id project-id)
(dissoc :options))
file (bfc/process-file cfg file)
file (ctf/check-file file)]
file (bfc/process-file cfg file)]
(bfm/register-pending-migrations! cfg file)
(bfc/save-file! cfg file ::db/return-keys false)

View File

@@ -41,7 +41,7 @@
(if (or (instance? java.util.concurrent.CompletionException cause)
(instance? java.util.concurrent.ExecutionException cause))
(-> record
(assoc ::trace (ex/format-throwable cause :data? true :explain? false :header? false :summary? false))
(assoc ::trace (ex/format-throwable cause :data? false :explain? false :header? false :summary? false))
(assoc ::l/cause (ex-cause cause))
(record->report))
@@ -64,18 +64,18 @@
message))
@message)
:trace (or (::trace record)
(some-> cause (ex/format-throwable :data? true :explain? false :header? false :summary? false)))}
(some-> cause (ex/format-throwable :data? false :explain? false :header? false :summary? false)))}
(when-let [params (or (:request/params context) (:params context))]
{:params (pp/pprint-str params :length 20 :level 15)})
{:params (pp/pprint-str params :length 30 :level 13)})
(when-let [value (:value context)]
{:value (pp/pprint-str value :length 30 :level 13)})
{:value (pp/pprint-str value :length 30 :level 12)})
(when-let [data (some-> data (dissoc ::s/problems ::s/value ::s/spec ::sm/explain :hint))]
{:data (pp/pprint-str data :length 30 :level 13)})
{:data (pp/pprint-str data :length 30 :level 12)})
(when-let [explain (ex/explain data :length 30 :level 13)]
(when-let [explain (ex/explain data :length 30 :level 12)]
{:explain explain})))))
(defn error-record?

View File

@@ -40,6 +40,7 @@
[app.svgo :as-alias svgo]
[app.util.time :as dt]
[app.worker :as-alias wrk]
[cider.nrepl :refer [cider-nrepl-handler]]
[clojure.test :as test]
[clojure.tools.namespace.repl :as repl]
[cuerdas.core :as str]
@@ -604,7 +605,7 @@
(let [p (promise)]
(when (contains? cf/flags :nrepl-server)
(l/inf :hint "start nrepl server" :port 6064)
(nrepl/start-server :bind "0.0.0.0" :port 6064))
(nrepl/start-server :bind "0.0.0.0" :port 6064 :handler cider-nrepl-handler))
(start)
(deref p))

View File

@@ -55,7 +55,7 @@
(sm/check-fn schema:input))
(defn validate-media-type!
([upload] (validate-media-type! upload cm/image-types))
([upload] (validate-media-type! upload cm/valid-image-types))
([upload allowed]
(when-not (contains? allowed (:mtype upload))
(ex/raise :type :validation

View File

@@ -231,14 +231,13 @@
:hint "email has complaint reports")))
(defn prepare-register
[{:keys [::db/pool] :as cfg} {:keys [fullname email accept-newsletter-updates] :as params}]
[{:keys [::db/pool] :as cfg} {:keys [email accept-newsletter-updates] :as params}]
(validate-register-attempt! cfg params)
(let [email (profile/clean-email email)
profile (profile/get-profile-by-email pool email)
params {:email email
:fullname fullname
:password (:password params)
:invitation-token (:invitation-token params)
:backend "penpot"
@@ -255,10 +254,8 @@
(def schema:prepare-register-profile
[:map {:title "prepare-register-profile"}
[:fullname ::sm/text]
[:email ::sm/email]
[:password schema:password]
[:create-welcome-file {:optional true} :boolean]
[:invitation-token {:optional true} schema:token]])
(sv/defmethod ::prepare-register-profile
@@ -362,9 +359,13 @@
:extra-data ptoken})))
(defn register-profile
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token] :as params}]
(let [claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register})
params (into claims params)
[{:keys [::db/conn ::wrk/executor] :as cfg} {:keys [token fullname theme] :as params}]
(let [theme (when (= theme "light") theme)
claims (tokens/verify (::setup/props cfg) {:token token :iss :prepared-register})
params (-> claims
(into params)
(assoc :fullname fullname)
(assoc :theme theme))
profile (if-let [profile-id (:profile-id claims)]
(profile/get-profile conn profile-id)
@@ -478,7 +479,10 @@
(def schema:register-profile
[:map {:title "register-profile"}
[:token schema:token]])
[:token schema:token]
[:fullname [::sm/word-string {:max 100}]]
[:theme {:optional true} [:string {:max 10}]]
[:create-welcome-file {:optional true} :boolean]])
(sv/defmethod ::register-profile
{::rpc/auth false

View File

@@ -559,10 +559,7 @@
f.project_id,
f.created_at,
f.modified_at,
f.data_backend,
f.data_ref_id,
f.name,
f.version,
f.is_shared,
ft.media_id,
p.team_id
@@ -598,11 +595,7 @@
(teams/check-read-permissions! conn profile-id team-id)
(->> (db/exec! conn [sql:team-shared-files team-id])
(into #{} (comp
;; NOTE: this decode operation is a workaround for a
;; fast fix, this should be approached with a more
;; efficient implementation, for now it loads all
;; the files in memory.
(map (partial bfc/decode-file cfg))
(map decode-row)
(map (fn [row]
(if-let [media-id (:media-id row)]
(-> row

View File

@@ -1,88 +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.srepl.fixes.lost-colors
"A collection of adhoc fixes scripts."
(:require
[app.binfile.common :as bfc]
[app.common.logging :as l]
[app.common.types.color :as types.color]
[app.db :as db]
[app.srepl.helpers :as h]))
(def sql:get-affected-files
"SELECT fm.file_id AS id FROM file_migration AS fm WHERE fm.name = '0008-fix-library-colors-v2'")
(def sql:get-matching-snapshot
"SELECT * FROM file_change
WHERE file_id = ?
AND created_at <= ?
AND label IS NOT NULL
AND data IS NOT NULL
ORDER BY created_at DESC
LIMIT 2")
(defn get-affected-migration
[conn file-id]
(db/get* conn :file-migration
{:name "0008-fix-library-colors-v2"
:file-id file-id}))
(defn get-last-valid-snapshot
[conn migration]
(let [[snapshot] (db/exec! conn [sql:get-matching-snapshot
(:file-id migration)
(:created-at migration)])]
(when snapshot
(let [snapshot (assoc snapshot :id (:file-id snapshot))]
(bfc/decode-file h/*system* snapshot)))))
(defn restore-color
[{:keys [data] :as snapshot} color]
(when-let [scolor (get-in data [:colors (:id color)])]
(-> (select-keys scolor types.color/library-color-attrs)
(types.color/check-library-color))))
(defn restore-missing-colors
[{:keys [id] :as file} & _opts]
(l/inf :hint "process file" :file-id (str id) :name (:name file) :has-colors (-> file :data :colors not-empty boolean))
(if-let [colors (-> file :data :colors not-empty)]
(let [migration (get-affected-migration h/*system* id)]
(if-let [snapshot (get-last-valid-snapshot h/*system* migration)]
(do
(l/inf :hint "using snapshot" :snapshot (:label snapshot))
(let [colors (reduce-kv (fn [colors color-id color]
(if-let [result (restore-color snapshot color)]
(do
(l/inf :hint "restored color" :file-id (str id) :color-id (str color-id))
(assoc colors color-id result))
(do
(l/wrn :hint "ignoring color" :file-id (str id) :color (pr-str color))
colors)))
colors
colors)
file (-> file
(update :data assoc :colors colors)
(update :migrations disj "0008-fix-library-colors-v2"))]
(db/delete! h/*system* :file-migration
{:name "0008-fix-library-colors-v2"
:file-id (:id file)})
file))
(do
(db/delete! h/*system* :file-migration
{:name "0008-fix-library-colors-v2"
:file-id (:id file)})
nil)))
(do
(db/delete! h/*system* :file-migration
{:name "0008-fix-library-colors-v2"
:file-id (:id file)})
nil)))

View File

@@ -17,7 +17,6 @@
[app.common.files.validate :as cfv]
[app.common.logging :as l]
[app.common.pprint :as p]
[app.common.schema :as sm]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -396,22 +395,6 @@
libs (bfc/get-resolved-file-libraries system file)]
(cfv/validate-file file libs))))))
(defn validate-file-schema
"Validate structure, referencial integrity and semantic coherence of
all contents of a file. Returns a list of errors."
[file-id]
(let [file-id (h/parse-uuid file-id)]
(db/tx-run! (assoc main/system ::db/rollback true)
(fn [system]
(try
(let [file (bfc/get-file system file-id)]
(cfv/validate-file-schema! file)
(println "OK"))
(catch Exception cause
(if-let [explain (-> cause ex-data ::sm/explain)]
(println (sm/humanize-explain explain))
(ex/print-throwable cause))))))))
(defn repair-file!
"Repair the list of errors detected by validation."
[file-id & {:keys [rollback?] :or {rollback? true} :as opts}]
@@ -491,8 +474,7 @@
:index idx)
(let [system (assoc main/system ::db/rollback rollback?)]
(db/tx-run! system (fn [system]
(binding [h/*system* system
db/*conn* (db/get-connection system)]
(binding [h/*system* system]
(h/process-file! system file-id update-fn opts)))))
(catch Throwable cause

View File

@@ -343,9 +343,8 @@
:name "image"
:frame-id uuid/zero
:parent-id uuid/zero
:type :rect
:fills [{:fill-opacity 1
:fill-image {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
:type :image
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}})}])
;; Check that reference storage objects on filemediaobjects
;; are the same because of deduplication feature.
@@ -463,8 +462,7 @@
fmo3 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
fmo4 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
fmo5 (add-file-media-object :profile-id (:id profile) :file-id (:id file))
s1-shid (uuid/random)
s2-shid (uuid/random)
s-shid (uuid/random)
t-shid (uuid/random)
page-id (first (get-in file [:data :pages]))]
@@ -483,31 +481,19 @@
:changes
[{:type :add-obj
:page-id page-id
:id s1-shid
:id s-shid
:parent-id uuid/zero
:frame-id uuid/zero
:components-v2 true
:obj (cts/setup-shape
{:id s1-shid
{:id s-shid
:name "image"
:frame-id uuid/zero
:parent-id uuid/zero
:type :rect
:fills [{:fill-opacity 1 :fill-image {:id (:id fmo2) :width 101 :height 100 :mtype "image/jpeg"}}]
:strokes [{:stroke-opacity 1 :stroke-image {:id (:id fmo3) :width 102 :height 100 :mtype "image/jpeg"}}]})}
{:type :add-obj
:page-id page-id
:id s2-shid
:parent-id uuid/zero
:frame-id uuid/zero
:components-v2 true
:obj (cts/setup-shape
{:id s2-shid
:name "image"
:frame-id uuid/zero
:parent-id uuid/zero
:type :rect
:fills [{:fill-opacity 1 :fill-image {:id (:id fmo1) :width 103 :height 100 :mtype "image/jpeg"}}]})}
:type :image
:metadata {:id (:id fmo1) :width 100 :height 100 :mtype "image/jpeg"}
:fills [{:opacity 1 :fill-image {:id (:id fmo2) :width 100 :height 100 :mtype "image/jpeg"}}]
:strokes [{:opacity 1 :stroke-image {:id (:id fmo3) :width 100 :height 100 :mtype "image/jpeg"}}]})}
{:type :add-obj
:page-id page-id
:id t-shid
@@ -533,8 +519,7 @@
{:fills [{:fill-opacity 1
:fill-color "#000000"}]
:text "bye"}]}]}]}
:strokes [{:stroke-opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
:strokes [{:opacity 1 :stroke-image {:id (:id fmo5) :width 100 :height 100 :mtype "image/jpeg"}}]})}])
;; run the file-gc task immediately without forced min-age
(t/is (false? (th/run-task! :file-gc {:file-id (:id file)})))
@@ -572,13 +557,10 @@
:vern 0
:changes [{:type :del-obj
:page-id (first (get-in file [:data :pages]))
:id s1-shid}
:id s-shid}
{:type :del-obj
:page-id (first (get-in file [:data :pages]))
:id t-shid}
{:type :del-obj
:page-id (first (get-in file [:data :pages]))
:id s2-shid}])
:id t-shid}])
;; Now, we have deleted the usage of pointers to the
;; file-media-objects, if we paste file-gc, they should be marked

View File

@@ -48,7 +48,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 122785 (:size mobj1)))
(t/is (= 3297 (:size mobj2)))))))
(t/is (= 3299 (:size mobj2)))))))
(t/deftest media-object-upload
(let [prof (th/create-profile* 1)
@@ -85,7 +85,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 312043 (:size mobj1)))
(t/is (= 3890 (:size mobj2)))))))
(t/is (= 3901 (:size mobj2)))))))
(t/deftest media-object-upload-idempotency
@@ -163,7 +163,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 122785 (:size mobj1)))
(t/is (= 3297 (:size mobj2)))))))
(t/is (= 3299 (:size mobj2)))))))
(t/deftest media-object-upload-command
(let [prof (th/create-profile* 1)
@@ -200,7 +200,7 @@
(t/is (sto/object? mobj1))
(t/is (sto/object? mobj2))
(t/is (= 312043 (:size mobj1)))
(t/is (= 3890 (:size mobj2)))))))
(t/is (= 3901 (:size mobj2)))))))
(t/deftest media-object-upload-idempotency-command

View File

@@ -379,14 +379,15 @@
(t/deftest prepare-register-and-register-profile-1
(let [data {::th/type :prepare-register-profile
:email "user@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
(t/is (string? token))
;; try register without token
(let [data {::th/type :register-profile}
(let [data {::th/type :register-profile
:fullname "foobar"
:accept-terms-and-privacy true}
out (th/command! data)]
;; (th/print-result! out)
(let [error (:error out)]
@@ -397,8 +398,11 @@
;; try correct register
(let [data {::th/type :register-profile
:token token
:fullname "foobar"
:utm_campaign "utma"
:mtm_campaign "mtma"}]
:mtm_campaign "mtma"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}]
(let [{:keys [result error]} (th/command! data)]
(t/is (nil? error))))
@@ -420,7 +424,6 @@
;; PREPARE REGISTER
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -429,7 +432,10 @@
;; DO REGISTRATION
(let [data {::th/type :register-profile
:token @current-token}
:token @current-token
:fullname "foobar"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= 1 (:call-count @mock))))
@@ -439,7 +445,6 @@
;; PREPARE REGISTER: second attempt
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -474,7 +479,6 @@
;; PREPARE REGISTER
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -483,7 +487,10 @@
;; DO REGISTRATION
(let [data {::th/type :register-profile
:token @current-token}
:token @current-token
:fullname "foobar"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= 1 (:call-count @mock))))
@@ -497,7 +504,6 @@
;; PREPARE REGISTER: second attempt
(let [data {::th/type :prepare-register-profile
:email "hello@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)
token (get-in out [:result :token])]
@@ -508,7 +514,10 @@
:return true}]
;; DO REGISTRATION: second attempt
(let [data {::th/type :register-profile
:token @current-token}
:token @current-token
:fullname "foobar"
:accept-terms-and-privacy true
:accept-newsletter-subscription true}
out (th/command! data)]
(t/is (nil? (:error out)))
(t/is (= 0 (:call-count @mock))))))))
@@ -523,7 +532,6 @@
:member-email "user@example.com"})
data {::th/type :prepare-register-profile
:invitation-token itoken
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
@@ -534,7 +542,8 @@
(let [rtoken (:token result)
data {::th/type :register-profile
:token rtoken}
:token rtoken
:fullname "foobar"}
{:keys [result error] :as out} (th/command! data)]
;; (th/print-result! out)
@@ -554,7 +563,6 @@
data {::th/type :prepare-register-profile
:invitation-token itoken
:email "user@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)]
@@ -574,7 +582,6 @@
:member-email "user@example.com"})
data {::th/type :prepare-register-profile
:invitation-token itoken
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
out (th/command! data)]
@@ -597,7 +604,6 @@
data {::th/type :prepare-register-profile
:invitation-token itoken
:email "user@example.com"
:fullname "foobar"
:password "foobar"}
out (th/command! data)]
@@ -618,7 +624,6 @@
data {::th/type :prepare-register-profile
:invitation-token itoken
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
out (th/command! data)]
@@ -631,7 +636,6 @@
(t/deftest prepare-register-with-registration-disabled
(with-redefs [app.config/flags #{}]
(let [data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "foobar"}
out (th/command! data)]
@@ -644,7 +648,6 @@
(t/deftest prepare-register-with-existing-user
(let [profile (th/create-profile* 1)
data {::th/type :prepare-register-profile
:fullname "foobar"
:email (:email profile)
:password "foobar"}
out (th/command! data)]
@@ -657,7 +660,6 @@
(let [pool (:app.db/pool th/*system*)
data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "foobar"}]
@@ -672,7 +674,6 @@
(t/deftest register-profile-with-complained-email
(let [pool (:app.db/pool th/*system*)
data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "foobar"}]
@@ -687,7 +688,6 @@
(t/deftest register-profile-with-email-as-password
(let [data {::th/type :prepare-register-profile
:fullname "foobar"
:email "user@example.com"
:password "USER@example.com"}
out (th/command! data)]

View File

@@ -19,34 +19,25 @@ __metadata:
languageName: node
linkType: hard
"@isaacs/fs-minipass@npm:^4.0.0":
version: 4.0.1
resolution: "@isaacs/fs-minipass@npm:4.0.1"
dependencies:
minipass: "npm:^7.0.4"
checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2
languageName: node
linkType: hard
"@npmcli/agent@npm:^3.0.0":
version: 3.0.0
resolution: "@npmcli/agent@npm:3.0.0"
"@npmcli/agent@npm:^2.0.0":
version: 2.2.2
resolution: "@npmcli/agent@npm:2.2.2"
dependencies:
agent-base: "npm:^7.1.0"
http-proxy-agent: "npm:^7.0.0"
https-proxy-agent: "npm:^7.0.1"
lru-cache: "npm:^10.0.1"
socks-proxy-agent: "npm:^8.0.3"
checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271
checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae
languageName: node
linkType: hard
"@npmcli/fs@npm:^4.0.0":
version: 4.0.0
resolution: "@npmcli/fs@npm:4.0.0"
"@npmcli/fs@npm:^3.1.0":
version: 3.1.1
resolution: "@npmcli/fs@npm:3.1.1"
dependencies:
semver: "npm:^7.3.5"
checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5
checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99
languageName: node
linkType: hard
@@ -57,17 +48,29 @@ __metadata:
languageName: node
linkType: hard
"abbrev@npm:^3.0.0":
version: 3.0.1
resolution: "abbrev@npm:3.0.1"
checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf
"abbrev@npm:^2.0.0":
version: 2.0.0
resolution: "abbrev@npm:2.0.0"
checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372
languageName: node
linkType: hard
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
version: 7.1.3
resolution: "agent-base@npm:7.1.3"
checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1":
version: 7.1.1
resolution: "agent-base@npm:7.1.1"
dependencies:
debug: "npm:^4.3.4"
checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50
languageName: node
linkType: hard
"aggregate-error@npm:^3.0.0":
version: 3.1.0
resolution: "aggregate-error@npm:3.1.0"
dependencies:
clean-stack: "npm:^2.0.0"
indent-string: "npm:^4.0.0"
checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039
languageName: node
linkType: hard
@@ -79,9 +82,9 @@ __metadata:
linkType: hard
"ansi-regex@npm:^6.0.1":
version: 6.1.0
resolution: "ansi-regex@npm:6.1.0"
checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc
version: 6.0.1
resolution: "ansi-regex@npm:6.0.1"
checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08
languageName: node
linkType: hard
@@ -138,21 +141,21 @@ __metadata:
linkType: hard
"brace-expansion@npm:^1.1.7":
version: 1.1.12
resolution: "brace-expansion@npm:1.1.12"
version: 1.1.11
resolution: "brace-expansion@npm:1.1.11"
dependencies:
balanced-match: "npm:^1.0.0"
concat-map: "npm:0.0.1"
checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73
checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668
languageName: node
linkType: hard
"brace-expansion@npm:^2.0.1":
version: 2.0.2
resolution: "brace-expansion@npm:2.0.2"
version: 2.0.1
resolution: "brace-expansion@npm:2.0.1"
dependencies:
balanced-match: "npm:^1.0.0"
checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf
checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f
languageName: node
linkType: hard
@@ -172,11 +175,11 @@ __metadata:
languageName: node
linkType: hard
"cacache@npm:^19.0.1":
version: 19.0.1
resolution: "cacache@npm:19.0.1"
"cacache@npm:^18.0.0":
version: 18.0.3
resolution: "cacache@npm:18.0.3"
dependencies:
"@npmcli/fs": "npm:^4.0.0"
"@npmcli/fs": "npm:^3.1.0"
fs-minipass: "npm:^3.0.0"
glob: "npm:^10.2.2"
lru-cache: "npm:^10.0.1"
@@ -184,11 +187,11 @@ __metadata:
minipass-collect: "npm:^2.0.1"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
p-map: "npm:^7.0.2"
ssri: "npm:^12.0.0"
tar: "npm:^7.4.3"
unique-filename: "npm:^4.0.0"
checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c
p-map: "npm:^4.0.0"
ssri: "npm:^10.0.0"
tar: "npm:^6.1.11"
unique-filename: "npm:^3.0.0"
checksum: 10c0/dfda92840bb371fb66b88c087c61a74544363b37a265023223a99965b16a16bbb87661fe4948718d79df6e0cc04e85e62784fbcf1832b2a5e54ff4c46fbb45b7
languageName: node
linkType: hard
@@ -211,10 +214,17 @@ __metadata:
languageName: node
linkType: hard
"chownr@npm:^3.0.0":
version: 3.0.0
resolution: "chownr@npm:3.0.0"
checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10
"chownr@npm:^2.0.0":
version: 2.0.0
resolution: "chownr@npm:2.0.0"
checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1
languageName: node
linkType: hard
@@ -241,26 +251,26 @@ __metadata:
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
"cross-spawn@npm:^7.0.0":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
dependencies:
path-key: "npm:^3.1.0"
shebang-command: "npm:^2.0.0"
which: "npm:^2.0.1"
checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1
checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
version: 4.4.1
resolution: "debug@npm:4.4.1"
version: 4.3.4
resolution: "debug@npm:4.3.4"
dependencies:
ms: "npm:^2.1.3"
ms: "npm:2.1.2"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736
languageName: node
linkType: hard
@@ -309,21 +319,9 @@ __metadata:
linkType: hard
"exponential-backoff@npm:^3.1.1":
version: 3.1.2
resolution: "exponential-backoff@npm:3.1.2"
checksum: 10c0/d9d3e1eafa21b78464297df91f1776f7fbaa3d5e3f7f0995648ca5b89c069d17055033817348d9f4a43d1c20b0eab84f75af6991751e839df53e4dfd6f22e844
languageName: node
linkType: hard
"fdir@npm:^6.4.4":
version: 6.4.6
resolution: "fdir@npm:6.4.6"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579
languageName: node
linkType: hard
@@ -337,12 +335,21 @@ __metadata:
linkType: hard
"foreground-child@npm:^3.1.0":
version: 3.3.1
resolution: "foreground-child@npm:3.3.1"
version: 3.1.1
resolution: "foreground-child@npm:3.1.1"
dependencies:
cross-spawn: "npm:^7.0.6"
cross-spawn: "npm:^7.0.0"
signal-exit: "npm:^4.0.1"
checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3
checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0
languageName: node
linkType: hard
"fs-minipass@npm:^2.0.0":
version: 2.1.0
resolution: "fs-minipass@npm:2.1.0"
dependencies:
minipass: "npm:^3.0.0"
checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004
languageName: node
linkType: hard
@@ -383,19 +390,18 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^10.2.2":
version: 10.4.5
resolution: "glob@npm:10.4.5"
"glob@npm:^10.2.2, glob@npm:^10.3.10":
version: 10.3.16
resolution: "glob@npm:10.3.16"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^3.1.2"
minimatch: "npm:^9.0.4"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^1.11.1"
minimatch: "npm:^9.0.1"
minipass: "npm:^7.0.4"
path-scurry: "npm:^1.11.0"
bin:
glob: dist/esm/bin.mjs
checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e
checksum: 10c0/f7eb4c3e66f221f0be3967c02527047167967549bdf8ed1bd5f6277d43a35191af4e2bb8c89f07a79664958bae088fd06659e69a0f1de462972f1eab52a715e8
languageName: node
linkType: hard
@@ -414,9 +420,9 @@ __metadata:
linkType: hard
"http-cache-semantics@npm:^4.1.1":
version: 4.2.0
resolution: "http-cache-semantics@npm:4.2.0"
checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc
languageName: node
linkType: hard
@@ -431,12 +437,12 @@ __metadata:
linkType: hard
"https-proxy-agent@npm:^7.0.1":
version: 7.0.6
resolution: "https-proxy-agent@npm:7.0.6"
version: 7.0.4
resolution: "https-proxy-agent@npm:7.0.4"
dependencies:
agent-base: "npm:^7.1.2"
agent-base: "npm:^7.0.2"
debug: "npm:4"
checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
checksum: 10c0/bc4f7c38da32a5fc622450b6cb49a24ff596f9bd48dcedb52d2da3fa1c1a80e100fb506bd59b326c012f21c863c69b275c23de1a01d0b84db396822fdf25e52b
languageName: node
linkType: hard
@@ -463,6 +469,13 @@ __metadata:
languageName: node
linkType: hard
"indent-string@npm:^4.0.0":
version: 4.0.0
resolution: "indent-string@npm:4.0.0"
checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f
languageName: node
linkType: hard
"ip-address@npm:^9.0.5":
version: 9.0.5
resolution: "ip-address@npm:9.0.5"
@@ -505,6 +518,13 @@ __metadata:
languageName: node
linkType: hard
"is-lambda@npm:^1.0.1":
version: 1.0.1
resolution: "is-lambda@npm:1.0.1"
checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d
languageName: node
linkType: hard
"is-number@npm:^7.0.0":
version: 7.0.0
resolution: "is-number@npm:7.0.0"
@@ -527,15 +547,15 @@ __metadata:
linkType: hard
"jackspeak@npm:^3.1.2":
version: 3.4.3
resolution: "jackspeak@npm:3.4.3"
version: 3.1.2
resolution: "jackspeak@npm:3.1.2"
dependencies:
"@isaacs/cliui": "npm:^8.0.2"
"@pkgjs/parseargs": "npm:^0.11.0"
dependenciesMeta:
"@pkgjs/parseargs":
optional: true
checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9
checksum: 10c0/5f1922a1ca0f19869e23f0dc4374c60d36e922f7926c76fecf8080cc6f7f798d6a9caac1b9428327d14c67731fd551bb3454cb270a5e13a0718f3b3660ec3d5d
languageName: node
linkType: hard
@@ -547,35 +567,36 @@ __metadata:
linkType: hard
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
version: 10.2.2
resolution: "lru-cache@npm:10.2.2"
checksum: 10c0/402d31094335851220d0b00985084288136136992979d0e015f0f1697e15d1c86052d7d53ae86b614e5b058425606efffc6969a31a091085d7a2b80a8a1e26d6
languageName: node
linkType: hard
"luxon@npm:^3.4.4":
version: 3.6.1
resolution: "luxon@npm:3.6.1"
checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5
version: 3.4.4
resolution: "luxon@npm:3.4.4"
checksum: 10c0/02e26a0b039c11fd5b75e1d734c8f0332c95510f6a514a9a0991023e43fb233884da02d7f966823ffb230632a733fc86d4a4b1e63c3fbe00058b8ee0f8c728af
languageName: node
linkType: hard
"make-fetch-happen@npm:^14.0.3":
version: 14.0.3
resolution: "make-fetch-happen@npm:14.0.3"
"make-fetch-happen@npm:^13.0.0":
version: 13.0.1
resolution: "make-fetch-happen@npm:13.0.1"
dependencies:
"@npmcli/agent": "npm:^3.0.0"
cacache: "npm:^19.0.1"
"@npmcli/agent": "npm:^2.0.0"
cacache: "npm:^18.0.0"
http-cache-semantics: "npm:^4.1.1"
is-lambda: "npm:^1.0.1"
minipass: "npm:^7.0.2"
minipass-fetch: "npm:^4.0.0"
minipass-fetch: "npm:^3.0.0"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
negotiator: "npm:^1.0.0"
proc-log: "npm:^5.0.0"
negotiator: "npm:^0.6.3"
proc-log: "npm:^4.2.0"
promise-retry: "npm:^2.0.1"
ssri: "npm:^12.0.0"
checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0
ssri: "npm:^10.0.0"
checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e
languageName: node
linkType: hard
@@ -588,12 +609,12 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^9.0.4":
version: 9.0.5
resolution: "minimatch@npm:9.0.5"
"minimatch@npm:^9.0.1":
version: 9.0.4
resolution: "minimatch@npm:9.0.4"
dependencies:
brace-expansion: "npm:^2.0.1"
checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed
checksum: 10c0/2c16f21f50e64922864e560ff97c587d15fd491f65d92a677a344e970fe62aafdbeafe648965fa96d33c061b4d0eabfe0213466203dd793367e7f28658cf6414
languageName: node
linkType: hard
@@ -606,18 +627,18 @@ __metadata:
languageName: node
linkType: hard
"minipass-fetch@npm:^4.0.0":
version: 4.0.1
resolution: "minipass-fetch@npm:4.0.1"
"minipass-fetch@npm:^3.0.0":
version: 3.0.5
resolution: "minipass-fetch@npm:3.0.5"
dependencies:
encoding: "npm:^0.1.13"
minipass: "npm:^7.0.3"
minipass-sized: "npm:^1.0.3"
minizlib: "npm:^3.0.1"
minizlib: "npm:^2.1.2"
dependenciesMeta:
encoding:
optional: true
checksum: 10c0/a3147b2efe8e078c9bf9d024a0059339c5a09c5b1dded6900a219c218cc8b1b78510b62dae556b507304af226b18c3f1aeb1d48660283602d5b6586c399eed5c
checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b
languageName: node
linkType: hard
@@ -657,68 +678,76 @@ __metadata:
languageName: node
linkType: hard
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
version: 7.1.2
resolution: "minipass@npm:7.1.2"
checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557
"minipass@npm:^5.0.0":
version: 5.0.0
resolution: "minipass@npm:5.0.0"
checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462
languageName: node
linkType: hard
"minizlib@npm:^3.0.1":
version: 3.0.2
resolution: "minizlib@npm:3.0.2"
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4":
version: 7.1.1
resolution: "minipass@npm:7.1.1"
checksum: 10c0/fdccc2f99c31083f45f881fd1e6971d798e333e078ab3c8988fb818c470fbd5e935388ad9adb286397eba50baebf46ef8ff487c8d3f455a69c6f3efc327bdff9
languageName: node
linkType: hard
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
version: 2.1.2
resolution: "minizlib@npm:2.1.2"
dependencies:
minipass: "npm:^7.1.2"
checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78
minipass: "npm:^3.0.0"
yallist: "npm:^4.0.0"
checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78
languageName: node
linkType: hard
"mkdirp@npm:^3.0.1":
version: 3.0.1
resolution: "mkdirp@npm:3.0.1"
"mkdirp@npm:^1.0.3":
version: 1.0.4
resolution: "mkdirp@npm:1.0.4"
bin:
mkdirp: dist/cjs/src/bin.js
checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d
mkdirp: bin/cmd.js
checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf
languageName: node
linkType: hard
"ms@npm:^2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
"ms@npm:2.1.2":
version: 2.1.2
resolution: "ms@npm:2.1.2"
checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
languageName: node
linkType: hard
"negotiator@npm:^1.0.0":
version: 1.0.0
resolution: "negotiator@npm:1.0.0"
checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b
"negotiator@npm:^0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 11.2.0
resolution: "node-gyp@npm:11.2.0"
version: 10.1.0
resolution: "node-gyp@npm:10.1.0"
dependencies:
env-paths: "npm:^2.2.0"
exponential-backoff: "npm:^3.1.1"
glob: "npm:^10.3.10"
graceful-fs: "npm:^4.2.6"
make-fetch-happen: "npm:^14.0.3"
nopt: "npm:^8.0.0"
proc-log: "npm:^5.0.0"
make-fetch-happen: "npm:^13.0.0"
nopt: "npm:^7.0.0"
proc-log: "npm:^3.0.0"
semver: "npm:^7.3.5"
tar: "npm:^7.4.3"
tinyglobby: "npm:^0.2.12"
which: "npm:^5.0.0"
tar: "npm:^6.1.2"
which: "npm:^4.0.0"
bin:
node-gyp: bin/node-gyp.js
checksum: 10c0/bd8d8c76b06be761239b0c8680f655f6a6e90b48e44d43415b11c16f7e8c15be346fba0cbf71588c7cdfb52c419d928a7d3db353afc1d952d19756237d8f10b9
checksum: 10c0/9cc821111ca244a01fb7f054db7523ab0a0cd837f665267eb962eb87695d71fb1e681f9e21464cc2fd7c05530dc4c81b810bca1a88f7d7186909b74477491a3c
languageName: node
linkType: hard
"nodemon@npm:^3.1.2":
version: 3.1.10
resolution: "nodemon@npm:3.1.10"
version: 3.1.2
resolution: "nodemon@npm:3.1.2"
dependencies:
chokidar: "npm:^3.5.2"
debug: "npm:^4"
@@ -732,18 +761,18 @@ __metadata:
undefsafe: "npm:^2.0.5"
bin:
nodemon: bin/nodemon.js
checksum: 10c0/95b64d647f2c22e85e375b250517b0a4b32c2d2392ad898444e331f70d6b1ab43b17f53a8a1d68d5879ab8401fc6cd6e26f0d2a8736240984f6b5a8435b407c0
checksum: 10c0/7a091067d766768fb6660b796194b01748bba5dc3f1e3ed3dd5f804bfa305e207d24635755078ee5e7cc53848cea35204901e0a6e51ac64483bb8e9ecb237c95
languageName: node
linkType: hard
"nopt@npm:^8.0.0":
version: 8.1.0
resolution: "nopt@npm:8.1.0"
"nopt@npm:^7.0.0":
version: 7.2.1
resolution: "nopt@npm:7.2.1"
dependencies:
abbrev: "npm:^3.0.0"
abbrev: "npm:^2.0.0"
bin:
nopt: bin/nopt.js
checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef
checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81
languageName: node
linkType: hard
@@ -754,17 +783,12 @@ __metadata:
languageName: node
linkType: hard
"p-map@npm:^7.0.2":
version: 7.0.3
resolution: "p-map@npm:7.0.3"
checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c
languageName: node
linkType: hard
"package-json-from-dist@npm:^1.0.0":
version: 1.0.1
resolution: "package-json-from-dist@npm:1.0.1"
checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b
"p-map@npm:^4.0.0":
version: 4.0.0
resolution: "p-map@npm:4.0.0"
dependencies:
aggregate-error: "npm:^3.0.0"
checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75
languageName: node
linkType: hard
@@ -775,7 +799,7 @@ __metadata:
languageName: node
linkType: hard
"path-scurry@npm:^1.11.1":
"path-scurry@npm:^1.11.0":
version: 1.11.1
resolution: "path-scurry@npm:1.11.1"
dependencies:
@@ -792,17 +816,17 @@ __metadata:
languageName: node
linkType: hard
"picomatch@npm:^4.0.2":
version: 4.0.2
resolution: "picomatch@npm:4.0.2"
checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
"proc-log@npm:^3.0.0":
version: 3.0.0
resolution: "proc-log@npm:3.0.0"
checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc
languageName: node
linkType: hard
"proc-log@npm:^5.0.0":
version: 5.0.0
resolution: "proc-log@npm:5.0.0"
checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3
"proc-log@npm:^4.2.0":
version: 4.2.0
resolution: "proc-log@npm:4.2.0"
checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9
languageName: node
linkType: hard
@@ -854,11 +878,11 @@ __metadata:
linkType: hard
"semver@npm:^7.3.5, semver@npm:^7.5.3":
version: 7.7.2
resolution: "semver@npm:7.7.2"
version: 7.6.2
resolution: "semver@npm:7.6.2"
bin:
semver: bin/semver.js
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
checksum: 10c0/97d3441e97ace8be4b1976433d1c32658f6afaff09f143e52c593bae7eef33de19e3e369c88bd985ce1042c6f441c80c6803078d1de2a9988080b66684cbb30c
languageName: node
linkType: hard
@@ -902,23 +926,23 @@ __metadata:
linkType: hard
"socks-proxy-agent@npm:^8.0.3":
version: 8.0.5
resolution: "socks-proxy-agent@npm:8.0.5"
version: 8.0.3
resolution: "socks-proxy-agent@npm:8.0.3"
dependencies:
agent-base: "npm:^7.1.2"
agent-base: "npm:^7.1.1"
debug: "npm:^4.3.4"
socks: "npm:^2.8.3"
checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6
socks: "npm:^2.7.1"
checksum: 10c0/4950529affd8ccd6951575e21c1b7be8531b24d924aa4df3ee32df506af34b618c4e50d261f4cc603f1bfd8d426915b7d629966c8ce45b05fb5ad8c8b9a6459d
languageName: node
linkType: hard
"socks@npm:^2.8.3":
version: 2.8.5
resolution: "socks@npm:2.8.5"
"socks@npm:^2.7.1":
version: 2.8.3
resolution: "socks@npm:2.8.3"
dependencies:
ip-address: "npm:^9.0.5"
smart-buffer: "npm:^4.2.0"
checksum: 10c0/e427d0eb0451cfd04e20b9156ea8c0e9b5e38a8d70f21e55c30fbe4214eda37cfc25d782c63f9adc5fbdad6d062a0f127ef2cefc9a44b6fee2b9ea5d1ed10827
checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7
languageName: node
linkType: hard
@@ -946,12 +970,12 @@ __metadata:
languageName: node
linkType: hard
"ssri@npm:^12.0.0":
version: 12.0.0
resolution: "ssri@npm:12.0.0"
"ssri@npm:^10.0.0":
version: 10.0.6
resolution: "ssri@npm:10.0.6"
dependencies:
minipass: "npm:^7.0.3"
checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d
checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227
languageName: node
linkType: hard
@@ -1004,27 +1028,17 @@ __metadata:
languageName: node
linkType: hard
"tar@npm:^7.4.3":
version: 7.4.3
resolution: "tar@npm:7.4.3"
"tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.2.1
resolution: "tar@npm:6.2.1"
dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.0.1"
mkdirp: "npm:^3.0.1"
yallist: "npm:^5.0.0"
checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d
languageName: node
linkType: hard
"tinyglobby@npm:^0.2.12":
version: 0.2.14
resolution: "tinyglobby@npm:0.2.14"
dependencies:
fdir: "npm:^6.4.4"
picomatch: "npm:^4.0.2"
checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
chownr: "npm:^2.0.0"
fs-minipass: "npm:^2.0.0"
minipass: "npm:^5.0.0"
minizlib: "npm:^2.1.1"
mkdirp: "npm:^1.0.3"
yallist: "npm:^4.0.0"
checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537
languageName: node
linkType: hard
@@ -1053,21 +1067,21 @@ __metadata:
languageName: node
linkType: hard
"unique-filename@npm:^4.0.0":
version: 4.0.0
resolution: "unique-filename@npm:4.0.0"
"unique-filename@npm:^3.0.0":
version: 3.0.0
resolution: "unique-filename@npm:3.0.0"
dependencies:
unique-slug: "npm:^5.0.0"
checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc
unique-slug: "npm:^4.0.0"
checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f
languageName: node
linkType: hard
"unique-slug@npm:^5.0.0":
version: 5.0.0
resolution: "unique-slug@npm:5.0.0"
"unique-slug@npm:^4.0.0":
version: 4.0.0
resolution: "unique-slug@npm:4.0.0"
dependencies:
imurmurhash: "npm:^0.1.4"
checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293
checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635
languageName: node
linkType: hard
@@ -1082,14 +1096,14 @@ __metadata:
languageName: node
linkType: hard
"which@npm:^5.0.0":
version: 5.0.0
resolution: "which@npm:5.0.0"
"which@npm:^4.0.0":
version: 4.0.0
resolution: "which@npm:4.0.0"
dependencies:
isexe: "npm:^3.1.1"
bin:
node-which: bin/which.js
checksum: 10c0/e556e4cd8b7dbf5df52408c9a9dd5ac6518c8c5267c8953f5b0564073c66ed5bf9503b14d876d0e9c7844d4db9725fb0dcf45d6e911e17e26ab363dc3965ae7b
checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a
languageName: node
linkType: hard
@@ -1116,8 +1130,8 @@ __metadata:
linkType: hard
"ws@npm:^8.17.0":
version: 8.18.2
resolution: "ws@npm:8.18.2"
version: 8.17.0
resolution: "ws@npm:8.17.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
@@ -1126,7 +1140,7 @@ __metadata:
optional: true
utf-8-validate:
optional: true
checksum: 10c0/4b50f67931b8c6943c893f59c524f0e4905bbd183016cfb0f2b8653aa7f28dad4e456b9d99d285bbb67cca4fedd9ce90dfdfaa82b898a11414ebd66ee99141e4
checksum: 10c0/55241ec93a66fdfc4bf4f8bc66c8eb038fda2c7a4ee8f6f157f2ca7dc7aa76aea0c0da0bf3adb2af390074a70a0e45456a2eaf80e581e630b75df10a64b0a990
languageName: node
linkType: hard
@@ -1136,10 +1150,3 @@ __metadata:
checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a
languageName: node
linkType: hard
"yallist@npm:^5.0.0":
version: 5.0.0
resolution: "yallist@npm:5.0.0"
checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416
languageName: node
linkType: hard

View File

@@ -1,10 +1,10 @@
{:deps
{org.clojure/clojure {:mvn/version "1.12.1"}
{org.clojure/clojure {:mvn/version "1.12.0"}
org.clojure/data.json {:mvn/version "2.5.1"}
org.clojure/tools.cli {:mvn/version "1.1.230"}
org.clojure/clojurescript {:mvn/version "1.12.38"}
org.clojure/test.check {:mvn/version "1.1.1"}
org.clojure/data.fressian {:mvn/version "1.1.0"}
org.clojure/clojurescript {:mvn/version "1.12.42"}
;; Logging
org.apache.logging.log4j/log4j-api {:mvn/version "2.24.3"}
@@ -28,7 +28,7 @@
integrant/integrant {:mvn/version "0.13.1"}
funcool/tubax {:mvn/version "2021.05.20-0"}
funcool/cuerdas {:mvn/version "2025.06.16-414"}
funcool/cuerdas {:mvn/version "2025.05.26-411"}
funcool/promesa
{:git/sha "f52f58cfacf62f59eab717e2637f37729d0cc383"
:git/url "https://github.com/funcool/promesa"}
@@ -59,7 +59,7 @@
{:dev
{:extra-deps
{org.clojure/tools.namespace {:mvn/version "RELEASE"}
thheller/shadow-cljs {:mvn/version "3.1.5"}
thheller/shadow-cljs {:mvn/version "3.0.5"}
com.clojure-goes-fast/clj-async-profiler {:mvn/version "RELEASE"}
com.bhauman/rebel-readline {:mvn/version "RELEASE"}
criterium/criterium {:mvn/version "RELEASE"}

View File

@@ -4,20 +4,21 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
"type": "module",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
},
"dependencies": {
"luxon": "^3.6.1"
"luxon": "^3.4.4"
},
"devDependencies": {
"concurrently": "^9.1.2",
"nodemon": "^3.1.10",
"concurrently": "^9.0.1",
"nodemon": "^3.1.7",
"shadow-cljs": "3.0.5",
"source-map-support": "^0.5.21",
"ws": "^8.18.2"
"ws": "^8.17.0"
},
"scripts": {
"fmt:clj:check": "cljfmt check --parallel=false src/ test/",

View File

@@ -1,165 +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.common.buffer
"A collection of helpers and macros for work with byte buffers"
(:refer-clojure :exclude [clone])
(:require
[app.common.uuid :as uuid])
#?(:cljs
(:require-macros [app.common.buffer])
:clj
(:import [java.nio ByteBuffer ByteOrder])))
(defmacro read-byte
[target offset]
(if (:ns &env)
`(.getInt8 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(long (.get ~target ~offset)))))
(defmacro read-bool
[target offset]
(if (:ns &env)
`(== 1 (.getInt8 ~target ~offset true))
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(== 1 (.get ~target ~offset)))))
(defmacro read-short
[target offset]
(if (:ns &env)
`(.getInt16 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.getShort ~target ~offset))))
(defmacro read-int
[target offset]
(if (:ns &env)
`(.getInt32 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(long (.getInt ~target ~offset)))))
(defmacro read-float
[target offset]
(if (:ns &env)
`(.getFloat32 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(double (.getFloat ~target ~offset)))))
(defmacro read-uuid
[target offset]
(if (:ns &env)
`(let [a# (.getUint32 ~target (+ ~offset 0) true)
b# (.getUint32 ~target (+ ~offset 4) true)
c# (.getUint32 ~target (+ ~offset 8) true)
d# (.getUint32 ~target (+ ~offset 12) true)]
(uuid/from-unsigned-parts a# b# c# d#))
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(try
(.order ~target ByteOrder/BIG_ENDIAN)
(let [msb# (.getLong ~target (+ ~offset 0))
lsb# (.getLong ~target (+ ~offset 8))]
(java.util.UUID. (long msb#) (long lsb#)))
(finally
(.order ~target ByteOrder/LITTLE_ENDIAN))))))
(defmacro write-byte
[target offset value]
(if (:ns &env)
`(.setInt8 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.put ~target ~offset (unchecked-byte ~value)))))
(defmacro write-short
[target offset value]
(if (:ns &env)
`(.setInt16 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putShort ~target ~offset (unchecked-short ~value)))))
(defmacro write-int
[target offset value]
(if (:ns &env)
`(.setInt32 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putInt ~target ~offset (unchecked-int ~value)))))
(defmacro write-float
[target offset value]
(if (:ns &env)
`(.setFloat32 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putFloat ~target ~offset (unchecked-float ~value)))))
(defmacro write-uuid
[target offset value]
(if (:ns &env)
`(let [barray# (uuid/get-u32 ~value)]
(.setUint32 ~target (+ ~offset 0) (aget barray# 0) true)
(.setUint32 ~target (+ ~offset 4) (aget barray# 1) true)
(.setUint32 ~target (+ ~offset 8) (aget barray# 2) true)
(.setUint32 ~target (+ ~offset 12) (aget barray# 3) true))
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})
value (with-meta value {:tag 'java.util.UUID})]
`(try
(.order ~target ByteOrder/BIG_ENDIAN)
(.putLong ~target (+ ~offset 0) (.getMostSignificantBits ~value))
(.putLong ~target (+ ~offset 8) (.getLeastSignificantBits ~value))
(finally
(.order ~target ByteOrder/LITTLE_ENDIAN))))))
(defn allocate
[size]
#?(:clj (let [buffer (ByteBuffer/allocate (int size))]
(.order buffer ByteOrder/LITTLE_ENDIAN))
:cljs (new js/DataView (new js/ArrayBuffer size))))
(defn clone
[buffer]
#?(:clj
(let [src (.array ^ByteBuffer buffer)
len (alength ^bytes src)
dst (byte-array len)]
(System/arraycopy src 0 dst 0 len)
(let [buffer (ByteBuffer/wrap dst)]
(.order buffer ByteOrder/LITTLE_ENDIAN)))
:cljs
(let [buffer' (.-buffer ^js/DataView buffer)
src-view (js/Uint32Array. buffer')
dst-buff (js/ArrayBuffer. (.-byteLength buffer'))
dst-view (js/Uint32Array. dst-buff)]
(.set dst-view src-view)
(js/DataView. dst-buff))))
(defn equals?
[buffer-a buffer-b]
#?(:clj
(.equals ^ByteBuffer buffer-a
^ByteBuffer buffer-b)
:cljs
(let [buffer-a (.-buffer buffer-a)
buffer-b (.-buffer buffer-b)]
(if (= (.-byteLength buffer-a)
(.-byteLength buffer-b))
(let [cb (js/Uint32Array. buffer-a)
ob (js/Uint32Array. buffer-b)
sz (alength cb)]
(loop [i 0]
(if (< i sz)
(if (== (aget ob i)
(aget cb i))
(recur (inc i))
false)
true)))
false))))
(defn buffer?
[o]
#?(:clj (instance? ByteBuffer o)
:cljs (instance? js/DataView o)))

View File

@@ -33,10 +33,6 @@
(def boolean-or-nil?
(some-fn nil? boolean?))
(defn in-range?
[size i]
(and (< i size) (>= i 0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Commonly used transducers
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -47,26 +47,10 @@
`(try ~@exprs (catch Throwable e# nil))))
(defmacro try!
[expr & {:keys [reraise-with on-exception]}]
(let [ex-sym
(gensym "exc")
generate-catch
(fn []
(cond
(map? reraise-with)
`(ex/raise ~@(mapcat identity reraise-with) :cause ~ex-sym)
on-exception
`(let [handler# ~on-exception]
(handler# ~ex-sym))
:else
ex-sym))]
(if (:ns &env)
`(try ~expr (catch :default ~ex-sym ~(generate-catch)))
`(try ~expr (catch Throwable ~ex-sym ~(generate-catch))))))
[& exprs]
(if (:ns &env)
`(try ~@exprs (catch :default e# e#))
`(try ~@exprs (catch Throwable e# e#))))
(defn ex-info?
[v]

View File

@@ -21,6 +21,7 @@
[app.common.time :as dt]
[app.common.types.color :as types.color]
[app.common.types.component :as types.comp]
[app.common.types.container :as types.cont]
[app.common.types.file :as types.file]
[app.common.types.page :as types.page]
[app.common.types.path :as types.path]
@@ -117,7 +118,7 @@
(sm/decode-fn types.shape/schema:shape-attrs sm/json-transformer))
(def decode-library-color
(sm/decode-fn types.color/schema:library-color sm/json-transformer))
(sm/decode-fn types.color/schema:color sm/json-transformer))
(def decode-library-typography
(sm/decode-fn types.typography/schema:typography sm/json-transformer))
@@ -340,7 +341,8 @@
(comp
(map (d/getf objects))
(remove cph/frame-shape?)
(remove types.comp/is-variant?))
(remove types.comp/is-variant?)
(remove (partial types.cont/has-any-copy-parent? objects)))
children
(->> (get bool-shape :shapes)

View File

@@ -16,6 +16,7 @@
[app.common.schema.desc-native :as smd]
[app.common.schema.generators :as sg]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
@@ -265,7 +266,7 @@
[:id ::sm/uuid]
;; All props are optional, background can be nil because is the
;; way to remove already set background
[:background {:optional true} [:maybe ctc/schema:hex-color]]
[:background {:optional true} [:maybe ::ctc/rgb-color]]
[:name {:optional true} :string]]]
[:set-plugin-data schema:set-plugin-data-change]
@@ -291,12 +292,12 @@
[:add-color
[:map {:title "AddColorChange"}
[:type [:= :add-color]]
[:color ctc/schema:library-color]]]
[:color ::ctc/color]]]
[:mod-color
[:map {:title "ModColorChange"}
[:type [:= :mod-color]]
[:color ctc/schema:library-color]]]
[:color ::ctc/color]]]
[:del-color
[:map {:title "DelColorChange"}
@@ -377,7 +378,7 @@
[:update-active-token-themes
[:map {:title "UpdateActiveTokenThemes"}
[:type [:= :update-active-token-themes]]
[:theme-paths [:set :string]]]]
[:theme-ids [:set :string]]]]
[:rename-token-set-group
[:map {:title "RenameTokenSetGroup"}
@@ -487,9 +488,7 @@
(cts/shape? shape-new))
(ex/raise :type :assertion
:code :data-validation
:hint (str "invalid shape found after applying changes on file "
(:id data-new))
:file-id (:id data-new)
:hint "invalid shape found after applying changes"
::sm/explain (cts/explain-shape shape-new))))))]
(->> (into #{} (map :page-id) items)
@@ -927,15 +926,15 @@
(defmethod process-change :add-color
[data {:keys [color]}]
(ctc/add-color data color))
(ctcl/add-color data color))
(defmethod process-change :mod-color
[data {:keys [color]}]
(ctc/set-color data color))
(ctcl/set-color data color))
(defmethod process-change :del-color
[data {:keys [id]}]
(ctc/delete-color data id))
(ctcl/delete-color data id))
;; DEPRECATED: remove before 2.3
(defmethod process-change :add-recent-color
@@ -1052,9 +1051,9 @@
(ctob/make-token-theme (merge prev-token-theme theme)))))))))
(defmethod process-change :update-active-token-themes
[data {:keys [theme-paths]}]
[data {:keys [theme-ids]}]
(update data :tokens-lib #(-> % (ctob/ensure-tokens-lib)
(ctob/set-active-themes theme-paths))))
(ctob/set-active-themes theme-ids))))
(defmethod process-change :rename-token-set-group
[data {:keys [set-group-path set-group-fname]}]

View File

@@ -161,6 +161,7 @@
(contains? (meta changes) ::file-data)
"Call (with-file-data) before using this function"))
(defn- lookup-objects
[changes]
(let [data (::file-data (meta changes))]
@@ -523,6 +524,7 @@
:or {ignore-geometry? false ignore-touched false with-objects? false}}]
(assert-container-id! changes)
(assert-objects! changes)
(assert-page-id! changes)
(let [page-id (::page-id (meta changes))
component-id (::component-id (meta changes))
objects (lookup-objects changes)
@@ -798,10 +800,10 @@
(apply-changes-local))))
(defn update-active-token-themes
[changes active-theme-paths prev-active-theme-paths]
[changes token-active-theme-ids prev-token-active-theme-ids]
(-> changes
(update :redo-changes conj {:type :update-active-token-themes :theme-paths active-theme-paths})
(update :undo-changes conj {:type :update-active-token-themes :theme-paths prev-active-theme-paths})
(update :redo-changes conj {:type :update-active-token-themes :theme-ids token-active-theme-ids})
(update :undo-changes conj {:type :update-active-token-themes :theme-ids prev-token-active-theme-ids})
(apply-changes-local)))
(defn set-token-theme [changes group theme-name theme]

View File

@@ -117,12 +117,6 @@
([shape]
(d/not-empty? (:shapes shape))))
(defn has-layout?
"Returns true if the provided shape has a layout assigned"
[objects id]
(let [shape (get objects id)]
(boolean (and shape (:layout shape)))))
(defn group-like-shape?
([objects id]
(group-like-shape? (get objects id)))
@@ -133,24 +127,6 @@
;; ---- ACCESSORS
(defn get-selected-type
"Returns the type of the shape if only one, or :multiple if more
than one"
[objects selected]
(if (= 1 (count selected))
(let [shape (get objects (first selected))]
(:type shape))
:multiple))
(defn get-shape-type
"Returns the type of the shape, or 'root' if it's Root Frame, always
as string"
[objects id]
(let [shape (get objects id)]
(if (root? shape)
:root
(dm/get-prop shape :type))))
(defn get-children-ids
[objects id]
(letfn [(get-children-ids-rec [id processed]

View File

@@ -22,16 +22,16 @@
[app.common.schema :as sm]
[app.common.svg :as csvg]
[app.common.text :as txt]
[app.common.types.color :as types.color]
[app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.fill :as types.fill]
[app.common.types.path :as path]
[app.common.types.path.segment :as path.segment]
[app.common.types.shape :as cts]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.shadow :as ctss]
[app.common.types.text :as cttx]
[app.common.uuid :as uuid]
[clojure.set :as set]
[cuerdas.core :as str]))
@@ -94,33 +94,22 @@
(defn migrate-file
[file libs]
(binding [cfeat/*new* (atom #{})]
(let [version
(or (:version file) (-> file :data :version))
migrations
(not-empty (get file :migrations))
file
(-> file
(assoc :version cfd/version)
(assoc :migrations
(if migrations
migrations
(generate-migrations-from-version version)))
;; NOTE: in some future we can consider to apply a
;; migration to the whole database and remove this code
;; from this function that executes on each file
;; migration operation
(update :features cfeat/migrate-legacy-features)
(migrate libs)
(update :features (fnil into #{}) (deref cfeat/*new*)))]
;; NOTE: When we have no previous migrations, we report all
;; migrations as migrated in order to correctly persist them all
;; and not only the really applied migrations
(if (not migrations)
(vary-meta file assoc ::migrated (:migrations file))
file))))
(let [version (or (:version file)
(-> file :data :version))]
(-> file
(assoc :version cfd/version)
(update :migrations
(fn [migrations]
(if (nil? migrations)
(generate-migrations-from-version version)
migrations)))
;; NOTE: in some future we can consider to apply
;; a migration to the whole database and remove
;; this code from this function that executes on
;; each file migration operation
(update :features cfeat/migrate-legacy-features)
(migrate libs)
(update :features (fnil into #{}) (deref cfeat/*new*))))))
(defn migrated?
[file]
@@ -837,7 +826,7 @@
(d/update-when :components d/update-vals update-container))))
(def ^:private valid-fill?
(sm/lazy-validator types.fill/schema:fill))
(sm/lazy-validator ::cts/fill))
(defmethod migrate-data "legacy-43"
[data _]
@@ -855,7 +844,7 @@
(update-object [object]
(if (cfh/text-shape? object)
(update object :content #(txt/transform-nodes txt/is-content-node? update-text-node %))
(update object :content #(txt/transform-nodes identity update-text-node %))
object))
(update-container [container]
@@ -1015,11 +1004,14 @@
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(def ^:private valid-color?
(sm/lazy-validator ::ctc/color))
(defmethod migrate-data "legacy-51"
[data _]
(let [update-colors
(fn [colors]
(into {} (filter #(-> % val types.color/valid-color?) colors)))]
(into {} (filter #(-> % val valid-color?) colors)))]
(update data :colors update-colors)))
(defmethod migrate-data "legacy-52"
@@ -1105,7 +1097,7 @@
;; The text shape also can has fills on the text
;; fragments so we need to fix fills there
(cond-> (cfh/text-shape? object)
(update :content (partial txt/transform-nodes txt/is-content-node? fix-fills)))))
(update :content (partial txt/transform-nodes identity fix-fills)))))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
@@ -1277,21 +1269,21 @@
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(defmethod migrate-data "0002-normalize-bool-content-v2"
(defmethod migrate-data "0002-normalize-bool-content"
[data _]
(letfn [(update-object [object]
;; NOTE: we still preserve the previous value for possible
;; rollback, we still need to perform an other migration
;; for properly delete the bool-content prop from shapes
;; once the know the migration was OK
(if (cfh/bool-shape? object)
(if (contains? object :content)
(dissoc object :bool-content)
(let [content (:bool-content object)]
(-> object
(assoc :content content)
(dissoc :bool-content))))
(if-let [content (:bool-content object)]
(assoc object :content content)
object)
(dissoc object :bool-content :bool-type)))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
(d/update-when container :objects update-vals update-object))]
(-> data
(update :pages-index d/update-vals update-container)
@@ -1319,71 +1311,48 @@
(d/update-when :components d/update-vals update-container)
(d/without-nils))))
(defmethod migrate-data "0003-convert-path-content-v2"
(defmethod migrate-data "0003-convert-path-content"
[data _]
(some-> cfeat/*new* (swap! conj "fdata/path-data"))
(let [decode-segments
(sm/decoder path/schema:segments sm/json-transformer)
(letfn [(update-object [object]
(if (or (cfh/bool-shape? object)
(cfh/path-shape? object))
(update object :content path/content)
object))
update-object
(fn [object]
(if (or (cfh/bool-shape? object)
(cfh/path-shape? object))
(let [content (get object :content)
content (cond
(path/content? content)
content
(nil? content)
(path/content [])
:else
(-> content
(decode-segments)
(path/content)))]
(assoc object :content content))
object))
update-container
(fn [container]
(d/update-when container :objects d/update-vals update-object))]
(update-container [container]
(d/update-when container :objects update-vals update-object))]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(defmethod migrate-data "0004-clean-shadow-color"
(defmethod migrate-data "0004-add-partial-text-touched-flags"
[data _]
(let [decode-color (sm/decoder types.color/schema:color sm/json-transformer)
(letfn [(update-object [page object]
(if (and (cfh/text-shape? object)
(ctk/in-component-copy? object))
(let [file {:id (:id data) :data data}
libs (when (:libs data)
(deref (:libs data)))
ref-shape (ctf/find-ref-shape file page libs object
{:include-deleted? true :with-context? true})
partial-touched (when ref-shape
(cttx/get-diff-type (:content object) (:content ref-shape)))]
(if (seq partial-touched)
(update object :touched (fn [touched]
(reduce #(ctk/set-touched-group %1 %2)
touched
partial-touched)))
object))
object))
clean-shadow-color
(fn [color]
(let [ref-id (get color :id)
ref-file (get color :file-id)]
(-> (d/without-qualified color)
(select-keys [:opacity :color :gradient :image :ref-id :ref-file])
(cond-> ref-id
(assoc :ref-id ref-id))
(cond-> ref-file
(assoc :ref-file ref-file))
(decode-color))))
(update-page [page]
(d/update-when page :objects d/update-vals (partial update-object page)))]
clean-shadow
(fn [shadow]
(update shadow :color clean-shadow-color))
update-object
(fn [object]
(d/update-when object :shadow #(mapv clean-shadow %)))
update-container
(fn [container]
(d/update-when container :objects d/update-vals update-object))]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(update data :pages-index d/update-vals update-page)))
(defmethod migrate-data "0005-deprecate-image-type"
[data _]
@@ -1400,7 +1369,7 @@
object))
(update-container [container]
(d/update-when container :objects d/update-vals update-object))]
(d/update-when container :objects update-vals update-object))]
(-> data
(update :pages-index d/update-vals update-container)
@@ -1409,12 +1378,17 @@
(defmethod migrate-data "0006-fix-old-texts-fills"
[data _]
(letfn [(fix-fills [node]
(let [fills (if (and (not (seq (:fills node)))
(or (some? (:fill-color node))
(some? (:fill-opacity node))
(some? (:fill-color-gradient node))))
(let [fills (cond
(or (some? (:fill-color node))
(some? (:fill-opacity node))
(some? (:fill-color-gradient node)))
[(d/without-nils (select-keys node [:fill-color :fill-opacity :fill-color-gradient
:fill-color-ref-id :fill-color-ref-file]))]
(nil? (:fills node))
[{:fill-color "#000000" :fill-opacity 1}]
:else
(:fills node))]
(-> node
(assoc :fills fills)
@@ -1423,7 +1397,7 @@
(update-object [object]
(if (cfh/text-shape? object)
(update object :content (partial txt/transform-nodes txt/is-content-node? fix-fills))
(update object :content (partial txt/transform-nodes identity fix-fills))
object))
(update-container [container]
@@ -1433,90 +1407,6 @@
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(def ^:private valid-stroke?
(sm/lazy-validator cts/schema:stroke))
(defmethod migrate-data "0007-clear-invalid-strokes-and-fills-v2"
[data _]
(letfn [(clear-color-image [image]
(select-keys image types.color/image-attrs))
(clear-color-gradient [gradient]
(select-keys gradient types.color/gradient-attrs))
(clear-stroke [stroke]
(-> stroke
(select-keys cts/stroke-attrs)
(d/update-when :stroke-color-gradient clear-color-gradient)
(d/update-when :stroke-image clear-color-image)
(d/update-when :stroke-style #(if (#{:svg :none} %) :solid %))))
(fix-strokes [strokes]
(->> (map clear-stroke strokes)
(filterv valid-stroke?)))
;; Fixes shapes with nested :fills in the :fills attribute
;; introduced in a migration `0006-fix-old-texts-fills` when
;; txt/transform-nodes with identity pred was broken
(remove-nested-fills [[fill :as fills]]
(if (and (= 1 (count fills))
(contains? fill :fills))
(:fills fill)
fills))
(clear-fill [fill]
(-> fill
(select-keys types.fill/fill-attrs)
(d/update-when :fill-image clear-color-image)
(d/update-when :fill-color-gradient clear-color-gradient)))
(fix-fills [fills]
(->> fills
(remove-nested-fills)
(map clear-fill)
(filterv valid-fill?)))
(fix-object [object]
(-> object
(d/update-when :strokes fix-strokes)
(d/update-when :fills fix-fills)))
(fix-text-content [content]
(->> content
(txt/transform-nodes txt/is-content-node? fix-object)
(txt/transform-nodes txt/is-paragraph-set-node? #(dissoc % :fills))))
(update-shape [object]
(-> object
(fix-object)
;; The text shape also can has strokes and fils on the
;; text fragments so we need to fix them there
(cond-> (cfh/text-shape? object)
(update :content fix-text-content))))
(update-container [container]
(d/update-when container :objects d/update-vals update-shape))]
(-> data
(update :pages-index d/update-vals update-container)
(d/update-when :components d/update-vals update-container))))
(defmethod migrate-data "0008-fix-library-colors-v4"
[data _]
(letfn [(clear-color-opacity [color]
(if (and (contains? color :opacity)
(nil? (get color :opacity)))
(assoc color :opacity 1)
color))
(clear-color [color]
(-> color
(select-keys types.color/library-color-attrs)
(clear-color-opacity)
(d/without-nils)))]
(d/update-when data :colors d/update-vals clear-color)))
(def available-migrations
(into (d/ordered-set)
["legacy-2"
@@ -1572,12 +1462,10 @@
"legacy-66"
"legacy-67"
"0001-remove-tokens-from-groups"
"0002-normalize-bool-content-v2"
"0002-normalize-bool-content"
"0002-clean-shape-interactions"
"0003-fix-root-shape"
"0003-convert-path-content-v2"
"0004-clean-shadow-color"
"0003-convert-path-content"
"0004-add-partial-text-touched-flags"
"0005-deprecate-image-type"
"0006-fix-old-texts-fills"
"0007-clear-invalid-strokes-and-fills-v2"
"0008-fix-library-colors-v4"]))
"0006-fix-old-texts-fills"]))

View File

@@ -91,10 +91,10 @@
parent-id (or parent-id (get selected-obj :parent-id))
base-parent (get objects parent-id)
layout-attrs
layout-props
(when (and (= 1 (count selected))
(ctl/any-layout? base-parent))
(select-keys selected-obj ctl/layout-child-attrs))
(select-keys selected-obj ctl/layout-item-props))
target-cell-id
(if (and (nil? target-cell-id)
@@ -129,8 +129,8 @@
:parent-id parent-id
:shapes (into [] selected))
(some? layout-attrs)
(d/patch-object layout-attrs)
(some? layout-props)
(d/patch-object layout-props)
;; Frames from shapes will not be displayed in viewer and no clipped
(or (not= frame-id uuid/zero) without-fill?)

View File

@@ -98,7 +98,7 @@
(defn encode
[data & {:as opts}]
#?(:clj (j/write-str data opts)
:cljs (.stringify js/JSON (->js data opts) nil (:indent opts))))
:cljs (.stringify js/JSON (->js data opts))))
(defn decode
[data & {:as opts}]

View File

@@ -29,6 +29,7 @@
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as cttx]
[app.common.types.token :as cto]
[app.common.types.typography :as cty]
[app.common.types.variant :as ctv]
@@ -1665,7 +1666,7 @@
(defn- add-update-attr-operations
[attr dest-shape origin-shape roperations uoperations touched]
[attr dest-shape origin-shape roperations uoperations touched is-text-partial-change?]
(let [orig-value (get origin-shape attr)
dest-value (get dest-shape attr)
;; position-data is a special case because can be affected by :geometry-group and :content-group
@@ -1677,10 +1678,27 @@
(not= orig-value dest-value)
(touched :geometry-group))
;; We want to split the changes on the text itself and on its properties
text-value
(when is-text-partial-change?
(cond
(touched :text-content-structure-same-attrs)
;; Keep the dest structure and texts, update its attrs to make them like the origin
(cttx/copy-attrs-keys dest-value (cttx/get-first-paragraph-text-attrs orig-value))
(touched :text-content-text)
;; Keep the texts touched in dest: copy the texts from dest over the attrs of origin
(cttx/copy-text-keys dest-value orig-value)
(touched :text-content-attribute)
;; Keep the attrs touched in dest: copy the texts from origin over the attrs of dest
(cttx/copy-text-keys orig-value dest-value)))
val (cond
;; If position data changes and the geometry group is touched
;; we need to put to nil so we can regenerate it
reset-pos-data? nil
is-text-partial-change? text-value
:else orig-value)
roperation {:type :set
@@ -1694,6 +1712,33 @@
[(conj roperations roperation)
(conj uoperations uoperation)]))
(defn- is-text-partial-change?
"Check if the attr update is a text partial change"
[origin-shape dest-shape attr touched]
(let [partial-text-keys [:text-content-attribute :text-content-text]
active-keys (filter touched partial-text-keys)
orig-content (get origin-shape attr)
orig-attrs (cttx/get-first-paragraph-text-attrs orig-content)
equal-orig-attrs? (cttx/equal-attrs? orig-content orig-attrs)]
(and
(or
;; One and only one of the keys is pressent
(= 1 (count active-keys))
(and
(not (touched :text-content-attribute))
(touched :text-content-structure-same-attrs)))
(or
;; Both has the same structure
(cttx/equal-structure? (:content origin-shape) (:content dest-shape))
;; The origin and destiny have different structures, but each have the same attrs
;; for all the items on its content tree
(and
equal-orig-attrs?
(touched :text-content-structure-same-attrs))))))
(defn- update-attrs
"The main function that implements the attribute sync algorithm. Copy
attributes that have changed in the origin shape to the dest shape.
@@ -1737,14 +1782,29 @@
(generate-update-tokens container dest-shape origin-shape touched omit-touched?))
(let [attr-group (get ctk/sync-attrs attr)
;; On texts, when we want to omit the touched attrs, both text (the actual letters)
;; and attrs (bold, font, etc) are in the same attr :content.
;; If only one of them is touched, we want to adress this case and
;; only update the untouched one
text-partial-change? (when (and
omit-touched?
(= :text (:type origin-shape))
(= :content attr)
(touched attr-group))
(is-text-partial-change? origin-shape dest-shape attr touched))
skip-operations? (or (= (get origin-shape attr) (get dest-shape attr))
(and (touched attr-group)
omit-touched?))
omit-touched?
;; When it is a text-partial-change, we should generate operations
;; even when omit-touched? is true, but updating only the text or
;; the attributes, omiting the other part
(not text-partial-change?)))
[roperations' uoperations']
(if skip-operations?
[roperations uoperations]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched))]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched text-partial-change?))]
(recur (next attrs)
roperations'
uoperations')))))))
@@ -1779,7 +1839,7 @@
;; If the attr is not touched in the origin shape, don't copy it
(not (touched-origin attr-group)))
[roperations uoperations]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched))]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched false))]
(recur (next attrs)
roperations'
uoperations'))

View File

@@ -16,30 +16,19 @@
[app.common.types.pages-list :as ctpl]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as ctt]
[app.common.types.token :as cto]
[app.common.uuid :as uuid]
[clojure.set :as set]))
[app.common.uuid :as uuid]))
(defn- generate-unapply-tokens
"When updating attributes that have a token applied, we must unapply it, because the value
of the attribute now has been given directly, and does not come from the token."
[changes objects changed-sub-attr]
(let [new-objects (pcb/get-objects changes)
mod-obj-changes (->> (:redo-changes changes)
(let [mod-obj-changes (->> (:redo-changes changes)
(filter #(= (:type %) :mod-obj)))
text-changed-attrs
(fn [shape]
(let [new-shape (get new-objects (:id shape))
attrs (ctt/get-diff-attrs (:content shape) (:content new-shape))]
(apply set/union (map cto/shape-attr->token-attrs attrs))))
check-attr (fn [shape changes attr]
(let [tokens (get shape :applied-tokens {})
token-attrs (if (or (not= (:type shape) :text) (not= attr :content))
(cto/shape-attr->token-attrs attr changed-sub-attr)
(text-changed-attrs shape))]
token-attrs (cto/shape-attr->token-attrs attr changed-sub-attr)]
(if (some #(contains? tokens %) token-attrs)
(pcb/update-shapes changes [(:id shape)] #(cto/unapply-token-id % token-attrs))
changes)))

View File

@@ -20,15 +20,15 @@
(let [prev-active-token-themes (ctob/get-active-theme-paths tokens-lib)
active-token-set-names (ctob/get-active-themes-set-names tokens-lib)
prev-hidden-theme (ctob/get-hidden-theme tokens-lib)
prev-hidden-token-theme (ctob/get-hidden-theme tokens-lib)
hidden-theme (-> (some-> prev-hidden-theme (ctob/set-sets active-token-set-names))
(update-theme-fn))]
hidden-token-theme (-> (some-> prev-hidden-token-theme (ctob/set-sets active-token-set-names))
(update-theme-fn))]
(-> changes
(pcb/update-active-token-themes #{(ctob/theme-path hidden-theme)} prev-active-token-themes)
(pcb/set-token-theme (:group prev-hidden-theme)
(:name prev-hidden-theme)
hidden-theme))))
(pcb/update-active-token-themes #{ctob/hidden-token-theme-path} prev-active-token-themes)
(pcb/set-token-theme (:group prev-hidden-token-theme)
(:name prev-hidden-token-theme)
hidden-token-theme))))
(defn generate-toggle-token-set
"Toggle a token set at `set-name` in `tokens-lib` without modifying a

View File

@@ -9,18 +9,11 @@
(:require
[cuerdas.core :as str]))
(def font-types
#{"font/ttf"
"font/woff"
"font/otf"
"font/opentype"})
(def image-types
#{"image/jpeg"
"image/png"
"image/webp"
"image/gif"
"image/svg+xml"})
;; We have added ".ttf" as string to solve a problem with chrome input selector
(def valid-font-types #{"font/ttf" ".ttf" "font/woff", "application/font-woff" "woff" "font/otf" ".otf" "font/opentype"})
(def valid-image-types #{"image/jpeg", "image/png", "image/webp", "image/gif", "image/svg+xml"})
(def str-image-types (str/join "," valid-image-types))
(def str-font-types (str/join "," valid-font-types))
(defn format->extension
[format]

View File

@@ -5,7 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.common.schema
(:refer-clojure :exclude [deref merge parse-uuid parse-long parse-double parse-boolean type keys])
(:refer-clojure :exclude [deref merge parse-uuid parse-long parse-double parse-boolean type])
#?(:cljs (:require-macros [app.common.schema :refer [ignoring]]))
(:require
[app.common.data :as d]
@@ -118,21 +118,6 @@
[& transformers]
(apply mt/transformer transformers))
(defn entries
"Get map entires of a map schema"
[schema]
(m/entries schema default-options))
(def ^:private xf:map-key
(map key))
(defn keys
"Given a map schema, return all keys as set"
[schema]
(->> (entries schema)
(into #{} xf:map-key)))
;; (defn key-transformer
;; [& {:as opts}]
;; (mt/key-transformer opts))
@@ -717,10 +702,7 @@
(fn [v]
(and (pred v)
(>= max v)))
pred)
gen (or (get props :gen/gen)
(sg/small-int :max max :min min))]
pred)]
{:pred pred
:type-properties
@@ -728,7 +710,7 @@
:description "int"
:error/message "expected to be int/long"
:error/code "errors.invalid-integer"
:gen/gen gen
:gen/gen (sg/small-int :max max :min min)
:decode/string parse-long
:decode/json parse-long
::oapi/type "integer"
@@ -786,11 +768,10 @@
(>= max v)))
pred)
gen (or (get props :gen/gen)
(sg/one-of
(sg/small-int :max max :min min)
(->> (sg/small-double :max max :min min)
(sg/fmap #(mth/precision % 2)))))]
gen (sg/one-of
(sg/small-int :max max :min min)
(->> (sg/small-double :max max :min min)
(sg/fmap #(mth/precision % 2))))]
{:pred pred
:type-properties
@@ -805,9 +786,7 @@
(register! ::safe-int [::int {:max max-safe-int :min min-safe-int}])
(register! ::safe-double [::double {:max max-safe-int :min min-safe-int}])
(register! ::safe-number [::number {:gen/gen (sg/small-double)
:max max-safe-int
:min min-safe-int}])
(register! ::safe-number [::number {:max max-safe-int :min min-safe-int}])
(defn parse-boolean
[v]

View File

@@ -8,7 +8,6 @@
(:refer-clojure :exclude [set subseq uuid filter map let boolean vector keyword int double])
#?(:cljs (:require-macros [app.common.schema.generators]))
(:require
[app.common.math :as mth]
[app.common.schema.registry :as sr]
[app.common.uri :as u]
[app.common.uuid :as uuid]
@@ -41,8 +40,7 @@
(defn small-double
[& {:keys [min max] :or {min -100 max 100}}]
(->> (tg/double* {:min min, :max max, :infinite? false, :NaN? false})
(tg/fmap #(mth/precision % 2))))
(tg/double* {:min min, :max max, :infinite? false, :NaN? false}))
(defn small-int
[& {:keys [min max] :or {min -100 max 100}}]

View File

@@ -29,14 +29,6 @@
:name "Rect1"}
params)))
(defn add-text
[file text-label content & {:keys [text-params] :as text}]
(let [shape (-> (cts/setup-shape {:type :text :x 0 :y 0})
(txt/change-text content))]
(ths/add-sample-shape file text-label
(merge shape
text-params))))
(defn add-frame
[file frame-label & {:keys [] :as params}]
;; Generated shape tree:

View File

@@ -7,12 +7,11 @@
(ns app.common.test-helpers.shapes
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.files.helpers :as cfh]
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.ids-map :as thi]
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.container :as ctn]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts]
@@ -82,31 +81,9 @@
(:id page)
#(ctst/set-shape % (ctn/set-shape-attr shape attr val)))))))
(defn update-shape-text
[file shape-label attr val & {:keys [page-label]}]
(let [page (if page-label
(thf/get-page file page-label)
(thf/current-page file))
shape (ctst/get-shape page (thi/id shape-label))]
(update file :data
(fn [file-data]
(ctpl/update-page file-data
(:id page)
#(ctst/set-shape % (txt/update-text-content shape
txt/is-content-node?
d/txt-merge
{attr val})))))))
(defn sample-library-color
[label & {:keys [name path color opacity gradient image]}]
(-> {:id (thi/new-id! label)
:name (or name color "Black")
:path path
:color (or color "#000000")
:opacity (or opacity 1)
:gradient gradient
:image image}
(d/without-nils)))
(defn sample-color
[label & {:keys [] :as params}]
(ctc/make-color (assoc params :id (thi/new-id! label))))
(defn sample-fill-color
[& {:keys [fill-color fill-opacity] :as params}]
@@ -124,8 +101,8 @@
(defn add-sample-library-color
[file label & {:keys [] :as params}]
(let [color (sample-library-color label params)]
(update file :data ctc/add-color color)))
(let [color (sample-color label params)]
(update file :data ctcl/add-color color)))
(defn sample-typography
[label & {:keys [] :as params}]
@@ -149,4 +126,4 @@
(fn [file-data]
(ctpl/update-page file-data
(:id page)
#(ctst/set-shape % (assoc origin :interactions interactions)))))))
#(ctst/set-shape % (assoc origin :interactions interactions)))))))

View File

@@ -36,9 +36,9 @@
(ctob/get-token token-name)))))
(defn token-data-eq?
"Compare token data without comparing unstable fields."
"Compare token data without comparing modified timestamp"
[t1 t2]
(= (dissoc t1 :id :modified-at) (dissoc t2 :id :modified-at)))
(= (dissoc t1 :modified-at) (dissoc t2 :modified-at)))
(defn- set-stroke-width
[shape stroke-width]

View File

@@ -121,51 +121,13 @@
{:name "Source Sans Pro Regular"}
(select-keys default-text-attrs typography-fields)))
(defn node-seq
([root] (node-seq identity root))
([match? root]
(->> (tree-seq map? :children root)
(filter match?)
(seq))))
(defn is-text-node?
[node]
(and (nil? (:type node))
(string? (:text node))))
(defn is-paragraph-set-node?
[node]
(= "paragraph-set" (:type node)))
(defn is-paragraph-node?
[node]
(= "paragraph" (:type node)))
(defn is-root-node?
[node]
(= "root" (:type node)))
(defn is-node?
[node]
(or ^boolean (is-text-node? node)
^boolean (is-paragraph-node? node)
^boolean (is-paragraph-set-node? node)
^boolean (is-root-node? node)))
(defn is-content-node?
"Only matches content nodes, ignoring the paragraph-set nodes."
[node]
(or ^boolean (is-text-node? node)
^boolean (is-paragraph-node? node)
^boolean (is-root-node? node)))
(defn transform-nodes
([transform root]
(transform-nodes identity transform root))
([pred transform root]
(walk/postwalk
(fn [item]
(if (and (is-node? item) (pred item))
(if (and (map? item) (pred item))
(transform item)
item))
root)))
@@ -178,17 +140,30 @@
(walk/postwalk
(fn [item]
(let [rf (xf rf)]
(if (is-node? item)
(if (map? item)
(d/nilv (rf nil item) item)
item)))
root)))
(defn update-text-content
[shape pred-fn update-fn attrs]
(let [update-attrs-fn #(update-fn % attrs)
transform #(transform-nodes pred-fn update-attrs-fn %)]
(-> shape
(update :content transform))))
(defn node-seq
([root] (node-seq identity root))
([match? root]
(->> (tree-seq map? :children root)
(filter match?)
(seq))))
(defn is-text-node?
[node]
(and (string? (:text node))
(not= (:text node) "")))
(defn is-paragraph-node?
[node]
(= "paragraph" (:type node)))
(defn is-root-node?
[node]
(= "root" (:type node)))
(defn generate-shape-name
[text]

View File

@@ -208,13 +208,9 @@
([data] (encode-str data nil))
([data opts]
#?(:cljs
(let [type (:type opts :json)
params {:handlers @write-handler-map}
params (if (:with-meta opts)
(assoc params :transform t/write-meta)
params)
writer (t/writer type params)]
(t/write writer data))
(let [t (:type opts :json)
w (t/writer t {:handlers @write-handler-map})]
(t/write w data))
:clj
(->> (encode data opts)
(bytes->str)))))
@@ -223,10 +219,9 @@
([data] (decode-str data nil))
([data opts]
#?(:cljs
(let [type (:type opts :json)
params {:handlers @read-handler-map}
reader (t/reader type params)]
(t/read reader data))
(let [t (:type opts :json)
r (t/reader t {:handlers @read-handler-map})]
(t/read r data))
:clj
(-> (str->bytes data)
(decode opts)))))

View File

@@ -8,36 +8,23 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.media :as cm]
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.schema.openapi :as-alias oapi]
[app.common.text :as txt]
[app.common.time :as dt]
[app.common.types.plugins :as ctpg]
[clojure.set :as set]
[app.common.uuid :as uuid]
[cuerdas.core :as str]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SCHEMAS & TYPES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private required-color-attrs
"A set used for proper check if color should contain only one of the
attrs listed in this set."
#{:image :gradient :color})
(defn has-valid-color-attrs?
"Check if color has correct color attrs"
[color]
(let [attrs (set (keys color))
result (set/intersection attrs required-color-attrs)]
(= 1 (count result))))
(def ^:private hex-color-rx
(def rgb-color-re
#"^#(?:[0-9a-fA-F]{3}){1,2}$")
(def ^:private hex-color-generator
(defn- generate-rgb-color
[]
(sg/fmap (fn [_]
#?(:clj (format "#%06x" (rand-int 16rFFFFFF))
:cljs
@@ -50,48 +37,38 @@
(.. b (toString 16) (padStart 2 "0"))))))
sg/int))
(defn hex-color-string?
(defn rgb-color-string?
[o]
(and (string? o) (some? (re-matches hex-color-rx o))))
(and (string? o) (some? (re-matches rgb-color-re o))))
(def schema:hex-color
(def schema:rgb-color
(sm/register!
{:type ::hex-color
:pred hex-color-string?
{:type ::rgb-color
:pred rgb-color-string?
:type-properties
{:title "hex-color"
:description "HEX Color String"
:error/message "expected a valid HEX color"
:error/code "errors.invalid-hex-color"
:gen/gen hex-color-generator
{:title "rgb-color"
:description "RGB Color String"
:error/message "expected a valid RGB color"
:error/code "errors.invalid-rgb-color"
:gen/gen (generate-rgb-color)
::oapi/type "integer"
::oapi/format "int64"}}))
(def schema:plain-color
[:map [:color schema:hex-color]])
(def schema:image
[:map {:title "ImageColor" :closed true}
[:width [::sm/int {:min 0 :gen/gen sg/int}]]
[:height [::sm/int {:min 0 :gen/gen sg/int}]]
[:mtype {:gen/gen (sg/elements cm/image-types)} ::sm/text]
[:map {:title "ImageColor"}
[:width ::sm/int]
[:height ::sm/int]
[:mtype ::sm/text]
[:id ::sm/uuid]
[:name {:optional true} ::sm/text]
[:keep-aspect-ratio {:optional true} :boolean]])
(def image-attrs
"A set of attrs that corresponds to image data type"
(sm/keys schema:image))
(def schema:image-color
[:map [:image schema:image]])
(def gradient-types
#{:linear :radial})
(def schema:gradient
[:map {:title "Gradient" :closed true}
[:type [::sm/one-of gradient-types]]
[:map {:title "Gradient"}
[:type [::sm/one-of #{:linear :radial}]]
[:start-x ::sm/safe-number]
[:start-y ::sm/safe-number]
[:end-x ::sm/safe-number]
@@ -100,89 +77,86 @@
[:stops
[:vector {:min 1 :gen/max 2}
[:map {:title "GradientStop"}
[:color schema:hex-color]
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
[:offset [::sm/number {:min 0 :max 1}]]]]]])
(def gradient-attrs
"A set of attrs that corresponds to gradient data type"
(sm/keys schema:gradient))
(def schema:gradient-color
[:map [:gradient schema:gradient]])
[:color schema:rgb-color]
[:opacity {:optional true} [:maybe ::sm/safe-number]]
[:offset ::sm/safe-number]]]]])
(def schema:color-attrs
[:map {:title "ColorAttrs" :closed true}
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
[:ref-id {:optional true} ::sm/uuid]
[:ref-file {:optional true} ::sm/uuid]])
(def schema:color
[:and
[:merge {:title "Color"}
schema:color-attrs
(sm/optional-keys schema:plain-color)
(sm/optional-keys schema:gradient-color)
(sm/optional-keys schema:image-color)]
[:fn has-valid-color-attrs?]])
(def color-attrs
(into required-color-attrs (sm/keys schema:color-attrs)))
(def schema:library-color-attrs
[:map {:title "ColorAttrs" :closed true}
[:id ::sm/uuid]
[:name ::sm/text]
[:path {:optional true} :string]
[:opacity {:optional true} [::sm/number {:min 0 :max 1}]]
[:map {:title "ColorAttrs"}
[:id {:optional true} ::sm/uuid]
[:name {:optional true} :string]
[:path {:optional true} [:maybe :string]]
[:value {:optional true} [:maybe :string]]
[:color {:optional true} [:maybe schema:rgb-color]]
[:opacity {:optional true} [:maybe ::sm/safe-number]]
[:modified-at {:optional true} ::sm/inst]
[:ref-id {:optional true} ::sm/uuid]
[:ref-file {:optional true} ::sm/uuid]
[:gradient {:optional true} [:maybe schema:gradient]]
[:image {:optional true} [:maybe schema:image]]
[:plugin-data {:optional true} ::ctpg/plugin-data]])
(def schema:library-color
"Used for in-transit representation of a color (per example when user
clicks a color on assets sidebar, the color should be properly identified with
the file-id where it belongs)"
[:and
[:merge
schema:library-color-attrs
(sm/optional-keys schema:plain-color)
(sm/optional-keys schema:gradient-color)
(sm/optional-keys schema:image-color)]
[:fn has-valid-color-attrs?]])
(def schema:color
[:and schema:color-attrs
[::sm/contains-any {:strict true} [:color :gradient :image]]])
(def library-color-attrs
(into required-color-attrs (sm/keys schema:library-color-attrs)))
(def schema:recent-color
[:and
[:map {:title "RecentColor"}
[:opacity {:optional true} [:maybe ::sm/safe-number]]
[:color {:optional true} [:maybe schema:rgb-color]]
[:gradient {:optional true} [:maybe schema:gradient]]
[:image {:optional true} [:maybe schema:image]]]
[::sm/contains-any {:strict true} [:color :gradient :image]]])
;; Same as color but with :id prop required
(def schema:library-color
[:and
(sm/required-keys schema:color-attrs [:id])
[::sm/contains-any {:strict true} [:color :gradient :image]]])
;; FIXME: revisit if we really need this all registers
(sm/register! ::color schema:color)
(sm/register! ::gradient schema:gradient)
(sm/register! ::image-color schema:image)
(sm/register! ::recent-color schema:recent-color)
(sm/register! ::color-attrs schema:color-attrs)
(def valid-color?
(sm/lazy-validator schema:color))
(def valid-library-color?
(sm/lazy-validator schema:library-color))
(def check-color
(sm/check-fn schema:color :hint "expected valid color"))
(def check-library-color
(sm/check-fn schema:library-color :hint "expected valid color"))
(sm/check-fn schema:library-color :hint "expected valid library color"))
(def check-recent-color
(sm/check-fn schema:recent-color :hint "expected valid recent color"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn library-color->color
"Converts a library color data structure to a plain color data structure"
[lcolor file-id]
(-> lcolor
(select-keys [:image :gradient :color :opacity])
(assoc :ref-id (get lcolor :id))
(assoc :ref-file file-id)
(vary-meta assoc
:path (get lcolor :path)
:name (get lcolor :name))))
;; --- factory
(defn make-color
[{:keys [id name path value color opacity ref-id ref-file gradient image]}]
(-> {:id (or id (uuid/next))
:name (or name color "Black")
:path path
:value value
:color (or color "#000000")
:opacity (or opacity 1)
:ref-id ref-id
:ref-file ref-file
:gradient gradient
:image image}
(d/without-nils)))
;; --- fill
(defn fill->color
(defn fill->shape-color
[fill]
(d/without-nils
{:color (:fill-color fill)
@@ -204,130 +178,126 @@
(defn attach-fill-color
[shape position ref-id ref-file]
(d/update-in-when shape [:fills position]
(fn [fill]
(-> fill
(assoc :fill-color-ref-file ref-file)
(assoc :fill-color-ref-id ref-id)))))
(-> shape
(assoc-in [:fills position :fill-color-ref-id] ref-id)
(assoc-in [:fills position :fill-color-ref-file] ref-file)))
(defn detach-fill-color
[shape position]
(d/update-in-when shape [:fills position] dissoc :fill-color-ref-id :fill-color-ref-file))
(-> shape
(d/dissoc-in [:fills position :fill-color-ref-id])
(d/dissoc-in [:fills position :fill-color-ref-file])))
;; stroke
(defn stroke->color
(defn stroke->shape-color
[stroke]
(d/without-nils
{:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:gradient (:stroke-color-gradient stroke)
:image (:stroke-image stroke)
:ref-id (:stroke-color-ref-id stroke)
:ref-file (:stroke-color-ref-file stroke)}))
(d/without-nils {:color (:stroke-color stroke)
:opacity (:stroke-opacity stroke)
:gradient (:stroke-color-gradient stroke)
:image (:stroke-image stroke)
:ref-id (:stroke-color-ref-id stroke)
:ref-file (:stroke-color-ref-file stroke)}))
(defn set-stroke-color
[shape position color opacity gradient image]
(d/update-in-when shape [:strokes position]
(fn [stroke]
(-> stroke
(assoc :stroke-color color)
(assoc :stroke-opacity opacity)
(assoc :stroke-color-gradient gradient)
(assoc :stroke-image image)
(d/without-nils)))))
(update-in shape [:strokes position]
(fn [stroke]
(d/without-nils (assoc stroke
:stroke-color color
:stroke-opacity opacity
:stroke-color-gradient gradient
:stroke-image image)))))
(defn attach-stroke-color
[shape position ref-id ref-file]
(d/update-in-when shape [:strokes position]
(fn [stroke]
(-> stroke
(assoc :stroke-color-ref-id ref-id)
(assoc :stroke-color-ref-file ref-file)))))
(-> shape
(assoc-in [:strokes position :stroke-color-ref-id] ref-id)
(assoc-in [:strokes position :stroke-color-ref-file] ref-file)))
(defn detach-stroke-color
[shape position]
(d/update-in-when shape [:strokes position] dissoc :stroke-color-ref-id :stroke-color-ref-file))
(-> shape
(d/dissoc-in [:strokes position :stroke-color-ref-id])
(d/dissoc-in [:strokes position :stroke-color-ref-file])))
;; shadow
(defn shadow->color
(defn shadow->shape-color
[shadow]
(:color shadow))
(d/without-nils {:color (-> shadow :color :color)
:opacity (-> shadow :color :opacity)
:gradient (-> shadow :color :gradient)
:ref-id (-> shadow :color :id)
:ref-file (-> shadow :color :file-id)}))
(defn set-shadow-color
[shape position color opacity gradient]
(d/update-in-when shape [:shadow position :color]
(fn [shadow-color]
(-> shadow-color
(assoc :color color)
(assoc :opacity opacity)
(assoc :gradient gradient)
(d/without-nils)))))
(update-in shape [:shadow position :color]
(fn [shadow-color]
(d/without-nils (assoc shadow-color
:color color
:opacity opacity
:gradient gradient)))))
(defn attach-shadow-color
[shape position ref-id ref-file]
(d/update-in-when shape [:shadow position :color]
(fn [color]
(-> color
(assoc :ref-id ref-id)
(assoc :ref-file ref-file)))))
(-> shape
(assoc-in [:shadow position :color :id] ref-id)
(assoc-in [:shadow position :color :file-id] ref-file)))
(defn detach-shadow-color
[shape position]
(d/update-in-when shape [:shadow position :color] dissoc :ref-id :ref-file))
(-> shape
(d/dissoc-in [:shadow position :color :id])
(d/dissoc-in [:shadow position :color :file-id])))
;; grid
;: FIXME: revisit colors...... WTF
(defn grid->color
(defn grid->shape-color
[grid]
(let [color (-> grid :params :color)]
(d/without-nils
{:color (-> color :color)
:opacity (-> color :opacity)
:gradient (-> color :gradient)
:ref-id (-> color :id)
:ref-file (-> color :file-id)})))
(d/without-nils {:color (-> grid :params :color :color)
:opacity (-> grid :params :color :opacity)
:gradient (-> grid :params :color :gradient)
:ref-id (-> grid :params :color :id)
:ref-file (-> grid :params :color :file-id)}))
(defn set-grid-color
[shape position color opacity gradient]
(d/update-in-when shape [:grids position :params :color]
(fn [grid-color]
(-> grid-color
(assoc :color color)
(assoc :opacity opacity)
(assoc :gradient gradient)
(d/without-nils)))))
(update-in shape [:grids position :params :color]
(fn [grid-color]
(d/without-nils (assoc grid-color
:color color
:opacity opacity
:gradient gradient)))))
(defn attach-grid-color
[shape position ref-id ref-file]
(d/update-in-when shape [:grids position :params :color]
(fn [color]
(-> color
(assoc :ref-id ref-id)
(assoc :ref-file ref-file)))))
(-> shape
(assoc-in [:grids position :params :color :id] ref-id)
(assoc-in [:grids position :params :color :file-id] ref-file)))
(defn detach-grid-color
[shape position]
(d/update-in-when shape [:grids position :params :color] dissoc :ref-id :ref-file))
(-> shape
(d/dissoc-in [:grids position :params :color :id])
(d/dissoc-in [:grids position :params :color :file-id])))
;; --- Helpers for all colors in a shape
(defn get-text-node-colors
"Get all colors used by a node of a text shape"
[node]
(concat (map fill->color (:fills node))
(map stroke->color (:strokes node))))
(concat (map fill->shape-color (:fills node))
(map stroke->shape-color (:strokes node))))
(defn get-all-colors
"Get all colors used by a shape, in any section."
[shape]
(concat (map fill->color (:fills shape))
(map stroke->color (:strokes shape))
(map shadow->color (:shadow shape))
(concat (map fill->shape-color (:fills shape))
(map stroke->shape-color (:strokes shape))
(map shadow->shape-color (:shadow shape))
(when (= (:type shape) :frame)
(map grid->color (:grids shape)))
(map grid->shape-color (:grids shape)))
(when (= (:type shape) :text)
(reduce (fn [colors node]
(concat colors (get-text-node-colors node)))
@@ -356,7 +326,7 @@
(let [process-fill (fn [shape [position fill]]
(process-fn shape
position
(fill->color fill)
(fill->shape-color fill)
set-fill-color
attach-fill-color
detach-fill-color))
@@ -364,7 +334,7 @@
process-stroke (fn [shape [position stroke]]
(process-fn shape
position
(stroke->color stroke)
(stroke->shape-color stroke)
set-stroke-color
attach-stroke-color
detach-stroke-color))
@@ -372,7 +342,7 @@
process-shadow (fn [shape [position shadow]]
(process-fn shape
position
(shadow->color shadow)
(shadow->shape-color shadow)
set-shadow-color
attach-shadow-color
detach-shadow-color))
@@ -380,7 +350,7 @@
process-grid (fn [shape [position grid]]
(process-fn shape
position
(grid->color grid)
(grid->shape-color grid)
set-grid-color
attach-grid-color
detach-grid-color))
@@ -437,76 +407,114 @@
(process-shape-colors shape sync-color)))
(defn- stroke->color-att
[stroke file-id libraries]
(let [ref-file (:stroke-color-ref-file stroke)
ref-id (:stroke-color-ref-id stroke)
shared-colors (dm/get-in libraries [ref-file :data :colors])
is-shared? (contains? shared-colors ref-id)
has-color? (or (:stroke-color stroke)
(:stroke-color-gradient stroke))
attrs (cond-> (stroke->color stroke)
(not (or is-shared? (= ref-file file-id)))
(dissoc :ref-id :ref-file))]
(defn- eq-recent-color?
[c1 c2]
(or (= c1 c2)
(and (some? (:color c1))
(some? (:color c2))
(= (:color c1) (:color c2)))))
(defn add-recent-color
"Moves the color to the top of the list and then truncates up to 15"
[state file-id color]
(update state file-id (fn [colors]
(let [colors (d/removev (partial eq-recent-color? color) colors)
colors (conj colors color)]
(cond-> colors
(> (count colors) 15)
(subvec 1))))))
(defn stroke->color-att
[stroke file-id shared-libs]
(let [color-file-id (:stroke-color-ref-file stroke)
color-id (:stroke-color-ref-id stroke)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
has-color? (or (not (nil? (:stroke-color stroke))) (not (nil? (:stroke-color-gradient stroke))))
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:id color-id
:file-id color-file-id
:gradient (:stroke-color-gradient stroke)})
(d/without-nils {:color (str/lower (:stroke-color stroke))
:opacity (:stroke-opacity stroke)
:gradient (:stroke-color-gradient stroke)}))]
(when has-color?
{:attrs attrs
:prop :stroke
:shape-id (:shape-id stroke)
:index (:index stroke)})))
(defn- shadow->color-att
[shadow file-id libraries]
(let [color (get shadow :color)
ref-file (get color :ref-file)
ref-id (get color :ref-id)
shared-colors (dm/get-in libraries [ref-file :data :colors])
is-shared? (contains? shared-colors ref-id)
attrs (cond-> (shadow->color shadow)
(not (or is-shared? (= ref-file file-id)))
(dissoc :ref-file :ref-id))]
(defn shadow->color-att
[shadow file-id shared-libs]
(let [color-file-id (dm/get-in shadow [:color :file-id])
color-id (dm/get-in shadow [:color :id])
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
:opacity (dm/get-in shadow [:color :opacity])
:id color-id
:file-id (dm/get-in shadow [:color :file-id])
:gradient (dm/get-in shadow [:color :gradient])})
(d/without-nils {:color (str/lower (dm/get-in shadow [:color :color]))
:opacity (dm/get-in shadow [:color :opacity])
:gradient (dm/get-in shadow [:color :gradient])}))]
{:attrs attrs
:prop :shadow
:shape-id (:shape-id shadow)
:index (:index shadow)}))
(defn- text->color-att
[fill file-id libraries]
(let [ref-file (:fill-color-ref-file fill)
ref-id (:fill-color-ref-id fill)
shared-colors (dm/get-in libraries [ref-file :data :colors])
is-shared? (contains? shared-colors ref-id)
attrs (cond-> (fill->color fill)
(not (or is-shared? (= ref-file file-id)))
(dissoc :ref-file :ref-id))]
(defn text->color-att
[fill file-id shared-libs]
(let [color-file-id (:fill-color-ref-file fill)
color-id (:fill-color-ref-id fill)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:id color-id
:file-id color-file-id
:gradient (:fill-color-gradient fill)})
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)}))]
{:attrs attrs
:prop :content
:shape-id (:shape-id fill)
:index (:index fill)}))
(defn- treat-node
(defn treat-node
[node shape-id]
(map-indexed #(assoc %2 :shape-id shape-id :index %1) node))
(defn- extract-text-colors
[text file-id libraries]
(->> (txt/node-seq txt/is-text-node? (:content text))
(map :fills)
(mapcat #(treat-node % (:id text)))
(map #(text->color-att % file-id libraries))))
(defn- fill->color-att
[fill file-id libraries]
(let [ref-file (:fill-color-ref-file fill)
ref-id (:fill-color-ref-id fill)
shared-colors (dm/get-in libraries [ref-file :data :colors])
is-shared? (contains? shared-colors ref-id)
has-color? (or (:fill-color fill)
(:fill-color-gradient fill))
attrs (cond-> (fill->color fill)
(not (or is-shared? (= ref-file file-id)))
(dissoc :ref-file :ref-id))]
(defn extract-text-colors
[text file-id shared-libs]
(let [content (txt/node-seq txt/is-text-node? (:content text))
content-filtered (map :fills content)
indexed (mapcat #(treat-node % (:id text)) content-filtered)]
(map #(text->color-att % file-id shared-libs) indexed)))
(defn fill->color-att
[fill file-id shared-libs]
(let [color-file-id (:fill-color-ref-file fill)
color-id (:fill-color-ref-id fill)
shared-libs-colors (dm/get-in shared-libs [color-file-id :data :colors])
is-shared? (contains? shared-libs-colors color-id)
has-color? (or (not (nil? (:fill-color fill))) (not (nil? (:fill-color-gradient fill))))
attrs (if (or is-shared? (= color-file-id file-id))
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:id color-id
:file-id color-file-id
:gradient (:fill-color-gradient fill)})
(d/without-nils {:color (str/lower (:fill-color fill))
:opacity (:fill-opacity fill)
:gradient (:fill-color-gradient fill)}))]
(when has-color?
{:attrs attrs
:prop :fill
@@ -514,67 +522,21 @@
:index (:index fill)})))
(defn extract-all-colors
[shapes file-id libraries]
[shapes file-id shared-libs]
(reduce
(fn [result shape]
(fn [list shape]
(let [fill-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:fills shape))
stroke-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:strokes shape))
shadow-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:shadow shape))]
(if (= :text (:type shape))
(-> result
(into (map #(stroke->color-att % file-id libraries)) stroke-obj)
(into (map #(shadow->color-att % file-id libraries)) shadow-obj)
(into (extract-text-colors shape file-id libraries)))
(-> list
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)
(into (extract-text-colors shape file-id shared-libs)))
(-> result
(into (map #(fill->color-att % file-id libraries)) fill-obj)
(into (map #(stroke->color-att % file-id libraries)) stroke-obj)
(into (map #(shadow->color-att % file-id libraries)) shadow-obj)))))
(-> list
(into (map #(fill->color-att % file-id shared-libs)) fill-obj)
(into (map #(stroke->color-att % file-id shared-libs)) stroke-obj)
(into (map #(shadow->color-att % file-id shared-libs)) shadow-obj)))))
[]
shapes))
(defn colors-seq
[file-data]
(vals (:colors file-data)))
(defn- touch
[color]
(assoc color :modified-at (dt/now)))
(defn add-color
[file-data color]
(update file-data :colors assoc (:id color) (touch color)))
(defn get-color
[file-data color-id]
(get-in file-data [:colors color-id]))
(defn get-ref-color
[library-data color]
(when (= (:ref-file color) (:id library-data))
(get-color library-data (:ref-id color))))
(defn set-color
[file-data color]
(d/assoc-in-when file-data [:colors (:id color)] (touch color)))
(defn update-color
[file-data color-id f & args]
(d/update-in-when file-data [:colors color-id] #(-> (apply f % args)
(touch))))
(defn delete-color
[file-data color-id]
(update file-data :colors dissoc color-id))
(defn used-colors-changed-since
"Find all usages of any color in the library by the given shape, of colors
that have ben modified after the date."
[shape library since-date]
(->> (get-all-colors shape)
(keep #(get-ref-color (:data library) %))
(remove #(< (:modified-at %) since-date)) ;; Note that :modified-at may be nil
(map (fn [color] {:shape-id (:id shape)
:asset-id (:id color)
:asset-type :color}))))

View File

@@ -0,0 +1,56 @@
;; 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.common.types.colors-list
(:require
[app.common.data :as d]
[app.common.time :as dt]
[app.common.types.color :as ctc]))
(defn colors-seq
[file-data]
(vals (:colors file-data)))
(defn- touch
[color]
(assoc color :modified-at (dt/now)))
(defn add-color
[file-data color]
(update file-data :colors assoc (:id color) (touch color)))
(defn get-color
[file-data color-id]
(get-in file-data [:colors color-id]))
(defn get-ref-color
[library-data color]
(when (= (:ref-file color) (:id library-data))
(get-color library-data (:ref-id color))))
(defn set-color
[file-data color]
(d/assoc-in-when file-data [:colors (:id color)] (touch color)))
(defn update-color
[file-data color-id f & args]
(d/update-in-when file-data [:colors color-id] #(-> (apply f % args)
(touch))))
(defn delete-color
[file-data color-id]
(update file-data :colors dissoc color-id))
(defn used-colors-changed-since
"Find all usages of any color in the library by the given shape, of colors
that have ben modified after the date."
[shape library since-date]
(->> (ctc/get-all-colors shape)
(keep #(get-ref-color (:data library) %))
(remove #(< (:modified-at %) since-date)) ;; Note that :modified-at may be nil
(map (fn [color] {:shape-id (:id shape)
:asset-id (:id color)
:asset-type :color}))))

View File

@@ -18,6 +18,7 @@
[app.common.types.plugins :as ctpg]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.common.types.text :as cttx]
[app.common.types.token :as ctt]
[app.common.uuid :as uuid]
[clojure.set :as set]))
@@ -569,13 +570,16 @@
(not equal?)
(not (and ignore-geometry is-geometry?)))
content-diff-type (when (and (= (:type shape) :text) (= attr :content))
(cttx/get-diff-type (:content shape) val))
token-groups (if (= attr :applied-tokens)
(get-token-groups shape val)
#{})
groups (cond-> token-groups
(and group (not equal?))
(set/union #{group}))]
(set/union #{group} content-diff-type))]
(cond-> shape
;; Depending on the origin of the attribute change, we need or not to
;; set the "touched" flag for the group the attribute belongs to.

View File

@@ -18,6 +18,7 @@
[app.common.schema :as sm]
[app.common.text :as ct]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
@@ -58,7 +59,7 @@
[:is-local {:optional true} :boolean]])
(def schema:colors
[:map-of {:gen/max 5} ::sm/uuid ctc/schema:library-color])
[:map-of {:gen/max 5} ::sm/uuid ::ctc/color])
(def schema:components
[:map-of {:gen/max 5} ::sm/uuid ::ctn/container])
@@ -487,7 +488,7 @@
[file-data library-data asset-type]
(let [assets-seq (case asset-type
:component (ctkl/components-seq library-data)
:color (ctc/colors-seq library-data)
:color (ctcl/colors-seq library-data)
:typography (ctyl/typographies-seq library-data))
find-usages-in-container
@@ -526,7 +527,7 @@
(letfn [(used-assets-shape [shape]
(concat
(ctkl/used-components-changed-since shape library since-date)
(ctc/used-colors-changed-since shape library since-date)
(ctcl/used-colors-changed-since shape library since-date)
(ctyl/used-typographies-changed-since shape library since-date)))
(used-assets-container [container]
@@ -662,7 +663,7 @@
%
shapes)))]
(as-> file-data $
(ctc/add-color $ color)
(ctcl/add-color $ color)
(reduce remap-shapes $ usages))))]
(reduce absorb-color
@@ -1045,4 +1046,4 @@
(defn set-base-font-size
[file-data base-font-size]
(assoc-in file-data [:options :base-font-size] base-font-size))
(assoc-in file-data [:options :base-font-size] base-font-size))

View File

@@ -1,68 +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.common.types.fill
(:require
[app.common.schema :as sm]
[app.common.types.color :as types.color]
[app.common.types.fill.impl :as impl]
[clojure.set :as set]))
(def ^:const MAX-GRADIENT-STOPS impl/MAX-GRADIENT-STOPS)
(def ^:const MAX-FILLS impl/MAX-FILLS)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SCHEMAS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def schema:fill-attrs
[:map {:title "FillAttrs" :closed true}
[:fill-color-ref-file {:optional true} ::sm/uuid]
[:fill-color-ref-id {:optional true} ::sm/uuid]
[:fill-opacity {:optional true} [::sm/number {:min 0 :max 1}]]
[:fill-color {:optional true} types.color/schema:hex-color]
[:fill-color-gradient {:optional true} types.color/schema:gradient]
[:fill-image {:optional true} types.color/schema:image]])
(def fill-attrs
"A set of attrs that corresponds to fill data type"
(sm/keys schema:fill-attrs))
(def valid-fill-attrs
"A set used for proper check if color should contain only one of the
attrs listed in this set."
#{:fill-image :fill-color :fill-color-gradient})
(defn has-valid-fill-attrs?
"Check if color has correct color attrs"
[color]
(let [attrs (set (keys color))
result (set/intersection attrs valid-fill-attrs)]
(= 1 (count result))))
(def schema:fill
[:and schema:fill-attrs
[:fn has-valid-fill-attrs?]])
(def check-fill
(sm/check-fn schema:fill))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONSTRUCTORS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn from-plain
[o]
(assert (every? check-fill o) "expected valid fills vector")
(impl/from-plain o))
(defn fills?
[o]
(impl/fills? o))

View File

@@ -1,404 +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.common.types.fill.impl
(:require
#?(:clj [clojure.data.json :as json])
#?(:cljs [app.common.weak-map :as weak-map])
[app.common.buffer :as buf]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.math :as mth]
[app.common.transit :as t]))
;; FIXME: Get these from the wasm module, and tweak the values
;; (we'd probably want 12 stops at most)
(def ^:const MAX-GRADIENT-STOPS 16)
(def ^:const MAX-FILLS 8)
(def ^:const GRADIENT-STOP-SIZE 8)
(def ^:const GRADIENT-BYTE-SIZE 156)
(def ^:const SOLID-BYTE-SIZE 4)
(def ^:const IMAGE-BYTE-SIZE 28)
(def ^:const METADATA-BYTE-SIZE 36)
(def ^:const FILL-BYTE-SIZE
(+ 4 (mth/max GRADIENT-BYTE-SIZE
IMAGE-BYTE-SIZE
SOLID-BYTE-SIZE)))
(def ^:private xf:take-stops
(take MAX-GRADIENT-STOPS))
(def ^:private xf:take-fills
(take MAX-FILLS))
(defn- hex->rgb
"Encode an hex string as rgb (int32)"
[hex]
(let [hex (subs hex 1)]
#?(:clj (Integer/parseInt hex 16)
:cljs (js/parseInt hex 16))))
(defn- rgb->rgba
"Use the first 2 bytes of in32 for encode the alpha channel"
[n alpha]
(let [result (mth/floor (* alpha 0xff))
result (unchecked-int result)
result (bit-shift-left result 24)
result (bit-or result n)]
result))
(defn- get-color-hex
[n]
(let [n (bit-and n 0x00ffffff)
n #?(:clj n :cljs (.toString n 16))]
(dm/str "#" #?(:clj (String/format "%06x" (into-array Object [n]))
:cljs (.padStart n 6 "0")))))
(defn- get-color-alpha
[rgb]
(let [n (bit-and rgb 0xff000000)
n (unsigned-bit-shift-right n 24)]
(mth/precision (/ (float n) 0xff) 2)))
(defn- write-solid-fill
[offset buffer color alpha]
(buf/write-byte buffer (+ offset 0) 0x00)
(buf/write-int buffer (+ offset 4)
(-> (hex->rgb color)
(rgb->rgba alpha)))
(+ offset FILL-BYTE-SIZE))
(defn- write-gradient-fill
[offset buffer gradient opacity]
(let [start-x (:start-x gradient)
start-y (:start-y gradient)
end-x (:end-x gradient)
end-y (:end-y gradient)
width (:width gradient 0)
stops (into [] xf:take-stops (:stops gradient))
type (if (= (:type gradient) :linear)
0x01
0x02)]
(buf/write-byte buffer (+ offset 0) type)
(buf/write-float buffer (+ offset 4) start-x)
(buf/write-float buffer (+ offset 8) start-y)
(buf/write-float buffer (+ offset 12) end-x)
(buf/write-float buffer (+ offset 16) end-y)
(buf/write-float buffer (+ offset 20) opacity)
(buf/write-float buffer (+ offset 24) width)
(buf/write-byte buffer (+ offset 28) (count stops))
(loop [stops (seq stops)
offset' (+ offset 32)]
(if-let [stop (first stops)]
(let [color (-> (hex->rgb (:color stop))
(rgb->rgba (:opacity stop 1)))]
;; NOTE: we write the color as signed integer but on rust
;; side it will be read as unsigned, on the end the binary
;; repr of the data is the same independently on how it is
;; interpreted
(buf/write-int buffer (+ offset' 0) color)
(buf/write-float buffer (+ offset' 4) (:offset stop))
(recur (rest stops)
(+ offset' GRADIENT-STOP-SIZE)))
(+ offset FILL-BYTE-SIZE)))))
(defn- write-image-fill
[offset buffer opacity image]
(let [image-id (get image :id)
image-width (get image :width)
image-height (get image :height)]
(buf/write-byte buffer (+ offset 0) 0x03)
(buf/write-uuid buffer (+ offset 4) image-id)
(buf/write-float buffer (+ offset 20) opacity)
(buf/write-int buffer (+ offset 24) image-width)
(buf/write-int buffer (+ offset 28) image-height)
(+ offset FILL-BYTE-SIZE)))
(defn- write-metadata
[offset buffer fill]
(let [ref-id (:fill-color-ref-id fill)
ref-file (:fill-color-ref-file fill)
mtype (dm/get-in fill [:fill-image :mtype])]
(when mtype
(let [val (case mtype
"image/jpeg" 0x01
"image/png" 0x02
"image/gif" 0x03
"image/webp" 0x04
"image/svg+xml" 0x05)]
(buf/write-short buffer (+ offset 2) val)))
(if (and (some? ref-file)
(some? ref-id))
(do
(buf/write-byte buffer (+ offset 0) 0x01)
(buf/write-uuid buffer (+ offset 4) ref-file)
(buf/write-uuid buffer (+ offset 20) ref-id))
(do
(buf/write-byte buffer (+ offset 0) 0x00)))))
(defn- read-stop
[buffer offset]
(let [rgba (buf/read-int buffer (+ offset 0))
soff (buf/read-float buffer (+ offset 4))]
{:color (get-color-hex rgba)
:opacity (get-color-alpha rgba)
:offset (mth/precision soff 2)}))
(defn- read-fill
"Read segment from binary buffer at specified index"
[dbuffer mbuffer index]
(let [doffset (+ 4 (* index FILL-BYTE-SIZE))
moffset (* index METADATA-BYTE-SIZE)
type (buf/read-byte dbuffer doffset)
refs? (buf/read-bool mbuffer (+ moffset 0))
fill (case type
0
(let [rgba (buf/read-int dbuffer (+ doffset 4))]
{:fill-color (get-color-hex rgba)
:fill-opacity (get-color-alpha rgba)})
(1 2)
(let [start-x (buf/read-float dbuffer (+ doffset 4))
start-y (buf/read-float dbuffer (+ doffset 8))
end-x (buf/read-float dbuffer (+ doffset 12))
end-y (buf/read-float dbuffer (+ doffset 16))
alpha (buf/read-float dbuffer (+ doffset 20))
width (buf/read-float dbuffer (+ doffset 24))
stops (buf/read-byte dbuffer (+ doffset 28))
type (if (= type 1)
:linear
:radial)
stops (loop [index 0
result []]
(if (< index stops)
(recur (inc index)
(conj result (read-stop dbuffer (+ doffset 32 (* GRADIENT-STOP-SIZE index)))))
result))]
{:fill-opacity alpha
:fill-color-gradient {:start-x start-x
:start-y start-y
:end-x end-x
:end-y end-y
:width width
:stops stops
:type type}})
3
(let [id (buf/read-uuid dbuffer (+ doffset 4))
alpha (buf/read-float dbuffer (+ doffset 20))
width (buf/read-int dbuffer (+ doffset 24))
height (buf/read-int dbuffer (+ doffset 28))
mtype (buf/read-short mbuffer (+ moffset 2))
mtype (case mtype
0x01 "image/jpeg"
0x02 "image/png"
0x03 "image/gif"
0x04 "image/webp"
0x05 "image/svg+xml")]
{:fill-opacity alpha
:fill-image {:id id
:width width
:height height
:mtype mtype
;; FIXME: we are not encodign the name, looks useless
:name "sample"}}))]
(if refs?
(let [ref-file (buf/read-uuid mbuffer (+ moffset 4))
ref-id (buf/read-uuid mbuffer (+ moffset 20))]
(-> fill
(assoc :fill-color-ref-id ref-id)
(assoc :fill-color-ref-file ref-file)))
fill)))
(declare from-plain)
#?(:clj
(deftype Fills [size dbuffer mbuffer ^:unsynchronized-mutable hash]
Object
(equals [_ other]
(if (instance? Fills other)
(and (buf/equals? dbuffer (.-dbuffer ^Fills other))
(buf/equals? mbuffer (.-mbuffer ^Fills other)))
false))
json/JSONWriter
(-write [this writter options]
(json/-write (vec this) writter options))
clojure.lang.IHashEq
(hasheq [this]
(when-not hash
(set! hash (clojure.lang.Murmur3/hashOrdered (seq this))))
hash)
clojure.lang.Sequential
clojure.lang.Seqable
(seq [_]
(when (pos? size)
((fn next-seq [i]
(when (< i size)
(cons (read-fill dbuffer mbuffer i)
(lazy-seq (next-seq (inc i))))))
0)))
clojure.lang.IReduceInit
(reduce [_ f start]
(loop [index 0
result start]
(if (< index size)
(let [result (f result (read-fill dbuffer mbuffer index))]
(if (reduced? result)
@result
(recur (inc index) result)))
result)))
clojure.lang.Indexed
(nth [_ i]
(if (d/in-range? size i)
(read-fill dbuffer mbuffer i)
nil))
(nth [_ i default]
(if (d/in-range? size i)
(read-fill dbuffer mbuffer i)
default))
clojure.lang.Counted
(count [_] size))
:cljs
#_:clj-kondo/ignore
(deftype Fills [size dbuffer mbuffer cache ^:mutable __hash]
cljs.core/ISequential
cljs.core/IEquiv
(-equiv [this other]
(if (instance? Fills other)
(and ^boolean (buf/equals? (.-dbuffer ^Fills other) dbuffer)
^boolean (buf/equals? (.-mbuffer ^Fills other) mbuffer))
false))
cljs.core/IEncodeJS
(-clj->js [this]
(clj->js (vec this)))
;; cljs.core/APersistentVector
cljs.core/IAssociative
(-assoc [coll k v]
(if (number? k)
(-> (vec coll)
(assoc k v)
(from-plain))
(throw (js/Error. "Vector's key for assoc must be a number."))))
(-contains-key? [coll k]
(if (integer? k)
(and (<= 0 k) (< k size))
false))
cljs.core/IReduce
(-reduce [_ f]
(loop [index 1
result (if (pos? size)
(read-fill dbuffer mbuffer 0)
nil)]
(if (< index size)
(let [result (f result (read-fill dbuffer mbuffer index))]
(if (reduced? result)
@result
(recur (inc index) result)))
result)))
(-reduce [_ f start]
(loop [index 0
result start]
(if (< index size)
(let [result (f result (read-fill dbuffer mbuffer index))]
(if (reduced? result)
@result
(recur (inc index) result)))
result)))
cljs.core/IHash
(-hash [coll]
(caching-hash coll hash-ordered-coll __hash))
cljs.core/ICounted
(-count [_] size)
cljs.core/IIndexed
(-nth [_ i]
(if (d/in-range? size i)
(read-fill dbuffer mbuffer i)
nil))
(-nth [_ i default]
(if (d/in-range? i size)
(read-fill dbuffer mbuffer i)
default))
cljs.core/ISeqable
(-seq [this]
(when (pos? size)
((fn next-seq [i]
(when (< i size)
(cons (read-fill dbuffer mbuffer i)
(lazy-seq (next-seq (inc i))))))
0)))))
(defn from-plain
[fills]
(let [fills (into [] xf:take-fills fills)
total (count fills)
dbuffer (buf/allocate (+ 4 (* MAX-FILLS FILL-BYTE-SIZE)))
mbuffer (buf/allocate (* total METADATA-BYTE-SIZE))]
(buf/write-byte dbuffer 0 total)
(loop [index 0]
(when (< index total)
(let [fill (nth fills index)
doffset (+ 4 (* index FILL-BYTE-SIZE))
moffset (* index METADATA-BYTE-SIZE)
opacity (get fill :fill-opacity 1)]
(if-let [color (get fill :fill-color)]
(do
(write-solid-fill doffset dbuffer color opacity)
(write-metadata moffset mbuffer fill)
(recur (inc index)))
(if-let [gradient (get fill :fill-color-gradient)]
(do
(write-gradient-fill doffset dbuffer gradient opacity)
(write-metadata moffset mbuffer fill)
(recur (inc index)))
(if-let [image (get fill :fill-image)]
(do
(write-image-fill doffset dbuffer opacity image)
(write-metadata moffset mbuffer fill)
(recur (inc index)))
(recur (inc index))))))))
#?(:cljs (Fills. total dbuffer mbuffer (weak-map/create) nil)
:clj (Fills. total dbuffer mbuffer nil))))
(defn fills?
[o]
(instance? Fills o))
(t/add-handlers!
{:id "penpot/fills"
:class Fills
:wfn (fn [^Fills fills]
(vec fills))
:rfn #?(:cljs from-plain
:clj identity)})

View File

@@ -8,7 +8,7 @@
(:require
[app.common.colors :as clr]
[app.common.schema :as sm]
[app.common.types.color :refer [schema:hex-color]]))
[app.common.types.color :as ctc]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SCHEMA
@@ -16,7 +16,7 @@
(def schema:grid-color
[:map {:title "PageGridColor"}
[:color schema:hex-color]
[:color ::ctc/rgb-color]
[:opacity ::sm/safe-number]])
(def schema:column-params

View File

@@ -10,7 +10,7 @@
[app.common.data :as d]
[app.common.geom.point :as-alias gpt]
[app.common.schema :as sm]
[app.common.types.color :as ctc]
[app.common.types.color :as-alias ctc]
[app.common.types.grid :as ctg]
[app.common.types.plugins :as ctpg]
[app.common.types.shape :as cts]
@@ -57,7 +57,7 @@
[:flows {:optional true} schema:flows]
[:guides {:optional true} schema:guides]
[:plugin-data {:optional true} ::ctpg/plugin-data]
[:background {:optional true} ctc/schema:hex-color]
[:background {:optional true} ::ctc/rgb-color]
[:comment-thread-positions {:optional true}
[:map-of ::sm/uuid schema:comment-thread-position]]])

View File

@@ -8,7 +8,6 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cpf]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
@@ -27,9 +26,6 @@
(def ^:const bool-style-properties bool/style-properties)
(def ^:const default-bool-fills bool/default-fills)
(def schema:content impl/schema:content)
(def schema:segments impl/schema:segments)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRANSFORMATIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -52,9 +48,9 @@
[data]
(impl/from-string data))
(defn check-content
(defn check-path-content
[content]
(impl/check-content content))
(impl/check-content-like content))
(defn get-byte-size
"Get byte size of a path content"
@@ -80,7 +76,7 @@
(defn apply-content-modifiers
"Apply delta modifiers over the path content"
[content modifiers]
(assert (impl/check-content content))
(assert (impl/check-content-like content))
(letfn [(apply-to-index [content [index params]]
(if (contains? content index)
@@ -200,34 +196,14 @@
contents
(sequence extract-content-xf (:shapes shape))]
(ex/try!
(bool/calculate-content (:bool-type shape) contents)
:on-exception
(fn [cause]
(ex/raise :type :internal
:code :invalid-path-content
:hint (str "unable to calculate bool content for shape " (:id shape))
:shapes (:shapes shape)
:content (mapv str contents)
:cause cause)))))
(bool/calculate-content (:bool-type shape) contents)))
(defn calc-bool-content
"Calculate the boolean content from shape and objects. Returns a
packed PathData instance"
[shape objects]
(ex/try!
(-> (calc-bool-content* shape objects)
(impl/path-data))
:on-exception
(fn [cause]
(ex/raise :type :internal
:code :invalid-path-content
:hint (str "unable to create bool content for shape " (:id shape))
:content (str (:content shape))
:shape-id (:id shape)
:cause cause))))
(-> (calc-bool-content* shape objects)
(impl/path-data)))
(defn update-bool-shape
"Calculates the selrect+points for the boolean shape"

View File

@@ -412,7 +412,7 @@
(defn calculate-content
"Create a bool content from a collection of contents and specified
type. Returns plain segments"
type."
[bool-type contents]
;; We apply the boolean operation in to each pair and the result to the next
;; element

View File

@@ -98,7 +98,7 @@
(defn segment->point
([segment] (segment->point segment :x))
([segment coord]
(when-let [params (get segment :params)]
(let [params (get segment :params)]
(case coord
:c1 (gpt/point (get params :c1x)
(get params :c1y))

View File

@@ -7,14 +7,13 @@
(ns app.common.types.path.impl
"Contains schemas and data type implementation for PathData binary
and plain formats"
#?(:cljs
(:require-macros [app.common.types.path.impl :refer [read-float read-short write-float write-short]]))
(:refer-clojure :exclude [-lookup -reduce])
#?(:cljs (:require-macros [app.common.types.path.impl]))
(:require
#?(:clj [app.common.fressian :as fres])
#?(:clj [clojure.data.json :as json])
#?(:cljs [app.common.weak-map :as weak-map])
[app.common.buffer :as buf]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
@@ -43,56 +42,107 @@
;; IMPL HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro read-short
[target offset]
(if (:ns &env)
`(.getInt16 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.getShort ~target ~offset))))
(defmacro read-float
[target offset]
(if (:ns &env)
`(.getFloat32 ~target ~offset true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(double (.getFloat ~target ~offset)))))
(defmacro write-float
[target offset value]
(if (:ns &env)
`(.setFloat32 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putFloat ~target ~offset ~value))))
(defmacro write-short
[target offset value]
(if (:ns &env)
`(.setInt16 ~target ~offset ~value true)
(let [target (with-meta target {:tag 'java.nio.ByteBuffer})]
`(.putShort ~target ~offset ~value))))
(defmacro with-cache
"A helper macro that facilitates cache handling for content
instance, only relevant on CLJS"
[target key & expr]
(if (:ns &env)
(let [target (with-meta target {:tag 'js})]
`(let [~'cache (.-cache ~target)
~'result (.get ~'cache ~key)]
(let [cache (gensym "cache-")
target (with-meta target {:tag 'js})]
`(let [~cache (.-cache ~target)
~'result (.get ~cache ~key)]
(if ~'result
(do
~'result)
(let [~'result (do ~@expr)]
(.set ~'cache ~key ~'result)
(.set ~cache ~key ~'result)
~'result))))
`(do ~@expr)))
(defn- allocate
[n-segments]
#?(:clj (let [buffer (ByteBuffer/allocate (* n-segments SEGMENT-BYTE-SIZE))]
(.order buffer ByteOrder/LITTLE_ENDIAN))
:cljs (new js/ArrayBuffer (* n-segments SEGMENT-BYTE-SIZE))))
(defn- clone-buffer
[buffer]
#?(:clj
(let [src (.array ^ByteBuffer buffer)
len (alength ^bytes src)
dst (byte-array len)]
(System/arraycopy src 0 dst 0 len)
(let [buffer (ByteBuffer/wrap dst)]
(.order buffer ByteOrder/LITTLE_ENDIAN)))
:cljs
(let [src-view (js/Uint32Array. buffer)
dst-buff (js/ArrayBuffer. (.-byteLength buffer))
dst-view (js/Uint32Array. dst-buff)]
(.set dst-view src-view)
dst-buff)))
(defn- impl-transform-segment
"Apply a transformation to a segment located under specified offset"
[buffer offset a b c d e f]
(let [t (buf/read-short buffer offset)]
(let [t (read-short buffer offset)]
(case t
(1 2)
(let [x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))
x' (+ (* x a) (* y c) e)
y' (+ (* x b) (* y d) f)]
(buf/write-float buffer (+ offset 20) x')
(buf/write-float buffer (+ offset 24) y'))
(let [x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))
x (+ (* x a) (* y c) e)
y (+ (* x b) (* y d) f)]
(write-float buffer (+ offset 20) x)
(write-float buffer (+ offset 24) y))
3
(let [c1x (buf/read-float buffer (+ offset 4))
c1y (buf/read-float buffer (+ offset 8))
c2x (buf/read-float buffer (+ offset 12))
c2y (buf/read-float buffer (+ offset 16))
x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))
(let [c1x (read-float buffer (+ offset 4))
c1y (read-float buffer (+ offset 8))
c2x (read-float buffer (+ offset 12))
c2y (read-float buffer (+ offset 16))
x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))
c1x' (+ (* c1x a) (* c1y c) e)
c1y' (+ (* c1x b) (* c1y d) f)
c2x' (+ (* c2x a) (* c2y c) e)
c2y' (+ (* c2x b) (* c2y d) f)
x' (+ (* x a) (* y c) e)
y' (+ (* x b) (* y d) f)]
c1x (+ (* c1x a) (* c1y c) e)
c1y (+ (* c1x b) (* c1y d) f)
c2x (+ (* c2x a) (* c2y c) e)
c2y (+ (* c2x b) (* c2y d) f)
x (+ (* x a) (* y c) e)
y (+ (* x b) (* y d) f)]
(buf/write-float buffer (+ offset 4) c1x')
(buf/write-float buffer (+ offset 8) c1y')
(buf/write-float buffer (+ offset 12) c2x')
(buf/write-float buffer (+ offset 16) c2y')
(buf/write-float buffer (+ offset 20) x')
(buf/write-float buffer (+ offset 24) y'))
(write-float buffer (+ offset 4) c1x)
(write-float buffer (+ offset 8) c1y)
(write-float buffer (+ offset 12) c2x)
(write-float buffer (+ offset 16) c2y)
(write-float buffer (+ offset 20) x)
(write-float buffer (+ offset 24) y))
nil)))
@@ -116,13 +166,13 @@
result (transient initial)]
(if (< index size)
(let [offset (* index SEGMENT-BYTE-SIZE)
type (buf/read-short buffer offset)
c1x (buf/read-float buffer (+ offset 4))
c1y (buf/read-float buffer (+ offset 8))
c2x (buf/read-float buffer (+ offset 12))
c2y (buf/read-float buffer (+ offset 16))
x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))
type (read-short buffer offset)
c1x (read-float buffer (+ offset 4))
c1y (read-float buffer (+ offset 8))
c2x (read-float buffer (+ offset 12))
c2y (read-float buffer (+ offset 16))
x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))
type (case type
1 :line-to
2 :move-to
@@ -141,13 +191,13 @@
result initial]
(if (< index size)
(let [offset (* index SEGMENT-BYTE-SIZE)
type (buf/read-short buffer offset)
c1x (buf/read-float buffer (+ offset 4))
c1y (buf/read-float buffer (+ offset 8))
c2x (buf/read-float buffer (+ offset 12))
c2y (buf/read-float buffer (+ offset 16))
x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))
type (read-short buffer offset)
c1x (read-float buffer (+ offset 4))
c1y (read-float buffer (+ offset 8))
c2x (read-float buffer (+ offset 12))
c2y (read-float buffer (+ offset 16))
x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))
type (case type
1 :line-to
2 :move-to
@@ -162,13 +212,13 @@
(defn impl-lookup
[buffer index f]
(let [offset (* index SEGMENT-BYTE-SIZE)
type (buf/read-short buffer offset)
c1x (buf/read-float buffer (+ offset 4))
c1y (buf/read-float buffer (+ offset 8))
c2x (buf/read-float buffer (+ offset 12))
c2y (buf/read-float buffer (+ offset 16))
x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))
type (read-short buffer offset)
c1x (read-float buffer (+ offset 4))
c1y (read-float buffer (+ offset 8))
c2x (read-float buffer (+ offset 12))
c2y (read-float buffer (+ offset 16))
x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))
type (case type
1 :line-to
2 :move-to
@@ -180,27 +230,27 @@
(defn- to-string-segment*
[buffer offset type ^StringBuilder builder]
(case (long type)
1 (let [x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))]
1 (let [x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))]
(doto builder
(.append "M")
(.append x)
(.append ",")
(.append y)))
2 (let [x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))]
2 (let [x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))]
(doto builder
(.append "L")
(.append x)
(.append ",")
(.append y)))
3 (let [c1x (buf/read-float buffer (+ offset 4))
c1y (buf/read-float buffer (+ offset 8))
c2x (buf/read-float buffer (+ offset 12))
c2y (buf/read-float buffer (+ offset 16))
x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))]
3 (let [c1x (read-float buffer (+ offset 4))
c1y (read-float buffer (+ offset 8))
c2x (read-float buffer (+ offset 12))
c2y (read-float buffer (+ offset 16))
x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))]
(doto builder
(.append "C")
(.append c1x)
@@ -225,7 +275,7 @@
(loop [index 0]
(when (< index size)
(let [offset (* index SEGMENT-BYTE-SIZE)
type (buf/read-short buffer offset)]
type (read-short buffer offset)]
(to-string-segment* buffer offset type builder)
(recur (inc index)))))
@@ -235,26 +285,26 @@
"Read segment from binary buffer at specified index"
[buffer index]
(let [offset (* index SEGMENT-BYTE-SIZE)
type (buf/read-short buffer offset)]
type (read-short buffer offset)]
(case (long type)
1 (let [x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))]
1 (let [x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))]
{:command :move-to
:params {:x (double x)
:y (double y)}})
2 (let [x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))]
2 (let [x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))]
{:command :line-to
:params {:x (double x)
:y (double y)}})
3 (let [c1x (buf/read-float buffer (+ offset 4))
c1y (buf/read-float buffer (+ offset 8))
c2x (buf/read-float buffer (+ offset 12))
c2y (buf/read-float buffer (+ offset 16))
x (buf/read-float buffer (+ offset 20))
y (buf/read-float buffer (+ offset 24))]
3 (let [c1x (read-float buffer (+ offset 4))
c1y (read-float buffer (+ offset 8))
c2x (read-float buffer (+ offset 12))
c2y (read-float buffer (+ offset 16))
x (read-float buffer (+ offset 20))
y (read-float buffer (+ offset 24))]
{:command :curve-to
:params {:x (double x)
:y (double y)
@@ -266,6 +316,10 @@
4 {:command :close-path
:params {}})))
(defn- in-range?
[size i]
(and (< i size) (>= i 0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TYPE: PATH-DATA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -280,12 +334,12 @@
(equals [_ other]
(if (instance? PathData other)
(buf/equals? buffer (.-buffer ^PathData other))
(.equals ^ByteBuffer buffer (.-buffer ^PathData other))
false))
ITransformable
(-transform [_ m]
(let [buffer (buf/clone buffer)]
(let [buffer (clone-buffer buffer)]
(impl-transform buffer m size)
(PathData. size buffer nil)))
@@ -307,7 +361,7 @@
clojure.lang.IHashEq
(hasheq [this]
(when-not hash
(set! hash (clojure.lang.Murmur3/hashOrdered (vec this))))
(set! hash (clojure.lang.Murmur3/hashOrdered (seq this))))
hash)
clojure.lang.Sequential
@@ -333,12 +387,12 @@
clojure.lang.Indexed
(nth [_ i]
(if (d/in-range? size i)
(if (in-range? size i)
(read-segment buffer i)
nil))
(nth [_ i default]
(if (d/in-range? size i)
(if (in-range? size i)
(read-segment buffer i)
default))
@@ -354,10 +408,10 @@
:cljs
#_:clj-kondo/ignore
(deftype PathData [size buffer cache ^:mutable __hash]
(deftype PathData [size buffer dview cache ^:mutable __hash]
Object
(toString [_]
(to-string buffer size))
(to-string dview size))
IPathData
(-get-byte-size [_]
@@ -367,43 +421,56 @@
;; NOTE: we still use u8 because until the heap refactor merge
;; we can't guarrantee the alignment of offset on 4 bytes
(assert (instance? js/ArrayBuffer into-buffer))
(let [buffer' (.-buffer ^js/DataView buffer)
size (.-byteLength buffer')
mem (js/Uint8Array. into-buffer offset size)]
(.set mem (js/Uint8Array. buffer'))))
(let [size (.-byteLength buffer)
mem (js/Uint8Array. into-buffer offset size)]
(.set mem (js/Uint8Array. buffer))))
ITransformable
(-transform [this m]
(let [buffer (buf/clone buffer)]
(impl-transform buffer m size)
(PathData. size buffer (weak-map/create) nil)))
(let [buffer (clone-buffer buffer)
dview (js/DataView. buffer)]
(impl-transform dview m size)
(PathData. size buffer dview (weak-map/create) nil)))
(-walk [_ f initial]
(impl-walk buffer f initial size))
(impl-walk dview f initial size))
(-reduce [_ f initial]
(impl-reduce buffer f initial size))
(impl-reduce dview f initial size))
(-lookup [_ index f]
(when (and (<= 0 index)
(< index size))
(impl-lookup buffer index f)))
(impl-lookup dview index f)))
cljs.core/ISequential
cljs.core/IEquiv
(-equiv [this other]
(if (instance? PathData other)
(buf/equals? buffer (.-buffer other))
(let [obuffer (.-buffer other)]
(if (= (.-byteLength obuffer)
(.-byteLength buffer))
(let [cb (js/Uint32Array. buffer)
ob (js/Uint32Array. obuffer)
sz (alength cb)]
(loop [i 0]
(if (< i sz)
(if (= (aget ob i)
(aget cb i))
(recur (inc i))
false)
true)))
false))
false))
cljs.core/IReduce
(-reduce [_ f]
(loop [index 1
result (if (pos? size)
(read-segment buffer 0)
(read-segment dview 0)
nil)]
(if (< index size)
(let [result (f result (read-segment buffer index))]
(let [result (f result (read-segment dview index))]
(if (reduced? result)
@result
(recur (inc index) result)))
@@ -413,7 +480,7 @@
(loop [index 0
result start]
(if (< index size)
(let [result (f result (read-segment buffer index))]
(let [result (f result (read-segment dview index))]
(if (reduced? result)
@result
(recur (inc index) result)))
@@ -428,13 +495,13 @@
cljs.core/IIndexed
(-nth [_ i]
(if (d/in-range? size i)
(read-segment buffer i)
(if (in-range? size i)
(read-segment dview i)
nil))
(-nth [_ i default]
(if (d/in-range? i size)
(read-segment buffer i)
(if (in-range? i size)
(read-segment dview i)
default))
cljs.core/ISeqable
@@ -442,7 +509,7 @@
(when (pos? size)
((fn next-seq [i]
(when (< i size)
(cons (read-segment buffer i)
(cons (read-segment dview i)
(lazy-seq (next-seq (inc i))))))
0)))
@@ -507,6 +574,18 @@
(= (:command e1) :move-to))))}
schema:segment])
(def schema:content-like
[:sequential schema:segment])
(def check-content-like
(sm/check-fn schema:content-like))
(def check-segment
(sm/check-fn schema:segment))
(def ^:private check-segments
(sm/check-fn schema:segments))
(defn path-data?
[o]
(instance? PathData o))
@@ -514,40 +593,37 @@
(declare from-string)
(declare from-plain)
(def schema:content
(sm/type-schema
{:type ::path/content
:compile
(fn [_ _ _]
(let [decoder (delay (sm/decoder schema:segments sm/json-transformer))
generator (->> (sg/generator schema:segments)
(sg/filter not-empty)
(sg/fmap from-plain))]
{:pred path-data?
:type-properties
{:gen/gen generator
:encode/json identity
:decode/json (fn [s]
(cond
(string? s)
(from-string s)
;; Mainly used on backend: features/components_v2.clj
(sm/register! ::path/segment schema:segment)
(sm/register! ::path/segments schema:segments)
(vector? s)
(let [decode-fn (deref decoder)]
(-> (decode-fn s)
(from-plain)))
(sm/register!
{:type ::path/content
:compile
(fn [_ _ _]
(let [decoder (delay (sm/decoder schema:segments sm/json-transformer))
generator (->> (sg/generator schema:segments)
(sg/filter not-empty)
(sg/fmap from-plain))]
{:pred path-data?
:type-properties
{:gen/gen generator
:encode/json identity
:decode/json (fn [s]
(cond
(string? s)
(from-string s)
:else
s))}}))}))
(vector? s)
(let [decode-fn (deref decoder)]
(-> (decode-fn s)
(from-plain)))
(def check-plain-content
(sm/check-fn schema:segments))
:else
s))}}))})
(def check-segment
(sm/check-fn schema:segment))
(def check-content
(sm/check-fn schema:content))
(def check-path-content
(sm/check-fn ::path/content))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONSTRUCTORS & PREDICATES
@@ -583,15 +659,17 @@
(let [size (.-byteLength buffer)
count (long (/ size SEGMENT-BYTE-SIZE))]
(PathData. count
buffer
(js/DataView. buffer)
(weak-map/create)
nil))
(instance? js/DataView buffer)
(let [buffer' (.-buffer ^js/DataView buffer)
size (.-byteLength ^js/ArrayBuffer buffer')
count (long (/ size SEGMENT-BYTE-SIZE))]
(PathData. count buffer (weak-map/create) nil))
(let [dview buffer
buffer (.-buffer dview)
size (.-byteLength buffer)
count (long (/ size SEGMENT-BYTE-SIZE))]
(PathData. count buffer dview (weak-map/create) nil))
(instance? js/Uint8Array buffer)
(from-bytes (.-buffer buffer))
@@ -608,10 +686,12 @@
(defn from-plain
"Create a PathData instance from plain data structures"
[segments]
(assert (check-plain-content segments))
(assert (check-segments segments))
(let [total (count segments)
buffer (buf/allocate (* total SEGMENT-BYTE-SIZE))]
(let [total (count segments)
#?@(:cljs [buffer' (allocate total)
buffer (new js/DataView buffer')]
:clj [buffer (allocate total)])]
(loop [index 0]
(when (< index total)
(let [segment (nth segments index)
@@ -621,18 +701,18 @@
(let [params (get segment :params)
x (float (get params :x))
y (float (get params :y))]
(buf/write-short buffer offset 1)
(buf/write-float buffer (+ offset 20) x)
(buf/write-float buffer (+ offset 24) y))
(write-short buffer offset 1)
(write-float buffer (+ offset 20) x)
(write-float buffer (+ offset 24) y))
:line-to
(let [params (get segment :params)
x (float (get params :x))
y (float (get params :y))]
(buf/write-short buffer offset 2)
(buf/write-float buffer (+ offset 20) x)
(buf/write-float buffer (+ offset 24) y))
(write-short buffer offset 2)
(write-float buffer (+ offset 20) x)
(write-float buffer (+ offset 24) y))
:curve-to
(let [params (get segment :params)
@@ -643,16 +723,16 @@
c2x (float (get params :c2x x))
c2y (float (get params :c2y y))]
(buf/write-short buffer offset 3)
(buf/write-float buffer (+ offset 4) c1x)
(buf/write-float buffer (+ offset 8) c1y)
(buf/write-float buffer (+ offset 12) c2x)
(buf/write-float buffer (+ offset 16) c2y)
(buf/write-float buffer (+ offset 20) x)
(buf/write-float buffer (+ offset 24) y))
(write-short buffer offset 3)
(write-float buffer (+ offset 4) c1x)
(write-float buffer (+ offset 8) c1y)
(write-float buffer (+ offset 12) c2x)
(write-float buffer (+ offset 16) c2y)
(write-float buffer (+ offset 20) x)
(write-float buffer (+ offset 24) y))
:close-path
(buf/write-short buffer offset 4))
(write-short buffer offset 4))
(recur (inc index)))))
(from-bytes buffer)))
@@ -683,7 +763,7 @@
:class PathData
:wfn (fn [^PathData pdata]
(let [buffer (.-buffer pdata)]
#?(:cljs (js/Uint8Array. (.-buffer ^js/DataView buffer))
#?(:cljs (js/Uint8Array. buffer)
:clj (.array ^ByteBuffer buffer))))
:rfn from-bytes})

View File

@@ -363,7 +363,7 @@
(defn make-curve-point
"Changes the content to make the point a 'curve'. The handlers will be
positioned in the same vector that results from the previous->next
points but with fixed length; return a plain segments vector"
points but with fixed length."
[content point]
(let [;; We perform this operation before because it can be
@@ -372,21 +372,17 @@
indices
(point-indices content point)
;; We transform content to a plain format for execute the
;; algorithm because right now is the only way to execute it
content
(vec content)
vectors
(map (fn [index]
(let [segment (get content index)
prev-i (dec index)
prev (when (not (= :move-to (:command segment)))
(get content prev-i))
next-i (inc index)
next (get content next-i)
next (when (not (= :move-to (:command next)))
next)]
(let [segment (nth content index)
prev-i (dec index)
prev (when (not (= :move-to (:command segment)))
(get content prev-i))
next-i (inc index)
next (get content next-i)
next (when (not (= :move-to (:command next)))
next)]
{:index index
:prev-i (when (some? prev) prev-i)
:prev-c prev
@@ -398,73 +394,81 @@
indices)
points
(into #{} xf:mapcat-points vectors)]
(into #{} xf:mapcat-points vectors)
(if (= (count points) 2)
(let [[fpoint spoint] (vec points)
v1 (gpt/to-vec fpoint point)
v2 (gpt/to-vec fpoint spoint)
vp (gpt/project v1 v2)
vh (gpt/subtract v1 vp)
;; We transform content to a plain format for execute the
;; algorithm because right now is the only way to execute it
content
(vec content)
add-curve
(fn [content {:keys [index prev-p next-p next-i]}]
(let [curr-segment (get content index)
curr-command (get curr-segment :command)
content
(if (= (count points) 2)
(let [[fpoint spoint] (vec points)
v1 (gpt/to-vec fpoint point)
v2 (gpt/to-vec fpoint spoint)
vp (gpt/project v1 v2)
vh (gpt/subtract v1 vp)
next-segment (get content next-i)
next-command (get next-segment :command)
add-curve
(fn [content {:keys [index prev-p next-p next-i]}]
(let [curr-segment (get content index)
curr-command (get curr-segment :command)
;; New handlers for prev-point and next-point
prev-h
(when (some? prev-p) (gpt/add prev-p vh))
next-segment (get content next-i)
next-command (get next-segment :command)
next-h
(when (some? next-p) (gpt/add next-p vh))
;; New handlers for prev-point and next-point
prev-h
(when (some? prev-p) (gpt/add prev-p vh))
;; Correct 1/3 to the point improves the curve
prev-correction
(when (some? prev-h) (gpt/scale (gpt/to-vec prev-h point) (/ 1 3)))
next-h
(when (some? next-p) (gpt/add next-p vh))
next-correction
(when (some? next-h) (gpt/scale (gpt/to-vec next-h point) (/ 1 3)))
;; Correct 1/3 to the point improves the curve
prev-correction
(when (some? prev-h) (gpt/scale (gpt/to-vec prev-h point) (/ 1 3)))
prev-h
(when (some? prev-h) (gpt/add prev-h prev-correction))
next-correction
(when (some? next-h) (gpt/scale (gpt/to-vec next-h point) (/ 1 3)))
next-h
(when (some? next-h) (gpt/add next-h next-correction))]
prev-h
(when (some? prev-h) (gpt/add prev-h prev-correction))
(cond-> content
(and (= :line-to curr-command) (some? prev-p))
(update index helpers/update-curve-to prev-p prev-h)
next-h
(when (some? next-h) (gpt/add next-h next-correction))]
(and (= :line-to next-command) (some? next-p))
(update next-i helpers/update-curve-to next-h next-p)
(cond-> content
(and (= :line-to curr-command) (some? prev-p))
(update index helpers/update-curve-to prev-p prev-h)
(and (= :curve-to curr-command) (some? prev-p))
(update index update-handler :c2 prev-h)
(and (= :line-to next-command) (some? next-p))
(update next-i helpers/update-curve-to next-h next-p)
(and (= :curve-to next-command) (some? next-p))
(update next-i update-handler :c1 next-h))))]
(and (= :curve-to curr-command) (some? prev-p))
(update index update-handler :c2 prev-h)
(reduce add-curve content vectors))
(and (= :curve-to next-command) (some? next-p))
(update next-i update-handler :c1 next-h))))]
(let [add-curve
(fn [content {:keys [index segment prev-p next-c next-i]}]
(cond-> content
(= :line-to (:command segment))
(update index #(line->curve prev-p %))
(reduce add-curve content vectors))
(= :curve-to (:command segment))
(update index #(line->curve prev-p %))
(let [add-curve
(fn [content {:keys [index segment prev-p next-c next-i]}]
(cond-> content
(= :line-to (:command segment))
(update index #(line->curve prev-p %))
(= :line-to (:command next-c))
(update next-i #(line->curve point %))
(= :curve-to (:command segment))
(update index #(line->curve prev-p %))
(= :curve-to (:command next-c))
(update next-i #(line->curve point %))))]
(reduce add-curve content vectors)))))
(= :line-to (:command next-c))
(update next-i #(line->curve point %))
(= :curve-to (:command next-c))
(update next-i #(line->curve point %))))]
(reduce add-curve content vectors)))]
(impl/from-plain content)))
(defn get-segments-with-points
"Given a content and a set of points return all the segments in the path
@@ -624,38 +628,27 @@
(rest content))))))))
(defn join-nodes
"Creates new segments between points that weren't previously.
Returns plain segments vector."
"Creates new segments between points that weren't previously"
[content points]
(let [;; Materialize the content to a vector (plain format)
content
(vec content)
(let [segments-set (into #{}
(map (juxt :start :end))
(get-segments-with-points content points))
segments-set
(into #{}
(map (juxt :start :end))
(get-segments-with-points content points))
create-line-command (fn [point other]
[(helpers/make-move-to point)
(helpers/make-line-to other)])
create-line-segment
(fn [point other]
[(helpers/make-move-to point)
(helpers/make-line-to other)])
not-segment? (fn [point other] (and (not (contains? segments-set [point other]))
(not (contains? segments-set [other point]))))
not-segment?
(fn [point other]
(and (not (contains? segments-set [point other]))
(not (contains? segments-set [other point]))))
;; FIXME: implement map-perm in terms of transducer, will
;; improve performance and remove the need to use flatten
new-content
(->> (d/map-perm create-line-segment not-segment? points)
(flatten)
(into []))]
new-content (->> (d/map-perm create-line-command not-segment? points)
(flatten)
(into []))]
(into content new-content)))
(defn separate-nodes
"Removes the segments between the points given"
[content points]

View File

@@ -20,8 +20,7 @@
[app.common.schema.generators :as sg]
[app.common.text :as txt]
[app.common.transit :as t]
[app.common.types.color :as types.color]
[app.common.types.fill :refer [schema:fill]]
[app.common.types.color :as ctc]
[app.common.types.grid :as ctg]
[app.common.types.path :as path]
[app.common.types.path.segment :as path.segment]
@@ -120,47 +119,36 @@
(def schema:points
[:vector {:gen/max 4 :gen/min 4} ::gpt/point])
;; FIXME: the register is necessary until this is moved to a separated
;; ns because it is used on shapes.text
(def valid-stroke-attrs
"A set used for proper check if color should contain only one of the
attrs listed in this set."
#{:stroke-image :stroke-color :stroke-color-gradient})
(defn has-valid-stroke-attrs?
"Check if color has correct color attrs"
[color]
(let [attrs (set (keys color))
result (set/intersection attrs valid-stroke-attrs)]
(= 1 (count result))))
(def schema:stroke-attrs
[:map {:title "StrokeAttrs" :closed true}
[:stroke-color-ref-file {:optional true} ::sm/uuid]
[:stroke-color-ref-id {:optional true} ::sm/uuid]
[:stroke-opacity {:optional true} ::sm/safe-number]
[:stroke-style {:optional true}
[::sm/one-of #{:solid :dotted :dashed :mixed}]]
[:stroke-width {:optional true} ::sm/safe-number]
[:stroke-alignment {:optional true}
[::sm/one-of #{:center :inner :outer}]]
[:stroke-cap-start {:optional true}
[::sm/one-of stroke-caps]]
[:stroke-cap-end {:optional true}
[::sm/one-of stroke-caps]]
[:stroke-color {:optional true} types.color/schema:hex-color]
[:stroke-color-gradient {:optional true} types.color/schema:gradient]
[:stroke-image {:optional true} types.color/schema:image]])
(def stroke-attrs
"A set of attrs that corresponds to stroke data type"
(sm/keys schema:stroke-attrs))
(def schema:fill
(sm/register!
^{::sm/type ::fill}
[:map {:title "Fill"}
[:fill-color {:optional true} ::ctc/rgb-color]
[:fill-opacity {:optional true} ::sm/safe-number]
[:fill-color-gradient {:optional true} [:maybe ::ctc/gradient]]
[:fill-color-ref-file {:optional true} [:maybe ::sm/uuid]]
[:fill-color-ref-id {:optional true} [:maybe ::sm/uuid]]
[:fill-image {:optional true} ::ctc/image-color]]))
(def schema:stroke
(sm/register!
^{::sm/type ::stroke}
[:and schema:stroke-attrs
[:fn has-valid-stroke-attrs?]]))
[:map {:title "Stroke"}
[:stroke-color {:optional true} :string]
[:stroke-color-ref-file {:optional true} ::sm/uuid]
[:stroke-color-ref-id {:optional true} ::sm/uuid]
[:stroke-opacity {:optional true} ::sm/safe-number]
[:stroke-style {:optional true}
[::sm/one-of #{:solid :dotted :dashed :mixed :none :svg}]]
[:stroke-width {:optional true} ::sm/safe-number]
[:stroke-alignment {:optional true}
[::sm/one-of #{:center :inner :outer}]]
[:stroke-cap-start {:optional true}
[::sm/one-of stroke-caps]]
[:stroke-cap-end {:optional true}
[::sm/one-of stroke-caps]]
[:stroke-color-gradient {:optional true} ::ctc/gradient]
[:stroke-image {:optional true} ::ctc/image-color]]))
(def check-stroke
(sm/check-fn schema:stroke))
@@ -228,7 +216,7 @@
[:blur {:optional true} ::ctsb/blur]
[:grow-type {:optional true}
[::sm/one-of grow-types]]
[:applied-tokens {:optional true} cto/schema:applied-tokens]
[:applied-tokens {:optional true} ::cto/applied-tokens]
[:plugin-data {:optional true} ::ctpg/plugin-data]])
(def schema:group-attrs
@@ -246,7 +234,7 @@
[:map {:title "BoolAttrs"}
[:shapes [:vector {:gen/max 10 :gen/min 1} ::sm/uuid]]
[:bool-type [::sm/one-of bool-types]]
[:content path/schema:content]])
[:content ::path/content]])
(def ^:private schema:rect-attrs
[:map {:title "RectAttrs"}])
@@ -271,7 +259,7 @@
(def ^:private schema:path-attrs
[:map {:title "PathAttrs"}
[:content path/schema:content]])
[:content ::path/content]])
(def ^:private schema:text-attrs
[:map {:title "TextAttrs"}
@@ -313,7 +301,7 @@
:title "Shape"}
[:group
[:merge {:title "GroupShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:group-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -321,8 +309,8 @@
[:frame
[:merge {:title "FrameShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
::ctsl/layout-attrs
schema:frame-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -332,14 +320,14 @@
[:bool
[:merge {:title "BoolShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:bool-attrs
schema:shape-generic-attrs
schema:shape-base-attrs]]
[:rect
[:merge {:title "RectShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:rect-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -347,7 +335,7 @@
[:circle
[:merge {:title "CircleShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:circle-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -355,7 +343,7 @@
[:image
[:merge {:title "ImageShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:image-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -363,7 +351,7 @@
[:svg-raw
[:merge {:title "SvgRawShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:svg-raw-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -371,14 +359,14 @@
[:path
[:merge {:title "PathShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:path-attrs
schema:shape-generic-attrs
schema:shape-base-attrs]]
[:text
[:merge {:title "TextShape"}
ctsl/schema:layout-child-attrs
ctsl/schema:layout-attrs
schema:text-attrs
schema:shape-generic-attrs
schema:shape-geom-attrs
@@ -682,6 +670,23 @@
:r3
:r4})
(def ^:private layout-extract-props
#{:layout
:layout-flex-dir
:layout-gap-type
:layout-gap
:layout-wrap-type
:layout-align-items
:layout-align-content
:layout-justify-items
:layout-justify-content
:layout-padding-type
:layout-padding
:layout-grid-dir
:layout-grid-rows
:layout-grid-columns
:layout-grid-cells})
(defn extract-props
"Retrieves an object with the 'pasteable' properties for a shape."
[shape]
@@ -712,8 +717,9 @@
(assoc-props node txt/text-node-attrs)))
props)))
(extract-layout-attrs [props shape]
(d/patch-object props (select-keys shape ctsl/layout-attrs)))]
(extract-layout-props
[props shape]
(d/patch-object props (select-keys shape layout-extract-props)))]
(let [;; For texts we don't extract the fill
extract-props
@@ -721,7 +727,7 @@
(-> shape
(select-keys extract-props)
(cond-> (cfh/text-shape? shape) (extract-text-props shape))
(cond-> (ctsl/any-layout? shape) (extract-layout-attrs shape))))))
(cond-> (ctsl/any-layout? shape) (extract-layout-props shape))))))
(defn patch-props
"Given the object of `extract-props` applies it to a shape. Adapt the shape if necesary"
@@ -746,7 +752,7 @@
(d/patch-object (select-keys props txt/text-node-attrs))))))))))
(patch-layout-props [shape props]
(let [shape (d/patch-object shape (select-keys props ctsl/layout-attrs))]
(let [shape (d/patch-object shape (select-keys props layout-extract-props))]
(cond-> shape
(ctsl/grid-layout? shape)
(ctsl/assign-cells objects))))]
@@ -755,3 +761,8 @@
(d/patch-object (select-keys props basic-extract-props))
(cond-> (cfh/text-shape? shape) (patch-text-props props))
(cond-> (cfh/frame-shape? shape) (patch-layout-props props)))))
;; FIXME: Get these from the wasm module, and tweak the values
;; (we'd probably want 12 stops at most)
(def MAX-GRADIENT-STOPS 16)
(def MAX-FILLS 8)

View File

@@ -43,6 +43,7 @@
;; :layout-item-absolute ;; boolean
;; :layout-item-z-index ;; int
(def layout-types
#{:flex :grid})
@@ -73,6 +74,49 @@
(def justify-items-types
#{:start :end :center :stretch})
(def layout-item-props
[:layout-item-margin
:layout-item-margin-type
:layout-item-h-sizing
:layout-item-v-sizing
:layout-item-max-h
:layout-item-min-h
:layout-item-max-w
:layout-item-min-w
:layout-item-absolute
:layout-item-z-index])
(sm/register!
^{::sm/type ::layout-attrs}
[:map {:title "LayoutAttrs"}
[:layout {:optional true} [::sm/one-of layout-types]]
[:layout-flex-dir {:optional true} [::sm/one-of flex-direction-types]]
[:layout-gap {:optional true}
[:map
[:row-gap {:optional true} ::sm/safe-number]
[:column-gap {:optional true} ::sm/safe-number]]]
[:layout-gap-type {:optional true} [::sm/one-of gap-types]]
[:layout-wrap-type {:optional true} [::sm/one-of wrap-types]]
[:layout-padding-type {:optional true} [::sm/one-of padding-type]]
[:layout-padding {:optional true}
[:map
[:p1 ::sm/safe-number]
[:p2 ::sm/safe-number]
[:p3 ::sm/safe-number]
[:p4 ::sm/safe-number]]]
[:layout-justify-content {:optional true} [::sm/one-of justify-content-types]]
[:layout-justify-items {:optional true} [::sm/one-of justify-items-types]]
[:layout-align-content {:optional true} [::sm/one-of align-content-types]]
[:layout-align-items {:optional true} [::sm/one-of align-items-types]]
[:layout-grid-dir {:optional true} [::sm/one-of grid-direction-types]]
[:layout-grid-rows {:optional true}
[:vector {:gen/max 2} ::grid-track]]
[:layout-grid-columns {:optional true}
[:vector {:gen/max 2} ::grid-track]]
[:layout-grid-cells {:optional true}
[:map-of {:gen/max 5} ::sm/uuid ::grid-cell]]])
;; Grid types
(def grid-track-types
#{:percent :flex :auto :fixed})
@@ -86,59 +130,29 @@
(def grid-cell-justify-self-types
#{:auto :start :center :end :stretch})
(def ^:private schema:grid-cell
[:map {:title "GridCell"}
[:id ::sm/uuid]
[:area-name {:optional true} :string]
[:row ::sm/safe-int]
[:row-span ::sm/safe-int]
[:column ::sm/safe-int]
[:column-span ::sm/safe-int]
[:position {:optional true} [::sm/one-of grid-position-types]]
[:align-self {:optional true} [::sm/one-of grid-cell-align-self-types]]
[:justify-self {:optional true} [::sm/one-of grid-cell-justify-self-types]]
[:shapes
[:vector {:gen/max 1} ::sm/uuid]]])
(sm/register!
^{::sm/type ::grid-cell}
[:map {:title "GridCell"}
[:id ::sm/uuid]
[:area-name {:optional true} :string]
[:row ::sm/safe-int]
[:row-span ::sm/safe-int]
[:column ::sm/safe-int]
[:column-span ::sm/safe-int]
[:position {:optional true} [::sm/one-of grid-position-types]]
[:align-self {:optional true} [::sm/one-of grid-cell-align-self-types]]
[:justify-self {:optional true} [::sm/one-of grid-cell-justify-self-types]]
[:shapes
[:vector {:gen/max 1} ::sm/uuid]]])
(def ^:private schema:grid-track
[:map {:title "GridTrack"}
[:type [::sm/one-of grid-track-types]]
[:value {:optional true} [:maybe ::sm/safe-number]]])
(sm/register!
^{::sm/type ::grid-track}
[:map {:title "GridTrack"}
[:type [::sm/one-of grid-track-types]]
[:value {:optional true} [:maybe ::sm/safe-number]]])
(def schema:layout-attrs
[:map {:title "LayoutAttrs"}
[:layout {:optional true} [::sm/one-of layout-types]]
[:layout-flex-dir {:optional true} [::sm/one-of flex-direction-types]]
[:layout-gap {:optional true}
[:map
[:row-gap {:optional true} ::sm/safe-number]
[:column-gap {:optional true} ::sm/safe-number]]]
[:layout-gap-type {:optional true} [::sm/one-of gap-types]]
[:layout-wrap-type {:optional true} [::sm/one-of wrap-types]]
[:layout-padding-type {:optional true} [::sm/one-of padding-type]]
[:layout-padding {:optional true}
[:map
[:p1 ::sm/safe-number]
[:p2 ::sm/safe-number]
[:p3 ::sm/safe-number]
[:p4 ::sm/safe-number]]]
[:layout-justify-content {:optional true} [::sm/one-of justify-content-types]]
[:layout-justify-items {:optional true} [::sm/one-of justify-items-types]]
[:layout-align-content {:optional true} [::sm/one-of align-content-types]]
[:layout-align-items {:optional true} [::sm/one-of align-items-types]]
[:layout-grid-dir {:optional true} [::sm/one-of grid-direction-types]]
[:layout-grid-rows {:optional true}
[:vector {:gen/max 2} schema:grid-track]]
[:layout-grid-columns {:optional true}
[:vector {:gen/max 2} schema:grid-track]]
[:layout-grid-cells {:optional true}
[:map-of {:gen/max 5} ::sm/uuid schema:grid-cell]]])
(def ^:private check-grid-track
(sm/check-fn schema:grid-track))
(def layout-attrs
(sm/keys schema:layout-attrs))
(def check-grid-track!
(sm/check-fn ::grid-track))
;; LAYOUT CHILDREN
@@ -154,7 +168,7 @@
(def item-align-self-types
#{:start :end :center :stretch})
(def schema:layout-child-attrs
(def schema:layout-attrs
[:map {:title "LayoutChildAttrs"}
[:layout-item-margin-type {:optional true} [::sm/one-of item-margin-types]]
[:layout-item-margin {:optional true}
@@ -173,9 +187,6 @@
[:layout-item-absolute {:optional true} :boolean]
[:layout-item-z-index {:optional true} ::sm/safe-number]])
(def layout-child-attrs
(sm/keys schema:layout-child-attrs))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SCHEMAS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -183,6 +194,8 @@
(def valid-layouts
#{:flex :grid})
(sm/register! ::layout [::sm/one-of valid-layouts])
(defn flex-layout?
([objects id]
(flex-layout? (get objects id)))
@@ -741,7 +754,9 @@
([type parent value]
(add-grid-track type parent value nil))
([type parent value index]
(assert (check-grid-track value))
(dm/assert!
"expected a valid grid definition for `value`"
(check-grid-track! value))
(let [[tracks-prop tracks-prop-other prop prop-other prop-span prop-span-other]
(if (= type :column)

View File

@@ -24,7 +24,7 @@
[:blur ::sm/safe-number]
[:spread ::sm/safe-number]
[:hidden :boolean]
[:color ctc/schema:color]])
[:color ::ctc/color]])
(def check-shadow
(sm/check-fn schema:shadow))

View File

@@ -7,7 +7,6 @@
(ns app.common.types.shape.text
(:require
[app.common.schema :as sm]
[app.common.types.fill :refer [schema:fill]]
[app.common.types.shape :as-alias shape]
[app.common.types.shape.text.position-data :as-alias position-data]))
@@ -35,7 +34,7 @@
[:key {:optional true} :string]
[:fills {:optional true}
[:maybe
[:vector {:gen/max 2} schema:fill]]]
[:vector {:gen/max 2} ::shape/fill]]]
[:font-family {:optional true} :string]
[:font-size {:optional true} :string]
[:font-style {:optional true} :string]
@@ -52,7 +51,7 @@
[:key {:optional true} :string]
[:fills {:optional true}
[:maybe
[:vector {:gen/max 2} schema:fill]]]
[:vector {:gen/max 2} ::shape/fill]]]
[:font-family {:optional true} :string]
[:font-size {:optional true} :string]
[:font-style {:optional true} :string]
@@ -76,7 +75,7 @@
[:y ::sm/safe-number]
[:width ::sm/safe-number]
[:height ::sm/safe-number]
[:fills [:vector {:gen/max 2} schema:fill]]
[:fills [:vector {:gen/max 2} ::shape/fill]]
[:font-family {:optional true} :string]
[:font-size {:optional true} :string]
[:font-style {:optional true} :string]

View File

@@ -11,12 +11,9 @@
(defn- compare-text-content
"Given two content text structures, conformed by maps and vectors,
compare them, and returns a set with the differences info.
If the structures are equal, it returns an empty set. If the structure
has changed, it returns :text-content-structure. There are two
callbacks to specify what to return when there is a text change with
the same structure, and when attributes change."
[a b {:keys [text-cb attribute-cb] :as callbacks}]
compare them, and returns a set with the type of differences.
The possibilities are :text-content-text :text-content-attribute and :text-content-structure."
[a b]
(cond
;; If a and b are equal, there is no diff
(= a b)
@@ -41,18 +38,18 @@
#{:text-content-structure}
(into acc
(apply set/union
(map #(compare-text-content %1 %2 callbacks) v1 v2))))
(map #(compare-text-content %1 %2) v1 v2))))
;; If the key is :text, and they are different, it is a text differece
(= k :text)
(if (not= v1 v2)
(text-cb acc)
(conj acc :text-content-text)
acc)
:else
;; If the key is not :text, and they are different, it is an attribute differece
(if (not= v1 v2)
(attribute-cb acc k)
(conj acc :text-content-attribute)
acc))))
#{}
keys))
@@ -60,6 +57,7 @@
:else
#{:text-content-structure}))
(defn equal-attrs?
"Given a text structure, and a map of attrs, check that all the internal attrs in
paragraphs and sentences have the same attrs"
@@ -81,15 +79,10 @@
(defn get-diff-type
"Given two content text structures, conformed by maps and vectors,
compare them, and returns a set with the type of differences.
The possibilities are
:text-content-text
:text-content-attribute,
:text-content-structure
:text-content-structure-same-attrs."
The possibilities are :text-content-text :text-content-attribute,
:text-content-structure and :text-content-structure-same-attrs."
[a b]
(let [diff-type (compare-text-content a b
{:text-cb (fn [acc] (conj acc :text-content-text))
:attribute-cb (fn [acc _] (conj acc :text-content-attribute))})]
(let [diff-type (compare-text-content a b)]
(if-not (contains? diff-type :text-content-structure)
diff-type
(let [;; get attrs of the first paragraph of the first paragraph-set
@@ -99,24 +92,6 @@
#{:text-content-structure :text-content-structure-same-attrs}
diff-type)))))
(defn get-diff-attrs
"Given two content text structures, conformed by maps and vectors,
compare them, and returns a set with the attributes that have changed.
This is independent of the text structure, so if the structure changes
but the attributes are the same, it will return an empty set."
[a b]
(let [diff-attrs (compare-text-content a b
{:text-cb identity
:attribute-cb (fn [acc attr] (conj acc attr))})]
(if-not (contains? diff-attrs :text-content-structure)
diff-attrs
(let [;; get attrs of the first paragraph of the first paragraph-set
attrs (get-first-paragraph-text-attrs a)]
(if (and (equal-attrs? a attrs)
(equal-attrs? b attrs))
#{}
(disj diff-attrs :text-content-structure))))))
;; TODO We know that there are cases that the blocks of texts are separated
;; differently: ["one" " " "two"], ["one " "two"], ["one" " two"]
;; so this won't work for 100% of the situations. But it's good enough for now,
@@ -141,6 +116,7 @@
:else
true))
(defn copy-text-keys
"Given two equal content text structures, deep copy all the keys :text
from origin to destiny"

View File

@@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.schema :as sm]
[app.common.schema.registry :as sr]
[clojure.data :as data]
[clojure.set :as set]
[malli.util :as mu]))
@@ -16,10 +17,16 @@
;; HELPERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- schema-keys
(defn merge-schemas
"Merge registered schemas."
[& schema-keys]
(let [schemas (map #(get @sr/registry %) schema-keys)]
(reduce sm/merge schemas)))
(defn schema-keys
"Converts registed map schema into set of keys."
[schema]
(->> schema
[registered-schema]
(->> (get @sr/registry registered-schema)
(sm/schema)
(mu/keys)
(into #{})))
@@ -33,7 +40,7 @@
:border-radius "borderRadius"
:color "color"
:dimensions "dimension"
:number "number"
:numeric "numeric"
:opacity "opacity"
:other "other"
:rotation "rotation"
@@ -48,86 +55,95 @@
(def token-types
(into #{} (keys token-type->dtcg-token-type)))
(defn valid-token-type?
[t]
(token-types t))
(def token-name-ref
[:and :string [:re #"^(?!\$)([a-zA-Z0-9-$_]+\.?)*(?<!\.)$"]])
(def ^:private schema:color
[:map
[:fill {:optional true} token-name-ref]
[:stroke-color {:optional true} token-name-ref]])
(defn valid-token-name-ref?
[n]
(string? n))
(def color-keys (schema-keys schema:color))
(sm/register!
^{::sm/type ::color}
[:map
[:fill {:optional true} token-name-ref]
[:stroke-color {:optional true} token-name-ref]])
(def ^:private schema:border-radius
[:map
[:r1 {:optional true} token-name-ref]
[:r2 {:optional true} token-name-ref]
[:r3 {:optional true} token-name-ref]
[:r4 {:optional true} token-name-ref]])
(def color-keys (schema-keys ::color))
(def border-radius-keys (schema-keys schema:border-radius))
(sm/register!
^{::sm/type ::border-radius}
[:map
[:r1 {:optional true} token-name-ref]
[:r2 {:optional true} token-name-ref]
[:r3 {:optional true} token-name-ref]
[:r4 {:optional true} token-name-ref]])
(def ^:private schema:stroke-width
[:map
[:stroke-width {:optional true} token-name-ref]])
(def border-radius-keys (schema-keys ::border-radius))
(def stroke-width-keys (schema-keys schema:stroke-width))
(sm/register!
^{::sm/type ::stroke-width}
[:map
[:stroke-width {:optional true} token-name-ref]])
(def ^:private schema:sizing
[:map
[:width {:optional true} token-name-ref]
[:height {:optional true} token-name-ref]
[:layout-item-min-w {:optional true} token-name-ref]
[:layout-item-max-w {:optional true} token-name-ref]
[:layout-item-min-h {:optional true} token-name-ref]
[:layout-item-max-h {:optional true} token-name-ref]])
(def stroke-width-keys (schema-keys ::stroke-width))
(def sizing-keys (schema-keys schema:sizing))
(sm/register!
^{::sm/type ::sizing}
[:map
[:width {:optional true} token-name-ref]
[:height {:optional true} token-name-ref]
[:layout-item-min-w {:optional true} token-name-ref]
[:layout-item-max-w {:optional true} token-name-ref]
[:layout-item-min-h {:optional true} token-name-ref]
[:layout-item-max-h {:optional true} token-name-ref]])
(def ^:private schema:opacity
[:map
[:opacity {:optional true} token-name-ref]])
(def sizing-keys (schema-keys ::sizing))
(def opacity-keys (schema-keys schema:opacity))
(sm/register!
^{::sm/type ::opacity}
[:map
[:opacity {:optional true} token-name-ref]])
(def ^:private schema:spacing
[:map
[:row-gap {:optional true} token-name-ref]
[:column-gap {:optional true} token-name-ref]
[:p1 {:optional true} token-name-ref]
[:p2 {:optional true} token-name-ref]
[:p3 {:optional true} token-name-ref]
[:p4 {:optional true} token-name-ref]
[:m1 {:optional true} token-name-ref]
[:m2 {:optional true} token-name-ref]
[:m3 {:optional true} token-name-ref]
[:m4 {:optional true} token-name-ref]
[:x {:optional true} token-name-ref]
[:y {:optional true} token-name-ref]])
(def opacity-keys (schema-keys ::opacity))
(def spacing-keys (schema-keys schema:spacing))
(sm/register!
^{::sm/type ::spacing}
[:map
[:row-gap {:optional true} token-name-ref]
[:column-gap {:optional true} token-name-ref]
[:p1 {:optional true} token-name-ref]
[:p2 {:optional true} token-name-ref]
[:p3 {:optional true} token-name-ref]
[:p4 {:optional true} token-name-ref]
[:m1 {:optional true} token-name-ref]
[:m2 {:optional true} token-name-ref]
[:m3 {:optional true} token-name-ref]
[:m4 {:optional true} token-name-ref]
[:x {:optional true} token-name-ref]
[:y {:optional true} token-name-ref]])
(def ^:private schema:dimensions
[:merge
schema:sizing
schema:spacing
schema:stroke-width
schema:border-radius])
(def spacing-keys (schema-keys ::spacing))
(def dimensions-keys (schema-keys schema:dimensions))
(sm/register!
^{::sm/type ::dimensions}
[:merge
::sizing
::spacing
::stroke-width
::border-radius])
(def ^:private schema:rotation
[:map
[:rotation {:optional true} token-name-ref]])
(def dimensions-keys (schema-keys ::dimensions))
(def rotation-keys (schema-keys schema:rotation))
(sm/register!
^{::sm/type ::rotation}
[:map
[:rotation {:optional true} token-name-ref]])
(def ^:private schema:number
[:map
[:rotation {:optional true} token-name-ref]
[:line-height {:optional true} token-name-ref]])
(def number-keys (schema-keys schema:number))
(def rotation-keys (schema-keys ::rotation))
(def all-keys (set/union color-keys
border-radius-keys
@@ -136,21 +152,21 @@
opacity-keys
spacing-keys
dimensions-keys
rotation-keys
number-keys))
rotation-keys))
(def ^:private schema:tokens
[:map {:title "Applied Tokens"}])
(sm/register!
^{::sm/type ::tokens}
[:map {:title "Applied Tokens"}])
(def schema:applied-tokens
[:merge
schema:tokens
schema:border-radius
schema:sizing
schema:spacing
schema:rotation
schema:number
schema:dimensions])
(sm/register!
^{::sm/type ::applied-tokens}
[:merge
::tokens
::border-radius
::sizing
::spacing
::rotation
::dimensions])
(defn shape-attr->token-attrs
([shape-attr] (shape-attr->token-attrs shape-attr nil))
@@ -181,8 +197,7 @@
(sizing-keys shape-attr) #{shape-attr}
(opacity-keys shape-attr) #{shape-attr}
(spacing-keys shape-attr) #{shape-attr}
(rotation-keys shape-attr) #{shape-attr}
(number-keys shape-attr) #{shape-attr})))
(rotation-keys shape-attr) #{shape-attr})))
(defn token-attr->shape-attr
[token-attr]

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,50 +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 common-tests.buffer-test
(:require
[app.common.buffer :as buf]
[app.common.uuid :as uuid]
[clojure.test :as t]))
(t/deftest allocate
(let [b (buf/allocate 1)]
(t/is (buf/buffer? b))))
(t/deftest rw-byte
(let [b (buf/allocate 1)]
(buf/write-byte b 0 123)
(let [res (buf/read-byte b 0)]
(t/is (= 123 res)))
(buf/write-byte b 0 252)
(let [res (buf/read-byte b 0)]
(t/is (= -4 res)))))
(t/deftest rw-int
(let [b (buf/allocate 4)]
(buf/write-int b 0 123)
(let [res (buf/read-int b 0)]
(t/is (= 123 res)))))
(t/deftest rw-float
(let [b (buf/allocate 4)]
(buf/write-float b 0 123)
(let [res (buf/read-float b 0)]
(t/is (= 123.0 res)))))
(t/deftest rw-short
(let [b (buf/allocate 2)]
(buf/write-short b 0 123)
(let [res (buf/read-short b 0)]
(t/is (= 123 res)))))
(t/deftest rw-uuid
(let [b (buf/allocate 16)
id (uuid/next)]
(buf/write-uuid b 0 id)
(let [res (buf/read-uuid b 0)]
(t/is (= id res)))))

View File

@@ -0,0 +1,881 @@
;; 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 common-tests.logic.text-sync-test
(:require
[app.common.files.changes-builder :as pcb]
[app.common.logic.libraries :as cll]
[app.common.logic.shapes :as cls]
[app.common.test-helpers.components :as thc]
[app.common.test-helpers.compositions :as tho]
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.ids-map :as thi]
[app.common.test-helpers.shapes :as ths]
[clojure.test :as t]))
(t/use-fixtures :each thi/test-fixture)
(t/deftest test-sync-unchanged-copy-when-changed-attribute
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
main-child (ths/get-shape file :main-child)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-size] "32"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "32" (:font-size line)))
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-unchanged-copy-when-changed-text
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
main-child (ths/get-shape file :main-child)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "14" (:font-size line)))
(t/is (= "Bye" (:text line)))))
(t/deftest test-sync-unchanged-copy-when-changed-both
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
main-child (ths/get-shape file :main-child)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Bye")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "32" (:font-size line)))
(t/is (= "Bye" (:text line)))))
(t/deftest test-sync-updated-attr-copy-when-changed-attribute
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-weight] "700"))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-size] "32"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr doesn't change, because it was touched
(t/is (= "14" (:font-size line)))
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-updated-attr-copy-when-changed-text
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-weight] "700"))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "14" (:font-size line)))
;; The text is updated because only attrs were touched
(t/is (= "Bye" (:text line)))))
(t/deftest test-sync-updated-attr-copy-when-changed-both
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-weight] "700"))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Bye")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr doesn't change, because it was touched
(t/is (= "14" (:font-size line)))
;; The text is updated because only attrs were touched
(t/is (= "Bye" (:text line)))))
(t/deftest test-sync-updated-text-copy-when-changed-attribute
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Hi"))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-size] "32"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr is updated because only text were touched
(t/is (= "32" (:font-size line)))
(t/is (= "Hi" (:text line)))))
(t/deftest test-sync-updated-text-copy-when-changed-text
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Hi"))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "14" (:font-size line)))
;; The text doesn't change, because it was touched
(t/is (= "Hi" (:text line)))))
(t/deftest test-sync-updated-text-copy-when-changed-both
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Hi"))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Bye")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr is updated because only text were touched
(t/is (= "32" (:font-size line)))
;; The text doesn't change, because it was touched
(t/is (= "Hi" (:text line)))))
(t/deftest test-sync-updated-both-copy-when-changed-attribute
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-weight] "700")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Hi")))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :font-size] "32"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr doesn't change, because it was touched
(t/is (= "14" (:font-size line)))
(t/is (= "Hi" (:text line)))))
(t/deftest test-sync-updated-both-copy-when-changed-text
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-weight] "700")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Hi")))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "14" (:font-size line)))
;; The text doesn't change, because it was touched
(t/is (= "Hi" (:text line)))))
(t/deftest test-sync-updated-both-copy-when-changed-both
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-weight] "700")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Hi")))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :children 0 :text] "Bye")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr doesn't change, because it was touched
(t/is (= "14" (:font-size line)))
;; The text doesn't change, because it was touched
(t/is (= "Hi" (:text line)))))
(t/deftest test-sync-updated-structure-same-attrs-copy-when-changed-attribute
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(let [line (get-in shape [:content :children 0 :children 0 :children 0])]
(update-in shape [:content :children 0 :children 0 :children]
#(conj % line))))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
;; Update the attrs on all the content tree
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :font-size] "32")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr is updated because all the attrs on the structure are equal
(t/is (= "32" (:font-size line)))
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-updated-structure-same-attrs-copy-when-changed-text
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(let [line (get-in shape [:content :children 0 :children 0 :children 0])]
(update-in shape [:content :children 0 :children 0 :children]
#(conj % line))))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "14" (:font-size line)))
;; The text doesn't change, because the structure was touched
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-updated-structure-same-attrs-copy-when-changed-both
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(let [line (get-in shape [:content :children 0 :children 0 :children 0])]
(update-in shape [:content :children 0 :children 0 :children]
#(conj % line))))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
;; Update the attrs on all the content tree
(-> shape
(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 :children 0 :text] "Bye")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr is updated because all the attrs on the structure are equal
(t/is (= "32" (:font-size line)))
;; The text doesn't change, because the structure was touched
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-updated-structure-diff-attrs-copy-when-changed-attribute
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(let [line (-> (get-in shape [:content :children 0 :children 0 :children 0])
(assoc :font-weight "700"))]
(update-in shape [:content :children 0 :children 0 :children]
#(conj % line))))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
;; Update the attrs on all the content tree
(-> shape
(assoc-in [:content :children 0 :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :font-size] "32")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr doesn't change, because not all the attrs on the structure are equal
(t/is (= "14" (:font-size line)))
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-updated-structure-diff-attrs-copy-when-changed-text
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(let [line (-> (get-in shape [:content :children 0 :children 0 :children 0])
(assoc :font-weight "700"))]
(update-in shape [:content :children 0 :children 0 :children]
#(conj % line))))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
(t/is (= "14" (:font-size line)))
;; The text doesn't change, because the structure was touched
(t/is (= "hello world" (:text line)))))
(t/deftest test-sync-updated-structure-diff-attrs-copy-when-changed-both
(let [;; ==== Setup
file0 (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page0 (thf/current-page file0)
copy-child (ths/get-shape file0 :copy-child)
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page0))
#{(:id copy-child)}
(fn [shape]
(let [line (-> (get-in shape [:content :children 0 :children 0 :children 0])
(assoc :font-weight "700"))]
(update-in shape [:content :children 0 :children 0 :children]
#(conj % line))))
(:objects (thf/current-page file0))
{})
file (thf/apply-changes file0 changes)
main-child (ths/get-shape file :main-child)
page (thf/current-page file)
;; ==== Action
changes1 (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id main-child)}
(fn [shape]
;; Update the attrs on all the content tree
(-> shape
(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 :children 0 :text] "Bye")))
(:objects page)
{})
updated-file (thf/apply-changes file changes1)
changes2 (cll/generate-sync-file-changes (pcb/empty-changes)
nil
:components
(:id updated-file)
(thi/id :component1)
(:id updated-file)
{(:id updated-file) updated-file}
(:id updated-file))
file' (thf/apply-changes updated-file changes2)
;; ==== Get
copy-child' (ths/get-shape file' :copy-child)
line (get-in copy-child' [:content :children 0 :children 0 :children 0])]
;; The attr doesn't change, because not all the attrs on the structure are equal
(t/is (= "14" (:font-size line)))
;; The text doesn't change, because the structure was touched
(t/is (= "hello world" (:text line)))))

View File

@@ -0,0 +1,132 @@
;; 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 common-tests.logic.text-touched-test
(:require
[app.common.files.changes-builder :as pcb]
[app.common.logic.shapes :as cls]
[app.common.test-helpers.components :as thc]
[app.common.test-helpers.compositions :as tho]
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.ids-map :as thi]
[app.common.test-helpers.shapes :as ths]
[clojure.test :as t]))
(t/use-fixtures :each thi/test-fixture)
(t/deftest test-text-copy-changed-attribute
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
copy-child (ths/get-shape file :copy-child)
;; ==== Action
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :font-size] "32"))
(:objects page)
{})
file' (thf/apply-changes file changes)
copy-child' (ths/get-shape file' :copy-child)]
(t/is (= #{:content-group :text-content-attribute} (:touched copy-child')))))
(t/deftest test-text-copy-changed-text
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
copy-child (ths/get-shape file :copy-child)
;; ==== Action
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id copy-child)}
(fn [shape]
(assoc-in shape [:content :children 0 :children 0 :text] "Bye"))
(:objects page)
{})
file' (thf/apply-changes file changes)
copy-child' (ths/get-shape file' :copy-child)]
(t/is (= #{:content-group :text-content-text} (:touched copy-child')))))
(t/deftest test-text-copy-changed-both
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
copy-child (ths/get-shape file :copy-child)
;; ==== Action
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id copy-child)}
(fn [shape]
(-> shape
(assoc-in [:content :children 0 :children 0 :font-size] "32")
(assoc-in [:content :children 0 :children 0 :text] "Bye")))
(:objects page)
{})
file' (thf/apply-changes file changes)
copy-child' (ths/get-shape file' :copy-child)]
(t/is (= #{:content-group :text-content-attribute :text-content-text} (:touched copy-child')))))
(t/deftest test-text-copy-changed-structure-same-attrs
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
copy-child (ths/get-shape file :copy-child)
;; ==== Action
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id copy-child)}
(fn [shape]
(let [line (get-in shape [:content :children 0 :children 0])]
(update-in shape [:content :children 0 :children]
#(conj % line))))
(:objects page)
{})
file' (thf/apply-changes file changes)
copy-child' (ths/get-shape file' :copy-child)]
(t/is (= #{:content-group :text-content-structure :text-content-structure-same-attrs} (:touched copy-child')))))
(t/deftest test-text-copy-changed-structure-diff-attrs
(let [;; ==== Setup
file (-> (thf/sample-file :file1)
(tho/add-frame-with-text :main-root :main-child "hello world")
(thc/make-component :component1 :main-root)
(thc/instantiate-component :component1 :copy-root {:children-labels [:copy-child]}))
page (thf/current-page file)
copy-child (ths/get-shape file :copy-child)
;; ==== Action
changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page))
#{(:id copy-child)}
(fn [shape]
(let [line (-> shape
(get-in [:content :children 0 :children 0])
(assoc :font-size "32"))]
(update-in shape [:content :children 0 :children]
#(conj % line))))
(:objects page)
{})
file' (thf/apply-changes file changes)
copy-child' (ths/get-shape file' :copy-child)]
(t/is (= #{:content-group :text-content-structure} (:touched copy-child')))))

View File

@@ -6,7 +6,6 @@
(ns common-tests.logic.token-apply-test
(:require
[app.common.data :as d]
[app.common.files.changes-builder :as pcb]
[app.common.logic.shapes :as cls]
[app.common.test-helpers.compositions :as tho]
@@ -14,7 +13,6 @@
[app.common.test-helpers.ids-map :as thi]
[app.common.test-helpers.shapes :as ths]
[app.common.test-helpers.tokens :as tht]
[app.common.text :as txt]
[app.common.types.container :as ctn]
[app.common.types.token :as cto]
[app.common.types.tokens-lib :as ctob]
@@ -55,8 +53,7 @@
(ctob/make-token :name "token-dimensions"
:type :dimensions
:value 100))))
(tho/add-frame :frame1)
(tho/add-text :text1 "Hello World")))
(tho/add-frame :frame1)))
(defn- apply-all-tokens
[file]
@@ -67,8 +64,7 @@
(tht/apply-token-to-shape :frame1 "token-stroke-width" [:stroke-width] [:stroke-width] 2)
(tht/apply-token-to-shape :frame1 "token-color" [:stroke-color] [:stroke-color] "#00ff00")
(tht/apply-token-to-shape :frame1 "token-color" [:fill] [:fill] "#00ff00")
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)
(tht/apply-token-to-shape :text1 "token-color" [:fill] [:fill] "#00ff00")))
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)))
(t/deftest apply-tokens-to-shape
(let [;; ==== Setup
@@ -175,7 +171,6 @@
(apply-all-tokens))
page (thf/current-page file)
frame1 (ths/get-shape file :frame1)
text1 (ths/get-shape file :text1)
;; ==== Action
changes (-> (-> (pcb/empty-changes nil)
@@ -195,25 +190,13 @@
(ctn/set-shape-attr :width 0)
(ctn/set-shape-attr :height 0)))
(:objects page)
{})
(cls/generate-update-shapes [(:id text1)]
(fn [shape]
(txt/update-text-content
shape
txt/is-content-node?
d/txt-merge
{:fills (ths/sample-fills-color :fill-color "#fabada")}))
(:objects page)
{}))
file' (thf/apply-changes file changes)
;; ==== Get
frame1' (ths/get-shape file' :frame1)
text1' (ths/get-shape file' :text1)
applied-tokens-frame' (:applied-tokens frame1')
applied-tokens-text' (:applied-tokens text1')]
frame1' (ths/get-shape file' :frame1)
applied-tokens' (:applied-tokens frame1')]
;; ==== Check
(t/is (= (count applied-tokens-frame') 0))
(t/is (= (count applied-tokens-text') 0))))
(t/is (= (count applied-tokens') 0))))

View File

@@ -36,7 +36,7 @@
redo-lib (tht/get-tokens-lib redo)
undo (thf/apply-undo-changes redo changes)
undo-lib (tht/get-tokens-lib undo)]
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib))))
;; Undo
@@ -56,7 +56,7 @@
redo-lib (tht/get-tokens-lib redo)
undo (thf/apply-undo-changes redo changes)
undo-lib (tht/get-tokens-lib undo)]
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib))))
;; Undo
@@ -65,8 +65,8 @@
(t/testing "toggling an set with hidden theme already active will toggle set in hidden theme"
(let [file (setup-file #(-> %
(ctob/add-set (ctob/make-token-set :name "foo/bar"))
(ctob/add-theme (ctob/make-hidden-theme))
(ctob/set-active-themes #{ctob/hidden-theme-path})))
(ctob/add-theme (ctob/make-hidden-token-theme))
(ctob/set-active-themes #{ctob/hidden-token-theme-path})))
changes (-> (pcb/empty-changes)
(pcb/with-library-data (:data file))
@@ -265,7 +265,7 @@
:type :color})
file (setup-file #(-> (ctob/add-set % (ctob/make-token-set :name set-name))
(ctob/add-token-in-set set-name token)))
prev-token-set (-> file tht/get-tokens-lib (ctob/get-set set-name))
prev-token-set (-> file tht/get-tokens-lib :sets first)
new-set-name "foo1"
changes (-> (pcb/empty-changes)
(pcb/with-library-data (:data file))
@@ -299,7 +299,7 @@
redo-lib (tht/get-tokens-lib redo)
undo (thf/apply-undo-changes redo changes)
undo-lib (tht/get-tokens-lib undo)]
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{"foo/bar/baz" "foo/bar/baz/baz-child"} (:sets (ctob/get-hidden-theme redo-lib))))
;; Undo
@@ -323,7 +323,7 @@
undo (thf/apply-undo-changes redo changes)
undo-lib (tht/get-tokens-lib undo)]
(t/is (= #{} (:sets (ctob/get-hidden-theme redo-lib))))
(t/is (= #{ctob/hidden-theme-path} (ctob/get-active-theme-paths redo-lib)))
(t/is (= #{ctob/hidden-token-theme-path} (ctob/get-active-theme-paths redo-lib)))
;; Undo
(t/is (= #{"/theme"} (ctob/get-active-theme-paths undo-lib))))))

View File

@@ -7,7 +7,6 @@
(ns common-tests.runner
(:require
[clojure.test :as t]
[common-tests.buffer-test]
[common-tests.colors-test]
[common-tests.data-test]
[common-tests.files-changes-test]
@@ -39,7 +38,6 @@
[common-tests.time-test]
[common-tests.types.absorb-assets-test]
[common-tests.types.components-test]
[common-tests.types.fill-test]
[common-tests.types.modifiers-test]
[common-tests.types.path-data-test]
[common-tests.types.shape-decode-encode-test]
@@ -58,7 +56,6 @@
(defn -main
[& args]
(t/run-tests
'common-tests.buffer-test
'common-tests.colors-test
'common-tests.data-test
'common-tests.files-changes-test
@@ -92,7 +89,6 @@
'common-tests.types.components-test
'common-tests.types.modifiers-test
'common-tests.types.path-data-test
'common-tests.types.fill-test
'common-tests.types.shape-decode-encode-test
'common-tests.types.shape-interactions-test
'common-tests.types.tokens-lib-test

View File

@@ -13,7 +13,7 @@
[app.common.test-helpers.ids-map :as thi]
[app.common.test-helpers.shapes :as ths]
[app.common.text :as txt]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.file :as ctf]
@@ -80,7 +80,7 @@
_ (thf/validate-file! file')
;; Get
colors' (ctc/colors-seq (ctf/file-data file'))
colors' (ctcl/colors-seq (ctf/file-data file'))
shape1' (ths/get-shape file' :shape1)
fill' (first (:fills shape1'))]

View File

@@ -1,214 +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 common-tests.types.fill-test
(:require
#?(:clj [app.common.fressian :as fres])
[app.common.data :as d]
[app.common.exceptions :as ex]
[app.common.math :as mth]
[app.common.pprint :as pp]
[app.common.pprint :as pp]
[app.common.schema.generators :as sg]
[app.common.schema.test :as smt]
[app.common.transit :as trans]
[app.common.types.fill :as types.fill]
[app.common.uuid :as uuid]
[clojure.test :as t]))
(defn equivalent-fill?
[fill-a fill-b]
;; (prn "-------------------")
;; (app.common.pprint/pprint fill-a)
;; (app.common.pprint/pprint fill-b)
(and (= (get fill-a :fill-color-ref-file)
(get fill-b :fill-color-ref-file))
(= (get fill-a :fill-color-ref-id)
(get fill-b :fill-color-ref-id))
(or (and (contains? fill-a :fill-color)
(= (:fill-color fill-a)
(:fill-color fill-b))
(mth/close? (:fill-opacity fill-a 1.0)
(:fill-opacity fill-b 1.0)))
(and (contains? fill-a :fill-image)
(mth/close? (:fill-opacity fill-a 1.0)
(:fill-opacity fill-b 1.0))
(let [image-a (:fill-image fill-a)
image-b (:fill-image fill-b)]
(and (= (:id image-a)
(:id image-b))
(= (:mtype image-a)
(:mtype image-b))
(mth/close? (:width image-a)
(:width image-b))
(mth/close? (:height image-a)
(:height image-b)))))
(and (contains? fill-a :fill-color-gradient)
(mth/close? (:fill-opacity fill-a 1)
(:fill-opacity fill-b 1))
(let [gradient-a (:fill-color-gradient fill-a)
gradient-b (:fill-color-gradient fill-b)]
(and (= (count (:stops gradient-a))
(count (:stops gradient-b)))
(= (get gradient-a :type)
(get gradient-b :type))
(mth/close? (get gradient-a :start-x)
(get gradient-b :start-x))
(mth/close? (get gradient-a :start-y)
(get gradient-b :start-y))
(mth/close? (get gradient-a :end-x)
(get gradient-b :end-x))
(mth/close? (get gradient-a :end-y)
(get gradient-b :end-y))
(mth/close? (get gradient-a :width)
(get gradient-b :width))
(every? true?
(map (fn [stop-a stop-b]
(and (= (get stop-a :color)
(get stop-b :color))
(mth/close? (get stop-a :opacity 1)
(get stop-b :opacity 1))
(mth/close? (get stop-a :offset)
(get stop-b :offset))))
(get gradient-a :stops)
(get gradient-b :stops)))))))))
(def sample-fill-1
{:fill-color "#fabada"
:fill-opacity 0.7})
(t/deftest build-from-plain-1
(let [fills (types.fill/from-plain [sample-fill-1])]
(t/is (types.fill/fills? fills))
(t/is (= 1 (count fills)))
(t/is (equivalent-fill? (first fills) sample-fill-1))))
(def sample-fill-2
{:fill-color-ref-file #uuid "4fcb3db7-d281-8004-8006-3a97e2e142ad"
:fill-color-ref-id #uuid "fb19956a-c9e0-8056-8006-3a9c78f531c6"
:fill-image {:width 200, :height 100, :mtype "image/gif",
:id #uuid "b30f028d-cc2f-8035-8006-3a93bd0e137b",
:name "ovba",
:keep-aspect-ratio false}})
(t/deftest build-from-plain-2
(let [fills (types.fill/from-plain [sample-fill-2])]
(t/is (types.fill/fills? fills))
(t/is (= 1 (count fills)))
(t/is (equivalent-fill? (first fills) sample-fill-2))))
(def sample-fill-3
{:fill-color-ref-id #uuid "fb19956a-c9e0-8056-8006-3a9c78f531c6"
:fill-color-ref-file #uuid "fb19956a-c9e0-8056-8006-3a9c78f531c5"
:fill-color-gradient
{:type :linear,
:start-x 0.75,
:start-y 3.0,
:end-x 1.0,
:end-y 1.5,
:width 200,
:stops [{:color "#631aa8", :offset 0.5}]}})
(t/deftest build-from-plain-3
(let [fills (types.fill/from-plain [sample-fill-3])]
(t/is (types.fill/fills? fills))
(t/is (= 1 (count fills)))
(t/is (equivalent-fill? (first fills) sample-fill-3))))
(def sample-fill-4
{:fill-color-ref-file #uuid "2eef07f1-e38a-8062-8006-3aa264d5b784",
:fill-color-gradient
{:type :radial,
:start-x 0.5,
:start-y -1.0,
:end-x -0.5,
:end-y 2,
:width 0.5,
:stops [{:color "#781025", :offset 0.0} {:color "#035c3f", :offset 0.2}]},
:fill-opacity 1.0,
:fill-color-ref-id #uuid "2eef07f1-e38a-8062-8006-3aa264d5b785"})
(t/deftest build-from-plain-4
(let [fills (types.fill/from-plain [sample-fill-4])]
(t/is (types.fill/fills? fills))
(t/is (= 1 (count fills)))
(t/is (equivalent-fill? (first fills) sample-fill-4))))
(def sample-fill-5
{:fill-color-ref-file #uuid "b0f76f9a-f548-806e-8006-3aa4456131d1",
:fill-color-ref-id #uuid "b0f76f9a-f548-806e-8006-3aa445618851",
:fill-color-gradient
{:type :radial,
:start-x -0.86,
:start-y 6.0,
:end-x 0.25,
:end-y -0.5,
:width 3.8,
:stops [{:color "#bba1aa", :opacity 0.37, :offset 0.84}]}})
(t/deftest build-from-plain-5
(let [fills (types.fill/from-plain [sample-fill-5])]
(t/is (types.fill/fills? fills))
(t/is (= 1 (count fills)))
(t/is (equivalent-fill? (first fills) sample-fill-5))))
(def sample-fill-6
{:fill-color-gradient
{:type :linear,
:start-x 3.5,
:start-y 0.39,
:end-x -1.87,
:end-y 1.95,
:width 2.62,
:stops [{:color "#e15610", :offset 0.4} {:color "#005a9e", :opacity 0.62, :offset 0.81}]}})
(t/deftest build-from-plain-6
(let [fills (types.fill/from-plain [sample-fill-6])]
(t/is (types.fill/fills? fills))
(t/is (= 1 (count fills)))
(t/is (equivalent-fill? (first fills) sample-fill-6))))
(t/deftest fills-datatype-roundtrip
(smt/check!
(smt/for [fill (->> (sg/generator types.fill/schema:fill)
(sg/fmap d/without-nils)
(sg/fmap (fn [fill]
(cond-> fill
(and (not (and (contains? fill :fill-color-ref-id)
(contains? fill :fill-color-ref-file)))
(or (contains? fill :fill-color-ref-id)
(contains? fill :fill-color-ref-file)))
(-> (assoc :fill-color-ref-file (uuid/next))
(assoc :fill-color-ref-id (uuid/next)))))))]
(let [bfills (types.fill/from-plain [fill])]
(and (= (count bfills) 1)
(equivalent-fill? (first bfills) fill))))
{:num 2000}))
(t/deftest equality-operation
(let [fills1 (types.fill/from-plain [sample-fill-6])
fills2 (types.fill/from-plain [sample-fill-6])]
(t/is (= fills1 fills2))))
(t/deftest reduce-impl
(let [fills1 (types.fill/from-plain [sample-fill-6])
fills2 (reduce (fn [result fill]
(conj result fill))
[]
fills1)
fills3 (types.fill/from-plain fills2)]
(t/is (= fills1 fills3))))
(t/deftest indexed-access
(let [fills1 (types.fill/from-plain [sample-fill-6])
fill0 (nth fills1 0)
fill1 (nth fills1 1)]
(t/is (nil? fill1))
(t/is (equivalent-fill? fill0 sample-fill-6))))

View File

@@ -25,15 +25,6 @@
{:command :curve-to :params {:c1x 368.0 :c1y 737.0 :c2x 310.0 :c2y 681.0 :x 264.0 :y 634.0}}
{:command :close-path :params {}}])
(def sample-content-square
[{:command :move-to, :params {:x 0, :y 0}}
{:command :line-to, :params {:x 10, :y 0}}
{:command :line-to, :params {:x 10, :y 10}}
{:command :line-to, :params {:x 10, :y 0}}
{:command :line-to, :params {:x 0, :y 10}}
{:command :line-to, :params {:x 0, :y 0}}
{:command :close-path :params {}}])
(def sample-content-large
[{:command :move-to :params {:x 480.0 :y 839.0}}
{:command :line-to :params {:x 439.0 :y 802.0}}
@@ -110,7 +101,7 @@
(let [pdata (path/content sample-content)]
(t/is (= sample-bytes
(vec
#?(:cljs (js/Int8Array. (.-buffer (.-buffer pdata)))
#?(:cljs (js/Int8Array. (.-buffer pdata))
:clj (.array (.-buffer pdata))))))
(t/is (= sample-content
(vec pdata)))))
@@ -188,42 +179,6 @@
(t/is (= (vec result1) result2))
(t/is (= result2 result3))))
(t/deftest path-transform-3
(let [matrix (gmt/rotate-matrix 42 (gpt/point 0 0))
content (path/content sample-content-square)
result1 (path/transform-content content matrix)
result2 (transform-plain-content sample-content-square matrix)
result3 (transform-plain-content content matrix)]
(t/is (= (count result1) (count result2)))
(doseq [[seg-a seg-b] (map vector result1 result2)]
(t/is (= (:command seg-a)
(:command seg-b)))
(let [params-a (get seg-a :params)
params-b (get seg-b :params)]
(t/is (mth/close? (get params-a :x 0)
(get params-b :x 0)))
(t/is (mth/close? (get params-a :y 0)
(get params-b :y 0)))))
(doseq [[seg-a seg-b] (map vector result1 result3)]
(t/is (= (:command seg-a)
(:command seg-b)))
(let [params-a (get seg-a :params)
params-b (get seg-b :params)]
(t/is (mth/close? (get params-a :x 0)
(get params-b :x 0)))
(t/is (mth/close? (get params-a :y 0)
(get params-b :y 0)))))))
(defn- content->points
"Given a content return all points.
@@ -323,6 +278,15 @@
(t/is (= result2 expect))
(t/is (= result3 expect))))
(def sample-content-square
[{:command :move-to, :params {:x 0, :y 0}}
{:command :line-to, :params {:x 10, :y 0}}
{:command :line-to, :params {:x 10, :y 10}}
{:command :line-to, :params {:x 10, :y 0}}
{:command :line-to, :params {:x 0, :y 10}}
{:command :line-to, :params {:x 0, :y 0}}
{:command :close-path :params {}}])
(t/deftest points-to-content
(let [initial [(gpt/point 0.0 0.0)
(gpt/point 10.0 10.0)

View File

@@ -113,10 +113,10 @@
{:num 500})))
(t/deftest shape-path-content-json-roundtrip
(let [encode (sm/encoder path/schema:content (sm/json-transformer))
decode (sm/decoder path/schema:content (sm/json-transformer))]
(let [encode (sm/encoder ::path/content (sm/json-transformer))
decode (sm/decoder ::path/content (sm/json-transformer))]
(smt/check!
(smt/for [path-content (sg/generator path/schema:content)]
(smt/for [path-content (sg/generator ::path/content)]
(let [path-content-1 (encode path-content)
path-content-2 (json-roundtrip path-content-1)
path-content-3 (decode path-content-2)]
@@ -145,4 +145,4 @@
;; (app.common.pprint/pprint shape)
;; (app.common.pprint/pprint shape-3)
(= shape shape-3)))
{:num 200})))
{:num 100})))

View File

@@ -29,12 +29,11 @@
#(conj % line)))
(t/deftest test-get-diff-type
(let [diff-text (cttx/get-diff-type content-base content-changed-text)
diff-attr (cttx/get-diff-type content-base content-changed-attr)
diff-both (cttx/get-diff-type content-base content-changed-both)
diff-structure (cttx/get-diff-type content-base content-changed-structure)
(let [diff-text (cttx/get-diff-type content-base content-changed-text)
diff-attr (cttx/get-diff-type content-base content-changed-attr)
diff-both (cttx/get-diff-type content-base content-changed-both)
diff-structure (cttx/get-diff-type content-base content-changed-structure)
diff-structure-same-attrs (cttx/get-diff-type content-base content-changed-structure-same-attrs)]
(t/is (= #{:text-content-text} diff-text))
(t/is (= #{:text-content-attribute} diff-attr))
(t/is (= #{:text-content-text :text-content-attribute} diff-both))
@@ -42,20 +41,6 @@
(t/is (= #{:text-content-structure :text-content-structure-same-attrs} diff-structure-same-attrs))))
(t/deftest test-get-diff-attrs
(let [attrs-text (cttx/get-diff-attrs content-base content-changed-text)
attrs-attr (cttx/get-diff-attrs content-base content-changed-attr)
attrs-both (cttx/get-diff-attrs content-base content-changed-both)
attrs-structure (cttx/get-diff-attrs content-base content-changed-structure)
attrs-structure-same-attrs (cttx/get-diff-attrs content-base content-changed-structure-same-attrs)]
(t/is (= #{} attrs-text))
(t/is (= #{:font-size} attrs-attr))
(t/is (= #{:font-size} attrs-both))
(t/is (= #{} attrs-structure))
(t/is (= #{} attrs-structure-same-attrs))))
(t/deftest test-equal-structure
(t/is (true? (cttx/equal-structure? content-base content-changed-text)))
(t/is (true? (cttx/equal-structure? content-base content-changed-attr)))

View File

@@ -9,7 +9,6 @@
#?(:clj [app.common.fressian :as fres])
#?(:clj [app.common.json :as json])
#?(:clj [app.common.test-helpers.tokens :as tht])
#?(:clj [app.common.uuid :as uuid])
[app.common.data :as d]
[app.common.time :as dt]
[app.common.transit :as tr]
@@ -30,7 +29,7 @@
:type :boolean
:value true)
token2 (ctob/make-token :name "test-token-2"
:type :number
:type :numeric
:value 66
:description "test description"
:modified-at now)]
@@ -43,7 +42,7 @@
(t/is (ctob/check-token token1))
(t/is (= (:name token2) "test-token-2"))
(t/is (= (:type token2) :number))
(t/is (= (:type token2) :numeric))
(t/is (= (:value token2) 66))
(t/is (= (:description token2) "test description"))
(t/is (= (:modified-at token2) now))
@@ -544,10 +543,10 @@
(ctob/make-token :name "token-4"
:type :border-radius
:value 4000)}))
(ctob/update-theme ctob/hidden-theme-group ctob/hidden-theme-name
(ctob/update-theme ctob/hidden-token-theme-group ctob/hidden-token-theme-name
#(ctob/enable-sets % #{"set-a" "set-b"})))
tokens (ctob/get-tokens-in-active-sets tokens-lib)]
tokens (ctob/get-active-themes-set-tokens tokens-lib)]
(t/is (= (mapv key tokens) ["token-1" "token-2" "token-3"]))
(t/is (= (get-in tokens ["token-1" :value]) 100))
@@ -595,7 +594,7 @@
:sets #{"set-b" "set-c" "set-a"}))
(ctob/set-active-themes #{"/single-theme"}))
tokens (ctob/get-tokens-in-active-sets tokens-lib)]
tokens (ctob/get-active-themes-set-tokens tokens-lib)]
;; Note that sets order inside the theme is undefined. What matters is order in that the
;; sets have been added to the library.
@@ -648,7 +647,7 @@
:sets #{"set-b" "set-a"}))
(ctob/set-active-themes #{"/theme-1" "/theme-2"}))
tokens (ctob/get-tokens-in-active-sets tokens-lib)]
tokens (ctob/get-active-themes-set-tokens tokens-lib)]
;; Note that themes order is irrelevant. What matters is the union of the active sets
;; and the order of the sets in the library.
@@ -693,7 +692,7 @@
:sets #{}))
(ctob/set-active-themes #{"App/Web" "Brand/Brand A"}))
tokens (ctob/get-tokens-in-active-sets tokens-lib)]
tokens (ctob/get-active-themes-set-tokens tokens-lib)]
(t/is (= (mapv key tokens) ["red" "border1"]))
(t/is (= (get-in tokens ["red" :value]) "#ff0000"))
@@ -703,13 +702,13 @@
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "set-a")))
tokens (ctob/get-tokens-in-active-sets tokens-lib)]
tokens (ctob/get-active-themes-set-tokens tokens-lib)]
(t/is (empty? tokens))))
(t/deftest list-active-themes-tokens-no-sets
(let [tokens-lib (ctob/make-tokens-lib)
tokens (ctob/get-tokens-in-active-sets tokens-lib)]
tokens (ctob/get-active-themes-set-tokens tokens-lib)]
(t/is (empty? tokens))))
@@ -872,6 +871,35 @@
(t/is (= (second path) "subgroup"))
(t/is (= (nth path 2) "name"))))
(t/deftest group-and-ungroup-token-set
(let [token-set1 (ctob/make-token-set :name "token-set1")
token-set2 (ctob/make-token-set :name "some group/token-set2")
token-set1' (ctob/group-item token-set1 "big group" "/")
token-set2' (ctob/group-item token-set2 "big group" "/")
token-set1'' (ctob/ungroup-item token-set1' "/")
token-set2'' (ctob/ungroup-item token-set2' "/")]
(t/is (= (:name token-set1') "big group/token-set1"))
(t/is (= (:name token-set2') "big group/some group/token-set2"))
(t/is (= (:name token-set1'') "token-set1"))
(t/is (= (:name token-set2'') "some group/token-set2"))))
(t/deftest get-token-set-groups-str
(let [token-set1 (ctob/make-token-set :name "token-set1")
token-set2 (ctob/make-token-set :name "some-group/token-set2")
token-set3 (ctob/make-token-set :name "some-group/some-subgroup/token-set3")]
(t/is (= (ctob/get-groups-str token-set1 "/") ""))
(t/is (= (ctob/get-groups-str token-set2 "/") "some-group"))
(t/is (= (ctob/get-groups-str token-set3 "/") "some-group/some-subgroup"))))
(t/deftest get-token-set-final-name
(let [token-set1 (ctob/make-token-set :name "token-set1")
token-set2 (ctob/make-token-set :name "some-group/token-set2")
token-set3 (ctob/make-token-set :name "some-group/some-subgroup/token-set3")]
(t/is (= (ctob/get-final-name token-set1 "/") "token-set1"))
(t/is (= (ctob/get-final-name token-set2 "/") "token-set2"))
(t/is (= (ctob/get-final-name token-set3 "/") "token-set3"))))
(t/deftest add-tokens-in-set
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
@@ -1033,6 +1061,65 @@
(t/is (nil? token'))
(t/is (dt/is-after? (:modified-at token-set') (:modified-at token-set)))))
(t/deftest add-token-set-with-groups
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
(ctob/add-set (ctob/make-token-set :name "group1/token-set-2"))
(ctob/add-set (ctob/make-token-set :name "group1/token-set-3"))
(ctob/add-set (ctob/make-token-set :name "group1/subgroup11/token-set-4"))
(ctob/add-set (ctob/make-token-set :name "group2/token-set-5")))
sets-list (ctob/get-sets tokens-lib)
sets-tree (ctob/get-set-tree tokens-lib)
[node-set1 node-group1 node-group2]
(ctob/get-children sets-tree)
[node-set2 node-set3 node-subgroup11]
(ctob/get-children (second node-group1))
[node-set4]
(ctob/get-children (second node-subgroup11))
[node-set5]
(ctob/get-children (second node-group2))]
(t/is (= (count sets-list) 5))
(t/is (= (:name (nth sets-list 0)) "token-set-1"))
(t/is (= (:name (nth sets-list 1)) "group1/token-set-2"))
(t/is (= (:name (nth sets-list 2)) "group1/token-set-3"))
(t/is (= (:name (nth sets-list 3)) "group1/subgroup11/token-set-4"))
(t/is (= (:name (nth sets-list 4)) "group2/token-set-5"))
(t/is (= (first node-set1) "S-token-set-1"))
(t/is (= (ctob/group? (second node-set1)) false))
(t/is (= (:name (second node-set1)) "token-set-1"))
(t/is (= (first node-group1) "G-group1"))
(t/is (= (ctob/group? (second node-group1)) true))
(t/is (= (count (second node-group1)) 3))
(t/is (= (first node-set2) "S-token-set-2"))
(t/is (= (ctob/group? (second node-set2)) false))
(t/is (= (:name (second node-set2)) "group1/token-set-2"))
(t/is (= (first node-set3) "S-token-set-3"))
(t/is (= (ctob/group? (second node-set3)) false))
(t/is (= (:name (second node-set3)) "group1/token-set-3"))
(t/is (= (first node-subgroup11) "G-subgroup11"))
(t/is (= (ctob/group? (second node-subgroup11)) true))
(t/is (= (count (second node-subgroup11)) 1))
(t/is (= (first node-set4) "S-token-set-4"))
(t/is (= (ctob/group? (second node-set4)) false))
(t/is (= (:name (second node-set4)) "group1/subgroup11/token-set-4"))
(t/is (= (first node-set5) "S-token-set-5"))
(t/is (= (ctob/group? (second node-set5)) false))
(t/is (= (:name (second node-set5)) "group2/token-set-5"))))
(t/deftest update-token-set-in-groups
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "token-set-1"))
@@ -1130,6 +1217,68 @@
(t/is (= (count sets-tree') 1))
(t/is (nil? token-set'))))
(t/deftest add-theme-with-groups
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-2"))
(ctob/add-theme (ctob/make-token-theme :group "group1" :name "token-theme-3"))
(ctob/add-theme (ctob/make-token-theme :group "group2" :name "token-theme-4")))
themes-list (ctob/get-themes tokens-lib)
themes-tree (ctob/get-theme-tree tokens-lib)
[node-group0 node-group1 node-group2]
(ctob/get-children themes-tree)
[hidden-theme node-theme1]
(ctob/get-children (second node-group0))
[node-theme2 node-theme3]
(ctob/get-children (second node-group1))
[node-theme4]
(ctob/get-children (second node-group2))]
(t/is (= (count themes-list) 5))
(t/is (= (:name (nth themes-list 0)) "__PENPOT__HIDDEN__TOKEN__THEME__"))
(t/is (= (:name (nth themes-list 1)) "token-theme-1"))
(t/is (= (:name (nth themes-list 2)) "token-theme-2"))
(t/is (= (:name (nth themes-list 3)) "token-theme-3"))
(t/is (= (:name (nth themes-list 4)) "token-theme-4"))
(t/is (= (:group (nth themes-list 1)) ""))
(t/is (= (:group (nth themes-list 2)) "group1"))
(t/is (= (:group (nth themes-list 3)) "group1"))
(t/is (= (:group (nth themes-list 4)) "group2"))
(t/is (= (first node-group0) ""))
(t/is (= (ctob/group? (second node-group0)) true))
(t/is (= (count (second node-group0)) 2))
(t/is (= (first hidden-theme) "__PENPOT__HIDDEN__TOKEN__THEME__"))
(t/is (= (ctob/group? (second hidden-theme)) false))
(t/is (= (:name (second hidden-theme)) "__PENPOT__HIDDEN__TOKEN__THEME__"))
(t/is (= (first node-theme1) "token-theme-1"))
(t/is (= (ctob/group? (second node-theme1)) false))
(t/is (= (:name (second node-theme1)) "token-theme-1"))
(t/is (= (first node-group1) "group1"))
(t/is (= (ctob/group? (second node-group1)) true))
(t/is (= (count (second node-group1)) 2))
(t/is (= (first node-theme2) "token-theme-2"))
(t/is (= (ctob/group? (second node-theme2)) false))
(t/is (= (:name (second node-theme2)) "token-theme-2"))
(t/is (= (first node-theme3) "token-theme-3"))
(t/is (= (ctob/group? (second node-theme3)) false))
(t/is (= (:name (second node-theme3)) "token-theme-3"))
(t/is (= (first node-theme4) "token-theme-4"))
(t/is (= (ctob/group? (second node-theme4)) false))
(t/is (= (:name (second node-theme4)) "token-theme-4"))))
(t/deftest update-token-theme-in-groups
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :group "" :name "token-theme-1"))
@@ -1327,7 +1476,7 @@
(t/is (= 1 (count themes)))
(t/testing "existing theme is default theme"
(t/is (= (:group first-theme) ""))
(t/is (= (:name first-theme) ctob/hidden-theme-name)))
(t/is (= (:name first-theme) ctob/hidden-token-theme-name)))
(t/testing "token exist in dark set"
(t/is (tht/token-data-eq? (ctob/get-token-in-set lib "dark" "small")
{:name "small"
@@ -1358,7 +1507,7 @@
:value "{accent.default}"})}))
(ctob/add-theme (ctob/make-token-theme :name "theme-1"
:group "group-1"
:external-id "test-id-00"
:id "test-id-00"
:modified-at now
:sets #{"core"})))
result (ctob/export-dtcg-json tokens-lib)
@@ -1388,8 +1537,7 @@
#?(:clj
(t/deftest export-parse-dtcg-json
(with-redefs [dt/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00")
uuid/next (constantly uuid/zero)]
(with-redefs [dt/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00")]
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "core"
:tokens {"colors.red.600"
@@ -1478,7 +1626,7 @@
:value "{accent.default}"})}))
(ctob/add-theme (ctob/make-token-theme :name "theme-1"
:group "group-1"
:external-id "test-id-01"
:id "test-id-01"
:modified-at now
:sets #{"core"}))
(ctob/toggle-theme-active? "group-1" "theme-1"))
@@ -1507,56 +1655,3 @@
"$type" "color"
"$description" ""}}}}}]
(t/is (= expected result)))))
#?(:clj
(t/deftest export-dtcg-multi-file
(let [now (dt/now)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "some/set"
:tokens {"colors.red.600"
(ctob/make-token
{:name "colors.red.600"
:type :color
:value "#e53e3e"})
"spacing.multi-value"
(ctob/make-token
{:name "spacing.multi-value"
:type :spacing
:value "{dimension.sm} {dimension.xl}"
:description "You can have multiple values in a single spacing token"})
"button.primary.background"
(ctob/make-token
{:name "button.primary.background"
:type :color
:value "{accent.default}"})}))
(ctob/add-theme (ctob/make-token-theme :name "theme-1"
:group "group-1"
:external-id "test-id-01"
:modified-at now
:sets #{"some/set"}))
(ctob/toggle-theme-active? "group-1" "theme-1"))
result (ctob/export-dtcg-multi-file tokens-lib)
expected {"$themes.json" [{"description" ""
"group" "group-1"
"is-source" false
"modified-at" now
"id" "test-id-01"
"name" "theme-1"
"selectedTokenSets" {"some/set" "enabled"}}]
"$metadata.json" {"tokenSetOrder" ["some/set"]
"activeThemes" #{"group-1/theme-1"}
"activeSets" #{"some/set"}}
"some/set.json"
{"colors" {"red" {"600" {"$value" "#e53e3e"
"$type" "color"
"$description" ""}}}
"spacing"
{"multi-value"
{"$value" "{dimension.sm} {dimension.xl}"
"$type" "spacing"
"$description" "You can have multiple values in a single spacing token"}}
"button"
{"primary" {"background" {"$value" "{accent.default}"
"$type" "color"
"$description" ""}}}}}]
(t/is (= expected result)))))

View File

@@ -18,7 +18,6 @@
(let [uuid (uuid/uuid "0227df82-63d7-8016-8005-48d9c0f33011")
result-bytes (uuid/get-bytes uuid)
expected-bytes [2 39 -33 -126 99 -41 -128 22 -128 5 72 -39 -64 -13 48 17]]
(t/testing "get-bytes"
(let [data (uuid/get-bytes uuid)]
(t/is (= (nth expected-bytes 0) (aget data 0)))

View File

@@ -19,34 +19,25 @@ __metadata:
languageName: node
linkType: hard
"@isaacs/fs-minipass@npm:^4.0.0":
version: 4.0.1
resolution: "@isaacs/fs-minipass@npm:4.0.1"
dependencies:
minipass: "npm:^7.0.4"
checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2
languageName: node
linkType: hard
"@npmcli/agent@npm:^3.0.0":
version: 3.0.0
resolution: "@npmcli/agent@npm:3.0.0"
"@npmcli/agent@npm:^2.0.0":
version: 2.2.2
resolution: "@npmcli/agent@npm:2.2.2"
dependencies:
agent-base: "npm:^7.1.0"
http-proxy-agent: "npm:^7.0.0"
https-proxy-agent: "npm:^7.0.1"
lru-cache: "npm:^10.0.1"
socks-proxy-agent: "npm:^8.0.3"
checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271
checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae
languageName: node
linkType: hard
"@npmcli/fs@npm:^4.0.0":
version: 4.0.0
resolution: "@npmcli/fs@npm:4.0.0"
"@npmcli/fs@npm:^3.1.0":
version: 3.1.1
resolution: "@npmcli/fs@npm:3.1.1"
dependencies:
semver: "npm:^7.3.5"
checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5
checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99
languageName: node
linkType: hard
@@ -57,17 +48,29 @@ __metadata:
languageName: node
linkType: hard
"abbrev@npm:^3.0.0":
version: 3.0.1
resolution: "abbrev@npm:3.0.1"
checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf
"abbrev@npm:^2.0.0":
version: 2.0.0
resolution: "abbrev@npm:2.0.0"
checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372
languageName: node
linkType: hard
"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
version: 7.1.3
resolution: "agent-base@npm:7.1.3"
checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1":
version: 7.1.1
resolution: "agent-base@npm:7.1.1"
dependencies:
debug: "npm:^4.3.4"
checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50
languageName: node
linkType: hard
"aggregate-error@npm:^3.0.0":
version: 3.1.0
resolution: "aggregate-error@npm:3.1.0"
dependencies:
clean-stack: "npm:^2.0.0"
indent-string: "npm:^4.0.0"
checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039
languageName: node
linkType: hard
@@ -79,9 +82,9 @@ __metadata:
linkType: hard
"ansi-regex@npm:^6.0.1":
version: 6.1.0
resolution: "ansi-regex@npm:6.1.0"
checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc
version: 6.0.1
resolution: "ansi-regex@npm:6.0.1"
checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08
languageName: node
linkType: hard
@@ -126,21 +129,21 @@ __metadata:
linkType: hard
"brace-expansion@npm:^1.1.7":
version: 1.1.12
resolution: "brace-expansion@npm:1.1.12"
version: 1.1.11
resolution: "brace-expansion@npm:1.1.11"
dependencies:
balanced-match: "npm:^1.0.0"
concat-map: "npm:0.0.1"
checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73
checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668
languageName: node
linkType: hard
"brace-expansion@npm:^2.0.1":
version: 2.0.2
resolution: "brace-expansion@npm:2.0.2"
version: 2.0.1
resolution: "brace-expansion@npm:2.0.1"
dependencies:
balanced-match: "npm:^1.0.0"
checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf
checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f
languageName: node
linkType: hard
@@ -160,11 +163,11 @@ __metadata:
languageName: node
linkType: hard
"cacache@npm:^19.0.1":
version: 19.0.1
resolution: "cacache@npm:19.0.1"
"cacache@npm:^18.0.0":
version: 18.0.3
resolution: "cacache@npm:18.0.3"
dependencies:
"@npmcli/fs": "npm:^4.0.0"
"@npmcli/fs": "npm:^3.1.0"
fs-minipass: "npm:^3.0.0"
glob: "npm:^10.2.2"
lru-cache: "npm:^10.0.1"
@@ -172,11 +175,11 @@ __metadata:
minipass-collect: "npm:^2.0.1"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
p-map: "npm:^7.0.2"
ssri: "npm:^12.0.0"
tar: "npm:^7.4.3"
unique-filename: "npm:^4.0.0"
checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c
p-map: "npm:^4.0.0"
ssri: "npm:^10.0.0"
tar: "npm:^6.1.11"
unique-filename: "npm:^3.0.0"
checksum: 10c0/dfda92840bb371fb66b88c087c61a74544363b37a265023223a99965b16a16bbb87661fe4948718d79df6e0cc04e85e62784fbcf1832b2a5e54ff4c46fbb45b7
languageName: node
linkType: hard
@@ -209,10 +212,17 @@ __metadata:
languageName: node
linkType: hard
"chownr@npm:^3.0.0":
version: 3.0.0
resolution: "chownr@npm:3.0.0"
checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10
"chownr@npm:^2.0.0":
version: 2.0.0
resolution: "chownr@npm:2.0.0"
checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1
languageName: node
linkType: hard
@@ -247,11 +257,12 @@ __metadata:
version: 0.0.0-use.local
resolution: "common@workspace:."
dependencies:
concurrently: "npm:^9.1.2"
luxon: "npm:^3.6.1"
nodemon: "npm:^3.1.10"
concurrently: "npm:^9.0.1"
luxon: "npm:^3.4.4"
nodemon: "npm:^3.1.7"
shadow-cljs: "npm:3.0.5"
source-map-support: "npm:^0.5.21"
ws: "npm:^8.18.2"
ws: "npm:^8.17.0"
languageName: unknown
linkType: soft
@@ -262,9 +273,9 @@ __metadata:
languageName: node
linkType: hard
"concurrently@npm:^9.1.2":
version: 9.1.2
resolution: "concurrently@npm:9.1.2"
"concurrently@npm:^9.0.1":
version: 9.1.0
resolution: "concurrently@npm:9.1.0"
dependencies:
chalk: "npm:^4.1.2"
lodash: "npm:^4.17.21"
@@ -276,30 +287,42 @@ __metadata:
bin:
conc: dist/bin/concurrently.js
concurrently: dist/bin/concurrently.js
checksum: 10c0/88e00269366aa885ca2b97fd53b04e7af2b0f31774d991bfc0e88c0de61cdebdf115ddacc9c897fbd1f1b90369014637fa77045a171d072a75693332b36dcc70
checksum: 10c0/f2f42f94dde508bfbaf47b5ac654db9e8a4bf07d3d7b6267dd058ae6f362eec677ae7c8ede398d081e5fd0d1de5811dc9a53e57d3f1f68e72ac6459db9e0896b
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.6":
version: 7.0.6
resolution: "cross-spawn@npm:7.0.6"
"cross-spawn@npm:^7.0.0":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
dependencies:
path-key: "npm:^3.1.0"
shebang-command: "npm:^2.0.0"
which: "npm:^2.0.1"
checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1
checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750
languageName: node
linkType: hard
"debug@npm:4, debug@npm:^4, debug@npm:^4.3.4":
version: 4.4.1
resolution: "debug@npm:4.4.1"
"debug@npm:4, debug@npm:^4.3.4":
version: 4.3.4
resolution: "debug@npm:4.3.4"
dependencies:
ms: "npm:2.1.2"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736
languageName: node
linkType: hard
"debug@npm:^4":
version: 4.3.7
resolution: "debug@npm:4.3.7"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b
languageName: node
linkType: hard
@@ -355,21 +378,9 @@ __metadata:
linkType: hard
"exponential-backoff@npm:^3.1.1":
version: 3.1.2
resolution: "exponential-backoff@npm:3.1.2"
checksum: 10c0/d9d3e1eafa21b78464297df91f1776f7fbaa3d5e3f7f0995648ca5b89c069d17055033817348d9f4a43d1c20b0eab84f75af6991751e839df53e4dfd6f22e844
languageName: node
linkType: hard
"fdir@npm:^6.4.4":
version: 6.4.6
resolution: "fdir@npm:6.4.6"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579
languageName: node
linkType: hard
@@ -383,12 +394,21 @@ __metadata:
linkType: hard
"foreground-child@npm:^3.1.0":
version: 3.3.1
resolution: "foreground-child@npm:3.3.1"
version: 3.1.1
resolution: "foreground-child@npm:3.1.1"
dependencies:
cross-spawn: "npm:^7.0.6"
cross-spawn: "npm:^7.0.0"
signal-exit: "npm:^4.0.1"
checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3
checksum: 10c0/9700a0285628abaeb37007c9a4d92bd49f67210f09067638774338e146c8e9c825c5c877f072b2f75f41dc6a2d0be8664f79ffc03f6576649f54a84fb9b47de0
languageName: node
linkType: hard
"fs-minipass@npm:^2.0.0":
version: 2.1.0
resolution: "fs-minipass@npm:2.1.0"
dependencies:
minipass: "npm:^3.0.0"
checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004
languageName: node
linkType: hard
@@ -436,19 +456,18 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^10.2.2":
version: 10.4.5
resolution: "glob@npm:10.4.5"
"glob@npm:^10.2.2, glob@npm:^10.3.10":
version: 10.3.16
resolution: "glob@npm:10.3.16"
dependencies:
foreground-child: "npm:^3.1.0"
jackspeak: "npm:^3.1.2"
minimatch: "npm:^9.0.4"
minipass: "npm:^7.1.2"
package-json-from-dist: "npm:^1.0.0"
path-scurry: "npm:^1.11.1"
minimatch: "npm:^9.0.1"
minipass: "npm:^7.0.4"
path-scurry: "npm:^1.11.0"
bin:
glob: dist/esm/bin.mjs
checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e
checksum: 10c0/f7eb4c3e66f221f0be3967c02527047167967549bdf8ed1bd5f6277d43a35191af4e2bb8c89f07a79664958bae088fd06659e69a0f1de462972f1eab52a715e8
languageName: node
linkType: hard
@@ -474,9 +493,9 @@ __metadata:
linkType: hard
"http-cache-semantics@npm:^4.1.1":
version: 4.2.0
resolution: "http-cache-semantics@npm:4.2.0"
checksum: 10c0/45b66a945cf13ec2d1f29432277201313babf4a01d9e52f44b31ca923434083afeca03f18417f599c9ab3d0e7b618ceb21257542338b57c54b710463b4a53e37
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc
languageName: node
linkType: hard
@@ -491,12 +510,12 @@ __metadata:
linkType: hard
"https-proxy-agent@npm:^7.0.1":
version: 7.0.6
resolution: "https-proxy-agent@npm:7.0.6"
version: 7.0.4
resolution: "https-proxy-agent@npm:7.0.4"
dependencies:
agent-base: "npm:^7.1.2"
agent-base: "npm:^7.0.2"
debug: "npm:4"
checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
checksum: 10c0/bc4f7c38da32a5fc622450b6cb49a24ff596f9bd48dcedb52d2da3fa1c1a80e100fb506bd59b326c012f21c863c69b275c23de1a01d0b84db396822fdf25e52b
languageName: node
linkType: hard
@@ -523,6 +542,13 @@ __metadata:
languageName: node
linkType: hard
"indent-string@npm:^4.0.0":
version: 4.0.0
resolution: "indent-string@npm:4.0.0"
checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f
languageName: node
linkType: hard
"ip-address@npm:^9.0.5":
version: 9.0.5
resolution: "ip-address@npm:9.0.5"
@@ -565,6 +591,13 @@ __metadata:
languageName: node
linkType: hard
"is-lambda@npm:^1.0.1":
version: 1.0.1
resolution: "is-lambda@npm:1.0.1"
checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d
languageName: node
linkType: hard
"is-number@npm:^7.0.0":
version: 7.0.0
resolution: "is-number@npm:7.0.0"
@@ -587,15 +620,15 @@ __metadata:
linkType: hard
"jackspeak@npm:^3.1.2":
version: 3.4.3
resolution: "jackspeak@npm:3.4.3"
version: 3.1.2
resolution: "jackspeak@npm:3.1.2"
dependencies:
"@isaacs/cliui": "npm:^8.0.2"
"@pkgjs/parseargs": "npm:^0.11.0"
dependenciesMeta:
"@pkgjs/parseargs":
optional: true
checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9
checksum: 10c0/5f1922a1ca0f19869e23f0dc4374c60d36e922f7926c76fecf8080cc6f7f798d6a9caac1b9428327d14c67731fd551bb3454cb270a5e13a0718f3b3660ec3d5d
languageName: node
linkType: hard
@@ -614,35 +647,36 @@ __metadata:
linkType: hard
"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
version: 10.4.3
resolution: "lru-cache@npm:10.4.3"
checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
version: 10.2.2
resolution: "lru-cache@npm:10.2.2"
checksum: 10c0/402d31094335851220d0b00985084288136136992979d0e015f0f1697e15d1c86052d7d53ae86b614e5b058425606efffc6969a31a091085d7a2b80a8a1e26d6
languageName: node
linkType: hard
"luxon@npm:^3.6.1":
version: 3.6.1
resolution: "luxon@npm:3.6.1"
checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5
"luxon@npm:^3.4.4":
version: 3.4.4
resolution: "luxon@npm:3.4.4"
checksum: 10c0/02e26a0b039c11fd5b75e1d734c8f0332c95510f6a514a9a0991023e43fb233884da02d7f966823ffb230632a733fc86d4a4b1e63c3fbe00058b8ee0f8c728af
languageName: node
linkType: hard
"make-fetch-happen@npm:^14.0.3":
version: 14.0.3
resolution: "make-fetch-happen@npm:14.0.3"
"make-fetch-happen@npm:^13.0.0":
version: 13.0.1
resolution: "make-fetch-happen@npm:13.0.1"
dependencies:
"@npmcli/agent": "npm:^3.0.0"
cacache: "npm:^19.0.1"
"@npmcli/agent": "npm:^2.0.0"
cacache: "npm:^18.0.0"
http-cache-semantics: "npm:^4.1.1"
is-lambda: "npm:^1.0.1"
minipass: "npm:^7.0.2"
minipass-fetch: "npm:^4.0.0"
minipass-fetch: "npm:^3.0.0"
minipass-flush: "npm:^1.0.5"
minipass-pipeline: "npm:^1.2.4"
negotiator: "npm:^1.0.0"
proc-log: "npm:^5.0.0"
negotiator: "npm:^0.6.3"
proc-log: "npm:^4.2.0"
promise-retry: "npm:^2.0.1"
ssri: "npm:^12.0.0"
checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0
ssri: "npm:^10.0.0"
checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e
languageName: node
linkType: hard
@@ -655,12 +689,12 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^9.0.4":
version: 9.0.5
resolution: "minimatch@npm:9.0.5"
"minimatch@npm:^9.0.1":
version: 9.0.4
resolution: "minimatch@npm:9.0.4"
dependencies:
brace-expansion: "npm:^2.0.1"
checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed
checksum: 10c0/2c16f21f50e64922864e560ff97c587d15fd491f65d92a677a344e970fe62aafdbeafe648965fa96d33c061b4d0eabfe0213466203dd793367e7f28658cf6414
languageName: node
linkType: hard
@@ -673,18 +707,18 @@ __metadata:
languageName: node
linkType: hard
"minipass-fetch@npm:^4.0.0":
version: 4.0.1
resolution: "minipass-fetch@npm:4.0.1"
"minipass-fetch@npm:^3.0.0":
version: 3.0.5
resolution: "minipass-fetch@npm:3.0.5"
dependencies:
encoding: "npm:^0.1.13"
minipass: "npm:^7.0.3"
minipass-sized: "npm:^1.0.3"
minizlib: "npm:^3.0.1"
minizlib: "npm:^2.1.2"
dependenciesMeta:
encoding:
optional: true
checksum: 10c0/a3147b2efe8e078c9bf9d024a0059339c5a09c5b1dded6900a219c218cc8b1b78510b62dae556b507304af226b18c3f1aeb1d48660283602d5b6586c399eed5c
checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b
languageName: node
linkType: hard
@@ -724,28 +758,43 @@ __metadata:
languageName: node
linkType: hard
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
version: 7.1.2
resolution: "minipass@npm:7.1.2"
checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557
"minipass@npm:^5.0.0":
version: 5.0.0
resolution: "minipass@npm:5.0.0"
checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462
languageName: node
linkType: hard
"minizlib@npm:^3.0.1":
version: 3.0.2
resolution: "minizlib@npm:3.0.2"
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4":
version: 7.1.1
resolution: "minipass@npm:7.1.1"
checksum: 10c0/fdccc2f99c31083f45f881fd1e6971d798e333e078ab3c8988fb818c470fbd5e935388ad9adb286397eba50baebf46ef8ff487c8d3f455a69c6f3efc327bdff9
languageName: node
linkType: hard
"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2":
version: 2.1.2
resolution: "minizlib@npm:2.1.2"
dependencies:
minipass: "npm:^7.1.2"
checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78
minipass: "npm:^3.0.0"
yallist: "npm:^4.0.0"
checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78
languageName: node
linkType: hard
"mkdirp@npm:^3.0.1":
version: 3.0.1
resolution: "mkdirp@npm:3.0.1"
"mkdirp@npm:^1.0.3":
version: 1.0.4
resolution: "mkdirp@npm:1.0.4"
bin:
mkdirp: dist/cjs/src/bin.js
checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d
mkdirp: bin/cmd.js
checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf
languageName: node
linkType: hard
"ms@npm:2.1.2":
version: 2.1.2
resolution: "ms@npm:2.1.2"
checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
languageName: node
linkType: hard
@@ -756,36 +805,36 @@ __metadata:
languageName: node
linkType: hard
"negotiator@npm:^1.0.0":
version: 1.0.0
resolution: "negotiator@npm:1.0.0"
checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b
"negotiator@npm:^0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 11.2.0
resolution: "node-gyp@npm:11.2.0"
version: 10.1.0
resolution: "node-gyp@npm:10.1.0"
dependencies:
env-paths: "npm:^2.2.0"
exponential-backoff: "npm:^3.1.1"
glob: "npm:^10.3.10"
graceful-fs: "npm:^4.2.6"
make-fetch-happen: "npm:^14.0.3"
nopt: "npm:^8.0.0"
proc-log: "npm:^5.0.0"
make-fetch-happen: "npm:^13.0.0"
nopt: "npm:^7.0.0"
proc-log: "npm:^3.0.0"
semver: "npm:^7.3.5"
tar: "npm:^7.4.3"
tinyglobby: "npm:^0.2.12"
which: "npm:^5.0.0"
tar: "npm:^6.1.2"
which: "npm:^4.0.0"
bin:
node-gyp: bin/node-gyp.js
checksum: 10c0/bd8d8c76b06be761239b0c8680f655f6a6e90b48e44d43415b11c16f7e8c15be346fba0cbf71588c7cdfb52c419d928a7d3db353afc1d952d19756237d8f10b9
checksum: 10c0/9cc821111ca244a01fb7f054db7523ab0a0cd837f665267eb962eb87695d71fb1e681f9e21464cc2fd7c05530dc4c81b810bca1a88f7d7186909b74477491a3c
languageName: node
linkType: hard
"nodemon@npm:^3.1.10":
version: 3.1.10
resolution: "nodemon@npm:3.1.10"
"nodemon@npm:^3.1.7":
version: 3.1.7
resolution: "nodemon@npm:3.1.7"
dependencies:
chokidar: "npm:^3.5.2"
debug: "npm:^4"
@@ -799,18 +848,18 @@ __metadata:
undefsafe: "npm:^2.0.5"
bin:
nodemon: bin/nodemon.js
checksum: 10c0/95b64d647f2c22e85e375b250517b0a4b32c2d2392ad898444e331f70d6b1ab43b17f53a8a1d68d5879ab8401fc6cd6e26f0d2a8736240984f6b5a8435b407c0
checksum: 10c0/e0b46939abdbce251b1d6281005a5763cee57db295bb00bc4a753b0f5320dac00fe53547fb6764c70a086cf6d1238875cccb800fbc71544b3ecbd3ef71183c87
languageName: node
linkType: hard
"nopt@npm:^8.0.0":
version: 8.1.0
resolution: "nopt@npm:8.1.0"
"nopt@npm:^7.0.0":
version: 7.2.1
resolution: "nopt@npm:7.2.1"
dependencies:
abbrev: "npm:^3.0.0"
abbrev: "npm:^2.0.0"
bin:
nopt: bin/nopt.js
checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef
checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81
languageName: node
linkType: hard
@@ -821,17 +870,12 @@ __metadata:
languageName: node
linkType: hard
"p-map@npm:^7.0.2":
version: 7.0.3
resolution: "p-map@npm:7.0.3"
checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c
languageName: node
linkType: hard
"package-json-from-dist@npm:^1.0.0":
version: 1.0.1
resolution: "package-json-from-dist@npm:1.0.1"
checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b
"p-map@npm:^4.0.0":
version: 4.0.0
resolution: "p-map@npm:4.0.0"
dependencies:
aggregate-error: "npm:^3.0.0"
checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75
languageName: node
linkType: hard
@@ -842,7 +886,7 @@ __metadata:
languageName: node
linkType: hard
"path-scurry@npm:^1.11.1":
"path-scurry@npm:^1.11.0":
version: 1.11.1
resolution: "path-scurry@npm:1.11.1"
dependencies:
@@ -859,17 +903,17 @@ __metadata:
languageName: node
linkType: hard
"picomatch@npm:^4.0.2":
version: 4.0.2
resolution: "picomatch@npm:4.0.2"
checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
"proc-log@npm:^3.0.0":
version: 3.0.0
resolution: "proc-log@npm:3.0.0"
checksum: 10c0/f66430e4ff947dbb996058f6fd22de2c66612ae1a89b097744e17fb18a4e8e7a86db99eda52ccf15e53f00b63f4ec0b0911581ff2aac0355b625c8eac509b0dc
languageName: node
linkType: hard
"proc-log@npm:^5.0.0":
version: 5.0.0
resolution: "proc-log@npm:5.0.0"
checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3
"proc-log@npm:^4.2.0":
version: 4.2.0
resolution: "proc-log@npm:4.2.0"
checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9
languageName: node
linkType: hard
@@ -899,6 +943,13 @@ __metadata:
languageName: node
linkType: hard
"readline-sync@npm:^1.4.10":
version: 1.4.10
resolution: "readline-sync@npm:1.4.10"
checksum: 10c0/0a4d0fe4ad501f8f005a3c9cbf3cc0ae6ca2ced93e9a1c7c46f226bdfcb6ef5d3f437ae7e9d2e1098ee13524a3739c830e4c8dbc7f543a693eecd293e41093a3
languageName: node
linkType: hard
"require-directory@npm:^2.1.1":
version: 2.1.1
resolution: "require-directory@npm:2.1.1"
@@ -914,11 +965,11 @@ __metadata:
linkType: hard
"rxjs@npm:^7.8.1":
version: 7.8.2
resolution: "rxjs@npm:7.8.2"
version: 7.8.1
resolution: "rxjs@npm:7.8.1"
dependencies:
tslib: "npm:^2.1.0"
checksum: 10c0/1fcd33d2066ada98ba8f21fcbbcaee9f0b271de1d38dc7f4e256bfbc6ffcdde68c8bfb69093de7eeb46f24b1fb820620bf0223706cff26b4ab99a7ff7b2e2c45
checksum: 10c0/3c49c1ecd66170b175c9cacf5cef67f8914dcbc7cd0162855538d365c83fea631167cacb644b3ce533b2ea0e9a4d0b12175186985f89d75abe73dbd8f7f06f68
languageName: node
linkType: hard
@@ -929,12 +980,43 @@ __metadata:
languageName: node
linkType: hard
"semver@npm:^7.3.5, semver@npm:^7.5.3":
version: 7.7.2
resolution: "semver@npm:7.7.2"
"semver@npm:^7.3.5":
version: 7.6.2
resolution: "semver@npm:7.6.2"
bin:
semver: bin/semver.js
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
checksum: 10c0/97d3441e97ace8be4b1976433d1c32658f6afaff09f143e52c593bae7eef33de19e3e369c88bd985ce1042c6f441c80c6803078d1de2a9988080b66684cbb30c
languageName: node
linkType: hard
"semver@npm:^7.5.3":
version: 7.6.3
resolution: "semver@npm:7.6.3"
bin:
semver: bin/semver.js
checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf
languageName: node
linkType: hard
"shadow-cljs-jar@npm:1.3.4":
version: 1.3.4
resolution: "shadow-cljs-jar@npm:1.3.4"
checksum: 10c0/c5548bb5f2bda5e0a90df6f42e4ec3a07ed4c72cdebb87619e8d9a2167bb3d4b60d6f6a305a3e15cbfb379d5fdbe2a989a0e7059b667cfb3911bc198a4489e94
languageName: node
linkType: hard
"shadow-cljs@npm:3.0.5":
version: 3.0.5
resolution: "shadow-cljs@npm:3.0.5"
dependencies:
readline-sync: "npm:^1.4.10"
shadow-cljs-jar: "npm:1.3.4"
source-map-support: "npm:^0.5.21"
which: "npm:^5.0.0"
ws: "npm:^8.18.1"
bin:
shadow-cljs: cli/runner.js
checksum: 10c0/2c5f3976f7bec16b7fb9fbba5d4a7581e0d0157384a470ce0670120f02cfe6b9c7183102133e0e9b300cbe318e9a3b6001309f96840999ca2814d39fc83c23e8
languageName: node
linkType: hard
@@ -955,9 +1037,9 @@ __metadata:
linkType: hard
"shell-quote@npm:^1.8.1":
version: 1.8.3
resolution: "shell-quote@npm:1.8.3"
checksum: 10c0/bee87c34e1e986cfb4c30846b8e6327d18874f10b535699866f368ade11ea4ee45433d97bf5eada22c4320c27df79c3a6a7eb1bf3ecfc47f2c997d9e5e2672fd
version: 1.8.1
resolution: "shell-quote@npm:1.8.1"
checksum: 10c0/8cec6fd827bad74d0a49347057d40dfea1e01f12a6123bf82c4649f3ef152fc2bc6d6176e6376bffcd205d9d0ccb4f1f9acae889384d20baff92186f01ea455a
languageName: node
linkType: hard
@@ -985,23 +1067,23 @@ __metadata:
linkType: hard
"socks-proxy-agent@npm:^8.0.3":
version: 8.0.5
resolution: "socks-proxy-agent@npm:8.0.5"
version: 8.0.3
resolution: "socks-proxy-agent@npm:8.0.3"
dependencies:
agent-base: "npm:^7.1.2"
agent-base: "npm:^7.1.1"
debug: "npm:^4.3.4"
socks: "npm:^2.8.3"
checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6
socks: "npm:^2.7.1"
checksum: 10c0/4950529affd8ccd6951575e21c1b7be8531b24d924aa4df3ee32df506af34b618c4e50d261f4cc603f1bfd8d426915b7d629966c8ce45b05fb5ad8c8b9a6459d
languageName: node
linkType: hard
"socks@npm:^2.8.3":
version: 2.8.5
resolution: "socks@npm:2.8.5"
"socks@npm:^2.7.1":
version: 2.8.3
resolution: "socks@npm:2.8.3"
dependencies:
ip-address: "npm:^9.0.5"
smart-buffer: "npm:^4.2.0"
checksum: 10c0/e427d0eb0451cfd04e20b9156ea8c0e9b5e38a8d70f21e55c30fbe4214eda37cfc25d782c63f9adc5fbdad6d062a0f127ef2cefc9a44b6fee2b9ea5d1ed10827
checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7
languageName: node
linkType: hard
@@ -1029,12 +1111,12 @@ __metadata:
languageName: node
linkType: hard
"ssri@npm:^12.0.0":
version: 12.0.0
resolution: "ssri@npm:12.0.0"
"ssri@npm:^10.0.0":
version: 10.0.6
resolution: "ssri@npm:10.0.6"
dependencies:
minipass: "npm:^7.0.3"
checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d
checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227
languageName: node
linkType: hard
@@ -1105,27 +1187,17 @@ __metadata:
languageName: node
linkType: hard
"tar@npm:^7.4.3":
version: 7.4.3
resolution: "tar@npm:7.4.3"
"tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.2.1
resolution: "tar@npm:6.2.1"
dependencies:
"@isaacs/fs-minipass": "npm:^4.0.0"
chownr: "npm:^3.0.0"
minipass: "npm:^7.1.2"
minizlib: "npm:^3.0.1"
mkdirp: "npm:^3.0.1"
yallist: "npm:^5.0.0"
checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d
languageName: node
linkType: hard
"tinyglobby@npm:^0.2.12":
version: 0.2.14
resolution: "tinyglobby@npm:0.2.14"
dependencies:
fdir: "npm:^6.4.4"
picomatch: "npm:^4.0.2"
checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
chownr: "npm:^2.0.0"
fs-minipass: "npm:^2.0.0"
minipass: "npm:^5.0.0"
minizlib: "npm:^2.1.1"
mkdirp: "npm:^1.0.3"
yallist: "npm:^4.0.0"
checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537
languageName: node
linkType: hard
@@ -1170,21 +1242,21 @@ __metadata:
languageName: node
linkType: hard
"unique-filename@npm:^4.0.0":
version: 4.0.0
resolution: "unique-filename@npm:4.0.0"
"unique-filename@npm:^3.0.0":
version: 3.0.0
resolution: "unique-filename@npm:3.0.0"
dependencies:
unique-slug: "npm:^5.0.0"
checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc
unique-slug: "npm:^4.0.0"
checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f
languageName: node
linkType: hard
"unique-slug@npm:^5.0.0":
version: 5.0.0
resolution: "unique-slug@npm:5.0.0"
"unique-slug@npm:^4.0.0":
version: 4.0.0
resolution: "unique-slug@npm:4.0.0"
dependencies:
imurmurhash: "npm:^0.1.4"
checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293
checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635
languageName: node
linkType: hard
@@ -1199,6 +1271,17 @@ __metadata:
languageName: node
linkType: hard
"which@npm:^4.0.0":
version: 4.0.0
resolution: "which@npm:4.0.0"
dependencies:
isexe: "npm:^3.1.1"
bin:
node-which: bin/which.js
checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a
languageName: node
linkType: hard
"which@npm:^5.0.0":
version: 5.0.0
resolution: "which@npm:5.0.0"
@@ -1232,7 +1315,22 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:^8.18.2":
"ws@npm:^8.17.0":
version: 8.17.0
resolution: "ws@npm:8.17.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
checksum: 10c0/55241ec93a66fdfc4bf4f8bc66c8eb038fda2c7a4ee8f6f157f2ca7dc7aa76aea0c0da0bf3adb2af390074a70a0e45456a2eaf80e581e630b75df10a64b0a990
languageName: node
linkType: hard
"ws@npm:^8.18.1":
version: 8.18.2
resolution: "ws@npm:8.18.2"
peerDependencies:
@@ -1261,13 +1359,6 @@ __metadata:
languageName: node
linkType: hard
"yallist@npm:^5.0.0":
version: 5.0.0
resolution: "yallist@npm:5.0.0"
checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416
languageName: node
linkType: hard
"yargs-parser@npm:^21.1.1":
version: 21.1.1
resolution: "yargs-parser@npm:21.1.1"

View File

@@ -1,16 +1,26 @@
FROM ubuntu:24.04 AS base
FROM ubuntu:24.04
LABEL maintainer="Penpot <docker@penpot.app>"
ENV LANG='C.UTF-8' \
LC_ALL='C.UTF-8' \
DEBIAN_FRONTEND=noninteractive
ARG DEBIAN_FRONTEND=noninteractive
ENV NODE_VERSION=v22.14.0 \
CLOJURE_VERSION=1.12.0.1501 \
CLJKONDO_VERSION=2025.01.16 \
BABASHKA_VERSION=1.12.196 \
CLJFMT_VERSION=0.13.0 \
RUSTUP_VERSION=1.27.1 \
RUST_VERSION=1.85.0 \
EMSCRIPTEN_VERSION=4.0.6 \
LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8
RUN set -ex; \
mkdir -p /etc/resolvconf/resolv.conf.d; \
echo "nameserver 8.8.8.8" > /etc/resolvconf/resolv.conf.d/tail; \
apt-get -qq update; \
apt-get -qq upgrade; \
apt-get -qqy --no-install-recommends install \
python3 \
unzip \
rsync \
apt-get -qqy install --no-install-recommends \
locales \
ca-certificates \
wget \
sudo \
tmux \
@@ -18,98 +28,97 @@ RUN set -ex; \
curl \
bash \
git \
\
curl \
ca-certificates \
\
binutils \
build-essential autoconf libtool pkg-config
; \
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen; \
locale-gen; \
rm -rf /var/lib/apt/lists/*;
COPY files/apt.sources /etc/apt/sources.list.d/ubuntu.sources
################################################################################
## IMAGE MAGICK
################################################################################
FROM base AS build-imagemagick
ENV IMAGEMAGICK_VERSION=7.1.1-47 \
DEBIAN_FRONTEND=noninteractive
RUN set -ex; \
usermod -l penpot -d /home/penpot -G users -s /bin/bash ubuntu; \
passwd penpot -d; \
echo "penpot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN set -ex; \
apt-get -qq update; \
apt-get -qq upgrade; \
apt-get -qqy --no-install-recommends install \
libltdl-dev \
libpng-dev \
libjpeg-dev \
libtiff-dev \
libwebp-dev \
libopenexr-dev \
libfftw3-dev \
libzip-dev \
liblcms2-dev \
liblzma-dev \
libzstd-dev \
libheif-dev \
librsvg2-dev \
apt-get -qqy install --no-install-recommends \
build-essential \
openssh-client \
redis-tools \
gnupg2 \
rlwrap \
unzip \
rsync \
fakeroot \
file \
less \
jq \
nginx \
\
python3 \
python3-tabulate \
imagemagick \
ghostscript \
netpbm \
poppler-utils \
potrace \
webp \
woff-tools \
woff2 \
fontforge \
libatk1.0-0 \
libatk-bridge2.0-0 \
libcairo2 \
libcups2 \
libdbus-1-3 \
libexpat1 \
libfontconfig1 \
libgcc1 \
libgdk-pixbuf2.0-0 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxi6 \
libxrandr2 \
libxrender1 \
libxshmfence1 \
libxss1 \
libxtst6 \
fonts-liberation \
libnss3 \
libgbm1 \
xvfb \
libfontconfig-dev \
\
fonts-noto-color-emoji \
fonts-unifont \
libfreetype6 \
xfonts-cyrillic \
xfonts-scalable \
fonts-ipafont-gothic \
fonts-wqy-zenhei \
fonts-tlwg-loma-otf \
fonts-freefont-ttf \
libasound2t64 \
libatk-bridge2.0-0t64 \
libatk1.0-0t64 \
libatspi2.0-0t64 \
libcups2t64 \
libdrm2 \
libxkbcommon0 \
; \
rm -rf /var/lib/apt/lists/*
RUN set -eux; \
curl -LfsSo /tmp/magick.tar.gz https://github.com/ImageMagick/ImageMagick/archive/refs/tags/${IMAGEMAGICK_VERSION}.tar.gz; \
mkdir -p /tmp/magick; \
cd /tmp/magick; \
tar -xf /tmp/magick.tar.gz --strip-components=1; \
./configure --prefix=/opt/imagick; \
make -j 2; \
make install; \
rm -rf /opt/imagick/lib/libMagick++*; \
rm -rf /opt/imagick/include; \
rm -rf /opt/imagick/share;
################################################################################
## NODE SETUP
################################################################################
FROM base AS setup-node
ENV NODE_VERSION=v22.16.0 \
PATH=/opt/node/bin:$PATH
RUN set -eux; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
OPENSSL_ARCH='linux-aarch64'; \
BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-arm64.tar.gz"; \
;; \
amd64|x86_64) \
OPENSSL_ARCH='linux-x86_64'; \
BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
curl -LfsSo /tmp/nodejs.tar.gz ${BINARY_URL}; \
mkdir -p /opt/node; \
cd /opt/node; \
tar -xf /tmp/nodejs.tar.gz --strip-components=1; \
chown -R root /opt/node; \
find /opt/node/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name "$OPENSSL_ARCH" -exec rm -rf {} \; ; \
corepack enable; \
rm -rf /tmp/nodejs.tar.gz;
################################################################################
## JVM SETUP
################################################################################
FROM base AS setup-jvm
ENV CLOJURE_VERSION=1.12.0.1501
rm -rf /var/lib/apt/lists/*;
RUN set -eux; \
ARCH="$(dpkg --print-architecture)"; \
@@ -129,33 +138,132 @@ RUN set -eux; \
esac; \
curl -LfsSo /tmp/openjdk.tar.gz ${BINARY_URL}; \
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
mkdir -p /opt/jdk; \
cd /opt/jdk; \
mkdir -p /usr/lib/jvm/openjdk; \
cd /usr/lib/jvm/openjdk; \
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
rm -rf /tmp/openjdk.tar.gz;
ENV PATH="/usr/lib/jvm/openjdk/bin:/usr/local/nodejs/bin:$PATH" JAVA_HOME=/usr/lib/jvm/openjdk
RUN set -ex; \
curl -LfsSo /tmp/clojure.sh https://download.clojure.org/install/linux-install-$CLOJURE_VERSION.sh; \
chmod +x /tmp/clojure.sh; \
mkdir -p /opt/clojure; \
/tmp/clojure.sh --prefix /opt/clojure; \
/tmp/clojure.sh; \
rm -rf /tmp/clojure.sh;
################################################################################
## RUST SETUP
################################################################################
RUN set -ex; \
install -d /usr/share/postgresql-common/pgdg; \
curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc; \
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt noble-pgdg main" >> /etc/apt/sources.list.d/postgresql.list; \
apt-get -qq update; \
apt-get -qqy install postgresql-client-16; \
rm -rf /var/lib/apt/lists/*;
FROM base AS setup-rust
RUN set -eux; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-arm64.tar.gz"; \
;; \
amd64|x86_64) \
BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
curl -LfsSo /tmp/nodejs.tar.gz ${BINARY_URL}; \
mkdir -p /usr/local/nodejs; \
cd /usr/local/nodejs; \
tar -xf /tmp/nodejs.tar.gz --strip-components=1; \
chown -R root /usr/local/nodejs; \
corepack enable; \
rm -rf /tmp/nodejs.tar.gz;
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://github.com/clj-kondo/clj-kondo/releases/download/v$CLJKONDO_VERSION/clj-kondo-$CLJKONDO_VERSION-linux-aarch64.zip"; \
;; \
amd64|x86_64) \
BINARY_URL="https://github.com/clj-kondo/clj-kondo/releases/download/v$CLJKONDO_VERSION/clj-kondo-$CLJKONDO_VERSION-linux-amd64.zip"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
cd /tmp; \
curl -LfsSo /tmp/clj-kondo.zip ${BINARY_URL}; \
cd /usr/local/bin; \
unzip /tmp/clj-kondo.zip; \
rm -rf /tmp/clj-kondo.zip;
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://github.com/babashka/babashka/releases/download/v$BABASHKA_VERSION/babashka-$BABASHKA_VERSION-linux-aarch64-static.tar.gz"; \
;; \
amd64|x86_64) \
BINARY_URL="https://github.com/babashka/babashka/releases/download/v$BABASHKA_VERSION/babashka-$BABASHKA_VERSION-linux-amd64-static.tar.gz"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
cd /tmp; \
curl -LfsSo /tmp/babashka.tar.gz ${BINARY_URL}; \
cd /usr/local/bin; \
tar -xf /tmp/babashka.tar.gz; \
rm -rf /tmp/babashka.tar.gz;
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://github.com/weavejester/cljfmt/releases/download/${CLJFMT_VERSION}/cljfmt-${CLJFMT_VERSION}-linux-aarch64.tar.gz"; \
;; \
amd64|x86_64) \
BINARY_URL="https://github.com/weavejester/cljfmt/releases/download/${CLJFMT_VERSION}/cljfmt-${CLJFMT_VERSION}-linux-amd64.tar.gz"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
cd /tmp; \
curl -LfsSo /tmp/cljfmt.tar.gz ${BINARY_URL}; \
cd /usr/local/bin; \
tar -xf /tmp/cljfmt.tar.gz; \
rm -rf /tmp/cljfmt.tar.gz;
# Install minio client
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://dl.min.io/client/mc/release/linux-arm64/mc"; \
;; \
amd64|x86_64) \
BINARY_URL="https://dl.min.io/client/mc/release/linux-amd64/mc"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
wget -O /tmp/mc ${BINARY_URL}; \
mv /tmp/mc /usr/local/bin/; \
chmod +x /usr/local/bin/mc;
WORKDIR /usr/local
# Install Rust toolchain
ENV PATH=/opt/cargo/bin:$PATH \
RUSTUP_HOME=/opt/rustup \
CARGO_HOME=/opt/cargo \
RUSTUP_VERSION=1.27.1 \
RUST_VERSION=1.85.0 \
EMSCRIPTEN_VERSION=4.0.6
WORKDIR /opt
ENV PATH=/usr/local/cargo/bin:$PATH RUSTUP_HOME=/usr/local/rustpo CARGO_HOME=/usr/local/cargo
RUN set -eux; \
# Same steps as in Rust official Docker image https://github.com/rust-lang/docker-rust/blob/9f287282d513a84cb7c7f38f197838f15d37b6a9/1.81.0/bookworm/Dockerfile
@@ -177,217 +285,8 @@ RUN set -eux; \
./emsdk install $EMSCRIPTEN_VERSION; \
./emsdk activate $EMSCRIPTEN_VERSION; \
rustup target add wasm32-unknown-emscripten; \
cargo install cargo-watch;
################################################################################
## UTILS SETUP
################################################################################
FROM base AS setup-utils
ENV CLJKONDO_VERSION=2025.01.16 \
BABASHKA_VERSION=1.12.196 \
CLJFMT_VERSION=0.13.0
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://github.com/clj-kondo/clj-kondo/releases/download/v$CLJKONDO_VERSION/clj-kondo-$CLJKONDO_VERSION-linux-aarch64.zip"; \
;; \
amd64|x86_64) \
BINARY_URL="https://github.com/clj-kondo/clj-kondo/releases/download/v$CLJKONDO_VERSION/clj-kondo-$CLJKONDO_VERSION-linux-amd64.zip"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
cd /tmp; \
curl -LfsSo /tmp/clj-kondo.zip ${BINARY_URL}; \
mkdir -p /opt/utils/bin; \
cd /opt/utils/bin; \
unzip /tmp/clj-kondo.zip; \
rm -rf /tmp/clj-kondo.zip;
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://github.com/babashka/babashka/releases/download/v$BABASHKA_VERSION/babashka-$BABASHKA_VERSION-linux-aarch64-static.tar.gz"; \
;; \
amd64|x86_64) \
BINARY_URL="https://github.com/babashka/babashka/releases/download/v$BABASHKA_VERSION/babashka-$BABASHKA_VERSION-linux-amd64-static.tar.gz"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
cd /tmp; \
curl -LfsSo /tmp/babashka.tar.gz ${BINARY_URL}; \
cd /opt/utils/bin; \
tar -xf /tmp/babashka.tar.gz; \
rm -rf /tmp/babashka.tar.gz;
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://github.com/weavejester/cljfmt/releases/download/${CLJFMT_VERSION}/cljfmt-${CLJFMT_VERSION}-linux-aarch64.tar.gz"; \
;; \
amd64|x86_64) \
BINARY_URL="https://github.com/weavejester/cljfmt/releases/download/${CLJFMT_VERSION}/cljfmt-${CLJFMT_VERSION}-linux-amd64.tar.gz"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
cd /tmp; \
curl -LfsSo /tmp/cljfmt.tar.gz ${BINARY_URL}; \
cd /opt/utils/bin; \
tar -xf /tmp/cljfmt.tar.gz; \
rm -rf /tmp/cljfmt.tar.gz;
# Install minio client
RUN set -ex; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
BINARY_URL="https://dl.min.io/client/mc/release/linux-arm64/mc"; \
;; \
amd64|x86_64) \
BINARY_URL="https://dl.min.io/client/mc/release/linux-amd64/mc"; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
wget -O /tmp/mc ${BINARY_URL}; \
mv /tmp/mc /opt/utils/bin/; \
chmod +x /opt/utils/bin/mc;
################################################################################
## DEVENV BASE
################################################################################
FROM base AS devenv-base
RUN set -ex; \
usermod -l penpot -d /home/penpot -G users -s /bin/bash ubuntu; \
passwd penpot -d; \
echo "penpot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN set -ex; \
apt-get -qq update; \
apt-get -qqy install --no-install-recommends \
redis-tools \
gnupg2 \
rlwrap \
file \
less \
jq \
nginx \
\
fontconfig \
woff-tools \
woff2 \
python3-tabulate \
fontforge \
\
xvfb \
fonts-noto-color-emoji \
fonts-unifont \
libfontconfig1 \
libfontconfig-dev \
libfreetype6 \
libfreetype-dev \
xfonts-cyrillic \
xfonts-scalable \
fonts-liberation \
fonts-ipafont-gothic \
fonts-wqy-zenhei \
fonts-tlwg-loma-otf \
fonts-freefont-ttf \
\
libasound2t64 \
libatk-bridge2.0-0t64 \
libatk1.0-0t64 \
libatspi2.0-0t64 \
libcairo2 \
libcups2t64 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libglib2.0-0t64 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libx11-6 \
libxcb1 \
libxcomposite1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxkbcommon0 \
libxrandr2 \
\
libpng16-16 \
libjpeg-turbo8 \
libtiff6 \
libwebp7 \
libopenexr-3-1-30 \
libfreetype6 \
libfontconfig1 \
libglib2.0-0 \
libxml2 \
liblcms2-2 \
libheif1 \
libopenjp2-7 \
libzstd1 \
librsvg2-2 \
libgomp1 \
libwebpmux3 \
libwebpdemux2 \
libzip4t64 \
; \
rm -rf /var/lib/apt/lists/*;
RUN set -ex; \
install -d /usr/share/postgresql-common/pgdg; \
curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc; \
echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt noble-pgdg main" >> /etc/apt/sources.list.d/postgresql.list; \
apt-get -qq update; \
apt-get -qqy install postgresql-client-16; \
rm -rf /var/lib/apt/lists/*;
################################################################################
## DEVENV
################################################################################
FROM devenv-base AS devenv
LABEL maintainer="Penpot <docker@penpot.app>"
ENV LANG='C.UTF-8' \
LC_ALL='C.UTF-8' \
DEBIAN_FRONTEND="noninteractive" \
JAVA_HOME="/opt/jdk" \
CARGO_HOME="/opt/cargo" \
RUSTUP_HOME="/opt/rustup" \
PATH="/opt/jdk/bin:/opt/utils/bin:/opt/clojure/bin:/opt/node/bin:/opt/imagick/bin:/opt/cargo/bin:$PATH"
COPY --from=build-imagemagick /opt/imagick /opt/imagick
COPY --from=setup-jvm /opt/jdk /opt/jdk
COPY --from=setup-jvm /opt/clojure /opt/clojure
COPY --from=setup-node /opt/node /opt/node
COPY --from=setup-utils /opt/utils /opt/utils
COPY --from=setup-rust /opt/cargo /opt/cargo
COPY --from=setup-rust /opt/rustup /opt/rustup
COPY --from=setup-rust /opt/emsdk /opt/emsdk
cargo install cargo-watch; \
chown -R penpot:users $CARGO_HOME;
COPY files/nginx.conf /etc/nginx/nginx.conf
COPY files/nginx-mime.types /etc/nginx/mime.types
@@ -404,4 +303,4 @@ COPY files/entrypoint.sh /home/entrypoint.sh
COPY files/init.sh /home/init.sh
ENTRYPOINT ["/home/entrypoint.sh"]
CMD ["/home/init.sh"]
CMD ["/home/init.sh"]

View File

@@ -9,7 +9,7 @@ volumes:
postgres_data_pg16:
user_data:
minio_data:
valkey_data:
redis_data:
services:
main:
@@ -98,13 +98,12 @@ services:
- postgres_data_pg16:/var/lib/postgresql/data
redis:
image: valkey/valkey:8.1
hostname: "penpot-devenv-valkey"
container_name: "penpot-devenv-valkey"
image: redis:7.2
hostname: "penpot-devenv-redis"
container_name: "penpot-devenv-redis"
restart: always
command: valkey-server --save 120 1 --loglevel warning
volumes:
- "valkey_data:/data"
- "redis_data:/data"
mailer:
image: sj26/mailcatcher:latest

View File

@@ -1,10 +1,5 @@
#!/usr/bin/env bash
EMSDK_QUIET=1 . /opt/emsdk/emsdk_env.sh;
export PATH="/home/penpot/.cargo/bin:/opt/jdk/bin:/opt/utils/bin:/opt/clojure/bin:/opt/node/bin:/opt/imagick/bin:/opt/cargo/bin:$PATH"
export CARGO_HOME="/home/penpot/.cargo"
alias l='ls --color -GFlh'
alias rm='rm -r'
alias ls='ls --color -F'

View File

@@ -2,7 +2,7 @@
set -e
EMSDK_QUIET=1 . /opt/emsdk/emsdk_env.sh;
EMSDK_QUIET=1 . /usr/local/emsdk/emsdk_env.sh;
usermod -u ${EXTERNAL_UID:-1000} penpot;
@@ -11,7 +11,7 @@ cp /root/.vimrc /home/penpot/.vimrc
cp /root/.tmux.conf /home/penpot/.tmux.conf
chown -R penpot:users /home/penpot
rsync -ar --chown=penpot:users /opt/cargo/ /home/penpot/.cargo/
rsync -ar --chown=penpot:users /usr/local/cargo/ /home/penpot/.cargo/
export PATH="/home/penpot/.cargo/bin:$PATH"
export CARGO_HOME="/home/penpot/.cargo"

View File

@@ -10,12 +10,10 @@ echo "[start-tmux.sh] Installing node dependencies"
pushd ~/penpot/frontend/
corepack install;
yarn install;
yarn playwright install chromium
popd
pushd ~/penpot/exporter/
corepack install;
yarn install
yarn playwright install chromium
popd
tmux -2 new-session -d -s penpot

View File

@@ -1,51 +1,49 @@
FROM ubuntu:24.04 AS build
FROM ubuntu:24.04
LABEL maintainer="Penpot <docker@penpot.app>"
ENV LANG='C.UTF-8' \
LC_ALL='C.UTF-8' \
ENV LANG='en_US.UTF-8' \
LC_ALL='en_US.UTF-8' \
JAVA_HOME="/opt/jdk" \
PATH=/opt/jdk/bin:/opt/node/bin:$PATH \
DEBIAN_FRONTEND=noninteractive \
NODE_VERSION=v22.16.0 \
IMAGEMAGICK_VERSION=7.1.1-47 \
NODE_VERSION=v20.18.0 \
TZ=Etc/UTC
RUN set -ex; \
useradd -U -M -u 1001 -s /bin/false -d /opt/penpot penpot; \
apt-get -qq update; \
apt-get -qq upgrade; \
apt-get -qqy --no-install-recommends install \
nano \
curl \
tzdata \
locales \
ca-certificates \
binutils \
build-essential autoconf libtool pkg-config \
libltdl-dev \
libpng-dev libjpeg-dev libtiff-dev libwebp-dev libopenexr-dev libfftw3-dev \
libzip-dev \
liblcms2-dev liblzma-dev libzstd-dev \
libheif-dev librsvg2-dev \
imagemagick \
webp \
rlwrap \
fontconfig \
woff-tools \
woff2 \
python3 \
python3-tabulate \
fontforge \
; \
rm -rf /var/lib/apt/lists/*
RUN set -eux; \
curl -LfsSo /tmp/magick.tar.gz https://github.com/ImageMagick/ImageMagick/archive/refs/tags/${IMAGEMAGICK_VERSION}.tar.gz; \
mkdir -p /tmp/magick; \
cd /tmp/magick; \
tar -xf /tmp/magick.tar.gz --strip-components=1; \
./configure --prefix=/opt/imagick; \
make -j 2; \
make install; \
rm -rf /opt/imagick/lib/libMagick++*; \
rm -rf /opt/imagick/include; \
rm -rf /opt/imagick/share;
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen; \
locale-gen; \
mkdir -p /opt/data/assets; \
mkdir -p /opt/penpot; \
chown -R penpot:penpot /opt/penpot; \
chown -R penpot:penpot /opt/data; \
rm -rf /var/lib/apt/lists/*;
RUN set -eux; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
OPENSSL_ARCH='linux-aarch64'; \
BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-arm64.tar.gz"; \
;; \
amd64|x86_64) \
OPENSSL_ARCH='linux-x86_64'; \
BINARY_URL="https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz"; \
;; \
*) \
@@ -58,19 +56,18 @@ RUN set -eux; \
cd /opt/node; \
tar -xf /tmp/nodejs.tar.gz --strip-components=1; \
chown -R root /opt/node; \
find /opt/node/include/node/openssl/archs -mindepth 1 -maxdepth 1 ! -name "$OPENSSL_ARCH" -exec rm -rf {} \; ; \
rm -rf /tmp/nodejs.tar.gz;
RUN set -eux; \
ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \
aarch64|arm64) \
ESUM='18071047526ab4b53131f9bb323e8703485ae37fcb2f2c5ef0f1b7bab66d1b94'; \
BINARY_URL='https://github.com/adoptium/temurin24-binaries/releases/download/jdk-24%2B36/OpenJDK24U-jdk_aarch64_linux_hotspot_24_36.tar.gz'; \
ESUM='3ce6a2b357e2ef45fd6b53d6587aa05bfec7771e7fb982f2c964f6b771b7526a'; \
BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.2_13.tar.gz'; \
;; \
amd64|x86_64) \
ESUM='c340dee97b6aa215d248bc196dcac5b56e7be9b5c5d45e691344d40d5d0b171d'; \
BINARY_URL='https://github.com/adoptium/temurin24-binaries/releases/download/jdk-24%2B36/OpenJDK24U-jdk_x64_linux_hotspot_24_36.tar.gz'; \
ESUM='454bebb2c9fe48d981341461ffb6bf1017c7b7c6e15c6b0c29b959194ba3aaa5'; \
BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jdk_x64_linux_hotspot_21.0.2_13.tar.gz'; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
@@ -82,69 +79,8 @@ RUN set -eux; \
mkdir -p /opt/jdk; \
cd /opt/jdk; \
tar -xf /tmp/openjdk.tar.gz --strip-components=1; \
rm -rf /tmp/openjdk.tar.gz; \
/opt/jdk/bin/jlink \
--no-header-files \
--no-man-pages \
--strip-debug \
--add-modules java.base,jdk.management.agent,java.se,jdk.compiler,jdk.javadoc,jdk.attach,jdk.unsupported \
--output /opt/jre;
rm -rf /tmp/openjdk.tar.gz;
FROM ubuntu:24.04 AS image
LABEL maintainer="Penpot <docker@penpot.app>"
ENV LANG='C.UTF-8' \
LC_ALL='C.UTF-8' \
JAVA_HOME="/opt/jre" \
PATH=/opt/jre/bin:/opt/node/bin:/opt/imagick/bin:$PATH \
DEBIAN_FRONTEND=noninteractive \
TZ=Etc/UTC
RUN set -ex; \
useradd -U -M -u 1001 -s /bin/false -d /opt/penpot penpot; \
apt-get -qq update; \
apt-get -qq upgrade; \
apt-get -qqy --no-install-recommends install \
tzdata \
ca-certificates \
fontconfig \
woff-tools \
woff2 \
python3 \
python3-tabulate \
fontforge \
\
libpng16-16 \
libjpeg-turbo8 \
libtiff6 \
libwebp7 \
libopenexr-3-1-30 \
libfreetype6 \
libfontconfig1 \
libglib2.0-0 \
libxml2 \
liblcms2-2 \
libheif1 \
libopenjp2-7 \
libzstd1 \
librsvg2-2 \
libgomp1 \
libwebpmux3 \
libwebpdemux2 \
libzip4t64 \
; \
find tmp/usr/share/zoneinfo/* -type d ! -name 'Etc' |xargs rm -rf; \
rm -rf /var/lib /var/cache; \
rm -rf /usr/include; \
mkdir -p /opt/data/assets; \
mkdir -p /opt/penpot; \
chown -R penpot:penpot /opt/penpot; \
chown -R penpot:penpot /opt/data; \
rm -rf /var/lib/apt/lists/*;
COPY --from=build /opt/jre /opt/jre
COPY --from=build /opt/node /opt/node
COPY --from=build /opt/imagick /opt/imagick
COPY --chown=penpot:penpot ./bundle-backend/ /opt/penpot/backend/
USER penpot:penpot

View File

@@ -3,7 +3,7 @@ LABEL maintainer="Penpot <docker@penpot.app>"
ENV LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8 \
NODE_VERSION=v22.16.0 \
NODE_VERSION=v20.11.1 \
DEBIAN_FRONTEND=noninteractive \
PATH=/opt/node/bin:$PATH
@@ -17,50 +17,56 @@ RUN set -ex; \
tzdata \
locales \
ca-certificates \
fontconfig \
xz-utils \
; \
rm -rf /var/lib/apt/lists/*; \
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen; \
locale-gen; \
find /usr/share/i18n/locales/ -type f ! -name "en_US" ! -name "POSIX" ! -name "C" -delete;
locale-gen;
RUN set -ex; \
apt-get -qq update; \
apt-get -qqy install \
\
xvfb \
fonts-noto-color-emoji \
fonts-unifont \
libfontconfig1 \
libfreetype6 \
xfonts-cyrillic \
xfonts-scalable \
fonts-liberation \
fonts-ipafont-gothic \
fonts-wqy-zenhei \
fonts-tlwg-loma-otf \
fonts-freefont-ttf \
\
imagemagick \
ghostscript \
netpbm \
poppler-utils \
potrace \
dconf-service \
libasound2t64 \
libatk-bridge2.0-0t64 \
libatk1.0-0t64 \
libatspi2.0-0t64 \
libatk1.0-0 \
libatk-bridge2.0-0 \
libatomic1 \
libcairo2 \
libcups2t64 \
libcups2 \
libdbus-1-3 \
libdrm2 \
libgbm1 \
libglib2.0-0t64 \
libexpat1 \
libfontconfig1 \
libgcc1 \
libgdk-pixbuf2.0-0 \
libglib2.0-0 \
libgtk-3-0 \
libnspr4 \
libnss3 \
libpango-1.0-0 \
libpangocairo-1.0-0 \
libx11-6 \
libx11-xcb1 \
libxcb1 \
libxcb-dri3-0 \
libxcomposite1 \
libxcursor1 \
libxdamage1 \
libxext6 \
libxfixes3 \
libxkbcommon0 \
libxi6 \
libxrandr2 \
libxrender1 \
libxshmfence1 \
libxss1 \
libxtst6 \
fonts-liberation \
libnss3 \
libgbm1 \
; \
rm -rf /var/lib/apt/lists/*;
@@ -83,8 +89,8 @@ RUN set -eux; \
cd /opt/node; \
tar -xf /tmp/nodejs.tar.gz --strip-components=1; \
chown -R root /opt/node; \
rm -rf /tmp/nodejs.tar.gz; \
corepack enable; \
rm -rf /tmp/nodejs.tar.gz; \
mkdir -p /opt/penpot; \
chown -R penpot:penpot /opt/penpot;
@@ -94,9 +100,7 @@ WORKDIR /opt/penpot/exporter
USER penpot:penpot
RUN set -ex; \
corepack install; \
yarn install; \
yarn run playwright install chromium; \
rm -rf /opt/penpot/.yarn
yarn run playwright install chromium;
CMD ["node", "app.js"]

View File

@@ -111,7 +111,7 @@ services:
depends_on:
penpot-postgres:
condition: service_healthy
penpot-valkey:
penpot-redis:
condition: service_healthy
networks:
@@ -148,10 +148,10 @@ services:
PENPOT_DATABASE_USERNAME: penpot
PENPOT_DATABASE_PASSWORD: penpot
## Valkey (or previously redis) is used for the websockets notifications. Don't touch
## unless the valkey container has different parameters or different name.
## Redis is used for the websockets notifications. Don't touch unless the redis
## container has different parameters or different name.
PENPOT_REDIS_URI: redis://penpot-valkey/0
PENPOT_REDIS_URI: redis://penpot-redis/0
## Default configuration for assets storage: using filesystem based with all files
## stored in a docker volume.
@@ -195,7 +195,7 @@ services:
restart: always
depends_on:
penpot-valkey:
penpot-redis:
condition: service_healthy
networks:
@@ -206,8 +206,8 @@ services:
# communicate with the frontend.
PENPOT_PUBLIC_URI: http://penpot-frontend:8080
## Valkey (or previously Redis) is used for the websockets notifications.
PENPOT_REDIS_URI: redis://penpot-valkey/0
## Redis is used for the websockets notifications.
PENPOT_REDIS_URI: redis://penpot-redis/0
penpot-postgres:
image: "postgres:15"
@@ -233,12 +233,12 @@ services:
- POSTGRES_USER=penpot
- POSTGRES_PASSWORD=penpot
penpot-valkey:
image: valkey/valkey:8.1
penpot-redis:
image: redis:7.2
restart: always
healthcheck:
test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
interval: 1s
timeout: 3s
retries: 5

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -140,8 +140,7 @@ The <code class="language-js">manifest.json</code> file contains the basic infor
"user:read",
"comment:read",
"comment:write",
"allow:downloads",
"allow:localstorage"
"allow:downloads"
]
}
```
@@ -174,9 +173,6 @@ Typical use cases: adding new comments to pages; deleting existing comments; rep
- <code class="language-js">allow:downloads</code>: Allows downloading of the project file. Grants access to endpoints and operations that enable the downloading of the entire project file.
Typical use cases: downloading the full project file for backup or sharing.
- <code class="language-js">allow:localstorage</code>: Allows the access to the local storage proxy to store information. This info is only available for the plugin installation but be aware that a user can see this information in the browser.
Typical use cases: storing authentication tokens for a plugin login
_Note: Write permissions automatically includes its corresponding read permission (e.g., <code class="language-js">content:write</code> includes <code class="language-js">content:read</code>) because reading is required to perform write or modification actions._
### What are plugin.ts and plugin.js files?

View File

@@ -353,9 +353,9 @@ If you are not using SMTP configuration and want to log the emails in the consol
PENPOT_FLAGS: [...] enable-log-emails
```
## Valkey
## Redis
The Valkey configuration is very simple, just provide a valid redis URI. Valkey is used
The Redis configuration is very simple, just provide a valid redis URI. Redis is used
mainly for websocket notifications coordination.
```bash

View File

@@ -72,7 +72,7 @@ argument to helm install. For example,
```bash
helm install my-release \
--set global.postgresqlEnabled=true \
--set global.valkeyEnabled=true \
--set global.redisEnabled=true \
--set persistence.assets.enabled=true \
penpot/penpot
```

View File

@@ -367,97 +367,12 @@ title: 10· Design Tokens
<h2 id="design-tokens-import-export">Importing and Exporting Tokens</h2>
<p>You can export Tokens from Penpot and import them from your computer to a Penpot file. Tokens can be imported from the <strong>Tools</strong> option at the bottom of the <strong>Tokens</strong> tab.</p>
<p>The <strong>Import</strong> functionality allows you to upload and replace the global token set using a single file or a folder with multiple files in it.</p>
<p>The <strong>Import</strong> functionality allows you to upload and replace the global token set using a single file, while the <strong>Export</strong> functionality lets you download the current global token set using a single file to your system.</p>
<p>These features support JSON files formatted according to specific guidelines and preserve the ability to undo changes if needed.</p>
<figure>
<img src="/img/design-tokens/21-tokens-import-export.webp" alt="Tokens import export" />
</figure>
<ol>
<li><strong>Import:</strong> Click <strong>Tools</strong>, then select <strong>Import</strong> to view import options. </li>
<li><strong>Export:</strong> Click <strong>Tools</strong>, then select <strong>Export</strong> to view export options.</li>
<li><strong>Import:</strong> At the <strong>Tools</strong> option, select <strong>Import</strong>, then select your <code class="language-js">tokens.json</code> file. </li>
<li><strong>Export:</strong> At the <strong>Tools</strong> option, select <strong>Export</strong>. This will export all the tokens, including token sets and themes.</li>
</ol>
<h3 id="design-tokens-import-options">Import Options</h3>
<h4>Single file</h4>
<p>You can import a JSON file comprising all tokens, token sets and token themes.</p>
<p>When importing a single file, the first-level keys of the json file will be interpreted as the set name.</p>
<pre class="language-json">
<code class="language-json">
{
"Global": {
// first-level key will be interpreted as set name
"color": {
"300": {
"$value": "red",
"$type": "color",
"$description": "my token description"
}
}
},
"Brands/A": {
// first-level key will be interpreted as set name
"color": {
"accent": {
"$value": "{red}",
"$type": "color",
"$description": "my token description"
}
}
},
"Brands/B": {
// first-level key will be interpreted as set name
"color": {
"accent": {
"$value": "#fabada",
"$type": "color",
"$description": "my token description"
}
}
}
}
</code>
</pre>
<h4>Multifile</h4>
<p>Imports a folder containing multiple JSON files (one per Token Set) and additional files like <code class="language-json">$themes.json</code> or <code class="language-json">$metadata.json</code> configurations. When importing multiple files, the name and path of the individual json files inside the folder will be interpreted as set names. These files should only contain tokens.</p>
<p>Multifile folder structure example:</p>
<code>
<pre>
folder/
├── global/
│ ├── colors.json // can only contain tokens
│ └── dimension.json // can only contain tokens
├── mode/
│ ├── dark.json // can only contain tokens
│ └── light.json // can only contain tokens
├── $themes.json // themes config
└── $metadata.json // other metadata config
</code>
</pre>
<figcaption>The main folder name wont be used to build token set names, so in this example, <strong>folder</strong> will be ignored in the set names.</figcaption>
<h3 id="design-tokens-export-options">Export Options</h3>
<p>Just like with importing, you can export tokens, themes and sets either in a single JSON file or in multiple files. There is no difference in the content being exported; the choice depends on your team's preferences for file organization: a single file with all the tokens, sets and themes, or a folder structure with separated JSON files organized by sets.</p>
<p>In both cases you can preview the result of the export options:</p>
<figure>
<img src="/img/design-tokens/22-tokens-export-multiple.webp" alt="Tokens export with multiple files" />
<figcaption>Exporting tokens as multiple files.</figcaption>
</figure>
<figure>
<img src="/img/design-tokens/23-tokens-export-single.webp" alt="Tokens export with single file" />
<figcaption>Exporting tokens as a single file.</figcaption>
</figure>

View File

@@ -14,7 +14,7 @@
:dev
{:extra-deps
{thheller/shadow-cljs {:mvn/version "3.1.5"}}}
{thheller/shadow-cljs {:mvn/version "3.1.4"}}}
:shadow-cljs
{:main-opts ["-m" "shadow.cljs.devtools.cli"]

View File

@@ -4,7 +4,7 @@
"license": "MPL-2.0",
"author": "Kaleidos INC",
"private": true,
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c",
"packageManager": "yarn@4.9.1+sha512.f95ce356460e05be48d66401c1ae64ef84d163dd689964962c6888a9810865e39097a5e9de748876c2e0bf89b232d583c33982773e9903ae7a76257270986538",
"repository": {
"type": "git",
"url": "https://github.com/penpot/penpot"
@@ -16,15 +16,15 @@
"inflation": "^2.1.0",
"ioredis": "^5.6.1",
"luxon": "^3.6.1",
"playwright": "^1.53.0",
"playwright": "^1.52.0",
"raw-body": "^3.0.0",
"svgo": "penpot/svgo#v3.1",
"xml-js": "^1.6.11",
"xregexp": "^5.1.2"
},
"devDependencies": {
"source-map-support": "^0.5.21",
"ws": "^8.18.2"
"shadow-cljs": "3.0.5",
"source-map-support": "^0.5.21"
},
"scripts": {
"fmt:clj:check": "cljfmt check --parallel=false src/",

View File

@@ -6,7 +6,7 @@ export CURRENT_VERSION=$1;
export NODE_ENV=production;
corepack enable;
corepack install || exit 1;
corepack up || exit 1;
yarn install || exit 1;
rm -rf target

View File

@@ -47,7 +47,7 @@
(s/def ::params
(s/keys :req-un [::exports ::profile-id]
:opt-un [::wait ::name ::skip-children]))
:opt-un [::wait ::name]))
(defn handler
[{:keys [:request/auth-token] :as exchange} {:keys [exports] :as params}]
@@ -60,7 +60,7 @@
(handle-multiple-export exchange (assoc params :exports exports)))))
(defn- handle-single-export
[exchange {:keys [export wait profile-id name skip-children] :as params}]
[exchange {:keys [export wait profile-id name] :as params}]
(let [topic (str profile-id)
resource (rsc/create (:type export) (or name (:name export)))
@@ -90,7 +90,7 @@
:resource-id (:id resource)
:status "error"
:cause (ex-message cause)})))
export (assoc export :skip-children skip-children)
proc (-> (rd/render export on-progress)
(p/then (constantly resource))
(p/catch on-error))]
@@ -99,7 +99,7 @@
(assoc exchange :response/body (dissoc resource :path)))))
(defn- handle-multiple-export
[exchange {:keys [exports wait profile-id name skip-children] :as params}]
[exchange {:keys [exports wait profile-id name] :as params}]
(let [resource (rsc/create :zip (or name (-> exports first :name)))
total (count exports)
topic (str profile-id)
@@ -141,8 +141,7 @@
proc (-> (p/do
(p/loop [exports (seq exports)]
(when-let [export (some-> (first exports)
(assoc :skip-children skip-children))]
(when-let [export (first exports)]
(p/do
(rd/render export append)
(p/recur (rest exports)))))

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