diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc index 23ef653592..9b64aca37b 100644 --- a/common/src/app/common/flags.cljc +++ b/common/src/app/common/flags.cljc @@ -162,7 +162,8 @@ ;; Activates the nitrate module :nitrate - :mcp}) + :mcp + :background-blur}) (def all-flags (set/union email login varia)) diff --git a/frontend/playwright/data/render-wasm/get-file-background-blur.json b/frontend/playwright/data/render-wasm/get-file-background-blur.json new file mode 100644 index 0000000000..34bbba7ad8 --- /dev/null +++ b/frontend/playwright/data/render-wasm/get-file-background-blur.json @@ -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" + } + } +} \ No newline at end of file diff --git a/frontend/playwright/ui/pages/WorkspacePage.js b/frontend/playwright/ui/pages/WorkspacePage.js index 92f9b9bef2..1dcac5c4ca 100644 --- a/frontend/playwright/ui/pages/WorkspacePage.js +++ b/frontend/playwright/ui/pages/WorkspacePage.js @@ -212,6 +212,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}`, @@ -219,12 +220,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) { diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js index 4d0597b9aa..21fb267806 100644 --- a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js @@ -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(); }); \ No newline at end of file diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-background-blur-on-shapes-overlapping-other-shapes-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-background-blur-on-shapes-overlapping-other-shapes-1.png new file mode 100644 index 0000000000..69f126eeaf Binary files /dev/null and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-background-blur-on-shapes-overlapping-other-shapes-1.png differ diff --git a/frontend/playwright/ui/specs/design-tab.spec.js b/frontend/playwright/ui/specs/design-tab.spec.js index 489579b844..8fe67b9d3a 100644 --- a/frontend/playwright/ui/specs/design-tab.spec.js +++ b/frontend/playwright/ui/specs/design-tab.spec.js @@ -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, }) => { diff --git a/frontend/src/app/main/ui/inspect/attributes/blur.cljs b/frontend/src/app/main/ui/inspect/attributes/blur.cljs index 21a21a6b4b..553178483d 100644 --- a/frontend/src/app/main/ui/inspect/attributes/blur.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/blur.cljs @@ -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)]]]]))]]))) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index 9dcbd0672e..6e0adbebea 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -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 diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs index 52a5570614..f1beba6eb0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.cljs @@ -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") diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss index e15a50c83e..c80e57e1ec 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/blur.scss @@ -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; diff --git a/frontend/src/app/util/code_gen/style_css.cljs b/frontend/src/app/util/code_gen/style_css.cljs index e72fc9a9ee..8354a73682 100644 --- a/frontend/src/app/util/code_gen/style_css.cljs +++ b/frontend/src/app/util/code_gen/style_css.cljs @@ -79,6 +79,7 @@ body { :border-end-end-radius :box-shadow :filter + :backdrop-filter :opacity :overflow :blend-mode diff --git a/frontend/src/app/util/code_gen/style_css_formats.cljs b/frontend/src/app/util/code_gen/style_css_formats.cljs index 74c074d2bc..547e022dc6 100644 --- a/frontend/src/app/util/code_gen/style_css_formats.cljs +++ b/frontend/src/app/util/code_gen/style_css_formats.cljs @@ -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 diff --git a/frontend/src/app/util/code_gen/style_css_values.cljs b/frontend/src/app/util/code_gen/style_css_values.cljs index cba1991599..83b6bd7a98 100644 --- a/frontend/src/app/util/code_gen/style_css_values.cljs +++ b/frontend/src/app/util/code_gen/style_css_values.cljs @@ -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 diff --git a/frontend/translations/en.po b/frontend/translations/en.po index ebe68d176b..c36e440412 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -6097,6 +6097,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" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 2721f60649..4d58a6dc7f 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -6059,6 +6059,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" diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index 271a7bc38d..68748effe0 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -281,6 +281,9 @@ pub(crate) struct RenderState { pub current_tile: Option, 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, @@ -358,6 +361,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, @@ -426,6 +430,104 @@ 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. @@ -788,6 +890,22 @@ impl RenderState { // We don't want to change the value in the global state let mut shape: Cow = 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(); @@ -1181,9 +1299,18 @@ impl RenderState { 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) { @@ -1685,7 +1812,7 @@ impl RenderState { // 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) { + if !bounds.intersects(self.render_area_with_margins) { return; } @@ -2051,11 +2178,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) }; @@ -2102,6 +2229,12 @@ impl RenderState { ); } + // 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); } @@ -2320,10 +2453,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); } } diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index 11239c2007..536737daba 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -115,6 +115,10 @@ impl Surfaces { self.tiles.clear(); } + pub fn margins(&self) -> skia::ISize { + self.margins + } + 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)); } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 390391e11b..dedf9ab770 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -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, }) } diff --git a/render-wasm/src/shapes/blurs.rs b/render-wasm/src/shapes/blurs.rs index 543e11efa8..828dcb10d3 100644 --- a/render-wasm/src/shapes/blurs.rs +++ b/render-wasm/src/shapes/blurs.rs @@ -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)] diff --git a/render-wasm/src/wasm/blurs.rs b/render-wasm/src/wasm/blurs.rs index 700ac053bc..e8e1a4242c 100644 --- a/render-wasm/src/wasm/blurs.rs +++ b/render-wasm/src/wasm/blurs.rs @@ -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 for RawBlurType { @@ -20,6 +21,7 @@ impl From for BlurType { fn from(value: RawBlurType) -> Self { match value { RawBlurType::LayerBlur => BlurType::LayerBlur, + RawBlurType::BackgroundBlur => BlurType::BackgroundBlur, } } }