mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
f068842a6c
@ -32,6 +32,9 @@
|
||||
### :sparkles: New features & Enhancements
|
||||
|
||||
- Access Tokens look & feel refinement [Taiga #13114](https://tree.taiga.io/project/penpot/us/13114)
|
||||
- Add MCP server integration [Taiga #13112](https://tree.taiga.io/project/penpot/us/13112), [Taiga #13114](https://tree.taiga.io/project/penpot/us/13114)
|
||||
- Access Tokens look & feel refinement [Taiga #13114](https://tree.taiga.io/project/penpot/us/13114)
|
||||
- Enhance readability of applied tokens in plugins API [Taiga #13714](https://tree.taiga.io/project/penpot/issue/13714)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
@ -43,6 +46,8 @@
|
||||
- Update copy on penpot update message [Taiga #12924](https://tree.taiga.io/project/penpot/issue/12924)
|
||||
- Fix id prop on switch component [Taiga #13534](https://tree.taiga.io/project/penpot/issue/13534)
|
||||
- Fix tooltip shown on tab change [Taiga #13627](https://tree.taiga.io/project/penpot/issue/13627)
|
||||
- Fix tooltip activated when tab change [Taiga #13627](https://tree.taiga.io/project/penpot/issue/13627)
|
||||
|
||||
|
||||
## 2.14.0 (Unreleased)
|
||||
|
||||
|
||||
@ -88,7 +88,6 @@
|
||||
:columns [:id :name :perms :type :created-at :updated-at :expires-at]})
|
||||
(mapv decode-row)))
|
||||
|
||||
|
||||
(def ^:private schema:get-current-mcp-token
|
||||
[:map {:title "get-current-mcp-token"}])
|
||||
|
||||
@ -101,6 +100,7 @@
|
||||
:type "mcp"}
|
||||
{:order-by [[:expires-at :asc] [:created-at :asc]]
|
||||
:columns [:token :expires-at]})
|
||||
(remove #(ct/is-after? (:expires-at %) request-at))
|
||||
(remove #(and (some? (:expires-at %))
|
||||
(ct/is-after? request-at (:expires-at %))))
|
||||
(map decode-row)
|
||||
(first)))
|
||||
|
||||
@ -136,6 +136,8 @@
|
||||
:webhooks
|
||||
;; TODO: deprecate this flag and consolidate the code
|
||||
:render-wasm-dpr
|
||||
;; Show WASM renderer info label (hidden by default).
|
||||
:render-wasm-info
|
||||
:hide-release-modal
|
||||
:subscriptions
|
||||
:subscriptions-old
|
||||
@ -162,7 +164,8 @@
|
||||
;; Activates the nitrate module
|
||||
:nitrate
|
||||
|
||||
:mcp})
|
||||
:mcp
|
||||
:background-blur})
|
||||
|
||||
(def all-flags
|
||||
(set/union email login varia))
|
||||
|
||||
@ -0,0 +1,160 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/objects-map",
|
||||
"text-editor/v2",
|
||||
"render-wasm/v1",
|
||||
"text-editor-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~ud7430f09-4f59-8049-8007-6277bb7586f6",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "test_feature",
|
||||
"~:revn": 39,
|
||||
"~:modified-at": "~m1773253429056",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u93bfc923-66b2-813c-8007-b2725507ba08",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects",
|
||||
"0015-fix-text-attrs-blank-strings",
|
||||
"0015-clean-shadow-color",
|
||||
"0016-copy-fills-from-position-data-to-text-node"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~ud7430f09-4f59-8049-8007-6277bb765abd",
|
||||
"~:created-at": "~m1773229633566",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u93bfc923-66b2-813c-8007-b2725507ba09"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u93bfc923-66b2-813c-8007-b2725507ba09": {
|
||||
"~: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]],\"~: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,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~u11813dac-4dc3-80d8-8007-b2804a521773\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ac\",\"~u11813dac-4dc3-80d8-8007-b28053db9865\",\"~u11813dac-4dc3-80d8-8007-b2806dfd8a36\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a4\",\"~u5b58e018-fa5e-805d-8007-b27294ad5af1\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a7\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14af\",\"~u11813dac-4dc3-80d8-8007-b28053db9864\",\"~u11813dac-4dc3-80d8-8007-b2806dfde19d\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14aa\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ab\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b2806dfd8a36": "[\"~#shape\",[\"^ \",\"~:y\",93.99999856948853,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:index\",2,\"~:name\",\"Group\",\"~:width\",303,\"~:type\",\"~:group\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1307.9999368190765,\"~:y\",93.99999856948853]],[\"^:\",[\"^ \",\"~:x\",1610.9999368190765,\"~:y\",93.99999856948853]],[\"^:\",[\"^ \",\"~:x\",1610.9999368190765,\"~:y\",396.9999985694885]],[\"^:\",[\"^ \",\"~:x\",1307.9999368190765,\"~:y\",396.9999985694885]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b2806dfd8a36\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1307.9999368190765,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1307.9999368190765,\"~:y\",93.99999856948853,\"^6\",303,\"~:height\",303,\"~:x1\",1307.9999368190765,\"~:y1\",93.99999856948853,\"~:x2\",1610.9999368190765,\"~:y2\",396.9999985694885]],\"~:fills\",[],\"~:flip-x\",null,\"^D\",303,\"~:flip-y\",null,\"~:shapes\",[\"~u11813dac-4dc3-80d8-8007-b2806dfd8a37\",\"~u11813dac-4dc3-80d8-8007-b2806dfde19c\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b2806dfd8a37": "[\"~#shape\",[\"^ \",\"~:y\",93.99999856948853,\"~: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\",\"Ellipse\",\"~:width\",194,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1416.9999368190765,\"~:y\",93.99999856948853]],[\"^<\",[\"^ \",\"~:x\",1610.9999368190765,\"~:y\",93.99999856948853]],[\"^<\",[\"^ \",\"~:x\",1610.9999368190765,\"~:y\",293.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1416.9999368190765,\"~:y\",293.9999985694885]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b2806dfd8a37\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b2806dfd8a36\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1416.9999368190765,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1416.9999368190765,\"~:y\",93.99999856948853,\"^8\",194,\"~:height\",200,\"~:x1\",1416.9999368190765,\"~:y1\",93.99999856948853,\"~:x2\",1610.9999368190765,\"~:y2\",293.9999985694885]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#174be9\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",200,\"~:flip-y\",null]]",
|
||||
"~u5b58e018-fa5e-805d-8007-b27294ad5af1": "[\"~#shape\",[\"^ \",\"~:y\",-22.00000122055286,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",445.0000023918125,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",162.9999992063131,\"~:y\",-22.000001220552832]],[\"^<\",[\"^ \",\"~:x\",608.0000015981256,\"~:y\",-22.000001220552832]],[\"^<\",[\"^ \",\"~:x\",608.0000015981256,\"~:y\",423.0000182011825]],[\"^<\",[\"^ \",\"~:x\",162.9999992063131,\"~:y\",423.0000182011825]]],\"~: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,\"~:blur\",[\"^ \",\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2729af5ed71\",\"^9\",\"~:background-blur\",\"~:value\",4,\"~:hidden\",false],\"~:r1\",20,\"^B\",\"~u5b58e018-fa5e-805d-8007-b27294ad5af1\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",162.99999920631308,\"~:proportion\",1,\"~:r4\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",162.99999920631308,\"~:y\",-22.00000122055286,\"^8\",445.0000023918125,\"~:height\",445.0000194217354,\"~:x1\",162.99999920631308,\"~:y1\",-22.00000122055286,\"~:x2\",608.0000015981256,\"~:y2\",423.0000182011825]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",0.30392156862745096]],\"~:flip-x\",null,\"^N\",445.0000194217354,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b2804a521773": "[\"~#shape\",[\"^ \",\"~:y\",49.00000846385956,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:index\",2,\"~:name\",\"Group\",\"~:width\",303,\"~:type\",\"~:group\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",49.00000846385956]],[\"^:\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",49.00000846385956]],[\"^:\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",352.00000846385956]],[\"^:\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",352.00000846385956]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b2804a521773\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",234.00000047683716,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",49.00000846385956,\"^6\",303,\"~:height\",303,\"~:x1\",234.00000047683716,\"~:y1\",49.00000846385956,\"~:x2\",537.0000004768372,\"~:y2\",352.00000846385956]],\"~:fills\",[],\"~:flip-x\",null,\"^D\",303,\"~:flip-y\",null,\"~:shapes\",[\"~u5b58e018-fa5e-805d-8007-b27288b0cd80\",\"~u5b58e018-fa5e-805d-8007-b2728b102fe6\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b2806dfde19c": "[\"~#shape\",[\"^ \",\"~:y\",189.99999856948853,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",206,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1307.9999368190765,\"~:y\",189.99999856948853]],[\"^<\",[\"^ \",\"~:x\",1513.9999368190765,\"~:y\",189.99999856948853]],[\"^<\",[\"^ \",\"~:x\",1513.9999368190765,\"~:y\",396.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1307.9999368190765,\"~:y\",396.9999985694885]]],\"~:r2\",0,\"~: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\",0,\"~:r1\",0,\"~:id\",\"~u11813dac-4dc3-80d8-8007-b2806dfde19c\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b2806dfd8a36\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1307.9999368190765,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1307.9999368190765,\"~:y\",189.99999856948853,\"^8\",206,\"~:height\",207,\"~:x1\",1307.9999368190765,\"~:y1\",189.99999856948853,\"~:x2\",1513.9999368190765,\"~:y2\",396.9999985694885]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#c8f00d\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",207,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b2806dfde19d": "[\"~#shape\",[\"^ \",\"~:y\",-32.00000238488809,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",302.9999596227117,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1307.9999330492521,\"~:y\",-32.00000238488809]],[\"^<\",[\"^ \",\"~:x\",1610.9998926719638,\"~:y\",-32.00000238488809]],[\"^<\",[\"^ \",\"~:x\",1610.9998926719638,\"~:y\",268.00000735984116]],[\"^<\",[\"^ \",\"~:x\",1307.9999330492521,\"~:y\",268.00000735984116]]],\"~: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,\"~:blur\",[\"^ \",\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2729af5ed71\",\"^9\",\"~:background-blur\",\"~:value\",4,\"~:hidden\",false],\"~:r1\",20,\"^B\",\"~u11813dac-4dc3-80d8-8007-b2806dfde19d\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1307.9999330492521,\"~:proportion\",1,\"~:r4\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1307.9999330492521,\"~:y\",-32.00000238488809,\"^8\",302.9999596227117,\"~:height\",300.00000974472925,\"~:x1\",1307.9999330492521,\"~:y1\",-32.00000238488809,\"~:x2\",1610.9998926719638,\"~:y2\",268.00000735984116]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",0.30392156862745096]],\"~:flip-x\",null,\"^N\",300.00000974472925,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14a4": "[\"~#shape\",[\"^ \",\"~:y\",592.0000023841858,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:index\",2,\"~:name\",\"Group\",\"~:width\",303,\"~:type\",\"~:group\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",592.0000023841858]],[\"^:\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",592.0000023841858]],[\"^:\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",895.0000023841858]],[\"^:\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",895.0000023841858]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a4\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",817.9999825954437,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",592.0000023841858,\"^6\",303,\"~:height\",303,\"~:x1\",817.9999825954437,\"~:y1\",592.0000023841858,\"~:x2\",1120.9999825954437,\"~:y2\",895.0000023841858]],\"~:fills\",[],\"~:flip-x\",null,\"^D\",303,\"~:flip-y\",null,\"~:shapes\",[\"~u11813dac-4dc3-80d8-8007-b280a6cd14a5\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a6\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b28053db9864": "[\"~#shape\",[\"^ \",\"~:y\",97.00000524450644,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",299.9999982638824,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",770.9999871603877,\"~:y\",97.00000524450644]],[\"^<\",[\"^ \",\"~:x\",1070.99998542427,\"~:y\",97.00000524450644]],[\"^<\",[\"^ \",\"~:x\",1070.99998542427,\"~:y\",397.0000149892357]],[\"^<\",[\"^ \",\"~:x\",770.9999871603877,\"~:y\",397.0000149892357]]],\"~: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,\"~:blur\",[\"^ \",\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2729af5ed71\",\"^9\",\"~:background-blur\",\"~:value\",4,\"~:hidden\",false],\"~:r1\",20,\"^B\",\"~u11813dac-4dc3-80d8-8007-b28053db9864\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",770.9999871603877,\"~:proportion\",1,\"~:r4\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",770.9999871603877,\"~:y\",97.00000524450644,\"^8\",299.9999982638824,\"~:height\",300.00000974472925,\"~:x1\",770.9999871603877,\"~:y1\",97.00000524450644,\"~:x2\",1070.99998542427,\"~:y2\",397.0000149892357]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",0.30392156862745096]],\"~:flip-x\",null,\"^N\",300.00000974472925,\"~:flip-y\",null]]",
|
||||
"~u5b58e018-fa5e-805d-8007-b2728b102fe6": "[\"~#shape\",[\"^ \",\"~:y\",145.00000846385956,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",206,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",145.00000846385956]],[\"^<\",[\"^ \",\"~:x\",440.00000047683716,\"~:y\",145.00000846385956]],[\"^<\",[\"^ \",\"~:x\",440.00000047683716,\"~:y\",352.00000846385956]],[\"^<\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",352.00000846385956]]],\"~:r2\",0,\"~: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\",0,\"~:r1\",0,\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2728b102fe6\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b2804a521773\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",234.00000047683716,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",145.00000846385956,\"^8\",206,\"~:height\",207,\"~:x1\",234.00000047683716,\"~:y1\",145.00000846385956,\"~:x2\",440.00000047683716,\"~:y2\",352.00000846385956]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#c8f00d\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",207,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14a5": "[\"~#shape\",[\"^ \",\"~:y\",592.0000023841858,\"~: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\",\"Ellipse\",\"~:width\",194,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",926.9999825954437,\"~:y\",592.0000023841858]],[\"^<\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",592.0000023841858]],[\"^<\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",792.0000023841858]],[\"^<\",[\"^ \",\"~:x\",926.9999825954437,\"~:y\",792.0000023841858]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a5\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a4\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",926.9999825954437,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",926.9999825954437,\"~:y\",592.0000023841858,\"^8\",194,\"~:height\",200,\"~:x1\",926.9999825954437,\"~:y1\",592.0000023841858,\"~:x2\",1120.9999825954437,\"~:y2\",792.0000023841858]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#174be9\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",200,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b28053db9865": "[\"~#shape\",[\"^ \",\"~:y\",48.00000238418579,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:index\",2,\"~:name\",\"Group\",\"~:width\",303,\"~:type\",\"~:group\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",48.00000238418579]],[\"^:\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",48.00000238418579]],[\"^:\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",351.0000023841858]],[\"^:\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",351.0000023841858]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b28053db9865\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",817.9999825954437,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",48.00000238418579,\"^6\",303,\"~:height\",303,\"~:x1\",817.9999825954437,\"~:y1\",48.00000238418579,\"~:x2\",1120.9999825954437,\"~:y2\",351.0000023841858]],\"~:fills\",[],\"~:flip-x\",null,\"^D\",303,\"~:flip-y\",null,\"~:shapes\",[\"~u11813dac-4dc3-80d8-8007-b28053db9866\",\"~u11813dac-4dc3-80d8-8007-b28053db9867\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14a6": "[\"~#shape\",[\"^ \",\"~:y\",688.0000023841858,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",206,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",688.0000023841858]],[\"^<\",[\"^ \",\"~:x\",1023.9999825954437,\"~:y\",688.0000023841858]],[\"^<\",[\"^ \",\"~:x\",1023.9999825954437,\"~:y\",895.0000023841858]],[\"^<\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",895.0000023841858]]],\"~:r2\",0,\"~: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\",0,\"~:r1\",0,\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a6\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a4\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",817.9999825954437,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",688.0000023841858,\"^8\",206,\"~:height\",207,\"~:x1\",817.9999825954437,\"~:y1\",688.0000023841858,\"~:x2\",1023.9999825954437,\"~:y2\",895.0000023841858]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#c8f00d\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",207,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b28053db9866": "[\"~#shape\",[\"^ \",\"~:y\",48.00000238418579,\"~: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\",\"Ellipse\",\"~:width\",194,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",926.9999825954437,\"~:y\",48.00000238418579]],[\"^<\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",48.00000238418579]],[\"^<\",[\"^ \",\"~:x\",1120.9999825954437,\"~:y\",248.0000023841858]],[\"^<\",[\"^ \",\"~:x\",926.9999825954437,\"~:y\",248.0000023841858]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b28053db9866\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b28053db9865\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",926.9999825954437,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",926.9999825954437,\"~:y\",48.00000238418579,\"^8\",194,\"~:height\",200,\"~:x1\",926.9999825954437,\"~:y1\",48.00000238418579,\"~:x2\",1120.9999825954437,\"~:y2\",248.0000023841858]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#174be9\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",200,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14a7": "[\"~#shape\",[\"^ \",\"~:y\",637.9999985694885,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:index\",2,\"~:name\",\"Group\",\"~:width\",303,\"~:type\",\"~:group\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1308.000058889389,\"~:y\",637.9999985694885]],[\"^:\",[\"^ \",\"~:x\",1611.000058889389,\"~:y\",637.9999985694885]],[\"^:\",[\"^ \",\"~:x\",1611.000058889389,\"~:y\",940.9999985694885]],[\"^:\",[\"^ \",\"~:x\",1308.000058889389,\"~:y\",940.9999985694885]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a7\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1308.000058889389,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1308.000058889389,\"~:y\",637.9999985694885,\"^6\",303,\"~:height\",303,\"~:x1\",1308.000058889389,\"~:y1\",637.9999985694885,\"~:x2\",1611.000058889389,\"~:y2\",940.9999985694885]],\"~:fills\",[],\"~:flip-x\",null,\"^D\",303,\"~:flip-y\",null,\"~:shapes\",[\"~u11813dac-4dc3-80d8-8007-b280a6cd14a8\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a9\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b28053db9867": "[\"~#shape\",[\"^ \",\"~:y\",144.0000023841858,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",206,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",144.0000023841858]],[\"^<\",[\"^ \",\"~:x\",1023.9999825954437,\"~:y\",144.0000023841858]],[\"^<\",[\"^ \",\"~:x\",1023.9999825954437,\"~:y\",351.0000023841858]],[\"^<\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",351.0000023841858]]],\"~:r2\",0,\"~: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\",0,\"~:r1\",0,\"~:id\",\"~u11813dac-4dc3-80d8-8007-b28053db9867\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b28053db9865\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",817.9999825954437,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",817.9999825954437,\"~:y\",144.0000023841858,\"^8\",206,\"~:height\",207,\"~:x1\",817.9999825954437,\"~:y1\",144.0000023841858,\"~:x2\",1023.9999825954437,\"~:y2\",351.0000023841858]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#c8f00d\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",207,\"~:flip-y\",null]]",
|
||||
"~u5b58e018-fa5e-805d-8007-b27288b0cd80": "[\"~#shape\",[\"^ \",\"~:y\",49.00000846385956,\"~: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\",\"Ellipse\",\"~:width\",194,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",343.00000047683716,\"~:y\",49.00000846385956]],[\"^<\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",49.00000846385956]],[\"^<\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",249.00000846385956]],[\"^<\",[\"^ \",\"~:x\",343.00000047683716,\"~:y\",249.00000846385956]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u5b58e018-fa5e-805d-8007-b27288b0cd80\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b2804a521773\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",343.00000047683716,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",343.00000047683716,\"~:y\",49.00000846385956,\"^8\",194,\"~:height\",200,\"~:x1\",343.00000047683716,\"~:y1\",49.00000846385956,\"~:x2\",537.0000004768372,\"~:y2\",249.00000846385956]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#174be9\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",200,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14ac": "[\"~#shape\",[\"^ \",\"~:y\",593.0000084638596,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:index\",2,\"~:name\",\"Group\",\"~:width\",303,\"~:type\",\"~:group\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",593.0000084638596]],[\"^:\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",593.0000084638596]],[\"^:\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",896.0000084638596]],[\"^:\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",896.0000084638596]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ac\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",234.00000047683716,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",593.0000084638596,\"^6\",303,\"~:height\",303,\"~:x1\",234.00000047683716,\"~:y1\",593.0000084638596,\"~:x2\",537.0000004768372,\"~:y2\",896.0000084638596]],\"~:fills\",[],\"~:flip-x\",null,\"^D\",303,\"~:flip-y\",null,\"~:shapes\",[\"~u11813dac-4dc3-80d8-8007-b280a6cd14ad\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ae\"]]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14ad": "[\"~#shape\",[\"^ \",\"~:y\",593.0000084638596,\"~: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\",\"Ellipse\",\"~:width\",194,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",343.00000047683716,\"~:y\",593.0000084638596]],[\"^<\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",593.0000084638596]],[\"^<\",[\"^ \",\"~:x\",537.0000004768372,\"~:y\",793.0000084638596]],[\"^<\",[\"^ \",\"~:x\",343.00000047683716,\"~:y\",793.0000084638596]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ad\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ac\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",343.00000047683716,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",343.00000047683716,\"~:y\",593.0000084638596,\"^8\",194,\"~:height\",200,\"~:x1\",343.00000047683716,\"~:y1\",593.0000084638596,\"~:x2\",537.0000004768372,\"~:y2\",793.0000084638596]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#174be9\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",200,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14ae": "[\"~#shape\",[\"^ \",\"~:y\",689.0000084638596,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",206,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",689.0000084638596]],[\"^<\",[\"^ \",\"~:x\",440.00000047683716,\"~:y\",689.0000084638596]],[\"^<\",[\"^ \",\"~:x\",440.00000047683716,\"~:y\",896.0000084638596]],[\"^<\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",896.0000084638596]]],\"~:r2\",0,\"~: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\",0,\"~:r1\",0,\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ae\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ac\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",234.00000047683716,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",234.00000047683716,\"~:y\",689.0000084638596,\"^8\",206,\"~:height\",207,\"~:x1\",234.00000047683716,\"~:y1\",689.0000084638596,\"~:x2\",440.00000047683716,\"~:y2\",896.0000084638596]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#c8f00d\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",207,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14af": "[\"~#shape\",[\"^ \",\"~:y\",512.0000283168957,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",445.0000023918125,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",157.99998871589563,\"~:y\",512.0000283168957]],[\"^<\",[\"^ \",\"~:x\",602.9999911077081,\"~:y\",512.0000283168957]],[\"^<\",[\"^ \",\"~:x\",602.9999911077081,\"~:y\",957.000047738631]],[\"^<\",[\"^ \",\"~:x\",157.99998871589563,\"~:y\",957.000047738631]]],\"~: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,\"~:blur\",[\"^ \",\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2729af5ed71\",\"^9\",\"~:background-blur\",\"~:value\",50,\"~:hidden\",false],\"~:r1\",20,\"^B\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14af\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",157.9999887158956,\"~:proportion\",1,\"~:r4\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",157.9999887158956,\"~:y\",512.0000283168957,\"^8\",445.0000023918125,\"~:height\",445.00001942173526,\"~:x1\",157.9999887158956,\"~:y1\",512.0000283168957,\"~:x2\",602.9999911077081,\"~:y2\",957.000047738631]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",0.30392156862745096]],\"~:flip-x\",null,\"^N\",445.00001942173526,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14a8": "[\"~#shape\",[\"^ \",\"~:y\",637.9999985694885,\"~: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\",\"Ellipse\",\"~:width\",194,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1417.000058889389,\"~:y\",637.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1611.000058889389,\"~:y\",637.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1611.000058889389,\"~:y\",837.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1417.000058889389,\"~:y\",837.9999985694885]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a8\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a7\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1417.000058889389,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1417.000058889389,\"~:y\",637.9999985694885,\"^8\",194,\"~:height\",200,\"~:x1\",1417.000058889389,\"~:y1\",637.9999985694885,\"~:x2\",1611.000058889389,\"~:y2\",837.9999985694885]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#174be9\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^F\",200,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14a9": "[\"~#shape\",[\"^ \",\"~:y\",733.9999985694885,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",206,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1308.000058889389,\"~:y\",733.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1514.000058889389,\"~:y\",733.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1514.000058889389,\"~:y\",940.9999985694885]],[\"^<\",[\"^ \",\"~:x\",1308.000058889389,\"~:y\",940.9999985694885]]],\"~:r2\",0,\"~: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\",0,\"~:r1\",0,\"~:id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a9\",\"~:parent-id\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14a7\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1308.000058889389,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1308.000058889389,\"~:y\",733.9999985694885,\"^8\",206,\"~:height\",207,\"~:x1\",1308.000058889389,\"~:y1\",733.9999985694885,\"~:x2\",1514.000058889389,\"~:y2\",940.9999985694885]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#c8f00d\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^J\",207,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14aa": "[\"~#shape\",[\"^ \",\"~:y\",511.9999976151119,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",302.9999596227117,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1308.0000551195646,\"~:y\",511.9999976151119]],[\"^<\",[\"^ \",\"~:x\",1611.0000147422763,\"~:y\",511.9999976151119]],[\"^<\",[\"^ \",\"~:x\",1611.0000147422763,\"~:y\",812.0000073598412]],[\"^<\",[\"^ \",\"~:x\",1308.0000551195646,\"~:y\",812.0000073598412]]],\"~: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,\"~:blur\",[\"^ \",\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2729af5ed71\",\"^9\",\"~:background-blur\",\"~:value\",50,\"~:hidden\",false],\"~:r1\",20,\"^B\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14aa\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",1308.0000551195646,\"~:proportion\",1,\"~:r4\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1308.0000551195646,\"~:y\",511.9999976151119,\"^8\",302.9999596227117,\"~:height\",300.0000097447293,\"~:x1\",1308.0000551195646,\"~:y1\",511.9999976151119,\"~:x2\",1611.0000147422763,\"~:y2\",812.0000073598412]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",0.30392156862745096]],\"~:flip-x\",null,\"^N\",300.0000097447293,\"~:flip-y\",null]]",
|
||||
"~u11813dac-4dc3-80d8-8007-b280a6cd14ab": "[\"~#shape\",[\"^ \",\"~:y\",512.00002813269,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",299.9999982638824,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",743.9999566428096,\"~:y\",512.00002813269]],[\"^<\",[\"^ \",\"~:x\",1043.999954906692,\"~:y\",512.00002813269]],[\"^<\",[\"^ \",\"~:x\",1043.999954906692,\"~:y\",812.0000378774193]],[\"^<\",[\"^ \",\"~:x\",743.9999566428096,\"~:y\",812.0000378774193]]],\"~: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,\"~:blur\",[\"^ \",\"~:id\",\"~u5b58e018-fa5e-805d-8007-b2729af5ed71\",\"^9\",\"~:background-blur\",\"~:value\",50,\"~:hidden\",false],\"~:r1\",20,\"^B\",\"~u11813dac-4dc3-80d8-8007-b280a6cd14ab\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",743.9999566428096,\"~:proportion\",1,\"~:r4\",20,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",743.9999566428096,\"~:y\",512.00002813269,\"^8\",299.9999982638824,\"~:height\",300.00000974472937,\"~:x1\",743.9999566428096,\"~:y1\",512.00002813269,\"~:x2\",1043.999954906692,\"~:y2\",812.0000378774193]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#ffffff\",\"~:fill-opacity\",0.30392156862745096]],\"~:flip-x\",null,\"^N\",300.00000974472937,\"~:flip-y\",null]]"
|
||||
}
|
||||
},
|
||||
"~:id": "~u93bfc923-66b2-813c-8007-b2725507ba09",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
},
|
||||
"~:id": "~u93bfc923-66b2-813c-8007-b2725507ba08",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -215,6 +215,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
async goToWorkspace({
|
||||
fileId = this.fileId ?? WorkspacePage.anyFileId,
|
||||
pageId = this.pageId ?? WorkspacePage.anyPageId,
|
||||
pageName = "Page 1",
|
||||
} = {}) {
|
||||
await this.page.goto(
|
||||
`/#/workspace?team-id=${WorkspacePage.anyTeamId}&file-id=${fileId}&page-id=${pageId}`,
|
||||
@ -222,12 +223,12 @@ export class WorkspacePage extends BaseWebSocketPage {
|
||||
|
||||
this.#ws = await this.waitForNotificationsWebSocket();
|
||||
await this.#ws.mockOpen();
|
||||
await this.#waitForWebSocketReadiness();
|
||||
await this.#waitForWebSocketReadiness(pageName);
|
||||
}
|
||||
|
||||
async #waitForWebSocketReadiness() {
|
||||
async #waitForWebSocketReadiness(pageName) {
|
||||
// TODO: find a better event to settle whether the app is ready to receive notifications via ws
|
||||
await expect(this.pageName).toHaveText("Page 1", { timeout: 30000 });
|
||||
await expect(this.pageName).toHaveText(pageName, { timeout: 30000 })
|
||||
}
|
||||
|
||||
async sendPresenceMessage(fixture) {
|
||||
|
||||
@ -545,5 +545,22 @@ test("BUG 13610 - Huge inner strokes", async ({
|
||||
pageId: "effcbebc-b8c8-802f-8007-b11dd34fe191",
|
||||
});
|
||||
await workspace.waitForFirstRenderWithoutUI();
|
||||
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test("Renders background blur on shapes overlapping other shapes", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
id: "93bfc923-66b2-813c-8007-b2725507ba08",
|
||||
pageId: "93bfc923-66b2-813c-8007-b2725507ba09",
|
||||
});
|
||||
await workspace.waitForFirstRenderWithoutUI();
|
||||
|
||||
await expect(workspace.canvas).toHaveScreenshot();
|
||||
});
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
@ -231,6 +231,90 @@ test("BUG 9061 - Group blur visibility toggle icon not updating", async ({
|
||||
await expect(blurIcon).toHaveAttribute("href", "#icon-hide");
|
||||
});
|
||||
|
||||
test.describe("Background blur", () => {
|
||||
test("Shows background blur option in blur type select when both render-wasm and background-blur flags are active", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-background-blur"]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "93bfc923-66b2-813c-8007-b2725507ba08",
|
||||
pageId: "93bfc923-66b2-813c-8007-b2725507ba09",
|
||||
});
|
||||
|
||||
// Click the first Rectangle (which has background-blur type)
|
||||
await workspace.clickLeafLayer("Rectangle");
|
||||
|
||||
// The blur type select should show "Background blur" as the current value
|
||||
const blurTypeSelect = workspace.page
|
||||
.getByTestId("blur-info")
|
||||
.getByRole("combobox");
|
||||
await expect(blurTypeSelect).toBeVisible();
|
||||
await expect(blurTypeSelect).toContainText("Background blur");
|
||||
});
|
||||
|
||||
test("Shows both layer-blur and background-blur options in the blur type dropdown", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-background-blur"]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "93bfc923-66b2-813c-8007-b2725507ba08",
|
||||
pageId: "93bfc923-66b2-813c-8007-b2725507ba09",
|
||||
});
|
||||
|
||||
await workspace.clickLeafLayer("Rectangle");
|
||||
|
||||
// Open the blur type dropdown
|
||||
const blurTypeSelect = workspace.page
|
||||
.getByTestId("blur-info")
|
||||
.getByRole("combobox");
|
||||
await blurTypeSelect.click();
|
||||
|
||||
// Both options should be visible
|
||||
const layerBlurOption = workspace.page.getByRole("option", {
|
||||
name: "Layer blur",
|
||||
});
|
||||
const backgroundBlurOption = workspace.page.getByRole("option", {
|
||||
name: "Background blur",
|
||||
});
|
||||
await expect(layerBlurOption).toBeVisible();
|
||||
await expect(backgroundBlurOption).toBeVisible();
|
||||
});
|
||||
|
||||
test("Does not show background blur option when background-blur flag is not active", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
// No enable-background-blur flag
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockGetFile("render-wasm/get-file-background-blur.json");
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "93bfc923-66b2-813c-8007-b2725507ba08",
|
||||
pageId: "93bfc923-66b2-813c-8007-b2725507ba09",
|
||||
});
|
||||
|
||||
await workspace.clickLeafLayer("Rectangle");
|
||||
|
||||
// Without the background-blur flag, no blur type dropdown should appear.
|
||||
// Instead, a plain "Blur" label is shown.
|
||||
const blurTypeSelect = workspace.page
|
||||
.getByTestId("blur-info")
|
||||
.getByRole("combobox");
|
||||
await expect(blurTypeSelect).not.toBeVisible();
|
||||
|
||||
const blurLabel = workspace.page.getByTestId("blur-info").getByText("Blur");
|
||||
await expect(blurLabel).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test("BUG 9543 - Layout padding inputs not showing 'mixed' when needed", async ({
|
||||
page,
|
||||
}) => {
|
||||
|
||||
@ -58,6 +58,14 @@
|
||||
[]
|
||||
(mf/render! app-root (mf/element ui/app)))
|
||||
|
||||
(defn- initialize-rasterizer
|
||||
[]
|
||||
(ptk/reify ::initialize-rasterizer
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(when (feat/active-feature? state "render-wasm/v1")
|
||||
(thr/init!)))))
|
||||
|
||||
(defn initialize
|
||||
[]
|
||||
(ptk/reify ::initialize
|
||||
@ -93,12 +101,12 @@
|
||||
(rx/map deref)
|
||||
(rx/filter dp/is-authenticated?)
|
||||
(rx/take 1)
|
||||
(rx/map #(ws/initialize)))))
|
||||
(rx/map #(ws/initialize)))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(when-not (feat/active-feature? state "render-wasm/v1")
|
||||
(thr/init!)))))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::feat/initialize))
|
||||
(rx/take 1)
|
||||
(rx/map #(initialize-rasterizer)))))))
|
||||
|
||||
(defn ^:export init
|
||||
[options]
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.texts :as dwtxt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.data.workspace.wasm-text :as dwwt]
|
||||
[app.main.errors]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
@ -970,10 +971,11 @@
|
||||
text (.-textContent root)
|
||||
content (tc/dom->cljs root)]
|
||||
(when (types.text/valid-content? content)
|
||||
(let [id (uuid/next)
|
||||
width (max 8 (min (* 7 (count text)) 700))
|
||||
height 16
|
||||
(let [id (uuid/next)
|
||||
width (max 8 (min (* 7 (count text)) 700))
|
||||
height 16
|
||||
{:keys [x y]} (calculate-paste-position state)
|
||||
skip-edition? (features/active-feature? state "text-editor-wasm/v1")
|
||||
|
||||
shape {:id id
|
||||
:type :text
|
||||
@ -985,9 +987,14 @@
|
||||
:grow-type (if (> (count text) 100) :auto-height :auto-width)
|
||||
:content content}
|
||||
undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dwsh/create-and-add-shape :text x y shape)
|
||||
(dwu/commit-undo-transaction undo-id))))))))
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dwsh/create-and-add-shape :text x y shape
|
||||
(when skip-edition? {:skip-edition? true})))
|
||||
(if skip-edition?
|
||||
(rx/of (dwwt/resize-wasm-text-debounce id {:undo-group id
|
||||
:undo-id undo-id}))
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))))
|
||||
|
||||
(defn- paste-text
|
||||
[text]
|
||||
@ -995,10 +1002,11 @@
|
||||
(ptk/reify ::paste-text
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [id (uuid/next)
|
||||
width (max 8 (min (* 7 (count text)) 700))
|
||||
height 16
|
||||
(let [id (uuid/next)
|
||||
width (max 8 (min (* 7 (count text)) 700))
|
||||
height 16
|
||||
{:keys [x y]} (calculate-paste-position state)
|
||||
skip-edition? (features/active-feature? state "text-editor-wasm/v1")
|
||||
|
||||
shape {:id id
|
||||
:type :text
|
||||
@ -1011,9 +1019,14 @@
|
||||
:content (as-content text)}
|
||||
undo-id (js/Symbol)]
|
||||
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dwsh/create-and-add-shape :text x y shape)
|
||||
(dwu/commit-undo-transaction undo-id))))))
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dwsh/create-and-add-shape :text x y shape
|
||||
(when skip-edition? {:skip-edition? true})))
|
||||
(if skip-edition?
|
||||
(rx/of (dwwt/resize-wasm-text-debounce id {:undo-group id
|
||||
:undo-id undo-id}))
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))
|
||||
|
||||
;; TODO: why not implement it in terms of upload-media-workspace?
|
||||
(defn- paste-svg-text
|
||||
|
||||
@ -53,21 +53,28 @@
|
||||
(rx/take 1)
|
||||
(rx/map #(ptk/data-event ::connect))))))
|
||||
|
||||
(defn manage-notification
|
||||
[mcp-enabled? mcp-connected?]
|
||||
(if mcp-enabled?
|
||||
(if mcp-connected?
|
||||
(rx/of (ntf/hide))
|
||||
(rx/of (ntf/dialog :content (tr "notifications.mcp.active-tab-switching.text")
|
||||
:cancel {:label (tr "labels.dismiss")
|
||||
:callback #(st/emit! (ntf/hide)
|
||||
(ptk/event ::ev/event {::ev/name "confirm-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))}
|
||||
:accept {:label (tr "labels.switch")
|
||||
:callback #(st/emit! (connect-mcp)
|
||||
(ptk/event ::ev/event {::ev/name "dismiss-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))})))
|
||||
(rx/of (ntf/hide))))
|
||||
(defn manage-mcp-notification
|
||||
[]
|
||||
(ptk/reify ::manage-mcp-notification
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [mcp-connected? (true? (-> state :workspace-local :mcp :connection))
|
||||
mcp-enabled? (true? (-> state :profile :props :mcp-enabled))
|
||||
num-sessions (-> state :workspace-presence count)
|
||||
multi-session? (> num-sessions 1)]
|
||||
(if (and mcp-enabled? multi-session?)
|
||||
(if mcp-connected?
|
||||
(rx/of (ntf/hide))
|
||||
(rx/of (ntf/dialog :content (tr "notifications.mcp.active-in-another-tab")
|
||||
:cancel {:label (tr "labels.dismiss")
|
||||
:callback #(st/emit! (ntf/hide)
|
||||
(ptk/event ::ev/event {::ev/name "confirm-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))}
|
||||
:accept {:label (tr "labels.switch")
|
||||
:callback #(st/emit! (connect-mcp)
|
||||
(ptk/event ::ev/event {::ev/name "dismiss-mcp-tab-switch"
|
||||
::ev/origin "workspace-notification"}))})))
|
||||
(rx/of (ntf/hide)))))))
|
||||
|
||||
(defn update-mcp-status
|
||||
[value]
|
||||
@ -77,26 +84,24 @@
|
||||
(update-in state [:profile :props] assoc :mcp-enabled value))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(watch [_ _ _]
|
||||
(rx/merge
|
||||
(let [mcp-connected? (-> state :workspace-local :mcp :connected)]
|
||||
(manage-notification value mcp-connected?))
|
||||
(case value
|
||||
true (rx/of (ptk/data-event ::connect))
|
||||
false (rx/of (ptk/data-event ::disconnect))
|
||||
nil)))))
|
||||
(rx/of (manage-mcp-notification)))
|
||||
(case value
|
||||
true (rx/of (ptk/data-event ::connect))
|
||||
false (rx/of (ptk/data-event ::disconnect))
|
||||
nil))))
|
||||
|
||||
(defn update-mcp-connection
|
||||
[value]
|
||||
(ptk/reify ::update-mcp-plugin-connection
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-local :mcp] assoc :connected value))
|
||||
(update-in state [:workspace-local :mcp] assoc :connection value))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [mcp-enabled? (-> state :profile :props :mcp-enabled)]
|
||||
(manage-notification mcp-enabled? value)))))
|
||||
(watch [_ _ _]
|
||||
(rx/of (manage-mcp-notification)))))
|
||||
|
||||
(defn init-mcp!
|
||||
[stream]
|
||||
@ -116,11 +121,12 @@
|
||||
:getServerUrl #(str cf/mcp-ws-uri)
|
||||
:setMcpStatus
|
||||
(fn [status]
|
||||
(let [mcp-connected? (case status
|
||||
(let [mcp-connection (case status
|
||||
"connected" true
|
||||
"disconnected" false
|
||||
nil)]
|
||||
(st/emit! (update-mcp-connection mcp-connected?))
|
||||
"error" nil
|
||||
"")]
|
||||
(st/emit! (update-mcp-connection mcp-connection))
|
||||
(log/info :hint "MCP STATUS" :status status)))
|
||||
|
||||
:on
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.layout :as dwly]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.mcp :as mcp]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.router :as rt]
|
||||
[app.util.globals :refer [global]]
|
||||
@ -212,7 +213,11 @@
|
||||
(update [_ state]
|
||||
(if (or (= :disconnect type) (= :leave-file type))
|
||||
(update state :workspace-presence dissoc session-id)
|
||||
(update state :workspace-presence update-presence))))))
|
||||
(update state :workspace-presence update-presence)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (mcp/manage-mcp-notification))))))
|
||||
|
||||
(defn handle-pointer-update
|
||||
[{:keys [page-id session-id position zoom zoom-inverse vbox vport] :as msg}]
|
||||
|
||||
@ -109,7 +109,7 @@
|
||||
(defn add-shape
|
||||
([shape]
|
||||
(add-shape shape {}))
|
||||
([shape {:keys [no-select? no-update-layout?]}]
|
||||
([shape {:keys [no-select? no-update-layout? skip-edition?]}]
|
||||
|
||||
(cts/check-shape shape)
|
||||
|
||||
@ -139,7 +139,11 @@
|
||||
(js/Symbol)
|
||||
|
||||
parent-type
|
||||
(cfh/get-shape-type objects (:parent-id shape))]
|
||||
(cfh/get-shape-type objects (:parent-id shape))
|
||||
|
||||
;; Skip edition when using embedded editor (v3) and shape already has content (e.g. paste)
|
||||
start-edition? (and (cfh/text-shape? shape)
|
||||
(not (and skip-edition? (some? (:content shape)))))]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
@ -149,7 +153,7 @@
|
||||
(when-not no-select?
|
||||
(dws/select-shapes (d/ordered-set (:id shape))))
|
||||
(dwu/commit-undo-transaction undo-id))
|
||||
(when (cfh/text-shape? shape)
|
||||
(when start-edition?
|
||||
(->> (rx/of (dwe/start-edition-mode (:id shape)))
|
||||
(rx/observe-on :async)))
|
||||
|
||||
@ -217,40 +221,42 @@
|
||||
(dwu/commit-undo-transaction undo-id)))))))
|
||||
|
||||
(defn create-and-add-shape
|
||||
[type frame-x frame-y {:keys [width height] :as attrs}]
|
||||
(ptk/reify ::create-and-add-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [vbc (dsh/get-viewport-center state)
|
||||
x (:x attrs (- (:x vbc) (/ width 2)))
|
||||
y (:y attrs (- (:y vbc) (/ height 2)))
|
||||
page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
frame-id (-> (dsh/lookup-page-objects state page-id)
|
||||
(ctst/top-nested-frame {:x frame-x :y frame-y}))
|
||||
([type frame-x frame-y attrs]
|
||||
(create-and-add-shape type frame-x frame-y attrs nil))
|
||||
([type frame-x frame-y {:keys [width height] :as attrs} {:keys [skip-edition?]}]
|
||||
(ptk/reify ::create-and-add-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [vbc (dsh/get-viewport-center state)
|
||||
x (:x attrs (- (:x vbc) (/ width 2)))
|
||||
y (:y attrs (- (:y vbc) (/ height 2)))
|
||||
page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
frame-id (-> (dsh/lookup-page-objects state page-id)
|
||||
(ctst/top-nested-frame {:x frame-x :y frame-y}))
|
||||
|
||||
selected (dsh/lookup-selected state)
|
||||
base (cfh/get-base-shape objects selected)
|
||||
selected (dsh/lookup-selected state)
|
||||
base (cfh/get-base-shape objects selected)
|
||||
|
||||
parent-id (if (or (and (= 1 (count selected))
|
||||
(cfh/frame-shape? (get objects (first selected))))
|
||||
(empty? selected))
|
||||
frame-id
|
||||
(:parent-id base))
|
||||
parent-id (if (or (and (= 1 (count selected))
|
||||
(cfh/frame-shape? (get objects (first selected))))
|
||||
(empty? selected))
|
||||
frame-id
|
||||
(:parent-id base))
|
||||
|
||||
;; If the parent-id or the frame-id are component-copies, we need to get the first not copy parent
|
||||
parent-id (:id (ctn/get-first-valid-parent objects parent-id)) ;; We don't want to change the structure of component copies
|
||||
frame-id (:id (ctn/get-first-valid-parent objects frame-id))
|
||||
;; If the parent-id or the frame-id are component-copies, we need to get the first not copy parent
|
||||
parent-id (:id (ctn/get-first-valid-parent objects parent-id)) ;; We don't want to change the structure of component copies
|
||||
frame-id (:id (ctn/get-first-valid-parent objects frame-id))
|
||||
|
||||
shape (cts/setup-shape
|
||||
(-> attrs
|
||||
(assoc :type type)
|
||||
(assoc :x x)
|
||||
(assoc :y y)
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :parent-id parent-id)))]
|
||||
shape (cts/setup-shape
|
||||
(-> attrs
|
||||
(assoc :type type)
|
||||
(assoc :x x)
|
||||
(assoc :y y)
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :parent-id parent-id)))]
|
||||
|
||||
(rx/of (add-shape shape))))))
|
||||
(rx/of (add-shape shape {:skip-edition? skip-edition?})))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Artboard
|
||||
|
||||
@ -966,7 +966,11 @@
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(if (features/active-feature? state "render-wasm/v1")
|
||||
(let [objects (dsh/lookup-page-objects state)
|
||||
(let [;; v3 editor always passes :finalize? from keyword opts; when absent
|
||||
;; that binds nil and :or defaults do not apply — coerce so undo flags
|
||||
;; stay strict booleans for changes-builder schema validation.
|
||||
finalize? (boolean finalize?)
|
||||
objects (dsh/lookup-page-objects state)
|
||||
shape (get objects id)
|
||||
new-shape? (contains? (:workspace-new-text-shapes state) id)
|
||||
prev-content (:content shape)
|
||||
@ -978,8 +982,7 @@
|
||||
(select-keys shape [:selrect :points :width :height]))
|
||||
content-has-text? (v2-content-has-text? content)
|
||||
prev-content-has-text? (v2-content-has-text? prev-content)
|
||||
new-size (when (and (not= :fixed (:grow-type shape))
|
||||
content-has-text?)
|
||||
new-size (when (not= :fixed (:grow-type shape))
|
||||
(dwwt/get-wasm-text-new-size shape content))
|
||||
;; New shapes: single undo on finalize only (no per-keystroke undo)
|
||||
effective-save-undo? (if new-shape? finalize? save-undo?)
|
||||
|
||||
@ -49,14 +49,14 @@
|
||||
|
||||
;; (note that dwsh/update-shapes function returns an event)
|
||||
|
||||
(defn update-shape-radius-all
|
||||
([value shape-ids attributes] (update-shape-radius-all value shape-ids attributes nil))
|
||||
([value shape-ids _attributes page-id] ; The attributes param is needed to have the same arity that other update functions
|
||||
(defn update-shape-radius
|
||||
([value shape-ids attributes] (update-shape-radius value shape-ids attributes nil))
|
||||
([value shape-ids attributes page-id]
|
||||
(when (number? value)
|
||||
(let [value (max 0 value)]
|
||||
(dwsh/update-shapes shape-ids
|
||||
(fn [shape]
|
||||
(ctsr/set-radius-to-all-corners shape value))
|
||||
(ctsr/set-radius-for-corners shape attributes value))
|
||||
{:reg-objects? true
|
||||
:ignore-touched true
|
||||
:page-id page-id
|
||||
@ -531,7 +531,7 @@
|
||||
|
||||
(some attributes #{:r1 :r2 :r3 :r4})
|
||||
(conj #(if (= attributes #{:r1 :r2 :r3 :r4})
|
||||
(update-shape-radius-all value shape-ids attributes page-id)
|
||||
(update-shape-radius value shape-ids attributes page-id)
|
||||
(update-shape-radius-for-corners
|
||||
value shape-ids
|
||||
(set (filter attributes #{:r1 :r2 :r3 :r4}))
|
||||
@ -862,7 +862,7 @@
|
||||
:border-radius
|
||||
{:title "Border Radius"
|
||||
:attributes ctt/border-radius-keys
|
||||
:on-update-shape update-shape-radius-all
|
||||
:on-update-shape update-shape-radius
|
||||
:modal {:key :tokens/border-radius
|
||||
:fields [{:label "Border Radius"
|
||||
:key :border-radius}]}}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[app.render-wasm.api.fonts :as wasm.fonts]
|
||||
[beicon.v2.core :as rx]
|
||||
@ -76,82 +77,115 @@
|
||||
(rx/empty))))))
|
||||
|
||||
(defn resize-wasm-text-debounce-commit
|
||||
[]
|
||||
(ptk/reify ::resize-wasm-text-debounce-commit
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [ids (get state ::resize-wasm-text-debounce-ids)
|
||||
objects (dsh/lookup-page-objects state)
|
||||
([]
|
||||
(resize-wasm-text-debounce-commit nil nil))
|
||||
([undo-group undo-id]
|
||||
(ptk/reify ::resize-wasm-text-debounce-commit
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [ids (get state ::resize-wasm-text-debounce-ids)
|
||||
objects (dsh/lookup-page-objects state)
|
||||
|
||||
modifiers
|
||||
(reduce
|
||||
(fn [modifiers id]
|
||||
(let [shape (get objects id)]
|
||||
(cond-> modifiers
|
||||
(and (some? shape)
|
||||
(cfh/text-shape? shape)
|
||||
(not= :fixed (:grow-type shape)))
|
||||
(merge (resize-wasm-text-modifiers shape)))))
|
||||
{}
|
||||
ids)]
|
||||
(if (not (empty? modifiers))
|
||||
(rx/of (dwm/apply-wasm-modifiers modifiers))
|
||||
(rx/empty))))))
|
||||
modifiers
|
||||
(reduce
|
||||
(fn [modifiers id]
|
||||
(let [shape (get objects id)]
|
||||
(cond-> modifiers
|
||||
(and (some? shape)
|
||||
(cfh/text-shape? shape)
|
||||
(not= :fixed (:grow-type shape)))
|
||||
(merge (resize-wasm-text-modifiers shape)))))
|
||||
{}
|
||||
ids)
|
||||
|
||||
;; When undo-id is present, extend the current undo transaction instead of
|
||||
;; creating a new one, and commit it after the resize (single undo action).
|
||||
extend-tx? (some? undo-id)
|
||||
apply-opts (cond-> {}
|
||||
(some? undo-group) (assoc :undo-group undo-group)
|
||||
extend-tx? (assoc :undo-transation? false))]
|
||||
(cond
|
||||
(not (empty? modifiers))
|
||||
(if extend-tx?
|
||||
(rx/concat
|
||||
(rx/of (dwm/apply-wasm-modifiers modifiers apply-opts))
|
||||
(rx/of (dwu/commit-undo-transaction undo-id)))
|
||||
(rx/of (dwm/apply-wasm-modifiers modifiers apply-opts)))
|
||||
|
||||
extend-tx?
|
||||
;; No resize needed (e.g. :fixed grow-type) but we must commit the add
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))
|
||||
|
||||
:else
|
||||
(rx/empty)))))))
|
||||
|
||||
;; This event will debounce the resize events so, if there are many, they
|
||||
;; are processed at the same time and not one-by-one. This will improve
|
||||
;; performance because it's better to make only one layout calculation instead
|
||||
;; of (potentialy) hundreds.
|
||||
(defn resize-wasm-text-debounce-inner
|
||||
[id]
|
||||
(let [cur-event (js/Symbol)]
|
||||
(ptk/reify ::resize-wasm-text-debounce-inner
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update ::resize-wasm-text-debounce-ids (fnil conj []) id)
|
||||
(cond-> (nil? (::resize-wasm-text-debounce-event state))
|
||||
(assoc ::resize-wasm-text-debounce-event cur-event))))
|
||||
([id]
|
||||
(resize-wasm-text-debounce-inner id nil))
|
||||
([id {:keys [undo-group undo-id]}]
|
||||
(let [cur-event (js/Symbol)]
|
||||
(ptk/reify ::resize-wasm-text-debounce-inner
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update ::resize-wasm-text-debounce-ids (fnil conj []) id)
|
||||
(cond-> (nil? (::resize-wasm-text-debounce-event state))
|
||||
(assoc ::resize-wasm-text-debounce-event cur-event))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(if (= (::resize-wasm-text-debounce-event state) cur-event)
|
||||
(let [stopper (->> stream (rx/filter (ptk/type? :app.main.data.workspace/finalize)))]
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::resize-wasm-text-debounce-inner))
|
||||
(rx/debounce 40)
|
||||
(rx/take 1)
|
||||
(rx/map #(resize-wasm-text-debounce-commit))
|
||||
(rx/take-until stopper))
|
||||
(rx/of (resize-wasm-text-debounce-inner id)))
|
||||
(rx/of #(dissoc %
|
||||
::resize-wasm-text-debounce-ids
|
||||
::resize-wasm-text-debounce-event))))
|
||||
(rx/empty))))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(if (= (::resize-wasm-text-debounce-event state) cur-event)
|
||||
(let [stopper (->> stream (rx/filter (ptk/type? :app.main.data.workspace/finalize)))]
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::resize-wasm-text-debounce-inner))
|
||||
(rx/debounce 40)
|
||||
(rx/take 1)
|
||||
(rx/map (fn [evt]
|
||||
(resize-wasm-text-debounce-commit
|
||||
(some-> evt meta :undo-group)
|
||||
(some-> evt meta :undo-id))))
|
||||
(rx/take-until stopper))
|
||||
(rx/of (with-meta
|
||||
(resize-wasm-text-debounce-inner id)
|
||||
{:undo-group undo-group :undo-id undo-id})))
|
||||
(rx/of #(dissoc %
|
||||
::resize-wasm-text-debounce-ids
|
||||
::resize-wasm-text-debounce-event))))
|
||||
(rx/empty)))))))
|
||||
|
||||
(defn resize-wasm-text-debounce
|
||||
[id]
|
||||
(ptk/reify ::resize-wasm-text-debounce
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
content (dm/get-in objects [id :content])
|
||||
fonts (wasm.fonts/get-content-fonts content)
|
||||
([id]
|
||||
(resize-wasm-text-debounce id nil))
|
||||
([id {:keys [undo-group undo-id] :as opts}]
|
||||
(ptk/reify ::resize-wasm-text-debounce
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (dsh/lookup-page-objects state page-id)
|
||||
content (dm/get-in objects [id :content])
|
||||
fonts (wasm.fonts/get-content-fonts content)
|
||||
|
||||
fonts-loaded?
|
||||
(->> fonts
|
||||
(every?
|
||||
(fn [font]
|
||||
(let [font-data (wasm.fonts/make-font-data font)]
|
||||
(wasm.fonts/font-stored? font-data (:emoji? font-data))))))]
|
||||
fonts-loaded?
|
||||
(->> fonts
|
||||
(every?
|
||||
(fn [font]
|
||||
(let [font-data (wasm.fonts/make-font-data font)]
|
||||
(wasm.fonts/font-stored? font-data (:emoji? font-data))))))]
|
||||
|
||||
(if (not fonts-loaded?)
|
||||
(->> (rx/of (resize-wasm-text-debounce id))
|
||||
(rx/delay 20))
|
||||
(rx/of (resize-wasm-text-debounce-inner id)))))))
|
||||
(if (not fonts-loaded?)
|
||||
(->> (rx/of (resize-wasm-text-debounce id opts))
|
||||
(rx/delay 20))
|
||||
(let [pass-opts (when (or (some? undo-group) (some? undo-id))
|
||||
(cond-> {}
|
||||
(some? undo-group) (assoc :undo-group undo-group)
|
||||
(some? undo-id) (assoc :undo-id undo-id)))]
|
||||
(rx/of (resize-wasm-text-debounce-inner id pass-opts)))))))))
|
||||
|
||||
(defn resize-wasm-text-all
|
||||
"Resize all text shapes (auto-width/auto-height) from a collection of ids."
|
||||
|
||||
@ -188,7 +188,7 @@
|
||||
(when-not (.-hidden js/document)
|
||||
(let [trigger-el (mf/ref-val trigger-ref)]
|
||||
(clear-schedule schedule-ref)
|
||||
(add-schedule schedule-ref delay
|
||||
(add-schedule schedule-ref (d/nilv delay 300)
|
||||
(fn []
|
||||
(when-let [active @active-tooltip]
|
||||
(when (not= (:id active) tooltip-id)
|
||||
|
||||
@ -17,6 +17,11 @@
|
||||
(defn- has-blur? [shape]
|
||||
(:blur shape))
|
||||
|
||||
(defn- blur-css-property [shape]
|
||||
(if (= :background-blur (get-in shape [:blur :type]))
|
||||
:backdrop-filter
|
||||
:filter))
|
||||
|
||||
(mf/defc blur-panel
|
||||
[{:keys [objects shapes]}]
|
||||
(let [shapes (->> shapes (filter has-blur?))]
|
||||
@ -27,15 +32,18 @@
|
||||
:class (stl/css :title-wrapper)
|
||||
:title-class (stl/css :blur-attr-title)}
|
||||
(when (= (count shapes) 1)
|
||||
[:> copy-button* {:data (css/get-css-property objects (first shapes) :filter)
|
||||
:class (stl/css :copy-btn-title)}])]
|
||||
(let [prop (blur-css-property (first shapes))]
|
||||
[:> copy-button* {:data (css/get-css-property objects (first shapes) prop)
|
||||
:class (stl/css :copy-btn-title)}]))]
|
||||
|
||||
[:div {:class (stl/css :attributes-content)}
|
||||
(for [shape shapes]
|
||||
[:div {:class (stl/css :blur-row)
|
||||
:key (dm/str "block-" (:id shape) "-blur")}
|
||||
[:div {:class (stl/css :global/attr-label)} "Filter"]
|
||||
[:div {:class (stl/css :global/attr-value)}
|
||||
[:> copy-button* {:data (css/get-css-property objects shape :filter)}
|
||||
[:div {:class (stl/css :button-children)}
|
||||
(css/get-css-value objects shape :filter)]]]])]])))
|
||||
(let [prop (blur-css-property shape)]
|
||||
[:div {:class (stl/css :blur-row)
|
||||
:key (dm/str "block-" (:id shape) "-blur")}
|
||||
[:div {:class (stl/css :global/attr-label)}
|
||||
(if (= prop :backdrop-filter) "Backdrop Filter" "Filter")]
|
||||
[:div {:class (stl/css :global/attr-value)}
|
||||
[:> copy-button* {:data (css/get-css-property objects shape prop)}
|
||||
[:div {:class (stl/css :button-children)}
|
||||
(css/get-css-value objects shape prop)]]]]))]])))
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
(ns app.main.ui.settings.integrations
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as ct]
|
||||
@ -410,7 +409,7 @@
|
||||
profile (mf/deref refs/profile)
|
||||
|
||||
mcp-key (some #(when (= (:type %) "mcp") %) tokens)
|
||||
mcp-enabled? (d/nilv (-> profile :props :mcp-enabled) false)
|
||||
mcp-enabled? (true? (-> profile :props :mcp-enabled))
|
||||
|
||||
expires-at (:expires-at mcp-key)
|
||||
expired? (and (some? expires-at) (> (ct/now) expires-at))
|
||||
|
||||
@ -21,7 +21,9 @@
|
||||
(defn filter-str
|
||||
[filter-id shape]
|
||||
(when (or (seq (->> (:shadow shape) (remove :hidden)))
|
||||
(and (:blur shape) (-> shape :blur :hidden not)))
|
||||
(and (:blur shape)
|
||||
(-> shape :blur :hidden not)
|
||||
(= :layer-blur (-> shape :blur :type))))
|
||||
(str/ffmt "url(#%)" filter-id)))
|
||||
|
||||
(mf/defc color-matrix
|
||||
|
||||
@ -749,8 +749,8 @@
|
||||
profile (mf/deref refs/profile)
|
||||
workspace-local (mf/deref refs/workspace-local)
|
||||
|
||||
mcp-enabled? (-> profile :props :mcp-enabled)
|
||||
mcp-connected? (-> workspace-local :mcp :connected)
|
||||
mcp-enabled? (true? (-> profile :props :mcp-enabled))
|
||||
mcp-connected? (true? (-> workspace-local :mcp :connection))
|
||||
|
||||
on-nav-to-integrations
|
||||
(mf/use-fn
|
||||
@ -978,9 +978,10 @@
|
||||
:class (stl/css :item-arrow)}]])
|
||||
|
||||
(when (contains? cf/flags :mcp)
|
||||
(let [mcp-enabled? (-> profile :props :mcp-enabled)
|
||||
mcp-connected? (-> workspace-local :mcp :connected)
|
||||
mcp-active? (and mcp-enabled? mcp-connected?)]
|
||||
(let [mcp-enabled? (true? (-> profile :props :mcp-enabled))
|
||||
mcp-connection (-> workspace-local :mcp :connection)
|
||||
mcp-connected? (true? mcp-connection)
|
||||
mcp-error? (nil? mcp-connection)]
|
||||
[:> dropdown-menu-item* {:class (stl/css :base-menu-item :menu-item)
|
||||
:on-click on-menu-click
|
||||
:on-key-down (fn [event]
|
||||
@ -992,7 +993,8 @@
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(tr "workspace.header.menu.option.mcp")]
|
||||
[:span {:class (stl/css-case :item-indicator true
|
||||
:active mcp-active?)}]
|
||||
:active (and mcp-enabled? mcp-connected?)
|
||||
:failed (and mcp-enabled? mcp-error?))}]
|
||||
[:> icon* {:icon-id i/arrow-right
|
||||
:class (stl/css :item-arrow)}]]))
|
||||
|
||||
|
||||
@ -134,6 +134,10 @@
|
||||
&.active {
|
||||
--menu-indicator-color: var(--color-accent-primary);
|
||||
}
|
||||
|
||||
&.failed {
|
||||
--menu-indicator-color: var(--color-foreground-error);
|
||||
}
|
||||
}
|
||||
|
||||
.item-arrow {
|
||||
|
||||
@ -75,7 +75,22 @@
|
||||
on-composition-start
|
||||
(mf/use-fn
|
||||
(fn [_event]
|
||||
(reset! composing? true)))
|
||||
(reset! composing? true)
|
||||
(text-editor/text-editor-composition-start)))
|
||||
|
||||
on-composition-update
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when-not composing?
|
||||
(reset! composing? true))
|
||||
|
||||
(let [data (.-data event)]
|
||||
(when data
|
||||
(text-editor/text-editor-composition-update data)
|
||||
(sync-wasm-text-editor-content!)
|
||||
(wasm.api/request-render "text-composition"))
|
||||
(when-let [node (mf/ref-val contenteditable-ref)]
|
||||
(set! (.-textContent node) "")))))
|
||||
|
||||
on-composition-end
|
||||
(mf/use-fn
|
||||
@ -83,7 +98,7 @@
|
||||
(reset! composing? false)
|
||||
(let [data (.-data event)]
|
||||
(when data
|
||||
(text-editor/text-editor-insert-text data)
|
||||
(text-editor/text-editor-composition-end data)
|
||||
(sync-wasm-text-editor-content!)
|
||||
(wasm.api/request-render "text-composition"))
|
||||
(when-let [node (mf/ref-val contenteditable-ref)]
|
||||
@ -326,6 +341,7 @@
|
||||
:contentEditable true
|
||||
:suppressContentEditableWarning true
|
||||
:on-composition-start on-composition-start
|
||||
:on-composition-update on-composition-update
|
||||
:on-composition-end on-composition-end
|
||||
:on-key-down on-key-down
|
||||
:on-input on-input
|
||||
|
||||
@ -7,11 +7,15 @@
|
||||
(ns app.main.ui.workspace.sidebar.options.menus.blur
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.features :as features]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
@ -31,6 +35,9 @@
|
||||
(mf/defc blur-menu [{:keys [ids type values]}]
|
||||
(let [blur (:blur values)
|
||||
has-value? (not (nil? blur))
|
||||
render-wasm? (features/use-feature "render-wasm/v1")
|
||||
bg-blur? (and render-wasm?
|
||||
(contains? cf/flags :background-blur))
|
||||
|
||||
state* (mf/use-state {:show-content true
|
||||
:show-more-options false})
|
||||
@ -75,12 +82,25 @@
|
||||
:always
|
||||
(assoc-in [:blur :value] value)))))
|
||||
|
||||
handle-type-change
|
||||
(mf/use-fn
|
||||
(mf/deps change! ids)
|
||||
(fn [value]
|
||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||
(change! #(assoc-in % [:blur :type] (keyword value)))))
|
||||
|
||||
handle-toggle-visibility
|
||||
(mf/use-fn
|
||||
(mf/deps change! ids)
|
||||
(fn []
|
||||
(st/emit! (udw/trigger-bounding-box-cloaking ids))
|
||||
(change! #(update-in % [:blur :hidden] not))))]
|
||||
(change! #(update-in % [:blur :hidden] not))))
|
||||
|
||||
type-options
|
||||
(mf/with-memo [bg-blur?]
|
||||
(cond-> [{:value "layer-blur" :label (tr "workspace.options.blur-options.layer-blur")}]
|
||||
bg-blur?
|
||||
(conj {:value "background-blur" :label (tr "workspace.options.blur-options.background-blur")})))]
|
||||
|
||||
[:div {:class (stl/css :element-set)}
|
||||
[:div {:class (stl/css :element-title)}
|
||||
@ -102,13 +122,20 @@
|
||||
[:div {:class (stl/css :element-set-content)}
|
||||
[:div {:class (stl/css-case :first-row true
|
||||
:hidden hidden?)}
|
||||
[:div {:class (stl/css :blur-info)}
|
||||
[:div {:class (stl/css :blur-info)
|
||||
:data-testid "blur-info"}
|
||||
[:button {:class (stl/css-case :show-more true
|
||||
:selected more-options?)
|
||||
:on-click toggle-more-options}
|
||||
deprecated-icon/menu]
|
||||
[:span {:class (stl/css :label)}
|
||||
(tr "workspace.options.blur-options.title")]]
|
||||
(if bg-blur?
|
||||
[:& select {:class (stl/css :blur-type-select)
|
||||
:default-value (d/name (:type blur))
|
||||
:options type-options
|
||||
:disabled hidden?
|
||||
:on-change handle-type-change}]
|
||||
[:span {:class (stl/css :label)}
|
||||
(tr "workspace.options.blur-options.title")])]
|
||||
[:div {:class (stl/css :actions)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "workspace.options.blur-options.toggle-blur")
|
||||
|
||||
@ -66,6 +66,10 @@
|
||||
box-sizing: border-box;
|
||||
border: deprecated.$s-1 solid var(--input-border-color);
|
||||
}
|
||||
.blur-type-select {
|
||||
flex-grow: 1;
|
||||
border-radius: 0 deprecated.$br-8 deprecated.$br-8 0;
|
||||
}
|
||||
}
|
||||
.actions {
|
||||
@include deprecated.flexRow;
|
||||
|
||||
@ -291,7 +291,7 @@
|
||||
:r4 "Bottom Left"
|
||||
:r3 "Bottom Right"}
|
||||
:hint (tr "workspace.tokens.radius")
|
||||
:on-update-shape-all dwta/update-shape-radius-all
|
||||
:on-update-shape-all dwta/update-shape-radius
|
||||
:on-update-shape update-shape-radius-for-corners})
|
||||
shadow (partial generic-attribute-actions #{:shadow} "Shadow")]
|
||||
{:border-radius border-radius
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.changes :as ch]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.helpers :as dsh]
|
||||
@ -84,6 +85,10 @@
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
|
||||
;; Public properties
|
||||
:version
|
||||
{:this true
|
||||
:get (constantly (:base cf/version))}
|
||||
|
||||
:root
|
||||
{:this true
|
||||
:get #(.getRoot ^js %)}
|
||||
@ -112,7 +117,7 @@
|
||||
(fn [_ shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :selection shapes)
|
||||
(u/not-valid plugin-id :selection shapes)
|
||||
|
||||
:else
|
||||
(let [ids (into (d/ordered-set) (map #(obj/get % "$id")) shapes)]
|
||||
@ -177,7 +182,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :shapesColors-shapes shapes)
|
||||
(u/not-valid plugin-id :shapesColors-shapes shapes)
|
||||
|
||||
:else
|
||||
(let [objects (u/locate-objects)
|
||||
@ -197,13 +202,13 @@
|
||||
new-color (parser/parse-color-data new-color)]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :replaceColor-shapes shapes)
|
||||
(u/not-valid plugin-id :replaceColor-shapes shapes)
|
||||
|
||||
(not (sm/validate ctc/schema:color old-color))
|
||||
(u/display-not-valid :replaceColor-oldColor old-color)
|
||||
(u/not-valid plugin-id :replaceColor-oldColor old-color)
|
||||
|
||||
(not (sm/validate ctc/schema:color new-color))
|
||||
(u/display-not-valid :replaceColor-newColor new-color)
|
||||
(u/not-valid plugin-id :replaceColor-newColor new-color)
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
@ -256,10 +261,10 @@
|
||||
(fn [name url]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :uploadMedia-name name)
|
||||
(u/not-valid plugin-id :uploadMedia-name name)
|
||||
|
||||
(not (string? url))
|
||||
(u/display-not-valid :uploadMedia-url url)
|
||||
(u/not-valid plugin-id :uploadMedia-url url)
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)]
|
||||
@ -290,7 +295,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :group-shapes shapes)
|
||||
(u/not-valid plugin-id :group-shapes shapes)
|
||||
|
||||
:else
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
@ -305,10 +310,10 @@
|
||||
(fn [group & rest]
|
||||
(cond
|
||||
(not (shape/shape-proxy? group))
|
||||
(u/display-not-valid :ungroup group)
|
||||
(u/not-valid plugin-id :ungroup group)
|
||||
|
||||
(and (some? rest) (not (every? shape/shape-proxy? rest)))
|
||||
(u/display-not-valid :ungroup rest)
|
||||
(u/not-valid plugin-id :ungroup rest)
|
||||
|
||||
:else
|
||||
(let [shapes (concat [group] rest)
|
||||
@ -348,7 +353,7 @@
|
||||
(fn [text]
|
||||
(cond
|
||||
(or (not (string? text)) (empty? text))
|
||||
(u/display-not-valid :createText text)
|
||||
(u/not-valid plugin-id :createText text)
|
||||
|
||||
:else
|
||||
(let [page (dsh/lookup-page @st/state)
|
||||
@ -379,7 +384,7 @@
|
||||
(fn [svg-string]
|
||||
(cond
|
||||
(or (not (string? svg-string)) (empty? svg-string))
|
||||
(u/display-not-valid :createShapeFromSvg svg-string)
|
||||
(u/not-valid plugin-id :createShapeFromSvg svg-string)
|
||||
|
||||
:else
|
||||
(let [id (uuid/next)
|
||||
@ -396,7 +401,7 @@
|
||||
(cond
|
||||
(or (not (string? svg-string)) (empty? svg-string))
|
||||
(do
|
||||
(u/display-not-valid :createShapeFromSvg "Svg not valid")
|
||||
(u/not-valid plugin-id :createShapeFromSvg "Svg not valid")
|
||||
(reject "Svg not valid"))
|
||||
|
||||
:else
|
||||
@ -414,10 +419,10 @@
|
||||
(let [bool-type (keyword bool-type)]
|
||||
(cond
|
||||
(not (contains? cts/bool-types bool-type))
|
||||
(u/display-not-valid :createBoolean-boolType bool-type)
|
||||
(u/not-valid plugin-id :createBoolean-boolType bool-type)
|
||||
|
||||
(or (not (array? shapes)) (empty? shapes) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :createBoolean-shapes shapes)
|
||||
(u/not-valid plugin-id :createBoolean-shapes shapes)
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)
|
||||
@ -431,10 +436,10 @@
|
||||
(let [type (d/nilv (obj/get options "type") "html")]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :generateMarkup-shapes shapes)
|
||||
(u/not-valid plugin-id :generateMarkup-shapes shapes)
|
||||
|
||||
(and (some? type) (not (contains? #{"html" "svg"} type)))
|
||||
(u/display-not-valid :generateMarkup-type type)
|
||||
(u/not-valid plugin-id :generateMarkup-type type)
|
||||
|
||||
:else
|
||||
(let [resolved-code
|
||||
@ -466,16 +471,16 @@
|
||||
children? (d/nilv (obj/get options "includeChildren") true)]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :generateStyle-shapes shapes)
|
||||
(u/not-valid plugin-id :generateStyle-shapes shapes)
|
||||
|
||||
(and (some? type) (not (contains? #{"css"} type)))
|
||||
(u/display-not-valid :generateStyle-type type)
|
||||
(u/not-valid plugin-id :generateStyle-type type)
|
||||
|
||||
(and (some? prelude?) (not (boolean? prelude?)))
|
||||
(u/display-not-valid :generateStyle-withPrelude prelude?)
|
||||
(u/not-valid plugin-id :generateStyle-withPrelude prelude?)
|
||||
|
||||
(and (some? children?) (not (boolean? children?)))
|
||||
(u/display-not-valid :generateStyle-includeChildren children?)
|
||||
(u/not-valid plugin-id :generateStyle-includeChildren children?)
|
||||
|
||||
:else
|
||||
(let [resolved-styles
|
||||
@ -548,7 +553,7 @@
|
||||
:else nil)
|
||||
new-window (if (boolean? new-window) new-window false)]
|
||||
(if (nil? id)
|
||||
(u/display-not-valid :openPage "Expected a Page object or a page UUID string")
|
||||
(u/not-valid plugin-id :openPage "Expected a Page object or a page UUID string")
|
||||
(st/emit! (dcm/go-to-workspace :page-id id ::rt/new-window new-window)))))
|
||||
|
||||
:alignHorizontal
|
||||
@ -560,10 +565,10 @@
|
||||
nil)]
|
||||
(cond
|
||||
(nil? dir)
|
||||
(u/display-not-valid :alignHorizontal-direction "Direction not valid")
|
||||
(u/not-valid plugin-id :alignHorizontal-direction "Direction not valid")
|
||||
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :alignHorizontal-shapes "Not valid shapes")
|
||||
(u/not-valid plugin-id :alignHorizontal-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
@ -578,10 +583,10 @@
|
||||
nil)]
|
||||
(cond
|
||||
(nil? dir)
|
||||
(u/display-not-valid :alignVertical-direction "Direction not valid")
|
||||
(u/not-valid plugin-id :alignVertical-direction "Direction not valid")
|
||||
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :alignVertical-shapes "Not valid shapes")
|
||||
(u/not-valid plugin-id :alignVertical-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
@ -591,7 +596,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :distributeHorizontal-shapes "Not valid shapes")
|
||||
(u/not-valid plugin-id :distributeHorizontal-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
@ -601,7 +606,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :distributeVertical-shapes "Not valid shapes")
|
||||
(u/not-valid plugin-id :distributeVertical-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
@ -611,7 +616,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
|
||||
(u/display-not-valid :flatten-shapes "Not valid shapes")
|
||||
(u/not-valid plugin-id :flatten-shapes "Not valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
|
||||
@ -622,7 +627,7 @@
|
||||
(cond
|
||||
(or (not (seq shapes))
|
||||
(not (every? u/is-main-component-proxy? shapes)))
|
||||
(u/display-not-valid :shapes shapes)
|
||||
(u/not-valid plugin-id :shapes shapes)
|
||||
|
||||
:else
|
||||
(let [file-id (obj/get (first shapes) "$file")
|
||||
|
||||
@ -60,13 +60,13 @@
|
||||
(let [profile (:profile @st/state)]
|
||||
(cond
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :content "Not valid")
|
||||
(u/not-valid plugin-id :content "Not valid")
|
||||
|
||||
(not= (:id profile) (:owner-id data))
|
||||
(u/display-not-valid :content "Cannot change content from another user's comments")
|
||||
(u/not-valid plugin-id :content "Cannot change content from another user's comments")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :content "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :content "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(->> (rp/cmd! :update-comment {:id (:id data) :content content})
|
||||
@ -81,7 +81,7 @@
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(do
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'comment:write' permission")
|
||||
(reject "Plugin doesn't have 'comment:write' permission"))
|
||||
|
||||
:else
|
||||
@ -120,10 +120,10 @@
|
||||
(cond
|
||||
(or (not (sm/valid-safe-number? (:x position)))
|
||||
(not (sm/valid-safe-number? (:y position))))
|
||||
(u/display-not-valid :position "Not valid point")
|
||||
(u/not-valid plugin-id :position "Not valid point")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :position "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)]))
|
||||
@ -137,10 +137,10 @@
|
||||
(fn [is-resolved]
|
||||
(cond
|
||||
(not (boolean? is-resolved))
|
||||
(u/display-not-valid :resolved "Not a boolean type")
|
||||
(u/not-valid plugin-id :resolved "Not a boolean type")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :resolved "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :resolved "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved)))
|
||||
@ -153,7 +153,7 @@
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:read"))
|
||||
(do
|
||||
(u/display-not-valid :findComments "Plugin doesn't have 'comment:read' permission")
|
||||
(u/not-valid plugin-id :findComments "Plugin doesn't have 'comment:read' permission")
|
||||
(reject "Plugin doesn't have 'comment:read' permission"))
|
||||
|
||||
:else
|
||||
@ -169,10 +169,10 @@
|
||||
(fn [content]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :reply "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :reply "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :reply "Not valid")
|
||||
(u/not-valid plugin-id :reply "Not valid")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
@ -186,10 +186,10 @@
|
||||
owner (dsh/lookup-profile @st/state (:owner-id data))]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
(not= (:id profile) owner)
|
||||
(u/display-not-valid :remove "Cannot change content from another user's comments")
|
||||
(u/not-valid plugin-id :remove "Cannot change content from another user's comments")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
|
||||
@ -45,10 +45,10 @@
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :label "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :label "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :label value)
|
||||
(u/not-valid plugin-id :label value)
|
||||
|
||||
:else
|
||||
(do (swap! data assoc :label value :created-by "user")
|
||||
@ -145,7 +145,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getPluginData-key key)
|
||||
(u/not-valid plugin-id :getPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)]
|
||||
@ -155,13 +155,13 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(or (not (string? key)) (empty? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(not (string? value))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data id :file (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -175,10 +175,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :getSharedPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)]
|
||||
@ -188,16 +188,16 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(or (not (string? namespace)) (empty? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(or (not (string? key)) (empty? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(not (string? value))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data id :file (keyword "shared" namespace) key value))))
|
||||
@ -206,7 +206,7 @@
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginDataKeys namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginDataKeys namespace)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file id)]
|
||||
@ -216,7 +216,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :createPage "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [page-id (uuid/next)]
|
||||
|
||||
@ -6,17 +6,11 @@
|
||||
|
||||
(ns app.plugins.flags
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn natural-child-ordering?
|
||||
[plugin-id]
|
||||
(boolean
|
||||
(dm/get-in @st/state [:plugins :flags plugin-id :natural-child-ordering])))
|
||||
|
||||
(defn clear
|
||||
[id]
|
||||
(ptk/reify ::reset
|
||||
@ -37,13 +31,27 @@
|
||||
:naturalChildOrdering
|
||||
{:this false
|
||||
:get
|
||||
(fn [] (natural-child-ordering? plugin-id))
|
||||
(fn [] (u/natural-child-ordering? plugin-id))
|
||||
|
||||
:set
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :naturalChildOrdering value)
|
||||
(u/not-valid plugin-id :naturalChildOrdering value)
|
||||
|
||||
:else
|
||||
(st/emit! (set-flag plugin-id :natural-child-ordering value))))}))
|
||||
(st/emit! (set-flag plugin-id :natural-child-ordering value))))}
|
||||
|
||||
:throwValidationErrors
|
||||
{:this false
|
||||
:get
|
||||
(fn [] (u/throw-validation-errors? plugin-id))
|
||||
|
||||
:set
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/not-valid plugin-id :throwValidationErrors value)
|
||||
|
||||
:else
|
||||
(st/emit! (set-flag plugin-id :throw-validation-errors value))))}))
|
||||
|
||||
@ -12,7 +12,6 @@
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.flags :refer [natural-child-ordering?]]
|
||||
[app.plugins.register :as r]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]))
|
||||
@ -39,10 +38,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/flex-direction-types value))
|
||||
(u/display-not-valid :dir value)
|
||||
(u/not-valid plugin-id :dir value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :dir "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :dir "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value})))))}
|
||||
@ -55,10 +54,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/wrap-types value))
|
||||
(u/display-not-valid :wrap value)
|
||||
(u/not-valid plugin-id :wrap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :wrap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value})))))}
|
||||
@ -71,10 +70,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-items-types value))
|
||||
(u/display-not-valid :alignItems value)
|
||||
(u/not-valid plugin-id :alignItems value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :alignItems "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
|
||||
@ -87,10 +86,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-content-types value))
|
||||
(u/display-not-valid :alignContent value)
|
||||
(u/not-valid plugin-id :alignContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :alignContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
|
||||
@ -103,10 +102,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-items-types value))
|
||||
(u/display-not-valid :justifyItems value)
|
||||
(u/not-valid plugin-id :justifyItems value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :justifyItems "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
|
||||
@ -119,10 +118,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-content-types value))
|
||||
(u/display-not-valid :justifyContent value)
|
||||
(u/not-valid plugin-id :justifyContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :justifyContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
|
||||
@ -134,10 +133,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rowGap value)
|
||||
(u/not-valid plugin-id :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rowGap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))}
|
||||
@ -149,10 +148,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :columnGap value)
|
||||
(u/not-valid plugin-id :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :columnGap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))}
|
||||
@ -164,10 +163,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :verticalPadding value)
|
||||
(u/not-valid plugin-id :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))}
|
||||
@ -179,10 +178,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
(u/not-valid plugin-id :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))}
|
||||
@ -195,10 +194,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :topPadding value)
|
||||
(u/not-valid plugin-id :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :topPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))}
|
||||
@ -210,10 +209,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rightPadding value)
|
||||
(u/not-valid plugin-id :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rightPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))}
|
||||
@ -225,10 +224,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :bottomPadding value)
|
||||
(u/not-valid plugin-id :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :bottomPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))}
|
||||
@ -240,10 +239,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :leftPadding value)
|
||||
(u/not-valid plugin-id :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :leftPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))}
|
||||
@ -256,13 +255,13 @@
|
||||
(fn [child]
|
||||
(cond
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild child)
|
||||
(u/not-valid plugin-id :appendChild child)
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")
|
||||
shape (u/locate-shape file-id page-id id)
|
||||
index
|
||||
(if (and (natural-child-ordering? plugin-id) (not (ctl/reverse? shape)))
|
||||
(if (and (u/natural-child-ordering? plugin-id) (not (ctl/reverse? shape)))
|
||||
0
|
||||
(count (:shapes shape)))]
|
||||
(st/emit! (dwsh/relocate-shapes #{child-id} id index)))))
|
||||
@ -275,10 +274,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-h-sizing-types value))
|
||||
(u/display-not-valid :horizontalSizing value)
|
||||
(u/not-valid plugin-id :horizontalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalSizing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :horizontalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-h-sizing value})))))}
|
||||
@ -291,10 +290,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-v-sizing-types value))
|
||||
(u/display-not-valid :verticalSizing value)
|
||||
(u/not-valid plugin-id :verticalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-v-sizing value})))))}))
|
||||
@ -317,10 +316,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :absolute value)
|
||||
(u/not-valid plugin-id :absolute value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :absolute "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value}))))}
|
||||
@ -332,10 +331,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(sm/valid-safe-int? value)
|
||||
(u/display-not-valid :zIndex value)
|
||||
(u/not-valid plugin-id :zIndex value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :zIndex "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value}))))}
|
||||
@ -348,10 +347,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-h-sizing-types value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
(u/not-valid plugin-id :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))}
|
||||
@ -364,10 +363,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-v-sizing-types value))
|
||||
(u/display-not-valid :verticalSizing value)
|
||||
(u/not-valid plugin-id :verticalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))}
|
||||
@ -380,10 +379,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/item-align-self-types value))
|
||||
(u/display-not-valid :alignSelf value)
|
||||
(u/not-valid plugin-id :alignSelf value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :alignSelf "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value})))))}
|
||||
@ -395,10 +394,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :verticalMargin value)
|
||||
(u/not-valid plugin-id :verticalMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}}))))}
|
||||
@ -410,10 +409,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :horizontalMargin value)
|
||||
(u/not-valid plugin-id :horizontalMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :horizontalMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}}))))}
|
||||
@ -425,10 +424,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :topMargin value)
|
||||
(u/not-valid plugin-id :topMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :topMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}}))))}
|
||||
@ -440,10 +439,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :rightMargin value)
|
||||
(u/not-valid plugin-id :rightMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rightMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}}))))}
|
||||
@ -455,10 +454,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :bottomMargin value)
|
||||
(u/not-valid plugin-id :bottomMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :bottomMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}}))))}
|
||||
@ -470,10 +469,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :leftMargin value)
|
||||
(u/not-valid plugin-id :leftMargin value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :leftMargin "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}}))))}
|
||||
@ -485,10 +484,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :maxWidth value)
|
||||
(u/not-valid plugin-id :maxWidth value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :maxWidth "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value}))))}
|
||||
@ -500,10 +499,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :minWidth value)
|
||||
(u/not-valid plugin-id :minWidth value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :minWidth "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value}))))}
|
||||
@ -515,10 +514,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :maxHeight value)
|
||||
(u/not-valid plugin-id :maxHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :maxHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value}))))}
|
||||
@ -530,10 +529,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :minHeight value)
|
||||
(u/not-valid plugin-id :minHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :minHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value}))))}))
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
(obj/type-of? p "FontProxy"))
|
||||
|
||||
(defn font-proxy
|
||||
[{:keys [id family name variants] :as font}]
|
||||
[plugin-id {:keys [id family name variants] :as font}]
|
||||
(when (some? font)
|
||||
(let [default-variant (fonts/get-default-variant font)]
|
||||
(obj/reify {:name "FontProxy"}
|
||||
@ -55,10 +55,10 @@
|
||||
(fn [text variant]
|
||||
(cond
|
||||
(not (shape/shape-proxy? text))
|
||||
(u/display-not-valid :applyToText text)
|
||||
(u/not-valid plugin-id :applyToText text)
|
||||
|
||||
(not (r/check-permission (obj/get text "$plugin") "content:write"))
|
||||
(u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get text "$id")
|
||||
@ -73,10 +73,10 @@
|
||||
(fn [range variant]
|
||||
(cond
|
||||
(not (text/text-range-proxy? range))
|
||||
(u/display-not-valid :applyToRange range)
|
||||
(u/not-valid plugin-id :applyToRange range)
|
||||
|
||||
(not (r/check-permission (obj/get range "$plugin") "content:write"))
|
||||
(u/display-not-valid :applyToRange "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :applyToRange "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get range "$id")
|
||||
@ -98,53 +98,53 @@
|
||||
{:get
|
||||
(fn []
|
||||
(format/format-array
|
||||
font-proxy
|
||||
(partial font-proxy plugin-id)
|
||||
(vals @fonts/fontsdb)))}
|
||||
|
||||
:findById
|
||||
(fn [id]
|
||||
(cond
|
||||
(not (string? id))
|
||||
(u/display-not-valid :findbyId id)
|
||||
(u/not-valid plugin-id :findbyId id)
|
||||
|
||||
:else
|
||||
(->> (vals @fonts/fontsdb)
|
||||
(d/seek #(str/includes? (str/lower (:id %)) (str/lower id)))
|
||||
(font-proxy))))
|
||||
(font-proxy plugin-id))))
|
||||
|
||||
:findByName
|
||||
(fn [name]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :findByName name)
|
||||
(u/not-valid plugin-id :findByName name)
|
||||
|
||||
:else
|
||||
(->> (vals @fonts/fontsdb)
|
||||
(d/seek #(str/includes? (str/lower (:name %)) (str/lower name)))
|
||||
(font-proxy))))
|
||||
(font-proxy plugin-id))))
|
||||
|
||||
:findAllById
|
||||
(fn [id]
|
||||
(cond
|
||||
(not (string? id))
|
||||
(u/display-not-valid :findAllById name)
|
||||
(u/not-valid plugin-id :findAllById name)
|
||||
|
||||
:else
|
||||
(format/format-array
|
||||
(fn [font]
|
||||
(when (str/includes? (str/lower (:id font)) (str/lower id))
|
||||
(font-proxy font)))
|
||||
(font-proxy plugin-id font)))
|
||||
(vals @fonts/fontsdb))))
|
||||
|
||||
:findAllByName
|
||||
(fn [name]
|
||||
(cond
|
||||
(not (string? name))
|
||||
(u/display-not-valid :findAllByName name)
|
||||
(u/not-valid plugin-id :findAllByName name)
|
||||
|
||||
:else
|
||||
(format/format-array
|
||||
(fn [font]
|
||||
(when (str/includes? (str/lower (:name font)) (str/lower name))
|
||||
(font-proxy font)))
|
||||
(font-proxy plugin-id font)))
|
||||
(vals @fonts/fontsdb))))))
|
||||
|
||||
@ -40,10 +40,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/grid-direction-types value))
|
||||
(u/display-not-valid :dir value)
|
||||
(u/not-valid plugin-id :dir value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :dir "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :dir "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-grid-dir value})))))}
|
||||
@ -64,10 +64,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-items-types value))
|
||||
(u/display-not-valid :alignItems value)
|
||||
(u/not-valid plugin-id :alignItems value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :alignItems "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
|
||||
@ -80,10 +80,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/align-content-types value))
|
||||
(u/display-not-valid :alignContent value)
|
||||
(u/not-valid plugin-id :alignContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :alignContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
|
||||
@ -96,10 +96,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-items-types value))
|
||||
(u/display-not-valid :justifyItems value)
|
||||
(u/not-valid plugin-id :justifyItems value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :justifyItems "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
|
||||
@ -112,10 +112,10 @@
|
||||
(let [value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/justify-content-types value))
|
||||
(u/display-not-valid :justifyContent value)
|
||||
(u/not-valid plugin-id :justifyContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :justifyContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
|
||||
@ -127,10 +127,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rowGap value)
|
||||
(u/not-valid plugin-id :rowGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rowGap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))}
|
||||
@ -142,10 +142,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :columnGap value)
|
||||
(u/not-valid plugin-id :columnGap value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :columnGap "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))}
|
||||
@ -157,10 +157,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :verticalPadding value)
|
||||
(u/not-valid plugin-id :verticalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))}
|
||||
@ -172,10 +172,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :horizontalPadding value)
|
||||
(u/not-valid plugin-id :horizontalPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :horizontalPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))}
|
||||
@ -187,10 +187,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :topPadding value)
|
||||
(u/not-valid plugin-id :topPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :topPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))}
|
||||
@ -202,10 +202,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rightPadding value)
|
||||
(u/not-valid plugin-id :rightPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :righPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :righPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))}
|
||||
@ -217,10 +217,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :bottomPadding value)
|
||||
(u/not-valid plugin-id :bottomPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :bottomPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))}
|
||||
@ -232,10 +232,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :leftPadding value)
|
||||
(u/not-valid plugin-id :leftPadding value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :leftPadding "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))}
|
||||
@ -245,14 +245,14 @@
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :addRow-type type)
|
||||
(u/not-valid plugin-id :addRow-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addRow-value value)
|
||||
(u/not-valid plugin-id :addRow-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addRow "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addRow "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value})))))
|
||||
@ -262,17 +262,17 @@
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :addRowAtIndex-index index)
|
||||
(u/not-valid plugin-id :addRowAtIndex-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :addRowAtIndex-type type)
|
||||
(u/not-valid plugin-id :addRowAtIndex-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addRowAtIndex-value value)
|
||||
(u/not-valid plugin-id :addRowAtIndex-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addRowAtIndex "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addRowAtIndex "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/add-layout-track #{id} :row {:type type :value value} index)))))
|
||||
@ -282,14 +282,14 @@
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :addColumn-type type)
|
||||
(u/not-valid plugin-id :addColumn-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :lex type))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addColumn-value value)
|
||||
(u/not-valid plugin-id :addColumn-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addColumn "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addColumn "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/add-layout-track #{id} :column {:type type :value value})))))
|
||||
@ -298,17 +298,17 @@
|
||||
(fn [index type value]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :addColumnAtIndex-index index)
|
||||
(u/not-valid plugin-id :addColumnAtIndex-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :addColumnAtIndex-type type)
|
||||
(u/not-valid plugin-id :addColumnAtIndex-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :addColumnAtIndex-value value)
|
||||
(u/not-valid plugin-id :addColumnAtIndex-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addColumnAtIndex "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addColumnAtIndex "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [type (keyword type)]
|
||||
@ -318,10 +318,10 @@
|
||||
(fn [index]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :removeRow index)
|
||||
(u/not-valid plugin-id :removeRow index)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :removeRow "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :removeRow "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/remove-layout-track #{id} :row index))))
|
||||
@ -330,10 +330,10 @@
|
||||
(fn [index]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :removeColumn index)
|
||||
(u/not-valid plugin-id :removeColumn index)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :removeColumn "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :removeColumn "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/remove-layout-track #{id} :column index))))
|
||||
@ -343,17 +343,17 @@
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :setColumn-index index)
|
||||
(u/not-valid plugin-id :setColumn-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :setColumn-type type)
|
||||
(u/not-valid plugin-id :setColumn-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :setColumn-value value)
|
||||
(u/not-valid plugin-id :setColumn-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setColumn "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setColumn "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/change-layout-track #{id} :column index (d/without-nils {:type type :value value}))))))
|
||||
@ -363,17 +363,17 @@
|
||||
(let [type (keyword type)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :setRow-index index)
|
||||
(u/not-valid plugin-id :setRow-index index)
|
||||
|
||||
(not (contains? ctl/grid-track-types type))
|
||||
(u/display-not-valid :setRow-type type)
|
||||
(u/not-valid plugin-id :setRow-type type)
|
||||
|
||||
(and (or (= :percent type) (= :flex type) (= :fixed type))
|
||||
(not (sm/valid-safe-number? value)))
|
||||
(u/display-not-valid :setRow-value value)
|
||||
(u/not-valid plugin-id :setRow-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setRow "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setRow "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/change-layout-track #{id} :row index (d/without-nils {:type type :value value}))))))
|
||||
@ -382,7 +382,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/remove-layout #{id}))))
|
||||
@ -391,16 +391,16 @@
|
||||
(fn [child row column]
|
||||
(cond
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild-child child)
|
||||
(u/not-valid plugin-id :appendChild-child child)
|
||||
|
||||
(or (< row 0) (not (sm/valid-safe-int? row)))
|
||||
(u/display-not-valid :appendChild-row row)
|
||||
(u/not-valid plugin-id :appendChild-row row)
|
||||
|
||||
(or (< column 0) (not (sm/valid-safe-int? column)))
|
||||
(u/display-not-valid :appendChild-column column)
|
||||
(u/not-valid plugin-id :appendChild-column column)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :appendChild "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")]
|
||||
@ -432,13 +432,13 @@
|
||||
shape (u/proxy->shape self)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :row-value value)
|
||||
(u/not-valid plugin-id :row-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :row-cell "cell not found")
|
||||
(u/not-valid plugin-id :row-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :row "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :row "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row value})))))}
|
||||
@ -452,13 +452,13 @@
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :rowSpan-value value)
|
||||
(u/not-valid plugin-id :rowSpan-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :rowSpan-cell "cell not found")
|
||||
(u/not-valid plugin-id :rowSpan-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rowSpan "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rowSpan "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:row-span value})))))}
|
||||
@ -472,13 +472,13 @@
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :column-value value)
|
||||
(u/not-valid plugin-id :column-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :column-cell "cell not found")
|
||||
(u/not-valid plugin-id :column-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :column "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :column "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column value})))))}
|
||||
@ -492,13 +492,13 @@
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :columnSpan-value value)
|
||||
(u/not-valid plugin-id :columnSpan-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :columnSpan-cell "cell not found")
|
||||
(u/not-valid plugin-id :columnSpan-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :columnSpan "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :columnSpan "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cell-position (:parent-id shape) (:id cell) {:column-span value})))))}
|
||||
@ -512,13 +512,13 @@
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :areaName-value value)
|
||||
(u/not-valid plugin-id :areaName-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :areaName-cell "cell not found")
|
||||
(u/not-valid plugin-id :areaName-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :areaName "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :areaName "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:area-name value})))))}
|
||||
@ -533,13 +533,13 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? ctl/grid-position-types value))
|
||||
(u/display-not-valid :position-value value)
|
||||
(u/not-valid plugin-id :position-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :position-cell "cell not found")
|
||||
(u/not-valid plugin-id :position-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :position "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/change-cells-mode (:parent-id shape) #{(:id cell)} value)))))}
|
||||
@ -554,13 +554,13 @@
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (contains? ctl/grid-cell-align-self-types value))
|
||||
(u/display-not-valid :alignSelf-value value)
|
||||
(u/not-valid plugin-id :alignSelf-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :alignSelf-cell "cell not found")
|
||||
(u/not-valid plugin-id :alignSelf-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :alignSelf "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:align-self value})))))}
|
||||
@ -575,13 +575,13 @@
|
||||
cell (locate-cell self)]
|
||||
(cond
|
||||
(not (contains? ctl/grid-cell-justify-self-types value))
|
||||
(u/display-not-valid :justifySelf-value value)
|
||||
(u/not-valid plugin-id :justifySelf-value value)
|
||||
|
||||
(nil? cell)
|
||||
(u/display-not-valid :justifySelf-cell "cell not found")
|
||||
(u/not-valid plugin-id :justifySelf-cell "cell not found")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :justifySelf "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :justifySelf "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-grid-cells (:parent-id shape) #{(:id cell)} {:justify-self value})))))})))
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (js/Symbol)]
|
||||
@ -35,10 +35,10 @@
|
||||
(fn [block-id]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not block-id)
|
||||
(u/display-not-valid :undoBlockFinish block-id)
|
||||
(u/not-valid plugin-id :undoBlockFinish block-id)
|
||||
|
||||
:else
|
||||
(st/emit! (dwu/commit-undo-transaction block-id))))))
|
||||
|
||||
@ -60,10 +60,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :name "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color (u/proxy->library-color self)
|
||||
@ -77,10 +77,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :path value)
|
||||
(u/not-valid plugin-id :path value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :path "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :path "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color (-> (u/proxy->library-color self)
|
||||
@ -94,10 +94,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(or (not (string? value)) (not (clr/valid-hex-color? value)))
|
||||
(u/display-not-valid :color value)
|
||||
(u/not-valid plugin-id :color value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :color "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :color "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color (-> (u/proxy->library-color self)
|
||||
@ -111,10 +111,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(or (not (number? value)) (< value 0) (> value 1))
|
||||
(u/display-not-valid :opacity value)
|
||||
(u/not-valid plugin-id :opacity value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :opacity "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :opacity "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color (-> (u/proxy->library-color self)
|
||||
@ -129,10 +129,10 @@
|
||||
(let [value (parser/parse-gradient value)]
|
||||
(cond
|
||||
(not (sm/validate clr/schema:gradient value))
|
||||
(u/display-not-valid :gradient value)
|
||||
(u/not-valid plugin-id :gradient value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :gradient "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :gradient "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color (-> (u/proxy->library-color self)
|
||||
@ -147,10 +147,10 @@
|
||||
(let [value (parser/parse-image-data value)]
|
||||
(cond
|
||||
(not (sm/validate clr/schema:image value))
|
||||
(u/display-not-valid :image value)
|
||||
(u/not-valid plugin-id :image value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :image "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :image "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color (-> (u/proxy->library-color self)
|
||||
@ -161,7 +161,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwl/delete-color {:id id}))))
|
||||
@ -170,7 +170,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :clone "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :clone "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color-id (uuid/next)
|
||||
@ -207,7 +207,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getPluginData-key key)
|
||||
(u/not-valid plugin-id :getPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [color (u/locate-library-color file-id id)]
|
||||
@ -217,16 +217,16 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not= file-id (:current-file-id @st/state))
|
||||
(u/display-not-valid :setPluginData-non-local-library file-id)
|
||||
(u/not-valid plugin-id :setPluginData-non-local-library file-id)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :color id (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -240,10 +240,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :getSharedPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [color (u/locate-library-color file-id id)]
|
||||
@ -253,19 +253,19 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not= file-id (:current-file-id @st/state))
|
||||
(u/display-not-valid :setSharedPluginData-non-local-library file-id)
|
||||
(u/not-valid plugin-id :setSharedPluginData-non-local-library file-id)
|
||||
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :color id (keyword "shared" namespace) key value))))
|
||||
@ -274,7 +274,7 @@
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginDataKeys-namespace namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginDataKeys-namespace namespace)
|
||||
|
||||
:else
|
||||
(let [color (u/locate-library-color file-id id)]
|
||||
@ -301,10 +301,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :name "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (u/proxy->library-typography self)
|
||||
@ -318,10 +318,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :path value)
|
||||
(u/not-valid plugin-id :path value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :path "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :path "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -335,10 +335,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontId value)
|
||||
(u/not-valid plugin-id :fontId value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :fontId "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :fontId "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -352,10 +352,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
(u/not-valid plugin-id :fontFamily value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :fontFamily "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :fontFamily "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -369,10 +369,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
(u/not-valid plugin-id :fontVariantId value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :fontVariantId "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :fontVariantId "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -386,10 +386,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontSize value)
|
||||
(u/not-valid plugin-id :fontSize value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :fontSize "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :fontSize "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -403,10 +403,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontWeight value)
|
||||
(u/not-valid plugin-id :fontWeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :fontWeight "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :fontWeight "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -420,10 +420,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontStyle value)
|
||||
(u/not-valid plugin-id :fontStyle value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :fontStyle "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :fontStyle "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -437,10 +437,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
(u/not-valid plugin-id :lineHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :lineHeight "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :lineHeight "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -454,10 +454,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
(u/not-valid plugin-id :letterSpacing value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :letterSpacing "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :letterSpacing "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -471,10 +471,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :textTransform value)
|
||||
(u/not-valid plugin-id :textTransform value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :textTransform "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :textTransform "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo (-> (u/proxy->library-typography self)
|
||||
@ -485,7 +485,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwl/delete-typography {:id id}))))
|
||||
@ -494,7 +494,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :clone "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :clone "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typo-id (uuid/next)
|
||||
@ -507,10 +507,10 @@
|
||||
(fn [shape]
|
||||
(cond
|
||||
(not (shape/shape-proxy? shape))
|
||||
(u/display-not-valid :applyToText shape)
|
||||
(u/not-valid plugin-id :applyToText shape)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [shape-id (obj/get shape "$id")
|
||||
@ -521,10 +521,10 @@
|
||||
(fn [range]
|
||||
(cond
|
||||
(not (text/text-range-proxy? range))
|
||||
(u/display-not-valid :applyToText range)
|
||||
(u/not-valid plugin-id :applyToText range)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :applyToText "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [shape-id (obj/get range "$id")
|
||||
@ -542,7 +542,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :typography-plugin-data-key key)
|
||||
(u/not-valid plugin-id :typography-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [typography (u/locate-library-typography file-id id)]
|
||||
@ -552,16 +552,16 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not= file-id (:current-file-id @st/state))
|
||||
(u/display-not-valid :setPluginData-non-local-library file-id)
|
||||
(u/not-valid plugin-id :setPluginData-non-local-library file-id)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :typography id (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -575,10 +575,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :getSharedPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [typography (u/locate-library-typography file-id id)]
|
||||
@ -588,19 +588,19 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not= file-id (:current-file-id @st/state))
|
||||
(u/display-not-valid :setSharedPluginData-non-local-library file-id)
|
||||
(u/not-valid plugin-id :setSharedPluginData-non-local-library file-id)
|
||||
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :typography id (keyword "shared" namespace) key value))))
|
||||
@ -609,7 +609,7 @@
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginDataKeys-namespace namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginDataKeys-namespace namespace)
|
||||
|
||||
:else
|
||||
(let [typography (u/locate-library-typography file-id id)]
|
||||
@ -674,7 +674,7 @@
|
||||
:removeProperty
|
||||
(fn [pos]
|
||||
(if (not (nat-int? pos))
|
||||
(u/display-not-valid :pos pos)
|
||||
(u/not-valid plugin-id :pos pos)
|
||||
(st/emit!
|
||||
(ev/event {::ev/name "remove-property" ::ev/origin "plugin:remove-property"})
|
||||
(dwv/remove-property id pos))))
|
||||
@ -683,10 +683,10 @@
|
||||
(fn [pos name]
|
||||
(cond
|
||||
(not (nat-int? pos))
|
||||
(u/display-not-valid :pos pos)
|
||||
(u/not-valid plugin-id :pos pos)
|
||||
|
||||
(not (string? name))
|
||||
(u/display-not-valid :name name)
|
||||
(u/not-valid plugin-id :name name)
|
||||
|
||||
:else
|
||||
(st/emit!
|
||||
@ -715,10 +715,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :name "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [component (u/proxy->library-component self)
|
||||
@ -732,10 +732,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :path value)
|
||||
(u/not-valid plugin-id :path value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :path "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :path "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [component (u/proxy->library-component self)
|
||||
@ -746,7 +746,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwl/delete-component {:id id}))))
|
||||
@ -755,7 +755,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :instance "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :instance "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id-ref (atom nil)]
|
||||
@ -766,7 +766,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :component-plugin-data-key key)
|
||||
(u/not-valid plugin-id :component-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [component (u/locate-library-component file-id id)]
|
||||
@ -776,16 +776,16 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not= file-id (:current-file-id @st/state))
|
||||
(u/display-not-valid :setPluginData-non-local-library file-id)
|
||||
(u/not-valid plugin-id :setPluginData-non-local-library file-id)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :component id (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -799,10 +799,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :component-plugin-data-namespace namespace)
|
||||
(u/not-valid plugin-id :component-plugin-data-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :component-plugin-data-key key)
|
||||
(u/not-valid plugin-id :component-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [component (u/locate-library-component file-id id)]
|
||||
@ -812,19 +812,19 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not= file-id (:current-file-id @st/state))
|
||||
(u/display-not-valid :setSharedPluginData-non-local-library file-id)
|
||||
(u/not-valid plugin-id :setSharedPluginData-non-local-library file-id)
|
||||
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :component id (keyword "shared" namespace) key value))))
|
||||
@ -833,7 +833,7 @@
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :component-plugin-data-namespace namespace)
|
||||
(u/not-valid plugin-id :component-plugin-data-namespace namespace)
|
||||
|
||||
:else
|
||||
(let [component (u/locate-library-component file-id id)]
|
||||
@ -901,10 +901,10 @@
|
||||
(fn [pos value]
|
||||
(cond
|
||||
(not (nat-int? pos))
|
||||
(u/display-not-valid :pos (str pos))
|
||||
(u/not-valid plugin-id :pos (str pos))
|
||||
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
:else
|
||||
(st/emit!
|
||||
@ -970,7 +970,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :createColor "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :createColor "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [color-id (uuid/next)]
|
||||
@ -981,7 +981,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :createTypography "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :createTypography "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [typography-id (uuid/next)]
|
||||
@ -992,7 +992,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :createComponent "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :createComponent "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(let [id-ref (atom nil)
|
||||
@ -1005,7 +1005,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :file-plugin-data-key key)
|
||||
(u/not-valid plugin-id :file-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file file-id)]
|
||||
@ -1015,13 +1015,13 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :file (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -1035,10 +1035,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :file-plugin-data-namespace namespace)
|
||||
(u/not-valid plugin-id :file-plugin-data-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :file-plugin-data-key key)
|
||||
(u/not-valid plugin-id :file-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file file-id)]
|
||||
@ -1048,16 +1048,16 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :file (keyword "shared" namespace) key value))))
|
||||
@ -1066,7 +1066,7 @@
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :namespace namespace)
|
||||
(u/not-valid plugin-id :namespace namespace)
|
||||
|
||||
:else
|
||||
(let [file (u/locate-file file-id)]
|
||||
@ -1110,14 +1110,14 @@
|
||||
(fn [library-id]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "library:write"))
|
||||
(u/display-not-valid :connectLibrary "Plugin doesn't have 'library:write' permission")
|
||||
(u/not-valid plugin-id :connectLibrary "Plugin doesn't have 'library:write' permission")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
(fn [resolve reject]
|
||||
(cond
|
||||
(not (string? library-id))
|
||||
(do (u/display-not-valid :connectLibrary library-id)
|
||||
(do (u/not-valid plugin-id :connectLibrary library-id)
|
||||
(reject nil))
|
||||
|
||||
:else
|
||||
|
||||
@ -30,10 +30,10 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "allow:localstorage"))
|
||||
(u/display-not-valid :getItem "Plugin doesn't have 'allow:localstorage' permission")
|
||||
(u/not-valid plugin-id :getItem "Plugin doesn't have 'allow:localstorage' permission")
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getItem "The key must be a string")
|
||||
(u/not-valid plugin-id :getItem "The key must be a string")
|
||||
|
||||
:else
|
||||
(.getItem ^js local-storage (prefix-key plugin-id key))))
|
||||
@ -42,10 +42,10 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "allow:localstorage"))
|
||||
(u/display-not-valid :setItem "Plugin doesn't have 'allow:localstorage' permission")
|
||||
(u/not-valid plugin-id :setItem "Plugin doesn't have 'allow:localstorage' permission")
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setItem "The key must be a string")
|
||||
(u/not-valid plugin-id :setItem "The key must be a string")
|
||||
|
||||
:else
|
||||
(.setItem ^js local-storage (prefix-key plugin-id key) value)))
|
||||
@ -54,10 +54,10 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "allow:localstorage"))
|
||||
(u/display-not-valid :removeItem "Plugin doesn't have 'allow:localstorage' permission")
|
||||
(u/not-valid plugin-id :removeItem "Plugin doesn't have 'allow:localstorage' permission")
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :removeItem "The key must be a string")
|
||||
(u/not-valid plugin-id :removeItem "The key must be a string")
|
||||
|
||||
:else
|
||||
(.getItem ^js local-storage (prefix-key plugin-id key))))
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
|
||||
@ -74,7 +74,7 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (shape/shape-proxy? value))
|
||||
(u/display-not-valid :startingBoard value)
|
||||
(u/not-valid plugin-id :startingBoard value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}
|
||||
@ -103,10 +103,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/rename-page id value))))}
|
||||
@ -127,10 +127,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (string? value)) (not (cc/valid-hex-color? value)))
|
||||
(u/display-not-valid :background value)
|
||||
(u/not-valid plugin-id :background value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :background "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :background "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/change-canvas-color id {:color value}))))}
|
||||
@ -158,7 +158,7 @@
|
||||
(fn [shape-id]
|
||||
(cond
|
||||
(not (string? shape-id))
|
||||
(u/display-not-valid :getShapeById shape-id)
|
||||
(u/not-valid plugin-id :getShapeById shape-id)
|
||||
|
||||
:else
|
||||
(let [shape-id (uuid/parse shape-id)
|
||||
@ -195,7 +195,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :page-plugin-data-key key)
|
||||
(u/not-valid plugin-id :page-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [page (u/locate-page file-id id)]
|
||||
@ -205,13 +205,13 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :page id (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -225,10 +225,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :page-plugin-data-namespace namespace)
|
||||
(u/not-valid plugin-id :page-plugin-data-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :page-plugin-data-key key)
|
||||
(u/not-valid plugin-id :page-plugin-data-key key)
|
||||
|
||||
:else
|
||||
(let [page (u/locate-page file-id id)]
|
||||
@ -238,16 +238,16 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :page id (keyword "shared" namespace) key value))))
|
||||
@ -256,7 +256,7 @@
|
||||
(fn [self namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :page-plugin-data-namespace namespace)
|
||||
(u/not-valid plugin-id :page-plugin-data-namespace namespace)
|
||||
|
||||
:else
|
||||
(let [page (u/proxy->page self)]
|
||||
@ -266,7 +266,7 @@
|
||||
(fn [new-window]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:read"))
|
||||
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
|
||||
(u/not-valid plugin-id :openPage "Plugin doesn't have 'content:read' permission")
|
||||
|
||||
:else
|
||||
(let [new-window (if (boolean? new-window) new-window false)]
|
||||
@ -276,10 +276,10 @@
|
||||
(fn [name frame]
|
||||
(cond
|
||||
(or (not (string? name)) (empty? name))
|
||||
(u/display-not-valid :createFlow-name name)
|
||||
(u/not-valid plugin-id :createFlow-name name)
|
||||
|
||||
(not (shape/shape-proxy? frame))
|
||||
(u/display-not-valid :createFlow-frame frame)
|
||||
(u/not-valid plugin-id :createFlow-frame frame)
|
||||
|
||||
:else
|
||||
(let [flow-id (uuid/next)]
|
||||
@ -290,7 +290,7 @@
|
||||
(fn [flow]
|
||||
(cond
|
||||
(not (flow-proxy? flow))
|
||||
(u/display-not-valid :removeFlow-flow flow)
|
||||
(u/not-valid plugin-id :removeFlow-flow flow)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/remove-flow id (obj/get flow "$id")))))
|
||||
@ -300,18 +300,18 @@
|
||||
(let [shape (u/proxy->shape board)]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :addRulerGuide "Value not a safe number")
|
||||
(u/not-valid plugin-id :addRulerGuide "Value not a safe number")
|
||||
|
||||
(not (contains? #{"vertical" "horizontal"} orientation))
|
||||
(u/display-not-valid :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
|
||||
(u/not-valid plugin-id :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
|
||||
|
||||
(and (some? shape)
|
||||
(or (not (shape/shape-proxy? board))
|
||||
(not (cfh/frame-shape? shape))))
|
||||
(u/display-not-valid :addRulerGuide "The shape is not a board")
|
||||
(u/not-valid plugin-id :addRulerGuide "The shape is not a board")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [ruler-id (uuid/next)]
|
||||
@ -328,10 +328,10 @@
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (rg/ruler-guide-proxy? value))
|
||||
(u/display-not-valid :removeRulerGuide "Guide not provided")
|
||||
(u/not-valid plugin-id :removeRulerGuide "Guide not provided")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :removeRulerGuide "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide value)]
|
||||
@ -343,17 +343,17 @@
|
||||
position (parser/parse-point position)]
|
||||
(cond
|
||||
(or (not (string? content)) (empty? content))
|
||||
(u/display-not-valid :addCommentThread "Content not valid")
|
||||
(u/not-valid plugin-id :addCommentThread "Content not valid")
|
||||
|
||||
(or (not (sm/valid-safe-number? (:x position)))
|
||||
(not (sm/valid-safe-number? (:y position))))
|
||||
(u/display-not-valid :addCommentThread "Position not valid")
|
||||
(u/not-valid plugin-id :addCommentThread "Position not valid")
|
||||
|
||||
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
|
||||
(u/display-not-valid :addCommentThread "Board not valid")
|
||||
(u/not-valid plugin-id :addCommentThread "Board not valid")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission")
|
||||
(u/not-valid plugin-id :addCommentThread "Plugin doesn't have 'comment:write' permission")
|
||||
|
||||
:else
|
||||
(let [position
|
||||
@ -378,10 +378,10 @@
|
||||
(fn [thread]
|
||||
(cond
|
||||
(not (pc/comment-thread-proxy? thread))
|
||||
(u/display-not-valid :removeCommentThread "Comment thread not valid")
|
||||
(u/not-valid plugin-id :removeCommentThread "Comment thread not valid")
|
||||
|
||||
(not (r/check-permission plugin-id "comment:write"))
|
||||
(u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :removeCommentThread "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(js/Promise.
|
||||
@ -400,7 +400,7 @@
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "comment:read"))
|
||||
(do
|
||||
(u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission")
|
||||
(u/not-valid plugin-id :findCommentThreads "Plugin doesn't have 'comment:read' permission")
|
||||
(reject "Plugin doesn't have 'comment:read' permission"))
|
||||
|
||||
:else
|
||||
|
||||
@ -14,10 +14,10 @@
|
||||
[app.plugins.utils :as u]))
|
||||
|
||||
(defn ^:export centerShapes
|
||||
[shapes]
|
||||
[plugin-id shapes]
|
||||
(cond
|
||||
(not (every? shape/shape-proxy? shapes))
|
||||
(u/display-not-valid :centerShapes shapes)
|
||||
(u/not-valid plugin-id :centerShapes shapes)
|
||||
|
||||
:else
|
||||
(let [shapes (->> shapes (map u/proxy->shape))]
|
||||
|
||||
@ -44,13 +44,13 @@
|
||||
(let [shape (u/locate-shape file-id page-id (obj/get value "$id"))]
|
||||
(cond
|
||||
(not (shape-proxy? value))
|
||||
(u/display-not-valid :board "The board is not a shape proxy")
|
||||
(u/not-valid plugin-id :board "The board is not a shape proxy")
|
||||
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :board "The shape is not a board")
|
||||
(u/not-valid plugin-id :board "The shape is not a board")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :board "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :board "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [board-id (when value (obj/get value "$id"))
|
||||
@ -78,10 +78,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :position "Not valid position")
|
||||
(u/not-valid plugin-id :position "Not valid position")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :position "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :position "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide self)
|
||||
|
||||
@ -31,7 +31,6 @@
|
||||
[app.common.types.shape.radius :as ctsr]
|
||||
[app.common.types.shape.shadow :as ctss]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.types.token :as cto]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.plugins :as dp]
|
||||
[app.main.data.workspace :as dw]
|
||||
@ -47,7 +46,6 @@
|
||||
[app.main.data.workspace.variants :as dwv]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.plugins.flags :refer [natural-child-ordering?]]
|
||||
[app.plugins.flex :as flex]
|
||||
[app.plugins.format :as format]
|
||||
[app.plugins.grid :as grid]
|
||||
@ -55,6 +53,7 @@
|
||||
[app.plugins.register :as r]
|
||||
[app.plugins.ruler-guides :as rg]
|
||||
[app.plugins.text :as text]
|
||||
[app.plugins.tokens :refer [applied-tokens-plugin->applied-tokens token-attr-plugin->token-attr token-attr?]]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.http :as http]
|
||||
[app.util.object :as obj]
|
||||
@ -91,7 +90,7 @@
|
||||
(let [value (parser/parse-keyword value)]
|
||||
(cond
|
||||
(not (contains? ctsi/event-types value))
|
||||
(u/display-not-valid :trigger value)
|
||||
(u/not-valid plugin-id :trigger value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-interaction
|
||||
@ -107,7 +106,7 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(or (not (number? value)) (not (pos? value)))
|
||||
(u/display-not-valid :delay value)
|
||||
(u/not-valid plugin-id :delay value)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-interaction
|
||||
@ -127,7 +126,7 @@
|
||||
(d/patch-object params))]
|
||||
(cond
|
||||
(not (sm/validate ctsi/schema:interaction interaction))
|
||||
(u/display-not-valid :action interaction)
|
||||
(u/not-valid plugin-id :action interaction)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/update-interaction
|
||||
@ -192,7 +191,8 @@
|
||||
(assert (uuid? id))
|
||||
|
||||
(let [data (u/locate-shape file-id page-id id)]
|
||||
(-> (obj/reify {:name "ShapeProxy" :on-error u/handle-error}
|
||||
(-> (obj/reify {:name "ShapeProxy"
|
||||
:on-error (u/handle-error plugin-id)}
|
||||
:$plugin {:enumerable false :get (fn [] plugin-id)}
|
||||
:$id {:enumerable false :get (fn [] id)}
|
||||
:$file {:enumerable false :get (fn [] file-id)}
|
||||
@ -218,10 +218,10 @@
|
||||
(not (str/blank? value)))]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :name "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not valid?)
|
||||
(u/display-not-valid :name value)
|
||||
(u/not-valid plugin-id :name value)
|
||||
|
||||
:else
|
||||
(st/emit! (dw/rename-shape-or-variant file-id page-id id value)))))}
|
||||
@ -233,10 +233,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :blocked value)
|
||||
(u/not-valid plugin-id :blocked value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :blocked "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :blocked "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
@ -249,10 +249,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :hidden value)
|
||||
(u/not-valid plugin-id :hidden value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :hidden "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :hidden "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
@ -265,10 +265,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :visible value)
|
||||
(u/not-valid plugin-id :visible value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :visible "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :visible "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
@ -281,10 +281,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :proportionLock value)
|
||||
(u/not-valid plugin-id :proportionLock value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :proportionLock "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :proportionLock "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
@ -299,10 +299,10 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? cts/horizontal-constraint-types value))
|
||||
(u/display-not-valid :constraintsHorizontal value)
|
||||
(u/not-valid plugin-id :constraintsHorizontal value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :constraintsHorizontal "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :constraintsHorizontal "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-h value))))))}
|
||||
@ -316,10 +316,10 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? cts/vertical-constraint-types value))
|
||||
(u/display-not-valid :constraintsVertical value)
|
||||
(u/not-valid plugin-id :constraintsVertical value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :constraintsVertical "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :constraintsVertical "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-v value))))))}
|
||||
@ -332,10 +332,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (sm/valid-safe-int? value)) (< value 0))
|
||||
(u/display-not-valid :borderRadius value)
|
||||
(u/not-valid plugin-id :borderRadius value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :borderRadius "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :borderRadius "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-all-corners % value))))))}
|
||||
@ -348,10 +348,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusTopLeft value)
|
||||
(u/not-valid plugin-id :borderRadiusTopLeft value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :borderRadiusTopLeft "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :borderRadiusTopLeft "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r1 value))))))}
|
||||
@ -364,10 +364,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusTopRight value)
|
||||
(u/not-valid plugin-id :borderRadiusTopRight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :borderRadiusTopRight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :borderRadiusTopRight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r2 value))))))}
|
||||
@ -380,10 +380,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusBottomRight value)
|
||||
(u/not-valid plugin-id :borderRadiusBottomRight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :borderRadiusBottomRight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :borderRadiusBottomRight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r3 value))))))}
|
||||
@ -396,10 +396,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? value))
|
||||
(u/display-not-valid :borderRadiusBottomLeft value)
|
||||
(u/not-valid plugin-id :borderRadiusBottomLeft value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :borderRadiusBottomLeft "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :borderRadiusBottomLeft "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r4 value))))))}
|
||||
@ -412,10 +412,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (sm/valid-safe-number? value)) (< value 0) (> value 1))
|
||||
(u/display-not-valid :opacity value)
|
||||
(u/not-valid plugin-id :opacity value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :opacity "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :opacity "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :opacity value))))))}
|
||||
@ -429,10 +429,10 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? cts/blend-modes value))
|
||||
(u/display-not-valid :blendMode value)
|
||||
(u/not-valid plugin-id :blendMode value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :blendMode "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :blendMode "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))}
|
||||
@ -446,10 +446,10 @@
|
||||
value (mapv #(shadow-defaults (parser/parse-shadow %)) value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ctss/schema:shadow] value))
|
||||
(u/display-not-valid :shadows value)
|
||||
(u/not-valid plugin-id :shadows value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :shadows "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :shadows "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value))))))}
|
||||
@ -465,10 +465,10 @@
|
||||
value (blur-defaults (parser/parse-blur value))]
|
||||
(cond
|
||||
(not (sm/validate ctsb/schema:blur value))
|
||||
(u/display-not-valid :blur value)
|
||||
(u/not-valid plugin-id :blur value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :blur "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :blur "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))}
|
||||
@ -482,10 +482,10 @@
|
||||
value (parser/parse-exports value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ctse/schema:export] value))
|
||||
(u/display-not-valid :exports value)
|
||||
(u/not-valid plugin-id :exports value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :exports "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :exports "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :exports value))))))}
|
||||
@ -499,10 +499,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :x value)
|
||||
(u/not-valid plugin-id :x value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :x "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :x "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/update-position id
|
||||
@ -517,10 +517,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :y value)
|
||||
(u/not-valid plugin-id :y value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :y "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :y "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/update-position id
|
||||
@ -562,10 +562,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :parentX value)
|
||||
(u/not-valid plugin-id :parentX value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :parentX "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :parentX "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")
|
||||
@ -589,10 +589,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :parentY value)
|
||||
(u/not-valid plugin-id :parentY value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :parentY "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :parentY "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")
|
||||
@ -616,10 +616,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :frameX value)
|
||||
(u/not-valid plugin-id :frameX value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :frameX "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :frameX "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")
|
||||
@ -643,10 +643,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :frameY value)
|
||||
(u/not-valid plugin-id :frameY value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :frameY "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :frameY "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")
|
||||
@ -680,10 +680,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (number? value))
|
||||
(u/display-not-valid :rotation value)
|
||||
(u/not-valid plugin-id :rotation value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rotation "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rotation "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [shape (u/proxy->shape self)]
|
||||
@ -696,10 +696,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :flipX value)
|
||||
(u/not-valid plugin-id :flipX value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :flipX "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :flipX "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
@ -712,10 +712,10 @@
|
||||
(fn [self value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :flipY value)
|
||||
(u/not-valid plugin-id :flipY value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :flipY "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :flipY "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [id (obj/get self "$id")]
|
||||
@ -734,13 +734,13 @@
|
||||
value (parser/parse-fills value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector types.fills/schema:fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
(u/not-valid plugin-id :fills value)
|
||||
|
||||
(cfh/text-shape? shape)
|
||||
(st/emit! (dwt/update-attrs id {:fills value}))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fills "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fills "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :fills value))))))}
|
||||
@ -754,10 +754,10 @@
|
||||
value (parser/parse-strokes value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector cts/schema:stroke] value))
|
||||
(u/display-not-valid :strokes value)
|
||||
(u/not-valid plugin-id :strokes value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :strokes "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :strokes "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value))))))}
|
||||
@ -802,13 +802,13 @@
|
||||
(fn [width height]
|
||||
(cond
|
||||
(or (not (sm/valid-safe-number? width)) (<= width 0))
|
||||
(u/display-not-valid :resize width)
|
||||
(u/not-valid plugin-id :resize width)
|
||||
|
||||
(or (not (sm/valid-safe-number? height)) (<= height 0))
|
||||
(u/display-not-valid :resize height)
|
||||
(u/not-valid plugin-id :resize height)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :resize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/update-dimensions [id] :width width)
|
||||
@ -819,13 +819,13 @@
|
||||
(let [center (when center {:x (obj/get center "x") :y (obj/get center "y")})]
|
||||
(cond
|
||||
(not (number? angle))
|
||||
(u/display-not-valid :rotate-angle angle)
|
||||
(u/not-valid plugin-id :rotate-angle angle)
|
||||
|
||||
(and (some? center) (or (not (number? (:x center))) (not (number? (:y center)))))
|
||||
(u/display-not-valid :rotate-center center)
|
||||
(u/not-valid plugin-id :rotate-center center)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :rotate "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :rotate "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/increase-rotation [id] angle {:center center :delta? true})))))
|
||||
@ -835,7 +835,7 @@
|
||||
(let [ret-v (atom nil)]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :clone "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :clone "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dws/duplicate-shapes #{id} :change-selection? false :return-ref ret-v))
|
||||
@ -845,7 +845,7 @@
|
||||
(fn []
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :remove "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/delete-shapes #{id}))))
|
||||
@ -855,7 +855,7 @@
|
||||
(fn [key]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getPluginData key)
|
||||
(u/not-valid plugin-id :getPluginData key)
|
||||
|
||||
:else
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
@ -865,13 +865,13 @@
|
||||
(fn [key value]
|
||||
(cond
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setPluginData-key key)
|
||||
(u/not-valid plugin-id :setPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setPluginData-value value)
|
||||
(u/not-valid plugin-id :setPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :shape id page-id (keyword "plugin" (str plugin-id)) key value))))
|
||||
@ -885,10 +885,10 @@
|
||||
(fn [namespace key]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :getSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :getSharedPluginData-key key)
|
||||
|
||||
:else
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
@ -898,16 +898,16 @@
|
||||
(fn [namespace key value]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :setSharedPluginData-namespace namespace)
|
||||
(u/not-valid plugin-id :setSharedPluginData-namespace namespace)
|
||||
|
||||
(not (string? key))
|
||||
(u/display-not-valid :setSharedPluginData-key key)
|
||||
(u/not-valid plugin-id :setSharedPluginData-key key)
|
||||
|
||||
(and (some? value) (not (string? value)))
|
||||
(u/display-not-valid :setSharedPluginData-value value)
|
||||
(u/not-valid plugin-id :setSharedPluginData-value value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setSharedPluginData "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dp/set-plugin-data file-id :shape id page-id (keyword "shared" namespace) key value))))
|
||||
@ -916,7 +916,7 @@
|
||||
(fn [namespace]
|
||||
(cond
|
||||
(not (string? namespace))
|
||||
(u/display-not-valid :getSharedPluginDataKeys namespace)
|
||||
(u/not-valid plugin-id :getSharedPluginDataKeys namespace)
|
||||
|
||||
:else
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
@ -931,12 +931,12 @@
|
||||
(not (cfh/group-shape? shape))
|
||||
(not (cfh/svg-raw-shape? shape))
|
||||
(not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :getChildren (:type shape))
|
||||
(u/not-valid plugin-id :getChildren (:type shape))
|
||||
|
||||
:else
|
||||
(let [is-reversed? (ctl/flex-layout? shape)
|
||||
reverse-fn
|
||||
(if (and (natural-child-ordering? plugin-id) is-reversed?)
|
||||
(if (and (u/natural-child-ordering? plugin-id) is-reversed?)
|
||||
reverse identity)]
|
||||
(->> (u/locate-shape file-id page-id id)
|
||||
(:shapes)
|
||||
@ -951,19 +951,19 @@
|
||||
(not (cfh/group-shape? shape))
|
||||
(not (cfh/svg-raw-shape? shape))
|
||||
(not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :appendChild (:type shape))
|
||||
(u/not-valid plugin-id :appendChild (:type shape))
|
||||
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :appendChild-child child)
|
||||
(u/not-valid plugin-id :appendChild-child child)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :appendChild "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :appendChild "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")
|
||||
is-reversed? (ctl/flex-layout? shape)
|
||||
index
|
||||
(if (or (not (natural-child-ordering? plugin-id)) is-reversed?)
|
||||
(if (or (not (u/natural-child-ordering? plugin-id)) is-reversed?)
|
||||
0
|
||||
(count (:shapes shape)))]
|
||||
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
||||
@ -976,19 +976,19 @@
|
||||
(not (cfh/group-shape? shape))
|
||||
(not (cfh/svg-raw-shape? shape))
|
||||
(not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :insertChild (:type shape))
|
||||
(u/not-valid plugin-id :insertChild (:type shape))
|
||||
|
||||
(not (shape-proxy? child))
|
||||
(u/display-not-valid :insertChild-child child)
|
||||
(u/not-valid plugin-id :insertChild-child child)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :insertChild "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :insertChild "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [child-id (obj/get child "$id")
|
||||
is-reversed? (ctl/flex-layout? shape)
|
||||
index
|
||||
(if (or (not (natural-child-ordering? plugin-id)) is-reversed?)
|
||||
(if (or (not (u/natural-child-ordering? plugin-id)) is-reversed?)
|
||||
(- (count (:shapes shape)) index)
|
||||
index)]
|
||||
(st/emit! (dwsh/relocate-shapes #{child-id} id index))))))
|
||||
@ -999,10 +999,10 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :addFlexLayout (:type shape))
|
||||
(u/not-valid plugin-id :addFlexLayout (:type shape))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addFlexLayout "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addFlexLayout "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dwsl/create-layout-from-id id :flex :from-frame? true :calculate-params? false))
|
||||
@ -1013,10 +1013,10 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :addGridLayout (:type shape))
|
||||
(u/not-valid plugin-id :addGridLayout (:type shape))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addGridLayout "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addGridLayout "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(do (st/emit! (dwsl/create-layout-from-id id :grid :from-frame? true :calculate-params? false))
|
||||
@ -1028,10 +1028,10 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (cfh/group-shape? shape))
|
||||
(u/display-not-valid :makeMask (:type shape))
|
||||
(u/not-valid plugin-id :makeMask (:type shape))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :makeMask "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :makeMask "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwg/mask-group #{id})))))
|
||||
@ -1041,10 +1041,10 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (cfh/mask-shape? shape))
|
||||
(u/display-not-valid :removeMask (:type shape))
|
||||
(u/not-valid plugin-id :removeMask (:type shape))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :removeMask "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :removeMask "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwg/unmask-group #{id})))))
|
||||
@ -1055,7 +1055,7 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(and (not (cfh/path-shape? shape)) (not (cfh/bool-shape? shape)))
|
||||
(u/display-not-valid :toD (:type shape))
|
||||
(u/not-valid plugin-id :toD (:type shape))
|
||||
|
||||
:else
|
||||
(.toString (:content shape)))))
|
||||
@ -1066,13 +1066,13 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (cfh/text-shape? shape))
|
||||
(u/display-not-valid :getRange-shape "shape is not text")
|
||||
(u/not-valid plugin-id :getRange-shape "shape is not text")
|
||||
|
||||
(or (not (sm/valid-safe-int? start)) (< start 0) (> start end))
|
||||
(u/display-not-valid :getRange-start start)
|
||||
(u/not-valid plugin-id :getRange-start start)
|
||||
|
||||
(not (sm/valid-safe-int? end))
|
||||
(u/display-not-valid :getRange-end end)
|
||||
(u/not-valid plugin-id :getRange-end end)
|
||||
|
||||
:else
|
||||
(text/text-range-proxy plugin-id file-id page-id id start end))))
|
||||
@ -1082,13 +1082,13 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (lib-typography-proxy? typography))
|
||||
(u/display-not-valid :applyTypography-typography typography)
|
||||
(u/not-valid plugin-id :applyTypography-typography typography)
|
||||
|
||||
(not (cfh/text-shape? shape))
|
||||
(u/display-not-valid :applyTypography-shape (:type shape))
|
||||
(u/not-valid plugin-id :applyTypography-shape (:type shape))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :applyTypography "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :applyTypography "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [typography (u/proxy->library-typography typography)]
|
||||
@ -1099,10 +1099,10 @@
|
||||
(fn [index]
|
||||
(cond
|
||||
(not (sm/valid-safe-int? index))
|
||||
(u/display-not-valid :setParentIndex index)
|
||||
(u/not-valid plugin-id :setParentIndex index)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :setParentIndex "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :setParentIndex "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/set-shape-index file-id page-id id index))))
|
||||
@ -1197,7 +1197,7 @@
|
||||
(let [value (parser/parse-export value)]
|
||||
(cond
|
||||
(not (sm/validate ctse/schema:export value))
|
||||
(u/display-not-valid :export value)
|
||||
(u/not-valid plugin-id :export value)
|
||||
|
||||
:else
|
||||
(let [shape (u/locate-shape file-id page-id id)
|
||||
@ -1233,7 +1233,7 @@
|
||||
(d/patch-object (parser/parse-interaction trigger action delay)))]
|
||||
(cond
|
||||
(not (sm/validate ctsi/schema:interaction interaction))
|
||||
(u/display-not-valid :addInteraction interaction)
|
||||
(u/not-valid plugin-id :addInteraction interaction)
|
||||
|
||||
:else
|
||||
(let [index (-> (u/locate-shape file-id page-id id) (:interactions []) count)]
|
||||
@ -1244,7 +1244,7 @@
|
||||
(fn [interaction]
|
||||
(cond
|
||||
(not (interaction-proxy? interaction))
|
||||
(u/display-not-valid :removeInteraction interaction)
|
||||
(u/not-valid plugin-id :removeInteraction interaction)
|
||||
|
||||
:else
|
||||
(st/emit! (dwi/remove-interaction {:id id} (obj/get interaction "$index")))))
|
||||
@ -1255,16 +1255,16 @@
|
||||
(let [shape (u/locate-shape file-id page-id id)]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :addRulerGuide "Value not a safe number")
|
||||
(u/not-valid plugin-id :addRulerGuide "Value not a safe number")
|
||||
|
||||
(not (contains? #{"vertical" "horizontal"} orientation))
|
||||
(u/display-not-valid :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
|
||||
(u/not-valid plugin-id :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
|
||||
|
||||
(not (cfh/frame-shape? shape))
|
||||
(u/display-not-valid :addRulerGuide "The shape is not a board")
|
||||
(u/not-valid plugin-id :addRulerGuide "The shape is not a board")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :addRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [ruler-id (uuid/next)
|
||||
@ -1285,10 +1285,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (rg/ruler-guide-proxy? value))
|
||||
(u/display-not-valid :removeRulerGuide "Guide not provided")
|
||||
(u/not-valid plugin-id :removeRulerGuide "Guide not provided")
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :removeRulerGuide "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(let [guide (u/proxy->ruler-guide value)]
|
||||
@ -1298,25 +1298,26 @@
|
||||
{:this true
|
||||
:get
|
||||
(fn [_]
|
||||
(let [tokens
|
||||
(let [applied-tokens
|
||||
(-> (u/locate-shape file-id page-id id)
|
||||
(get :applied-tokens))]
|
||||
(get :applied-tokens)
|
||||
(applied-tokens-plugin->applied-tokens))]
|
||||
(reduce
|
||||
(fn [acc [prop name]]
|
||||
(obj/set! acc (json/write-camel-key prop) name))
|
||||
#js {}
|
||||
tokens)))}
|
||||
applied-tokens)))}
|
||||
|
||||
:applyToken
|
||||
{:enumerable false
|
||||
:schema [:tuple
|
||||
[:fn token-proxy?]
|
||||
[:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
[:maybe [:set [:and ::sm/keyword [:fn token-attr?]]]]]
|
||||
:fn (fn [token attrs]
|
||||
(let [token (u/locate-token file-id (obj/get token "$set-id") (obj/get token "$id"))
|
||||
kw-attrs (into #{} (map keyword attrs))]
|
||||
(if (some #(not (cto/token-attr? %)) kw-attrs)
|
||||
(u/display-not-valid :applyToken attrs)
|
||||
kw-attrs (into #{} (map token-attr-plugin->token-attr attrs))]
|
||||
(if (some #(not (token-attr? %)) kw-attrs)
|
||||
(u/not-valid plugin-id :applyToken attrs)
|
||||
(st/emit!
|
||||
(dwta/toggle-token {:token token
|
||||
:attrs kw-attrs
|
||||
@ -1338,10 +1339,10 @@
|
||||
(fn [pos value]
|
||||
(cond
|
||||
(not (nat-int? pos))
|
||||
(u/display-not-valid :pos pos)
|
||||
(u/not-valid plugin-id :pos pos)
|
||||
|
||||
(not (string? value))
|
||||
(u/display-not-valid :value value)
|
||||
(u/not-valid plugin-id :value value)
|
||||
|
||||
:else
|
||||
(let [shape (u/locate-shape file-id page-id id)
|
||||
@ -1353,7 +1354,7 @@
|
||||
(fn [ids]
|
||||
(cond
|
||||
(or (not (seq ids)) (not (every? uuid/parse* ids)))
|
||||
(u/display-not-valid :ids ids)
|
||||
(u/not-valid plugin-id :ids ids)
|
||||
|
||||
:else
|
||||
(let [shape (u/locate-shape file-id page-id id)
|
||||
@ -1381,21 +1382,21 @@
|
||||
(fn [^js self children]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :children "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :children "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not (every? shape-proxy? children))
|
||||
(u/display-not-valid :children "Every children needs to be shape proxies")
|
||||
(u/not-valid plugin-id :children "Every children needs to be shape proxies")
|
||||
|
||||
:else
|
||||
(let [shape (u/proxy->shape self)
|
||||
file-id (obj/get self "$file")
|
||||
page-id (obj/get self "$page")
|
||||
reverse-fn (if (natural-child-ordering? plugin-id) reverse identity)
|
||||
reverse-fn (if (u/natural-child-ordering? plugin-id) reverse identity)
|
||||
ids (->> children reverse-fn (map #(obj/get % "$id")))]
|
||||
|
||||
(cond
|
||||
(not= (set ids) (set (:shapes shape)))
|
||||
(u/display-not-valid :children "Not all children are present in the input")
|
||||
(u/not-valid plugin-id :children "Not all children are present in the input")
|
||||
|
||||
:else
|
||||
(st/emit! (dw/reorder-children file-id page-id (:id shape) ids))))))}))
|
||||
@ -1411,10 +1412,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :clipContent value)
|
||||
(u/not-valid plugin-id :clipContent value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :clipContent "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :clipContent "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :show-content (not value))))))}
|
||||
@ -1427,10 +1428,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(not (boolean? value))
|
||||
(u/display-not-valid :showInViewMode value)
|
||||
(u/not-valid plugin-id :showInViewMode value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :showInViewMode "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :showInViewMode "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :hide-in-viewer (not value))))))}
|
||||
@ -1462,10 +1463,10 @@
|
||||
value (parser/parse-frame-guides value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::ctg/grid] value))
|
||||
(u/display-not-valid :guides value)
|
||||
(u/not-valid plugin-id :guides value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :guides "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :guides "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grids value))))))}
|
||||
@ -1487,10 +1488,10 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? #{:fix :auto} value))
|
||||
(u/display-not-valid :horizontalSizing value)
|
||||
(u/not-valid plugin-id :horizontalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :horizontalSizing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :horizontalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-h-sizing value})))))}
|
||||
@ -1503,10 +1504,10 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? #{:fix :auto} value))
|
||||
(u/display-not-valid :verticalSizing value)
|
||||
(u/not-valid plugin-id :verticalSizing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalSizing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsl/update-layout #{id} {:layout-item-v-sizing value})))))}
|
||||
@ -1530,10 +1531,10 @@
|
||||
(let [segments (parser/parse-commands value)]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :content "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :content "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not (sm/validate path/schema:segments segments))
|
||||
(u/display-not-valid :content segments)
|
||||
(u/not-valid plugin-id :content segments)
|
||||
|
||||
:else
|
||||
(let [selrect (path/calc-selrect segments)
|
||||
@ -1556,13 +1557,13 @@
|
||||
value)]
|
||||
(cond
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :content "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :content "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(not (cfh/path-shape? data))
|
||||
(u/display-not-valid :content-type type)
|
||||
(u/not-valid plugin-id :content-type type)
|
||||
|
||||
(not (sm/validate path/schema:segments segments))
|
||||
(u/display-not-valid :content segments)
|
||||
(u/not-valid plugin-id :content segments)
|
||||
|
||||
:else
|
||||
(let [selrect (path/calc-selrect segments)
|
||||
|
||||
@ -119,10 +119,10 @@
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not font)
|
||||
(u/display-not-valid :fontId value)
|
||||
(u/not-valid plugin-id :fontId value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontId "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontId "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
@ -141,10 +141,10 @@
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontFamily value)
|
||||
(u/not-valid plugin-id :fontFamily value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontFamily "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontFamily "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
|
||||
@ -162,10 +162,10 @@
|
||||
variant (fonts/get-variant font value)]
|
||||
(cond
|
||||
(not (string? value))
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
(u/not-valid plugin-id :fontVariantId value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontVariantId "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontVariantId "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
@ -182,10 +182,10 @@
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches font-size-re value)))
|
||||
(u/display-not-valid :fontSize value)
|
||||
(u/not-valid plugin-id :fontSize value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontSize "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontSize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:font-size value})))))}
|
||||
@ -209,10 +209,10 @@
|
||||
(fonts/find-variant font {:weight weight}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
(u/not-valid plugin-id :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontWeight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontWeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
@ -235,10 +235,10 @@
|
||||
(fonts/find-variant font {:style style}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
(u/not-valid plugin-id :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontStyle "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontStyle "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
|
||||
@ -255,10 +255,10 @@
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches line-height-re value)))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
(u/not-valid plugin-id :lineHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :lineHeight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :lineHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:line-height value})))))}
|
||||
@ -275,10 +275,10 @@
|
||||
(let [value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (re-matches letter-spacing-re value))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
(u/not-valid plugin-id :letterSpacing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :letterSpacing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :letterSpacing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))}
|
||||
@ -294,10 +294,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (not (re-matches text-transform-re value)))
|
||||
(u/display-not-valid :textTransform value)
|
||||
(u/not-valid plugin-id :textTransform value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textTransform "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :textTransform "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
|
||||
@ -313,10 +313,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-decoration-re value))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
(u/not-valid plugin-id :textDecoration value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textDecoration "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :textDecoration "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
|
||||
@ -332,10 +332,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-direction-re value))
|
||||
(u/display-not-valid :direction value)
|
||||
(u/not-valid plugin-id :direction value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :direction "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :direction "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:direction value}))))}
|
||||
@ -351,10 +351,10 @@
|
||||
(fn [_ value]
|
||||
(cond
|
||||
(and (string? value) (re-matches text-align-re value))
|
||||
(u/display-not-valid :align value)
|
||||
(u/not-valid plugin-id :align value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :align "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :align "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:text-align value}))))}
|
||||
@ -371,10 +371,10 @@
|
||||
(let [value (parser/parse-fills value)]
|
||||
(cond
|
||||
(not (sm/validate [:vector ::cts/fill] value))
|
||||
(u/display-not-valid :fills value)
|
||||
(u/not-valid plugin-id :fills value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fills "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fills "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-text-range id start end {:fills value})))))}
|
||||
@ -401,10 +401,10 @@
|
||||
;; editor as well
|
||||
(cond
|
||||
(or (not (string? value)) (empty? value))
|
||||
(u/display-not-valid :characters value)
|
||||
(u/not-valid plugin-id :characters value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :characters "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :characters "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
(contains? (:workspace-editor-state @st/state) id)
|
||||
(let [shape (u/proxy->shape self)
|
||||
@ -428,10 +428,10 @@
|
||||
value (keyword value)]
|
||||
(cond
|
||||
(not (contains? #{:auto-width :auto-height :fixed} value))
|
||||
(u/display-not-valid :growType value)
|
||||
(u/not-valid plugin-id :growType value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :growType "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :growType "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grow-type value))))))}
|
||||
@ -445,10 +445,10 @@
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not font)
|
||||
(u/display-not-valid :fontId value)
|
||||
(u/not-valid plugin-id :fontId value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontId "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontId "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id (font-data font variant))))))}
|
||||
@ -462,10 +462,10 @@
|
||||
variant (fonts/get-default-variant font)]
|
||||
(cond
|
||||
(not font)
|
||||
(u/display-not-valid :fontFamily value)
|
||||
(u/not-valid plugin-id :fontFamily value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontFamily "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontFamily "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id (font-data font variant))))))}
|
||||
@ -479,10 +479,10 @@
|
||||
variant (fonts/get-variant font value)]
|
||||
(cond
|
||||
(not variant)
|
||||
(u/display-not-valid :fontVariantId value)
|
||||
(u/not-valid plugin-id :fontVariantId value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontVariantId "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontVariantId "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id (variant-data variant))))))}
|
||||
@ -495,10 +495,10 @@
|
||||
value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches font-size-re value)))
|
||||
(u/display-not-valid :fontSize value)
|
||||
(u/not-valid plugin-id :fontSize value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontSize "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontSize "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:font-size value})))))}
|
||||
@ -517,10 +517,10 @@
|
||||
(fonts/find-variant font {:weight weight}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
(u/not-valid plugin-id :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontWeight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontWeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id (variant-data variant))))))}
|
||||
@ -539,10 +539,10 @@
|
||||
(fonts/find-variant font {:style style}))]
|
||||
(cond
|
||||
(nil? variant)
|
||||
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
(u/not-valid plugin-id :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :fontStyle "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :fontStyle "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id (variant-data variant))))))}
|
||||
@ -555,10 +555,10 @@
|
||||
value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (empty? value) (not (re-matches line-height-re value)))
|
||||
(u/display-not-valid :lineHeight value)
|
||||
(u/not-valid plugin-id :lineHeight value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :lineHeight "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :lineHeight "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:line-height value})))))}
|
||||
@ -571,10 +571,10 @@
|
||||
value (str/trim (dm/str value))]
|
||||
(cond
|
||||
(or (not (string? value)) (not (re-matches letter-spacing-re value)))
|
||||
(u/display-not-valid :letterSpacing value)
|
||||
(u/not-valid plugin-id :letterSpacing value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :letterSpacing "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :letterSpacing "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:letter-spacing value})))))}
|
||||
@ -586,10 +586,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (string? value)) (not (re-matches text-transform-re value)))
|
||||
(u/display-not-valid :textTransform value)
|
||||
(u/not-valid plugin-id :textTransform value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textTransform "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :textTransform "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-transform value})))))}
|
||||
@ -601,10 +601,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (string? value)) (not (re-matches text-decoration-re value)))
|
||||
(u/display-not-valid :textDecoration value)
|
||||
(u/not-valid plugin-id :textDecoration value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textDecoration "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :textDecoration "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-decoration value})))))}
|
||||
@ -616,10 +616,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (string? value)) (not (re-matches text-direction-re value)))
|
||||
(u/display-not-valid :textDirection value)
|
||||
(u/not-valid plugin-id :textDirection value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :textDirection "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :textDirection "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-direction value})))))}
|
||||
@ -631,10 +631,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (string? value)) (not (re-matches text-align-re value)))
|
||||
(u/display-not-valid :align value)
|
||||
(u/not-valid plugin-id :align value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :align "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :align "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:text-align value})))))}
|
||||
@ -646,10 +646,10 @@
|
||||
(let [id (obj/get self "$id")]
|
||||
(cond
|
||||
(or (not (string? value)) (not (re-matches vertical-align-re value)))
|
||||
(u/display-not-valid :verticalAlign value)
|
||||
(u/not-valid plugin-id :verticalAlign value)
|
||||
|
||||
(not (r/check-permission plugin-id "content:write"))
|
||||
(u/display-not-valid :verticalAlign "Plugin doesn't have 'content:write' permission")
|
||||
(u/not-valid plugin-id :verticalAlign "Plugin doesn't have 'content:write' permission")
|
||||
|
||||
:else
|
||||
(st/emit! (dwt/update-attrs id {:vertical-align value})))))}
|
||||
|
||||
@ -19,18 +19,58 @@
|
||||
[app.main.store :as st]
|
||||
[app.plugins.utils :as u]
|
||||
[app.util.object :as obj]
|
||||
[clojure.datafy :refer [datafy]]))
|
||||
[clojure.datafy :refer [datafy]]
|
||||
[clojure.set :refer [map-invert]]))
|
||||
|
||||
;; === Token
|
||||
|
||||
;; Give more semantic names to the shape attributes that tokens can be applied to
|
||||
(def ^:private map:token-attr->token-attr-plugin
|
||||
{:r1 :border-radius-top-left
|
||||
:r2 :border-radius-top-right
|
||||
:r3 :border-radius-bottom-right
|
||||
:r4 :border-radius-bottom-left
|
||||
|
||||
:p1 :padding-top-left
|
||||
:p2 :padding-top-right
|
||||
:p3 :padding-bottom-right
|
||||
:p4 :padding-bottom-left
|
||||
|
||||
:m1 :margin-top-left
|
||||
:m2 :margin-top-right
|
||||
:m3 :margin-bottom-right
|
||||
:m4 :margin-bottom-left})
|
||||
|
||||
(def ^:private map:token-attr-plugin->token-attr
|
||||
(map-invert map:token-attr->token-attr-plugin))
|
||||
|
||||
(defn token-attr->token-attr-plugin
|
||||
[k]
|
||||
(get map:token-attr->token-attr-plugin k k))
|
||||
|
||||
(defn token-attr-plugin->token-attr
|
||||
[k]
|
||||
(get map:token-attr-plugin->token-attr k k))
|
||||
|
||||
(defn applied-tokens-plugin->applied-tokens
|
||||
[value]
|
||||
(into {}
|
||||
(map (fn [[k v]] [(token-attr->token-attr-plugin k) v]))
|
||||
value))
|
||||
|
||||
(defn token-attr?
|
||||
[attr]
|
||||
(cto/token-attr? (token-attr-plugin->token-attr attr)))
|
||||
|
||||
(defn- apply-token-to-shapes
|
||||
[file-id set-id id shape-ids attrs]
|
||||
[plugin-id file-id set-id id shape-ids attrs]
|
||||
|
||||
(let [token (u/locate-token file-id set-id id)]
|
||||
(if (some #(not (cto/token-attr? %)) attrs)
|
||||
(u/display-not-valid :applyToSelected attrs)
|
||||
(if (some #(not (token-attr? %)) attrs)
|
||||
(u/not-valid plugin-id :applyToSelected attrs)
|
||||
(st/emit!
|
||||
(dwta/toggle-token {:token token
|
||||
:attrs attrs
|
||||
:attrs (into #{} (map token-attr-plugin->token-attr) attrs)
|
||||
:shape-ids shape-ids
|
||||
:expand-with-children false})))))
|
||||
|
||||
@ -52,7 +92,7 @@
|
||||
(defn token-proxy
|
||||
[plugin-id file-id set-id id]
|
||||
(obj/reify {:name "TokenProxy"
|
||||
:on-error u/handle-error}
|
||||
:on-error (u/handle-error plugin-id)}
|
||||
:$plugin {:enumerable false :get (constantly plugin-id)}
|
||||
:$file-id {:enumerable false :get (constantly file-id)}
|
||||
:$set-id {:enumerable false :get (constantly set-id)}
|
||||
@ -146,16 +186,16 @@
|
||||
{:enumerable false
|
||||
:schema [:tuple
|
||||
[:vector [:fn shape-proxy?]]
|
||||
[:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
[:maybe [:set [:and ::sm/keyword [:fn token-attr?]]]]]
|
||||
:fn (fn [shapes attrs]
|
||||
(apply-token-to-shapes file-id set-id id (map #(obj/get % "$id") shapes) attrs))}
|
||||
(apply-token-to-shapes plugin-id file-id set-id id (map #(obj/get % "$id") shapes) attrs))}
|
||||
|
||||
:applyToSelected
|
||||
{:enumerable false
|
||||
:schema [:tuple [:maybe [:set [:and ::sm/keyword [:fn cto/token-attr?]]]]]
|
||||
:schema [:tuple [:maybe [:set [:and ::sm/keyword [:fn token-attr?]]]]]
|
||||
:fn (fn [attrs]
|
||||
(let [selected (get-in @st/state [:workspace-local :selected])]
|
||||
(apply-token-to-shapes file-id set-id id selected attrs)))}))
|
||||
(apply-token-to-shapes plugin-id file-id set-id id selected attrs)))}))
|
||||
|
||||
;; === Token Set
|
||||
|
||||
@ -165,7 +205,7 @@
|
||||
(defn token-set-proxy
|
||||
[plugin-id file-id id]
|
||||
(obj/reify {:name "TokenSetProxy"
|
||||
:on-error u/handle-error}
|
||||
:on-error (u/handle-error plugin-id)}
|
||||
:$plugin {:enumerable false :get (constantly plugin-id)}
|
||||
:$file-id {:enumerable false :get (constantly file-id)}
|
||||
:$id {:enumerable false :get (constantly id)}
|
||||
@ -270,7 +310,7 @@
|
||||
(if resolved-value
|
||||
(do (st/emit! (dwtl/create-token id token))
|
||||
(token-proxy plugin-id file-id id (:id token)))
|
||||
(do (u/display-not-valid :addToken (str errors))
|
||||
(do (u/not-valid plugin-id :addToken (str errors))
|
||||
nil))))}
|
||||
|
||||
:duplicate
|
||||
@ -287,7 +327,7 @@
|
||||
(defn token-theme-proxy
|
||||
[plugin-id file-id id]
|
||||
(obj/reify {:name "TokenThemeProxy"
|
||||
:on-error u/handle-error}
|
||||
:on-error (u/handle-error plugin-id)}
|
||||
:$plugin {:enumerable false :get (constantly plugin-id)}
|
||||
:$file-id {:enumerable false :get (constantly file-id)}
|
||||
:$id {:enumerable false :get (constantly id)}
|
||||
@ -394,7 +434,7 @@
|
||||
(defn tokens-catalog
|
||||
[plugin-id file-id]
|
||||
(obj/reify {:name "TokensCatalog"
|
||||
:on-error u/handle-error}
|
||||
:on-error (u/handle-error plugin-id)}
|
||||
:$plugin {:enumerable false :get (constantly plugin-id)}
|
||||
:$id {:enumerable false :get (constantly file-id)}
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.json :as json]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.container :as ctn]
|
||||
@ -222,6 +221,16 @@
|
||||
(resolve value)))))]
|
||||
[ret-v ret-p]))
|
||||
|
||||
(defn natural-child-ordering?
|
||||
[plugin-id]
|
||||
(boolean
|
||||
(dm/get-in @st/state [:plugins :flags plugin-id :natural-child-ordering])))
|
||||
|
||||
(defn throw-validation-errors?
|
||||
[plugin-id]
|
||||
(boolean
|
||||
(dm/get-in @st/state [:plugins :flags plugin-id :throw-validation-errors])))
|
||||
|
||||
(defn display-not-valid
|
||||
[code value]
|
||||
(if (some? value)
|
||||
@ -229,23 +238,25 @@
|
||||
(.error js/console (dm/str "[PENPOT PLUGIN] Value not valid. Code: " code)))
|
||||
nil)
|
||||
|
||||
(defn throw-not-valid
|
||||
[code value]
|
||||
(if (some? value)
|
||||
(throw (js/Error. (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)))
|
||||
(throw (js/Error. (dm/str "[PENPOT PLUGIN] Value not valid. Code: " code))))
|
||||
nil)
|
||||
|
||||
(defn not-valid
|
||||
[plugin-id code value]
|
||||
(if (throw-validation-errors? plugin-id)
|
||||
(throw-not-valid code value)
|
||||
(display-not-valid code value)))
|
||||
|
||||
(defn reject-not-valid
|
||||
[reject code value]
|
||||
(let [msg (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)]
|
||||
(.error js/console msg)
|
||||
(reject msg)))
|
||||
|
||||
(defn coerce
|
||||
"Decodes a javascript object into clj and check against schema. If schema validation fails,
|
||||
displays a not-valid message with the code and hint provided and returns nil."
|
||||
[attrs schema code hint]
|
||||
(let [decoder (sm/decoder schema sm/json-transformer)
|
||||
explainer (sm/explainer schema)
|
||||
attrs (-> attrs json/->clj decoder)]
|
||||
(if-let [explain (explainer attrs)]
|
||||
(display-not-valid code (str hint " " (sm/humanize-explain explain)))
|
||||
attrs)))
|
||||
|
||||
(defn mixed-value
|
||||
[values]
|
||||
(let [s (set values)]
|
||||
@ -254,12 +265,14 @@
|
||||
(defn handle-error
|
||||
"Function to be used in plugin proxies methods to handle errors and print a readable
|
||||
message to the console."
|
||||
[cause]
|
||||
(display-not-valid (ex-message cause) nil)
|
||||
(if-let [explain (-> cause ex-data ::sm/explain)]
|
||||
(println (sm/humanize-explain explain))
|
||||
(js/console.log (ex-data cause)))
|
||||
(js/console.log (.-stack cause)))
|
||||
[plugin-id]
|
||||
(fn [cause]
|
||||
(let [message
|
||||
(if-let [explain (-> cause ex-data ::sm/explain)]
|
||||
(sm/humanize-explain explain)
|
||||
(ex-data cause))]
|
||||
(js/console.log (.-stack cause))
|
||||
(not-valid plugin-id :error message))))
|
||||
|
||||
(defn is-main-component-proxy?
|
||||
[p]
|
||||
|
||||
@ -38,10 +38,10 @@
|
||||
new-y (obj/get value "y")]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? new-x))
|
||||
(u/display-not-valid :center-x new-x)
|
||||
(u/not-valid plugin-id :center-x new-x)
|
||||
|
||||
(not (sm/valid-safe-number? new-y))
|
||||
(u/display-not-valid :center-y new-y)
|
||||
(u/not-valid plugin-id :center-y new-y)
|
||||
|
||||
:else
|
||||
(let [vb (dm/get-in @st/state [:workspace-local :vbox])
|
||||
@ -63,7 +63,7 @@
|
||||
(fn [value]
|
||||
(cond
|
||||
(not (sm/valid-safe-number? value))
|
||||
(u/display-not-valid :zoom value)
|
||||
(u/not-valid plugin-id :zoom value)
|
||||
|
||||
:else
|
||||
(let [z (dm/get-in @st/state [:workspace-local :zoom])]
|
||||
@ -87,7 +87,7 @@
|
||||
(fn [shapes]
|
||||
(cond
|
||||
(not (every? ps/shape-proxy? shapes))
|
||||
(u/display-not-valid :zoomIntoView "Argument should be valid shapes")
|
||||
(u/not-valid plugin-id :zoomIntoView "Argument should be valid shapes")
|
||||
|
||||
:else
|
||||
(let [ids (->> shapes
|
||||
|
||||
@ -55,10 +55,11 @@
|
||||
|
||||
(defn text-editor-wasm?
|
||||
[]
|
||||
(let [runtime-features (get @st/state :features-runtime)
|
||||
enabled-features (get @st/state :features)]
|
||||
(or (contains? runtime-features "text-editor-wasm/v1")
|
||||
(contains? enabled-features "text-editor-wasm/v1"))))
|
||||
(or (contains? cf/flags :feature-text-editor-wasm)
|
||||
(let [runtime-features (get @st/state :features-runtime)
|
||||
enabled-features (get @st/state :features)]
|
||||
(or (contains? runtime-features "text-editor-wasm/v1")
|
||||
(contains? enabled-features "text-editor-wasm/v1")))))
|
||||
|
||||
(def ^:const UUID-U8-SIZE 16)
|
||||
(def ^:const UUID-U32-SIZE (/ UUID-U8-SIZE 4))
|
||||
@ -951,14 +952,14 @@
|
||||
(= result 1)))
|
||||
|
||||
(def render-finish
|
||||
(letfn [(do-render [ts]
|
||||
(letfn [(do-render []
|
||||
;; Check if context is still initialized before executing
|
||||
;; to prevent errors when navigating quickly
|
||||
(when wasm/context-initialized?
|
||||
(perf/begin-measure "render-finish")
|
||||
(h/call wasm/internal-module "_set_view_end")
|
||||
(render ts)
|
||||
(perf/end-measure "render-finish")))]
|
||||
(perf/end-measure "render-finish")
|
||||
(render (js/performance.now))))]
|
||||
(fns/debounce do-render DEBOUNCE_DELAY_MS)))
|
||||
|
||||
(def render-pan
|
||||
@ -1401,6 +1402,8 @@
|
||||
(dbg/enabled? :wasm-viewbox)
|
||||
(bit-or 2r00000000000000000000000000000001)
|
||||
(text-editor-wasm?)
|
||||
(bit-or 2r00000000000000000000000000000100)
|
||||
(contains? cf/flags :render-wasm-info)
|
||||
(bit-or 2r00000000000000000000000000001000)))
|
||||
|
||||
(defn set-canvas-size
|
||||
|
||||
@ -66,17 +66,48 @@
|
||||
(let [res (h/call wasm/internal-module "_text_editor_poll_event")]
|
||||
res)))
|
||||
|
||||
(defn text-editor-insert-text
|
||||
(defn text-editor-encode-text-pre
|
||||
[text]
|
||||
(when wasm/context-initialized?
|
||||
(when (and (not (empty? text))
|
||||
wasm/context-initialized?)
|
||||
(let [encoder (js/TextEncoder.)
|
||||
buf (.encode encoder text)
|
||||
heapu8 (mem/get-heap-u8)
|
||||
size (mem/size buf)
|
||||
offset (mem/alloc size)]
|
||||
(mem/write-buffer offset heapu8 buf)
|
||||
(h/call wasm/internal-module "_text_editor_insert_text")
|
||||
(mem/free))))
|
||||
(mem/write-buffer offset heapu8 buf))))
|
||||
|
||||
(defn text-editor-encode-text-post
|
||||
[text]
|
||||
(when (and (not (empty? text))
|
||||
wasm/context-initialized?)
|
||||
(mem/free)))
|
||||
|
||||
(defn text-editor-composition-start
|
||||
[]
|
||||
(when wasm/context-initialized?
|
||||
(h/call wasm/internal-module "_text_editor_composition_start")))
|
||||
|
||||
(defn text-editor-composition-update
|
||||
[text]
|
||||
(when wasm/context-initialized?
|
||||
(text-editor-encode-text-pre text)
|
||||
(h/call wasm/internal-module "_text_editor_composition_update")
|
||||
(text-editor-encode-text-post text)))
|
||||
|
||||
(defn text-editor-composition-end
|
||||
[text]
|
||||
(when wasm/context-initialized?
|
||||
(text-editor-encode-text-pre text)
|
||||
(h/call wasm/internal-module "_text_editor_composition_end")
|
||||
(text-editor-encode-text-post text)))
|
||||
|
||||
(defn text-editor-insert-text
|
||||
[text]
|
||||
(when wasm/context-initialized?
|
||||
(text-editor-encode-text-pre text)
|
||||
(h/call wasm/internal-module "_text_editor_insert_text")
|
||||
(text-editor-encode-text-post text)))
|
||||
|
||||
(defn text-editor-delete-backward
|
||||
([]
|
||||
|
||||
@ -79,6 +79,7 @@ body {
|
||||
:border-end-end-radius
|
||||
:box-shadow
|
||||
:filter
|
||||
:backdrop-filter
|
||||
:opacity
|
||||
:overflow
|
||||
:blend-mode
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
:border-color :border-color
|
||||
:box-shadow :shadows
|
||||
:filter :blur
|
||||
:backdrop-filter :blur
|
||||
:gap :size-array
|
||||
:row-gap :size-array
|
||||
:column-gap :size-array
|
||||
|
||||
@ -256,7 +256,14 @@
|
||||
(defn- get-filter
|
||||
[shape]
|
||||
(when-not (cgc/svg-markup? shape)
|
||||
(get-in shape [:blur :value])))
|
||||
(when (= :layer-blur (get-in shape [:blur :type]))
|
||||
(get-in shape [:blur :value]))))
|
||||
|
||||
(defn- get-backdrop-filter
|
||||
[shape]
|
||||
(when-not (cgc/svg-markup? shape)
|
||||
(when (= :background-blur (get-in shape [:blur :type]))
|
||||
(get-in shape [:blur :value]))))
|
||||
|
||||
(defn- get-display
|
||||
[shape]
|
||||
@ -546,6 +553,7 @@
|
||||
:opacity (get-opacity shape)
|
||||
:box-shadow (get-box-shadow shape)
|
||||
:filter (get-filter shape)
|
||||
:backdrop-filter (get-backdrop-filter shape)
|
||||
:overflow (get-overflow shape)
|
||||
|
||||
;; Display
|
||||
|
||||
@ -141,7 +141,7 @@
|
||||
events [(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-2")
|
||||
:on-update-shape dwta/update-shape-radius-all})]
|
||||
:on-update-shape dwta/update-shape-radius})]
|
||||
|
||||
step2 (fn [_]
|
||||
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
|
||||
@ -249,11 +249,11 @@
|
||||
events [(dwta/apply-token {:shape-ids [(cthi/id :c-frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-2")
|
||||
:on-update-shape dwta/update-shape-radius-all})
|
||||
:on-update-shape dwta/update-shape-radius})
|
||||
(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-3")
|
||||
:on-update-shape dwta/update-shape-radius-all})]
|
||||
:on-update-shape dwta/update-shape-radius})]
|
||||
|
||||
step2 (fn [_]
|
||||
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
|
||||
@ -293,7 +293,7 @@
|
||||
(dwta/apply-token {:shape-ids [(cthi/id :frame1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "test-token-3")
|
||||
:on-update-shape dwta/update-shape-radius-all})]
|
||||
:on-update-shape dwta/update-shape-radius})]
|
||||
|
||||
step2 (fn [_]
|
||||
(let [events2 [(dwl/sync-file (:id file) (:id file))]]
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "borderRadius.md")
|
||||
:on-update-shape dwta/update-shape-radius-all})]]
|
||||
:on-update-shape dwta/update-shape-radius})]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
(fn [new-state]
|
||||
@ -89,11 +89,11 @@
|
||||
events [(dwta/apply-token {:shape-ids [(:id rect-1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "borderRadius.sm")
|
||||
:on-update-shape dwta/update-shape-radius-all})
|
||||
:on-update-shape dwta/update-shape-radius})
|
||||
(dwta/apply-token {:shape-ids [(:id rect-1)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "borderRadius.md")
|
||||
:on-update-shape dwta/update-shape-radius-all})]]
|
||||
:on-update-shape dwta/update-shape-radius})]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
(fn [new-state]
|
||||
@ -117,14 +117,14 @@
|
||||
(dwta/apply-token {:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "borderRadius.sm")
|
||||
:shape-ids [(:id rect-1)]
|
||||
:on-update-shape dwta/update-shape-radius-all})
|
||||
:on-update-shape dwta/update-shape-radius})
|
||||
;; Apply single `:r1` attribute to same shape
|
||||
;; while removing other attributes from the border-radius set
|
||||
;; but keep `:r4` for testing purposes
|
||||
(dwta/apply-token {:attributes #{:r1 :r2 :r3}
|
||||
:token (toht/get-token file "borderRadius.md")
|
||||
:shape-ids [(:id rect-1)]
|
||||
:on-update-shape dwta/update-shape-radius-all})]]
|
||||
:on-update-shape dwta/update-shape-radius})]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
(fn [new-state]
|
||||
@ -153,7 +153,7 @@
|
||||
(dwta/apply-token {:shape-ids [(:id rect-2)]
|
||||
:attributes #{:r1 :r2 :r3 :r4}
|
||||
:token (toht/get-token file "borderRadius.sm")
|
||||
:on-update-shape dwta/update-shape-radius-all})]]
|
||||
:on-update-shape dwta/update-shape-radius})]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
(fn [new-state]
|
||||
@ -762,7 +762,7 @@
|
||||
rect-2 (cths/get-shape file :rect-2)
|
||||
events [(dwta/toggle-token {:shape-ids [(:id rect-1) (:id rect-2)]
|
||||
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}
|
||||
:on-update-shape dwta/update-shape-radius-all}
|
||||
:on-update-shape dwta/update-shape-radius}
|
||||
:token (toht/get-token file "borderRadius.md")})]]
|
||||
(tohs/run-store-async
|
||||
store done events
|
||||
|
||||
@ -3833,9 +3833,12 @@ msgstr "Invitation sent successfully"
|
||||
msgid "notifications.invitation-link-copied"
|
||||
msgstr "Invitation link copied"
|
||||
|
||||
msgid "notifications.mcp.active-tab-switching.text"
|
||||
msgid "notifications.mcp.active-in-another-tab"
|
||||
msgstr "MCP is active in another tab. Switch here?"
|
||||
|
||||
msgid "notifications.mcp.active-in-this-tab"
|
||||
msgstr "MCP is now active in this tab."
|
||||
|
||||
#: src/app/main/ui/settings/delete_account.cljs:24
|
||||
msgid "notifications.profile-deletion-not-allowed"
|
||||
msgstr "You can't delete your profile. Reassign your teams before proceed."
|
||||
@ -6106,6 +6109,14 @@ msgstr "Remove blur"
|
||||
msgid "workspace.options.blur-options.title"
|
||||
msgstr "Blur"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
|
||||
msgid "workspace.options.blur-options.layer-blur"
|
||||
msgstr "Layer blur"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
|
||||
msgid "workspace.options.blur-options.background-blur"
|
||||
msgstr "Background blur"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:91
|
||||
msgid "workspace.options.blur-options.title.group"
|
||||
msgstr "Group blur"
|
||||
|
||||
@ -3790,9 +3790,12 @@ msgstr "Invitación enviada con éxito"
|
||||
msgid "notifications.invitation-link-copied"
|
||||
msgstr "Enlace de invitacion copiado"
|
||||
|
||||
msgid "notifications.mcp.active-tab-switching.text"
|
||||
msgid "notifications.mcp.active-in-another-tab"
|
||||
msgstr "MCP está activo en otra pestaña. ¿Cambiar a esta?"
|
||||
|
||||
msgid "notifications.mcp.active-in-this-tab"
|
||||
msgstr "MCP está ahora activo en esta pestaña."
|
||||
|
||||
#: src/app/main/ui/settings/delete_account.cljs:24
|
||||
msgid "notifications.profile-deletion-not-allowed"
|
||||
msgstr "No puedes borrar tu perfil. Reasigna tus equipos antes de seguir."
|
||||
@ -6059,6 +6062,14 @@ msgstr "Eliminar desenfoque"
|
||||
msgid "workspace.options.blur-options.title"
|
||||
msgstr "Desenfoque"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
|
||||
msgid "workspace.options.blur-options.layer-blur"
|
||||
msgstr "Desenfoque de capa"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
|
||||
msgid "workspace.options.blur-options.background-blur"
|
||||
msgstr "Desenfoque de fondo"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:91
|
||||
msgid "workspace.options.blur-options.title.group"
|
||||
msgstr "Desenfoque del grupo"
|
||||
|
||||
@ -55,6 +55,8 @@ function connectToMcpServer(baseUrl?: string, token?: string): void {
|
||||
|
||||
try {
|
||||
let wsUrl = baseUrl || PENPOT_MCP_WEBSOCKET_URL;
|
||||
let wsError: unknown | undefined;
|
||||
|
||||
if (token) {
|
||||
wsUrl += `?userToken=${encodeURIComponent(token)}`;
|
||||
}
|
||||
@ -79,14 +81,18 @@ function connectToMcpServer(baseUrl?: string, token?: string): void {
|
||||
};
|
||||
|
||||
ws.onclose = (event: CloseEvent) => {
|
||||
console.log("Disconnected from MCP server");
|
||||
const message = event.reason || undefined;
|
||||
updateConnectionStatus("disconnected", "Disconnected", false, message);
|
||||
// If we've send the error update we don't send the disconnect as well
|
||||
if (!wsError) {
|
||||
console.log("Disconnected from MCP server");
|
||||
const message = event.reason || undefined;
|
||||
updateConnectionStatus("disconnected", "Disconnected", false, message);
|
||||
}
|
||||
ws = null;
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error("WebSocket error:", error);
|
||||
wsError = error;
|
||||
// note: WebSocket error events typically don't contain detailed error messages
|
||||
updateConnectionStatus("error", "Connection error", false);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -282,7 +282,7 @@ Variants are a system for grouping related component versions along named proper
|
||||
- check with `isVariantContainer()`
|
||||
- property `variants: Variants`.
|
||||
* `Variants`: Defines the combinations of property values for which component variants can exist and manages the concrete component variants.
|
||||
- `properties: string[]` (ordered list of property names); `addProperty()`, `renameProperty(pos, name)`, `currentValues(property)`
|
||||
- `properties: string[]` (ordered list of property names); `addProperty(): void`, `renameProperty(pos, name)`, `currentValues(property)`
|
||||
- `variantComponents(): LibraryVariantComponent[]`
|
||||
* `LibraryVariantComponent` (extends `LibraryComponent`): full library component with metadata, for which `isVariant()` returns true.
|
||||
- `variantProps: { [property: string]: string }` (this component's value for each property)
|
||||
@ -292,11 +292,11 @@ Variants are a system for grouping related component versions along named proper
|
||||
Properties are often addressed positionally: `pos` parameter in various methods = index in `Variants.properties`.
|
||||
|
||||
**Creating a variant group**:
|
||||
- `component.transformInVariant(): null`: Converts a standard component into a variant group, creating a `VariantContainer` and a second duplicate variant.
|
||||
Both start with a default property `Property 1` with values `Value 1` / `Value 2`; there is no name-based auto-parsing.
|
||||
- `board.combineAsVariants(ids: string[]): null`: Combines the board (a main component instance) with other main components (referenced via IDs) into a new variant group.
|
||||
All components end up inside a single new `VariantContainer` on the canvas.
|
||||
- In both cases, look for the created `VariantContainer` on the page, and then edit properties using `variants.renameProperty(pos, name)`, `variants.addProperty()`, and `comp.setVariantProperty(pos, value)`.
|
||||
- `penpot.createVariantFromComponents(mainInstances: Board[]): VariantContainer`: Combines several main component instances into a new variant group.
|
||||
All components end up inside a single new container on the canvas.
|
||||
NOTE: The returned instance `variantContainer` is not usable but has an usable id; use `penpot.findShapeById(variantContainer.id)` to get the actual instance you can work with.
|
||||
The container's `Variants` instance is initialised with one property `Property 1`, with the property values set to the respective component's name.
|
||||
- After creation, edit properties using `variants.renameProperty(pos, name)`, `variants.addProperty()`, and `comp.setVariantProperty(pos, value)`.
|
||||
|
||||
**Adding a variant to an existing group**:
|
||||
Use `variantContainer.appendChild(mainInstance)` to move a component's main instance into the container, then set its position manually and assign property values via `setVariantProperty`.
|
||||
@ -342,7 +342,7 @@ Applying tokens:
|
||||
(if properties is undefined, use a default property based on the token type - not usually recommended).
|
||||
`TokenProperty` is a union type; possible values are:
|
||||
- "all": applies the token to all properties it can control
|
||||
- TokenBorderRadiusProps: "r1", "r2", "r3", "r4"
|
||||
- TokenBorderRadiusProps: "borderRadiusTopLeft", "borderRadiusTopRight", "borderRadiusBottomRight", "borderRadiusBottomLeft"
|
||||
- TokenShadowProps: "shadow"
|
||||
- TokenColorProps: "fill", "strokeColor"
|
||||
- TokenDimensionProps: "x", "y", "strokeWidth"
|
||||
@ -353,7 +353,7 @@ Applying tokens:
|
||||
- TokenNumberProps: "rotation"
|
||||
- TokenOpacityProps: "opacity"
|
||||
- TokenSizingProps: "width", "height", "layoutItemMinW", "layoutItemMaxW", "layoutItemMinH", "layoutItemMaxH"
|
||||
- TokenSpacingProps: "rowGap", "columnGap", "p1", "p2", "p3", "p4", "m1", "m2", "m3", "m4"
|
||||
- TokenSpacingProps: "rowGap", "columnGap", "paddingLeft", "paddingTop", "paddingRight", "paddingBottom", "marginLeft", "marginTop", "marginRight", "marginBottom"
|
||||
- TokenBorderWidthProps: "strokeWidth"
|
||||
- TokenTextCaseProps: "textCase"
|
||||
- TokenTextDecorationProps: "textDecoration"
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
## 1.5.0 (Unreleased)
|
||||
|
||||
- **plugin-types**: Added a flags subcontexts with the flag `naturalChildrenOrdering`
|
||||
- **plugins-runtime**: Added `version` field that returns the current version
|
||||
- **plugin-types**: Fix penpot.openPage() to navigate in same tab by default
|
||||
- **plugin-types**: Added `createVariantFromComponents`
|
||||
- **plugin-types**: Change return type of `combineAsVariants`
|
||||
- **plugin-types**: Added `textBounds` property for text shapes
|
||||
- **plugin-types**: Added flag `throwValidationErrors` to enable exceptions on validation
|
||||
|
||||
## 1.4.2 (2026-01-21)
|
||||
|
||||
|
||||
@ -264,7 +264,7 @@ export class AppComponent {
|
||||
type: 'apply-token',
|
||||
setId: this.currentSetId,
|
||||
tokenId,
|
||||
// properties: ['strokeColor'] // Uncomment to choose attribute to apply
|
||||
// properties: ['borderRadiusTopRight'] // Uncomment to choose attribute to apply
|
||||
}); // (incompatible attributes will have no effect)
|
||||
}
|
||||
}
|
||||
|
||||
34
plugins/libs/plugin-types/index.d.ts
vendored
34
plugins/libs/plugin-types/index.d.ts
vendored
@ -768,6 +768,11 @@ export interface CommonLayout {
|
||||
* Represents the context of Penpot, providing access to various Penpot functionalities and data.
|
||||
*/
|
||||
export interface Context {
|
||||
/**
|
||||
* Returns the current penpot version.
|
||||
*/
|
||||
readonly version: string;
|
||||
|
||||
/**
|
||||
* The root shape in the current Penpot context. Requires `content:read` permission.
|
||||
*
|
||||
@ -1710,6 +1715,13 @@ export interface Flags {
|
||||
* Defaults to false
|
||||
*/
|
||||
naturalChildOrdering: boolean;
|
||||
|
||||
/**
|
||||
* If `true` the validation errors will throw an exception instead of displaying an
|
||||
* error in the debugger console.
|
||||
* Defaults to false
|
||||
*/
|
||||
throwValidationErrors: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5231,7 +5243,11 @@ export interface TokenTheme {
|
||||
/**
|
||||
* The properties that a BorderRadius token can be applied to.
|
||||
*/
|
||||
type TokenBorderRadiusProps = 'r1' | 'r2' | 'r3' | 'r4';
|
||||
type TokenBorderRadiusProps =
|
||||
| 'borderRadiusTopLeft'
|
||||
| 'borderRadiusTopRight'
|
||||
| 'borderRadiusBottomRight'
|
||||
| 'borderRadiusBottomLeft';
|
||||
|
||||
/**
|
||||
* The properties that a Shadow token can be applied to.
|
||||
@ -5307,16 +5323,16 @@ type TokenSpacingProps =
|
||||
| 'columnGap'
|
||||
|
||||
// Spacing / Padding
|
||||
| 'p1'
|
||||
| 'p2'
|
||||
| 'p3'
|
||||
| 'p4'
|
||||
| 'paddingLeft'
|
||||
| 'paddingTop'
|
||||
| 'paddingRight'
|
||||
| 'paddingBottom'
|
||||
|
||||
// Spacing / Margin
|
||||
| 'm1'
|
||||
| 'm2'
|
||||
| 'm3'
|
||||
| 'm4';
|
||||
| 'marginLeft'
|
||||
| 'marginTop'
|
||||
| 'marginRight'
|
||||
| 'marginBottom';
|
||||
|
||||
/**
|
||||
* The properties that a BorderWidth token can be applied to.
|
||||
|
||||
@ -165,6 +165,10 @@ export function createApi(
|
||||
|
||||
// Penpot State API
|
||||
|
||||
get version(): string {
|
||||
return plugin.context.version;
|
||||
},
|
||||
|
||||
get root(): Shape | null {
|
||||
checkPermission('content:read');
|
||||
return plugin.context.root;
|
||||
|
||||
@ -61,11 +61,13 @@ pub fn wasm_error(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let _: &dyn std::error::Error = &__e;
|
||||
let __msg = __e.to_string();
|
||||
crate::mem::set_error_code(__e.into());
|
||||
crate::mem::free_bytes().expect("Failed to free bytes");
|
||||
panic!("WASM error: {}",__msg);
|
||||
}
|
||||
},
|
||||
Err(__payload) => {
|
||||
crate::mem::set_error_code(0x02); // critical, same as Error::Critical
|
||||
crate::mem::free_bytes().expect("Failed to free bytes");
|
||||
std::panic::resume_unwind(__payload);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ mod emscripten;
|
||||
mod error;
|
||||
mod math;
|
||||
mod mem;
|
||||
mod options;
|
||||
mod performance;
|
||||
mod render;
|
||||
mod shapes;
|
||||
@ -30,6 +29,9 @@ use uuid::Uuid;
|
||||
|
||||
pub(crate) static mut STATE: Option<Box<State>> = None;
|
||||
|
||||
// FIXME: These with_state* macros should be using our CriticalError instead of expect.
|
||||
// But to do that, we need to not use them at domain-level (i.e. in business logic), just
|
||||
// in the context of the wasm call.
|
||||
#[macro_export]
|
||||
macro_rules! with_state_mut {
|
||||
($state:ident, $block:block) => {{
|
||||
@ -102,7 +104,7 @@ macro_rules! with_state_mut_current_shape {
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn init(width: i32, height: i32) -> Result<()> {
|
||||
let state_box = Box::new(State::new(width, height));
|
||||
let state_box = Box::new(State::try_new(width, height)?);
|
||||
unsafe {
|
||||
STATE = Some(state_box);
|
||||
}
|
||||
@ -138,7 +140,7 @@ pub extern "C" fn set_render_options(debug: u32, dpr: f32) -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
let render_state = state.render_state_mut();
|
||||
render_state.set_debug_flags(debug);
|
||||
render_state.set_dpr(dpr);
|
||||
render_state.set_dpr(dpr)?;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -162,7 +164,7 @@ pub extern "C" fn render(_: i32) -> Result<()> {
|
||||
state.rebuild_touched_tiles();
|
||||
state
|
||||
.start_render_loop(performance::get_time())
|
||||
.expect("Error rendering");
|
||||
.map_err(|_| Error::RecoverableError("Error rendering".to_string()))?;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -174,7 +176,7 @@ pub extern "C" fn render_sync() -> Result<()> {
|
||||
state.rebuild_tiles();
|
||||
state
|
||||
.render_sync(performance::get_time())
|
||||
.expect("Error rendering");
|
||||
.map_err(|_| Error::RecoverableError("Error rendering".to_string()))?;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -236,24 +238,12 @@ pub extern "C" fn render_preview() -> Result<()> {
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn process_animation_frame(timestamp: i32) -> Result<()> {
|
||||
let result = std::panic::catch_unwind(|| {
|
||||
with_state_mut!(state, {
|
||||
state
|
||||
.process_animation_frame(timestamp)
|
||||
.expect("Error processing animation frame");
|
||||
});
|
||||
});
|
||||
let result = with_state_mut!(state, { state.process_animation_frame(timestamp) });
|
||||
|
||||
match result {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
match err.downcast_ref::<String>() {
|
||||
Some(message) => println!("process_animation_frame error: {}", message),
|
||||
None => println!("process_animation_frame error: {:?}", err),
|
||||
}
|
||||
std::panic::resume_unwind(err);
|
||||
}
|
||||
if let Err(err) = result {
|
||||
eprintln!("process_animation_frame error: {}", err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -270,7 +260,7 @@ pub extern "C" fn reset_canvas() -> Result<()> {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn resize_viewbox(width: i32, height: i32) -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
state.resize(width, height);
|
||||
state.resize(width, height)?;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -305,43 +295,33 @@ pub extern "C" fn set_view_start() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Finishes a view interaction (zoom or pan). Rebuilds the tile index
|
||||
/// and invalidates the tile texture cache so the subsequent render
|
||||
/// re-draws all tiles at full quality (fast_mode is off at this point).
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn set_view_end() -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
let _end_start = performance::begin_timed_log!("set_view_end");
|
||||
performance::begin_measure!("set_view_end");
|
||||
state.render_state.options.set_fast_mode(false);
|
||||
state.render_state.cancel_animation_frame();
|
||||
|
||||
// Update tile_viewbox first so that get_tiles_for_shape uses the correct interest area
|
||||
// This is critical because we limit tiles to the interest area for optimization
|
||||
let scale = state.render_state.get_scale();
|
||||
state
|
||||
.render_state
|
||||
.tile_viewbox
|
||||
.update(state.render_state.viewbox, scale);
|
||||
|
||||
// We rebuild the tile index on both pan and zoom because `get_tiles_for_shape`
|
||||
// clips each shape to the current `TileViewbox::interest_rect` (viewport-dependent).
|
||||
let _rebuild_start = performance::begin_timed_log!("rebuild_tiles");
|
||||
performance::begin_measure!("set_view_end::rebuild_tiles");
|
||||
if state.render_state.options.is_profile_rebuild_tiles() {
|
||||
state.rebuild_tiles();
|
||||
} else {
|
||||
// Rebuild tile index + invalidate tile texture cache.
|
||||
// Cache canvas is preserved so render_from_cache can still
|
||||
// show a scaled preview during zoom.
|
||||
state.rebuild_tiles_shallow();
|
||||
}
|
||||
performance::end_measure!("set_view_end::rebuild_tiles");
|
||||
performance::end_timed_log!("rebuild_tiles", _rebuild_start);
|
||||
|
||||
state.render_state.sync_cached_viewbox();
|
||||
performance::end_measure!("set_view_end");
|
||||
performance::end_timed_log!("set_view_end", _end_start);
|
||||
#[cfg(feature = "profile-macros")]
|
||||
{
|
||||
let total_time = performance::get_time() - unsafe { VIEW_INTERACTION_START };
|
||||
performance::console_log!("[PERF] view_interaction: {}ms", total_time);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -362,8 +342,8 @@ pub extern "C" fn set_focus_mode() -> Result<()> {
|
||||
|
||||
let entries: Vec<Uuid> = bytes
|
||||
.chunks(size_of::<<Uuid as SerializableResult>::BytesType>())
|
||||
.map(|data| Uuid::try_from(data).unwrap())
|
||||
.collect();
|
||||
.map(|data| Uuid::try_from(data).map_err(|e| Error::RecoverableError(e.to_string())))
|
||||
.collect::<Result<Vec<Uuid>>>()?;
|
||||
|
||||
with_state_mut!(state, {
|
||||
state.set_focus_mode(entries);
|
||||
@ -637,8 +617,8 @@ pub extern "C" fn set_children() -> Result<()> {
|
||||
|
||||
let entries: Vec<Uuid> = bytes
|
||||
.chunks(size_of::<<Uuid as SerializableResult>::BytesType>())
|
||||
.map(|data| Uuid::try_from(data).unwrap())
|
||||
.collect();
|
||||
.map(|data| Uuid::try_from(data).map_err(|e| Error::CriticalError(e.to_string())))
|
||||
.collect::<Result<Vec<Uuid>>>()?;
|
||||
|
||||
set_children_set(entries)?;
|
||||
|
||||
@ -761,10 +741,15 @@ pub extern "C" fn get_selection_rect() -> Result<*mut u8> {
|
||||
pub extern "C" fn set_structure_modifiers() -> Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
let entries: Vec<_> = bytes
|
||||
let entries: Vec<StructureEntry> = bytes
|
||||
.chunks(44)
|
||||
.map(|data| StructureEntry::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
.map(|chunk| {
|
||||
let data = chunk
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid StructureEntry bytes".to_string()))?;
|
||||
Ok(StructureEntry::from_bytes(data))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
with_state_mut!(state, {
|
||||
let mut structure = HashMap::new();
|
||||
@ -783,7 +768,9 @@ pub extern "C" fn set_structure_modifiers() -> Result<()> {
|
||||
structure.entry(entry.parent).or_insert_with(Vec::new);
|
||||
structure
|
||||
.get_mut(&entry.parent)
|
||||
.expect("Parent not found for entry")
|
||||
.ok_or(Error::CriticalError(
|
||||
"Parent not found for entry".to_string(),
|
||||
))?
|
||||
.push(entry);
|
||||
}
|
||||
}
|
||||
@ -814,10 +801,10 @@ pub extern "C" fn clean_modifiers() -> Result<()> {
|
||||
pub extern "C" fn set_modifiers() -> Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
let entries: Vec<_> = bytes
|
||||
let entries: Vec<TransformEntry> = bytes
|
||||
.chunks(size_of::<<TransformEntry as SerializableResult>::BytesType>())
|
||||
.map(|data| TransformEntry::try_from(data).unwrap())
|
||||
.collect();
|
||||
.map(|data| TransformEntry::try_from(data).map_err(|e| Error::CriticalError(e.to_string())))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let mut modifiers = HashMap::new();
|
||||
let mut ids = Vec::<Uuid>::new();
|
||||
@ -828,7 +815,7 @@ pub extern "C" fn set_modifiers() -> Result<()> {
|
||||
|
||||
with_state_mut!(state, {
|
||||
state.set_modifiers(modifiers);
|
||||
state.rebuild_modifier_tiles(ids);
|
||||
state.rebuild_modifier_tiles(ids)?;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
@ -838,8 +825,10 @@ pub extern "C" fn set_modifiers() -> Result<()> {
|
||||
pub extern "C" fn start_temp_objects() -> Result<()> {
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
let mut state = STATE.take().expect("Got an invalid state pointer");
|
||||
state = Box::new(state.start_temp_objects());
|
||||
let mut state = STATE.take().ok_or(Error::CriticalError(
|
||||
"Got an invalid state pointer".to_string(),
|
||||
))?;
|
||||
state = Box::new(state.start_temp_objects()?);
|
||||
STATE = Some(state);
|
||||
}
|
||||
Ok(())
|
||||
@ -850,8 +839,10 @@ pub extern "C" fn start_temp_objects() -> Result<()> {
|
||||
pub extern "C" fn end_temp_objects() -> Result<()> {
|
||||
unsafe {
|
||||
#[allow(static_mut_refs)]
|
||||
let mut state = STATE.take().expect("Got an invalid state pointer");
|
||||
state = Box::new(state.end_temp_objects());
|
||||
let mut state = STATE.take().ok_or(Error::CriticalError(
|
||||
"Got an invalid state pointer".to_string(),
|
||||
))?;
|
||||
state = Box::new(state.end_temp_objects()?);
|
||||
STATE = Some(state);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
pub const DEBUG_VISIBLE: u32 = 0x01;
|
||||
pub const PROFILE_REBUILD_TILES: u32 = 0x02;
|
||||
pub const FAST_MODE: u32 = 0x04;
|
||||
pub const INFO_TEXT: u32 = 0x08;
|
||||
@ -21,6 +21,7 @@ use gpu_state::GpuState;
|
||||
use options::RenderOptions;
|
||||
pub use surfaces::{SurfaceId, Surfaces};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::performance;
|
||||
use crate::shapes::{
|
||||
all_with_ancestors, radius_to_sigma, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor,
|
||||
@ -281,6 +282,9 @@ pub(crate) struct RenderState {
|
||||
pub current_tile: Option<tiles::Tile>,
|
||||
pub sampling_options: skia::SamplingOptions,
|
||||
pub render_area: Rect,
|
||||
// render_area expanded by surface margins — used for visibility checks so that
|
||||
// shapes in the margin zone are rendered (needed for background blur sampling).
|
||||
pub render_area_with_margins: Rect,
|
||||
pub tile_viewbox: tiles::TileViewbox,
|
||||
pub tiles: tiles::TileHashMap,
|
||||
pub pending_tiles: PendingTiles,
|
||||
@ -323,19 +327,19 @@ pub fn get_cache_size(viewbox: Viewbox, scale: f32) -> skia::ISize {
|
||||
}
|
||||
|
||||
impl RenderState {
|
||||
pub fn new(width: i32, height: i32) -> RenderState {
|
||||
pub fn try_new(width: i32, height: i32) -> Result<RenderState> {
|
||||
// This needs to be done once per WebGL context.
|
||||
let mut gpu_state = GpuState::new();
|
||||
let mut gpu_state = GpuState::try_new()?;
|
||||
let sampling_options =
|
||||
skia::SamplingOptions::new(skia::FilterMode::Linear, skia::MipmapMode::Nearest);
|
||||
|
||||
let fonts = FontStore::new();
|
||||
let surfaces = Surfaces::new(
|
||||
let fonts = FontStore::try_new()?;
|
||||
let surfaces = Surfaces::try_new(
|
||||
&mut gpu_state,
|
||||
(width, height),
|
||||
sampling_options,
|
||||
tiles::get_tile_dimensions(),
|
||||
);
|
||||
)?;
|
||||
|
||||
// This is used multiple times everywhere so instead of creating new instances every
|
||||
// time we reuse this one.
|
||||
@ -343,7 +347,7 @@ impl RenderState {
|
||||
let viewbox = Viewbox::new(width as f32, height as f32);
|
||||
let tiles = tiles::TileHashMap::new();
|
||||
|
||||
RenderState {
|
||||
Ok(RenderState {
|
||||
gpu_state: gpu_state.clone(),
|
||||
options: RenderOptions::default(),
|
||||
surfaces,
|
||||
@ -358,6 +362,7 @@ impl RenderState {
|
||||
current_tile: None,
|
||||
sampling_options,
|
||||
render_area: Rect::new_empty(),
|
||||
render_area_with_margins: Rect::new_empty(),
|
||||
tiles,
|
||||
tile_viewbox: tiles::TileViewbox::new_with_interest(
|
||||
viewbox,
|
||||
@ -373,7 +378,7 @@ impl RenderState {
|
||||
touched_ids: HashSet::default(),
|
||||
ignore_nested_blurs: false,
|
||||
preview_mode: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Combines every visible layer blur currently active (ancestors + shape)
|
||||
@ -426,18 +431,116 @@ impl RenderState {
|
||||
shape.frame_clip_layer_blur()
|
||||
}
|
||||
|
||||
/// Renders background blur effect directly to the Current surface.
|
||||
/// Must be called BEFORE any save_layer for the shape's own opacity/blend,
|
||||
/// so that the backdrop blur is independent of the shape's visual properties.
|
||||
fn render_background_blur(&mut self, shape: &Shape) {
|
||||
if self.options.is_fast_mode() {
|
||||
return;
|
||||
}
|
||||
if matches!(shape.shape_type, Type::Text(_)) || matches!(shape.shape_type, Type::SVGRaw(_))
|
||||
{
|
||||
return;
|
||||
}
|
||||
let blur = match shape
|
||||
.blur
|
||||
.filter(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
|
||||
{
|
||||
Some(blur) => blur,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let scale = self.get_scale();
|
||||
let scaled_sigma = radius_to_sigma(blur.value * scale);
|
||||
// Cap sigma so the blur kernel (≈3σ) stays within the tile margin.
|
||||
// This prevents visible seams at tile boundaries when zoomed in.
|
||||
let margin = self.surfaces.margins().width as f32;
|
||||
let max_sigma = margin / 3.0;
|
||||
let capped_sigma = scaled_sigma.min(max_sigma);
|
||||
|
||||
let blur_filter = match skia::image_filters::blur(
|
||||
(capped_sigma, capped_sigma),
|
||||
skia::TileMode::Clamp,
|
||||
None,
|
||||
None,
|
||||
) {
|
||||
Some(filter) => filter,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let snapshot = self.surfaces.snapshot(SurfaceId::Current);
|
||||
let translation = self
|
||||
.surfaces
|
||||
.get_render_context_translation(self.render_area, scale);
|
||||
|
||||
let center = shape.center();
|
||||
let mut matrix = shape.transform;
|
||||
matrix.post_translate(center);
|
||||
matrix.pre_translate(-center);
|
||||
|
||||
let canvas = self.surfaces.canvas(SurfaceId::Current);
|
||||
canvas.save();
|
||||
|
||||
// Current has no render context transform (identity canvas).
|
||||
// Apply scale + translate + shape transform so the clip maps
|
||||
// from shape-local coords to device pixels correctly.
|
||||
canvas.scale((scale, scale));
|
||||
canvas.translate(translation);
|
||||
canvas.concat(&matrix);
|
||||
|
||||
// Clip to shape's path based on shape type
|
||||
match &shape.shape_type {
|
||||
Type::Rect(data) if data.corners.is_some() => {
|
||||
let rrect = RRect::new_rect_radii(shape.selrect, data.corners.as_ref().unwrap());
|
||||
canvas.clip_rrect(rrect, skia::ClipOp::Intersect, true);
|
||||
}
|
||||
Type::Frame(data) if data.corners.is_some() => {
|
||||
let rrect = RRect::new_rect_radii(shape.selrect, data.corners.as_ref().unwrap());
|
||||
canvas.clip_rrect(rrect, skia::ClipOp::Intersect, true);
|
||||
}
|
||||
Type::Rect(_) | Type::Frame(_) => {
|
||||
canvas.clip_rect(shape.selrect, skia::ClipOp::Intersect, true);
|
||||
}
|
||||
Type::Circle => {
|
||||
let mut pb = skia::PathBuilder::new();
|
||||
pb.add_oval(shape.selrect, None, None);
|
||||
canvas.clip_path(&pb.detach(), skia::ClipOp::Intersect, true);
|
||||
}
|
||||
_ => {
|
||||
if let Some(path) = shape.get_skia_path() {
|
||||
canvas.clip_path(&path, skia::ClipOp::Intersect, true);
|
||||
} else {
|
||||
canvas.clip_rect(shape.selrect, skia::ClipOp::Intersect, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset matrix so snapshot draws pixel-for-pixel on the surface.
|
||||
// Clips survive reset_matrix (stored in device coords).
|
||||
canvas.reset_matrix();
|
||||
|
||||
// Use Src blend to replace content within the clip with the
|
||||
// blurred version (not SrcOver which would double-render).
|
||||
let mut paint = skia::Paint::default();
|
||||
paint.set_image_filter(blur_filter);
|
||||
paint.set_blend_mode(skia::BlendMode::Src);
|
||||
canvas.draw_image(&snapshot, (0, 0), Some(&paint));
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/// Runs `f` with `ignore_nested_blurs` temporarily forced to `true`.
|
||||
/// Certain off-screen passes (e.g. shadow masks) must render shapes without
|
||||
/// inheriting ancestor blur. This helper guarantees the flag is restored.
|
||||
fn with_nested_blurs_suppressed<F, R>(&mut self, f: F) -> R
|
||||
fn with_nested_blurs_suppressed<F, R>(&mut self, f: F) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut RenderState) -> R,
|
||||
F: FnOnce(&mut RenderState) -> Result<R>,
|
||||
{
|
||||
let previous = self.ignore_nested_blurs;
|
||||
self.ignore_nested_blurs = true;
|
||||
let result = f(self);
|
||||
let result = f(self)?;
|
||||
self.ignore_nested_blurs = previous;
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn fonts(&self) -> &FontStore {
|
||||
@ -448,12 +551,7 @@ impl RenderState {
|
||||
&mut self.fonts
|
||||
}
|
||||
|
||||
pub fn add_image(
|
||||
&mut self,
|
||||
id: Uuid,
|
||||
is_thumbnail: bool,
|
||||
image_data: &[u8],
|
||||
) -> Result<(), String> {
|
||||
pub fn add_image(&mut self, id: Uuid, is_thumbnail: bool, image_data: &[u8]) -> Result<()> {
|
||||
self.images.add(id, is_thumbnail, image_data)
|
||||
}
|
||||
|
||||
@ -465,7 +563,7 @@ impl RenderState {
|
||||
texture_id: u32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
self.images
|
||||
.add_image_from_gl_texture(id, is_thumbnail, texture_id, width, height)
|
||||
}
|
||||
@ -478,15 +576,16 @@ impl RenderState {
|
||||
self.options.flags = debug;
|
||||
}
|
||||
|
||||
pub fn set_dpr(&mut self, dpr: f32) {
|
||||
pub fn set_dpr(&mut self, dpr: f32) -> Result<()> {
|
||||
if Some(dpr) != self.options.dpr {
|
||||
self.options.dpr = Some(dpr);
|
||||
self.resize(
|
||||
self.viewbox.width.floor() as i32,
|
||||
self.viewbox.height.floor() as i32,
|
||||
);
|
||||
)?;
|
||||
self.fonts.set_scale_debug_font(dpr);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_background_color(&mut self, color: skia::Color) {
|
||||
@ -497,13 +596,15 @@ impl RenderState {
|
||||
self.preview_mode = enabled;
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: i32, height: i32) {
|
||||
pub fn resize(&mut self, width: i32, height: i32) -> Result<()> {
|
||||
let dpr_width = (width as f32 * self.options.dpr()).floor() as i32;
|
||||
let dpr_height = (height as f32 * self.options.dpr()).floor() as i32;
|
||||
self.surfaces
|
||||
.resize(&mut self.gpu_state, dpr_width, dpr_height);
|
||||
.resize(&mut self.gpu_state, dpr_width, dpr_height)?;
|
||||
self.viewbox.set_wh(width as f32, height as f32);
|
||||
self.tile_viewbox.update(self.viewbox, self.get_scale());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush_and_submit(&mut self) {
|
||||
@ -525,19 +626,23 @@ impl RenderState {
|
||||
self.surfaces.canvas(surface_id).restore();
|
||||
}
|
||||
|
||||
pub fn apply_render_to_final_canvas(&mut self, rect: skia::Rect) {
|
||||
let tile_rect = self.get_current_aligned_tile_bounds();
|
||||
pub fn apply_render_to_final_canvas(&mut self, rect: skia::Rect) -> Result<()> {
|
||||
let tile_rect = self.get_current_aligned_tile_bounds()?;
|
||||
self.surfaces.cache_current_tile_texture(
|
||||
&self.tile_viewbox,
|
||||
&self.current_tile.unwrap(),
|
||||
&self
|
||||
.current_tile
|
||||
.ok_or(Error::CriticalError("Current tile not found".to_string()))?,
|
||||
&tile_rect,
|
||||
);
|
||||
|
||||
self.surfaces.draw_cached_tile_surface(
|
||||
self.current_tile.unwrap(),
|
||||
self.current_tile
|
||||
.ok_or(Error::CriticalError("Current tile not found".to_string()))?,
|
||||
rect,
|
||||
self.background_color,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_drawing_to_render_canvas(&mut self, shape: Option<&Shape>) {
|
||||
@ -646,7 +751,7 @@ impl RenderState {
|
||||
offset: Option<(f32, f32)>,
|
||||
parent_shadows: Option<Vec<skia_safe::Paint>>,
|
||||
outset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let surface_ids = fills_surface_id as u32
|
||||
| strokes_surface_id as u32
|
||||
| innershadows_surface_id as u32
|
||||
@ -711,7 +816,7 @@ impl RenderState {
|
||||
antialias,
|
||||
SurfaceId::Current,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
|
||||
// Pass strokes in natural order; stroke merging handles top-most ordering internally.
|
||||
let visible_strokes: Vec<&Stroke> = shape.visible_strokes().collect();
|
||||
@ -722,7 +827,7 @@ impl RenderState {
|
||||
Some(SurfaceId::Current),
|
||||
antialias,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
|
||||
self.surfaces.apply_mut(SurfaceId::Current as u32, |s| {
|
||||
s.canvas().restore();
|
||||
@ -738,7 +843,7 @@ impl RenderState {
|
||||
s.canvas().restore();
|
||||
});
|
||||
}
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// set clipping
|
||||
@ -788,6 +893,22 @@ impl RenderState {
|
||||
|
||||
// We don't want to change the value in the global state
|
||||
let mut shape: Cow<Shape> = Cow::Borrowed(shape);
|
||||
|
||||
// Remove background blur from the shape so it doesn't get processed
|
||||
// as a layer blur. The actual rendering is done before the save_layer
|
||||
// in render_background_blur() so it's independent of shape opacity.
|
||||
if !fast_mode
|
||||
&& apply_to_current_surface
|
||||
&& fills_surface_id == SurfaceId::Fills
|
||||
&& !matches!(shape.shape_type, Type::Text(_))
|
||||
&& !matches!(shape.shape_type, Type::SVGRaw(_))
|
||||
&& shape
|
||||
.blur
|
||||
.is_some_and(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
|
||||
{
|
||||
shape.to_mut().set_blur(None);
|
||||
}
|
||||
|
||||
let frame_has_blur = Self::frame_clip_layer_blur(&shape).is_some();
|
||||
let shape_has_blur = shape.blur.is_some();
|
||||
|
||||
@ -899,7 +1020,7 @@ impl RenderState {
|
||||
None,
|
||||
text_fill_inset,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
|
||||
for (stroke_paragraphs, layer_opacity) in stroke_paragraphs_list
|
||||
.iter_mut()
|
||||
@ -916,7 +1037,7 @@ impl RenderState {
|
||||
text_stroke_blur_outset,
|
||||
None,
|
||||
*layer_opacity,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
let mut drop_shadows = shape.drop_shadow_paints();
|
||||
@ -959,7 +1080,7 @@ impl RenderState {
|
||||
blur_filter.as_ref(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
shadows::render_text_shadows(
|
||||
@ -970,7 +1091,7 @@ impl RenderState {
|
||||
text_drop_shadows_surface_id.into(),
|
||||
&parent_shadows,
|
||||
&blur_filter,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
// 1. Text drop shadows
|
||||
@ -986,7 +1107,7 @@ impl RenderState {
|
||||
blur_filter.as_ref(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1001,7 +1122,7 @@ impl RenderState {
|
||||
blur_filter.as_ref(),
|
||||
text_fill_inset,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
|
||||
// 3. Stroke drop shadows
|
||||
shadows::render_text_shadows(
|
||||
@ -1012,7 +1133,7 @@ impl RenderState {
|
||||
text_drop_shadows_surface_id.into(),
|
||||
&drop_shadows,
|
||||
&blur_filter,
|
||||
);
|
||||
)?;
|
||||
|
||||
// 4. Stroke fills
|
||||
for (stroke_paragraphs, layer_opacity) in stroke_paragraphs_list
|
||||
@ -1030,7 +1151,7 @@ impl RenderState {
|
||||
text_stroke_blur_outset,
|
||||
None,
|
||||
*layer_opacity,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
// 5. Stroke inner shadows
|
||||
@ -1042,7 +1163,7 @@ impl RenderState {
|
||||
Some(innershadows_surface_id),
|
||||
&inner_shadows,
|
||||
&blur_filter,
|
||||
);
|
||||
)?;
|
||||
|
||||
// 6. Fill Inner shadows
|
||||
if !shape.has_visible_strokes() {
|
||||
@ -1057,7 +1178,7 @@ impl RenderState {
|
||||
blur_filter.as_ref(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1105,7 +1226,7 @@ impl RenderState {
|
||||
antialias,
|
||||
fills_surface_id,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
fills::render(
|
||||
@ -1115,7 +1236,7 @@ impl RenderState {
|
||||
antialias,
|
||||
fills_surface_id,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
// Skip stroke rendering for clipped frames - they are drawn in render_shape_exit
|
||||
@ -1131,7 +1252,7 @@ impl RenderState {
|
||||
Some(strokes_surface_id),
|
||||
antialias,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
if !fast_mode {
|
||||
for stroke in &visible_strokes {
|
||||
shadows::render_stroke_inner_shadows(
|
||||
@ -1140,7 +1261,7 @@ impl RenderState {
|
||||
stroke,
|
||||
antialias,
|
||||
innershadows_surface_id,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1177,13 +1298,23 @@ impl RenderState {
|
||||
s.canvas().restore();
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_render_context(&mut self, tile: tiles::Tile) {
|
||||
self.current_tile = Some(tile);
|
||||
self.render_area = tiles::get_tile_rect(tile, self.get_scale());
|
||||
self.surfaces
|
||||
.update_render_context(self.render_area, self.get_scale());
|
||||
let scale = self.get_scale();
|
||||
self.render_area = tiles::get_tile_rect(tile, scale);
|
||||
let margins = self.surfaces.margins();
|
||||
let margin_w = margins.width as f32 / scale;
|
||||
let margin_h = margins.height as f32 / scale;
|
||||
self.render_area_with_margins = skia::Rect::from_ltrb(
|
||||
self.render_area.left - margin_w,
|
||||
self.render_area.top - margin_h,
|
||||
self.render_area.right + margin_w,
|
||||
self.render_area.bottom + margin_h,
|
||||
);
|
||||
self.surfaces.update_render_context(self.render_area, scale);
|
||||
}
|
||||
|
||||
pub fn cancel_animation_frame(&mut self) {
|
||||
@ -1246,7 +1377,7 @@ impl RenderState {
|
||||
|
||||
/// Render a preview of the shapes during loading.
|
||||
/// This rebuilds tiles for touched shapes and renders synchronously.
|
||||
pub fn render_preview(&mut self, tree: ShapesPoolRef, timestamp: i32) -> Result<(), String> {
|
||||
pub fn render_preview(&mut self, tree: ShapesPoolRef, timestamp: i32) -> Result<()> {
|
||||
let _start = performance::begin_timed_log!("render_preview");
|
||||
performance::begin_measure!("render_preview");
|
||||
|
||||
@ -1276,7 +1407,7 @@ impl RenderState {
|
||||
tree: ShapesPoolRef,
|
||||
timestamp: i32,
|
||||
sync_render: bool,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
let _start = performance::begin_timed_log!("start_render_loop");
|
||||
let scale = self.get_scale();
|
||||
|
||||
@ -1303,7 +1434,7 @@ impl RenderState {
|
||||
|| viewbox_cache_size.height > cached_viewbox_cache_size.height
|
||||
{
|
||||
self.surfaces
|
||||
.resize_cache(viewbox_cache_size, VIEWPORT_INTEREST_AREA_THRESHOLD);
|
||||
.resize_cache(viewbox_cache_size, VIEWPORT_INTEREST_AREA_THRESHOLD)?;
|
||||
}
|
||||
|
||||
// FIXME - review debug
|
||||
@ -1348,7 +1479,7 @@ impl RenderState {
|
||||
base_object: Option<&Uuid>,
|
||||
tree: ShapesPoolRef,
|
||||
timestamp: i32,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
performance::begin_measure!("process_animation_frame");
|
||||
if self.render_in_progress {
|
||||
if tree.len() != 0 {
|
||||
@ -1372,7 +1503,7 @@ impl RenderState {
|
||||
base_object: Option<&Uuid>,
|
||||
tree: ShapesPoolRef,
|
||||
timestamp: i32,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
if tree.len() != 0 {
|
||||
self.render_shape_tree_partial(base_object, tree, timestamp, false)?;
|
||||
}
|
||||
@ -1462,7 +1593,7 @@ impl RenderState {
|
||||
element: &Shape,
|
||||
visited_mask: bool,
|
||||
clip_bounds: Option<ClipStack>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if visited_mask {
|
||||
// Because masked groups needs two rendering passes (first drawing
|
||||
// the content and then drawing the mask), we need to do an
|
||||
@ -1533,7 +1664,7 @@ impl RenderState {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
// Only restore if we created a layer (optimization for simple shapes)
|
||||
@ -1545,19 +1676,22 @@ impl RenderState {
|
||||
}
|
||||
|
||||
self.focus_mode.exit(&element.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_current_tile_bounds(&mut self) -> Rect {
|
||||
let tiles::Tile(tile_x, tile_y) = self.current_tile.unwrap();
|
||||
pub fn get_current_tile_bounds(&mut self) -> Result<Rect> {
|
||||
let tiles::Tile(tile_x, tile_y) = self
|
||||
.current_tile
|
||||
.ok_or(Error::CriticalError("Current tile not found".to_string()))?;
|
||||
let scale = self.get_scale();
|
||||
let offset_x = self.viewbox.area.left * scale;
|
||||
let offset_y = self.viewbox.area.top * scale;
|
||||
Rect::from_xywh(
|
||||
Ok(Rect::from_xywh(
|
||||
(tile_x as f32 * tiles::TILE_SIZE) - offset_x,
|
||||
(tile_y as f32 * tiles::TILE_SIZE) - offset_y,
|
||||
tiles::TILE_SIZE,
|
||||
tiles::TILE_SIZE,
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_rect_bounds(&mut self, rect: skia::Rect) -> Rect {
|
||||
@ -1605,8 +1739,11 @@ impl RenderState {
|
||||
// lower multiple of `TILE_SIZE`. This ensures the tile bounds are aligned
|
||||
// with the global tile grid, which is useful for rendering tiles in a
|
||||
/// consistent and predictable layout.
|
||||
pub fn get_current_aligned_tile_bounds(&mut self) -> Rect {
|
||||
self.get_aligned_tile_bounds(self.current_tile.unwrap())
|
||||
pub fn get_current_aligned_tile_bounds(&mut self) -> Result<Rect> {
|
||||
Ok(self.get_aligned_tile_bounds(
|
||||
self.current_tile
|
||||
.ok_or(Error::CriticalError("Current tile not found".to_string()))?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Renders a drop shadow effect for the given shape.
|
||||
@ -1623,7 +1760,7 @@ impl RenderState {
|
||||
scale: f32,
|
||||
translation: (f32, f32),
|
||||
extra_layer_blur: Option<Blur>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let mut transformed_shadow: Cow<Shadow> = Cow::Borrowed(shadow);
|
||||
transformed_shadow.to_mut().offset = (0.0, 0.0);
|
||||
transformed_shadow.to_mut().color = skia::Color::BLACK;
|
||||
@ -1678,15 +1815,15 @@ impl RenderState {
|
||||
plain_shape_mut.clip_content = false;
|
||||
|
||||
let Some(drop_filter) = transformed_shadow.get_drop_shadow_filter() else {
|
||||
return;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut bounds = drop_filter.compute_fast_bounds(shape_bounds);
|
||||
// Account for the shadow offset so the temporary surface fully contains the shifted blur.
|
||||
bounds.offset(world_offset);
|
||||
// Early cull if the shadow bounds are outside the render area.
|
||||
if !bounds.intersects(self.render_area) {
|
||||
return;
|
||||
if !bounds.intersects(self.render_area_with_margins) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// blur=0 at high zoom: draw directly on DropShadows with geometric spread (no filter).
|
||||
@ -1708,11 +1845,11 @@ impl RenderState {
|
||||
Some(shadow.offset),
|
||||
None,
|
||||
Some(shadow.spread),
|
||||
);
|
||||
});
|
||||
)
|
||||
})?;
|
||||
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Create filter with blur only (no offset, no spread - handled geometrically)
|
||||
@ -1750,11 +1887,11 @@ impl RenderState {
|
||||
Some(shadow.offset), // Offset is geometric
|
||||
None,
|
||||
Some(shadow.spread),
|
||||
);
|
||||
});
|
||||
)
|
||||
})?;
|
||||
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Adaptive downscale for large blur values (lossless GPU optimization).
|
||||
@ -1791,12 +1928,13 @@ impl RenderState {
|
||||
Some(shadow.offset), // Offset is geometric
|
||||
None,
|
||||
Some(shadow.spread),
|
||||
);
|
||||
});
|
||||
)
|
||||
})?;
|
||||
|
||||
state.surfaces.canvas(temp_surface).restore();
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
)?;
|
||||
|
||||
if let Some((mut surface, filter_scale)) = filter_result {
|
||||
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||
@ -1831,6 +1969,7 @@ impl RenderState {
|
||||
}
|
||||
drop_canvas.restore();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders element drop shadows to DropShadows surface and composites to Current.
|
||||
@ -1845,7 +1984,7 @@ impl RenderState {
|
||||
scale: f32,
|
||||
translation: (f32, f32),
|
||||
node_render_state: &NodeRenderState,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let element_extrect = extrect.get_or_insert_with(|| element.extrect(tree, scale));
|
||||
let inherited_layer_blur = match element.shape_type {
|
||||
Type::Frame(_) | Type::Group(_) => element.blur,
|
||||
@ -1867,7 +2006,7 @@ impl RenderState {
|
||||
scale,
|
||||
translation,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
|
||||
if !matches!(element.shape_type, Type::Bool(_)) {
|
||||
let shadow_children = if element.is_recursive() {
|
||||
@ -1896,7 +2035,7 @@ impl RenderState {
|
||||
scale,
|
||||
translation,
|
||||
inherited_layer_blur,
|
||||
);
|
||||
)?;
|
||||
} else {
|
||||
let paint = skia::Paint::default();
|
||||
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&paint);
|
||||
@ -1932,8 +2071,8 @@ impl RenderState {
|
||||
None,
|
||||
Some(vec![new_shadow_paint.clone()]),
|
||||
None,
|
||||
);
|
||||
});
|
||||
)
|
||||
})?;
|
||||
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||
}
|
||||
}
|
||||
@ -1990,6 +2129,7 @@ impl RenderState {
|
||||
self.surfaces
|
||||
.canvas(SurfaceId::DropShadows)
|
||||
.clear(skia::Color::TRANSPARENT);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn render_shape_tree_partial_uncached(
|
||||
@ -1997,7 +2137,7 @@ impl RenderState {
|
||||
tree: ShapesPoolRef,
|
||||
timestamp: i32,
|
||||
allow_stop: bool,
|
||||
) -> Result<(bool, bool), String> {
|
||||
) -> Result<(bool, bool)> {
|
||||
let mut iteration = 0;
|
||||
let mut is_empty = true;
|
||||
|
||||
@ -2025,7 +2165,7 @@ impl RenderState {
|
||||
|
||||
if visited_children {
|
||||
if !node_render_state.flattened {
|
||||
self.render_shape_exit(element, visited_mask, clip_bounds);
|
||||
self.render_shape_exit(element, visited_mask, clip_bounds)?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -2051,11 +2191,11 @@ impl RenderState {
|
||||
let is_visible = if is_container || has_effects {
|
||||
let element_extrect =
|
||||
extrect.get_or_insert_with(|| transformed_element.extrect(tree, scale));
|
||||
element_extrect.intersects(self.render_area)
|
||||
element_extrect.intersects(self.render_area_with_margins)
|
||||
&& !transformed_element.visually_insignificant(scale, tree)
|
||||
} else {
|
||||
let selrect = transformed_element.selrect();
|
||||
selrect.intersects(self.render_area)
|
||||
selrect.intersects(self.render_area_with_margins)
|
||||
&& !transformed_element.visually_insignificant(scale, tree)
|
||||
};
|
||||
|
||||
@ -2099,7 +2239,19 @@ impl RenderState {
|
||||
scale,
|
||||
translation,
|
||||
&node_render_state,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
// Render background blur BEFORE save_layer so it modifies
|
||||
// the backdrop independently of the shape's opacity.
|
||||
if !node_render_state.is_root() && self.focus_mode.is_active() {
|
||||
self.render_background_blur(element);
|
||||
}
|
||||
|
||||
// Render background blur BEFORE save_layer so it modifies
|
||||
// the backdrop independently of the shape's opacity.
|
||||
if !node_render_state.is_root() && self.focus_mode.is_active() {
|
||||
self.render_background_blur(element);
|
||||
}
|
||||
|
||||
self.render_shape_enter(element, mask);
|
||||
@ -2129,7 +2281,7 @@ impl RenderState {
|
||||
scale,
|
||||
translation,
|
||||
&node_render_state,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
self.render_shape(
|
||||
@ -2143,7 +2295,7 @@ impl RenderState {
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
|
||||
self.surfaces
|
||||
.canvas(SurfaceId::DropShadows)
|
||||
@ -2240,14 +2392,14 @@ impl RenderState {
|
||||
tree: ShapesPoolRef,
|
||||
timestamp: i32,
|
||||
allow_stop: bool,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
let mut should_stop = false;
|
||||
let root_ids = {
|
||||
if let Some(shape_id) = base_object {
|
||||
vec![*shape_id]
|
||||
} else {
|
||||
let Some(root) = tree.get(&Uuid::nil()) else {
|
||||
return Err(String::from("Root shape not found"));
|
||||
return Err(Error::CriticalError("Root shape not found".to_string()));
|
||||
};
|
||||
root.children_ids(false)
|
||||
}
|
||||
@ -2257,7 +2409,7 @@ impl RenderState {
|
||||
if let Some(current_tile) = self.current_tile {
|
||||
if self.surfaces.has_cached_tile_surface(current_tile) {
|
||||
performance::begin_measure!("render_shape_tree::cached");
|
||||
let tile_rect = self.get_current_tile_bounds();
|
||||
let tile_rect = self.get_current_tile_bounds()?;
|
||||
self.surfaces.draw_cached_tile_surface(
|
||||
current_tile,
|
||||
tile_rect,
|
||||
@ -2287,9 +2439,9 @@ impl RenderState {
|
||||
return Ok(());
|
||||
}
|
||||
performance::end_measure!("render_shape_tree::uncached");
|
||||
let tile_rect = self.get_current_tile_bounds();
|
||||
let tile_rect = self.get_current_tile_bounds()?;
|
||||
if !is_empty {
|
||||
self.apply_render_to_final_canvas(tile_rect);
|
||||
self.apply_render_to_final_canvas(tile_rect)?;
|
||||
|
||||
if self.options.is_debug_visible() {
|
||||
debug::render_workspace_current_tile(
|
||||
@ -2320,10 +2472,22 @@ impl RenderState {
|
||||
|
||||
if !self.surfaces.has_cached_tile_surface(next_tile) {
|
||||
if let Some(ids) = self.tiles.get_shapes_at(next_tile) {
|
||||
// Check if any shape on this tile has a background blur.
|
||||
// If so, we need ALL root shapes rendered (not just those
|
||||
// assigned to this tile) because the blur snapshots Current
|
||||
// which must contain the shapes behind it.
|
||||
let tile_has_bg_blur = ids.iter().any(|id| {
|
||||
tree.get(id).is_some_and(|s| {
|
||||
s.blur.is_some_and(|b| {
|
||||
!b.hidden && b.blur_type == BlurType::BackgroundBlur
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// We only need first level shapes, in the same order as the parent node
|
||||
let mut valid_ids = Vec::with_capacity(ids.len());
|
||||
for root_id in root_ids.iter() {
|
||||
if ids.contains(root_id) {
|
||||
if tile_has_bg_blur || ids.contains(root_id) {
|
||||
valid_ids.push(*root_id);
|
||||
}
|
||||
}
|
||||
@ -2511,24 +2675,20 @@ impl RenderState {
|
||||
self.surfaces.remove_cached_tile_surface(tile);
|
||||
}
|
||||
|
||||
pub fn rebuild_tiles_shallow(&mut self, tree: ShapesPoolRef) {
|
||||
performance::begin_measure!("rebuild_tiles_shallow");
|
||||
|
||||
// Check if zoom changed - if so, we need full cache invalidation
|
||||
// because tiles are rendered at specific zoom levels
|
||||
/// Rebuild the tile index (shape→tile mapping) for all top-level shapes.
|
||||
/// This does NOT invalidate the tile texture cache — cached tile images
|
||||
/// survive so that fast-mode renders during pan still show shadows/blur.
|
||||
pub fn rebuild_tile_index(&mut self, tree: ShapesPoolRef) {
|
||||
let zoom_changed = self.zoom_changed();
|
||||
|
||||
let mut tiles_to_invalidate = HashSet::<tiles::Tile>::new();
|
||||
let mut nodes = vec![Uuid::nil()];
|
||||
while let Some(shape_id) = nodes.pop() {
|
||||
if let Some(shape) = tree.get(&shape_id) {
|
||||
if shape_id != Uuid::nil() {
|
||||
if zoom_changed {
|
||||
// Zoom changed: use full update that tracks all affected tiles
|
||||
tiles_to_invalidate.extend(self.update_shape_tiles(shape, tree));
|
||||
let _ = self.update_shape_tiles(shape, tree);
|
||||
} else {
|
||||
// Pan only: use incremental update that preserves valid cached tiles
|
||||
self.update_shape_tiles_incremental(shape, tree);
|
||||
let _ = self.update_shape_tiles_incremental(shape, tree);
|
||||
}
|
||||
} else {
|
||||
// We only need to rebuild tiles from the first level.
|
||||
@ -2538,9 +2698,17 @@ impl RenderState {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate changed tiles - old content stays visible until new tiles render
|
||||
self.surfaces.remove_cached_tiles(self.background_color);
|
||||
pub fn rebuild_tiles_shallow(&mut self, tree: ShapesPoolRef) {
|
||||
performance::begin_measure!("rebuild_tiles_shallow");
|
||||
|
||||
self.rebuild_tile_index(tree);
|
||||
|
||||
// Invalidate the tile texture cache so all tiles are re-rendered, but
|
||||
// preserve the cache canvas so render_from_cache can still show a scaled
|
||||
// preview of old content while new tiles load progressively.
|
||||
self.surfaces.invalidate_tile_cache();
|
||||
|
||||
performance::end_measure!("rebuild_tiles_shallow");
|
||||
}
|
||||
@ -2615,7 +2783,11 @@ impl RenderState {
|
||||
///
|
||||
/// This is useful when you have a pre-computed set of shape IDs that need to be refreshed,
|
||||
/// regardless of their relationship to other shapes (e.g., ancestors, descendants, or any other collection).
|
||||
pub fn update_tiles_shapes(&mut self, shape_ids: &[Uuid], tree: ShapesPoolMutRef<'_>) {
|
||||
pub fn update_tiles_shapes(
|
||||
&mut self,
|
||||
shape_ids: &[Uuid],
|
||||
tree: ShapesPoolMutRef<'_>,
|
||||
) -> Result<()> {
|
||||
performance::begin_measure!("invalidate_and_update_tiles");
|
||||
let mut all_tiles = HashSet::<tiles::Tile>::new();
|
||||
for shape_id in shape_ids {
|
||||
@ -2627,6 +2799,7 @@ impl RenderState {
|
||||
self.remove_cached_tile(tile);
|
||||
}
|
||||
performance::end_measure!("invalidate_and_update_tiles");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rebuilds tiles for shapes with modifiers and processes their ancestors
|
||||
@ -2635,9 +2808,14 @@ impl RenderState {
|
||||
/// Additionally, it processes all ancestors of modified shapes to ensure their
|
||||
/// extended rectangles are properly recalculated and their tiles are updated.
|
||||
/// This is crucial for frames and groups that contain transformed children.
|
||||
pub fn rebuild_modifier_tiles(&mut self, tree: ShapesPoolMutRef<'_>, ids: Vec<Uuid>) {
|
||||
pub fn rebuild_modifier_tiles(
|
||||
&mut self,
|
||||
tree: ShapesPoolMutRef<'_>,
|
||||
ids: Vec<Uuid>,
|
||||
) -> Result<()> {
|
||||
let ancestors = all_with_ancestors(&ids, tree, false);
|
||||
self.update_tiles_shapes(&ancestors, tree);
|
||||
self.update_tiles_shapes(&ancestors, tree)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_scale(&self) -> f32 {
|
||||
@ -2652,10 +2830,6 @@ impl RenderState {
|
||||
(self.viewbox.zoom - self.cached_viewbox.zoom).abs() > f32::EPSILON
|
||||
}
|
||||
|
||||
pub fn sync_cached_viewbox(&mut self) {
|
||||
self.cached_viewbox = self.viewbox;
|
||||
}
|
||||
|
||||
pub fn mark_touched(&mut self, uuid: Uuid) {
|
||||
self.touched_ids.insert(uuid);
|
||||
}
|
||||
|
||||
@ -41,6 +41,10 @@ pub fn render_debug_cache_surface(render_state: &mut RenderState) {
|
||||
}
|
||||
|
||||
pub fn render_wasm_label(render_state: &mut RenderState) {
|
||||
if !render_state.options.show_wasm_info() {
|
||||
return;
|
||||
}
|
||||
|
||||
let canvas = render_state.surfaces.canvas(SurfaceId::Target);
|
||||
let skia::ISize { width, height } = canvas.base_layer_size();
|
||||
let mut paint = skia::Paint::default();
|
||||
@ -57,7 +61,7 @@ pub fn render_wasm_label(render_state: &mut RenderState) {
|
||||
let debug_font = render_state.fonts.debug_font();
|
||||
canvas.draw_str(str, p, debug_font, &paint);
|
||||
|
||||
if render_state.options.show_info_text() {
|
||||
if render_state.options.is_text_editor_v3() {
|
||||
str = "TEXT EDITOR / V3";
|
||||
|
||||
let (scalar, _) = render_state.fonts.debug_font().measure_str(str, None);
|
||||
@ -179,9 +183,12 @@ pub fn render_debug_shape(
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[allow(dead_code)]
|
||||
pub fn console_debug_surface(render_state: &mut RenderState, id: SurfaceId) {
|
||||
let base64_image = render_state.surfaces.base64_snapshot(id);
|
||||
let base64_image = render_state
|
||||
.surfaces
|
||||
.base64_snapshot(id)
|
||||
.expect("Failed to get base64 image");
|
||||
|
||||
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"))
|
||||
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -194,7 +201,10 @@ pub fn console_debug_surface_rect(render_state: &mut RenderState, id: SurfaceId,
|
||||
rect.bottom as i32,
|
||||
);
|
||||
|
||||
let base64_image = render_state.surfaces.base64_snapshot_rect(id, int_rect);
|
||||
let base64_image = render_state
|
||||
.surfaces
|
||||
.base64_snapshot_rect(id, int_rect)
|
||||
.expect("Failed to get base64 image");
|
||||
|
||||
if let Some(base64_image) = base64_image {
|
||||
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"))
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use skia_safe::{self as skia, Paint, RRect};
|
||||
|
||||
use super::{filters, RenderState, SurfaceId};
|
||||
use crate::error::Result;
|
||||
use crate::render::get_source_rect;
|
||||
use crate::shapes::{merge_fills, Fill, Frame, ImageFill, Rect, Shape, StrokeKind, Type};
|
||||
|
||||
@ -20,12 +21,11 @@ fn draw_image_fill(
|
||||
antialias: bool,
|
||||
surface_id: SurfaceId,
|
||||
) {
|
||||
let image = render_state.images.get(&image_fill.id());
|
||||
if image.is_none() {
|
||||
let Some(image) = render_state.images.get(&image_fill.id()) else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let size = image.unwrap().dimensions();
|
||||
let size = image.dimensions();
|
||||
let canvas = render_state.surfaces.canvas_and_mark_dirty(surface_id);
|
||||
let container = &shape.selrect;
|
||||
let path_transform = shape.to_path_transform();
|
||||
@ -85,15 +85,13 @@ fn draw_image_fill(
|
||||
}
|
||||
|
||||
// Draw the image with the calculated destination rectangle
|
||||
if let Some(image) = image {
|
||||
canvas.draw_image_rect_with_sampling_options(
|
||||
image,
|
||||
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
||||
dest_rect,
|
||||
render_state.sampling_options,
|
||||
paint,
|
||||
);
|
||||
}
|
||||
canvas.draw_image_rect_with_sampling_options(
|
||||
image,
|
||||
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
||||
dest_rect,
|
||||
render_state.sampling_options,
|
||||
paint,
|
||||
);
|
||||
|
||||
// Restore the canvas to remove the clipping
|
||||
canvas.restore();
|
||||
@ -109,9 +107,9 @@ pub fn render(
|
||||
antialias: bool,
|
||||
surface_id: SurfaceId,
|
||||
outset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if fills.is_empty() {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let scale = render_state.get_scale().max(1e-6);
|
||||
@ -134,9 +132,9 @@ pub fn render(
|
||||
surface_id,
|
||||
outset,
|
||||
inset,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut paint = merge_fills(fills, shape.selrect);
|
||||
@ -152,15 +150,17 @@ pub fn render(
|
||||
let mut filtered_paint = paint.clone();
|
||||
filtered_paint.set_image_filter(image_filter.clone());
|
||||
draw_fill_to_surface(state, shape, temp_surface, &filtered_paint, outset, inset);
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
return;
|
||||
)? {
|
||||
return Ok(());
|
||||
} else {
|
||||
paint.set_image_filter(image_filter);
|
||||
}
|
||||
}
|
||||
|
||||
draw_fill_to_surface(render_state, shape, surface_id, &paint, outset, inset);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Draws a single paint (with a merged shader) to the appropriate surface
|
||||
@ -203,7 +203,7 @@ fn render_single_fill(
|
||||
surface_id: SurfaceId,
|
||||
outset: Option<f32>,
|
||||
inset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let mut paint = fill.to_paint(&shape.selrect, antialias);
|
||||
if let Some(image_filter) = shape.image_filter(1.) {
|
||||
let bounds = image_filter.compute_fast_bounds(shape.selrect);
|
||||
@ -224,9 +224,10 @@ fn render_single_fill(
|
||||
outset,
|
||||
inset,
|
||||
);
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
return;
|
||||
)? {
|
||||
return Ok(());
|
||||
} else {
|
||||
paint.set_image_filter(image_filter);
|
||||
}
|
||||
@ -242,6 +243,7 @@ fn render_single_fill(
|
||||
outset,
|
||||
inset,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use skia_safe::{self as skia, ImageFilter, Rect};
|
||||
|
||||
use super::{RenderState, SurfaceId};
|
||||
use crate::error::Result;
|
||||
|
||||
/// Composes two image filters, returning a combined filter if both are present,
|
||||
/// or the individual filter if only one is present, or None if neither is present.
|
||||
@ -36,12 +37,12 @@ pub fn render_with_filter_surface<F>(
|
||||
bounds: Rect,
|
||||
target_surface: SurfaceId,
|
||||
draw_fn: F,
|
||||
) -> bool
|
||||
) -> Result<bool>
|
||||
where
|
||||
F: FnOnce(&mut RenderState, SurfaceId),
|
||||
F: FnOnce(&mut RenderState, SurfaceId) -> Result<()>,
|
||||
{
|
||||
if let Some((mut surface, scale)) =
|
||||
render_into_filter_surface(render_state, bounds, 1.0, draw_fn)
|
||||
render_into_filter_surface(render_state, bounds, 1.0, draw_fn)?
|
||||
{
|
||||
let canvas = render_state.surfaces.canvas_and_mark_dirty(target_surface);
|
||||
|
||||
@ -58,9 +59,9 @@ where
|
||||
surface.draw(canvas, (0.0, 0.0), render_state.sampling_options, None);
|
||||
canvas.restore();
|
||||
}
|
||||
true
|
||||
Ok(true)
|
||||
} else {
|
||||
false
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,12 +82,12 @@ pub fn render_into_filter_surface<F>(
|
||||
bounds: Rect,
|
||||
extra_downscale: f32,
|
||||
draw_fn: F,
|
||||
) -> Option<(skia::Surface, f32)>
|
||||
) -> Result<Option<(skia::Surface, f32)>>
|
||||
where
|
||||
F: FnOnce(&mut RenderState, SurfaceId),
|
||||
F: FnOnce(&mut RenderState, SurfaceId) -> Result<()>,
|
||||
{
|
||||
if !bounds.is_finite() || bounds.width() <= 0.0 || bounds.height() <= 0.0 {
|
||||
return None;
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let filter_id = SurfaceId::Filter;
|
||||
@ -125,10 +126,10 @@ where
|
||||
canvas.translate((-bounds.left, -bounds.top));
|
||||
}
|
||||
|
||||
draw_fn(render_state, filter_id);
|
||||
draw_fn(render_state, filter_id)?;
|
||||
|
||||
render_state.surfaces.canvas(filter_id).restore();
|
||||
|
||||
let filter_surface = render_state.surfaces.surface_clone(filter_id);
|
||||
Some((filter_surface, scale))
|
||||
Ok(Some((filter_surface, scale)))
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use skia_safe::{self as skia, textlayout, Font, FontMgr};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::shapes::{FontFamily, FontStyle};
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
@ -26,7 +27,7 @@ pub struct FontStore {
|
||||
}
|
||||
|
||||
impl FontStore {
|
||||
pub fn new() -> Self {
|
||||
pub fn try_new() -> Result<Self> {
|
||||
let font_mgr = FontMgr::new();
|
||||
let font_provider = load_default_provider(&font_mgr);
|
||||
let mut font_collection = skia::textlayout::FontCollection::new();
|
||||
@ -34,17 +35,19 @@ impl FontStore {
|
||||
|
||||
let debug_typeface = font_provider
|
||||
.match_family_style(default_font().as_str(), skia::FontStyle::default())
|
||||
.unwrap();
|
||||
.ok_or(Error::CriticalError(
|
||||
"Failed to match default font".to_string(),
|
||||
))?;
|
||||
|
||||
let debug_font = skia::Font::new(debug_typeface, 10.0);
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
font_mgr,
|
||||
font_provider,
|
||||
font_collection,
|
||||
debug_font,
|
||||
fallback_fonts: HashSet::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_scale_debug_font(&mut self, dpr: f32) {
|
||||
@ -70,7 +73,7 @@ impl FontStore {
|
||||
font_data: &[u8],
|
||||
is_emoji: bool,
|
||||
is_fallback: bool,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
if self.has_family(&family, is_emoji) {
|
||||
return Ok(());
|
||||
}
|
||||
@ -78,7 +81,9 @@ impl FontStore {
|
||||
let typeface = self
|
||||
.font_mgr
|
||||
.new_from_data(font_data, None)
|
||||
.ok_or("Failed to create typeface")?;
|
||||
.ok_or(Error::CriticalError(
|
||||
"Failed to create typeface".to_string(),
|
||||
))?;
|
||||
|
||||
let alias = format!("{}", family);
|
||||
let font_name = if is_emoji {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::error::{Error, Result};
|
||||
use skia_safe::gpu::{self, gl::FramebufferInfo, gl::TextureInfo, DirectContext};
|
||||
use skia_safe::{self as skia, ISize};
|
||||
|
||||
@ -8,24 +9,30 @@ pub struct GpuState {
|
||||
}
|
||||
|
||||
impl GpuState {
|
||||
pub fn new() -> Self {
|
||||
let interface = gpu::gl::Interface::new_native().unwrap();
|
||||
let context = gpu::direct_contexts::make_gl(interface, None).unwrap();
|
||||
pub fn try_new() -> Result<Self> {
|
||||
let interface = gpu::gl::Interface::new_native().ok_or(Error::CriticalError(
|
||||
"Failed to create GL interface".to_string(),
|
||||
))?;
|
||||
let context = gpu::direct_contexts::make_gl(interface, None).ok_or(
|
||||
Error::CriticalError("Failed to create GL context".to_string()),
|
||||
)?;
|
||||
let framebuffer_info = {
|
||||
let mut fboid: gl::types::GLint = 0;
|
||||
unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) };
|
||||
|
||||
FramebufferInfo {
|
||||
fboid: fboid.try_into().unwrap(),
|
||||
fboid: fboid.try_into().map_err(|_| {
|
||||
Error::CriticalError("Failed to convert GL framebuffer ID to u32".to_string())
|
||||
})?,
|
||||
format: gpu::gl::Format::RGBA8.into(),
|
||||
protected: gpu::Protected::No,
|
||||
}
|
||||
};
|
||||
|
||||
GpuState {
|
||||
Ok(GpuState {
|
||||
context,
|
||||
framebuffer_info,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_webgl_texture(&mut self, width: i32, height: i32) -> gl::types::GLuint {
|
||||
@ -56,7 +63,11 @@ impl GpuState {
|
||||
texture_id
|
||||
}
|
||||
|
||||
pub fn create_surface_with_isize(&mut self, label: String, size: ISize) -> skia::Surface {
|
||||
pub fn create_surface_with_isize(
|
||||
&mut self,
|
||||
label: String,
|
||||
size: ISize,
|
||||
) -> Result<skia::Surface> {
|
||||
self.create_surface_with_dimensions(label, size.width, size.height)
|
||||
}
|
||||
|
||||
@ -65,7 +76,7 @@ impl GpuState {
|
||||
label: String,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> skia::Surface {
|
||||
) -> Result<skia::Surface> {
|
||||
let backend_texture = unsafe {
|
||||
let texture_id = self.create_webgl_texture(width, height);
|
||||
let texture_info = TextureInfo {
|
||||
@ -77,7 +88,7 @@ impl GpuState {
|
||||
gpu::backend_textures::make_gl((width, height), gpu::Mipmapped::No, texture_info, label)
|
||||
};
|
||||
|
||||
gpu::surfaces::wrap_backend_texture(
|
||||
let surface = gpu::surfaces::wrap_backend_texture(
|
||||
&mut self.context,
|
||||
&backend_texture,
|
||||
gpu::SurfaceOrigin::BottomLeft,
|
||||
@ -86,15 +97,19 @@ impl GpuState {
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.ok_or(Error::CriticalError(
|
||||
"Failed to create Skia surface".to_string(),
|
||||
))?;
|
||||
|
||||
Ok(surface)
|
||||
}
|
||||
|
||||
/// Create a Skia surface that will be used for rendering.
|
||||
pub fn create_target_surface(&mut self, width: i32, height: i32) -> skia::Surface {
|
||||
pub fn create_target_surface(&mut self, width: i32, height: i32) -> Result<skia::Surface> {
|
||||
let backend_render_target =
|
||||
gpu::backend_render_targets::make_gl((width, height), 1, 8, self.framebuffer_info);
|
||||
|
||||
gpu::surfaces::wrap_backend_render_target(
|
||||
let surface = gpu::surfaces::wrap_backend_render_target(
|
||||
&mut self.context,
|
||||
&backend_render_target,
|
||||
gpu::SurfaceOrigin::BottomLeft,
|
||||
@ -102,6 +117,10 @@ impl GpuState {
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.ok_or(Error::CriticalError(
|
||||
"Failed to create Skia surface".to_string(),
|
||||
))?;
|
||||
|
||||
Ok(surface)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ use crate::math::Rect as MathRect;
|
||||
use crate::shapes::ImageFill;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use skia_safe::gpu::{surfaces, Budgeted, DirectContext};
|
||||
use skia_safe::{self as skia, Codec, ISize};
|
||||
use std::collections::HashMap;
|
||||
@ -70,7 +71,7 @@ fn create_image_from_gl_texture(
|
||||
texture_id: u32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<Image, String> {
|
||||
) -> Result<Image> {
|
||||
use skia_safe::gpu;
|
||||
use skia_safe::gpu::gl::TextureInfo;
|
||||
|
||||
@ -99,7 +100,9 @@ fn create_image_from_gl_texture(
|
||||
skia::AlphaType::Premul,
|
||||
None,
|
||||
)
|
||||
.ok_or("Failed to create Skia image from GL texture")?;
|
||||
.ok_or(crate::error::Error::CriticalError(
|
||||
"Failed to create Skia image from GL texture".to_string(),
|
||||
))?;
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
@ -147,11 +150,16 @@ impl ImageStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, id: Uuid, is_thumbnail: bool, image_data: &[u8]) -> Result<(), String> {
|
||||
pub fn add(
|
||||
&mut self,
|
||||
id: Uuid,
|
||||
is_thumbnail: bool,
|
||||
image_data: &[u8],
|
||||
) -> crate::error::Result<()> {
|
||||
let key = (id, is_thumbnail);
|
||||
|
||||
if self.images.contains_key(&key) {
|
||||
return Err("Image already exists".to_string());
|
||||
return Err(Error::RecoverableError("Image already exists".to_string()));
|
||||
}
|
||||
|
||||
let raw_data = image_data.to_vec();
|
||||
@ -174,11 +182,11 @@ impl ImageStore {
|
||||
texture_id: u32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
let key = (id, is_thumbnail);
|
||||
|
||||
if self.images.contains_key(&key) {
|
||||
return Err("Image already exists".to_string());
|
||||
return Err(Error::RecoverableError("Image already exists".to_string()));
|
||||
}
|
||||
|
||||
// Create a Skia image from the existing GL texture
|
||||
|
||||
@ -1,38 +1,43 @@
|
||||
use crate::options;
|
||||
// Render options flags
|
||||
const DEBUG_VISIBLE: u32 = 0x01;
|
||||
const PROFILE_REBUILD_TILES: u32 = 0x02;
|
||||
const TEXT_EDITOR_V3: u32 = 0x04;
|
||||
const SHOW_WASM_INFO: u32 = 0x08;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Default)]
|
||||
pub struct RenderOptions {
|
||||
pub flags: u32,
|
||||
pub dpr: Option<f32>,
|
||||
fast_mode: bool,
|
||||
}
|
||||
|
||||
impl RenderOptions {
|
||||
pub fn is_debug_visible(&self) -> bool {
|
||||
self.flags & options::DEBUG_VISIBLE == options::DEBUG_VISIBLE
|
||||
self.flags & DEBUG_VISIBLE == DEBUG_VISIBLE
|
||||
}
|
||||
|
||||
pub fn is_profile_rebuild_tiles(&self) -> bool {
|
||||
self.flags & options::PROFILE_REBUILD_TILES == options::PROFILE_REBUILD_TILES
|
||||
self.flags & PROFILE_REBUILD_TILES == PROFILE_REBUILD_TILES
|
||||
}
|
||||
|
||||
/// Use fast mode to enable / disable expensive operations
|
||||
pub fn is_fast_mode(&self) -> bool {
|
||||
self.flags & options::FAST_MODE == options::FAST_MODE
|
||||
self.fast_mode
|
||||
}
|
||||
|
||||
pub fn set_fast_mode(&mut self, enabled: bool) {
|
||||
if enabled {
|
||||
self.flags |= options::FAST_MODE;
|
||||
} else {
|
||||
self.flags &= !options::FAST_MODE;
|
||||
}
|
||||
self.fast_mode = enabled;
|
||||
}
|
||||
|
||||
pub fn dpr(&self) -> f32 {
|
||||
self.dpr.unwrap_or(1.0)
|
||||
}
|
||||
|
||||
pub fn show_info_text(&self) -> bool {
|
||||
self.flags & options::INFO_TEXT == options::INFO_TEXT
|
||||
pub fn is_text_editor_v3(&self) -> bool {
|
||||
self.flags & TEXT_EDITOR_V3 == TEXT_EDITOR_V3
|
||||
}
|
||||
|
||||
pub fn show_wasm_info(&self) -> bool {
|
||||
self.flags & SHOW_WASM_INFO == SHOW_WASM_INFO
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::render::strokes;
|
||||
use crate::shapes::{ParagraphBuilderGroup, Shadow, Shape, Stroke, Type};
|
||||
use skia_safe::{canvas::SaveLayerRec, Paint, Path};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::render::text;
|
||||
|
||||
// Fill Shadows
|
||||
@ -36,7 +37,7 @@ pub fn render_stroke_inner_shadows(
|
||||
stroke: &Stroke,
|
||||
antialias: bool,
|
||||
surface_id: SurfaceId,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if !shape.has_fills() {
|
||||
for shadow in shape.inner_shadows_visible() {
|
||||
let filter = shadow.get_inner_shadow_filter();
|
||||
@ -48,9 +49,10 @@ pub fn render_stroke_inner_shadows(
|
||||
filter.as_ref(),
|
||||
antialias,
|
||||
None, // Inner shadows don't use spread
|
||||
)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Render text paths (unused)
|
||||
@ -133,9 +135,9 @@ pub fn render_text_shadows(
|
||||
surface_id: Option<SurfaceId>,
|
||||
shadows: &[Paint],
|
||||
blur_filter: &Option<skia_safe::ImageFilter>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if stroke_paragraphs_group.is_empty() {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let canvas = render_state
|
||||
@ -156,7 +158,7 @@ pub fn render_text_shadows(
|
||||
blur_filter.as_ref(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
|
||||
for stroke_paragraphs in stroke_paragraphs_group.iter_mut() {
|
||||
text::render(
|
||||
@ -169,9 +171,10 @@ pub fn render_text_shadows(
|
||||
blur_filter.as_ref(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ use crate::shapes::{
|
||||
use skia_safe::{self as skia, ImageFilter, RRect};
|
||||
|
||||
use super::{filters, RenderState, SurfaceId};
|
||||
use crate::error::{Error, Result};
|
||||
use crate::render::filters::compose_filters;
|
||||
use crate::render::{get_dest_rect, get_source_rect};
|
||||
|
||||
@ -294,16 +295,16 @@ fn handle_stroke_caps(
|
||||
blur: Option<&ImageFilter>,
|
||||
_antialias: bool,
|
||||
) {
|
||||
let mut points = path.points().to_vec();
|
||||
// Curves can have duplicated points, so let's remove consecutive duplicated points
|
||||
points.dedup();
|
||||
let c_points = points.len();
|
||||
|
||||
// Closed shapes don't have caps
|
||||
if c_points >= 2 && is_open {
|
||||
let first_point = points.first().unwrap();
|
||||
let last_point = points.last().unwrap();
|
||||
if !is_open {
|
||||
return;
|
||||
}
|
||||
|
||||
// Curves can have duplicated points, so let's remove consecutive duplicated points
|
||||
let mut points = path.points().to_vec();
|
||||
points.dedup();
|
||||
|
||||
if let [first_point, .., last_point] = points.as_slice() {
|
||||
let mut paint_stroke = paint.clone();
|
||||
|
||||
if let Some(filter) = blur {
|
||||
@ -328,7 +329,7 @@ fn handle_stroke_caps(
|
||||
stroke.width,
|
||||
&mut paint_stroke,
|
||||
last_point,
|
||||
&points[c_points - 2],
|
||||
&points[points.len() - 2],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -456,14 +457,13 @@ fn draw_image_stroke_in_container(
|
||||
image_fill: &ImageFill,
|
||||
antialias: bool,
|
||||
surface_id: SurfaceId,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let scale = render_state.get_scale();
|
||||
let image = render_state.images.get(&image_fill.id());
|
||||
if image.is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(image) = render_state.images.get(&image_fill.id()) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let size = image.unwrap().dimensions();
|
||||
let size = image.dimensions();
|
||||
let canvas = render_state.surfaces.canvas_and_mark_dirty(surface_id);
|
||||
let container = &shape.selrect;
|
||||
let path_transform = shape.to_path_transform();
|
||||
@ -509,7 +509,10 @@ fn draw_image_stroke_in_container(
|
||||
shape_type @ (Type::Path(_) | Type::Bool(_)) => {
|
||||
if let Some(p) = shape_type.path() {
|
||||
canvas.save();
|
||||
let path = p.to_skia_path().make_transform(&path_transform.unwrap());
|
||||
|
||||
let path = p.to_skia_path().make_transform(
|
||||
&path_transform.ok_or(Error::CriticalError("No path transform".to_string()))?,
|
||||
);
|
||||
let stroke_kind = stroke.render_kind(p.is_open());
|
||||
match stroke_kind {
|
||||
StrokeKind::Inner => {
|
||||
@ -561,7 +564,7 @@ fn draw_image_stroke_in_container(
|
||||
|
||||
canvas.clip_rect(dest_rect, skia::ClipOp::Intersect, antialias);
|
||||
canvas.draw_image_rect_with_sampling_options(
|
||||
image.unwrap(),
|
||||
image,
|
||||
Some((&src_rect, skia::canvas::SrcRectConstraint::Strict)),
|
||||
dest_rect,
|
||||
render_state.sampling_options,
|
||||
@ -571,7 +574,9 @@ fn draw_image_stroke_in_container(
|
||||
// Clear outer stroke for paths if necessary. When adding an outer stroke we need to empty the stroke added too in the inner area.
|
||||
if let Type::Path(p) = &shape.shape_type {
|
||||
if stroke.render_kind(p.is_open()) == StrokeKind::Outer {
|
||||
let path = p.to_skia_path().make_transform(&path_transform.unwrap());
|
||||
let path = p.to_skia_path().make_transform(
|
||||
&path_transform.ok_or(Error::CriticalError("No path transform".to_string()))?,
|
||||
);
|
||||
let mut clear_paint = skia::Paint::default();
|
||||
clear_paint.set_blend_mode(skia::BlendMode::Clear);
|
||||
clear_paint.set_anti_alias(antialias);
|
||||
@ -581,6 +586,7 @@ fn draw_image_stroke_in_container(
|
||||
|
||||
// Restore canvas state
|
||||
canvas.restore();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders all strokes for a shape. Merges strokes that share the same
|
||||
@ -593,9 +599,9 @@ pub fn render(
|
||||
surface_id: Option<SurfaceId>,
|
||||
antialias: bool,
|
||||
outset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if strokes.is_empty() {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let has_image_fills = strokes.iter().any(|s| matches!(s.fill, Fill::Image(_)));
|
||||
@ -655,13 +661,14 @@ pub fn render(
|
||||
true,
|
||||
true,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
|
||||
state.surfaces.canvas(temp_surface).restore();
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
return;
|
||||
)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,9 +682,9 @@ pub fn render(
|
||||
None,
|
||||
antialias,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
render_merged(
|
||||
@ -688,7 +695,7 @@ pub fn render(
|
||||
antialias,
|
||||
false,
|
||||
outset,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
fn strokes_share_geometry(strokes: &[&Stroke]) -> bool {
|
||||
@ -709,7 +716,7 @@ fn render_merged(
|
||||
antialias: bool,
|
||||
bypass_filter: bool,
|
||||
outset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let representative = *strokes
|
||||
.last()
|
||||
.expect("render_merged expects at least one stroke");
|
||||
@ -761,14 +768,15 @@ fn render_merged(
|
||||
antialias,
|
||||
true,
|
||||
outset,
|
||||
);
|
||||
)?;
|
||||
|
||||
state.surfaces.apply_mut(temp_surface as u32, |surface| {
|
||||
surface.canvas().restore();
|
||||
});
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
return;
|
||||
)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -844,6 +852,7 @@ fn render_merged(
|
||||
}
|
||||
_ => unreachable!("This shape should not have strokes"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Renders a single stroke. Used by the shadow module which needs per-stroke
|
||||
@ -857,7 +866,7 @@ pub fn render_single(
|
||||
shadow: Option<&ImageFilter>,
|
||||
antialias: bool,
|
||||
outset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
render_single_internal(
|
||||
render_state,
|
||||
shape,
|
||||
@ -868,7 +877,7 @@ pub fn render_single(
|
||||
false,
|
||||
false,
|
||||
outset,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -882,7 +891,7 @@ fn render_single_internal(
|
||||
bypass_filter: bool,
|
||||
skip_blur: bool,
|
||||
outset: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if !bypass_filter {
|
||||
if let Some(image_filter) = shape.image_filter(1.) {
|
||||
let mut content_bounds = shape.selrect;
|
||||
@ -916,10 +925,10 @@ fn render_single_internal(
|
||||
true,
|
||||
true,
|
||||
outset,
|
||||
);
|
||||
)
|
||||
},
|
||||
) {
|
||||
return;
|
||||
)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -949,7 +958,7 @@ fn render_single_internal(
|
||||
image_fill,
|
||||
antialias,
|
||||
target_surface,
|
||||
);
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
match &shape.shape_type {
|
||||
@ -1014,6 +1023,7 @@ fn render_single_internal(
|
||||
_ => unreachable!("This shape should not have strokes"),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Render text paths (unused)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::performance;
|
||||
use crate::shapes::Shape;
|
||||
|
||||
@ -61,38 +62,39 @@ pub struct Surfaces {
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Surfaces {
|
||||
pub fn new(
|
||||
pub fn try_new(
|
||||
gpu_state: &mut GpuState,
|
||||
(width, height): (i32, i32),
|
||||
sampling_options: skia::SamplingOptions,
|
||||
tile_dims: skia::ISize,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
let extra_tile_dims = skia::ISize::new(
|
||||
tile_dims.width * TILE_SIZE_MULTIPLIER,
|
||||
tile_dims.height * TILE_SIZE_MULTIPLIER,
|
||||
);
|
||||
let margins = skia::ISize::new(extra_tile_dims.width / 4, extra_tile_dims.height / 4);
|
||||
|
||||
let target = gpu_state.create_target_surface(width, height);
|
||||
let filter = gpu_state.create_surface_with_isize("filter".to_string(), extra_tile_dims);
|
||||
let cache = gpu_state.create_surface_with_dimensions("cache".to_string(), width, height);
|
||||
let current = gpu_state.create_surface_with_isize("current".to_string(), extra_tile_dims);
|
||||
let target = gpu_state.create_target_surface(width, height)?;
|
||||
let filter = gpu_state.create_surface_with_isize("filter".to_string(), extra_tile_dims)?;
|
||||
let cache = gpu_state.create_surface_with_dimensions("cache".to_string(), width, height)?;
|
||||
let current =
|
||||
gpu_state.create_surface_with_isize("current".to_string(), extra_tile_dims)?;
|
||||
let drop_shadows =
|
||||
gpu_state.create_surface_with_isize("drop_shadows".to_string(), extra_tile_dims);
|
||||
gpu_state.create_surface_with_isize("drop_shadows".to_string(), extra_tile_dims)?;
|
||||
let inner_shadows =
|
||||
gpu_state.create_surface_with_isize("inner_shadows".to_string(), extra_tile_dims);
|
||||
let text_drop_shadows =
|
||||
gpu_state.create_surface_with_isize("text_drop_shadows".to_string(), extra_tile_dims);
|
||||
gpu_state.create_surface_with_isize("inner_shadows".to_string(), extra_tile_dims)?;
|
||||
let text_drop_shadows = gpu_state
|
||||
.create_surface_with_isize("text_drop_shadows".to_string(), extra_tile_dims)?;
|
||||
let shape_fills =
|
||||
gpu_state.create_surface_with_isize("shape_fills".to_string(), extra_tile_dims);
|
||||
gpu_state.create_surface_with_isize("shape_fills".to_string(), extra_tile_dims)?;
|
||||
let shape_strokes =
|
||||
gpu_state.create_surface_with_isize("shape_strokes".to_string(), extra_tile_dims);
|
||||
gpu_state.create_surface_with_isize("shape_strokes".to_string(), extra_tile_dims)?;
|
||||
|
||||
let ui = gpu_state.create_surface_with_dimensions("ui".to_string(), width, height);
|
||||
let debug = gpu_state.create_surface_with_dimensions("debug".to_string(), width, height);
|
||||
let ui = gpu_state.create_surface_with_dimensions("ui".to_string(), width, height)?;
|
||||
let debug = gpu_state.create_surface_with_dimensions("debug".to_string(), width, height)?;
|
||||
|
||||
let tiles = TileTextureCache::new();
|
||||
Surfaces {
|
||||
Ok(Surfaces {
|
||||
target,
|
||||
filter,
|
||||
cache,
|
||||
@ -108,15 +110,25 @@ impl Surfaces {
|
||||
sampling_options,
|
||||
margins,
|
||||
dirty_surfaces: 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clear_tiles(&mut self) {
|
||||
self.tiles.clear();
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, gpu_state: &mut GpuState, new_width: i32, new_height: i32) {
|
||||
self.reset_from_target(gpu_state.create_target_surface(new_width, new_height));
|
||||
pub fn margins(&self) -> skia::ISize {
|
||||
self.margins
|
||||
}
|
||||
|
||||
pub fn resize(
|
||||
&mut self,
|
||||
gpu_state: &mut GpuState,
|
||||
new_width: i32,
|
||||
new_height: i32,
|
||||
) -> Result<()> {
|
||||
self.reset_from_target(gpu_state.create_target_surface(new_width, new_height)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn snapshot(&mut self, id: SurfaceId) -> skia::Image {
|
||||
@ -128,26 +140,33 @@ impl Surfaces {
|
||||
(self.filter.width(), self.filter.height())
|
||||
}
|
||||
|
||||
pub fn base64_snapshot(&mut self, id: SurfaceId) -> String {
|
||||
pub fn base64_snapshot(&mut self, id: SurfaceId) -> Result<String> {
|
||||
let surface = self.get_mut(id);
|
||||
let image = surface.image_snapshot();
|
||||
let mut context = surface.direct_context();
|
||||
let encoded_image = image
|
||||
.encode(context.as_mut(), skia::EncodedImageFormat::PNG, None)
|
||||
.unwrap();
|
||||
general_purpose::STANDARD.encode(encoded_image.as_bytes())
|
||||
.ok_or(Error::CriticalError("Failed to encode image".to_string()))?;
|
||||
Ok(general_purpose::STANDARD.encode(encoded_image.as_bytes()))
|
||||
}
|
||||
|
||||
pub fn base64_snapshot_rect(&mut self, id: SurfaceId, irect: skia::IRect) -> Option<String> {
|
||||
pub fn base64_snapshot_rect(
|
||||
&mut self,
|
||||
id: SurfaceId,
|
||||
irect: skia::IRect,
|
||||
) -> Result<Option<String>> {
|
||||
let surface = self.get_mut(id);
|
||||
if let Some(image) = surface.image_snapshot_with_bounds(irect) {
|
||||
let mut context = surface.direct_context();
|
||||
let encoded_image = image
|
||||
.encode(context.as_mut(), skia::EncodedImageFormat::PNG, None)
|
||||
.unwrap();
|
||||
return Some(general_purpose::STANDARD.encode(encoded_image.as_bytes()));
|
||||
.ok_or(Error::CriticalError("Failed to encode image".to_string()))?;
|
||||
Ok(Some(
|
||||
general_purpose::STANDARD.encode(encoded_image.as_bytes()),
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the canvas and automatically marks
|
||||
@ -337,22 +356,41 @@ impl Surfaces {
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_from_target(&mut self, target: skia::Surface) {
|
||||
fn reset_from_target(&mut self, target: skia::Surface) -> Result<()> {
|
||||
let dim = (target.width(), target.height());
|
||||
self.target = target;
|
||||
self.filter = self.target.new_surface_with_dimensions(dim).unwrap();
|
||||
self.debug = self.target.new_surface_with_dimensions(dim).unwrap();
|
||||
self.ui = self.target.new_surface_with_dimensions(dim).unwrap();
|
||||
self.filter = self
|
||||
.target
|
||||
.new_surface_with_dimensions(dim)
|
||||
.ok_or(Error::CriticalError("Failed to create surface".to_string()))?;
|
||||
self.debug = self
|
||||
.target
|
||||
.new_surface_with_dimensions(dim)
|
||||
.ok_or(Error::CriticalError("Failed to create surface".to_string()))?;
|
||||
self.ui = self
|
||||
.target
|
||||
.new_surface_with_dimensions(dim)
|
||||
.ok_or(Error::CriticalError("Failed to create surface".to_string()))?;
|
||||
// The rest are tile size surfaces
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn resize_cache(&mut self, cache_dims: skia::ISize, interest_area_threshold: i32) {
|
||||
self.cache = self.target.new_surface_with_dimensions(cache_dims).unwrap();
|
||||
pub fn resize_cache(
|
||||
&mut self,
|
||||
cache_dims: skia::ISize,
|
||||
interest_area_threshold: i32,
|
||||
) -> Result<()> {
|
||||
self.cache = self
|
||||
.target
|
||||
.new_surface_with_dimensions(cache_dims)
|
||||
.ok_or(Error::CriticalError("Failed to create surface".to_string()))?;
|
||||
self.cache.canvas().reset_matrix();
|
||||
self.cache.canvas().translate((
|
||||
(interest_area_threshold as f32 * TILE_SIZE),
|
||||
(interest_area_threshold as f32 * TILE_SIZE),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn draw_rect_to(
|
||||
@ -570,11 +608,22 @@ impl Surfaces {
|
||||
);
|
||||
}
|
||||
|
||||
/// Full cache reset: clears both the tile texture cache and the cache canvas.
|
||||
/// Used by `rebuild_tiles` (full rebuild). For shallow rebuilds that preserve
|
||||
/// the cache canvas for scaled previews, use `invalidate_tile_cache` instead.
|
||||
pub fn remove_cached_tiles(&mut self, color: skia::Color) {
|
||||
self.tiles.clear();
|
||||
self.cache.canvas().clear(color);
|
||||
}
|
||||
|
||||
/// Invalidate the tile texture cache without clearing the cache canvas.
|
||||
/// This forces all tiles to be re-rendered, but preserves the cache canvas
|
||||
/// so that `render_from_cache` can still show a scaled preview of the old
|
||||
/// content while new tiles are being rendered.
|
||||
pub fn invalidate_tile_cache(&mut self) {
|
||||
self.tiles.clear();
|
||||
}
|
||||
|
||||
pub fn gc(&mut self) {
|
||||
self.tiles.gc();
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use super::{filters, RenderState, Shape, SurfaceId};
|
||||
use crate::{
|
||||
error::Result,
|
||||
math::Rect,
|
||||
shapes::{
|
||||
calculate_position_data, calculate_text_layout_data, merge_fills, set_paint_fill,
|
||||
@ -66,7 +67,7 @@ pub fn stroke_paragraph_builder_group_from_text(
|
||||
}
|
||||
|
||||
let stroke_paragraphs: Vec<ParagraphBuilder> = (0..stroke_paragraphs_map.len())
|
||||
.map(|i| stroke_paragraphs_map.remove(&i).unwrap())
|
||||
.filter_map(|i| stroke_paragraphs_map.remove(&i))
|
||||
.collect();
|
||||
|
||||
paragraph_group.push(stroke_paragraphs);
|
||||
@ -195,7 +196,7 @@ pub fn render_with_bounds_outset(
|
||||
stroke_bounds_outset: f32,
|
||||
fill_inset: Option<f32>,
|
||||
layer_opacity: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
if let Some(render_state) = render_state {
|
||||
let target_surface = surface_id.unwrap_or(SurfaceId::Fills);
|
||||
|
||||
@ -225,9 +226,10 @@ pub fn render_with_bounds_outset(
|
||||
fill_inset,
|
||||
layer_opacity,
|
||||
);
|
||||
Ok(())
|
||||
},
|
||||
) {
|
||||
return;
|
||||
)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,7 +244,7 @@ pub fn render_with_bounds_outset(
|
||||
fill_inset,
|
||||
layer_opacity,
|
||||
);
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(canvas) = canvas {
|
||||
@ -256,6 +258,7 @@ pub fn render_with_bounds_outset(
|
||||
layer_opacity,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -269,7 +272,7 @@ pub fn render(
|
||||
blur: Option<&ImageFilter>,
|
||||
fill_inset: Option<f32>,
|
||||
layer_opacity: Option<f32>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
render_with_bounds_outset(
|
||||
render_state,
|
||||
canvas,
|
||||
@ -281,7 +284,7 @@ pub fn render(
|
||||
0.0,
|
||||
fill_inset,
|
||||
layer_opacity,
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
fn render_text_on_canvas(
|
||||
|
||||
@ -1241,6 +1241,7 @@ impl Shape {
|
||||
let sigma = radius_to_sigma(blur.value * scale);
|
||||
skia::image_filters::blur((sigma, sigma), None, None, None)
|
||||
}
|
||||
BlurType::BackgroundBlur => None,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1253,6 +1254,7 @@ impl Shape {
|
||||
let sigma = radius_to_sigma(blur.value * scale);
|
||||
skia::MaskFilter::blur(skia::BlurStyle::Normal, sigma, Some(true))
|
||||
}
|
||||
BlurType::BackgroundBlur => None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ pub fn radius_to_sigma(radius: f32) -> f32 {
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum BlurType {
|
||||
LayerBlur,
|
||||
BackgroundBlur,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
||||
@ -9,6 +9,7 @@ pub mod grid_layout;
|
||||
use crate::math::{self as math, bools, identitish, is_close_to, Bounds, Matrix, Point};
|
||||
use common::GetBounds;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::shapes::{
|
||||
ConstraintH, ConstraintV, Frame, Group, GrowType, Layout, Modifier, Shape, TransformEntry,
|
||||
TransformEntrySource, Type,
|
||||
@ -24,9 +25,9 @@ fn propagate_children(
|
||||
parent_bounds_after: &Bounds,
|
||||
transform: Matrix,
|
||||
bounds: &HashMap<Uuid, Bounds>,
|
||||
) -> VecDeque<Modifier> {
|
||||
) -> Result<VecDeque<Modifier>> {
|
||||
if identitish(&transform) {
|
||||
return VecDeque::new();
|
||||
return Ok(VecDeque::new());
|
||||
}
|
||||
|
||||
let mut result = VecDeque::new();
|
||||
@ -74,12 +75,12 @@ fn propagate_children(
|
||||
constraint_v,
|
||||
transform,
|
||||
child.ignore_constraints,
|
||||
);
|
||||
)?;
|
||||
|
||||
result.push_back(Modifier::transform_propagate(*child_id, transform));
|
||||
}
|
||||
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn calculate_group_bounds(
|
||||
@ -172,9 +173,9 @@ fn propagate_transform(
|
||||
entries: &mut VecDeque<Modifier>,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
modifiers: &mut HashMap<Uuid, Matrix>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let Some(shape) = state.shapes.get(&entry.id) else {
|
||||
return;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let shapes = &state.shapes;
|
||||
@ -249,7 +250,7 @@ fn propagate_transform(
|
||||
&shape_bounds_after,
|
||||
transform,
|
||||
bounds,
|
||||
);
|
||||
)?;
|
||||
entries.append(&mut children);
|
||||
}
|
||||
|
||||
@ -275,6 +276,7 @@ fn propagate_transform(
|
||||
entries.push_back(Modifier::reflow(parent.id, false));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn propagate_reflow(
|
||||
@ -338,34 +340,35 @@ fn reflow_shape(
|
||||
reflown: &mut HashSet<Uuid>,
|
||||
entries: &mut VecDeque<Modifier>,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let Some(shape) = state.shapes.get(id) else {
|
||||
return;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let shapes = &state.shapes;
|
||||
|
||||
let Type::Frame(frame_data) = &shape.shape_type else {
|
||||
return;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if let Some(Layout::FlexLayout(layout_data, flex_data)) = &frame_data.layout {
|
||||
let mut children =
|
||||
flex_layout::reflow_flex_layout(shape, layout_data, flex_data, shapes, bounds);
|
||||
flex_layout::reflow_flex_layout(shape, layout_data, flex_data, shapes, bounds)?;
|
||||
entries.append(&mut children);
|
||||
} else if let Some(Layout::GridLayout(layout_data, grid_data)) = &frame_data.layout {
|
||||
let mut children =
|
||||
grid_layout::reflow_grid_layout(shape, layout_data, grid_data, shapes, bounds);
|
||||
grid_layout::reflow_grid_layout(shape, layout_data, grid_data, shapes, bounds)?;
|
||||
entries.append(&mut children);
|
||||
}
|
||||
reflown.insert(*id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn propagate_modifiers(
|
||||
state: &State,
|
||||
modifiers: &[TransformEntry],
|
||||
pixel_precision: bool,
|
||||
) -> Vec<TransformEntry> {
|
||||
) -> Result<Vec<TransformEntry>> {
|
||||
let mut entries: VecDeque<_> = modifiers
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
@ -399,7 +402,7 @@ pub fn propagate_modifiers(
|
||||
&mut entries,
|
||||
&mut bounds,
|
||||
&mut modifiers,
|
||||
),
|
||||
)?,
|
||||
Modifier::Reflow(id, force_reflow) => {
|
||||
if force_reflow {
|
||||
reflown.remove(&id);
|
||||
@ -437,16 +440,16 @@ pub fn propagate_modifiers(
|
||||
if reflown.contains(id) {
|
||||
continue;
|
||||
}
|
||||
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp);
|
||||
reflow_shape(id, state, &mut reflown, &mut entries, &mut bounds_temp)?;
|
||||
}
|
||||
layout_reflows = HashSet::new();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
modifiers
|
||||
// #[allow(dead_code)]
|
||||
Ok(modifiers
|
||||
.iter()
|
||||
.map(|(key, val)| TransformEntry::from_input(*key, *val))
|
||||
.collect()
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -494,7 +497,8 @@ mod tests {
|
||||
&bounds_after,
|
||||
transform,
|
||||
&HashMap::new(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result.len(), 1);
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::math::{is_move_only_matrix, Bounds, Matrix};
|
||||
use crate::shapes::{ConstraintH, ConstraintV};
|
||||
|
||||
@ -105,14 +106,14 @@ pub fn propagate_shape_constraints(
|
||||
constraint_v: ConstraintV,
|
||||
transform: Matrix,
|
||||
ignore_constrainst: bool,
|
||||
) -> Matrix {
|
||||
) -> Result<Matrix> {
|
||||
// if the constrains are scale & scale or the transform has only moves we
|
||||
// can propagate as is
|
||||
if (ignore_constrainst
|
||||
|| constraint_h == ConstraintH::Scale && constraint_v == ConstraintV::Scale)
|
||||
|| is_move_only_matrix(&transform)
|
||||
{
|
||||
return transform;
|
||||
return Ok(transform);
|
||||
}
|
||||
|
||||
let mut transform = transform;
|
||||
@ -133,7 +134,9 @@ pub fn propagate_shape_constraints(
|
||||
parent_transform.post_translate(center);
|
||||
parent_transform.pre_translate(-center);
|
||||
|
||||
let parent_transform_inv = &parent_transform.invert().unwrap();
|
||||
let parent_transform_inv = &parent_transform.invert().ok_or(Error::CriticalError(
|
||||
"Failed to invert parent transform".to_string(),
|
||||
))?;
|
||||
let origin = parent_transform_inv.map_point(child_bounds_after.nw);
|
||||
|
||||
let mut scale = Matrix::scale((scale_width, scale_height));
|
||||
@ -160,5 +163,5 @@ pub fn propagate_shape_constraints(
|
||||
transform.post_concat(&Matrix::translate(th + tv));
|
||||
}
|
||||
|
||||
transform
|
||||
Ok(transform)
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::math::{self as math, Bounds, Matrix, Point, Vector, VectorExt};
|
||||
use crate::shapes::{
|
||||
AlignContent, AlignItems, AlignSelf, FlexData, JustifyContent, LayoutData, LayoutItem,
|
||||
@ -588,7 +590,7 @@ pub fn reflow_flex_layout(
|
||||
flex_data: &FlexData,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
) -> VecDeque<Modifier> {
|
||||
) -> Result<VecDeque<Modifier>> {
|
||||
let mut result = VecDeque::new();
|
||||
let layout_bounds = &bounds.find(shape);
|
||||
let layout_axis = LayoutAxis::new(shape, layout_bounds, layout_data, flex_data);
|
||||
@ -724,7 +726,9 @@ pub fn reflow_flex_layout(
|
||||
|
||||
let parent_transform = layout_bounds.transform_matrix().unwrap_or_default();
|
||||
|
||||
let parent_transform_inv = &parent_transform.invert().unwrap();
|
||||
let parent_transform_inv = &parent_transform.invert().ok_or(Error::CriticalError(
|
||||
"Failed to invert parent transform".to_string(),
|
||||
))?;
|
||||
let origin = parent_transform_inv.map_point(layout_bounds.nw);
|
||||
|
||||
let mut scale = Matrix::scale((scale_width, scale_height));
|
||||
@ -737,5 +741,5 @@ pub fn reflow_flex_layout(
|
||||
result.push_back(Modifier::parent(shape.id, scale));
|
||||
bounds.insert(shape.id, layout_bounds_after);
|
||||
}
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::math::{self as math, intersect_rays, Bounds, Matrix, Point, Ray, Vector, VectorExt};
|
||||
use crate::shapes::{
|
||||
AlignContent, AlignItems, AlignSelf, Frame, GridCell, GridData, GridTrack, GridTrackType,
|
||||
@ -6,6 +7,7 @@ use crate::shapes::{
|
||||
};
|
||||
use crate::state::ShapesPoolRef;
|
||||
use crate::uuid::Uuid;
|
||||
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
||||
use super::common::GetBounds;
|
||||
@ -704,7 +706,7 @@ pub fn reflow_grid_layout(
|
||||
grid_data: &GridData,
|
||||
shapes: ShapesPoolRef,
|
||||
bounds: &mut HashMap<Uuid, Bounds>,
|
||||
) -> VecDeque<Modifier> {
|
||||
) -> Result<VecDeque<Modifier>> {
|
||||
let mut result = VecDeque::new();
|
||||
let layout_bounds = bounds.find(shape);
|
||||
let children: HashSet<Uuid> = shape.children_ids_iter(true).copied().collect();
|
||||
@ -825,7 +827,9 @@ pub fn reflow_grid_layout(
|
||||
|
||||
let parent_transform = layout_bounds.transform_matrix().unwrap_or_default();
|
||||
|
||||
let parent_transform_inv = &parent_transform.invert().unwrap();
|
||||
let parent_transform_inv = &parent_transform.invert().ok_or(Error::CriticalError(
|
||||
"Failed to invert parent transform".to_string(),
|
||||
))?;
|
||||
let origin = parent_transform_inv.map_point(layout_bounds.nw);
|
||||
|
||||
let mut scale = Matrix::scale((scale_width, scale_height));
|
||||
@ -839,5 +843,5 @@ pub fn reflow_grid_layout(
|
||||
bounds.insert(shape.id, layout_bounds_after);
|
||||
}
|
||||
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -1139,7 +1139,11 @@ impl TextSpan {
|
||||
fn process_ignored_chars(text: &str, browser: u8) -> String {
|
||||
text.chars()
|
||||
.filter_map(|c| {
|
||||
if c < '\u{0020}' || c == '\u{2028}' || c == '\u{2029}' {
|
||||
// Preserve line breaks: \n (U+000A), \r (U+000D), and Unicode separators
|
||||
if c == '\n' || c == '\r' || c == '\u{2028}' || c == '\u{2029}' {
|
||||
return Some(c);
|
||||
}
|
||||
if c < '\u{0020}' {
|
||||
if browser == Browser::Firefox as u8 {
|
||||
None
|
||||
} else {
|
||||
|
||||
@ -6,6 +6,7 @@ mod text_editor;
|
||||
pub use shapes_pool::{ShapesPool, ShapesPoolMutRef, ShapesPoolRef};
|
||||
pub use text_editor::*;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::render::RenderState;
|
||||
use crate::shapes::Shape;
|
||||
use crate::tiles;
|
||||
@ -28,41 +29,44 @@ pub(crate) struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(width: i32, height: i32) -> Self {
|
||||
State {
|
||||
render_state: RenderState::new(width, height),
|
||||
pub fn try_new(width: i32, height: i32) -> Result<Self> {
|
||||
Ok(State {
|
||||
render_state: RenderState::try_new(width, height)?,
|
||||
text_editor_state: TextEditorState::new(),
|
||||
current_id: None,
|
||||
current_browser: 0,
|
||||
shapes: ShapesPool::new(),
|
||||
// TODO: Maybe this can be moved to a different object
|
||||
saved_shapes: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Creates a new temporary shapes pool.
|
||||
// Will panic if a previous temporary pool exists.
|
||||
pub fn start_temp_objects(mut self) -> Self {
|
||||
pub fn start_temp_objects(mut self) -> Result<Self> {
|
||||
if self.saved_shapes.is_some() {
|
||||
panic!("Tried to start a temp objects while the previous have not been restored");
|
||||
return Err(Error::CriticalError(
|
||||
"Tried to start a temp objects while the previous have not been restored"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
self.saved_shapes = Some(self.shapes);
|
||||
self.shapes = ShapesPool::new();
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
// Disposes of the temporary shapes pool restoring the normal pool
|
||||
// Will panic if a there is no temporary pool.
|
||||
pub fn end_temp_objects(mut self) -> Self {
|
||||
self.shapes = self
|
||||
.saved_shapes
|
||||
.expect("Tried to end temp objects but not content to be restored is present");
|
||||
pub fn end_temp_objects(mut self) -> Result<Self> {
|
||||
self.shapes = self.saved_shapes.ok_or(Error::CriticalError(
|
||||
"Tried to end temp objects but not content to be restored is present".to_string(),
|
||||
))?;
|
||||
self.saved_shapes = None;
|
||||
self
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, width: i32, height: i32) {
|
||||
self.render_state.resize(width, height);
|
||||
pub fn resize(&mut self, width: i32, height: i32) -> Result<()> {
|
||||
self.render_state.resize(width, height)
|
||||
}
|
||||
|
||||
pub fn render_state_mut(&mut self) -> &mut RenderState {
|
||||
@ -87,38 +91,34 @@ impl State {
|
||||
self.render_state.render_from_cache(&self.shapes);
|
||||
}
|
||||
|
||||
pub fn render_sync(&mut self, timestamp: i32) -> Result<(), String> {
|
||||
pub fn render_sync(&mut self, timestamp: i32) -> Result<()> {
|
||||
self.render_state
|
||||
.start_render_loop(None, &self.shapes, timestamp, true)?;
|
||||
Ok(())
|
||||
.start_render_loop(None, &self.shapes, timestamp, true)
|
||||
}
|
||||
|
||||
pub fn render_sync_shape(&mut self, id: &Uuid, timestamp: i32) -> Result<(), String> {
|
||||
pub fn render_sync_shape(&mut self, id: &Uuid, timestamp: i32) -> Result<()> {
|
||||
self.render_state
|
||||
.start_render_loop(Some(id), &self.shapes, timestamp, true)?;
|
||||
Ok(())
|
||||
.start_render_loop(Some(id), &self.shapes, timestamp, true)
|
||||
}
|
||||
|
||||
pub fn start_render_loop(&mut self, timestamp: i32) -> Result<(), String> {
|
||||
// If zoom changed, we MUST rebuild the tile index before using it.
|
||||
// Otherwise, the index will have tiles from the old zoom level, causing visible
|
||||
// tiles to appear empty. This can happen if start_render_loop() is called before
|
||||
// set_view_end() finishes rebuilding the index, or if set_view_end() hasn't been
|
||||
// called yet.
|
||||
let zoom_changed = self.render_state.zoom_changed();
|
||||
if zoom_changed {
|
||||
self.rebuild_tiles_shallow();
|
||||
pub fn start_render_loop(&mut self, timestamp: i32) -> Result<()> {
|
||||
// If zoom changed (e.g. interrupted zoom render followed by pan), the
|
||||
// tile index may be stale for the new viewport position. Rebuild the
|
||||
// index so shapes are mapped to the correct tiles. We use
|
||||
// rebuild_tile_index (NOT rebuild_tiles_shallow) to preserve the tile
|
||||
// texture cache — otherwise cached tiles with shadows/blur would be
|
||||
// cleared and re-rendered in fast mode without effects.
|
||||
if self.render_state.zoom_changed() {
|
||||
self.render_state.rebuild_tile_index(&self.shapes);
|
||||
}
|
||||
|
||||
self.render_state
|
||||
.start_render_loop(None, &self.shapes, timestamp, false)?;
|
||||
Ok(())
|
||||
.start_render_loop(None, &self.shapes, timestamp, false)
|
||||
}
|
||||
|
||||
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<(), String> {
|
||||
pub fn process_animation_frame(&mut self, timestamp: i32) -> Result<()> {
|
||||
self.render_state
|
||||
.process_animation_frame(None, &self.shapes, timestamp)?;
|
||||
Ok(())
|
||||
.process_animation_frame(None, &self.shapes, timestamp)
|
||||
}
|
||||
|
||||
pub fn clear_focus_mode(&mut self) {
|
||||
@ -227,10 +227,10 @@ impl State {
|
||||
let _ = self.render_state.render_preview(&self.shapes, timestamp);
|
||||
}
|
||||
|
||||
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) {
|
||||
pub fn rebuild_modifier_tiles(&mut self, ids: Vec<Uuid>) -> Result<()> {
|
||||
// Index-based storage is safe
|
||||
self.render_state
|
||||
.rebuild_modifier_tiles(&mut self.shapes, ids);
|
||||
.rebuild_modifier_tiles(&mut self.shapes, ids)
|
||||
}
|
||||
|
||||
pub fn font_collection(&self) -> &FontCollection {
|
||||
|
||||
@ -103,9 +103,68 @@ pub struct TextEditorTheme {
|
||||
pub cursor_color: Color,
|
||||
}
|
||||
|
||||
pub struct TextComposition {
|
||||
pub previous: String,
|
||||
pub current: String,
|
||||
pub is_composing: bool,
|
||||
}
|
||||
|
||||
impl TextComposition {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
previous: String::new(),
|
||||
current: String::new(),
|
||||
is_composing: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> bool {
|
||||
if self.is_composing {
|
||||
return false;
|
||||
}
|
||||
self.is_composing = true;
|
||||
self.previous = String::new();
|
||||
self.current = String::new();
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update(&mut self, text: &str) -> bool {
|
||||
if !self.is_composing {
|
||||
self.is_composing = true;
|
||||
}
|
||||
self.previous = self.current.clone();
|
||||
self.current = text.to_owned();
|
||||
true
|
||||
}
|
||||
|
||||
pub fn end(&mut self) -> bool {
|
||||
if !self.is_composing {
|
||||
return false;
|
||||
}
|
||||
self.is_composing = false;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get_selection(&self, selection: &TextSelection) -> TextSelection {
|
||||
if self.previous.is_empty() {
|
||||
return *selection;
|
||||
}
|
||||
|
||||
let focus = selection.focus;
|
||||
let previous_len = self.previous.chars().count();
|
||||
let anchor = TextPositionWithAffinity::new_without_affinity(
|
||||
focus.paragraph,
|
||||
focus.offset + previous_len,
|
||||
);
|
||||
|
||||
TextSelection { anchor, focus }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextEditorState {
|
||||
pub theme: TextEditorTheme,
|
||||
pub selection: TextSelection,
|
||||
pub composition: TextComposition,
|
||||
pub is_active: bool,
|
||||
// This property indicates that we've started
|
||||
// selecting something with the pointer.
|
||||
@ -125,6 +184,7 @@ impl TextEditorState {
|
||||
cursor_color: CURSOR_COLOR,
|
||||
},
|
||||
selection: TextSelection::new(),
|
||||
composition: TextComposition::new(),
|
||||
is_active: false,
|
||||
is_pointer_selection_active: false,
|
||||
active_shape_id: None,
|
||||
|
||||
@ -3,7 +3,6 @@ use crate::uuid::Uuid;
|
||||
use crate::view::Viewbox;
|
||||
use skia_safe as skia;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
|
||||
pub struct Tile(pub i32, pub i32);
|
||||
|
||||
@ -178,13 +177,10 @@ impl TileHashMap {
|
||||
}
|
||||
|
||||
pub fn add_shape_at(&mut self, tile: Tile, shape_id: Uuid) {
|
||||
self.grid.entry(tile).or_default();
|
||||
self.index.entry(shape_id).or_default();
|
||||
|
||||
let tile_set = self.grid.get_mut(&tile).unwrap();
|
||||
let tile_set = self.grid.entry(tile).or_default();
|
||||
tile_set.insert(shape_id);
|
||||
|
||||
let index_set = self.index.get_mut(&shape_id).unwrap();
|
||||
let index_set = self.index.entry(shape_id).or_default();
|
||||
index_set.insert(tile);
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,8 @@ use crate::{with_current_shape_mut, STATE};
|
||||
#[repr(u8)]
|
||||
#[allow(dead_code)]
|
||||
pub enum RawBlurType {
|
||||
LayerBlur = 0, // odd naming to comply with cljs value
|
||||
LayerBlur = 0,
|
||||
BackgroundBlur = 1,
|
||||
}
|
||||
|
||||
impl From<u8> for RawBlurType {
|
||||
@ -20,6 +21,7 @@ impl From<RawBlurType> for BlurType {
|
||||
fn from(value: RawBlurType) -> Self {
|
||||
match value {
|
||||
RawBlurType::LayerBlur => BlurType::LayerBlur,
|
||||
RawBlurType::BackgroundBlur => BlurType::BackgroundBlur,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
use crate::error::{Error, Result};
|
||||
use crate::mem;
|
||||
use macros::wasm_error;
|
||||
// use crate::mem::SerializableResult;
|
||||
use crate::error::Error;
|
||||
use crate::uuid::Uuid;
|
||||
use crate::with_state_mut;
|
||||
use crate::STATE;
|
||||
use crate::{shapes::ImageFill, utils::uuid_from_u32_quartet};
|
||||
use macros::wasm_error;
|
||||
|
||||
const FLAG_KEEP_ASPECT_RATIO: u8 = 1 << 0;
|
||||
const IMAGE_IDS_SIZE: usize = 32;
|
||||
@ -50,6 +49,7 @@ pub struct ShapeImageIds {
|
||||
|
||||
impl From<[u8; IMAGE_IDS_SIZE]> for ShapeImageIds {
|
||||
fn from(bytes: [u8; IMAGE_IDS_SIZE]) -> Self {
|
||||
// FIXME: this should probably be a try_from instead
|
||||
let shape_id = Uuid::try_from(&bytes[0..16]).unwrap();
|
||||
let image_id = Uuid::try_from(&bytes[16..32]).unwrap();
|
||||
ShapeImageIds { shape_id, image_id }
|
||||
@ -57,9 +57,9 @@ impl From<[u8; IMAGE_IDS_SIZE]> for ShapeImageIds {
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for ShapeImageIds {
|
||||
type Error = &'static str;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
fn try_from(value: Vec<u8>) -> Result<Self> {
|
||||
let mut arr = [0u8; IMAGE_IDS_SIZE];
|
||||
arr.copy_from_slice(&value);
|
||||
Ok(ShapeImageIds::from(arr))
|
||||
@ -68,13 +68,16 @@ impl TryFrom<Vec<u8>> for ShapeImageIds {
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn store_image() -> crate::error::Result<()> {
|
||||
pub extern "C" fn store_image() -> Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
let ids = ShapeImageIds::try_from(bytes[0..IMAGE_IDS_SIZE].to_vec()).unwrap();
|
||||
let ids = ShapeImageIds::try_from(bytes[0..IMAGE_IDS_SIZE].to_vec())?;
|
||||
|
||||
// Read is_thumbnail flag (4 bytes as u32)
|
||||
let is_thumbnail_bytes = &bytes[IMAGE_IDS_SIZE..IMAGE_HEADER_SIZE];
|
||||
let is_thumbnail_value = u32::from_le_bytes(is_thumbnail_bytes.try_into().unwrap());
|
||||
let is_thumbnail_value =
|
||||
u32::from_le_bytes(is_thumbnail_bytes.try_into().map_err(|_| {
|
||||
Error::CriticalError("Invalid bytes for is_thumbnail flag".to_string())
|
||||
})?);
|
||||
let is_thumbnail = is_thumbnail_value != 0;
|
||||
|
||||
let image_bytes = &bytes[IMAGE_HEADER_SIZE..];
|
||||
@ -104,9 +107,10 @@ pub extern "C" fn store_image() -> crate::error::Result<()> {
|
||||
/// - bytes 44-47: height (i32)
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn store_image_from_texture() -> crate::error::Result<()> {
|
||||
pub extern "C" fn store_image_from_texture() -> Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
// FIXME: where does this 48 come from?
|
||||
if bytes.len() < 48 {
|
||||
// FIXME: Review if this should be an critical or a recoverable error.
|
||||
eprintln!("store_image_from_texture: insufficient data");
|
||||
@ -116,23 +120,41 @@ pub extern "C" fn store_image_from_texture() -> crate::error::Result<()> {
|
||||
));
|
||||
}
|
||||
|
||||
let ids = ShapeImageIds::try_from(bytes[0..IMAGE_IDS_SIZE].to_vec()).unwrap();
|
||||
let ids = ShapeImageIds::try_from(bytes[0..IMAGE_IDS_SIZE].to_vec())
|
||||
.map_err(|_| Error::CriticalError("Invalid image ids".to_string()))?;
|
||||
|
||||
// FIXME: read bytes in a safe way
|
||||
|
||||
// Read is_thumbnail flag (4 bytes as u32)
|
||||
let is_thumbnail_bytes = &bytes[IMAGE_IDS_SIZE..IMAGE_HEADER_SIZE];
|
||||
let is_thumbnail_value = u32::from_le_bytes(is_thumbnail_bytes.try_into().unwrap());
|
||||
let is_thumbnail_value =
|
||||
u32::from_le_bytes(is_thumbnail_bytes.try_into().map_err(|_| {
|
||||
Error::CriticalError("Invalid bytes for is_thumbnail flag".to_string())
|
||||
})?);
|
||||
let is_thumbnail = is_thumbnail_value != 0;
|
||||
|
||||
// Read GL texture ID (4 bytes as u32)
|
||||
let texture_id_bytes = &bytes[36..40];
|
||||
let texture_id = u32::from_le_bytes(texture_id_bytes.try_into().unwrap());
|
||||
let texture_id = u32::from_le_bytes(
|
||||
texture_id_bytes
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for texture id".to_string()))?,
|
||||
);
|
||||
|
||||
// Read width and height (8 bytes as two i32s)
|
||||
let width_bytes = &bytes[40..44];
|
||||
let width = i32::from_le_bytes(width_bytes.try_into().unwrap());
|
||||
let width = i32::from_le_bytes(
|
||||
width_bytes
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for width".to_string()))?,
|
||||
);
|
||||
|
||||
let height_bytes = &bytes[44..48];
|
||||
let height = i32::from_le_bytes(height_bytes.try_into().unwrap());
|
||||
let height = i32::from_le_bytes(
|
||||
height_bytes
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for height".to_string()))?,
|
||||
);
|
||||
|
||||
with_state_mut!(state, {
|
||||
if let Err(msg) = state.render_state_mut().add_image_from_gl_texture(
|
||||
@ -142,6 +164,7 @@ pub extern "C" fn store_image_from_texture() -> crate::error::Result<()> {
|
||||
width,
|
||||
height,
|
||||
) {
|
||||
// FIXME: Review if we should return a RecoverableError
|
||||
eprintln!("store_image_from_texture error: {}", msg);
|
||||
}
|
||||
state.touch_shape(ids.shape_id);
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{uuid_from_u32_quartet, with_current_shape_mut, with_state, with_stat
|
||||
use super::align;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::error::Result;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(1))]
|
||||
@ -177,9 +177,13 @@ pub extern "C" fn set_grid_columns() -> Result<()> {
|
||||
|
||||
let entries: Vec<GridTrack> = bytes
|
||||
.chunks(size_of::<RawGridTrack>())
|
||||
.map(|data| data.try_into().unwrap())
|
||||
.map(|data: [u8; size_of::<RawGridTrack>()]| RawGridTrack::from(data).into())
|
||||
.collect();
|
||||
.map(|data| {
|
||||
let track_bytes: [u8; size_of::<RawGridTrack>()] = data
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for grid track".to_string()))?;
|
||||
Ok(RawGridTrack::from(track_bytes).into())
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||
shape.set_grid_columns(entries);
|
||||
@ -196,9 +200,13 @@ pub extern "C" fn set_grid_rows() -> Result<()> {
|
||||
|
||||
let entries: Vec<GridTrack> = bytes
|
||||
.chunks(size_of::<RawGridTrack>())
|
||||
.map(|data| data.try_into().unwrap())
|
||||
.map(|data: [u8; size_of::<RawGridTrack>()]| RawGridTrack::from(data).into())
|
||||
.collect();
|
||||
.map(|data| {
|
||||
let track_bytes: [u8; size_of::<RawGridTrack>()] = data
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for grid track".to_string()))?;
|
||||
Ok(RawGridTrack::from(track_bytes).into())
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||
shape.set_grid_rows(entries);
|
||||
@ -215,9 +223,13 @@ pub extern "C" fn set_grid_cells() -> Result<()> {
|
||||
|
||||
let cells: Vec<RawGridCell> = bytes
|
||||
.chunks(size_of::<RawGridCell>())
|
||||
.map(|data| data.try_into().expect("Invalid grid cell data"))
|
||||
.map(|data: [u8; size_of::<RawGridCell>()]| RawGridCell::from(data))
|
||||
.collect();
|
||||
.map(|data| {
|
||||
let cell_bytes: [u8; size_of::<RawGridCell>()] = data
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for grid cell".to_string()))?;
|
||||
Ok(RawGridCell::from(cell_bytes))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||
shape.set_grid_cells(cells.into_iter().map(|raw| raw.into()).collect());
|
||||
|
||||
@ -4,6 +4,7 @@ use mem::SerializableResult;
|
||||
use std::mem::size_of;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::shapes::{Path, Segment, ToPath};
|
||||
use crate::{mem, with_current_shape, with_current_shape_mut, STATE};
|
||||
|
||||
@ -41,12 +42,12 @@ impl From<[u8; size_of::<RawSegmentData>()]> for RawSegmentData {
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for RawSegmentData {
|
||||
type Error = String;
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
type Error = Error;
|
||||
fn try_from(bytes: &[u8]) -> Result<Self> {
|
||||
let data: [u8; RAW_SEGMENT_DATA_SIZE] = bytes
|
||||
.get(0..RAW_SEGMENT_DATA_SIZE)
|
||||
.and_then(|slice| slice.try_into().ok())
|
||||
.ok_or("Invalid path data".to_string())?;
|
||||
.ok_or(Error::CriticalError("Invalid path data".to_string()))?;
|
||||
Ok(RawSegmentData::from(data))
|
||||
}
|
||||
}
|
||||
@ -154,10 +155,14 @@ fn get_path_upload_buffer() -> &'static Mutex<Vec<u8>> {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn start_shape_path_buffer() {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn start_shape_path_buffer() -> Result<()> {
|
||||
let buffer = get_path_upload_buffer();
|
||||
let mut buffer = buffer.lock().unwrap();
|
||||
let mut buffer = buffer
|
||||
.lock()
|
||||
.map_err(|_| Error::CriticalError("Failed to lock path buffer".to_string()))?;
|
||||
buffer.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -165,32 +170,40 @@ pub extern "C" fn start_shape_path_buffer() {
|
||||
pub extern "C" fn set_shape_path_chunk_buffer() -> Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
let buffer = get_path_upload_buffer();
|
||||
let mut buffer = buffer.lock().unwrap();
|
||||
let mut buffer = buffer
|
||||
.lock()
|
||||
.map_err(|_| Error::CriticalError("Failed to lock path buffer".to_string()))?;
|
||||
buffer.extend_from_slice(&bytes);
|
||||
mem::free_bytes()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_shape_path_buffer() {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn set_shape_path_buffer() -> Result<()> {
|
||||
let buffer = get_path_upload_buffer();
|
||||
let mut buffer = buffer
|
||||
.lock()
|
||||
.map_err(|_| Error::CriticalError("Failed to lock path buffer".to_string()))?;
|
||||
let chunk_size = size_of::<RawSegmentData>();
|
||||
if !buffer.len().is_multiple_of(chunk_size) {
|
||||
// FIXME
|
||||
println!("Warning: buffer length is not a multiple of chunk size!");
|
||||
}
|
||||
let mut segments = Vec::new();
|
||||
for (i, chunk) in buffer.chunks(chunk_size).enumerate() {
|
||||
match RawSegmentData::try_from(chunk) {
|
||||
Ok(seg) => segments.push(Segment::from(seg)),
|
||||
Err(e) => println!("Error at segment {}: {}", i, e),
|
||||
}
|
||||
}
|
||||
|
||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||
let buffer = get_path_upload_buffer();
|
||||
let mut buffer = buffer.lock().unwrap();
|
||||
let chunk_size = size_of::<RawSegmentData>();
|
||||
if !buffer.len().is_multiple_of(chunk_size) {
|
||||
// FIXME
|
||||
println!("Warning: buffer length is not a multiple of chunk size!");
|
||||
}
|
||||
let mut segments = Vec::new();
|
||||
for (i, chunk) in buffer.chunks(chunk_size).enumerate() {
|
||||
match RawSegmentData::try_from(chunk) {
|
||||
Ok(seg) => segments.push(Segment::from(seg)),
|
||||
Err(e) => println!("Error at segment {}: {}", i, e),
|
||||
}
|
||||
}
|
||||
shape.set_path_segments(segments);
|
||||
buffer.clear();
|
||||
});
|
||||
buffer.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@ -6,6 +6,10 @@ use crate::wasm::blend::RawBlendMode;
|
||||
use crate::wasm::layouts::constraints::{RawConstraintH, RawConstraintV};
|
||||
use crate::{with_state_mut, STATE};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::error::{Error, Result};
|
||||
use macros::wasm_error;
|
||||
|
||||
use super::RawShapeType;
|
||||
|
||||
const FLAG_CLIP_CONTENT: u8 = 0b0000_0001;
|
||||
@ -106,14 +110,18 @@ impl From<[u8; RAW_BASE_PROPS_SIZE]> for RawBasePropsData {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_shape_base_props() {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn set_shape_base_props() -> Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
if bytes.len() < RAW_BASE_PROPS_SIZE {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let data: [u8; RAW_BASE_PROPS_SIZE] = bytes[..RAW_BASE_PROPS_SIZE].try_into().unwrap();
|
||||
// FIXME: this should just be a try_from
|
||||
let data: [u8; RAW_BASE_PROPS_SIZE] = bytes[..RAW_BASE_PROPS_SIZE]
|
||||
.try_into()
|
||||
.map_err(|_| Error::CriticalError("Invalid bytes for base props".to_string()))?;
|
||||
let raw = RawBasePropsData::from(data);
|
||||
|
||||
let id = raw.id();
|
||||
@ -151,6 +159,7 @@ pub extern "C" fn set_shape_base_props() {
|
||||
shape.set_corners((raw.corner_r1, raw.corner_r2, raw.corner_r3, raw.corner_r4));
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -292,9 +292,10 @@ pub extern "C" fn clear_shape_text() {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn set_shape_text_content() -> crate::error::Result<()> {
|
||||
let bytes = mem::bytes();
|
||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||
let raw_text_data = RawParagraph::try_from(&bytes).unwrap();
|
||||
let raw_text_data = RawParagraph::try_from(&bytes)
|
||||
.map_err(|_| Error::CriticalError("Invalid text data".to_string()))?;
|
||||
|
||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||
shape.add_paragraph(raw_text_data.into()).map_err(|_| {
|
||||
Error::RecoverableError(format!(
|
||||
"Error with set_shape_text_content on {:?}",
|
||||
|
||||
@ -293,6 +293,135 @@ pub extern "C" fn text_editor_set_cursor_from_point(x: f32, y: f32) {
|
||||
// TEXT OPERATIONS
|
||||
// ============================================================================
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn text_editor_composition_start() -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.is_active {
|
||||
return Ok(());
|
||||
}
|
||||
state.text_editor_state.composition.start();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn text_editor_composition_end() -> Result<()> {
|
||||
let bytes = crate::mem::bytes();
|
||||
let text = match String::from_utf8(bytes) {
|
||||
Ok(text) => text,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.is_active {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(shape) = state.shapes.get_mut(&shape_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Type::Text(text_content) = &mut shape.shape_type else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
state.text_editor_state.composition.update(&text);
|
||||
|
||||
let selection = state
|
||||
.text_editor_state
|
||||
.composition
|
||||
.get_selection(&state.text_editor_state.selection);
|
||||
text_helpers::delete_selection_range(text_content, &selection);
|
||||
|
||||
let cursor = state.text_editor_state.selection.focus;
|
||||
if let Some(new_cursor) =
|
||||
text_helpers::insert_text_with_newlines(text_content, &cursor, &text)
|
||||
{
|
||||
state.text_editor_state.selection.set_caret(new_cursor);
|
||||
}
|
||||
|
||||
text_content.layout.paragraphs.clear();
|
||||
text_content.layout.paragraph_builders.clear();
|
||||
|
||||
state.text_editor_state.reset_blink();
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::ContentChanged);
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::NeedsLayout);
|
||||
|
||||
state.render_state.mark_touched(shape_id);
|
||||
|
||||
state.text_editor_state.composition.end();
|
||||
});
|
||||
|
||||
crate::mem::free_bytes()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
pub extern "C" fn text_editor_composition_update() -> Result<()> {
|
||||
let bytes = crate::mem::bytes();
|
||||
let text = match String::from_utf8(bytes) {
|
||||
Ok(text) => text,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
with_state_mut!(state, {
|
||||
if !state.text_editor_state.is_active {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let Some(shape_id) = state.text_editor_state.active_shape_id else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(shape) = state.shapes.get_mut(&shape_id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Type::Text(text_content) = &mut shape.shape_type else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
state.text_editor_state.composition.update(&text);
|
||||
|
||||
let selection = state
|
||||
.text_editor_state
|
||||
.composition
|
||||
.get_selection(&state.text_editor_state.selection);
|
||||
text_helpers::delete_selection_range(text_content, &selection);
|
||||
|
||||
let cursor = state.text_editor_state.selection.focus;
|
||||
text_helpers::insert_text_with_newlines(text_content, &cursor, &text);
|
||||
|
||||
text_content.layout.paragraphs.clear();
|
||||
text_content.layout.paragraph_builders.clear();
|
||||
|
||||
state.text_editor_state.reset_blink();
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::ContentChanged);
|
||||
state
|
||||
.text_editor_state
|
||||
.push_event(crate::state::TextEditorEvent::NeedsLayout);
|
||||
|
||||
state.render_state.mark_touched(shape_id);
|
||||
});
|
||||
|
||||
crate::mem::free_bytes()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// FIXME: Review if all the return Ok(()) should be Err instead.
|
||||
#[no_mangle]
|
||||
#[wasm_error]
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
use macros::ToJs;
|
||||
#[allow(unused_imports)]
|
||||
use crate::error::{Error, Result};
|
||||
use macros::{wasm_error, ToJs};
|
||||
|
||||
use skia_safe as skia;
|
||||
|
||||
@ -39,11 +41,11 @@ impl From<[u8; RAW_TRANSFORM_ENTRY_SIZE]> for RawTransformEntry {
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for RawTransformEntry {
|
||||
type Error = String;
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
type Error = Error;
|
||||
fn try_from(bytes: &[u8]) -> Result<Self> {
|
||||
let bytes: [u8; RAW_TRANSFORM_ENTRY_SIZE] = bytes
|
||||
.try_into()
|
||||
.map_err(|_| "Invalid transform entry bytes".to_string())?;
|
||||
.map_err(|_| Error::CriticalError("Invalid transform entry bytes".to_string()))?;
|
||||
Ok(RawTransformEntry::from(bytes))
|
||||
}
|
||||
}
|
||||
@ -73,16 +75,17 @@ impl From<RawTransformEntry> for TransformEntry {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> *mut u8 {
|
||||
#[wasm_error]
|
||||
pub extern "C" fn propagate_modifiers(pixel_precision: bool) -> Result<*mut u8> {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
let entries: Vec<TransformEntry> = bytes
|
||||
.chunks(RAW_TRANSFORM_ENTRY_SIZE)
|
||||
.map(|data| RawTransformEntry::try_from(data).unwrap().into())
|
||||
.collect();
|
||||
.map(|data| RawTransformEntry::try_from(data).map(|entry| entry.into()))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
with_state!(state, {
|
||||
let result = shapes::propagate_modifiers(state, &entries, pixel_precision);
|
||||
mem::write_vec(result)
|
||||
let result = shapes::propagate_modifiers(state, &entries, pixel_precision)?;
|
||||
Ok(mem::write_vec(result))
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user