mirror of
https://github.com/penpot/penpot.git
synced 2026-02-05 12:12:07 -05:00
Compare commits
4 Commits
niwinz-plu
...
hiru-plugi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ddfb5cce5 | ||
|
|
6e48e73b40 | ||
|
|
17935443df | ||
|
|
150d57b1eb |
@@ -124,6 +124,7 @@
|
||||
:token-base-font-size
|
||||
:token-color
|
||||
:token-shadow
|
||||
:token-tokenscript
|
||||
:transit-readable-response
|
||||
:user-feedback
|
||||
;; TODO: remove this flag.
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ In the `apps` folder you'll find some examples that use the libraries mentioned
|
||||
- example-styles: to run this example you should run
|
||||
|
||||
```
|
||||
npm run start:styles-example
|
||||
pnpm run start:styles-example
|
||||
```
|
||||
|
||||
Open in your browser: `http://localhost:4202/`
|
||||
@@ -28,8 +28,8 @@ Open in your browser: `http://localhost:4202/`
|
||||
|
||||
This guide will help you launch a Penpot plugin from the penpot-plugins repository. Before proceeding, ensure that you have Penpot running locally by following the [setup instructions](https://help.penpot.app/technical-guide/developer/devenv/).
|
||||
|
||||
In the terminal, navigate to the **penpot-plugins** repository and run `npm install` to install the required dependencies.
|
||||
Then, run `npm start` to launch the plugins wrapper.
|
||||
In the terminal, navigate to the **penpot-plugins** repository and run `pnpm install` to install the required dependencies.
|
||||
Then, run `pnpm run start` to launch the plugins wrapper.
|
||||
|
||||
After installing the dependencies, choose a plugin to launch. You can either run one of the provided examples or create your own (see "Creating a plugin from scratch" below).
|
||||
To launch a plugin, Open a new terminal tab and run the appropriate startup script for the chosen plugin.
|
||||
@@ -38,7 +38,7 @@ For instance, to launch the Contrast plugin, use the following command:
|
||||
|
||||
```
|
||||
// for the contrast plugin
|
||||
npm run start:plugin:contrast
|
||||
pnpm run start:plugin:contrast
|
||||
```
|
||||
|
||||
Finally, open in your browser the specific port. In this specific example would be `http://localhost:4302`
|
||||
@@ -49,21 +49,22 @@ A table listing the available plugins and their corresponding startup commands i
|
||||
|
||||
| Plugin | Description | PORT | Start command | Manifest URL |
|
||||
| ----------------------- | ----------------------------------------------------------- | ---- | ------------------------------------- | ------------------------------------------ |
|
||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | npm run start:plugin:poc-state | http://localhost:4301/assets/manifest.json |
|
||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | npm run start:plugin:contrast | http://localhost:4302/assets/manifest.json |
|
||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | npm run start:plugin:icons | http://localhost:4303/assets/manifest.json |
|
||||
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | npm run start:plugin:loremipsum | http://localhost:4304/assets/manifest.json |
|
||||
| create-palette-plugin | Creates a board with all the palette colors | 4305 | npm run start:plugin:palette | http://localhost:4305/assets/manifest.json |
|
||||
| table-plugin | Create or import table | 4306 | npm run start:table-plugin | http://localhost:4306/assets/manifest.json |
|
||||
| rename-layers-plugin | Rename layers in bulk | 4307 | npm run start:plugin:renamelayers | http://localhost:4307/assets/manifest.json |
|
||||
| colors-to-tokens-plugin | Generate tokens JSON file | 4308 | npm run start:plugin:colors-to-tokens | http://localhost:4308/assets/manifest.json |
|
||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | pnpm run start:plugin:poc-state | http://localhost:4301/assets/manifest.json |
|
||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | pnpm run start:plugin:contrast | http://localhost:4302/assets/manifest.json |
|
||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | pnpm run start:plugin:icons | http://localhost:4303/assets/manifest.json |
|
||||
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | pnpm run start:plugin:loremipsum | http://localhost:4304/assets/manifest.json |
|
||||
| create-palette-plugin | Creates a board with all the palette colors | 4305 | pnpm run start:plugin:palette | http://localhost:4305/assets/manifest.json |
|
||||
| table-plugin | Create or import table | 4306 | pnpm run start:table-plugin | http://localhost:4306/assets/manifest.json |
|
||||
| rename-layers-plugin | Rename layers in bulk | 4307 | pnpm run start:plugin:renamelayers | http://localhost:4307/assets/manifest.json |
|
||||
| colors-to-tokens-plugin | Generate tokens JSON file | 4308 | pnpm run start:plugin:colors-to-tokens | http://localhost:4308/assets/manifest.json |
|
||||
| poc-tokens-plugin | Sandbox plugin to test tokens functionality | 4309 | pnpm run start:plugin:poc-tokens | http://localhost:4309/assets/manifest.json |
|
||||
|
||||
## Web Apps
|
||||
|
||||
| App | Description | PORT | Start command | URL |
|
||||
| --------------- | ----------------------------------------------------------------- | ---- | -------------------------------- | ---------------------- |
|
||||
| plugins-runtime | Runtime for the plugins subsystem | 4200 | npm run start:app:runtime | |
|
||||
| example-styles | Showcase of some of the Penpot styles that can be used in plugins | 4201 | npm run start:app:styles-example | http://localhost:4201/ |
|
||||
| plugins-runtime | Runtime for the plugins subsystem | 4200 | pnpm run start:app:runtime | |
|
||||
| example-styles | Showcase of some of the Penpot styles that can be used in plugins | 4201 | pnpm run start:app:styles-example | http://localhost:4201/ |
|
||||
|
||||
## Creating a plugin from scratch
|
||||
|
||||
|
||||
51
plugins/apps/poc-tokens-plugin/eslint.config.js
Normal file
51
plugins/apps/poc-tokens-plugin/eslint.config.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import baseConfig from '../../eslint.config.js';
|
||||
import { compat } from '../../eslint.base.config.js';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...compat
|
||||
.config({
|
||||
extends: [
|
||||
'plugin:@nx/angular',
|
||||
'plugin:@angular-eslint/template/process-inline-templates',
|
||||
],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'app',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'app',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
...compat
|
||||
.config({ extends: ['plugin:@nx/angular-template'] })
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.html'],
|
||||
rules: {},
|
||||
})),
|
||||
{ ignores: ['**/assets/*.js'] },
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: './tsconfig.*?.json',
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
79
plugins/apps/poc-tokens-plugin/project.json
Normal file
79
plugins/apps/poc-tokens-plugin/project.json
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"name": "poc-tokens-plugin",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/poc-tokens-plugin/src",
|
||||
"tags": ["type:plugin"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/poc-tokens-plugin",
|
||||
"index": "apps/poc-tokens-plugin/src/index.html",
|
||||
"browser": "apps/poc-tokens-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/poc-tokens-plugin/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/poc-tokens-plugin/src/favicon.ico",
|
||||
"apps/poc-tokens-plugin/src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"libs/plugins-styles/src/lib/styles.css",
|
||||
"apps/poc-tokens-plugin/src/styles.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"optimization": {
|
||||
"scripts": true,
|
||||
"styles": true,
|
||||
"fonts": false
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"dependsOn": ["buildPlugin"]
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "poc-tokens-plugin:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "poc-tokens-plugin:build:development",
|
||||
"port": 4309,
|
||||
"host": "0.0.0.0"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "poc-tokens-plugin:build"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
127
plugins/apps/poc-tokens-plugin/src/app/app.component.css
Normal file
127
plugins/apps/poc-tokens-plugin/src/app/app.component.css
Normal file
@@ -0,0 +1,127 @@
|
||||
/* @import "@penpot/plugin-styles/styles.css"; */
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title-l {
|
||||
margin: var(--spacing-16) 0;
|
||||
}
|
||||
|
||||
.columns {
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
flex-grow: 1;
|
||||
margin-block-end: var(--spacing-16);
|
||||
}
|
||||
|
||||
.panels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 0 var(--spacing-8);
|
||||
}
|
||||
|
||||
.panel {
|
||||
padding: var(--spacing-8);
|
||||
display: flex;
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.panel:not(:first-child) {
|
||||
border-block-start: 1px solid var(--df-secondary);
|
||||
padding-block-start: var(--spacing-16);
|
||||
}
|
||||
|
||||
.panel-heading,
|
||||
.token-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding-inline-end: var(--spacing-8);
|
||||
}
|
||||
|
||||
.panel-heading p,
|
||||
.token-group span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.panel-heading button,
|
||||
.token-group button {
|
||||
background: none;
|
||||
padding: var(--spacing-4) calc(var(--spacing-12) / 2);
|
||||
}
|
||||
|
||||
.panel-heading button:focus,
|
||||
.token-group button:focus {
|
||||
padding: calc(var(--spacing-4) - 2px) calc(var(--spacing-12) / 2 - 2px);
|
||||
}
|
||||
|
||||
.panel-item button {
|
||||
opacity: 0;
|
||||
margin-inline-end: var(--spacing-8);
|
||||
padding: var(--spacing-4) calc(var(--spacing-12) / 2);
|
||||
}
|
||||
|
||||
.panel-item button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.panel-item button:focus {
|
||||
opacity: 1;
|
||||
padding: calc(var(--spacing-4) - 2px) calc(var(--spacing-12) / 2 - 2px);
|
||||
}
|
||||
|
||||
.panel ul {
|
||||
/* flex-grow: 1; */
|
||||
overflow-y: auto;
|
||||
padding-inline-end: var(--spacing-8);
|
||||
}
|
||||
|
||||
.panel-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.panel-item span {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.set-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.set-item.selected {
|
||||
background-color: var(--db-quaternary);
|
||||
}
|
||||
|
||||
.set-item:hover {
|
||||
color: var(--da-primary);
|
||||
background-color: var(--db-secondary);
|
||||
}
|
||||
|
||||
.token-group:not(:first-child) {
|
||||
margin-top: var(--spacing-8);
|
||||
}
|
||||
|
||||
.token-group {
|
||||
border-block-end: 1px solid var(--df-secondary);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.token-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.token-item:hover {
|
||||
color: var(--da-primary);
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
144
plugins/apps/poc-tokens-plugin/src/app/app.component.html
Normal file
144
plugins/apps/poc-tokens-plugin/src/app/app.component.html
Normal file
@@ -0,0 +1,144 @@
|
||||
<div class="container">
|
||||
<p class="title-l">Design tokens plugin POC</p>
|
||||
|
||||
<div class="columns">
|
||||
<div class="panels">
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<p class="headline-m">THEMES</p>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="addTheme()"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul data-handler="themes-list">
|
||||
@for (theme of themes; track theme.id) {
|
||||
<li class="body-m panel-item theme-item">
|
||||
<span>{{ theme.group }} / {{ theme.name }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="renameTheme(theme.id, theme.name)"
|
||||
>
|
||||
🖊️
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="deleteTheme(theme.id)"
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
<div class="checkbox-container">
|
||||
<input
|
||||
class="checkbox-input"
|
||||
type="checkbox"
|
||||
id="checkbox1"
|
||||
[checked]="isThemeActive(theme.id)"
|
||||
(change)="toggleTheme(theme.id)"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<p class="headline-m">SETS</p>
|
||||
<button type="button" data-appearance="secondary" (click)="addSet()">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul data-handler="sets-list">
|
||||
@for (set of sets; track set.id) {
|
||||
<li
|
||||
class="body-m panel-item set-item"
|
||||
[class.selected]="set.id === currentSetId"
|
||||
>
|
||||
<span (click)="loadTokens(set.id)">
|
||||
{{ set.name }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="renameSet(set.id, set.name)"
|
||||
>
|
||||
🖊️
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="deleteSet(set.id)"
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
<div class="checkbox-container">
|
||||
<input
|
||||
class="checkbox-input"
|
||||
type="checkbox"
|
||||
id="checkbox1"
|
||||
[checked]="isSetActive(set.id)"
|
||||
(change)="toggleSet(set.id)"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panels">
|
||||
<div class="panel">
|
||||
<p class="headline-m">TOKENS</p>
|
||||
|
||||
<ul data-handler="tokens-list">
|
||||
@for (group of tokenGroups; track group[0]) {
|
||||
<li class="body-m token-group">
|
||||
<span>{{ group[0] }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="addToken(group[0])"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</li>
|
||||
@for (token of group[1]; track token.id) {
|
||||
<li
|
||||
class="body-m panel-item token-item"
|
||||
(click)="applyToken(token.id)"
|
||||
>
|
||||
<span>{{ token.name }}</span>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="renameToken(token.id, token.name)"
|
||||
>
|
||||
🖊️
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="deleteToken(token.id)"
|
||||
>
|
||||
❌
|
||||
</button>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<button type="button" data-appearance="primary" (click)="loadLibrary()">
|
||||
Load
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
290
plugins/apps/poc-tokens-plugin/src/app/app.component.ts
Normal file
290
plugins/apps/poc-tokens-plugin/src/app/app.component.ts
Normal file
@@ -0,0 +1,290 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { fromEvent, map, filter, take, merge } from 'rxjs';
|
||||
import { PluginMessageEvent, PluginUIEvent } from '../model';
|
||||
|
||||
type TokenTheme = {
|
||||
id: string;
|
||||
name: string;
|
||||
group: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
type TokenSet = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
active: boolean;
|
||||
};
|
||||
|
||||
type Token = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
type TokensGroup = [string, Token[]];
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.css',
|
||||
host: {
|
||||
'[attr.data-theme]': 'theme()',
|
||||
},
|
||||
})
|
||||
export class AppComponent {
|
||||
public route = inject(ActivatedRoute);
|
||||
|
||||
public messages$ = fromEvent<MessageEvent<PluginMessageEvent>>(
|
||||
window,
|
||||
'message',
|
||||
);
|
||||
|
||||
public initialTheme$ = this.route.queryParamMap.pipe(
|
||||
map((params) => params.get('theme')),
|
||||
filter((theme) => !!theme),
|
||||
take(1),
|
||||
);
|
||||
|
||||
public theme = toSignal(
|
||||
merge(
|
||||
this.initialTheme$,
|
||||
this.messages$.pipe(
|
||||
filter((event) => event.data.type === 'theme'),
|
||||
map((event) => {
|
||||
return event.data.content;
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public themes: TokenTheme[] = [];
|
||||
public sets: TokenSet[] = [];
|
||||
public tokenGroups: TokensGroup[] = [];
|
||||
public currentSetId: string | undefined = undefined;
|
||||
|
||||
constructor() {
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data.type === 'set-themes') {
|
||||
this.#setThemes(event.data.themesData);
|
||||
} else if (event.data.type === 'set-sets') {
|
||||
this.#setSets(event.data.setsData);
|
||||
} else if (event.data.type === 'set-tokens') {
|
||||
this.#setTokens(event.data.tokenGroupsData);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadLibrary() {
|
||||
this.#sendMessage({ type: 'load-library' });
|
||||
}
|
||||
|
||||
loadTokens(setId: string) {
|
||||
this.currentSetId = setId;
|
||||
this.#sendMessage({ type: 'load-tokens', setId });
|
||||
}
|
||||
|
||||
addTheme() {
|
||||
this.#sendMessage({
|
||||
type: 'add-theme',
|
||||
themeGroup: this.#randomString(),
|
||||
themeName: this.#randomString(),
|
||||
});
|
||||
}
|
||||
|
||||
addSet() {
|
||||
this.#sendMessage({ type: 'add-set', setName: this.#randomString() });
|
||||
}
|
||||
|
||||
addToken(tokenType: string) {
|
||||
let tokenValue;
|
||||
switch (tokenType) {
|
||||
case 'borderRadius':
|
||||
tokenValue = 25;
|
||||
break;
|
||||
case 'shadow':
|
||||
tokenValue = [
|
||||
{
|
||||
color: '#123456',
|
||||
inset: 'false',
|
||||
offsetX: '6',
|
||||
offsetY: '6',
|
||||
spread: '0',
|
||||
blur: '4',
|
||||
},
|
||||
];
|
||||
break;
|
||||
case 'color':
|
||||
tokenValue = '#fabada';
|
||||
break;
|
||||
case 'dimension':
|
||||
tokenValue = 100;
|
||||
break;
|
||||
case 'fontFamilies':
|
||||
tokenValue = ['Source Sans Pro', 'Sans serif'];
|
||||
break;
|
||||
case 'fontSizes':
|
||||
tokenValue = 24;
|
||||
break;
|
||||
case 'fontWeights':
|
||||
tokenValue = 'bold';
|
||||
break;
|
||||
case 'letterSpacing':
|
||||
tokenValue = 0.5;
|
||||
break;
|
||||
case 'number':
|
||||
tokenValue = 33;
|
||||
break;
|
||||
case 'opacity':
|
||||
tokenValue = 0.6;
|
||||
break;
|
||||
case 'rotation':
|
||||
tokenValue = 45;
|
||||
break;
|
||||
case 'sizing':
|
||||
tokenValue = 200;
|
||||
break;
|
||||
case 'spacing':
|
||||
tokenValue = 16;
|
||||
break;
|
||||
case 'borderWidth':
|
||||
tokenValue = 3;
|
||||
break;
|
||||
case 'textCase':
|
||||
tokenValue = 'lowercase';
|
||||
break;
|
||||
case 'textDecoration':
|
||||
tokenValue = 'underline';
|
||||
break;
|
||||
case 'typography':
|
||||
tokenValue = {
|
||||
fontFamilies: ['Acme', 'Arial', 'Sans Serif'],
|
||||
fontSizes: '36',
|
||||
letterSpacing: '0.8',
|
||||
textCase: 'uppercase',
|
||||
textDecoration: 'none',
|
||||
fontWeights: '600',
|
||||
lineHeight: '1.5',
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.currentSetId && tokenValue) {
|
||||
this.#sendMessage({
|
||||
type: 'add-token',
|
||||
setId: this.currentSetId,
|
||||
tokenType,
|
||||
tokenName: this.#randomString(),
|
||||
tokenValue,
|
||||
});
|
||||
} else {
|
||||
console.log('Invalid token type');
|
||||
}
|
||||
}
|
||||
|
||||
renameTheme(themeId: string, themeName: string) {
|
||||
const newName = prompt('Rename theme', themeName);
|
||||
if (newName && newName !== '') {
|
||||
this.#sendMessage({ type: 'rename-theme', themeId, newName });
|
||||
}
|
||||
}
|
||||
|
||||
renameSet(setId: string, setName: string) {
|
||||
const newName = prompt('Rename set', setName);
|
||||
if (newName && newName !== '') {
|
||||
this.#sendMessage({ type: 'rename-set', setId, newName });
|
||||
}
|
||||
}
|
||||
|
||||
renameToken(tokenId: string, tokenName: string) {
|
||||
const newName = prompt('Rename token', tokenName);
|
||||
if (this.currentSetId && newName && newName !== '') {
|
||||
this.#sendMessage({
|
||||
type: 'rename-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
newName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
deleteTheme(themeId: string) {
|
||||
this.#sendMessage({ type: 'delete-theme', themeId });
|
||||
}
|
||||
|
||||
deleteSet(setId: string) {
|
||||
this.#sendMessage({ type: 'delete-set', setId });
|
||||
}
|
||||
|
||||
deleteToken(tokenId: string) {
|
||||
if (this.currentSetId) {
|
||||
this.#sendMessage({
|
||||
type: 'delete-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isThemeActive(themeId: string) {
|
||||
for (const theme of this.themes) {
|
||||
if (theme.id === themeId) {
|
||||
return theme.active;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleTheme(themeId: string) {
|
||||
this.#sendMessage({ type: 'toggle-theme', themeId });
|
||||
}
|
||||
|
||||
isSetActive(setId: string) {
|
||||
for (const set of this.sets) {
|
||||
if (set.id === setId) {
|
||||
return set.active;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toggleSet(setId: string) {
|
||||
this.#sendMessage({ type: 'toggle-set', setId });
|
||||
}
|
||||
|
||||
applyToken(tokenId: string) {
|
||||
if (this.currentSetId) {
|
||||
this.#sendMessage({
|
||||
type: 'apply-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
// attributes: ['stroke-color'] // Uncomment to choose attribute to apply
|
||||
}); // (incompatible attributes will have no effect)
|
||||
}
|
||||
}
|
||||
|
||||
#sendMessage(message: PluginUIEvent) {
|
||||
parent.postMessage(message, '*');
|
||||
}
|
||||
|
||||
#setThemes(themes: TokenTheme[]) {
|
||||
this.themes = themes;
|
||||
}
|
||||
|
||||
#setSets(sets: TokenSet[]) {
|
||||
this.sets = sets;
|
||||
}
|
||||
|
||||
#setTokens(tokenGroups: TokensGroup[]) {
|
||||
this.tokenGroups = tokenGroups;
|
||||
}
|
||||
|
||||
#randomString() {
|
||||
// Generate a big random number and convert it to string using base 36
|
||||
// (the number of letters in the ascii alphabet)
|
||||
return Math.floor(Math.random() * Date.now()).toString(36);
|
||||
}
|
||||
}
|
||||
11
plugins/apps/poc-tokens-plugin/src/app/app.config.ts
Normal file
11
plugins/apps/poc-tokens-plugin/src/app/app.config.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
provideZoneChangeDetection({ eventCoalescing: true }),
|
||||
provideRouter(routes),
|
||||
],
|
||||
};
|
||||
3
plugins/apps/poc-tokens-plugin/src/app/app.routes.ts
Normal file
3
plugins/apps/poc-tokens-plugin/src/app/app.routes.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Routes } from '@angular/router';
|
||||
|
||||
export const routes: Routes = [];
|
||||
1
plugins/apps/poc-tokens-plugin/src/assets/CORS
Normal file
1
plugins/apps/poc-tokens-plugin/src/assets/CORS
Normal file
@@ -0,0 +1 @@
|
||||
*
|
||||
2
plugins/apps/poc-tokens-plugin/src/assets/_headers
Normal file
2
plugins/apps/poc-tokens-plugin/src/assets/_headers
Normal file
@@ -0,0 +1,2 @@
|
||||
/*
|
||||
Access-Control-Allow-Origin: *
|
||||
BIN
plugins/apps/poc-tokens-plugin/src/assets/favicon.ico
Normal file
BIN
plugins/apps/poc-tokens-plugin/src/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
plugins/apps/poc-tokens-plugin/src/assets/icon.png
Normal file
BIN
plugins/apps/poc-tokens-plugin/src/assets/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
14
plugins/apps/poc-tokens-plugin/src/assets/manifest.json
Normal file
14
plugins/apps/poc-tokens-plugin/src/assets/manifest.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Design tokens plugin POC",
|
||||
"description": "This is a plugin to try Design Tokens in Penpot API",
|
||||
"code": "/assets/plugin.js",
|
||||
"permissions": [
|
||||
"page:read",
|
||||
"content:read",
|
||||
"file:read",
|
||||
"selection:read",
|
||||
"content:write",
|
||||
"library:read",
|
||||
"library:write"
|
||||
]
|
||||
}
|
||||
13
plugins/apps/poc-tokens-plugin/src/index.html
Normal file
13
plugins/apps/poc-tokens-plugin/src/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Angular example plugin</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
7
plugins/apps/poc-tokens-plugin/src/main.ts
Normal file
7
plugins/apps/poc-tokens-plugin/src/main.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
112
plugins/apps/poc-tokens-plugin/src/model.ts
Normal file
112
plugins/apps/poc-tokens-plugin/src/model.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { TokenProperty } from '@penpot/plugin-types';
|
||||
|
||||
/**
|
||||
* This file contains the typescript interfaces for the plugin events.
|
||||
*/
|
||||
|
||||
// Events sent from the ui to the plugin
|
||||
|
||||
export interface LoadLibraryEvent {
|
||||
type: 'load-library';
|
||||
}
|
||||
|
||||
export interface LoadTokensEvent {
|
||||
type: 'load-tokens';
|
||||
setId: string;
|
||||
}
|
||||
|
||||
export interface AddThemeEvent {
|
||||
type: 'add-theme';
|
||||
themeGroup: string;
|
||||
themeName: string;
|
||||
}
|
||||
|
||||
export interface AddSetEvent {
|
||||
type: 'add-set';
|
||||
setName: string;
|
||||
}
|
||||
|
||||
export interface AddTokenEvent {
|
||||
type: 'add-token';
|
||||
setId: string;
|
||||
tokenType: string;
|
||||
tokenName: string;
|
||||
tokenValue: unknown;
|
||||
}
|
||||
|
||||
export interface RenameThemeEvent {
|
||||
type: 'rename-theme';
|
||||
themeId: string;
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface RenameSetEvent {
|
||||
type: 'rename-set';
|
||||
setId: string;
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface RenameTokenEvent {
|
||||
type: 'rename-token';
|
||||
setId: string;
|
||||
tokenId: string;
|
||||
newName: string;
|
||||
}
|
||||
|
||||
export interface DeleteThemeEvent {
|
||||
type: 'delete-theme';
|
||||
themeId: string;
|
||||
}
|
||||
|
||||
export interface DeleteSetEvent {
|
||||
type: 'delete-set';
|
||||
setId: string;
|
||||
}
|
||||
|
||||
export interface DeleteTokenEvent {
|
||||
type: 'delete-token';
|
||||
setId: string;
|
||||
tokenId: string;
|
||||
}
|
||||
|
||||
export interface ToggleThemeEvent {
|
||||
type: 'toggle-theme';
|
||||
themeId: string;
|
||||
}
|
||||
|
||||
export interface ToggleSetEvent {
|
||||
type: 'toggle-set';
|
||||
setId: string;
|
||||
}
|
||||
|
||||
export interface ApplyTokenEvent {
|
||||
type: 'apply-token';
|
||||
setId: string;
|
||||
tokenId: string;
|
||||
attributes?: TokenProperty[];
|
||||
}
|
||||
|
||||
export type PluginUIEvent =
|
||||
| LoadLibraryEvent
|
||||
| LoadTokensEvent
|
||||
| AddThemeEvent
|
||||
| AddSetEvent
|
||||
| AddTokenEvent
|
||||
| RenameThemeEvent
|
||||
| RenameSetEvent
|
||||
| RenameTokenEvent
|
||||
| DeleteThemeEvent
|
||||
| DeleteSetEvent
|
||||
| DeleteTokenEvent
|
||||
| ToggleThemeEvent
|
||||
| ToggleSetEvent
|
||||
| ApplyTokenEvent;
|
||||
|
||||
// Events sent from the plugin to the ui
|
||||
|
||||
export interface ThemePluginEvent {
|
||||
type: 'theme';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export type PluginMessageEvent = ThemePluginEvent;
|
||||
246
plugins/apps/poc-tokens-plugin/src/plugin.ts
Normal file
246
plugins/apps/poc-tokens-plugin/src/plugin.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import type { PluginMessageEvent, PluginUIEvent } from './model.js';
|
||||
import { TokenType, TokenProperty } from '@penpot/plugin-types';
|
||||
|
||||
penpot.ui.open('Design Tokens test', `?theme=${penpot.theme}`, {
|
||||
width: 1000,
|
||||
height: 800,
|
||||
});
|
||||
|
||||
penpot.on('themechange', (theme) => {
|
||||
sendMessage({ type: 'theme', content: theme });
|
||||
});
|
||||
|
||||
penpot.ui.onMessage<PluginUIEvent>(async (message) => {
|
||||
if (message.type === 'load-library') {
|
||||
loadLibrary();
|
||||
} else if (message.type === 'load-tokens') {
|
||||
loadTokens(message.setId);
|
||||
} else if (message.type === 'add-theme') {
|
||||
addTheme(message.themeGroup, message.themeName);
|
||||
} else if (message.type === 'add-set') {
|
||||
addSet(message.setName);
|
||||
} else if (message.type === 'add-token') {
|
||||
addToken(
|
||||
message.setId,
|
||||
message.tokenType,
|
||||
message.tokenName,
|
||||
message.tokenValue,
|
||||
);
|
||||
} else if (message.type === 'rename-theme') {
|
||||
renameTheme(message.themeId, message.newName);
|
||||
} else if (message.type === 'rename-set') {
|
||||
renameSet(message.setId, message.newName);
|
||||
} else if (message.type === 'rename-token') {
|
||||
renameToken(message.setId, message.tokenId, message.newName);
|
||||
} else if (message.type === 'delete-theme') {
|
||||
deleteTheme(message.themeId);
|
||||
} else if (message.type === 'delete-set') {
|
||||
deleteSet(message.setId);
|
||||
} else if (message.type === 'delete-token') {
|
||||
deleteToken(message.setId, message.tokenId);
|
||||
} else if (message.type === 'toggle-theme') {
|
||||
toggleTheme(message.themeId);
|
||||
} else if (message.type === 'toggle-set') {
|
||||
toggleSet(message.setId);
|
||||
} else if (message.type === 'apply-token') {
|
||||
applyToken(message.setId, message.tokenId, message.attributes);
|
||||
}
|
||||
});
|
||||
|
||||
function sendMessage(message: PluginMessageEvent) {
|
||||
penpot.ui.sendMessage(message);
|
||||
}
|
||||
|
||||
function loadLibrary() {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
|
||||
const themes = tokensCatalog.themes;
|
||||
|
||||
const themesData = themes.map((theme) => {
|
||||
return {
|
||||
id: theme.id,
|
||||
group: theme.group,
|
||||
name: theme.name,
|
||||
active: theme.active,
|
||||
};
|
||||
});
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
source: 'penpot',
|
||||
type: 'set-themes',
|
||||
themesData,
|
||||
});
|
||||
|
||||
const sets = tokensCatalog.sets;
|
||||
|
||||
const setsData = sets.map((set) => {
|
||||
return {
|
||||
id: set.id,
|
||||
name: set.name,
|
||||
active: set.active,
|
||||
};
|
||||
});
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
source: 'penpot',
|
||||
type: 'set-sets',
|
||||
setsData,
|
||||
});
|
||||
}
|
||||
|
||||
function loadTokens(setId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const tokensByType = set?.tokensByType;
|
||||
|
||||
const tokenGroupsData = [];
|
||||
if (tokensByType) {
|
||||
for (const group of tokensByType) {
|
||||
const type = group[0];
|
||||
const tokens = group[1];
|
||||
tokenGroupsData.push([
|
||||
type,
|
||||
tokens.map((token) => {
|
||||
return {
|
||||
id: token.id,
|
||||
name: token.name,
|
||||
description: token.description,
|
||||
};
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
penpot.ui.sendMessage({
|
||||
source: 'penpot',
|
||||
type: 'set-tokens',
|
||||
tokenGroupsData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addTheme(themeGroup: string, themeName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.addTheme(themeGroup, themeName);
|
||||
if (theme) {
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function addSet(setName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.addSet(setName);
|
||||
if (set) {
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function addToken(
|
||||
setId: string,
|
||||
tokenType: string,
|
||||
tokenName: string,
|
||||
tokenValue: unknown,
|
||||
) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.addToken(tokenType as TokenType, tokenName, tokenValue);
|
||||
if (token) {
|
||||
loadTokens(setId);
|
||||
}
|
||||
}
|
||||
|
||||
function renameTheme(themeId: string, newName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.getThemeById(themeId);
|
||||
if (theme) {
|
||||
theme.name = newName;
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function renameSet(setId: string, newName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
if (set) {
|
||||
set.name = newName;
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function renameToken(setId: string, tokenId: string, newName: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.getTokenById(tokenId);
|
||||
if (token) {
|
||||
token.name = newName;
|
||||
loadTokens(setId);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteTheme(themeId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.getThemeById(themeId);
|
||||
if (theme) {
|
||||
theme.remove();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteSet(setId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
if (set) {
|
||||
set.remove();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteToken(setId: string, tokenId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.getTokenById(tokenId);
|
||||
if (token) {
|
||||
token.remove();
|
||||
loadTokens(setId);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTheme(themeId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const theme = tokensCatalog?.getThemeById(themeId);
|
||||
if (theme) {
|
||||
theme.toggleActive();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSet(setId: string) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
if (set) {
|
||||
set.toggleActive();
|
||||
loadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
function applyToken(
|
||||
setId: string,
|
||||
tokenId: string,
|
||||
attributes: TokenProperty[] | undefined,
|
||||
) {
|
||||
const tokensCatalog = penpot.library.local.tokens;
|
||||
const set = tokensCatalog?.getSetById(setId);
|
||||
const token = set?.getTokenById(tokenId);
|
||||
|
||||
if (token) {
|
||||
token.applyToSelected(attributes);
|
||||
}
|
||||
|
||||
// Alternatve way
|
||||
//
|
||||
// const selection = penpot.selection;
|
||||
// if (token && selection) {
|
||||
// for (const shape of selection) {
|
||||
// shape.applyToken(token, attributes);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
23
plugins/apps/poc-tokens-plugin/src/styles.css
Normal file
23
plugins/apps/poc-tokens-plugin/src/styles.css
Normal file
@@ -0,0 +1,23 @@
|
||||
/* @import "@penpot/plugin-styles/styles.css"; */
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
line-height: 1.5;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-block-start: var(--spacing-12);
|
||||
}
|
||||
|
||||
.title-l {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.headline-l {
|
||||
margin-block-start: var(--spacing-8);
|
||||
}
|
||||
10
plugins/apps/poc-tokens-plugin/tsconfig.app.json
Normal file
10
plugins/apps/poc-tokens-plugin/tsconfig.app.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||
}
|
||||
7
plugins/apps/poc-tokens-plugin/tsconfig.editor.json
Normal file
7
plugins/apps/poc-tokens-plugin/tsconfig.editor.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
||||
33
plugins/apps/poc-tokens-plugin/tsconfig.json
Normal file
33
plugins/apps/poc-tokens-plugin/tsconfig.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"useDefineForClassFields": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.editor.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.plugin.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
||||
8
plugins/apps/poc-tokens-plugin/tsconfig.plugin.json
Normal file
8
plugins/apps/poc-tokens-plugin/tsconfig.plugin.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/plugin.ts"],
|
||||
"include": ["../../libs/plugin-types/index.d.ts"]
|
||||
}
|
||||
1078
plugins/libs/plugin-types/index.d.ts
vendored
1078
plugins/libs/plugin-types/index.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
||||
"start:plugin:table": "nx run table-plugin:init",
|
||||
"start:plugin:renamelayers": "nx run rename-layers-plugin:init",
|
||||
"start:plugin:colors-to-tokens": "nx run colors-to-tokens-plugin:init",
|
||||
"start:plugin:poc-tokens": "nx run poc-tokens-plugin:init",
|
||||
"build": "nx build plugins-runtime --emptyOutDir=true",
|
||||
"build:plugins": "nx run-many -t build --parallel -p tag:type:plugin --exclude=poc-state-plugin",
|
||||
"build:styles-example": "nx run example-styles:build",
|
||||
|
||||
Reference in New Issue
Block a user