mirror of
https://github.com/penpot/penpot.git
synced 2026-02-05 20:22:15 -05:00
Compare commits
33 Commits
xaviju-131
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ccb33ba89 | ||
|
|
ee88ee63a2 | ||
|
|
f961f9a123 | ||
|
|
dda3377596 | ||
|
|
17935443df | ||
|
|
150d57b1eb | ||
|
|
490619119e | ||
|
|
834b513562 | ||
|
|
1656fefdc9 | ||
|
|
7f318bb110 | ||
|
|
44c7d3fbd6 | ||
|
|
3d50aa6cb2 | ||
|
|
06afd94a74 | ||
|
|
e7d9dca55e | ||
|
|
c14ccc18b8 | ||
|
|
ca4d00df69 | ||
|
|
9667477d6b | ||
|
|
485005477e | ||
|
|
86ca260ea2 | ||
|
|
24c8fc484f | ||
|
|
bc16b8ddc3 | ||
|
|
b07c98faa5 | ||
|
|
25aff100cf | ||
|
|
5be887f10b | ||
|
|
f7403935c8 | ||
|
|
7d09d930fe | ||
|
|
79be3ab7df | ||
|
|
629649aca6 | ||
|
|
cc326f23cf | ||
|
|
2c4efc6b53 | ||
|
|
4d5c874b91 | ||
|
|
e3b97638b4 | ||
|
|
daedc660b9 |
@@ -34,11 +34,7 @@
|
||||
- Fix viewer can update library [Taiga #13186](https://tree.taiga.io/project/penpot/issue/13186)
|
||||
- Fix remove fill affects different element than selected [Taiga #13128](https://tree.taiga.io/project/penpot/issue/13128)
|
||||
|
||||
## 2.13.0 (Unreleased)
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
### :rocket: Epics and highlights
|
||||
## 2.13.0
|
||||
|
||||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
:deps
|
||||
{penpot/common {:local/root "../common"}
|
||||
org.clojure/clojure {:mvn/version "1.12.2"}
|
||||
org.clojure/clojure {:mvn/version "1.12.4"}
|
||||
org.clojure/tools.namespace {:mvn/version "1.5.0"}
|
||||
|
||||
com.github.luben/zstd-jni {:mvn/version "1.5.7-4"}
|
||||
@@ -28,8 +28,8 @@
|
||||
com.google.guava/guava {:mvn/version "33.4.8-jre"}
|
||||
|
||||
funcool/yetti
|
||||
{:git/tag "v11.8"
|
||||
:git/sha "1d1b33f"
|
||||
{:git/tag "v11.9"
|
||||
:git/sha "5fad7a9"
|
||||
:git/url "https://github.com/funcool/yetti.git"
|
||||
:exclusions [org.slf4j/slf4j-api]}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
metosin/reitit-core {:mvn/version "0.9.1"}
|
||||
nrepl/nrepl {:mvn/version "1.4.0"}
|
||||
|
||||
org.postgresql/postgresql {:mvn/version "42.7.7"}
|
||||
org.postgresql/postgresql {:mvn/version "42.7.9"}
|
||||
org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
|
||||
|
||||
com.zaxxer/HikariCP {:mvn/version "7.0.2"}
|
||||
@@ -49,7 +49,7 @@
|
||||
buddy/buddy-hashers {:mvn/version "2.0.167"}
|
||||
buddy/buddy-sign {:mvn/version "3.6.1-359"}
|
||||
|
||||
com.github.ben-manes.caffeine/caffeine {:mvn/version "3.2.2"}
|
||||
com.github.ben-manes.caffeine/caffeine {:mvn/version "3.2.3"}
|
||||
|
||||
org.jsoup/jsoup {:mvn/version "1.21.2"}
|
||||
org.im4java/im4java
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
;; Pretty Print specs
|
||||
pretty-spec/pretty-spec {:mvn/version "0.1.4"}
|
||||
software.amazon.awssdk/s3 {:mvn/version "2.33.10"}}
|
||||
software.amazon.awssdk/s3 {:mvn/version "2.41.21"}}
|
||||
|
||||
:paths ["src" "resources" "target/classes"]
|
||||
:aliases
|
||||
|
||||
@@ -369,6 +369,7 @@
|
||||
;; FIXME: revisit if db/pool is necessary here
|
||||
::db/pool (ig/ref ::db/pool)
|
||||
::session/manager (ig/ref ::session/manager)
|
||||
::setup/props (ig/ref ::setup/props)
|
||||
::setup/shared-keys (ig/ref ::setup/shared-keys)}
|
||||
|
||||
::wrk/registry
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.logging :as l]
|
||||
[app.db :as db]
|
||||
[app.migrations.clj.migration-0023 :as mg0023]
|
||||
[app.migrations.clj.migration-0145 :as mg0145]
|
||||
[app.util.migrations :as mg]
|
||||
[integrant.core :as ig]))
|
||||
|
||||
@@ -459,7 +460,11 @@
|
||||
:fn (mg/resource "app/migrations/sql/0143-add-http-session-v2-table.sql")}
|
||||
|
||||
{:name "0144-mod-server-error-report-table"
|
||||
:fn (mg/resource "app/migrations/sql/0144-mod-server-error-report-table.sql")}])
|
||||
:fn (mg/resource "app/migrations/sql/0144-mod-server-error-report-table.sql")}
|
||||
|
||||
{:name "0145-fix-plugins-uri-on-profile"
|
||||
:fn mg0145/migrate}])
|
||||
|
||||
|
||||
(defn apply-migrations!
|
||||
[pool name migrations]
|
||||
|
||||
83
backend/src/app/migrations/clj/migration_0145.clj
Normal file
83
backend/src/app/migrations/clj/migration_0145.clj
Normal file
@@ -0,0 +1,83 @@
|
||||
;; 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.migrations.clj.migration-0145
|
||||
"Migrate plugins references on profiles"
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.logging :as l]
|
||||
[app.db :as db]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def ^:private replacements
|
||||
{"https://colors-to-tokens-plugin.pages.dev"
|
||||
"https://colors-to-tokens.plugins.penpot.app"
|
||||
|
||||
"https://contrast-penpot-plugin.pages.dev"
|
||||
"https://contrast.plugins.penpot.app"
|
||||
|
||||
"https://create-palette-penpot-plugin.pages.dev"
|
||||
"https://create-palette.plugins.penpot.app"
|
||||
|
||||
"https://icons-penpot-plugin.pages.dev"
|
||||
"https://icons.plugins.penpot.app"
|
||||
|
||||
"https://lorem-ipsum-penpot-plugin.pages.dev"
|
||||
"https://lorem-ipsum.plugins.penpot.app"
|
||||
|
||||
"https://rename-layers-penpot-plugin.pages.dev"
|
||||
"https://rename-layers.plugins.penpot.app"
|
||||
|
||||
"https://table-penpot-plugin.pages.dev"
|
||||
"https://table.plugins.penpot.app"})
|
||||
|
||||
(defn- fix-url
|
||||
[url]
|
||||
(reduce-kv (fn [url prefix replacement]
|
||||
(if (str/starts-with? url prefix)
|
||||
(reduced (str replacement (subs url (count prefix))))
|
||||
url))
|
||||
url
|
||||
replacements))
|
||||
|
||||
|
||||
(defn- fix-manifest
|
||||
[manifest]
|
||||
(-> manifest
|
||||
(d/update-when :url fix-url)
|
||||
(d/update-when :host fix-url)))
|
||||
|
||||
(defn- fix-plugins-data
|
||||
[props]
|
||||
(d/update-in-when props [:plugins :data]
|
||||
(fn [data]
|
||||
(reduce-kv (fn [data id manifest]
|
||||
(let [manifest' (fix-manifest manifest)]
|
||||
(if (= manifest manifest')
|
||||
data
|
||||
(assoc data id manifest'))))
|
||||
data
|
||||
data))))
|
||||
|
||||
(def ^:private sql:get-profiles
|
||||
"SELECT id, props FROM profile
|
||||
WHERE props ?? '~:plugins'
|
||||
ORDER BY created_at
|
||||
FOR UPDATE")
|
||||
|
||||
(defn migrate
|
||||
[conn]
|
||||
(->> (db/plan conn [sql:get-profiles])
|
||||
(run! (fn [{:keys [id props]}]
|
||||
(when-let [props (some-> props db/decode-transit-pgobject)]
|
||||
(let [props' (fix-plugins-data props)]
|
||||
(when (not= props props')
|
||||
(l/inf :hint "fixing plugins data on profile props" :profile-id (str id))
|
||||
(db/update! conn :profile
|
||||
{:props (db/tjson props')}
|
||||
{:id id}
|
||||
{::db/return-keys false}))))))))
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
ALTER TABLE server_error_report DROP CONSTRAINT server_error_report_pkey;
|
||||
|
||||
DELETE FROM server_error_report a
|
||||
USING server_error_report b
|
||||
WHERE a.id = b.id
|
||||
AND a.ctid < b.ctid;
|
||||
|
||||
ALTER TABLE server_error_report ADD PRIMARY KEY (id);
|
||||
|
||||
CREATE INDEX server_error_report__version__idx
|
||||
ON server_error_report ( version );
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{:deps
|
||||
{org.clojure/clojure {:mvn/version "1.12.2"}
|
||||
{org.clojure/clojure {:mvn/version "1.12.4"}
|
||||
org.clojure/data.json {:mvn/version "2.5.1"}
|
||||
org.clojure/tools.cli {:mvn/version "1.1.230"}
|
||||
org.clojure/test.check {:mvn/version "1.1.1"}
|
||||
@@ -9,15 +9,15 @@
|
||||
org.apache.commons/commons-pool2 {:mvn/version "2.12.1"}
|
||||
|
||||
;; Logging
|
||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.25.1"}
|
||||
org.apache.logging.log4j/log4j-core {:mvn/version "2.25.1"}
|
||||
org.apache.logging.log4j/log4j-web {:mvn/version "2.25.1"}
|
||||
org.apache.logging.log4j/log4j-jul {:mvn/version "2.25.1"}
|
||||
org.apache.logging.log4j/log4j-slf4j2-impl {:mvn/version "2.25.1"}
|
||||
org.apache.logging.log4j/log4j-api {:mvn/version "2.25.3"}
|
||||
org.apache.logging.log4j/log4j-core {:mvn/version "2.25.3"}
|
||||
org.apache.logging.log4j/log4j-web {:mvn/version "2.25.3"}
|
||||
org.apache.logging.log4j/log4j-jul {:mvn/version "2.25.3"}
|
||||
org.apache.logging.log4j/log4j-slf4j2-impl {:mvn/version "2.25.3"}
|
||||
org.slf4j/slf4j-api {:mvn/version "2.0.17"}
|
||||
pl.tkowalcz.tjahzi/log4j2-appender {:mvn/version "0.9.40"}
|
||||
pl.tkowalcz.tjahzi/log4j2-appender {:mvn/version "0.9.41"}
|
||||
|
||||
selmer/selmer {:mvn/version "1.12.69"}
|
||||
selmer/selmer {:mvn/version "1.12.70"}
|
||||
criterium/criterium {:mvn/version "0.4.6"}
|
||||
|
||||
metosin/jsonista {:mvn/version "0.3.13"}
|
||||
@@ -27,7 +27,7 @@
|
||||
com.cognitect/transit-clj {:mvn/version "1.0.333"}
|
||||
com.cognitect/transit-cljs {:mvn/version "0.8.280"}
|
||||
java-http-clj/java-http-clj {:mvn/version "0.4.3"}
|
||||
integrant/integrant {:mvn/version "1.0.0"}
|
||||
integrant/integrant {:mvn/version "1.0.1"}
|
||||
|
||||
funcool/cuerdas {:mvn/version "2026.415"}
|
||||
funcool/promesa
|
||||
|
||||
@@ -39,12 +39,12 @@
|
||||
(into {})))
|
||||
|
||||
(defn remove-attributes-for-token
|
||||
"Removes applied tokens with `token-id` for the given `attributes` set from `applied-tokens`."
|
||||
[attributes token applied-tokens]
|
||||
"Removes applied tokens with `token-name` for the given `attributes` set from `applied-tokens`."
|
||||
[attributes token-name applied-tokens]
|
||||
(let [attr? (set attributes)]
|
||||
(->> (remove (fn [[k v]]
|
||||
(and (attr? k)
|
||||
(= v (or (token-identifier token) token))))
|
||||
(= v token-name)))
|
||||
applied-tokens)
|
||||
(into {}))))
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@
|
||||
:token-base-font-size
|
||||
:token-color
|
||||
:token-shadow
|
||||
:token-tokenscript
|
||||
:transit-readable-response
|
||||
:user-feedback
|
||||
;; TODO: remove this flag.
|
||||
|
||||
@@ -31,7 +31,7 @@ RUN set -ex; \
|
||||
|
||||
FROM base AS setup-node
|
||||
|
||||
ENV NODE_VERSION=v22.21.1 \
|
||||
ENV NODE_VERSION=v22.22.0 \
|
||||
PATH=/opt/node/bin:$PATH
|
||||
|
||||
RUN set -eux; \
|
||||
@@ -97,18 +97,19 @@ RUN set -eux; \
|
||||
|
||||
FROM base AS setup-jvm
|
||||
|
||||
ENV CLOJURE_VERSION=1.12.3.1577
|
||||
# https://clojure.org/releases/tools
|
||||
ENV CLOJURE_VERSION=1.12.4.1602
|
||||
|
||||
RUN set -eux; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
case "${ARCH}" in \
|
||||
aarch64|arm64) \
|
||||
ESUM='8c5321f16d9f1d8149f83e4e9ff8ca5d9e94320b92d205e6db42a604de3d1140'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.30.17-ca-jdk25.0.1-linux_aarch64.tar.gz'; \
|
||||
ESUM='9903c6b19183a33725ca1dfdae5b72400c9d00995c76fafc4a0d31c5152f33f7'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.32.21-ca-jdk25.0.2-linux_aarch64.tar.gz'; \
|
||||
;; \
|
||||
amd64|x86_64) \
|
||||
ESUM='471b3e62bdffaed27e37005d842d8639f10d244ccce1c7cdebf7abce06c8313e'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.30.17-ca-jdk25.0.1-linux_x64.tar.gz'; \
|
||||
ESUM='946ad9766d98fc6ab495a1a120072197db54997f6925fb96680f1ecd5591db4e'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.32.21-ca-jdk25.0.2-linux_x64.tar.gz'; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Unsupported arch: ${ARCH}"; \
|
||||
@@ -396,7 +397,7 @@ ENV LANG='C.UTF-8' \
|
||||
RUSTUP_HOME="/opt/rustup" \
|
||||
PATH="/opt/jdk/bin:/opt/utils/bin:/opt/clojure/bin:/opt/node/bin:/opt/imagick/bin:/opt/cargo/bin:$PATH"
|
||||
|
||||
COPY --from=penpotapp/imagemagick:7.1.2-0 /opt/imagick /opt/imagick
|
||||
COPY --from=penpotapp/imagemagick:7.1.2-13 /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
|
||||
|
||||
@@ -6,7 +6,8 @@ export PATH="/home/penpot/.cargo/bin:/opt/jdk/bin:/opt/utils/bin:/opt/clojure/bi
|
||||
export CARGO_HOME="/home/penpot/.cargo"
|
||||
|
||||
alias l='ls --color -GFlh'
|
||||
alias rm='rm -r'
|
||||
alias ll='ls --color -GFlh'
|
||||
alias rm='rm -rf'
|
||||
alias ls='ls --color -F'
|
||||
alias lsd='ls -d *(/)'
|
||||
alias lsf='ls -h *(.)'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
set -g default-command "${SHELL}"
|
||||
set -g mouse off
|
||||
set -g history-limit 50000
|
||||
setw -g mode-keys emacs
|
||||
|
||||
@@ -6,7 +6,7 @@ ENV LANG='C.UTF-8' \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
TZ=Etc/UTC
|
||||
|
||||
ARG IMAGEMAGICK_VERSION=7.1.1-47
|
||||
ARG IMAGEMAGICK_VERSION=7.1.2-13
|
||||
|
||||
RUN set -e; \
|
||||
apt-get -qq update; \
|
||||
|
||||
@@ -5,7 +5,7 @@ ENV LANG='C.UTF-8' \
|
||||
LC_ALL='C.UTF-8' \
|
||||
JAVA_HOME="/opt/jdk" \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
NODE_VERSION=v22.21.1 \
|
||||
NODE_VERSION=v22.22.0 \
|
||||
TZ=Etc/UTC
|
||||
|
||||
RUN set -ex; \
|
||||
@@ -46,12 +46,12 @@ RUN set -eux; \
|
||||
ARCH="$(dpkg --print-architecture)"; \
|
||||
case "${ARCH}" in \
|
||||
aarch64|arm64) \
|
||||
ESUM='8c5321f16d9f1d8149f83e4e9ff8ca5d9e94320b92d205e6db42a604de3d1140'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.30.17-ca-jdk25.0.1-linux_aarch64.tar.gz'; \
|
||||
ESUM='9903c6b19183a33725ca1dfdae5b72400c9d00995c76fafc4a0d31c5152f33f7'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.32.21-ca-jdk25.0.2-linux_aarch64.tar.gz'; \
|
||||
;; \
|
||||
amd64|x86_64) \
|
||||
ESUM='471b3e62bdffaed27e37005d842d8639f10d244ccce1c7cdebf7abce06c8313e'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.30.17-ca-jdk25.0.1-linux_x64.tar.gz'; \
|
||||
ESUM='946ad9766d98fc6ab495a1a120072197db54997f6925fb96680f1ecd5591db4e'; \
|
||||
BINARY_URL='https://cdn.azul.com/zulu/bin/zulu25.32.21-ca-jdk25.0.2-linux_x64.tar.gz'; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Unsupported arch: ${ARCH}"; \
|
||||
|
||||
@@ -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.21.1 \
|
||||
NODE_VERSION=v22.22.0 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
PATH=/opt/node/bin:/opt/imagick/bin:$PATH
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"devDependencies": {
|
||||
"@penpot/draft-js": "workspace:./packages/draft-js",
|
||||
"@penpot/mousetrap": "workspace:./packages/mousetrap",
|
||||
"@penpot/tokenscript": "workspace:./packages/tokenscript",
|
||||
"@penpot/plugins-runtime": "1.4.2",
|
||||
"@penpot/svgo": "penpot/svgo#v3.2",
|
||||
"@penpot/text-editor": "workspace:./text-editor",
|
||||
|
||||
3
frontend/packages/tokenscript/.gitignore
vendored
Normal file
3
frontend/packages/tokenscript/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
*.sublime-workspace
|
||||
/.yarn
|
||||
2
frontend/packages/tokenscript/index.js
Normal file
2
frontend/packages/tokenscript/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./schemas.js";
|
||||
export * from "@tokens-studio/tokenscript-interpreter";
|
||||
13
frontend/packages/tokenscript/package.json
Normal file
13
frontend/packages/tokenscript/package.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@penpot/tokenscript",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
|
||||
"author": "Andrey Antukh",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@tokens-studio/tokenscript-interpreter": "^0.23.1"
|
||||
}
|
||||
}
|
||||
1431
frontend/packages/tokenscript/schemas.js
Normal file
1431
frontend/packages/tokenscript/schemas.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,5 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
import { platform } from "os";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
@@ -6,6 +7,10 @@ import { defineConfig, devices } from "@playwright/test";
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
const userAgent = platform === 'darwin' ?
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" :
|
||||
undefined;
|
||||
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*/
|
||||
@@ -43,12 +48,20 @@ export default defineConfig({
|
||||
projects: [
|
||||
{
|
||||
name: "default",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
testDir: "./playwright/ui/specs",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
viewport: { width: 1920, height: 1080 }, // Add custom viewport size
|
||||
video: 'retain-on-failure',
|
||||
trace: 'retain-on-failure',
|
||||
}
|
||||
userAgent,
|
||||
},
|
||||
snapshotPathTemplate: "{testDir}/{testFilePath}-snapshots/{arg}.png",
|
||||
expect: {
|
||||
toHaveScreenshot: {
|
||||
maxDiffPixelRatio: 0.001,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ds",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"~:file-id": "~u8d38942d-b01f-800e-8007-79ee6a9bac45",
|
||||
"~:tag": "component",
|
||||
"~:object-id": "8d38942d-b01f-800e-8007-79ee6a9bac45/8d38942d-b01f-800e-8007-79ee6a9bac46/6b68aedd-4c5b-80b9-8007-7b38c1d34ce4/component",
|
||||
"~:media-id": "~ube2dc82e-615b-486b-a193-8768bdb51d7a",
|
||||
"~:created-at": "~m1769523563389"
|
||||
}
|
||||
@@ -253,7 +253,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
|
||||
async #waitForWebSocketReadiness() {
|
||||
// TODO: find a better event to settle whether the app is ready to receive notifications via ws
|
||||
await expect(this.pageName).toHaveText("Page 1");
|
||||
await expect(this.pageName).toHaveText("Page 1", { timeout: 30000 })
|
||||
}
|
||||
|
||||
async sendPresenceMessage(fixture) {
|
||||
@@ -383,19 +383,46 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
await this.page.keyboard.press("T");
|
||||
await this.page.waitForTimeout(timeToWait);
|
||||
await this.clickAndMove(x1, y1, x2, y2);
|
||||
await this.page.waitForTimeout(timeToWait);
|
||||
await expect(this.page.getByTestId("text-editor")).toBeVisible();
|
||||
|
||||
if (initialText) {
|
||||
await this.page.keyboard.type(initialText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the selected element into the clipboard.
|
||||
* Copies the selected element into the clipboard, or copy the
|
||||
* content of the locator into the clipboard.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async copy() {
|
||||
return this.page.keyboard.press("Control+C");
|
||||
async copy(kind = "keyboard", locator = undefined) {
|
||||
if (kind === "context-menu" && locator) {
|
||||
await locator.click({ button: "right" });
|
||||
await this.page.getByText("Copy", { exact: true }).click();
|
||||
} else {
|
||||
await this.page.keyboard.press("ControlOrMeta+C");
|
||||
}
|
||||
// wait for the clipboard to be updated
|
||||
await this.page.waitForFunction(async () => {
|
||||
const content = await navigator.clipboard.readText()
|
||||
return content !== "";
|
||||
}, { timeout: 1000 });
|
||||
}
|
||||
|
||||
async cut(kind = "keyboard", locator = undefined) {
|
||||
if (kind === "context-menu" && locator) {
|
||||
await locator.click({ button: "right" });
|
||||
await this.page.getByText("Cut", { exact: true }).click();
|
||||
} else {
|
||||
await this.page.keyboard.press("ControlOrMeta+X");
|
||||
}
|
||||
// wait for the clipboard to be updated
|
||||
await this.page.waitForFunction(async () => {
|
||||
const content = await navigator.clipboard.readText()
|
||||
return content !== "";
|
||||
}, { timeout: 1000 });
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,9 +434,9 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
async paste(kind = "keyboard") {
|
||||
if (kind === "context-menu") {
|
||||
await this.viewport.click({ button: "right" });
|
||||
return this.page.getByText("PasteCtrlV").click();
|
||||
return this.page.getByText("Paste", { exact: true }).click();
|
||||
}
|
||||
return this.page.keyboard.press("Control+V");
|
||||
return this.page.keyboard.press("ControlOrMeta+V");
|
||||
}
|
||||
|
||||
async panOnViewportAt(x, y, width, height) {
|
||||
@@ -448,11 +475,11 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
const layer = this.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: name });
|
||||
const button = layer.getByRole("button");
|
||||
const button = layer.getByTestId("toggle-content");
|
||||
|
||||
await button.waitFor();
|
||||
await expect(button).toBeVisible();
|
||||
await button.click(clickOptions);
|
||||
await this.page.waitForTimeout(500);
|
||||
await button.waitFor({ ariaExpanded: true });
|
||||
}
|
||||
|
||||
async expectSelectedLayer(name) {
|
||||
@@ -495,13 +522,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
|
||||
async clickColorPalette(clickOptions = {}) {
|
||||
await this.palette
|
||||
.getByRole("button", { name: "Color Palette (Alt+P)" })
|
||||
.click(clickOptions);
|
||||
}
|
||||
|
||||
async clickColorPalette(clickOptions = {}) {
|
||||
await this.palette
|
||||
.getByRole("button", { name: "Color Palette (Alt+P)" })
|
||||
.getByRole("button", { name: /Color Palette/ })
|
||||
.click(clickOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ test("User can complete the onboarding", async ({ page }) => {
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
const onboardingPage = new OnboardingPage(page);
|
||||
|
||||
await dashboardPage.mockConfigFlags(["enable-onboarding"]);
|
||||
|
||||
await dashboardPage.goToDashboard();
|
||||
await expect(
|
||||
page.getByRole("heading", { name: "Help us get to know you" }),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WasmWorkspacePage, WASM_FLAGS } from "../pages/WasmWorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WasmWorkspacePage.init(page);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
const timeToWait = 100;
|
||||
|
||||
test.beforeEach(async ({ page, context }) => {
|
||||
await Clipboard.enable(context, Clipboard.Permission.ONLY_WRITE);
|
||||
await Clipboard.enable(context, Clipboard.Permission.ALL);
|
||||
|
||||
await WorkspacePage.init(page);
|
||||
await WorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]);
|
||||
|
||||
@@ -774,7 +774,7 @@ test.describe("Tokens: Apply token", () => {
|
||||
await workspace.layers
|
||||
.getByTestId("layer-row")
|
||||
.nth(1)
|
||||
.getByRole("button", { name: "Toggle layer" })
|
||||
.getByTestId("toggle-content")
|
||||
.click();
|
||||
|
||||
await workspace.layers.getByTestId("layer-row").nth(2).click();
|
||||
@@ -831,15 +831,102 @@ test.describe("Tokens: Apply token", () => {
|
||||
});
|
||||
await detachButton.click();
|
||||
await expect(marginPillXL).not.toBeVisible();
|
||||
const horizontalMarginInput = layoutItemSectionSidebar.getByText('Horizontal marginOpen token');
|
||||
const horizontalMarginInput = layoutItemSectionSidebar.getByText(
|
||||
"Horizontal marginOpen token",
|
||||
);
|
||||
await expect(horizontalMarginInput).toBeVisible();
|
||||
|
||||
const tokenDropdown = horizontalMarginInput.getByRole('button', { name: 'Open token list' });
|
||||
const tokenDropdown = horizontalMarginInput.getByRole("button", {
|
||||
name: "Open token list",
|
||||
});
|
||||
await tokenDropdown.click();
|
||||
|
||||
await expect(dimensionTokenOptionXl).toBeVisible();
|
||||
await dimensionTokenOptionXl.click();
|
||||
|
||||
|
||||
await expect(marginPillXL).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Tokens: Detach token", () => {
|
||||
test("User applies border-radius token to a shape from sidebar", async ({
|
||||
page,
|
||||
}) => {
|
||||
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
|
||||
await setupTokensFile(page);
|
||||
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
|
||||
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
|
||||
|
||||
// Open tokens sections on left sidebar
|
||||
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
|
||||
await tokensTabButton.click();
|
||||
|
||||
// Unfold border radius tokens
|
||||
await page.getByRole("button", { name: "Border Radius 3" }).click();
|
||||
await expect(
|
||||
tokensSidebar.getByRole("button", { name: "borderRadius" }),
|
||||
).toBeVisible();
|
||||
await tokensSidebar.getByRole("button", { name: "borderRadius" }).click();
|
||||
await expect(
|
||||
tokensSidebar.getByRole("button", { name: "borderRadius.sm" }),
|
||||
).toBeVisible();
|
||||
|
||||
// Apply border radius token from token panels
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "borderRadius.sm" })
|
||||
.click();
|
||||
|
||||
// Check if border radius sections is visible on right sidebar
|
||||
const borderRadiusSection = page.getByRole("region", {
|
||||
name: "border-radius-section",
|
||||
});
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
|
||||
// Check if token pill is visible on design tab on right sidebar
|
||||
const brTokenPillSM = borderRadiusSection.getByRole("button", {
|
||||
name: "borderRadius.sm",
|
||||
});
|
||||
await expect(brTokenPillSM).toBeVisible();
|
||||
await brTokenPillSM.click();
|
||||
|
||||
// Rename token
|
||||
await tokensSidebar
|
||||
.getByRole("button", { name: "borderRadius.sm" })
|
||||
.click({ button: "right" });
|
||||
await expect(page.getByText("Edit token")).toBeVisible();
|
||||
await page.getByText("Edit token").click();
|
||||
const editModal = page.getByTestId("token-update-create-modal");
|
||||
await expect(editModal).toBeVisible();
|
||||
await expect(
|
||||
editModal.getByRole("textbox", { name: "Name" }),
|
||||
).toBeVisible();
|
||||
await editModal
|
||||
.getByRole("textbox", { name: "Name" })
|
||||
.fill("BorderRadius.smBis");
|
||||
const submitButton = editModal.getByRole("button", { name: "Save" });
|
||||
await expect(submitButton).toBeEnabled();
|
||||
await submitButton.click();
|
||||
await expect(page.getByText("Don't remap")).toBeVisible();
|
||||
await page.getByText("Don't remap").click();
|
||||
const brokenPill = borderRadiusSection.getByRole("button", {
|
||||
name: "This token is not in any",
|
||||
});
|
||||
await expect(brokenPill).toBeVisible();
|
||||
|
||||
// Detach broken token
|
||||
const detachButton = borderRadiusSection.getByRole("button", {
|
||||
name: "Detach token",
|
||||
});
|
||||
await detachButton.click();
|
||||
await expect(brokenPill).not.toBeVisible();
|
||||
|
||||
//De-select and select shape again to double check token is detached
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
|
||||
await workspacePage.layers.getByTestId("layer-row").nth(0).click();
|
||||
await workspacePage.layers.getByTestId("layer-row").nth(1).click();
|
||||
await expect(brokenPill).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { BaseWebSocketPage } from "../pages/BaseWebSocketPage";
|
||||
import { Clipboard } from "../../helpers/Clipboard";
|
||||
|
||||
test.beforeEach(async ({ page, context }) => {
|
||||
await Clipboard.enable(context, Clipboard.Permission.ALL);
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-variants.json");
|
||||
});
|
||||
|
||||
test.afterEach(async ({ context }) => {
|
||||
context.clearPermissions();
|
||||
});
|
||||
|
||||
const setupVariantsFile = async (workspacePage) => {
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
@@ -34,9 +41,9 @@ const setupVariantsFileWithVariant = async (workspacePage) => {
|
||||
await setupVariantsFile(workspacePage);
|
||||
|
||||
await workspacePage.clickLeafLayer("Rectangle");
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
await workspacePage.page.waitForTimeout(500);
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
await workspacePage.page.waitForTimeout(500);
|
||||
|
||||
// We wait until layer-row starts looking like it an component
|
||||
@@ -156,7 +163,7 @@ test("User duplicates a variant container", async ({ page }) => {
|
||||
await variant.container.click();
|
||||
|
||||
//Duplicate the variant container
|
||||
await workspacePage.page.keyboard.press("Control+d");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+d");
|
||||
|
||||
const variant_original = await findVariant(workspacePage, 1); // On duplicate, the new item is the first
|
||||
const variant_duplicate = await findVariant(workspacePage, 0);
|
||||
@@ -169,25 +176,27 @@ test("User duplicates a variant container", async ({ page }) => {
|
||||
await validateVariant(variant_duplicate);
|
||||
});
|
||||
|
||||
test("User copy paste a variant container", async ({ page }) => {
|
||||
test("User copy paste a variant container", async ({ page, context }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
// Access to the read/write clipboard necesary for this functionality
|
||||
await setupVariantsFileWithVariant(workspacePage);
|
||||
await workspacePage.mockRPC(
|
||||
/create-file-object-thumbnail.*/,
|
||||
"workspace/create-file-object-thumbnail.json",
|
||||
);
|
||||
|
||||
const variant = findVariantNoWait(workspacePage, 0);
|
||||
|
||||
// await variant.container.waitFor();
|
||||
|
||||
// Select the variant container
|
||||
await variant.container.click();
|
||||
|
||||
await workspacePage.page.waitForTimeout(1000);
|
||||
|
||||
// Copy the variant container
|
||||
await workspacePage.page.keyboard.press("Control+c");
|
||||
await workspacePage.clickLeafLayer("Rectangle");
|
||||
await workspacePage.copy("keyboard");
|
||||
|
||||
// Paste the variant container
|
||||
await workspacePage.clickAt(400, 400);
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
const variants = workspacePage.layers.getByText("Rectangle");
|
||||
await expect(variants).toHaveCount(2);
|
||||
|
||||
const variantDuplicate = findVariantNoWait(workspacePage, 0);
|
||||
const variantOriginal = findVariantNoWait(workspacePage, 1);
|
||||
@@ -212,18 +221,17 @@ test("User cut paste a variant container", async ({ page }) => {
|
||||
await variant.container.click();
|
||||
|
||||
//Cut the variant container
|
||||
await workspacePage.page.keyboard.press("Control+x");
|
||||
await workspacePage.page.waitForTimeout(500);
|
||||
await workspacePage.cut("keyboard");
|
||||
|
||||
//Paste the variant container
|
||||
await workspacePage.clickAt(500, 500);
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
await workspacePage.page.waitForTimeout(500);
|
||||
|
||||
const variantPasted = await findVariant(workspacePage, 0);
|
||||
|
||||
// Expand the layers
|
||||
await variantPasted.container.locator("button").first().click();
|
||||
await workspacePage.clickToggableLayer("Rectangle");
|
||||
|
||||
// The variants are valid
|
||||
await validateVariant(variantPasted);
|
||||
@@ -239,27 +247,34 @@ test("User cut paste a variant container into a board, and undo twice", async ({
|
||||
|
||||
//Create a board
|
||||
await workspacePage.boardButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 100, 100);
|
||||
// NOTE: this board should not intersect the existing variants, otherwise
|
||||
// this test is flaky
|
||||
await workspacePage.clickWithDragViewportAt(200, 200, 100, 100);
|
||||
await workspacePage.clickAt(495, 495);
|
||||
const board = await workspacePage.rootShape.locator("Board");
|
||||
|
||||
// Select the variant container
|
||||
await variant.container.click();
|
||||
// await variant.container.click();
|
||||
await workspacePage.clickLeafLayer("Rectangle");
|
||||
|
||||
//Cut the variant container
|
||||
await workspacePage.page.keyboard.press("Control+x");
|
||||
await workspacePage.page.waitForTimeout(500);
|
||||
await workspacePage.cut("keyboard");
|
||||
await expect(variant.container).not.toBeVisible();
|
||||
|
||||
//Select the board
|
||||
await workspacePage.clickLeafLayer("Board");
|
||||
|
||||
//Paste the variant container inside the board
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
await expect(variant.container).toBeVisible();
|
||||
|
||||
//Undo twice
|
||||
await workspacePage.page.keyboard.press("Control+z");
|
||||
await workspacePage.page.keyboard.press("Control+z");
|
||||
await workspacePage.page.waitForTimeout(500);
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+z");
|
||||
|
||||
await expect(variant.container).not.toBeVisible();
|
||||
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+z");
|
||||
await expect(variant.container).toBeVisible();
|
||||
|
||||
const variantAfterUndo = await findVariant(workspacePage, 0);
|
||||
|
||||
@@ -276,12 +291,12 @@ test("User copy paste a variant", async ({ page }) => {
|
||||
// Select the variant1
|
||||
await variant.variant1.click();
|
||||
|
||||
//Cut the variant
|
||||
await workspacePage.page.keyboard.press("Control+c");
|
||||
// Copy the variant
|
||||
await workspacePage.copy("keyboard");
|
||||
|
||||
//Paste the variant
|
||||
// Paste the variant
|
||||
await workspacePage.clickAt(500, 500);
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
const copy = await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
@@ -302,11 +317,11 @@ test("User cut paste a variant outside the container", async ({ page }) => {
|
||||
await variant.variant1.click();
|
||||
|
||||
//Cut the variant
|
||||
await workspacePage.page.keyboard.press("Control+x");
|
||||
await workspacePage.cut("keyboard");
|
||||
|
||||
//Paste the variant
|
||||
await workspacePage.clickAt(500, 500);
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
const component = await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
@@ -324,15 +339,11 @@ test("User drag and drop a variant outside the container", async ({ page }) => {
|
||||
const variant = await findVariant(workspacePage, 0);
|
||||
|
||||
// Drag and drop the variant
|
||||
await workspacePage.clickWithDragViewportAt(350, 400, 0, 200);
|
||||
// FIXME: to make this test more resilient, we should get the bounding box of the Value 1 variant
|
||||
// and use it to calculate the target position
|
||||
await workspacePage.clickWithDragViewportAt(600, 500, 0, 300);
|
||||
|
||||
const component = await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ has: workspacePage.page.getByText("Rectangle / Value 1") })
|
||||
.filter({ has: workspacePage.page.getByTestId("icon-component") });
|
||||
|
||||
//The component exists and is visible
|
||||
await expect(component).toBeVisible();
|
||||
await expect(workspacePage.layers.getByText("Rectangle / Value 1")).toBeVisible();
|
||||
});
|
||||
|
||||
test("User cut paste a component inside a variant", async ({ page }) => {
|
||||
@@ -345,14 +356,14 @@ test("User cut paste a component inside a variant", async ({ page }) => {
|
||||
await workspacePage.ellipseShapeButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||
await workspacePage.clickLeafLayer("Ellipse");
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
|
||||
//Cut the component
|
||||
await workspacePage.page.keyboard.press("Control+x");
|
||||
await workspacePage.cut("keyboard");
|
||||
|
||||
//Paste the component inside the variant
|
||||
await variant.container.click();
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
const variant3 = await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
@@ -376,7 +387,7 @@ test("User cut paste a component with path inside a variant", async ({
|
||||
await workspacePage.ellipseShapeButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||
await workspacePage.clickLeafLayer("Ellipse");
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
|
||||
//Rename the component
|
||||
await workspacePage.layers.getByText("Ellipse").dblclick();
|
||||
@@ -387,11 +398,11 @@ test("User cut paste a component with path inside a variant", async ({
|
||||
await workspacePage.page.keyboard.press("Enter");
|
||||
|
||||
//Cut the component
|
||||
await workspacePage.page.keyboard.press("Control+x");
|
||||
await workspacePage.cut("keyboard");
|
||||
|
||||
//Paste the component inside the variant
|
||||
await variant.container.click();
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
const variant3 = await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
@@ -415,7 +426,7 @@ test("User drag and drop a component with path inside a variant", async ({
|
||||
await workspacePage.ellipseShapeButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||
await workspacePage.clickLeafLayer("Ellipse");
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
|
||||
//Rename the component
|
||||
await workspacePage.layers.getByText("Ellipse").dblclick();
|
||||
@@ -426,7 +437,7 @@ test("User drag and drop a component with path inside a variant", async ({
|
||||
await workspacePage.page.keyboard.press("Enter");
|
||||
|
||||
//Drag and drop the component the component
|
||||
await workspacePage.clickWithDragViewportAt(510, 510, 0, -200);
|
||||
await workspacePage.clickWithDragViewportAt(510, 510, 200, 0);
|
||||
|
||||
const variant3 = await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
@@ -446,8 +457,8 @@ test("User cut paste a variant into another container", async ({ page }) => {
|
||||
await workspacePage.ellipseShapeButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||
await workspacePage.clickLeafLayer("Ellipse");
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("Control+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||
|
||||
const variantOrigin = await findVariantNoWait(workspacePage, 1);
|
||||
|
||||
@@ -457,11 +468,11 @@ test("User cut paste a variant into another container", async ({ page }) => {
|
||||
await variantOrigin.variant1.click();
|
||||
|
||||
//Cut the variant
|
||||
await workspacePage.page.keyboard.press("Control+x");
|
||||
await workspacePage.cut("keyboard");
|
||||
|
||||
//Paste the variant
|
||||
await workspacePage.layers.getByText("Ellipse").first().click();
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
await workspacePage.paste("keyboard");
|
||||
|
||||
const variant3 = workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
import { presenceFixture, joinFixture2, joinFixture3 } from "../../data/workspace/ws-notifications";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
test("User loads worskpace with empty file", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -16,7 +16,7 @@ test("User loads worskpace with empty file", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("User opens a file with a bad page id", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace({
|
||||
@@ -29,7 +29,7 @@ test("User opens a file with a bad page id", async ({ page }) => {
|
||||
test("User receives presence notifications updates in the workspace", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -43,7 +43,7 @@ test("User receives presence notifications updates in the workspace", async ({
|
||||
test("BUG 13058 - Presence list shows up to 3 user avatars", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -63,7 +63,7 @@ test("BUG 13058 - Presence list shows up to 3 user avatars", async ({
|
||||
});
|
||||
|
||||
test("User draws a rect", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
"update-file?id=*",
|
||||
@@ -74,13 +74,12 @@ test("User draws a rect", async ({ page }) => {
|
||||
await workspacePage.rectShapeButton.click();
|
||||
await workspacePage.clickWithDragViewportAt(128, 128, 200, 100);
|
||||
|
||||
const shape = await workspacePage.rootShape.locator("rect");
|
||||
await expect(shape).toHaveAttribute("width", "200");
|
||||
await expect(shape).toHaveAttribute("height", "100");
|
||||
await workspacePage.hideUI();
|
||||
await expect(workspacePage.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("User makes a group", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
@@ -96,14 +95,14 @@ test("User makes a group", async ({ page }) => {
|
||||
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
||||
});
|
||||
await workspacePage.clickLeafLayer("Rectangle");
|
||||
await workspacePage.page.keyboard.press("Control+g");
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+g");
|
||||
await workspacePage.expectSelectedLayer("Group");
|
||||
});
|
||||
|
||||
test("Bug 7654 - Toolbar keeps toggling on and off on spacebar press", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
@@ -113,10 +112,10 @@ test("Bug 7654 - Toolbar keeps toggling on and off on spacebar press", async ({
|
||||
await workspacePage.expectHiddenToolbarOptions();
|
||||
});
|
||||
|
||||
test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", async ({
|
||||
test("Bug 7525 - User moves a scrollbar and no selection rectangle appears", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
@@ -132,8 +131,8 @@ test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", asyn
|
||||
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
||||
});
|
||||
|
||||
// Move created rect to a corner, in orther to get scrollbars
|
||||
await workspacePage.panOnViewportAt(128, 128, 300, 300);
|
||||
// Move created rect to a corner, in order to get scrollbars
|
||||
await workspacePage.panOnViewportAt(128, 128, 600, 600);
|
||||
|
||||
// Check scrollbars appear
|
||||
const horizontalScrollbar = workspacePage.horizontalScrollbar;
|
||||
@@ -152,7 +151,7 @@ test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", asyn
|
||||
test("User adds a library and its automatically selected in the color palette", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
"link-file-to-library",
|
||||
@@ -197,7 +196,7 @@ test("User adds a library and its automatically selected in the color palette",
|
||||
test("Bug 10179 - Drag & drop doesn't add colors to the Recent Colors palette", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.moveButton.click();
|
||||
@@ -240,7 +239,7 @@ test("Bug 10179 - Drag & drop doesn't add colors to the Recent Colors palette",
|
||||
test("Bug 7489 - Workspace-palette items stay hidden when opening with keyboard-shortcut", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
@@ -257,7 +256,7 @@ test("Bug 7489 - Workspace-palette items stay hidden when opening with keyboard-
|
||||
test("Bug 8784 - Use keyboard arrow to move inside a text input does not change tabs", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.pageName.click();
|
||||
@@ -267,7 +266,7 @@ test("Bug 8784 - Use keyboard arrow to move inside a text input does not change
|
||||
});
|
||||
|
||||
test("Bug 9066 - Problem with grid layout", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9066.json");
|
||||
|
||||
@@ -295,7 +294,7 @@ test("Bug 9066 - Problem with grid layout", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("User have toolbar", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
@@ -304,7 +303,7 @@ test("User have toolbar", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("User have edition menu entries", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
@@ -320,7 +319,7 @@ test("User have edition menu entries", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("Copy/paste properties", async ({ page, context }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
@@ -386,23 +385,23 @@ test("Copy/paste properties", async ({ page, context }) => {
|
||||
});
|
||||
|
||||
test("[Taiga #9929] Paste text in workspace", async ({ page, context }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.goToWorkspace();
|
||||
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
|
||||
await page.evaluate(() => navigator.clipboard.writeText("Lorem ipsum dolor"));
|
||||
await workspacePage.viewport.click({ button: "right" });
|
||||
await page.getByText("PasteCtrlV").click();
|
||||
await page.getByText(/^Paste/i).click();
|
||||
await workspacePage.viewport
|
||||
.getByRole("textbox")
|
||||
.getByText("Lorem ipsum dolor");
|
||||
});
|
||||
|
||||
test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
||||
test("[Taiga #9930] Zoom fit all doesn't fit all shapes", async ({
|
||||
page,
|
||||
context,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9930.json");
|
||||
await workspacePage.goToWorkspace({
|
||||
@@ -410,16 +409,18 @@ test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
||||
pageId: "fb9798e7-a547-80ae-8005-9ffda4a13e2c",
|
||||
});
|
||||
|
||||
const zoom = await page.getByTitle("Zoom");
|
||||
const zoom = page.getByTitle("Zoom");
|
||||
await zoom.click();
|
||||
|
||||
const zoomIn = await page.getByRole("button", { name: "Zoom in" });
|
||||
const zoomIn = page.getByRole("button", { name: "Zoom in" });
|
||||
await zoomIn.click();
|
||||
await zoomIn.click();
|
||||
await zoomIn.click();
|
||||
|
||||
// Zoom fit all
|
||||
await page.keyboard.press("Shift+1");
|
||||
// Select all shapes to display selrect
|
||||
await workspacePage.page.keyboard.press("ControlOrMeta+a");
|
||||
|
||||
const ids = [
|
||||
"shape-165d1e5a-5873-8010-8005-9ffdbeaeec59",
|
||||
@@ -441,7 +442,7 @@ test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
||||
|
||||
const viewportBoundingBox = await workspacePage.viewport.boundingBox();
|
||||
for (const id of ids) {
|
||||
const shape = await page.locator(`.ws-shape-wrapper > g#${id}`);
|
||||
const shape = page.locator(`.viewport-selrect`);
|
||||
const shapeBoundingBox = await shape.boundingBox();
|
||||
expect(contains(viewportBoundingBox, shapeBoundingBox)).toBeTruthy();
|
||||
}
|
||||
@@ -450,7 +451,7 @@ test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
||||
test("Bug 9877, user navigation to dashboard from header goes to blank page", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -467,7 +468,7 @@ test("Bug 9877, user navigation to dashboard from header goes to blank page", as
|
||||
test("Bug 8371 - Flatten option is not visible in context menu", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.mockGetFile("workspace/get-file-8371.json");
|
||||
await workspacePage.goToWorkspace({
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
123
frontend/pnpm-lock.yaml
generated
123
frontend/pnpm-lock.yaml
generated
@@ -28,6 +28,9 @@ importers:
|
||||
'@penpot/text-editor':
|
||||
specifier: workspace:./text-editor
|
||||
version: link:text-editor
|
||||
'@penpot/tokenscript':
|
||||
specifier: workspace:./packages/tokenscript
|
||||
version: link:packages/tokenscript
|
||||
'@playwright/test':
|
||||
specifier: 1.58.0
|
||||
version: 1.58.0
|
||||
@@ -248,6 +251,12 @@ importers:
|
||||
|
||||
packages/mousetrap: {}
|
||||
|
||||
packages/tokenscript:
|
||||
dependencies:
|
||||
'@tokens-studio/tokenscript-interpreter':
|
||||
specifier: ^0.23.1
|
||||
version: 0.23.1
|
||||
|
||||
text-editor:
|
||||
devDependencies:
|
||||
'@playwright/test':
|
||||
@@ -299,6 +308,12 @@ packages:
|
||||
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@ark/schema@0.56.0':
|
||||
resolution: {integrity: sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==}
|
||||
|
||||
'@ark/util@0.56.0':
|
||||
resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==}
|
||||
|
||||
'@asamuzakjp/css-color@4.1.1':
|
||||
resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==}
|
||||
|
||||
@@ -924,36 +939,42 @@ packages:
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.1':
|
||||
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.1':
|
||||
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.1':
|
||||
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.1':
|
||||
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
|
||||
@@ -1035,24 +1056,28 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@resvg/resvg-js-linux-arm64-musl@2.6.2':
|
||||
resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@resvg/resvg-js-linux-x64-gnu@2.6.2':
|
||||
resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@resvg/resvg-js-linux-x64-musl@2.6.2':
|
||||
resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@resvg/resvg-js-win32-arm64-msvc@2.6.2':
|
||||
resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==}
|
||||
@@ -1149,121 +1174,145 @@ packages:
|
||||
resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.56.0':
|
||||
resolution: {integrity: sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.54.0':
|
||||
resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.56.0':
|
||||
resolution: {integrity: sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.54.0':
|
||||
resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.56.0':
|
||||
resolution: {integrity: sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.54.0':
|
||||
resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.56.0':
|
||||
resolution: {integrity: sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.54.0':
|
||||
resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.56.0':
|
||||
resolution: {integrity: sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.56.0':
|
||||
resolution: {integrity: sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.54.0':
|
||||
resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.56.0':
|
||||
resolution: {integrity: sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.56.0':
|
||||
resolution: {integrity: sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.54.0':
|
||||
resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.56.0':
|
||||
resolution: {integrity: sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.54.0':
|
||||
resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.56.0':
|
||||
resolution: {integrity: sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.54.0':
|
||||
resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.56.0':
|
||||
resolution: {integrity: sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.54.0':
|
||||
resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.56.0':
|
||||
resolution: {integrity: sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.54.0':
|
||||
resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.56.0':
|
||||
resolution: {integrity: sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.56.0':
|
||||
resolution: {integrity: sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==}
|
||||
@@ -1436,6 +1485,11 @@ packages:
|
||||
peerDependencies:
|
||||
style-dictionary: '>=4.3.0 < 6'
|
||||
|
||||
'@tokens-studio/tokenscript-interpreter@0.23.1':
|
||||
resolution: {integrity: sha512-aIcJprCkHIyckl0Knn78Sn7ef3U3IXLjNv9MOePdNR0Mz3Z4PleerldtfLmr1DdXUXiroVSyJROyJrO3TfB2Gg==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@tokens-studio/types@0.5.2':
|
||||
resolution: {integrity: sha512-rzMcZP0bj2E5jaa7Fj0LGgYHysoCrbrxILVbT0ohsCUH5uCHY/u6J7Qw/TE0n6gR9Js/c9ZO9T8mOoz0HdLMbA==}
|
||||
|
||||
@@ -1674,6 +1728,12 @@ packages:
|
||||
resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
arkregex@0.0.5:
|
||||
resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==}
|
||||
|
||||
arktype@2.1.29:
|
||||
resolution: {integrity: sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ==}
|
||||
|
||||
array-buffer-byte-length@1.0.2:
|
||||
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1782,6 +1842,9 @@ packages:
|
||||
buffer-builder@0.2.0:
|
||||
resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==}
|
||||
|
||||
buffer-crc32@0.2.13:
|
||||
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
|
||||
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
@@ -1952,6 +2015,10 @@ packages:
|
||||
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
commander@14.0.2:
|
||||
resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
commander@7.2.0:
|
||||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -3360,6 +3427,9 @@ packages:
|
||||
resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==}
|
||||
engines: {node: '>= 14.16'}
|
||||
|
||||
pend@1.2.0:
|
||||
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
@@ -3637,6 +3707,10 @@ packages:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
readline-sync@1.4.10:
|
||||
resolution: {integrity: sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
recast@0.23.11:
|
||||
resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==}
|
||||
engines: {node: '>= 4'}
|
||||
@@ -3784,48 +3858,56 @@ packages:
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-linux-arm@1.97.1:
|
||||
resolution: {integrity: sha512-48VxaTUApLyx1NXFdZhKqI/7FYLmz8Ju3Ki2V/p+mhn5raHgAiYeFgn8O1WGxTOh+hBb9y3FdSR5a8MNTbmKMQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-linux-musl-arm64@1.97.1:
|
||||
resolution: {integrity: sha512-kD35WSD9o0279Ptwid3Jnbovo1FYnuG2mayYk9z4ZI4mweXEK6vTu+tlvCE/MdF/zFKSj11qaxaH+uzXe2cO5A==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-musl-arm@1.97.1:
|
||||
resolution: {integrity: sha512-FUFs466t3PVViVOKY/60JgLLtl61Pf7OW+g5BeEfuqVcSvYUECVHeiYHtX1fT78PEVa0h9tHpM6XpWti+7WYFA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-musl-riscv64@1.97.1:
|
||||
resolution: {integrity: sha512-ZgpYps5YHuhA2+KiLkPukRbS5298QObgUhPll/gm5i0LOZleKCwrFELpVPcbhsSBuxqji2uaag5OL+n3JRBVVg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-musl-x64@1.97.1:
|
||||
resolution: {integrity: sha512-wcAigOyyvZ6o1zVypWV7QLZqpOEVnlBqJr9MbpnRIm74qFTSbAEmShoh8yMXBymzuVSmEbThxAwW01/TLf62tA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: musl
|
||||
|
||||
sass-embedded-linux-riscv64@1.97.1:
|
||||
resolution: {integrity: sha512-9j1qE1ZrLMuGb+LUmBzw93Z4TNfqlRkkxjPVZy6u5vIggeSfvGbte7eRoYBNWX6SFew/yBCL90KXIirWFSGrlQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-linux-x64@1.97.1:
|
||||
resolution: {integrity: sha512-7nrLFYMH/UgvEgXR5JxQJ6y9N4IJmnFnYoDxN0nw0jUp+CQWQL4EJ4RqAKTGelneueRbccvt2sEyPK+X0KJ9Jg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: glibc
|
||||
|
||||
sass-embedded-unknown-all@1.97.1:
|
||||
resolution: {integrity: sha512-oPSeKc7vS2dx3ZJHiUhHKcyqNq0GWzAiR8zMVpPd/kVMl5ZfVyw+5HTCxxWDBGkX02lNpou27JkeBPCaneYGAQ==}
|
||||
@@ -4156,6 +4238,7 @@ packages:
|
||||
tar@6.2.1:
|
||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
|
||||
|
||||
tdigest@0.1.2:
|
||||
resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==}
|
||||
@@ -4677,6 +4760,10 @@ packages:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yauzl@3.2.0:
|
||||
resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yocto-queue@1.2.2:
|
||||
resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==}
|
||||
engines: {node: '>=12.20'}
|
||||
@@ -4695,6 +4782,12 @@ snapshots:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@ark/schema@0.56.0':
|
||||
dependencies:
|
||||
'@ark/util': 0.56.0
|
||||
|
||||
'@ark/util@0.56.0': {}
|
||||
|
||||
'@asamuzakjp/css-color@4.1.1':
|
||||
dependencies:
|
||||
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
|
||||
@@ -5608,6 +5701,13 @@ snapshots:
|
||||
is-mergeable-object: 1.1.1
|
||||
style-dictionary: 5.0.0-rc.1
|
||||
|
||||
'@tokens-studio/tokenscript-interpreter@0.23.1':
|
||||
dependencies:
|
||||
arktype: 2.1.29
|
||||
commander: 14.0.2
|
||||
readline-sync: 1.4.10
|
||||
yauzl: 3.2.0
|
||||
|
||||
'@tokens-studio/types@0.5.2': {}
|
||||
|
||||
'@trysound/sax@0.2.0': {}
|
||||
@@ -5901,6 +6001,16 @@ snapshots:
|
||||
|
||||
aria-query@5.3.2: {}
|
||||
|
||||
arkregex@0.0.5:
|
||||
dependencies:
|
||||
'@ark/util': 0.56.0
|
||||
|
||||
arktype@2.1.29:
|
||||
dependencies:
|
||||
'@ark/schema': 0.56.0
|
||||
'@ark/util': 0.56.0
|
||||
arkregex: 0.0.5
|
||||
|
||||
array-buffer-byte-length@1.0.2:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@@ -6040,6 +6150,8 @@ snapshots:
|
||||
|
||||
buffer-builder@0.2.0: {}
|
||||
|
||||
buffer-crc32@0.2.13: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
buffer@5.7.1:
|
||||
@@ -6212,6 +6324,8 @@ snapshots:
|
||||
|
||||
commander@12.1.0: {}
|
||||
|
||||
commander@14.0.2: {}
|
||||
|
||||
commander@7.2.0: {}
|
||||
|
||||
component-emitter@2.0.0: {}
|
||||
@@ -7735,6 +7849,8 @@ snapshots:
|
||||
|
||||
pathval@2.0.1: {}
|
||||
|
||||
pend@1.2.0: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
@@ -8029,6 +8145,8 @@ snapshots:
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
readline-sync@1.4.10: {}
|
||||
|
||||
recast@0.23.11:
|
||||
dependencies:
|
||||
ast-types: 0.16.1
|
||||
@@ -9195,6 +9313,11 @@ snapshots:
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
yauzl@3.2.0:
|
||||
dependencies:
|
||||
buffer-crc32: 0.2.13
|
||||
pend: 1.2.0
|
||||
|
||||
yocto-queue@1.2.2: {}
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
@@ -6,4 +6,5 @@ shamefullyHoist: true
|
||||
packages:
|
||||
- "packages/draft-js"
|
||||
- "packages/mousetrap"
|
||||
- "packages/tokenscript"
|
||||
- "text-editor"
|
||||
|
||||
90
frontend/resources/wasm-playground/shadows.html
Normal file
90
frontend/resources/wasm-playground/shadows.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>WASM + WebGL2 Canvas</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: #111;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script type="module">
|
||||
import initWasmModule from '/js/render-wasm.js';
|
||||
import {
|
||||
init, addShapeSolidFill, assignCanvas, hexToU32ARGB, getRandomInt, getRandomColor,
|
||||
getRandomFloat, useShape, setShapeChildren, setupInteraction, addShapeSolidStrokeFill
|
||||
} from './js/lib.js';
|
||||
|
||||
const canvas = document.getElementById("canvas");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
const params = new URLSearchParams(document.location.search);
|
||||
const shapes = params.get("shapes") || 1000;
|
||||
|
||||
initWasmModule().then(Module => {
|
||||
init(Module);
|
||||
assignCanvas(canvas);
|
||||
Module._set_canvas_background(hexToU32ARGB("#FABADA", 1));
|
||||
Module._init_shapes_pool(shapes + 1);
|
||||
setupInteraction(canvas);
|
||||
|
||||
const children = [];
|
||||
for (let i = 0; i < shapes; i++) {
|
||||
const uuid = crypto.randomUUID();
|
||||
children.push(uuid);
|
||||
|
||||
useShape(uuid);
|
||||
Module._set_parent(0, 0, 0, 0);
|
||||
Module._set_shape_type(3);
|
||||
const x1 = getRandomInt(0, canvas.width);
|
||||
const y1 = getRandomInt(0, canvas.height);
|
||||
const width = getRandomInt(20, 100);
|
||||
const height = getRandomInt(20, 100);
|
||||
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||
|
||||
const color = getRandomColor();
|
||||
const argb = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidFill(argb)
|
||||
|
||||
Module._add_shape_center_stroke(10, 0, 0, 0);
|
||||
const argb2 = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||
addShapeSolidStrokeFill(argb2);
|
||||
|
||||
// Add shadows
|
||||
// Shadow 1: drop-shadow, #dedede opacity 0.33, blur 4, spread -2, offsetX 0, offsetY 2
|
||||
Module._add_shape_shadow(hexToU32ARGB("#dedede", 0.33), 4, -2, 0, 2, 0, false);
|
||||
// Shadow 2: drop-shadow, #dedede opacity 1, blur 12, spread -8, offsetX 0, offsetY 12
|
||||
Module._add_shape_shadow(hexToU32ARGB("#dedede", 1), 12, -8, 0, 12, 0, false);
|
||||
// Shadow 3: inner-shadow, #002046 opacity 0.12, blur 12, spread -8, offsetX 0, offsetY -4
|
||||
Module._add_shape_shadow(hexToU32ARGB("#002046", 0.12), 12, -8, 0, -4, 1, false);
|
||||
}
|
||||
|
||||
useShape("00000000-0000-0000-0000-000000000000");
|
||||
setShapeChildren(children);
|
||||
|
||||
performance.mark('render:begin');
|
||||
Module._set_view(1, 0, 0);
|
||||
Module._render(Date.now());
|
||||
performance.mark('render:end');
|
||||
const { duration } = performance.measure('render', 'render:begin', 'render:end');
|
||||
// alert(`render time: ${duration.toFixed(2)}ms`);
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -14,7 +14,9 @@
|
||||
[app.common.time :as ct]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.data.tinycolor :as tinycolor]
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[app.main.data.workspace.tokens.errors :as wte]
|
||||
[app.main.data.workspace.tokens.warnings :as wtw]
|
||||
[beicon.v2.core :as rx]
|
||||
@@ -586,22 +588,25 @@
|
||||
;; FIXME: this with effect with trigger all the time because
|
||||
;; `config` will be always a different instance
|
||||
(mf/with-effect [tokens config]
|
||||
(let [cached (get @cache-atom tokens)]
|
||||
(cond
|
||||
(nil? tokens) nil
|
||||
;; The tokens are already processing somewhere
|
||||
(rx/observable? cached) (rx/sub! cached #(reset! tokens-state %))
|
||||
;; Get the cached entry
|
||||
(some? cached) (reset! tokens-state cached)
|
||||
;; No cached entry, start processing
|
||||
:else (let [resolved-tokens-s (if interactive?
|
||||
(resolve-tokens-interactive tokens)
|
||||
(resolve-tokens tokens))]
|
||||
(swap! cache-atom assoc tokens resolved-tokens-s)
|
||||
(rx/sub! resolved-tokens-s (fn [resolved-tokens]
|
||||
(swap! cache-atom assoc tokens resolved-tokens)
|
||||
(reset! tokens-state resolved-tokens)))))))
|
||||
@tokens-state))
|
||||
(when-not (contains? cf/flags :tokenscript)
|
||||
(let [cached (get @cache-atom tokens)]
|
||||
(cond
|
||||
(nil? tokens) nil
|
||||
;; The tokens are already processing somewhere
|
||||
(rx/observable? cached) (rx/sub! cached #(reset! tokens-state %))
|
||||
;; Get the cached entry
|
||||
(some? cached) (reset! tokens-state cached)
|
||||
;; No cached entry, start processing
|
||||
:else (let [resolved-tokens-s (if interactive?
|
||||
(resolve-tokens-interactive tokens)
|
||||
(resolve-tokens tokens))]
|
||||
(swap! cache-atom assoc tokens resolved-tokens-s)
|
||||
(rx/sub! resolved-tokens-s (fn [resolved-tokens]
|
||||
(swap! cache-atom assoc tokens resolved-tokens)
|
||||
(reset! tokens-state resolved-tokens))))))))
|
||||
(if (contains? cf/flags :tokenscript)
|
||||
(ts/resolve-tokens tokens)
|
||||
@tokens-state)))
|
||||
|
||||
(defn use-resolved-tokens*
|
||||
"This hook will return the unresolved tokens as state until they are
|
||||
@@ -612,16 +617,19 @@
|
||||
[tokens & {:keys [interactive?]}]
|
||||
(let [state* (mf/use-state tokens)]
|
||||
(mf/with-effect [tokens interactive?]
|
||||
(if (seq tokens)
|
||||
(let [tpoint (ct/tpoint-ms)
|
||||
tokens-s (if interactive?
|
||||
(resolve-tokens-interactive tokens)
|
||||
(resolve-tokens tokens))]
|
||||
(when-not (contains? cf/flags :tokenscript)
|
||||
(if (seq tokens)
|
||||
(let [tpoint (ct/tpoint-ms)
|
||||
tokens-s (if interactive?
|
||||
(resolve-tokens-interactive tokens)
|
||||
(resolve-tokens tokens))]
|
||||
|
||||
(-> tokens-s
|
||||
(rx/sub! (fn [resolved-tokens]
|
||||
(let [elapsed (tpoint)]
|
||||
(l/dbg :hint "use-resolved-tokens*" :elapsed elapsed)
|
||||
(reset! state* resolved-tokens))))))
|
||||
(reset! state* tokens)))
|
||||
@state*))
|
||||
(-> tokens-s
|
||||
(rx/sub! (fn [resolved-tokens]
|
||||
(let [elapsed (tpoint)]
|
||||
(l/dbg :hint "use-resolved-tokens*" :elapsed elapsed)
|
||||
(reset! state* resolved-tokens))))))
|
||||
(reset! state* tokens))))
|
||||
(if (contains? cf/flags :tokenscript)
|
||||
(ts/resolve-tokens tokens)
|
||||
@state*)))
|
||||
|
||||
164
frontend/src/app/main/data/tokenscript.cljs
Normal file
164
frontend/src/app/main/data/tokenscript.cljs
Normal file
@@ -0,0 +1,164 @@
|
||||
(ns app.main.data.tokenscript
|
||||
(:require
|
||||
["@penpot/tokenscript" :refer [BaseSymbolType
|
||||
ColorSymbol
|
||||
ListSymbol NumberSymbol
|
||||
NumberWithUnitSymbol
|
||||
ProcessorError
|
||||
processTokens
|
||||
TokenSymbol
|
||||
makeConfig]]
|
||||
[app.common.logging :as l]
|
||||
[app.common.time :as ct]
|
||||
[app.main.data.workspace.tokens.errors :as wte]))
|
||||
|
||||
(l/set-level! :debug)
|
||||
|
||||
;; Config ----------------------------------------------------------------------
|
||||
|
||||
(def config (makeConfig))
|
||||
|
||||
;; Class predicates ------------------------------------------------------------
|
||||
;; Predicates to get information about the tokenscript interpreter symbol type
|
||||
;; Or to determine the error
|
||||
|
||||
(defn tokenscript-symbol? [v]
|
||||
(instance? BaseSymbolType v))
|
||||
|
||||
(defn structured-token? [v]
|
||||
(instance? TokenSymbol v))
|
||||
|
||||
(defn structured-record-token? [^js v]
|
||||
(and (structured-token? v) (instance? js/Map (.-value v))))
|
||||
|
||||
(defn structured-array-token? [^js v]
|
||||
(and (structured-token? v) (instance? js/Array (.-value v))))
|
||||
|
||||
(defn number-with-unit-symbol? [v]
|
||||
(instance? NumberWithUnitSymbol v))
|
||||
|
||||
(defn number-symbol? [v]
|
||||
(instance? NumberSymbol v))
|
||||
|
||||
(defn list-symbol? [v]
|
||||
(instance? ListSymbol v))
|
||||
|
||||
(defn color-symbol? [v]
|
||||
(instance? ColorSymbol v))
|
||||
|
||||
(defn processor-error? [err]
|
||||
(instance? ProcessorError err))
|
||||
|
||||
;; Conversion Tools ------------------------------------------------------------
|
||||
;; Helpers to convert tokenscript symbols to penpot accepted formats
|
||||
|
||||
(defn color-symbol->hex-string [^js v]
|
||||
(when (color-symbol? v)
|
||||
(.toString (.to v "hex"))))
|
||||
|
||||
(defn color-alpha [^js v]
|
||||
(if (.isHex v)
|
||||
1
|
||||
(or (.getAttribute v "alpha") 1)))
|
||||
|
||||
(defn color-symbol->penpot-color [^js v]
|
||||
{:color (color-symbol->hex-string v)
|
||||
:opacity (color-alpha v)})
|
||||
|
||||
(defn rem-number-with-unit? [v]
|
||||
(and (number-with-unit-symbol? v)
|
||||
(= (.-unit v) "rem")))
|
||||
|
||||
(defn rem->px [^js v]
|
||||
(* (.-value v) 16))
|
||||
|
||||
(declare tokenscript-symbols->penpot-unit)
|
||||
|
||||
(defn structured-token->penpot-map
|
||||
"Converts structured token (record or array) to penpot map format.
|
||||
Structured tokens are non-primitive token types like `typography` or `box-shadow`."
|
||||
[^js token-symbol]
|
||||
(if (instance? js/Array (.-value token-symbol))
|
||||
(mapv structured-token->penpot-map (.-value token-symbol))
|
||||
(let [entries (es6-iterator-seq (.entries (.-value token-symbol)))]
|
||||
(into {} (map (fn [[k v :as V]]
|
||||
[(keyword k) (tokenscript-symbols->penpot-unit v)])
|
||||
entries)))))
|
||||
|
||||
(defn tokenscript-symbols->penpot-unit [^js v]
|
||||
(cond
|
||||
(structured-token? v) (structured-token->penpot-map v)
|
||||
(list-symbol? v) (tokenscript-symbols->penpot-unit (.nth 1 v))
|
||||
(color-symbol? v) (.-value (.to v "hex"))
|
||||
(rem-number-with-unit? v) (rem->px v)
|
||||
:else (.-value v)))
|
||||
|
||||
;; Processors ------------------------------------------------------------------
|
||||
;; The processor resolves tokens
|
||||
;; resolved/error tokens get put back into a clojure structure directly during build time
|
||||
;; For updating tokens we use the `TokenResolver` crud methods from the processing result
|
||||
;; The `TokenResolver` has detailed information for each tokens dependency graph
|
||||
|
||||
(defn create-token-builder
|
||||
"Collects resolved tokens during build time into a clojure structure.
|
||||
Returns Tokenscript Symbols in `:resolved-value` key."
|
||||
[tokens]
|
||||
(let [output (volatile! tokens)
|
||||
|
||||
;; When a token is resolved (No parsing / reference errors) we assing `:resolved-value` for the original token
|
||||
on-resolve
|
||||
(fn [^js/String token-name ^js/Symbol resolved-symbol]
|
||||
(vswap! output assoc-in [token-name :resolved-value] resolved-symbol))
|
||||
|
||||
;; When a token contains any errors we assing `:errors` for the original token
|
||||
on-error
|
||||
(fn [^js/String token-name ^js/Error _error ^js/String _original-value]
|
||||
(let [value (get tokens token-name)
|
||||
default-error [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]]
|
||||
(vswap! output assoc-in [token-name :errors] default-error)))
|
||||
|
||||
;; Extract the atom value
|
||||
get-result
|
||||
(fn [] @output)]
|
||||
#js {:onResolve on-resolve
|
||||
:onError on-error
|
||||
:getResult get-result}))
|
||||
|
||||
(defn clj->token->tokenscript-token
|
||||
"Convert penpot token into a format that tokenscript can handle."
|
||||
[{:keys [type value]}]
|
||||
#js {"$type" (name type)
|
||||
"$value" (clj->js value)})
|
||||
|
||||
(defn clj-tokens->tokenscript-tokens
|
||||
"Convert penpot map of tokens into tokenscript map structure.
|
||||
tokenscript accepts a map of [token-name {\"$type\": string, \"$value\": any}]"
|
||||
[tokens]
|
||||
(let [token-map (js/Map.)]
|
||||
(doseq [[k token] tokens]
|
||||
(.set token-map k (clj->token->tokenscript-token token)))
|
||||
token-map))
|
||||
|
||||
(defn process-tokens
|
||||
"Builds tokens using `tokenscript`."
|
||||
[tokens]
|
||||
(let [input (clj-tokens->tokenscript-tokens tokens)
|
||||
result (processTokens input #js {:config config
|
||||
:builder (create-token-builder tokens)})]
|
||||
result))
|
||||
|
||||
(defn update-token
|
||||
[tokens token]
|
||||
(let [result (process-tokens tokens)
|
||||
resolver (.-resolver result)]
|
||||
(.updateToken resolver #js {:tokenPath (:name token)
|
||||
:tokenData (clj->token->tokenscript-token token)})))
|
||||
|
||||
;; Main ------------------------------------------------------------------------
|
||||
|
||||
(defn resolve-tokens [tokens]
|
||||
(let [tpoint (ct/tpoint-ms)
|
||||
result (process-tokens tokens)
|
||||
elapsed (tpoint)]
|
||||
(l/dbg :hint "tokenscript/resolve-tokens" :elapsed elapsed)
|
||||
(.-output result)))
|
||||
@@ -18,11 +18,13 @@
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.types.typography :as cty]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.tinycolor :as tinycolor]
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.colors :as wdc]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
@@ -627,7 +629,9 @@
|
||||
(when-let [tokens (some-> (dsh/lookup-file-data state)
|
||||
(get :tokens-lib)
|
||||
(ctob/get-tokens-in-active-sets))]
|
||||
(->> (sd/resolve-tokens tokens)
|
||||
(->> (if (contains? cf/flags :tokenscript)
|
||||
(rx/of (ts/resolve-tokens tokens))
|
||||
(sd/resolve-tokens tokens))
|
||||
(rx/mapcat
|
||||
(fn [resolved-tokens]
|
||||
(let [undo-id (js/Symbol)
|
||||
@@ -645,6 +649,9 @@
|
||||
any-variant? (->> shapes vals (some ctk/is-variant?) boolean)
|
||||
|
||||
resolved-value (get-in resolved-tokens [(cft/token-identifier token) :resolved-value])
|
||||
resolved-value (if (contains? cf/flags :tokenscript)
|
||||
(ts/tokenscript-symbols->penpot-unit resolved-value)
|
||||
resolved-value)
|
||||
tokenized-attributes (cft/attributes-map attributes token)
|
||||
type (:type token)]
|
||||
(rx/concat
|
||||
@@ -699,17 +706,18 @@
|
||||
"Removes `attributes` that match `token` for `shape-ids`.
|
||||
|
||||
Doesn't update shape attributes."
|
||||
[{:keys [attributes token shape-ids] :as _props}]
|
||||
[{:keys [attributes token-name shape-ids] :as _props}]
|
||||
(ptk/reify ::unapply-token
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of
|
||||
(let [remove-token #(when % (cft/remove-attributes-for-token attributes token %))]
|
||||
(let [remove-token #(when % (cft/remove-attributes-for-token attributes token-name %))]
|
||||
(dwsh/update-shapes
|
||||
shape-ids
|
||||
(fn [shape]
|
||||
(update shape :applied-tokens remove-token))))))))
|
||||
|
||||
|
||||
(defn toggle-token
|
||||
[{:keys [token attrs shape-ids expand-with-children]}]
|
||||
(ptk/reify ::on-toggle-token
|
||||
@@ -739,7 +747,7 @@
|
||||
(if unapply-tokens?
|
||||
(rx/of
|
||||
(unapply-token {:attributes (or attrs all-attributes attributes)
|
||||
:token token
|
||||
:token-name (:name token)
|
||||
:shape-ids shape-ids}))
|
||||
(rx/of
|
||||
(cond
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
(ns app.main.data.workspace.tokens.color
|
||||
(:require
|
||||
[app.common.files.tokens :as cft]
|
||||
[app.main.data.tinycolor :as tinycolor]))
|
||||
[app.config :as cf]
|
||||
[app.main.data.tinycolor :as tinycolor]
|
||||
[app.main.data.tokenscript :as ts]))
|
||||
|
||||
(defn color-bullet-color [token-color-value]
|
||||
(when-let [tc (tinycolor/valid-color token-color-value)]
|
||||
@@ -17,5 +19,8 @@
|
||||
(tinycolor/->hex-string tc))))
|
||||
|
||||
(defn resolved-token-bullet-color [{:keys [resolved-value] :as token}]
|
||||
(when (and resolved-value (cft/color-token? token))
|
||||
(color-bullet-color resolved-value)))
|
||||
(if (contains? cf/flags :tokenscript)
|
||||
(when (and resolved-value (ts/color-symbol? resolved-value))
|
||||
(ts/color-symbol->penpot-color resolved-value))
|
||||
(when (and resolved-value (cft/color-token? token))
|
||||
(color-bullet-color resolved-value))))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns app.main.data.workspace.tokens.format
|
||||
(:require
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def category-dictionary
|
||||
@@ -23,14 +24,52 @@
|
||||
:color "Color"
|
||||
:inset "Inner Shadow"})
|
||||
|
||||
(declare format-token-value)
|
||||
|
||||
(defn- format-map-entries
|
||||
"Formats a sequence of [k v] entries into a formatted string."
|
||||
[entries]
|
||||
(->> entries
|
||||
(map (fn [[k v]]
|
||||
(str "- " (category-dictionary (keyword k)) ": " (format-token-value v))))
|
||||
(str/join "\n")
|
||||
(str "\n")))
|
||||
|
||||
(defn- format-structured-token
|
||||
"Formats tokenscript Token"
|
||||
[token-symbol]
|
||||
(->> (.-value token-symbol)
|
||||
(.entries)
|
||||
(es6-iterator-seq)
|
||||
(format-map-entries)))
|
||||
|
||||
(defn format-tokenscript-symbol
|
||||
[^js tokenscript-symbol]
|
||||
(cond
|
||||
(ts/rem-number-with-unit? tokenscript-symbol)
|
||||
(str (ts/rem->px tokenscript-symbol) "px")
|
||||
|
||||
(ts/color-symbol? tokenscript-symbol)
|
||||
(ts/color-symbol->hex-string tokenscript-symbol)
|
||||
|
||||
(ts/structured-record-token? tokenscript-symbol)
|
||||
(format-structured-token tokenscript-symbol)
|
||||
|
||||
(ts/structured-array-token? tokenscript-symbol)
|
||||
(str/join "\n" (map format-tokenscript-symbol (.-value tokenscript-symbol)))
|
||||
|
||||
:else
|
||||
(.toString tokenscript-symbol)))
|
||||
|
||||
(defn format-token-value
|
||||
"Converts token value of any shape to a string."
|
||||
[token-value]
|
||||
(cond
|
||||
(ts/tokenscript-symbol? token-value)
|
||||
(format-tokenscript-symbol token-value)
|
||||
|
||||
(map? token-value)
|
||||
(->> (map (fn [[k v]] (str "- " (category-dictionary k) ": " (format-token-value v))) token-value)
|
||||
(str/join "\n")
|
||||
(str "\n"))
|
||||
(format-map-entries token-value)
|
||||
|
||||
(and (sequential? token-value) (every? map? token-value))
|
||||
(str/join "\n" (map format-token-value token-value))
|
||||
|
||||
@@ -564,18 +564,17 @@
|
||||
(mf/use-fn
|
||||
(mf/deps on-detach tokens disabled token-applied)
|
||||
(fn [event]
|
||||
(let [token (get-token-op tokens token-applied)]
|
||||
(when-not disabled
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(reset! token-applied* nil)
|
||||
(reset! selected-id* nil)
|
||||
(reset! focused-id* nil)
|
||||
(when on-detach
|
||||
(on-detach token))
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(dom/focus! (mf/ref-val ref))))))))
|
||||
(when-not disabled
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(reset! token-applied* nil)
|
||||
(reset! selected-id* nil)
|
||||
(reset! focused-id* nil)
|
||||
(when on-detach
|
||||
(on-detach token-applied))
|
||||
(ts/schedule-on-idle
|
||||
(fn []
|
||||
(dom/focus! (mf/ref-val ref)))))))
|
||||
|
||||
on-token-key-down
|
||||
(mf/use-fn
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
(def ^:private schema:properties-row
|
||||
[:map
|
||||
[:term :string]
|
||||
[:detail :string]
|
||||
[:detail {:optional true} [:maybe :string]]
|
||||
[:property {:optional true} :string] ;; CSS valid property
|
||||
[:token {:optional true} :any] ;; resolved token object
|
||||
[:copiable {:optional true} :boolean]])
|
||||
|
||||
@@ -78,13 +78,15 @@
|
||||
(fn []
|
||||
(close-modals)
|
||||
;; FIXME: move set-mode to uri?
|
||||
(st/emit! (dw/set-options-mode :design)
|
||||
(st/emit! :interrupt
|
||||
(dw/set-options-mode :design)
|
||||
(dcm/go-to-dashboard-recent))))
|
||||
|
||||
nav-to-project
|
||||
(mf/use-fn
|
||||
(mf/deps project-id)
|
||||
#(st/emit! (dcm/go-to-dashboard-files ::rt/new-window true :project-id project-id)))]
|
||||
#(st/emit! :interrupt
|
||||
(dcm/go-to-dashboard-files ::rt/new-window true :project-id project-id)))]
|
||||
|
||||
(mf/with-effect [editing?]
|
||||
(when ^boolean editing?
|
||||
|
||||
@@ -401,7 +401,8 @@
|
||||
(dm/fmt "scale(%)" maybe-zoom))}))]
|
||||
|
||||
[:g.text-editor {:clip-path (dm/fmt "url(#%)" clip-id)
|
||||
:transform (dm/str transform)}
|
||||
:transform (dm/str transform)
|
||||
:data-testid "text-editor"}
|
||||
[:defs
|
||||
[:clipPath {:id clip-id}
|
||||
[:rect {:x x :y y :width width :height height}]]]
|
||||
|
||||
@@ -9,10 +9,15 @@
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.constants :refer [right-sidebar-default-width right-sidebar-default-max-width left-sidebar-default-max-width left-sidebar-default-width]]
|
||||
[app.config :as cf]
|
||||
[app.main.constants :refer [left-sidebar-default-max-width
|
||||
left-sidebar-default-width
|
||||
right-sidebar-default-max-width
|
||||
right-sidebar-default-width]]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
@@ -369,6 +374,12 @@
|
||||
(ctob/get-tokens-in-active-sets tokens-lib)
|
||||
{}))
|
||||
|
||||
tokenscript? (contains? cf/flags :tokenscript)
|
||||
|
||||
tokenscript-resolved-active-tokens
|
||||
(mf/with-memo [tokens-lib tokenscript?]
|
||||
(when tokenscript? (ts/resolve-tokens active-tokens)))
|
||||
|
||||
resolved-active-tokens
|
||||
(sd/use-resolved-tokens* active-tokens)]
|
||||
|
||||
@@ -380,7 +391,9 @@
|
||||
:page-id page-id
|
||||
:tokens-lib tokens-lib
|
||||
:active-tokens active-tokens
|
||||
:resolved-active-tokens resolved-active-tokens}])
|
||||
:resolved-active-tokens (if (contains? cf/flags :tokenscript)
|
||||
tokenscript-resolved-active-tokens
|
||||
resolved-active-tokens)}])
|
||||
[:> right-sidebar* {:section section
|
||||
:selected selected
|
||||
:drawing-tool drawing-tool
|
||||
|
||||
@@ -119,7 +119,8 @@
|
||||
[:button {:class (stl/css-case
|
||||
:toggle-content true
|
||||
:inverse expanded?)
|
||||
:aria-label "Toggle layer"
|
||||
:data-testid "toggle-content"
|
||||
:aria-expanded expanded?
|
||||
:on-click on-toggle-collapse}
|
||||
deprecated-icon/arrow])
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
:on-blur accept-edit
|
||||
:on-key-down on-key-down
|
||||
:auto-focus true
|
||||
:id (dm/str "layer-name-" shape-id)
|
||||
:default-value (d/nilv default-value "")}]
|
||||
[:*
|
||||
[:span
|
||||
@@ -118,6 +119,7 @@
|
||||
:hidden is-hidden
|
||||
:type-comp type-comp
|
||||
:type-frame type-frame)
|
||||
:id (dm/str "layer-name-" shape-id)
|
||||
:style {"--depth" depth "--parent-size" parent-size}
|
||||
:ref ref
|
||||
:on-double-click start-edit}
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -152,9 +152,9 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps token-colors groups)
|
||||
(fn [token]
|
||||
(fn [token-name]
|
||||
(let [prev-colors (mf/ref-val prev-colors-ref)
|
||||
token-color (some #(when (= (:token-name %) (:name token)) %) token-colors)
|
||||
token-color (some #(when (= (:token-name %) token-name) %) token-colors)
|
||||
|
||||
[color-operations _] (retrieve-color-operations groups token-color prev-colors)]
|
||||
(doseq [op color-operations]
|
||||
@@ -166,8 +166,8 @@
|
||||
(d/without-nils))]
|
||||
(mf/set-ref-val! prev-colors-ref
|
||||
(conj prev-colors color))
|
||||
(st/emit! (dwta/unapply-token {:attributes attr
|
||||
:token token
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes attr
|
||||
:shape-ids [(:shape-id op)]})))))))
|
||||
|
||||
select-only
|
||||
|
||||
@@ -74,7 +74,6 @@
|
||||
|
||||
render-wasm? (feat/use-feature "render-wasm/v1")
|
||||
|
||||
|
||||
^boolean
|
||||
multiple? (= :multiple fills)
|
||||
|
||||
@@ -183,9 +182,9 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token]
|
||||
(st/emit! (dwta/unapply-token {:attributes #{:fill}
|
||||
:token token
|
||||
(fn [token-name]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{:fill}
|
||||
:shape-ids ids}))))]
|
||||
|
||||
(mf/with-layout-effect [hide-on-export]
|
||||
@@ -215,7 +214,8 @@
|
||||
(when open?
|
||||
[:div {:class (stl/css :fill-content)}
|
||||
(cond
|
||||
(= :multiple fills)
|
||||
(or (= :multiple fills)
|
||||
(= :multiple fill-token-applied))
|
||||
[:div {:class (stl/css :fill-multiple)}
|
||||
[:div {:class (stl/css :fill-multiple-label)}
|
||||
(tr "settings.multiple")]
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -229,13 +229,13 @@
|
||||
:property (tr "workspace.options.opacity")
|
||||
:applied-token (get applied-tokens :opacity)
|
||||
:placeholder (if (or (= :multiple (get applied-tokens :opacity))
|
||||
(= :multiple (or (get values name) 1)))
|
||||
(= :multiple (or (get values :opacity) 1)))
|
||||
(tr "settings.multiple")
|
||||
"--")
|
||||
:align :right
|
||||
:class (stl/css :numeric-input-wrapper)
|
||||
:value (* 100
|
||||
(or (get values name) 1))}]
|
||||
(or (get values :opacity) 1))}]
|
||||
|
||||
[:div {:class (stl/css :input)
|
||||
:title (tr "workspace.options.opacity")}
|
||||
@@ -248,7 +248,6 @@
|
||||
:max 100
|
||||
:className (stl/css :numeric-input)}]])
|
||||
|
||||
|
||||
[:div {:class (stl/css :actions)}
|
||||
(cond
|
||||
(or (= :multiple hidden?) (not hidden?))
|
||||
|
||||
@@ -339,8 +339,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -475,7 +475,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(st/emit! (dwta/unapply-token {:token-name token
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -722,7 +722,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(st/emit! (dwta/unapply-token {:token-name token
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -97,8 +97,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -220,8 +220,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
@@ -550,8 +550,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -319,8 +319,8 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attr]
|
||||
(st/emit! (dwta/unapply-token {:token (first token)
|
||||
(fn [token-name attr]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes #{attr}
|
||||
:shape-ids ids}))))
|
||||
|
||||
|
||||
@@ -171,9 +171,9 @@
|
||||
on-detach-token
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [token attrs]
|
||||
(st/emit! (dwta/unapply-token {:attributes attrs
|
||||
:token token
|
||||
(fn [token-name attrs]
|
||||
(st/emit! (dwta/unapply-token {:token-name token-name
|
||||
:attributes attrs
|
||||
:shape-ids ids}))))]
|
||||
|
||||
[:section {:class (stl/css :stroke-section)
|
||||
|
||||
@@ -85,14 +85,14 @@
|
||||
(mf/use-fn
|
||||
(mf/deps detach-token token applied-token-name)
|
||||
(fn []
|
||||
(let [token (or token applied-token-name)]
|
||||
(detach-token token))))
|
||||
(let [token-name (or (:name token) applied-token-name)]
|
||||
(detach-token token-name))))
|
||||
|
||||
has-errors (some? (:errors token))
|
||||
token-name (:name token)
|
||||
resolved (:resolved-value token)
|
||||
not-active (and (empty? active-tokens)
|
||||
(nil? token))
|
||||
not-active (or (empty? active-tokens)
|
||||
(nil? token))
|
||||
id (dm/str (:id token) "-name")
|
||||
swatch-tooltip-content (cond
|
||||
not-active
|
||||
@@ -344,7 +344,6 @@
|
||||
(mf/with-effect [color prev-color disable-picker]
|
||||
(when (and (not disable-picker) (not= prev-color color))
|
||||
(modal/update-props! :colorpicker {:data (parse-color color)})))
|
||||
|
||||
[:div {:class [class row-class]}
|
||||
;; Drag handler
|
||||
(when (some? on-reorder)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[app.common.types.shape.layout :as ctsl]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
@@ -14,7 +13,6 @@
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.workspace.tokens.management.context-menu :refer [token-context-menu]]
|
||||
[app.main.ui.workspace.tokens.management.forms.rename-node-modal :refer [rename-node-modal*]]
|
||||
[app.main.ui.workspace.tokens.management.group :refer [token-group*]]
|
||||
[app.main.ui.workspace.tokens.management.node-context-menu :refer [token-node-context-menu*]]
|
||||
[app.util.array :as array]
|
||||
@@ -145,7 +143,8 @@
|
||||
(let [token-ids (set tokens-in-path-ids)
|
||||
remaining-tokens (filter (fn [token]
|
||||
(not (contains? token-ids (:id token))))
|
||||
selected-token-set-tokens)]
|
||||
selected-token-set-tokens)
|
||||
_ (prn "Remaining tokens:" remaining-tokens)]
|
||||
(seq remaining-tokens))))
|
||||
|
||||
delete-token
|
||||
@@ -165,22 +164,6 @@
|
||||
(st/emit! (dwtl/toggle-token-path (str (name type) "." path)))
|
||||
(st/emit! (dwtl/toggle-token-path (name type)))))))
|
||||
|
||||
;; node-to-rename (mf/use-state nil)
|
||||
;; node-to-rename* (deref node-to-rename)
|
||||
|
||||
;; rename-node
|
||||
;; (fn [node type]
|
||||
;; (st/emit! (dwtl/assign-token-node-context-menu nil))
|
||||
;; (st/emit! (modal/show :tokens/rename-node {:node node
|
||||
;; :type type})))
|
||||
|
||||
rename-node
|
||||
(mf/use-fn
|
||||
(fn [node type]
|
||||
(prn "Open rename node modal")
|
||||
(modal/show! :tokens/rename-node {:node node
|
||||
:type type})))
|
||||
|
||||
delete-node
|
||||
(mf/with-memo [selected-token-set-tokens selected-token-set-id]
|
||||
(fn [node type]
|
||||
@@ -208,9 +191,7 @@
|
||||
|
||||
[:*
|
||||
[:& token-context-menu {:on-delete-token delete-token}]
|
||||
[:> token-node-context-menu* {:on-rename-node rename-node
|
||||
:on-delete-node delete-node}]
|
||||
;; [:> rename-node-modal* {}]
|
||||
[:> token-node-context-menu* {:on-delete-node delete-node}]
|
||||
|
||||
[:> selected-set-info* {:tokens-lib tokens-lib
|
||||
:selected-token-set-id selected-token-set-id}]
|
||||
|
||||
@@ -64,14 +64,17 @@
|
||||
(let [selected? (selected-pred attribute)
|
||||
props {:attributes #{attribute}
|
||||
:token token
|
||||
:shape-ids shape-ids}]
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes #{attribute}
|
||||
:shape-ids shape-ids}]
|
||||
|
||||
{:title title
|
||||
:hint hint
|
||||
:selected? selected?
|
||||
:action (fn []
|
||||
(if selected?
|
||||
(st/emit! (dwta/unapply-token props))
|
||||
(st/emit! (dwta/unapply-token unnaply-props))
|
||||
(st/emit! (dwta/apply-token (assoc props :on-update-shape on-update-shape-fn)))))}))
|
||||
allowed-attributes)))
|
||||
|
||||
@@ -82,12 +85,15 @@
|
||||
{:keys [all-selected? selected-pred shape-ids]} (attribute-actions token selected-shapes attributes)
|
||||
all-action (let [props {:attributes attributes
|
||||
:token token
|
||||
:shape-ids shape-ids}]
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes attributes
|
||||
:shape-ids shape-ids}]
|
||||
{:title (tr "labels.all")
|
||||
:selected? all-selected?
|
||||
:hint hint
|
||||
:action #(if all-selected?
|
||||
(st/emit! (dwta/unapply-token props))
|
||||
(st/emit! (dwta/unapply-token unnaply-props))
|
||||
(st/emit! (dwta/apply-token (assoc props :on-update-shape (or on-update-shape-all on-update-shape)))))})
|
||||
single-actions (map (fn [[attr title]]
|
||||
(let [selected? (selected-pred attr)]
|
||||
@@ -96,10 +102,13 @@
|
||||
:action #(let [props {:attributes #{attr}
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes #{attr}
|
||||
:shape-ids shape-ids}
|
||||
event (cond
|
||||
all-selected? (-> (assoc props :attributes-to-remove attributes)
|
||||
(dwta/apply-token))
|
||||
selected? (dwta/unapply-token props)
|
||||
selected? (dwta/unapply-token unnaply-props)
|
||||
:else (-> (assoc props :on-update-shape on-update-shape)
|
||||
(dwta/apply-token)))]
|
||||
(st/emit! event))}))
|
||||
@@ -123,9 +132,12 @@
|
||||
:action (fn []
|
||||
(let [props {:attributes attrs
|
||||
:token token
|
||||
:shape-ids shape-ids}]
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes attrs
|
||||
:shape-ids shape-ids}]
|
||||
(if all-selected?
|
||||
(st/emit! (dwta/unapply-token props))
|
||||
(st/emit! (dwta/unapply-token unnaply-props))
|
||||
(st/emit! (dwta/apply-token (assoc props :on-update-shape on-update-shape))))))}
|
||||
{:title "Horizontal"
|
||||
:selected? horizontal-selected?
|
||||
@@ -165,10 +177,13 @@
|
||||
:action #(let [props {:attributes #{attr}
|
||||
:token token
|
||||
:shape-ids shape-ids}
|
||||
unnaply-props {:token-name (:name token)
|
||||
:attributes #{attr}
|
||||
:shape-ids shape-ids}
|
||||
event (cond
|
||||
all-selected? (-> (assoc props :attributes-to-remove attrs)
|
||||
(dwta/apply-token))
|
||||
selected? (dwta/unapply-token props)
|
||||
selected? (dwta/unapply-token unnaply-props)
|
||||
:else (-> (assoc props :on-update-shape on-update-shape)
|
||||
(dwta/apply-token)))]
|
||||
(st/emit! event))}))
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
[app.common.files.tokens :as cft]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.config :as cf]
|
||||
[app.main.data.style-dictionary :as sd]
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[app.main.data.workspace.tokens.errors :as wte]
|
||||
[app.main.data.workspace.tokens.format :as dwtf]
|
||||
[app.main.ui.ds.controls.input :as ds]
|
||||
[app.main.ui.forms :as fc]
|
||||
@@ -139,6 +142,18 @@
|
||||
;; -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
(defn- resolve-value-tokenscript
|
||||
[tokens prev-token value]
|
||||
(let [result (ts/update-token tokens (assoc prev-token :value value))
|
||||
token-result (.-resolved result)]
|
||||
(rx/of
|
||||
(cond
|
||||
(ts/processor-error? token-result) {:error (wte/error-with-value :error.style-dictionary/missing-reference (some->> (.-dependencyChain token-result)
|
||||
(seq)
|
||||
(rest)))}
|
||||
(instance? js/Error token-result) {:error (wte/error-with-value :error.style-dictionary/invalid-token-value value)}
|
||||
:else {:value token-result}))))
|
||||
|
||||
(defn- resolve-value
|
||||
[tokens prev-token token-name value]
|
||||
(let [valid-token-name?
|
||||
@@ -216,7 +231,10 @@
|
||||
|
||||
(mf/with-effect [resolve-stream tokens token input-name token-name]
|
||||
|
||||
(let [subs (->> resolve-stream
|
||||
(let [resolve-value (if (contains? cf/flags :tokenscript)
|
||||
resolve-value-tokenscript
|
||||
resolve-value)
|
||||
subs (->> resolve-stream
|
||||
(rx/debounce 300)
|
||||
(rx/mapcat (partial resolve-value tokens token token-name))
|
||||
(rx/map (fn [result]
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||
[app.main.ui.forms :as fc]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls :as token.controls]
|
||||
[app.main.ui.workspace.tokens.management.forms.validators :refer [default-validate-token]]
|
||||
@@ -143,10 +142,6 @@
|
||||
(fm/use-form :schema schema
|
||||
:initial initial)
|
||||
|
||||
warning-name-change?
|
||||
(not= (get-in @form [:data :name])
|
||||
(:name initial))
|
||||
|
||||
on-toggle-tab
|
||||
(mf/use-fn
|
||||
(mf/deps form)
|
||||
@@ -276,12 +271,7 @@
|
||||
:max-length max-input-length
|
||||
:variant "comfortable"
|
||||
:trim true
|
||||
:auto-focus true}]
|
||||
|
||||
(when (and warning-name-change? (= action "edit"))
|
||||
[:div {:class (stl/css :warning-name-change-notification-wrapper)}
|
||||
[:> context-notification*
|
||||
{:level :warning :appearance :ghost} (tr "workspace.tokens.warning-name-change")]])]
|
||||
:auto-focus true}]]
|
||||
|
||||
[:div {:class (stl/css :input-row)}
|
||||
(case type
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
(ns app.main.ui.workspace.tokens.management.forms.rename-node-modal
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc rename-node-modal*
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :tokens/rename-node}
|
||||
[{:keys [node type]}]
|
||||
|
||||
(let [on-close
|
||||
(mf/use-fn
|
||||
(mf/deps [])
|
||||
(fn []
|
||||
(modal/hide!)))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog)}
|
||||
[:> icon-button* {:class (stl/css :close-btn)
|
||||
:on-click on-close
|
||||
:aria-label (tr "labels.close")
|
||||
:variant "ghost"
|
||||
:icon i/close}]
|
||||
(str "Rename node modal for node: " node " of type: " type)]]))
|
||||
@@ -1,41 +0,0 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/typography.scss" as t;
|
||||
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
|
||||
.modal-overlay {
|
||||
--modal-title-foreground-color: var(--color-foreground-primary);
|
||||
--modal-text-foreground-color: var(--color-foreground-secondary);
|
||||
|
||||
@extend .modal-overlay-base;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
inset-inline-start: 0;
|
||||
inset-block-start: 0;
|
||||
block-size: 100%;
|
||||
inline-size: 100%;
|
||||
background-color: var(--overlay-color);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
inset-block-start: $sz-6;
|
||||
inset-inline-end: $sz-6;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
@extend .modal-container-base;
|
||||
inline-size: 100%;
|
||||
max-inline-size: 32rem;
|
||||
max-block-size: unset;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
(def ^:private schema:token-node-context-menu
|
||||
[:map
|
||||
[:on-rename-node fn?]
|
||||
[:on-delete-node fn?]])
|
||||
|
||||
(def ^:private tokens-node-menu-ref
|
||||
@@ -26,7 +25,7 @@
|
||||
|
||||
(mf/defc token-node-context-menu*
|
||||
{::mf/schema schema:token-node-context-menu}
|
||||
[{:keys [on-rename-node on-delete-node]}]
|
||||
[{:keys [on-delete-node]}]
|
||||
(let [mdata (mf/deref tokens-node-menu-ref)
|
||||
is-open? (boolean mdata)
|
||||
dropdown-ref (mf/use-ref)
|
||||
@@ -36,13 +35,7 @@
|
||||
dropdown-direction-change* (mf/use-ref 0)
|
||||
top (+ (get-in mdata [:position :y]) 5)
|
||||
left (+ (get-in mdata [:position :x]) 5)
|
||||
rename-node (mf/use-fn
|
||||
(mf/deps mdata)
|
||||
(fn []
|
||||
(let [node (get mdata :node)
|
||||
type (get mdata :type)]
|
||||
(when node
|
||||
(on-rename-node node type)))))
|
||||
|
||||
delete-node (mf/use-fn
|
||||
(mf/deps mdata)
|
||||
(fn []
|
||||
@@ -82,11 +75,6 @@
|
||||
:on-context-menu prevent-default}
|
||||
(when mdata
|
||||
[:ul {:class (stl/css :token-node-context-menu-list)}
|
||||
[:li {:class (stl/css :token-node-context-menu-listitem)}
|
||||
[:button {:class (stl/css :token-node-context-menu-action)
|
||||
:type "button"
|
||||
:on-click rename-node}
|
||||
(tr "labels.rename")]]
|
||||
[:li {:class (stl/css :token-node-context-menu-listitem)}
|
||||
[:button {:class (stl/css :token-node-context-menu-action)
|
||||
:type "button"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[app.common.files.tokens :as cft]
|
||||
[app.common.path-names :as cpn]
|
||||
[app.common.types.token :as ctt]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.color :as dwtc]
|
||||
[app.main.data.workspace.tokens.format :as dwtf]
|
||||
@@ -177,6 +178,8 @@
|
||||
[{:keys [on-click token on-context-menu selected-shapes is-selected-inside-layout active-theme-tokens]}]
|
||||
(let [{:keys [name value errors type]} token
|
||||
|
||||
resolved-token (get active-theme-tokens (:name token))
|
||||
|
||||
has-selected? (pos? (count selected-shapes))
|
||||
is-reference? (cft/is-reference? token)
|
||||
contains-path? (str/includes? name ".")
|
||||
@@ -209,8 +212,10 @@
|
||||
is-viewer? (not can-edit?)
|
||||
|
||||
ref-not-in-active-set
|
||||
(and is-reference?
|
||||
(not (contains-reference-value? value active-theme-tokens)))
|
||||
(if (contains? cf/flags :tokenscript)
|
||||
(seq (:errors resolved-token))
|
||||
(and is-reference?
|
||||
(not (contains-reference-value? value active-theme-tokens))))
|
||||
|
||||
no-valid-value (seq errors)
|
||||
|
||||
@@ -220,9 +225,8 @@
|
||||
|
||||
color
|
||||
(when (cft/color-token? token)
|
||||
(let [theme-token (get active-theme-tokens name)]
|
||||
(or (dwtc/resolved-token-bullet-color theme-token)
|
||||
(dwtc/resolved-token-bullet-color token))))
|
||||
(or (dwtc/resolved-token-bullet-color resolved-token)
|
||||
(dwtc/resolved-token-bullet-color token)))
|
||||
|
||||
status-icon? (contains? token-types-with-status-icon type)
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@
|
||||
;; ==== Action
|
||||
events [(dwta/unapply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-1")})]
|
||||
:token-name "test-token-1"})]
|
||||
|
||||
step2 (fn [_]
|
||||
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
|
||||
@@ -289,7 +289,7 @@
|
||||
;; ==== Action
|
||||
events [(dwta/unapply-token {:shape-ids [(cthi/id :c-frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-1")})
|
||||
:token-name "test-token-1"})
|
||||
(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-3")
|
||||
|
||||
@@ -7,7 +7,7 @@ export DEVENV_PNAME="penpotdev";
|
||||
export CURRENT_USER_ID=$(id -u);
|
||||
export CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD);
|
||||
|
||||
export IMAGEMAGICK_VERSION=7.1.2-0
|
||||
export IMAGEMAGICK_VERSION=7.1.2-13
|
||||
|
||||
# Safe directory to avoid ownership errors with Git
|
||||
git config --global --add safe.directory /home/penpot/penpot || true
|
||||
|
||||
@@ -55,8 +55,8 @@ export class PenpotMcpServer {
|
||||
this.port = parseInt(process.env.PENPOT_MCP_SERVER_PORT ?? "4401", 10);
|
||||
this.webSocketPort = parseInt(process.env.PENPOT_MCP_WEBSOCKET_PORT ?? "4402", 10);
|
||||
this.replPort = parseInt(process.env.PENPOT_MCP_REPL_PORT ?? "4403", 10);
|
||||
this.listenAddress = process.env.PENPOT_MCP_SERVER_LISTEN_ADDRESS ?? "localhost";
|
||||
this.serverAddress = process.env.PENPOT_MCP_SERVER_ADDRESS ?? "localhost";
|
||||
this.listenAddress = process.env.PENPOT_MCP_SERVER_LISTEN_ADDRESS ?? "0.0.0.0";
|
||||
this.serverAddress = process.env.PENPOT_MCP_SERVER_ADDRESS ?? "0.0.0.0";
|
||||
|
||||
this.configLoader = new ConfigurationLoader(process.cwd());
|
||||
this.apiDocs = new ApiDocs();
|
||||
|
||||
@@ -22,7 +22,7 @@ pub use surfaces::{SurfaceId, Surfaces};
|
||||
|
||||
use crate::performance;
|
||||
use crate::shapes::{
|
||||
all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke, Type,
|
||||
all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Type,
|
||||
};
|
||||
use crate::state::{ShapesPoolMutRef, ShapesPoolRef};
|
||||
use crate::tiles::{self, PendingTiles, TileRect};
|
||||
@@ -1512,6 +1512,16 @@ impl RenderState {
|
||||
Self::combine_blur_values(self.combined_layer_blur(shape.blur), extra_layer_blur);
|
||||
let blur_filter = combined_blur
|
||||
.and_then(|blur| skia::image_filters::blur((blur.value, blur.value), None, None, None));
|
||||
// Legacy path is only stable up to 1.0 zoom: the canvas is scaled and the shadow
|
||||
// filter is evaluated in that scaled space, so for scale > 1 it over-inflates blur/spread.
|
||||
// We also disable it when combined layer blur is present to avoid incorrect composition.
|
||||
let use_low_zoom_path = scale <= 1.0 && combined_blur.is_none();
|
||||
|
||||
if use_low_zoom_path {
|
||||
// Match pre-commit behavior: scale blur/spread with zoom for low zoom levels.
|
||||
transformed_shadow.to_mut().blur = shadow.blur * scale;
|
||||
transformed_shadow.to_mut().spread = shadow.spread * scale;
|
||||
}
|
||||
|
||||
let mut transform_matrix = shape.transform;
|
||||
let center = shape.center();
|
||||
@@ -1526,28 +1536,20 @@ impl RenderState {
|
||||
let world_offset = (mapped.x, mapped.y);
|
||||
|
||||
// The opacity of fills and strokes shouldn't affect the shadow,
|
||||
// so we paint everything black with the same opacity
|
||||
plain_shape.to_mut().clear_fills();
|
||||
// so we paint everything black with the same opacity.
|
||||
let plain_shape_mut = plain_shape.to_mut();
|
||||
plain_shape_mut.clear_fills();
|
||||
if shape.has_fills() {
|
||||
plain_shape
|
||||
.to_mut()
|
||||
.add_fill(Fill::Solid(SolidColor(skia::Color::BLACK)));
|
||||
plain_shape_mut.add_fill(Fill::Solid(SolidColor(skia::Color::BLACK)));
|
||||
}
|
||||
|
||||
plain_shape.to_mut().clear_strokes();
|
||||
for stroke in shape.strokes.iter() {
|
||||
plain_shape.to_mut().add_stroke(Stroke {
|
||||
fill: Fill::Solid(SolidColor(skia::Color::BLACK)),
|
||||
width: stroke.width,
|
||||
style: stroke.style,
|
||||
cap_end: stroke.cap_end,
|
||||
cap_start: stroke.cap_start,
|
||||
kind: stroke.kind,
|
||||
});
|
||||
// Reuse existing strokes and only override their fill color.
|
||||
for stroke in plain_shape_mut.strokes.iter_mut() {
|
||||
stroke.fill = Fill::Solid(SolidColor(skia::Color::BLACK));
|
||||
}
|
||||
|
||||
plain_shape.to_mut().clear_shadows();
|
||||
plain_shape.to_mut().blur = None;
|
||||
plain_shape_mut.clear_shadows();
|
||||
plain_shape_mut.blur = None;
|
||||
|
||||
let Some(drop_filter) = transformed_shadow.get_drop_shadow_filter() else {
|
||||
return;
|
||||
@@ -1556,6 +1558,39 @@ impl RenderState {
|
||||
let mut bounds = drop_filter.compute_fast_bounds(shape_bounds);
|
||||
// Account for the shadow offset so the temporary surface fully contains the shifted blur.
|
||||
bounds.offset(world_offset);
|
||||
// Early cull if the shadow bounds are outside the render area.
|
||||
if !bounds.intersects(self.render_area) {
|
||||
return;
|
||||
}
|
||||
|
||||
if use_low_zoom_path {
|
||||
let mut shadow_paint = skia::Paint::default();
|
||||
shadow_paint.set_image_filter(drop_filter);
|
||||
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
|
||||
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||
drop_canvas.save_layer(&layer_rec);
|
||||
drop_canvas.scale((scale, scale));
|
||||
drop_canvas.translate(translation);
|
||||
|
||||
self.with_nested_blurs_suppressed(|state| {
|
||||
state.render_shape(
|
||||
&plain_shape,
|
||||
clip_bounds,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::DropShadows,
|
||||
SurfaceId::DropShadows,
|
||||
false,
|
||||
Some(shadow.offset),
|
||||
None,
|
||||
);
|
||||
});
|
||||
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
return;
|
||||
}
|
||||
|
||||
let filter_result =
|
||||
filters::render_into_filter_surface(self, bounds, |state, temp_surface| {
|
||||
|
||||
Reference in New Issue
Block a user