Merge remote-tracking branch 'origin/staging' into develop
@ -139,8 +139,8 @@
|
||||
[:map {:title "OpenOverlayInteraction"}
|
||||
[:action-type [:= :open-overlay]]
|
||||
[:event-type [::sm/one-of event-types]]
|
||||
[:overlay-position ::gpt/point]
|
||||
[:overlay-pos-type [::sm/one-of overlay-positioning-types]]
|
||||
[:overlay-position {:optional true} ::gpt/point]
|
||||
[:overlay-pos-type {:optional true} [::sm/one-of overlay-positioning-types]]
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:close-click-outside {:optional true} :boolean]
|
||||
[:background-overlay {:optional true} :boolean]
|
||||
@ -151,8 +151,8 @@
|
||||
[:map {:title "ToggleOverlayInteraction"}
|
||||
[:action-type [:= :toggle-overlay]]
|
||||
[:event-type [::sm/one-of event-types]]
|
||||
[:overlay-position ::gpt/point]
|
||||
[:overlay-pos-type [::sm/one-of overlay-positioning-types]]
|
||||
[:overlay-position {:optional true} ::gpt/point]
|
||||
[:overlay-pos-type {:optional true} [::sm/one-of overlay-positioning-types]]
|
||||
[:destination {:optional true} [:maybe ::sm/uuid]]
|
||||
[:close-click-outside {:optional true} :boolean]
|
||||
[:background-overlay {:optional true} :boolean]
|
||||
|
||||
BIN
docs/img/styling/background-blur.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
@ -27,16 +27,16 @@
|
||||
"@11ty/eleventy": "^3.1.6",
|
||||
"@11ty/eleventy-navigation": "^1.0.5",
|
||||
"@11ty/eleventy-plugin-rss": "^3.0.0",
|
||||
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
|
||||
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2",
|
||||
"@tigersway/eleventy-plugin-ancestry": "^1.0.3",
|
||||
"@types/markdown-it": "14.1.2",
|
||||
"elasticlunr": "^0.9.5",
|
||||
"eleventy-plugin-metagen": "^1.8.3",
|
||||
"eleventy-plugin-metagen": "^1.8.4",
|
||||
"eleventy-plugin-nesting-toc": "^1.3.0",
|
||||
"eleventy-plugin-youtube-embed": "^1.10.2",
|
||||
"luxon": "^3.4.4",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-anchor": "^9.0.1",
|
||||
"eleventy-plugin-youtube-embed": "^1.13.2",
|
||||
"luxon": "^3.7.2",
|
||||
"markdown-it": "^14.2.0",
|
||||
"markdown-it-anchor": "^9.2.0",
|
||||
"markdown-it-plantuml": "^1.4.1"
|
||||
},
|
||||
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
|
||||
|
||||
28
docs/pnpm-lock.yaml
generated
@ -18,7 +18,7 @@ importers:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
'@11ty/eleventy-plugin-syntaxhighlight':
|
||||
specifier: ^5.0.0
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
'@tigersway/eleventy-plugin-ancestry':
|
||||
specifier: ^1.0.3
|
||||
@ -30,22 +30,22 @@ importers:
|
||||
specifier: ^0.9.5
|
||||
version: 0.9.5
|
||||
eleventy-plugin-metagen:
|
||||
specifier: ^1.8.3
|
||||
specifier: ^1.8.4
|
||||
version: 1.8.4
|
||||
eleventy-plugin-nesting-toc:
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0
|
||||
eleventy-plugin-youtube-embed:
|
||||
specifier: ^1.10.2
|
||||
specifier: ^1.13.2
|
||||
version: 1.13.2
|
||||
luxon:
|
||||
specifier: ^3.4.4
|
||||
specifier: ^3.7.2
|
||||
version: 3.7.2
|
||||
markdown-it:
|
||||
specifier: ^14.1.0
|
||||
specifier: ^14.2.0
|
||||
version: 14.2.0
|
||||
markdown-it-anchor:
|
||||
specifier: ^9.0.1
|
||||
specifier: ^9.2.0
|
||||
version: 9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.2.0)
|
||||
markdown-it-plantuml:
|
||||
specifier: ^1.4.1
|
||||
@ -436,8 +436,8 @@ packages:
|
||||
resolution: {integrity: sha512-gXkz5+KN7HrG0Q5UGqSMO2qB9AsbEeyLP54kF1YrMsIxmu+g4BdB7rflReZTSTZGpfj8wywu6pfPBCylPIzGQA==}
|
||||
engines: {node: '>=6.0'}
|
||||
|
||||
js-yaml@3.14.2:
|
||||
resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
|
||||
js-yaml@3.15.0:
|
||||
resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==}
|
||||
hasBin: true
|
||||
|
||||
js-yaml@4.2.0:
|
||||
@ -692,8 +692,8 @@ packages:
|
||||
uc.micro@2.1.0:
|
||||
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
|
||||
|
||||
undici@7.27.2:
|
||||
resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==}
|
||||
undici@7.28.0:
|
||||
resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unpipe@1.0.0:
|
||||
@ -943,7 +943,7 @@ snapshots:
|
||||
parse5: 7.3.0
|
||||
parse5-htmlparser2-tree-adapter: 7.1.0
|
||||
parse5-parser-stream: 7.1.2
|
||||
undici: 7.27.2
|
||||
undici: 7.28.0
|
||||
whatwg-mimetype: 4.0.0
|
||||
|
||||
chokidar@3.6.0:
|
||||
@ -1126,7 +1126,7 @@ snapshots:
|
||||
|
||||
gray-matter@4.0.3:
|
||||
dependencies:
|
||||
js-yaml: 3.14.2
|
||||
js-yaml: 3.15.0
|
||||
kind-of: 6.0.3
|
||||
section-matter: 1.0.0
|
||||
strip-bom-string: 1.0.0
|
||||
@ -1188,7 +1188,7 @@ snapshots:
|
||||
|
||||
iso-639-1@3.1.5: {}
|
||||
|
||||
js-yaml@3.14.2:
|
||||
js-yaml@3.15.0:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
@ -1410,7 +1410,7 @@ snapshots:
|
||||
|
||||
uc.micro@2.1.0: {}
|
||||
|
||||
undici@7.27.2: {}
|
||||
undici@7.28.0: {}
|
||||
|
||||
unpipe@1.0.0: {}
|
||||
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
minimumReleaseAgeExclude:
|
||||
- undici@7.28.0
|
||||
- js-yaml@3.15.0
|
||||
@ -31,6 +31,7 @@ desc: Learn how to create, manage and apply Penpot Design Tokens using W3C DTCG
|
||||
<li><strong>Description:</strong> You can also choose to add a description to your token.</li>
|
||||
</ul>
|
||||
<p>Once you have named the token and assigned it a value, click <strong>Save</strong> to store the token and start referencing it.</p>
|
||||
<p>Token value fields suggest existing tokens as you type. Pick a suggestion to insert a reference, or keep typing a custom value.</p>
|
||||
|
||||
<h2 id="design-tokens-aliases">Referencing tokens into values (aliases)</h2>
|
||||
<p>When assigning a value to a token, you can reference existing tokens - these are called aliases at the <a href="https://www.designtokens.org/glossary/" target="_blank">DTCG Glossary</a>.</p>
|
||||
@ -111,7 +112,7 @@ desc: Learn how to create, manage and apply Penpot Design Tokens using W3C DTCG
|
||||
<p>This ensures the spacing is at least 8, even if the base token is smaller.</p>
|
||||
|
||||
<h2 id="design-tokens-edit">Editing a token</h2>
|
||||
<p>Tokens can be edited by right-clicking the token and selecting <strong>Edit token</strong>. This will allow you to change the tokens name, value and description. Once the changes are made, click <strong>Save</strong>.</p>
|
||||
<p>Tokens can be edited by right-clicking the token and selecting <strong>Edit token</strong>. This will allow you to change the tokens name, value and description. Value fields offer the same token suggestions as when creating a token. Once the changes are made, click <strong>Save</strong>.</p>
|
||||
<figure>
|
||||
<img src="/img/design-tokens/05-tokens-edit.webp" alt="Tokens edit" />
|
||||
</figure>
|
||||
@ -141,12 +142,15 @@ desc: Learn how to create, manage and apply Penpot Design Tokens using W3C DTCG
|
||||
<li><strong>Size and position:</strong> width, height, x, y</li>
|
||||
<li><strong>Rotation and border radius</strong></li>
|
||||
<li><strong>Layout spacing:</strong> gaps, paddings, margins, min and max widths and heights</li>
|
||||
<li><strong>Typography:</strong> letter spacing and line height</li>
|
||||
<li><strong>Typography:</strong> letter spacing, line height, and composite typography tokens on text layers</li>
|
||||
<li><strong>Stroke width</strong></li>
|
||||
<li><strong>Shadows:</strong> x, y, blur, spread</li>
|
||||
<li><strong>Blur</strong> amount</li>
|
||||
</ul>
|
||||
<p>Click the token control on a field to open a list of applicable tokens from your active sets and themes. Select a token to apply it; a token pill shows the bound token. Use the pill menu to detach the token when you need a custom value again.</p>
|
||||
<p>On text layers, the Typography section shows a composite typography token row when one is applied. Open the token list from the section header to pick or switch tokens without going to the Tokens panel. Hover a token name to see its resolved values (font family, size, weight, and the rest).</p>
|
||||
<p>When you multi-select text layers that share typography tokens, the Typography section shows the applied token. If the selection mixes different tokens, detach them from there or apply a new one to the whole selection.</p>
|
||||
<p>Typography options in the dropdown list show resolved values on hover, so you can tell styles apart before applying them.</p>
|
||||
<p class="advice">When a numeric field has a token applied, drag-to-change is disabled for that field. Edit the token or detach it first.</p>
|
||||
<figure>
|
||||
<img src="/img/design-tokens/41-design-tab-token-dropdown.webp" alt="Token list on a numeric field in the Design sidebar">
|
||||
@ -528,6 +532,7 @@ ExtraBold Italic
|
||||
|
||||
<h4 id="apply-typography-token">Apply a Typography token</h4>
|
||||
<p>A <strong>Typography composite token</strong> can be applied to a full text layer to set all typography properties at once. This lets you manage complete text styles using a single token instead of combining multiple individual ones.</p>
|
||||
<p>Apply it from the Tokens panel, or from the Typography section in the Design sidebar when a text layer is selected.</p>
|
||||
<p>When applying a Typography composite token to a layer, any previously applied <em>Typography composite token</em> or <em>style</em> will be detached. The same happens in reverse. Only one of them can be active at a time.</p>
|
||||
|
||||
<h3 id="design-tokens-shadow">Shadow</h3>
|
||||
|
||||
@ -57,7 +57,7 @@ desc: Style your designs with Penpot's options! Learn about color fills, gradien
|
||||
<li><strong>Color type</strong> - Solid, gradient, or image.</li>
|
||||
<li><strong>Sliders</strong> - Easily manage settings like brightness, saturation or opacity.</li>
|
||||
<li><strong>Values</strong> - Set precise color values. Available formats depend on the selected color model.</li>
|
||||
<li><strong>Libraries</strong> - Switch between recent colors and libraries.</li>
|
||||
<li><strong>Libraries</strong> - Switch between recent colors and libraries. Color tokens in a library can be shown as a grid or a list. Use the list view when you need to scan names or work with long token lists.</li>
|
||||
<li><strong>Color palette</strong> - A quick launcher of the palette with the selected library.</li>
|
||||
</ol>
|
||||
|
||||
@ -116,6 +116,7 @@ desc: Style your designs with Penpot's options! Learn about color fills, gradien
|
||||
<li><strong>Style</strong> - solid, dotted, dashed, mixed</li>
|
||||
<li><strong>Visibility</strong> - show or hide the stroke without removing it</li>
|
||||
</ul>
|
||||
<p>When the style is set to <strong>dashed</strong>, two extra fields appear to control the <strong>dash</strong> and <strong>gap</strong> length in pixels.</p>
|
||||
<p>You can add as many strokes as you want to the same layer.</p>
|
||||
<figure>
|
||||
<img alt="Multiple strokes" src="/img/styling/stroke-multiple.webp"/>
|
||||
|
||||
@ -461,8 +461,18 @@ You can choose to edit individual nodes or create new ones. Press <kbd>Esc</kbd>
|
||||
</ul>
|
||||
|
||||
<h3 id="blur">Blur</h3>
|
||||
<p>You can set a blur for each and every layer at Penpot.</p>
|
||||
<p><strong></strong>Applying a lot and/or big values for blurs can affect Penpot’s performance as it requires a lot from the browser.</p>
|
||||
<p>You can add blur effects to layers from the Design sidebar. Penpot supports two types:</p>
|
||||
<ul>
|
||||
<li><strong>Layer blur</strong> blurs the layer content itself.</li>
|
||||
<li><strong>Background blur</strong> blurs whatever sits behind the layer. Use it for frosted-glass or depth effects. Background blur requires the <a href="/user-guide/first-steps/troubleshooting-webgl/">WebGL renderer</a> to be enabled.</li>
|
||||
</ul>
|
||||
<p class="advice">Background blur is not included in exported files yet (PNG, SVG, PDF, and others). The exporter still uses the legacy SVG renderer, so the effect only shows in the workspace and View mode. Export support is a current priority and will land in an upcoming release.</p>
|
||||
<figure>
|
||||
<img src="/img/styling/background-blur.webp" alt="Colorful shapes behind a frosted panel with background blur applied" />
|
||||
<figcaption>Background blur affects the layers behind the shape, not the shape itself.</figcaption>
|
||||
</figure>
|
||||
<p>Click <strong>+</strong> in the Blur section to add an effect. You can apply both types on the same layer. Use the type selector to switch between them, the eye icon to show or hide an effect without removing it, and the value field to set the blur amount.</p>
|
||||
<p class="advice">Applying many or large blur values can affect Penpot’s performance, as it requires a lot from the browser.</p>
|
||||
<figure>
|
||||
<video title="Apply blur to a layer" muted="" playsinline="" controls="" width="100%" poster="/img/styling/blur.webp" height="auto">
|
||||
<source src="/img/styling/blur.mp4" type="video/mp4">
|
||||
|
||||
@ -42,6 +42,10 @@ desc: This Penpot user guide explains how to prototype interactions, connect boa
|
||||
<strong>10)</strong> Flow indicator and launcher
|
||||
</p>
|
||||
|
||||
<h2 id="interaction-settings">Interaction settings</h2>
|
||||
<p>When a layer with interactions is selected, the Prototype sidebar lists every connection attached to it. From there you can change the trigger, action, destination, animation, and other options.</p>
|
||||
<p>Destination dropdowns include a search field. Type to filter boards on the current page when your file has many screens.</p>
|
||||
|
||||
<h2 id="interaction-triggers">Interaction triggers</h2>
|
||||
<figure>
|
||||
<img src="/img/prototype/prototype-trigger.webp" alt="Prototype trigger options">
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
"@storybook/addon-vitest": "10.4.6",
|
||||
"@storybook/react-vite": "10.4.6",
|
||||
"@tokens-studio/sd-transforms": "2.0.3",
|
||||
"@types/node": "^26.0.1",
|
||||
"@types/node": "^26.1.0",
|
||||
"@vitest/browser": "4.1.9",
|
||||
"@vitest/browser-playwright": "^4.1.9",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
@ -80,7 +80,7 @@
|
||||
"getopts": "^2.3.0",
|
||||
"gettext-parser": "^9.0.2",
|
||||
"highlight.js": "^11.10.0",
|
||||
"js-beautify": "^1.15.4",
|
||||
"js-beautify": "^2.0.3",
|
||||
"jsdom": "^29.0.2",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
@ -95,7 +95,7 @@
|
||||
"playwright": "1.61.1",
|
||||
"postcss": "^8.5.16",
|
||||
"postcss-clean": "^1.2.2",
|
||||
"postcss-modules": "^6.0.1",
|
||||
"postcss-modules": "^9.0.0",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"prettier": "3.9.4",
|
||||
"pretty-time": "^1.1.0",
|
||||
@ -122,11 +122,11 @@
|
||||
"tdigest": "^0.1.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"typescript": "^6.0.2",
|
||||
"vite": "^8.1.0",
|
||||
"vite": "^8.1.2",
|
||||
"vitest": "^4.1.9",
|
||||
"wait-on": "^9.0.4",
|
||||
"watcher": "^2.3.1",
|
||||
"workerpool": "^10.0.1",
|
||||
"workerpool": "^10.0.3",
|
||||
"xregexp": "^5.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
382
frontend/playwright/data/workspace/get-file-token-tooltip.json
Normal file
@ -0,0 +1,382 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"design-tokens/v1",
|
||||
"layout/grid",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "Tooltip check",
|
||||
"~:revn": 36,
|
||||
"~:modified-at": "~m1737542758402",
|
||||
"~:vern": 0,
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
|
||||
"~:is-shared": false,
|
||||
"~:version": 60,
|
||||
"~:project-id": "~u0df61468-6cbf-8067-8005-6b453ce996d0",
|
||||
"~:created-at": "~m1737536563847",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u4530574a-7a0a-807b-8008-0107b2c4628e"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u4530574a-7a0a-807b-8008-0107b2c4628e": {
|
||||
"~:id": "~u4530574a-7a0a-807b-8008-0107b2c4628e",
|
||||
"~:name": "Page 1",
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u4530574a-7a0a-807b-8008-0107b2c4628e\",\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^I\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~ua7df6ab8-bee5-802f-8008-010d9ff3da26\",\"~uf2256046-b127-808e-8008-411c4cba63d8\",\"~u1de8c150-538b-80b2-8008-40356208fc39\",\"~uf2256046-b127-808e-8008-411c4cba63d9\",\"~u1de8c150-538b-80b2-8008-4036773cc8d0\",\"~u886f2846-a2c5-808d-8008-4115e86119e9\",\"~u1de8c150-538b-80b2-8008-403b5150a59b\",\"~u886f2846-a2c5-808d-8008-4115e86119ea\",\"~u20f9ccb0-68e2-80d9-8008-413fa842a6b5\",\"~u20f9ccb0-68e2-80d9-8008-413fb1f2ef51\"]]]",
|
||||
"~u1de8c150-538b-80b2-8008-403b5150a59b": "[\"~#shape\",[\"^ \",\"~:y\",419,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Deleted token rect\",\"~:width\",162,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",-147,\"~:y\",419]],[\"^<\",[\"^ \",\"~:x\",15,\"~:y\",419]],[\"^<\",[\"^ \",\"~:x\",15,\"~:y\",565]],[\"^<\",[\"^ \",\"~:x\",-147,\"~:y\",565]]],\"~:r2\",20,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",20,\"~:r1\",20,\"~:id\",\"~u1de8c150-538b-80b2-8008-403b5150a59b\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"blue\",\"^A\",\"deleted\",\"^=\",\"deleted\",\"^@\",\"deleted\",\"~:r4\",\"deleted\"],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",-147,\"~:proportion\",1,\"^F\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",-147,\"~:y\",419,\"^8\",162,\"~:height\",146,\"~:x1\",-147,\"~:y1\",419,\"~:x2\",15,\"~:y2\",565]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#0a20bf\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^L\",146,\"~:flip-y\",null]]",
|
||||
"~u20f9ccb0-68e2-80d9-8008-413fa842a6b5": "[\"~#shape\",[\"^ \",\"~:y\",369,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle with token\",\"~:width\",274.99999999999994,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",-536,\"~:y\",369]],[\"^<\",[\"^ \",\"~:x\",-261.00000000000006,\"~:y\",369]],[\"^<\",[\"^ \",\"~:x\",-261.00000000000006,\"~:y\",565]],[\"^<\",[\"^ \",\"~:x\",-536,\"~:y\",565]]],\"~:r2\",50,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",50,\"~:r1\",50,\"~:id\",\"~u20f9ccb0-68e2-80d9-8008-413fa842a6b5\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"out-ref\",\"^A\",\"base-50\",\"^=\",\"base-50\",\"^@\",\"base-50\",\"~:r4\",\"base-50\"],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",-536,\"~:proportion\",1,\"^F\",50,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",-536,\"~:y\",369,\"^8\",274.99999999999994,\"~:height\",196,\"~:x1\",-536,\"~:y1\",369,\"~:x2\",-261.00000000000006,\"~:y2\",565]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#da1fea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^L\",196,\"~:flip-y\",null]]",
|
||||
"~u1de8c150-538b-80b2-8008-4036773cc8d0": "[\"~#shape\",[\"^ \",\"~:y\",588,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^7\",\"paragraph-set\",\"^8\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:font-style\",\"normal\",\"^8\",[[\"^ \",\"^9\",\"1.2\",\"^:\",\"normal\",\"~:text-transform\",\"none\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-alegreya-sans-sc\",\"~:font-size\",\"23\",\"~:font-weight\",\"400\",\"~:text-direction\",\"ltr\",\"~:font-variant-id\",\"regular\",\"~:text-decoration\",\"none\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-opacity\",1]],\"~:font-family\",\"Alegreya Sans SC\",\"~:text\",\"Text with deleted token\"]],\"^;\",\"none\",\"^<\",\"left\",\"^=\",\"gfont-alegreya-sans-sc\",\"~:key\",\"fu3vh\",\"^>\",\"23\",\"^?\",\"400\",\"^@\",\"ltr\",\"^7\",\"paragraph\",\"^A\",\"regular\",\"^B\",\"none\",\"^C\",\"0\",\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"^G\",\"Alegreya Sans SC\"]]]]],\"~:hide-in-viewer\",false,\"~:name\",\"Text with deleted token\",\"~:width\",198,\"^7\",\"^H\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",-161,\"~:y\",588]],[\"^N\",[\"^ \",\"~:x\",37,\"~:y\",588]],[\"^N\",[\"^ \",\"~:x\",37,\"~:y\",641]],[\"^N\",[\"^ \",\"~:x\",-161,\"~:y\",641]]],\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u1de8c150-538b-80b2-8008-4036773cc8d0\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:typography\",\"deleted-typo\"],\"~:position-data\",[[\"~#rect\",[\"^ \",\"~:y\",613,\"^:\",\"normal\",\"^;\",\"none\",\"^>\",\"23px\",\"^?\",\"400\",\"~:y1\",2,\"^L\",176.203125,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",-161,\"~:x1\",0,\"~:y2\",25,\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"~:x2\",176.203125,\"~:direction\",\"ltr\",\"^G\",\"\\\"Alegreya Sans SC\\\"\",\"~:height\",23,\"^H\",\"Text with deleted \"]],[\"^U\",[\"^ \",\"~:y\",640.59375,\"^:\",\"normal\",\"^;\",\"none\",\"^>\",\"23px\",\"^?\",\"400\",\"^V\",29.59375,\"^L\",56.9375,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",-161,\"^W\",0,\"^X\",52.59375,\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"^Y\",56.9375,\"^Z\",\"ltr\",\"^G\",\"\\\"Alegreya Sans SC\\\"\",\"^[\",23,\"^H\",\"token\"]]],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:x\",-161,\"~:selrect\",[\"^U\",[\"^ \",\"~:x\",-161,\"~:y\",588,\"^L\",198,\"^[\",53,\"^W\",-161,\"^V\",588,\"^Y\",37,\"^X\",641]],\"~:flip-x\",null,\"^[\",53,\"~:flip-y\",null]]",
|
||||
"~u20f9ccb0-68e2-80d9-8008-413fb1f2ef51": "[\"~#shape\",[\"^ \",\"~:y\",609,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^7\",\"paragraph-set\",\"^8\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:font-style\",\"normal\",\"^8\",[[\"^ \",\"^9\",\"1.2\",\"^:\",\"normal\",\"~:text-transform\",\"none\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-arizonia\",\"~:font-size\",\"23\",\"~:font-weight\",\"400\",\"~:text-direction\",\"ltr\",\"~:font-variant-id\",\"regular\",\"~:text-decoration\",\"none\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#da1fea\",\"~:fill-opacity\",1]],\"~:font-family\",\"Arizonia\",\"~:text\",\"Text with token\"]],\"^;\",\"none\",\"^<\",\"left\",\"^=\",\"gfont-arizonia\",\"~:key\",\"fu3vh\",\"^>\",\"23\",\"^?\",\"400\",\"^@\",\"ltr\",\"^7\",\"paragraph\",\"^A\",\"regular\",\"^B\",\"none\",\"^C\",\"0\",\"^D\",[[\"^ \",\"^E\",\"#da1fea\",\"^F\",1]],\"^G\",\"Arizonia\"]]]]],\"~:hide-in-viewer\",false,\"~:name\",\"Text with token\",\"~:width\",198,\"^7\",\"^H\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",-516,\"~:y\",609]],[\"^N\",[\"^ \",\"~:x\",-318,\"~:y\",609]],[\"^N\",[\"^ \",\"~:x\",-318,\"~:y\",662]],[\"^N\",[\"^ \",\"~:x\",-516,\"~:y\",662]]],\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u20f9ccb0-68e2-80d9-8008-413fb1f2ef51\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:typography\",\"out-typo\",\"~:fill\",\"out-ref\"],\"~:position-data\",[[\"~#rect\",[\"^ \",\"~:y\",637,\"^:\",\"normal\",\"^;\",\"none\",\"^>\",\"23px\",\"^?\",\"400\",\"~:y1\",-1,\"^L\",117.453125,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",-516,\"~:x1\",0,\"~:y2\",28,\"^D\",[[\"^ \",\"^E\",\"#da1fea\",\"^F\",1]],\"~:x2\",117.453125,\"~:direction\",\"ltr\",\"^G\",\"Arizonia\",\"~:height\",29,\"^H\",\"Text with token\"]]],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",-516,\"~:selrect\",[\"^V\",[\"^ \",\"~:x\",-516,\"~:y\",609,\"^L\",198,\"^10\",53,\"^X\",-516,\"^W\",609,\"^Z\",-318,\"^Y\",662]],\"~:flip-x\",null,\"^10\",53,\"~:flip-y\",null]]",
|
||||
"~uf2256046-b127-808e-8008-411c4cba63d8": "[\"~#shape\",[\"^ \",\"~:y\",267,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle with not active reference\",\"~:width\",287,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",390,\"~:y\",267]],[\"^<\",[\"^ \",\"~:x\",677,\"~:y\",267]],[\"^<\",[\"^ \",\"~:x\",677,\"~:y\",579]],[\"^<\",[\"^ \",\"~:x\",390,\"~:y\",579]]],\"~:r2\",50,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u4530574a-7a0a-807b-8008-0107b2c4628e\",\"~:r3\",50,\"~:r1\",50,\"~:id\",\"~uf2256046-b127-808e-8008-411c4cba63d8\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"in-color\",\"^=\",\"in-br\",\"^A\",\"in-br\",\"^B\",\"in-br\",\"~:r4\",\"in-br\"],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",390,\"~:proportion\",1,\"^G\",50,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",390,\"~:y\",267,\"^8\",287,\"~:height\",312,\"~:x1\",390,\"~:y1\",267,\"~:x2\",677,\"~:y2\",579]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#da1fea\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^M\",312,\"~:flip-y\",null]]",
|
||||
"~uf2256046-b127-808e-8008-411c4cba63d9": "[\"~#shape\",[\"^ \",\"~:y\",602,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^7\",\"paragraph-set\",\"^8\",[[\"^ \",\"~:line-height\",\"1.2\",\"^8\",[[\"^ \",\"^9\",\"1.2\",\"~:text-transform\",\"none\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-arizonia\",\"~:font-size\",\"14\",\"~:font-weight\",\"200\",\"~:text-direction\",\"ltr\",\"~:font-variant-id\",\"regular\",\"~:weight\",\"200\",\"~:text-decoration\",\"none\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#da1fea\",\"~:fill-opacity\",1]],\"~:font-family\",\"Arizonia\",\"~:text\",\"Text with not active reference\"]],\"^:\",\"none\",\"^;\",\"left\",\"^<\",\"gfont-arizonia\",\"~:key\",\"d22fo\",\"^=\",\"14\",\"^>\",\"200\",\"^?\",\"ltr\",\"^7\",\"paragraph\",\"^@\",\"regular\",\"^A\",\"200\",\"^B\",\"none\",\"^C\",\"0\",\"^D\",[[\"^ \",\"^E\",\"#da1fea\",\"^F\",1]],\"^G\",\"Arizonia\"]]]]],\"~:hide-in-viewer\",false,\"~:name\",\"Text with not active reference\",\"~:width\",139,\"^7\",\"^H\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",479,\"~:y\",602]],[\"^N\",[\"^ \",\"~:x\",618,\"~:y\",602]],[\"^N\",[\"^ \",\"~:x\",618,\"~:y\",630]],[\"^N\",[\"^ \",\"~:x\",479,\"~:y\",630]]],\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~uf2256046-b127-808e-8008-411c4cba63d9\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"in-color\",\"~:typography\",\"in-typo\"],\"~:position-data\",[[\"~#rect\",[\"^ \",\"~:y\",619,\"~:font-style\",\"normal\",\"^:\",\"none\",\"^=\",\"14px\",\"^>\",\"400\",\"~:y1\",-1,\"^L\",97.46875,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",479,\"~:x1\",0,\"~:y2\",17,\"^D\",[[\"^ \",\"^E\",\"#da1fea\",\"^F\",1]],\"~:x2\",97.46875,\"~:direction\",\"ltr\",\"^G\",\"Arizonia\",\"~:height\",18,\"^H\",\"Text with not active \"]],[\"^V\",[\"^ \",\"~:y\",635.796875,\"^W\",\"normal\",\"^:\",\"none\",\"^=\",\"14px\",\"^>\",\"400\",\"^X\",15.796875,\"^L\",42.8125,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",479,\"^Y\",0,\"^Z\",33.796875,\"^D\",[[\"^ \",\"^E\",\"#da1fea\",\"^F\",1]],\"^[\",42.8125,\"^10\",\"ltr\",\"^G\",\"Arizonia\",\"^11\",18,\"^H\",\"reference\"]]],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:x\",479,\"~:selrect\",[\"^V\",[\"^ \",\"~:x\",479,\"~:y\",602,\"^L\",139,\"^11\",28,\"^Y\",479,\"^X\",602,\"^[\",618,\"^Z\",630]],\"~:flip-x\",null,\"^11\",28,\"~:flip-y\",null]]",
|
||||
"~u1de8c150-538b-80b2-8008-40356208fc39": "[\"~#shape\",[\"^ \",\"~:y\",609,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^7\",\"paragraph-set\",\"^8\",[[\"^ \",\"~:line-height\",\"1.2\",\"^8\",[[\"^ \",\"^9\",\"1.2\",\"~:text-transform\",\"none\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-ar-one-sans\",\"~:font-size\",\"14\",\"~:font-weight\",\"200\",\"~:text-direction\",\"ltr\",\"~:font-variant-id\",\"regular\",\"~:weight\",\"200\",\"~:text-decoration\",\"none\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ef0c0c\",\"~:fill-opacity\",1]],\"~:font-family\",\"AR One Sans\",\"~:text\",\"Text with not active token\"]],\"^:\",\"none\",\"^;\",\"left\",\"^<\",\"gfont-ar-one-sans\",\"~:key\",\"d22fo\",\"^=\",\"14\",\"^>\",\"200\",\"^?\",\"ltr\",\"^7\",\"paragraph\",\"^@\",\"regular\",\"^A\",\"200\",\"^B\",\"none\",\"^C\",\"0\",\"^D\",[[\"^ \",\"^E\",\"#ef0c0c\",\"^F\",1]],\"^G\",\"AR One Sans\"]]]]],\"~:hide-in-viewer\",false,\"~:name\",\"Text with not active token\",\"~:width\",139,\"^7\",\"^H\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",812,\"~:y\",609]],[\"^N\",[\"^ \",\"~:x\",951,\"~:y\",609]],[\"^N\",[\"^ \",\"~:x\",951,\"~:y\",637]],[\"^N\",[\"^ \",\"~:x\",812,\"~:y\",637]]],\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u1de8c150-538b-80b2-8008-40356208fc39\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"red\",\"~:typography\",\"typo-2\"],\"~:position-data\",[[\"~#rect\",[\"^ \",\"~:y\",626,\"~:font-style\",\"normal\",\"^:\",\"none\",\"^=\",\"14px\",\"^>\",\"400\",\"~:y1\",-2,\"^L\",134.625,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",812,\"~:x1\",0,\"~:y2\",17,\"^D\",[[\"^ \",\"^E\",\"#ef0c0c\",\"^F\",1]],\"~:x2\",134.625,\"~:direction\",\"ltr\",\"^G\",\"\\\"AR One Sans\\\"\",\"~:height\",19,\"^H\",\"Text with not active \"]],[\"^V\",[\"^ \",\"~:y\",642.796875,\"^W\",\"normal\",\"^:\",\"none\",\"^=\",\"14px\",\"^>\",\"400\",\"^X\",14.796875,\"^L\",37.40625,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",812,\"^Y\",0,\"^Z\",33.796875,\"^D\",[[\"^ \",\"^E\",\"#ef0c0c\",\"^F\",1]],\"^[\",37.40625,\"^10\",\"ltr\",\"^G\",\"\\\"AR One Sans\\\"\",\"^11\",19,\"^H\",\"token\"]]],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:x\",812,\"~:selrect\",[\"^V\",[\"^ \",\"~:x\",812,\"~:y\",609,\"^L\",139,\"^11\",28,\"^Y\",812,\"^X\",609,\"^[\",951,\"^Z\",637]],\"~:flip-x\",null,\"^11\",28,\"~:flip-y\",null]]",
|
||||
"~u886f2846-a2c5-808d-8008-4115e86119ea": "[\"~#shape\",[\"^ \",\"~:y\",399,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle with deleted reference\",\"~:width\",162,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",193,\"~:y\",399]],[\"^<\",[\"^ \",\"~:x\",355,\"~:y\",399]],[\"^<\",[\"^ \",\"~:x\",355,\"~:y\",545]],[\"^<\",[\"^ \",\"~:x\",193,\"~:y\",545]]],\"~:r2\",20,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",20,\"~:r1\",20,\"~:id\",\"~u886f2846-a2c5-808d-8008-4115e86119ea\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"ref-grfeen\",\"^A\",\"ref-1\",\"^=\",\"ref-1\",\"^@\",\"ref-1\",\"~:r4\",\"ref-1\"],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",193,\"~:proportion\",1,\"^F\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",193,\"~:y\",399,\"^8\",162,\"~:height\",146,\"~:x1\",193,\"~:y1\",399,\"~:x2\",355,\"~:y2\",545]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#0a20bf\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^L\",146,\"~:flip-y\",null]]",
|
||||
"~u886f2846-a2c5-808d-8008-4115e86119e9": "[\"~#shape\",[\"^ \",\"~:y\",586,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:content\",[\"^ \",\"~:type\",\"root\",\"~:children\",[[\"^ \",\"^7\",\"paragraph-set\",\"^8\",[[\"^ \",\"~:line-height\",\"1.2\",\"~:font-style\",\"normal\",\"^8\",[[\"^ \",\"^9\",\"1.2\",\"^:\",\"normal\",\"~:text-transform\",\"none\",\"~:text-align\",\"left\",\"~:font-id\",\"gfont-adlam-display\",\"~:font-size\",\"23\",\"~:font-weight\",\"400\",\"~:text-direction\",\"ltr\",\"~:font-variant-id\",\"regular\",\"~:text-decoration\",\"none\",\"~:letter-spacing\",\"0\",\"~:fills\",[[\"^ \",\"~:fill-color\",\"#000000\",\"~:fill-opacity\",1]],\"~:font-family\",\"ADLaM Display\",\"~:text\",\"Text with deleted reference\"]],\"^;\",\"none\",\"^<\",\"left\",\"^=\",\"gfont-adlam-display\",\"~:key\",\"fu3vh\",\"^>\",\"23\",\"^?\",\"400\",\"^@\",\"ltr\",\"^7\",\"paragraph\",\"^A\",\"regular\",\"^B\",\"none\",\"^C\",\"0\",\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"^G\",\"ADLaM Display\"]]]]],\"~:hide-in-viewer\",false,\"~:name\",\"Text with deleted reference\",\"~:width\",137,\"^7\",\"^H\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",198,\"~:y\",586]],[\"^N\",[\"^ \",\"~:x\",335,\"~:y\",586]],[\"^N\",[\"^ \",\"~:x\",335,\"~:y\",683]],[\"^N\",[\"^ \",\"~:x\",198,\"~:y\",683]]],\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u886f2846-a2c5-808d-8008-4115e86119e9\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"ref-grfeen\",\"~:typography\",\"ref-typo\"],\"~:position-data\",[[\"~#rect\",[\"^ \",\"~:y\",614,\"^:\",\"normal\",\"^;\",\"none\",\"^>\",\"23px\",\"^?\",\"400\",\"~:y1\",-2,\"^L\",107.921875,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",198,\"~:x1\",0,\"~:y2\",28,\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"~:x2\",107.921875,\"~:direction\",\"ltr\",\"^G\",\"\\\"ADLaM Display\\\"\",\"~:height\",30,\"^H\",\"Text with \"]],[\"^V\",[\"^ \",\"~:y\",641.59375,\"^:\",\"normal\",\"^;\",\"none\",\"^>\",\"23px\",\"^?\",\"400\",\"^W\",25.59375,\"^L\",88.890625,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",198,\"^X\",0,\"^Y\",55.59375,\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"^Z\",88.890625,\"^[\",\"ltr\",\"^G\",\"\\\"ADLaM Display\\\"\",\"^10\",30,\"^H\",\"deleted \"]],[\"^V\",[\"^ \",\"~:y\",669.1875,\"^:\",\"normal\",\"^;\",\"none\",\"^>\",\"23px\",\"^?\",\"400\",\"^W\",53.1875,\"^L\",103.46875,\"^B\",\"none\",\"^C\",\"normal\",\"~:x\",198,\"^X\",0,\"^Y\",83.1875,\"^D\",[[\"^ \",\"^E\",\"#000000\",\"^F\",1]],\"^Z\",103.46875,\"^[\",\"ltr\",\"^G\",\"\\\"ADLaM Display\\\"\",\"^10\",30,\"^H\",\"reference\"]]],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:x\",198,\"~:selrect\",[\"^V\",[\"^ \",\"~:x\",198,\"~:y\",586,\"^L\",137,\"^10\",97,\"^X\",198,\"^W\",586,\"^Z\",335,\"^Y\",683]],\"~:flip-x\",null,\"^10\",97,\"~:flip-y\",null]]",
|
||||
"~ua7df6ab8-bee5-802f-8008-010d9ff3da26": "[\"~#shape\",[\"^ \",\"~:y\",274,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle with not active token\",\"~:width\",287,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",723,\"~:y\",274]],[\"^<\",[\"^ \",\"~:x\",1010,\"~:y\",274]],[\"^<\",[\"^ \",\"~:x\",1010,\"~:y\",586]],[\"^<\",[\"^ \",\"~:x\",723,\"~:y\",586]]],\"~:r2\",100,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:page-id\",\"~u4530574a-7a0a-807b-8008-0107b2c4628e\",\"~:r3\",100,\"~:r1\",100,\"~:id\",\"~ua7df6ab8-bee5-802f-8008-010d9ff3da26\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:applied-tokens\",[\"^ \",\"~:fill\",\"red\",\"^B\",\"border-radius\",\"^=\",\"border-radius\",\"^A\",\"border-radius\",\"~:r4\",\"border-radius\"],\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",723,\"~:proportion\",1,\"^G\",100,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",723,\"~:y\",274,\"^8\",287,\"~:height\",312,\"~:x1\",723,\"~:y1\",274,\"~:x2\",1010,\"~:y2\",586]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ef0c0c\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^M\",312,\"~:flip-y\",null]]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:tokens-lib": {
|
||||
"~#penpot/tokens-lib": {
|
||||
"~:sets": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"S-Global",
|
||||
{
|
||||
"~#penpot/token-set": {
|
||||
"~:id": "~uf71719b3-63a1-8157-8008-40346917de8c",
|
||||
"~:name": "Global",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782813028640",
|
||||
"~:tokens": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"base-50",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf71719b3-63a1-8157-8008-40346917de87",
|
||||
"~:name": "base-50",
|
||||
"~:type": "~:border-radius",
|
||||
"~:value": "50",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782742859871"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"out-ref",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf2256046-b127-808e-8008-411bec9b1109",
|
||||
"~:name": "out-ref",
|
||||
"~:type": "~:color",
|
||||
"~:value": "rgb(218, 31, 234)",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782803549804"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"out-typo",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf2256046-b127-808e-8008-411bf90df57e",
|
||||
"~:name": "out-typo",
|
||||
"~:type": "~:typography",
|
||||
"~:value": {
|
||||
"~:font-family": [
|
||||
"Arizonia"
|
||||
]
|
||||
},
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782803562551"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"G-Mode",
|
||||
{
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"S-Dark",
|
||||
{
|
||||
"~#penpot/token-set": {
|
||||
"~:id": "~uf71719b3-63a1-8157-8008-40346917de8d",
|
||||
"~:name": "Mode/Dark",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782803647245",
|
||||
"~:tokens": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"red",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf71719b3-63a1-8157-8008-40346917de88",
|
||||
"~:name": "red",
|
||||
"~:type": "~:color",
|
||||
"~:value": "#ef0c0c",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782742859871"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"ref-grfeen",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u886f2846-a2c5-808d-8008-4115e5fdae2e",
|
||||
"~:name": "ref-grfeen",
|
||||
"~:type": "~:color",
|
||||
"~:value": "{green}",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782801970166"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"ref-typo",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u886f2846-a2c5-808d-8008-41160c4641f4",
|
||||
"~:name": "ref-typo",
|
||||
"~:type": "~:typography",
|
||||
"~:value": "{typo1}",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782802009369"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"ref-1",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u886f2846-a2c5-808d-8008-411628261dc7",
|
||||
"~:name": "ref-1",
|
||||
"~:type": "~:border-radius",
|
||||
"~:value": "{b1}",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782802037912"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"in-br",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf2256046-b127-808e-8008-411c0aced089",
|
||||
"~:name": "in-br",
|
||||
"~:type": "~:border-radius",
|
||||
"~:value": "{base-50}",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782803580731"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"in-color",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf2256046-b127-808e-8008-411c263c999b",
|
||||
"~:name": "in-color",
|
||||
"~:type": "~:color",
|
||||
"~:value": "{out-ref}",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782803608818"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"in-typo",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf2256046-b127-808e-8008-411c472f33d3",
|
||||
"~:name": "in-typo",
|
||||
"~:type": "~:typography",
|
||||
"~:value": "{out-typo}",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782803642556"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"S-Light",
|
||||
{
|
||||
"~#penpot/token-set": {
|
||||
"~:id": "~uf71719b3-63a1-8157-8008-40346917de8e",
|
||||
"~:name": "Mode/Light",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782812999332",
|
||||
"~:tokens": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"red",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf71719b3-63a1-8157-8008-40346917de89",
|
||||
"~:name": "red",
|
||||
"~:type": "~:color",
|
||||
"~:value": "#ec9090",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782742859871"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"typo-2",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~u1de8c150-538b-80b2-8008-40357ed6d13a",
|
||||
"~:name": "typo-2",
|
||||
"~:type": "~:typography",
|
||||
"~:value": {
|
||||
"~:font-family": [
|
||||
"AR One Sans"
|
||||
],
|
||||
"~:font-weight": "200"
|
||||
},
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782812999332"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"border-radius",
|
||||
{
|
||||
"~#penpot/token": {
|
||||
"~:id": "~uf2256046-b127-808e-8008-4134c130c292",
|
||||
"~:name": "border-radius",
|
||||
"~:type": "~:border-radius",
|
||||
"~:value": "{base-50} * 2",
|
||||
"~:description": "",
|
||||
"~:modified-at": "~m1782810058947"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"~:themes": {
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"",
|
||||
{
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"__PENPOT__HIDDEN__TOKEN__THEME__",
|
||||
{
|
||||
"~#penpot/token-theme": {
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:name": "__PENPOT__HIDDEN__TOKEN__THEME__",
|
||||
"~:group": "",
|
||||
"~:description": "",
|
||||
"~:is-source": false,
|
||||
"~:external-id": "",
|
||||
"~:modified-at": "~m1782812922760",
|
||||
"~:sets": {
|
||||
"~#set": [
|
||||
"Global"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"App",
|
||||
{
|
||||
"~#ordered-map": [
|
||||
[
|
||||
"Web",
|
||||
{
|
||||
"~#penpot/token-theme": {
|
||||
"~:id": "~u37900259-7f15-8003-8007-9ee75dd2f2d2",
|
||||
"~:name": "Web",
|
||||
"~:group": "App",
|
||||
"~:description": "",
|
||||
"~:is-source": false,
|
||||
"~:external-id": "37900259-7f15-8003-8007-9ee75dd2f2d2",
|
||||
"~:modified-at": "~m1782810072529",
|
||||
"~:sets": {
|
||||
"~#set": [
|
||||
"Mode/Dark",
|
||||
"Global"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"Mobile",
|
||||
{
|
||||
"~#penpot/token-theme": {
|
||||
"~:id": "~u37900259-7f15-8003-8007-9ee77eb567bd",
|
||||
"~:name": "Mobile",
|
||||
"~:group": "App",
|
||||
"~:description": "",
|
||||
"~:is-source": false,
|
||||
"~:external-id": "37900259-7f15-8003-8007-9ee77eb567bd",
|
||||
"~:modified-at": "~m1782810072529",
|
||||
"~:sets": {
|
||||
"~#set": [
|
||||
"Mode/Light",
|
||||
"Global"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"~:active-themes": {
|
||||
"~#set": [
|
||||
"/__PENPOT__HIDDEN__TOKEN__THEME__"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:id": "~u55608328-aed3-807a-8008-41341f2f7247",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
@ -209,7 +209,10 @@ test("Renders a file with emoji and text decoration", async ({ page }) => {
|
||||
pageId: "82d128e1-d3b1-80a5-8006-ae60fedcd5e8",
|
||||
});
|
||||
await workspace.waitForFirstRenderWithoutUI();
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
await expect(workspace.canvas).toHaveScreenshot({
|
||||
maxDiffPixelRatio: 0,
|
||||
threshold: 0.1,
|
||||
});
|
||||
});
|
||||
|
||||
test("Renders a file with multiple emoji", async ({ page }) => {
|
||||
|
||||
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 80 KiB |
@ -331,3 +331,33 @@ test("BUG 14098 - Fix text editor having 0 width or height", async ({ page }) =>
|
||||
const textEditor = workspace.page.locator(`div[class*="viewport"]`).first().getByRole('textbox').first();
|
||||
await expect(textEditor).toBeVisible();
|
||||
});
|
||||
|
||||
test("Preserves empty fill after editing text without changes", async ({ page }) => {
|
||||
const initialText = "Hello";
|
||||
const workspace = new WasmWorkspacePage(page, {
|
||||
textEditor: true,
|
||||
});
|
||||
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC("update-file?id=*", "text-editor/update-file.json");
|
||||
await workspace.goToWorkspace();
|
||||
|
||||
await workspace.createTextShape(190, 150, 300, 200, initialText);
|
||||
await workspace.textEditor.stopEditing();
|
||||
|
||||
const fillColorButton = workspace.page.getByRole("button", {
|
||||
name: "#000000",
|
||||
});
|
||||
await expect(fillColorButton).toBeVisible();
|
||||
await workspace.page.getByRole("button", { name: "Remove color" }).click();
|
||||
await expect(fillColorButton).toHaveCount(0);
|
||||
|
||||
await workspace.doubleClickLeafLayer(initialText);
|
||||
await workspace.textEditor.waitForEditor();
|
||||
await workspace.moveButton.click();
|
||||
await workspace.clickAt(100, 100);
|
||||
|
||||
await workspace.clickLeafLayer(initialText);
|
||||
await expect(fillColorButton).toHaveCount(0);
|
||||
await expect(workspace.page.getByTestId("add-fill")).toBeVisible();
|
||||
});
|
||||
|
||||
@ -907,7 +907,7 @@ test.describe("Tokens: Detach token", () => {
|
||||
await expect(page.getByText("Don't remap")).toBeVisible();
|
||||
await page.getByText("Don't remap").click();
|
||||
const brokenPill = borderRadiusSection.getByRole("button", {
|
||||
name: "is not in any active set",
|
||||
name: "{borderRadius.sm} token does not exist or has been deleted",
|
||||
});
|
||||
await expect(brokenPill).toBeVisible();
|
||||
|
||||
@ -1775,3 +1775,375 @@ test("BUG: 14234, Numeric input token filtering must be case sensitive", async (
|
||||
measuresSection.getByText("dim-up", { exact: true }),
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("BUG: 10471, Correct tooltip on right sidebar tokens", async ({
|
||||
page,
|
||||
}) => {
|
||||
const {
|
||||
workspacePage,
|
||||
tokensSidebar,
|
||||
tokenContextMenuForToken,
|
||||
tokenThemesSetsSidebar,
|
||||
tokenSetGroupItems,
|
||||
} = await setupTokensFileRender(page, {
|
||||
file: "workspace/get-file-token-tooltip.json",
|
||||
});
|
||||
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
const borderRadiusSection = page.getByRole("region", {
|
||||
name: "Border radius section",
|
||||
});
|
||||
|
||||
const fillSection = workspacePage.rightSidebar.getByRole("region", {
|
||||
name: "Fill section",
|
||||
});
|
||||
|
||||
const typographySection = workspacePage.rightSidebar.getByRole("region", {
|
||||
name: "Text section",
|
||||
});
|
||||
// ----------------------------------
|
||||
// Select rectangle with active token
|
||||
// ----------------------------------
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Rectangle with token" })
|
||||
.click();
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
const brPill = borderRadiusSection.getByRole("button", { name: "base-50" });
|
||||
await expect(brPill).toBeVisible();
|
||||
await brPill.hover();
|
||||
await expect(page.getByRole("tooltip", { name: "base-50" })).toBeVisible();
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillTokenPill = fillSection.getByLabel("out-ref", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(fillTokenPill).toBeVisible();
|
||||
await fillTokenPill.hover();
|
||||
const colorTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "out-ref",
|
||||
});
|
||||
await expect(colorTokenTooltip).toBeVisible();
|
||||
await expect(colorTokenTooltip).toHaveText(
|
||||
"Name: out-refResolved value: #da1fea",
|
||||
);
|
||||
// ----------------------------------
|
||||
// Select text with active token
|
||||
// ----------------------------------
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Text with token" })
|
||||
.click();
|
||||
await expect(fillSection).toBeVisible();
|
||||
const textFillTokenPill = fillSection.getByLabel("out-ref", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(textFillTokenPill).toBeVisible();
|
||||
await textFillTokenPill.hover();
|
||||
await expect(colorTokenTooltip).toBeVisible();
|
||||
await expect(colorTokenTooltip).toHaveText(
|
||||
"Name: out-refResolved value: #da1fea",
|
||||
);
|
||||
await expect(typographySection).toBeVisible();
|
||||
const typographyTokenPill = typographySection.getByLabel("out-typo", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(typographyTokenPill).toBeVisible();
|
||||
await typographyTokenPill.hover();
|
||||
const typographyTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "out-typo",
|
||||
});
|
||||
await expect(typographyTokenTooltip).toBeVisible();
|
||||
await expect(typographyTokenTooltip).toHaveText(
|
||||
'Name: out-typoResolved value:- font-family: "Arizonia"',
|
||||
);
|
||||
// -----------------------------------------
|
||||
// Select rectangle layer with deleted token
|
||||
// -----------------------------------------
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Deleted token rect" })
|
||||
.click();
|
||||
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
const deletedBrPill = borderRadiusSection.getByRole("button", {
|
||||
name: "{deleted} token does not exist or",
|
||||
});
|
||||
await expect(deletedBrPill).toBeVisible();
|
||||
await deletedBrPill.hover();
|
||||
await expect(
|
||||
page.getByRole("tooltip", { name: "{deleted} token does not exist or" }),
|
||||
).toBeVisible();
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillBrokenTokenPill = fillSection.getByLabel("blue", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(fillBrokenTokenPill).toBeVisible();
|
||||
await fillBrokenTokenPill.hover();
|
||||
const fillBrokenTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "blue",
|
||||
});
|
||||
await expect(fillBrokenTokenTooltip).toBeVisible();
|
||||
await expect(fillBrokenTokenTooltip).toHaveText(
|
||||
"{blue} token does not exist or has been deleted.",
|
||||
);
|
||||
// ------------------------------------
|
||||
// Select text layer with deleted token
|
||||
// ------------------------------------
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Text with deleted token" })
|
||||
.click();
|
||||
await expect(typographySection).toBeVisible();
|
||||
const brokenTypographyTokenPill = typographySection.getByLabel(
|
||||
"deleted-typo",
|
||||
{ exact: true },
|
||||
);
|
||||
await expect(brokenTypographyTokenPill).toBeVisible();
|
||||
await brokenTypographyTokenPill.hover();
|
||||
const brokenTypographyTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "deleted-typo",
|
||||
});
|
||||
await expect(brokenTypographyTokenTooltip).toBeVisible();
|
||||
await expect(brokenTypographyTokenTooltip).toHaveText(
|
||||
"{deleted-typo} token does not exist or has been deleted.",
|
||||
);
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Select rectangle layer with deleted reference token
|
||||
// ---------------------------------------------------
|
||||
await page.getByRole("tab", { name: "Tokens" }).click();
|
||||
await tokenThemesSetsSidebar.getByRole("button", { name: "Dark" }).click();
|
||||
|
||||
await tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "Dark" })
|
||||
.getByRole("checkbox")
|
||||
.click();
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Rectangle with deleted reference" })
|
||||
.click();
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
const deletedReferenceBrPill = borderRadiusSection.getByRole("button", {
|
||||
name: "Reference in {ref-1} is not valid or is not in any active set.",
|
||||
});
|
||||
await expect(deletedReferenceBrPill).toBeVisible();
|
||||
await deletedReferenceBrPill.hover();
|
||||
await expect(
|
||||
page.getByRole("tooltip", {
|
||||
name: "Reference in {ref-1} is not valid or is not in any active set.",
|
||||
}),
|
||||
).toBeVisible();
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillDeletedReferenceTokenPill = fillSection.getByLabel("ref-grfeen", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(fillDeletedReferenceTokenPill).toBeVisible();
|
||||
await fillDeletedReferenceTokenPill.hover();
|
||||
const fillDeletedReferenceTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "Reference in {ref-grfeen} is not valid or is not in any active set.",
|
||||
});
|
||||
await expect(fillDeletedReferenceTokenTooltip).toBeVisible();
|
||||
await expect(fillDeletedReferenceTokenTooltip).toHaveText(
|
||||
"Reference in {ref-grfeen} is not valid or is not in any active set.",
|
||||
);
|
||||
|
||||
// ----------------------------------------------
|
||||
// Select text layer with deleted reference token
|
||||
// ----------------------------------------------
|
||||
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Text with deleted reference" })
|
||||
.click();
|
||||
|
||||
await expect(typographySection).toBeVisible();
|
||||
const deletedRefTypographyTokenPill = typographySection.getByLabel(
|
||||
"ref-typo",
|
||||
{ exact: true },
|
||||
);
|
||||
await expect(deletedRefTypographyTokenPill).toBeVisible();
|
||||
await deletedRefTypographyTokenPill.hover();
|
||||
const deletedRefTypographyTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "Reference in {ref-typo} is not valid or is",
|
||||
});
|
||||
await expect(deletedRefTypographyTokenTooltip).toBeVisible();
|
||||
await expect(deletedRefTypographyTokenTooltip).toHaveText(
|
||||
"Reference in {ref-typo} is not valid or is not in any active set.",
|
||||
);
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillTextDeletedReferenceTokenPill = fillSection.getByLabel(
|
||||
"ref-grfeen",
|
||||
{
|
||||
exact: true,
|
||||
},
|
||||
);
|
||||
await expect(fillTextDeletedReferenceTokenPill).toBeVisible();
|
||||
await fillTextDeletedReferenceTokenPill.hover();
|
||||
const fillTextDeletedReferenceTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "Reference in {ref-grfeen} is not valid or is not in any active set.",
|
||||
});
|
||||
await expect(fillTextDeletedReferenceTokenTooltip).toBeVisible();
|
||||
await expect(fillTextDeletedReferenceTokenTooltip).toHaveText(
|
||||
"Reference in {ref-grfeen} is not valid or is not in any active set.",
|
||||
);
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Select rectangle layer with reference token on inactive set
|
||||
// -----------------------------------------------------------
|
||||
await page.getByRole("tab", { name: "Tokens" }).click();
|
||||
await tokenThemesSetsSidebar.getByRole("button", { name: "Global" }).click();
|
||||
|
||||
await tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "Global" })
|
||||
.getByRole("checkbox")
|
||||
.click();
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Rectangle with not active reference" })
|
||||
.click();
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
const nonActiveReferenceBrPill = borderRadiusSection.getByRole("button", {
|
||||
name: "Reference in {in-br} is not valid or is not in any active set.",
|
||||
});
|
||||
await expect(nonActiveReferenceBrPill).toBeVisible();
|
||||
await nonActiveReferenceBrPill.hover();
|
||||
await expect(
|
||||
page.getByRole("tooltip", {
|
||||
name: "Reference in {in-br} is not valid or is not in any active set.",
|
||||
}),
|
||||
).toBeVisible();
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillNonActiveReferenceTokenPill = fillSection.getByLabel("in-color", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(fillNonActiveReferenceTokenPill).toBeVisible();
|
||||
await fillNonActiveReferenceTokenPill.hover();
|
||||
const fillNonActiveReferenceTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "Reference in {in-color} is not valid or is not in any active set.",
|
||||
});
|
||||
await expect(fillNonActiveReferenceTokenTooltip).toBeVisible();
|
||||
await expect(fillNonActiveReferenceTokenTooltip).toHaveText(
|
||||
"Reference in {in-color} is not valid or is not in any active set.",
|
||||
);
|
||||
// ------------------------------------------------------
|
||||
// Select text layer with reference token on inactive set
|
||||
// ------------------------------------------------------
|
||||
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Text with not active reference" })
|
||||
.click();
|
||||
|
||||
await expect(typographySection).toBeVisible();
|
||||
const notActiveRefTypographyTokenPill = typographySection.getByLabel(
|
||||
"in-typo",
|
||||
{ exact: true },
|
||||
);
|
||||
await expect(notActiveRefTypographyTokenPill).toBeVisible();
|
||||
await notActiveRefTypographyTokenPill.hover();
|
||||
const notActiveRefTypographyTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "Reference in {in-typo} is not valid or is",
|
||||
});
|
||||
await expect(notActiveRefTypographyTokenTooltip).toBeVisible();
|
||||
await expect(notActiveRefTypographyTokenTooltip).toHaveText(
|
||||
"Reference in {in-typo} is not valid or is not in any active set.",
|
||||
);
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillTextNotActiveReferenceTokenPill = fillSection.getByLabel(
|
||||
"in-color",
|
||||
{
|
||||
exact: true,
|
||||
},
|
||||
);
|
||||
await expect(fillTextNotActiveReferenceTokenPill).toBeVisible();
|
||||
await fillTextNotActiveReferenceTokenPill.hover();
|
||||
const fillTextNotActiveReferenceTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "Reference in {in-color} is not valid or is not in any active set.",
|
||||
});
|
||||
await expect(fillTextNotActiveReferenceTokenTooltip).toBeVisible();
|
||||
await expect(fillTextNotActiveReferenceTokenTooltip).toHaveText(
|
||||
"Reference in {in-color} is not valid or is not in any active set.",
|
||||
);
|
||||
|
||||
// -------------------------------------------------
|
||||
// Select rectangle layer with token on inactive set
|
||||
// -------------------------------------------------
|
||||
await page.getByRole("tab", { name: "Tokens" }).click();
|
||||
await tokenThemesSetsSidebar.getByRole("button", { name: "Dark" }).click();
|
||||
|
||||
await tokenThemesSetsSidebar
|
||||
.getByRole("button", { name: "Dark" })
|
||||
.getByRole("checkbox")
|
||||
.click();
|
||||
await page.getByRole("tab", { name: "Layers" }).click();
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Rectangle with not active token" })
|
||||
.click();
|
||||
await expect(borderRadiusSection).toBeVisible();
|
||||
const nonActiveBrPill = borderRadiusSection.getByRole("button", {
|
||||
name: "{border-radius} token is not in any active set",
|
||||
});
|
||||
await expect(nonActiveBrPill).toBeVisible();
|
||||
await nonActiveBrPill.hover();
|
||||
await expect(
|
||||
page.getByRole("tooltip", {
|
||||
name: "{border-radius} token is not in any active set or has an invalid value",
|
||||
}),
|
||||
).toBeVisible();
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillNonActiveTokenPill = fillSection.getByLabel("red", {
|
||||
exact: true,
|
||||
});
|
||||
await expect(fillNonActiveTokenPill).toBeVisible();
|
||||
await fillNonActiveTokenPill.hover();
|
||||
const fillNonActiveTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "{red} token is not in any active set or has an invalid value.",
|
||||
});
|
||||
await expect(fillNonActiveTokenTooltip).toBeVisible();
|
||||
await expect(fillNonActiveTokenTooltip).toHaveText(
|
||||
"{red} token is not in any active set or has an invalid value.",
|
||||
);
|
||||
|
||||
// --------------------------------------------
|
||||
// Select text layer with token on inactive set
|
||||
// --------------------------------------------
|
||||
|
||||
await workspacePage.layers
|
||||
.getByTestId("layer-row")
|
||||
.filter({ hasText: "Text with not active token" })
|
||||
.click();
|
||||
|
||||
await expect(typographySection).toBeVisible();
|
||||
const notActiveTypographyTokenPill = typographySection.getByLabel(
|
||||
"typo-2",
|
||||
{ exact: true },
|
||||
);
|
||||
await expect(notActiveTypographyTokenPill).toBeVisible();
|
||||
await notActiveTypographyTokenPill.hover();
|
||||
const notActiveTypographyTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "{typo-2} token is not in any active set or has an invalid value.",
|
||||
});
|
||||
await expect(notActiveTypographyTokenTooltip).toBeVisible();
|
||||
await expect(notActiveTypographyTokenTooltip).toHaveText(
|
||||
"{typo-2} token is not in any active set or has an invalid value.",
|
||||
);
|
||||
await expect(fillSection).toBeVisible();
|
||||
const fillTextNotActiveTokenPill = fillSection.getByLabel(
|
||||
"red",
|
||||
{
|
||||
exact: true,
|
||||
},
|
||||
);
|
||||
await expect(fillTextNotActiveTokenPill).toBeVisible();
|
||||
await fillTextNotActiveTokenPill.hover();
|
||||
const fillTextNotActiveTokenTooltip = page.getByRole("tooltip", {
|
||||
name: "{red} token is not in any active set or has an invalid value.",
|
||||
});
|
||||
await expect(fillTextNotActiveTokenTooltip).toBeVisible();
|
||||
await expect(fillTextNotActiveTokenTooltip).toHaveText(
|
||||
"{red} token is not in any active set or has an invalid value.",
|
||||
);
|
||||
});
|
||||
|
||||
564
frontend/pnpm-lock.yaml
generated
@ -204,6 +204,10 @@
|
||||
(mf/with-effect [options]
|
||||
(mf/set-ref-val! options-ref options))
|
||||
|
||||
(mf/with-effect [default-selected options]
|
||||
(reset! selected-id*
|
||||
(get-selected-option-id options default-selected)))
|
||||
|
||||
[:div {:class [wrapper-class (stl/css :select-wrapper)]
|
||||
:on-click on-click
|
||||
:ref select-ref
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.tooltip :refer [tooltip*]]
|
||||
@ -43,10 +44,25 @@
|
||||
token-has-errors]}]
|
||||
(let [set-active? (some? id)
|
||||
|
||||
all-tokens-map (mf/deref refs/workspace-all-tokens-map)
|
||||
token-exists? (contains? all-tokens-map label)
|
||||
token-not-active (and token-exists? (not set-active?))
|
||||
content (cond
|
||||
token-has-errors (tr "workspace.tokens.ref-not-valid")
|
||||
(not set-active?) (tr "ds.inputs.token-field.no-active-token-option" label)
|
||||
:else label)
|
||||
(not token-exists?)
|
||||
(tr "options.deleted-token-with-name" label)
|
||||
|
||||
(and token-exists? (not set-active?))
|
||||
(tr "ds.inputs.token-field.no-active-token-option" label)
|
||||
|
||||
(and token-exists? token-has-errors)
|
||||
(tr "workspace.tokens.ref-not-valid" label)
|
||||
|
||||
:else
|
||||
label)
|
||||
|
||||
broken-state (or (not token-exists?)
|
||||
token-has-errors
|
||||
token-not-active)
|
||||
|
||||
default-id (mf/use-id)
|
||||
id (d/nilv id default-id)
|
||||
@ -84,15 +100,13 @@
|
||||
[:button {:on-click on-click
|
||||
:ref pill-ref
|
||||
:class (stl/css-case :pill true
|
||||
:no-set-pill (or (not set-active?)
|
||||
token-has-errors)
|
||||
:no-set-pill broken-state
|
||||
:pill-disabled disabled)
|
||||
:disabled disabled
|
||||
:aria-labelledby (dm/str id "-pill")
|
||||
:on-key-down on-token-key-down}
|
||||
value
|
||||
(when (or (not set-active?)
|
||||
token-has-errors)
|
||||
(when broken-state
|
||||
[:div {:class (stl/css :pill-dot)}])]]]
|
||||
|
||||
(when-not ^boolean disabled
|
||||
|
||||
@ -159,6 +159,7 @@ $arrow-side: 12px;
|
||||
padding: var(--sp-s) var(--sp-m);
|
||||
grid-area: content;
|
||||
block-size: fit-content;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.tooltip-trigger {
|
||||
|
||||
@ -193,10 +193,12 @@
|
||||
.interaction-row-position {
|
||||
grid-column: 4 / span 5;
|
||||
display: grid;
|
||||
grid-template:
|
||||
"topleft top topright" 1fr
|
||||
"left center right" 1fr
|
||||
"bottomleft bottom bottomright" 1fr / repeat(3, 1fr);
|
||||
grid-template-areas:
|
||||
"topleft top topright"
|
||||
"left center right"
|
||||
"bottomleft bottom bottomright";
|
||||
grid-template-rows: 1fr 1fr 1fr;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
inline-size: calc($sz-32 * 3);
|
||||
block-size: calc($sz-32 * 3);
|
||||
border-radius: $br-8;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.controls.shared.token-option :as to]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
@ -25,31 +26,46 @@
|
||||
token (->> (:typography active-tokens)
|
||||
(d/seek #(= (:name %) token-name)))
|
||||
|
||||
has-errors (some? (:errors token))
|
||||
display-name (or (:name token) token-name)
|
||||
|
||||
resolved-value (:resolved-value token)
|
||||
not-active (or (nil? token)
|
||||
(empty? (:typography active-tokens)))
|
||||
on-detach
|
||||
(mf/use-fn
|
||||
(mf/deps display-name)
|
||||
(fn []
|
||||
(detach-token display-name)))
|
||||
|
||||
all-tokens-map (mf/deref refs/workspace-all-tokens-map)
|
||||
|
||||
token-exists? (contains? all-tokens-map token-name)
|
||||
has-errors (and token-exists?
|
||||
(some? (:errors token)))
|
||||
|
||||
not-active (and
|
||||
token-exists?
|
||||
(or (nil? token)
|
||||
(empty? (:typography active-tokens))))
|
||||
|
||||
broken-state (or (not token-exists?)
|
||||
has-errors
|
||||
not-active)
|
||||
tooltip-content (cond
|
||||
not-active
|
||||
(tr "options.deleted-token")
|
||||
(tr "ds.inputs.token-field.no-active-token-option" token-name)
|
||||
|
||||
(not token-exists?)
|
||||
(tr "options.deleted-token-with-name" token-name)
|
||||
|
||||
has-errors
|
||||
(tr "not-active-token.no-name")
|
||||
(tr "workspace.tokens.ref-not-valid" token-name)
|
||||
|
||||
:else
|
||||
(mf/html [:> to/resolved-value-tooltip* {:token-name token-name
|
||||
:resolved-value resolved-value}]))]
|
||||
|
||||
[:div {:class (stl/css-case :token-typography-row true
|
||||
:token-typography-row-with-errors has-errors
|
||||
:token-typography-row-not-active not-active)}
|
||||
(when (or has-errors not-active)
|
||||
:token-typography-row-with-errors broken-state)}
|
||||
(when broken-state
|
||||
[:div {:class (stl/css :error-dot)}])
|
||||
[:> icon* {:icon-id i/text-typography
|
||||
:class (stl/css :icon)}]
|
||||
|
||||
@ -35,8 +35,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.token-typography-row-with-errors,
|
||||
.token-typography-row-not-active {
|
||||
.token-typography-row-with-errors {
|
||||
--token-typography-row-background-color: var(--color-background-primary);
|
||||
--token-typography-row-foreground-color: var(--color-foreground-secondary);
|
||||
--token-typography-row-border-color: var(--color-token-border);
|
||||
|
||||
@ -89,42 +89,72 @@
|
||||
(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 (or (empty? active-tokens)
|
||||
(nil? token))
|
||||
|
||||
|
||||
;; Tooltip content for the swatch and token name, based on the token's state
|
||||
all-tokens-map (mf/deref refs/workspace-all-tokens-map)
|
||||
|
||||
token-exists? (contains? all-tokens-map applied-token-name)
|
||||
has-errors (and token-exists?
|
||||
(some? (:errors token)))
|
||||
|
||||
not-active (and
|
||||
token-exists?
|
||||
(or (empty? active-tokens)
|
||||
(nil? token)))
|
||||
|
||||
id (dm/str (:id token) "-name")
|
||||
token-name-ref (mf/use-ref nil)
|
||||
|
||||
broken-state (or (not token-exists?)
|
||||
has-errors
|
||||
not-active)
|
||||
|
||||
swatch-tooltip-content (cond
|
||||
not-active
|
||||
(tr "options.deleted-token")
|
||||
(tr "ds.inputs.token-field.no-active-token-option" applied-token-name)
|
||||
|
||||
(not token-exists?)
|
||||
(tr "options.deleted-token-with-name" applied-token-name)
|
||||
|
||||
has-errors
|
||||
(tr "not-active-token.no-name")
|
||||
(tr "workspace.tokens.ref-not-valid" applied-token-name)
|
||||
|
||||
:else
|
||||
(tr "workspace.tokens.resolved-value" resolved))
|
||||
|
||||
name-tooltip-content (cond
|
||||
not-active
|
||||
(tr "options.deleted-token")
|
||||
has-errors
|
||||
(tr "not-active-token.no-name")
|
||||
(tr "workspace.tokens.ref-not-valid" applied-token-name)
|
||||
|
||||
not-active
|
||||
(tr "ds.inputs.token-field.no-active-token-option" applied-token-name)
|
||||
|
||||
(not token-exists?)
|
||||
(tr "options.deleted-token-with-name" applied-token-name)
|
||||
|
||||
:else
|
||||
#(mf/html
|
||||
[:div
|
||||
[:span (dm/str (tr "workspace.tokens.token-name") ": ")]
|
||||
[:span {:class (stl/css :token-name-tooltip)} applied-token-name]]))]
|
||||
[:span {:class (stl/css :token-name-tooltip)} applied-token-name]
|
||||
[:div
|
||||
[:span (tr "inspect.tabs.styles.token-resolved-value")]
|
||||
[:span {:class (stl/css :resolved-value)} (dm/str " " resolved)]]]))]
|
||||
|
||||
|
||||
[:div {:class (stl/css :color-info)}
|
||||
[:div {:class (stl/css-case :token-color-wrapper true
|
||||
:token-color-with-errors has-errors
|
||||
:token-color-not-active not-active)}
|
||||
:token-color-with-errors broken-state)}
|
||||
[:div {:class (stl/css :color-bullet-wrapper)}
|
||||
(when (or has-errors not-active)
|
||||
(when broken-state
|
||||
[:div {:class (stl/css :error-dot)}])
|
||||
[:> swatch* {:background color
|
||||
:tooltip-content swatch-tooltip-content
|
||||
:on-click on-swatch-click-token
|
||||
:has-errors (or has-errors not-active)
|
||||
:has-errors broken-state
|
||||
:size "small"}]]
|
||||
[:> tooltip* {:content name-tooltip-content
|
||||
:id id
|
||||
|
||||
@ -171,7 +171,7 @@
|
||||
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||
|
||||
(when is-layout-child?
|
||||
[:& layout-item-menu*
|
||||
[:> layout-item-menu*
|
||||
{:ids ids
|
||||
:type type
|
||||
:values layout-item-values
|
||||
|
||||
@ -127,7 +127,7 @@
|
||||
(cond
|
||||
;; If there are errors, show the appropriate message
|
||||
ref-not-in-active-set
|
||||
(tr "workspace.tokens.ref-not-valid")
|
||||
(tr "workspace.tokens.ref-not-valid" name)
|
||||
|
||||
is-name-collision
|
||||
(wte/resolve-error-message (first errors))
|
||||
@ -290,7 +290,7 @@
|
||||
:token-pill-disabled disabled?
|
||||
:token-pill-applied (and can-edit? has-selected? (or half-applied? full-applied?))
|
||||
:token-pill-invalid (and can-edit? errors?)
|
||||
:token-pill-invalid-applied (and full-applied? errors? can-edit?)
|
||||
:token-pill-invalid-applied (and (or half-applied? full-applied?) errors? can-edit?)
|
||||
:token-pill-viewer is-viewer?
|
||||
:token-pill-applied-viewer (and is-viewer? has-selected?
|
||||
(or half-applied? full-applied?))
|
||||
|
||||
@ -507,7 +507,8 @@
|
||||
(when (and (seq selected-shapes)
|
||||
(not transform)
|
||||
(not text-editing?)
|
||||
(not edition))
|
||||
(not edition)
|
||||
(not mode-inspect?))
|
||||
[:> msr/selection-size-badge*
|
||||
{:selrect (gsh/shapes->rect selected-shapes)
|
||||
:zoom zoom}])
|
||||
|
||||
@ -778,6 +778,7 @@
|
||||
(not transform)
|
||||
(not text-editing?)
|
||||
(not edition)
|
||||
(not mode-inspect?)
|
||||
(not page-transition?))
|
||||
[:> msr/selection-size-badge*
|
||||
{:selrect (gsh/shapes->rect selected-shapes)
|
||||
|
||||
@ -151,7 +151,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -169,7 +169,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -187,7 +187,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -205,7 +205,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -223,7 +223,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -241,7 +241,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -259,7 +259,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -277,7 +277,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
|
||||
@ -141,7 +141,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -159,7 +159,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -177,7 +177,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -195,7 +195,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -213,7 +213,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -231,14 +231,14 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/not-valid plugin-id :righPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rightPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not (u/page-active? page-id))
|
||||
(u/not-valid plugin-id :righPadding "Cannot modify a page that is not currently active")
|
||||
(u/not-valid plugin-id :rightPadding "Cannot modify a page that is not currently active")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))}
|
||||
@ -249,7 +249,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -267,7 +267,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
|
||||
@ -336,7 +336,7 @@
|
||||
(d/without-nils
|
||||
{:type (-> (obj/get guide "type") parse-keyword)
|
||||
:display (obj/get guide "display")
|
||||
:params (-> (obj/get guide "params") parse-frame-guide-column-params)})))
|
||||
:params (-> (obj/get guide "params") parse-frame-guide-square-params)})))
|
||||
|
||||
(defn parse-frame-guide
|
||||
[^js guide]
|
||||
@ -488,8 +488,8 @@
|
||||
{:action-type action-type
|
||||
:destination (-> (obj/get action "destination") (obj/get "$id"))
|
||||
:relative-to (-> (obj/get action "relativeTo") (obj/get "$id"))
|
||||
:overlay-pos-type (-> (obj/get action "position") parse-keyword)
|
||||
:overlay-position (-> (obj/get action "manualPositionLocation") parse-point (d/nilv (gpt/point 0 0)))
|
||||
:overlay-pos-type (or (-> (obj/get action "position") parse-keyword) :center)
|
||||
:overlay-position (-> (obj/get action "manualPositionLocation") parse-point)
|
||||
:close-click-outside (obj/get action "closeWhenClickOutside")
|
||||
:background-overlay (obj/get action "addBackgroundOverlay")
|
||||
:animation (-> (obj/get action "animation") parse-animation)}
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
:set
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (number? value)) (not (pos? value)))
|
||||
(or (not (sm/valid-safe-int? value)) (neg? value))
|
||||
(u/not-valid plugin-id :delay value)
|
||||
|
||||
:else
|
||||
@ -422,7 +422,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (sm/valid-safe-int? value)) (< value 0))
|
||||
(or (not (sm/valid-safe-number? value)) (< value 0))
|
||||
(u/not-valid plugin-id :borderRadius value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -441,7 +441,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :borderRadiusTopLeft value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -460,7 +460,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :borderRadiusTopRight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -479,7 +479,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :borderRadiusBottomRight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
@ -498,7 +498,7 @@
|
||||
(fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/not-valid plugin-id :borderRadiusBottomLeft value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
|
||||
@ -453,6 +453,23 @@
|
||||
(defn token-theme-proxy? [p]
|
||||
(obj/type-of? p "TokenThemeProxy"))
|
||||
|
||||
(defn- resolve-token-set
|
||||
"Resolves an addSet/removeSet argument to a token set. A proxy is returned
|
||||
as-is; an id is located in the file's token library."
|
||||
[file-id set-arg]
|
||||
(if (token-set-proxy? set-arg)
|
||||
set-arg
|
||||
(u/locate-token-set file-id set-arg)))
|
||||
|
||||
(defn- token-set-name
|
||||
"Reads the name from a resolved token set, supporting both proxies (whose
|
||||
getter falls back to the freshly-created name) and located sets."
|
||||
[set]
|
||||
(when (some? set)
|
||||
(if (token-set-proxy? set)
|
||||
(obj/get set "name")
|
||||
(ctob/get-name set))))
|
||||
|
||||
(defn token-theme-proxy
|
||||
[plugin-id file-id id]
|
||||
(obj/reify {:name "TokenThemeProxy"
|
||||
@ -535,27 +552,20 @@
|
||||
|
||||
:addSet
|
||||
{:enumerable false
|
||||
:schema [:tuple [:fn token-set-proxy?]]
|
||||
:fn (fn [token-set]
|
||||
;; Resolve the set name before the theme lookup. The proxy's :name
|
||||
;; getter now falls back to `initial-name` when state hasn't
|
||||
;; propagated, so this is safe even for freshly created sets.
|
||||
;; Guard against nil to prevent `enable-set` from conj'ing nil
|
||||
;; into the theme's :sets — which would send `:sets #{nil}` to the
|
||||
;; backend and crash the workspace.
|
||||
(let [set-name (obj/get token-set "name")
|
||||
:schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]]
|
||||
:fn (fn [set-arg]
|
||||
(let [set-name (token-set-name (resolve-token-set file-id set-arg))
|
||||
theme (u/locate-token-theme file-id id)]
|
||||
(when (and (some? set-name) (some? theme))
|
||||
(when (and set-name theme)
|
||||
(st/emit! (dwtl/update-token-theme id (ctob/enable-set theme set-name))))))}
|
||||
|
||||
:removeSet
|
||||
{:enumerable false
|
||||
:schema [:tuple [:fn token-set-proxy?]]
|
||||
:fn (fn [token-set]
|
||||
;; Same nil guard as addSet — see comment above.
|
||||
(let [set-name (obj/get token-set "name")
|
||||
:schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]]
|
||||
:fn (fn [set-arg]
|
||||
(let [set-name (token-set-name (resolve-token-set file-id set-arg))
|
||||
theme (u/locate-token-theme file-id id)]
|
||||
(when (and (some? set-name) (some? theme))
|
||||
(when (and set-name theme)
|
||||
(st/emit! (dwtl/update-token-theme id (ctob/disable-set theme set-name))))))}
|
||||
|
||||
:duplicate
|
||||
|
||||
@ -311,12 +311,16 @@
|
||||
|
||||
(defn error-messages
|
||||
[explain]
|
||||
(->> (:errors explain)
|
||||
(reduce csm/interpret-schema-problem {})
|
||||
(flatten-error-map)
|
||||
(map (fn [[field message]]
|
||||
(tr "plugins.validation.message" field message)))
|
||||
(str/join ". ")))
|
||||
(let [msg (->> (:errors explain)
|
||||
(reduce csm/interpret-schema-problem {})
|
||||
(flatten-error-map)
|
||||
(map (fn [[field message]]
|
||||
(tr "plugins.validation.message" field message)))
|
||||
(str/join ". "))]
|
||||
;; Return nil (not "") when the explain has no mappable errors, so
|
||||
;; `handle-error` can fall back to a non-empty message instead of
|
||||
;; surfacing a bare "Value not valid. Code: :error" (#9692).
|
||||
(when-not (str/blank? msg) msg)))
|
||||
|
||||
(defn handle-error
|
||||
"Function to be used in plugin proxies methods to handle errors and print a readable
|
||||
@ -340,8 +344,8 @@
|
||||
(if explain
|
||||
(do
|
||||
(js/console.error (sm/humanize-explain explain))
|
||||
(error-messages explain))
|
||||
(ex-data cause))]
|
||||
(or (error-messages explain) (pr-str explain)))
|
||||
(or (ex-data cause) (ex-message cause) (str cause)))]
|
||||
(js/console.log (.-stack cause))
|
||||
(not-valid plugin-id :error message))))))
|
||||
|
||||
|
||||
@ -1423,18 +1423,20 @@
|
||||
(set-shape-layout shape)
|
||||
(set-layout-data shape)
|
||||
(let [is-text? (= type :text)
|
||||
pending_thumbnails (into [] (concat
|
||||
(when is-text? (set-shape-text-content id content))
|
||||
text-content-pending (when is-text? (set-shape-text-content id content))
|
||||
pending-thumbnails (into [] (concat
|
||||
text-content-pending
|
||||
(when is-text? (set-shape-text-images id content true))
|
||||
(set-shape-fills id fills true)
|
||||
(set-shape-strokes id strokes true)))
|
||||
pending_full (into [] (concat
|
||||
pending-full (into [] (concat
|
||||
(when is-text? (set-shape-text-images id content false))
|
||||
(set-shape-fills id fills false)
|
||||
(set-shape-strokes id strokes false)))]
|
||||
(perf/end-measure "set-object")
|
||||
{:thumbnails pending_thumbnails
|
||||
:full pending_full}))))
|
||||
{:thumbnails pending-thumbnails
|
||||
:full pending-full
|
||||
:font-pending-ids (if (some :callback text-content-pending) [id] [])}))))
|
||||
|
||||
(defn- update-text-layouts
|
||||
"Synchronously update text layouts for all shapes and send rect updates
|
||||
@ -1442,8 +1444,29 @@
|
||||
[text-ids]
|
||||
(run! f/update-text-layout text-ids))
|
||||
|
||||
(defn- force-update-text-layouts
|
||||
"Like update-text-layouts but forces a relayout. Use after pending fonts
|
||||
resolve so layouts (and the extrect/tiles derived from them) use real glyph
|
||||
metrics instead of fallback-font estimates."
|
||||
[text-ids]
|
||||
(run! f/force-update-text-layout text-ids))
|
||||
|
||||
(defn- relayout-after-fonts!
|
||||
"Relayout text shapes once their pending fonts have resolved. Shapes in
|
||||
`font-pending-ids` had a font fetched, so they get a forced relayout to pick
|
||||
up the real glyph metrics; the remaining text shapes get a normal layout."
|
||||
[shapes font-pending-ids]
|
||||
(let [force-ids (set font-pending-ids)
|
||||
text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)
|
||||
forced (filterv force-ids text-ids)
|
||||
rest-ids (filterv (complement force-ids) text-ids)]
|
||||
(when (seq forced)
|
||||
(force-update-text-layouts forced))
|
||||
(when (seq rest-ids)
|
||||
(update-text-layouts rest-ids))))
|
||||
|
||||
(defn process-pending
|
||||
[shapes thumbnails full on-complete]
|
||||
[shapes thumbnails full font-pending-ids on-complete]
|
||||
(let [pending-thumbnails
|
||||
(d/index-by :key :callback thumbnails)
|
||||
|
||||
@ -1467,11 +1490,7 @@
|
||||
(rx/catch #(rx/empty))))
|
||||
(rx/subs!
|
||||
(fn [_]
|
||||
;; Fonts are now loaded — recompute text layouts so Skia
|
||||
;; uses the real metrics instead of fallback-font estimates.
|
||||
(let [text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)]
|
||||
(when (seq text-ids)
|
||||
(update-text-layouts text-ids)))
|
||||
(relayout-after-fonts! shapes font-pending-ids)
|
||||
(request-render "images-loaded"))
|
||||
noop-fn
|
||||
(fn [] (when (fn? on-complete) (on-complete)))))
|
||||
@ -1480,8 +1499,8 @@
|
||||
|
||||
(defn process-object
|
||||
[shape]
|
||||
(let [{:keys [thumbnails full]} (set-object shape)]
|
||||
(process-pending [shape] thumbnails full noop-fn)))
|
||||
(let [{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||
(process-pending [shape] thumbnails full font-pending-ids noop-fn)))
|
||||
|
||||
(defn process-objects
|
||||
"Like process-object but for multiple shapes at once. Accumulates all
|
||||
@ -1490,37 +1509,43 @@
|
||||
just the first shape that triggered the fetch."
|
||||
[shapes]
|
||||
(let [total-shapes (count shapes)
|
||||
{:keys [thumbnails full]}
|
||||
(loop [index 0 thumbnails-acc (transient []) full-acc (transient [])]
|
||||
{:keys [thumbnails full font-pending-ids]}
|
||||
(loop [index 0 thumbnails-acc (transient []) full-acc (transient []) font-acc (transient [])]
|
||||
(if (< index total-shapes)
|
||||
(let [shape (nth shapes index)
|
||||
{:keys [thumbnails full]} (set-object shape)]
|
||||
{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||
(recur (inc index)
|
||||
(reduce conj! thumbnails-acc thumbnails)
|
||||
(reduce conj! full-acc full)))
|
||||
{:thumbnails (persistent! thumbnails-acc) :full (persistent! full-acc)}))]
|
||||
(process-pending shapes thumbnails full noop-fn)))
|
||||
(reduce conj! full-acc full)
|
||||
(reduce conj! font-acc font-pending-ids)))
|
||||
{:thumbnails (persistent! thumbnails-acc)
|
||||
:full (persistent! full-acc)
|
||||
:font-pending-ids (persistent! font-acc)}))]
|
||||
(process-pending shapes thumbnails full font-pending-ids noop-fn)))
|
||||
|
||||
(defn- process-shapes-chunk
|
||||
"Process shapes starting at `start-index` until the time budget is exhausted.
|
||||
Returns {:thumbnails [...] :full [...] :next-index n}"
|
||||
[shapes start-index thumbnails-acc full-acc]
|
||||
Returns {:thumbnails [...] :full [...] :font-pending-ids [...] :next-index n}"
|
||||
[shapes start-index thumbnails-acc full-acc font-pending-acc]
|
||||
(let [total (count shapes)
|
||||
deadline (+ (js/performance.now) CHUNK_TIME_BUDGET_MS)]
|
||||
(loop [index start-index
|
||||
t-acc (transient thumbnails-acc)
|
||||
f-acc (transient full-acc)]
|
||||
f-acc (transient full-acc)
|
||||
fp-acc (transient font-pending-acc)]
|
||||
(if (and (< index total)
|
||||
;; Check performance.now every 8 shapes to reduce overhead
|
||||
(or (pos? (bit-and (- index start-index) 7))
|
||||
(<= (js/performance.now) deadline)))
|
||||
(let [shape (nth shapes index)
|
||||
{:keys [thumbnails full]} (set-object shape)]
|
||||
{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||
(recur (inc index)
|
||||
(reduce conj! t-acc thumbnails)
|
||||
(reduce conj! f-acc full)))
|
||||
(reduce conj! f-acc full)
|
||||
(reduce conj! fp-acc font-pending-ids)))
|
||||
{:thumbnails (persistent! t-acc)
|
||||
:full (persistent! f-acc)
|
||||
:font-pending-ids (persistent! fp-acc)
|
||||
:next-index index}))))
|
||||
|
||||
(defn- set-objects-async
|
||||
@ -1531,16 +1556,16 @@
|
||||
(let [total-shapes (count shapes)]
|
||||
(p/create
|
||||
(fn [resolve _reject]
|
||||
(letfn [(process-next-chunk [index thumbnails-acc full-acc]
|
||||
(letfn [(process-next-chunk [index thumbnails-acc full-acc font-pending-acc]
|
||||
(if (< index total-shapes)
|
||||
;; Process one time-budgeted chunk
|
||||
(let [{:keys [thumbnails full next-index]}
|
||||
(let [{:keys [thumbnails full font-pending-ids next-index]}
|
||||
(process-shapes-chunk shapes index
|
||||
thumbnails-acc full-acc)]
|
||||
thumbnails-acc full-acc font-pending-acc)]
|
||||
;; Yield to browser, then continue with next chunk
|
||||
(-> (yield-to-browser)
|
||||
(p/then (fn [_]
|
||||
(process-next-chunk next-index thumbnails full)))))
|
||||
(process-next-chunk next-index thumbnails full font-pending-ids)))))
|
||||
;; All chunks done - finalize
|
||||
(do
|
||||
(perf/end-measure "set-objects")
|
||||
@ -1587,13 +1612,11 @@
|
||||
(rx/reduce conj [])))
|
||||
(rx/subs!
|
||||
(fn [_]
|
||||
(let [text-ids (into [] (comp (filter cfh/text-shape?) (map :id)) shapes)]
|
||||
(when (seq text-ids)
|
||||
(update-text-layouts text-ids)))
|
||||
(relayout-after-fonts! shapes font-pending-acc)
|
||||
(request-render "images-loaded"))
|
||||
noop-fn
|
||||
noop-fn)))))))]
|
||||
(process-next-chunk 0 [] []))))))
|
||||
(process-next-chunk 0 [] [] []))))))
|
||||
|
||||
|
||||
;; This is a version of process-pending that doesn't have sideffects
|
||||
@ -1646,22 +1669,25 @@
|
||||
"Synchronously process all shapes (for small shape counts)."
|
||||
[shapes render-callback on-shapes-ready]
|
||||
(let [total-shapes (count shapes)
|
||||
{:keys [thumbnails full]}
|
||||
(loop [index 0 thumbnails-acc (transient []) full-acc (transient [])]
|
||||
{:keys [thumbnails full font-pending-ids]}
|
||||
(loop [index 0 thumbnails-acc (transient []) full-acc (transient []) font-acc (transient [])]
|
||||
(if (< index total-shapes)
|
||||
(let [shape (nth shapes index)
|
||||
{:keys [thumbnails full]} (set-object shape)]
|
||||
{:keys [thumbnails full font-pending-ids]} (set-object shape)]
|
||||
(recur (inc index)
|
||||
(reduce conj! thumbnails-acc thumbnails)
|
||||
(reduce conj! full-acc full)))
|
||||
{:thumbnails (persistent! thumbnails-acc) :full (persistent! full-acc)}))]
|
||||
(reduce conj! full-acc full)
|
||||
(reduce conj! font-acc font-pending-ids)))
|
||||
{:thumbnails (persistent! thumbnails-acc)
|
||||
:full (persistent! full-acc)
|
||||
:font-pending-ids (persistent! font-acc)}))]
|
||||
(perf/end-measure "set-objects")
|
||||
(when on-shapes-ready (on-shapes-ready))
|
||||
;; Rebuild the tile index so _render knows which shapes
|
||||
;; map to which tiles after a page switch.
|
||||
(h/call wasm/internal-module "_set_view_end")
|
||||
(reset! view-interaction-active? false)
|
||||
(process-pending shapes thumbnails full
|
||||
(process-pending shapes thumbnails full font-pending-ids
|
||||
(fn []
|
||||
(if render-callback
|
||||
(render-callback)
|
||||
|
||||
@ -119,6 +119,16 @@
|
||||
(aget shape-id-buffer 2)
|
||||
(aget shape-id-buffer 3)))))
|
||||
|
||||
(defn force-update-text-layout
|
||||
[id]
|
||||
(when wasm/context-initialized?
|
||||
(let [shape-id-buffer (uuid/get-u32 id)]
|
||||
(h/call wasm/internal-module "_force_update_shape_text_layout_for"
|
||||
(aget shape-id-buffer 0)
|
||||
(aget shape-id-buffer 1)
|
||||
(aget shape-id-buffer 2)
|
||||
(aget shape-id-buffer 3)))))
|
||||
|
||||
;; IMPORTANT: Only TTF fonts can be stored.
|
||||
(defn- store-font-buffer
|
||||
[font-data font-array-buffer emoji? fallback?]
|
||||
|
||||
@ -250,13 +250,15 @@
|
||||
(api/set-shape-svg-raw-content (api/get-static-markup shape))
|
||||
|
||||
(cfh/text-shape? shape)
|
||||
(let [pending-thumbnails (into [] (concat (api/set-shape-text-content id v)))
|
||||
pending-full (into [] (concat (api/set-shape-text-images id v)))]
|
||||
(let [text-content-pending (api/set-shape-text-content id v)
|
||||
pending-thumbnails (vec text-content-pending)
|
||||
pending-full (vec (api/set-shape-text-images id v))
|
||||
font-pending-ids (when (some :callback text-content-pending) [id])]
|
||||
;; FIXME: this is a hack to process the pending tasks
|
||||
;; asynchronously we should probably modify set-wasm-attr!
|
||||
;; to return a list of callbacks to be executed in a
|
||||
;; second pass.
|
||||
(api/process-pending [shape] pending-thumbnails pending-full api/noop-fn)
|
||||
(api/process-pending [shape] pending-thumbnails pending-full font-pending-ids api/noop-fn)
|
||||
nil))
|
||||
|
||||
:grow-type
|
||||
|
||||
@ -42,8 +42,7 @@
|
||||
(let [attrs (or attrs [])
|
||||
value-empty? (fn [v]
|
||||
(or (nil? v)
|
||||
(and (string? v) (empty? v))
|
||||
(and (coll? v) (empty? v))))]
|
||||
(and (string? v) (empty? v))))]
|
||||
(reduce (fn [acc key]
|
||||
(let [style (.-style element)
|
||||
value (if (contains? styles/mapping key)
|
||||
|
||||
@ -14,6 +14,12 @@ export default {
|
||||
"at-rule-no-unknown": null,
|
||||
"declaration-property-value-no-unknown": null,
|
||||
"property-no-unknown": [true, { ignoreProperties: ["text-box"] }],
|
||||
"declaration-block-no-redundant-longhand-properties": [
|
||||
true,
|
||||
{
|
||||
ignoreShorthands: ["grid-template"],
|
||||
},
|
||||
],
|
||||
"selector-pseudo-class-no-unknown": [
|
||||
true,
|
||||
{ ignorePseudoClasses: ["global"] }, // TODO: Avoid global selector usage and remove this exception
|
||||
|
||||
@ -11,7 +11,8 @@
|
||||
[app.plugins.api :as api]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.state :as ths]
|
||||
[frontend-tests.helpers.wasm :as thw]))
|
||||
[frontend-tests.helpers.wasm :as thw]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn- flows-of
|
||||
"The vals of the current page flows from the store."
|
||||
@ -80,3 +81,27 @@
|
||||
(.addInteraction board1 "click" #js {:type "open-url" :url "https://example.com"})
|
||||
(t/is (empty? (flows-of store context page))
|
||||
"open-url interactions do not create a flow"))))))
|
||||
|
||||
(def ^:private plugin-id "00000000-0000-0000-0000-000000000000")
|
||||
|
||||
(defn- throws?
|
||||
[thunk]
|
||||
(try (thunk) false (catch :default _ true)))
|
||||
|
||||
(t/deftest interaction-delay-accepts-zero
|
||||
;; Regression: the InteractionProxy `:delay` setter rejected 0 via
|
||||
;; `(not (pos? value))`, but the model (`set-delay` -> `check-safe-int`) allows
|
||||
;; 0 (an immediate after-delay interaction). With `throwValidationErrors`
|
||||
;; enabled, setting 0 must NOT throw (its validation guard passes), while a
|
||||
;; negative value must still be rejected.
|
||||
(thw/with-wasm-mocks*
|
||||
(fn []
|
||||
(let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1))
|
||||
^js context (api/create-context plugin-id)
|
||||
_ (set! st/state store)
|
||||
^js board1 (.createBoard context)
|
||||
^js board2 (.createBoard context)
|
||||
^js inter (.addInteraction board1 "after-delay" #js {:type "navigate-to" :destination board2} 300)]
|
||||
(ptk/emit! store #(assoc-in % [:plugins :flags plugin-id :throw-validation-errors] true))
|
||||
(t/is (not (throws? #(set! (.-delay inter) 0))) "delay = 0 must be accepted")
|
||||
(t/is (throws? #(set! (.-delay inter) -1)) "negative delay must be rejected")))))
|
||||
|
||||
@ -269,3 +269,50 @@
|
||||
(doseq [[label thunk] (setter-specs m)]
|
||||
(t/is (not (throws? thunk)) (str label " must be allowed on the active page")))))))
|
||||
|
||||
(t/deftest test-layout-gap-padding-accepts-fractional-values
|
||||
;; Regression: the flex/grid gap and padding setters validated with
|
||||
;; `valid-safe-int?`, but the layout model types `:row-gap`/`:column-gap` and
|
||||
;; `:p1`-`:p4` as `safe-number` (and the sidebar accepts decimals), so a
|
||||
;; fractional value was wrongly rejected. With `throwValidationErrors` on (set
|
||||
;; by `setup`) and page2 active, a fractional value must be accepted (no throw).
|
||||
(thw/with-wasm-mocks*
|
||||
(fn []
|
||||
(let [{:keys [store page2-id ^js flex ^js grid]} (setup)]
|
||||
(activate-page! store page2-id)
|
||||
(doseq [[label thunk]
|
||||
[["flex.rowGap" #(set! (.-rowGap flex) 10.5)]
|
||||
["flex.columnGap" #(set! (.-columnGap flex) 3.25)]
|
||||
["flex.verticalPadding" #(set! (.-verticalPadding flex) 4.5)]
|
||||
["flex.horizontalPadding" #(set! (.-horizontalPadding flex) 4.5)]
|
||||
["flex.topPadding" #(set! (.-topPadding flex) 1.5)]
|
||||
["flex.rightPadding" #(set! (.-rightPadding flex) 1.5)]
|
||||
["flex.bottomPadding" #(set! (.-bottomPadding flex) 1.5)]
|
||||
["flex.leftPadding" #(set! (.-leftPadding flex) 1.5)]
|
||||
["grid.rowGap" #(set! (.-rowGap grid) 7.5)]
|
||||
["grid.columnGap" #(set! (.-columnGap grid) 2.25)]
|
||||
["grid.verticalPadding" #(set! (.-verticalPadding grid) 4.5)]
|
||||
["grid.horizontalPadding" #(set! (.-horizontalPadding grid) 4.5)]
|
||||
["grid.topPadding" #(set! (.-topPadding grid) 1.5)]
|
||||
["grid.rightPadding" #(set! (.-rightPadding grid) 1.5)]
|
||||
["grid.bottomPadding" #(set! (.-bottomPadding grid) 1.5)]
|
||||
["grid.leftPadding" #(set! (.-leftPadding grid) 1.5)]]]
|
||||
(t/is (not (throws? thunk)) (str label " must accept a fractional value")))))))
|
||||
|
||||
(t/deftest test-border-radius-accepts-fractional-values
|
||||
;; Regression: the ShapeProxy borderRadius setters validated with
|
||||
;; `valid-safe-int?`, but the model types `:r1`-`:r4` as `safe-number` (and the
|
||||
;; radius sidebar input has min 0, not integer-only), so a fractional radius was
|
||||
;; wrongly rejected. With `throwValidationErrors` on and page2 active, a
|
||||
;; fractional radius must be accepted (no throw).
|
||||
(thw/with-wasm-mocks*
|
||||
(fn []
|
||||
(let [{:keys [store page2-id ^js rect]} (setup)]
|
||||
(activate-page! store page2-id)
|
||||
(doseq [[label thunk]
|
||||
[["borderRadius" #(set! (.-borderRadius rect) 7.5)]
|
||||
["borderRadiusTopLeft" #(set! (.-borderRadiusTopLeft rect) 2.5)]
|
||||
["borderRadiusTopRight" #(set! (.-borderRadiusTopRight rect) 2.5)]
|
||||
["borderRadiusBottomRight" #(set! (.-borderRadiusBottomRight rect) 2.5)]
|
||||
["borderRadiusBottomLeft" #(set! (.-borderRadiusBottomLeft rect) 2.5)]]]
|
||||
(t/is (not (throws? thunk)) (str label " must accept a fractional value")))))))
|
||||
|
||||
|
||||
@ -7,9 +7,31 @@
|
||||
(ns frontend-tests.plugins.parser-test
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.grid :as ctg]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.plugins.parser :as parser]
|
||||
[cljs.test :as t :include-macros true]))
|
||||
|
||||
(defn- overlay-action
|
||||
[{:keys [type destination position manual-position-location]}]
|
||||
(let [action (js-obj "type" type
|
||||
"destination" (js-obj "$id" destination))]
|
||||
(when (some? position)
|
||||
(unchecked-set action "position" position))
|
||||
(when (some? manual-position-location)
|
||||
(unchecked-set action "manualPositionLocation" manual-position-location))
|
||||
action))
|
||||
|
||||
(defn- parse-overlay-interaction
|
||||
[action]
|
||||
(parser/parse-interaction "click" (overlay-action action) nil))
|
||||
|
||||
(defn- valid-interaction?
|
||||
[interaction]
|
||||
(sm/validate ctsi/schema:interaction interaction))
|
||||
|
||||
(t/deftest test-parse-point-returns-gpt-point-record
|
||||
;; Regression test for issue #8409.
|
||||
;;
|
||||
@ -32,18 +54,6 @@
|
||||
(t/is (= 0 (:x result)))
|
||||
(t/is (= 0 (:y result))))))
|
||||
|
||||
(t/deftest test-parse-overlay-action-defaults-manual-position
|
||||
(let [destination #js {"$id" (random-uuid)}
|
||||
action (parser/parse-action
|
||||
#js {:type "open-overlay"
|
||||
:destination destination
|
||||
:position "center"})]
|
||||
(t/is (= :open-overlay (:action-type action)))
|
||||
(t/is (= :center (:overlay-pos-type action)))
|
||||
(t/is (gpt/point? (:overlay-position action)))
|
||||
(t/is (= 0 (:x (:overlay-position action))))
|
||||
(t/is (= 0 (:y (:overlay-position action))))))
|
||||
|
||||
(t/deftest test-parse-frame-guide-calls-guide-parser
|
||||
(let [column (parser/parse-frame-guide
|
||||
#js {:type "column"
|
||||
@ -61,3 +71,105 @@
|
||||
(t/is (= :row (:type row)))
|
||||
(t/is (= false (:display row)))
|
||||
(t/is (= :center (get-in row [:params :type])))))
|
||||
|
||||
(t/deftest test-parse-frame-guides
|
||||
;; Regression test for issue #9773.
|
||||
;;
|
||||
;; `parse-frame-guide` returned the parser fns for column/row instead of
|
||||
;; calling them with the guide, and the `board.guides` setter validated
|
||||
;; against an unregistered `::ctg/grid` reference (now `ctg/schema:grid`).
|
||||
;; Parsed guides must be plain maps that validate against the same direct
|
||||
;; schema the setter uses, and clearing (empty input) must validate too.
|
||||
(let [column #js {:type "column" :display true
|
||||
:params #js {:color #js {:color "#DE4762" :opacity 0.2}
|
||||
:type "stretch" :size 12 :gutter 16 :margin 16}}
|
||||
square #js {:type "square" :display true
|
||||
:params #js {:color #js {:color "#DE4762" :opacity 0.2} :size 8}}
|
||||
parsed (parser/parse-frame-guides #js [column square])]
|
||||
(t/is (= :column (-> parsed first :type)))
|
||||
(t/is (= :square (-> parsed second :type)))
|
||||
(t/is (map? (-> parsed first :params)))
|
||||
(t/is (sm/validate [:vector ctg/schema:grid] parsed)))
|
||||
|
||||
(t/testing "clearing guides with an empty vector validates"
|
||||
(t/is (sm/validate [:vector ctg/schema:grid] (parser/parse-frame-guides #js [])))))
|
||||
|
||||
(t/deftest test-parse-overlay-action-position-is-optional
|
||||
(t/testing "open-overlay defaults omitted position to center"
|
||||
(let [destination (uuid/next)
|
||||
result (parse-overlay-interaction {:type "open-overlay"
|
||||
:destination destination})]
|
||||
(t/is (= :open-overlay (:action-type result)))
|
||||
(t/is (= :click (:event-type result)))
|
||||
(t/is (= destination (:destination result)))
|
||||
(t/is (= :center (:overlay-pos-type result)))
|
||||
(t/is (not (contains? result :overlay-position)))
|
||||
(t/is (valid-interaction? result))))
|
||||
|
||||
(t/testing "toggle-overlay preserves manualPositionLocation"
|
||||
(let [destination (uuid/next)
|
||||
result (parse-overlay-interaction
|
||||
{:type "toggle-overlay"
|
||||
:destination destination
|
||||
:position "manual"
|
||||
:manual-position-location #js {:x 10 :y 20}})
|
||||
position (:overlay-position result)]
|
||||
(t/is (= :toggle-overlay (:action-type result)))
|
||||
(t/is (= :manual (:overlay-pos-type result)))
|
||||
(t/is (gpt/point? position))
|
||||
(t/is (= 10 (:x position)))
|
||||
(t/is (= 20 (:y position)))
|
||||
(t/is (valid-interaction? result))))
|
||||
|
||||
(t/testing "explicit center position does not require manualPositionLocation"
|
||||
(let [destination (uuid/next)
|
||||
result (parse-overlay-interaction {:type "open-overlay"
|
||||
:destination destination
|
||||
:position "center"})]
|
||||
(t/is (= :center (:overlay-pos-type result)))
|
||||
(t/is (not (contains? result :overlay-position)))
|
||||
(t/is (valid-interaction? result))))
|
||||
|
||||
(t/testing "manual position without manualPositionLocation still parses"
|
||||
(let [destination (uuid/next)
|
||||
result (parse-overlay-interaction {:type "open-overlay"
|
||||
:destination destination
|
||||
:position "manual"})]
|
||||
(t/is (= :manual (:overlay-pos-type result)))
|
||||
(t/is (not (contains? result :overlay-position)))
|
||||
(t/is (valid-interaction? result)))))
|
||||
|
||||
(t/deftest test-parse-close-overlay-without-animation-validates
|
||||
(t/testing "close-overlay without animation parses and validates"
|
||||
(let [result (parser/parse-interaction "click" #js {:type "close-overlay"} nil)]
|
||||
(t/is (= {:event-type :click
|
||||
:action-type :close-overlay}
|
||||
result))
|
||||
(t/is (false? (contains? result :animation)))
|
||||
(t/is (true? (sm/validate ctsi/schema:interaction result)))))
|
||||
|
||||
(t/testing "close-overlay preserves destination without animation"
|
||||
(let [destination-id (uuid/next)
|
||||
result (parser/parse-interaction
|
||||
"click"
|
||||
#js {:type "close-overlay"
|
||||
:destination #js {"$id" destination-id}}
|
||||
nil)]
|
||||
(t/is (= destination-id (:destination result)))
|
||||
(t/is (false? (contains? result :animation)))
|
||||
(t/is (true? (sm/validate ctsi/schema:interaction result)))))
|
||||
|
||||
(t/testing "close-overlay preserves an explicit dissolve animation"
|
||||
(let [result (parser/parse-interaction
|
||||
"click"
|
||||
#js {:type "close-overlay"
|
||||
:animation #js {:type "dissolve"
|
||||
:duration 300
|
||||
:easing "linear"}}
|
||||
nil)]
|
||||
(t/is (= {:animation-type :dissolve
|
||||
:duration 300
|
||||
:easing :linear}
|
||||
(:animation result)))
|
||||
(t/is (true? (sm/validate ctsi/schema:interaction result))))))
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
[app.common.test-helpers.ids-map :as cthi]
|
||||
[app.common.test-helpers.tokens :as ctht]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.tokenscript :as ts]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
[app.main.store :as st]
|
||||
@ -338,3 +339,66 @@
|
||||
result (get-resolved-value token {(:name token) token})]
|
||||
(t/is (array? result))
|
||||
(t/is (= ["Inter" "Arial"] (vec result)))))
|
||||
|
||||
(t/deftest token-theme-add-set-accepts-token-set-id
|
||||
(let [plugin-id "plugin-id"
|
||||
file-id (uuid/next)
|
||||
theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
token-set (ctob/make-token-set :id set-id :name "Core")
|
||||
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
||||
emitted (atom [])
|
||||
invalid (atom [])]
|
||||
(with-redefs [u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
|
||||
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
||||
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
||||
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
||||
st/emit! (fn ([event] (swap! emitted conj event) nil)
|
||||
([event & _] (swap! emitted conj event) nil))]
|
||||
(let [theme-proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||
(.addSet theme-proxy (str set-id))
|
||||
(t/is (= #{"Core"} (-> @emitted first :theme :sets)))
|
||||
(t/is (empty? @invalid))))))
|
||||
|
||||
(t/deftest token-theme-add-set-accepts-token-set-proxy
|
||||
(let [plugin-id "plugin-id"
|
||||
file-id (uuid/next)
|
||||
theme-id (uuid/next)
|
||||
set-id (uuid/next)
|
||||
token-set (ctob/make-token-set :id set-id :name "Core")
|
||||
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
||||
emitted (atom [])
|
||||
invalid (atom [])]
|
||||
(with-redefs [u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
|
||||
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
||||
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
||||
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
||||
st/emit! (fn ([event] (swap! emitted conj event) nil)
|
||||
([event & _] (swap! emitted conj event) nil))]
|
||||
(let [theme-proxy (ptok/token-theme-proxy plugin-id file-id theme-id)
|
||||
set-proxy (ptok/token-set-proxy plugin-id file-id set-id "Core")]
|
||||
(.addSet theme-proxy set-proxy)
|
||||
(t/is (= #{"Core"} (-> @emitted first :theme :sets)))
|
||||
(t/is (empty? @invalid))))))
|
||||
|
||||
(t/deftest token-theme-add-set-rejects-invalid-arguments
|
||||
(let [plugin-id "plugin-id"
|
||||
file-id (uuid/next)
|
||||
theme-id (uuid/next)
|
||||
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
|
||||
emitted (atom [])
|
||||
invalid (atom [])]
|
||||
(with-redefs [u/locate-token-set (constantly nil)
|
||||
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
|
||||
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
|
||||
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
|
||||
st/emit! (fn ([event] (swap! emitted conj event) nil)
|
||||
([event & _] (swap! emitted conj event) nil))]
|
||||
(let [theme-proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
|
||||
;; Non-id, non-proxy arguments are rejected by the schema coercer.
|
||||
(.addSet theme-proxy 42)
|
||||
(.removeSet theme-proxy nil)
|
||||
(t/is (empty? @emitted))
|
||||
(t/is (= 2 (count @invalid)))
|
||||
(t/is (every? #(= :error (first %)) @invalid))))))
|
||||
|
||||
|
||||
@ -54,3 +54,36 @@
|
||||
;; No validation errors -> no output (callers join with ". " and would
|
||||
;; otherwise emit an empty string, which is fine).
|
||||
(t/is (empty? (flatten-error-map {}))))
|
||||
|
||||
;; ---------------------------------------------------------------------
|
||||
;; Issue #9692 — `handle-error` must surface a useful message instead of a
|
||||
;; bare "Value not valid. Code: :error".
|
||||
;;
|
||||
;; `not-valid` is redefined to capture the rendered message directly, so the
|
||||
;; assertions don't depend on `st/state` or the console.
|
||||
|
||||
(t/deftest test-handle-error-plain-js-error
|
||||
;; A plain JS error has no `::sm/explain` and CLJS `ex-data` returns nil, so
|
||||
;; the handler must fall back to the error's own message rather than nil.
|
||||
(let [captured (atom ::none)]
|
||||
(with-redefs [plugins.utils/not-valid (fn [_plugin-id _code value]
|
||||
(reset! captured value) nil)]
|
||||
((plugins.utils/handle-error #uuid "00000000-0000-0000-0000-000000000000")
|
||||
(js/Error. "boom: not a function")))
|
||||
(t/is (= "boom: not a function" @captured))))
|
||||
|
||||
(t/deftest test-handle-error-empty-explain
|
||||
;; An explain whose errors don't render any message must not produce an
|
||||
;; empty string; the handler falls back to the raw explain.
|
||||
(let [captured (atom ::none)
|
||||
cause (ex-info "invalid" {:app.common.schema/explain {:errors [] :value 1}})]
|
||||
(with-redefs [plugins.utils/not-valid (fn [_plugin-id _code value]
|
||||
(reset! captured value) nil)]
|
||||
((plugins.utils/handle-error #uuid "00000000-0000-0000-0000-000000000000") cause))
|
||||
(t/is (string? @captured))
|
||||
(t/is (not= "" @captured))))
|
||||
|
||||
(t/deftest test-error-messages-empty-returns-nil
|
||||
;; `error-messages` returns nil (not "") on an explain with no mappable
|
||||
;; errors, so `handle-error` can distinguish "no message" from a real one.
|
||||
(t/is (nil? (plugins.utils/error-messages {:errors []}))))
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
(let [shapes [(make-shape :text) (make-shape :text) (make-shape :rect)]
|
||||
visited-ids (atom [])
|
||||
mock-set (fn [s] (swap! visited-ids conj (:id s)) {:thumbnails [] :full []})
|
||||
mock-pend (fn [_sh _t _f _cb] nil)]
|
||||
mock-pend (fn [_sh _t _f _fp _cb] nil)]
|
||||
|
||||
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
(let [shapes [(make-shape :text) (make-shape :text)]
|
||||
captured (atom nil)
|
||||
mock-set (fn [_s] {:thumbnails [] :full []})
|
||||
mock-pend (fn [sh t f cb] (reset! captured {:shapes sh :thumbnails t :full f :cb cb}))]
|
||||
mock-pend (fn [sh t f _fp cb] (reset! captured {:shapes sh :thumbnails t :full f :cb cb}))]
|
||||
|
||||
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
||||
|
||||
@ -99,7 +99,7 @@
|
||||
{:thumbnails [] :full []}))
|
||||
|
||||
mock-pend
|
||||
(fn [sh t f _cb] (reset! captured {:shapes sh :thumbnails t :full f}))]
|
||||
(fn [sh t f _fp _cb] (reset! captured {:shapes sh :thumbnails t :full f}))]
|
||||
|
||||
(with-mocks* mock-set mock-pend #(wasm.api/process-objects shapes))
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.61.1",
|
||||
"@types/node": "^26.0.1",
|
||||
"@types/node": "^26.1.0",
|
||||
"@vitest/browser": "^4.1.9",
|
||||
"@vitest/coverage-v8": "^4.1.9",
|
||||
"@vitest/ui": "^4.1.9",
|
||||
@ -25,7 +25,7 @@
|
||||
"jsdom": "^29.1.1",
|
||||
"playwright": "1.61.1",
|
||||
"prettier": "^3.9.4",
|
||||
"vite": "^8.1.1",
|
||||
"vite": "^8.1.2",
|
||||
"vitest": "^4.1.9"
|
||||
},
|
||||
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b"
|
||||
|
||||
@ -378,9 +378,8 @@ msgstr "صدّر التحديد"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "تحميل %s ملفات أساسية (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"ملف أو أكثر تريد تصديرهم يستخدمون مكتبات مشتركة. ماذا تريد أن تفعل في "
|
||||
"أصولهم*؟"
|
||||
|
||||
@ -367,9 +367,8 @@ msgstr "Selecció d'exportació"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Baixa %s fitxers estàndard (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Un o més fitxers que voleu exportar utilitzen biblioteques compartides. Què "
|
||||
"voleu fer amb els seus recursos*?"
|
||||
|
||||
@ -462,9 +462,8 @@ msgstr "Výběr exportu"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Stáhnout %s soubory (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Jeden nebo více souborů, které chcete exportovat, používá sdílené knihovny. "
|
||||
"Co chcete dělat s jejich položkami*?"
|
||||
@ -6309,9 +6308,6 @@ msgstr "Momentálně nemáte žádné motivy."
|
||||
msgid "workspace.tokens.original-value"
|
||||
msgstr "Původní hodnota: %s"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Reference není platná nebo není v žádné aktivní sadě"
|
||||
|
||||
#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:59, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:87, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:105, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:123
|
||||
#, fuzzy
|
||||
|
||||
@ -589,9 +589,8 @@ msgstr "Auswahl exportieren"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "%s Standarddateien herunterladen (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Eine oder mehrere Dateien, die Sie exportieren möchten, verwenden geteilte "
|
||||
"Bibliotheken. Was möchten Sie mit den Assets* aus diesen Bibliotheken "
|
||||
@ -1295,10 +1294,6 @@ msgstr "Keine Treffer gefunden."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Token-Liste öffnen"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} ist nicht Teil eines aktiven Sets oder ungültig."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Auth-Provider für dieses Profil nicht erlaubt"
|
||||
@ -7836,10 +7831,6 @@ msgstr "Innenabstände"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Radius"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referenz ist ungültig oder befindet sich nicht in einem aktiven Set"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Geben Sie einen Typografie-Alias für diesen Token ein"
|
||||
|
||||
@ -613,14 +613,7 @@ msgstr "Export selection"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Download %s standard files (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
msgstr ""
|
||||
"One or more files that you want to download are using shared libraries. "
|
||||
"What do you want to do with their assets*?"
|
||||
|
||||
#: src/app/main/ui/dashboard/file_menu.cljs:267
|
||||
#: src/app/main/ui/dashboard/file_menu.cljs:266
|
||||
msgid "dashboard.file-menu.delete-files-permanently-option"
|
||||
msgid_plural "dashboard.file-menu.delete-files-permanently-option"
|
||||
msgstr[0] "Delete file"
|
||||
@ -1471,7 +1464,7 @@ msgstr "Open token list"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} is not in any active set or has an invalid value."
|
||||
msgstr "{%s} token is not in any active set or has an invalid value."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
@ -2070,9 +2063,10 @@ msgid "feedback.type.issue"
|
||||
msgstr "Issue"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:131
|
||||
#, fuzzy
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"One or more files that you want to download are using shared libraries. "
|
||||
"What do you want to do with their assets*?"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-2"
|
||||
@ -4480,10 +4474,6 @@ msgstr "Cancel subscription"
|
||||
msgid "nitrate.subscription.settings.renew-with-code"
|
||||
msgstr "Renew with activation code"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:44, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:110
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr "This token is not in any active set or has an invalid value."
|
||||
|
||||
#: src/app/main/ui/static.cljs:309
|
||||
msgid "not-found.desc-message.doesnt-exist"
|
||||
msgstr "This page doesn't exist"
|
||||
@ -5026,7 +5016,12 @@ msgstr "Penpot"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:42, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:101, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:108
|
||||
msgid "options.deleted-token"
|
||||
msgstr "This token does not exists or has been deleted."
|
||||
msgstr "This token does not exist or has been deleted."
|
||||
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs
|
||||
msgid "options.deleted-token-with-name"
|
||||
msgstr "{%s} token does not exist or has been deleted."
|
||||
|
||||
#: src/app/plugins/utils.cljs:318
|
||||
msgid "plugins.validation.message"
|
||||
@ -9460,7 +9455,7 @@ msgstr "Radius"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Reference is not valid or is not in any active set"
|
||||
msgstr "Reference in {%s} is not valid or is not in any active set."
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
|
||||
@ -610,9 +610,8 @@ msgstr "Exportar selección"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Descargar %s archivos estándar (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Uno o mas ficheros que quieres descargar usan librerias compartidas. ¿Qué "
|
||||
"quieres hacer con los recursos*?"
|
||||
@ -4348,10 +4347,6 @@ msgstr "Cancelar subscripción"
|
||||
msgid "nitrate.subscription.settings.renew-with-code"
|
||||
msgstr "Renovar con código de activación"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:44, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:110
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr "Este token no está disponible en ningún set o tiene un valor inválido."
|
||||
|
||||
#: src/app/main/ui/static.cljs:309
|
||||
msgid "not-found.desc-message.doesnt-exist"
|
||||
msgstr "Esta página no existe"
|
||||
@ -4897,6 +4892,10 @@ msgstr "Penpot"
|
||||
msgid "options.deleted-token"
|
||||
msgstr "Este token no existe o ha sido borrado."
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs
|
||||
msgid "options.deleted-token-with-name"
|
||||
msgstr "El token {%s} no existe o ha sido borrado."
|
||||
|
||||
#: src/app/main/ui/auth/recovery.cljs:88
|
||||
msgid "profile.recovery.go-to-login"
|
||||
msgstr "Ir al login"
|
||||
@ -9165,7 +9164,7 @@ msgstr "Valor original: %s"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "La referencia no es válida o no se encuentra en ningún set activo."
|
||||
msgstr "La referencia en {%s} no es válida o no se encuentra en ningún set activo."
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs
|
||||
#, unused
|
||||
|
||||
@ -375,9 +375,8 @@ msgstr "Selección de exportación"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Descargar %s archivos estándar (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Uno o más archivos que desea exportar utilizan bibliotecas compartidas. "
|
||||
"¿Qué quiere hacer con sus activos*?"
|
||||
|
||||
@ -360,9 +360,8 @@ msgstr "Esportatu aukeraketa"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Deskargatu %s fitxategi estandar (.svn + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Esportatu nahi duzun fitxategi bat edo gehiagok partekatutako liburutegiak "
|
||||
"darabiltzate. Zer egin nahi duzu baliabideekin*?"
|
||||
|
||||
@ -420,9 +420,8 @@ msgstr "انتخاب اکسپورت"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "دانلود %s عدد فایل های استاندارد (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"یک یا چند فایلی که میخواهید اکسپورت کنید از کتابخانههای مشترک استفاده "
|
||||
"میکنند. با داراییهای آنها چه میخواهید بکنید*؟"
|
||||
|
||||
@ -588,9 +588,8 @@ msgstr "Exporter la sélection"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Télécharger %s fichiers standard (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Un ou plusieurs fichiers que vous souhaitez exporter utilisent des "
|
||||
"bibliothèques partagées. Que voulez-vous faire avec leurs ressources ?"
|
||||
@ -1294,10 +1293,6 @@ msgstr "Aucune correspondance."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Ouvrir la liste des tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} n'est pas disponible dans la collection ou le thème actif."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Le fournisseur d'authentification n'est pas autorisé pour ce profil"
|
||||
@ -8119,10 +8114,6 @@ msgstr "Marges intérieures"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Rayons"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "La référence n'est pas valide ou n'est pas dans une collection active"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Entrer un alias de typographie pour un token"
|
||||
|
||||
@ -616,9 +616,8 @@ msgstr "Exporter la sélection"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Télécharger %s fichiers standard (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Un ou plus des fichiers que tu veux télécharger utilisent une bibliothèque "
|
||||
"partagée. Que veux-tu faire avec ces atouts*?"
|
||||
@ -1482,10 +1481,6 @@ msgstr "Aucun résultat trouvé."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Ouvrir la liste de tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} n'est disponible dans aucune collection ou est invalide."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Fournisseur d'authentification non permis pour ce profil utilisateur"
|
||||
@ -4510,10 +4505,6 @@ msgstr "Annuler l'abonnement"
|
||||
msgid "nitrate.subscription.settings.renew-with-code"
|
||||
msgstr "Renouveler avec code d'activation"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:44, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:110
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr "Ce token n'est disponible dans aucun ensemble ou a une valeur invalide."
|
||||
|
||||
#: src/app/main/ui/static.cljs:309
|
||||
msgid "not-found.desc-message.doesnt-exist"
|
||||
msgstr "Cette page n'existe pas"
|
||||
@ -9426,10 +9417,6 @@ msgstr "Marges intérieures"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Rayon"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "La référence est invalide ou n'existe dans aucun ensemble actif"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Entrer un alias de token typographique"
|
||||
|
||||
@ -358,9 +358,8 @@ msgstr "Exportar selección"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Descargar %s ficheiros estándar (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Un ou máis ficheiros dos que queres exportar usan bibliotecas compartidas. "
|
||||
"Que queres facer cos recursos?"
|
||||
|
||||
@ -351,9 +351,8 @@ msgstr "Fitar da zavi"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Sauke %s cikakken kundi (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr "za ka iya fitar da kundi daya ko fiye ta hanyar tura taska. \"me \"*?"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:336
|
||||
|
||||
@ -561,9 +561,8 @@ msgstr "ייצוא הבחירה"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "הורדת %s קבצים תקניים (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"אחד או יותר מהקבצים שברצונך לייצא משתמשים בספריות משותפות. מה לעשות עם "
|
||||
"המשאבים שלהן*?"
|
||||
@ -7994,10 +7993,6 @@ msgstr "ריפודים"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "רדיוס"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "ההפניה לא תקפה או שאינה באף סדרה פעילה"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "נא למלא כינוי לטיפוגרפיית אסימון"
|
||||
|
||||
@ -558,9 +558,8 @@ msgstr "निर्यात चयन"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "%s मानक फ़ाइलें डाउनलोड करें (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"एक या एक से अधिक फ़ाइलें जिन्हें आप निर्यात करना चाहते हैं, वे साझा की गई "
|
||||
"लाइब्रेरीज़ का उपयोग कर रही हैं। आप उनके एसेट्स के साथ क्या करना चाहते हैं?"
|
||||
@ -7929,10 +7928,6 @@ msgstr "पैडिंग्स"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "त्रिज्या"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "संदर्भ मान्य नहीं है या किसी सक्रिय सेट में नहीं है"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "token टाइपोग्राफी उपनाम दर्ज करें"
|
||||
|
||||
@ -461,9 +461,8 @@ msgstr "Izvezi odabir"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Preuzmi %s standardne datoteke (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Jedna ili više datoteka koju želiš izvesti koristi zajedničke biblioteke. "
|
||||
"Što želiš učiniti s njihovim stavkama*?"
|
||||
@ -6330,10 +6329,6 @@ msgstr "Trenutno nemate nijednu temu."
|
||||
msgid "workspace.tokens.original-value"
|
||||
msgstr "Izvorna vrijednost: %s"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referenca nije važeća ili nije ni u jednom aktivnom skupu"
|
||||
|
||||
#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:59, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:87, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:105, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:123
|
||||
#, fuzzy
|
||||
msgid "workspace.tokens.resolved-value"
|
||||
|
||||
@ -494,9 +494,8 @@ msgstr "Ekspor pilihan"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Unduh %s berkas standar (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Satu atau lebih berkas yang ingin Anda ekspor menggunakan pustaka bersama. "
|
||||
"Apa yang ingin Anda lakukan dengan asetnya*?"
|
||||
@ -6675,10 +6674,6 @@ msgstr "Padding"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Radius"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referensi tidak valid atau tidak dalam set aktif mana pun"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs
|
||||
#, unused
|
||||
msgid "workspace.tokens.reference-error"
|
||||
|
||||
@ -615,9 +615,8 @@ msgstr "Esporta selezionati"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Scarica %s file standard (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Uno o più file che desideri scaricare utilizzano librerie condivise. Che "
|
||||
"cosa desideri fare con le loro risorse*?"
|
||||
@ -1485,10 +1484,6 @@ msgstr "Nessuna corrispondenza trovata."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Apri elenco token"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} non è disponibile in nessun set o tema attivo."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Provider di autenticazione non consentito per questo profilo"
|
||||
@ -4041,10 +4036,6 @@ msgstr ""
|
||||
msgid "modals.update-remote-component.message"
|
||||
msgstr "Aggiorna un componente in una libreria condivisa"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:44, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:110
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr "Questo token non è presente in alcun set attivo o ha un valore non valido."
|
||||
|
||||
#: src/app/main/ui/static.cljs:309
|
||||
msgid "not-found.desc-message.doesnt-exist"
|
||||
msgstr "Questa pagina non esiste"
|
||||
@ -8543,10 +8534,6 @@ msgstr "Padding"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Raggio"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Il riferimento non è valido o non è presente in nessun set attivo"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Inserisci un alias tipografico del token"
|
||||
|
||||
@ -554,10 +554,11 @@ msgstr "선택 영역 내보내기"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "표준 파일 %s개(.svg + .json) 다운로드"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
msgstr "다운로드하려는 하나 이상의 파일이 공유 라이브러리를 사용 중입니다. 해당 에셋*을 어떻게 처리하시겠습니까?"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"다운로드하려는 하나 이상의 파일이 공유 라이브러리를 사용 중입니다. 해당 에셋*"
|
||||
"을 어떻게 처리하시겠습니까?"
|
||||
|
||||
#: src/app/main/ui/dashboard/file_menu.cljs:267
|
||||
msgid "dashboard.file-menu.delete-files-permanently-option"
|
||||
@ -1225,10 +1226,6 @@ msgstr "일치하는 항목이 없습니다."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "token 목록 열기"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "이 token은 활성 세트에 없거나 유효하지 않은 값을 가지고 있습니다."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "이 프로필에 허용되지 않는 인증 제공자입니다"
|
||||
@ -7713,10 +7710,6 @@ msgstr "패딩"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "반지름"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "참조가 유효하지 않거나 활성 세트에 없습니다"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "타이포그래피 token 별칭 입력"
|
||||
|
||||
@ -334,9 +334,8 @@ msgstr "Nėra elementų su eksporto nustatymais."
|
||||
msgid "dashboard.export-shapes.title"
|
||||
msgstr "Eksportuoti pažymėtą sritį"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Viename ar keliuose failuose, kuriuos norite eksportuoti, naudojamos "
|
||||
"bendros bibliotekos. Ką norite daryti su jų komponentais*?"
|
||||
|
||||
@ -507,9 +507,8 @@ msgstr "Izgūt atlasi"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Lejupielādēt %s standarta datnes (. svg +. json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Viena vai vairākas lejupielādējamās datnes izmanto koplietojamas "
|
||||
"bibliotēkas. Ko iesākt ar to līdzekļiem*?"
|
||||
@ -7476,10 +7475,6 @@ msgstr "Atbīdes"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Rādiuss"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Atsauce nav derīga vai tā nav nevienā aktīvā kopā"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs
|
||||
#, unused
|
||||
msgid "workspace.tokens.reference-error"
|
||||
|
||||
@ -357,9 +357,8 @@ msgstr "Eksport Pemilihan"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Muat turun %s fail standard (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Satu atau lebih fail yang anda ingin eksport menggunakan perpustakaan "
|
||||
"kongsi. Apa yang anda mahu lakukan dengan aset mereka*?"
|
||||
|
||||
@ -585,9 +585,8 @@ msgstr "Selectie exporteren"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "%s standaardbestanden downloaden (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Een of meer bestanden die je wilt downloaden maken gebruik van gedeelde "
|
||||
"bibliotheken. Wat wil je doen met hun assets*?"
|
||||
@ -1283,10 +1282,6 @@ msgstr "Geen overeenkomsten gevonden."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Lijst met tokens openen"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} is niet beschikbaar in een actieve verzameling of thema."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Auth-provider is niet toegestaan voor dit profiel"
|
||||
@ -8125,10 +8120,6 @@ msgstr "Vulling"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Radius"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referentie is niet geldig of zit niet in een actieve verzameling"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Voer een alias voor tokentypografie in"
|
||||
|
||||
@ -362,9 +362,8 @@ msgstr "Eksportuj wybrane"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Pobierz %s plików standardowych (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Co najmniej jeden plik, który chcesz wyeksportować, korzysta z bibliotek "
|
||||
"udostępnionych. Co chcesz zrobić z ich zasobami*?"
|
||||
|
||||
@ -497,9 +497,8 @@ msgstr "Exportar seleção"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Baixar %s arquivos padrões (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Um ou mais arquivos que você deseja exportar estão usando bibliotecas "
|
||||
"compartilhadas. O que você quer fazer com seus recursos*?"
|
||||
@ -1108,10 +1107,6 @@ msgstr "Nenhum resultado encontrado."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Abrir lista de tokens"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} não está em nenhum conjunto ativo ou possui um valor inválido."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Provedor de autenticação não permitido para este perfil"
|
||||
|
||||
@ -475,9 +475,8 @@ msgstr "Exportar seleção"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Descarregar %s ficheiros standard (svg + json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Um ou mais ficheiros que queres exportar estão a utilizar bibliotecas "
|
||||
"partilhadas. O que queres fazer com os recursos*?"
|
||||
|
||||
@ -506,9 +506,8 @@ msgstr "Exportă selecția"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Descarcă %s fișiere standard (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Unul sau mai multe fișiere pe care dorești să le exporți folosesc "
|
||||
"biblioteci partajate. Ce vrei să faci cu obiectele lor*?"
|
||||
@ -1125,10 +1124,6 @@ msgstr "Nu a fost găsit nimic."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Deschide lista de token-uri"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} nu este în nici un set activ sau are o valoare invalidă."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Furnizor de autentificare neautorizat pentru acest profil"
|
||||
@ -7753,10 +7748,6 @@ msgstr "Margini interioare"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Raze"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referința nu este validă sau nu este în nici unul dintre seturile active"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Introdu un alias de token tipografic"
|
||||
|
||||
@ -579,9 +579,8 @@ msgstr "Выбор экспорта"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Скачать стандартные файлы (.svg + .json) (%s)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Один или несколько файлов на экспорт используют общие библиотеки. Что нужно "
|
||||
"сделать с их ресурсами*?"
|
||||
|
||||
@ -411,9 +411,8 @@ msgstr "Избор извоза"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Преузмите &s стандардних датотека (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Једна или више датотека које желите да извезете користе дељене библиотеке. "
|
||||
"Шта желите да урадите са њиховим средстрвима*?"
|
||||
|
||||
@ -611,9 +611,8 @@ msgstr "Exportera markerade"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Ladda ner %s standardfiler (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"En eller flera filer som du vill exportera använder delade bibliotek. Vad "
|
||||
"vill du göra med deras tillgångar*?"
|
||||
@ -1470,10 +1469,6 @@ msgstr "Inga träffar hittades."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Öppna token-lista"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} är inte i någon aktiv uppsättning eller har ett ogiltigt värde."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Autentiseringsleverantör inte tillåten för denna profil"
|
||||
@ -4478,12 +4473,6 @@ msgstr "Avsluta prenumeration"
|
||||
msgid "nitrate.subscription.settings.renew-with-code"
|
||||
msgstr "Förnya med aktiveringskod"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:44, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:110
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr ""
|
||||
"Denna token finns inte i någon aktiv uppsättning eller har ett ogiltigt "
|
||||
"värde."
|
||||
|
||||
#: src/app/main/ui/static.cljs:309
|
||||
msgid "not-found.desc-message.doesnt-exist"
|
||||
msgstr "Denna sida existerar inte"
|
||||
@ -9373,10 +9362,6 @@ msgstr "Utfyllnader"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Radie"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referensen är inte giltig eller finns inte i någon aktiv uppsättning"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Ange ett alias för token-typografi"
|
||||
|
||||
@ -612,9 +612,8 @@ msgstr "Seçimi dışa aktar"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "%s standart dosyayı indir (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"İndirmek istediğiniz bir veya daha fazla dosya, paylaşılan kütüphaneleri "
|
||||
"kullanıyor. Bunların varlıklarıyla ne yapmak istiyorsunuz*?"
|
||||
@ -1479,10 +1478,6 @@ msgstr "Eşleşme bulunamadı."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Token listesini aç"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr "{%s} herhangi bir etkin kümede bulunmuyor veya geçersiz bir değere sahip."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Kimlik doğrulama sağlayıcısına bu profil için izin verilmiyor"
|
||||
@ -4483,12 +4478,6 @@ msgstr "Aboneliği iptal et"
|
||||
msgid "nitrate.subscription.settings.renew-with-code"
|
||||
msgstr "Etkinleştirme koduyla yenile"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/token_typography_row.cljs:44, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:103, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:110
|
||||
msgid "not-active-token.no-name"
|
||||
msgstr ""
|
||||
"Bu token herhangi bir etkin kümede yer almıyor veya geçersiz bir değere "
|
||||
"sahip."
|
||||
|
||||
#: src/app/main/ui/static.cljs:309
|
||||
msgid "not-found.desc-message.doesnt-exist"
|
||||
msgstr "Bu sayfa yok"
|
||||
@ -9380,10 +9369,6 @@ msgstr "Dolgular"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Yarıçap"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Referans geçerli değil veya herhangi bir etkin kümede bulunmuyor"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Bir token tipografi takma adı girin"
|
||||
|
||||
@ -583,9 +583,8 @@ msgstr "Вибір експорту"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "Завантажити %s стандартних файлів (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr ""
|
||||
"Файли, які Ви хочете експортувати, використовують спільні бібліотеки. Що "
|
||||
"плануєте зробити з їхніми ресурсами*?"
|
||||
@ -1282,12 +1281,6 @@ msgstr "Збігів не виявлено."
|
||||
msgid "ds.inputs.numeric-input.open-token-list-dropdown"
|
||||
msgstr "Відкрити список токенів"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:48
|
||||
msgid "ds.inputs.token-field.no-active-token-option"
|
||||
msgstr ""
|
||||
"Цей токен не міститься в жодному з активних наборів або має недійсне "
|
||||
"значення."
|
||||
|
||||
#: src/app/main/data/auth.cljs:346
|
||||
msgid "errors.auth-provider-not-allowed"
|
||||
msgstr "Провайдер автентифікації не дозволений для цього профілю"
|
||||
@ -8041,10 +8034,6 @@ msgstr "Внутрішні відступи"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "Радіус"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "Посилання помилкове або ні на одному із активних наборів"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/management/forms/typography.cljs:176
|
||||
msgid "workspace.tokens.reference-composite"
|
||||
msgstr "Введіть псевдо токену типографіки"
|
||||
|
||||
@ -472,9 +472,8 @@ msgstr "导出已选中"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "下载 %s 标准文件 (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr "你想导出的一个或多个文件用到了共享库。你想怎么处理它们的素材?"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:336
|
||||
@ -7022,10 +7021,6 @@ msgstr "内边距"
|
||||
msgid "workspace.tokens.radius"
|
||||
msgstr "圆角半径"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "引用无效或不在任何活动集中"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/style_dictionary.cljs
|
||||
#, unused
|
||||
msgid "workspace.tokens.reference-error"
|
||||
|
||||
@ -443,9 +443,8 @@ msgstr "匯出已選取項目"
|
||||
msgid "dashboard.export-standard-multi"
|
||||
msgstr "下載%s個標準檔案 (.svg + .json)"
|
||||
|
||||
#: src/app/main/ui/exports/files.cljs:155
|
||||
#, unused
|
||||
msgid "dashboard.export.explain"
|
||||
#: src/app/main/ui/exports/files.cljs:132
|
||||
msgid "files-download-modal.description-1"
|
||||
msgstr "你想匯出的單個或多個檔案中使用了共用資料庫,你想要如何處理它們的素材*?"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:336
|
||||
@ -6149,10 +6148,6 @@ msgstr "現主時您沒有任何主題。"
|
||||
msgid "workspace.tokens.original-value"
|
||||
msgstr "原始值:%s"
|
||||
|
||||
#: src/app/main/ui/ds/controls/utilities/token_field.cljs:47, src/app/main/ui/workspace/tokens/management/token_pill.cljs:130
|
||||
msgid "workspace.tokens.ref-not-valid"
|
||||
msgstr "參照無效或不在任何啟用的集內"
|
||||
|
||||
#: src/app/main/data/workspace/tokens/warnings.cljs:15, src/app/main/data/workspace/tokens/warnings.cljs:19, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:59, src/app/main/ui/workspace/colorpicker/color_tokens.cljs:87, src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:105, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:296, src/app/main/ui/workspace/tokens/management/forms/controls/color_input.cljs:489, src/app/main/ui/workspace/tokens/management/forms/controls/combobox.cljs:298, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:189, src/app/main/ui/workspace/tokens/management/forms/controls/fonts_combobox.cljs:324, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:259, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:381, src/app/main/ui/workspace/tokens/management/forms/controls/input.cljs:505, src/app/main/ui/workspace/tokens/management/token_pill.cljs:123
|
||||
#, fuzzy
|
||||
msgid "workspace.tokens.resolved-value"
|
||||
|
||||
@ -25,6 +25,6 @@
|
||||
"packageManager": "pnpm@11.9.0+sha512.bd682d5d03fe525ef7c9fd6780c6884d1e756ac4c9c9fe00c538782824310dcf90e3ddc4f53835f06dfaebd5085e41855e0bcbb3b60de2ac5bbab89e5036f03b",
|
||||
"devDependencies": {
|
||||
"concurrently": "^10.0.3",
|
||||
"prettier": "^3.9.1"
|
||||
"prettier": "^3.9.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
"devDependencies": {
|
||||
"cross-env": "^10.1.0",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.1.0",
|
||||
"vite": "^8.1.1",
|
||||
"vite-live-preview": "^0.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,14 +29,14 @@
|
||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.15.1",
|
||||
"express": "^5.1.0",
|
||||
"express": "^5.2.1",
|
||||
"ioredis": "^5.11.1",
|
||||
"js-yaml": "^5.2.0",
|
||||
"nrepl-client": "^0.3.0",
|
||||
"penpot-mcp": "file:..",
|
||||
"pino": "^10.3.1",
|
||||
"pino-loki": "^3.0.0",
|
||||
"pino-pretty": "^13.1.1",
|
||||
"pino-pretty": "^13.1.3",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"sharp": "^0.35.2",
|
||||
"ws": "^8.21.0",
|
||||
@ -47,11 +47,11 @@
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^26.0.1",
|
||||
"@types/ws": "^8.5.10",
|
||||
"@types/ws": "^8.18.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"esbuild": "^0.28.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsx": "^4.22.3",
|
||||
"tsx": "^4.22.4",
|
||||
"typescript": "^6.0.3"
|
||||
},
|
||||
"ts-node": {
|
||||
|
||||
87
mcp/pnpm-lock.yaml
generated
@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^10.0.3
|
||||
version: 10.0.3
|
||||
prettier:
|
||||
specifier: ^3.9.1
|
||||
version: 3.9.1
|
||||
specifier: ^3.9.4
|
||||
version: 3.9.4
|
||||
|
||||
packages/common:
|
||||
devDependencies:
|
||||
@ -37,11 +37,11 @@ importers:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
vite:
|
||||
specifier: ^8.1.0
|
||||
version: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4)
|
||||
specifier: ^8.1.1
|
||||
version: 8.1.1(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4)
|
||||
vite-live-preview:
|
||||
specifier: ^0.4.0
|
||||
version: 0.4.0(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4))
|
||||
version: 0.4.0(vite@8.1.1(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4))
|
||||
|
||||
packages/server:
|
||||
dependencies:
|
||||
@ -55,7 +55,7 @@ importers:
|
||||
specifier: ^0.15.1
|
||||
version: 0.15.1
|
||||
express:
|
||||
specifier: ^5.1.0
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
ioredis:
|
||||
specifier: ^5.11.1
|
||||
@ -76,7 +76,7 @@ importers:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
pino-pretty:
|
||||
specifier: ^13.1.1
|
||||
specifier: ^13.1.3
|
||||
version: 13.1.3
|
||||
reflect-metadata:
|
||||
specifier: ^0.2.2
|
||||
@ -104,7 +104,7 @@ importers:
|
||||
specifier: ^26.0.1
|
||||
version: 26.0.1
|
||||
'@types/ws':
|
||||
specifier: ^8.5.10
|
||||
specifier: ^8.18.1
|
||||
version: 8.18.1
|
||||
cross-env:
|
||||
specifier: ^10.1.0
|
||||
@ -116,7 +116,7 @@ importers:
|
||||
specifier: ^10.9.2
|
||||
version: 10.9.2(@types/node@26.0.1)(typescript@6.0.3)
|
||||
tsx:
|
||||
specifier: ^4.22.3
|
||||
specifier: ^4.22.4
|
||||
version: 4.22.4
|
||||
typescript:
|
||||
specifier: ^6.0.3
|
||||
@ -835,8 +835,8 @@ packages:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
|
||||
es-object-atoms@1.1.2:
|
||||
resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.28.1:
|
||||
@ -942,8 +942,8 @@ packages:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
hasown@2.0.4:
|
||||
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
help-me@5.0.0:
|
||||
@ -1144,8 +1144,8 @@ packages:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
path-to-regexp@8.3.0:
|
||||
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
|
||||
path-to-regexp@8.4.2:
|
||||
resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==}
|
||||
|
||||
penpot-mcp@file:packages:
|
||||
resolution: {directory: packages, type: directory}
|
||||
@ -1184,8 +1184,8 @@ packages:
|
||||
resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
prettier@3.9.1:
|
||||
resolution: {integrity: sha512-ppiDo2CSwexck1eyZUwJHg/N3nf1+6IRCv7W/VJ5vaLnVCmB7+3CdRfMwoCHBBX6xTrREDTksZ4OZl5SSf4zXA==}
|
||||
prettier@3.9.4:
|
||||
resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
@ -1202,8 +1202,8 @@ packages:
|
||||
pump@3.0.4:
|
||||
resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
|
||||
|
||||
qs@6.14.1:
|
||||
resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
|
||||
qs@6.15.3:
|
||||
resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
quick-format-unescaped@4.0.4:
|
||||
@ -1293,8 +1293,8 @@ packages:
|
||||
resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
|
||||
side-channel-list@1.0.1:
|
||||
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
side-channel-map@1.0.1:
|
||||
@ -1305,8 +1305,8 @@ packages:
|
||||
resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
side-channel@1.1.0:
|
||||
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
|
||||
side-channel@1.1.1:
|
||||
resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
sonic-boom@4.2.0:
|
||||
@ -1417,8 +1417,8 @@ packages:
|
||||
peerDependencies:
|
||||
vite: '>=5.4.0'
|
||||
|
||||
vite@8.1.0:
|
||||
resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==}
|
||||
vite@8.1.1:
|
||||
resolution: {integrity: sha512-X/05/cT+VITy2AeDc1der6smvGWWREtL4hPbPTaVbjSBuuWkmNOjR6HP3NzqcQA2nF6VHGUPaFRJyft/2AE9Kg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -1927,7 +1927,7 @@ snapshots:
|
||||
http-errors: 2.0.1
|
||||
iconv-lite: 0.7.2
|
||||
on-finished: 2.4.1
|
||||
qs: 6.14.1
|
||||
qs: 6.15.3
|
||||
raw-body: 3.0.2
|
||||
type-is: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
@ -2034,7 +2034,7 @@ snapshots:
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
es-object-atoms@1.1.1:
|
||||
es-object-atoms@1.1.2:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
|
||||
@ -2108,7 +2108,7 @@ snapshots:
|
||||
once: 1.4.0
|
||||
parseurl: 1.3.3
|
||||
proxy-addr: 2.0.7
|
||||
qs: 6.14.1
|
||||
qs: 6.15.3
|
||||
range-parser: 1.2.1
|
||||
router: 2.2.0
|
||||
send: 1.2.1
|
||||
@ -2160,24 +2160,24 @@ snapshots:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
es-object-atoms: 1.1.1
|
||||
es-object-atoms: 1.1.2
|
||||
function-bind: 1.1.2
|
||||
get-proto: 1.0.1
|
||||
gopd: 1.2.0
|
||||
has-symbols: 1.1.0
|
||||
hasown: 2.0.2
|
||||
hasown: 2.0.4
|
||||
math-intrinsics: 1.1.0
|
||||
|
||||
get-proto@1.0.1:
|
||||
dependencies:
|
||||
dunder-proto: 1.0.1
|
||||
es-object-atoms: 1.1.1
|
||||
es-object-atoms: 1.1.2
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
hasown@2.0.2:
|
||||
hasown@2.0.4:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
@ -2327,7 +2327,7 @@ snapshots:
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
path-to-regexp@8.3.0: {}
|
||||
path-to-regexp@8.4.2: {}
|
||||
|
||||
penpot-mcp@file:packages: {}
|
||||
|
||||
@ -2384,7 +2384,7 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
prettier@3.9.1: {}
|
||||
prettier@3.9.4: {}
|
||||
|
||||
process-warning@5.0.0: {}
|
||||
|
||||
@ -2403,9 +2403,10 @@ snapshots:
|
||||
end-of-stream: 1.4.5
|
||||
once: 1.4.0
|
||||
|
||||
qs@6.14.1:
|
||||
qs@6.15.3:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
es-define-property: 1.0.1
|
||||
side-channel: 1.1.1
|
||||
|
||||
quick-format-unescaped@4.0.4: {}
|
||||
|
||||
@ -2459,7 +2460,7 @@ snapshots:
|
||||
depd: 2.0.0
|
||||
is-promise: 4.0.0
|
||||
parseurl: 1.3.3
|
||||
path-to-regexp: 8.3.0
|
||||
path-to-regexp: 8.4.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@ -2542,7 +2543,7 @@ snapshots:
|
||||
|
||||
shell-quote@1.8.4: {}
|
||||
|
||||
side-channel-list@1.0.0:
|
||||
side-channel-list@1.0.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
object-inspect: 1.13.4
|
||||
@ -2562,11 +2563,11 @@ snapshots:
|
||||
object-inspect: 1.13.4
|
||||
side-channel-map: 1.0.1
|
||||
|
||||
side-channel@1.1.0:
|
||||
side-channel@1.1.1:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
object-inspect: 1.13.4
|
||||
side-channel-list: 1.0.0
|
||||
side-channel-list: 1.0.1
|
||||
side-channel-map: 1.0.1
|
||||
side-channel-weakmap: 1.0.2
|
||||
|
||||
@ -2657,20 +2658,20 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vite-live-preview@0.4.0(vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4)):
|
||||
vite-live-preview@0.4.0(vite@8.1.1(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4)):
|
||||
dependencies:
|
||||
'@seahax/deep-copy': 0.1.0
|
||||
'@seahax/semaphore': 0.5.1
|
||||
'@types/ws': 8.18.1
|
||||
escape-goat: 4.0.0
|
||||
strip-ansi: 7.2.0
|
||||
vite: 8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4)
|
||||
vite: 8.1.1(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4)
|
||||
ws: 8.21.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
vite@8.1.0(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4):
|
||||
vite@8.1.1(@types/node@26.0.1)(esbuild@0.28.1)(tsx@4.22.4):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
picomatch: 4.0.4
|
||||
|
||||
@ -4,6 +4,10 @@ allowBuilds:
|
||||
|
||||
linkWorkspacePackages: true
|
||||
|
||||
minimumReleaseAgeExclude:
|
||||
- qs@6.14.2 || 6.15.2
|
||||
- path-to-regexp@8.4.0
|
||||
|
||||
packages:
|
||||
- "./packages/common"
|
||||
- "./packages/server"
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
- **plugin-types**: Added `fixedWhenScrolling` property for shapes
|
||||
- **plugin-runtime:** `addToken` now resolves references against all token sets, allowing references to tokens in inactive sets
|
||||
- **plugin-types:** `TokenCatalog.addSet` now accepts an optional `active` flag to create an already-active set (sets are inactive by default)
|
||||
- **plugin-types:** `TokenTheme.addSet` and `TokenTheme.removeSet` now accept a token set id (`string`) in addition to a `TokenSet`
|
||||
- **plugin-runtime:** A `fontFamilies` token's `resolvedValue` now returns the documented `string[]` (the resolved family list) instead of leaking the raw tokenscript list symbol
|
||||
|
||||
### 🩹 Fixes
|
||||
|
||||
@ -17,6 +17,6 @@
|
||||
"test:ci:mocked": "pnpm run build:headless && MOCK_BACKEND=1 tsx ci/run-ci.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"playwright": "^1.61.0"
|
||||
"playwright": "^1.61.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,9 +4,9 @@ import type { CommentThread, Page } from '@penpot/plugin-types';
|
||||
import type { TestContext } from '../framework/types';
|
||||
|
||||
// Comments.
|
||||
// Comment threads are created on the current page. Both thread removal APIs are
|
||||
// currently broken (see the dedicated red tests), so cleanup is best-effort to
|
||||
// keep the other assertions meaningful.
|
||||
// Comment threads are created on the current page. Thread/comment removal is
|
||||
// asynchronous (it resolves once the backend delete RPC completes), so callers
|
||||
// must await it; cleanup is best-effort and swallows errors.
|
||||
|
||||
function page(ctx: TestContext): Page {
|
||||
const p = ctx.penpot.currentPage;
|
||||
@ -124,7 +124,7 @@ describe.skipIfMocked('Comments', () => {
|
||||
x: 8,
|
||||
y: 8,
|
||||
});
|
||||
thread.remove();
|
||||
await thread.remove();
|
||||
const threads = await p.findCommentThreads();
|
||||
expect(threads.every((t) => t.seqNumber !== thread.seqNumber)).toBe(true);
|
||||
});
|
||||
|
||||
@ -83,6 +83,21 @@ describe('Interactions', () => {
|
||||
expect(interaction.action.type).toBe('open-overlay');
|
||||
});
|
||||
|
||||
// position is optional; when omitted the overlay defaults to 'center'.
|
||||
test('open-overlay without a position defaults to center', (ctx) => {
|
||||
const overlay = board(ctx);
|
||||
const r = rect(ctx);
|
||||
const interaction = r.addInteraction('click', {
|
||||
type: 'open-overlay',
|
||||
destination: overlay,
|
||||
});
|
||||
expect(interaction.action.type).toBe('open-overlay');
|
||||
if (interaction.action.type === 'open-overlay') {
|
||||
expect(interaction.action.destination.id).toBe(overlay.id);
|
||||
expect(interaction.action.position).toBe('center');
|
||||
}
|
||||
});
|
||||
|
||||
test('toggle-overlay interaction round-trips', (ctx) => {
|
||||
const overlay = board(ctx);
|
||||
const r = rect(ctx);
|
||||
@ -116,6 +131,23 @@ describe('Interactions', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// animation is optional on close-overlay; omitting it closes with no transition.
|
||||
test('close-overlay without an animation round-trips', (ctx) => {
|
||||
const overlay = board(ctx);
|
||||
const r = rect(ctx);
|
||||
const interaction = r.addInteraction('click', {
|
||||
type: 'close-overlay',
|
||||
destination: overlay,
|
||||
});
|
||||
expect(interaction.action.type).toBe('close-overlay');
|
||||
if (interaction.action.type === 'close-overlay') {
|
||||
expect(
|
||||
interaction.action.destination && interaction.action.destination.id,
|
||||
).toBe(overlay.id);
|
||||
expect(interaction.action.animation).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
test('previous-screen interaction round-trips', (ctx) => {
|
||||
const r = rect(ctx);
|
||||
const interaction = r.addInteraction('click', { type: 'previous-screen' });
|
||||
@ -134,6 +166,19 @@ describe('Interactions', () => {
|
||||
expect(interaction.delay).toBeCloseTo(1000, 0);
|
||||
});
|
||||
|
||||
// A zero delay is a valid value (fires immediately), not an error.
|
||||
test('after-delay accepts a zero delay', (ctx) => {
|
||||
const dest = board(ctx);
|
||||
const r = rect(ctx);
|
||||
const interaction = r.addInteraction(
|
||||
'after-delay',
|
||||
{ type: 'navigate-to', destination: dest },
|
||||
0,
|
||||
);
|
||||
expect(interaction.trigger).toBe('after-delay');
|
||||
expect(interaction.delay).toBeCloseTo(0, 0);
|
||||
});
|
||||
|
||||
test('mouse-leave trigger is recorded', (ctx) => {
|
||||
// click / mouse-enter / after-delay are covered above; mouse-leave is the
|
||||
// remaining trigger.
|
||||
@ -167,6 +212,20 @@ describe('Interactions', () => {
|
||||
expect(interaction.action.type).toBe('previous-screen');
|
||||
});
|
||||
|
||||
// The delay setter accepts zero (fires immediately) as a valid value.
|
||||
test('delay setter accepts a zero value', (ctx) => {
|
||||
const dest = board(ctx);
|
||||
const r = rect(ctx);
|
||||
const interaction = r.addInteraction(
|
||||
'after-delay',
|
||||
{ type: 'navigate-to', destination: dest },
|
||||
1000,
|
||||
);
|
||||
|
||||
interaction.delay = 0;
|
||||
expect(interaction.delay).toBeCloseTo(0, 0);
|
||||
});
|
||||
|
||||
describe('Animations', () => {
|
||||
test('dissolve animation round-trips', (ctx) => {
|
||||
const dest = board(ctx);
|
||||
|
||||
@ -65,6 +65,23 @@ describe('Layout', () => {
|
||||
expect(flex.leftPadding).toBeCloseTo(4, 0);
|
||||
});
|
||||
|
||||
// Gap and padding setters accept fractional numbers, not just integers.
|
||||
test('gaps and padding accept fractional values', (ctx) => {
|
||||
const flex = board(ctx).addFlexLayout();
|
||||
flex.rowGap = 5.5;
|
||||
flex.columnGap = 10.25;
|
||||
flex.topPadding = 1.5;
|
||||
flex.rightPadding = 2.25;
|
||||
flex.bottomPadding = 3.75;
|
||||
flex.leftPadding = 4.5;
|
||||
expect(flex.rowGap).toBeCloseTo(5.5, 2);
|
||||
expect(flex.columnGap).toBeCloseTo(10.25, 2);
|
||||
expect(flex.topPadding).toBeCloseTo(1.5, 2);
|
||||
expect(flex.rightPadding).toBeCloseTo(2.25, 2);
|
||||
expect(flex.bottomPadding).toBeCloseTo(3.75, 2);
|
||||
expect(flex.leftPadding).toBeCloseTo(4.5, 2);
|
||||
});
|
||||
|
||||
test('sizing round-trips', (ctx) => {
|
||||
const flex = board(ctx).addFlexLayout();
|
||||
flex.horizontalSizing = 'fix';
|
||||
@ -176,6 +193,23 @@ describe('Layout', () => {
|
||||
expect(grid.columnGap).toBeCloseTo(9, 0);
|
||||
});
|
||||
|
||||
// Gap and padding setters accept fractional numbers, not just integers.
|
||||
test('gaps and padding accept fractional values', (ctx) => {
|
||||
const grid = board(ctx).addGridLayout();
|
||||
grid.rowGap = 7.5;
|
||||
grid.columnGap = 9.25;
|
||||
grid.topPadding = 1.5;
|
||||
grid.rightPadding = 2.75;
|
||||
grid.bottomPadding = 3.25;
|
||||
grid.leftPadding = 4.5;
|
||||
expect(grid.rowGap).toBeCloseTo(7.5, 2);
|
||||
expect(grid.columnGap).toBeCloseTo(9.25, 2);
|
||||
expect(grid.topPadding).toBeCloseTo(1.5, 2);
|
||||
expect(grid.rightPadding).toBeCloseTo(2.75, 2);
|
||||
expect(grid.bottomPadding).toBeCloseTo(3.25, 2);
|
||||
expect(grid.leftPadding).toBeCloseTo(4.5, 2);
|
||||
});
|
||||
|
||||
// Index boundaries — invalid indices must be rejected.
|
||||
test('addRowAtIndex with a negative index throws', (ctx) => {
|
||||
const grid = board(ctx).addGridLayout();
|
||||
|
||||
@ -237,6 +237,21 @@ describe('Shapes', () => {
|
||||
expect(r.borderRadiusBottomRight).toBeCloseTo(3, 0);
|
||||
expect(r.borderRadiusBottomLeft).toBeCloseTo(4, 0);
|
||||
});
|
||||
|
||||
// Border radius setters accept fractional numbers, not just integers.
|
||||
test('border radius accepts fractional values', (ctx) => {
|
||||
const r = rect(ctx);
|
||||
r.borderRadius = 4.5;
|
||||
expect(r.borderRadius).toBeCloseTo(4.5, 2);
|
||||
r.borderRadiusTopLeft = 1.25;
|
||||
r.borderRadiusTopRight = 2.5;
|
||||
r.borderRadiusBottomRight = 3.75;
|
||||
r.borderRadiusBottomLeft = 0.5;
|
||||
expect(r.borderRadiusTopLeft).toBeCloseTo(1.25, 2);
|
||||
expect(r.borderRadiusTopRight).toBeCloseTo(2.5, 2);
|
||||
expect(r.borderRadiusBottomRight).toBeCloseTo(3.75, 2);
|
||||
expect(r.borderRadiusBottomLeft).toBeCloseTo(0.5, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ordering', () => {
|
||||
|
||||
@ -148,6 +148,19 @@ describe('Tokens', () => {
|
||||
theme.removeSet(set);
|
||||
});
|
||||
|
||||
// addSet/removeSet also accept a token set id (string), not just a TokenSet.
|
||||
test('addSet and removeSet accept a set id', async (ctx) => {
|
||||
const cat = catalog(ctx);
|
||||
const theme = cat.addTheme({ group: '', name: unique('theme') });
|
||||
const set = cat.addSet({ name: unique('set'), active: false });
|
||||
theme.addSet(set.id);
|
||||
await sleep(300);
|
||||
expect(theme.activeSets.length).toBeGreaterThan(0);
|
||||
theme.removeSet(set.id);
|
||||
await sleep(300);
|
||||
expect(theme.activeSets.length).toBe(0);
|
||||
});
|
||||
|
||||
test('duplicate and remove a theme', (ctx) => {
|
||||
const theme = catalog(ctx).addTheme({ group: '', name: unique('theme') });
|
||||
const dup = theme.duplicate();
|
||||
|
||||
12
plugins/libs/plugin-types/index.d.ts
vendored
@ -485,9 +485,9 @@ export interface CloseOverlay {
|
||||
readonly destination?: Board;
|
||||
|
||||
/**
|
||||
* Animation displayed with this interaction.
|
||||
* Animation displayed with this interaction. Omit it to close with no transition.
|
||||
*/
|
||||
readonly animation: Animation;
|
||||
readonly animation?: Animation;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5291,13 +5291,17 @@ export interface TokenTheme {
|
||||
|
||||
/**
|
||||
* Adds a set to the list of the theme.
|
||||
*
|
||||
* @param tokenSet a `TokenSet` or the id of a token set.
|
||||
*/
|
||||
addSet(tokenSet: TokenSet): void;
|
||||
addSet(tokenSet: TokenSet | string): void;
|
||||
|
||||
/**
|
||||
* Removes a set from the list of the theme.
|
||||
*
|
||||
* @param tokenSet a `TokenSet` or the id of a token set.
|
||||
*/
|
||||
removeSet(tokenSet: TokenSet): void;
|
||||
removeSet(tokenSet: TokenSet | string): void;
|
||||
|
||||
/**
|
||||
* Adds to the catalog a new TokenTheme equal to this one but with a new id.
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
"name": "@penpot/plugins-runtime",
|
||||
"version": "1.4.2",
|
||||
"dependencies": {
|
||||
"@penpot/plugin-types": "^1.4.2",
|
||||
"@penpot/plugin-types": "workspace:^",
|
||||
"ses": "^2.1.0",
|
||||
"zod": "^3.22.4"
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"module": "./dist/index.js",
|
||||
"typings": "./dist/index.d.ts",
|
||||
|
||||
@ -45,9 +45,9 @@
|
||||
"@types/feather-icons": "^4.29.4",
|
||||
"@types/node": "26.0.1",
|
||||
"@types/yargs": "^17.0.35",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.0",
|
||||
"@typescript-eslint/parser": "8.62.0",
|
||||
"@typescript-eslint/utils": "^8.62.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.62.1",
|
||||
"@typescript-eslint/parser": "8.62.1",
|
||||
"@typescript-eslint/utils": "^8.62.1",
|
||||
"@vitest/coverage-v8": "4.1.9",
|
||||
"@vitest/ui": "4.1.9",
|
||||
"concurrently": "^10.0.3",
|
||||
@ -60,18 +60,18 @@
|
||||
"eslint-plugin-react": "7.37.5",
|
||||
"eslint-plugin-react-hooks": "7.1.1",
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"fs-extra": "^11.3.5",
|
||||
"fs-extra": "^11.3.6",
|
||||
"globals": "^17.7.0",
|
||||
"happy-dom": "^20.10.6",
|
||||
"jiti": "2.7.0",
|
||||
"jsdom": "~29.1.1",
|
||||
"jsonc-eslint-parser": "^3.1.0",
|
||||
"prettier": "^3.9.1",
|
||||
"prettier": "^3.9.4",
|
||||
"tsx": "^4.22.4",
|
||||
"typedoc": "^0.28.19",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "^8.62.0",
|
||||
"vite": "8.1.0",
|
||||
"typescript-eslint": "^8.62.1",
|
||||
"vite": "8.1.1",
|
||||
"vite-plugin-checker": "^0.14.4",
|
||||
"vite-plugin-dts": "5.0.3",
|
||||
"vite-plugin-static-copy": "^4.1.1",
|
||||
|
||||
630
plugins/pnpm-lock.yaml
generated
@ -13,6 +13,22 @@ allowBuilds:
|
||||
|
||||
linkWorkspacePackages: true
|
||||
|
||||
minimumReleaseAgeExclude:
|
||||
- minimatch@10.2.1 || 10.2.3
|
||||
- ajv@8.18.0
|
||||
- lodash@4.17.24
|
||||
- esbuild@0.28.1
|
||||
- '@babel/core@7.29.1'
|
||||
- undici@7.28.0
|
||||
|
||||
overrides:
|
||||
'@babel/core@<=7.29.0': ^7.29.1
|
||||
ajv@>=7.0.0-alpha.0 <8.18.0: ^8.18.0
|
||||
lodash@<=4.17.23: ^4.17.24
|
||||
lodash@>=4.0.0 <=4.17.23: ^4.17.24
|
||||
minimatch@>=10.0.0 <10.2.1: ^10.2.1
|
||||
minimatch@>=10.0.0 <10.2.3: ^10.2.3
|
||||
|
||||
peerDependencyRules:
|
||||
allowedVersions:
|
||||
"eslint-plugin-import>eslint": "10.6.0"
|
||||
|
||||
@ -733,7 +733,11 @@ pub extern "C" fn set_shape_svg_raw_content() -> Result<()> {
|
||||
.map_err(|e| Error::RecoverableError(e.to_string()))?
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
|
||||
let render_state = get_render_state();
|
||||
let font_manager = skia::FontMgr::from(render_state.fonts().font_provider().clone());
|
||||
shape.set_svg_raw_content(svg_raw_content);
|
||||
shape.update_svg_raw_content(font_manager);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
||||